home *** CD-ROM | disk | FTP | other *** search
/ Freelog 115 / FreelogNo115-MaiJuin2013.iso / Internet / AvantBrowser / asetup.exe / _data / webkit / chrome.dll / 0 / BINDATA / 583 < prev    next >
Encoding:
Text File  |  2013-04-03  |  376.4 KB  |  11,913 lines

  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.  
  5. // TODO(rltoscano): Move data/* into print_preview.data namespace
  6.  
  7. var localStrings = new LocalStrings(templateData);
  8.  
  9. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10. // Use of this source code is governed by a BSD-style license that can be
  11. // found in the LICENSE file.
  12.  
  13. cr.define('print_preview', function() {
  14.   'use strict';
  15.  
  16.   /**
  17.    * Class that represents a UI component.
  18.    * @constructor
  19.    * @extends {cr.EventTarget}
  20.    */
  21.   function Component() {
  22.     cr.EventTarget.call(this);
  23.  
  24.     /**
  25.      * Component's HTML element.
  26.      * @type {Element}
  27.      * @private
  28.      */
  29.     this.element_ = null;
  30.  
  31.     this.isInDocument_ = false;
  32.  
  33.     /**
  34.      * Component's event tracker.
  35.      * @type {EventTracker}
  36.      * @private
  37.      */
  38.      this.tracker_ = new EventTracker();
  39.  
  40.     /**
  41.      * Child components of the component.
  42.      * @type {Array.<print_preview.Component>}
  43.      * @private
  44.      */
  45.     this.children_ = [];
  46.   };
  47.  
  48.   Component.prototype = {
  49.     __proto__: cr.EventTarget.prototype,
  50.  
  51.     /** Gets the component's element. */
  52.     getElement: function() {
  53.       return this.element_;
  54.     },
  55.  
  56.     /** @return {EventTracker} Component's event tracker. */
  57.     get tracker() {
  58.       return this.tracker_;
  59.     },
  60.  
  61.     /**
  62.      * @return {boolean} Whether the element of the component is already in the
  63.      *     HTML document.
  64.      */
  65.     get isInDocument() {
  66.       return this.isInDocument_;
  67.     },
  68.  
  69.     /**
  70.      * Creates the root element of the component. Sub-classes should override
  71.      * this method.
  72.      */
  73.     createDom: function() {
  74.       this.element_ = cr.doc.createElement('div');
  75.     },
  76.  
  77.     /**
  78.      * Called when the component's element is known to be in the document.
  79.      * Anything using document.getElementById etc. should be done at this stage.
  80.      * Sub-classes should extend this method and attach listeners.
  81.      */
  82.     enterDocument: function() {
  83.       this.isInDocument_ = true;
  84.       this.children_.forEach(function(child) {
  85.         if (!child.isInDocument && child.getElement()) {
  86.           child.enterDocument();
  87.         }
  88.       });
  89.     },
  90.  
  91.     /** Removes all event listeners. */
  92.     exitDocument: function() {
  93.       this.children_.forEach(function(child) {
  94.         if (child.isInDocument) {
  95.           child.exitDocument();
  96.         }
  97.       });
  98.       this.tracker_.removeAll();
  99.       this.isInDocument_ = false;
  100.     },
  101.  
  102.     /**
  103.      * Renders this UI component and appends the element to the given parent
  104.      * element.
  105.      * @param {!Element} parentElement Element to render the component's
  106.      *     element into.
  107.      */
  108.     render: function(parentElement) {
  109.       assert(!this.isInDocument, 'Component is already in the document');
  110.       if (!this.element_) {
  111.         this.createDom();
  112.       }
  113.       parentElement.appendChild(this.element_);
  114.       this.enterDocument();
  115.     },
  116.  
  117.     /**
  118.      * Decorates an existing DOM element. Sub-classes should override the
  119.      * override the decorateInternal method.
  120.      * @param {Element} element Element to decorate.
  121.      */
  122.     decorate: function(element) {
  123.       assert(!this.isInDocument, 'Component is already in the document');
  124.       this.setElementInternal(element);
  125.       this.decorateInternal();
  126.       this.enterDocument();
  127.     },
  128.  
  129.     /**
  130.      * @param {print_preview.Component} child Component to add as a child of
  131.      *     this component.
  132.      */
  133.     addChild: function(child) {
  134.       this.children_.push(child);
  135.     },
  136.  
  137.     /**
  138.      * @param {!print_preview.Component} child Component to remove from this
  139.      *     component's children.
  140.      */
  141.     removeChild: function(child) {
  142.       var childIdx = this.children_.indexOf(child);
  143.       if (childIdx != -1) {
  144.         this.children_.splice(childIdx, 1);
  145.       }
  146.       if (child.isInDocument) {
  147.         child.exitDocument();
  148.         if (child.getElement()) {
  149.           child.getElement().parentNode.removeChild(child.getElement());
  150.         }
  151.       }
  152.     },
  153.  
  154.     /** Removes all of the component's children. */
  155.     removeChildren: function() {
  156.       while (this.children_.length > 0) {
  157.         this.removeChild(this.children_[0]);
  158.       }
  159.     },
  160.  
  161.     /**
  162.      * @param {string} query Selector query to select an element starting from
  163.      *     the component's root element using a depth first search for the first
  164.      *     element that matches the query.
  165.      * @return {HTMLElement} Element selected by the given query.
  166.      */
  167.     getChildElement: function(query) {
  168.       return this.element_.querySelector(query);
  169.     },
  170.  
  171.     /**
  172.      * Sets the component's element.
  173.      * @param {Element} element HTML element to set as the component's element.
  174.      * @protected
  175.      */
  176.     setElementInternal: function(element) {
  177.       this.element_ = element;
  178.     },
  179.  
  180.     /**
  181.      * Decorates the given element for use as the element of the component.
  182.      * @protected
  183.      */
  184.     decorateInternal: function() { /*abstract*/ },
  185.  
  186.     /**
  187.      * Clones a template HTML DOM tree.
  188.      * @param {string} templateId Template element ID.
  189.      * @param {boolean=} opt_keepHidden Whether to leave the cloned template
  190.      *     hidden after cloning.
  191.      * @return {Element} Cloned element with its 'id' attribute stripped.
  192.      * @protected
  193.      */
  194.     cloneTemplateInternal: function(templateId, opt_keepHidden) {
  195.       var templateEl = $(templateId);
  196.       assert(templateEl != null,
  197.              'Could not find element with ID: ' + templateId);
  198.       var el = templateEl.cloneNode(true);
  199.       el.id = '';
  200.       if (!opt_keepHidden) {
  201.         setIsVisible(el, true);
  202.       }
  203.       return el;
  204.     }
  205.   };
  206.  
  207.   return {
  208.     Component: Component
  209.   };
  210. });
  211.  
  212.  
  213. cr.define('print_preview', function() {
  214.   'use strict';
  215.  
  216.   /**
  217.    * Container class for Chromium's print preview.
  218.    * @constructor
  219.    * @extends {print_preview.Component}
  220.    */
  221.   function PrintPreview() {
  222.     print_preview.Component.call(this);
  223.  
  224.     /**
  225.      * Used to communicate with Chromium's print system.
  226.      * @type {!print_preview.NativeLayer}
  227.      * @private
  228.      */
  229.     this.nativeLayer_ = new print_preview.NativeLayer();
  230.  
  231.     /**
  232.      * Event target that contains information about the logged in user.
  233.      * @type {!print_preview.UserInfo}
  234.      * @private
  235.      */
  236.     this.userInfo_ = new print_preview.UserInfo();
  237.  
  238.     /**
  239.      * Metrics object used to report usage statistics.
  240.      * @type {!print_preview.Metrics}
  241.      * @private
  242.      */
  243.     this.metrics_ = new print_preview.Metrics();
  244.  
  245.     /**
  246.      * Application state.
  247.      * @type {!print_preview.AppState}
  248.      * @private
  249.      */
  250.     this.appState_ = new print_preview.AppState();
  251.  
  252.     /**
  253.      * Data store which holds print destinations.
  254.      * @type {!print_preview.DestinationStore}
  255.      * @private
  256.      */
  257.     this.destinationStore_ = new print_preview.DestinationStore(
  258.         this.nativeLayer_, this.appState_);
  259.  
  260.     /**
  261.      * Storage of the print ticket used to create the print job.
  262.      * @type {!print_preview.PrintTicketStore}
  263.      * @private
  264.      */
  265.     this.printTicketStore_ = new print_preview.PrintTicketStore(
  266.         this.destinationStore_, this.appState_);
  267.  
  268.     /**
  269.      * Holds the print and cancel buttons and renders some document statistics.
  270.      * @type {!print_preview.PrintHeader}
  271.      * @private
  272.      */
  273.     this.printHeader_ = new print_preview.PrintHeader(
  274.         this.printTicketStore_, this.destinationStore_);
  275.     this.addChild(this.printHeader_);
  276.  
  277.     /**
  278.      * Component used to search for print destinations.
  279.      * @type {!print_preview.DestinationSearch}
  280.      * @private
  281.      */
  282.     this.destinationSearch_ = new print_preview.DestinationSearch(
  283.         this.destinationStore_, this.userInfo_, this.metrics_);
  284.     this.addChild(this.destinationSearch_);
  285.  
  286.     /**
  287.      * Component that renders the print destination.
  288.      * @type {!print_preview.DestinationSettings}
  289.      * @private
  290.      */
  291.     this.destinationSettings_ = new print_preview.DestinationSettings(
  292.         this.destinationStore_);
  293.     this.addChild(this.destinationSettings_);
  294.  
  295.     /**
  296.      * Component that renders UI for entering in page range.
  297.      * @type {!print_preview.PageSettings}
  298.      * @private
  299.      */
  300.     this.pageSettings_ = new print_preview.PageSettings(this.printTicketStore_);
  301.     this.addChild(this.pageSettings_);
  302.  
  303.     /**
  304.      * Component that renders the copies settings.
  305.      * @type {!print_preview.CopiesSettings}
  306.      * @private
  307.      */
  308.     this.copiesSettings_ = new print_preview.CopiesSettings(
  309.         this.printTicketStore_);
  310.     this.addChild(this.copiesSettings_);
  311.  
  312.     /**
  313.      * Component that renders the layout settings.
  314.      * @type {!print_preview.LayoutSettings}
  315.      * @private
  316.      */
  317.     this.layoutSettings_ = new print_preview.LayoutSettings(
  318.           this.printTicketStore_);
  319.     this.addChild(this.layoutSettings_);
  320.  
  321.     /**
  322.      * Component that renders the color options.
  323.      * @type {!print_preview.ColorSettings}
  324.      * @private
  325.      */
  326.     this.colorSettings_ = new print_preview.ColorSettings(
  327.         this.printTicketStore_);
  328.     this.addChild(this.colorSettings_);
  329.  
  330.     /**
  331.      * Component that renders a select box for choosing margin settings.
  332.      * @type {!print_preview.MarginSettings}
  333.      * @private
  334.      */
  335.     this.marginSettings_ = new print_preview.MarginSettings(
  336.         this.printTicketStore_);
  337.     this.addChild(this.marginSettings_);
  338.  
  339.     /**
  340.      * Component that renders miscellaneous print options.
  341.      * @type {!print_preview.OtherOptionsSettings}
  342.      * @private
  343.      */
  344.     this.otherOptionsSettings_ = new print_preview.OtherOptionsSettings(
  345.         this.printTicketStore_);
  346.     this.addChild(this.otherOptionsSettings_);
  347.  
  348.     /**
  349.      * Area of the UI that holds the print preview.
  350.      * @type {!print_preview.PreviewArea}
  351.      * @private
  352.      */
  353.     this.previewArea_ = new print_preview.PreviewArea(
  354.         this.destinationStore_, this.printTicketStore_, this.nativeLayer_);
  355.     this.addChild(this.previewArea_);
  356.  
  357.     /**
  358.      * Interface to the Google Cloud Print API. Null if Google Cloud Print
  359.      * integration is disabled.
  360.      * @type {cloudprint.CloudPrintInterface}
  361.      * @private
  362.      */
  363.     this.cloudPrintInterface_ = null;
  364.  
  365.     /**
  366.      * Whether in kiosk mode where print preview can print automatically without
  367.      * user intervention. See http://crbug.com/31395. Print will start when
  368.      * both the print ticket has been initialized, and an initial printer has
  369.      * been selected.
  370.      * @type {boolean}
  371.      * @private
  372.      */
  373.     this.isInKioskAutoPrintMode_ = false;
  374.  
  375.     /**
  376.      * State of the print preview UI.
  377.      * @type {print_preview.PrintPreview.UiState_}
  378.      * @private
  379.      */
  380.     this.uiState_ = PrintPreview.UiState_.INITIALIZING;
  381.  
  382.     /**
  383.      * Whether document preview generation is in progress.
  384.      * @type {boolean}
  385.      * @private
  386.      */
  387.     this.isPreviewGenerationInProgress_ = true;
  388.   };
  389.  
  390.   /**
  391.    * States of the print preview.
  392.    * @enum {string}
  393.    * @private
  394.    */
  395.   PrintPreview.UiState_ = {
  396.     INITIALIZING: 'initializing',
  397.     READY: 'ready',
  398.     OPENING_PDF_PREVIEW: 'opening-pdf-preview',
  399.     OPENING_NATIVE_PRINT_DIALOG: 'opening-native-print-dialog',
  400.     PRINTING: 'printing',
  401.     FILE_SELECTION: 'file-selection',
  402.     CLOSING: 'closing',
  403.     ERROR: 'error'
  404.   };
  405.  
  406.   PrintPreview.prototype = {
  407.     __proto__: print_preview.Component.prototype,
  408.  
  409.     /** Sets up the page and print preview by getting the printer list. */
  410.     initialize: function() {
  411.       this.decorate($('print-preview'));
  412.       i18nTemplate.process(document, templateData);
  413.       if (!this.previewArea_.hasCompatiblePlugin) {
  414.         this.setIsEnabled_(false);
  415.       }
  416.       this.nativeLayer_.startGetInitialSettings();
  417.       this.destinationStore_.startLoadLocalDestinations();
  418.     },
  419.  
  420.     /** @override */
  421.     enterDocument: function() {
  422.       // Native layer events.
  423.       this.tracker.add(
  424.           this.nativeLayer_,
  425.           print_preview.NativeLayer.EventType.INITIAL_SETTINGS_SET,
  426.           this.onInitialSettingsSet_.bind(this));
  427.       this.tracker.add(
  428.           this.nativeLayer_,
  429.           print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE,
  430.           this.onCloudPrintEnable_.bind(this));
  431.       this.tracker.add(
  432.           this.nativeLayer_,
  433.           print_preview.NativeLayer.EventType.PRINT_TO_CLOUD,
  434.           this.onPrintToCloud_.bind(this));
  435.       this.tracker.add(
  436.           this.nativeLayer_,
  437.           print_preview.NativeLayer.EventType.FILE_SELECTION_CANCEL,
  438.           this.onFileSelectionCancel_.bind(this));
  439.       this.tracker.add(
  440.           this.nativeLayer_,
  441.           print_preview.NativeLayer.EventType.FILE_SELECTION_COMPLETE,
  442.           this.onFileSelectionComplete_.bind(this));
  443.       this.tracker.add(
  444.           this.nativeLayer_,
  445.           print_preview.NativeLayer.EventType.SETTINGS_INVALID,
  446.           this.onSettingsInvalid_.bind(this));
  447.       this.tracker.add(
  448.           this.nativeLayer_,
  449.           print_preview.NativeLayer.EventType.DISABLE_SCALING,
  450.           this.onDisableScaling_.bind(this));
  451.  
  452.       this.tracker.add(
  453.           $('system-dialog-link'),
  454.           'click',
  455.           this.openSystemPrintDialog_.bind(this));
  456.       this.tracker.add(
  457.           $('cloud-print-dialog-link'),
  458.           'click',
  459.           this.onCloudPrintDialogLinkClick_.bind(this));
  460.       this.tracker.add(
  461.           $('open-pdf-in-preview-link'),
  462.           'click',
  463.           this.onOpenPdfInPreviewLinkClick_.bind(this));
  464.  
  465.       this.tracker.add(
  466.           this.previewArea_,
  467.           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS,
  468.           this.onPreviewGenerationInProgress_.bind(this));
  469.       this.tracker.add(
  470.           this.previewArea_,
  471.           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_DONE,
  472.           this.onPreviewGenerationDone_.bind(this));
  473.       this.tracker.add(
  474.           this.previewArea_,
  475.           print_preview.PreviewArea.EventType.PREVIEW_GENERATION_FAIL,
  476.           this.onPreviewGenerationFail_.bind(this));
  477.       this.tracker.add(
  478.           this.previewArea_,
  479.           print_preview.PreviewArea.EventType.OPEN_SYSTEM_DIALOG_CLICK,
  480.           this.openSystemPrintDialog_.bind(this));
  481.  
  482.       this.tracker.add(
  483.           this.destinationStore_,
  484.           print_preview.DestinationStore.EventType.
  485.               SELECTED_DESTINATION_CAPABILITIES_READY,
  486.           this.printIfReady_.bind(this));
  487.       this.tracker.add(
  488.           this.destinationStore_,
  489.           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
  490.           this.onDestinationSelect_.bind(this));
  491.       this.tracker.add(
  492.           this.destinationStore_,
  493.           print_preview.DestinationStore.EventType.DESTINATION_SEARCH_DONE,
  494.           this.onDestinationSearchDone_.bind(this));
  495.  
  496.       this.tracker.add(
  497.           this.printHeader_,
  498.           print_preview.PrintHeader.EventType.PRINT_BUTTON_CLICK,
  499.           this.onPrintButtonClick_.bind(this));
  500.       this.tracker.add(
  501.           this.printHeader_,
  502.           print_preview.PrintHeader.EventType.CANCEL_BUTTON_CLICK,
  503.           this.onCancelButtonClick_.bind(this));
  504.  
  505.       this.tracker.add(window, 'keydown', this.onKeyDown_.bind(this));
  506.  
  507.       this.tracker.add(
  508.           this.destinationSettings_,
  509.           print_preview.DestinationSettings.EventType.CHANGE_BUTTON_ACTIVATE,
  510.           this.onDestinationChangeButtonActivate_.bind(this));
  511.  
  512.       this.tracker.add(
  513.           this.destinationSearch_,
  514.           print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS,
  515.           this.onManageCloudDestinationsActivated_.bind(this));
  516.       this.tracker.add(
  517.           this.destinationSearch_,
  518.           print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS,
  519.           this.onManageLocalDestinationsActivated_.bind(this));
  520.       this.tracker.add(
  521.           this.destinationSearch_,
  522.           print_preview.DestinationSearch.EventType.SIGN_IN,
  523.           this.onCloudPrintSignInActivated_.bind(this));
  524.  
  525.       // TODO(rltoscano): Move no-destinations-promo into its own component
  526.       // instead being part of PrintPreview.
  527.       this.tracker.add(
  528.           this.getChildElement('#no-destinations-promo .close-button'),
  529.           'click',
  530.           this.onNoDestinationsPromoClose_.bind(this));
  531.       this.tracker.add(
  532.           this.getChildElement('#no-destinations-promo .not-now-button'),
  533.           'click',
  534.           this.onNoDestinationsPromoClose_.bind(this));
  535.       this.tracker.add(
  536.           this.getChildElement('#no-destinations-promo .add-printer-button'),
  537.           'click',
  538.           this.onNoDestinationsPromoClick_.bind(this));
  539.     },
  540.  
  541.     /** @override */
  542.     decorateInternal: function() {
  543.       this.printHeader_.decorate($('print-header'));
  544.       this.destinationSearch_.decorate($('destination-search'));
  545.       this.destinationSettings_.decorate($('destination-settings'));
  546.       this.pageSettings_.decorate($('page-settings'));
  547.       this.copiesSettings_.decorate($('copies-settings'));
  548.       this.layoutSettings_.decorate($('layout-settings'));
  549.       this.colorSettings_.decorate($('color-settings'));
  550.       this.marginSettings_.decorate($('margin-settings'));
  551.       this.otherOptionsSettings_.decorate($('other-options-settings'));
  552.       this.previewArea_.decorate($('preview-area'));
  553.  
  554.       // Set some of the parameterized text in the no-destinations promotion.
  555.       var noDestsPromoGcpDescription =
  556.           this.getChildElement('#no-destinations-promo .gcp-description');
  557.       noDestsPromoGcpDescription.innerHTML = localStrings.getStringF(
  558.           'noDestsPromoGcpDesc',
  559.           '<a target="_blank" href="http://www.google.com/cloudprint/learn/">',
  560.           '</a>');
  561.  
  562.       setIsVisible($('open-pdf-in-preview-link'), cr.isMac);
  563.     },
  564.  
  565.     /**
  566.      * Sets whether the controls in the print preview are enabled.
  567.      * @param {boolean} isEnabled Whether the controls in the print preview are
  568.      *     enabled.
  569.      * @private
  570.      */
  571.     setIsEnabled_: function(isEnabled) {
  572.       $('system-dialog-link').disabled = !isEnabled;
  573.       $('cloud-print-dialog-link').disabled = !isEnabled;
  574.       $('open-pdf-in-preview-link').disabled = !isEnabled;
  575.       this.printHeader_.isEnabled = isEnabled;
  576.       this.destinationSettings_.isEnabled = isEnabled;
  577.       this.pageSettings_.isEnabled = isEnabled;
  578.       this.copiesSettings_.isEnabled = isEnabled;
  579.       this.layoutSettings_.isEnabled = isEnabled;
  580.       this.colorSettings_.isEnabled = isEnabled;
  581.       this.marginSettings_.isEnabled = isEnabled;
  582.       this.otherOptionsSettings_.isEnabled = isEnabled;
  583.     },
  584.  
  585.     /**
  586.      * Prints the document or launches a pdf preview on the local system.
  587.      * @param {boolean} isPdfPreview Whether to launch the pdf preview.
  588.      * @private
  589.      */
  590.     printDocumentOrOpenPdfPreview_: function(isPdfPreview) {
  591.       assert(this.uiState_ == PrintPreview.UiState_.READY,
  592.              'Print document request received when not in ready state: ' +
  593.                  this.uiState_);
  594.       if (isPdfPreview) {
  595.         this.uiState_ = PrintPreview.UiState_.OPENING_PDF_PREVIEW;
  596.       } else if (this.destinationStore_.selectedDestination.id ==
  597.           print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) {
  598.         this.uiState_ = PrintPreview.UiState_.FILE_SELECTION;
  599.       } else {
  600.         this.uiState_ = PrintPreview.UiState_.PRINTING;
  601.       }
  602.       this.setIsEnabled_(false);
  603.       if (this.printIfReady_() &&
  604.           ((this.destinationStore_.selectedDestination.isLocal &&
  605.             this.destinationStore_.selectedDestination.id !=
  606.                 print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) ||
  607.            this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW)) {
  608.         // Hide the dialog for now. The actual print command will be issued when
  609.         // the preview generation is done.
  610.         this.nativeLayer_.startHideDialog();
  611.       }
  612.     },
  613.  
  614.     /**
  615.      * Attempts to print if needed and if ready.
  616.      * @return {boolean} Whether a print request was issued.
  617.      * @private
  618.      */
  619.     printIfReady_: function() {
  620.       if ((this.uiState_ == PrintPreview.UiState_.PRINTING ||
  621.               this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW ||
  622.               this.uiState_ == PrintPreview.UiState_.FILE_SELECTION ||
  623.               this.isInKioskAutoPrintMode_) &&
  624.           !this.isPreviewGenerationInProgress_ &&
  625.           this.destinationStore_.selectedDestination &&
  626.           this.destinationStore_.selectedDestination.capabilities) {
  627.         assert(this.printTicketStore_.isTicketValid(),
  628.                'Trying to print with invalid ticket');
  629.         this.nativeLayer_.startPrint(
  630.             this.destinationStore_.selectedDestination,
  631.             this.printTicketStore_,
  632.             this.cloudPrintInterface_,
  633.             this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW);
  634.         return true;
  635.       } else {
  636.         return false;
  637.       }
  638.     },
  639.  
  640.     /**
  641.      * Closes the print preview.
  642.      * @private
  643.      */
  644.     close_: function() {
  645.       this.exitDocument();
  646.       this.uiState_ = PrintPreview.UiState_.CLOSING;
  647.       this.nativeLayer_.startCloseDialog();
  648.     },
  649.  
  650.     /**
  651.      * Opens the native system print dialog after disabling all controls.
  652.      * @private
  653.      */
  654.     openSystemPrintDialog_: function() {
  655.       setIsVisible($('system-dialog-throbber'), true);
  656.       this.setIsEnabled_(false);
  657.       this.uiState_ = PrintPreview.UiState_.OPENING_NATIVE_PRINT_DIALOG;
  658.       this.nativeLayer_.startShowSystemDialog();
  659.     },
  660.  
  661.     /**
  662.      * Called when the native layer has initial settings to set. Sets the
  663.      * initial settings of the print preview and begins fetching print
  664.      * destinations.
  665.      * @param {cr.Event} event Contains the initial print preview settings
  666.      *     persisted through the session.
  667.      * @private
  668.      */
  669.     onInitialSettingsSet_: function(event) {
  670.       assert(this.uiState_ == PrintPreview.UiState_.INITIALIZING,
  671.              'Updating initial settings when not in initializing state: ' +
  672.                  this.uiState_);
  673.       this.uiState_ = PrintPreview.UiState_.READY;
  674.  
  675.       var settings = event.initialSettings;
  676.       this.isInKioskAutoPrintMode_ = settings.isInKioskAutoPrintMode;
  677.       document.title = settings.documentTitle;
  678.  
  679.       // The following components must be initialized in this order.
  680.       this.appState_.init(settings.serializedAppStateStr);
  681.       this.printTicketStore_.init(
  682.           settings.isDocumentModifiable,
  683.           settings.documentTitle,
  684.           settings.thousandsDelimeter,
  685.           settings.decimalDelimeter,
  686.           settings.unitType);
  687.       this.destinationStore_.init(settings.systemDefaultDestinationId);
  688.     },
  689.  
  690.     /**
  691.      * Calls when the native layer enables Google Cloud Print integration.
  692.      * Fetches the user's cloud printers.
  693.      * @param {cr.Event} event Contains the base URL of the Google Cloud Print
  694.      *     service.
  695.      * @private
  696.      */
  697.     onCloudPrintEnable_: function(event) {
  698.       this.cloudPrintInterface_ =
  699.           new cloudprint.CloudPrintInterface(event.baseCloudPrintUrl);
  700.       this.tracker.add(
  701.           this.cloudPrintInterface_,
  702.           cloudprint.CloudPrintInterface.EventType.SUBMIT_DONE,
  703.           this.onCloudPrintSubmitDone_.bind(this));
  704.       this.tracker.add(
  705.           this.cloudPrintInterface_,
  706.           cloudprint.CloudPrintInterface.EventType.SEARCH_FAILED,
  707.           this.onCloudPrintError_.bind(this));
  708.       this.tracker.add(
  709.           this.cloudPrintInterface_,
  710.           cloudprint.CloudPrintInterface.EventType.SUBMIT_FAILED,
  711.           this.onCloudPrintError_.bind(this));
  712.       this.tracker.add(
  713.           this.cloudPrintInterface_,
  714.           cloudprint.CloudPrintInterface.EventType.PRINTER_FAILED,
  715.           this.onCloudPrintError_.bind(this));
  716.       this.tracker.add(
  717.           this.cloudPrintInterface_,
  718.           cloudprint.CloudPrintInterface.EventType.
  719.               UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED,
  720.           this.onCloudPrintError_.bind(this));
  721.  
  722.       this.userInfo_.setCloudPrintInterface(this.cloudPrintInterface_);
  723.       this.destinationStore_.setCloudPrintInterface(this.cloudPrintInterface_);
  724.       this.destinationStore_.startLoadRecentCloudDestinations();
  725.       if (this.destinationSearch_.getIsVisible()) {
  726.         this.destinationStore_.startLoadAllCloudDestinations();
  727.       }
  728.     },
  729.  
  730.     /**
  731.      * Called from the native layer when ready to print to Google Cloud Print.
  732.      * @param {cr.Event} event Contains the body to send in the HTTP request.
  733.      * @private
  734.      */
  735.     onPrintToCloud_: function(event) {
  736.       assert(this.uiState_ == PrintPreview.UiState_.PRINTING,
  737.              'Document ready to be sent to the cloud when not in printing ' +
  738.                  'state: ' + this.uiState_);
  739.       assert(this.cloudPrintInterface_ != null,
  740.              'Google Cloud Print is not enabled');
  741.       this.cloudPrintInterface_.submit(
  742.           this.destinationStore_.selectedDestination,
  743.           this.printTicketStore_,
  744.           event.data);
  745.     },
  746.  
  747.     /**
  748.      * Called from the native layer when the user cancels the save-to-pdf file
  749.      * selection dialog.
  750.      * @private
  751.      */
  752.     onFileSelectionCancel_: function() {
  753.       assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION,
  754.              'File selection cancelled when not in file-selection state: ' +
  755.                  this.uiState_);
  756.       this.setIsEnabled_(true);
  757.       this.uiState_ = PrintPreview.UiState_.READY;
  758.     },
  759.  
  760.     /**
  761.      * Called from the native layer when save-to-pdf file selection is complete.
  762.      * @private
  763.      */
  764.     onFileSelectionComplete_: function() {
  765.       assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION,
  766.              'File selection completed when not in file-selection state: ' +
  767.                  this.uiState_);
  768.       this.previewArea_.showCustomMessage(
  769.           localStrings.getString('printingToPDFInProgress'));
  770.       this.uiState_ = PrintPreview.UiState_.PRINTING;
  771.     },
  772.  
  773.     /**
  774.      * Called after successfully submitting a job to Google Cloud Print.
  775.      * @param {!cr.Event} event Contains the ID of the submitted print job.
  776.      * @private
  777.      */
  778.     onCloudPrintSubmitDone_: function(event) {
  779.       assert(this.uiState_ == PrintPreview.UiState_.PRINTING,
  780.              'Submited job to Google Cloud Print but not in printing state ' +
  781.                  this.uiState_);
  782.       if (this.destinationStore_.selectedDestination.id ==
  783.               print_preview.Destination.GooglePromotedId.FEDEX) {
  784.         window.open(
  785.             'https://www.google.com/cloudprint/fedexcode.html?jobid=' +
  786.             event.jobId);
  787.       }
  788.       this.close_();
  789.     },
  790.  
  791.     /**
  792.      * Called when there was an error communicating with Google Cloud print.
  793.      * Displays an error message in the print header.
  794.      * @param {!cr.Event} event Contains the error message.
  795.      * @private
  796.      */
  797.     onCloudPrintError_: function(event) {
  798.       if (event.status == 403) {
  799.         this.destinationSearch_.showCloudPrintPromo();
  800.       } else if (event.status == 0) {
  801.         return; // Ignore, the system does not have internet connectivity.
  802.       } else {
  803.         this.printHeader_.setErrorMessage(event.message);
  804.       }
  805.       if (event.status == 200) {
  806.         console.error('Google Cloud Print Error: (' + event.errorCode + ') ' +
  807.                       event.message);
  808.       } else {
  809.         console.error('Google Cloud Print Error: HTTP status ' + event.status);
  810.       }
  811.     },
  812.  
  813.     /**
  814.      * Called when the preview area's preview generation is in progress.
  815.      * @private
  816.      */
  817.     onPreviewGenerationInProgress_: function() {
  818.       this.isPreviewGenerationInProgress_ = true;
  819.     },
  820.  
  821.     /**
  822.      * Called when the preview area's preview generation is complete.
  823.      * @private
  824.      */
  825.     onPreviewGenerationDone_: function() {
  826.       this.isPreviewGenerationInProgress_ = false;
  827.       this.printIfReady_();
  828.     },
  829.  
  830.     /**
  831.      * Called when the preview area's preview failed to load.
  832.      * @private
  833.      */
  834.     onPreviewGenerationFail_: function() {
  835.       this.isPreviewGenerationInProgress_ = false;
  836.       if (this.uiState_ == PrintPreview.UiState_.PRINTING) {
  837.         this.nativeLayer_.startCancelPendingPrint();
  838.       }
  839.     },
  840.  
  841.     /**
  842.      * Called when the 'Open pdf in preview' link is clicked. Launches the pdf
  843.      * preview app.
  844.      * @private
  845.      */
  846.     onOpenPdfInPreviewLinkClick_: function() {
  847.       assert(this.uiState_ == PrintPreview.UiState_.READY,
  848.              'Trying to open pdf in preview when not in ready state: ' +
  849.                  this.uiState_);
  850.       setIsVisible($('open-preview-app-throbber'), true);
  851.       this.previewArea_.showCustomMessage(
  852.           localStrings.getString('openingPDFInPreview'));
  853.       this.printDocumentOrOpenPdfPreview_(true /*isPdfPreview*/);
  854.     },
  855.  
  856.     /**
  857.      * Called when the print header's print button is clicked. Prints the
  858.      * document.
  859.      * @private
  860.      */
  861.     onPrintButtonClick_: function() {
  862.       assert(this.uiState_ == PrintPreview.UiState_.READY,
  863.              'Trying to print when not in ready state: ' + this.uiState_);
  864.       this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
  865.     },
  866.  
  867.     /**
  868.      * Called when the print header's cancel button is clicked. Closes the
  869.      * print dialog.
  870.      * @private
  871.      */
  872.     onCancelButtonClick_: function() {
  873.       this.close_();
  874.     },
  875.  
  876.     /**
  877.      * Consume escape key presses and ctrl + shift + p. Delegate everything else
  878.      * to the preview area.
  879.      * @param {KeyboardEvent} e The keyboard event.
  880.      * @private
  881.      */
  882.     onKeyDown_: function(e) {
  883.       // Escape key closes the dialog.
  884.       if (e.keyCode == 27 && !e.shiftKey && !e.ctrlKey && !e.altKey &&
  885.           !e.metaKey) {
  886.         if (this.destinationSearch_.getIsVisible()) {
  887.           this.destinationSearch_.setIsVisible(false);
  888.           this.metrics_.incrementDestinationSearchBucket(
  889.               print_preview.Metrics.DestinationSearchBucket.CANCELED);
  890.         } else {
  891.           // // On the toolkit_views environment, ESC key is handled by C++-side
  892.           // instead of JS-side.
  893.           return;
  894.           //
  895.           // 
  896.         }
  897.         e.preventDefault();
  898.         return;
  899.       }
  900.  
  901.       // Ctrl + Shift + p / Mac equivalent.
  902.       if (e.keyCode == 80) {
  903.         if ((cr.isMac && e.metaKey && e.altKey && !e.shiftKey && !e.ctrlKey) ||
  904.             (!cr.isMac && e.shiftKey && e.ctrlKey && !e.altKey && !e.metaKey)) {
  905.           this.openSystemPrintDialog_();
  906.           e.preventDefault();
  907.           return;
  908.         }
  909.       }
  910.  
  911.       if (e.keyCode == 13 /*enter*/ &&
  912.           !this.destinationSearch_.getIsVisible() &&
  913.           this.printTicketStore_.isTicketValid()) {
  914.         assert(this.uiState_ == PrintPreview.UiState_.READY,
  915.           'Trying to print when not in ready state: ' + this.uiState_);
  916.         this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/);
  917.         e.preventDefault();
  918.         return;
  919.       }
  920.  
  921.       // Pass certain directional keyboard events to the PDF viewer.
  922.       this.previewArea_.handleDirectionalKeyEvent(e);
  923.     },
  924.  
  925.     /**
  926.      * Called when native layer receives invalid settings for a print request.
  927.      * @private
  928.      */
  929.     onSettingsInvalid_: function() {
  930.       this.uiState_ = PrintPreview.UiState_.ERROR;
  931.       console.error('Invalid settings error reported from native layer');
  932.       this.previewArea_.showCustomMessage(
  933.           localStrings.getString('invalidPrinterSettings'));
  934.     },
  935.  
  936.     /**
  937.      * Called when the destination settings' change button is activated.
  938.      * Displays the destination search component.
  939.      * @private
  940.      */
  941.     onDestinationChangeButtonActivate_: function() {
  942.       this.destinationSearch_.setIsVisible(true);
  943.       this.destinationStore_.startLoadAllCloudDestinations();
  944.       this.metrics_.incrementDestinationSearchBucket(
  945.           print_preview.Metrics.DestinationSearchBucket.SHOWN);
  946.     },
  947.  
  948.     /**
  949.      * Called when the destination search dispatches manage cloud destinations
  950.      * event. Calls corresponding native layer method.
  951.      * @private
  952.      */
  953.     onManageCloudDestinationsActivated_: function() {
  954.       this.nativeLayer_.startManageCloudDestinations();
  955.     },
  956.  
  957.     /**
  958.      * Called when the destination search dispatches manage local destinations
  959.      * event. Calls corresponding native layer method.
  960.      * @private
  961.      */
  962.     onManageLocalDestinationsActivated_: function() {
  963.       this.nativeLayer_.startManageLocalDestinations();
  964.     },
  965.  
  966.     /**
  967.      * Called when the user wants to sign in to Google Cloud Print. Calls the
  968.      * corresponding native layer event.
  969.      * @private
  970.      */
  971.     onCloudPrintSignInActivated_: function() {
  972.       this.nativeLayer_.startCloudPrintSignIn();
  973.     },
  974.  
  975.     /**
  976.      * Called when the native layer dispatches a DISABLE_SCALING event. Updates
  977.      * the print ticket.
  978.      * @private
  979.      */
  980.     onDisableScaling_: function() {
  981.       this.printTicketStore_.updateFitToPage(false);
  982.     },
  983.  
  984.     /**
  985.      * Called when the open-cloud-print-dialog link is clicked. Opens the Google
  986.      * Cloud Print web dialog.
  987.      * @private
  988.      */
  989.     onCloudPrintDialogLinkClick_: function() {
  990.       assert(this.uiState_ == PrintPreview.UiState_.READY,
  991.              'Opening Google Cloud Print dialog when not in ready state: ' +
  992.                  this.uiState_);
  993.       setIsVisible($('cloud-print-dialog-throbber'), true);
  994.       this.setIsEnabled_(false);
  995.       this.uiState_ = PrintPreview.UiState_.OPENING_NATIVE_PRINT_DIALOG;
  996.       this.nativeLayer_.startShowCloudPrintDialog();
  997.     },
  998.  
  999.     /**
  1000.      * Called when a print destination is selected. Shows/hides the "Print with
  1001.      * Cloud Print" link in the navbar.
  1002.      * @private
  1003.      */
  1004.     onDestinationSelect_: function() {
  1005.       var selectedDest = this.destinationStore_.selectedDestination;
  1006.       setIsVisible($('cloud-print-dialog-link'),
  1007.                    !cr.isChromeOS && !selectedDest.isLocal);
  1008.     },
  1009.  
  1010.     /**
  1011.      * Called when the destination store loads a group of destinations. Shows
  1012.      * a promo on Chrome OS if the user has no print destinations promoting
  1013.      * Google Cloud Print.
  1014.      * @private
  1015.      */
  1016.     onDestinationSearchDone_: function() {
  1017.       var isPromoVisible = cr.isChromeOS &&
  1018.           this.cloudPrintInterface_ &&
  1019.           this.userInfo_.getUserEmail() &&
  1020.           !this.appState_.isGcpPromoDismissed &&
  1021.           !this.destinationStore_.isLocalDestinationsSearchInProgress &&
  1022.           !this.destinationStore_.isCloudDestinationsSearchInProgress &&
  1023.           this.destinationStore_.hasOnlyDefaultCloudDestinations();
  1024.       setIsVisible(this.getChildElement('#no-destinations-promo'),
  1025.                    isPromoVisible);
  1026.       if (isPromoVisible) {
  1027.         this.metrics_.incrementGcpPromoBucket(
  1028.             print_preview.Metrics.GcpPromoBucket.SHOWN);
  1029.       }
  1030.     },
  1031.  
  1032.     /**
  1033.      * Called when the close button on the no-destinations-promotion is clicked.
  1034.      * Hides the promotion.
  1035.      * @private
  1036.      */
  1037.     onNoDestinationsPromoClose_: function() {
  1038.       this.metrics_.incrementGcpPromoBucket(
  1039.           print_preview.Metrics.GcpPromoBucket.DISMISSED);
  1040.       setIsVisible(this.getChildElement('#no-destinations-promo'), false);
  1041.       this.appState_.persistIsGcpPromoDismissed(true);
  1042.     },
  1043.  
  1044.     /**
  1045.      * Called when the no-destinations promotion link is clicked. Opens the
  1046.      * Google Cloud Print management page and closes the print preview.
  1047.      * @private
  1048.      */
  1049.     onNoDestinationsPromoClick_: function() {
  1050.       this.metrics_.incrementGcpPromoBucket(
  1051.           print_preview.Metrics.GcpPromoBucket.CLICKED);
  1052.       this.appState_.persistIsGcpPromoDismissed(true);
  1053.       window.open(this.cloudPrintInterface_.baseUrl + '?user=' +
  1054.                   this.userInfo_.getUserEmail() + '#printers');
  1055.       this.close_();
  1056.     }
  1057.   };
  1058.  
  1059.   // Export
  1060.   return {
  1061.     PrintPreview: PrintPreview
  1062.   };
  1063. });
  1064.  
  1065. // Pull in all other scripts in a single shot.
  1066. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1067. // Use of this source code is governed by a BSD-style license that can be
  1068. // found in the LICENSE file.
  1069.  
  1070. cr.define('print_preview', function() {
  1071.   'use strict';
  1072.  
  1073.   /**
  1074.    * An immutable ordered set of page numbers.
  1075.    * @param {!Array.<number>} pageNumberList A list of page numbers to include
  1076.    *     in the set.
  1077.    * @constructor
  1078.    */
  1079.   function PageNumberSet(pageNumberList) {
  1080.     /**
  1081.      * Internal data store for the page number set.
  1082.      * @type {!Array.<number>}
  1083.      * @private
  1084.      */
  1085.     this.pageNumberSet_ = pageListToPageSet(pageNumberList);
  1086.   };
  1087.  
  1088.   /**
  1089.    * @param {string} pageRangeStr String form of a page range. I.e. '2,3,4-5'.
  1090.    *     If string is empty, all page numbers will be in the page number set.
  1091.    * @param {number} totalPageCount Total number of pages in the original
  1092.    *     document.
  1093.    * @return {print_preview.PageNumberSet} Page number set parsed from the
  1094.    *     given page range string and total page count. Null returned if
  1095.    *     the given page range string is invalid.
  1096.    */
  1097.   PageNumberSet.parse = function(pageRangeStr, totalPageCount) {
  1098.     if (pageRangeStr == '') {
  1099.       var pageNumberList = [];
  1100.       for (var i = 0; i < totalPageCount; i++) {
  1101.         pageNumberList.push(i + 1);
  1102.       }
  1103.       return new PageNumberSet(pageNumberList);
  1104.     } else {
  1105.       return isPageRangeTextValid(pageRangeStr, totalPageCount) ?
  1106.           new PageNumberSet(
  1107.               pageRangeTextToPageList(pageRangeStr, totalPageCount)) : null;
  1108.     }
  1109.   };
  1110.  
  1111.   PageNumberSet.prototype = {
  1112.     /** @return {number} The number of page numbers in the set. */
  1113.     get size() {
  1114.       return this.pageNumberSet_.length;
  1115.     },
  1116.  
  1117.     /**
  1118.      * @param {number} index 0-based index of the page number to get.
  1119.      * @return {number} Page number at the given index.
  1120.      */
  1121.     getPageNumberAt: function(index) {
  1122.       return this.pageNumberSet_[index];
  1123.     },
  1124.  
  1125.     /**
  1126.      * @param {number} 1-based page number to check for.
  1127.      * @return {boolean} Whether the given page number is in the page range.
  1128.      */
  1129.     hasPageNumber: function(pageNumber) {
  1130.       return arrayContains(this.pageNumberSet_, pageNumber);
  1131.     },
  1132.  
  1133.     /**
  1134.      * @param {number} 1-based number of the page to get index of.
  1135.      * @return {number} 0-based index of the given page number with respect to
  1136.      *     all of the pages in the page range.
  1137.      */
  1138.     getPageNumberIndex: function(pageNumber) {
  1139.       return this.pageNumberSet_.indexOf(pageNumber);
  1140.     },
  1141.  
  1142.     /**
  1143.      * @return {!Array.<object.<{from: number, to: number}>>} A list of page
  1144.      *     ranges suitable for use in the native layer.
  1145.      */
  1146.     getPageRanges: function() {
  1147.       return pageSetToPageRanges(this.pageNumberSet_);
  1148.     },
  1149.  
  1150.     /** @return {!Array.<number>} Array representation of the set. */
  1151.     asArray: function() {
  1152.       return this.pageNumberSet_.slice(0);
  1153.     },
  1154.  
  1155.     /**
  1156.      * @param {print_preview.PageNumberSet} other Page number set to compare
  1157.      *     against.
  1158.      * @return {boolean} Whether another page number set is equal to this one.
  1159.      */
  1160.     equals: function(other) {
  1161.       return other == null ?
  1162.           false : areArraysEqual(this.pageNumberSet_, other.pageNumberSet_);
  1163.     }
  1164.   };
  1165.  
  1166.   // Export
  1167.   return {
  1168.     PageNumberSet: PageNumberSet
  1169.   };
  1170. });
  1171.  
  1172. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1173. // Use of this source code is governed by a BSD-style license that can be
  1174. // found in the LICENSE file.
  1175.  
  1176. cr.define('print_preview', function() {
  1177.   'use strict';
  1178.  
  1179.   /**
  1180.    * Print destination data object that holds data for both local and cloud
  1181.    * destinations.
  1182.    * @param {string} id ID of the destination.
  1183.    * @param {!print_preview.Destination.Type} type Type of the destination.
  1184.    * @param {string} displayName Display name of the destination.
  1185.    * @param {boolean} isRecent Whether the destination has been used recently.
  1186.    * @param {!print_preview.Destination.ConnectionStatus} connectionStatus
  1187.    *     Connection status of the print destination.
  1188.    * @param {{tags: Array.<string>,
  1189.    *          isOwned: ?boolean,
  1190.    *          lastAccessTime: ?number,
  1191.    *          isTosAccepted: ?boolean}=} opt_params Optional parameters for the
  1192.    *     destination.
  1193.    * @constructor
  1194.    */
  1195.   function Destination(id, type, displayName, isRecent, connectionStatus,
  1196.                        opt_params) {
  1197.     /**
  1198.      * ID of the destination.
  1199.      * @type {string}
  1200.      * @private
  1201.      */
  1202.     this.id_ = id;
  1203.  
  1204.     /**
  1205.      * Type of the destination.
  1206.      * @type {!print_preview.Destination.Type}
  1207.      * @private
  1208.      */
  1209.     this.type_ = type;
  1210.  
  1211.     /**
  1212.      * Display name of the destination.
  1213.      * @type {string}
  1214.      * @private
  1215.      */
  1216.     this.displayName_ = displayName;
  1217.  
  1218.     /**
  1219.      * Whether the destination has been used recently.
  1220.      * @type {boolean}
  1221.      * @private
  1222.      */
  1223.     this.isRecent_ = isRecent;
  1224.  
  1225.     /**
  1226.      * Tags associated with the destination.
  1227.      * @type {!Array.<string>}
  1228.      * @private
  1229.      */
  1230.     this.tags_ = (opt_params && opt_params.tags) || [];
  1231.  
  1232.     /**
  1233.      * Print capabilities of the destination.
  1234.      * @type {print_preview.ChromiumCapabilities}
  1235.      * @private
  1236.      */
  1237.     this.capabilities_ = null;
  1238.  
  1239.     /**
  1240.      * Whether the destination is owned by the user.
  1241.      * @type {boolean}
  1242.      * @private
  1243.      */
  1244.     this.isOwned_ = (opt_params && opt_params.isOwned) || false;
  1245.  
  1246.     /**
  1247.      * Cache of destination location fetched from tags.
  1248.      * @type {?string}
  1249.      * @private
  1250.      */
  1251.     this.location_ = null;
  1252.  
  1253.     /**
  1254.      * Connection status of the destination.
  1255.      * @type {!print_preview.Destination.ConnectionStatus}
  1256.      * @private
  1257.      */
  1258.     this.connectionStatus_ = connectionStatus;
  1259.  
  1260.     /**
  1261.      * Number of milliseconds since the epoch when the printer was last
  1262.      * accessed.
  1263.      * @type {number}
  1264.      * @private
  1265.      */
  1266.     this.lastAccessTime_ = (opt_params && opt_params.lastAccessTime) ||
  1267.                            Date.now();
  1268.  
  1269.     /**
  1270.      * Whether the user has accepted the terms-of-service for the print
  1271.      * destination. Only applies to the FedEx Office cloud-based printer.
  1272.      * {@code} null if terms-of-service does not apply to the print destination.
  1273.      * @type {?boolean}
  1274.      * @private
  1275.      */
  1276.     this.isTosAccepted_ = (opt_params && opt_params.isTosAccepted) || false;
  1277.   };
  1278.  
  1279.   /**
  1280.    * Prefix of the location destination tag.
  1281.    * @type {string}
  1282.    * @const
  1283.    */
  1284.   Destination.LOCATION_TAG_PREFIX = '__cp__printer-location=';
  1285.  
  1286.   /**
  1287.    * Enumeration of Google-promoted destination IDs.
  1288.    * @enum {string}
  1289.    */
  1290.   Destination.GooglePromotedId = {
  1291.     DOCS: '__google__docs',
  1292.     FEDEX: '__google__fedex',
  1293.     SAVE_AS_PDF: 'Save as PDF'
  1294.   };
  1295.  
  1296.   /**
  1297.    * Enumeration of the types of destinations.
  1298.    * @enum {string}
  1299.    */
  1300.   Destination.Type = {
  1301.     GOOGLE: 'google',
  1302.     LOCAL: 'local',
  1303.     MOBILE: 'mobile'
  1304.   };
  1305.  
  1306.   /**
  1307.    * Enumeration of the connection statuses of printer destinations.
  1308.    * @enum {string}
  1309.    */
  1310.   Destination.ConnectionStatus = {
  1311.     DORMANT: 'DORMANT',
  1312.     OFFLINE: 'OFFLINE',
  1313.     ONLINE: 'ONLINE',
  1314.     UNKNOWN: 'UNKNOWN'
  1315.   };
  1316.  
  1317.   /**
  1318.    * Enumeration of relative icon URLs for various types of destinations.
  1319.    * @enum {string}
  1320.    * @private
  1321.    */
  1322.   Destination.IconUrl_ = {
  1323.     CLOUD: 'images/printer.png',
  1324.     CLOUD_SHARED: 'images/printer_shared.png',
  1325.     LOCAL: 'images/printer.png',
  1326.     MOBILE: 'images/mobile.png',
  1327.     MOBILE_SHARED: 'images/mobile_shared.png',
  1328.     THIRD_PARTY: 'images/third_party.png',
  1329.     PDF: 'images/pdf.png',
  1330.     DOCS: 'images/google_doc.png'
  1331.   };
  1332.  
  1333.   Destination.prototype = {
  1334.     /** @return {string} ID of the destination. */
  1335.     get id() {
  1336.       return this.id_;
  1337.     },
  1338.  
  1339.     /** @return {!print_preview.Destination.Type} Type of the destination. */
  1340.     get type() {
  1341.       return this.type_;
  1342.     },
  1343.  
  1344.     /** @return {string} Display name of the destination. */
  1345.     get displayName() {
  1346.       return this.displayName_;
  1347.     },
  1348.  
  1349.     /** @return {boolean} Whether the destination has been used recently. */
  1350.     get isRecent() {
  1351.       return this.isRecent_;
  1352.     },
  1353.  
  1354.     /**
  1355.      * @param {boolean} isRecent Whether the destination has been used recently.
  1356.      */
  1357.     set isRecent(isRecent) {
  1358.       this.isRecent_ = isRecent;
  1359.     },
  1360.  
  1361.     /**
  1362.      * @return {boolean} Whether the user owns the destination. Only applies to
  1363.      *     cloud-based destinations.
  1364.      */
  1365.     get isOwned() {
  1366.       return this.isOwned_;
  1367.     },
  1368.  
  1369.     /** @return {boolean} Whether the destination is local or cloud-based. */
  1370.     get isLocal() {
  1371.       return this.type_ == Destination.Type.LOCAL;
  1372.     },
  1373.  
  1374.     /**
  1375.      * @return {string} The location of the destination, or an empty string if
  1376.      *     the location is unknown.
  1377.      */
  1378.     get location() {
  1379.       if (this.location_ == null) {
  1380.         for (var tag, i = 0; tag = this.tags_[i]; i++) {
  1381.           if (tag.indexOf(Destination.LOCATION_TAG_PREFIX) == 0) {
  1382.             this.location_ = tag.substring(
  1383.                 Destination.LOCATION_TAG_PREFIX.length) || '';
  1384.             break;
  1385.           }
  1386.         }
  1387.       }
  1388.       return this.location_;
  1389.     },
  1390.  
  1391.     /** @return {!Array.<string>} Tags associated with the destination. */
  1392.     get tags() {
  1393.       return this.tags_.slice(0);
  1394.     },
  1395.  
  1396.     /**
  1397.      * @return {print_preview.ChromiumCapabilities} Print capabilities of the
  1398.      *     destination.
  1399.      */
  1400.     get capabilities() {
  1401.       return this.capabilities_;
  1402.     },
  1403.  
  1404.     /**
  1405.      * @param {!print_preview.ChromiumCapabilities} capabilities Print
  1406.      *     capabilities of the destination.
  1407.      */
  1408.     set capabilities(capabilities) {
  1409.       this.capabilities_ = capabilities;
  1410.     },
  1411.  
  1412.     /**
  1413.      * @return {!print_preview.Destination.ConnectionStatus} Connection status
  1414.      *     of the print destination.
  1415.      */
  1416.     get connectionStatus() {
  1417.       return this.connectionStatus_;
  1418.     },
  1419.  
  1420.     /**
  1421.      * @param {!print_preview.Destination.ConnectionStatus} status Connection
  1422.      *     status of the print destination.
  1423.      */
  1424.     set connectionStatus(status) {
  1425.       this.connectionStatus_ = status;
  1426.     },
  1427.  
  1428.     /**
  1429.      * @return {number} Number of milliseconds since the epoch when the printer
  1430.      *     was last accessed.
  1431.      */
  1432.     get lastAccessTime() {
  1433.       return this.lastAccessTime_;
  1434.     },
  1435.  
  1436.     /** @return {string} Relative URL of the destination's icon. */
  1437.     get iconUrl() {
  1438.       if (this.id_ == Destination.GooglePromotedId.DOCS) {
  1439.         return Destination.IconUrl_.DOCS;
  1440.       } else if (this.id_ == Destination.GooglePromotedId.FEDEX) {
  1441.         return Destination.IconUrl_.THIRD_PARTY;
  1442.       } else if (this.id_ == Destination.GooglePromotedId.SAVE_AS_PDF) {
  1443.         return Destination.IconUrl_.PDF;
  1444.       } else if (this.isLocal) {
  1445.         return Destination.IconUrl_.LOCAL;
  1446.       } else if (this.type_ == Destination.Type.MOBILE && this.isOwned_) {
  1447.         return Destination.IconUrl_.MOBILE;
  1448.       } else if (this.type_ == Destination.Type.MOBILE) {
  1449.         return Destination.IconUrl_.MOBILE_SHARED;
  1450.       } else if (this.isOwned_) {
  1451.         return Destination.IconUrl_.CLOUD;
  1452.       } else {
  1453.         return Destination.IconUrl_.CLOUD_SHARED;
  1454.       }
  1455.     },
  1456.  
  1457.     /**
  1458.      * @return {?boolean} Whether the user has accepted the terms-of-service of
  1459.      *     the print destination or {@code null} if a terms-of-service does not
  1460.      *     apply.
  1461.      */
  1462.     get isTosAccepted() {
  1463.       return this.isTosAccepted_;
  1464.     },
  1465.  
  1466.     /**
  1467.      * @param {?boolean} Whether the user has accepted the terms-of-service of
  1468.      *     the print destination or {@code null} if a terms-of-service does not
  1469.      *     apply.
  1470.      */
  1471.     set isTosAccepted(isTosAccepted) {
  1472.       this.isTosAccepted_ = isTosAccepted;
  1473.     },
  1474.  
  1475.     /**
  1476.      * Matches a query against the destination.
  1477.      * @param {string} query Query to match against the destination.
  1478.      * @return {boolean} {@code true} if the query matches this destination,
  1479.      *     {@code false} otherwise.
  1480.      */
  1481.     matches: function(query) {
  1482.       return this.displayName_.toLowerCase().indexOf(
  1483.           query.toLowerCase().trim()) != -1;
  1484.     }
  1485.   };
  1486.  
  1487.   // Export
  1488.   return {
  1489.     Destination: Destination
  1490.   };
  1491. });
  1492.  
  1493. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1494. // Use of this source code is governed by a BSD-style license that can be
  1495. // found in the LICENSE file.
  1496.  
  1497. cr.define('print_preview', function() {
  1498.   'use strict';
  1499.  
  1500.   /** Namespace that contains a method to parse local print destinations. */
  1501.   function LocalDestinationParser() {};
  1502.  
  1503.   /**
  1504.    * Parses a local print destination.
  1505.    * @param {!Object} destinationInfo Information describing a local print
  1506.    *     destination.
  1507.    * @return {!print_preview.Destination} Parsed local print destination.
  1508.    */
  1509.   LocalDestinationParser.parse = function(destinationInfo) {
  1510.     return new print_preview.Destination(
  1511.         destinationInfo.deviceName,
  1512.         print_preview.Destination.Type.LOCAL,
  1513.         destinationInfo.printerName,
  1514.         false /*isRecent*/,
  1515.         print_preview.Destination.ConnectionStatus.ONLINE);
  1516.   };
  1517.  
  1518.   /** Namespace that contains a method to parse local print capabilities. */
  1519.   function LocalCapabilitiesParser() {};
  1520.  
  1521.   /**
  1522.    * Parses local print capabilities.
  1523.    * @param {!Object} settingsInfo Object that describes local print
  1524.    *     capabilities.
  1525.    * @return {!print_preview.ChromiumCapabilities} Parsed local print
  1526.    *     capabilities.
  1527.    */
  1528.   LocalCapabilitiesParser.parse = function(settingsInfo) {
  1529.     var hasColorCapability = false;
  1530.     var defaultIsColorEnabled = false;
  1531.     if (hasColorCapability = !settingsInfo['disableColorOption']) {
  1532.       defaultIsColorEnabled = settingsInfo['setColorAsDefault'];
  1533.     }
  1534.  
  1535.     var hasDuplexCapability = false;
  1536.     var defaultIsDuplexEnabled = false;
  1537.     // On Windows, some printers don't specify their duplex values in the
  1538.     // printer schema. If the printer duplex value is UNKNOWN_DUPLEX_MODE,
  1539.     // hide the two sided option in preview tab UI.
  1540.     // Ref bug: http://crbug.com/89204
  1541.     if (hasDuplexCapability =
  1542.         settingsInfo['printerDefaultDuplexValue'] !=
  1543.         print_preview.NativeLayer.DuplexMode.UNKNOWN_DUPLEX_MODE) {
  1544.       defaultIsDuplexEnabled = settingsInfo['setDuplexAsDefault'] || false;
  1545.     }
  1546.  
  1547.     return new print_preview.ChromiumCapabilities(
  1548.         !settingsInfo['disableCopiesOption'] /*hasCopiesCapability*/,
  1549.         '1' /*defaultCopiesStr*/,
  1550.         true /*hasCollateCapability*/,
  1551.         true /*defaultIsCollateEnabled*/,
  1552.         hasDuplexCapability,
  1553.         defaultIsDuplexEnabled,
  1554.         !settingsInfo['disableLandscapeOption'] /*hasOrientationCapability*/,
  1555.         false /*defaultIsLandscapeEnabled*/,
  1556.         hasColorCapability,
  1557.         defaultIsColorEnabled);
  1558.   };
  1559.  
  1560.   // Export
  1561.   return {
  1562.     LocalCapabilitiesParser: LocalCapabilitiesParser,
  1563.     LocalDestinationParser: LocalDestinationParser
  1564.   };
  1565. });
  1566.  
  1567. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1568. // Use of this source code is governed by a BSD-style license that can be
  1569. // found in the LICENSE file.
  1570.  
  1571. cr.define('cloudprint', function() {
  1572.   'use strict';
  1573.  
  1574.   /** Namespace which contains a method to parse cloud destinations directly. */
  1575.   function CloudDestinationParser() {};
  1576.  
  1577.   /**
  1578.    * Enumeration of cloud destination field names.
  1579.    * @enum {string}
  1580.    * @private
  1581.    */
  1582.   CloudDestinationParser.Field_ = {
  1583.     LAST_ACCESS: 'accessTime',
  1584.     CAPABILITIES: 'capabilities',
  1585.     CONNECTION_STATUS: 'connectionStatus',
  1586.     DISPLAY_NAME: 'displayName',
  1587.     FORMAT: 'capsFormat',
  1588.     ID: 'id',
  1589.     IS_TOS_ACCEPTED: 'isTosAccepted',
  1590.     TAGS: 'tags',
  1591.     TYPE: 'type'
  1592.   };
  1593.  
  1594.   /**
  1595.    * Special tag that denotes whether the destination has been recently used.
  1596.    * @type {string}
  1597.    * @const
  1598.    * @private
  1599.    */
  1600.   CloudDestinationParser.RECENT_TAG_ = '^recent';
  1601.  
  1602.   /**
  1603.    * Special tag that denotes whether the destination is owned by the user.
  1604.    * @type {string}
  1605.    * @const
  1606.    * @private
  1607.    */
  1608.   CloudDestinationParser.OWNED_TAG_ = '^own';
  1609.  
  1610.   /**
  1611.    * Enumeration of cloud destination types that are supported by print preview.
  1612.    * @enum {string}
  1613.    * @private
  1614.    */
  1615.   CloudDestinationParser.CloudType_ = {
  1616.     ANDROID: 'ANDROID_CHROME_SNAPSHOT',
  1617.     DOCS: 'DOCS',
  1618.     IOS: 'IOS_CHROME_SNAPSHOT'
  1619.   };
  1620.  
  1621.   /**
  1622.    * Parses a destination from JSON from a Google Cloud Print search or printer
  1623.    * response.
  1624.    * @param {!Object} json Object that represents a Google Cloud Print search or
  1625.    *     printer response.
  1626.    * @return {!print_preview.Destination} Parsed destination.
  1627.    */
  1628.   CloudDestinationParser.parse = function(json) {
  1629.     if (!json.hasOwnProperty(CloudDestinationParser.Field_.ID) ||
  1630.         !json.hasOwnProperty(CloudDestinationParser.Field_.TYPE) ||
  1631.         !json.hasOwnProperty(CloudDestinationParser.Field_.DISPLAY_NAME)) {
  1632.       throw Error('Cloud destination does not have an ID or a display name');
  1633.     }
  1634.     var id = json[CloudDestinationParser.Field_.ID];
  1635.     var tags = json[CloudDestinationParser.Field_.TAGS] || [];
  1636.     var connectionStatus =
  1637.         json[CloudDestinationParser.Field_.CONNECTION_STATUS] ||
  1638.         print_preview.Destination.ConnectionStatus.UNKNOWN;
  1639.     var optionalParams = {
  1640.       tags: tags,
  1641.       isOwned: arrayContains(tags, CloudDestinationParser.OWNED_TAG_),
  1642.       lastAccessTime: parseInt(
  1643.           json[CloudDestinationParser.Field_.LAST_ACCESS], 10) || Date.now(),
  1644.       isTosAccepted: (id == print_preview.Destination.GooglePromotedId.FEDEX) ?
  1645.           json[CloudDestinationParser.Field_.IS_TOS_ACCEPTED] : null
  1646.     };
  1647.     var cloudDest = new print_preview.Destination(
  1648.         id,
  1649.         CloudDestinationParser.parseType_(
  1650.             json[CloudDestinationParser.Field_.TYPE]),
  1651.         json[CloudDestinationParser.Field_.DISPLAY_NAME],
  1652.         arrayContains(tags, CloudDestinationParser.RECENT_TAG_) /*isRecent*/,
  1653.         connectionStatus,
  1654.         optionalParams);
  1655.     if (json.hasOwnProperty(CloudDestinationParser.Field_.CAPABILITIES) &&
  1656.         json.hasOwnProperty(CloudDestinationParser.Field_.FORMAT)) {
  1657.       cloudDest.capabilities = CloudCapabilitiesParser.parse(
  1658.           json[CloudDestinationParser.Field_.FORMAT],
  1659.           json[CloudDestinationParser.Field_.CAPABILITIES]);
  1660.     }
  1661.     return cloudDest;
  1662.   };
  1663.  
  1664.   /**
  1665.    * Parses the destination type.
  1666.    * @param {string} typeStr Destination type given by the Google Cloud Print
  1667.    *     server.
  1668.    * @return {!print_preview.Destination.Type} Destination type.
  1669.    * @private
  1670.    */
  1671.   CloudDestinationParser.parseType_ = function(typeStr) {
  1672.     if (typeStr == CloudDestinationParser.CloudType_.ANDROID ||
  1673.         typeStr == CloudDestinationParser.CloudType_.IOS) {
  1674.       return print_preview.Destination.Type.MOBILE;
  1675.     } else if (typeStr == CloudDestinationParser.CloudType_.DOCS) {
  1676.       return print_preview.Destination.Type.GOOGLE_PROMOTED;
  1677.     } else {
  1678.       return print_preview.Destination.Type.GOOGLE;
  1679.     }
  1680.   };
  1681.  
  1682.   /**
  1683.    * Namespace which contains a method to parse a cloud destination's print
  1684.    * capabilities.
  1685.    */
  1686.   function CloudCapabilitiesParser() {};
  1687.  
  1688.   /**
  1689.    * Enumeration of cloud destination print capabilities field names.
  1690.    * @enum {string}
  1691.    * @private
  1692.    */
  1693.   CloudCapabilitiesParser.Field_ = {
  1694.     CAP_ID: 'name',
  1695.     DEFAULT: 'default',
  1696.     IS_DEFAULT: 'default',
  1697.     OPTIONS: 'options',
  1698.     OPTION_ID: 'name'
  1699.   };
  1700.  
  1701.   /**
  1702.    * Parses print capabilities from an object in a given capabilities format.
  1703.    * @param {print_preview.CloudCapabilities.Format} capsFormat Format of the
  1704.    *     printer capabilities.
  1705.    * @param {!Array.<!Object>} json Object representing the cloud capabilities.
  1706.    * @return {!print_preview.CloudCapabilities} Parsed print capabilities.
  1707.    */
  1708.   CloudCapabilitiesParser.parse = function(capsFormat, json) {
  1709.     var colorCapability = null;
  1710.     var duplexCapability = null;
  1711.     var copiesCapability = null;
  1712.     var collateCapability = null;
  1713.     json.forEach(function(cap) {
  1714.       var capId = cap[CloudCapabilitiesParser.Field_.CAP_ID];
  1715.       if (capId == print_preview.CollateCapability.Id[capsFormat]) {
  1716.         collateCapability = CloudCapabilitiesParser.parseCollate(capId, cap);
  1717.       } else if (capId == print_preview.ColorCapability.Id[capsFormat]) {
  1718.         colorCapability = CloudCapabilitiesParser.parseColor(capId, cap);
  1719.       } else if (capId == print_preview.CopiesCapability.Id[capsFormat]) {
  1720.         copiesCapability = new print_preview.CopiesCapability(capId);
  1721.       } else if (capId == print_preview.DuplexCapability.Id[capsFormat]) {
  1722.         duplexCapability = CloudCapabilitiesParser.parseDuplex(capId, cap);
  1723.       }
  1724.     });
  1725.     return new print_preview.CloudCapabilities(
  1726.         collateCapability, colorCapability, copiesCapability, duplexCapability);
  1727.   };
  1728.  
  1729.   /**
  1730.    * Parses a collate capability from the given object.
  1731.    * @param {string} capId Native ID of the given capability object.
  1732.    * @param {!Object} Object that represents the collate capability.
  1733.    * @return {print_preview.CollateCapability} Parsed collate capability or
  1734.    *     {@code null} if the given capability object was not a valid collate
  1735.    *     capability.
  1736.    */
  1737.   CloudCapabilitiesParser.parseCollate = function(capId, cap) {
  1738.     var options = cap[CloudCapabilitiesParser.Field_.OPTIONS];
  1739.     var collateOption = null;
  1740.     var noCollateOption = null;
  1741.     var isCollateDefault = false;
  1742.     options.forEach(function(option) {
  1743.       var optionId = option[CloudCapabilitiesParser.Field_.OPTION_ID];
  1744.       if (!collateOption &&
  1745.           print_preview.CollateCapability.COLLATE_REGEX.test(optionId)) {
  1746.         collateOption = optionId;
  1747.         isCollateDefault = !!option[CloudCapabilitiesParser.Field_.DEFAULT];
  1748.       } else if (!noCollateOption &&
  1749.           print_preview.CollateCapability.NO_COLLATE_REGEX.test(optionId)) {
  1750.         noCollateOption = optionId;
  1751.       }
  1752.     });
  1753.     if (!collateOption || !noCollateOption) {
  1754.       return null;
  1755.     }
  1756.     return new print_preview.CollateCapability(
  1757.         capId, collateOption, noCollateOption, isCollateDefault);
  1758.   };
  1759.  
  1760.   /**
  1761.    * Parses a color capability from the given object.
  1762.    * @param {string} capId Native ID of the given capability object.
  1763.    * @param {!Object} Object that represents the color capability.
  1764.    * @return {print_preview.ColorCapability} Parsed color capability or
  1765.    *     {@code null} if the given capability object was not a valid color
  1766.    *     capability.
  1767.    */
  1768.   CloudCapabilitiesParser.parseColor = function(capId, cap) {
  1769.     var options = cap[CloudCapabilitiesParser.Field_.OPTIONS];
  1770.     var colorOption = null;
  1771.     var bwOption = null;
  1772.     var isColorDefault = false;
  1773.     options.forEach(function(option) {
  1774.       var optionId = option[CloudCapabilitiesParser.Field_.OPTION_ID];
  1775.       if (!colorOption &&
  1776.           print_preview.ColorCapability.COLOR_REGEX.test(optionId)) {
  1777.         colorOption = optionId;
  1778.         isColorDefault = !!option[CloudCapabilitiesParser.Field_.DEFAULT];
  1779.       } else if (!bwOption &&
  1780.           print_preview.ColorCapability.BW_REGEX.test(optionId)) {
  1781.         bwOption = optionId;
  1782.       }
  1783.     });
  1784.     if (!colorOption || !bwOption) {
  1785.       return null;
  1786.     }
  1787.     return new print_preview.ColorCapability(
  1788.         capId, colorOption, bwOption, isColorDefault);
  1789.   };
  1790.  
  1791.   /**
  1792.    * Parses a duplex capability from the given object.
  1793.    * @param {string} capId Native ID of the given capability object.
  1794.    * @param {!Object} Object that represents the duplex capability.
  1795.    * @return {print_preview.DuplexCapability} Parsed duplex capability or
  1796.    *     {@code null} if the given capability object was not a valid duplex
  1797.    *     capability.
  1798.    */
  1799.   CloudCapabilitiesParser.parseDuplex = function(capId, cap) {
  1800.     var options = cap[CloudCapabilitiesParser.Field_.OPTIONS];
  1801.     var simplexOption = null;
  1802.     var longEdgeOption = null;
  1803.     var isDuplexDefault = false;
  1804.     options.forEach(function(option) {
  1805.       var optionId = option[CloudCapabilitiesParser.Field_.OPTION_ID];
  1806.       if (!simplexOption &&
  1807.           print_preview.DuplexCapability.SIMPLEX_REGEX.test(optionId)) {
  1808.         simplexOption = optionId;
  1809.       } else if (!longEdgeOption &&
  1810.           print_preview.DuplexCapability.LONG_EDGE_REGEX.test(optionId)) {
  1811.         longEdgeOption = optionId;
  1812.         isDuplexDefault = !!option[CloudCapabilitiesParser.Field_.DEFAULT];
  1813.       }
  1814.     });
  1815.     if (!simplexOption || !longEdgeOption) {
  1816.       return null;
  1817.     }
  1818.     return new print_preview.DuplexCapability(
  1819.         capId, simplexOption, longEdgeOption, isDuplexDefault);
  1820.   };
  1821.  
  1822.   // Export
  1823.   return {
  1824.     CloudCapabilitiesParser: CloudCapabilitiesParser,
  1825.     CloudDestinationParser: CloudDestinationParser
  1826.   };
  1827. });
  1828.  
  1829.  
  1830. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1831. // Use of this source code is governed by a BSD-style license that can be
  1832. // found in the LICENSE file.
  1833.  
  1834. cr.define('print_preview', function() {
  1835.   'use strict';
  1836.  
  1837.   /**
  1838.    * Capabilities of a print destination not including the capabilities of the
  1839.    * document renderer.
  1840.    * @param {boolean} hasCopiesCapability Whether the print destination has a
  1841.    *     copies capability.
  1842.    * @param {string} defaultCopiesStr Default string representation of the
  1843.    *     copies value.
  1844.    * @param {boolean} hasCollateCapability Whether the print destination has
  1845.    *     collation capability.
  1846.    * @param {boolean} defaultIsCollateEnabled Whether collate is enabled by
  1847.    *     default.
  1848.    * @param {boolean} hasDuplexCapability Whether the print destination has
  1849.    *     duplexing capability.
  1850.    * @param {boolean} defaultIsDuplexEnabled Whether duplexing is enabled by
  1851.    *     default.
  1852.    * @param {boolean} hasOrientationCapability Whether the print destination has
  1853.    *     orientation capability.
  1854.    * @param {boolean} defaultIsLandscapeEnabled Whether the document should be
  1855.    *     printed in landscape by default.
  1856.    * @param {boolean} hasColorCapability Whether the print destination has
  1857.    *     color printing capability.
  1858.    * @param {boolean} defaultIsColorEnabled Whether the document should be
  1859.    *     printed in color by default.
  1860.    * @constructor
  1861.    */
  1862.   function ChromiumCapabilities(
  1863.       hasCopiesCapability,
  1864.       defaultCopiesStr,
  1865.       hasCollateCapability,
  1866.       defaultIsCollateEnabled,
  1867.       hasDuplexCapability,
  1868.       defaultIsDuplexEnabled,
  1869.       hasOrientationCapability,
  1870.       defaultIsLandscapeEnabled,
  1871.       hasColorCapability,
  1872.       defaultIsColorEnabled) {
  1873.     /**
  1874.      * Whether the print destination has a copies capability.
  1875.      * @type {boolean}
  1876.      * @private
  1877.      */
  1878.     this.hasCopiesCapability_ = hasCopiesCapability;
  1879.  
  1880.     /**
  1881.      * Default string representation of the copies value.
  1882.      * @type {string}
  1883.      * @private
  1884.      */
  1885.     this.defaultCopiesStr_ = defaultCopiesStr;
  1886.  
  1887.     /**
  1888.      * Whether the print destination has collation capability.
  1889.      * @type {boolean}
  1890.      * @private
  1891.      */
  1892.     this.hasCollateCapability_ = hasCollateCapability;
  1893.  
  1894.     /**
  1895.      * Whether collate is enabled by default.
  1896.      * @type {boolean}
  1897.      * @private
  1898.      */
  1899.     this.defaultIsCollateEnabled_ = defaultIsCollateEnabled;
  1900.  
  1901.     /**
  1902.      * Whether the print destination has duplexing capability.
  1903.      * @type {boolean}
  1904.      * @private
  1905.      */
  1906.     this.hasDuplexCapability_ = hasDuplexCapability;
  1907.  
  1908.     /**
  1909.      * Whether duplex is enabled by default.
  1910.      * @type {boolean}
  1911.      * @private
  1912.      */
  1913.     this.defaultIsDuplexEnabled_ = defaultIsDuplexEnabled;
  1914.  
  1915.     /**
  1916.      * Whether the print destination has orientation capability.
  1917.      * @type {boolean}
  1918.      * @private
  1919.      */
  1920.     this.hasOrientationCapability_ = hasOrientationCapability;
  1921.  
  1922.     /**
  1923.      * Whether the document should be printed in landscape by default.
  1924.      * @type {boolean}
  1925.      * @private
  1926.      */
  1927.     this.defaultIsLandscapeEnabled_ = defaultIsLandscapeEnabled;
  1928.  
  1929.     /**
  1930.      * Whether the print destination has color printing capability.
  1931.      * @type {boolean}
  1932.      * @private
  1933.      */
  1934.     this.hasColorCapability_ = hasColorCapability;
  1935.  
  1936.     /**
  1937.      * Whether the document should be printed in color.
  1938.      * @type {boolean}
  1939.      * @private
  1940.      */
  1941.     this.defaultIsColorEnabled_ = defaultIsColorEnabled;
  1942.   };
  1943.  
  1944.   ChromiumCapabilities.prototype = {
  1945.     /** @return {boolean} Whether the destination has the copies capability. */
  1946.     get hasCopiesCapability() {
  1947.       return this.hasCopiesCapability_;
  1948.     },
  1949.  
  1950.     /** @return {string} Default number of copies in string format. */
  1951.     get defaultCopiesStr() {
  1952.       return this.defaultCopiesStr_;
  1953.     },
  1954.  
  1955.     /** @return {boolean} Whether the destination has collation capability. */
  1956.     get hasCollateCapability() {
  1957.       return this.hasCollateCapability_;
  1958.     },
  1959.  
  1960.     /** @return {boolean} Whether collation is enabled by default. */
  1961.     get defaultIsCollateEnabled() {
  1962.       return this.defaultIsCollateEnabled_;
  1963.     },
  1964.  
  1965.     /** @return {boolean} Whether the destination has the duplex capability. */
  1966.     get hasDuplexCapability() {
  1967.       return this.hasDuplexCapability_;
  1968.     },
  1969.  
  1970.     /** @return {boolean} Whether duplexing is enabled by default. */
  1971.     get defaultIsDuplexEnabled() {
  1972.       return this.defaultIsDuplexEnabled_;
  1973.     },
  1974.  
  1975.     /**
  1976.      * @return {boolean} Whether the destination has the orientation capability.
  1977.      */
  1978.     get hasOrientationCapability() {
  1979.       return this.hasOrientationCapability_;
  1980.     },
  1981.  
  1982.     /**
  1983.      * @return {boolean} Whether document should be printed in landscape by
  1984.      *     default.
  1985.      */
  1986.     get defaultIsLandscapeEnabled() {
  1987.       return this.defaultIsLandscapeEnabled_;
  1988.     },
  1989.  
  1990.     /**
  1991.      * @return {boolean} Whether the destination has color printing capability.
  1992.      */
  1993.     get hasColorCapability() {
  1994.       return this.hasColorCapability_;
  1995.     },
  1996.  
  1997.     /**
  1998.      * @return {boolean} Whether document should be printed in color by default.
  1999.      */
  2000.     get defaultIsColorEnabled() {
  2001.       return this.defaultIsColorEnabled_;
  2002.     }
  2003.   };
  2004.  
  2005.   // Export
  2006.   return {
  2007.     ChromiumCapabilities: ChromiumCapabilities
  2008.   };
  2009. });
  2010.  
  2011. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2012. // Use of this source code is governed by a BSD-style license that can be
  2013. // found in the LICENSE file.
  2014.  
  2015. cr.define('print_preview', function() {
  2016.   'use strict';
  2017.  
  2018.   /**
  2019.    * Capabilities of a cloud-based print destination.
  2020.    * @param {print_preview.CollateCapability} collateCapability Print
  2021.    *     destination collate capability.
  2022.    * @param {print_preview.ColorCapability} colorCapability Print destination
  2023.    *     color capability.
  2024.    * @param {print_preview.CopiesCapability} copiesCapability Print destination
  2025.    *     copies capability.
  2026.    * @param {print_preview.DuplexCapability} duplexCapability Print destination
  2027.    *     duplexing capability.
  2028.    * @constructor
  2029.    * @extends {print_preview.ChromiumCapabilities}
  2030.    */
  2031.   function CloudCapabilities(
  2032.       collateCapability, colorCapability, copiesCapability, duplexCapability) {
  2033.     print_preview.ChromiumCapabilities.call(
  2034.         this,
  2035.         !!copiesCapability,
  2036.         '1' /*defaultCopiesStr*/,
  2037.         !!collateCapability,
  2038.         !!collateCapability && collateCapability.isCollateDefault,
  2039.         !!duplexCapability,
  2040.         !!duplexCapability && duplexCapability.isDuplexDefault,
  2041.         true /*hasOrientationCapability*/,
  2042.         false /*defaultIsLandscapeEnabled*/,
  2043.         !!colorCapability,
  2044.         !!colorCapability && colorCapability.isColorDefault);
  2045.  
  2046.     /**
  2047.      * Print destination collate capability.
  2048.      * @type {print_preview.CollateCapability}
  2049.      * @private
  2050.      */
  2051.     this.collateCapability_ = collateCapability;
  2052.  
  2053.     /**
  2054.      * Print destination color capability.
  2055.      * @type {print_preview.ColorCapability}
  2056.      * @private
  2057.      */
  2058.     this.colorCapability_ = colorCapability;
  2059.  
  2060.     /**
  2061.      * Print destination copies capability.
  2062.      * @type {print_preview.CopiesCapability}
  2063.      * @private
  2064.      */
  2065.     this.copiesCapability_ = copiesCapability;
  2066.  
  2067.     /**
  2068.      * Print destination duplexing capability.
  2069.      * @type {print_preview.DuplexCapability}
  2070.      * @private
  2071.      */
  2072.     this.duplexCapability_ = duplexCapability;
  2073.   };
  2074.  
  2075.   /**
  2076.    * Enumeration of the capability formats of cloud-based print destinations.
  2077.    * @enum {string}
  2078.    */
  2079.   CloudCapabilities.Format = {
  2080.     HP: 'hp',
  2081.     PPD: 'ppd',
  2082.     XPS: 'xps'
  2083.   };
  2084.  
  2085.   CloudCapabilities.prototype = {
  2086.     __proto__: print_preview.ChromiumCapabilities.prototype,
  2087.  
  2088.     /**
  2089.      * @return {print_preview.CollateCapability} The print destination's collate
  2090.      *     capability.
  2091.      */
  2092.     get collateCapability() {
  2093.       return this.collateCapability_;
  2094.     },
  2095.  
  2096.     /**
  2097.      * @return {print_preview.CollateCapability} The print destination's color
  2098.      *     capability.
  2099.      */
  2100.     get colorCapability() {
  2101.       return this.colorCapability_;
  2102.     },
  2103.  
  2104.     /**
  2105.      * @return {print_preview.CollateCapability} The print destination's copies
  2106.      *     capability.
  2107.      */
  2108.     get copiesCapability() {
  2109.       return this.copiesCapability_;
  2110.     },
  2111.  
  2112.     /**
  2113.      * @return {print_preview.CollateCapability} The print destination's
  2114.      *     duplexing capability.
  2115.      */
  2116.     get duplexCapability() {
  2117.       return this.duplexCapability_;
  2118.     }
  2119.   };
  2120.  
  2121.   /**
  2122.    * A single print capability of a cloud-based print destination.
  2123.    * @param {string} id Identifier of the capability.
  2124.    * @param {!print_preview.CloudCapability.Type} type Type of the capability.
  2125.    * @constructor
  2126.    */
  2127.   function CloudCapability(id, type) {
  2128.     /**
  2129.      * Identifier of the capability.
  2130.      * @type {string}
  2131.      * @private
  2132.      */
  2133.     this.id_ = id;
  2134.  
  2135.     /**
  2136.      * Type of the capability.
  2137.      * @type {!print_preview.CloudCapability.Type}
  2138.      * @private
  2139.      */
  2140.     this.type_ = type;
  2141.   };
  2142.  
  2143.   /**
  2144.    * Enumeration of the types of cloud-based print capabilities.
  2145.    * @enum {string}
  2146.    */
  2147.   CloudCapability.Type = {
  2148.     FEATURE: 'Feature',
  2149.     PARAMETER_DEF: 'ParameterDef'
  2150.   };
  2151.  
  2152.   CloudCapability.prototype = {
  2153.     /** @return {string} Identifier of the capability. */
  2154.     get id() {
  2155.       return this.id_;
  2156.     },
  2157.  
  2158.     /** @return {!print_preview.CloudCapability.Type} Type of the capability. */
  2159.     get type() {
  2160.       return this.type_;
  2161.     }
  2162.   };
  2163.  
  2164.   /**
  2165.    * Cloud-based collate capability.
  2166.    * @param {string} id Identifier of the collate capability.
  2167.    * @param {string} collateOption Identifier of the option that enables
  2168.    *     collation.
  2169.    * @param {string} noCollateOption Identifier of the option that disables
  2170.    *     collation.
  2171.    * @param {boolean} isCollateDefault Whether collation is enabled by default.
  2172.    * @constructor
  2173.    * @extends {print_preview.CloudCapability}
  2174.    */
  2175.   function CollateCapability(
  2176.       id, collateOption, noCollateOption, isCollateDefault) {
  2177.     CloudCapability.call(this, id, CloudCapability.Type.FEATURE);
  2178.  
  2179.     /**
  2180.      * Identifier of the option that enables collation.
  2181.      * @type {string}
  2182.      * @private
  2183.      */
  2184.     this.collateOption_ = collateOption;
  2185.  
  2186.     /**
  2187.      * Identifier of the option that disables collation.
  2188.      * @type {string}
  2189.      * @private
  2190.      */
  2191.     this.noCollateOption_ = noCollateOption;
  2192.  
  2193.     /**
  2194.      * Whether collation is enabled by default.
  2195.      * @type {boolean}
  2196.      * @private
  2197.      */
  2198.     this.isCollateDefault_ = isCollateDefault;
  2199.   };
  2200.  
  2201.   /**
  2202.    * Mapping of capability formats to an identifier of the collate capability.
  2203.    * @type {!Object.<!CloudCapabilities.Format, string>}
  2204.    */
  2205.   CollateCapability.Id = {};
  2206.   CollateCapability.Id[CloudCapabilities.Format.PPD] = 'Collate';
  2207.   CollateCapability.Id[CloudCapabilities.Format.XPS] = 'psk:DocumentCollate';
  2208.  
  2209.   /**
  2210.    * Regular expression that matches a collate option.
  2211.    * @type {!RegExp}
  2212.    * @const
  2213.    */
  2214.   CollateCapability.COLLATE_REGEX = /(.*:collated.*|true)/i;
  2215.  
  2216.   /**
  2217.    * Regular expression that matches a no-collate option.
  2218.    * @type {!RegExp}
  2219.    * @const
  2220.    */
  2221.   CollateCapability.NO_COLLATE_REGEX = /(.*:uncollated.*|false)/i;
  2222.  
  2223.   CollateCapability.prototype = {
  2224.     __proto__: CloudCapability.prototype,
  2225.  
  2226.     /** @return {string} Identifier of the option that enables collation. */
  2227.     get collateOption() {
  2228.       return this.collateOption_;
  2229.     },
  2230.  
  2231.     /** @return {string} Identifier of the option that disables collation. */
  2232.     get noCollateOption() {
  2233.       return this.noCollateOption_;
  2234.     },
  2235.  
  2236.     /** @return {boolean} Whether collation is enabled by default. */
  2237.     get isCollateDefault() {
  2238.       return this.isCollateDefault_;
  2239.     }
  2240.   };
  2241.  
  2242.   /**
  2243.    * Cloud-based color print capability.
  2244.    * @param {string} id Identifier of the color capability.
  2245.    * @param {string} colorOption Identifier of the color option.
  2246.    * @param {string} bwOption Identifier of the black-white option.
  2247.    * @param {boolean} Whether color printing is enabled by default.
  2248.    * @constructor
  2249.    */
  2250.   function ColorCapability(id, colorOption, bwOption, isColorDefault) {
  2251.     CloudCapability.call(this, id, CloudCapability.Type.FEATURE);
  2252.  
  2253.     /**
  2254.      * Identifier of the color option.
  2255.      * @type {string}
  2256.      * @private
  2257.      */
  2258.     this.colorOption_ = colorOption;
  2259.  
  2260.     /**
  2261.      * Identifier of the black-white option.
  2262.      * @type {string}
  2263.      * @private
  2264.      */
  2265.     this.bwOption_ = bwOption;
  2266.  
  2267.     /**
  2268.      * Whether to print in color by default.
  2269.      * @type {boolean}
  2270.      * @private
  2271.      */
  2272.     this.isColorDefault_ = isColorDefault;
  2273.   };
  2274.  
  2275.   /**
  2276.    * Mapping of capability formats to an identifier of the color capability.
  2277.    * @type {!Object.<!CloudCapabilities.Format, string>}
  2278.    */
  2279.   ColorCapability.Id = {};
  2280.   ColorCapability.Id[CloudCapabilities.Format.HP] = 'ns1:Colors';
  2281.   ColorCapability.Id[CloudCapabilities.Format.PPD] = 'ColorModel';
  2282.   ColorCapability.Id[CloudCapabilities.Format.XPS] = 'psk:PageOutputColor';
  2283.  
  2284.   /**
  2285.    * Regular expression that matches a color option.
  2286.    * @type {!RegExp}
  2287.    * @const
  2288.    */
  2289.   ColorCapability.COLOR_REGEX = /(.*color.*|.*rgb.*|.*cmy.*|true)/i;
  2290.  
  2291.   /**
  2292.    * Regular expression that matches a black-white option.
  2293.    * @type {!RegExp}
  2294.    * @const
  2295.    */
  2296.   ColorCapability.BW_REGEX = /(.*gray.*|.*mono.*|.*black.*|false|grey_k)/i;
  2297.  
  2298.   ColorCapability.prototype = {
  2299.     __proto__: CloudCapability.prototype,
  2300.  
  2301.     /** @return {string} Identifier of the color option. */
  2302.     get colorOption() {
  2303.       return this.colorOption_;
  2304.     },
  2305.  
  2306.     /** @return {string} Identifier of the black-white option. */
  2307.     get bwOption() {
  2308.       return this.bwOption_;
  2309.     },
  2310.  
  2311.     /** @return {boolean} Whether to print in color by default. */
  2312.     get isColorDefault() {
  2313.       return this.isColorDefault_;
  2314.     }
  2315.   };
  2316.  
  2317.   /**
  2318.    * Cloud-based copies print capability.
  2319.    * @param {string} id Identifier of the copies capability.
  2320.    * @constructor
  2321.    */
  2322.   function CopiesCapability(id) {
  2323.     CloudCapability.call(this, id, CloudCapability.Type.PARAMETER_DEF);
  2324.   };
  2325.  
  2326.   CopiesCapability.prototype = {
  2327.     __proto__: CloudCapability.prototype
  2328.   };
  2329.  
  2330.   /**
  2331.    * Mapping of capability formats to an identifier of the copies capability.
  2332.    * @type {!Object.<!CloudCapabilities.Format, string>}
  2333.    */
  2334.   CopiesCapability.Id = {};
  2335.   CopiesCapability.Id[CloudCapabilities.Format.XPS] =
  2336.       'psk:JobCopiesAllDocuments';
  2337.  
  2338.   /**
  2339.    * Cloud-based duplex print capability.
  2340.    * @param {string} id Identifier of the duplex capability.
  2341.    * @param {string} simplexOption Identifier of the no-duplexing option.
  2342.    * @param {string} longEdgeOption Identifier of the duplex on long edge
  2343.    *     option.
  2344.    * @param {boolean} Whether duplexing is enabled by default.
  2345.    * @constructor
  2346.    */
  2347.   function DuplexCapability(
  2348.       id, simplexOption, longEdgeOption, isDuplexDefault) {
  2349.     CloudCapability.call(this, id, CloudCapability.Type.FEATURE);
  2350.  
  2351.     /**
  2352.      * Identifier of the no-duplexing option.
  2353.      * @type {string}
  2354.      * @private
  2355.      */
  2356.     this.simplexOption_ = simplexOption;
  2357.  
  2358.     /**
  2359.      * Identifier of the duplex on long edge option.
  2360.      * @type {string}
  2361.      * @private
  2362.      */
  2363.     this.longEdgeOption_ = longEdgeOption;
  2364.  
  2365.     /**
  2366.      * Whether duplexing is enabled by default.
  2367.      * @type {boolean}
  2368.      * @private
  2369.      */
  2370.     this.isDuplexDefault_ = isDuplexDefault;
  2371.   };
  2372.  
  2373.   /**
  2374.    * Mapping of capability formats to an identifier of the duplex capability.
  2375.    * @type {!Object.<!CloudCapabilities.Format, string>}
  2376.    */
  2377.   DuplexCapability.Id = {};
  2378.   DuplexCapability.Id[CloudCapabilities.Format.PPD] = 'Duplex';
  2379.   DuplexCapability.Id[CloudCapabilities.Format.XPS] =
  2380.       'psk:JobDuplexAllDocumentsContiguously';
  2381.  
  2382.   /**
  2383.    * Regular expression that matches a no-duplexing option.
  2384.    * @type {!RegExp}
  2385.    * @const
  2386.    */
  2387.   DuplexCapability.SIMPLEX_REGEX = /(.*onesided.*|.*none.*)/i;
  2388.  
  2389.   /**
  2390.    * Regular expression that matches a duplex on long edge option.
  2391.    * @type {!RegExp}
  2392.    * @const
  2393.    */
  2394.   DuplexCapability.LONG_EDGE_REGEX = /(.*longedge.*|duplexNoTumble)/i;
  2395.  
  2396.   DuplexCapability.prototype = {
  2397.     __proto__: CloudCapability.prototype,
  2398.  
  2399.     /** @return {string} Identifier of the no-duplexing option. */
  2400.     get simplexOption() {
  2401.       return this.simplexOption_;
  2402.     },
  2403.  
  2404.     /** @return {string} Identifier of the duplex on long edge option. */
  2405.     get longEdgeOption() {
  2406.       return this.longEdgeOption_;
  2407.     },
  2408.  
  2409.     /** @return {boolean} Whether duplexing is enabled by default. */
  2410.     get isDuplexDefault() {
  2411.       return this.isDuplexDefault_;
  2412.     }
  2413.   };
  2414.  
  2415.   // Export
  2416.   return {
  2417.     CloudCapabilities: CloudCapabilities,
  2418.     CollateCapability: CollateCapability,
  2419.     ColorCapability: ColorCapability,
  2420.     CopiesCapability: CopiesCapability,
  2421.     DuplexCapability: DuplexCapability
  2422.   };
  2423. });
  2424.  
  2425. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2426. // Use of this source code is governed by a BSD-style license that can be
  2427. // found in the LICENSE file.
  2428.  
  2429. cr.define('print_preview', function() {
  2430.   'use strict';
  2431.  
  2432.   /**
  2433.    * A data store that stores destinations and dispatches events when the data
  2434.    * store changes.
  2435.    * @param {!print_preview.NativeLayer} nativeLayer Used to fetch local print
  2436.    *     destinations.
  2437.    * @param {!print_preview.AppState} appState Application state.
  2438.    * @constructor
  2439.    * @extends {cr.EventTarget}
  2440.    */
  2441.   function DestinationStore(nativeLayer, appState) {
  2442.     cr.EventTarget.call(this);
  2443.  
  2444.     /**
  2445.      * Used to fetch local print destinations.
  2446.      * @type {!print_preview.NativeLayer}
  2447.      * @private
  2448.      */
  2449.     this.nativeLayer_ = nativeLayer;
  2450.  
  2451.     /**
  2452.      * Used to load and persist the selected destination.
  2453.      * @type {!print_preview.AppState}
  2454.      * @private
  2455.      */
  2456.     this.appState_ = appState;
  2457.  
  2458.     /**
  2459.      * Internal backing store for the data store.
  2460.      * @type {!Array.<!print_preview.Destination>}
  2461.      * @private
  2462.      */
  2463.     this.destinations_ = [];
  2464.  
  2465.     /**
  2466.      * Cache used for constant lookup of destinations by ID.
  2467.      * @type {object.<string, !print_preview.Destination>}
  2468.      * @private
  2469.      */
  2470.     this.destinationMap_ = {};
  2471.  
  2472.     /**
  2473.      * Currently selected destination.
  2474.      * @type {print_preview.Destination}
  2475.      * @private
  2476.      */
  2477.     this.selectedDestination_ = null;
  2478.  
  2479.     /**
  2480.      * Initial destination ID used to auto-select the first inserted destination
  2481.      * that matches. If {@code null}, the first destination inserted into the
  2482.      * store will be selected.
  2483.      * @type {?string}
  2484.      * @private
  2485.      */
  2486.     this.initialDestinationId_ = null;
  2487.  
  2488.     /**
  2489.      * Whether the initial destination is a local one or not.
  2490.      * @type {boolean}
  2491.      * @private
  2492.      */
  2493.     this.isInitialDestinationLocal_ = true;
  2494.  
  2495.     /**
  2496.      * Whether the destination store will auto select the destination that
  2497.      * matches the initial destination.
  2498.      * @type {boolean}
  2499.      * @private
  2500.      */
  2501.     this.isInAutoSelectMode_ = false;
  2502.  
  2503.     /**
  2504.      * Event tracker used to track event listeners of the destination store.
  2505.      * @type {!EventTracker}
  2506.      * @private
  2507.      */
  2508.     this.tracker_ = new EventTracker();
  2509.  
  2510.     /**
  2511.      * Used to fetch cloud-based print destinations.
  2512.      * @type {print_preview.CloudPrintInterface}
  2513.      * @private
  2514.      */
  2515.     this.cloudPrintInterface_ = null;
  2516.  
  2517.     /**
  2518.      * Whether the destination store has already loaded or is loading all cloud
  2519.      * destinations.
  2520.      * @type {boolean}
  2521.      * @private
  2522.      */
  2523.     this.hasLoadedAllCloudDestinations_ = false;
  2524.  
  2525.     /**
  2526.      * ID of a timeout after the initial destination ID is set. If no inserted
  2527.      * destination matches the initial destination ID after the specified
  2528.      * timeout, the first destination in the store will be automatically
  2529.      * selected.
  2530.      * @type {?number}
  2531.      * @private
  2532.      */
  2533.     this.autoSelectTimeout_ = null;
  2534.  
  2535.     /**
  2536.      * Whether a search for local destinations is in progress.
  2537.      * @type {boolean}
  2538.      * @private
  2539.      */
  2540.     this.isLocalDestinationSearchInProgress_ = false;
  2541.  
  2542.     /**
  2543.      * Number of outstanding cloud destination search requests.
  2544.      * @type {number}
  2545.      * @private
  2546.      */
  2547.     this.outstandingCloudSearchRequestCount_ = 0;
  2548.  
  2549.     this.addEventListeners_();
  2550.     this.reset_();
  2551.   };
  2552.  
  2553.   /**
  2554.    * Event types dispatched by the data store.
  2555.    * @enum {string}
  2556.    */
  2557.   DestinationStore.EventType = {
  2558.     DESTINATION_SEARCH_DONE:
  2559.         'print_preview.DestinationStore.DESTINATION_SEARCH_DONE',
  2560.     DESTINATION_SEARCH_STARTED:
  2561.         'print_preview.DestinationStore.DESTINATION_SEARCH_STARTED',
  2562.     DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT',
  2563.     DESTINATIONS_INSERTED:
  2564.         'print_preview.DestinationStore.DESTINATIONS_INSERTED',
  2565.     SELECTED_DESTINATION_CAPABILITIES_READY:
  2566.         'print_preview.DestinationStore.SELECTED_DESTINATION_CAPABILITIES_READY'
  2567.   };
  2568.  
  2569.   /**
  2570.    * Delay in milliseconds before the destination store ignores the initial
  2571.    * destination ID and just selects any printer (since the initial destination
  2572.    * was not found).
  2573.    * @type {number}
  2574.    * @const
  2575.    * @private
  2576.    */
  2577.   DestinationStore.AUTO_SELECT_TIMEOUT_ = 15000;
  2578.  
  2579.   /**
  2580.    * Creates a local PDF print destination.
  2581.    * @return {!print_preview.Destination} Created print destination.
  2582.    * @private
  2583.    */
  2584.   DestinationStore.createLocalPdfPrintDestination_ = function() {
  2585.     var dest = new print_preview.Destination(
  2586.         print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
  2587.         print_preview.Destination.Type.LOCAL,
  2588.         localStrings.getString('printToPDF'),
  2589.         false /*isRecent*/,
  2590.         print_preview.Destination.ConnectionStatus.ONLINE);
  2591.     dest.capabilities = new print_preview.ChromiumCapabilities(
  2592.         false /*hasCopiesCapability*/,
  2593.         '1' /*defaultCopiesStr*/,
  2594.         false /*hasCollateCapability*/,
  2595.         false /*defaultIsCollateEnabled*/,
  2596.         false /*hasDuplexCapability*/,
  2597.         false /*defaultIsDuplexEnabled*/,
  2598.         true /*hasOrientationCapability*/,
  2599.         false /*defaultIsLandscapeEnabled*/,
  2600.         true /*hasColorCapability*/,
  2601.         true /*defaultIsColorEnabled*/);
  2602.     return dest;
  2603.   };
  2604.  
  2605.   DestinationStore.prototype = {
  2606.     __proto__: cr.EventTarget.prototype,
  2607.  
  2608.     /**
  2609.      * @return {!Array.<!print_preview.Destination>} List of destinations in
  2610.      *     the store.
  2611.      */
  2612.     get destinations() {
  2613.       return this.destinations_.slice(0);
  2614.     },
  2615.  
  2616.     /**
  2617.      * @return {print_preview.Destination} The currently selected destination or
  2618.      *     {@code null} if none is selected.
  2619.      */
  2620.     get selectedDestination() {
  2621.       return this.selectedDestination_;
  2622.     },
  2623.  
  2624.     /**
  2625.      * @return {boolean} Whether a search for local destinations is in progress.
  2626.      */
  2627.     get isLocalDestinationSearchInProgress() {
  2628.       return this.isLocalDestinationSearchInProgress_;
  2629.     },
  2630.  
  2631.     /**
  2632.      * @return {boolean} Whether a search for cloud destinations is in progress.
  2633.      */
  2634.     get isCloudDestinationSearchInProgress() {
  2635.       return this.outstandingCloudSearchRequestCount_ > 0;
  2636.     },
  2637.  
  2638.     /**
  2639.      * Initializes the destination store. Sets the initially selected
  2640.      * destination. If any inserted destinations match this ID, that destination
  2641.      * will be automatically selected. This method must be called after the
  2642.      * print_preview.AppState has been initialized.
  2643.      * @param {?string} systemDefaultDestinationId ID of the system default
  2644.      *     destination.
  2645.      * @private
  2646.      */
  2647.     init: function(systemDefaultDestinationId) {
  2648.       if (this.appState_.selectedDestinationId) {
  2649.         this.initialDestinationId_ = this.appState_.selectedDestinationId;
  2650.         this.isInitialDestinationLocal_ =
  2651.             this.appState_.isSelectedDestinationLocal;
  2652.       } else {
  2653.         this.initialDestinationId_ = systemDefaultDestinationId;
  2654.         this.isInitialDestinationLocal_ = true;
  2655.       }
  2656.  
  2657.       this.isInAutoSelectMode_ = true;
  2658.       if (this.initialDestinationId_ == null) {
  2659.         assert(this.destinations_.length > 0,
  2660.                'No destinations available to select');
  2661.         this.selectDestination(this.destinations_[0]);
  2662.       } else {
  2663.         var candidate = this.destinationMap_[this.initialDestinationId_];
  2664.         if (candidate != null) {
  2665.           this.selectDestination(candidate);
  2666.         } else if (!cr.isChromeOS && this.isInitialDestinationLocal_) {
  2667.           this.nativeLayer_.startGetLocalDestinationCapabilities(
  2668.               this.initialDestinationId_);
  2669.         }
  2670.       }
  2671.     },
  2672.  
  2673.     /**
  2674.      * Sets the destination store's Google Cloud Print interface.
  2675.      * @param {!print_preview.CloudPrintInterface} cloudPrintInterface Interface
  2676.      *     to set.
  2677.      */
  2678.     setCloudPrintInterface: function(cloudPrintInterface) {
  2679.       this.cloudPrintInterface_ = cloudPrintInterface;
  2680.       this.tracker_.add(
  2681.           this.cloudPrintInterface_,
  2682.           cloudprint.CloudPrintInterface.EventType.SEARCH_DONE,
  2683.           this.onCloudPrintSearchDone_.bind(this));
  2684.       this.tracker_.add(
  2685.           this.cloudPrintInterface_,
  2686.           cloudprint.CloudPrintInterface.EventType.SEARCH_FAILED,
  2687.           this.onCloudPrintSearchFailed_.bind(this));
  2688.       this.tracker_.add(
  2689.           this.cloudPrintInterface_,
  2690.           cloudprint.CloudPrintInterface.EventType.PRINTER_DONE,
  2691.           this.onCloudPrintPrinterDone_.bind(this));
  2692.       this.tracker_.add(
  2693.           this.cloudPrintInterface_,
  2694.           cloudprint.CloudPrintInterface.EventType.PRINTER_FAILED,
  2695.           this.onCloudPrintPrinterFailed_.bind(this));
  2696.       // Fetch initial destination if its a cloud destination.
  2697.       if (this.isInAutoSelectMode_ && !this.isInitialDestinationLocal_) {
  2698.         this.cloudPrintInterface_.printer(this.initialDestinationId_);
  2699.       }
  2700.     },
  2701.  
  2702.     /**
  2703.      * @return {boolean} Whether only default cloud destinations have been
  2704.      *     loaded.
  2705.      */
  2706.     hasOnlyDefaultCloudDestinations: function() {
  2707.       return this.destinations_.every(function(dest) {
  2708.         return dest.isLocal ||
  2709.             dest.id == print_preview.Destination.GooglePromotedId.DOCS ||
  2710.             dest.id == print_preview.Destination.GooglePromotedId.FEDEX;
  2711.       });
  2712.     },
  2713.  
  2714.     /** @param {!print_preview.Destination} Destination to select. */
  2715.     selectDestination: function(destination) {
  2716.       this.selectedDestination_ = destination;
  2717.       this.selectedDestination_.isRecent = true;
  2718.       this.isInAutoSelectMode_ = false;
  2719.       if (this.autoSelectTimeout_ != null) {
  2720.         clearTimeout(this.autoSelectTimeout_);
  2721.         this.autoSelectTimeout_ = null;
  2722.       }
  2723.       if (destination.id == print_preview.Destination.GooglePromotedId.FEDEX &&
  2724.           !destination.isTosAccepted) {
  2725.         assert(this.cloudPrintInterface_ != null,
  2726.                'Selected FedEx Office destination, but Google Cloud Print is ' +
  2727.                'not enabled');
  2728.         destination.isTosAccepted = true;
  2729.         this.cloudPrintInterface_.updatePrinterTosAcceptance(destination.id,
  2730.                                                              true);
  2731.       }
  2732.       this.appState_.persistSelectedDestination(this.selectedDestination_);
  2733.       cr.dispatchSimpleEvent(
  2734.           this, DestinationStore.EventType.DESTINATION_SELECT);
  2735.       if (destination.capabilities == null) {
  2736.          if (destination.isLocal) {
  2737.           this.nativeLayer_.startGetLocalDestinationCapabilities(
  2738.               destination.id);
  2739.         } else {
  2740.           assert(this.cloudPrintInterface_ != null,
  2741.                  'Selected destination is a cloud destination, but Google ' +
  2742.                  'Cloud Print is not enabled');
  2743.           this.cloudPrintInterface_.printer(destination.id);
  2744.         }
  2745.       } else {
  2746.         cr.dispatchSimpleEvent(
  2747.             this,
  2748.             DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
  2749.       }
  2750.     },
  2751.  
  2752.     /**
  2753.      * Inserts a print destination to the data store and dispatches a
  2754.      * DESTINATIONS_INSERTED event. If the destination matches the initial
  2755.      * destination ID, then the destination will be automatically selected.
  2756.      * @param {!print_preview.Destination} destination Print destination to
  2757.      *     insert.
  2758.      */
  2759.     insertDestination: function(destination) {
  2760.       if (this.insertDestination_(destination)) {
  2761.         cr.dispatchSimpleEvent(
  2762.             this, DestinationStore.EventType.DESTINATIONS_INSERTED);
  2763.         if (this.isInAutoSelectMode_ &&
  2764.             (this.initialDestinationId_ == null ||
  2765.                 destination.id == this.initialDestinationId_)) {
  2766.           this.selectDestination(destination);
  2767.         }
  2768.       }
  2769.     },
  2770.  
  2771.     /**
  2772.      * Inserts multiple print destinations to the data store and dispatches one
  2773.      * DESTINATIONS_INSERTED event. If any of the destinations match the initial
  2774.      * destination ID, then that destination will be automatically selected.
  2775.      * @param {!Array.<print_preview.Destination>} destinations Print
  2776.      *     destinations to insert.
  2777.      */
  2778.     insertDestinations: function(destinations) {
  2779.       var insertedDestination = false;
  2780.       var destinationToAutoSelect = null;
  2781.       destinations.forEach(function(dest) {
  2782.         if (this.insertDestination_(dest)) {
  2783.           insertedDestination = true;
  2784.           if (this.isInAutoSelectMode_ &&
  2785.               destinationToAutoSelect == null &&
  2786.               (this.initialDestinationId_ == null ||
  2787.                   dest.id == this.initialDestinationId_)) {
  2788.             destinationToAutoSelect = dest;
  2789.           }
  2790.         }
  2791.       }, this);
  2792.       if (insertedDestination) {
  2793.         cr.dispatchSimpleEvent(
  2794.             this, DestinationStore.EventType.DESTINATIONS_INSERTED);
  2795.       }
  2796.       if (destinationToAutoSelect != null) {
  2797.         this.selectDestination(destinationToAutoSelect);
  2798.       }
  2799.     },
  2800.  
  2801.     /**
  2802.      * Updates an existing print destination with capabilities information. If
  2803.      * the destination doesn't already exist, it will be added.
  2804.      * @param {!print_preview.Destination} destination Destination to update.
  2805.      * @return {!print_preview.Destination} The existing destination that was
  2806.      *     updated.
  2807.      */
  2808.     updateDestination: function(destination) {
  2809.       var existingDestination = this.destinationMap_[destination.id];
  2810.       if (existingDestination != null) {
  2811.         existingDestination.capabilities = destination.capabilities;
  2812.         return existingDestination;
  2813.       } else {
  2814.         this.insertDestination(destination);
  2815.       }
  2816.     },
  2817.  
  2818.     /** Initiates loading of local print destinations. */
  2819.     startLoadLocalDestinations: function() {
  2820.       this.nativeLayer_.startGetLocalDestinations();
  2821.       this.isLocalDestinationSearchInProgress_ = true;
  2822.       cr.dispatchSimpleEvent(
  2823.           this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED);
  2824.     },
  2825.  
  2826.     /** Initiates loading of recent cloud destinations. */
  2827.     startLoadRecentCloudDestinations: function() {
  2828.       if (this.cloudPrintInterface_ != null) {
  2829.         this.cloudPrintInterface_.search(true /*isRecent*/);
  2830.         this.outstandingCloudSearchRequestCount_++;
  2831.         cr.dispatchSimpleEvent(
  2832.             this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED);
  2833.       }
  2834.     },
  2835.  
  2836.     /** Initiates loading of all cloud destinations. */
  2837.     startLoadAllCloudDestinations: function() {
  2838.       if (this.cloudPrintInterface_ != null &&
  2839.           !this.hasLoadedAllCloudDestinations_) {
  2840.         this.cloudPrintInterface_.search(false /*isRecent*/);
  2841.         this.outstandingCloudSearchRequestCount_++;
  2842.         this.hasLoadedAllCloudDestinations_ = true;
  2843.         cr.dispatchSimpleEvent(
  2844.             this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED);
  2845.       }
  2846.     },
  2847.  
  2848.     /**
  2849.      * Inserts a destination into the store without dispatching any events.
  2850.      * @return {boolean} Whether the inserted destination was not already in the
  2851.      *     store.
  2852.      * @private
  2853.      */
  2854.     insertDestination_: function(destination) {
  2855.       var existingDestination = this.destinationMap_[destination.id];
  2856.       if (existingDestination == null) {
  2857.         this.destinations_.push(destination);
  2858.         this.destinationMap_[destination.id] = destination;
  2859.         return true;
  2860.       } else if (existingDestination.connectionStatus ==
  2861.                      print_preview.Destination.ConnectionStatus.UNKNOWN &&
  2862.                  destination.connectionStatus !=
  2863.                      print_preview.Destination.ConnectionStatus.UNKNOWN) {
  2864.         existingDestination.connectionStatus = destination.connectionStatus;
  2865.         return true;
  2866.       } else {
  2867.         return false;
  2868.       }
  2869.     },
  2870.  
  2871.     /**
  2872.      * Binds handlers to events.
  2873.      * @private
  2874.      */
  2875.     addEventListeners_: function() {
  2876.       this.tracker_.add(
  2877.           this.nativeLayer_,
  2878.           print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET,
  2879.           this.onLocalDestinationsSet_.bind(this));
  2880.       this.tracker_.add(
  2881.           this.nativeLayer_,
  2882.           print_preview.NativeLayer.EventType.CAPABILITIES_SET,
  2883.           this.onLocalDestinationCapabilitiesSet_.bind(this));
  2884.       this.tracker_.add(
  2885.           this.nativeLayer_,
  2886.           print_preview.NativeLayer.EventType.GET_CAPABILITIES_FAIL,
  2887.           this.onGetCapabilitiesFail_.bind(this));
  2888.       this.tracker_.add(
  2889.           this.nativeLayer_,
  2890.           print_preview.NativeLayer.EventType.DESTINATIONS_RELOAD,
  2891.           this.onDestinationsReload_.bind(this));
  2892.     },
  2893.  
  2894.     /**
  2895.      * Resets the state of the destination store to its initial state.
  2896.      * @private
  2897.      */
  2898.     reset_: function() {
  2899.       this.destinations_ = [];
  2900.       this.destinationMap_ = {};
  2901.       this.selectedDestination_ = null;
  2902.       this.hasLoadedAllCloudDestinations_ = false;
  2903.       this.insertDestination(
  2904.           DestinationStore.createLocalPdfPrintDestination_());
  2905.       this.autoSelectTimeout_ = setTimeout(
  2906.           this.onAutoSelectTimeoutExpired_.bind(this),
  2907.           DestinationStore.AUTO_SELECT_TIMEOUT_);
  2908.     },
  2909.  
  2910.     /**
  2911.      * Called when the local destinations have been got from the native layer.
  2912.      * @param {cr.Event} Contains the local destinations.
  2913.      * @private
  2914.      */
  2915.     onLocalDestinationsSet_: function(event) {
  2916.       var localDestinations = event.destinationInfos.map(function(destInfo) {
  2917.         return print_preview.LocalDestinationParser.parse(destInfo);
  2918.       });
  2919.       this.insertDestinations(localDestinations);
  2920.       this.isLocalDestinationSearchInProgress_ = false;
  2921.       cr.dispatchSimpleEvent(
  2922.           this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
  2923.     },
  2924.  
  2925.     /**
  2926.      * Called when the native layer retrieves the capabilities for the selected
  2927.      * local destination. Updates the destination with new capabilities if the
  2928.      * destination already exists, otherwise it creates a new destination and
  2929.      * then updates its capabilities.
  2930.      * @param {cr.Event} event Contains the capabilities of the local print
  2931.      *     destination.
  2932.      * @private
  2933.      */
  2934.     onLocalDestinationCapabilitiesSet_: function(event) {
  2935.       var destinationId = event.settingsInfo['printerId'];
  2936.       var destination = this.destinationMap_[destinationId];
  2937.       var capabilities = print_preview.LocalCapabilitiesParser.parse(
  2938.             event.settingsInfo);
  2939.       if (destination) {
  2940.         // In case there were multiple capabilities request for this local
  2941.         // destination, just ignore the later ones.
  2942.         if (destination.capabilities != null) {
  2943.           return;
  2944.         }
  2945.         destination.capabilities = capabilities;
  2946.       } else {
  2947.         // TODO(rltoscano): This makes the assumption that the "deviceName" is
  2948.         // the same as "printerName". We should include the "printerName" in the
  2949.         // response. See http://crbug.com/132831.
  2950.         destination = print_preview.LocalDestinationParser.parse(
  2951.             {deviceName: destinationId, printerName: destinationId});
  2952.         destination.capabilities = capabilities;
  2953.         this.insertDestination(destination);
  2954.       }
  2955.       if (this.selectedDestination_ &&
  2956.           this.selectedDestination_.id == destinationId) {
  2957.         cr.dispatchSimpleEvent(this,
  2958.                                DestinationStore.EventType.
  2959.                                    SELECTED_DESTINATION_CAPABILITIES_READY);
  2960.       }
  2961.     },
  2962.  
  2963.     /**
  2964.      * Called when a request to get a local destination's print capabilities
  2965.      * fails. If the destination is the initial destination, auto-select another
  2966.      * destination instead.
  2967.      * @param {cr.Event} event Contains the destination ID that failed.
  2968.      * @private
  2969.      */
  2970.     onGetCapabilitiesFail_: function(event) {
  2971.       console.error('Failed to get print capabilities for printer ' +
  2972.                     event.destinationId);
  2973.       if (this.isInAutoSelectMode_ &&
  2974.           this.initialDestinationId_ == event.destinationId) {
  2975.         assert(this.destinations_.length > 0,
  2976.                'No destinations were loaded when failed to get initial ' +
  2977.                'destination');
  2978.         this.selectDestination(this.destinations_[0]);
  2979.       }
  2980.     },
  2981.  
  2982.     /**
  2983.      * Called when the /search call completes. Adds the fetched destinations to
  2984.      * the destination store.
  2985.      * @param {cr.Event} event Contains the fetched destinations.
  2986.      * @private
  2987.      */
  2988.     onCloudPrintSearchDone_: function(event) {
  2989.       this.outstandingCloudSearchRequestCount_--;
  2990.       this.insertDestinations(event.printers);
  2991.       cr.dispatchSimpleEvent(
  2992.           this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
  2993.     },
  2994.  
  2995.     /**
  2996.      * Called when the /search call fails. Updates outstanding request count and
  2997.      * dispatches CLOUD_DESTINATIONS_LOADED event.
  2998.      * @private
  2999.      */
  3000.     onCloudPrintSearchFailed_: function() {
  3001.       this.outstandingCloudSearchRequestCount_--;
  3002.       cr.dispatchSimpleEvent(
  3003.           this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
  3004.     },
  3005.  
  3006.     /**
  3007.      * Called when /printer call completes. Updates the specified destination's
  3008.      * print capabilities.
  3009.      * @param {cr.Event} event Contains detailed information about the
  3010.      *     destination.
  3011.      * @private
  3012.      */
  3013.     onCloudPrintPrinterDone_: function(event) {
  3014.       var dest = this.updateDestination(event.printer);
  3015.       if (this.selectedDestination_ == dest) {
  3016.         cr.dispatchSimpleEvent(
  3017.             this,
  3018.             DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
  3019.       }
  3020.     },
  3021.  
  3022.     /**
  3023.      * Called when the Google Cloud Print interface fails to lookup a
  3024.      * destination. Selects another destination if the failed destination was
  3025.      * the initial destination.
  3026.      * @param {object} event Contains the ID of the destination that was failed
  3027.      *     to be looked up.
  3028.      * @private
  3029.      */
  3030.     onCloudPrintPrinterFailed_: function(event) {
  3031.       if (this.isInAutoSelectMode_ &&
  3032.           this.initialDestinationId_ == event.destinationId) {
  3033.         console.error('Could not find initial printer: ' + event.destinationId);
  3034.         assert(this.destinations_.length > 0,
  3035.                'No destinations were loaded when failed to get initial ' +
  3036.                'destination');
  3037.         this.selectDestination(this.destinations_[0]);
  3038.       }
  3039.     },
  3040.  
  3041.     /**
  3042.      * Called from native layer after the user was requested to sign in, and did
  3043.      * so successfully.
  3044.      * @private
  3045.      */
  3046.     onDestinationsReload_: function() {
  3047.       this.reset_();
  3048.       this.isInAutoSelectMode_ = true;
  3049.       this.startLoadLocalDestinations();
  3050.       this.startLoadRecentCloudDestinations();
  3051.       this.startLoadAllCloudDestinations();
  3052.     },
  3053.  
  3054.     /**
  3055.      * Called when no destination was auto-selected after some timeout. Selects
  3056.      * the first destination in store.
  3057.      * @private
  3058.      */
  3059.     onAutoSelectTimeoutExpired_: function() {
  3060.       this.autoSelectTimeout_ = null;
  3061.       assert(this.destinations_.length > 0,
  3062.              'No destinations were loaded before auto-select timeout expired');
  3063.       this.selectDestination(this.destinations_[0]);
  3064.     }
  3065.   };
  3066.  
  3067.   // Export
  3068.   return {
  3069.     DestinationStore: DestinationStore
  3070.   };
  3071. });
  3072.  
  3073. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3074. // Use of this source code is governed by a BSD-style license that can be
  3075. // found in the LICENSE file.
  3076.  
  3077. cr.define('print_preview', function() {
  3078.   'use strict';
  3079.  
  3080.   /**
  3081.    * Creates a Margins object that holds four margin values in points.
  3082.    * @param {number} top The top margin in pts.
  3083.    * @param {number} right The right margin in pts.
  3084.    * @param {number} bottom The bottom margin in pts.
  3085.    * @param {number} left The left margin in pts.
  3086.    * @constructor
  3087.    */
  3088.   function Margins(top, right, bottom, left) {
  3089.     /**
  3090.      * Backing store for the margin values in points.
  3091.      * @type {!Object.<
  3092.      *     !print_preview.ticket_items.CustomMargins.Orientation, number>}
  3093.      * @private
  3094.      */
  3095.     this.value_ = {};
  3096.     this.value_[print_preview.ticket_items.CustomMargins.Orientation.TOP] = top;
  3097.     this.value_[print_preview.ticket_items.CustomMargins.Orientation.RIGHT] =
  3098.         right;
  3099.     this.value_[print_preview.ticket_items.CustomMargins.Orientation.BOTTOM] =
  3100.         bottom;
  3101.     this.value_[print_preview.ticket_items.CustomMargins.Orientation.LEFT] =
  3102.         left;
  3103.   };
  3104.  
  3105.   /**
  3106.    * Parses a margins object from the given serialized state.
  3107.    * @param {Object} state Serialized representation of the margins created by
  3108.    *     the {@code serialize} method.
  3109.    * @return {!print_preview.Margins} New margins instance.
  3110.    */
  3111.   Margins.parse = function(state) {
  3112.     return new print_preview.Margins(
  3113.         state[print_preview.ticket_items.CustomMargins.Orientation.TOP],
  3114.         state[print_preview.ticket_items.CustomMargins.Orientation.RIGHT],
  3115.         state[print_preview.ticket_items.CustomMargins.Orientation.BOTTOM],
  3116.         state[print_preview.ticket_items.CustomMargins.Orientation.LEFT]);
  3117.   };
  3118.  
  3119.   Margins.prototype = {
  3120.     /**
  3121.      * @param {!print_preview.ticket_items.CustomMargins.Orientation}
  3122.      *     orientation Specifies the margin value to get.
  3123.      * @return {number} Value of the margin of the given orientation.
  3124.      */
  3125.     get: function(orientation) {
  3126.       return this.value_[orientation];
  3127.     },
  3128.  
  3129.     /**
  3130.      * @param {!print_preview.ticket_items.CustomMargins.Orientation}
  3131.      *     orientation Specifies the margin to set.
  3132.      * @param {number} value Updated value of the margin in points to modify.
  3133.      * @return {!print_preview.Margins} A new copy of |this| with the
  3134.      *     modification made to the specified margin.
  3135.      */
  3136.     set: function(orientation, value) {
  3137.       var newValue = this.clone_();
  3138.       newValue[orientation] = value;
  3139.       return new Margins(
  3140.           newValue[print_preview.ticket_items.CustomMargins.Orientation.TOP],
  3141.           newValue[print_preview.ticket_items.CustomMargins.Orientation.RIGHT],
  3142.           newValue[print_preview.ticket_items.CustomMargins.Orientation.BOTTOM],
  3143.           newValue[print_preview.ticket_items.CustomMargins.Orientation.LEFT]);
  3144.     },
  3145.  
  3146.     /**
  3147.      * @param {print_preview.Margins} other The other margins object to compare
  3148.      *     against.
  3149.      * @return {boolean} Whether this margins object is equal to another.
  3150.      */
  3151.     equals: function(other) {
  3152.       if (other == null) {
  3153.         return false;
  3154.       }
  3155.       for (var orientation in this.value_) {
  3156.         if (this.value_[orientation] != other.value_[orientation]) {
  3157.           return false;
  3158.         }
  3159.       }
  3160.       return true;
  3161.     },
  3162.  
  3163.     /** @return {Object} A serialized representation of the margins. */
  3164.     serialize: function() {
  3165.       return this.clone_();
  3166.     },
  3167.  
  3168.     /**
  3169.      * @return {Object} Cloned state of the margins.
  3170.      * @private
  3171.      */
  3172.     clone_: function() {
  3173.       var clone = {};
  3174.       for (var o in this.value_) {
  3175.         clone[o] = this.value_[o];
  3176.       }
  3177.       return clone;
  3178.     }
  3179.   };
  3180.  
  3181.   // Export
  3182.   return {
  3183.     Margins: Margins
  3184.   };
  3185. });
  3186.  
  3187. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3188. // Use of this source code is governed by a BSD-style license that can be
  3189. // found in the LICENSE file.
  3190.  
  3191. cr.define('print_preview', function() {
  3192.   'use strict';
  3193.  
  3194.   /**
  3195.    * Object which contains information related to the document to print.
  3196.    * @constructor
  3197.    */
  3198.   function DocumentInfo() {
  3199.     /**
  3200.      * Whether the document to print is modifiable (i.e. can be reflowed).
  3201.      * @type {boolean}
  3202.      */
  3203.     this.isModifiable = true;
  3204.  
  3205.     /**
  3206.      * Number of pages in the document to print.
  3207.      * @type {number}
  3208.      */
  3209.     this.pageCount = 1;
  3210.  
  3211.     /**
  3212.      * Size of the pages of the document in points.
  3213.      * @type {!print_preview.Size}
  3214.      */
  3215.     this.pageSize = new print_preview.Size(0, 0);
  3216.  
  3217.     /**
  3218.      * Printable area of the document in points.
  3219.      * @type {!print_preview.PrintableArea}
  3220.      */
  3221.     this.printableArea = new print_preview.PrintableArea(
  3222.         new print_preview.Coordinate2d(0, 0), new print_preview.Size(0, 0));
  3223.  
  3224.     /**
  3225.      * Whether the document is styled by CSS media styles.
  3226.      * @type {boolean}
  3227.      */
  3228.     this.hasCssMediaStyles = false;
  3229.  
  3230.     /**
  3231.      * Margins of the document in points.
  3232.      * @type {print_preview.Margins}
  3233.      */
  3234.     this.margins = null;
  3235.  
  3236.     /**
  3237.      * Title of document.
  3238.      * @type {string}
  3239.      */
  3240.     this.title = '';
  3241.   };
  3242.  
  3243.   // Export
  3244.   return {
  3245.     DocumentInfo: DocumentInfo
  3246.   };
  3247. });
  3248.  
  3249. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3250. // Use of this source code is governed by a BSD-style license that can be
  3251. // found in the LICENSE file.
  3252.  
  3253. cr.define('print_preview', function() {
  3254.   'use strict';
  3255.  
  3256.   /**
  3257.    * Object describing the printable area of a page in the document.
  3258.    * @param {!print_preview.Coordinate2d} origin Top left corner of the
  3259.    *     printable area of the document.
  3260.    * @param {!print_preview.Size} size Size of the printable area of the
  3261.    *     document.
  3262.    * @constructor
  3263.    */
  3264.   function PrintableArea(origin, size) {
  3265.     /**
  3266.      * Top left corner of the printable area of the document.
  3267.      * @type {!print_preview.Coordinate2d}
  3268.      * @private
  3269.      */
  3270.     this.origin_ = origin;
  3271.  
  3272.     /**
  3273.      * Size of the printable area of the document.
  3274.      * @type {!print_preview.Size}
  3275.      * @private
  3276.      */
  3277.     this.size_ = size;
  3278.   };
  3279.  
  3280.   PrintableArea.prototype = {
  3281.     /**
  3282.      * @return {!print_preview.Coordinate2d} Top left corner of the printable
  3283.      *     area of the document.
  3284.      */
  3285.     get origin() {
  3286.       return this.origin_;
  3287.     },
  3288.  
  3289.     /**
  3290.      * @return {!print_preview.Size} Size of the printable area of the document.
  3291.      */
  3292.     get size() {
  3293.       return this.size_;
  3294.     },
  3295.  
  3296.     /**
  3297.      * @param {print_preview.PrintableArea} other Other printable area to check
  3298.      *     for equality.
  3299.      * @return {boolean} Whether another printable area is equal to this one.
  3300.      */
  3301.     equals: function(other) {
  3302.       return other != null &&
  3303.           this.origin_.equals(other.origin_) &&
  3304.           this.size_.equals(other.size_);
  3305.     }
  3306.   };
  3307.  
  3308.   // Export
  3309.   return {
  3310.     PrintableArea: PrintableArea
  3311.   };
  3312. });
  3313.  
  3314. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3315. // Use of this source code is governed by a BSD-style license that can be
  3316. // found in the LICENSE file.
  3317.  
  3318. cr.define('print_preview', function() {
  3319.   'use strict';
  3320.  
  3321.   /**
  3322.    * Measurement system of the print preview. Used to parse and serialize point
  3323.    * measurements into the system's local units (e.g. millimeters, inches).
  3324.    * @param {string} thousandsDelimeter Delimeter between thousands digits.
  3325.    * @param {string} decimalDelimeter Delimeter between integers and decimals.
  3326.    * @param {!print_preview.MeasurementSystem.UnitType} unitType Measurement
  3327.    *     unit type of the system.
  3328.    * @constructor
  3329.    */
  3330.   function MeasurementSystem(thousandsDelimeter, decimalDelimeter, unitType) {
  3331.     this.thousandsDelimeter_ = thousandsDelimeter || ',';
  3332.     this.decimalDelimeter_ = decimalDelimeter || '.';
  3333.     this.unitType_ = unitType;
  3334.   };
  3335.  
  3336.   /**
  3337.    * Parses |numberFormat| and extracts the symbols used for the thousands point
  3338.    * and decimal point.
  3339.    * @param {string} numberFormat The formatted version of the number 12345678.
  3340.    * @return {!Array.<string>} The extracted symbols in the order
  3341.    *     [thousandsSymbol, decimalSymbol]. For example,
  3342.    *     parseNumberFormat("123,456.78") returns [",", "."].
  3343.    */
  3344.   MeasurementSystem.parseNumberFormat = function(numberFormat) {
  3345.     if (!numberFormat) {
  3346.       return [',', '.'];
  3347.     }
  3348.     var regex = /^(\d+)(\W?)(\d+)(\W?)(\d+)$/;
  3349.     var matches = numberFormat.match(regex) || ['', '', ',', '', '.'];
  3350.     return [matches[2], matches[4]];
  3351.   };
  3352.  
  3353.   /**
  3354.    * Enumeration of measurement unit types.
  3355.    * @enum {number}
  3356.    */
  3357.   MeasurementSystem.UnitType = {
  3358.     METRIC: 0, // millimeters
  3359.     IMPERIAL: 1 // inches
  3360.   };
  3361.  
  3362.   /**
  3363.    * Maximum resolution of local unit values.
  3364.    * @type {!Object.<!print_preview.MeasurementSystem.UnitType, number>}
  3365.    * @private
  3366.    */
  3367.   MeasurementSystem.Precision_ = {};
  3368.   MeasurementSystem.Precision_[MeasurementSystem.UnitType.METRIC] = 0.5;
  3369.   MeasurementSystem.Precision_[MeasurementSystem.UnitType.IMPERIAL] = 0.01;
  3370.  
  3371.   /**
  3372.    * Maximum number of decimal places to keep for local unit.
  3373.    * @type {!Object.<!print_preview.MeasurementSystem.UnitType, number>}
  3374.    * @private
  3375.    */
  3376.   MeasurementSystem.DecimalPlaces_ = {};
  3377.   MeasurementSystem.DecimalPlaces_[MeasurementSystem.UnitType.METRIC] = 1;
  3378.   MeasurementSystem.DecimalPlaces_[MeasurementSystem.UnitType.IMPERIAL] = 2;
  3379.  
  3380.   /**
  3381.    * Number of points per inch.
  3382.    * @type {number}
  3383.    * @const
  3384.    * @private
  3385.    */
  3386.   MeasurementSystem.PTS_PER_INCH_ = 72.0;
  3387.  
  3388.   /**
  3389.    * Number of points per millimeter.
  3390.    * @type {number}
  3391.    * @const
  3392.    * @private
  3393.    */
  3394.   MeasurementSystem.PTS_PER_MM_ = MeasurementSystem.PTS_PER_INCH_ / 25.4;
  3395.  
  3396.   MeasurementSystem.prototype = {
  3397.     /** @return {string} The unit type symbol of the measurement system. */
  3398.     get unitSymbol() {
  3399.       if (this.unitType_ == MeasurementSystem.UnitType.METRIC) {
  3400.         return 'mm';
  3401.       } else if (this.unitType_ == MeasurementSystem.UnitType.IMPERIAL) {
  3402.         return '"';
  3403.       } else {
  3404.         throw Error('Unit type not supported: ' + this.unitType_);
  3405.       }
  3406.     },
  3407.  
  3408.     /**
  3409.      * @return {string} The thousands delimeter character of the measurement
  3410.      *     system.
  3411.      */
  3412.     get thousandsDelimeter() {
  3413.       return this.thousandsDelimeter_;
  3414.     },
  3415.  
  3416.     /**
  3417.      * @return {string} The decimal delimeter character of the measurement
  3418.      *     system.
  3419.      */
  3420.     get decimalDelimeter() {
  3421.       return this.decimalDelimeter_;
  3422.     },
  3423.  
  3424.     setSystem: function(thousandsDelimeter, decimalDelimeter, unitType) {
  3425.       this.thousandsDelimeter_ = thousandsDelimeter;
  3426.       this.decimalDelimeter_ = decimalDelimeter;
  3427.       this.unitType_ = unitType;
  3428.     },
  3429.  
  3430.     /**
  3431.      * Rounds a value in the local system's units to the appropriate precision.
  3432.      * @param {number} value Value to round.
  3433.      * @return {number} Rounded value.
  3434.      */
  3435.     roundValue: function(value) {
  3436.       var precision = MeasurementSystem.Precision_[this.unitType_];
  3437.       var roundedValue = Math.round(value / precision) * precision;
  3438.       // Truncate
  3439.       return roundedValue.toFixed(
  3440.           MeasurementSystem.DecimalPlaces_[this.unitType_]);
  3441.     },
  3442.  
  3443.     /**
  3444.      * @param {number} pts Value in points to convert to local units.
  3445.      * @return {number} Value in local units.
  3446.      */
  3447.     convertFromPoints: function(pts) {
  3448.       if (this.unitType_ == MeasurementSystem.UnitType.METRIC) {
  3449.         return pts / MeasurementSystem.PTS_PER_MM_;
  3450.       } else {
  3451.         return pts / MeasurementSystem.PTS_PER_INCH_;
  3452.       }
  3453.     },
  3454.  
  3455.     /**
  3456.      * @param {number} Value in local units to convert to points.
  3457.      * @return {number} Value in points.
  3458.      */
  3459.     convertToPoints: function(localUnits) {
  3460.       if (this.unitType_ == MeasurementSystem.UnitType.METRIC) {
  3461.         return localUnits * MeasurementSystem.PTS_PER_MM_;
  3462.       } else {
  3463.         return localUnits * MeasurementSystem.PTS_PER_INCH_;
  3464.       }
  3465.     }
  3466.   };
  3467.  
  3468.   // Export
  3469.   return {
  3470.     MeasurementSystem: MeasurementSystem
  3471.   };
  3472. });
  3473.  
  3474. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3475. // Use of this source code is governed by a BSD-style license that can be
  3476. // found in the LICENSE file.
  3477.  
  3478. cr.define('print_preview', function() {
  3479.   'use strict';
  3480.  
  3481.   // TODO(rltoscano): Maybe clear print ticket when destination changes. Or
  3482.   // better yet, carry over any print ticket state that is possible. I.e. if
  3483.   // destination changes, the new destination might not support duplex anymore,
  3484.   // so we should clear the ticket's isDuplexEnabled state.
  3485.  
  3486.   /**
  3487.    * Storage of the print ticket and document statistics. Dispatches events when
  3488.    * the contents of the print ticket or document statistics change. Also
  3489.    * handles validation of the print ticket against destination capabilities and
  3490.    * against the document.
  3491.    * @param {!print_preview.DestinationStore} destinationStore Used to
  3492.    *     understand which printer is selected.
  3493.    * @param {!print_preview.AppState} appState Print preview application state.
  3494.    * @constructor
  3495.    * @extends {cr.EventTarget}
  3496.    */
  3497.   function PrintTicketStore(destinationStore, appState) {
  3498.     cr.EventTarget.call(this);
  3499.  
  3500.     /**
  3501.      * Destination store used to understand which printer is selected.
  3502.      * @type {!print_preview.DestinationStore}
  3503.      * @private
  3504.      */
  3505.     this.destinationStore_ = destinationStore;
  3506.  
  3507.     /**
  3508.      * App state used to persist and load ticket values.
  3509.      * @type {!print_preview.AppState}
  3510.      * @private
  3511.      */
  3512.     this.appState_ = appState;
  3513.  
  3514.     // Create the document info with some initial settings. Actual
  3515.     // page-related information won't be set until preview generation occurs,
  3516.     // so we'll use some defaults until then. This way, the print ticket store
  3517.     // will be valid even if no preview can be generated.
  3518.     var initialPageSize = new print_preview.Size(612, 792); // 8.5"x11"
  3519.  
  3520.     /**
  3521.      * Information about the document to print.
  3522.      * @type {!print_preview.DocumentInfo}
  3523.      * @private
  3524.      */
  3525.     this.documentInfo_ = new print_preview.DocumentInfo();
  3526.     this.documentInfo_.isModifiable = true;
  3527.     this.documentInfo_.pageCount = 1;
  3528.     this.documentInfo_.pageSize = initialPageSize;
  3529.     this.documentInfo_.printableArea = new print_preview.PrintableArea(
  3530.         new print_preview.Coordinate2d(0, 0), initialPageSize);
  3531.  
  3532.     /**
  3533.      * Printing capabilities of Chromium and the currently selected destination.
  3534.      * @type {!print_preview.CapabilitiesHolder}
  3535.      * @private
  3536.      */
  3537.     this.capabilitiesHolder_ = new print_preview.CapabilitiesHolder();
  3538.  
  3539.     /**
  3540.      * Current measurement system. Used to work with margin measurements.
  3541.      * @type {!print_preview.MeasurementSystem}
  3542.      * @private
  3543.      */
  3544.     this.measurementSystem_ = new print_preview.MeasurementSystem(
  3545.         ',', '.', print_preview.MeasurementSystem.UnitType.IMPERIAL);
  3546.  
  3547.     /**
  3548.      * Collate ticket item.
  3549.      * @type {!print_preview.ticket_items.Collate}
  3550.      * @private
  3551.      */
  3552.     this.collate_ =
  3553.         new print_preview.ticket_items.Collate(this.capabilitiesHolder_);
  3554.  
  3555.     /**
  3556.      * Color ticket item.
  3557.      * @type {!print_preview.ticket_items.Color}
  3558.      * @private
  3559.      */
  3560.     this.color_ = new print_preview.ticket_items.Color(
  3561.         this.capabilitiesHolder_, this.destinationStore_);
  3562.  
  3563.     /**
  3564.      * Copies ticket item.
  3565.      * @type {!print_preview.ticket_items.Copies}
  3566.      * @private
  3567.      */
  3568.     this.copies_ =
  3569.         new print_preview.ticket_items.Copies(this.capabilitiesHolder_);
  3570.  
  3571.     /**
  3572.      * Duplex ticket item.
  3573.      * @type {!print_preview.ticket_items.Duplex}
  3574.      * @private
  3575.      */
  3576.     this.duplex_ =
  3577.         new print_preview.ticket_items.Duplex(this.capabilitiesHolder_);
  3578.  
  3579.     /**
  3580.      * Landscape ticket item.
  3581.      * @type {!print_preview.ticket_items.Landscape}
  3582.      * @private
  3583.      */
  3584.     this.landscape_ = new print_preview.ticket_items.Landscape(
  3585.         this.capabilitiesHolder_, this.documentInfo_);
  3586.  
  3587.     /**
  3588.      * Page range ticket item.
  3589.      * @type {!print_preview.ticket_items.PageRange}
  3590.      * @private
  3591.      */
  3592.     this.pageRange_ =
  3593.         new print_preview.ticket_items.PageRange(this.documentInfo_);
  3594.  
  3595.     /**
  3596.      * Margins type ticket item.
  3597.      * @type {!print_preview.ticket_items.MarginsType}
  3598.      * @private
  3599.      */
  3600.     this.marginsType_ =
  3601.         new print_preview.ticket_items.MarginsType(this.documentInfo_);
  3602.  
  3603.     /**
  3604.      * Custom margins ticket item.
  3605.      * @type {!print_preview.ticket_items.CustomMargins}
  3606.      * @private
  3607.      */
  3608.     this.customMargins_ = new print_preview.ticket_items.CustomMargins(
  3609.         this.documentInfo_, this.measurementSystem_);
  3610.  
  3611.     /**
  3612.      * Header-footer ticket item.
  3613.      * @type {!print_preview.ticket_items.HeaderFooter}
  3614.      * @private
  3615.      */
  3616.     this.headerFooter_ = new print_preview.ticket_items.HeaderFooter(
  3617.         this.documentInfo_, this.marginsType_, this.customMargins_);
  3618.  
  3619.     /**
  3620.      * Fit-to-page ticket item.
  3621.      * @type {!print_preview.ticket_items.FitToPage}
  3622.      * @private
  3623.      */
  3624.     this.fitToPage_ = new print_preview.ticket_items.FitToPage(
  3625.         this.documentInfo_, this.destinationStore_);
  3626.  
  3627.     /**
  3628.      * Keeps track of event listeners for the print ticket store.
  3629.      * @type {!EventTracker}
  3630.      * @private
  3631.      */
  3632.     this.tracker_ = new EventTracker();
  3633.  
  3634.     this.addEventListeners_();
  3635.   };
  3636.  
  3637.   /**
  3638.    * Event types dispatched by the print ticket store.
  3639.    * @enum {string}
  3640.    */
  3641.   PrintTicketStore.EventType = {
  3642.     CAPABILITIES_CHANGE: 'print_preview.PrintTicketStore.CAPABILITIES_CHANGE',
  3643.     DOCUMENT_CHANGE: 'print_preview.PrintTicketStore.DOCUMENT_CHANGE',
  3644.     INITIALIZE: 'print_preview.PrintTicketStore.INITIALIZE',
  3645.     TICKET_CHANGE: 'print_preview.PrintTicketStore.TICKET_CHANGE'
  3646.   };
  3647.  
  3648.   PrintTicketStore.prototype = {
  3649.     __proto__: cr.EventTarget.prototype,
  3650.  
  3651.     /** @return {boolean} Whether the document is modifiable. */
  3652.     get isDocumentModifiable() {
  3653.       return this.documentInfo_.isModifiable;
  3654.     },
  3655.  
  3656.     /** @return {number} Number of pages in the document. */
  3657.     get pageCount() {
  3658.       return this.documentInfo_.pageCount;
  3659.     },
  3660.  
  3661.     /**
  3662.      * @param {number} pageCount New number of pages in the document.
  3663.      *     Dispatches a DOCUMENT_CHANGE event if the value changes.
  3664.      */
  3665.     updatePageCount: function(pageCount) {
  3666.       if (this.documentInfo_.pageCount != pageCount) {
  3667.         this.documentInfo_.pageCount = pageCount;
  3668.         cr.dispatchSimpleEvent(
  3669.             this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
  3670.       }
  3671.     },
  3672.  
  3673.     /**
  3674.      * @return {!print_preview.PrintableArea} Printable area of the document in
  3675.      *     points.
  3676.      */
  3677.     get printableArea() {
  3678.       return this.documentInfo_.printableArea;
  3679.     },
  3680.  
  3681.     /** @return {!print_preview.Size} Size of the document in points. */
  3682.     get pageSize() {
  3683.       return this.documentInfo_.pageSize;
  3684.     },
  3685.  
  3686.     /**
  3687.      * Updates a subset of fields of the print document relating to the format
  3688.      * of the page.
  3689.      * @param {!print_preview.PrintableArea} printableArea New printable area of
  3690.      *     the document in points. Dispatches a DOCUMENT_CHANGE event if the
  3691.      *     value changes.
  3692.      * @param {!print_preview.Size} pageSize New size of the document in points.
  3693.      *     Dispatches a DOCUMENT_CHANGE event if the value changes.
  3694.      * @param {boolean} documentHasCssMediaStyles Whether the document is styled
  3695.      *     with CSS media styles.
  3696.      * @param {!print_preview.Margins} margins Document margins in points.
  3697.      */
  3698.     updateDocumentPageInfo: function(
  3699.         printableArea, pageSize, documentHasCssMediaStyles, margins) {
  3700.       if (!this.documentInfo_.printableArea.equals(printableArea) ||
  3701.           !this.documentInfo_.pageSize.equals(pageSize) ||
  3702.           this.documentInfo_.hasCssMediaStyles != documentHasCssMediaStyles ||
  3703.           this.documentInfo_.margins == null ||
  3704.           !this.documentInfo_.margins.equals(margins)) {
  3705.         this.documentInfo_.printableArea = printableArea;
  3706.         this.documentInfo_.pageSize = pageSize;
  3707.         this.documentInfo_.hasCssMediaStyles = documentHasCssMediaStyles;
  3708.         this.documentInfo_.margins = margins;
  3709.         cr.dispatchSimpleEvent(
  3710.             this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
  3711.       }
  3712.     },
  3713.  
  3714.     /**
  3715.      * @return {!print_preview.MeasurementSystem} Measurement system of the
  3716.      *     local system.
  3717.      */
  3718.     get measurementSystem() {
  3719.       return this.measurementSystem_;
  3720.     },
  3721.  
  3722.     /**
  3723.      * @return {print_preview.Margins} Document margins of the currently
  3724.      *     generated preview.
  3725.      */
  3726.     getDocumentMargins: function() {
  3727.       return this.documentInfo_.margins;
  3728.     },
  3729.  
  3730.     /** @return {string} Title of the document. */
  3731.     getDocumentTitle: function() {
  3732.       return this.documentInfo_.title;
  3733.     },
  3734.  
  3735.     /**
  3736.      * Initializes the print ticket store. Dispatches an INITIALIZE event.
  3737.      * @param {boolean} isDocumentModifiable Whether the document to print is
  3738.      *     modifiable (i.e. can be re-flowed by Chromium).
  3739.      * @param {string} documentTitle Title of the document to print.
  3740.      * @param {string} thousandsDelimeter Delimeter of the thousands place.
  3741.      * @param {string} decimalDelimeter Delimeter of the decimal point.
  3742.      * @param {!print_preview.MeasurementSystem.UnitType} unitType Type of unit
  3743.      *     of the local measurement system.
  3744.      */
  3745.     init: function(
  3746.         isDocumentModifiable,
  3747.         documentTitle,
  3748.         thousandsDelimeter,
  3749.         decimalDelimeter,
  3750.         unitType) {
  3751.  
  3752.       this.documentInfo_.isModifiable = isDocumentModifiable;
  3753.       this.documentInfo_.title = documentTitle;
  3754.       this.measurementSystem_.setSystem(
  3755.           thousandsDelimeter, decimalDelimeter, unitType);
  3756.  
  3757.       // Initialize ticket with user's previous values.
  3758.       this.marginsType_.updateValue(this.appState_.marginsType);
  3759.       this.customMargins_.updateValue(this.appState_.customMargins);
  3760.       this.color_.updateValue(this.appState_.isColorEnabled);
  3761.       this.duplex_.updateValue(this.appState_.isDuplexEnabled);
  3762.       this.headerFooter_.updateValue(this.appState_.isHeaderFooterEnabled);
  3763.       this.landscape_.updateValue(this.appState_.isLandscapeEnabled);
  3764.       this.collate_.updateValue(this.appState_.isCollateEnabled);
  3765.     },
  3766.  
  3767.     /** @return {boolean} Whether the ticket store has the copies capability. */
  3768.     hasCopiesCapability: function() {
  3769.       return this.copies_.isCapabilityAvailable();
  3770.     },
  3771.  
  3772.     /**
  3773.      * @return {boolean} Whether the string representation of the copies value
  3774.      *     currently in the ticket store is valid.
  3775.      */
  3776.     isCopiesValid: function() {
  3777.       return this.copies_.isValid();
  3778.     },
  3779.  
  3780.     isCopiesValidForValue: function(value) {
  3781.       return this.copies_.wouldValueBeValid(value);
  3782.     },
  3783.  
  3784.     /** @return {number} Number of copies to print. */
  3785.     getCopies: function() {
  3786.       return this.copies_.getValueAsNumber();
  3787.     },
  3788.  
  3789.     /**
  3790.      * @return {string} String representation of the number of copies to print.
  3791.      */
  3792.     getCopiesStr: function() {
  3793.       return this.copies_.getValue();
  3794.     },
  3795.  
  3796.     /**
  3797.      * Updates the string representation of the number of copies to print.
  3798.      * Dispatches a TICKET_CHANGE event if the string value has changed.
  3799.      * @param {string} New string representation of the number of copies to
  3800.      *     print.
  3801.      */
  3802.     updateCopies: function(copies) {
  3803.       if (this.copies_.getValue() != copies) {
  3804.         this.copies_.updateValue(copies);
  3805.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  3806.       }
  3807.     },
  3808.  
  3809.     /** @return {boolean} Whether the ticket store has a collate capability. */
  3810.     hasCollateCapability: function() {
  3811.       return this.collate_.isCapabilityAvailable();
  3812.     },
  3813.  
  3814.     /** @return {boolean} Whether collate is enabled. */
  3815.     isCollateEnabled: function() {
  3816.       return this.collate_.getValue();
  3817.     },
  3818.  
  3819.     /**
  3820.      * Updates whether collate is enabled. Dispatches a TICKET_CHANGE event if
  3821.      * collate has changed.
  3822.      * @param {boolean} isCollateEnabled Whether collate is enabled.
  3823.      */
  3824.     updateCollate: function(isCollateEnabled) {
  3825.       if (this.collate_.getValue() != isCollateEnabled) {
  3826.         this.collate_.updateValue(isCollateEnabled);
  3827.         this.appState_.persistIsCollateEnabled(isCollateEnabled);
  3828.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  3829.       }
  3830.     },
  3831.  
  3832.     /**
  3833.      * @return {boolean} Whether the ticket store has color printing capability.
  3834.      */
  3835.     hasColorCapability: function() {
  3836.       return this.color_.isCapabilityAvailable();
  3837.     },
  3838.  
  3839.     /** @return {boolean} Whether color printing is enabled. */
  3840.     isColorEnabled: function() {
  3841.       return this.color_.getValue();
  3842.     },
  3843.  
  3844.     /**
  3845.      * Updates whether color printing is enabled. Dispatches a TICKET_CHANGE if
  3846.      * color has changed.
  3847.      * @param {boolean} isColorEnabled Whether the color printing is enabled.
  3848.      */
  3849.     updateColor: function(isColorEnabled) {
  3850.       if (this.color_.getValue() != isColorEnabled) {
  3851.         this.color_.updateValue(isColorEnabled);
  3852.         this.appState_.persistIsColorEnabled(isColorEnabled);
  3853.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  3854.       }
  3855.     },
  3856.  
  3857.     /** @return {boolean} Whether the header-footer capability is available. */
  3858.     hasHeaderFooterCapability: function() {
  3859.       return this.headerFooter_.isCapabilityAvailable();
  3860.     },
  3861.  
  3862.     /** @return {boolean} Whether the header-footer setting is enabled. */
  3863.     isHeaderFooterEnabled: function() {
  3864.       return this.headerFooter_.getValue();
  3865.     },
  3866.  
  3867.     /**
  3868.      * Updates the whether the header-footer setting is enabled. Dispatches a
  3869.      * TICKET_CHANGE event if the setting changed.
  3870.      * @param {boolean} isHeaderFooterEnabled Whether the header-footer setting
  3871.      *     is enabled.
  3872.      */
  3873.     updateHeaderFooter: function(isHeaderFooterEnabled) {
  3874.       if (this.headerFooter_.getValue() != isHeaderFooterEnabled) {
  3875.         this.headerFooter_.updateValue(isHeaderFooterEnabled);
  3876.         this.appState_.persistIsHeaderFooterEnabled(isHeaderFooterEnabled);
  3877.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  3878.       }
  3879.     },
  3880.  
  3881.     /**
  3882.      * @return {boolean} Whether the page orientation capability is available.
  3883.      */
  3884.     hasOrientationCapability: function() {
  3885.       return this.landscape_.isCapabilityAvailable();
  3886.     },
  3887.  
  3888.     /**
  3889.      * @return {boolean} Whether the document should be printed in landscape.
  3890.      */
  3891.     isLandscapeEnabled: function() {
  3892.       return this.landscape_.getValue();
  3893.     },
  3894.  
  3895.     /**
  3896.      * Updates whether the document should be printed in landscape. Dispatches
  3897.      * a TICKET_CHANGE event if the setting changes.
  3898.      * @param {boolean} isLandscapeEnabled Whether the document should be
  3899.      *     printed in landscape.
  3900.      */
  3901.     updateOrientation: function(isLandscapeEnabled) {
  3902.       if (this.landscape_.getValue() != isLandscapeEnabled) {
  3903.         this.landscape_.updateValue(isLandscapeEnabled);
  3904.         // Reset the user set margins.
  3905.         this.marginsType_.updateValue(
  3906.             print_preview.ticket_items.MarginsType.Value.DEFAULT);
  3907.         this.customMargins_.updateValue(null);
  3908.         this.appState_.persistMarginsType(
  3909.             print_preview.ticket_items.MarginsType.Value.DEFAULT);
  3910.         this.appState_.persistCustomMargins(null);
  3911.         this.appState_.persistIsLandscapeEnabled(isLandscapeEnabled);
  3912.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  3913.       }
  3914.     },
  3915.  
  3916.     /** @return {boolean} Whether the duplexing capability is available. */
  3917.     hasDuplexCapability: function() {
  3918.       return this.duplex_.isCapabilityAvailable();
  3919.     },
  3920.  
  3921.     /** @return {boolean} Whether the document should be printed in duplex. */
  3922.     isDuplexEnabled: function() {
  3923.       return this.duplex_.getValue();
  3924.     },
  3925.  
  3926.     /**
  3927.      * Updates the duplexing setting. Dispatches a TICKET_CHANGE event if the
  3928.      * value changes.
  3929.      * @param {boolean} isDuplexEnabled Whether the document should be printed
  3930.      *     in duplex.
  3931.      */
  3932.     updateDuplex: function(isDuplexEnabled) {
  3933.       if (this.duplex_.getValue() != isDuplexEnabled) {
  3934.         this.duplex_.updateValue(isDuplexEnabled);
  3935.         this.appState_.persistIsDuplexEnabled(isDuplexEnabled);
  3936.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  3937.       }
  3938.     },
  3939.  
  3940.     /** @return {boolean} Whether the margins capability is available. */
  3941.     hasMarginsCapability: function() {
  3942.       return this.marginsType_.isCapabilityAvailable();
  3943.     },
  3944.  
  3945.     /**
  3946.      * @return {!print_preview.ticket_items.MarginsType.Value} Type of
  3947.      *     predefined margins.
  3948.      */
  3949.     getMarginsType: function() {
  3950.       return this.marginsType_.getValue();
  3951.     },
  3952.  
  3953.     /**
  3954.      * Updates the type of predefined margins. Dispatches a TICKET_CHANGE event
  3955.      * if the margins type changes.
  3956.      * @param {print_preview.ticket_items.MarginsType.Value} marginsType Type of
  3957.      *     predefined margins.
  3958.      */
  3959.     updateMarginsType: function(marginsType) {
  3960.       if (this.marginsType_.getValue() != marginsType) {
  3961.         this.marginsType_.updateValue(marginsType);
  3962.         this.appState_.persistMarginsType(marginsType);
  3963.         if (marginsType ==
  3964.             print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  3965.           // If CUSTOM, set the value of the custom margins so that it won't be
  3966.           // overridden by the default value.
  3967.           this.customMargins_.updateValue(this.customMargins_.getValue());
  3968.           this.appState_.persistCustomMargins(this.customMargins_.getValue());
  3969.         }
  3970.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  3971.       }
  3972.     },
  3973.  
  3974.     /** @return {boolean} Whether all of the custom margins are valid. */
  3975.     isCustomMarginsValid: function() {
  3976.       return this.customMargins_.isValid();
  3977.     },
  3978.  
  3979.     /**
  3980.      * @return {!print_preview.Margins} Custom margins of the document in
  3981.      *     points.
  3982.      */
  3983.     getCustomMargins: function() {
  3984.       return this.customMargins_.getValue();
  3985.     },
  3986.  
  3987.     /**
  3988.      * @param {!print_preview.ticket_items.CustomMargins.Orientation}
  3989.      *     orientation Specifies the margin to get the maximum value for.
  3990.      * @return {number} Maximum value in points of the specified margin.
  3991.      */
  3992.     getCustomMarginMax: function(orientation) {
  3993.       return this.customMargins_.getMarginMax(orientation);
  3994.     },
  3995.  
  3996.     /**
  3997.      * Updates the custom margins of the document. Dispatches a TICKET_CHANGE
  3998.      * event if the margins have changed.
  3999.      * @param {!print_preview.Margins} margins New document page margins in
  4000.      *     points.
  4001.      */
  4002.     updateCustomMargins: function(margins) {
  4003.       if (!this.isCustomMarginsValid() ||
  4004.           !margins.equals(this.getCustomMargins())) {
  4005.         this.customMargins_.updateValue(margins);
  4006.         this.appState_.persistCustomMargins(margins);
  4007.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  4008.       }
  4009.     },
  4010.  
  4011.     /**
  4012.      * Updates a single custom margin's value in points.
  4013.      * @param {!print_preview.ticket_items.CustomMargins.Orientation}
  4014.      *     orientation Specifies the margin to update.
  4015.      * @param {number} value Updated margin in points.
  4016.      */
  4017.     updateCustomMargin: function(orientation, value) {
  4018.       if (this.customMargins_.getValue().get(orientation) != value) {
  4019.         this.customMargins_.updateMargin(orientation, value);
  4020.         this.appState_.persistCustomMargins(this.customMargins_.getValue());
  4021.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  4022.       }
  4023.     },
  4024.  
  4025.     /** @return {boolean} Whether the page range capability is available. */
  4026.     hasPageRangeCapability: function() {
  4027.       return this.pageRange_.isCapabilityAvailable();
  4028.     },
  4029.  
  4030.     /**
  4031.      * @return {boolean} Whether the current page range string is defines a
  4032.      *     valid page number set.
  4033.      */
  4034.     isPageRangeValid: function() {
  4035.       return this.pageRange_.isValid();
  4036.     },
  4037.  
  4038.     /** @return {string} String representation of the page range. */
  4039.     getPageRangeStr: function() {
  4040.       return this.pageRange_.getValue();
  4041.     },
  4042.  
  4043.     /**
  4044.      * @return {!print_preview.PageNumberSet} Page number set specified by the
  4045.      *     string representation of the page range string.
  4046.      */
  4047.     getPageNumberSet: function() {
  4048.       return this.pageRange_.getPageNumberSet();
  4049.     },
  4050.  
  4051.     /**
  4052.      * Updates the page range string. Dispatches a TICKET_CHANGE if the string
  4053.      * changed.
  4054.      * @param {string} pageRangeStr New page range string.
  4055.      */
  4056.     updatePageRange: function(pageRangeStr) {
  4057.       if (this.pageRange_.getValue() != pageRangeStr) {
  4058.         this.pageRange_.updateValue(pageRangeStr);
  4059.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  4060.       }
  4061.     },
  4062.  
  4063.     /** @return {boolean} Whether the fit-to-page capability is available. */
  4064.     hasFitToPageCapability: function() {
  4065.       return this.fitToPage_.isCapabilityAvailable();
  4066.     },
  4067.  
  4068.     /** @return {boolean} Whether the fit-to-page capability is enabled. */
  4069.     isFitToPageEnabled: function() {
  4070.       return this.fitToPage_.getValue();
  4071.     },
  4072.  
  4073.     /**
  4074.      * @param {boolean} isFitToPageEnabled Whether to enable the fit-to-page
  4075.      *     capability.
  4076.      */
  4077.     updateFitToPage: function(isFitToPageEnabled) {
  4078.       if (this.fitToPage_.getValue() != isFitToPageEnabled) {
  4079.         this.fitToPage_.updateValue(isFitToPageEnabled);
  4080.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE);
  4081.       }
  4082.     },
  4083.  
  4084.     /**
  4085.      * @return {boolean} {@code true} if the stored print ticket is valid,
  4086.      *     {@code false} otherwise.
  4087.      */
  4088.     isTicketValid: function() {
  4089.       return this.isTicketValidForPreview() &&
  4090.           (!this.hasPageRangeCapability() || this.isPageRangeValid());
  4091.     },
  4092.  
  4093.     /** @return {boolean} Whether the ticket is valid for preview generation. */
  4094.     isTicketValidForPreview: function() {
  4095.       return (!this.hasCopiesCapability() || this.isCopiesValid()) &&
  4096.           (!this.hasMarginsCapability() ||
  4097.               this.getMarginsType() !=
  4098.                   print_preview.ticket_items.MarginsType.Value.CUSTOM ||
  4099.               this.isCustomMarginsValid());
  4100.     },
  4101.  
  4102.     /**
  4103.      * Adds event listeners for the print ticket store.
  4104.      * @private
  4105.      */
  4106.     addEventListeners_: function() {
  4107.       this.tracker_.add(
  4108.           this.destinationStore_,
  4109.           print_preview.DestinationStore.EventType.
  4110.               SELECTED_DESTINATION_CAPABILITIES_READY,
  4111.           this.onSelectedDestinationCapabilitiesReady_.bind(this));
  4112.     },
  4113.  
  4114.     /**
  4115.      * Called when the capabilities of the selected destination are ready.
  4116.      * @private
  4117.      */
  4118.     onSelectedDestinationCapabilitiesReady_: function() {
  4119.       var caps = this.destinationStore_.selectedDestination.capabilities;
  4120.       var isFirstUpdate = this.capabilitiesHolder_.get() == null;
  4121.       this.capabilitiesHolder_.set(caps);
  4122.       if (isFirstUpdate) {
  4123.         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.INITIALIZE);
  4124.       } else {
  4125.         // Reset user selection for certain ticket items.
  4126.         this.customMargins_.updateValue(null);
  4127.         this.appState_.persistCustomMargins(null);
  4128.  
  4129.         if (this.marginsType_.getValue() ==
  4130.             print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  4131.           this.marginsType_.updateValue(
  4132.               print_preview.ticket_items.MarginsType.Value.DEFAULT);
  4133.           this.appState_.persistMarginsType(
  4134.               print_preview.ticket_items.MarginsType.Value.DEFAULT);
  4135.         }
  4136.         cr.dispatchSimpleEvent(
  4137.             this, PrintTicketStore.EventType.CAPABILITIES_CHANGE);
  4138.       }
  4139.     }
  4140.   };
  4141.  
  4142.   // Export
  4143.   return {
  4144.     PrintTicketStore: PrintTicketStore
  4145.   };
  4146. });
  4147.  
  4148. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4149. // Use of this source code is governed by a BSD-style license that can be
  4150. // found in the LICENSE file.
  4151.  
  4152. cr.define('print_preview', function() {
  4153.   'use strict';
  4154.  
  4155.   /**
  4156.    * Immutable two dimensional point in space. The units of the dimensions are
  4157.    * undefined.
  4158.    * @param {number} x X-dimension of the point.
  4159.    * @param {number} y Y-dimension of the point.
  4160.    * @constructor
  4161.    */
  4162.   function Coordinate2d(x, y) {
  4163.     /**
  4164.      * X-dimension of the point.
  4165.      * @type {number}
  4166.      * @private
  4167.      */
  4168.     this.x_ = x;
  4169.  
  4170.     /**
  4171.      * Y-dimension of the point.
  4172.      * @type {number}
  4173.      * @private
  4174.      */
  4175.     this.y_ = y;
  4176.   };
  4177.  
  4178.   Coordinate2d.prototype = {
  4179.     /** @return {number} X-dimension of the point. */
  4180.     get x() {
  4181.       return this.x_;
  4182.     },
  4183.  
  4184.     /** @return {number} Y-dimension of the point. */
  4185.     get y() {
  4186.       return this.y_;
  4187.     },
  4188.  
  4189.     /**
  4190.      * @param {number} x Amount to translate in the X dimension.
  4191.      * @param {number} y Amount to translate in the Y dimension.
  4192.      * @return {!print_preview.Coordinate2d} A new two-dimensional point
  4193.      *     translated along the X and Y dimensions.
  4194.      */
  4195.     translate: function(x, y) {
  4196.       return new Coordinate2d(this.x_ + x, this.y_ + y);
  4197.     },
  4198.  
  4199.     /**
  4200.      * @param {number} factor Amount to scale the X and Y dimensions.
  4201.      * @return {!print_preview.Coordinate2d} A new two-dimensional point scaled
  4202.      *     by the given factor.
  4203.      */
  4204.     scale: function(factor) {
  4205.       return new Coordinate2d(this.x_ * factor, this.y_ * factor);
  4206.     },
  4207.  
  4208.     /**
  4209.      * @param {print_preview.Coordinate2d} other The point to compare against.
  4210.      * @return {boolean} Whether another point is equal to this one.
  4211.      */
  4212.     equals: function(other) {
  4213.       return other != null &&
  4214.           this.x_ == other.x_ &&
  4215.           this.y_ == other.y_;
  4216.     }
  4217.   };
  4218.  
  4219.   // Export
  4220.   return {
  4221.     Coordinate2d: Coordinate2d
  4222.   };
  4223. });
  4224.  
  4225. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4226. // Use of this source code is governed by a BSD-style license that can be
  4227. // found in the LICENSE file.
  4228.  
  4229. cr.define('print_preview', function() {
  4230.   'use strict';
  4231.  
  4232.   /**
  4233.    * Immutable two-dimensional size.
  4234.    * @param {number} width Width of the size.
  4235.    * @param {number} height Height of the size.
  4236.    * @constructor
  4237.    */
  4238.   function Size(width, height) {
  4239.     /**
  4240.      * Width of the size.
  4241.      * @type {number}
  4242.      * @private
  4243.      */
  4244.     this.width_ = width;
  4245.  
  4246.     /**
  4247.      * Height of the size.
  4248.      * @type {number}
  4249.      * @private
  4250.      */
  4251.     this.height_ = height;
  4252.   };
  4253.  
  4254.   Size.prototype = {
  4255.     /** @return {number} Width of the size. */
  4256.     get width() {
  4257.       return this.width_;
  4258.     },
  4259.  
  4260.     /** @return {number} Height of the size. */
  4261.     get height() {
  4262.       return this.height_;
  4263.     },
  4264.  
  4265.     /**
  4266.      * @param {print_preview.Size} other Other size object to compare against.
  4267.      * @return {boolean} Whether this size object is equal to another.
  4268.      */
  4269.     equals: function(other) {
  4270.       return other != null &&
  4271.           this.width_ == other.width_ &&
  4272.           this.height_ == other.height_;
  4273.     }
  4274.   };
  4275.  
  4276.   // Export
  4277.   return {
  4278.     Size: Size
  4279.   };
  4280. });
  4281.  
  4282. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4283. // Use of this source code is governed by a BSD-style license that can be
  4284. // found in the LICENSE file.
  4285.  
  4286. cr.define('print_preview', function() {
  4287.   'use strict';
  4288.  
  4289.   /**
  4290.    * Mutable reference to a capabilities object.
  4291.    * @constructor
  4292.    */
  4293.   function CapabilitiesHolder() {
  4294.     /**
  4295.      * Reference to the capabilities object.
  4296.      * @type {print_preview.ChromiumCapabilities}
  4297.      * @private
  4298.      */
  4299.     this.capabilities_ = null;
  4300.   };
  4301.  
  4302.   CapabilitiesHolder.prototype = {
  4303.     /**
  4304.      * @return {print_preview.ChromiumCapabilities} The instance held by the
  4305.      *     holder.
  4306.      */
  4307.     get: function() {
  4308.       return this.capabilities_;
  4309.     },
  4310.  
  4311.     /**
  4312.      * @param {!print_preview.ChromiumCapabilities} New instance to put into the
  4313.      *     holder.
  4314.      */
  4315.     set: function(capabilities) {
  4316.       this.capabilities_ = capabilities;
  4317.     }
  4318.   };
  4319.  
  4320.   // Export
  4321.   return {
  4322.     CapabilitiesHolder: CapabilitiesHolder
  4323.   };
  4324. });
  4325.  
  4326. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4327. // Use of this source code is governed by a BSD-style license that can be
  4328. // found in the LICENSE file.
  4329.  
  4330. cr.define('print_preview', function() {
  4331.   'use strict';
  4332.  
  4333.   /**
  4334.    * Repository which stores information about the user. Events are dispatched
  4335.    * when the information changes.
  4336.    * @constructor
  4337.    * @extends {cr.EventTarget}
  4338.    */
  4339.   function UserInfo() {
  4340.     cr.EventTarget.call(this);
  4341.  
  4342.     /**
  4343.      * Tracker used to keep track of event listeners.
  4344.      * @type {!EventTracker}
  4345.      * @private
  4346.      */
  4347.     this.tracker_ = new EventTracker();
  4348.  
  4349.     /**
  4350.      * Google Cloud Print interface to listen to for events. Currently, through
  4351.      * Google Cloud Print is how we determine the info of the logged in user.
  4352.      * @type {cloudprint.CloudPrintInterface}
  4353.      * @private
  4354.      */
  4355.     this.cloudPrintInterface_ = null;
  4356.  
  4357.     /**
  4358.      * Email address of the logged in user or {@code null} if no user is logged
  4359.      * in.
  4360.      * @type {?string}
  4361.      * @private
  4362.      */
  4363.     this.userEmail_ = null;
  4364.   };
  4365.  
  4366.   /**
  4367.    * Enumeration of event types dispatched by the user info.
  4368.    * @enum {string}
  4369.    */
  4370.   UserInfo.EventType = {
  4371.     EMIAL_CHANGE: 'print_preview.UserInfo.EMAIL_CHANGE'
  4372.   };
  4373.  
  4374.   UserInfo.prototype = {
  4375.     __proto__: cr.EventTarget.prototype,
  4376.  
  4377.     /**
  4378.      * @return {?string} Email address of the logged in user or {@code null} if
  4379.      *     no user is logged.
  4380.      */
  4381.     getUserEmail: function() {
  4382.       return this.userEmail_;
  4383.     },
  4384.  
  4385.     /**
  4386.      * @param {!cloudprint.CloudPrintInterface} cloudPrintInterface Interface
  4387.      *     to Google Cloud Print that the print preview uses.
  4388.      */
  4389.     setCloudPrintInterface: function(cloudPrintInterface) {
  4390.       this.cloudPrintInterface_ = cloudPrintInterface;
  4391.       this.tracker_.add(
  4392.           this.cloudPrintInterface_,
  4393.           cloudprint.CloudPrintInterface.EventType.SEARCH_DONE,
  4394.           this.onCloudPrintSearchDone_.bind(this));
  4395.     },
  4396.  
  4397.     /** Removes all event listeners. */
  4398.     removeEventListeners: function() {
  4399.       this.tracker_.removeAll();
  4400.     },
  4401.  
  4402.     /**
  4403.      * Called when a Google Cloud Print printer search completes. Updates user
  4404.      * information.
  4405.      * @type {cr.Event} event Contains information about the logged in user.
  4406.      * @private
  4407.      */
  4408.     onCloudPrintSearchDone_: function(event) {
  4409.       this.userEmail_ = event.email;
  4410.       cr.dispatchSimpleEvent(this, UserInfo.EventType.EMAIL_CHANGE);
  4411.     }
  4412.   };
  4413.  
  4414.   return {
  4415.     UserInfo: UserInfo
  4416.   };
  4417. });
  4418.  
  4419. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4420. // Use of this source code is governed by a BSD-style license that can be
  4421. // found in the LICENSE file.
  4422.  
  4423. cr.define('print_preview', function() {
  4424.   'use strict';
  4425.  
  4426.   /**
  4427.    * Object used to get and persist the print preview application state.
  4428.    * @constructor
  4429.    */
  4430.   function AppState() {
  4431.     /**
  4432.      * ID of the selected destination.
  4433.      * @type {?string}
  4434.      * @private
  4435.      */
  4436.     this.selectedDestinationId_ = null;
  4437.  
  4438.     /**
  4439.      * Whether the selected destination is a local destination.
  4440.      * @type {?boolean}
  4441.      * @private
  4442.      */
  4443.     this.isSelectedDestinationLocal_ = null;
  4444.  
  4445.     /**
  4446.      * Whether the GCP promotion has been dismissed.
  4447.      * @type {boolean}
  4448.      * @private
  4449.      */
  4450.     this.isGcpPromoDismissed_ = true;
  4451.  
  4452.     /**
  4453.      * Margins type.
  4454.      * @type {print_preview.ticket_items.MarginsType.Value}
  4455.      * @private
  4456.      */
  4457.     this.marginsType_ = null;
  4458.  
  4459.     /**
  4460.      * Custom margins.
  4461.      * @type {print_preview.Margins}
  4462.      * @private
  4463.      */
  4464.     this.customMargins_ = null;
  4465.  
  4466.     /**
  4467.      * Whether the color option is enabled.
  4468.      * @type {?boolean}
  4469.      * @private
  4470.      */
  4471.     this.isColorEnabled_ = null;
  4472.  
  4473.     /**
  4474.      * Whether duplex printing is enabled.
  4475.      * @type {?boolean}
  4476.      * @private
  4477.      */
  4478.     this.isDuplexEnabled_ = null;
  4479.  
  4480.     /**
  4481.      * Whether the header-footer option is enabled.
  4482.      * @type {?boolean}
  4483.      * @private
  4484.      */
  4485.     this.isHeaderFooterEnabled_ = null;
  4486.  
  4487.     /**
  4488.      * Whether landscape page orientation is selected.
  4489.      * @type {?boolean}
  4490.      * @private
  4491.      */
  4492.     this.isLandscapeEnabled_ = null;
  4493.  
  4494.     /**
  4495.      * Whether printing collation is enabled.
  4496.      * @type {?boolean}
  4497.      * @private
  4498.      */
  4499.     this.isCollateEnabled_ = null;
  4500.   };
  4501.  
  4502.   /**
  4503.    * Current version of the app state. This value helps to understand how to
  4504.    * parse earlier versions of the app state.
  4505.    * @type {number}
  4506.    * @const
  4507.    * @private
  4508.    */
  4509.   AppState.VERSION_ = 2;
  4510.  
  4511.   /**
  4512.    * Enumeration of field names for serialized app state.
  4513.    * @enum {string}
  4514.    * @private
  4515.    */
  4516.   AppState.Field_ = {
  4517.     VERSION: 'version',
  4518.     SELECTED_DESTINATION_ID: 'selectedDestinationId',
  4519.     IS_SELECTED_DESTINATION_LOCAL: 'isSelectedDestinationLocal',
  4520.     IS_GCP_PROMO_DISMISSED: 'isGcpPromoDismissed',
  4521.     MARGINS_TYPE: 'marginsType',
  4522.     CUSTOM_MARGINS: 'customMargins',
  4523.     IS_COLOR_ENABLED: 'isColorEnabled',
  4524.     IS_DUPLEX_ENABLED: 'isDuplexEnabled',
  4525.     IS_HEADER_FOOTER_ENABLED: 'isHeaderFooterEnabled',
  4526.     IS_LANDSCAPE_ENABLED: 'isLandscapeEnabled',
  4527.     IS_COLLATE_ENABLED: 'isCollateEnabled'
  4528.   };
  4529.  
  4530.   /**
  4531.    * Name of C++ layer function to persist app state.
  4532.    * @type {string}
  4533.    * @const
  4534.    * @private
  4535.    */
  4536.   AppState.NATIVE_FUNCTION_NAME_ = 'saveAppState';
  4537.  
  4538.   AppState.prototype = {
  4539.     /** @return {?string} ID of the selected destination. */
  4540.     get selectedDestinationId() {
  4541.       return this.selectedDestinationId_;
  4542.     },
  4543.  
  4544.     /** @return {?boolean} Whether the selected destination is local. */
  4545.     get isSelectedDestinationLocal() {
  4546.       return this.isSelectedDestinationLocal_;
  4547.     },
  4548.  
  4549.     /** @return {boolean} Whether the GCP promotion has been dismissed. */
  4550.     get isGcpPromoDismissed() {
  4551.       return this.isGcpPromoDismissed_;
  4552.     },
  4553.  
  4554.     /** @return {print_preview.ticket_items.MarginsType.Value} Margins type. */
  4555.     get marginsType() {
  4556.       return this.marginsType_;
  4557.     },
  4558.  
  4559.     /** @return {print_preview.Margins} Custom margins. */
  4560.     get customMargins() {
  4561.       return this.customMargins_;
  4562.     },
  4563.  
  4564.     /** @return {?boolean} Whether the color option is enabled. */
  4565.     get isColorEnabled() {
  4566.       return this.isColorEnabled_;
  4567.     },
  4568.  
  4569.     /** @return {?boolean} Whether duplex printing is enabled. */
  4570.     get isDuplexEnabled() {
  4571.       return this.isDuplexEnabled_;
  4572.     },
  4573.  
  4574.     /** @return {?boolean} Whether the header-footer option is enabled. */
  4575.     get isHeaderFooterEnabled() {
  4576.       return this.isHeaderFooterEnabled_;
  4577.     },
  4578.  
  4579.     /** @return {?boolean} Whether landscape page orientation is selected. */
  4580.     get isLandscapeEnabled() {
  4581.       return this.isLandscapeEnabled_;
  4582.     },
  4583.  
  4584.     /** @return {?boolean} Whether printing collation is enabled. */
  4585.     get isCollateEnabled() {
  4586.       return this.isCollateEnabled_;
  4587.     },
  4588.  
  4589.     /**
  4590.      * Initializes the app state from a serialized string returned by the native
  4591.      * layer.
  4592.      * @param {?string} serializedAppStateStr Serialized string representation
  4593.      *     of the app state.
  4594.      */
  4595.     init: function(serializedAppStateStr) {
  4596.       if (!serializedAppStateStr) {
  4597.         // Set some state defaults.
  4598.         this.isGcpPromoDismissed_ = false;
  4599.         return;
  4600.       }
  4601.  
  4602.       var state = JSON.parse(serializedAppStateStr);
  4603.       if (state[AppState.Field_.VERSION] == 2) {
  4604.         this.selectedDestinationId_ =
  4605.             state[AppState.Field_.SELECTED_DESTINATION_ID] || null;
  4606.         if (state.hasOwnProperty(
  4607.                 AppState.Field_.IS_SELECTED_DESTINATION_LOCAL)) {
  4608.           this.isSelectedDestinationLocal_ =
  4609.               state[AppState.Field_.IS_SELECTED_DESTINATION_LOCAL];
  4610.         }
  4611.         this.isGcpPromoDismissed_ =
  4612.             state[AppState.Field_.IS_GCP_PROMO_DISMISSED] || false;
  4613.         if (state.hasOwnProperty(AppState.Field_.MARGINS_TYPE)) {
  4614.           this.marginsType_ = state[AppState.Field_.MARGINS_TYPE];
  4615.         }
  4616.         if (state[AppState.Field_.CUSTOM_MARGINS]) {
  4617.           this.customMargins_ = print_preview.Margins.parse(
  4618.               state[AppState.Field_.CUSTOM_MARGINS]);
  4619.         }
  4620.         if (state.hasOwnProperty(AppState.Field_.IS_COLOR_ENABLED)) {
  4621.           this.isColorEnabled_ = state[AppState.Field_.IS_COLOR_ENABLED];
  4622.         }
  4623.         if (state.hasOwnProperty(AppState.Field_.IS_DUPLEX_ENABLED)) {
  4624.           this.isDuplexEnabled_ = state[AppState.Field_.IS_DUPLEX_ENABLED];
  4625.         }
  4626.         if (state.hasOwnProperty(AppState.Field_.IS_HEADER_FOOTER_ENABLED)) {
  4627.           this.isHeaderFooterEnabled_ =
  4628.               state[AppState.Field_.IS_HEADER_FOOTER_ENABLED];
  4629.         }
  4630.         if (state.hasOwnProperty(AppState.Field_.IS_LANDSCAPE_ENABLED)) {
  4631.           this.isLandscapeEnabled_ =
  4632.               state[AppState.Field_.IS_LANDSCAPE_ENABLED];
  4633.         }
  4634.         if (state.hasOwnProperty(AppState.Field_.IS_COLLATE_ENABLED)) {
  4635.           this.isCollateEnabled_ = state[AppState.Field_.IS_COLLATE_ENABLED];
  4636.         }
  4637.       }
  4638.     },
  4639.  
  4640.     /**
  4641.      * Persists the selected destination.
  4642.      * @param {!print_preview.Destination} dest Destination to persist.
  4643.      */
  4644.     persistSelectedDestination: function(dest) {
  4645.       this.selectedDestinationId_ = dest.id;
  4646.       this.isSelectedDestinationLocal_ = dest.isLocal;
  4647.       this.persist_();
  4648.     },
  4649.  
  4650.    /**
  4651.     * Persists whether the GCP promotion has been dismissed.
  4652.     * @param {boolean} isGcpPromoDismissed Whether the GCP promotion has been
  4653.     *     dismissed.
  4654.     */
  4655.    persistIsGcpPromoDismissed: function(isGcpPromoDismissed) {
  4656.      this.isGcpPromoDismissed_ = isGcpPromoDismissed;
  4657.      this.persist_();
  4658.    },
  4659.  
  4660.     /**
  4661.      * Persists the margins type.
  4662.      * @param {print_preview.ticket_items.MarginsType.Value} marginsType Margins
  4663.      *     type.
  4664.      */
  4665.     persistMarginsType: function(marginsType) {
  4666.       this.marginsType_ = marginsType;
  4667.       this.persist_();
  4668.     },
  4669.  
  4670.     /**
  4671.      * Persists custom margins.
  4672.      * @param {print_preview.Margins} customMargins Custom margins.
  4673.      */
  4674.     persistCustomMargins: function(customMargins) {
  4675.       this.customMargins_ = customMargins;
  4676.       this.persist_();
  4677.     },
  4678.  
  4679.     /**
  4680.      * Persists whether color printing is enabled.
  4681.      * @param {?boolean} isColorEnabled Whether color printing is enabled.
  4682.      */
  4683.     persistIsColorEnabled: function(isColorEnabled) {
  4684.       this.isColorEnabled_ = isColorEnabled;
  4685.       this.persist_();
  4686.     },
  4687.  
  4688.     /**
  4689.      * Persists whether duplexing is enabled.
  4690.      * @param {?boolean} isDuplexEnabled Whether duplex printing is enabled.
  4691.      */
  4692.     persistIsDuplexEnabled: function(isDuplexEnabled) {
  4693.       this.isDuplexEnabled_ = isDuplexEnabled;
  4694.       this.persist_();
  4695.     },
  4696.  
  4697.     /**
  4698.      * Persists whether header-footer is enabled.
  4699.      * @param {?boolean} Whether header-footer is enabled.
  4700.      */
  4701.     persistIsHeaderFooterEnabled: function(isHeaderFooterEnabled) {
  4702.       this.isHeaderFooterEnabled_ = isHeaderFooterEnabled;
  4703.       this.persist_();
  4704.     },
  4705.  
  4706.     /**
  4707.      * Persists whether landscape printing is enabled.
  4708.      * @param {?boolean} isLandscapeEnabled landscape printing is enabled.
  4709.      */
  4710.     persistIsLandscapeEnabled: function(isLandscapeEnabled) {
  4711.       this.isLandscapeEnabled_ = isLandscapeEnabled;
  4712.       this.persist_();
  4713.     },
  4714.  
  4715.     /**
  4716.      * Persists whether printing collation is enabled.
  4717.      * @param {?boolean} isCollateEnabled Whether printing collation is enabled.
  4718.      */
  4719.     persistIsCollateEnabled: function(isCollateEnabled) {
  4720.       this.isCollateEnabled_ = isCollateEnabled;
  4721.       this.persist_();
  4722.     },
  4723.  
  4724.     /**
  4725.      * Calls into the native layer to persist the application state.
  4726.      * @private
  4727.      */
  4728.     persist_: function() {
  4729.       var obj = {};
  4730.       obj[AppState.Field_.VERSION] = AppState.VERSION_;
  4731.       obj[AppState.Field_.SELECTED_DESTINATION_ID] =
  4732.           this.selectedDestinationId_;
  4733.       obj[AppState.Field_.IS_SELECTED_DESTINATION_LOCAL] =
  4734.           this.isSelectedDestinationLocal_;
  4735.       obj[AppState.Field_.IS_GCP_PROMO_DISMISSED] = this.isGcpPromoDismissed_;
  4736.       obj[AppState.Field_.MARGINS_TYPE] = this.marginsType_;
  4737.       if (this.customMargins_) {
  4738.         obj[AppState.Field_.CUSTOM_MARGINS] = this.customMargins_.serialize();
  4739.       }
  4740.       obj[AppState.Field_.IS_COLOR_ENABLED] = this.isColorEnabled_;
  4741.       obj[AppState.Field_.IS_DUPLEX_ENABLED] = this.isDuplexEnabled_;
  4742.       obj[AppState.Field_.IS_HEADER_FOOTER_ENABLED] =
  4743.           this.isHeaderFooterEnabled_;
  4744.       obj[AppState.Field_.IS_LANDSCAPE_ENABLED] = this.isLandscapeEnabled_;
  4745.       obj[AppState.Field_.IS_COLLATE_ENABLED] = this.isCollateEnabled_;
  4746.       chrome.send(AppState.NATIVE_FUNCTION_NAME_, [JSON.stringify(obj)]);
  4747.     }
  4748.   };
  4749.  
  4750.   return {
  4751.     AppState: AppState
  4752.   };
  4753. });
  4754.  
  4755.  
  4756. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4757. // Use of this source code is governed by a BSD-style license that can be
  4758. // found in the LICENSE file.
  4759.  
  4760. cr.define('print_preview.ticket_items', function() {
  4761.   'use strict';
  4762.  
  4763.   /**
  4764.    * An object that represents a user modifiable item in a print ticket. Each
  4765.    * ticket item has a value which can be set by the user. Ticket items can also
  4766.    * be unavailable for modifying if the print destination doesn't support it or
  4767.    * if other ticket item constraints are not met.
  4768.    * @constructor
  4769.    */
  4770.   function TicketItem() {
  4771.     /**
  4772.      * Backing store of the print ticket item.
  4773.      * @type {Object}
  4774.      * @private
  4775.      */
  4776.     this.value_ = null;
  4777.   };
  4778.  
  4779.   TicketItem.prototype = {
  4780.     /**
  4781.      * Determines whether a given value is valid for the ticket item.
  4782.      * @param {Object} value The value to check for validity.
  4783.      * @return {boolean} Whether the given value is valid for the ticket item.
  4784.      */
  4785.     wouldValueBeValid: function(value) {
  4786.       throw Error('Abstract method not overridden');
  4787.     },
  4788.  
  4789.     /**
  4790.      * @return {boolean} Whether the print destination capability is available.
  4791.      */
  4792.     isCapabilityAvailable: function() {
  4793.       throw Error('Abstract method not overridden');
  4794.     },
  4795.  
  4796.     /** @return {!Object} The value of the ticket item. */
  4797.     getValue: function() {
  4798.       if (this.isCapabilityAvailable()) {
  4799.         if (this.value_ == null) {
  4800.           return this.getDefaultValueInternal();
  4801.         } else {
  4802.           return this.value_;
  4803.         }
  4804.       } else {
  4805.         return this.getCapabilityNotAvailableValueInternal();
  4806.       }
  4807.     },
  4808.  
  4809.     /** @return {boolean} Whether the ticket item was modified by the user. */
  4810.     isUserEdited: function() {
  4811.       return this.value_ != null;
  4812.     },
  4813.  
  4814.     /** @return {boolean} Whether the ticket item's value is valid. */
  4815.     isValid: function() {
  4816.       if (!this.isUserEdited()) {
  4817.         return true;
  4818.       }
  4819.       return this.wouldValueBeValid(this.value_);
  4820.     },
  4821.  
  4822.     /** @param {!Object} Value to set as the value of the ticket item. */
  4823.     updateValue: function(value) {
  4824.       this.value_ = value;
  4825.     },
  4826.  
  4827.     /**
  4828.      * @return {!Object} Default value of the ticket item if no value was set by
  4829.      *     the user.
  4830.      * @protected
  4831.      */
  4832.     getDefaultValueInternal: function() {
  4833.       throw Error('Abstract method not overridden');
  4834.     },
  4835.  
  4836.     /**
  4837.      * @return {!Object} Default value of the ticket item if the capability is
  4838.      *     not available.
  4839.      * @protected
  4840.      */
  4841.     getCapabilityNotAvailableValueInternal: function() {
  4842.       throw Error('Abstract method not overridden');
  4843.     }
  4844.   };
  4845.  
  4846.   // Export
  4847.   return {
  4848.     TicketItem: TicketItem
  4849.   };
  4850. });
  4851.  
  4852.  
  4853. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4854. // Use of this source code is governed by a BSD-style license that can be
  4855. // found in the LICENSE file.
  4856.  
  4857. cr.define('print_preview.ticket_items', function() {
  4858.   'use strict';
  4859.  
  4860.   /**
  4861.    * Custom page margins ticket item whose value is a
  4862.    * {@code print_preview.Margins}.
  4863.    * @param {!print_preview.DocumentInfo} documentInfo Information about the
  4864.    *     document to print.
  4865.    * @param {!print_preview.MeasurementSystem} measurementSystem Used to convert
  4866.    *     from string input into measurements in points.
  4867.    * @constructor
  4868.    * @extends {print_preview.ticket_items.TicketItem}
  4869.    */
  4870.   function CustomMargins(documentInfo, measurementSystem) {
  4871.     print_preview.ticket_items.TicketItem.call(this);
  4872.  
  4873.     /**
  4874.      * Information about the document to print.
  4875.      * @type {!print_preview.DocumentInfo}
  4876.      * @private
  4877.      */
  4878.     this.documentInfo_ = documentInfo;
  4879.  
  4880.     /**
  4881.      * Used to convert from string input to measurements in points.
  4882.      * @type {!print_preview.MeasurementSystem}
  4883.      * @private
  4884.      */
  4885.     this.measurementSystem_ = measurementSystem;
  4886.   };
  4887.  
  4888.   /**
  4889.    * Enumeration of the orientations of margins.
  4890.    * @enum {string}
  4891.    */
  4892.   CustomMargins.Orientation = {
  4893.     TOP: 'top',
  4894.     RIGHT: 'right',
  4895.     BOTTOM: 'bottom',
  4896.     LEFT: 'left'
  4897.   };
  4898.  
  4899.   /**
  4900.    * Mapping of a margin orientation to its opposite.
  4901.    * @type {!Object.<!CustomMargins.Orientation, !CustomMargins.Orientation>}
  4902.    * @private
  4903.    */
  4904.   CustomMargins.OppositeOrientation_ = {};
  4905.   CustomMargins.OppositeOrientation_[CustomMargins.Orientation.TOP] =
  4906.       CustomMargins.Orientation.BOTTOM;
  4907.   CustomMargins.OppositeOrientation_[CustomMargins.Orientation.RIGHT] =
  4908.       CustomMargins.Orientation.LEFT;
  4909.   CustomMargins.OppositeOrientation_[CustomMargins.Orientation.BOTTOM] =
  4910.       CustomMargins.Orientation.TOP;
  4911.   CustomMargins.OppositeOrientation_[CustomMargins.Orientation.LEFT] =
  4912.       CustomMargins.Orientation.RIGHT;
  4913.  
  4914.   /**
  4915.    * Minimum distance in points that two margins can be separated by.
  4916.    * @type {number}
  4917.    * @const
  4918.    * @private
  4919.    */
  4920.   CustomMargins.MINIMUM_MARGINS_DISTANCE_ = 72; // 1 inch.
  4921.  
  4922.   CustomMargins.prototype = {
  4923.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  4924.  
  4925.     /** @override */
  4926.     wouldValueBeValid: function(value) {
  4927.       var margins = /** @type {!print_preview.Margins} */ (value);
  4928.       for (var key in CustomMargins.Orientation) {
  4929.         var o = CustomMargins.Orientation[key];
  4930.         var max = this.getMarginMax_(
  4931.             o, margins.get(CustomMargins.OppositeOrientation_[o]));
  4932.         if (margins.get(o) > max || margins.get(o) < 0) {
  4933.           return false;
  4934.         }
  4935.       }
  4936.       return true;
  4937.     },
  4938.  
  4939.     /** @override */
  4940.     isCapabilityAvailable: function() {
  4941.       return this.documentInfo_.isModifiable;
  4942.     },
  4943.  
  4944.     /**
  4945.      * @param {!print_preview.ticket_items.CustomMargins.Orientation}
  4946.      *     orientation Specifies the margin to get the maximum value for.
  4947.      * @return {number} Maximum value in points of the specified margin.
  4948.      */
  4949.     getMarginMax: function(orientation) {
  4950.       var oppositeOrient = CustomMargins.OppositeOrientation_[orientation];
  4951.       var margins = /** @type {!print_preview.Margins} */ (this.getValue());
  4952.       return this.getMarginMax_(orientation, margins.get(oppositeOrient));
  4953.     },
  4954.  
  4955.     /** @override */
  4956.     updateValue: function(value) {
  4957.       var margins = /** @type {!print_preview.Margins} */ (value);
  4958.       if (margins != null) {
  4959.         margins = new print_preview.Margins(
  4960.             Math.round(margins.get(CustomMargins.Orientation.TOP)),
  4961.             Math.round(margins.get(CustomMargins.Orientation.RIGHT)),
  4962.             Math.round(margins.get(CustomMargins.Orientation.BOTTOM)),
  4963.             Math.round(margins.get(CustomMargins.Orientation.LEFT)));
  4964.       }
  4965.       print_preview.ticket_items.TicketItem.prototype.updateValue.call(
  4966.           this, margins);
  4967.     },
  4968.  
  4969.     /**
  4970.      * Updates the specified margin in points while keeping the value within
  4971.      * a maximum and minimum.
  4972.      * @param {!print_preview.ticket_items.CustomMargins.Orientation}
  4973.      *     orientation Specifies the margin to update.
  4974.      * @param {number} value Updated margin value in points.
  4975.      */
  4976.     updateMargin: function(orientation, value) {
  4977.       var margins = /** @type {!print_preview.Margins} */ (this.getValue());
  4978.       var oppositeOrientation = CustomMargins.OppositeOrientation_[orientation];
  4979.       var max =
  4980.           this.getMarginMax_(orientation, margins.get(oppositeOrientation));
  4981.       value = Math.max(0, Math.min(max, value));
  4982.       this.updateValue(margins.set(orientation, value));
  4983.     },
  4984.  
  4985.     /** @override */
  4986.     getDefaultValueInternal: function() {
  4987.       return this.documentInfo_.margins ||
  4988.              new print_preview.Margins(72, 72, 72, 72);
  4989.     },
  4990.  
  4991.     /** @override */
  4992.     getCapabilityNotAvailableValueInternal: function() {
  4993.       return this.documentInfo_.margins ||
  4994.              new print_preview.Margins(72, 72, 72, 72);
  4995.     },
  4996.  
  4997.     /**
  4998.      * @param {!print_preview.ticket_items.CustomMargins.Orientation}
  4999.      *     orientation Specifies which margin to get the maximum value of.
  5000.      * @param {number} oppositeMargin Value of the margin in points
  5001.      *     opposite the specified margin.
  5002.      * @return {number} Maximum value in points of the specified margin.
  5003.      * @private
  5004.      */
  5005.     getMarginMax_: function(orientation, oppositeMargin) {
  5006.       var max;
  5007.       if (orientation == CustomMargins.Orientation.TOP ||
  5008.           orientation == CustomMargins.Orientation.BOTTOM) {
  5009.         max = this.documentInfo_.pageSize.height - oppositeMargin -
  5010.             CustomMargins.MINIMUM_MARGINS_DISTANCE_;
  5011.       } else {
  5012.         max = this.documentInfo_.pageSize.width - oppositeMargin -
  5013.             CustomMargins.MINIMUM_MARGINS_DISTANCE_;
  5014.       }
  5015.       return Math.round(max);
  5016.     }
  5017.   };
  5018.  
  5019.   // Export
  5020.   return {
  5021.     CustomMargins: CustomMargins
  5022.   };
  5023. });
  5024.  
  5025. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5026. // Use of this source code is governed by a BSD-style license that can be
  5027. // found in the LICENSE file.
  5028.  
  5029. cr.define('print_preview.ticket_items', function() {
  5030.   'use strict';
  5031.  
  5032.   /**
  5033.    * Collate ticket item whose value is a {@code boolean} that indicates whether
  5034.    * collation is enabled.
  5035.    * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities
  5036.    *     holder used to determine the default collate value and if the collate
  5037.    *     capability is available.
  5038.    * @constructor
  5039.    * @extends {print_preview.ticket_items.TicketItem}
  5040.    */
  5041.   function Collate(capabilitiesHolder) {
  5042.     print_preview.ticket_items.TicketItem.call(this);
  5043.  
  5044.     /**
  5045.      * Capabilities holder used to determine the default collate value and if
  5046.      * the collate capability is available.
  5047.      * @type {!print_preview.CapabilitiesHolder}
  5048.      * @private
  5049.      */
  5050.     this.capabilitiesHolder_ = capabilitiesHolder;
  5051.   };
  5052.  
  5053.   Collate.prototype = {
  5054.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5055.  
  5056.     /** @override */
  5057.     wouldValueBeValid: function(value) {
  5058.       return true;
  5059.     },
  5060.  
  5061.     /** @override */
  5062.     isCapabilityAvailable: function() {
  5063.       return this.capabilitiesHolder_.get().hasCollateCapability;
  5064.     },
  5065.  
  5066.     /** @override */
  5067.     getDefaultValueInternal: function() {
  5068.       return this.capabilitiesHolder_.get().defaultIsCollateEnabled;
  5069.     },
  5070.  
  5071.     /** @override */
  5072.     getCapabilityNotAvailableValueInternal: function() {
  5073.       return false;
  5074.     }
  5075.   };
  5076.  
  5077.   // Export
  5078.   return {
  5079.     Collate: Collate
  5080.   };
  5081. });
  5082.  
  5083. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5084. // Use of this source code is governed by a BSD-style license that can be
  5085. // found in the LICENSE file.
  5086.  
  5087. cr.define('print_preview.ticket_items', function() {
  5088.   'use strict';
  5089.  
  5090.   /**
  5091.    * Color ticket item whose value is a {@code boolean} that indicates whether
  5092.    * the document should be printed in color.
  5093.    * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities
  5094.    *     holder used to determine the default color value and if the color
  5095.    *     capability is available.
  5096.    * @param {!print_preview.DestinationStore} destinationStore Used to determine
  5097.    *     whether color printing should be available.
  5098.    * @constructor
  5099.    * @extends {print_preview.ticket_items.TicketItem}
  5100.    */
  5101.   function Color(capabilitiesHolder, destinationStore) {
  5102.     print_preview.ticket_items.TicketItem.call(this);
  5103.  
  5104.     /**
  5105.      * Capabilities holder used to determine the default color value and if the
  5106.      * color capability is available.
  5107.      * @type {!print_preview.CapabilitiesHolder}
  5108.      * @private
  5109.      */
  5110.     this.capabilitiesHolder_ = capabilitiesHolder;
  5111.  
  5112.     /**
  5113.      * Used to determine whether color printing should be available.
  5114.      * @type {!print_preview.DestinationStore}
  5115.      * @private
  5116.      */
  5117.     this.destinationStore_ = destinationStore;
  5118.   };
  5119.  
  5120.   Color.prototype = {
  5121.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5122.  
  5123.     /** @override */
  5124.     wouldValueBeValid: function(value) {
  5125.       return true;
  5126.     },
  5127.  
  5128.     /** @override */
  5129.     isCapabilityAvailable: function() {
  5130.       return this.capabilitiesHolder_.get().hasColorCapability &&
  5131.           (!this.destinationStore_.selectedDestination ||
  5132.               this.destinationStore_.selectedDestination.id !=
  5133.                   print_preview.Destination.GooglePromotedId.SAVE_AS_PDF);
  5134.     },
  5135.  
  5136.     /** @override */
  5137.     getDefaultValueInternal: function() {
  5138.       return this.capabilitiesHolder_.get().defaultIsColorEnabled;
  5139.     },
  5140.  
  5141.     /** @override */
  5142.     getCapabilityNotAvailableValueInternal: function() {
  5143.       var dest = this.destinationStore_.selectedDestination;
  5144.       if (!dest) {
  5145.         return false;
  5146.       }
  5147.       return dest.id == print_preview.Destination.GooglePromotedId.DOCS ||
  5148.           dest.id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ||
  5149.           dest.id == print_preview.Destination.GooglePromotedId.FEDEX ||
  5150.           dest.type == print_preview.Destination.Type.MOBILE;
  5151.     }
  5152.   };
  5153.  
  5154.   // Export
  5155.   return {
  5156.     Color: Color
  5157.   };
  5158. });
  5159.  
  5160. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5161. // Use of this source code is governed by a BSD-style license that can be
  5162. // found in the LICENSE file.
  5163.  
  5164. cr.define('print_preview.ticket_items', function() {
  5165.   'use strict';
  5166.  
  5167.   /**
  5168.    * Copies ticket item whose value is a {@code string} that indicates how many
  5169.    * copies of the document should be printed. The ticket item is backed by a
  5170.    * string since the user can textually input the copies value.
  5171.    * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities
  5172.    *     holder used to determine the default number of copies and if the copies
  5173.    *     capability is available.
  5174.    * @constructor
  5175.    * @extends {print_preview.ticket_items.TicketItem}
  5176.    */
  5177.   function Copies(capabilitiesHolder) {
  5178.     print_preview.ticket_items.TicketItem.call(this);
  5179.  
  5180.     /**
  5181.      * Capabilities holder used to determine the default number of copies and if
  5182.      * the copies capability is available.
  5183.      * @type {!print_preview.CapabilitiesHolder}
  5184.      * @private
  5185.      */
  5186.     this.capabilitiesHolder_ = capabilitiesHolder;
  5187.   };
  5188.  
  5189.   Copies.prototype = {
  5190.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5191.  
  5192.     /** @override */
  5193.     wouldValueBeValid: function(value) {
  5194.       if (/[^\d]+/.test(value)) {
  5195.         return false;
  5196.       }
  5197.       var copies = parseInt(value);
  5198.       if (copies > 999 || copies < 1) {
  5199.         return false;
  5200.       }
  5201.       return true;
  5202.     },
  5203.  
  5204.     /** @override */
  5205.     isCapabilityAvailable: function() {
  5206.       return this.capabilitiesHolder_.get().hasCopiesCapability;
  5207.     },
  5208.  
  5209.     /** @return {number} The number of copies indicated by the ticket item. */
  5210.     getValueAsNumber: function() {
  5211.       return parseInt(this.getValue());
  5212.     },
  5213.  
  5214.     /** @override */
  5215.     getDefaultValueInternal: function() {
  5216.       return this.capabilitiesHolder_.get().defaultCopiesStr;
  5217.     },
  5218.  
  5219.     /** @override */
  5220.     getCapabilityNotAvailableValueInternal: function() {
  5221.       return '1';
  5222.     }
  5223.   };
  5224.  
  5225.   // Export
  5226.   return {
  5227.     Copies: Copies
  5228.   };
  5229. });
  5230.  
  5231. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5232. // Use of this source code is governed by a BSD-style license that can be
  5233. // found in the LICENSE file.
  5234.  
  5235. cr.define('print_preview.ticket_items', function() {
  5236.   'use strict';
  5237.  
  5238.   /**
  5239.    * Duplex ticket item whose value is a {@code boolean} that indicates whether
  5240.    * the document should be duplex printed.
  5241.    * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities
  5242.    *     holder used to determine the default duplex value and if duplexing
  5243.    *     is available.
  5244.    * @constructor
  5245.    * @extends {print_preview.ticket_items.TicketItem}
  5246.    */
  5247.   function Duplex(capabilitiesHolder) {
  5248.     print_preview.ticket_items.TicketItem.call(this);
  5249.  
  5250.     /**
  5251.      * Capabilities holder used to determine the default duplex value and if
  5252.      * duplexing is available.
  5253.      * @type {!print_preview.CapabilitiesHolder}
  5254.      * @private
  5255.      */
  5256.     this.capabilitiesHolder_ = capabilitiesHolder;
  5257.   };
  5258.  
  5259.   Duplex.prototype = {
  5260.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5261.  
  5262.     /** @override */
  5263.     wouldValueBeValid: function(value) {
  5264.       return true;
  5265.     },
  5266.  
  5267.     /** @override */
  5268.     isCapabilityAvailable: function() {
  5269.       return this.capabilitiesHolder_.get().hasDuplexCapability;
  5270.     },
  5271.  
  5272.     /** @override */
  5273.     getDefaultValueInternal: function() {
  5274.       return this.capabilitiesHolder_.get().defaultIsDuplexEnabled;
  5275.     },
  5276.  
  5277.     /** @override */
  5278.     getCapabilityNotAvailableValueInternal: function() {
  5279.       return false;
  5280.     }
  5281.   };
  5282.  
  5283.   // Export
  5284.   return {
  5285.     Duplex: Duplex
  5286.   };
  5287. });
  5288.  
  5289. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5290. // Use of this source code is governed by a BSD-style license that can be
  5291. // found in the LICENSE file.
  5292.  
  5293. cr.define('print_preview.ticket_items', function() {
  5294.   'use strict';
  5295.  
  5296.   /**
  5297.    * Header-footer ticket item whose value is a {@code boolean} that indicates
  5298.    * whether the document should be printed with headers and footers.
  5299.    * @param {!print_preview.DocumentInfo} documentInfo Information about the
  5300.    *     document to print.
  5301.    * @param {!print_preview.ticket_items.MarginsType} marginsType Ticket item
  5302.    *     that stores which predefined margins to print with.
  5303.    * @param {!print_preview.ticket_items.CustomMargins} customMargins Ticket
  5304.    *     item that stores custom margin values.
  5305.    * @constructor
  5306.    * @extends {print_preview.ticket_items.TicketItem}
  5307.    */
  5308.   function HeaderFooter(documentInfo, marginsType, customMargins) {
  5309.     print_preview.ticket_items.TicketItem.call(this);
  5310.  
  5311.     /**
  5312.      * Information about the document to print.
  5313.      * @type {!print_preview.DocumentInfo}
  5314.      * @private
  5315.      */
  5316.     this.documentInfo_ = documentInfo;
  5317.  
  5318.     /**
  5319.      * Ticket item that stores which predefined margins to print with.
  5320.      * @type {!print_preview.ticket_items.MarginsType}
  5321.      * @private
  5322.      */
  5323.     this.marginsType_ = marginsType;
  5324.  
  5325.     /**
  5326.      * Ticket item that stores custom margin values.
  5327.      * @type {!print_preview.ticket_items.CustomMargins}
  5328.      * @private
  5329.      */
  5330.     this.customMargins_ = customMargins;
  5331.   };
  5332.  
  5333.   HeaderFooter.prototype = {
  5334.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5335.  
  5336.     /** @override */
  5337.     wouldValueBeValid: function(value) {
  5338.       return true;
  5339.     },
  5340.  
  5341.     /** @override */
  5342.     isCapabilityAvailable: function() {
  5343.       if (!this.documentInfo_.isModifiable) {
  5344.         return false;
  5345.       } else if (this.marginsType_.getValue() ==
  5346.           print_preview.ticket_items.MarginsType.Value.NO_MARGINS) {
  5347.         return false;
  5348.       } else if (this.marginsType_.getValue() ==
  5349.           print_preview.ticket_items.MarginsType.Value.MINIMUM) {
  5350.         return true;
  5351.       }
  5352.       var margins;
  5353.       if (this.marginsType_.getValue() ==
  5354.           print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  5355.         if (!this.customMargins_.isValid()) {
  5356.           return false;
  5357.         }
  5358.         margins = this.customMargins_.getValue();
  5359.       } else {
  5360.         margins = this.documentInfo_.margins;
  5361.       }
  5362.       var orientEnum = print_preview.ticket_items.CustomMargins.Orientation;
  5363.       return margins == null ||
  5364.              margins.get(orientEnum.TOP) > 0 ||
  5365.              margins.get(orientEnum.BOTTOM) > 0;
  5366.     },
  5367.  
  5368.     /** @override */
  5369.     getDefaultValueInternal: function() {
  5370.       return true;
  5371.     },
  5372.  
  5373.     /** @override */
  5374.     getCapabilityNotAvailableValueInternal: function() {
  5375.       return false;
  5376.     }
  5377.   };
  5378.  
  5379.   // Export
  5380.   return {
  5381.     HeaderFooter: HeaderFooter
  5382.   };
  5383. });
  5384.  
  5385. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5386. // Use of this source code is governed by a BSD-style license that can be
  5387. // found in the LICENSE file.
  5388.  
  5389. cr.define('print_preview.ticket_items', function() {
  5390.   'use strict';
  5391.  
  5392.   /**
  5393.    * Landscape ticket item whose value is a {@code boolean} that indicates
  5394.    * whether the document should be printed in landscape orientation.
  5395.    * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities
  5396.    *     holder used to determine the default landscape value and if landscape
  5397.    *     printing is available.
  5398.    * @param {!print_preview.DocumentInfo} documentInfo Information about the
  5399.    *     document to print.
  5400.    * @constructor
  5401.    * @extends {print_preview.ticket_items.TicketItem}
  5402.    */
  5403.   function Landscape(capabilitiesHolder, documentInfo) {
  5404.     print_preview.ticket_items.TicketItem.call(this);
  5405.  
  5406.     /**
  5407.      * Capabilities holder used to determine the default landscape value and if
  5408.      * landscape printing is available.
  5409.      * @type {!print_preview.CapabilitiesHolder}
  5410.      * @private
  5411.      */
  5412.     this.capabilitiesHolder_ = capabilitiesHolder;
  5413.  
  5414.     /**
  5415.      * Information about the document to print.
  5416.      * @type {!print_preview.DocumentInfo}
  5417.      * @private
  5418.      */
  5419.     this.documentInfo_ = documentInfo;
  5420.   };
  5421.  
  5422.   Landscape.prototype = {
  5423.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5424.  
  5425.     /** @override */
  5426.     wouldValueBeValid: function(value) {
  5427.       return true;
  5428.     },
  5429.  
  5430.     /** @override */
  5431.     isCapabilityAvailable: function() {
  5432.       // TODO(rltoscano): Technically, the print destination can still change
  5433.       // the orientation of the print out (at least for cloud printers) if the
  5434.       // document is not modifiable. But the preview wouldn't update in this
  5435.       // case so it would be a bad user experience.
  5436.       return this.documentInfo_.isModifiable &&
  5437.           !this.documentInfo_.hasCssMediaStyles &&
  5438.           this.capabilitiesHolder_.get().hasOrientationCapability;
  5439.     },
  5440.  
  5441.     /** @override */
  5442.     getDefaultValueInternal: function() {
  5443.       return this.capabilitiesHolder_.get().defaultIsLandscapeEnabled;
  5444.     },
  5445.  
  5446.     /** @override */
  5447.     getCapabilityNotAvailableValueInternal: function() {
  5448.       return this.documentInfo_.hasCssMediaStyles ?
  5449.           (this.documentInfo_.pageSize.width >
  5450.               this.documentInfo_.pageSize.height) :
  5451.           false;
  5452.     }
  5453.   };
  5454.  
  5455.   // Export
  5456.   return {
  5457.     Landscape: Landscape
  5458.   };
  5459. });
  5460.  
  5461. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5462. // Use of this source code is governed by a BSD-style license that can be
  5463. // found in the LICENSE file.
  5464.  
  5465. cr.define('print_preview.ticket_items', function() {
  5466.   'use strict';
  5467.  
  5468.   /**
  5469.    * Margins type ticket item whose value is a
  5470.    * {@link print_preview.ticket_items.MarginsType.Value} that indicates what
  5471.    * predefined margins type to use.
  5472.    * @param {!print_preview.DocumentInfo} documentInfo Information about the
  5473.    *     document to print.
  5474.    * @constructor
  5475.    * @extends {print_preview.ticket_items.TicketItem}
  5476.    */
  5477.   function MarginsType(documentInfo) {
  5478.     print_preview.ticket_items.TicketItem.call(this);
  5479.  
  5480.     /**
  5481.      * Information about the document to print.
  5482.      * @type {!print_preview.DocumentInfo}
  5483.      * @private
  5484.      */
  5485.     this.documentInfo_ = documentInfo;
  5486.   };
  5487.  
  5488.   /**
  5489.    * Enumeration of margin types. Matches enum MarginType in
  5490.    * printing/print_job_constants.h.
  5491.    * @enum {number}
  5492.    */
  5493.   MarginsType.Value = {
  5494.     DEFAULT: 0,
  5495.     NO_MARGINS: 1,
  5496.     MINIMUM: 2,
  5497.     CUSTOM: 3
  5498.   };
  5499.  
  5500.   MarginsType.prototype = {
  5501.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5502.  
  5503.     /** @override */
  5504.     wouldValueBeValid: function(value) {
  5505.       return true;
  5506.     },
  5507.  
  5508.     /** @override */
  5509.     isCapabilityAvailable: function() {
  5510.       return this.documentInfo_.isModifiable;
  5511.     },
  5512.  
  5513.     /** @override */
  5514.     getDefaultValueInternal: function() {
  5515.       return MarginsType.Value.DEFAULT;
  5516.     },
  5517.  
  5518.     /** @override */
  5519.     getCapabilityNotAvailableValueInternal: function() {
  5520.       return MarginsType.Value.DEFAULT;
  5521.     }
  5522.   };
  5523.  
  5524.   // Export
  5525.   return {
  5526.     MarginsType: MarginsType
  5527.   };
  5528. });
  5529.  
  5530. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5531. // Use of this source code is governed by a BSD-style license that can be
  5532. // found in the LICENSE file.
  5533.  
  5534. cr.define('print_preview.ticket_items', function() {
  5535.   'use strict';
  5536.  
  5537.   /**
  5538.    * Page range ticket item whose value is a {@code string} that represents
  5539.    * which pages in the document should be printed.
  5540.    * @param {!print_preview.DocumentInfo} documentInfo Information about the
  5541.    *     document to print.
  5542.    * @constructor
  5543.    * @extends {print_preview.ticket_items.TicketItem}
  5544.    */
  5545.   function PageRange(documentInfo) {
  5546.     print_preview.ticket_items.TicketItem.call(this);
  5547.  
  5548.     /**
  5549.      * Information about the document to print.
  5550.      * @type {!print_preview.DocumentInfo}
  5551.      * @private
  5552.      */
  5553.     this.documentInfo_ = documentInfo;
  5554.   };
  5555.  
  5556.   PageRange.prototype = {
  5557.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5558.  
  5559.     /** @override */
  5560.     wouldValueBeValid: function(value) {
  5561.       return value == '' ||
  5562.           isPageRangeTextValid(value, this.documentInfo_.pageCount);
  5563.     },
  5564.  
  5565.     /**
  5566.      * @return {!print_preview.PageNumberSet} Set of page numbers defined by the
  5567.      *     page range string.
  5568.      */
  5569.     getPageNumberSet: function() {
  5570.       if (this.isValid()) {
  5571.         return print_preview.PageNumberSet.parse(
  5572.             this.getValue(), this.documentInfo_.pageCount);
  5573.       } else {
  5574.         return print_preview.PageNumberSet.parse(
  5575.             this.getDefaultValueInternal(), this.documentInfo_.pageCount);
  5576.       }
  5577.     },
  5578.  
  5579.     /** @override */
  5580.     isCapabilityAvailable: function() {
  5581.       return true;
  5582.     },
  5583.  
  5584.     /** @override */
  5585.     getDefaultValueInternal: function() {
  5586.       return '';
  5587.     },
  5588.  
  5589.     /** @override */
  5590.     getCapabilityNotAvailableValueInternal: function() {
  5591.       return '';
  5592.     }
  5593.   };
  5594.  
  5595.   // Export
  5596.   return {
  5597.     PageRange: PageRange
  5598.   };
  5599. });
  5600.  
  5601. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5602. // Use of this source code is governed by a BSD-style license that can be
  5603. // found in the LICENSE file.
  5604.  
  5605. cr.define('print_preview.ticket_items', function() {
  5606.   'use strict';
  5607.  
  5608.   /**
  5609.    * Fit-to-page ticket item whose value is a {@code boolean} that indicates
  5610.    * whether to scale the document to fit the page.
  5611.    * @param {!print_preview.DocumentInfo} documentInfo Information about the
  5612.    *     document to print.
  5613.    * @param {!print_preview.DestinationStore} destinationStore Used to determine
  5614.    *     whether fit to page should be available.
  5615.    * @constructor
  5616.    * @extends {print_preview.ticket_items.TicketItem}
  5617.    */
  5618.   function FitToPage(documentInfo, destinationStore) {
  5619.     print_preview.ticket_items.TicketItem.call(this);
  5620.  
  5621.     /**
  5622.      * Information about the document to print.
  5623.      * @type {!print_preview.DocumentInfo}
  5624.      * @private
  5625.      */
  5626.     this.documentInfo_ = documentInfo;
  5627.  
  5628.     /**
  5629.      * Used to determine whether fit to page should be available.
  5630.      * @type {!print_preview.DestinationStore}
  5631.      * @private
  5632.      */
  5633.     this.destinationStore_ = destinationStore;
  5634.   };
  5635.  
  5636.   FitToPage.prototype = {
  5637.     __proto__: print_preview.ticket_items.TicketItem.prototype,
  5638.  
  5639.     /** @override */
  5640.     wouldValueBeValid: function(value) {
  5641.       return true;
  5642.     },
  5643.  
  5644.     /** @override */
  5645.     isCapabilityAvailable: function() {
  5646.       return !this.documentInfo_.isModifiable &&
  5647.           (!this.destinationStore_.selectedDestination ||
  5648.               this.destinationStore_.selectedDestination.id !=
  5649.                   print_preview.Destination.GooglePromotedId.SAVE_AS_PDF);
  5650.     },
  5651.  
  5652.     /** @override */
  5653.     getDefaultValueInternal: function() {
  5654.       return true;
  5655.     },
  5656.  
  5657.     /** @override */
  5658.     getCapabilityNotAvailableValueInternal: function() {
  5659.       return this.destinationStore_.selectedDestination &&
  5660.           this.destinationStore_.selectedDestination.id ==
  5661.               print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
  5662.     }
  5663.   };
  5664.  
  5665.   // Export
  5666.   return {
  5667.     FitToPage: FitToPage
  5668.   };
  5669. });
  5670.  
  5671.  
  5672. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5673. // Use of this source code is governed by a BSD-style license that can be
  5674. // found in the LICENSE file.
  5675.  
  5676. cr.define('print_preview', function() {
  5677.   'use strict';
  5678.  
  5679.   /**
  5680.    * An interface to the native Chromium printing system layer.
  5681.    * @constructor
  5682.    * @extends {cr.EventTarget}
  5683.    */
  5684.   function NativeLayer() {
  5685.     cr.EventTarget.call(this);
  5686.  
  5687.     // Bind global handlers
  5688.     global['setInitialSettings'] = this.onSetInitialSettings_.bind(this);
  5689.     global['setUseCloudPrint'] = this.onSetUseCloudPrint_.bind(this);
  5690.     global['setPrinters'] = this.onSetPrinters_.bind(this);
  5691.     global['updateWithPrinterCapabilities'] =
  5692.         this.onUpdateWithPrinterCapabilities_.bind(this);
  5693.     global['failedToGetPrinterCapabilities'] =
  5694.         this.onFailedToGetPrinterCapabilities_.bind(this);
  5695.     global['reloadPrintersList'] = this.onReloadPrintersList_.bind(this);
  5696.     global['printToCloud'] = this.onPrintToCloud_.bind(this);
  5697.     global['fileSelectionCancelled'] =
  5698.         this.onFileSelectionCancelled_.bind(this);
  5699.     global['fileSelectionCompleted'] =
  5700.         this.onFileSelectionCompleted_.bind(this);
  5701.     global['printPreviewFailed'] = this.onPrintPreviewFailed_.bind(this);
  5702.     global['invalidPrinterSettings'] =
  5703.         this.onInvalidPrinterSettings_.bind(this);
  5704.     global['onDidGetDefaultPageLayout'] =
  5705.         this.onDidGetDefaultPageLayout_.bind(this);
  5706.     global['onDidGetPreviewPageCount'] =
  5707.         this.onDidGetPreviewPageCount_.bind(this);
  5708.     global['reloadPreviewPages'] = this.onReloadPreviewPages_.bind(this);
  5709.     global['onDidPreviewPage'] = this.onDidPreviewPage_.bind(this);
  5710.     global['updatePrintPreview'] = this.onUpdatePrintPreview_.bind(this);
  5711.     global['printScalingDisabledForSourcePDF'] =
  5712.         this.onPrintScalingDisabledForSourcePDF_.bind(this);
  5713.   };
  5714.  
  5715.   /**
  5716.    * Event types dispatched from the Chromium native layer.
  5717.    * @enum {string}
  5718.    * @const
  5719.    */
  5720.   NativeLayer.EventType = {
  5721.     CAPABILITIES_SET: 'print_preview.NativeLayer.CAPABILITIES_SET',
  5722.     CLOUD_PRINT_ENABLE: 'print_preview.NativeLayer.CLOUD_PRINT_ENABLE',
  5723.     DESTINATIONS_RELOAD: 'print_preview.NativeLayer.DESTINATIONS_RELOAD',
  5724.     DISABLE_SCALING: 'print_preview.NativeLayer.DISABLE_SCALING',
  5725.     FILE_SELECTION_CANCEL: 'print_preview.NativeLayer.FILE_SELECTION_CANCEL',
  5726.     FILE_SELECTION_COMPLETE:
  5727.         'print_preview.NativeLayer.FILE_SELECTION_COMPLETE',
  5728.     GET_CAPABILITIES_FAIL: 'print_preview.NativeLayer.GET_CAPABILITIES_FAIL',
  5729.     INITIAL_SETTINGS_SET: 'print_preview.NativeLayer.INITIAL_SETTINGS_SET',
  5730.     LOCAL_DESTINATIONS_SET: 'print_preview.NativeLayer.LOCAL_DESTINATIONS_SET',
  5731.     PAGE_COUNT_READY: 'print_preview.NativeLayer.PAGE_COUNT_READY',
  5732.     PAGE_LAYOUT_READY: 'print_preview.NativeLayer.PAGE_LAYOUT_READY',
  5733.     PAGE_PREVIEW_READY: 'print_preview.NativeLayer.PAGE_PREVIEW_READY',
  5734.     PREVIEW_GENERATION_DONE:
  5735.         'print_preview.NativeLayer.PREVIEW_GENERATION_DONE',
  5736.     PREVIEW_GENERATION_FAIL:
  5737.         'print_preview.NativeLayer.PREVIEW_GENERATION_FAIL',
  5738.     PREVIEW_RELOAD: 'print_preview.NativeLayer.PREVIEW_RELOAD',
  5739.     PRINT_TO_CLOUD: 'print_preview.NativeLayer.PRINT_TO_CLOUD',
  5740.     SETTINGS_INVALID: 'print_preview.NativeLayer.SETTINGS_INVALID'
  5741.   };
  5742.  
  5743.   /**
  5744.    * Constant values matching printing::DuplexMode enum.
  5745.    * @enum {number}
  5746.    */
  5747.   NativeLayer.DuplexMode = {
  5748.     SIMPLEX: 0,
  5749.     LONG_EDGE: 1,
  5750.     UNKNOWN_DUPLEX_MODE: -1
  5751.   };
  5752.  
  5753.   /**
  5754.    * Enumeration of color modes used by Chromium.
  5755.    * @enum {number}
  5756.    * @private
  5757.    */
  5758.   NativeLayer.ColorMode_ = {
  5759.     GRAY: 1,
  5760.     COLOR: 2
  5761.   };
  5762.  
  5763.   /**
  5764.    * Version of the serialized state of the print preview.
  5765.    * @type {number}
  5766.    * @const
  5767.    * @private
  5768.    */
  5769.   NativeLayer.SERIALIZED_STATE_VERSION_ = 1;
  5770.  
  5771.   NativeLayer.prototype = {
  5772.     __proto__: cr.EventTarget.prototype,
  5773.  
  5774.     /** Gets the initial settings to initialize the print preview with. */
  5775.     startGetInitialSettings: function() {
  5776.       chrome.send('getInitialSettings');
  5777.     },
  5778.  
  5779.     /**
  5780.      * Requests the system's local print destinations. A LOCAL_DESTINATIONS_SET
  5781.      * event will be dispatched in response.
  5782.      */
  5783.     startGetLocalDestinations: function() {
  5784.       chrome.send('getPrinters');
  5785.     },
  5786.  
  5787.     /**
  5788.      * Requests the destination's printing capabilities. A CAPABILITIES_SET
  5789.      * event will be dispatched in response.
  5790.      * @param {string} destinationId ID of the destination.
  5791.      */
  5792.     startGetLocalDestinationCapabilities: function(destinationId) {
  5793.       chrome.send('getPrinterCapabilities', [destinationId]);
  5794.     },
  5795.  
  5796.     /**
  5797.      * Requests that a preview be generated. The following events may be
  5798.      * dispatched in response:
  5799.      *   - PAGE_COUNT_READY
  5800.      *   - PAGE_LAYOUT_READY
  5801.      *   - PAGE_PREVIEW_READY
  5802.      *   - PREVIEW_GENERATION_DONE
  5803.      *   - PREVIEW_GENERATION_FAIL
  5804.      *   - PREVIEW_RELOAD
  5805.      * @param {print_preview.Destination} destination Destination to print to.
  5806.      * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the
  5807.      *     state of the print ticket.
  5808.      * @param {number} ID of the preview request.
  5809.      */
  5810.     startGetPreview: function(destination, printTicketStore, requestId) {
  5811.       assert(printTicketStore.isTicketValidForPreview(),
  5812.              'Trying to generate preview when ticket is not valid');
  5813.  
  5814.       var pageRanges =
  5815.           (requestId > 0 && printTicketStore.hasPageRangeCapability()) ?
  5816.           printTicketStore.getPageNumberSet().getPageRanges() : [];
  5817.  
  5818.       var ticket = {
  5819.         'pageRange': pageRanges,
  5820.         'landscape': printTicketStore.isLandscapeEnabled(),
  5821.         'color': printTicketStore.isColorEnabled() ?
  5822.             NativeLayer.ColorMode_.COLOR : NativeLayer.ColorMode_.GRAY,
  5823.         'headerFooterEnabled': printTicketStore.isHeaderFooterEnabled(),
  5824.         'marginsType': printTicketStore.getMarginsType(),
  5825.         'isFirstRequest': requestId == 0,
  5826.         'requestID': requestId,
  5827.         'previewModifiable': printTicketStore.isDocumentModifiable,
  5828.         'printToPDF':
  5829.             destination != null &&
  5830.             destination.id ==
  5831.                 print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
  5832.         'printWithCloudPrint': destination != null && !destination.isLocal,
  5833.         'deviceName': destination == null ? 'foo' : destination.id,
  5834.         'generateDraftData': printTicketStore.isDocumentModifiable,
  5835.         'fitToPageEnabled': printTicketStore.isFitToPageEnabled(),
  5836.  
  5837.         // NOTE: Even though the following fields don't directly relate to the
  5838.         // preview, they still need to be included.
  5839.         'duplex': printTicketStore.isDuplexEnabled() ?
  5840.             NativeLayer.DuplexMode.LONG_EDGE : NativeLayer.DuplexMode.SIMPLEX,
  5841.         'copies': printTicketStore.getCopies(),
  5842.         'collate': printTicketStore.isCollateEnabled()
  5843.       };
  5844.  
  5845.       // Set 'cloudPrintID' only if the destination is not local.
  5846.       if (!destination.isLocal) {
  5847.         ticket['cloudPrintID'] = destination.id;
  5848.       }
  5849.  
  5850.       if (printTicketStore.hasMarginsCapability() &&
  5851.           printTicketStore.getMarginsType() ==
  5852.               print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  5853.         var customMargins = printTicketStore.getCustomMargins();
  5854.         var orientationEnum =
  5855.             print_preview.ticket_items.CustomMargins.Orientation;
  5856.         ticket['marginsCustom'] = {
  5857.           'marginTop': customMargins.get(orientationEnum.TOP),
  5858.           'marginRight': customMargins.get(orientationEnum.RIGHT),
  5859.           'marginBottom': customMargins.get(orientationEnum.BOTTOM),
  5860.           'marginLeft': customMargins.get(orientationEnum.LEFT)
  5861.         };
  5862.       }
  5863.  
  5864.       chrome.send(
  5865.           'getPreview',
  5866.           [JSON.stringify(ticket),
  5867.            requestId > 0 ? printTicketStore.pageCount : -1,
  5868.            printTicketStore.isDocumentModifiable]);
  5869.     },
  5870.  
  5871.     /**
  5872.      * Requests that the document be printed.
  5873.      * @param {!print_preview.Destination} destination Destination to print to.
  5874.      * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the
  5875.      *     state of the print ticket.
  5876.      * @param {print_preview.CloudPrintInterface} cloudPrintInterface Interface
  5877.      *     to Google Cloud Print.
  5878.      * @param {boolean=} opt_isOpenPdfInPreview Whether to open the PDF in the
  5879.      *     system's preview application.
  5880.      */
  5881.     startPrint: function(destination, printTicketStore, cloudPrintInterface,
  5882.                          opt_isOpenPdfInPreview) {
  5883.       assert(printTicketStore.isTicketValid(),
  5884.              'Trying to print when ticket is not valid');
  5885.  
  5886.       var ticket = {
  5887.         'pageRange': printTicketStore.hasPageRangeCapability() ?
  5888.             printTicketStore.getPageNumberSet().getPageRanges() : [],
  5889.         'landscape': printTicketStore.isLandscapeEnabled(),
  5890.         'color': printTicketStore.isColorEnabled() ?
  5891.             NativeLayer.ColorMode_.COLOR : NativeLayer.ColorMode_.GRAY,
  5892.         'headerFooterEnabled': printTicketStore.isHeaderFooterEnabled(),
  5893.         'marginsType': printTicketStore.getMarginsType(),
  5894.         'generateDraftData': true, // TODO(rltoscano): What should this be?
  5895.         'duplex': printTicketStore.isDuplexEnabled() ?
  5896.             NativeLayer.DuplexMode.LONG_EDGE : NativeLayer.DuplexMode.SIMPLEX,
  5897.         'copies': printTicketStore.getCopies(),
  5898.         'collate': printTicketStore.isCollateEnabled(),
  5899.         'previewModifiable': printTicketStore.isDocumentModifiable,
  5900.         'printToPDF': destination.id ==
  5901.             print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
  5902.         'printWithCloudPrint': !destination.isLocal,
  5903.         'deviceName': destination.id,
  5904.         'isFirstRequest': false,
  5905.         'requestID': -1,
  5906.         'fitToPageEnabled': printTicketStore.isFitToPageEnabled()
  5907.       };
  5908.  
  5909.       if (!destination.isLocal) {
  5910.         // We can't set cloudPrintID if the destination is "Print with Cloud
  5911.         // Print" because the native system will try to print to Google Cloud
  5912.         // Print with this ID instead of opening a Google Cloud Print dialog.
  5913.         ticket['cloudPrintID'] = destination.id;
  5914.       }
  5915.  
  5916.       if (printTicketStore.hasMarginsCapability() &&
  5917.           printTicketStore.getMarginsType() ==
  5918.               print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  5919.         var customMargins = printTicketStore.getCustomMargins();
  5920.         var orientationEnum =
  5921.             print_preview.ticket_items.CustomMargins.Orientation;
  5922.         ticket['marginsCustom'] = {
  5923.           'marginTop': customMargins.get(orientationEnum.TOP),
  5924.           'marginRight': customMargins.get(orientationEnum.RIGHT),
  5925.           'marginBottom': customMargins.get(orientationEnum.BOTTOM),
  5926.           'marginLeft': customMargins.get(orientationEnum.LEFT)
  5927.         };
  5928.       }
  5929.  
  5930.       if (opt_isOpenPdfInPreview) {
  5931.         ticket['OpenPDFInPreview'] = true;
  5932.       }
  5933.  
  5934.       chrome.send('print', [JSON.stringify(ticket)]);
  5935.     },
  5936.  
  5937.     /** Requests that the current pending print request be cancelled. */
  5938.     startCancelPendingPrint: function() {
  5939.       chrome.send('cancelPendingPrintRequest');
  5940.     },
  5941.  
  5942.     /** Shows the system's native printing dialog. */
  5943.     startShowSystemDialog: function() {
  5944.       chrome.send('showSystemDialog');
  5945.     },
  5946.  
  5947.     /** Shows Google Cloud Print's web-based print dialog. */
  5948.     startShowCloudPrintDialog: function() {
  5949.       chrome.send('printWithCloudPrint');
  5950.     },
  5951.  
  5952.     /** Closes the print preview dialog. */
  5953.     startCloseDialog: function() {
  5954.       chrome.send('closePrintPreviewTab');
  5955.       chrome.send('DialogClose');
  5956.     },
  5957.  
  5958.     /** Hide the print preview dialog and allow the native layer to close it. */
  5959.     startHideDialog: function() {
  5960.       chrome.send('hidePreview');
  5961.     },
  5962.  
  5963.     /**
  5964.      * Opens the Google Cloud Print sign-in dialog. The DESTINATIONS_RELOAD
  5965.      * event will be dispatched in response.
  5966.      */
  5967.     startCloudPrintSignIn: function() {
  5968.       chrome.send('signIn');
  5969.     },
  5970.  
  5971.     /** Navigates the user to the system printer settings interface. */
  5972.     startManageLocalDestinations: function() {
  5973.       chrome.send('manageLocalPrinters');
  5974.     },
  5975.  
  5976.     /** Navigates the user to the Google Cloud Print management page. */
  5977.     startManageCloudDestinations: function() {
  5978.       chrome.send('manageCloudPrinters');
  5979.     },
  5980.  
  5981.     /**
  5982.      * @param {!Object} initialSettings Object containing all initial settings.
  5983.      */
  5984.     onSetInitialSettings_: function(initialSettings) {
  5985.       var numberFormatSymbols =
  5986.           print_preview.MeasurementSystem.parseNumberFormat(
  5987.               initialSettings['numberFormat']);
  5988.       var unitType = print_preview.MeasurementSystem.UnitType.IMPERIAL;
  5989.       if (initialSettings['measurementSystem'] != null) {
  5990.         unitType = initialSettings['measurementSystem'];
  5991.       }
  5992.  
  5993.       var nativeInitialSettings = new print_preview.NativeInitialSettings(
  5994.           initialSettings['printAutomaticallyInKioskMode'] || false,
  5995.           numberFormatSymbols[0] || ',',
  5996.           numberFormatSymbols[1] || '.',
  5997.           unitType,
  5998.           initialSettings['previewModifiable'] || false,
  5999.           initialSettings['initiatorTabTitle'] || '',
  6000.           initialSettings['printerName'] || null,
  6001.           initialSettings['appState'] || null);
  6002.  
  6003.       var initialSettingsSetEvent = new cr.Event(
  6004.           NativeLayer.EventType.INITIAL_SETTINGS_SET);
  6005.       initialSettingsSetEvent.initialSettings = nativeInitialSettings;
  6006.       this.dispatchEvent(initialSettingsSetEvent);
  6007.     },
  6008.  
  6009.     /**
  6010.      * Turn on the integration of Cloud Print.
  6011.      * @param {string} cloudPrintURL The URL to use for cloud print servers.
  6012.      * @private
  6013.      */
  6014.     onSetUseCloudPrint_: function(cloudPrintURL) {
  6015.       var cloudPrintEnableEvent = new cr.Event(
  6016.           NativeLayer.EventType.CLOUD_PRINT_ENABLE);
  6017.       cloudPrintEnableEvent.baseCloudPrintUrl = cloudPrintURL;
  6018.       this.dispatchEvent(cloudPrintEnableEvent);
  6019.     },
  6020.  
  6021.     /**
  6022.      * Updates the print preview with local printers.
  6023.      * Called from PrintPreviewHandler::SetupPrinterList().
  6024.      * @param {Array} printers Array of printer info objects.
  6025.      * @private
  6026.      */
  6027.     onSetPrinters_: function(printers) {
  6028.       var localDestsSetEvent = new cr.Event(
  6029.           NativeLayer.EventType.LOCAL_DESTINATIONS_SET);
  6030.       localDestsSetEvent.destinationInfos = printers;
  6031.       this.dispatchEvent(localDestsSetEvent);
  6032.     },
  6033.  
  6034.     /**
  6035.      * Called when native layer gets settings information for a requested local
  6036.      * destination.
  6037.      * @param {Object} settingsInfo printer setting information.
  6038.      * @private
  6039.      */
  6040.     onUpdateWithPrinterCapabilities_: function(settingsInfo) {
  6041.       var capsSetEvent = new cr.Event(NativeLayer.EventType.CAPABILITIES_SET);
  6042.       capsSetEvent.settingsInfo = settingsInfo;
  6043.       this.dispatchEvent(capsSetEvent);
  6044.     },
  6045.  
  6046.     /**
  6047.      * Called when native layer gets settings information for a requested local
  6048.      * destination.
  6049.      * @param {string} printerId printer affected by error.
  6050.      * @private
  6051.      */
  6052.     onFailedToGetPrinterCapabilities_: function(destinationId) {
  6053.       var getCapsFailEvent = new cr.Event(
  6054.           NativeLayer.EventType.GET_CAPABILITIES_FAIL);
  6055.       getCapsFailEvent.destinationId = destinationId;
  6056.       this.dispatchEvent(getCapsFailEvent);
  6057.     },
  6058.  
  6059.     /** Reloads the printer list. */
  6060.     onReloadPrintersList_: function() {
  6061.       cr.dispatchSimpleEvent(this, NativeLayer.EventType.DESTINATIONS_RELOAD);
  6062.     },
  6063.  
  6064.     /**
  6065.      * Called from the C++ layer.
  6066.      * Take the PDF data handed to us and submit it to the cloud, closing the
  6067.      * print preview tab once the upload is successful.
  6068.      * @param {string} data Data to send as the print job.
  6069.      * @private
  6070.      */
  6071.     onPrintToCloud_: function(data) {
  6072.       var printToCloudEvent = new cr.Event(
  6073.           NativeLayer.EventType.PRINT_TO_CLOUD);
  6074.       printToCloudEvent.data = data;
  6075.       this.dispatchEvent(printToCloudEvent);
  6076.     },
  6077.  
  6078.     /**
  6079.      * Called from PrintPreviewUI::OnFileSelectionCancelled to notify the print
  6080.      * preview tab regarding the file selection cancel event.
  6081.      * @private
  6082.      */
  6083.     onFileSelectionCancelled_: function() {
  6084.       cr.dispatchSimpleEvent(this, NativeLayer.EventType.FILE_SELECTION_CANCEL);
  6085.     },
  6086.  
  6087.     /**
  6088.      * Called from PrintPreviewUI::OnFileSelectionCompleted to notify the print
  6089.      * preview tab regarding the file selection completed event.
  6090.      * @private
  6091.      */
  6092.     onFileSelectionCompleted_: function() {
  6093.       // If the file selection is completed and the tab is not already closed it
  6094.       // means that a pending print to pdf request exists.
  6095.       cr.dispatchSimpleEvent(
  6096.           this, NativeLayer.EventType.FILE_SELECTION_COMPLETE);
  6097.     },
  6098.  
  6099.     /**
  6100.      * Display an error message when print preview fails.
  6101.      * Called from PrintPreviewMessageHandler::OnPrintPreviewFailed().
  6102.      * @private
  6103.      */
  6104.     onPrintPreviewFailed_: function() {
  6105.       cr.dispatchSimpleEvent(
  6106.           this, NativeLayer.EventType.PREVIEW_GENERATION_FAIL);
  6107.     },
  6108.  
  6109.     /**
  6110.      * Display an error message when encountered invalid printer settings.
  6111.      * Called from PrintPreviewMessageHandler::OnInvalidPrinterSettings().
  6112.      * @private
  6113.      */
  6114.     onInvalidPrinterSettings_: function() {
  6115.       cr.dispatchSimpleEvent(this, NativeLayer.EventType.SETTINGS_INVALID);
  6116.     },
  6117.  
  6118.     /**
  6119.      * @param {{contentWidth: number, contentHeight: number, marginLeft: number,
  6120.      *          marginRight: number, marginTop: number, marginBottom: number,
  6121.      *          printableAreaX: number, printableAreaY: number,
  6122.      *          printableAreaWidth: number, printableAreaHeight: number}}
  6123.      *          pageLayout Specifies default page layout details in points.
  6124.      * @param {boolean} hasCustomPageSizeStyle Indicates whether the previewed
  6125.      *     document has a custom page size style.
  6126.      * @private
  6127.      */
  6128.     onDidGetDefaultPageLayout_: function(pageLayout, hasCustomPageSizeStyle) {
  6129.       var pageLayoutChangeEvent = new cr.Event(
  6130.           NativeLayer.EventType.PAGE_LAYOUT_READY);
  6131.       pageLayoutChangeEvent.pageLayout = pageLayout;
  6132.       pageLayoutChangeEvent.hasCustomPageSizeStyle = hasCustomPageSizeStyle;
  6133.       this.dispatchEvent(pageLayoutChangeEvent);
  6134.     },
  6135.  
  6136.     /**
  6137.      * Update the page count and check the page range.
  6138.      * Called from PrintPreviewUI::OnDidGetPreviewPageCount().
  6139.      * @param {number} pageCount The number of pages.
  6140.      * @param {number} previewResponseId The preview request id that resulted in
  6141.      *      this response.
  6142.      * @private
  6143.      */
  6144.     onDidGetPreviewPageCount_: function(pageCount, previewResponseId) {
  6145.       var pageCountChangeEvent = new cr.Event(
  6146.           NativeLayer.EventType.PAGE_COUNT_READY);
  6147.       pageCountChangeEvent.pageCount = pageCount;
  6148.       pageCountChangeEvent.previewResponseId = previewResponseId;
  6149.       this.dispatchEvent(pageCountChangeEvent);
  6150.     },
  6151.  
  6152.     /**
  6153.      * Called when no pipelining previewed pages.
  6154.      * @param {number} previewUid Preview unique identifier.
  6155.      * @param {number} previewResponseId The preview request id that resulted in
  6156.      *     this response.
  6157.      * @private
  6158.      */
  6159.     onReloadPreviewPages_: function(previewUid, previewResponseId) {
  6160.       var previewReloadEvent = new cr.Event(
  6161.           NativeLayer.EventType.PREVIEW_RELOAD);
  6162.       previewReloadEvent.previewUid = previewUid;
  6163.       previewReloadEvent.previewResponseId = previewResponseId;
  6164.       this.dispatchEvent(previewReloadEvent);
  6165.     },
  6166.  
  6167.     /**
  6168.      * Notification that a print preview page has been rendered.
  6169.      * Check if the settings have changed and request a regeneration if needed.
  6170.      * Called from PrintPreviewUI::OnDidPreviewPage().
  6171.      * @param {number} pageNumber The page number, 0-based.
  6172.      * @param {number} previewUid Preview unique identifier.
  6173.      * @param {number} previewResponseId The preview request id that resulted in
  6174.      *     this response.
  6175.      * @private
  6176.      */
  6177.     onDidPreviewPage_: function(pageNumber, previewUid, previewResponseId) {
  6178.       var pagePreviewGenEvent = new cr.Event(
  6179.           NativeLayer.EventType.PAGE_PREVIEW_READY);
  6180.       pagePreviewGenEvent.pageIndex = pageNumber;
  6181.       pagePreviewGenEvent.previewUid = previewUid;
  6182.       pagePreviewGenEvent.previewResponseId = previewResponseId;
  6183.       this.dispatchEvent(pagePreviewGenEvent);
  6184.     },
  6185.  
  6186.     /**
  6187.      * Update the print preview when new preview data is available.
  6188.      * Create the PDF plugin as needed.
  6189.      * Called from PrintPreviewUI::PreviewDataIsAvailable().
  6190.      * @param {number} previewUid Preview unique identifier.
  6191.      * @param {number} previewResponseId The preview request id that resulted in
  6192.      *     this response.
  6193.      * @private
  6194.      */
  6195.     onUpdatePrintPreview_: function(previewUid, previewResponseId) {
  6196.       var previewGenDoneEvent = new cr.Event(
  6197.           NativeLayer.EventType.PREVIEW_GENERATION_DONE);
  6198.       previewGenDoneEvent.previewUid = previewUid;
  6199.       previewGenDoneEvent.previewResponseId = previewResponseId;
  6200.       this.dispatchEvent(previewGenDoneEvent);
  6201.     },
  6202.  
  6203.     /**
  6204.      * Updates the fit to page option state based on the print scaling option of
  6205.      * source pdf. PDF's have an option to enable/disable print scaling. When we
  6206.      * find out that the print scaling option is disabled for the source pdf, we
  6207.      * uncheck the fitToPage_ to page checkbox. This function is called from C++
  6208.      * code.
  6209.      * @private
  6210.      */
  6211.     onPrintScalingDisabledForSourcePDF_: function() {
  6212.       cr.dispatchSimpleEvent(this, NativeLayer.EventType.DISABLE_SCALING);
  6213.     }
  6214.   };
  6215.  
  6216.   /**
  6217.    * Initial settings retrieved from the native layer.
  6218.    * @param {boolean} isInKioskAutoPrintMode Whether the print preview should be
  6219.    *     in auto-print mode.
  6220.    * @param {string} thousandsDelimeter Character delimeter of thousands digits.
  6221.    * @param {string} decimalDelimeter Character delimeter of the decimal point.
  6222.    * @param {!print_preview.MeasurementSystem.UnitType} unitType Unit type of
  6223.    *     local machine's measurement system.
  6224.    * @param {boolean} isDocumentModifiable Whether the document to print is
  6225.    *     modifiable.
  6226.    * @param {string} documentTitle Title of the document.
  6227.    * @param {?string} systemDefaultDestinationId ID of the system default
  6228.    *     destination.
  6229.    * @param {?string} serializedAppStateStr Serialized app state.
  6230.    * @constructor
  6231.    */
  6232.   function NativeInitialSettings(
  6233.       isInKioskAutoPrintMode,
  6234.       thousandsDelimeter,
  6235.       decimalDelimeter,
  6236.       unitType,
  6237.       isDocumentModifiable,
  6238.       documentTitle,
  6239.       systemDefaultDestinationId,
  6240.       serializedAppStateStr) {
  6241.  
  6242.     /**
  6243.      * Whether the print preview should be in auto-print mode.
  6244.      * @type {boolean}
  6245.      * @private
  6246.      */
  6247.     this.isInKioskAutoPrintMode_ = isInKioskAutoPrintMode;
  6248.  
  6249.     /**
  6250.      * Character delimeter of thousands digits.
  6251.      * @type {string}
  6252.      * @private
  6253.      */
  6254.     this.thousandsDelimeter_ = thousandsDelimeter;
  6255.  
  6256.     /**
  6257.      * Character delimeter of the decimal point.
  6258.      * @type {string}
  6259.      * @private
  6260.      */
  6261.     this.decimalDelimeter_ = decimalDelimeter;
  6262.  
  6263.     /**
  6264.      * Unit type of local machine's measurement system.
  6265.      * @type {string}
  6266.      * @private
  6267.      */
  6268.     this.unitType_ = unitType;
  6269.  
  6270.     /**
  6271.      * Whether the document to print is modifiable.
  6272.      * @type {boolean}
  6273.      * @private
  6274.      */
  6275.     this.isDocumentModifiable_ = isDocumentModifiable;
  6276.  
  6277.     /**
  6278.      * Title of the document.
  6279.      * @type {string}
  6280.      * @private
  6281.      */
  6282.     this.documentTitle_ = documentTitle;
  6283.  
  6284.     /**
  6285.      * ID of the system default destination.
  6286.      * @type {?string}
  6287.      * @private
  6288.      */
  6289.     this.systemDefaultDestinationId_ = systemDefaultDestinationId;
  6290.  
  6291.     /**
  6292.      * Serialized app state.
  6293.      * @type {?string}
  6294.      * @private
  6295.      */
  6296.     this.serializedAppStateStr_ = serializedAppStateStr;
  6297.   };
  6298.  
  6299.   NativeInitialSettings.prototype = {
  6300.     /**
  6301.      * @return {boolean} Whether the print preview should be in auto-print mode.
  6302.      */
  6303.     get isInKioskAutoPrintMode() {
  6304.       return this.isInKioskAutoPrintMode_;
  6305.     },
  6306.  
  6307.     /** @return {string} Character delimeter of thousands digits. */
  6308.     get thousandsDelimeter() {
  6309.       return this.thousandsDelimeter_;
  6310.     },
  6311.  
  6312.     /** @return {string} Character delimeter of the decimal point. */
  6313.     get decimalDelimeter() {
  6314.       return this.decimalDelimeter_;
  6315.     },
  6316.  
  6317.     /**
  6318.      * @return {!print_preview.MeasurementSystem.UnitType} Unit type of local
  6319.      *     machine's measurement system.
  6320.      */
  6321.     get unitType() {
  6322.       return this.unitType_;
  6323.     },
  6324.  
  6325.     /** @return {boolean} Whether the document to print is modifiable. */
  6326.     get isDocumentModifiable() {
  6327.       return this.isDocumentModifiable_;
  6328.     },
  6329.  
  6330.     /** @return {string} Document title. */
  6331.     get documentTitle() {
  6332.       return this.documentTitle_;
  6333.     },
  6334.  
  6335.     /** @return {?string} ID of the system default destination. */
  6336.     get systemDefaultDestinationId() {
  6337.       return this.systemDefaultDestinationId_;
  6338.     },
  6339.  
  6340.     /** @return {?string} Serialized app state. */
  6341.     get serializedAppStateStr() {
  6342.       return this.serializedAppStateStr_;
  6343.     }
  6344.   };
  6345.  
  6346.   // Export
  6347.   return {
  6348.     NativeInitialSettings: NativeInitialSettings,
  6349.     NativeLayer: NativeLayer
  6350.   };
  6351. });
  6352.  
  6353. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  6354. // Use of this source code is governed by a BSD-style license that can be
  6355. // found in the LICENSE file.
  6356.  
  6357. // Counter used to give webkit animations unique names.
  6358. var animationCounter = 0;
  6359.  
  6360. function addAnimation(code) {
  6361.   var name = 'anim' + animationCounter;
  6362.   animationCounter++;
  6363.   var rules = document.createTextNode(
  6364.       '@-webkit-keyframes ' + name + ' {' + code + '}');
  6365.   var el = document.createElement('style');
  6366.   el.type = 'text/css';
  6367.   el.appendChild(rules);
  6368.   el.setAttribute('id', name);
  6369.   document.body.appendChild(el);
  6370.  
  6371.   return name;
  6372. }
  6373.  
  6374. /**
  6375.  * Generates css code for fading in an element by animating the height.
  6376.  * @param {number} targetHeight The desired height in pixels after the animation
  6377.  *     ends.
  6378.  * @return {string} The css code for the fade in animation.
  6379.  */
  6380. function getFadeInAnimationCode(targetHeight) {
  6381.   return '0% { opacity: 0; height: 0; } ' +
  6382.       '80% { height: ' + (targetHeight + 4) + 'px; }' +
  6383.       '100% { opacity: 1; height: ' + targetHeight + 'px; }';
  6384. }
  6385.  
  6386. /**
  6387.  * Fades in an element. Used for both printing options and error messages
  6388.  * appearing underneath the textfields.
  6389.  * @param {HTMLElement} el The element to be faded in.
  6390.  */
  6391. function fadeInElement(el) {
  6392.   if (el.classList.contains('visible'))
  6393.     return;
  6394.   el.classList.remove('closing');
  6395.   el.hidden = false;
  6396.   el.style.height = 'auto';
  6397.   var height = el.offsetHeight;
  6398.   el.style.height = height + 'px';
  6399.   var animName = addAnimation(getFadeInAnimationCode(height));
  6400.   var eventTracker = new EventTracker();
  6401.   eventTracker.add(el, 'webkitAnimationEnd',
  6402.                    onFadeInAnimationEnd.bind(el, eventTracker),
  6403.                    false);
  6404.   el.style.webkitAnimationName = animName;
  6405.   el.classList.add('visible');
  6406. }
  6407.  
  6408. /**
  6409.  * Fades out an element. Used for both printing options and error messages
  6410.  * appearing underneath the textfields.
  6411.  * @param {HTMLElement} el The element to be faded out.
  6412.  */
  6413. function fadeOutElement(el) {
  6414.   if (!el.classList.contains('visible'))
  6415.     return;
  6416.   el.style.height = 'auto';
  6417.   var height = el.offsetHeight;
  6418.   el.style.height = height + 'px';
  6419.   var animName = addAnimation('');
  6420.   var eventTracker = new EventTracker();
  6421.   eventTracker.add(el, 'webkitTransitionEnd',
  6422.                    onFadeOutTransitionEnd.bind(el, animName, eventTracker),
  6423.                    false);
  6424.   el.classList.add('closing');
  6425.   el.classList.remove('visible');
  6426. }
  6427.  
  6428. /**
  6429.  * Executes when a fade out animation ends.
  6430.  * @param {string} animationName The name of the animation to be removed.
  6431.  * @param {EventTracker} eventTracker The |EventTracker| object that was used
  6432.  *     for adding this listener.
  6433.  * @param {WebkitTransitionEvent} event The event that triggered this listener.
  6434.  * @this {HTMLElement} The element where the transition occurred.
  6435.  */
  6436. function onFadeOutTransitionEnd(animationName, eventTracker, event) {
  6437.   if (event.propertyName != 'height')
  6438.     return;
  6439.   fadeInOutCleanup(animationName);
  6440.   eventTracker.remove(this, 'webkitTransitionEnd');
  6441.   this.hidden = true;
  6442. }
  6443.  
  6444. /**
  6445.  * Executes when a fade in animation ends.
  6446.  * @param {EventTracker} eventTracker The |EventTracker| object that was used
  6447.  *     for adding this listener.
  6448.  * @param {WebkitAnimationEvent} event The event that triggered this listener.
  6449.  * @this {HTMLElement} The element where the transition occurred.
  6450.  */
  6451. function onFadeInAnimationEnd(eventTracker, event) {
  6452.   this.style.height = '';
  6453.   this.style.webkitAnimationName = '';
  6454.   fadeInOutCleanup(event.animationName);
  6455.   eventTracker.remove(this, 'webkitAnimationEnd');
  6456. }
  6457.  
  6458. /**
  6459.  * Removes the <style> element corrsponding to |animationName| from the DOM.
  6460.  * @param {string} animationName The name of the animation to be removed.
  6461.  */
  6462. function fadeInOutCleanup(animationName) {
  6463.   var animEl = document.getElementById(animationName);
  6464.   if (animEl)
  6465.     animEl.parentNode.removeChild(animEl);
  6466. }
  6467.  
  6468. /**
  6469.  * Fades in a printing option existing under |el|.
  6470.  * @param {HTMLElement} el The element to hide.
  6471.  */
  6472. function fadeInOption(el) {
  6473.   if (el.classList.contains('visible'))
  6474.     return;
  6475.  
  6476.   wrapContentsInDiv(el.querySelector('h1'), ['invisible']);
  6477.   var rightColumn = el.querySelector('.right-column');
  6478.   wrapContentsInDiv(rightColumn, ['invisible']);
  6479.  
  6480.   var toAnimate = el.querySelectorAll('.collapsible');
  6481.   for (var i = 0; i < toAnimate.length; i++)
  6482.     fadeInElement(toAnimate[i]);
  6483.   el.classList.add('visible');
  6484. }
  6485.  
  6486. /**
  6487.  * Fades out a printing option existing under |el|.
  6488.  * @param {HTMLElement} el The element to hide.
  6489.  */
  6490. function fadeOutOption(el) {
  6491.   if (!el.classList.contains('visible'))
  6492.     return;
  6493.  
  6494.   wrapContentsInDiv(el.querySelector('h1'), ['visible']);
  6495.   var rightColumn = el.querySelector('.right-column');
  6496.   wrapContentsInDiv(rightColumn, ['visible']);
  6497.  
  6498.   var toAnimate = el.querySelectorAll('.collapsible');
  6499.   for (var i = 0; i < toAnimate.length; i++)
  6500.     fadeOutElement(toAnimate[i]);
  6501.   el.classList.remove('visible');
  6502. }
  6503.  
  6504. /**
  6505.  * Wraps the contents of |el| in a div element and attaches css classes
  6506.  * |classes| in the new div, only if has not been already done. It is neccesary
  6507.  * for animating the height of table cells.
  6508.  * @param {HTMLElement} el The element to be processed.
  6509.  * @param {array} classes The css classes to add.
  6510.  */
  6511. function wrapContentsInDiv(el, classes) {
  6512.   var div = el.querySelector('div');
  6513.   if (!div || !div.classList.contains('collapsible')) {
  6514.     div = document.createElement('div');
  6515.     while (el.childNodes.length > 0)
  6516.       div.appendChild(el.firstChild);
  6517.     el.appendChild(div);
  6518.   }
  6519.  
  6520.   div.className = '';
  6521.   div.classList.add('collapsible');
  6522.   for (var i = 0; i < classes.length; i++)
  6523.     div.classList.add(classes[i]);
  6524. }
  6525.  
  6526. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  6527. // Use of this source code is governed by a BSD-style license that can be
  6528. // found in the LICENSE file.
  6529.  
  6530. cr.define('cloudprint', function() {
  6531.   'use strict';
  6532.  
  6533.   /**
  6534.    * API to the Google Cloud Print service.
  6535.    * @param {string} baseUrl Base part of the Google Cloud Print service URL
  6536.    *     with no trailing slash. For example,
  6537.    *     'https://www.google.com/cloudprint'.
  6538.    * @constructor
  6539.    * @extends {cr.EventTarget}
  6540.    */
  6541.   function CloudPrintInterface(baseUrl) {
  6542.     /**
  6543.      * The base URL of the Google Cloud Print API.
  6544.      * @type {string}
  6545.      * @private
  6546.      */
  6547.     this.baseUrl_ = baseUrl;
  6548.  
  6549.     /**
  6550.      * Last received XSRF token. Sent as a parameter in every request.
  6551.      * @type {string}
  6552.      * @private
  6553.      */
  6554.     this.xsrfToken_ = '';
  6555.   };
  6556.  
  6557.   /**
  6558.    * Event types dispatched by the interface.
  6559.    * @enum {string}
  6560.    */
  6561.   CloudPrintInterface.EventType = {
  6562.     PRINTER_DONE: 'cloudprint.CloudPrintInterface.PRINTER_DONE',
  6563.     PRINTER_FAILED: 'cloudprint.CloudPrintInterface.PRINTER_FAILED',
  6564.     SEARCH_DONE: 'cloudprint.CloudPrintInterface.SEARCH_DONE',
  6565.     SEARCH_FAILED: 'cloudprint.CloudPrintInterface.SEARCH_FAILED',
  6566.     SUBMIT_DONE: 'cloudprint.CloudPrintInterface.SUBMIT_DONE',
  6567.     SUBMIT_FAILED: 'cloudprint.CloudPrintInterface.SUBMIT_FAILED',
  6568.     UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED:
  6569.         'cloudprint.CloudPrintInterface.UPDATE_PRINTER_TOS_ACCEPTANCE_FAILED'
  6570.   };
  6571.  
  6572.   /**
  6573.    * Content type header value for a URL encoded HTTP request.
  6574.    * @type {string}
  6575.    * @const
  6576.    * @private
  6577.    */
  6578.   CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_ =
  6579.       'application/x-www-form-urlencoded';
  6580.  
  6581.   /**
  6582.    * Multi-part POST request boundary used in communication with Google
  6583.    * Cloud Print.
  6584.    * @type {string}
  6585.    * @const
  6586.    * @private
  6587.    */
  6588.   CloudPrintInterface.MULTIPART_BOUNDARY_ =
  6589.       '----CloudPrintFormBoundaryjc9wuprokl8i';
  6590.  
  6591.   /**
  6592.    * Content type header value for a multipart HTTP request.
  6593.    * @type {string}
  6594.    * @const
  6595.    * @private
  6596.    */
  6597.   CloudPrintInterface.MULTIPART_CONTENT_TYPE_ =
  6598.       'multipart/form-data; boundary=' +
  6599.       CloudPrintInterface.MULTIPART_BOUNDARY_;
  6600.  
  6601.   /**
  6602.    * Regex that extracts Chrome's version from the user-agent string.
  6603.    * @type {!RegExp}
  6604.    * @const
  6605.    * @private
  6606.    */
  6607.   CloudPrintInterface.VERSION_REGEXP_ = /.*Chrome\/([\d\.]+)/i;
  6608.  
  6609.   /**
  6610.    * Enumeration of JSON response fields from Google Cloud Print API.
  6611.    * @enum {string}
  6612.    * @private
  6613.    */
  6614.   CloudPrintInterface.JsonFields_ = {
  6615.     PRINTER: 'printer'
  6616.   };
  6617.  
  6618.   CloudPrintInterface.prototype = {
  6619.     __proto__: cr.EventTarget.prototype,
  6620.  
  6621.     /** @return {string} Base URL of the Google Cloud Print service. */
  6622.     get baseUrl() {
  6623.       return this.baseUrl_;
  6624.     },
  6625.  
  6626.     /**
  6627.      * Sends a Google Cloud Print search API request.
  6628.      * @param {boolean} isRecent Whether to search for only recently used
  6629.      *     printers.
  6630.      */
  6631.     search: function(isRecent) {
  6632.       var params = [
  6633.         new HttpParam('connection_status', 'ALL'),
  6634.         new HttpParam('client', 'chrome')
  6635.       ];
  6636.       if (isRecent) {
  6637.         params.push(new HttpParam('q', '^recent'));
  6638.       }
  6639.       this.sendRequest_('GET', 'search', params,
  6640.                         this.onSearchDone_.bind(this, isRecent));
  6641.     },
  6642.  
  6643.     /**
  6644.      * Sends a Google Cloud Print submit API request.
  6645.      * @param {!print_preview.Destination} destination Cloud destination to
  6646.      *     print to.
  6647.      * @param {!print_preview.PrintTicketStore} printTicketStore Contains the
  6648.      *     print ticket to print.
  6649.      * @param {string} data Base64 encoded data of the document.
  6650.      */
  6651.     submit: function(destination, printTicketStore, data) {
  6652.       var result =
  6653.           CloudPrintInterface.VERSION_REGEXP_.exec(navigator.userAgent);
  6654.       var chromeVersion = 'unknown';
  6655.       if (result && result.length == 2) {
  6656.         chromeVersion = result[1];
  6657.       }
  6658.       var params = [
  6659.         new HttpParam('printerid', destination.id),
  6660.         new HttpParam('contentType', 'dataUrl'),
  6661.         new HttpParam('title', printTicketStore.getDocumentTitle()),
  6662.         new HttpParam('capabilities',
  6663.                       this.createPrintTicket_(destination, printTicketStore)),
  6664.         new HttpParam('content', 'data:application/pdf;base64,' + data),
  6665.         new HttpParam('tag',
  6666.                       '__google__chrome_version=' + chromeVersion),
  6667.         new HttpParam('tag', '__google__os=' + navigator.platform)
  6668.       ];
  6669.       this.sendRequest_('POST', 'submit', params,
  6670.                         this.onSubmitDone_.bind(this));
  6671.     },
  6672.  
  6673.     /**
  6674.      * Sends a Google Cloud Print printer API request.
  6675.      * @param {string} printerId ID of the printer to lookup.
  6676.      */
  6677.     printer: function(printerId) {
  6678.       var params = [new HttpParam('printerid', printerId)];
  6679.       this.sendRequest_('GET', 'printer', params,
  6680.                         this.onPrinterDone_.bind(this, printerId));
  6681.     },
  6682.  
  6683.     /**
  6684.      * Sends a Google Cloud Print update API request to accept (or reject) the
  6685.      * terms-of-service of the given printer.
  6686.      * @param {string} printerId ID of the printer to accept the
  6687.      *     terms-of-service for.
  6688.      * @param {boolean} isAccepted Whether the user accepted the
  6689.      *     terms-of-service.
  6690.      */
  6691.     updatePrinterTosAcceptance: function(printerId, isAccepted) {
  6692.       var params = [
  6693.         new HttpParam('printerid', printerId),
  6694.         new HttpParam('is_tos_accepted', isAccepted)
  6695.       ];
  6696.       this.sendRequest_('POST', 'update', params,
  6697.                         this.onUpdatePrinterTosAcceptanceDone_.bind(this));
  6698.     },
  6699.  
  6700.     /**
  6701.      * Creates an object that represents a Google Cloud Print print ticket.
  6702.      * @param {!print_preview.Destination} destination Destination to print to.
  6703.      * @param {!print_preview.PrintTicketStore} printTicketStore Used to create
  6704.      *     the state of the print ticket.
  6705.      * @return {!Object} Google Cloud Print print ticket.
  6706.      * @private
  6707.      */
  6708.     createPrintTicket_: function(destination, printTicketStore) {
  6709.       assert(!destination.isLocal,
  6710.              'Trying to create a Google Cloud Print print ticket for a local ' +
  6711.                  'destination');
  6712.       assert(destination.capabilities,
  6713.              'Trying to create a Google Cloud Print print ticket for a ' +
  6714.                  'destination with no print capabilities');
  6715.  
  6716.       var ticketItems = [];
  6717.  
  6718.       if (destination.capabilities.collateCapability) {
  6719.         var collateCap = destination.capabilities.collateCapability;
  6720.         var ticketItem = {
  6721.           'name': collateCap.id,
  6722.           'type': collateCap.type,
  6723.           'options': [{'name': printTicketStore.isCollateEnabled() ?
  6724.               collateCap.collateOption : collateCap.noCollateOption}]
  6725.         };
  6726.         ticketItems.push(ticketItem);
  6727.       }
  6728.  
  6729.       if (destination.capabilities.colorCapability) {
  6730.         var colorCap = destination.capabilities.colorCapability;
  6731.         var ticketItem = {
  6732.           'name': colorCap.id,
  6733.           'type': colorCap.type,
  6734.           'options': [{'name': printTicketStore.isColorEnabled() ?
  6735.               colorCap.colorOption : colorCap.bwOption}]
  6736.         };
  6737.         ticketItems.push(ticketItem);
  6738.       }
  6739.  
  6740.       if (destination.capabilities.copiesCapability) {
  6741.         var copiesCap = destination.capabilities.copiesCapability;
  6742.         var ticketItem = {
  6743.           'name': copiesCap.id,
  6744.           'type': copiesCap.type,
  6745.           'value': printTicketStore.getCopies()
  6746.         };
  6747.         ticketItems.push(ticketItem);
  6748.       }
  6749.  
  6750.       if (destination.capabilities.duplexCapability) {
  6751.         var duplexCap = destination.capabilities.duplexCapability;
  6752.         var ticketItem = {
  6753.           'name': duplexCap.id,
  6754.           'type': duplexCap.type,
  6755.           'options': [{'name': printTicketStore.isDuplexEnabled() ?
  6756.               duplexCap.longEdgeOption : duplexCap.simplexOption}]
  6757.         };
  6758.         ticketItems.push(ticketItem);
  6759.       }
  6760.  
  6761.       return JSON.stringify({'capabilities': ticketItems});
  6762.     },
  6763.  
  6764.     /**
  6765.      * Sends a request to the Google Cloud Print API.
  6766.      * @param {string} method HTTP method of the request.
  6767.      * @param {string} action Google Cloud Print action to perform.
  6768.      * @param {Array.<!HttpParam>} params HTTP parameters to include in the
  6769.      *     request.
  6770.      * @param {function(number, Object)} callback Callback to invoke when
  6771.      *     request completes.
  6772.      */
  6773.     sendRequest_: function(method, action, params, callback) {
  6774.       if (!this.xsrfToken_) {
  6775.         // TODO(rltoscano): Should throw an error if not a read-only action or
  6776.         // issue an xsrf token request.
  6777.       }
  6778.       var url = this.baseUrl_ + '/' + action + '?xsrf=' + this.xsrfToken_;
  6779.       var body = null;
  6780.  
  6781.       if (params) {
  6782.         if (method == 'GET') {
  6783.           url = params.reduce(function(partialUrl, param) {
  6784.             return partialUrl + '&' + param.name + '=' +
  6785.                 encodeURIComponent(param.value);
  6786.           }, url);
  6787.         } else if (method == 'POST') {
  6788.           body = params.reduce(function(partialBody, param) {
  6789.             return partialBody + 'Content-Disposition: form-data; name=\"' +
  6790.                 param.name + '\"\r\n\r\n' + param.value + '\r\n--' +
  6791.                 CloudPrintInterface.MULTIPART_BOUNDARY_ + '\r\n';
  6792.           }, '--' + CloudPrintInterface.MULTIPART_BOUNDARY_ + '\r\n');
  6793.         }
  6794.       }
  6795.  
  6796.       var headers = {};
  6797.       headers['X-CloudPrint-Proxy'] = 'ChromePrintPreview';
  6798.       if (method == 'GET') {
  6799.         headers['Content-Type'] = CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_;
  6800.       } else if (method == 'POST') {
  6801.         headers['Content-Type'] = CloudPrintInterface.MULTIPART_CONTENT_TYPE_;
  6802.       }
  6803.  
  6804.       var xhr = new XMLHttpRequest();
  6805.       xhr.onreadystatechange =
  6806.           this.onReadyStateChange_.bind(this, xhr, callback);
  6807.       xhr.open(method, url, true);
  6808.       xhr.withCredentials = true;
  6809.       for (var header in headers) {
  6810.         xhr.setRequestHeader(header, headers[header]);
  6811.       }
  6812.       xhr.send(body);
  6813.     },
  6814.  
  6815.     /**
  6816.      * Creates a Google Cloud Print interface error that is ready to dispatch.
  6817.      * @param {!CloudPrintInterface.EventType} type Type of the error.
  6818.      * @param {number} status HTTP status code of the failed request.
  6819.      * @param {Object} result JSON response of the request. {@code null} if
  6820.      *     status was not 200.
  6821.      * @return {!cr.Event} Google Cloud Print interface error event.
  6822.      * @private
  6823.      */
  6824.     createErrorEvent_: function(type, status, result) {
  6825.       var errorEvent = new cr.Event(type);
  6826.       errorEvent.status = status;
  6827.       errorEvent.errorCode = status == 200 ? result['errorCode'] : 0;
  6828.       errorEvent.message = status == 200 ? result['message'] : '';
  6829.       return errorEvent;
  6830.     },
  6831.  
  6832.     /**
  6833.      * Called when the ready-state of a XML http request changes.
  6834.      * Calls the successCallback with the result or dispatches an ERROR event.
  6835.      * @param {XMLHttpRequest} xhr XML http request that changed.
  6836.      * @param {function(number, Object)} callback Callback to invoke when
  6837.      *     request completes.
  6838.      * @private
  6839.      */
  6840.     onReadyStateChange_: function(xhr, callback) {
  6841.       if (xhr.readyState == 4) {
  6842.         if (xhr.status == 200) {
  6843.           var result = JSON.parse(xhr.responseText);
  6844.           if (result['success']) {
  6845.             this.xsrfToken_ = result['xsrf_token'];
  6846.           }
  6847.         }
  6848.         callback(xhr.status, result);
  6849.       }
  6850.     },
  6851.  
  6852.     /**
  6853.      * Called when the search request completes.
  6854.      * @param {boolean} isRecent Whether the search request was for recent
  6855.      *     destinations.
  6856.      * @param {number} status Status of the HTTP request.
  6857.      * @param {Object} result JSON response.
  6858.      * @private
  6859.      */
  6860.     onSearchDone_: function(isRecent, status, result) {
  6861.       if (status == 200 && result['success']) {
  6862.         var printerListJson = result['printers'] || [];
  6863.         var printerList = [];
  6864.         printerListJson.forEach(function(printerJson) {
  6865.           try {
  6866.             printerList.push(
  6867.                 cloudprint.CloudDestinationParser.parse(printerJson));
  6868.           } catch (err) {
  6869.             console.error('Unable to parse cloud print destination: ' + err);
  6870.           }
  6871.         });
  6872.         var searchDoneEvent =
  6873.             new cr.Event(CloudPrintInterface.EventType.SEARCH_DONE);
  6874.         searchDoneEvent.printers = printerList;
  6875.         searchDoneEvent.isRecent = isRecent;
  6876.         searchDoneEvent.email = result['request']['user'];
  6877.         this.dispatchEvent(searchDoneEvent);
  6878.       } else {
  6879.         var errorEvent = this.createErrorEvent_(
  6880.             CloudPrintInterface.EventType.SEARCH_FAILED, status, result);
  6881.         this.dispatchEvent(errorEvent);
  6882.       }
  6883.     },
  6884.  
  6885.     /**
  6886.      * Called when the submit request completes.
  6887.      * @param {number} status Status of the HTTP request.
  6888.      * @param {Object} result JSON response.
  6889.      * @private
  6890.      */
  6891.     onSubmitDone_: function(status, result) {
  6892.       if (status == 200 && result['success']) {
  6893.         var submitDoneEvent = new cr.Event(
  6894.             CloudPrintInterface.EventType.SUBMIT_DONE);
  6895.         submitDoneEvent.jobId = result['job']['id'];
  6896.         this.dispatchEvent(submitDoneEvent);
  6897.       } else {
  6898.         var errorEvent = this.createErrorEvent_(
  6899.             CloudPrintInterface.EventType.SUBMIT_FAILED, status, result);
  6900.         this.dispatchEvent(errorEvent);
  6901.       }
  6902.     },
  6903.  
  6904.     /**
  6905.      * Called when the printer request completes.
  6906.      * @param {string} destinationId ID of the destination that was looked up.
  6907.      * @param {number} status Status of the HTTP request.
  6908.      * @param {Object} result JSON response.
  6909.      * @private
  6910.      */
  6911.     onPrinterDone_: function(destinationId, status, result) {
  6912.       if (status == 200 && result['success']) {
  6913.         var printerJson = result['printers'][0];
  6914.         var printer;
  6915.         try {
  6916.           printer = cloudprint.CloudDestinationParser.parse(printerJson);
  6917.         } catch (err) {
  6918.           console.error('Failed to parse cloud print destination: ' +
  6919.               JSON.stringify(printerJson));
  6920.           return;
  6921.         }
  6922.         var printerDoneEvent =
  6923.             new cr.Event(CloudPrintInterface.EventType.PRINTER_DONE);
  6924.         printerDoneEvent.printer = printer;
  6925.         this.dispatchEvent(printerDoneEvent);
  6926.       } else {
  6927.         var errorEvent = this.createErrorEvent_(
  6928.             CloudPrintInterface.EventType.PRINTER_FAILED, status, result);
  6929.         errorEvent.destinationId = destinationId;
  6930.         this.dispatchEvent(errorEvent);
  6931.       }
  6932.     },
  6933.  
  6934.     /**
  6935.      * Called when the update printer TOS acceptance request completes.
  6936.      * @param {number} status Status of the HTTP request.
  6937.      * @param {Object} result JSON response.
  6938.      * @private
  6939.      */
  6940.     onUpdatePrinterTosAcceptanceDone_: function(status, result) {
  6941.       if (status == 200 && result['success']) {
  6942.         // Do nothing.
  6943.       } else {
  6944.         var errorEvent = this.createErrorEvent_(
  6945.             CloudPrintInterface.EventType.SUBMIT_FAILED, status, result);
  6946.         this.dispatchEvent(errorEvent);
  6947.       }
  6948.     }
  6949.   };
  6950.  
  6951.   /**
  6952.    * Data structure that represents an HTTP parameter.
  6953.    * @param {string} name Name of the parameter.
  6954.    * @param {string} value Value of the parameter.
  6955.    * @constructor
  6956.    */
  6957.   function HttpParam(name, value) {
  6958.     /**
  6959.      * Name of the parameter.
  6960.      * @type {string}
  6961.      */
  6962.     this.name = name;
  6963.  
  6964.     /**
  6965.      * Name of the value.
  6966.      * @type {string}
  6967.      */
  6968.     this.value = value;
  6969.   };
  6970.  
  6971.   // Export
  6972.   return {
  6973.     CloudPrintInterface: CloudPrintInterface
  6974.   };
  6975. });
  6976.  
  6977. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  6978. // Use of this source code is governed by a BSD-style license that can be
  6979. // found in the LICENSE file.
  6980.  
  6981. /**
  6982.  * @param {string} toTest The string to be tested.
  6983.  * @return {boolean} True if |toTest| contains only digits. Leading and trailing
  6984.  *     whitespace is allowed.
  6985.  */
  6986. function isInteger(toTest) {
  6987.   var numericExp = /^\s*[0-9]+\s*$/;
  6988.   return numericExp.test(toTest);
  6989. }
  6990.  
  6991. /**
  6992.  * Returns true if |value| is a valid non zero positive integer.
  6993.  * @param {string} value The string to be tested.
  6994.  *
  6995.  * @return {boolean} true if the |value| is valid non zero positive integer.
  6996.  */
  6997. function isPositiveInteger(value) {
  6998.   return isInteger(value) && parseInt(value, 10) > 0;
  6999. }
  7000.  
  7001. /**
  7002.  * Returns true if the contents of the two arrays are equal.
  7003.  * @param {Array} array1 The first array.
  7004.  * @param {Array} array2 The second array.
  7005.  *
  7006.  * @return {boolean} true if the arrays are equal.
  7007.  */
  7008. function areArraysEqual(array1, array2) {
  7009.   if (array1.length != array2.length)
  7010.     return false;
  7011.   for (var i = 0; i < array1.length; i++)
  7012.     if (array1[i] !== array2[i])
  7013.       return false;
  7014.   return true;
  7015. }
  7016.  
  7017. /**
  7018.  * Removes duplicate elements from |inArray| and returns a new array.
  7019.  * |inArray| is not affected. It assumes that |inArray| is already sorted.
  7020.  * @param {Array.<number>} inArray The array to be processed.
  7021.  * @return {Array.<number>} The array after processing.
  7022.  */
  7023. function removeDuplicates(inArray) {
  7024.   var out = [];
  7025.  
  7026.   if (inArray.length == 0)
  7027.     return out;
  7028.  
  7029.   out.push(inArray[0]);
  7030.   for (var i = 1; i < inArray.length; ++i)
  7031.     if (inArray[i] != inArray[i - 1])
  7032.       out.push(inArray[i]);
  7033.   return out;
  7034. }
  7035.  
  7036. /**
  7037.  * Checks if |pageRangeText| represents a valid page selection.
  7038.  * A valid selection has a parsable format and every page identifier is
  7039.  * <= |totalPageCount| unless wildcards are used (see examples).
  7040.  * Example: "1-4, 9, 3-6, 10, 11" is valid, assuming |totalPageCount| >= 11.
  7041.  * Example: "1-4, 6-6" is valid, assuming |totalPageCount| >= 6.
  7042.  * Example: "2-" is valid, assuming |totalPageCount| >= 2, means from 2 to the
  7043.  * end.
  7044.  * Example: "1-10000" is valid, regardless of |totalPageCount|, means from 1 to
  7045.  * the end if |totalPageCount| < 10000.
  7046.  * Example: "1-4dsf, 11" is invalid regardless of |totalPageCount|.
  7047.  * Example: "4-2, 11, -6" is invalid regardless of |totalPageCount|.
  7048.  *
  7049.  * Note: If |totalPageCount| is undefined the validation does not take
  7050.  * |totalPageCount| into account.
  7051.  * Example: "34853253" is valid.
  7052.  * Example: "1-4, 9, 3-6, 10, 11" is valid.
  7053.  *
  7054.  * @param {string} pageRangeText The text to be checked.
  7055.  * @param {number} totalPageCount The total number of pages.
  7056.  * @return {boolean} true if the |pageRangeText| is valid.
  7057.  */
  7058. function isPageRangeTextValid(pageRangeText, totalPageCount) {
  7059.   var regex = /^\s*([0-9]+)\s*-\s*([0-9]*)\s*$/;
  7060.   var successfullyParsed = 0;
  7061.  
  7062.   // Splitting around commas.
  7063.   var parts = pageRangeText.split(/,/);
  7064.  
  7065.   for (var i = 0; i < parts.length; ++i) {
  7066.     // Removing whitespace.
  7067.     parts[i] = parts[i].replace(/\s*/g, '');
  7068.     var match = parts[i].match(regex);
  7069.     if (parts[i].length == 0)
  7070.       continue;
  7071.  
  7072.     if (match && match[1] && isPositiveInteger(match[1])) {
  7073.       var from = parseInt(match[1], 10);
  7074.       var to = isPositiveInteger(match[2]) ? parseInt(match[2], 10) :
  7075.           totalPageCount;
  7076.       if (from > to || from > totalPageCount)
  7077.         return false;
  7078.     } else if (!isPositiveInteger(parts[i]) || (totalPageCount != -1 &&
  7079.           parseInt(parts[i], 10) > totalPageCount)) {
  7080.       return false;
  7081.     }
  7082.     successfullyParsed++;
  7083.   }
  7084.   return successfullyParsed > 0;
  7085. }
  7086.  
  7087. /**
  7088.  * Returns a list of all pages specified in |pagesRangeText|. The pages are
  7089.  * listed in the order they appear in |pageRangeText| and duplicates are not
  7090.  * eliminated. If |pageRangeText| is not valid according to
  7091.  * isPageRangeTextValid(), or |totalPageCount| is undefined an empty list is
  7092.  * returned.
  7093.  * @param {string} pageRangeText The text to be checked.
  7094.  * @param {number} totalPageCount The total number of pages.
  7095.  * @return {Array.<number>} A list of all pages.
  7096.  */
  7097. function pageRangeTextToPageList(pageRangeText, totalPageCount) {
  7098.   var pageList = [];
  7099.   if ((totalPageCount &&
  7100.        !isPageRangeTextValid(pageRangeText, totalPageCount)) ||
  7101.       !totalPageCount) {
  7102.     return pageList;
  7103.   }
  7104.  
  7105.   var regex = /^\s*([0-9]+)\s*-\s*([0-9]*)\s*$/;
  7106.   var parts = pageRangeText.split(/,/);
  7107.  
  7108.   for (var i = 0; i < parts.length; ++i) {
  7109.     var match = parts[i].match(regex);
  7110.  
  7111.     if (match && match[1]) {
  7112.       var from = parseInt(match[1], 10);
  7113.       var to = match[2] ? parseInt(match[2], 10) : totalPageCount;
  7114.  
  7115.       for (var j = from; j <= Math.min(to, totalPageCount); ++j)
  7116.         pageList.push(j);
  7117.     } else {
  7118.       var singlePageNumber = parseInt(parts[i], 10);
  7119.       if (isPositiveInteger(singlePageNumber.toString()) &&
  7120.           singlePageNumber <= totalPageCount) {
  7121.         pageList.push(singlePageNumber);
  7122.       }
  7123.     }
  7124.   }
  7125.   return pageList;
  7126. }
  7127.  
  7128. /**
  7129.  * @param {Array.<number>} pageList The list to be processed.
  7130.  * @return {Array.<number>} The contents of |pageList| in ascending order and
  7131.  *     without any duplicates. |pageList| is not affected.
  7132.  */
  7133. function pageListToPageSet(pageList) {
  7134.   var pageSet = [];
  7135.   if (pageList.length == 0)
  7136.     return pageSet;
  7137.   pageSet = pageList.slice(0);
  7138.   pageSet.sort(function(a, b) {
  7139.     return (/** @type {number} */ a) - (/** @type {number} */ b);
  7140.   });
  7141.   pageSet = removeDuplicates(pageSet);
  7142.   return pageSet;
  7143. }
  7144.  
  7145. /**
  7146.  * Converts |pageSet| to page ranges. It squashes whenever possible.
  7147.  * Example: '1-2,3,5-7' becomes 1-3,5-7.
  7148.  *
  7149.  * @param {Array.<number>} pageSet The set of pages to be processed. Callers
  7150.  *     should ensure that no duplicates exist.
  7151.  * @return {Array.<{from: number, to: number}>} An array of page range objects.
  7152.  */
  7153. function pageSetToPageRanges(pageSet) {
  7154.   var pageRanges = [];
  7155.   for (var i = 0; i < pageSet.length; ++i) {
  7156.     var tempFrom = pageSet[i];
  7157.     while (i + 1 < pageSet.length && pageSet[i + 1] == pageSet[i] + 1)
  7158.       ++i;
  7159.     var tempTo = pageSet[i];
  7160.     pageRanges.push({'from': tempFrom, 'to': tempTo});
  7161.   }
  7162.   return pageRanges;
  7163. }
  7164.  
  7165. /**
  7166.  * @param {!HTMLElement} element Element to check for visibility.
  7167.  * @return {boolean} Whether the given element is visible.
  7168.  */
  7169. function getIsVisible(element) {
  7170.   return !element.hidden;
  7171. }
  7172.  
  7173. /**
  7174.  * Shows or hides an element.
  7175.  * @param {!HTMLElement} element Element to show or hide.
  7176.  * @param {boolean} isVisible Whether the element should be visible or not.
  7177.  */
  7178. function setIsVisible(element, isVisible) {
  7179.   element.hidden = !isVisible;
  7180. }
  7181.  
  7182. /**
  7183.  * @param {!Array} array Array to check for item.
  7184.  * @param {*} item Item to look for in array.
  7185.  * @return {boolean} Whether the item is in the array.
  7186.  */
  7187. function arrayContains(array, item) {
  7188.   return array.indexOf(item) != -1;
  7189. }
  7190.  
  7191. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7192. // Use of this source code is governed by a BSD-style license that can be
  7193. // found in the LICENSE file.
  7194.  
  7195. cr.define('print_preview', function() {
  7196.   'use strict';
  7197.  
  7198.   /**
  7199.    * Creates a PrintHeader object. This object encapsulates all the elements
  7200.    * and logic related to the top part of the left pane in print_preview.html.
  7201.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to read
  7202.    *     information about the document.
  7203.    * @param {!print_preview.DestinationStore} destinationStore Used to get the
  7204.    *     selected destination.
  7205.    * @constructor
  7206.    * @extends {print_preview.Component}
  7207.    */
  7208.   function PrintHeader(printTicketStore, destinationStore) {
  7209.     print_preview.Component.call(this);
  7210.  
  7211.     /**
  7212.      * Used to read information about the document.
  7213.      * @type {!print_preview.PrintTicketStore}
  7214.      * @private
  7215.      */
  7216.     this.printTicketStore_ = printTicketStore;
  7217.  
  7218.     /**
  7219.      * Used to get the selected destination.
  7220.      * @type {!print_preview.DestinationStore}
  7221.      * @private
  7222.      */
  7223.     this.destinationStore_ = destinationStore;
  7224.  
  7225.     /**
  7226.      * Whether the component is enabled.
  7227.      * @type {boolean}
  7228.      * @private
  7229.      */
  7230.     this.isEnabled_ = true;
  7231.   };
  7232.  
  7233.   /**
  7234.    * Event types dispatched by the print header.
  7235.    * @enum {string}
  7236.    */
  7237.   PrintHeader.EventType = {
  7238.     PRINT_BUTTON_CLICK: 'print_preview.PrintHeader.PRINT_BUTTON_CLICK',
  7239.     CANCEL_BUTTON_CLICK: 'print_preview.PrintHeader.CANCEL_BUTTON_CLICK'
  7240.   },
  7241.  
  7242.   /**
  7243.    * CSS classes used by the print header.
  7244.    * @enum {string}
  7245.    * @private
  7246.    */
  7247.   PrintHeader.Classes_ = {
  7248.     CANCEL_BUTTON: 'print-header-cancel-button',
  7249.     PRINT_BUTTON: 'print-header-print-button',
  7250.     SUMMARY: 'print-header-summary'
  7251.   };
  7252.  
  7253.   PrintHeader.prototype = {
  7254.     __proto__: print_preview.Component.prototype,
  7255.  
  7256.     set isEnabled(isEnabled) {
  7257.       this.isEnabled_ = isEnabled;
  7258.       this.printButton_.disabled = !isEnabled;
  7259.       this.cancelButton_.disabled = !isEnabled;
  7260.     },
  7261.  
  7262.     /** @param {string} message Error message to display in the print header. */
  7263.     setErrorMessage: function(message) {
  7264.       var summaryEl = this.getElement().getElementsByClassName(
  7265.           PrintHeader.Classes_.SUMMARY)[0];
  7266.       summaryEl.innerHTML = '';
  7267.       summaryEl.textContent = message;
  7268.     },
  7269.  
  7270.     /** @override */
  7271.     enterDocument: function() {
  7272.       print_preview.Component.prototype.enterDocument.call(this);
  7273.  
  7274.       // User events
  7275.       this.tracker.add(
  7276.           this.cancelButton_, 'click', this.onCancelButtonClick_.bind(this));
  7277.       this.tracker.add(
  7278.           this.printButton_, 'click', this.onPrintButtonClick_.bind(this));
  7279.  
  7280.       // Data events.
  7281.       this.tracker.add(
  7282.           this.printTicketStore_,
  7283.           print_preview.PrintTicketStore.EventType.INITIALIZE,
  7284.           this.onTicketChange_.bind(this));
  7285.       this.tracker.add(
  7286.           this.printTicketStore_,
  7287.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  7288.           this.onTicketChange_.bind(this));
  7289.       this.tracker.add(
  7290.           this.printTicketStore_,
  7291.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  7292.           this.onTicketChange_.bind(this));
  7293.       this.tracker.add(
  7294.           this.destinationStore_,
  7295.           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
  7296.           this.onDestinationSelect_.bind(this));
  7297.     },
  7298.  
  7299.     /**
  7300.      * @return {Element} Print button element.
  7301.      * @private
  7302.      */
  7303.     get printButton_() {
  7304.       return this.getElement().getElementsByClassName(
  7305.           PrintHeader.Classes_.PRINT_BUTTON)[0];
  7306.     },
  7307.  
  7308.     /**
  7309.      * @return {Element} Cancel button element.
  7310.      * @private
  7311.      */
  7312.     get cancelButton_() {
  7313.       return this.getElement().getElementsByClassName(
  7314.           PrintHeader.Classes_.CANCEL_BUTTON)[0];
  7315.     },
  7316.  
  7317.     /**
  7318.      * Updates the summary element based on the currently selected user options.
  7319.      * @private
  7320.      */
  7321.     updateSummary_: function() {
  7322.       var summaryEl = this.getElement().getElementsByClassName(
  7323.           PrintHeader.Classes_.SUMMARY)[0];
  7324.       if (!this.printTicketStore_.isTicketValid()) {
  7325.         summaryEl.innerHTML = '';
  7326.         return;
  7327.       }
  7328.  
  7329.       var summaryLabel =
  7330.           localStrings.getString('printPreviewSheetsLabelSingular');
  7331.       var pagesLabel = localStrings.getString('printPreviewPageLabelPlural');
  7332.  
  7333.       var saveToPdf = this.destinationStore_.selectedDestination &&
  7334.           this.destinationStore_.selectedDestination.id ==
  7335.               print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
  7336.       if (saveToPdf) {
  7337.         summaryLabel = localStrings.getString('printPreviewPageLabelSingular');
  7338.       }
  7339.  
  7340.       var numPages = this.printTicketStore_.getPageNumberSet().size;
  7341.       var numSheets = numPages;
  7342.       if (!saveToPdf && this.printTicketStore_.isDuplexEnabled()) {
  7343.         numSheets = Math.ceil(numPages / 2);
  7344.       }
  7345.  
  7346.       var copies = this.printTicketStore_.getCopies();
  7347.       numSheets *= copies;
  7348.       numPages *= copies;
  7349.  
  7350.       if (numSheets > 1) {
  7351.         summaryLabel = saveToPdf ? pagesLabel :
  7352.             localStrings.getString('printPreviewSheetsLabelPlural');
  7353.       }
  7354.  
  7355.       var html;
  7356.       if (numPages != numSheets) {
  7357.         html = localStrings.getStringF('printPreviewSummaryFormatLong',
  7358.                                        '<b>' + numSheets + '</b>',
  7359.                                        '<b>' + summaryLabel + '</b>',
  7360.                                        numPages,
  7361.                                        pagesLabel);
  7362.       } else {
  7363.         html = localStrings.getStringF('printPreviewSummaryFormatShort',
  7364.                                        '<b>' + numSheets + '</b>',
  7365.                                        '<b>' + summaryLabel + '</b>');
  7366.       }
  7367.  
  7368.       // Removing extra spaces from within the string.
  7369.       html = html.replace(/\s{2,}/g, ' ');
  7370.       summaryEl.innerHTML = html;
  7371.     },
  7372.  
  7373.     /**
  7374.      * Called when the print button is clicked. Dispatches a PRINT_DOCUMENT
  7375.      * common event.
  7376.      * @private
  7377.      */
  7378.     onPrintButtonClick_: function() {
  7379.       if (this.destinationStore_.selectedDestination.id !=
  7380.           print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) {
  7381.         this.printButton_.classList.add('loading');
  7382.         this.cancelButton_.classList.add('loading');
  7383.         var summaryEl = this.getElement().getElementsByClassName(
  7384.             PrintHeader.Classes_.SUMMARY)[0];
  7385.         summaryEl.innerHTML = localStrings.getString('printing');
  7386.       }
  7387.       cr.dispatchSimpleEvent(this, PrintHeader.EventType.PRINT_BUTTON_CLICK);
  7388.     },
  7389.  
  7390.     /**
  7391.      * Called when the cancel button is clicked. Dispatches a
  7392.      * CLOSE_PRINT_PREVIEW event.
  7393.      * @private
  7394.      */
  7395.     onCancelButtonClick_: function() {
  7396.       cr.dispatchSimpleEvent(this, PrintHeader.EventType.CANCEL_BUTTON_CLICK);
  7397.     },
  7398.  
  7399.     /**
  7400.      * Called when a new destination is selected. Updates the text on the print
  7401.      * button.
  7402.      * @private
  7403.      */
  7404.     onDestinationSelect_: function() {
  7405.       if (this.destinationStore_.selectedDestination.id ==
  7406.               print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ||
  7407.           this.destinationStore_.selectedDestination.id ==
  7408.               print_preview.Destination.GooglePromotedId.DOCS) {
  7409.         this.printButton_.textContent = localStrings.getString('saveButton');
  7410.       } else {
  7411.         this.printButton_.textContent = localStrings.getString('printButton');
  7412.       }
  7413.       this.printButton_.focus();
  7414.     },
  7415.  
  7416.     /**
  7417.      * Called when the print ticket has changed. Disables the print button if
  7418.      * any of the settings are invalid.
  7419.      * @private
  7420.      */
  7421.     onTicketChange_: function() {
  7422.       this.printButton_.disabled =
  7423.           !this.printTicketStore_.isTicketValid() ||
  7424.           !this.isEnabled_;
  7425.       this.updateSummary_();
  7426.       if (document.activeElement == null ||
  7427.           document.activeElement == document.body) {
  7428.         this.printButton_.focus();
  7429.       }
  7430.     }
  7431.   };
  7432.  
  7433.   // Export
  7434.   return {
  7435.     PrintHeader: PrintHeader
  7436.   };
  7437. });
  7438.  
  7439. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7440. // Use of this source code is governed by a BSD-style license that can be
  7441. // found in the LICENSE file.
  7442.  
  7443. cr.define('print_preview', function() {
  7444.   'use strict';
  7445.  
  7446.   /**
  7447.    * Object used to measure usage statistics.
  7448.    * @constructor
  7449.    */
  7450.   function Metrics() {};
  7451.  
  7452.   /**
  7453.    * Enumeration of metrics bucket groups. Each group describes a set of events
  7454.    * that can happen in order. This implies that an event cannot occur twice and
  7455.    * an event that occurs semantically before another event, should not occur
  7456.    * after.
  7457.    * @enum {number}
  7458.    */
  7459.   Metrics.BucketGroup = {
  7460.     DESTINATION_SEARCH: 0,
  7461.     GCP_PROMO: 1
  7462.   };
  7463.  
  7464.   /**
  7465.    * Enumeration of buckets that a user can enter while using the destination
  7466.    * search widget.
  7467.    * @enum {number}
  7468.    */
  7469.   Metrics.DestinationSearchBucket = {
  7470.     // Used when the print destination search widget is shown.
  7471.     SHOWN: 0,
  7472.     // Used when the user selects a print destination.
  7473.     DESTINATION_SELECTED: 1,
  7474.     // Used when the print destination search widget is closed without selecting
  7475.     // a print destination.
  7476.     CANCELED: 2,
  7477.     // Used when the Google Cloud Print promotion (shown in the destination
  7478.     // search widget) is shown to the user.
  7479.     CLOUDPRINT_PROMO_SHOWN: 3,
  7480.     // Used when the user chooses to sign-in to their Google account.
  7481.     SIGNIN_TRIGGERED: 4
  7482.   };
  7483.  
  7484.   /**
  7485.    * Enumeration of buckets that a user can enter while using the Google Cloud
  7486.    * Print promotion.
  7487.    * @enum {number}
  7488.    */
  7489.   Metrics.GcpPromoBucket = {
  7490.     // Used when the Google Cloud Print pomotion (shown above the pdf preview
  7491.     // plugin) is shown to the user.
  7492.     SHOWN: 0,
  7493.     // Used when the user clicks the "Get started" link in the promotion shown
  7494.     // in CLOUDPRINT_BIG_PROMO_SHOWN.
  7495.     CLICKED: 1,
  7496.     // Used when the user dismisses the promotion shown in
  7497.     // CLOUDPRINT_BIG_PROMO_SHOWN.
  7498.     DISMISSED: 2
  7499.   };
  7500.  
  7501.   /**
  7502.    * Name of the C++ function to call to increment bucket counts.
  7503.    * @type {string}
  7504.    * @const
  7505.    * @private
  7506.    */
  7507.   Metrics.NATIVE_FUNCTION_NAME_ = 'reportUiEvent';
  7508.  
  7509.   Metrics.prototype = {
  7510.     /**
  7511.      * Increments the counter of a destination-search bucket.
  7512.      * @param {!Metrics.DestinationSearchBucket} bucket Bucket to increment.
  7513.      */
  7514.     incrementDestinationSearchBucket: function(bucket) {
  7515.       chrome.send(Metrics.NATIVE_FUNCTION_NAME_,
  7516.                   [Metrics.BucketGroup.DESTINATION_SEARCH, bucket]);
  7517.     },
  7518.  
  7519.     /**
  7520.      * Increments the counter of a gcp-promo bucket.
  7521.      * @param {!Metrics.GcpPromoBucket} bucket Bucket to increment.
  7522.      */
  7523.     incrementGcpPromoBucket: function(bucket) {
  7524.       chrome.send(Metrics.NATIVE_FUNCTION_NAME_,
  7525.                   [Metrics.BucketGroup.GCP_PROMO, bucket]);
  7526.     }
  7527.   };
  7528.  
  7529.   // Export
  7530.   return {
  7531.     Metrics: Metrics
  7532.   };
  7533. });
  7534.  
  7535.  
  7536. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7537. // Use of this source code is governed by a BSD-style license that can be
  7538. // found in the LICENSE file.
  7539.  
  7540. cr.define('print_preview', function() {
  7541.   'use strict';
  7542.  
  7543.   /**
  7544.    * Creates a PageSettings object. This object encapsulates all settings and
  7545.    * logic related to page selection.
  7546.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to read and
  7547.    *     write page range settings.
  7548.    * @constructor
  7549.    * @extends {print_preview.Component}
  7550.    */
  7551.   function PageSettings(printTicketStore) {
  7552.     print_preview.Component.call(this);
  7553.  
  7554.     /**
  7555.      * Used to read and write page range settings.
  7556.      * @type {!print_preview.PrintTicketStore}
  7557.      * @private
  7558.      */
  7559.     this.printTicketStore_ = printTicketStore;
  7560.  
  7561.     /**
  7562.      * Timeout used to delay processing of the custom page range input.
  7563.      * @type {?number}
  7564.      * @private
  7565.      */
  7566.     this.customInputTimeout_ = null;
  7567.  
  7568.     /**
  7569.      * Custom page range input.
  7570.      * @type {HTMLInputElement}
  7571.      * @private
  7572.      */
  7573.     this.customInput_ = null;
  7574.  
  7575.     /**
  7576.      * Custom page range radio button.
  7577.      * @type {HTMLInputElement}
  7578.      * @private
  7579.      */
  7580.     this.customRadio_ = null;
  7581.  
  7582.     /**
  7583.      * All page rage radio button.
  7584.      * @type {HTMLInputElement}
  7585.      * @private
  7586.      */
  7587.     this.allRadio_ = null;
  7588.  
  7589.     /**
  7590.      * Container of a hint to show when the custom page range is invalid.
  7591.      * @type {HTMLElement}
  7592.      * @private
  7593.      */
  7594.     this.customHintEl_ = null;
  7595.   };
  7596.  
  7597.   /**
  7598.    * CSS classes used by the page settings.
  7599.    * @enum {string}
  7600.    * @private
  7601.    */
  7602.   PageSettings.Classes_ = {
  7603.     ALL_RADIO: 'page-settings-all-radio',
  7604.     CUSTOM_HINT: 'page-settings-custom-hint',
  7605.     CUSTOM_INPUT: 'page-settings-custom-input',
  7606.     CUSTOM_RADIO: 'page-settings-custom-radio'
  7607.   };
  7608.  
  7609.   /**
  7610.    * Delay in milliseconds before processing custom page range input.
  7611.    * @type {number}
  7612.    * @private
  7613.    */
  7614.   PageSettings.CUSTOM_INPUT_DELAY_ = 500;
  7615.  
  7616.   PageSettings.prototype = {
  7617.     __proto__: print_preview.Component.prototype,
  7618.  
  7619.     set isEnabled(isEnabled) {
  7620.       this.customInput_.disabled = !isEnabled;
  7621.       this.allRadio_.disabled = !isEnabled;
  7622.       this.customRadio_.disabled = !isEnabled;
  7623.     },
  7624.  
  7625.     /** @override */
  7626.     enterDocument: function() {
  7627.       print_preview.Component.prototype.enterDocument.call(this);
  7628.       this.tracker.add(
  7629.           this.allRadio_, 'click', this.onAllRadioClick_.bind(this));
  7630.       this.tracker.add(
  7631.           this.customRadio_, 'click', this.onCustomRadioClick_.bind(this));
  7632.       this.tracker.add(
  7633.           this.customInput_, 'blur', this.onCustomInputBlur_.bind(this));
  7634.       this.tracker.add(
  7635.           this.customInput_, 'keyup', this.onCustomInputKeyUp_.bind(this));
  7636.       this.tracker.add(
  7637.           this.printTicketStore_,
  7638.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  7639.           this.onPrintTicketStoreChange_.bind(this));
  7640.       this.tracker.add(
  7641.           this.printTicketStore_,
  7642.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  7643.           this.onPrintTicketStoreChange_.bind(this));
  7644.       this.tracker.add(
  7645.           this.printTicketStore_,
  7646.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  7647.           this.onPrintTicketStoreChange_.bind(this));
  7648.     },
  7649.  
  7650.     /** @override */
  7651.     exitDocument: function() {
  7652.       print_preview.Component.prototype.exitDocument.call(this);
  7653.       this.customInput_ = null;
  7654.       this.customRadio_ = null;
  7655.       this.allRadio_ = null;
  7656.       this.customHintEl_ = null;
  7657.     },
  7658.  
  7659.     /** @override */
  7660.     decorateInternal: function() {
  7661.       this.customInput_ = this.getElement().getElementsByClassName(
  7662.           PageSettings.Classes_.CUSTOM_INPUT)[0];
  7663.       this.allRadio_ = this.getElement().getElementsByClassName(
  7664.           PageSettings.Classes_.ALL_RADIO)[0];
  7665.       this.customRadio_ = this.getElement().getElementsByClassName(
  7666.           PageSettings.Classes_.CUSTOM_RADIO)[0];
  7667.       this.customHintEl_ = this.getElement().getElementsByClassName(
  7668.           PageSettings.Classes_.CUSTOM_HINT)[0];
  7669.       this.customHintEl_.textContent = localStrings.getStringF(
  7670.           'pageRangeInstruction',
  7671.           localStrings.getString('examplePageRangeText'));
  7672.     },
  7673.  
  7674.     /**
  7675.      * @param {boolean} Whether the custom hint is visible.
  7676.      * @private
  7677.      */
  7678.     setInvalidStateVisible_: function(isVisible) {
  7679.       if (isVisible) {
  7680.         this.customInput_.classList.add('invalid');
  7681.         this.customHintEl_.setAttribute('aria-hidden', 'false');
  7682.         fadeInElement(this.customHintEl_);
  7683.       } else {
  7684.         this.customInput_.classList.remove('invalid');
  7685.         fadeOutElement(this.customHintEl_);
  7686.         this.customHintEl_.setAttribute('aria-hidden', 'true');
  7687.       }
  7688.     },
  7689.  
  7690.     /**
  7691.      * Called when the all radio button is clicked. Updates the print ticket.
  7692.      * @private
  7693.      */
  7694.     onAllRadioClick_: function() {
  7695.       this.printTicketStore_.updatePageRange('');
  7696.     },
  7697.  
  7698.     /**
  7699.      * Called when the custom radio button is clicked. Updates the print ticket.
  7700.      * @private
  7701.      */
  7702.     onCustomRadioClick_: function() {
  7703.       this.customInput_.focus();
  7704.       this.printTicketStore_.updatePageRange(this.customInput_.value);
  7705.     },
  7706.  
  7707.     /**
  7708.      * Called when the custom input is blurred. Enables the all radio button if
  7709.      * the custom input is empty.
  7710.      * @private
  7711.      */
  7712.     onCustomInputBlur_: function() {
  7713.       if (this.customInput_.value == '') {
  7714.         this.allRadio_.checked = true;
  7715.         this.customRadio_.checked = false;
  7716.       }
  7717.     },
  7718.  
  7719.     /**
  7720.      * Called when a key is pressed on the custom input.
  7721.      * @param {Event} event Contains the key that was pressed.
  7722.      * @private
  7723.      */
  7724.     onCustomInputKeyUp_: function(event) {
  7725.       if (this.customInputTimeout_) {
  7726.         clearTimeout(this.customInputTimeout_);
  7727.       }
  7728.       if (event.keyIdentifier == 'Enter') {
  7729.         this.printTicketStore_.updatePageRange(this.customInput_.value);
  7730.       } else {
  7731.         this.allRadio_.checked = false;
  7732.         this.customRadio_.checked = true;
  7733.         this.customInputTimeout_ = setTimeout(
  7734.             this.onCustomInputTimeout_.bind(this),
  7735.             PageSettings.CUSTOM_INPUT_DELAY_);
  7736.       }
  7737.     },
  7738.  
  7739.     /**
  7740.      * Called after a delay following a key press in the custom input.
  7741.      * @private
  7742.      */
  7743.     onCustomInputTimeout_: function() {
  7744.       this.customInputTimeout_ = null;
  7745.       if (this.customRadio_.checked) {
  7746.         this.printTicketStore_.updatePageRange(this.customInput_.value);
  7747.       }
  7748.     },
  7749.  
  7750.     /**
  7751.      * Called when the print ticket changes. Updates the state of the component.
  7752.      * @private
  7753.      */
  7754.     onPrintTicketStoreChange_: function() {
  7755.       if (this.printTicketStore_.hasPageRangeCapability()) {
  7756.         var pageRangeStr = this.printTicketStore_.getPageRangeStr();
  7757.         if (pageRangeStr || this.customRadio_.checked) {
  7758.           if (!document.hasFocus() ||
  7759.               document.activeElement != this.customInput_) {
  7760.             this.customInput_.value = pageRangeStr;
  7761.           }
  7762.           this.customRadio_.checked = true;
  7763.           this.allRadio_.checked = false;
  7764.           this.setInvalidStateVisible_(
  7765.               !this.printTicketStore_.isPageRangeValid());
  7766.         } else {
  7767.           this.allRadio_.checked = true;
  7768.           this.customRadio_.checked = false;
  7769.           this.setInvalidStateVisible_(false);
  7770.         }
  7771.         fadeInOption(this.getElement());
  7772.       } else {
  7773.         fadeOutOption(this.getElement());
  7774.       }
  7775.     }
  7776.   };
  7777.  
  7778.   // Export
  7779.   return {
  7780.     PageSettings: PageSettings
  7781.   };
  7782. });
  7783.  
  7784. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7785. // Use of this source code is governed by a BSD-style license that can be
  7786. // found in the LICENSE file.
  7787.  
  7788. cr.define('print_preview', function() {
  7789.   'use strict';
  7790.  
  7791.   /**
  7792.    * Component that renders the copies settings UI.
  7793.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to read and
  7794.    *     write the copies settings.
  7795.    * @constructor
  7796.    * @extends {print_preview.Component}
  7797.    */
  7798.   function CopiesSettings(printTicketStore) {
  7799.     print_preview.Component.call(this);
  7800.  
  7801.     /**
  7802.      * Used for writing to the print ticket and validating inputted values.
  7803.      * @type {!print_preview.PrintTicketStore}
  7804.      * @private
  7805.      */
  7806.     this.printTicketStore_ = printTicketStore;
  7807.  
  7808.     /**
  7809.      * Timeout used to delay processing of the copies input.
  7810.      * @type {?number}
  7811.      * @private
  7812.      */
  7813.     this.textfieldTimeout_ = null;
  7814.  
  7815.     /**
  7816.      * Whether this component is enabled or not.
  7817.      * @type {boolean}
  7818.      * @private
  7819.      */
  7820.     this.isEnabled_ = true;
  7821.  
  7822.     /**
  7823.      * Textfield for entering copies values.
  7824.      * @type {HTMLInputElement}
  7825.      * @private
  7826.      */
  7827.     this.textfield_ = null;
  7828.  
  7829.     /**
  7830.      * Increment button used to increment the copies value.
  7831.      * @type {HTMLButtonElement}
  7832.      * @private
  7833.      */
  7834.     this.incrementButton_ = null;
  7835.  
  7836.     /**
  7837.      * Decrement button used to decrement the copies value.
  7838.      * @type {HTMLButtonElement}
  7839.      * @private
  7840.      */
  7841.     this.decrementButton_ = null;
  7842.  
  7843.     /**
  7844.      * Container div for the collate checkbox.
  7845.      * @type {HTMLDivElement}
  7846.      * @private
  7847.      */
  7848.     this.collateDiv_ = null;
  7849.  
  7850.     /**
  7851.      * Checkbox used to enable/disable collation.
  7852.      * @type {HTMLInputElement}
  7853.      * @private
  7854.      */
  7855.     this.collateCheckbox_ = null;
  7856.  
  7857.     /**
  7858.      * Hint element used to show a hint when the copies value is invalid.
  7859.      * @type {HTMLElement}
  7860.      * @private
  7861.      */
  7862.     this.hintEl_ = null;
  7863.   };
  7864.  
  7865.   /**
  7866.    * CSS classes used by the component.
  7867.    * @enum {string}
  7868.    * @private
  7869.    */
  7870.   CopiesSettings.Classes_ = {
  7871.     COPIES: 'copies-settings-copies',
  7872.     INCREMENT: 'copies-settings-increment',
  7873.     DECREMENT: 'copies-settings-decrement',
  7874.     HINT: 'copies-settings-hint',
  7875.     COLLATE: 'copies-settings-collate',
  7876.     COLLATE_CHECKBOX: 'copies-settings-collate-checkbox',
  7877.   };
  7878.  
  7879.   /**
  7880.    * Delay in milliseconds before processing the textfield.
  7881.    * @type {number}
  7882.    * @private
  7883.    */
  7884.   CopiesSettings.TEXTFIELD_DELAY_ = 250;
  7885.  
  7886.   CopiesSettings.prototype = {
  7887.     __proto__: print_preview.Component.prototype,
  7888.  
  7889.     /** @param {boolean} isEnabled Whether the copies settings is enabled. */
  7890.     set isEnabled(isEnabled) {
  7891.       this.textfield_.disabled = !isEnabled;
  7892.       this.collateCheckbox_.disabled = !isEnabled;
  7893.       this.isEnabled_ = isEnabled;
  7894.       if (isEnabled) {
  7895.         this.updateState_();
  7896.       } else {
  7897.         this.textfield_.disabled = true;
  7898.         this.incrementButton_.disabled = true;
  7899.         this.decrementButton_.disabled = true;
  7900.       }
  7901.     },
  7902.  
  7903.     /** @override */
  7904.     enterDocument: function() {
  7905.       print_preview.Component.prototype.enterDocument.call(this);
  7906.  
  7907.       this.tracker.add(
  7908.           this.textfield_, 'keyup', this.onTextfieldKeyUp_.bind(this));
  7909.       this.tracker.add(
  7910.           this.textfield_, 'blur', this.onTextfieldBlur_.bind(this));
  7911.       this.tracker.add(
  7912.           this.incrementButton_, 'click', this.onButtonClicked_.bind(this, 1));
  7913.       this.tracker.add(
  7914.           this.decrementButton_, 'click', this.onButtonClicked_.bind(this, -1));
  7915.       this.tracker.add(
  7916.           this.collateCheckbox_,
  7917.           'click',
  7918.           this.onCollateCheckboxClick_.bind(this));
  7919.       this.tracker.add(
  7920.           this.printTicketStore_,
  7921.           print_preview.PrintTicketStore.EventType.INITIALIZE,
  7922.           this.updateState_.bind(this));
  7923.       this.tracker.add(
  7924.           this.printTicketStore_,
  7925.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  7926.           this.updateState_.bind(this));
  7927.       this.tracker.add(
  7928.           this.printTicketStore_,
  7929.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  7930.           this.updateState_.bind(this));
  7931.     },
  7932.  
  7933.     /** @override */
  7934.     exitDocument: function() {
  7935.       print_preview.Component.prototype.exitDocument.call(this);
  7936.       this.textfield_ = null;
  7937.       this.incrementButton_ = null;
  7938.       this.decrementButton_ = null;
  7939.       this.collateDiv_ = null;
  7940.       this.collateCheckbox_ = null;
  7941.       this.hintEl_ = null;
  7942.     },
  7943.  
  7944.     /** @override */
  7945.     decorateInternal: function() {
  7946.       this.textfield_ = this.getElement().getElementsByClassName(
  7947.           CopiesSettings.Classes_.COPIES)[0];
  7948.       this.incrementButton_ = this.getElement().getElementsByClassName(
  7949.           CopiesSettings.Classes_.INCREMENT)[0];
  7950.       this.decrementButton_ = this.getElement().getElementsByClassName(
  7951.           CopiesSettings.Classes_.DECREMENT)[0];
  7952.       this.collateDiv_ = this.getElement().getElementsByClassName(
  7953.           CopiesSettings.Classes_.COLLATE)[0];
  7954.       this.collateCheckbox_ = this.getElement().getElementsByClassName(
  7955.           CopiesSettings.Classes_.COLLATE_CHECKBOX)[0];
  7956.       this.hintEl_ = this.getElement().getElementsByClassName(
  7957.           CopiesSettings.Classes_.HINT)[0];
  7958.     },
  7959.  
  7960.     /**
  7961.      * Updates the state of the copies settings UI controls.
  7962.      * @private
  7963.      */
  7964.     updateState_: function() {
  7965.       if (!this.printTicketStore_.hasCopiesCapability()) {
  7966.         fadeOutOption(this.getElement());
  7967.         return;
  7968.       }
  7969.  
  7970.       if (this.textfield_.value != this.printTicketStore_.getCopiesStr()) {
  7971.         this.textfield_.value = this.printTicketStore_.getCopiesStr();
  7972.       }
  7973.  
  7974.       var currentValueGreaterThan1 = false;
  7975.       if (this.printTicketStore_.isCopiesValid()) {
  7976.         this.textfield_.classList.remove('invalid');
  7977.         fadeOutElement(this.hintEl_);
  7978.         this.hintEl_.setAttribute('aria-hidden', true);
  7979.         var currentValue = parseInt(this.printTicketStore_.getCopiesStr());
  7980.         var currentValueGreaterThan1 = currentValue > 1;
  7981.         this.incrementButton_.disabled =
  7982.             !this.isEnabled_ ||
  7983.             !this.printTicketStore_.isCopiesValidForValue(currentValue + 1);
  7984.         this.decrementButton_.disabled =
  7985.             !this.isEnabled_ ||
  7986.             !this.printTicketStore_.isCopiesValidForValue(currentValue - 1);
  7987.       } else {
  7988.         this.textfield_.classList.add('invalid');
  7989.         this.hintEl_.setAttribute('aria-hidden', false);
  7990.         fadeInElement(this.hintEl_);
  7991.         this.incrementButton_.disabled = true;
  7992.         this.decrementButton_.disabled = true;
  7993.       }
  7994.  
  7995.       if (!(this.collateDiv_.hidden =
  7996.              !this.printTicketStore_.hasCollateCapability() ||
  7997.              !currentValueGreaterThan1)) {
  7998.         this.collateCheckbox_.checked =
  7999.             this.printTicketStore_.isCollateEnabled();
  8000.       }
  8001.  
  8002.       fadeInOption(this.getElement());
  8003.     },
  8004.  
  8005.     /**
  8006.      * Called whenever the increment/decrement buttons are clicked.
  8007.      * @param {number} delta Must be 1 for an increment button click and -1 for
  8008.      *     a decrement button click.
  8009.      * @private
  8010.      */
  8011.     onButtonClicked_: function(delta) {
  8012.       // Assumes text field has a valid number.
  8013.       var newValue = parseInt(this.textfield_.value) + delta;
  8014.       this.printTicketStore_.updateCopies(newValue);
  8015.     },
  8016.  
  8017.     /**
  8018.      * Called after a timeout after user input into the textfield.
  8019.      * @private
  8020.      */
  8021.     onTextfieldTimeout_: function() {
  8022.       if (this.textfield_ != '') {
  8023.         this.printTicketStore_.updateCopies(this.textfield_.value);
  8024.       }
  8025.     },
  8026.  
  8027.     /**
  8028.      * Called when a keyup event occurs on the textfield. Starts an input
  8029.      * timeout.
  8030.      * @param {Event} event Contains the pressed key.
  8031.      * @private
  8032.      */
  8033.     onTextfieldKeyUp_: function(event) {
  8034.       if (this.textfieldTimeout_) {
  8035.         clearTimeout(this.textfieldTimeout_);
  8036.       }
  8037.       this.textfieldTimeout_ = setTimeout(
  8038.           this.onTextfieldTimeout_.bind(this), CopiesSettings.TEXTFIELD_DELAY_);
  8039.     },
  8040.  
  8041.     /**
  8042.      * Called when the focus leaves the textfield. If the textfield is empty,
  8043.      * its value is set to 1.
  8044.      * @private
  8045.      */
  8046.     onTextfieldBlur_: function() {
  8047.       if (this.textfield_.value == '') {
  8048.         this.printTicketStore_.updateCopies('1');
  8049.       }
  8050.     },
  8051.  
  8052.     /**
  8053.      * Called when the collate checkbox is clicked. Updates the print ticket.
  8054.      * @private
  8055.      */
  8056.     onCollateCheckboxClick_: function() {
  8057.       this.printTicketStore_.updateCollate(this.collateCheckbox_.checked);
  8058.     }
  8059.   };
  8060.  
  8061.   // Export
  8062.   return {
  8063.     CopiesSettings: CopiesSettings
  8064.   };
  8065. });
  8066.  
  8067. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8068. // Use of this source code is governed by a BSD-style license that can be
  8069. // found in the LICENSE file.
  8070.  
  8071. cr.define('print_preview', function() {
  8072.   'use strict';
  8073.  
  8074.   /**
  8075.    * Creates a LayoutSettings object. This object encapsulates all settings and
  8076.    * logic related to layout mode (portrait/landscape).
  8077.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the
  8078.    *     layout written to the print ticket.
  8079.    * @constructor
  8080.    * @extends {print_preview.Component}
  8081.    */
  8082.   function LayoutSettings(printTicketStore) {
  8083.     print_preview.Component.call(this);
  8084.  
  8085.     /**
  8086.      * Used to get the layout written to the print ticket.
  8087.      * @type {!print_preview.PrintTicketStore}
  8088.      * @private
  8089.      */
  8090.     this.printTicketStore_ = printTicketStore;
  8091.   };
  8092.  
  8093.   /**
  8094.    * CSS classes used by the layout settings.
  8095.    * @enum {string}
  8096.    * @private
  8097.    */
  8098.   LayoutSettings.Classes_ = {
  8099.     LANDSCAPE_RADIO: 'layout-settings-landscape-radio',
  8100.     PORTRAIT_RADIO: 'layout-settings-portrait-radio'
  8101.   };
  8102.  
  8103.   LayoutSettings.prototype = {
  8104.     __proto__: print_preview.Component.prototype,
  8105.  
  8106.     /** @param {boolean} isEnabled Whether this component is enabled. */
  8107.     set isEnabled(isEnabled) {
  8108.       this.landscapeRadioButton_.disabled = !isEnabled;
  8109.       this.portraitRadioButton_.disabled = !isEnabled;
  8110.     },
  8111.  
  8112.     /** @override */
  8113.     enterDocument: function() {
  8114.       print_preview.Component.prototype.enterDocument.call(this);
  8115.       this.tracker.add(
  8116.           this.portraitRadioButton_,
  8117.           'click',
  8118.           this.onLayoutButtonClick_.bind(this));
  8119.       this.tracker.add(
  8120.           this.landscapeRadioButton_,
  8121.           'click',
  8122.           this.onLayoutButtonClick_.bind(this));
  8123.       this.tracker.add(
  8124.           this.printTicketStore_,
  8125.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  8126.           this.onPrintTicketStoreChange_.bind(this));
  8127.       this.tracker.add(
  8128.           this.printTicketStore_,
  8129.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  8130.           this.onPrintTicketStoreChange_.bind(this));
  8131.       this.tracker.add(
  8132.           this.printTicketStore_,
  8133.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  8134.           this.onPrintTicketStoreChange_.bind(this));
  8135.       this.tracker.add(
  8136.           this.printTicketStore_,
  8137.           print_preview.PrintTicketStore.EventType.INITIALIZE,
  8138.           this.onPrintTicketStoreChange_.bind(this));
  8139.     },
  8140.  
  8141.     /**
  8142.      * @return {HTMLInputElement} The portrait orientation radio button.
  8143.      * @private
  8144.      */
  8145.     get portraitRadioButton_() {
  8146.       return this.getElement().getElementsByClassName(
  8147.           LayoutSettings.Classes_.PORTRAIT_RADIO)[0];
  8148.     },
  8149.  
  8150.     /**
  8151.      * @return {HTMLInputElement} The landscape orientation radio button.
  8152.      * @private
  8153.      */
  8154.     get landscapeRadioButton_() {
  8155.       return this.getElement().getElementsByClassName(
  8156.           LayoutSettings.Classes_.LANDSCAPE_RADIO)[0];
  8157.     },
  8158.  
  8159.     /**
  8160.      * Called when one of the radio buttons is clicked. Updates the print ticket
  8161.      * store.
  8162.      * @private
  8163.      */
  8164.     onLayoutButtonClick_: function() {
  8165.       this.printTicketStore_.updateOrientation(
  8166.           this.landscapeRadioButton_.checked);
  8167.     },
  8168.  
  8169.     /**
  8170.      * Called when the print ticket store changes state. Updates the state of
  8171.      * the radio buttons and hides the setting if necessary.
  8172.      * @private
  8173.      */
  8174.     onPrintTicketStoreChange_: function() {
  8175.       if (this.printTicketStore_.hasOrientationCapability()) {
  8176.         var isLandscapeEnabled = this.printTicketStore_.isLandscapeEnabled();
  8177.         this.portraitRadioButton_.checked = !isLandscapeEnabled;
  8178.         this.landscapeRadioButton_.checked = isLandscapeEnabled;
  8179.         fadeInOption(this.getElement());
  8180.       } else {
  8181.         fadeOutOption(this.getElement());
  8182.       }
  8183.     }
  8184.   };
  8185.  
  8186.   // Export
  8187.   return {
  8188.     LayoutSettings: LayoutSettings
  8189.   };
  8190. });
  8191.  
  8192. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8193. // Use of this source code is governed by a BSD-style license that can be
  8194. // found in the LICENSE file.
  8195.  
  8196. cr.define('print_preview', function() {
  8197.   'use strict';
  8198.  
  8199.   /**
  8200.    * Creates a ColorSettings object. This object encapsulates all settings and
  8201.    * logic related to color selection (color/bw).
  8202.    * @param {!print_preview.PrintTicketStore} printTicketStore Used for writing
  8203.    *     to the print ticket.
  8204.    * @constructor
  8205.    * @extends {print_preview.Component}
  8206.    */
  8207.   function ColorSettings(printTicketStore) {
  8208.     print_preview.Component.call(this);
  8209.  
  8210.     /**
  8211.      * Used for writing to the print ticket.
  8212.      * @type {!print_preview.PrintTicketStore}
  8213.      * @private
  8214.      */
  8215.     this.printTicketStore_ = printTicketStore;
  8216.   };
  8217.  
  8218.   /**
  8219.    * CSS classes used by the color settings.
  8220.    * @enum {string}
  8221.    * @private
  8222.    */
  8223.   ColorSettings.Classes_ = {
  8224.     BW_OPTION: 'color-settings-bw-option',
  8225.     COLOR_OPTION: 'color-settings-color-option'
  8226.   };
  8227.  
  8228.   ColorSettings.prototype = {
  8229.     __proto__: print_preview.Component.prototype,
  8230.  
  8231.     set isEnabled(isEnabled) {
  8232.       this.colorRadioButton_.disabled = !isEnabled;
  8233.       this.bwRadioButton_.disabled = !isEnabled;
  8234.     },
  8235.  
  8236.     /** @override */
  8237.     enterDocument: function() {
  8238.       print_preview.Component.prototype.enterDocument.call(this);
  8239.       this.addEventListeners_();
  8240.     },
  8241.  
  8242.     get colorRadioButton_() {
  8243.       return this.getElement().getElementsByClassName(
  8244.           ColorSettings.Classes_.COLOR_OPTION)[0];
  8245.     },
  8246.  
  8247.     get bwRadioButton_() {
  8248.       return this.getElement().getElementsByClassName(
  8249.           ColorSettings.Classes_.BW_OPTION)[0];
  8250.     },
  8251.  
  8252.     /**
  8253.      * Adding listeners to all targets and UI controls.
  8254.      * @private
  8255.      */
  8256.     addEventListeners_: function() {
  8257.       this.tracker.add(
  8258.           this.colorRadioButton_,
  8259.           'click',
  8260.           this.updatePrintTicket_.bind(this, true));
  8261.       this.tracker.add(
  8262.           this.bwRadioButton_,
  8263.           'click',
  8264.           this.updatePrintTicket_.bind(this, false));
  8265.       this.tracker.add(
  8266.           this.printTicketStore_,
  8267.           print_preview.PrintTicketStore.EventType.INITIALIZE,
  8268.           this.onCapabilitiesChange_.bind(this));
  8269.       this.tracker.add(
  8270.           this.printTicketStore_,
  8271.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  8272.           this.onCapabilitiesChange_.bind(this));
  8273.       this.tracker.add(
  8274.           this.printTicketStore_,
  8275.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  8276.           this.onCapabilitiesChange_.bind(this));
  8277.       this.tracker.add(
  8278.           this.printTicketStore_,
  8279.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  8280.           this.onTicketChange_.bind(this));
  8281.     },
  8282.  
  8283.     /**
  8284.      * Updates print ticket with whether the document should be printed in
  8285.      * color.
  8286.      * @param {boolean} isColor Whether the document should be printed in color.
  8287.      * @private
  8288.      */
  8289.     updatePrintTicket_: function(isColor) {
  8290.       this.printTicketStore_.updateColor(isColor);
  8291.     },
  8292.  
  8293.     /**
  8294.      * Called when the ticket store's capabilities have changed. Shows or hides
  8295.      * the color settings.
  8296.      * @private
  8297.      */
  8298.     onCapabilitiesChange_: function() {
  8299.       if (this.printTicketStore_.hasColorCapability()) {
  8300.         fadeInOption(this.getElement());
  8301.         var isColorEnabled = this.printTicketStore_.isColorEnabled();
  8302.         this.colorRadioButton_.checked = isColorEnabled;
  8303.         this.bwRadioButton_.checked = !isColorEnabled;
  8304.       } else {
  8305.         fadeOutOption(this.getElement());
  8306.       }
  8307.       this.getElement().setAttribute(
  8308.           'aria-hidden', !this.printTicketStore_.hasColorCapability());
  8309.     },
  8310.  
  8311.     onTicketChange_: function() {
  8312.       if (this.printTicketStore_.hasColorCapability()) {
  8313.         var isColorEnabled = this.printTicketStore_.isColorEnabled();
  8314.         this.colorRadioButton_.checked = isColorEnabled;
  8315.         this.bwRadioButton_.checked = !isColorEnabled;
  8316.       }
  8317.     }
  8318.   };
  8319.  
  8320.   // Export
  8321.   return {
  8322.     ColorSettings: ColorSettings
  8323.   };
  8324. });
  8325.  
  8326. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8327. // Use of this source code is governed by a BSD-style license that can be
  8328. // found in the LICENSE file.
  8329.  
  8330. cr.define('print_preview', function() {
  8331.   'use strict';
  8332.  
  8333.   /**
  8334.    * Creates a MarginSettings object. This object encapsulates all settings and
  8335.    * logic related to the margins mode.
  8336.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to get
  8337.    *     ticket margins value.
  8338.    * @constructor
  8339.    * @extends {print_preview.Component}
  8340.    */
  8341.   function MarginSettings(printTicketStore) {
  8342.     print_preview.Component.call(this);
  8343.  
  8344.     /**
  8345.      * Used to get ticket margins value.
  8346.      * @type {!print_preview.PrintTicketStore}
  8347.      * @private
  8348.      */
  8349.     this.printTicketStore_ = printTicketStore;
  8350.   };
  8351.  
  8352.   /**
  8353.    * CSS classes used by the margin settings component.
  8354.    * @enum {string}
  8355.    * @private
  8356.    */
  8357.   MarginSettings.Classes_ = {
  8358.     SELECT: 'margin-settings-select'
  8359.   };
  8360.  
  8361.   MarginSettings.prototype = {
  8362.     __proto__: print_preview.Component.prototype,
  8363.  
  8364.     /** @param {boolean} isEnabled Whether this component is enabled. */
  8365.     set isEnabled(isEnabled) {
  8366.       this.select_.disabled = !isEnabled;
  8367.     },
  8368.  
  8369.     /** @override */
  8370.     enterDocument: function() {
  8371.       print_preview.Component.prototype.enterDocument.call(this);
  8372.       this.tracker.add(
  8373.           this.select_, 'change', this.onSelectChange_.bind(this));
  8374.       this.tracker.add(
  8375.           this.printTicketStore_,
  8376.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  8377.           this.onPrintTicketStoreChange_.bind(this));
  8378.       this.tracker.add(
  8379.           this.printTicketStore_,
  8380.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  8381.           this.onPrintTicketStoreChange_.bind(this));
  8382.       this.tracker.add(
  8383.           this.printTicketStore_,
  8384.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  8385.           this.onPrintTicketStoreChange_.bind(this));
  8386.     },
  8387.  
  8388.     /**
  8389.      * @return {HTMLSelectElement} Select element containing the margin options.
  8390.      * @private
  8391.      */
  8392.     get select_() {
  8393.       return this.getElement().getElementsByClassName(
  8394.           MarginSettings.Classes_.SELECT)[0];
  8395.     },
  8396.  
  8397.     /**
  8398.      * Called when the select element is changed. Updates the print ticket
  8399.      * margin type.
  8400.      * @private
  8401.      */
  8402.     onSelectChange_: function() {
  8403.       var select = this.select_;
  8404.       var marginsType =
  8405.           /** @type {!print_preview.ticket_items.MarginsType.Value} */ (
  8406.               select.selectedIndex);
  8407.       this.printTicketStore_.updateMarginsType(marginsType);
  8408.     },
  8409.  
  8410.     /**
  8411.      * Called when the print ticket store changes. Selects the corresponding
  8412.      * select option.
  8413.      * @private
  8414.      */
  8415.     onPrintTicketStoreChange_: function() {
  8416.       if (this.printTicketStore_.hasMarginsCapability()) {
  8417.         var select = this.select_;
  8418.         var marginsType = this.printTicketStore_.getMarginsType();
  8419.         var selectedMarginsType =
  8420.             /** @type {!print_preview.ticket_items.MarginsType.Value} */ (
  8421.                 select.selectedIndex);
  8422.         if (marginsType != selectedMarginsType) {
  8423.           select.options[selectedMarginsType].selected = false;
  8424.           select.options[marginsType].selected = true;
  8425.         }
  8426.         fadeInOption(this.getElement());
  8427.       } else {
  8428.         fadeOutOption(this.getElement());
  8429.       }
  8430.     }
  8431.   };
  8432.  
  8433.   // Export
  8434.   return {
  8435.     MarginSettings: MarginSettings
  8436.   };
  8437. });
  8438.  
  8439. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8440. // Use of this source code is governed by a BSD-style license that can be
  8441. // found in the LICENSE file.
  8442.  
  8443. cr.define('print_preview', function() {
  8444.   'use strict';
  8445.  
  8446.   // TODO(rltoscano): This class needs a throbber while loading the destination
  8447.   // or another solution is persist the settings of the printer so that next
  8448.   // load is fast.
  8449.  
  8450.   /**
  8451.    * Component used to render the print destination.
  8452.    * @param {!print_preview.DestinationStore} destinationStore Used to determine
  8453.    *     the selected destination.
  8454.    * @constructor
  8455.    * @extends {print_preview.Component}
  8456.    */
  8457.   function DestinationSettings(destinationStore) {
  8458.     print_preview.Component.call(this);
  8459.  
  8460.     /**
  8461.      * Used to determine the selected destination.
  8462.      * @type {!print_preview.DestinationStore}
  8463.      * @private
  8464.      */
  8465.     this.destinationStore_ = destinationStore;
  8466.  
  8467.     /**
  8468.      * Current CSS class of the destination icon.
  8469.      * @type {?DestinationSettings.Classes_}
  8470.      * @private
  8471.      */
  8472.     this.iconClass_ = null;
  8473.   };
  8474.  
  8475.   /**
  8476.    * Event types dispatched by the component.
  8477.    * @enum {string}
  8478.    */
  8479.   DestinationSettings.EventType = {
  8480.     CHANGE_BUTTON_ACTIVATE:
  8481.         'print_preview.DestinationSettings.CHANGE_BUTTON_ACTIVATE'
  8482.   };
  8483.  
  8484.   /**
  8485.    * CSS classes used by the component.
  8486.    * @enum {string}
  8487.    * @private
  8488.    */
  8489.   DestinationSettings.Classes_ = {
  8490.     CHANGE_BUTTON: 'destination-settings-change-button',
  8491.     ICON: 'destination-settings-icon',
  8492.     ICON_CLOUD: 'destination-settings-icon-cloud',
  8493.     ICON_CLOUD_SHARED: 'destination-settings-icon-cloud-shared',
  8494.     ICON_GOOGLE_PROMOTED: 'destination-settings-icon-google-promoted',
  8495.     ICON_LOCAL: 'destination-settings-icon-local',
  8496.     ICON_MOBILE: 'destination-settings-icon-mobile',
  8497.     ICON_MOBILE_SHARED: 'destination-settings-icon-mobile-shared',
  8498.     LOCATION: 'destination-settings-location',
  8499.     NAME: 'destination-settings-name'
  8500.   };
  8501.  
  8502.   DestinationSettings.prototype = {
  8503.     __proto__: print_preview.Component.prototype,
  8504.  
  8505.     /** @param {boolean} Whether the component is enabled. */
  8506.     set isEnabled(isEnabled) {
  8507.       var changeButton = this.getElement().getElementsByClassName(
  8508.           DestinationSettings.Classes_.CHANGE_BUTTON)[0];
  8509.       changeButton.disabled = !isEnabled;
  8510.     },
  8511.  
  8512.     /** @override */
  8513.     enterDocument: function() {
  8514.       print_preview.Component.prototype.enterDocument.call(this);
  8515.       var changeButton = this.getElement().getElementsByClassName(
  8516.           DestinationSettings.Classes_.CHANGE_BUTTON)[0];
  8517.       this.tracker.add(
  8518.           changeButton, 'click', this.onChangeButtonClick_.bind(this));
  8519.       this.tracker.add(
  8520.           this.destinationStore_,
  8521.           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
  8522.           this.onDestinationSelect_.bind(this));
  8523.     },
  8524.  
  8525.     /**
  8526.      * Called when the "Change" button is clicked. Dispatches the
  8527.      * CHANGE_BUTTON_ACTIVATE event.
  8528.      * @private
  8529.      */
  8530.     onChangeButtonClick_: function() {
  8531.       cr.dispatchSimpleEvent(
  8532.           this, DestinationSettings.EventType.CHANGE_BUTTON_ACTIVATE);
  8533.     },
  8534.  
  8535.     /**
  8536.      * Called when the destination selection has changed. Updates UI elements.
  8537.      * @private
  8538.      */
  8539.     onDestinationSelect_: function() {
  8540.       var destination = this.destinationStore_.selectedDestination;
  8541.       var nameEl = this.getElement().getElementsByClassName(
  8542.           DestinationSettings.Classes_.NAME)[0];
  8543.       nameEl.textContent = destination.displayName;
  8544.       nameEl.title = destination.displayName;
  8545.  
  8546.       var iconEl = this.getElement().getElementsByClassName(
  8547.           DestinationSettings.Classes_.ICON)[0];
  8548.       iconEl.src = destination.iconUrl;
  8549.  
  8550.       var locationEl = this.getElement().getElementsByClassName(
  8551.           DestinationSettings.Classes_.LOCATION)[0];
  8552.       locationEl.textContent = destination.location;
  8553.       locationEl.title = destination.location;
  8554.  
  8555.       setIsVisible(this.getElement().querySelector('.throbber'), false);
  8556.       setIsVisible(
  8557.           this.getElement().querySelector('.destination-settings-box'), true);
  8558.     }
  8559.   };
  8560.  
  8561.   // Export
  8562.   return {
  8563.     DestinationSettings: DestinationSettings
  8564.   };
  8565. });
  8566.  
  8567. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8568. // Use of this source code is governed by a BSD-style license that can be
  8569. // found in the LICENSE file.
  8570.  
  8571. cr.define('print_preview', function() {
  8572.   'use strict';
  8573.  
  8574.   /**
  8575.    * UI component that renders checkboxes for various print options.
  8576.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to monitor
  8577.    *     the state of the print ticket.
  8578.    * @constructor
  8579.    * @extends {print_preview.Component}
  8580.    */
  8581.   function OtherOptionsSettings(printTicketStore) {
  8582.     print_preview.Component.call(this);
  8583.  
  8584.     /**
  8585.      * Used to monitor the state of the print ticket.
  8586.      * @type {!print_preview.PrintTicketStore}
  8587.      * @private
  8588.      */
  8589.     this.printTicketStore_ = printTicketStore;
  8590.  
  8591.     /**
  8592.      * Header footer container element.
  8593.      * @type {HTMLElement}
  8594.      * @private
  8595.      */
  8596.     this.headerFooterContainer_ = null;
  8597.  
  8598.     /**
  8599.      * Header footer checkbox.
  8600.      * @type {HTMLInputElement}
  8601.      * @private
  8602.      */
  8603.     this.headerFooterCheckbox_ = null;
  8604.  
  8605.     /**
  8606.      * Fit-to-page container element.
  8607.      * @type {HTMLElement}
  8608.      * @private
  8609.      */
  8610.     this.fitToPageContainer_ = null;
  8611.  
  8612.     /**
  8613.      * Fit-to-page checkbox.
  8614.      * @type {HTMLInputElement}
  8615.      * @private
  8616.      */
  8617.     this.fitToPageCheckbox_ = null;
  8618.  
  8619.     /**
  8620.      * Duplex container element.
  8621.      * @type {HTMLElement}
  8622.      * @private
  8623.      */
  8624.     this.duplexContainer_ = null;
  8625.  
  8626.     /**
  8627.      * Duplex checkbox.
  8628.      * @type {HTMLInputElement}
  8629.      * @private
  8630.      */
  8631.     this.duplexCheckbox_ = null;
  8632.   };
  8633.  
  8634.   OtherOptionsSettings.prototype = {
  8635.     __proto__: print_preview.Component.prototype,
  8636.  
  8637.     /** @param {boolean} isEnabled Whether the settings is enabled. */
  8638.     set isEnabled(isEnabled) {
  8639.       this.headerFooterCheckbox_.disabled = !isEnabled;
  8640.       this.fitToPageCheckbox_.disabled = !isEnabled;
  8641.       this.duplexCheckbox_.disabled = !isEnabled;
  8642.     },
  8643.  
  8644.     /** @override */
  8645.     enterDocument: function() {
  8646.       print_preview.Component.prototype.enterDocument.call(this);
  8647.       this.tracker.add(
  8648.           this.headerFooterCheckbox_,
  8649.           'click',
  8650.           this.onHeaderFooterCheckboxClick_.bind(this));
  8651.       this.tracker.add(
  8652.           this.fitToPageCheckbox_,
  8653.           'click',
  8654.           this.onFitToPageCheckboxClick_.bind(this));
  8655.       this.tracker.add(
  8656.           this.duplexCheckbox_,
  8657.           'click',
  8658.           this.onDuplexCheckboxClick_.bind(this));
  8659.       this.tracker.add(
  8660.           this.printTicketStore_,
  8661.           print_preview.PrintTicketStore.EventType.INITIALIZE,
  8662.           this.onPrintTicketStoreChange_.bind(this));
  8663.       this.tracker.add(
  8664.           this.printTicketStore_,
  8665.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  8666.           this.onPrintTicketStoreChange_.bind(this));
  8667.       this.tracker.add(
  8668.           this.printTicketStore_,
  8669.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  8670.           this.onPrintTicketStoreChange_.bind(this));
  8671.       this.tracker.add(
  8672.           this.printTicketStore_,
  8673.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  8674.           this.onPrintTicketStoreChange_.bind(this));
  8675.     },
  8676.  
  8677.     /** @override */
  8678.     exitDocument: function() {
  8679.       print_preview.Component.prototype.exitDocument.call(this);
  8680.       this.headerFooterContainer_ = null;
  8681.       this.headerFooterCheckbox_ = null;
  8682.       this.fitToPageContainer_ = null;
  8683.       this.fitToPageCheckbox_ = null;
  8684.       this.duplexContainer_ = null;
  8685.       this.duplexCheckbox_ = null;
  8686.     },
  8687.  
  8688.     /** @override */
  8689.     decorateInternal: function() {
  8690.       this.headerFooterContainer_ = this.getElement().querySelector(
  8691.           '.header-footer-container');
  8692.       this.headerFooterCheckbox_ = this.headerFooterContainer_.querySelector(
  8693.           '.header-footer-checkbox');
  8694.       this.fitToPageContainer_ = this.getElement().querySelector(
  8695.           '.fit-to-page-container');
  8696.       this.fitToPageCheckbox_ = this.fitToPageContainer_.querySelector(
  8697.           '.fit-to-page-checkbox');
  8698.       this.duplexContainer_ = this.getElement().querySelector(
  8699.           '.duplex-container');
  8700.       this.duplexCheckbox_ = this.duplexContainer_.querySelector(
  8701.           '.duplex-checkbox');
  8702.     },
  8703.  
  8704.     /**
  8705.      * Called when the header-footer checkbox is clicked. Updates the print
  8706.      * ticket.
  8707.      * @private
  8708.      */
  8709.     onHeaderFooterCheckboxClick_: function() {
  8710.       this.printTicketStore_.updateHeaderFooter(
  8711.           this.headerFooterCheckbox_.checked);
  8712.     },
  8713.  
  8714.     /**
  8715.      * Called when the fit-to-page checkbox is clicked. Updates the print
  8716.      * ticket.
  8717.      * @private
  8718.      */
  8719.     onFitToPageCheckboxClick_: function() {
  8720.       this.printTicketStore_.updateFitToPage(this.fitToPageCheckbox_.checked);
  8721.     },
  8722.  
  8723.     /**
  8724.      * Called when the duplex checkbox is clicked. Updates the print ticket.
  8725.      * @private
  8726.      */
  8727.     onDuplexCheckboxClick_: function() {
  8728.       this.printTicketStore_.updateDuplex(this.duplexCheckbox_.checked);
  8729.     },
  8730.  
  8731.     /**
  8732.      * Called when the print ticket store has changed. Hides or shows the
  8733.      * setting.
  8734.      * @private
  8735.      */
  8736.     onPrintTicketStoreChange_: function() {
  8737.       setIsVisible(this.headerFooterContainer_,
  8738.                    this.printTicketStore_.hasHeaderFooterCapability());
  8739.       this.headerFooterCheckbox_.checked =
  8740.           this.printTicketStore_.isHeaderFooterEnabled();
  8741.  
  8742.       setIsVisible(this.fitToPageContainer_,
  8743.                    this.printTicketStore_.hasFitToPageCapability());
  8744.       this.fitToPageCheckbox_.checked =
  8745.           this.printTicketStore_.isFitToPageEnabled();
  8746.  
  8747.       setIsVisible(this.duplexContainer_,
  8748.                    this.printTicketStore_.hasDuplexCapability());
  8749.       this.duplexCheckbox_.checked = this.printTicketStore_.isDuplexEnabled();
  8750.  
  8751.       if (this.printTicketStore_.hasHeaderFooterCapability() ||
  8752.           this.printTicketStore_.hasFitToPageCapability() ||
  8753.           this.printTicketStore_.hasDuplexCapability()) {
  8754.         fadeInOption(this.getElement());
  8755.       } else {
  8756.         fadeOutOption(this.getElement());
  8757.       }
  8758.     }
  8759.   };
  8760.  
  8761.   // Export
  8762.   return {
  8763.     OtherOptionsSettings: OtherOptionsSettings
  8764.   };
  8765. });
  8766.  
  8767.  
  8768. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8769. // Use of this source code is governed by a BSD-style license that can be
  8770. // found in the LICENSE file.
  8771.  
  8772. cr.define('print_preview', function() {
  8773.   'use strict';
  8774.  
  8775.   /**
  8776.    * Draggable control for setting a page margin.
  8777.    * @param {!print_preview.ticket_items.CustomMargins.Orientation} orientation
  8778.    *     Orientation of the margin control that determines where the margin
  8779.    *     textbox will be placed.
  8780.    * @constructor
  8781.    * @extends {print_preview.Component}
  8782.    */
  8783.   function MarginControl(orientation) {
  8784.     print_preview.Component.call(this);
  8785.  
  8786.     /**
  8787.      * Determines where the margin textbox will be placed.
  8788.      * @type {!print_preview.ticket_items.CustomMargins.Orientation}
  8789.      * @private
  8790.      */
  8791.     this.orientation_ = orientation;
  8792.  
  8793.     /**
  8794.      * Position of the margin control in points.
  8795.      * @type {number}
  8796.      * @private
  8797.      */
  8798.     this.positionInPts_ = 0;
  8799.  
  8800.     /**
  8801.      * Page size of the document to print.
  8802.      * @type {!print_preview.Size}
  8803.      * @private
  8804.      */
  8805.     this.pageSize_ = new print_preview.Size(0, 0);
  8806.  
  8807.     /**
  8808.      * Amount to scale pixel values by to convert to pixel space.
  8809.      * @type {number}
  8810.      * @private
  8811.      */
  8812.     this.scaleTransform_ = 1;
  8813.  
  8814.     /**
  8815.      * Amount to translate values in pixel space.
  8816.      * @type {!print_preview.Coordinate2d}
  8817.      * @private
  8818.      */
  8819.     this.translateTransform_ = new print_preview.Coordinate2d(0, 0);
  8820.  
  8821.     /**
  8822.      * Position of the margin control when dragging starts.
  8823.      * @type {print_preview.Coordinate2d}
  8824.      * @private
  8825.      */
  8826.     this.marginStartPositionInPixels_ = null;
  8827.  
  8828.     /**
  8829.      * Position of the mouse when the dragging starts.
  8830.      * @type {print_preview.Coordinate2d}
  8831.      * @private
  8832.      */
  8833.     this.mouseStartPositionInPixels_ = null;
  8834.  
  8835.     /**
  8836.      * Processing timeout for the textbox.
  8837.      * @type {?number}
  8838.      * @private
  8839.      */
  8840.     this.textTimeout_ = null;
  8841.  
  8842.     /**
  8843.      * Value of the textbox when the timeout was started.
  8844.      * @type {?string}
  8845.      * @private
  8846.      */
  8847.     this.preTimeoutValue_ = null;
  8848.  
  8849.     /**
  8850.      * Textbox used to display and receive the value of the margin.
  8851.      * @type {HTMLInputElement}
  8852.      * @private
  8853.      */
  8854.     this.textbox_ = null;
  8855.  
  8856.     /**
  8857.      * Element of the margin control line.
  8858.      * @type {HTMLElement}
  8859.      * @private
  8860.      */
  8861.     this.marginLineEl_ = null;
  8862.  
  8863.     /**
  8864.      * Whether this margin control's textbox has keyboard focus.
  8865.      * @type {boolean}
  8866.      * @private
  8867.      */
  8868.     this.isFocused_ = false;
  8869.  
  8870.     /**
  8871.      * Whether the margin control is in an error state.
  8872.      * @type {boolean}
  8873.      * @private
  8874.      */
  8875.     this.isInError_ = false;
  8876.   };
  8877.  
  8878.   /**
  8879.    * Event types dispatched by the margin control.
  8880.    * @enum {string}
  8881.    */
  8882.   MarginControl.EventType = {
  8883.     // Dispatched when the margin control starts dragging.
  8884.     DRAG_START: 'print_preview.MarginControl.DRAG_START',
  8885.  
  8886.     // Dispatched when the text in the margin control's textbox changes.
  8887.     TEXT_CHANGE: 'print_preview.MarginControl.TEXT_CHANGE'
  8888.   };
  8889.  
  8890.   /**
  8891.    * CSS classes used by this component.
  8892.    * @enum {string}
  8893.    * @private
  8894.    */
  8895.   MarginControl.Classes_ = {
  8896.     TOP: 'margin-control-top',
  8897.     RIGHT: 'margin-control-right',
  8898.     BOTTOM: 'margin-control-bottom',
  8899.     LEFT: 'margin-control-left',
  8900.     TEXTBOX: 'margin-control-textbox',
  8901.     INVALID: 'invalid',
  8902.     INVISIBLE: 'invisible',
  8903.     DISABLED: 'margin-control-disabled',
  8904.     DRAGGING: 'margin-control-dragging',
  8905.     LINE: 'margin-control-line'
  8906.   };
  8907.  
  8908.   /**
  8909.    * Map from orientation to CSS class name.
  8910.    * @type {!Object.<
  8911.    *     !print_preview.ticket_items.CustomMargins.Orientation,
  8912.    *     !MarginControl.Classes_>}
  8913.    * @private
  8914.    */
  8915.   MarginControl.OrientationToClass_ = {};
  8916.   MarginControl.OrientationToClass_[
  8917.       print_preview.ticket_items.CustomMargins.Orientation.TOP] =
  8918.       MarginControl.Classes_.TOP;
  8919.   MarginControl.OrientationToClass_[
  8920.       print_preview.ticket_items.CustomMargins.Orientation.RIGHT] =
  8921.       MarginControl.Classes_.RIGHT;
  8922.   MarginControl.OrientationToClass_[
  8923.       print_preview.ticket_items.CustomMargins.Orientation.BOTTOM] =
  8924.       MarginControl.Classes_.BOTTOM;
  8925.   MarginControl.OrientationToClass_[
  8926.       print_preview.ticket_items.CustomMargins.Orientation.LEFT] =
  8927.       MarginControl.Classes_.LEFT;
  8928.  
  8929.   /**
  8930.    * Radius of the margin control in pixels. Padding of control + 1 for border.
  8931.    * @type {number}
  8932.    * @const
  8933.    * @private
  8934.    */
  8935.   MarginControl.RADIUS_ = 9;
  8936.  
  8937.   /**
  8938.    * Timeout after a text change after which the text in the textbox is saved to
  8939.    * the print ticket. Value in milliseconds.
  8940.    * @type {number}
  8941.    * @const
  8942.    * @private
  8943.    */
  8944.   MarginControl.TEXTBOX_TIMEOUT_ = 1000;
  8945.  
  8946.   MarginControl.prototype = {
  8947.     __proto__: print_preview.Component.prototype,
  8948.  
  8949.     /** @return {boolean} Whether this margin control is in focus. */
  8950.     getIsFocused: function() {
  8951.       return this.isFocused_;
  8952.     },
  8953.  
  8954.     /**
  8955.      * @return {!print_preview.ticket_items.CustomMargins.Orientation}
  8956.      *     Orientation of the margin control.
  8957.      */
  8958.     getOrientation: function() {
  8959.       return this.orientation_;
  8960.     },
  8961.  
  8962.     /**
  8963.      * @param {number} scaleTransform New scale transform of the margin control.
  8964.      */
  8965.     setScaleTransform: function(scaleTransform) {
  8966.       this.scaleTransform_ = scaleTransform;
  8967.       // Reset position
  8968.       this.setPositionInPts(this.positionInPts_);
  8969.     },
  8970.  
  8971.     /**
  8972.      * @param {!print_preview.Coordinate2d} translateTransform New translate
  8973.      *     transform of the margin control.
  8974.      */
  8975.     setTranslateTransform: function(translateTransform) {
  8976.       this.translateTransform_ = translateTransform;
  8977.       // Reset position
  8978.       this.setPositionInPts(this.positionInPts_);
  8979.     },
  8980.  
  8981.     /**
  8982.      * @param {!print_preview.Size} pageSize New size of the document's pages.
  8983.      */
  8984.     setPageSize: function(pageSize) {
  8985.       this.pageSize_ = pageSize;
  8986.       this.setPositionInPts(this.positionInPts_);
  8987.     },
  8988.  
  8989.     /** @param {boolean} isVisible Whether the margin control is visible. */
  8990.     setIsVisible: function(isVisible) {
  8991.       if (isVisible) {
  8992.         this.getElement().classList.remove(MarginControl.Classes_.INVISIBLE);
  8993.       } else {
  8994.         this.getElement().classList.add(MarginControl.Classes_.INVISIBLE);
  8995.       }
  8996.     },
  8997.  
  8998.     /** @return {boolean} Whether the margin control is in an error state. */
  8999.     getIsInError: function() {
  9000.       return this.isInError_;
  9001.     },
  9002.  
  9003.     /**
  9004.      * @param {boolean} isInError Whether the margin control is in an error
  9005.      *     state.
  9006.      */
  9007.     setIsInError: function(isInError) {
  9008.       this.isInError_ = isInError;
  9009.       if (isInError) {
  9010.         this.textbox_.classList.add(MarginControl.Classes_.INVALID);
  9011.       } else {
  9012.         this.textbox_.classList.remove(MarginControl.Classes_.INVALID);
  9013.       }
  9014.     },
  9015.  
  9016.     /** @param {boolean} isEnabled Whether to enable the margin control. */
  9017.     setIsEnabled: function(isEnabled) {
  9018.       this.textbox_.disabled = !isEnabled;
  9019.       if (isEnabled) {
  9020.         this.getElement().classList.remove(MarginControl.Classes_.DISABLED);
  9021.       } else {
  9022.         this.getElement().classList.add(MarginControl.Classes_.DISABLED);
  9023.       }
  9024.     },
  9025.  
  9026.     /** @return {number} Current position of the margin control in points. */
  9027.     getPositionInPts: function() {
  9028.       return this.positionInPts_;
  9029.     },
  9030.  
  9031.     /**
  9032.      * @param {number} posInPts New position of the margin control in points.
  9033.      */
  9034.     setPositionInPts: function(posInPts) {
  9035.       this.positionInPts_ = posInPts;
  9036.       var orientationEnum =
  9037.           print_preview.ticket_items.CustomMargins.Orientation;
  9038.       var x = this.translateTransform_.x;
  9039.       var y = this.translateTransform_.y;
  9040.       var width = null, height = null;
  9041.       if (this.orientation_ == orientationEnum.TOP) {
  9042.         y = this.scaleTransform_ * posInPts + this.translateTransform_.y -
  9043.             MarginControl.RADIUS_;
  9044.         width = this.scaleTransform_ * this.pageSize_.width;
  9045.       } else if (this.orientation_ == orientationEnum.RIGHT) {
  9046.         x = this.scaleTransform_ * (this.pageSize_.width - posInPts) +
  9047.             this.translateTransform_.x - MarginControl.RADIUS_;
  9048.         height = this.scaleTransform_ * this.pageSize_.height;
  9049.       } else if (this.orientation_ == orientationEnum.BOTTOM) {
  9050.         y = this.scaleTransform_ * (this.pageSize_.height - posInPts) +
  9051.             this.translateTransform_.y - MarginControl.RADIUS_;
  9052.         width = this.scaleTransform_ * this.pageSize_.width;
  9053.       } else {
  9054.         x = this.scaleTransform_ * posInPts + this.translateTransform_.x -
  9055.             MarginControl.RADIUS_;
  9056.         height = this.scaleTransform_ * this.pageSize_.height;
  9057.       }
  9058.       this.getElement().style.left = Math.round(x) + 'px';
  9059.       this.getElement().style.top = Math.round(y) + 'px';
  9060.       if (width != null) {
  9061.         this.getElement().style.width = Math.round(width) + 'px';
  9062.       }
  9063.       if (height != null) {
  9064.         this.getElement().style.height = Math.round(height) + 'px';
  9065.       }
  9066.     },
  9067.  
  9068.     /** @return {string} The value in the margin control's textbox. */
  9069.     getTextboxValue: function() {
  9070.       return this.textbox_.value;
  9071.     },
  9072.  
  9073.     /** @param {string} value New value of the margin control's textbox. */
  9074.     setTextboxValue: function(value) {
  9075.       if (this.textbox_.value != value) {
  9076.         this.textbox_.value = value;
  9077.       }
  9078.     },
  9079.  
  9080.     /**
  9081.      * Converts a value in pixels to points.
  9082.      * @param {number} Pixel value to convert.
  9083.      * @return {number} Given value expressed in points.
  9084.      */
  9085.     convertPixelsToPts: function(pixels) {
  9086.       var pts;
  9087.       var orientationEnum =
  9088.           print_preview.ticket_items.CustomMargins.Orientation;
  9089.       if (this.orientation_ == orientationEnum.TOP) {
  9090.         pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_;
  9091.         pts /= this.scaleTransform_;
  9092.       } else if (this.orientation_ == orientationEnum.RIGHT) {
  9093.         pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_;
  9094.         pts /= this.scaleTransform_;
  9095.         pts = this.pageSize_.width - pts;
  9096.       } else if (this.orientation_ == orientationEnum.BOTTOM) {
  9097.         pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_;
  9098.         pts /= this.scaleTransform_;
  9099.         pts = this.pageSize_.height - pts;
  9100.       } else {
  9101.         pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_;
  9102.         pts /= this.scaleTransform_;
  9103.       }
  9104.       return pts;
  9105.     },
  9106.  
  9107.     /**
  9108.      * Translates the position of the margin control relative to the mouse
  9109.      * position in pixels.
  9110.      * @param {!print_preview.Coordinate2d} mousePosition New position of
  9111.      *     the mouse.
  9112.      * @return {!print_preview.Coordinate2d} New position of the margin control.
  9113.      */
  9114.     translateMouseToPositionInPixels: function(mousePosition) {
  9115.       return new print_preview.Coordinate2d(
  9116.           mousePosition.x - this.mouseStartPositionInPixels_.x +
  9117.               this.marginStartPositionInPixels_.x,
  9118.           mousePosition.y - this.mouseStartPositionInPixels_.y +
  9119.               this.marginStartPositionInPixels_.y);
  9120.     },
  9121.  
  9122.     /** @override */
  9123.     createDom: function() {
  9124.       this.setElementInternal(this.cloneTemplateInternal(
  9125.           'margin-control-template'));
  9126.       this.getElement().classList.add(MarginControl.OrientationToClass_[
  9127.           this.orientation_]);
  9128.       this.textbox_ = this.getElement().getElementsByClassName(
  9129.           MarginControl.Classes_.TEXTBOX)[0];
  9130.       this.marginLineEl_ = this.getElement().getElementsByClassName(
  9131.           MarginControl.Classes_.LINE)[0];
  9132.     },
  9133.  
  9134.     /** @override */
  9135.     enterDocument: function() {
  9136.       print_preview.Component.prototype.enterDocument.call(this);
  9137.       this.tracker.add(
  9138.           this.getElement(), 'mousedown', this.onMouseDown_.bind(this));
  9139.       this.tracker.add(
  9140.           this.textbox_, 'keydown', this.onTextboxKeyDown_.bind(this));
  9141.       this.tracker.add(
  9142.           this.textbox_, 'focus', this.setIsFocused_.bind(this, true));
  9143.       this.tracker.add(this.textbox_, 'blur', this.onTexboxBlur_.bind(this));
  9144.     },
  9145.  
  9146.     /** @override */
  9147.     exitDocument: function() {
  9148.       print_preview.Component.prototype.exitDocument.call(this);
  9149.       this.textbox_ = null;
  9150.       this.marginLineEl_ = null;
  9151.     },
  9152.  
  9153.     /**
  9154.      * @param {boolean} isFocused Whether the margin control is in focus.
  9155.      * @private
  9156.      */
  9157.     setIsFocused_: function(isFocused) {
  9158.       this.isFocused_ = isFocused;
  9159.     },
  9160.  
  9161.     /**
  9162.      * Called whenever a mousedown event occurs on the component.
  9163.      * @param {MouseEvent} event The event that occured.
  9164.      * @private
  9165.      */
  9166.     onMouseDown_: function(event) {
  9167.       if (!this.textbox_.disabled &&
  9168.           event.button == 0 &&
  9169.           (event.target == this.getElement() ||
  9170.               event.target == this.marginLineEl_)) {
  9171.         this.mouseStartPositionInPixels_ =
  9172.             new print_preview.Coordinate2d(event.x, event.y);
  9173.         this.marginStartPositionInPixels_ = new print_preview.Coordinate2d(
  9174.             this.getElement().offsetLeft, this.getElement().offsetTop);
  9175.         this.setIsInError(false);
  9176.         cr.dispatchSimpleEvent(this, MarginControl.EventType.DRAG_START);
  9177.       }
  9178.     },
  9179.  
  9180.     /**
  9181.      * Called when a key down event occurs on the textbox. Dispatches a
  9182.      * TEXT_CHANGE event if the "Enter" key was pressed.
  9183.      * @param {Event} event Contains the key that was pressed.
  9184.      * @private
  9185.      */
  9186.     onTextboxKeyDown_: function(event) {
  9187.       if (this.textTimeout_) {
  9188.         clearTimeout(this.textTimeout_);
  9189.         this.textTimeout_ = null;
  9190.       }
  9191.       if (event.keyIdentifier == 'Enter') {
  9192.         this.preTimeoutValue_ = null;
  9193.         cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
  9194.       } else {
  9195.         if (this.preTimeoutValue_ == null) {
  9196.           this.preTimeoutValue_ = this.textbox_.value;
  9197.         }
  9198.         this.textTimeout_ = setTimeout(
  9199.             this.onTextboxTimeout_.bind(this), MarginControl.TEXTBOX_TIMEOUT_);
  9200.       }
  9201.     },
  9202.  
  9203.     /**
  9204.      * Called after a timeout after the text in the textbox has changed. Saves
  9205.      * the textbox's value to the print ticket.
  9206.      * @private
  9207.      */
  9208.     onTextboxTimeout_: function() {
  9209.       this.textTimeout_ = null;
  9210.       if (this.textbox_.value != this.preTimeoutValue_) {
  9211.         cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
  9212.       }
  9213.       this.preTimeoutValue_ = null;
  9214.     },
  9215.  
  9216.     /**
  9217.      * Called when the textbox loses focus. Dispatches a TEXT_CHANGE event.
  9218.      */
  9219.     onTexboxBlur_: function() {
  9220.       if (this.textTimeout_) {
  9221.         clearTimeout(this.textTimeout_);
  9222.         this.textTimeout_ = null;
  9223.         this.preTimeoutValue_ = null;
  9224.       }
  9225.       this.setIsFocused_(false);
  9226.       cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE);
  9227.     }
  9228.   };
  9229.  
  9230.   // Export
  9231.   return {
  9232.     MarginControl: MarginControl
  9233.   };
  9234. });
  9235.  
  9236. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  9237. // Use of this source code is governed by a BSD-style license that can be
  9238. // found in the LICENSE file.
  9239.  
  9240. cr.define('print_preview', function() {
  9241.   'use strict';
  9242.  
  9243.   /**
  9244.    * UI component used for setting custom print margins.
  9245.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to read and
  9246.    *     write custom margin values.
  9247.    * @constructor
  9248.    * @extends {print_preview.Component}
  9249.    */
  9250.   function MarginControlContainer(printTicketStore) {
  9251.     print_preview.Component.call(this);
  9252.  
  9253.     /**
  9254.      * Used to read and write custom margin values.
  9255.      * @type {!print_preview.PrintTicketStore}
  9256.      * @private
  9257.      */
  9258.     this.printTicketStore_ = printTicketStore;
  9259.  
  9260.     /**
  9261.      * Used to convert between the system's local units and points.
  9262.      * @type {!print_preview.MeasurementSystem}
  9263.      * @private
  9264.      */
  9265.     this.measurementSystem_ = printTicketStore.measurementSystem;
  9266.  
  9267.     /**
  9268.      * Convenience array that contains all of the margin controls.
  9269.      * @type {!Object.<
  9270.      *     !print_preview.ticket_items.CustomMargins.Orientation,
  9271.      *     !print_preview.MarginControl>}
  9272.      * @private
  9273.      */
  9274.     this.controls_ = {};
  9275.     for (var key in print_preview.ticket_items.CustomMargins.Orientation) {
  9276.       var orientation = print_preview.ticket_items.CustomMargins.Orientation[
  9277.           key];
  9278.       var control = new print_preview.MarginControl(orientation);
  9279.       this.controls_[orientation] = control;
  9280.       this.addChild(control);
  9281.     }
  9282.  
  9283.     /**
  9284.      * Margin control currently being dragged. Null if no control is being
  9285.      * dragged.
  9286.      * @type {print_preview.MarginControl}
  9287.      * @private
  9288.      */
  9289.     this.draggedControl_ = null;
  9290.  
  9291.     /**
  9292.      * Translation transformation in pixels to translate from the origin of the
  9293.      * custom margins component to the top-left corner of the most visible
  9294.      * preview page.
  9295.      * @type {!print_preview.Coordinate2d}
  9296.      * @private
  9297.      */
  9298.     this.translateTransform_ = new print_preview.Coordinate2d(0, 0);
  9299.  
  9300.     /**
  9301.      * Scaling transformation to scale from pixels to the units which the
  9302.      * print preview is in. The scaling factor is the same in both dimensions,
  9303.      * so this field is just a single number.
  9304.      * @type {number}
  9305.      * @private
  9306.      */
  9307.     this.scaleTransform_ = 1;
  9308.  
  9309.     /**
  9310.      * Clipping size for clipping the margin controls.
  9311.      * @type {print_preview.Size}
  9312.      * @private
  9313.      */
  9314.     this.clippingSize_ = null;
  9315.   };
  9316.  
  9317.   /**
  9318.    * CSS classes used by the custom margins component.
  9319.    * @enum {string}
  9320.    * @private
  9321.    */
  9322.   MarginControlContainer.Classes_ = {
  9323.     DRAGGING_HORIZONTAL: 'margin-control-container-dragging-horizontal',
  9324.     DRAGGING_VERTICAL: 'margin-control-container-dragging-vertical'
  9325.   };
  9326.  
  9327.   /**
  9328.    * @param {!print_preview.ticket_items.CustomMargins.Orientation} orientation
  9329.    *     Orientation value to test.
  9330.    * @return {boolean} Whether the given orientation is TOP or BOTTOM.
  9331.    * @private
  9332.    */
  9333.   MarginControlContainer.isTopOrBottom_ = function(orientation) {
  9334.     return orientation ==
  9335.         print_preview.ticket_items.CustomMargins.Orientation.TOP ||
  9336.         orientation ==
  9337.             print_preview.ticket_items.CustomMargins.Orientation.BOTTOM;
  9338.   };
  9339.  
  9340.   MarginControlContainer.prototype = {
  9341.     __proto__: print_preview.Component.prototype,
  9342.  
  9343.     /**
  9344.      * Updates the translation transformation that translates pixel values in
  9345.      * the space of the HTML DOM.
  9346.      * @param {print_preview.Coordinate2d} translateTransform Updated value of
  9347.      *     the translation transformation.
  9348.      */
  9349.     updateTranslationTransform: function(translateTransform) {
  9350.       if (!translateTransform.equals(this.translateTransform_)) {
  9351.         this.translateTransform_ = translateTransform;
  9352.         for (var orientation in this.controls_) {
  9353.           this.controls_[orientation].setTranslateTransform(translateTransform);
  9354.         }
  9355.       }
  9356.     },
  9357.  
  9358.     /**
  9359.      * Updates the scaling transform that scales pixels values to point values.
  9360.      * @param {number} scaleTransform Updated value of the scale transform.
  9361.      */
  9362.     updateScaleTransform: function(scaleTransform) {
  9363.       if (scaleTransform != this.scaleTransform_) {
  9364.         this.scaleTransform_ = scaleTransform;
  9365.         for (var orientation in this.controls_) {
  9366.           this.controls_[orientation].setScaleTransform(scaleTransform);
  9367.         }
  9368.       }
  9369.     },
  9370.  
  9371.     /**
  9372.      * Clips margin controls to the given clip size in pixels.
  9373.      * @param {print_preview.Size} Size to clip the margin controls to.
  9374.      */
  9375.     updateClippingMask: function(clipSize) {
  9376.       if (!clipSize) {
  9377.         return;
  9378.       }
  9379.       this.clippingSize_ = clipSize;
  9380.       for (var orientation in this.controls_) {
  9381.         var el = this.controls_[orientation].getElement();
  9382.         el.style.clip = 'rect(' +
  9383.             (-el.offsetTop) + 'px, ' +
  9384.             (clipSize.width - el.offsetLeft) + 'px, ' +
  9385.             (clipSize.height - el.offsetTop) + 'px, ' +
  9386.             (-el.offsetLeft) + 'px)';
  9387.       }
  9388.     },
  9389.  
  9390.     /** Shows the margin controls if the need to be shown. */
  9391.     showMarginControlsIfNeeded: function() {
  9392.       if (this.printTicketStore_.getMarginsType() ==
  9393.           print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  9394.         this.setIsMarginControlsVisible_(true);
  9395.       }
  9396.     },
  9397.  
  9398.     /** @override */
  9399.     enterDocument: function() {
  9400.       print_preview.Component.prototype.enterDocument.call(this);
  9401.  
  9402.       // We want to respond to mouse up events even beyond the component's
  9403.       // element.
  9404.       this.tracker.add(window, 'mouseup', this.onMouseUp_.bind(this));
  9405.       this.tracker.add(window, 'mousemove', this.onMouseMove_.bind(this));
  9406.       this.tracker.add(
  9407.           this.getElement(), 'mouseover', this.onMouseOver_.bind(this));
  9408.       this.tracker.add(
  9409.           this.getElement(), 'mouseout', this.onMouseOut_.bind(this));
  9410.  
  9411.       this.tracker.add(
  9412.           this.printTicketStore_,
  9413.           print_preview.PrintTicketStore.EventType.INITIALIZE,
  9414.           this.onTicketChange_.bind(this));
  9415.       this.tracker.add(
  9416.           this.printTicketStore_,
  9417.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  9418.           this.onTicketChange_.bind(this));
  9419.       this.tracker.add(
  9420.           this.printTicketStore_,
  9421.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  9422.           this.onTicketChange_.bind(this));
  9423.       this.tracker.add(
  9424.           this.printTicketStore_,
  9425.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  9426.           this.onTicketChange_.bind(this));
  9427.  
  9428.       for (var orientation in this.controls_) {
  9429.         this.tracker.add(
  9430.             this.controls_[orientation],
  9431.             print_preview.MarginControl.EventType.DRAG_START,
  9432.             this.onControlDragStart_.bind(this, this.controls_[orientation]));
  9433.         this.tracker.add(
  9434.             this.controls_[orientation],
  9435.             print_preview.MarginControl.EventType.TEXT_CHANGE,
  9436.             this.onControlTextChange_.bind(this, this.controls_[orientation]));
  9437.       }
  9438.     },
  9439.  
  9440.     /** @override */
  9441.     decorateInternal: function() {
  9442.       for (var orientation in this.controls_) {
  9443.         this.controls_[orientation].render(this.getElement());
  9444.       }
  9445.     },
  9446.  
  9447.     /**
  9448.      * @param {boolean} isVisible Whether the margin controls are visible.
  9449.      * @private
  9450.      */
  9451.     setIsMarginControlsVisible_: function(isVisible) {
  9452.       for (var orientation in this.controls_) {
  9453.         this.controls_[orientation].setIsVisible(isVisible);
  9454.       }
  9455.     },
  9456.  
  9457.     /**
  9458.      * Moves the position of the given control to the desired position in
  9459.      * pixels within some constraint minimum and maximum.
  9460.      * @param {!print_preview.MarginControl} control Control to move.
  9461.      * @param {!print_preview.Coordinate2d} posInPixels Desired position to move
  9462.      *     to in pixels.
  9463.      * @private
  9464.      */
  9465.     moveControlWithConstraints_: function(control, posInPixels) {
  9466.       var newPosInPts;
  9467.       if (MarginControlContainer.isTopOrBottom_(control.getOrientation())) {
  9468.         newPosInPts = control.convertPixelsToPts(posInPixels.y);
  9469.       } else {
  9470.         newPosInPts = control.convertPixelsToPts(posInPixels.x);
  9471.       }
  9472.       newPosInPts = Math.min(
  9473.           this.printTicketStore_.getCustomMarginMax(control.getOrientation()),
  9474.           newPosInPts);
  9475.       newPosInPts = Math.max(0, newPosInPts);
  9476.       newPosInPts = Math.round(newPosInPts);
  9477.       control.setPositionInPts(newPosInPts);
  9478.       control.setTextboxValue(this.serializeValueFromPts_(newPosInPts));
  9479.     },
  9480.  
  9481.     /**
  9482.      * @param {string} value Value to parse to points. E.g. '3.40"' or '200mm'.
  9483.      * @return {number} Value in points represented by the input value.
  9484.      * @private
  9485.      */
  9486.     parseValueToPts_: function(value) {
  9487.       // Removing whitespace anywhere in the string.
  9488.       value = value.replace(/\s*/g, '');
  9489.       if (value.length == 0) {
  9490.         return null;
  9491.       }
  9492.       var validationRegex = new RegExp('^(^-?)(\\d)+(\\' +
  9493.           this.measurementSystem_.thousandsDelimeter + '\\d{3})*(\\' +
  9494.           this.measurementSystem_.decimalDelimeter + '\\d*)?' +
  9495.           '(' + this.measurementSystem_.unitSymbol + ')?$');
  9496.       if (validationRegex.test(value)) {
  9497.         // Replacing decimal point with the dot symbol in order to use
  9498.         // parseFloat() properly.
  9499.         var replacementRegex =
  9500.             new RegExp('\\' + this.measurementSystem_.decimalDelimeter + '{1}');
  9501.         value = value.replace(replacementRegex, '.');
  9502.         return this.measurementSystem_.convertToPoints(parseFloat(value));
  9503.       }
  9504.       return null;
  9505.     },
  9506.  
  9507.     /**
  9508.      * @param {number} value Value in points to serialize.
  9509.      * @return {string} String representation of the value in the system's local
  9510.      *     units.
  9511.      * @private
  9512.      */
  9513.     serializeValueFromPts_: function(value) {
  9514.       value = this.measurementSystem_.convertFromPoints(value);
  9515.       value = this.measurementSystem_.roundValue(value);
  9516.       return value + this.measurementSystem_.unitSymbol;
  9517.     },
  9518.  
  9519.     /**
  9520.      * Called when a margin control starts to drag.
  9521.      * @param {print_preview.MarginControl} control The control which started to
  9522.      *     drag.
  9523.      * @private
  9524.      */
  9525.     onControlDragStart_: function(control) {
  9526.       this.draggedControl_ = control;
  9527.       this.getElement().classList.add(
  9528.           MarginControlContainer.isTopOrBottom_(control.getOrientation()) ?
  9529.               MarginControlContainer.Classes_.DRAGGING_VERTICAL :
  9530.               MarginControlContainer.Classes_.DRAGGING_HORIZONTAL);
  9531.     },
  9532.  
  9533.     /**
  9534.      * Called when the mouse moves in the custom margins component. Moves the
  9535.      * dragged margin control.
  9536.      * @param {MouseEvent} event Contains the position of the mouse.
  9537.      * @private
  9538.      */
  9539.     onMouseMove_: function(event) {
  9540.       if (this.draggedControl_) {
  9541.         this.moveControlWithConstraints_(
  9542.             this.draggedControl_,
  9543.             this.draggedControl_.translateMouseToPositionInPixels(
  9544.                 new print_preview.Coordinate2d(event.x, event.y)));
  9545.         this.updateClippingMask(this.clippingSize_);
  9546.       }
  9547.     },
  9548.  
  9549.     /**
  9550.      * Called when the mouse is released in the custom margins component.
  9551.      * Releases the dragged margin control.
  9552.      * @param {MouseEvent} event Contains the position of the mouse.
  9553.      * @private
  9554.      */
  9555.     onMouseUp_: function(event) {
  9556.       if (this.draggedControl_) {
  9557.         this.getElement().classList.remove(
  9558.             MarginControlContainer.Classes_.DRAGGING_VERTICAL);
  9559.         this.getElement().classList.remove(
  9560.             MarginControlContainer.Classes_.DRAGGING_HORIZONTAL);
  9561.         if (event) {
  9562.           var posInPixels =
  9563.               this.draggedControl_.translateMouseToPositionInPixels(
  9564.                   new print_preview.Coordinate2d(event.x, event.y));
  9565.           this.moveControlWithConstraints_(this.draggedControl_, posInPixels);
  9566.         }
  9567.         this.updateClippingMask(this.clippingSize_);
  9568.         this.printTicketStore_.updateCustomMargin(
  9569.             this.draggedControl_.getOrientation(),
  9570.             this.draggedControl_.getPositionInPts());
  9571.         this.draggedControl_ = null;
  9572.       }
  9573.     },
  9574.  
  9575.     /**
  9576.      * Called when the mouse moves onto the component. Shows the margin
  9577.      * controls.
  9578.      * @private
  9579.      */
  9580.     onMouseOver_: function() {
  9581.       var fromElement = event.fromElement;
  9582.       while (fromElement != null) {
  9583.         if (fromElement == this.getElement()) {
  9584.           return;
  9585.         }
  9586.         fromElement = fromElement.parentElement;
  9587.       }
  9588.       if (this.printTicketStore_.hasMarginsCapability() &&
  9589.           this.printTicketStore_.getMarginsType() ==
  9590.               print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  9591.         this.setIsMarginControlsVisible_(true);
  9592.       }
  9593.     },
  9594.  
  9595.     /**
  9596.      * Called when the mouse moves off of the component. Hides the margin
  9597.      * controls.
  9598.      * @private
  9599.      */
  9600.     onMouseOut_: function(event) {
  9601.       var toElement = event.toElement;
  9602.       while (toElement != null) {
  9603.         if (toElement == this.getElement()) {
  9604.           return;
  9605.         }
  9606.         toElement = toElement.parentElement;
  9607.       }
  9608.       if (this.draggedControl_ != null) {
  9609.         return;
  9610.       }
  9611.       for (var orientation in this.controls_) {
  9612.         if (this.controls_[orientation].getIsFocused() ||
  9613.             this.controls_[orientation].getIsInError()) {
  9614.           return;
  9615.         }
  9616.       }
  9617.       this.setIsMarginControlsVisible_(false);
  9618.     },
  9619.  
  9620.     /**
  9621.      * Called when the print ticket changes. Updates the position of the margin
  9622.      * controls.
  9623.      * @private
  9624.      */
  9625.     onTicketChange_: function() {
  9626.       var margins = this.printTicketStore_.getCustomMargins();
  9627.       for (var orientation in this.controls_) {
  9628.         var control = this.controls_[orientation];
  9629.         control.setPageSize(this.printTicketStore_.pageSize);
  9630.         control.setTextboxValue(
  9631.             this.serializeValueFromPts_(margins.get(orientation)));
  9632.         control.setPositionInPts(margins.get(orientation));
  9633.         control.setIsInError(false);
  9634.         control.setIsEnabled(true);
  9635.       }
  9636.       this.updateClippingMask(this.clippingSize_);
  9637.       if (this.printTicketStore_.getMarginsType() !=
  9638.           print_preview.ticket_items.MarginsType.Value.CUSTOM) {
  9639.         this.setIsMarginControlsVisible_(false);
  9640.       }
  9641.     },
  9642.  
  9643.     /**
  9644.      * Called when the text in a textbox of a margin control changes or the
  9645.      * textbox loses focus.
  9646.      * Updates the print ticket store.
  9647.      * @param {!print_preview.MarginControl} control Updated control.
  9648.      * @private
  9649.      */
  9650.     onControlTextChange_: function(control) {
  9651.       var marginValue = this.parseValueToPts_(control.getTextboxValue());
  9652.       if (marginValue != null) {
  9653.         this.printTicketStore_.updateCustomMargin(
  9654.             control.getOrientation(), marginValue);
  9655.       } else {
  9656.         var enableOtherControls;
  9657.         if (!control.getIsFocused()) {
  9658.           // If control no longer in focus, revert to previous valid value.
  9659.           control.setTextboxValue(
  9660.               this.serializeValueFromPts_(control.getPositionInPts()));
  9661.           control.setIsInError(false);
  9662.           enableOtherControls = true;
  9663.         } else {
  9664.           control.setIsInError(true);
  9665.           enableOtherControls = false;
  9666.         }
  9667.         // Enable other controls.
  9668.         for (var o in this.controls_) {
  9669.           if (control.getOrientation() != o) {
  9670.             this.controls_[o].setIsEnabled(enableOtherControls);
  9671.           }
  9672.         }
  9673.       }
  9674.     }
  9675.   };
  9676.  
  9677.   // Export
  9678.   return {
  9679.     MarginControlContainer: MarginControlContainer
  9680.   };
  9681. });
  9682.  
  9683. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  9684. // Use of this source code is governed by a BSD-style license that can be
  9685. // found in the LICENSE file.
  9686.  
  9687. cr.define('print_preview', function() {
  9688.   'use strict';
  9689.  
  9690.   /**
  9691.    * Creates a PreviewArea object. It represents the area where the preview
  9692.    * document is displayed.
  9693.    * @param {!print_preview.DestinationStore} destinationStore Used to get the
  9694.    *     currently selected destination.
  9695.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to get
  9696.    *     information about how the preview should be displayed.
  9697.    * @param {!print_preview.NativeLayer} nativeLayer Needed to communicate with
  9698.    *     Chromium's preview generation system.
  9699.    * @constructor
  9700.    * @extends {print_preview.Component}
  9701.    */
  9702.   function PreviewArea(destinationStore, printTicketStore, nativeLayer) {
  9703.     print_preview.Component.call(this);
  9704.  
  9705.     /**
  9706.      * Used to get the currently selected destination.
  9707.      * @type {!print_preview.DestinationStore}
  9708.      * @private
  9709.      */
  9710.     this.destinationStore_ = destinationStore;
  9711.  
  9712.     /**
  9713.      * Used to get information about how the preview should be displayed.
  9714.      * @type {!print_preview.PrintTicketStore}
  9715.      * @private
  9716.      */
  9717.     this.printTicketStore_ = printTicketStore;
  9718.  
  9719.     /**
  9720.      * Used to contruct the preview generator.
  9721.      * @type {!print_preview.NativeLayer}
  9722.      * @private
  9723.      */
  9724.     this.nativeLayer_ = nativeLayer;
  9725.  
  9726.     /**
  9727.      * Used to read generated page previews.
  9728.      * @type {print_preview.PreviewGenerator}
  9729.      * @private
  9730.      */
  9731.     this.previewGenerator_ = null;
  9732.  
  9733.     /**
  9734.      * The embedded pdf plugin object. It's value is null if not yet loaded.
  9735.      * @type {HTMLEmbedElement}
  9736.      * @private
  9737.      */
  9738.     this.plugin_ = null;
  9739.  
  9740.     /**
  9741.      * Custom margins component superimposed on the preview plugin.
  9742.      * @type {!print_preview.MarginControlContainer}
  9743.      * @private
  9744.      */
  9745.     this.marginControlContainer_ =
  9746.         new print_preview.MarginControlContainer(this.printTicketStore_);
  9747.     this.addChild(this.marginControlContainer_);
  9748.  
  9749.     /**
  9750.      * Current zoom level as a percentage.
  9751.      * @type {?number}
  9752.      * @private
  9753.      */
  9754.     this.zoomLevel_ = null;
  9755.  
  9756.     /**
  9757.      * Current page offset which can be used to calculate scroll amount.
  9758.      * @type {print_preview.Coordinate2d}
  9759.      * @private
  9760.      */
  9761.     this.pageOffset_ = null;
  9762.  
  9763.     /**
  9764.      * Whether the plugin has finished reloading.
  9765.      * @type {boolean}
  9766.      * @private
  9767.      */
  9768.     this.isPluginReloaded_ = false;
  9769.  
  9770.     /**
  9771.      * Whether the document preview is ready.
  9772.      * @type {boolean}
  9773.      * @private
  9774.      */
  9775.     this.isDocumentReady_ = false;
  9776.  
  9777.     /**
  9778.      * Timeout object used to display a loading message if the preview is taking
  9779.      * a long time to generate.
  9780.      * @type {?number}
  9781.      * @private
  9782.      */
  9783.     this.loadingTimeout_ = null;
  9784.  
  9785.     /**
  9786.      * Overlay element.
  9787.      * @type {HTMLElement}
  9788.      * @private
  9789.      */
  9790.     this.overlayEl_ = null;
  9791.  
  9792.     /**
  9793.      * The "Open system dialog" button.
  9794.      * @type {HTMLButtonElement}
  9795.      * @private
  9796.      */
  9797.     this.openSystemDialogButton_ = null;
  9798.   };
  9799.  
  9800.   /**
  9801.    * Event types dispatched by the preview area.
  9802.    * @enum {string}
  9803.    */
  9804.   PreviewArea.EventType = {
  9805.     // Dispatched when the "Open system dialog" button is clicked.
  9806.     OPEN_SYSTEM_DIALOG_CLICK:
  9807.         'print_preview.PreviewArea.OPEN_SYSTEM_DIALOG_CLICK',
  9808.  
  9809.     // Dispatched when the document preview is complete.
  9810.     PREVIEW_GENERATION_DONE:
  9811.         'print_preview.PreviewArea.PREVIEW_GENERATION_DONE',
  9812.  
  9813.     // Dispatched when the document preview failed to be generated.
  9814.     PREVIEW_GENERATION_FAIL:
  9815.         'print_preview.PreviewArea.PREVIEW_GENERATION_FAIL',
  9816.  
  9817.     // Dispatched when a new document preview is being generated.
  9818.     PREVIEW_GENERATION_IN_PROGRESS:
  9819.         'print_preview.PreviewArea.PREVIEW_GENERATION_IN_PROGRESS'
  9820.   };
  9821.  
  9822.   /**
  9823.    * CSS classes used by the preview area.
  9824.    * @enum {string}
  9825.    * @private
  9826.    */
  9827.   PreviewArea.Classes_ = {
  9828.     COMPATIBILITY_OBJECT: 'preview-area-compatibility-object',
  9829.     CUSTOM_MESSAGE_TEXT: 'preview-area-custom-message-text',
  9830.     MESSAGE: 'preview-area-message',
  9831.     INVISIBLE: 'invisible',
  9832.     OPEN_SYSTEM_DIALOG_BUTTON: 'preview-area-open-system-dialog-button',
  9833.     OPEN_SYSTEM_DIALOG_BUTTON_THROBBER:
  9834.         'preview-area-open-system-dialog-button-throbber',
  9835.     OVERLAY: 'preview-area-overlay-layer'
  9836.   };
  9837.  
  9838.   /**
  9839.    * Enumeration of IDs shown in the preview area.
  9840.    * @enum {string}
  9841.    * @private
  9842.    */
  9843.   PreviewArea.MessageId_ = {
  9844.     CUSTOM: 'custom',
  9845.     LOADING: 'loading',
  9846.     PREVIEW_FAILED: 'preview-failed'
  9847.   };
  9848.  
  9849.   /**
  9850.    * Maps message IDs to the CSS class that contains them.
  9851.    * @type {object.<PreviewArea.MessageId_, string>}
  9852.    * @private
  9853.    */
  9854.   PreviewArea.MessageIdClassMap_ = {};
  9855.   PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.CUSTOM] =
  9856.       'preview-area-custom-message';
  9857.   PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.LOADING] =
  9858.       'preview-area-loading-message';
  9859.   PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.PREVIEW_FAILED] =
  9860.       'preview-area-preview-failed-message';
  9861.  
  9862.   /**
  9863.    * Amount of time in milliseconds to wait after issueing a new preview before
  9864.    * the loading message is shown.
  9865.    * @type {number}
  9866.    * @const
  9867.    * @private
  9868.    */
  9869.   PreviewArea.LOADING_TIMEOUT_ = 200;
  9870.  
  9871.   PreviewArea.prototype = {
  9872.     __proto__: print_preview.Component.prototype,
  9873.  
  9874.     /**
  9875.      * Should only be called after calling this.render().
  9876.      * @return {boolean} Whether the preview area has a compatible plugin to
  9877.      *     display the print preview in.
  9878.      */
  9879.     get hasCompatiblePlugin() {
  9880.       return this.previewGenerator_ != null;
  9881.     },
  9882.  
  9883.     /**
  9884.      * Processes a keyboard event that could possibly be used to change state of
  9885.      * the preview plugin.
  9886.      * @param {MouseEvent} e Mouse event to process.
  9887.      */
  9888.     handleDirectionalKeyEvent: function(e) {
  9889.       // Make sure the PDF plugin is there.
  9890.       // We only care about: PageUp, PageDown, Left, Up, Right, Down.
  9891.       // If the user is holding a modifier key, ignore.
  9892.       if (!this.plugin_ ||
  9893.           !arrayContains([33, 34, 37, 38, 39, 40], e.keyCode) ||
  9894.           e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) {
  9895.         return;
  9896.       }
  9897.  
  9898.       // Don't handle the key event for these elements.
  9899.       var tagName = document.activeElement.tagName;
  9900.       if (arrayContains(['INPUT', 'SELECT', 'EMBED'], tagName)) {
  9901.         return;
  9902.       }
  9903.  
  9904.       // For the most part, if any div of header was the last clicked element,
  9905.       // then the active element is the body. Starting with the last clicked
  9906.       // element, and work up the DOM tree to see if any element has a
  9907.       // scrollbar. If there exists a scrollbar, do not handle the key event
  9908.       // here.
  9909.       var element = e.target;
  9910.       while (element) {
  9911.         if (element.scrollHeight > element.clientHeight ||
  9912.             element.scrollWidth > element.clientWidth) {
  9913.           return;
  9914.         }
  9915.         element = element.parentElement;
  9916.       }
  9917.  
  9918.       // No scroll bar anywhere, or the active element is something else, like a
  9919.       // button. Note: buttons have a bigger scrollHeight than clientHeight.
  9920.       this.plugin_.sendKeyEvent(e.keyCode);
  9921.       e.preventDefault();
  9922.     },
  9923.  
  9924.     /**
  9925.      * Shows a custom message on the preview area's overlay.
  9926.      * @param {string} message Custom message to show.
  9927.      */
  9928.     showCustomMessage: function(message) {
  9929.       this.showMessage_(PreviewArea.MessageId_.CUSTOM, message);
  9930.     },
  9931.  
  9932.     /** @override */
  9933.     enterDocument: function() {
  9934.       print_preview.Component.prototype.enterDocument.call(this);
  9935.       this.tracker.add(
  9936.           this.openSystemDialogButton_,
  9937.           'click',
  9938.           this.onOpenSystemDialogButtonClick_.bind(this));
  9939.  
  9940.       this.tracker.add(
  9941.           this.printTicketStore_,
  9942.           print_preview.PrintTicketStore.EventType.INITIALIZE,
  9943.           this.onTicketChange_.bind(this));
  9944.       this.tracker.add(
  9945.           this.printTicketStore_,
  9946.           print_preview.PrintTicketStore.EventType.TICKET_CHANGE,
  9947.           this.onTicketChange_.bind(this));
  9948.       this.tracker.add(
  9949.           this.printTicketStore_,
  9950.           print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE,
  9951.           this.onTicketChange_.bind(this));
  9952.       this.tracker.add(
  9953.           this.printTicketStore_,
  9954.           print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE,
  9955.           this.onTicketChange_.bind(this));
  9956.  
  9957.       if (this.checkPluginCompatibility_()) {
  9958.         this.previewGenerator_ = new print_preview.PreviewGenerator(
  9959.             this.destinationStore_, this.printTicketStore_, this.nativeLayer_);
  9960.         this.tracker.add(
  9961.             this.previewGenerator_,
  9962.             print_preview.PreviewGenerator.EventType.PREVIEW_START,
  9963.             this.onPreviewStart_.bind(this));
  9964.         this.tracker.add(
  9965.             this.previewGenerator_,
  9966.             print_preview.PreviewGenerator.EventType.PAGE_READY,
  9967.             this.onPagePreviewReady_.bind(this));
  9968.         this.tracker.add(
  9969.             this.previewGenerator_,
  9970.             print_preview.PreviewGenerator.EventType.FAIL,
  9971.             this.onPreviewGenerationFail_.bind(this));
  9972.         this.tracker.add(
  9973.             this.previewGenerator_,
  9974.             print_preview.PreviewGenerator.EventType.DOCUMENT_READY,
  9975.             this.onDocumentReady_.bind(this));
  9976.       } else {
  9977.         this.showCustomMessage(localStrings.getString('noPlugin'));
  9978.       }
  9979.     },
  9980.  
  9981.     /** @override */
  9982.     exitDocument: function() {
  9983.       print_preview.Component.prototype.exitDocument.call(this);
  9984.       if (this.previewGenerator_) {
  9985.         this.previewGenerator_.removeEventListeners();
  9986.       }
  9987.       this.overlayEl_ = null;
  9988.       this.openSystemDialogButton_ = null;
  9989.     },
  9990.  
  9991.     /** @override */
  9992.     decorateInternal: function() {
  9993.       this.marginControlContainer_.decorate(this.getElement());
  9994.       this.overlayEl_ = this.getElement().getElementsByClassName(
  9995.           PreviewArea.Classes_.OVERLAY)[0];
  9996.       this.openSystemDialogButton_ = this.getElement().getElementsByClassName(
  9997.           PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON)[0];
  9998.     },
  9999.  
  10000.     /**
  10001.      * Checks to see if a suitable plugin for rendering the preview exists. If
  10002.      * one does not exist, then an error message will be displayed.
  10003.      * @return {boolean} Whether Chromium has a suitable plugin for rendering
  10004.      *     the preview.
  10005.      * @private
  10006.      */
  10007.     checkPluginCompatibility_: function() {
  10008.       var compatObj = this.getElement().getElementsByClassName(
  10009.           PreviewArea.Classes_.COMPATIBILITY_OBJECT)[0];
  10010.       var isCompatible =
  10011.           compatObj.onload &&
  10012.           compatObj.goToPage &&
  10013.           compatObj.removePrintButton &&
  10014.           compatObj.loadPreviewPage &&
  10015.           compatObj.printPreviewPageCount &&
  10016.           compatObj.resetPrintPreviewUrl &&
  10017.           compatObj.onPluginSizeChanged &&
  10018.           compatObj.onScroll &&
  10019.           compatObj.pageXOffset &&
  10020.           compatObj.pageYOffset &&
  10021.           compatObj.setZoomLevel &&
  10022.           compatObj.setPageNumbers &&
  10023.           compatObj.setPageXOffset &&
  10024.           compatObj.setPageYOffset &&
  10025.           compatObj.getHorizontalScrollbarThickness &&
  10026.           compatObj.getVerticalScrollbarThickness &&
  10027.           compatObj.getPageLocationNormalized &&
  10028.           compatObj.getHeight &&
  10029.           compatObj.getWidth;
  10030.       compatObj.parentElement.removeChild(compatObj);
  10031.       return isCompatible;
  10032.     },
  10033.  
  10034.     /**
  10035.      * Shows a given message on the overlay.
  10036.      * @param {!print_preview.PreviewArea.MessageId_} messageId ID of the
  10037.      *     message to show.
  10038.      * @param {string=} opt_message Optional message to show that can be used
  10039.      *     by some message IDs.
  10040.      * @private
  10041.      */
  10042.     showMessage_: function(messageId, opt_message) {
  10043.       // Hide all messages.
  10044.       var messageEls = this.getElement().getElementsByClassName(
  10045.           PreviewArea.Classes_.MESSAGE);
  10046.       for (var i = 0, messageEl; messageEl = messageEls[i]; i++) {
  10047.         setIsVisible(messageEl, false);
  10048.       }
  10049.       // Disable jumping animation to conserve cycles.
  10050.       var jumpingDotsEl = this.getElement().querySelector(
  10051.           '.preview-area-loading-message-jumping-dots');
  10052.       jumpingDotsEl.classList.remove('jumping-dots');
  10053.  
  10054.       // Show specific message.
  10055.       if (messageId == PreviewArea.MessageId_.CUSTOM) {
  10056.         var customMessageTextEl = this.getElement().getElementsByClassName(
  10057.             PreviewArea.Classes_.CUSTOM_MESSAGE_TEXT)[0];
  10058.         customMessageTextEl.textContent = opt_message;
  10059.       } else if (messageId == PreviewArea.MessageId_.LOADING) {
  10060.         jumpingDotsEl.classList.add('jumping-dots');
  10061.       }
  10062.       var messageEl = this.getElement().getElementsByClassName(
  10063.             PreviewArea.MessageIdClassMap_[messageId])[0];
  10064.       setIsVisible(messageEl, true);
  10065.  
  10066.       // Show overlay.
  10067.       this.overlayEl_.classList.remove(PreviewArea.Classes_.INVISIBLE);
  10068.     },
  10069.  
  10070.     /**
  10071.      * Hides the message overlay.
  10072.      * @private
  10073.      */
  10074.     hideOverlay_: function() {
  10075.       this.overlayEl_.classList.add(PreviewArea.Classes_.INVISIBLE);
  10076.       // Disable jumping animation to conserve cycles.
  10077.       var jumpingDotsEl = this.getElement().querySelector(
  10078.           '.preview-area-loading-message-jumping-dots');
  10079.       jumpingDotsEl.classList.remove('jumping-dots');
  10080.     },
  10081.  
  10082.     /**
  10083.      * Creates a preview plugin and adds it to the DOM.
  10084.      * @param {string} srcUrl Initial URL of the plugin.
  10085.      * @private
  10086.      */
  10087.     createPlugin_: function(srcUrl) {
  10088.       if (this.plugin_) {
  10089.         console.warn('Pdf preview plugin already created');
  10090.         return;
  10091.       }
  10092.       this.plugin_ = document.createElement('embed');
  10093.       // NOTE: The plugin's 'id' field must be set to 'pdf-viewer' since
  10094.       // chrome/renderer/print_web_view_helper.cc actually references it.
  10095.       this.plugin_.setAttribute('id', 'pdf-viewer');
  10096.       this.plugin_.setAttribute('class', 'preview-area-plugin');
  10097.       this.plugin_.setAttribute(
  10098.           'type', 'application/x-google-chrome-print-preview-pdf');
  10099.       this.plugin_.setAttribute('src', srcUrl);
  10100.       this.plugin_.setAttribute('aria-live', 'polite');
  10101.       this.plugin_.setAttribute('aria-atomic', 'true');
  10102.       this.getChildElement('.preview-area-plugin-wrapper').
  10103.           appendChild(this.plugin_);
  10104.  
  10105.       global['onPreviewPluginLoad'] = this.onPluginLoad_.bind(this);
  10106.       this.plugin_.onload('onPreviewPluginLoad()');
  10107.  
  10108.       global['onPreviewPluginVisualStateChange'] =
  10109.           this.onPreviewVisualStateChange_.bind(this);
  10110.       this.plugin_.onScroll('onPreviewPluginVisualStateChange()');
  10111.       this.plugin_.onPluginSizeChanged('onPreviewPluginVisualStateChange()');
  10112.  
  10113.       this.plugin_.removePrintButton();
  10114.       this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled());
  10115.     },
  10116.  
  10117.     /**
  10118.      * Dispatches a PREVIEW_GENERATION_DONE event if all conditions are met.
  10119.      * @private
  10120.      */
  10121.     dispatchPreviewGenerationDoneIfReady_: function() {
  10122.       if (this.isDocumentReady_ && this.isPluginReloaded_) {
  10123.         cr.dispatchSimpleEvent(
  10124.             this, PreviewArea.EventType.PREVIEW_GENERATION_DONE);
  10125.         this.marginControlContainer_.showMarginControlsIfNeeded();
  10126.       }
  10127.     },
  10128.  
  10129.     /**
  10130.      * Called when the open-system-dialog button is clicked. Disables the
  10131.      * button, shows the throbber, and dispatches the OPEN_SYSTEM_DIALOG_CLICK
  10132.      * event.
  10133.      * @private
  10134.      */
  10135.     onOpenSystemDialogButtonClick_: function() {
  10136.       this.openSystemDialogButton_.disabled = true;
  10137.       var openSystemDialogThrobber = this.getElement().getElementsByClassName(
  10138.           PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON_THROBBER)[0];
  10139.       setIsVisible(openSystemDialogThrobber, true);
  10140.       cr.dispatchSimpleEvent(
  10141.           this, PreviewArea.EventType.OPEN_SYSTEM_DIALOG_CLICK);
  10142.     },
  10143.  
  10144.     /**
  10145.      * Called when the print ticket changes. Updates the preview.
  10146.      * @private
  10147.      */
  10148.     onTicketChange_: function() {
  10149.       if (this.previewGenerator_ && this.previewGenerator_.requestPreview()) {
  10150.         if (this.loadingTimeout_ == null) {
  10151.           this.loadingTimeout_ = setTimeout(
  10152.               this.showMessage_.bind(this, PreviewArea.MessageId_.LOADING),
  10153.               PreviewArea.LOADING_TIMEOUT_);
  10154.         }
  10155.       } else {
  10156.         this.marginControlContainer_.showMarginControlsIfNeeded();
  10157.       }
  10158.     },
  10159.  
  10160.     /**
  10161.      * Called when the preview generator begins loading the preview.
  10162.      * @param {cr.Event} Contains the URL to initialize the plugin to.
  10163.      * @private
  10164.      */
  10165.     onPreviewStart_: function(event) {
  10166.       this.isDocumentReady_ = false;
  10167.       this.isPluginReloaded_ = false;
  10168.       if (!this.plugin_) {
  10169.         this.createPlugin_(event.previewUrl);
  10170.       }
  10171.       this.plugin_.goToPage('0');
  10172.       this.plugin_.resetPrintPreviewUrl(event.previewUrl);
  10173.       this.plugin_.reload();
  10174.       this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled());
  10175.       cr.dispatchSimpleEvent(
  10176.           this, PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS);
  10177.     },
  10178.  
  10179.     /**
  10180.      * Called when a page preview has been generated. Updates the plugin with
  10181.      * the new page.
  10182.      * @param {cr.Event} event Contains information about the page preview.
  10183.      * @private
  10184.      */
  10185.     onPagePreviewReady_: function(event) {
  10186.       this.plugin_.loadPreviewPage(event.previewUrl, event.previewIndex);
  10187.     },
  10188.  
  10189.     /**
  10190.      * Called when the preview generation is complete and the document is ready
  10191.      * to print.
  10192.      * @private
  10193.      */
  10194.     onDocumentReady_: function(event) {
  10195.       this.isDocumentReady_ = true;
  10196.       this.dispatchPreviewGenerationDoneIfReady_();
  10197.     },
  10198.  
  10199.     /**
  10200.      * Called when the generation of a preview fails. Shows an error message.
  10201.      * @private
  10202.      */
  10203.     onPreviewGenerationFail_: function() {
  10204.       this.showMessage_(PreviewArea.MessageId_.PREVIEW_FAILED);
  10205.       cr.dispatchSimpleEvent(
  10206.           this, PreviewArea.EventType.PREVIEW_GENERATION_FAIL);
  10207.     },
  10208.  
  10209.     /**
  10210.      * Called when the plugin loads. This is a consequence of calling
  10211.      * plugin.reload(). Certain plugin state can only be set after the plugin
  10212.      * has loaded.
  10213.      * @private
  10214.      */
  10215.     onPluginLoad_: function() {
  10216.       if (this.loadingTimeout_) {
  10217.         clearTimeout(this.loadingTimeout_);
  10218.         this.loadingTimeout_ = null;
  10219.       }
  10220.       // Setting the plugin's page count can only be called after the plugin is
  10221.       // loaded and the document must be modifiable.
  10222.       if (this.printTicketStore_.isDocumentModifiable) {
  10223.         this.plugin_.printPreviewPageCount(
  10224.             this.printTicketStore_.getPageNumberSet().size);
  10225.       }
  10226.       this.plugin_.setPageNumbers(JSON.stringify(
  10227.           this.printTicketStore_.getPageNumberSet().asArray()));
  10228.       if (this.zoomLevel_ != null && this.pageOffset_ != null) {
  10229.         this.plugin_.setZoomLevel(this.zoomLevel_);
  10230.         this.plugin_.setPageXOffset(this.pageOffset_.x);
  10231.         this.plugin_.setPageYOffset(this.pageOffset_.y);
  10232.       } else {
  10233.         this.plugin_.fitToHeight();
  10234.       }
  10235.       this.hideOverlay_();
  10236.       this.isPluginReloaded_ = true;
  10237.       this.dispatchPreviewGenerationDoneIfReady_();
  10238.     },
  10239.  
  10240.     /**
  10241.      * Called when the preview plugin's visual state has changed. This is a
  10242.      * consequence of scrolling or zooming the plugin. Updates the custom
  10243.      * margins component if shown.
  10244.      * @private
  10245.      */
  10246.     onPreviewVisualStateChange_: function() {
  10247.       if (this.isPluginReloaded_) {
  10248.         this.zoomLevel_ = this.plugin_.getZoomLevel();
  10249.         this.pageOffset_ = new print_preview.Coordinate2d(
  10250.             this.plugin_.pageXOffset(), this.plugin_.pageYOffset());
  10251.       }
  10252.       var pageLocationNormalizedStr = this.plugin_.getPageLocationNormalized();
  10253.       if (!pageLocationNormalizedStr) {
  10254.         return;
  10255.       }
  10256.       var normalized = pageLocationNormalizedStr.split(';');
  10257.       var pluginWidth = this.plugin_.getWidth();
  10258.       var pluginHeight = this.plugin_.getHeight();
  10259.       var translationTransform = new print_preview.Coordinate2d(
  10260.           parseFloat(normalized[0]) * pluginWidth,
  10261.           parseFloat(normalized[1]) * pluginHeight);
  10262.       this.marginControlContainer_.updateTranslationTransform(
  10263.           translationTransform);
  10264.       var pageWidthInPixels = parseFloat(normalized[2]) * pluginWidth;
  10265.       this.marginControlContainer_.updateScaleTransform(
  10266.           pageWidthInPixels / this.printTicketStore_.pageSize.width);
  10267.       this.marginControlContainer_.updateClippingMask(
  10268.           new print_preview.Size(
  10269.               pluginWidth - this.plugin_.getVerticalScrollbarThickness(),
  10270.               pluginHeight - this.plugin_.getHorizontalScrollbarThickness()));
  10271.     }
  10272.   };
  10273.  
  10274.   // Export
  10275.   return {
  10276.     PreviewArea: PreviewArea
  10277.   };
  10278. });
  10279.  
  10280. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10281. // Use of this source code is governed by a BSD-style license that can be
  10282. // found in the LICENSE file.
  10283.  
  10284. cr.define('print_preview', function() {
  10285.   'use strict';
  10286.  
  10287.   /**
  10288.    * Interface to the Chromium print preview generator.
  10289.    * @param {!print_preview.DestinationStore} destinationStore Used to get the
  10290.    *     currently selected destination.
  10291.    * @param {!print_preview.PrintTicketStore} printTicketStore Used to read the
  10292.    *     state of the ticket and write document information.
  10293.    * @param {!print_preview.NativeLayer} nativeLayer Used to communicate to
  10294.    *     Chromium's preview rendering system.
  10295.    * @constructor
  10296.    * @extends {cr.EventTarget}
  10297.    */
  10298.   function PreviewGenerator(destinationStore, printTicketStore, nativeLayer) {
  10299.     cr.EventTarget.call(this);
  10300.  
  10301.     /**
  10302.      * Used to get the currently selected destination.
  10303.      * @type {!print_preview.DestinationStore}
  10304.      * @private
  10305.      */
  10306.     this.destinationStore_ = destinationStore;
  10307.  
  10308.     /**
  10309.      * Used to read the state of the ticket and write document information.
  10310.      * @type {!print_preview.PrintTicketStore}
  10311.      * @private
  10312.      */
  10313.     this.printTicketStore_ = printTicketStore;
  10314.  
  10315.     /**
  10316.      * Interface to the Chromium native layer.
  10317.      * @type {!print_preview.NativeLayer}
  10318.      * @private
  10319.      */
  10320.     this.nativeLayer_ = nativeLayer;
  10321.  
  10322.     /**
  10323.      * ID of current in-flight request. Requests that do not share this ID will
  10324.      * be ignored.
  10325.      * @type {number}
  10326.      * @private
  10327.      */
  10328.     this.inFlightRequestId_ = -1;
  10329.  
  10330.     /**
  10331.      * Whether the previews are being generated in landscape mode.
  10332.      * @type {boolean}
  10333.      * @private
  10334.      */
  10335.     this.isLandscapeEnabled_ = false;
  10336.  
  10337.     /**
  10338.      * Whether the previews are being generated with a header and footer.
  10339.      * @type {boolean}
  10340.      * @private
  10341.      */
  10342.     this.isHeaderFooterEnabled_ = false;
  10343.  
  10344.     /**
  10345.      * Whether the previews are being generated in color.
  10346.      * @type {boolean}
  10347.      * @private
  10348.      */
  10349.     this.isColorEnabled_ = false;
  10350.  
  10351.     /**
  10352.      * Whether the document should be fitted to the page.
  10353.      * @type {boolean}
  10354.      * @private
  10355.      */
  10356.     this.isFitToPageEnabled_ = false;
  10357.  
  10358.     /**
  10359.      * Page number set used to generate the last preview.
  10360.      * @type {print_preview.PageNumberSet}
  10361.      * @private
  10362.      */
  10363.     this.pageNumberSet_ = null;
  10364.  
  10365.     /**
  10366.      * Margins type used to generate the last preview.
  10367.      * @type {!print_preview.ticket_items.MarginsType.Value}
  10368.      * @private
  10369.      */
  10370.     this.marginsType_ = print_preview.ticket_items.MarginsType.Value.DEFAULT;
  10371.  
  10372.     /**
  10373.      * Destination that was selected for the last preview.
  10374.      * @type {print_preview.Destination}
  10375.      * @private
  10376.      */
  10377.     this.selectedDestination_ = null;
  10378.  
  10379.     /**
  10380.      * Event tracker used to keep track of native layer events.
  10381.      * @type {!EventTracker}
  10382.      * @private
  10383.      */
  10384.     this.tracker_ = new EventTracker();
  10385.  
  10386.     this.addEventListeners_();
  10387.   };
  10388.  
  10389.   /**
  10390.    * Event types dispatched by the preview generator.
  10391.    * @enum {string}
  10392.    */
  10393.   PreviewGenerator.EventType = {
  10394.     // Dispatched when the document can be printed.
  10395.     DOCUMENT_READY: 'print_preview.PreviewGenerator.DOCUMENT_READY',
  10396.  
  10397.     // Dispatched when a page preview is ready. The previewIndex field of the
  10398.     // event is the index of the page in the modified document, not the
  10399.     // original. So page 4 of the original document might be previewIndex = 0 of
  10400.     // the modified document.
  10401.     PAGE_READY: 'print_preview.PreviewGenerator.PAGE_READY',
  10402.  
  10403.     // Dispatched when the document preview starts to be generated.
  10404.     PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START',
  10405.  
  10406.     // Dispatched when the current print preview request fails.
  10407.     FAIL: 'print_preview.PreviewGenerator.FAIL'
  10408.   };
  10409.  
  10410.   PreviewGenerator.prototype = {
  10411.     __proto__: cr.EventTarget.prototype,
  10412.  
  10413.     /**
  10414.      * Request that new preview be generated. A preview request will not be
  10415.      * generated if the print ticket has not changed sufficiently.
  10416.      * @return {boolean} Whether a new preview was actually requested.
  10417.      */
  10418.     requestPreview: function() {
  10419.       if (!this.printTicketStore_.isTicketValidForPreview()) {
  10420.         return false;
  10421.       }
  10422.       if (!this.hasPreviewChanged_()) {
  10423.         // Changes to these ticket items might not trigger a new preview, but
  10424.         // they still need to be recorded.
  10425.         this.marginsType_ = this.printTicketStore_.getMarginsType();
  10426.         return false;
  10427.       }
  10428.       this.isLandscapeEnabled_ = this.printTicketStore_.isLandscapeEnabled();
  10429.       this.isHeaderFooterEnabled_ =
  10430.           this.printTicketStore_.isHeaderFooterEnabled();
  10431.       this.isColorEnabled_ = this.printTicketStore_.isColorEnabled();
  10432.       this.isFitToPageEnabled_ = this.printTicketStore_.isFitToPageEnabled();
  10433.       this.pageNumberSet_ = this.printTicketStore_.getPageNumberSet();
  10434.       this.marginsType_ = this.printTicketStore_.getMarginsType();
  10435.       this.selectedDestination_ = this.destinationStore_.selectedDestination;
  10436.  
  10437.       this.inFlightRequestId_++;
  10438.       this.nativeLayer_.startGetPreview(
  10439.           this.destinationStore_.selectedDestination,
  10440.           this.printTicketStore_,
  10441.           this.inFlightRequestId_);
  10442.       return true;
  10443.     },
  10444.  
  10445.     /** Removes all event listeners that the preview generator has attached. */
  10446.     removeEventListeners: function() {
  10447.       this.tracker_.removeAll();
  10448.     },
  10449.  
  10450.     /**
  10451.      * Adds event listeners to the relevant native layer events.
  10452.      * @private
  10453.      */
  10454.     addEventListeners_: function() {
  10455.       this.tracker_.add(
  10456.           this.nativeLayer_,
  10457.           print_preview.NativeLayer.EventType.PAGE_LAYOUT_READY,
  10458.           this.onPageLayoutReady_.bind(this));
  10459.       this.tracker_.add(
  10460.           this.nativeLayer_,
  10461.           print_preview.NativeLayer.EventType.PAGE_COUNT_READY,
  10462.           this.onPageCountReady_.bind(this));
  10463.       this.tracker_.add(
  10464.           this.nativeLayer_,
  10465.           print_preview.NativeLayer.EventType.PREVIEW_RELOAD,
  10466.           this.onPreviewReload_.bind(this));
  10467.       this.tracker_.add(
  10468.           this.nativeLayer_,
  10469.           print_preview.NativeLayer.EventType.PAGE_PREVIEW_READY,
  10470.           this.onPagePreviewReady_.bind(this));
  10471.       this.tracker_.add(
  10472.           this.nativeLayer_,
  10473.           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_DONE,
  10474.           this.onPreviewGenerationDone_.bind(this));
  10475.       this.tracker_.add(
  10476.           this.nativeLayer_,
  10477.           print_preview.NativeLayer.EventType.PREVIEW_GENERATION_FAIL,
  10478.           this.onPreviewGenerationFail_.bind(this));
  10479.     },
  10480.  
  10481.     /**
  10482.      * Dispatches a PAGE_READY event to signal that a page preview is ready.
  10483.      * @param {number} previewIndex Index of the page with respect to the pages
  10484.      *     shown in the preview. E.g an index of 0 is the first displayed page,
  10485.      *     but not necessarily the first original document page.
  10486.      * @param {number} pageNumber Number of the page with respect to the
  10487.      *     document. A value of 3 means it's the third page of the original
  10488.      *     document.
  10489.      * @param {number} previewUid Unique identifier of the preview.
  10490.      * @private
  10491.      */
  10492.     dispatchPageReadyEvent_: function(previewIndex, pageNumber, previewUid) {
  10493.       var pageGenEvent = new cr.Event(PreviewGenerator.EventType.PAGE_READY);
  10494.       pageGenEvent.previewIndex = previewIndex;
  10495.       pageGenEvent.previewUrl = 'chrome://print/' + previewUid.toString() +
  10496.           '/' + (pageNumber - 1) + '/print.pdf';
  10497.       this.dispatchEvent(pageGenEvent);
  10498.     },
  10499.  
  10500.     /**
  10501.      * Dispatches a PREVIEW_START event. Signals that the preview should be
  10502.      * reloaded.
  10503.      * @param {number} previewUid Unique identifier of the preview.
  10504.      * @param {number} index Index of the first page of the preview.
  10505.      * @private
  10506.      */
  10507.     dispatchPreviewStartEvent_: function(previewUid, index) {
  10508.       var previewStartEvent = new cr.Event(
  10509.           PreviewGenerator.EventType.PREVIEW_START);
  10510.       if (!this.printTicketStore_.isDocumentModifiable) {
  10511.         index = -1;
  10512.       }
  10513.       previewStartEvent.previewUrl = 'chrome://print/' +
  10514.           previewUid.toString() + '/' + index + '/print.pdf';
  10515.       this.dispatchEvent(previewStartEvent);
  10516.     },
  10517.  
  10518.     /**
  10519.      * @return {boolean} Whether the print ticket has changed sufficiently to
  10520.      *     determine whether a new preview request should be issued.
  10521.      * @private
  10522.      */
  10523.     hasPreviewChanged_: function() {
  10524.       var ticketStore = this.printTicketStore_;
  10525.       return this.inFlightRequestId_ == -1 ||
  10526.           ticketStore.isLandscapeEnabled() != this.isLandscapeEnabled_ ||
  10527.           ticketStore.isHeaderFooterEnabled() != this.isHeaderFooterEnabled_ ||
  10528.           ticketStore.isColorEnabled() != this.isColorEnabled_ ||
  10529.           ticketStore.isFitToPageEnabled() != this.isFitToPageEnabled_ ||
  10530.           !ticketStore.getPageNumberSet().equals(this.pageNumberSet_) ||
  10531.           (ticketStore.getMarginsType() != this.marginsType_ &&
  10532.               ticketStore.getMarginsType() !=
  10533.                   print_preview.ticket_items.MarginsType.Value.CUSTOM) ||
  10534.           (ticketStore.getMarginsType() ==
  10535.               print_preview.ticket_items.MarginsType.Value.CUSTOM &&
  10536.               !ticketStore.getCustomMargins().equals(
  10537.                   ticketStore.getDocumentMargins())) ||
  10538.           (this.selectedDestination_ !=
  10539.               this.destinationStore_.selectedDestination);
  10540.     },
  10541.  
  10542.     /**
  10543.      * Called when the page layout of the document is ready. Always occurs
  10544.      * as a result of a preview request.
  10545.      * @param {cr.Event} event Contains layout info about the document.
  10546.      * @private
  10547.      */
  10548.     onPageLayoutReady_: function(event) {
  10549.       // NOTE: A request ID is not specified, so assuming its for the current
  10550.       // in-flight request.
  10551.  
  10552.       var origin = new print_preview.Coordinate2d(
  10553.           event.pageLayout.printableAreaX,
  10554.           event.pageLayout.printableAreaY);
  10555.       var size = new print_preview.Size(
  10556.           event.pageLayout.printableAreaWidth,
  10557.           event.pageLayout.printableAreaHeight);
  10558.  
  10559.       var margins = new print_preview.Margins(
  10560.           Math.round(event.pageLayout.marginTop),
  10561.           Math.round(event.pageLayout.marginRight),
  10562.           Math.round(event.pageLayout.marginBottom),
  10563.           Math.round(event.pageLayout.marginLeft));
  10564.  
  10565.       var o = print_preview.ticket_items.CustomMargins.Orientation;
  10566.       var pageSize = new print_preview.Size(
  10567.           event.pageLayout.contentWidth +
  10568.               margins.get(o.LEFT) + margins.get(o.RIGHT),
  10569.           event.pageLayout.contentHeight +
  10570.               margins.get(o.TOP) + margins.get(o.BOTTOM));
  10571.  
  10572.       this.printTicketStore_.updateDocumentPageInfo(
  10573.           new print_preview.PrintableArea(origin, size),
  10574.           pageSize,
  10575.           event.hasCustomPageSizeStyle,
  10576.           margins);
  10577.     },
  10578.  
  10579.     /**
  10580.      * Called when the document page count is received from the native layer.
  10581.      * Always occurs as a result of a preview request.
  10582.      * @param {cr.Event} event Contains the document's page count.
  10583.      * @private
  10584.      */
  10585.     onPageCountReady_: function(event) {
  10586.       if (this.inFlightRequestId_ != event.previewResponseId) {
  10587.         return; // Ignore old response.
  10588.       }
  10589.       this.printTicketStore_.updatePageCount(event.pageCount);
  10590.       this.pageNumberSet_ = this.printTicketStore_.getPageNumberSet();
  10591.     },
  10592.  
  10593.     /**
  10594.      * Called when the print preview should be reloaded.
  10595.      * @param {cr.Event} event Contains the preview UID and request ID.
  10596.      * @private
  10597.      */
  10598.     onPreviewReload_: function(event) {
  10599.       if (this.inFlightRequestId_ != event.previewResponseId) {
  10600.         return; // Ignore old response.
  10601.       }
  10602.       var pageNumberSet = this.printTicketStore_.getPageNumberSet();
  10603.       this.dispatchPreviewStartEvent_(
  10604.           event.previewUid, pageNumberSet.getPageNumberAt(0) - 1);
  10605.       for (var i = 0; i < pageNumberSet.size; i++) {
  10606.         var pageNumber = pageNumberSet.getPageNumberAt(i);
  10607.         this.dispatchPageReadyEvent_(i, pageNumber, event.previewUid);
  10608.       }
  10609.       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
  10610.     },
  10611.  
  10612.     /**
  10613.      * Called when a page's preview has been generated. Dispatches a
  10614.      * PAGE_READY event.
  10615.      * @param {cr.Event} event Contains the page index and preview UID.
  10616.      * @private
  10617.      */
  10618.     onPagePreviewReady_: function(event) {
  10619.       if (this.inFlightRequestId_ != event.previewResponseId) {
  10620.         return; // Ignore old response.
  10621.       }
  10622.       var pageNumber = event.pageIndex + 1;
  10623.       if (this.printTicketStore_.getPageNumberSet().hasPageNumber(pageNumber)) {
  10624.         var previewIndex = this.printTicketStore_.getPageNumberSet()
  10625.             .getPageNumberIndex(pageNumber);
  10626.         if (previewIndex == 0) {
  10627.           this.dispatchPreviewStartEvent_(event.previewUid, event.pageIndex);
  10628.         }
  10629.         this.dispatchPageReadyEvent_(
  10630.             previewIndex, pageNumber, event.previewUid);
  10631.       }
  10632.     },
  10633.  
  10634.     /**
  10635.      * Called when the preview generation is complete. Dispatches a
  10636.      * DOCUMENT_READY event.
  10637.      * @param {cr.Event} event Contains the preview UID and response ID.
  10638.      * @private
  10639.      */
  10640.     onPreviewGenerationDone_: function(event) {
  10641.       if (this.inFlightRequestId_ != event.previewResponseId) {
  10642.         return; // Ignore old response.
  10643.       }
  10644.       // Dispatch a PREVIEW_START event since non-modifiable documents don't
  10645.       // trigger PAGE_READY events.
  10646.       if (!this.printTicketStore_.isDocumentModifiable) {
  10647.         this.dispatchPreviewStartEvent_(event.previewUid, 0);
  10648.       }
  10649.       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
  10650.     },
  10651.  
  10652.     /**
  10653.      * Called when the preview generation fails.
  10654.      * @private
  10655.      */
  10656.     onPreviewGenerationFail_: function() {
  10657.       // NOTE: No request ID is returned from Chromium so its assumed its the
  10658.       // current one.
  10659.       cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.FAIL);
  10660.     }
  10661.   };
  10662.  
  10663.   // Export
  10664.   return {
  10665.     PreviewGenerator: PreviewGenerator
  10666.   };
  10667. });
  10668.  
  10669.  
  10670. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10671. // Use of this source code is governed by a BSD-style license that can be
  10672. // found in the LICENSE file.
  10673.  
  10674. cr.define('print_preview', function() {
  10675.   'use strict';
  10676.  
  10677.   /**
  10678.    * Component that displays a list of destinations with a heading, action link,
  10679.    * and "Show All..." button. An event is dispatched when the action link is
  10680.    * activated.
  10681.    * @param {!cr.EventTarget} eventTarget Event target to pass to destination
  10682.    *     items for dispatching SELECT events.
  10683.    * @param {string} title Title of the destination list.
  10684.    * @param {string=} opt_actionLinkLabel Optional label of the action link. If
  10685.    *     no label is provided, the action link will not be shown.
  10686.    * @constructor
  10687.    * @extends {print_preview.Component}
  10688.    */
  10689.   function DestinationList(eventTarget, title, opt_actionLinkLabel) {
  10690.     print_preview.Component.call(this);
  10691.  
  10692.     /**
  10693.      * Event target to pass to destination items for dispatching SELECT events.
  10694.      * @type {!cr.EventTarget}
  10695.      * @private
  10696.      */
  10697.     this.eventTarget_ = eventTarget;
  10698.  
  10699.     /**
  10700.      * Title of the destination list.
  10701.      * @type {string}
  10702.      * @private
  10703.      */
  10704.     this.title_ = title;
  10705.  
  10706.     /**
  10707.      * Label of the action link.
  10708.      * @type {?string}
  10709.      * @private
  10710.      */
  10711.     this.actionLinkLabel_ = opt_actionLinkLabel || null;
  10712.  
  10713.     /**
  10714.      * Backing store for the destination list.
  10715.      * @type {!Array.<print_preview.Destination>}
  10716.      * @private
  10717.      */
  10718.     this.destinations_ = [];
  10719.  
  10720.     /**
  10721.      * Current query used for filtering.
  10722.      * @type {?string}
  10723.      * @private
  10724.      */
  10725.     this.query_ = null;
  10726.  
  10727.     /**
  10728.      * Whether the destination list is fully expanded.
  10729.      * @type {boolean}
  10730.      * @private
  10731.      */
  10732.     this.isShowAll_ = false;
  10733.  
  10734.     /**
  10735.      * Maximum number of destinations before showing the "Show All..." button.
  10736.      * @type {number}
  10737.      * @private
  10738.      */
  10739.     this.shortListSize_ = DestinationList.DEFAULT_SHORT_LIST_SIZE_;
  10740.   };
  10741.  
  10742.   /**
  10743.    * Enumeration of event types dispatched by the destination list.
  10744.    * @enum {string}
  10745.    */
  10746.   DestinationList.EventType = {
  10747.     // Dispatched when the action linked is activated.
  10748.     ACTION_LINK_ACTIVATED: 'print_preview.DestinationList.ACTION_LINK_ACTIVATED'
  10749.   };
  10750.  
  10751.   /**
  10752.    * Default maximum number of destinations before showing the "Show All..."
  10753.    * button.
  10754.    * @type {number}
  10755.    * @const
  10756.    * @private
  10757.    */
  10758.   DestinationList.DEFAULT_SHORT_LIST_SIZE_ = 4;
  10759.  
  10760.   /**
  10761.    * Height of a destination list item in pixels.
  10762.    * @type {number}
  10763.    * @const
  10764.    * @private
  10765.    */
  10766.   DestinationList.HEIGHT_OF_ITEM_ = 30;
  10767.  
  10768.   DestinationList.prototype = {
  10769.     __proto__: print_preview.Component.prototype,
  10770.  
  10771.     /** @param {boolean} isShowAll Whether the show-all button is activated. */
  10772.     setIsShowAll: function(isShowAll) {
  10773.       this.isShowAll_ = isShowAll;
  10774.       this.renderDestinations_();
  10775.     },
  10776.  
  10777.     /**
  10778.      * @return {number} Size of list when destination list is in collapsed
  10779.      *     mode (a.k.a non-show-all mode).
  10780.      */
  10781.     getShortListSize: function() {
  10782.       return this.shortListSize_;
  10783.     },
  10784.  
  10785.     /** @return {number} Count of the destinations in the list. */
  10786.     getDestinationsCount: function() {
  10787.       return this.destinations_.length;
  10788.     },
  10789.  
  10790.     /**
  10791.      * Gets estimated height of the destination list for the given number of
  10792.      * items.
  10793.      * @param {number} Number of items to render in the destination list.
  10794.      * @return {number} Height (in pixels) of the destination list.
  10795.      */
  10796.     getEstimatedHeightInPixels: function(numItems) {
  10797.       numItems = Math.min(numItems, this.destinations_.length);
  10798.       var headerHeight =
  10799.           this.getChildElement('.destination-list > header').offsetHeight;
  10800.       var throbberHeight =
  10801.           getIsVisible(this.getChildElement('.throbber-container')) ?
  10802.               DestinationList.HEIGHT_OF_ITEM_ : 0;
  10803.       return headerHeight + numItems * DestinationList.HEIGHT_OF_ITEM_ +
  10804.           throbberHeight;
  10805.     },
  10806.  
  10807.     /** @param {boolean} isVisible Whether the throbber is visible. */
  10808.     setIsThrobberVisible: function(isVisible) {
  10809.       setIsVisible(this.getChildElement('.throbber-container'), isVisible);
  10810.     },
  10811.  
  10812.     /**
  10813.      * @param {number} size Size of list when destination list is in collapsed
  10814.      *     mode (a.k.a non-show-all mode).
  10815.      */
  10816.     updateShortListSize: function(size) {
  10817.       size = Math.max(1, Math.min(size, this.destinations_.length));
  10818.       if (size == 1 && this.destinations_.length > 1) {
  10819.         // If this is the case, we will only show the "Show All" button and
  10820.         // nothing else. Increment the short list size by one so that we can see
  10821.         // at least one print destination.
  10822.         size++;
  10823.       }
  10824.       this.setShortListSizeInternal(size);
  10825.     },
  10826.  
  10827.     /** @override */
  10828.     createDom: function() {
  10829.       this.setElementInternal(this.cloneTemplateInternal(
  10830.           'destination-list-template'));
  10831.       this.getChildElement('.title').textContent = this.title_;
  10832.       if (this.actionLinkLabel_) {
  10833.         var actionLinkEl = this.getChildElement('.action-link');
  10834.         actionLinkEl.textContent = this.actionLinkLabel_;
  10835.         setIsVisible(actionLinkEl, true);
  10836.       }
  10837.     },
  10838.  
  10839.     /** @override */
  10840.     enterDocument: function() {
  10841.       print_preview.Component.prototype.enterDocument.call(this);
  10842.       this.tracker.add(
  10843.           this.getChildElement('.action-link'),
  10844.           'click',
  10845.           this.onActionLinkClick_.bind(this));
  10846.       this.tracker.add(
  10847.           this.getChildElement('.show-all-button'),
  10848.           'click',
  10849.           this.setIsShowAll.bind(this, true));
  10850.     },
  10851.  
  10852.     /**
  10853.      * Updates the destinations to render in the destination list.
  10854.      * @param {!Array.<print_preview.Destination>} destinations Destinations to
  10855.      *     render.
  10856.      */
  10857.     updateDestinations: function(destinations) {
  10858.       this.destinations_ = destinations;
  10859.       this.renderDestinations_();
  10860.     },
  10861.  
  10862.     /** @param {?string} query Query to update the filter with. */
  10863.     updateSearchQuery: function(query) {
  10864.       this.query_ = query;
  10865.       this.renderDestinations_();
  10866.     },
  10867.  
  10868.     /**
  10869.      * @param {string} text Text to set the action link to.
  10870.      * @protected
  10871.      */
  10872.     setActionLinkTextInternal: function(text) {
  10873.       this.actionLinkLabel_ = text;
  10874.       this.getChildElement('.action-link').textContent = text;
  10875.     },
  10876.  
  10877.     /**
  10878.      * Sets the short list size without constraints.
  10879.      * @protected
  10880.      */
  10881.     setShortListSizeInternal: function(size) {
  10882.       this.shortListSize_ = size;
  10883.       this.renderDestinations_();
  10884.     },
  10885.  
  10886.     /**
  10887.      * Renders all destinations in the given list.
  10888.      * @param {!Array.<print_preview.Destination>} destinations List of
  10889.      *     destinations to render.
  10890.      * @protected
  10891.      */
  10892.     renderListInternal: function(destinations) {
  10893.       setIsVisible(this.getChildElement('.no-destinations-message'),
  10894.                    destinations.length == 0);
  10895.       setIsVisible(this.getChildElement('.destination-list > footer'), false);
  10896.       var numItems = destinations.length;
  10897.       if (destinations.length > this.shortListSize_ && !this.isShowAll_) {
  10898.         numItems = this.shortListSize_ - 1;
  10899.         this.getChildElement('.total').textContent =
  10900.             localStrings.getStringF('destinationCount', destinations.length);
  10901.         setIsVisible(this.getChildElement('.destination-list > footer'), true);
  10902.       }
  10903.       for (var i = 0; i < numItems; i++) {
  10904.         var destListItem = new print_preview.DestinationListItem(
  10905.             this.eventTarget_, destinations[i]);
  10906.         this.addChild(destListItem);
  10907.         destListItem.render(this.getChildElement('.destination-list > ul'));
  10908.       }
  10909.     },
  10910.  
  10911.     /**
  10912.      * Renders all destinations in the list that match the current query. For
  10913.      * each render, all old destination items are first removed.
  10914.      * @private
  10915.      */
  10916.     renderDestinations_: function() {
  10917.       this.removeChildren();
  10918.  
  10919.       var filteredDests = [];
  10920.       this.destinations_.forEach(function(destination) {
  10921.         if (!this.query_ || destination.matches(this.query_)) {
  10922.           filteredDests.push(destination);
  10923.         }
  10924.       }, this);
  10925.  
  10926.       this.renderListInternal(filteredDests);
  10927.     },
  10928.  
  10929.     /**
  10930.      * Called when the action link is clicked. Dispatches an
  10931.      * ACTION_LINK_ACTIVATED event.
  10932.      * @private
  10933.      */
  10934.     onActionLinkClick_: function() {
  10935.       cr.dispatchSimpleEvent(this,
  10936.                              DestinationList.EventType.ACTION_LINK_ACTIVATED);
  10937.     }
  10938.   };
  10939.  
  10940.   // Export
  10941.   return {
  10942.     DestinationList: DestinationList
  10943.   };
  10944. });
  10945.  
  10946. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10947. // Use of this source code is governed by a BSD-style license that can be
  10948. // found in the LICENSE file.
  10949.  
  10950. cr.define('print_preview', function() {
  10951.   'use strict';
  10952.  
  10953.   /**
  10954.    * Sub-class of a destination list that shows cloud-based destinations.
  10955.    * @param {!cr.EventTarget} eventTarget Event target to pass to destination
  10956.    *     items for dispatching SELECT events.
  10957.    * @constructor
  10958.    * @extends {print_preview.DestinationList}
  10959.    */
  10960.   function CloudDestinationList(eventTarget) {
  10961.     print_preview.DestinationList.call(
  10962.         this,
  10963.         eventTarget,
  10964.         localStrings.getString('cloudDestinationsTitle'),
  10965.         localStrings.getString('manage'));
  10966.   };
  10967.  
  10968.   CloudDestinationList.prototype = {
  10969.     __proto__: print_preview.DestinationList.prototype,
  10970.  
  10971.     /** @override */
  10972.     updateDestinations: function(destinations) {
  10973.       // Change the action link from "Manage..." to "Setup..." if user only has
  10974.       // Docs and FedEx printers.
  10975.       var docsId = print_preview.Destination.GooglePromotedId.DOCS;
  10976.       var fedexId = print_preview.Destination.GooglePromotedId.FEDEX;
  10977.       if ((destinations.length == 1 && destinations[0].id == docsId) ||
  10978.           (destinations.length == 2 &&
  10979.            ((destinations[0].id == docsId && destinations[1].id == fedexId) ||
  10980.             (destinations[0].id == fedexId && destinations[1].id == docsId)))) {
  10981.         this.setActionLinkTextInternal(
  10982.             localStrings.getString('setupCloudPrinters'));
  10983.       } else {
  10984.         this.setActionLinkTextInternal(localStrings.getString('manage'));
  10985.       }
  10986.       print_preview.DestinationList.prototype.updateDestinations.call(
  10987.           this, destinations);
  10988.     }
  10989.   };
  10990.  
  10991.   return {
  10992.     CloudDestinationList: CloudDestinationList
  10993.   };
  10994. });
  10995.  
  10996. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10997. // Use of this source code is governed by a BSD-style license that can be
  10998. // found in the LICENSE file.
  10999.  
  11000. cr.define('print_preview', function() {
  11001.   'use strict';
  11002.  
  11003.   /**
  11004.    * Sub-class of a destination list that shows recent destinations. This list
  11005.    * does not render a "Show all" button.
  11006.    * @param {!cr.EventTarget} eventTarget Event target to pass to destination
  11007.    *     items for dispatching SELECT events.
  11008.    * @constructor
  11009.    * @extends {print_preview.DestinationList}
  11010.    */
  11011.   function RecentDestinationList(eventTarget) {
  11012.     print_preview.DestinationList.call(
  11013.         this,
  11014.         eventTarget,
  11015.         localStrings.getString('recentDestinationsTitle'));
  11016.   };
  11017.  
  11018.   RecentDestinationList.prototype = {
  11019.     __proto__: print_preview.DestinationList.prototype,
  11020.  
  11021.     /** @override */
  11022.     updateShortListSize: function(size) {
  11023.       this.setShortListSizeInternal(
  11024.           Math.max(1, Math.min(size, this.getDestinationsCount())));
  11025.     },
  11026.  
  11027.     /** @override */
  11028.     renderListInternal: function(destinations) {
  11029.       setIsVisible(this.getChildElement('.no-destinations-message'),
  11030.                    destinations.length == 0);
  11031.       setIsVisible(this.getChildElement('.destination-list > footer'), false);
  11032.       var numItems = Math.min(destinations.length, this.shortListSize_);
  11033.       for (var i = 0; i < numItems; i++) {
  11034.         var destListItem = new print_preview.DestinationListItem(
  11035.             this.eventTarget_, destinations[i]);
  11036.         this.addChild(destListItem);
  11037.         destListItem.render(this.getChildElement('.destination-list > ul'));
  11038.       }
  11039.     }
  11040.   };
  11041.  
  11042.   return {
  11043.     RecentDestinationList: RecentDestinationList
  11044.   };
  11045. });
  11046.  
  11047. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  11048. // Use of this source code is governed by a BSD-style license that can be
  11049. // found in the LICENSE file.
  11050.  
  11051. cr.define('print_preview', function() {
  11052.   'use strict';
  11053.  
  11054.   /**
  11055.    * Component that renders a destination item in a destination list.
  11056.    * @param {!cr.EventTarget} eventTarget Event target to dispatch selection
  11057.    *     events to.
  11058.    * @param {!print_preview.Destination} destination Destination data object to
  11059.    *     render.
  11060.    * @constructor
  11061.    * @extends {print_preview.Component}
  11062.    */
  11063.   function DestinationListItem(eventTarget, destination) {
  11064.     print_preview.Component.call(this);
  11065.  
  11066.     /**
  11067.      * Event target to dispatch selection events to.
  11068.      * @type {!cr.EventTarget}
  11069.      * @private
  11070.      */
  11071.     this.eventTarget_ = eventTarget;
  11072.  
  11073.     /**
  11074.      * Destination that the list item renders.
  11075.      * @type {!print_preview.Destination}
  11076.      * @private
  11077.      */
  11078.     this.destination_ = destination;
  11079.  
  11080.     /**
  11081.      * FedEx terms-of-service widget or {@code null} if this list item does not
  11082.      * render the FedEx Office print destination.
  11083.      * @type {print_preview.FedexTos}
  11084.      * @private
  11085.      */
  11086.     this.fedexTos_ = null;
  11087.   };
  11088.  
  11089.   /**
  11090.    * Event types dispatched by the destination list item.
  11091.    * @enum {string}
  11092.    */
  11093.   DestinationListItem.EventType = {
  11094.     // Dispatched when the list item is activated.
  11095.     SELECT: 'print_preview.DestinationListItem.SELECT'
  11096.   };
  11097.  
  11098.   /**
  11099.    * CSS classes used by the destination list item.
  11100.    * @enum {string}
  11101.    * @private
  11102.    */
  11103.   DestinationListItem.Classes_ = {
  11104.     ICON: 'destination-list-item-icon',
  11105.     NAME: 'destination-list-item-name',
  11106.     STALE: 'stale'
  11107.   };
  11108.  
  11109.   DestinationListItem.prototype = {
  11110.     __proto__: print_preview.Component.prototype,
  11111.  
  11112.     /** @override */
  11113.     createDom: function() {
  11114.       this.setElementInternal(this.cloneTemplateInternal(
  11115.           'destination-list-item-template'));
  11116.  
  11117.       var iconImg = this.getElement().getElementsByClassName(
  11118.           print_preview.DestinationListItem.Classes_.ICON)[0];
  11119.       iconImg.src = this.destination_.iconUrl;
  11120.  
  11121.       var nameEl = this.getElement().getElementsByClassName(
  11122.           DestinationListItem.Classes_.NAME)[0];
  11123.       nameEl.textContent = this.destination_.displayName;
  11124.       nameEl.title = this.destination_.displayName;
  11125.  
  11126.       this.initializeOfflineStatusElement_();
  11127.     },
  11128.  
  11129.     /** @override */
  11130.     enterDocument: function() {
  11131.       print_preview.Component.prototype.enterDocument.call(this);
  11132.       this.tracker.add(this.getElement(), 'click', this.onActivate_.bind(this));
  11133.     },
  11134.  
  11135.     /**
  11136.      * Initializes the element which renders the print destination's
  11137.      * offline status.
  11138.      * @private
  11139.      */
  11140.     initializeOfflineStatusElement_: function() {
  11141.       if (arrayContains([print_preview.Destination.ConnectionStatus.OFFLINE,
  11142.                          print_preview.Destination.ConnectionStatus.DORMANT],
  11143.                         this.destination_.connectionStatus)) {
  11144.         this.getElement().classList.add(DestinationListItem.Classes_.STALE);
  11145.         var offlineDurationMs = Date.now() - this.destination_.lastAccessTime;
  11146.         var offlineMessageId;
  11147.         if (offlineDurationMs > 31622400000.0) { // One year.
  11148.           offlineMessageId = 'offlineForYear';
  11149.         } else if (offlineDurationMs > 2678400000.0) { // One month.
  11150.           offlineMessageId = 'offlineForMonth';
  11151.         } else if (offlineDurationMs > 604800000.0) { // One week.
  11152.           offlineMessageId = 'offlineForWeek';
  11153.         } else {
  11154.           offlineMessageId = 'offline';
  11155.         }
  11156.         var offlineStatusEl = this.getElement().querySelector(
  11157.             '.offline-status');
  11158.         offlineStatusEl.textContent = localStrings.getString(offlineMessageId);
  11159.         setIsVisible(offlineStatusEl, true);
  11160.       }
  11161.     },
  11162.  
  11163.     /**
  11164.      * Called when the destination item is activated. Dispatches a SELECT event
  11165.      * on the given event target.
  11166.      * @private
  11167.      */
  11168.     onActivate_: function() {
  11169.       if (this.destination_.id ==
  11170.               print_preview.Destination.GooglePromotedId.FEDEX &&
  11171.           !this.destination_.isTosAccepted) {
  11172.         if (!this.fedexTos_) {
  11173.           this.fedexTos_ = new print_preview.FedexTos();
  11174.           this.fedexTos_.render(this.getElement());
  11175.           this.tracker.add(
  11176.               this.fedexTos_,
  11177.               print_preview.FedexTos.EventType.AGREE,
  11178.               this.onTosAgree_.bind(this));
  11179.         }
  11180.         this.fedexTos_.setIsVisible(true);
  11181.       } else {
  11182.         var selectEvt = new cr.Event(DestinationListItem.EventType.SELECT);
  11183.         selectEvt.destination = this.destination_;
  11184.         this.eventTarget_.dispatchEvent(selectEvt);
  11185.       }
  11186.     },
  11187.  
  11188.     /**
  11189.      * Called when the user agrees to the print destination's terms-of-service.
  11190.      * Selects the print destination that was agreed to.
  11191.      * @private
  11192.      */
  11193.     onTosAgree_: function() {
  11194.       var selectEvt = new cr.Event(DestinationListItem.EventType.SELECT);
  11195.       selectEvt.destination = this.destination_;
  11196.       this.eventTarget_.dispatchEvent(selectEvt);
  11197.     }
  11198.   };
  11199.  
  11200.   // Export
  11201.   return {
  11202.     DestinationListItem: DestinationListItem
  11203.   };
  11204. });
  11205.  
  11206. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  11207. // Use of this source code is governed by a BSD-style license that can be
  11208. // found in the LICENSE file.
  11209.  
  11210. cr.define('print_preview', function() {
  11211.   'use strict';
  11212.  
  11213.   /**
  11214.    * Component used for searching for a print destination.
  11215.    * This is a modal dialog that allows the user to search and select a
  11216.    * destination to print to. When a destination is selected, it is written to
  11217.    * the destination store.
  11218.    * @param {!print_preview.DestinationStore} destinationStore Data store
  11219.    *     containing the destinations to search through.
  11220.    * @param {!print_preview.UserInfo} userInfo Event target that contains
  11221.    *     information about the logged in user.
  11222.    * @param {!print_preview.Metrics} metrics Used to record usage statistics.
  11223.    * @constructor
  11224.    * @extends {print_preview.Component}
  11225.    */
  11226.   function DestinationSearch(destinationStore, userInfo, metrics) {
  11227.     print_preview.Component.call(this);
  11228.  
  11229.     /**
  11230.      * Data store containing the destinations to search through.
  11231.      * @type {!print_preview.DestinationStore}
  11232.      * @private
  11233.      */
  11234.     this.destinationStore_ = destinationStore;
  11235.  
  11236.     /**
  11237.      * Event target that contains information about the logged in user.
  11238.      * @type {!print_preview.UserInfo}
  11239.      * @private
  11240.      */
  11241.     this.userInfo_ = userInfo;
  11242.  
  11243.     /**
  11244.      * Used to record usage statistics.
  11245.      * @type {!print_preview.Metrics}
  11246.      * @private
  11247.      */
  11248.     this.metrics_ = metrics;
  11249.  
  11250.     /**
  11251.      * Search box used to search through the destination lists.
  11252.      * @type {!print_preview.SearchBox}
  11253.      * @private
  11254.      */
  11255.     this.searchBox_ = new print_preview.SearchBox();
  11256.     this.addChild(this.searchBox_);
  11257.  
  11258.     /**
  11259.      * Destination list containing recent destinations.
  11260.      * @type {!print_preview.DestinationList}
  11261.      * @private
  11262.      */
  11263.     this.recentList_ = new print_preview.RecentDestinationList(this);
  11264.     this.addChild(this.recentList_);
  11265.  
  11266.     /**
  11267.      * Destination list containing local destinations.
  11268.      * @type {!print_preview.DestinationList}
  11269.      * @private
  11270.      */
  11271.     this.localList_ = new print_preview.DestinationList(
  11272.         this,
  11273.         localStrings.getString('localDestinationsTitle'),
  11274.         cr.isChromeOS ? null : localStrings.getString('manage'));
  11275.     this.addChild(this.localList_);
  11276.  
  11277.     /**
  11278.      * Destination list containing cloud destinations.
  11279.      * @type {!print_preview.DestinationList}
  11280.      * @private
  11281.      */
  11282.     this.cloudList_ = new print_preview.CloudDestinationList(this);
  11283.     this.addChild(this.cloudList_);
  11284.   };
  11285.  
  11286.   /**
  11287.    * Event types dispatched by the component.
  11288.    * @enum {string}
  11289.    */
  11290.   DestinationSearch.EventType = {
  11291.     // Dispatched when the user requests to manage their cloud destinations.
  11292.     MANAGE_CLOUD_DESTINATIONS:
  11293.         'print_preview.DestinationSearch.MANAGE_CLOUD_DESTINATIONS',
  11294.  
  11295.     // Dispatched when the user requests to manage their local destinations.
  11296.     MANAGE_LOCAL_DESTINATIONS:
  11297.         'print_preview.DestinationSearch.MANAGE_LOCAL_DESTINATIONS',
  11298.  
  11299.     // Dispatched when the user requests to sign-in to their Google account.
  11300.     SIGN_IN: 'print_preview.DestinationSearch.SIGN_IN'
  11301.   };
  11302.  
  11303.   /**
  11304.    * Padding at the bottom of a destination list in pixels.
  11305.    * @type {number}
  11306.    * @const
  11307.    * @private
  11308.    */
  11309.   DestinationSearch.LIST_BOTTOM_PADDING_ = 18;
  11310.  
  11311.   DestinationSearch.prototype = {
  11312.     __proto__: print_preview.Component.prototype,
  11313.  
  11314.     /** @return {boolean} Whether the component is visible. */
  11315.     getIsVisible: function() {
  11316.       return !this.getElement().classList.contains('transparent');
  11317.     },
  11318.  
  11319.     /** @param {boolean} isVisible Whether the component is visible. */
  11320.     setIsVisible: function(isVisible) {
  11321.       if (isVisible) {
  11322.         this.searchBox_.focus();
  11323.         this.getElement().classList.remove('transparent');
  11324.         var promoEl = this.getChildElement('.cloudprint-promo');
  11325.         if (getIsVisible(promoEl)) {
  11326.           this.metrics_.incrementDestinationSearchBucket(
  11327.               print_preview.Metrics.DestinationSearchBucket.
  11328.                   CLOUDPRINT_PROMO_SHOWN);
  11329.         }
  11330.         this.reflowLists_();
  11331.       } else {
  11332.         this.getElement().classList.add('transparent');
  11333.         // Collapse all destination lists
  11334.         this.localList_.setIsShowAll(false);
  11335.         this.cloudList_.setIsShowAll(false);
  11336.         this.searchBox_.setQuery('');
  11337.         this.filterLists_(null);
  11338.       }
  11339.     },
  11340.  
  11341.     /** @param {string} email Email of the logged-in user. */
  11342.     setCloudPrintEmail: function(email) {
  11343.       var userInfoEl = this.getChildElement('.user-info');
  11344.       userInfoEl.textContent = localStrings.getStringF('userInfo', email);
  11345.       userInfoEl.title = localStrings.getStringF('userInfo', email);
  11346.       setIsVisible(userInfoEl, true);
  11347.       setIsVisible(this.getChildElement('.cloud-list'), true);
  11348.       setIsVisible(this.getChildElement('.cloudprint-promo'), false);
  11349.       this.reflowLists_();
  11350.     },
  11351.  
  11352.     /** Shows the Google Cloud Print promotion banner. */
  11353.     showCloudPrintPromo: function() {
  11354.       setIsVisible(this.getChildElement('.cloudprint-promo'), true);
  11355.       if (this.getIsVisible()) {
  11356.         this.metrics_.incrementDestinationSearchBucket(
  11357.             print_preview.Metrics.DestinationSearchBucket.
  11358.                 CLOUDPRINT_PROMO_SHOWN);
  11359.       }
  11360.       this.reflowLists_();
  11361.     },
  11362.  
  11363.     /** @override */
  11364.     enterDocument: function() {
  11365.       print_preview.Component.prototype.enterDocument.call(this);
  11366.       this.tracker.add(
  11367.           this.getChildElement('.page > .close-button'),
  11368.           'click',
  11369.           this.onCloseClick_.bind(this));
  11370.  
  11371.       this.tracker.add(
  11372.           this.getChildElement('.sign-in'),
  11373.           'click',
  11374.           this.onSignInActivated_.bind(this));
  11375.  
  11376.       this.tracker.add(
  11377.           this.getChildElement('.cloudprint-promo > .close-button'),
  11378.           'click',
  11379.           this.onCloudprintPromoCloseButtonClick_.bind(this));
  11380.       this.tracker.add(
  11381.           this.searchBox_,
  11382.           print_preview.SearchBox.EventType.SEARCH,
  11383.           this.onSearch_.bind(this));
  11384.       this.tracker.add(
  11385.           this,
  11386.           print_preview.DestinationListItem.EventType.SELECT,
  11387.           this.onDestinationSelect_.bind(this));
  11388.  
  11389.       this.tracker.add(
  11390.           this.destinationStore_,
  11391.           print_preview.DestinationStore.EventType.DESTINATIONS_INSERTED,
  11392.           this.onDestinationsInserted_.bind(this));
  11393.       this.tracker.add(
  11394.           this.destinationStore_,
  11395.           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
  11396.           this.onDestinationStoreSelect_.bind(this));
  11397.       this.tracker.add(
  11398.           this.destinationStore_,
  11399.           print_preview.DestinationStore.EventType.DESTINATION_SEARCH_STARTED,
  11400.           this.updateThrobbers_.bind(this));
  11401.       this.tracker.add(
  11402.           this.destinationStore_,
  11403.           print_preview.DestinationStore.EventType.DESTINATION_SEARCH_DONE,
  11404.           this.updateThrobbers_.bind(this));
  11405.  
  11406.       this.tracker.add(
  11407.           this.localList_,
  11408.           print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED,
  11409.           this.onManageLocalDestinationsActivated_.bind(this));
  11410.       this.tracker.add(
  11411.           this.cloudList_,
  11412.           print_preview.DestinationList.EventType.ACTION_LINK_ACTIVATED,
  11413.           this.onManageCloudDestinationsActivated_.bind(this));
  11414.  
  11415.       this.tracker.add(
  11416.           this.getElement(), 'click', this.onClick_.bind(this));
  11417.       this.tracker.add(
  11418.           this.getChildElement('.page'),
  11419.           'webkitAnimationEnd',
  11420.           this.onAnimationEnd_.bind(this));
  11421.  
  11422.       this.tracker.add(
  11423.           this.userInfo_,
  11424.           print_preview.UserInfo.EventType.EMAIL_CHANGE,
  11425.           this.onEmailChange_.bind(this));
  11426.  
  11427.       this.tracker.add(window, 'resize', this.onWindowResize_.bind(this));
  11428.  
  11429.       this.updateThrobbers_();
  11430.  
  11431.       // Render any destinations already in the store.
  11432.       this.renderDestinations_();
  11433.     },
  11434.  
  11435.     /** @override */
  11436.     decorateInternal: function() {
  11437.       this.searchBox_.decorate($('search-box'));
  11438.       this.recentList_.render(this.getChildElement('.recent-list'));
  11439.       this.localList_.render(this.getChildElement('.local-list'));
  11440.       this.cloudList_.render(this.getChildElement('.cloud-list'));
  11441.       this.getChildElement('.promo-text').innerHTML = localStrings.getStringF(
  11442.           'cloudPrintPromotion',
  11443.           '<span class="sign-in link-button">',
  11444.           '</span>');
  11445.     },
  11446.  
  11447.     /**
  11448.      * @return {number} Height available for destination lists, in pixels.
  11449.      * @private
  11450.      */
  11451.     getAvailableListsHeight_: function() {
  11452.       var elStyle = window.getComputedStyle(this.getElement());
  11453.       return this.getElement().offsetHeight -
  11454.           parseInt(elStyle.getPropertyValue('padding-top')) -
  11455.           parseInt(elStyle.getPropertyValue('padding-bottom')) -
  11456.           this.getChildElement('.lists').offsetTop -
  11457.           this.getChildElement('.cloudprint-promo').offsetHeight;
  11458.     },
  11459.  
  11460.     /**
  11461.      * Filters all destination lists with the given query.
  11462.      * @param {?string} query Query to filter destination lists by.
  11463.      * @private
  11464.      */
  11465.     filterLists_: function(query) {
  11466.       this.recentList_.updateSearchQuery(query);
  11467.       this.localList_.updateSearchQuery(query);
  11468.       this.cloudList_.updateSearchQuery(query);
  11469.     },
  11470.  
  11471.     /**
  11472.      * Resets the search query.
  11473.      * @private
  11474.      */
  11475.     resetSearch_: function() {
  11476.       this.searchBox_.setQuery(null);
  11477.       this.filterLists_(null);
  11478.     },
  11479.  
  11480.     /**
  11481.      * Renders all of the destinations in the destination store.
  11482.      * @private
  11483.      */
  11484.     renderDestinations_: function() {
  11485.       var recentDestinations = [];
  11486.       var localDestinations = [];
  11487.       var cloudDestinations = [];
  11488.       this.destinationStore_.destinations.forEach(function(destination) {
  11489.         if (destination.isRecent) {
  11490.           recentDestinations.push(destination);
  11491.         }
  11492.         if (destination.isLocal) {
  11493.           localDestinations.push(destination);
  11494.         } else {
  11495.           cloudDestinations.push(destination);
  11496.         }
  11497.       });
  11498.       this.recentList_.updateDestinations(recentDestinations);
  11499.       this.localList_.updateDestinations(localDestinations);
  11500.       this.cloudList_.updateDestinations(cloudDestinations);
  11501.     },
  11502.  
  11503.     /**
  11504.      * Reflows the destination lists according to the available height.
  11505.      * @private
  11506.      */
  11507.     reflowLists_: function() {
  11508.       if (!this.getIsVisible()) {
  11509.         return;
  11510.       }
  11511.  
  11512.       var hasCloudList = getIsVisible(this.getChildElement('.cloud-list'));
  11513.       var lists = [this.recentList_, this.localList_];
  11514.       if (hasCloudList) {
  11515.         lists.push(this.cloudList_);
  11516.       }
  11517.  
  11518.       var availableHeight = this.getAvailableListsHeight_();
  11519.       this.getChildElement('.lists').style.maxHeight = availableHeight + 'px';
  11520.  
  11521.       var maxListLength = lists.reduce(function(prevCount, list) {
  11522.         return Math.max(prevCount, list.getDestinationsCount());
  11523.       }, 0);
  11524.       for (var i = 1; i <= maxListLength; i++) {
  11525.         var listsHeight = lists.reduce(function(sum, list) {
  11526.           return sum + list.getEstimatedHeightInPixels(i) +
  11527.               DestinationSearch.LIST_BOTTOM_PADDING_;
  11528.         }, 0);
  11529.         if (listsHeight > availableHeight) {
  11530.           i -= 1;
  11531.           break;
  11532.         }
  11533.       }
  11534.  
  11535.       lists.forEach(function(list) {
  11536.         list.updateShortListSize(i);
  11537.       });
  11538.  
  11539.       // Set height of the list manually so that search filter doesn't change
  11540.       // lists height.
  11541.       this.getChildElement('.lists').style.height =
  11542.           lists.reduce(function(sum, list) {
  11543.             return sum + list.getEstimatedHeightInPixels(i) +
  11544.                 DestinationSearch.LIST_BOTTOM_PADDING_;
  11545.           }, 0) + 'px';
  11546.     },
  11547.  
  11548.     /**
  11549.      * Updates whether the throbbers for the various destination lists should be
  11550.      * shown or hidden.
  11551.      * @private
  11552.      */
  11553.     updateThrobbers_: function() {
  11554.       this.localList_.setIsThrobberVisible(
  11555.           this.destinationStore_.isLocalDestinationSearchInProgress);
  11556.       this.cloudList_.setIsThrobberVisible(
  11557.           this.destinationStore_.isCloudDestinationSearchInProgress);
  11558.       this.recentList_.setIsThrobberVisible(
  11559.           this.destinationStore_.isLocalDestinationSearchInProgress &&
  11560.           this.destinationStore_.isCloudDestinationSearchInProgress);
  11561.       this.reflowLists_();
  11562.     },
  11563.  
  11564.     /**
  11565.      * Called when a destination search should be executed. Filters the
  11566.      * destination lists with the given query.
  11567.      * @param {cr.Event} evt Contains the search query.
  11568.      * @private
  11569.      */
  11570.     onSearch_: function(evt) {
  11571.       this.filterLists_(evt.query);
  11572.     },
  11573.  
  11574.     /**
  11575.      * Called when the close button is clicked. Hides the search widget.
  11576.      * @private
  11577.      */
  11578.     onCloseClick_: function() {
  11579.       this.setIsVisible(false);
  11580.       this.resetSearch_();
  11581.       this.metrics_.incrementDestinationSearchBucket(
  11582.           print_preview.Metrics.DestinationSearchBucket.CANCELED);
  11583.     },
  11584.  
  11585.     /**
  11586.      * Called when a destination is selected. Clears the search and hides the
  11587.      * widget.
  11588.      * @param {cr.Event} evt Contains the selected destination.
  11589.      * @private
  11590.      */
  11591.     onDestinationSelect_: function(evt) {
  11592.       this.setIsVisible(false);
  11593.       this.resetSearch_();
  11594.       this.destinationStore_.selectDestination(evt.destination);
  11595.       this.metrics_.incrementDestinationSearchBucket(
  11596.           print_preview.Metrics.DestinationSearchBucket.DESTINATION_SELECTED);
  11597.     },
  11598.  
  11599.     /**
  11600.      * Called when a destination is selected. Selected destination are marked as
  11601.      * recent, so we have to update our recent destinations list.
  11602.      * @private
  11603.      */
  11604.     onDestinationStoreSelect_: function() {
  11605.       var destinations = this.destinationStore_.destinations;
  11606.       var recentDestinations = [];
  11607.       destinations.forEach(function(destination) {
  11608.         if (destination.isRecent) {
  11609.           recentDestinations.push(destination);
  11610.         }
  11611.       });
  11612.       this.recentList_.updateDestinations(recentDestinations);
  11613.       this.reflowLists_();
  11614.     },
  11615.  
  11616.     /**
  11617.      * Called when destinations are inserted into the store. Rerenders
  11618.      * destinations.
  11619.      * @private
  11620.      */
  11621.     onDestinationsInserted_: function() {
  11622.       this.renderDestinations_();
  11623.       this.reflowLists_();
  11624.     },
  11625.  
  11626.     /**
  11627.      * Called when the manage cloud printers action is activated.
  11628.      * @private
  11629.      */
  11630.     onManageCloudDestinationsActivated_: function() {
  11631.       cr.dispatchSimpleEvent(
  11632.           this,
  11633.           print_preview.DestinationSearch.EventType.MANAGE_CLOUD_DESTINATIONS);
  11634.     },
  11635.  
  11636.     /**
  11637.      * Called when the manage local printers action is activated.
  11638.      * @private
  11639.      */
  11640.     onManageLocalDestinationsActivated_: function() {
  11641.       cr.dispatchSimpleEvent(
  11642.           this,
  11643.           print_preview.DestinationSearch.EventType.MANAGE_LOCAL_DESTINATIONS);
  11644.     },
  11645.  
  11646.     /**
  11647.      * Called when the "Sign in" link on the Google Cloud Print promo is
  11648.      * activated.
  11649.      * @private
  11650.      */
  11651.     onSignInActivated_: function() {
  11652.       cr.dispatchSimpleEvent(this, DestinationSearch.EventType.SIGN_IN);
  11653.       this.metrics_.incrementDestinationSearchBucket(
  11654.           print_preview.Metrics.DestinationSearchBucket.SIGNIN_TRIGGERED);
  11655.     },
  11656.  
  11657.     /**
  11658.      * Called when the close button on the cloud print promo is clicked. Hides
  11659.      * the promo.
  11660.      * @private
  11661.      */
  11662.     onCloudprintPromoCloseButtonClick_: function() {
  11663.       setIsVisible(this.getChildElement('.cloudprint-promo'), false);
  11664.     },
  11665.  
  11666.     /**
  11667.      * Called when the overlay is clicked. Pulses the page.
  11668.      * @param {Event} event Contains the element that was clicked.
  11669.      * @private
  11670.      */
  11671.     onClick_: function(event) {
  11672.       if (event.target == this.getElement()) {
  11673.         this.getChildElement('.page').classList.add('pulse');
  11674.       }
  11675.     },
  11676.  
  11677.     /**
  11678.      * Called when an animation ends on the page.
  11679.      * @private
  11680.      */
  11681.     onAnimationEnd_: function() {
  11682.       this.getChildElement('.page').classList.remove('pulse');
  11683.     },
  11684.  
  11685.     /**
  11686.      * Called when the user's email field has changed. Updates the UI.
  11687.      * @private
  11688.      */
  11689.     onEmailChange_: function() {
  11690.       var userEmail = this.userInfo_.getUserEmail();
  11691.       if (userEmail) {
  11692.         this.setCloudPrintEmail(userEmail);
  11693.       }
  11694.     },
  11695.  
  11696.     /**
  11697.      * Called when the window is resized. Reflows layout of destination lists.
  11698.      * @private
  11699.      */
  11700.     onWindowResize_: function() {
  11701.       this.reflowLists_();
  11702.     }
  11703.   };
  11704.  
  11705.   // Export
  11706.   return {
  11707.     DestinationSearch: DestinationSearch
  11708.   };
  11709. });
  11710.  
  11711. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  11712. // Use of this source code is governed by a BSD-style license that can be
  11713. // found in the LICENSE file.
  11714.  
  11715. cr.define('print_preview', function() {
  11716.   'use strict';
  11717.  
  11718.   /**
  11719.    * Component that renders a search box for searching through destinations.
  11720.    * @constructor
  11721.    * @extends {print_preview.Component}
  11722.    */
  11723.   function SearchBox() {
  11724.     print_preview.Component.call(this);
  11725.  
  11726.     /**
  11727.      * Timeout used to control incremental search.
  11728.      * @type {?number}
  11729.      * @private
  11730.      */
  11731.      this.timeout_ = null;
  11732.  
  11733.     /**
  11734.      * Input box where the query is entered.
  11735.      * @type {HTMLInputElement}
  11736.      * @private
  11737.      */
  11738.     this.input_ = null;
  11739.   };
  11740.  
  11741.   /**
  11742.    * Enumeration of event types dispatched from the search box.
  11743.    * @enum {string}
  11744.    */
  11745.   SearchBox.EventType = {
  11746.     SEARCH: 'print_preview.SearchBox.SEARCH'
  11747.   };
  11748.  
  11749.   /**
  11750.    * CSS classes used by the search box.
  11751.    * @enum {string}
  11752.    * @private
  11753.    */
  11754.   SearchBox.Classes_ = {
  11755.     INPUT: 'search-box-input'
  11756.   };
  11757.  
  11758.   /**
  11759.    * Delay in milliseconds before dispatching a SEARCH event.
  11760.    * @type {number}
  11761.    * @const
  11762.    * @private
  11763.    */
  11764.   SearchBox.SEARCH_DELAY_ = 150;
  11765.  
  11766.   SearchBox.prototype = {
  11767.     __proto__: print_preview.Component.prototype,
  11768.  
  11769.     /** @param {string} New query to set the search box's query to. */
  11770.     setQuery: function(query) {
  11771.       query = query || '';
  11772.       this.input_.value = query.trim();
  11773.     },
  11774.  
  11775.     /** Sets the input element of the search box in focus. */
  11776.     focus: function() {
  11777.       this.input_.focus();
  11778.     },
  11779.  
  11780.     /** @override */
  11781.     enterDocument: function() {
  11782.       print_preview.Component.prototype.enterDocument.call(this);
  11783.       this.tracker.add(this.input_, 'keydown', this.onInputKeyDown_.bind(this));
  11784.     },
  11785.  
  11786.     /** @override */
  11787.     exitDocument: function() {
  11788.       print_preview.Component.prototype.exitDocument.call(this);
  11789.       this.input_ = null;
  11790.     },
  11791.  
  11792.     /** @override */
  11793.     decorateInternal: function() {
  11794.       this.input_ = this.getElement().getElementsByClassName(
  11795.           SearchBox.Classes_.INPUT)[0];
  11796.     },
  11797.  
  11798.     /**
  11799.      * @return {string} The current query of the search box.
  11800.      * @private
  11801.      */
  11802.     getQuery_: function() {
  11803.       return this.input_.value.trim();
  11804.     },
  11805.  
  11806.     /**
  11807.      * Dispatches a SEARCH event.
  11808.      * @private
  11809.      */
  11810.     dispatchSearchEvent_: function() {
  11811.       this.timeout_ = null;
  11812.       var searchEvent = new cr.Event(SearchBox.EventType.SEARCH);
  11813.       searchEvent.query = this.getQuery_();
  11814.       this.dispatchEvent(searchEvent);
  11815.     },
  11816.  
  11817.     /**
  11818.      * Called when the input element's value changes. Dispatches a search event.
  11819.      * @private
  11820.      */
  11821.     onInputKeyDown_: function() {
  11822.       if (this.timeout_) {
  11823.         clearTimeout(this.timeout_);
  11824.       }
  11825.       this.timeout_ = setTimeout(
  11826.           this.dispatchSearchEvent_.bind(this), SearchBox.SEARCH_DELAY_);
  11827.     }
  11828.   };
  11829.  
  11830.   // Export
  11831.   return {
  11832.     SearchBox: SearchBox
  11833.   };
  11834. });
  11835.  
  11836. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  11837. // Use of this source code is governed by a BSD-style license that can be
  11838. // found in the LICENSE file.
  11839.  
  11840. cr.define('print_preview', function() {
  11841.   'use strict';
  11842.  
  11843.   /**
  11844.    * Widget that renders a terms-of-service agreement for using the FedEx Office
  11845.    * print destination.
  11846.    * @constructor
  11847.    * @extends {print_preview.Component}
  11848.    */
  11849.   function FedexTos() {
  11850.     print_preview.Component.call(this);
  11851.   };
  11852.  
  11853.   /**
  11854.    * Enumeration of event types dispatched by the widget.
  11855.    * @enum {string}
  11856.    */
  11857.   FedexTos.EventType = {
  11858.     // Dispatched when the user agrees to the terms-of-service.
  11859.     AGREE: 'print_preview.FedexTos.AGREE'
  11860.   };
  11861.  
  11862.   FedexTos.prototype = {
  11863.     __proto__: print_preview.Component.prototype,
  11864.  
  11865.     /** @param {boolean} isVisible Whether the widget is visible. */
  11866.     setIsVisible: function(isVisible) {
  11867.       if (isVisible) {
  11868.         var heightHelperEl = this.getElement().querySelector('.height-helper');
  11869.         this.getElement().style.height = heightHelperEl.offsetHeight + 'px';
  11870.       } else {
  11871.         this.getElement().style.height = 0;
  11872.       }
  11873.     },
  11874.  
  11875.     /** @override */
  11876.     createDom: function() {
  11877.       this.setElementInternal(this.cloneTemplateInternal('fedex-tos-template'));
  11878.       var tosTextEl = this.getElement().querySelector('.tos-text');
  11879.       tosTextEl.innerHTML = localStrings.getStringF(
  11880.           'fedexTos',
  11881.           '<a href="http://www.fedex.com/us/office/copyprint/online/' +
  11882.               'googlecloudprint/termsandconditions">',
  11883.           '</a>');
  11884.     },
  11885.  
  11886.     /** @override */
  11887.     enterDocument: function() {
  11888.       var agreeCheckbox = this.getElement().querySelector('.agree-checkbox');
  11889.       this.tracker.add(
  11890.           agreeCheckbox, 'click', this.onAgreeCheckboxClick_.bind(this));
  11891.     },
  11892.  
  11893.     /**
  11894.      * Called when the agree checkbox is clicked. Dispatches a AGREE event.
  11895.      * @private
  11896.      */
  11897.     onAgreeCheckboxClick_: function() {
  11898.       cr.dispatchSimpleEvent(this, FedexTos.EventType.AGREE);
  11899.     }
  11900.   };
  11901.  
  11902.   // Export
  11903.   return {
  11904.     FedexTos: FedexTos
  11905.   };
  11906. });
  11907.  
  11908.  
  11909. window.addEventListener('DOMContentLoaded', function() {
  11910.   printPreview = new print_preview.PrintPreview();
  11911.   printPreview.initialize();
  11912. });
  11913.