Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(588)

Side by Side Diff: chrome/browser/resources/print_preview/data/destination_store.js

Issue 10450022: Print Preview Print Destination Search Widget (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Reduces size of search image. Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 cr.define('print_preview', function() { 5 cr.define('print_preview', function() {
6 'use strict'; 6 'use strict';
7 7
8 /** 8 /**
9 * A data store that stores destinations and dispatches events when the data 9 * A data store that stores destinations and dispatches events when the data
10 * store changes. 10 * store changes.
11 * @param {!print_preview.NativeLayer} nativeLayer Used to fetch local print
12 * destinations.
11 * @constructor 13 * @constructor
12 * @extends {cr.EventTarget} 14 * @extends {cr.EventTarget}
13 */ 15 */
14 function DestinationStore() { 16 function DestinationStore(nativeLayer) {
15 cr.EventTarget.call(this); 17 cr.EventTarget.call(this);
16 18
17 /** 19 /**
20 * Used to fetch local print destinations.
21 * @type {!print_preview.NativeLayer}
22 * @private
23 */
24 this.nativeLayer_ = nativeLayer;
25
26 /**
18 * Internal backing store for the data store. 27 * Internal backing store for the data store.
19 * @type {!Array.<print_preview.Destination>} 28 * @type {!Array.<!print_preview.Destination>}
20 * @private 29 * @private
21 */ 30 */
22 this.destinations_ = []; 31 this.destinations_ = [];
23 32
24 /** 33 /**
34 * Cache used for constant lookup of printers.
35 * @type {object.<string, !print_preview.Destination>}
36 * @private
37 */
38 this.destinationMap_ = {};
39
40 /**
25 * Currently selected destination. 41 * Currently selected destination.
26 * @type {print_preview.Destination} 42 * @type {print_preview.Destination}
27 * @private 43 * @private
28 */ 44 */
29 this.selectedDestination_ = null; 45 this.selectedDestination_ = null;
30 46
31 /** 47 /**
32 * Initial destination ID used to auto-select the first inserted destination 48 * Initial destination ID used to auto-select the first inserted destination
33 * that matches. If {@code null}, the first destination inserted into the 49 * that matches. If {@code null}, the first destination inserted into the
34 * store will be selected. 50 * store will be selected.
35 * @type {?string} 51 * @type {?string}
36 * @private 52 * @private
37 */ 53 */
38 this.initialDestinationId_ = null; 54 this.initialDestinationId_ = null;
39 55
40 /** 56 /**
41 * Whether the destination store will auto select the destination that 57 * Whether the destination store will auto select the destination that
42 * matches the initial destination. 58 * matches the initial destination.
43 * @type {boolean} 59 * @type {boolean}
44 * @private 60 * @private
45 */ 61 */
46 this.isInAutoSelectMode_ = false; 62 this.isInAutoSelectMode_ = false;
63
64 /**
65 * Event tracker used to track event listeners of the destination store.
66 * @type {!EventTracker}
67 * @private
68 */
69 this.tracker_ = new EventTracker();
70
71 /**
72 * Used to fetch cloud-based print destinations.
73 * @type {!print_preview.CloudPrintInterface}
dpapad 2012/05/25 16:18:03 cant use @type {!Type} this.blah_ = null; ! means
Robert Toscano 2012/05/25 19:47:01 Whoops copy paste error. Done.
74 * @private
75 */
76 this.cloudPrintInterface_ = null;
77
78 /**
79 * Whether the destination store has already loaded or is loading all cloud
80 * destinations.
81 * @type {boolean}
82 * @private
83 */
84 this.hasLoadedAllCloudDestinations_ = false;
85
86 /**
87 * Timeout object that fires after the initial destination is set, but not
88 * matched with a destination within a timeout.
89 * @type {Object}
90 * @private
91 */
92 this.autoSelectTimeout_ = null;
dpapad 2012/05/25 16:18:03 Isn't this just a timeout id, therefore a number?
Robert Toscano 2012/05/25 19:47:01 I never looked up what exactly was returned by set
93
94 this.addEventListeners_();
95 this.reset_();
47 }; 96 };
48 97
49 /** 98 /**
50 * Event types dispatched by the data store. 99 * Event types dispatched by the data store.
51 * @enum {string} 100 * @enum {string}
52 */ 101 */
53 DestinationStore.EventType = { 102 DestinationStore.EventType = {
54 DESTINATIONS_INSERTED: 103 DESTINATIONS_INSERTED:
55 'print_preview.DestinationStore.DESTINATIONS_INSERTED', 104 'print_preview.DestinationStore.DESTINATIONS_INSERTED',
56 DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT' 105 DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT',
106 SELECTED_DESTINATION_CAPABILITIES_READY:
107 'print_preview.DestinationStore.SELECTED_DESTINATION_CAPABILITIES_READY'
108 };
109
110 /**
111 * Delay in milliseconds before the destination store ignores the initial
112 * destination ID and just selects any printer (since the initial destination
113 * was not found).
114 * @type {number}
115 * @const
116 * @private
117 */
118 DestinationStore.AUTO_SELECT_TIMEOUT_ = 2000;
119
120 /**
121 * Creates a local PDF print destination.
122 * @return {!print_preview.Destination} Created print destination.
123 * @private
124 */
125 DestinationStore.createLocalPdfPrintDestination_ = function() {
126 var dest = new print_preview.Destination(
127 print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
128 print_preview.Destination.Type.LOCAL,
129 localStrings.getString('printToPDF'),
130 false /*isRecent*/);
131 dest.capabilities = new print_preview.ChromiumCapabilities(
132 false /*hasCopiesCapability*/,
133 '1' /*defaultCopiesStr*/,
134 false /*hasCollateCapability*/,
135 false /*defaultIsCollateEnabled*/,
136 false /*hasDuplexCapability*/,
137 false /*defaultIsDuplexEnabled*/,
138 true /*hasOrientationCapability*/,
139 false /*defaultIsLandscapeEnabled*/,
140 true /*hasColorCapability*/,
141 true /*defaultIsColorEnabled*/);
142 return dest;
57 }; 143 };
58 144
59 DestinationStore.prototype = { 145 DestinationStore.prototype = {
60 __proto__: cr.EventTarget.prototype, 146 __proto__: cr.EventTarget.prototype,
61 147
62 /** 148 /**
63 * @return {!Array.<!print_preview.Destination>} List of destinations in 149 * @return {!Array.<!print_preview.Destination>} List of destinations in
64 * the store. 150 * the store.
65 */ 151 */
66 get destinations() { 152 get destinations() {
67 return this.destinations_.slice(0); 153 return this.destinations_.slice(0);
68 }, 154 },
69 155
70 /** 156 /**
71 * @return {print_preview.Destination} The currently selected destination or 157 * @return {print_preview.Destination} The currently selected destination or
72 * {@code null} if none is selected. 158 * {@code null} if none is selected.
73 */ 159 */
74 get selectedDestination() { 160 get selectedDestination() {
75 return this.selectedDestination_; 161 return this.selectedDestination_;
76 }, 162 },
77 163
78 /** 164 /**
79 * Sets the initially selected destination. If any inserted destinations 165 * Sets the initially selected destination. If any inserted destinations
80 * match this ID, that destination will be automatically selected. This 166 * match this ID, that destination will be automatically selected. This
81 * occurs only once for every time this setter is called or if the store is 167 * occurs only once for every time this setter is called or if the store is
82 * cleared. 168 * cleared.
83 * @param {string} ID of the destination that should be selected 169 * @param {?string} ID of the destination that should be selected
84 * automatically when added to the store. 170 * automatically when added to the store or {@code null} if the first
171 * destination that is inserted should be selected.
85 */ 172 */
86 setInitialDestinationId: function(initialDestinationId) { 173 setInitialDestinationId: function(initialDestinationId) {
87 this.initialDestinationId_ = initialDestinationId; 174 this.initialDestinationId_ = initialDestinationId;
88 this.isInAutoSelectMode_ = true; 175 this.isInAutoSelectMode_ = true;
89 if (this.initialDestinationId_ == null && this.destinations_.length > 0) { 176 if (this.initialDestinationId_ == null) {
177 assert(this.destinations_.length > 0,
178 'No destinations available to select');
90 this.selectDestination(this.destinations_[0]); 179 this.selectDestination(this.destinations_[0]);
91 } else if (this.initialDestinationId_ != null) { 180 } else {
92 for (var dest, i = 0; dest = this.destinations_[i]; i++) { 181 var candidate = this.destinationMap_[this.initialDestinationId_];
93 if (dest.id == initialDestinationId) { 182 if (candidate != null) {
94 this.selectDestination(dest); 183 this.selectDestination(candidate);
95 break;
96 }
97 } 184 }
98 } 185 }
99 }, 186 },
100 187
188 /**
189 * Sets the destination store's Google Cloud Print interface.
190 * @param {!print_preview.CloudPrintInterface} cloudPrintInterface Interface
191 * to set.
192 */
193 setCloudPrintInterface: function(cloudPrintInterface) {
194 this.cloudPrintInterface_ = cloudPrintInterface;
195 this.tracker_.add(
196 this.cloudPrintInterface_,
197 cloudprint.CloudPrintInterface.EventType.SEARCH_DONE,
198 this.onCloudPrintSearchDone_.bind(this));
199 this.tracker_.add(
200 this.cloudPrintInterface_,
201 cloudprint.CloudPrintInterface.EventType.PRINTER_DONE,
202 this.onCloudPrintPrinterDone_.bind(this));
203 },
204
101 /** @param {!print_preview.Destination} Destination to select. */ 205 /** @param {!print_preview.Destination} Destination to select. */
102 selectDestination: function(destination) { 206 selectDestination: function(destination) {
103 this.selectedDestination_ = destination; 207 this.selectedDestination_ = destination;
104 this.selectedDestination_.isRecent = true; 208 this.selectedDestination_.isRecent = true;
105 this.isInAutoSelectMode_ = false; 209 this.isInAutoSelectMode_ = false;
210 if (this.autoSelectTimeout_ != null) {
211 clearTimeout(this.autoSelectTimeout_);
212 this.autoSelectTimeout_ = null;
213 }
106 cr.dispatchSimpleEvent( 214 cr.dispatchSimpleEvent(
107 this, DestinationStore.EventType.DESTINATION_SELECT); 215 this, DestinationStore.EventType.DESTINATION_SELECT);
216 if (destination.capabilities == null) {
217 if (destination.type == print_preview.Destination.Type.LOCAL) {
218 this.nativeLayer_.startGetLocalDestinationCapabilities(
219 destination.id);
220 } else {
221 assert(this.cloudPrintInterface_ != null,
222 'Selected destination is a cloud destination, but Google ' +
223 'Cloud Print is not enabled');
224 this.cloudPrintInterface_.printer(destination.id);
225 }
226 } else {
227 cr.dispatchSimpleEvent(
228 this,
229 DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
230 }
108 }, 231 },
109 232
110 /** 233 /**
111 * Inserts a print destination to the data store and dispatches a 234 * Inserts a print destination to the data store and dispatches a
112 * DESTINATIONS_INSERTED event. If the destination matches the initial 235 * DESTINATIONS_INSERTED event. If the destination matches the initial
113 * destination ID, then the destination will be automatically selected. 236 * destination ID, then the destination will be automatically selected.
114 * @param {!print_preview.Destination} destination Print destination to 237 * @param {!print_preview.Destination} destination Print destination to
115 * insert. 238 * insert.
116 */ 239 */
117 insertDestination: function(destination) { 240 insertDestination: function(destination) {
118 this.destinations_.push(destination); 241 if (this.insertDestination_(destination)) {
119 cr.dispatchSimpleEvent( 242 cr.dispatchSimpleEvent(
120 this, DestinationStore.EventType.DESTINATIONS_INSERTED); 243 this, DestinationStore.EventType.DESTINATIONS_INSERTED);
121 if (this.isInAutoSelectMode_) { 244 if (this.isInAutoSelectMode_ &&
122 if (this.initialDestinationId_ == null) { 245 (this.initialDestinationId_ == null ||
246 destination.id == this.initialDestinationId_)) {
123 this.selectDestination(destination); 247 this.selectDestination(destination);
124 } else {
125 if (destination.id == this.initialDestinationId_) {
126 this.selectDestination(destination);
127 }
128 } 248 }
129 } 249 }
130 }, 250 },
131 251
132 /** 252 /**
133 * Inserts multiple print destinations to the data store and dispatches one 253 * Inserts multiple print destinations to the data store and dispatches one
134 * DESTINATIONS_INSERTED event. If any of the destinations match the initial 254 * DESTINATIONS_INSERTED event. If any of the destinations match the initial
135 * destination ID, then that destination will be automatically selected. 255 * destination ID, then that destination will be automatically selected.
136 * @param {!Array.<print_preview.Destination>} destinations Print 256 * @param {!Array.<print_preview.Destination>} destinations Print
137 * destinations to insert. 257 * destinations to insert.
138 */ 258 */
139 insertDestinations: function(destinations) { 259 insertDestinations: function(destinations) {
140 this.destinations_ = this.destinations_.concat(destinations); 260 var insertedDestination = false;
141 cr.dispatchSimpleEvent( 261 var destinationToAutoSelect = null;
142 this, DestinationStore.EventType.DESTINATIONS_INSERTED); 262 for (var dest, i = 0; dest = destinations[i]; i++) {
dpapad 2012/05/25 16:18:03 This can probably be simplified if you use forEach
Robert Toscano 2012/05/25 19:47:01 Thanks! Didn't know about this one. Done.
143 if (this.isInAutoSelectMode_) { 263 if (this.insertDestination_(dest)) {
dpapad 2012/05/25 16:18:03 You can merge these 2 nested if statements as foll
Robert Toscano 2012/05/25 19:47:01 Kausalya recommended not using assignments in "if"
144 if (this.initialDestinationId_ == null && destinations.length > 0) { 264 insertedDestination = true;
145 this.selectDestination(destinations[0]); 265 if (this.isInAutoSelectMode_ &&
146 } else if (this.initialDestinationId_ != null) { 266 destinationToAutoSelect == null &&
147 for (var dest, i = 0; dest = destinations[i]; i++) { 267 (this.initialDestinationId_ == null ||
148 if (dest.id == this.initialDestinationId_) { 268 dest.id == this.initialDestinationId_)) {
149 this.selectDestination(dest); 269 destinationToAutoSelect = dest;
150 break;
151 }
152 } 270 }
153 } 271 }
154 } 272 }
273 if (insertedDestination) {
274 cr.dispatchSimpleEvent(
275 this, DestinationStore.EventType.DESTINATIONS_INSERTED);
276 }
277 if (destinationToAutoSelect != null) {
278 this.selectDestination(destinationToAutoSelect);
279 }
155 }, 280 },
156 281
157 /** 282 /**
158 * Updates an existing print destination with capabilities information. If 283 * Updates an existing print destination with capabilities information. If
159 * the destination doesn't already exist, it will be added. 284 * the destination doesn't already exist, it will be added.
160 * @param {!print_preview.Destination} destination Destination to update. 285 * @param {!print_preview.Destination} destination Destination to update.
161 * @return {!print_preview.Destination} The existing destination that was 286 * @return {!print_preview.Destination} The existing destination that was
162 * updated. 287 * updated.
163 */ 288 */
164 updateDestination: function(destination) { 289 updateDestination: function(destination) {
165 var existingDestination = null; 290 var existingDestination = this.destinationMap_[destination.id];
166 for (var d, i = 0; d = this.destinations_[i]; i++) { 291 if (existingDestination != null) {
167 if (destination.id == d.id) {
168 existingDestination = d;
169 break;
170 }
171 }
172 if (existingDestination) {
173 existingDestination.capabilities = destination.capabilities; 292 existingDestination.capabilities = destination.capabilities;
174 return existingDestination; 293 return existingDestination;
175 } else { 294 } else {
176 this.insertDestination(destination); 295 this.insertDestination(destination);
177 } 296 }
178 }, 297 },
179 298
180 /** Clears all print destinations. */ 299 /** Initiates loading of local print destinations. */
181 clear: function() { 300 startLoadLocalDestinations: function() {
301 this.nativeLayer_.startGetLocalDestinations();
302 },
303
304 /** Initiates loading of recent cloud destinations. */
305 startLoadRecentCloudDestinations: function() {
306 if (this.cloudPrintInterface_ != null) {
307 this.cloudPrintInterface_.search(true /*isRecent*/);
308 }
309 },
310
311 /** Initiates loading of all cloud destinations. */
312 startLoadAllCloudDestinations: function() {
313 if (this.cloudPrintInterface_ != null &&
314 !this.hasLoadedAllCloudDestinations_) {
315 this.cloudPrintInterface_.search(false /*isRecent*/);
316 this.hasLoadedAllCloudDestinations_ = true;
317 }
318 },
319
320 /**
321 * Inserts a destination into the store without dispatching any events.
322 * @return {boolean} Whether the inserted destination was not already in the
323 * store.
324 * @private
325 */
326 insertDestination_: function(destination) {
327 if (this.destinationMap_[destination.id] == null) {
328 this.destinations_.push(destination);
329 this.destinationMap_[destination.id] = destination;
330 return true;
331 } else {
332 return false;
333 }
334 },
335
336 /**
337 * Binds handlers to events.
338 * @private
339 */
340 addEventListeners_: function() {
341 this.tracker_.add(
342 this.nativeLayer_,
343 print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET,
344 this.onLocalDestinationsSet_.bind(this));
345 this.tracker_.add(
346 this.nativeLayer_,
347 print_preview.NativeLayer.EventType.CAPABILITIES_SET,
348 this.onLocalDestinationCapabilitiesSet_.bind(this));
349 this.tracker_.add(
350 this.nativeLayer_,
351 print_preview.NativeLayer.EventType.DESTINATIONS_RELOAD,
352 this.onDestinationsReload_.bind(this));
353 },
354
355 /**
356 * Resets the state of the destination store to its initial state.
357 * @private
358 */
359 reset_: function() {
182 this.destinations_ = []; 360 this.destinations_ = [];
361 this.destinationMap_ = {};
183 this.selectedDestination_ = null; 362 this.selectedDestination_ = null;
184 this.isInAutoSelectMode_ = true; 363 this.isInAutoSelectMode_ = true;
364 this.hasLoadedAllCloudDestinations_ = false;
365 this.insertDestination(
366 DestinationStore.createLocalPdfPrintDestination_());
367 this.autoSelectTimeout_ = setTimeout(
368 this.onAutoSelectTimeoutExpired_.bind(this),
369 DestinationStore.AUTO_SELECT_TIMEOUT_);
370 },
371
372 /**
373 * Called when the local destinations have been got from the native layer.
374 * @param {cr.Event} Contains the local destinations.
375 * @private
376 */
377 onLocalDestinationsSet_: function(event) {
378 var localDestinations = [];
379 for (var destInfo, i = 0; destInfo = event.destinationInfos[i]; i++) {
dpapad 2012/05/25 16:18:03 use forEach.
Robert Toscano 2012/05/25 19:47:01 Done.
380 localDestinations.push(
381 print_preview.LocalDestinationParser.parse(destInfo));
382 }
383 this.insertDestinations(localDestinations);
384 },
385
386 /**
387 * Called when the native layer retrieves the capabilities for the selected
388 * local destination.
389 * @param {cr.Event} event Contains the capabilities of the local print
390 * destination.
391 * @private
392 */
393 onLocalDestinationCapabilitiesSet_: function(event) {
394 // TODO There may be a race condition here. This method is assumed to
395 // return capabilities for the currently selected printer. But between the
396 // time the local printer was selected and the capabilities were
397 // retrieved, the selected printer can change. One way to address this is
398 // to include the destination ID in the event.settingsInfo parameter.
399 if (this.selectedDestination_ && this.selectedDestination_.isLocal) {
400 var capabilities = print_preview.LocalCapabilitiesParser.parse(
401 event.settingsInfo);
402 this.selectedDestination_.capabilities = capabilities;
403 cr.dispatchSimpleEvent(
404 this,
405 DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
406 }
407 },
408
409 /**
410 * Called when the /search call completes. Adds the fetched printers to the
411 * destination store.
412 * @param {cr.Event} event Contains the fetched printers.
413 * @private
414 */
415 onCloudPrintSearchDone_: function(event) {
416 this.insertDestinations(event.printers);
417 },
418
419 /**
420 * Called when /printer call completes. Updates the specified destination's
421 * print capabilities.
422 * @param {cr.Event} event Contains detailed information about the
423 * destination.
424 * @private
425 */
426 onCloudPrintPrinterDone_: function(event) {
427 var dest = this.updateDestination(event.printer);
428 if (this.selectedDestination_ == dest) {
429 cr.dispatchSimpleEvent(
430 this,
431 DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
432 }
433 },
434
435 /**
436 * Called from native layer after the user was requested to sign in, and did
437 * so successfully.
438 * @private
439 */
440 onDestinationsReload_: function() {
441 this.reset_();
442 this.startLoadLocalDestinations();
443 this.startLoadRecentCloudDestinations();
444 this.startLoadAllCloudDestinations();
445 },
446
447 /**
448 * Called when no destination was auto-selected after some timeout. Selects
449 * the first destination in store.
450 * @private
451 */
452 onAutoSelectTimeoutExpired_: function() {
453 this.autoSelectTimeout_ = null;
454 assert(this.destinations_.length > 0,
455 'No destinations were loaded before auto-select timeout expired');
456 this.selectDestination(this.destinations_[0]);
185 } 457 }
186 }; 458 };
187 459
188 // Export 460 // Export
189 return { 461 return {
190 DestinationStore: DestinationStore 462 DestinationStore: DestinationStore
191 }; 463 };
192 }); 464 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698