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 'use strict'; 6 7 8 /** 9 * SelectAlbumDialog contains a message, a list box, an ok button, and a 10 * cancel button. 11 * Operates on a list of objects representing albums: { name, url, create }. 12 * If user chooses to create a new album, result will be a fake album with 13 * |create == true|. 14 * 15 * @param {HTMLElement} parentNode Node to be parent for this dialog. 16 * @constructor 17 */ 18 function SelectAlbumDialog(parentNode) { 19 this.parentNode_ = parentNode; 20 this.document_ = parentNode.ownerDocument; 21 22 this.container_ = this.document_.createElement('div'); 23 this.container_.className = 'select-album-dialog-container'; 24 this.container_.addEventListener('keydown', 25 this.onContainerKeyDown_.bind(this)); 26 27 this.shield_ = this.document_.createElement('div'); 28 this.shield_.className = 'select-album-dialog-shield'; 29 this.container_.appendChild(this.shield_); 30 31 this.frame_ = this.document_.createElement('div'); 32 this.frame_.className = 'select-album-dialog-frame'; 33 this.container_.appendChild(this.frame_); 34 35 this.caption_ = this.document_.createElement('div'); 36 this.caption_.className = 'select-album-dialog-caption'; 37 this.frame_.appendChild(this.caption_); 38 39 this.list_ = new cr.ui.List(); 40 this.list_.classList.add('select-album-list'); 41 this.frame_.appendChild(this.list_); 42 43 this.dataModel_ = this.list_.dataModel = new cr.ui.ArrayDataModel([]); 44 this.selectionModel_ = this.list_.selectionModel = 45 new cr.ui.ListSingleSelectionModel(); 46 this.selectionModel_.addEventListener('change', 47 this.onSelectionChanged_.bind(this)); 48 49 // TODO(dgozman): add shades at top and bottom of the list. 50 // List has max-height defined at css, so that list grows automatically, 51 // but doesn't exceed predefined size. 52 this.list_.autoExpands = true; 53 this.list_.activateItemAtIndex = this.activateItemAtIndex_.bind(this); 54 // Binding stuff doesn't work with constructors, so we have to create 55 // closure here. 56 var self = this; 57 this.list_.itemConstructor = function(item) { 58 return self.renderItem(item); 59 }; 60 61 var buttons = this.document_.createElement('div'); 62 buttons.className = 'select-album-dialog-buttons'; 63 this.frame_.appendChild(buttons); 64 65 this.okButton_ = this.document_.createElement('button'); 66 this.okButton_.className = 'no-icon'; 67 this.okButton_.addEventListener('click', this.onOkClick_.bind(this)); 68 buttons.appendChild(this.okButton_); 69 70 this.cancelButton_ = this.document_.createElement('button'); 71 this.cancelButton_.className = 'no-icon'; 72 this.cancelButton_.textContent = 73 loadTimeData.getString('PHOTO_IMPORT_CANCEL_BUTTON'); 74 this.cancelButton_.addEventListener('click', this.onCancelClick_.bind(this)); 75 buttons.appendChild(this.cancelButton_); 76 77 this.nameEdit_ = this.document_.createElement('input'); 78 this.nameEdit_.setAttribute('type', 'text'); 79 this.nameEdit_.className = 'name'; 80 this.nameEdit_.addEventListener('input', 81 this.updateOkButtonEnabled_.bind(this)); 82 } 83 84 SelectAlbumDialog.prototype = { 85 __proto__: cr.ui.dialogs.BaseDialog.prototype 86 }; 87 88 /** 89 * Renders item for list. 90 * @param {Object} item Item to render. 91 * @return {HTMLLIElement} Rendered item. 92 */ 93 SelectAlbumDialog.prototype.renderItem = function(item) { 94 var result = this.document_.createElement('li'); 95 96 var frame = this.document_.createElement('div'); 97 frame.className = 'img-frame'; 98 result.appendChild(frame); 99 100 var box = this.document_.createElement('div'); 101 box.className = 'img-container'; 102 frame.appendChild(box); 103 104 if (item.create) { 105 result.appendChild(this.nameEdit_); 106 this.nameEdit_.value = item.name; 107 } else { 108 var name = this.document_.createElement('div'); 109 name.className = 'name'; 110 name.textContent = item.name; 111 result.appendChild(name); 112 } 113 114 cr.defineProperty(result, 'lead', cr.PropertyKind.BOOL_ATTR); 115 cr.defineProperty(result, 'selected', cr.PropertyKind.BOOL_ATTR); 116 117 new ThumbnailLoader(item.url).load(box, ThumbnailLoader.FillMode.FILL); 118 119 return result; 120 }; 121 122 /** 123 * Shows dialog. 124 * 125 * @param {string} message Message in dialog caption. 126 * @param {Array} items Albums to render in list. 127 * @param {string} defaultNewName Default name of the new album. 128 * @param {string} okCaption Text on the ok button. 129 * @param {function} onOk Callback function. 130 */ 131 SelectAlbumDialog.prototype.show = function( 132 message, items, defaultNewName, okCaption, onOk) { 133 134 this.onOk_ = onOk; 135 this.okButton_.textContent = okCaption; 136 this.caption_.textContent = message; 137 138 // Fake item to create new album. 139 var newAlbum = { 140 create: true, 141 name: defaultNewName, 142 url: chrome.extension.getURL('../../images/photo/new_album.png') 143 }; 144 145 this.list_.startBatchUpdates(); 146 this.dataModel_.splice(0, this.dataModel_.length); 147 this.dataModel_.push(newAlbum); 148 for (var i = 0; i < items.length; i++) { 149 this.dataModel_.push(items[i]); 150 } 151 this.selectionModel_.selectedIndex = 0; 152 this.list_.endBatchUpdates(); 153 154 this.parentNode_.appendChild(this.container_); 155 }; 156 157 /** 158 * Hides dialog. 159 */ 160 SelectAlbumDialog.prototype.hide = function() { 161 this.parentNode_.removeChild(this.container_); 162 }; 163 164 /** 165 * List activation handler. Closes dialog and calls 'ok' callback. 166 * 167 * @param {number} index Activated index. 168 * @private 169 */ 170 SelectAlbumDialog.prototype.activateItemAtIndex_ = function(index) { 171 if (this.okButton_.disabled) return; 172 this.hide(); 173 var album = this.dataModel_.item(index); 174 if (index == 0) 175 album.name = this.nameEdit_.value; 176 this.onOk_(album); 177 }; 178 179 /** 180 * Closes dialog and invokes callback with currently-selected item. 181 * @private 182 */ 183 SelectAlbumDialog.prototype.onOkClick_ = function() { 184 this.activateItemAtIndex_(this.selectionModel_.selectedIndex); 185 }; 186 187 /** 188 * Closes dialog. 189 * @private 190 */ 191 SelectAlbumDialog.prototype.onCancelClick_ = function() { 192 this.hide(); 193 }; 194 195 /** 196 * Event handler for keydown event. 197 * @param {Event} event The event. 198 * @private 199 */ 200 SelectAlbumDialog.prototype.onContainerKeyDown_ = function(event) { 201 // Handle Escape. 202 if (event.keyCode == 27) { 203 this.onCancelClick_(event); 204 event.preventDefault(); 205 } else if (event.keyCode == 13) { 206 this.onOkClick_(); 207 event.preventDefault(); 208 } 209 }; 210 211 /** 212 * Event handler for selection change. 213 * @param {Event} event The event. 214 * @private 215 */ 216 SelectAlbumDialog.prototype.onSelectionChanged_ = function(event) { 217 if (this.selectionModel_.selectedIndex == 0) { 218 setTimeout(this.nameEdit_.focus.bind(this.nameEdit_), 0); 219 } else { 220 this.nameEdit_.blur(); 221 this.list_.focus(); 222 } 223 this.updateOkButtonEnabled_(); 224 }; 225 226 /** 227 * Updates ok button. 228 * @private 229 */ 230 SelectAlbumDialog.prototype.updateOkButtonEnabled_ = function() { 231 this.okButton_.disabled = this.selectionModel_.selectedIndex == 0 && 232 this.nameEdit_.value == ''; 233 }; 234