OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 /** | 5 /** |
6 * @fileoverview 'settings-autofill-section' is the section containing saved | 6 * @fileoverview 'settings-autofill-section' is the section containing saved |
7 * addresses and credit cards for use in autofill. | 7 * addresses and credit cards for use in autofill. |
8 */ | 8 */ |
9 | 9 |
10 /** | 10 /** |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
139 chrome.autofillPrivate.maskCreditCard(assert(guid)); | 139 chrome.autofillPrivate.maskCreditCard(assert(guid)); |
140 }, | 140 }, |
141 | 141 |
142 /** @override */ | 142 /** @override */ |
143 saveCreditCard: function(creditCard) { | 143 saveCreditCard: function(creditCard) { |
144 chrome.autofillPrivate.saveCreditCard(creditCard); | 144 chrome.autofillPrivate.saveCreditCard(creditCard); |
145 } | 145 } |
146 }; | 146 }; |
147 | 147 |
148 (function() { | 148 (function() { |
149 'use strict'; | 149 'use strict'; |
150 | 150 |
151 Polymer({ | 151 Polymer({ |
152 is: 'settings-autofill-section', | 152 is: 'settings-autofill-section', |
153 | 153 |
154 behaviors: [I18nBehavior], | 154 behaviors: [I18nBehavior], |
155 | 155 |
156 properties: { | 156 properties: { |
157 /** | |
158 * An array of saved addresses. | |
159 * @type {!Array<!AutofillManager.AddressEntry>} | |
160 */ | |
161 addresses: Array, | |
162 | |
163 /** | |
164 * The model for any address related action menus or dialogs. | |
165 * @private {?chrome.autofillPrivate.AddressEntry} | |
166 */ | |
167 activeAddress: Object, | |
168 | |
169 /** @private */ | |
170 showAddressDialog_: Boolean, | |
171 | |
172 /** | |
173 * An array of saved credit cards. | |
174 * @type {!Array<!AutofillManager.CreditCardEntry>} | |
175 */ | |
176 creditCards: Array, | |
177 | |
178 /** | |
179 * The model for any credit card related action menus or dialogs. | |
180 * @private {?chrome.autofillPrivate.CreditCardEntry} | |
181 */ | |
182 activeCreditCard: Object, | |
183 | |
184 /** @private */ | |
185 showCreditCardDialog_: Boolean, | |
186 }, | |
187 | |
188 listeners: { | |
189 'save-address': 'saveAddress_', | |
190 'save-credit-card': 'saveCreditCard_', | |
191 }, | |
192 | |
193 /** | 157 /** |
194 * The element to return focus to, when the currently active dialog is | 158 * An array of saved addresses. |
195 * closed. | 159 * @type {!Array<!AutofillManager.AddressEntry>} |
196 * @private {?HTMLElement} | |
197 */ | 160 */ |
198 activeDialogAnchor_: null, | 161 addresses: Array, |
199 | 162 |
200 /** | 163 /** |
201 * @type {AutofillManager} | 164 * The model for any address related action menus or dialogs. |
202 * @private | 165 * @private {?chrome.autofillPrivate.AddressEntry} |
203 */ | 166 */ |
204 autofillManager_: null, | 167 activeAddress: Object, |
| 168 |
| 169 /** @private */ |
| 170 showAddressDialog_: Boolean, |
205 | 171 |
206 /** | 172 /** |
207 * @type {?function(!Array<!AutofillManager.AddressEntry>)} | 173 * An array of saved credit cards. |
208 * @private | 174 * @type {!Array<!AutofillManager.CreditCardEntry>} |
209 */ | 175 */ |
210 setAddressesListener_: null, | 176 creditCards: Array, |
211 | 177 |
212 /** | 178 /** |
213 * @type {?function(!Array<!AutofillManager.CreditCardEntry>)} | 179 * The model for any credit card related action menus or dialogs. |
214 * @private | 180 * @private {?chrome.autofillPrivate.CreditCardEntry} |
215 */ | 181 */ |
216 setCreditCardsListener_: null, | 182 activeCreditCard: Object, |
217 | |
218 /** @override */ | |
219 attached: function() { | |
220 // Create listener functions. | |
221 /** @type {function(!Array<!AutofillManager.AddressEntry>)} */ | |
222 var setAddressesListener = function(list) { | |
223 this.addresses = list; | |
224 }.bind(this); | |
225 | |
226 /** @type {function(!Array<!AutofillManager.CreditCardEntry>)} */ | |
227 var setCreditCardsListener = function(list) { | |
228 this.creditCards = list; | |
229 }.bind(this); | |
230 | |
231 // Remember the bound reference in order to detach. | |
232 this.setAddressesListener_ = setAddressesListener; | |
233 this.setCreditCardsListener_ = setCreditCardsListener; | |
234 | |
235 // Set the managers. These can be overridden by tests. | |
236 this.autofillManager_ = AutofillManagerImpl.getInstance(); | |
237 | |
238 // Request initial data. | |
239 this.autofillManager_.getAddressList(setAddressesListener); | |
240 this.autofillManager_.getCreditCardList(setCreditCardsListener); | |
241 | |
242 // Listen for changes. | |
243 this.autofillManager_.addAddressListChangedListener(setAddressesListener); | |
244 this.autofillManager_.addCreditCardListChangedListener( | |
245 setCreditCardsListener); | |
246 }, | |
247 | |
248 /** @override */ | |
249 detached: function() { | |
250 this.autofillManager_.removeAddressListChangedListener( | |
251 /** @type {function(!Array<!AutofillManager.AddressEntry>)} */( | |
252 this.setAddressesListener_)); | |
253 this.autofillManager_.removeCreditCardListChangedListener( | |
254 /** @type {function(!Array<!AutofillManager.CreditCardEntry>)} */( | |
255 this.setCreditCardsListener_)); | |
256 }, | |
257 | |
258 /** | |
259 * Formats the expiration date so it's displayed as MM/YYYY. | |
260 * @param {!chrome.autofillPrivate.CreditCardEntry} item | |
261 * @return {string} | |
262 * @private | |
263 */ | |
264 expiration_: function(item) { | |
265 return item.expirationMonth + '/' + item.expirationYear; | |
266 }, | |
267 | |
268 /** | |
269 * Open the address action menu. | |
270 * @param {!Event} e The polymer event. | |
271 * @private | |
272 */ | |
273 onAddressMenuTap_: function(e) { | |
274 var menuEvent = /** @type {!{model: !{item: !Object}}} */(e); | |
275 | |
276 /* TODO(scottchen): drop the [dataHost][dataHost] once this bug is fixed: | |
277 https://github.com/Polymer/polymer/issues/2574 */ | |
278 var item = menuEvent.model['dataHost']['dataHost'].item; | |
279 | |
280 // Copy item so dialog won't update model on cancel. | |
281 this.activeAddress = /** @type {!chrome.autofillPrivate.AddressEntry} */( | |
282 Object.assign({}, item)); | |
283 | |
284 var dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); | |
285 /** @type {!CrActionMenuElement} */ ( | |
286 this.$.addressSharedMenu).showAt(dotsButton); | |
287 this.activeDialogAnchor_ = dotsButton; | |
288 }, | |
289 | |
290 /** | |
291 * Handles tapping on the "Add address" button. | |
292 * @param {!Event} e The polymer event. | |
293 * @private | |
294 */ | |
295 onAddAddressTap_: function(e) { | |
296 e.preventDefault(); | |
297 this.activeAddress = {}; | |
298 this.showAddressDialog_ = true; | |
299 this.activeDialogAnchor_ = this.$.addAddress; | |
300 }, | |
301 | 183 |
302 /** @private */ | 184 /** @private */ |
303 onAddressDialogClosed_: function() { | 185 showCreditCardDialog_: Boolean, |
304 this.showAddressDialog_ = false; | 186 }, |
305 cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); | 187 |
306 this.activeDialogAnchor_ = null; | 188 listeners: { |
307 }, | 189 'save-address': 'saveAddress_', |
308 | 190 'save-credit-card': 'saveCreditCard_', |
309 /** | 191 }, |
310 * Handles tapping on the "Edit" address button. | 192 |
311 * @param {!Event} e The polymer event. | 193 /** |
312 * @private | 194 * The element to return focus to, when the currently active dialog is |
313 */ | 195 * closed. |
314 onMenuEditAddressTap_: function(e) { | 196 * @private {?HTMLElement} |
315 e.preventDefault(); | 197 */ |
316 this.showAddressDialog_ = true; | 198 activeDialogAnchor_: null, |
317 this.$.addressSharedMenu.close(); | 199 |
318 }, | 200 /** |
319 | 201 * @type {AutofillManager} |
320 /** @private */ | 202 * @private |
321 onRemoteEditAddressTap_: function() { | 203 */ |
322 window.open(this.i18n('manageAddressesUrl')); | 204 autofillManager_: null, |
323 }, | 205 |
324 | 206 /** |
325 /** | 207 * @type {?function(!Array<!AutofillManager.AddressEntry>)} |
326 * Handles tapping on the "Remove" address button. | 208 * @private |
327 * @private | 209 */ |
328 */ | 210 setAddressesListener_: null, |
329 onMenuRemoveAddressTap_: function() { | 211 |
330 this.autofillManager_.removeAddress( | 212 /** |
331 /** @type {string} */(this.activeAddress.guid)); | 213 * @type {?function(!Array<!AutofillManager.CreditCardEntry>)} |
332 this.$.addressSharedMenu.close(); | 214 * @private |
333 }, | 215 */ |
334 | 216 setCreditCardsListener_: null, |
335 /** | 217 |
336 * Opens the credit card action menu. | 218 /** @override */ |
337 * @param {!Event} e The polymer event. | 219 attached: function() { |
338 * @private | 220 // Create listener functions. |
339 */ | 221 /** @type {function(!Array<!AutofillManager.AddressEntry>)} */ |
340 onCreditCardMenuTap_: function(e) { | 222 var setAddressesListener = function(list) { |
341 var menuEvent = /** @type {!{model: !{item: !Object}}} */(e); | 223 this.addresses = list; |
342 | 224 }.bind(this); |
343 /* TODO(scottchen): drop the [dataHost][dataHost] once this bug is fixed: | 225 |
344 https://github.com/Polymer/polymer/issues/2574 */ | 226 /** @type {function(!Array<!AutofillManager.CreditCardEntry>)} */ |
345 var item = menuEvent.model['dataHost']['dataHost'].item; | 227 var setCreditCardsListener = function(list) { |
346 | 228 this.creditCards = list; |
347 // Copy item so dialog won't update model on cancel. | 229 }.bind(this); |
348 this.activeCreditCard = | 230 |
349 /** @type {!chrome.autofillPrivate.CreditCardEntry} */( | 231 // Remember the bound reference in order to detach. |
350 Object.assign({}, item)); | 232 this.setAddressesListener_ = setAddressesListener; |
351 | 233 this.setCreditCardsListener_ = setCreditCardsListener; |
352 var dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); | 234 |
353 /** @type {!CrActionMenuElement} */ ( | 235 // Set the managers. These can be overridden by tests. |
354 this.$.creditCardSharedMenu).showAt(dotsButton); | 236 this.autofillManager_ = AutofillManagerImpl.getInstance(); |
355 this.activeDialogAnchor_ = dotsButton; | 237 |
356 }, | 238 // Request initial data. |
357 | 239 this.autofillManager_.getAddressList(setAddressesListener); |
358 /** | 240 this.autofillManager_.getCreditCardList(setCreditCardsListener); |
359 * Handles tapping on the "Add credit card" button. | 241 |
360 * @param {!Event} e | 242 // Listen for changes. |
361 * @private | 243 this.autofillManager_.addAddressListChangedListener(setAddressesListener); |
362 */ | 244 this.autofillManager_.addCreditCardListChangedListener( |
363 onAddCreditCardTap_: function(e) { | 245 setCreditCardsListener); |
364 e.preventDefault(); | 246 }, |
365 var date = new Date(); // Default to current month/year. | 247 |
366 var expirationMonth = date.getMonth() + 1; // Months are 0 based. | 248 /** @override */ |
367 this.activeCreditCard = { | 249 detached: function() { |
368 expirationMonth: expirationMonth.toString(), | 250 this.autofillManager_.removeAddressListChangedListener( |
369 expirationYear: date.getFullYear().toString(), | 251 /** @type {function(!Array<!AutofillManager.AddressEntry>)} */ ( |
370 }; | 252 this.setAddressesListener_)); |
| 253 this.autofillManager_.removeCreditCardListChangedListener( |
| 254 /** @type {function(!Array<!AutofillManager.CreditCardEntry>)} */ ( |
| 255 this.setCreditCardsListener_)); |
| 256 }, |
| 257 |
| 258 /** |
| 259 * Formats the expiration date so it's displayed as MM/YYYY. |
| 260 * @param {!chrome.autofillPrivate.CreditCardEntry} item |
| 261 * @return {string} |
| 262 * @private |
| 263 */ |
| 264 expiration_: function(item) { |
| 265 return item.expirationMonth + '/' + item.expirationYear; |
| 266 }, |
| 267 |
| 268 /** |
| 269 * Open the address action menu. |
| 270 * @param {!Event} e The polymer event. |
| 271 * @private |
| 272 */ |
| 273 onAddressMenuTap_: function(e) { |
| 274 var menuEvent = /** @type {!{model: !{item: !Object}}} */ (e); |
| 275 |
| 276 /* TODO(scottchen): drop the [dataHost][dataHost] once this bug is fixed: |
| 277 https://github.com/Polymer/polymer/issues/2574 */ |
| 278 var item = menuEvent.model['dataHost']['dataHost'].item; |
| 279 |
| 280 // Copy item so dialog won't update model on cancel. |
| 281 this.activeAddress = /** @type {!chrome.autofillPrivate.AddressEntry} */ ( |
| 282 Object.assign({}, item)); |
| 283 |
| 284 var dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); |
| 285 /** @type {!CrActionMenuElement} */ (this.$.addressSharedMenu) |
| 286 .showAt(dotsButton); |
| 287 this.activeDialogAnchor_ = dotsButton; |
| 288 }, |
| 289 |
| 290 /** |
| 291 * Handles tapping on the "Add address" button. |
| 292 * @param {!Event} e The polymer event. |
| 293 * @private |
| 294 */ |
| 295 onAddAddressTap_: function(e) { |
| 296 e.preventDefault(); |
| 297 this.activeAddress = {}; |
| 298 this.showAddressDialog_ = true; |
| 299 this.activeDialogAnchor_ = this.$.addAddress; |
| 300 }, |
| 301 |
| 302 /** @private */ |
| 303 onAddressDialogClosed_: function() { |
| 304 this.showAddressDialog_ = false; |
| 305 cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); |
| 306 this.activeDialogAnchor_ = null; |
| 307 }, |
| 308 |
| 309 /** |
| 310 * Handles tapping on the "Edit" address button. |
| 311 * @param {!Event} e The polymer event. |
| 312 * @private |
| 313 */ |
| 314 onMenuEditAddressTap_: function(e) { |
| 315 e.preventDefault(); |
| 316 this.showAddressDialog_ = true; |
| 317 this.$.addressSharedMenu.close(); |
| 318 }, |
| 319 |
| 320 /** @private */ |
| 321 onRemoteEditAddressTap_: function() { |
| 322 window.open(this.i18n('manageAddressesUrl')); |
| 323 }, |
| 324 |
| 325 /** |
| 326 * Handles tapping on the "Remove" address button. |
| 327 * @private |
| 328 */ |
| 329 onMenuRemoveAddressTap_: function() { |
| 330 this.autofillManager_.removeAddress( |
| 331 /** @type {string} */ (this.activeAddress.guid)); |
| 332 this.$.addressSharedMenu.close(); |
| 333 }, |
| 334 |
| 335 /** |
| 336 * Opens the credit card action menu. |
| 337 * @param {!Event} e The polymer event. |
| 338 * @private |
| 339 */ |
| 340 onCreditCardMenuTap_: function(e) { |
| 341 var menuEvent = /** @type {!{model: !{item: !Object}}} */ (e); |
| 342 |
| 343 /* TODO(scottchen): drop the [dataHost][dataHost] once this bug is fixed: |
| 344 https://github.com/Polymer/polymer/issues/2574 */ |
| 345 var item = menuEvent.model['dataHost']['dataHost'].item; |
| 346 |
| 347 // Copy item so dialog won't update model on cancel. |
| 348 this.activeCreditCard = |
| 349 /** @type {!chrome.autofillPrivate.CreditCardEntry} */ ( |
| 350 Object.assign({}, item)); |
| 351 |
| 352 var dotsButton = /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget); |
| 353 /** @type {!CrActionMenuElement} */ (this.$.creditCardSharedMenu) |
| 354 .showAt(dotsButton); |
| 355 this.activeDialogAnchor_ = dotsButton; |
| 356 }, |
| 357 |
| 358 /** |
| 359 * Handles tapping on the "Add credit card" button. |
| 360 * @param {!Event} e |
| 361 * @private |
| 362 */ |
| 363 onAddCreditCardTap_: function(e) { |
| 364 e.preventDefault(); |
| 365 var date = new Date(); // Default to current month/year. |
| 366 var expirationMonth = date.getMonth() + 1; // Months are 0 based. |
| 367 this.activeCreditCard = { |
| 368 expirationMonth: expirationMonth.toString(), |
| 369 expirationYear: date.getFullYear().toString(), |
| 370 }; |
| 371 this.showCreditCardDialog_ = true; |
| 372 this.activeDialogAnchor_ = this.$.addCreditCard; |
| 373 }, |
| 374 |
| 375 /** @private */ |
| 376 onCreditCardDialogClosed_: function() { |
| 377 this.showCreditCardDialog_ = false; |
| 378 cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); |
| 379 this.activeDialogAnchor_ = null; |
| 380 }, |
| 381 |
| 382 /** |
| 383 * Handles tapping on the "Edit" credit card button. |
| 384 * @param {!Event} e The polymer event. |
| 385 * @private |
| 386 */ |
| 387 onMenuEditCreditCardTap_: function(e) { |
| 388 e.preventDefault(); |
| 389 |
| 390 if (this.activeCreditCard.metadata.isLocal) |
371 this.showCreditCardDialog_ = true; | 391 this.showCreditCardDialog_ = true; |
372 this.activeDialogAnchor_ = this.$.addCreditCard; | 392 else |
373 }, | 393 this.onRemoteEditCreditCardTap_(); |
374 | 394 |
375 /** @private */ | 395 this.$.creditCardSharedMenu.close(); |
376 onCreditCardDialogClosed_: function() { | 396 }, |
377 this.showCreditCardDialog_ = false; | 397 |
378 cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); | 398 /** @private */ |
379 this.activeDialogAnchor_ = null; | 399 onRemoteEditCreditCardTap_: function() { |
380 }, | 400 window.open(this.i18n('manageCreditCardsUrl')); |
381 | 401 }, |
382 /** | 402 |
383 * Handles tapping on the "Edit" credit card button. | 403 /** |
384 * @param {!Event} e The polymer event. | 404 * Handles tapping on the "Remove" credit card button. |
385 * @private | 405 * @private |
386 */ | 406 */ |
387 onMenuEditCreditCardTap_: function(e) { | 407 onMenuRemoveCreditCardTap_: function() { |
388 e.preventDefault(); | 408 this.autofillManager_.removeCreditCard( |
389 | 409 /** @type {string} */ (this.activeCreditCard.guid)); |
390 if (this.activeCreditCard.metadata.isLocal) | 410 this.$.creditCardSharedMenu.close(); |
391 this.showCreditCardDialog_ = true; | 411 }, |
392 else | 412 |
393 this.onRemoteEditCreditCardTap_(); | 413 /** |
394 | 414 * Handles tapping on the "Clear copy" button for cached credit cards. |
395 this.$.creditCardSharedMenu.close(); | 415 * @private |
396 }, | 416 */ |
397 | 417 onMenuClearCreditCardTap_: function() { |
398 /** @private */ | 418 this.autofillManager_.clearCachedCreditCard( |
399 onRemoteEditCreditCardTap_: function() { | 419 /** @type {string} */ (this.activeCreditCard.guid)); |
400 window.open(this.i18n('manageCreditCardsUrl')); | 420 this.$.creditCardSharedMenu.close(); |
401 }, | 421 }, |
402 | 422 |
403 /** | 423 /** |
404 * Handles tapping on the "Remove" credit card button. | 424 * The 3-dot menu should not be shown if the card is entirely remote. |
405 * @private | 425 * @param {!chrome.autofillPrivate.AutofillMetadata} metadata |
406 */ | 426 * @return {boolean} |
407 onMenuRemoveCreditCardTap_: function() { | 427 * @private |
408 this.autofillManager_.removeCreditCard( | 428 */ |
409 /** @type {string} */(this.activeCreditCard.guid)); | 429 showDots_: function(metadata) { |
410 this.$.creditCardSharedMenu.close(); | 430 return !!(metadata.isLocal || metadata.isCached); |
411 }, | 431 }, |
412 | 432 |
413 /** | 433 /** |
414 * Handles tapping on the "Clear copy" button for cached credit cards. | 434 * Returns true if the list exists and has items. |
415 * @private | 435 * @param {Array<Object>} list |
416 */ | 436 * @return {boolean} |
417 onMenuClearCreditCardTap_: function() { | 437 * @private |
418 this.autofillManager_.clearCachedCreditCard( | 438 */ |
419 /** @type {string} */(this.activeCreditCard.guid)); | 439 hasSome_: function(list) { |
420 this.$.creditCardSharedMenu.close(); | 440 return !!(list && list.length); |
421 }, | 441 }, |
422 | 442 |
423 /** | 443 /** |
424 * The 3-dot menu should not be shown if the card is entirely remote. | 444 * Listens for the save-address event, and calls the private API. |
425 * @param {!chrome.autofillPrivate.AutofillMetadata} metadata | 445 * @param {!Event} event |
426 * @return {boolean} | 446 * @private |
427 * @private | 447 */ |
428 */ | 448 saveAddress_: function(event) { |
429 showDots_: function(metadata) { | 449 this.autofillManager_.saveAddress(event.detail); |
430 return !!(metadata.isLocal || metadata.isCached); | 450 }, |
431 }, | 451 |
432 | 452 /** |
433 /** | 453 * Listens for the save-credit-card event, and calls the private API. |
434 * Returns true if the list exists and has items. | 454 * @param {!Event} event |
435 * @param {Array<Object>} list | 455 * @private |
436 * @return {boolean} | 456 */ |
437 * @private | 457 saveCreditCard_: function(event) { |
438 */ | 458 this.autofillManager_.saveCreditCard(event.detail); |
439 hasSome_: function(list) { | 459 }, |
440 return !!(list && list.length); | 460 |
441 }, | 461 /** |
442 | 462 * @private |
443 /** | 463 * @param {boolean} toggleValue |
444 * Listens for the save-address event, and calls the private API. | 464 * @return {string} |
445 * @param {!Event} event | 465 */ |
446 * @private | 466 getOnOffLabel_: function(toggleValue) { |
447 */ | 467 return toggleValue ? this.i18n('toggleOn') : this.i18n('toggleOff'); |
448 saveAddress_: function(event) { | 468 } |
449 this.autofillManager_.saveAddress(event.detail); | 469 }); |
450 }, | |
451 | |
452 /** | |
453 * Listens for the save-credit-card event, and calls the private API. | |
454 * @param {!Event} event | |
455 * @private | |
456 */ | |
457 saveCreditCard_: function(event) { | |
458 this.autofillManager_.saveCreditCard(event.detail); | |
459 }, | |
460 | |
461 /** | |
462 * @private | |
463 * @param {boolean} toggleValue | |
464 * @return {string} | |
465 */ | |
466 getOnOffLabel_: function(toggleValue) { | |
467 return toggleValue ? this.i18n('toggleOn') : this.i18n('toggleOff'); | |
468 } | |
469 }); | |
470 })(); | 470 })(); |
OLD | NEW |