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

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

Issue 2862203002: Print Preview: Fix data/ errors (Closed)
Patch Set: Make tests pass Created 3 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.exportPath('print_preview'); 5 cr.exportPath('print_preview');
6 6
7 /** 7 /**
8 * Enumeration of the types of destinations.
9 * @enum {string}
10 */
11 print_preview.DestinationType = {
12 GOOGLE: 'google',
13 GOOGLE_PROMOTED: 'google_promoted',
14 LOCAL: 'local',
15 MOBILE: 'mobile'
16 };
17
18 /**
19 * Enumeration of the origin types for cloud destinations.
20 * @enum {string}
21 */
22 print_preview.DestinationOrigin = {
23 LOCAL: 'local',
24 COOKIES: 'cookies',
25 PROFILE: 'profile',
26 DEVICE: 'device',
27 PRIVET: 'privet',
28 EXTENSION: 'extension',
29 CROS: 'chrome_os',
30 };
31
32 /**
33 * Enumeration of the connection statuses of printer destinations.
34 * @enum {string}
35 */
36 print_preview.DestinationConnectionStatus = {
37 DORMANT: 'DORMANT',
38 OFFLINE: 'OFFLINE',
39 ONLINE: 'ONLINE',
40 UNKNOWN: 'UNKNOWN',
41 UNREGISTERED: 'UNREGISTERED'
42 };
43
44 /**
45 * Enumeration specifying whether a destination is provisional and the reason
46 * the destination is provisional.
47 * @enum {string}
48 */
49 print_preview.DestinationProvisionalType = {
50 /** Destination is not provisional. */
51 NONE: 'NONE',
52 /**
53 * User has to grant USB access for the destination to its provider.
54 * Used for destinations with extension origin.
55 */
56 NEEDS_USB_PERMISSION: 'NEEDS_USB_PERMISSION'
57 };
58
59 /**
8 * The CDD (Cloud Device Description) describes the capabilities of a print 60 * The CDD (Cloud Device Description) describes the capabilities of a print
9 * destination. 61 * destination.
10 * 62 *
11 * @typedef {{ 63 * @typedef {{
12 * version: string, 64 * version: string,
13 * printer: { 65 * printer: {
14 * vendor_capability: !Array<{Object}>, 66 * vendor_capability: !Array<{Object}>,
15 * collate: ({default: (boolean|undefined)}|undefined), 67 * collate: ({default: (boolean|undefined)}|undefined),
16 * color: ({ 68 * color: ({
17 * option: !Array<{ 69 * option: !Array<{
18 * type: (string|undefined), 70 * type: (string|undefined),
19 * vendor_id: (string|undefined), 71 * vendor_id: (string|undefined),
20 * custom_display_name: (string|undefined), 72 * custom_display_name: (string|undefined),
21 * is_default: (boolean|undefined) 73 * is_default: (boolean|undefined)
22 * }> 74 * }>
23 * }|undefined), 75 * }|undefined),
24 * copies: ({default: (number|undefined), 76 * copies: ({default: (number|undefined),
25 * max: (number|undefined)}|undefined), 77 * max: (number|undefined)}|undefined),
26 * duplex: ({option: !Array<{type: (string|undefined), 78 * duplex: ({option: !Array<{type: (string|undefined),
27 * is_default: (boolean|undefined)}>}|undefined), 79 * is_default: (boolean|undefined)}>}|undefined),
28 * page_orientation: ({ 80 * page_orientation: ({
29 * option: !Array<{type: (string|undefined), 81 * option: !Array<{type: (string|undefined),
30 * is_default: (boolean|undefined)}> 82 * is_default: (boolean|undefined)}>
83 * }|undefined),
84 * media_size: ({
85 * option: !Array<{
86 * type: (string|undefined),
87 * vendor_id: (string|undefined),
88 * custom_display_name: (string|undefined),
89 * is_default: (boolean|undefined)
90 * }>
31 * }|undefined) 91 * }|undefined)
32 * } 92 * }
33 * }} 93 * }}
34 */ 94 */
35 print_preview.Cdd; 95 print_preview.Cdd;
36 96
37 cr.define('print_preview', function() { 97 cr.define('print_preview', function() {
38 'use strict'; 98 'use strict';
39 99
40 /** 100 /**
41 * Print destination data object that holds data for both local and cloud 101 * Print destination data object that holds data for both local and cloud
42 * destinations. 102 * destinations.
43 * @param {string} id ID of the destination. 103 * @param {string} id ID of the destination.
44 * @param {!print_preview.Destination.Type} type Type of the destination. 104 * @param {!print_preview.DestinationType} type Type of the destination.
45 * @param {!print_preview.Destination.Origin} origin Origin of the 105 * @param {!print_preview.DestinationOrigin} origin Origin of the
46 * destination. 106 * destination.
47 * @param {string} displayName Display name of the destination. 107 * @param {string} displayName Display name of the destination.
48 * @param {boolean} isRecent Whether the destination has been used recently. 108 * @param {boolean} isRecent Whether the destination has been used recently.
49 * @param {!print_preview.Destination.ConnectionStatus} connectionStatus 109 * @param {!print_preview.DestinationConnectionStatus} connectionStatus
50 * Connection status of the print destination. 110 * Connection status of the print destination.
51 * @param {{tags: (Array<string>|undefined), 111 * @param {{tags: (Array<string>|undefined),
52 * isOwned: (boolean|undefined), 112 * isOwned: (boolean|undefined),
53 * isEnterprisePrinter: (boolean|undefined), 113 * isEnterprisePrinter: (boolean|undefined),
54 * account: (string|undefined), 114 * account: (string|undefined),
55 * lastAccessTime: (number|undefined), 115 * lastAccessTime: (number|undefined),
56 * cloudID: (string|undefined), 116 * cloudID: (string|undefined),
57 * provisionalType: 117 * provisionalType:
58 * (print_preview.Destination.ProvisionalType|undefined), 118 * (print_preview.DestinationProvisionalType|undefined),
59 * extensionId: (string|undefined), 119 * extensionId: (string|undefined),
60 * extensionName: (string|undefined), 120 * extensionName: (string|undefined),
61 * description: (string|undefined)}=} opt_params Optional parameters 121 * description: (string|undefined)}=} opt_params Optional parameters
62 * for the destination. 122 * for the destination.
63 * @constructor 123 * @constructor
64 */ 124 */
65 function Destination(id, type, origin, displayName, isRecent, 125 function Destination(id, type, origin, displayName, isRecent,
66 connectionStatus, opt_params) { 126 connectionStatus, opt_params) {
67 /** 127 /**
68 * ID of the destination. 128 * ID of the destination.
69 * @private {string} 129 * @private {string}
70 */ 130 */
71 this.id_ = id; 131 this.id_ = id;
72 132
73 /** 133 /**
74 * Type of the destination. 134 * Type of the destination.
75 * @private {!print_preview.Destination.Type} 135 * @private {!print_preview.DestinationType}
76 */ 136 */
77 this.type_ = type; 137 this.type_ = type;
78 138
79 /** 139 /**
80 * Origin of the destination. 140 * Origin of the destination.
81 * @private {!print_preview.Destination.Origin} 141 * @private {!print_preview.DestinationOrigin}
82 */ 142 */
83 this.origin_ = origin; 143 this.origin_ = origin;
84 144
85 /** 145 /**
86 * Display name of the destination. 146 * Display name of the destination.
87 * @private {string} 147 * @private {string}
88 */ 148 */
89 this.displayName_ = displayName || ''; 149 this.displayName_ = displayName || '';
90 150
91 /** 151 /**
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 this.location_ = null; 192 this.location_ = null;
133 193
134 /** 194 /**
135 * Printer description. 195 * Printer description.
136 * @private {string} 196 * @private {string}
137 */ 197 */
138 this.description_ = (opt_params && opt_params.description) || ''; 198 this.description_ = (opt_params && opt_params.description) || '';
139 199
140 /** 200 /**
141 * Connection status of the destination. 201 * Connection status of the destination.
142 * @private {!print_preview.Destination.ConnectionStatus} 202 * @private {!print_preview.DestinationConnectionStatus}
143 */ 203 */
144 this.connectionStatus_ = connectionStatus; 204 this.connectionStatus_ = connectionStatus;
145 205
146 /** 206 /**
147 * Number of milliseconds since the epoch when the printer was last 207 * Number of milliseconds since the epoch when the printer was last
148 * accessed. 208 * accessed.
149 * @private {number} 209 * @private {number}
150 */ 210 */
151 this.lastAccessTime_ = (opt_params && opt_params.lastAccessTime) || 211 this.lastAccessTime_ = (opt_params && opt_params.lastAccessTime) ||
152 Date.now(); 212 Date.now();
(...skipping 10 matching lines...) Expand all
163 */ 223 */
164 this.extensionId_ = (opt_params && opt_params.extensionId) || ''; 224 this.extensionId_ = (opt_params && opt_params.extensionId) || '';
165 225
166 /** 226 /**
167 * Extension name for extension managed printers. 227 * Extension name for extension managed printers.
168 * @private {string} 228 * @private {string}
169 */ 229 */
170 this.extensionName_ = (opt_params && opt_params.extensionName) || ''; 230 this.extensionName_ = (opt_params && opt_params.extensionName) || '';
171 231
172 /** 232 /**
173 * Different from {@code Destination.ProvisionalType.NONE} if the 233 * Different from {@code print_preview.DestinationProvisionalType.NONE} if
174 * destination is provisional. Provisional destinations cannot be selected 234 * the destination is provisional. Provisional destinations cannot be
175 * as they are, but have to be resolved first (i.e. extra steps have to be 235 * selected as they are, but have to be resolved first (i.e. extra steps
176 * taken to get actual destination properties, which should replace the 236 * have to be taken to get actual destination properties, which should
177 * provisional ones). Provisional destination resolvment flow will be 237 * replace the provisional ones). Provisional destination resolvment flow
178 * started when the user attempts to select the destination in search UI. 238 * will be started when the user attempts to select the destination in
179 * @private {Destination.ProvisionalType} 239 * search UI.
240 * @private {print_preview.DestinationProvisionalType}
180 */ 241 */
181 this.provisionalType_ = (opt_params && opt_params.provisionalType) || 242 this.provisionalType_ = (opt_params && opt_params.provisionalType) ||
182 Destination.ProvisionalType.NONE; 243 print_preview.DestinationProvisionalType.NONE;
183 244
184 assert(this.provisionalType_ != 245 assert(this.provisionalType_ !=
185 Destination.ProvisionalType.NEEDS_USB_PERMISSON || 246 print_preview.DestinationProvisionalType.NEEDS_USB_PERMISSION ||
186 this.isExtension, 247 this.isExtension,
187 'Provisional USB destination only supprted with extension origin.'); 248 'Provisional USB destination only supprted with extension origin.');
188 }; 249 }
189 250
190 /** 251 /**
191 * Prefix of the location destination tag. 252 * Prefix of the location destination tag.
192 * @type {!Array<string>} 253 * @type {!Array<string>}
193 * @const 254 * @const
194 */ 255 */
195 Destination.LOCATION_TAG_PREFIXES = [ 256 Destination.LOCATION_TAG_PREFIXES = [
196 '__cp__location=', 257 '__cp__location=',
197 '__cp__printer-location=' 258 '__cp__printer-location='
198 ]; 259 ];
199 260
200 /** 261 /**
201 * Enumeration of Google-promoted destination IDs. 262 * Enumeration of Google-promoted destination IDs.
202 * @enum {string} 263 * @enum {string}
203 */ 264 */
204 Destination.GooglePromotedId = { 265 Destination.GooglePromotedId = {
205 DOCS: '__google__docs', 266 DOCS: '__google__docs',
206 SAVE_AS_PDF: 'Save as PDF' 267 SAVE_AS_PDF: 'Save as PDF'
207 }; 268 };
208 269
209 /** 270 /**
210 * Enumeration of the types of destinations.
211 * @enum {string}
212 */
213 Destination.Type = {
214 GOOGLE: 'google',
215 GOOGLE_PROMOTED: 'google_promoted',
216 LOCAL: 'local',
217 MOBILE: 'mobile'
218 };
219
220 /**
221 * Enumeration of the origin types for cloud destinations.
222 * @enum {string}
223 */
224 Destination.Origin = {
225 LOCAL: 'local',
226 COOKIES: 'cookies',
227 PROFILE: 'profile',
228 DEVICE: 'device',
229 PRIVET: 'privet',
230 EXTENSION: 'extension',
231 CROS: 'chrome_os',
232 };
233
234 /**
235 * Enumeration of the connection statuses of printer destinations.
236 * @enum {string}
237 */
238 Destination.ConnectionStatus = {
239 DORMANT: 'DORMANT',
240 OFFLINE: 'OFFLINE',
241 ONLINE: 'ONLINE',
242 UNKNOWN: 'UNKNOWN',
243 UNREGISTERED: 'UNREGISTERED'
244 };
245
246 /**
247 * Enumeration specifying whether a destination is provisional and the reason
248 * the destination is provisional.
249 * @enum {string}
250 */
251 Destination.ProvisionalType = {
252 /** Destination is not provisional. */
253 NONE: 'NONE',
254 /**
255 * User has to grant USB access for the destination to its provider.
256 * Used for destinations with extension origin.
257 */
258 NEEDS_USB_PERMISSION: 'NEEDS_USB_PERMISSION'
259 };
260
261 /**
262 * Enumeration of relative icon URLs for various types of destinations. 271 * Enumeration of relative icon URLs for various types of destinations.
263 * @enum {string} 272 * @enum {string}
264 * @private 273 * @private
265 */ 274 */
266 Destination.IconUrl_ = { 275 Destination.IconUrl_ = {
267 CLOUD: 'images/printer.png', 276 CLOUD: 'images/printer.png',
268 CLOUD_SHARED: 'images/printer_shared.png', 277 CLOUD_SHARED: 'images/printer_shared.png',
269 LOCAL: 'images/printer.png', 278 LOCAL: 'images/printer.png',
270 MOBILE: 'images/mobile.png', 279 MOBILE: 'images/mobile.png',
271 MOBILE_SHARED: 'images/mobile_shared.png', 280 MOBILE_SHARED: 'images/mobile_shared.png',
272 THIRD_PARTY: 'images/third_party.png', 281 THIRD_PARTY: 'images/third_party.png',
273 PDF: 'images/pdf.png', 282 PDF: 'images/pdf.png',
274 DOCS: 'images/google_doc.png', 283 DOCS: 'images/google_doc.png',
275 ENTERPRISE: 'images/business.svg' 284 ENTERPRISE: 'images/business.svg'
276 }; 285 };
277 286
278 Destination.prototype = { 287 Destination.prototype = {
279 /** @return {string} ID of the destination. */ 288 /** @return {string} ID of the destination. */
280 get id() { 289 get id() {
281 return this.id_; 290 return this.id_;
282 }, 291 },
283 292
284 /** @return {!print_preview.Destination.Type} Type of the destination. */ 293 /** @return {!print_preview.DestinationType} Type of the destination. */
285 get type() { 294 get type() {
286 return this.type_; 295 return this.type_;
287 }, 296 },
288 297
289 /** 298 /**
290 * @return {!print_preview.Destination.Origin} Origin of the destination. 299 * @return {!print_preview.DestinationOrigin} Origin of the destination.
291 */ 300 */
292 get origin() { 301 get origin() {
293 return this.origin_; 302 return this.origin_;
294 }, 303 },
295 304
296 /** @return {string} Display name of the destination. */ 305 /** @return {string} Display name of the destination. */
297 get displayName() { 306 get displayName() {
298 return this.displayName_; 307 return this.displayName_;
299 }, 308 },
300 309
(...skipping 19 matching lines...) Expand all
320 329
321 /** 330 /**
322 * @return {string} Account this destination is registered for, if known. 331 * @return {string} Account this destination is registered for, if known.
323 */ 332 */
324 get account() { 333 get account() {
325 return this.account_; 334 return this.account_;
326 }, 335 },
327 336
328 /** @return {boolean} Whether the destination is local or cloud-based. */ 337 /** @return {boolean} Whether the destination is local or cloud-based. */
329 get isLocal() { 338 get isLocal() {
330 return this.origin_ == Destination.Origin.LOCAL || 339 return this.origin_ == print_preview.DestinationOrigin.LOCAL ||
331 this.origin_ == Destination.Origin.EXTENSION || 340 this.origin_ == print_preview.DestinationOrigin.EXTENSION ||
332 this.origin_ == Destination.Origin.CROS || 341 this.origin_ == print_preview.DestinationOrigin.CROS ||
333 (this.origin_ == Destination.Origin.PRIVET && 342 (this.origin_ == print_preview.DestinationOrigin.PRIVET &&
334 this.connectionStatus_ != 343 this.connectionStatus_ !=
335 Destination.ConnectionStatus.UNREGISTERED); 344 print_preview.DestinationConnectionStatus.UNREGISTERED);
336 }, 345 },
337 346
338 /** @return {boolean} Whether the destination is a Privet local printer */ 347 /** @return {boolean} Whether the destination is a Privet local printer */
339 get isPrivet() { 348 get isPrivet() {
340 return this.origin_ == Destination.Origin.PRIVET; 349 return this.origin_ == print_preview.DestinationOrigin.PRIVET;
341 }, 350 },
342 351
343 /** 352 /**
344 * @return {boolean} Whether the destination is an extension managed 353 * @return {boolean} Whether the destination is an extension managed
345 * printer. 354 * printer.
346 */ 355 */
347 get isExtension() { 356 get isExtension() {
348 return this.origin_ == Destination.Origin.EXTENSION; 357 return this.origin_ == print_preview.DestinationOrigin.EXTENSION;
349 }, 358 },
350 359
351 /** 360 /**
352 * @return {string} The location of the destination, or an empty string if 361 * @return {string} The location of the destination, or an empty string if
353 * the location is unknown. 362 * the location is unknown.
354 */ 363 */
355 get location() { 364 get location() {
356 if (this.location_ == null) { 365 if (this.location_ == null) {
357 this.location_ = ''; 366 this.location_ = '';
358 this.tags_.some(function(tag) { 367 this.tags_.some(function(tag) {
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
419 428
420 /** 429 /**
421 * @param {!print_preview.Cdd} capabilities Print capabilities of the 430 * @param {!print_preview.Cdd} capabilities Print capabilities of the
422 * destination. 431 * destination.
423 */ 432 */
424 set capabilities(capabilities) { 433 set capabilities(capabilities) {
425 this.capabilities_ = capabilities; 434 this.capabilities_ = capabilities;
426 }, 435 },
427 436
428 /** 437 /**
429 * @return {!print_preview.Destination.ConnectionStatus} Connection status 438 * @return {!print_preview.DestinationConnectionStatus} Connection status
430 * of the print destination. 439 * of the print destination.
431 */ 440 */
432 get connectionStatus() { 441 get connectionStatus() {
433 return this.connectionStatus_; 442 return this.connectionStatus_;
434 }, 443 },
435 444
436 /** 445 /**
437 * @param {!print_preview.Destination.ConnectionStatus} status Connection 446 * @param {!print_preview.DestinationConnectionStatus} status Connection
438 * status of the print destination. 447 * status of the print destination.
439 */ 448 */
440 set connectionStatus(status) { 449 set connectionStatus(status) {
441 this.connectionStatus_ = status; 450 this.connectionStatus_ = status;
442 }, 451 },
443 452
444 /** @return {boolean} Whether the destination is considered offline. */ 453 /** @return {boolean} Whether the destination is considered offline. */
445 get isOffline() { 454 get isOffline() {
446 return arrayContains([print_preview.Destination.ConnectionStatus.OFFLINE, 455 return arrayContains([print_preview.DestinationConnectionStatus.OFFLINE,
447 print_preview.Destination.ConnectionStatus.DORMANT], 456 print_preview.DestinationConnectionStatus.DORMANT],
448 this.connectionStatus_); 457 this.connectionStatus_);
449 }, 458 },
450 459
451 /** @return {string} Human readable status for offline destination. */ 460 /** @return {string} Human readable status for offline destination. */
452 get offlineStatusText() { 461 get offlineStatusText() {
453 if (!this.isOffline) { 462 if (!this.isOffline) {
454 return ''; 463 return '';
455 } 464 }
456 var offlineDurationMs = Date.now() - this.lastAccessTime_; 465 var offlineDurationMs = Date.now() - this.lastAccessTime_;
457 var offlineMessageId; 466 var offlineMessageId;
(...skipping 24 matching lines...) Expand all
482 } 491 }
483 if (this.id_ == Destination.GooglePromotedId.SAVE_AS_PDF) { 492 if (this.id_ == Destination.GooglePromotedId.SAVE_AS_PDF) {
484 return Destination.IconUrl_.PDF; 493 return Destination.IconUrl_.PDF;
485 } 494 }
486 if (this.isEnterprisePrinter) { 495 if (this.isEnterprisePrinter) {
487 return Destination.IconUrl_.ENTERPRISE; 496 return Destination.IconUrl_.ENTERPRISE;
488 } 497 }
489 if (this.isLocal) { 498 if (this.isLocal) {
490 return Destination.IconUrl_.LOCAL; 499 return Destination.IconUrl_.LOCAL;
491 } 500 }
492 if (this.type_ == Destination.Type.MOBILE && this.isOwned_) { 501 if (this.type_ == print_preview.DestinationType.MOBILE &&
502 this.isOwned_) {
493 return Destination.IconUrl_.MOBILE; 503 return Destination.IconUrl_.MOBILE;
494 } 504 }
495 if (this.type_ == Destination.Type.MOBILE) { 505 if (this.type_ == print_preview.DestinationType.MOBILE) {
496 return Destination.IconUrl_.MOBILE_SHARED; 506 return Destination.IconUrl_.MOBILE_SHARED;
497 } 507 }
498 if (this.isOwned_) { 508 if (this.isOwned_) {
499 return Destination.IconUrl_.CLOUD; 509 return Destination.IconUrl_.CLOUD;
500 } 510 }
501 return Destination.IconUrl_.CLOUD_SHARED; 511 return Destination.IconUrl_.CLOUD_SHARED;
502 }, 512 },
503 513
504 /** 514 /**
505 * @return {!Array<string>} Properties (besides display name) to match 515 * @return {!Array<string>} Properties (besides display name) to match
(...skipping 12 matching lines...) Expand all
518 matches: function(query) { 528 matches: function(query) {
519 return !!this.displayName_.match(query) || 529 return !!this.displayName_.match(query) ||
520 !!this.extensionName_.match(query) || 530 !!this.extensionName_.match(query) ||
521 this.extraPropertiesToMatch.some(function(property) { 531 this.extraPropertiesToMatch.some(function(property) {
522 return property.match(query); 532 return property.match(query);
523 }); 533 });
524 }, 534 },
525 535
526 /** 536 /**
527 * Gets the destination's provisional type. 537 * Gets the destination's provisional type.
528 * @return {Destination.ProvisionalType} 538 * @return {print_preview.DestinationProvisionalType}
529 */ 539 */
530 get provisionalType() { 540 get provisionalType() {
531 return this.provisionalType_; 541 return this.provisionalType_;
532 }, 542 },
533 543
534 /** 544 /**
535 * Whether the destinaion is provisional. 545 * Whether the destinaion is provisional.
536 * @return {boolean} 546 * @return {boolean}
537 */ 547 */
538 get isProvisional() { 548 get isProvisional() {
539 return this.provisionalType_ != Destination.ProvisionalType.NONE; 549 return this.provisionalType_ !=
550 print_preview.DestinationProvisionalType.NONE;
540 }, 551 },
541 552
542 /** 553 /**
543 * Whether the printer is enterprise policy controlled printer. 554 * Whether the printer is enterprise policy controlled printer.
544 * @return {boolean} 555 * @return {boolean}
545 */ 556 */
546 get isEnterprisePrinter() { 557 get isEnterprisePrinter() {
547 return this.isEnterprisePrinter_; 558 return this.isEnterprisePrinter_;
548 }, 559 },
549 }; 560 };
550 561
551 // Export 562 // Export
552 return { 563 return {
553 Destination: Destination, 564 Destination: Destination,
554 }; 565 };
555 }); 566 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698