OLD | NEW |
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 /** | 5 /** |
6 * Slide mode displays a single image and has a set of controls to navigate | 6 * Slide mode displays a single image and has a set of controls to navigate |
7 * between the images and to edit an image. | 7 * between the images and to edit an image. |
8 * | 8 * |
9 * @param {Element} container Container element. | 9 * TODO(kaznacheev): Introduce a parameter object. |
| 10 * |
| 11 * @param {Element} container Main container element. |
| 12 * @param {Element} content Content container element. |
10 * @param {Element} toolbar Toolbar element. | 13 * @param {Element} toolbar Toolbar element. |
11 * @param {ImageEditor.Prompt} prompt Prompt. | 14 * @param {ImageEditor.Prompt} prompt Prompt. |
| 15 * @param {cr.ui.ArrayDataModel} dataModel Data model. |
| 16 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. |
12 * @param {Object} context Context. | 17 * @param {Object} context Context. |
13 * @param {function(string):string} displayStringFunction String formatting | 18 * @param {function(string):string} displayStringFunction String formatting |
14 * function. | 19 * function. |
15 * @constructor | 20 * @constructor |
16 */ | 21 */ |
17 function SlideMode(container, toolbar, prompt, context, displayStringFunction) { | 22 function SlideMode(container, content, toolbar, prompt, |
| 23 dataModel, selectionModel, |
| 24 context, displayStringFunction) { |
18 this.container_ = container; | 25 this.container_ = container; |
| 26 this.document_ = container.ownerDocument; |
| 27 this.content = content; |
19 this.toolbar_ = toolbar; | 28 this.toolbar_ = toolbar; |
20 this.document_ = container.ownerDocument; | |
21 this.prompt_ = prompt; | 29 this.prompt_ = prompt; |
| 30 this.dataModel_ = dataModel; |
| 31 this.selectionModel_ = selectionModel; |
22 this.context_ = context; | 32 this.context_ = context; |
23 this.metadataCache_ = context.metadataCache; | 33 this.metadataCache_ = context.metadataCache; |
24 this.displayStringFunction_ = displayStringFunction; | 34 this.displayStringFunction_ = displayStringFunction; |
25 | 35 |
| 36 this.onSelectionBound_ = this.onSelection_.bind(this); |
| 37 this.onSpliceBound_ = this.onSplice_.bind(this); |
| 38 |
26 this.initListeners_(); | 39 this.initListeners_(); |
27 this.initDom_(); | 40 this.initDom_(); |
28 } | 41 } |
29 | 42 |
30 /** | 43 /** |
31 * SlideMode extends cr.EventTarget. | 44 * SlideMode extends cr.EventTarget. |
32 */ | 45 */ |
33 SlideMode.prototype.__proto__ = cr.EventTarget.prototype; | 46 SlideMode.prototype.__proto__ = cr.EventTarget.prototype; |
34 | 47 |
35 /** | 48 /** |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 | 142 |
130 util.createChild(this.arrowBox_, 'arrow-spacer'); | 143 util.createChild(this.arrowBox_, 'arrow-spacer'); |
131 | 144 |
132 this.arrowRight_ = | 145 this.arrowRight_ = |
133 util.createChild(this.arrowBox_, 'arrow right tool dimmable'); | 146 util.createChild(this.arrowBox_, 'arrow right tool dimmable'); |
134 this.arrowRight_.addEventListener('click', | 147 this.arrowRight_.addEventListener('click', |
135 this.selectNext.bind(this, 1, null)); | 148 this.selectNext.bind(this, 1, null)); |
136 util.createChild(this.arrowRight_); | 149 util.createChild(this.arrowRight_); |
137 | 150 |
138 this.ribbonSpacer_ = util.createChild(this.toolbar_, 'ribbon-spacer'); | 151 this.ribbonSpacer_ = util.createChild(this.toolbar_, 'ribbon-spacer'); |
| 152 this.ribbon_ = new Ribbon(this.document_, |
| 153 this.metadataCache_, this.dataModel_, this.selectionModel_); |
| 154 this.ribbonSpacer_.appendChild(this.ribbon_); |
139 | 155 |
140 // Error indicator. | 156 // Error indicator. |
141 var errorWrapper = util.createChild(this.container_, 'prompt-wrapper'); | 157 var errorWrapper = util.createChild(this.container_, 'prompt-wrapper'); |
142 errorWrapper.setAttribute('pos', 'center'); | 158 errorWrapper.setAttribute('pos', 'center'); |
143 | 159 |
144 this.errorBanner_ = util.createChild(errorWrapper, 'error-banner'); | 160 this.errorBanner_ = util.createChild(errorWrapper, 'error-banner'); |
145 | 161 |
146 util.createChild(this.container_, 'spinner'); | 162 util.createChild(this.container_, 'spinner'); |
147 | 163 |
| 164 this.slideShowButton_ = util.createChild(this.toolbar_, 'button slideshow'); |
| 165 this.slideShowButton_.title = this.displayStringFunction_('slideshow'); |
| 166 this.slideShowButton_.addEventListener('click', |
| 167 this.toggleSlideshow_.bind(this, SlideMode.SLIDESHOW_INTERVAL_FIRST)); |
| 168 |
| 169 // Editor. |
148 | 170 |
149 this.editButton_ = util.createChild(this.toolbar_, 'button edit'); | 171 this.editButton_ = util.createChild(this.toolbar_, 'button edit'); |
150 this.editButton_.textContent = this.displayStringFunction_('edit'); | 172 this.editButton_.title = this.displayStringFunction_('edit'); |
151 this.editButton_.addEventListener('click', this.onEdit_.bind(this)); | 173 this.editButton_.addEventListener('click', this.toggleEditor_.bind(this)); |
152 | |
153 // Editor toolbar. | |
154 | 174 |
155 this.editBar_ = util.createChild(this.toolbar_, 'edit-bar'); | 175 this.editBar_ = util.createChild(this.toolbar_, 'edit-bar'); |
156 this.editBarMain_ = util.createChild(this.editBar_, 'edit-main'); | 176 this.editBarMain_ = util.createChild(this.editBar_, 'edit-main'); |
157 | 177 |
158 this.editBarMode_ = util.createChild(this.container_, 'edit-modal'); | 178 this.editBarMode_ = util.createChild(this.container_, 'edit-modal'); |
159 this.editBarModeWrapper_ = util.createChild( | 179 this.editBarModeWrapper_ = util.createChild( |
160 this.editBarMode_, 'edit-modal-wrapper'); | 180 this.editBarMode_, 'edit-modal-wrapper'); |
161 this.editBarModeWrapper_.hidden = true; | 181 this.editBarModeWrapper_.hidden = true; |
162 | 182 |
163 // Objects supporting image display and editing. | 183 // Objects supporting image display and editing. |
164 this.viewport_ = new Viewport(); | 184 this.viewport_ = new Viewport(); |
165 | 185 |
166 this.imageView_ = new ImageView( | 186 this.imageView_ = new ImageView( |
167 this.imageContainer_, | 187 this.imageContainer_, |
168 this.viewport_, | 188 this.viewport_, |
169 this.metadataCache_); | 189 this.metadataCache_); |
170 | 190 |
171 this.imageView_.addContentCallback(this.onImageContentChanged_.bind(this)); | |
172 | |
173 this.editor_ = new ImageEditor( | 191 this.editor_ = new ImageEditor( |
174 this.viewport_, | 192 this.viewport_, |
175 this.imageView_, | 193 this.imageView_, |
176 this.prompt_, | 194 this.prompt_, |
177 { | 195 { |
178 root: this.container_, | 196 root: this.container_, |
179 image: this.imageContainer_, | 197 image: this.imageContainer_, |
180 toolbar: this.editBarMain_, | 198 toolbar: this.editBarMain_, |
181 mode: this.editBarModeWrapper_ | 199 mode: this.editBarModeWrapper_ |
182 }, | 200 }, |
183 SlideMode.editorModes, | 201 SlideMode.editorModes, |
184 this.displayStringFunction_); | 202 this.displayStringFunction_); |
185 | 203 |
186 this.editor_.getBuffer().addOverlay( | 204 this.editor_.getBuffer().addOverlay( |
187 new SwipeOverlay(this.selectNext.bind(this))); | 205 new SwipeOverlay(this.selectNext.bind(this))); |
188 }; | 206 }; |
189 | 207 |
190 /** | 208 /** |
191 * Load items, display the selected item. | 209 * Load items, display the selected item. |
192 * | 210 * |
193 * @param {Array.<Gallery.Item>} items Array of items. | 211 * @param {function} opt_callback Callback. |
194 * @param {number} selectedIndex Selected index. | |
195 * @param {function} callback Callback. | |
196 */ | 212 */ |
197 SlideMode.prototype.load = function(items, selectedIndex, callback) { | 213 SlideMode.prototype.enter = function(opt_callback) { |
198 var selectedItem = items[selectedIndex]; | 214 this.container_.setAttribute('mode', 'slide'); |
199 if (!selectedItem) { | 215 |
200 this.showErrorBanner_('IMAGE_ERROR'); | 216 this.sequenceDirection_ = 0; |
201 return; | 217 this.sequenceLength_ = 0; |
202 } | |
203 | 218 |
204 var loadDone = function() { | 219 var loadDone = function() { |
205 this.items_ = items; | 220 this.active_ = true; |
206 this.setSelectedIndex_(selectedIndex); | 221 |
207 this.setupNavigation_(); | 222 this.selectionModel_.addEventListener('change', this.onSelectionBound_); |
208 setTimeout(this.requestPrefetch.bind(this, 1 /* Next item */), 1000); | 223 this.dataModel_.addEventListener('splice', this.onSpliceBound_); |
209 callback(); | 224 |
| 225 this.ribbon_.enable(); |
| 226 |
| 227 this.prefetchTimer_ = setTimeout(function() { |
| 228 this.prefetchTimer_ = null; |
| 229 this.requestPrefetch(1); // Prefetch the next image. |
| 230 }.bind(this), 1000); |
| 231 if (opt_callback) opt_callback(); |
210 }.bind(this); | 232 }.bind(this); |
211 | 233 |
212 var selectedUrl = selectedItem.getUrl(); | 234 if (this.getItemCount_() == 0) { |
213 // Show the selected item ASAP, then complete the initialization | 235 this.displayedIndex_ = -1; |
214 // (loading the ribbon thumbnails can take some time). | 236 //TODO(kaznacheev) Show this message in the grid mode too. |
215 this.metadataCache_.get(selectedUrl, Gallery.METADATA_TYPE, | 237 this.showErrorBanner_('NO_IMAGES'); |
216 function(metadata) { | 238 loadDone(); |
217 this.loadItem_(selectedUrl, metadata, 0, loadDone); | 239 } else { |
218 }.bind(this)); | 240 // Ensure valid single selection. |
| 241 // Note that the SlideMode object is not listening to selection change yet. |
| 242 this.select(Math.max(0, this.getSelectedIndex())); |
| 243 cr.dispatchSimpleEvent(this, 'namechange'); // Update name in the UI. |
| 244 this.displayedIndex_ = this.getSelectedIndex(); |
| 245 |
| 246 var selectedItem = this.getSelectedItem(); |
| 247 var selectedUrl = selectedItem.getUrl(); |
| 248 // Show the selected item ASAP, then complete the initialization |
| 249 // (loading the ribbon thumbnails can take some time). |
| 250 this.metadataCache_.get(selectedUrl, Gallery.METADATA_TYPE, |
| 251 function(metadata) { |
| 252 this.loadItem_(selectedUrl, metadata, 0, loadDone); |
| 253 }.bind(this)); |
| 254 |
| 255 } |
219 }; | 256 }; |
220 | 257 |
221 /** | 258 /** |
222 * Setup navigation controls. | 259 * Leave the mode. |
| 260 * @param {function} opt_callback Callback. |
| 261 */ |
| 262 SlideMode.prototype.leave = function(opt_callback) { |
| 263 if (this.prefetchTimer_) { |
| 264 clearTimeout(this.prefetchTimer_); |
| 265 this.prefetchTimer_ = null; |
| 266 } |
| 267 |
| 268 var commitDone = function() { |
| 269 this.stopEditing_(); |
| 270 this.stopSlideshow_(); |
| 271 this.unloadImage_(); |
| 272 this.selectionModel_.removeEventListener( |
| 273 'change', this.onSelectionBound_); |
| 274 this.dataModel_.removeEventListener('splice', this.onSpliceBound_); |
| 275 this.ribbon_.disable(); |
| 276 this.active_ = false; |
| 277 if (opt_callback) opt_callback(); |
| 278 }.bind(this); |
| 279 |
| 280 if (this.getItemCount_() == 0) { |
| 281 this.showErrorBanner_(false); |
| 282 commitDone(); |
| 283 } else { |
| 284 this.commitItem_(commitDone); |
| 285 } |
| 286 }; |
| 287 |
| 288 /** |
| 289 * @return {boolean} True if the mode has active tools (that should not fade). |
| 290 */ |
| 291 SlideMode.prototype.hasActiveTool = function() { |
| 292 return this.isEditing(); |
| 293 }; |
| 294 |
| 295 /** |
| 296 * @return {number} Item count. |
223 * @private | 297 * @private |
224 */ | 298 */ |
225 SlideMode.prototype.setupNavigation_ = function() { | 299 SlideMode.prototype.getItemCount_ = function() { |
226 this.sequenceDirection_ = 0; | 300 return this.dataModel_.length; |
227 this.sequenceLength_ = 0; | 301 }; |
228 | 302 |
229 ImageUtil.setAttribute(this.arrowLeft_, 'active', this.items_.length > 1); | 303 /** |
230 ImageUtil.setAttribute(this.arrowRight_, 'active', this.items_.length > 1); | 304 * @param {number} index Index. |
| 305 * @return {Gallery.Item} Item. |
| 306 */ |
| 307 SlideMode.prototype.getItem = function(index) { |
| 308 return this.dataModel_.item(index); |
| 309 }; |
231 | 310 |
232 this.ribbon_ = new Ribbon( | 311 /** |
233 this.document_, this.metadataCache_, this.select.bind(this)); | 312 * @return {Gallery.Item} Selected index. |
234 this.ribbonSpacer_.appendChild(this.ribbon_); | 313 */ |
235 this.ribbon_.update(this.items_, this.selectedIndex_); | 314 SlideMode.prototype.getSelectedIndex = function() { |
| 315 return this.selectionModel_.selectedIndexes.length ? |
| 316 this.selectionModel_.selectedIndexes[0] : |
| 317 -1; |
236 }; | 318 }; |
237 | 319 |
238 /** | 320 /** |
239 * @return {Gallery.Item} Selected item | 321 * @return {Gallery.Item} Selected item |
240 */ | 322 */ |
241 SlideMode.prototype.getSelectedItem = function() { | 323 SlideMode.prototype.getSelectedItem = function() { |
242 return this.items_ && this.items_[this.selectedIndex_]; | 324 return this.getItem(this.getSelectedIndex()); |
| 325 }; |
| 326 |
| 327 /** |
| 328 * Selection change handler. |
| 329 * |
| 330 * Commits the current image and displays the newly selected image. |
| 331 * @private |
| 332 */ |
| 333 SlideMode.prototype.onSelection_ = function() { |
| 334 if (this.selectionModel_.selectedIndexes.length == 0) |
| 335 return; // Temporary empty selection. |
| 336 |
| 337 if (this.getSelectedIndex() == this.displayedIndex_) |
| 338 return; // Do not reselect. |
| 339 |
| 340 this.commitItem_(this.loadSelectedItem_.bind(this)); |
243 }; | 341 }; |
244 | 342 |
245 /** | 343 /** |
246 * Change the selection. | 344 * Change the selection. |
247 * | 345 * |
248 * Commits the current image and changes the selection. | |
249 * | |
250 * @param {number} index New selected index. | 346 * @param {number} index New selected index. |
251 * @param {number} opt_slideDir Slide animation direction (-1|0|1). | 347 * @param {number} opt_slideHint Slide animation direction (-1|1). |
252 * Derived from the new and the old selected indices if omitted. | |
253 * @param {function} opt_callback Callback. | |
254 */ | 348 */ |
255 SlideMode.prototype.select = function(index, opt_slideDir, opt_callback) { | 349 SlideMode.prototype.select = function(index, opt_slideHint) { |
256 if (!this.items_) | 350 this.slideHint_ = opt_slideHint; |
257 return; // Not fully initialized, still loading the first image. | 351 this.selectionModel_.unselectAll(); |
258 | 352 this.selectionModel_.setIndexSelected(index, true); |
259 if (index == this.selectedIndex_) | |
260 return; // Do not reselect. | |
261 | |
262 this.commitItem_( | |
263 this.doSelect_.bind(this, index, opt_slideDir, opt_callback)); | |
264 }; | 353 }; |
265 | 354 |
266 /** | 355 /** |
267 * Set the new selected index value. | 356 * Load the selected item. |
268 * @param {number} index New selected index. | 357 * |
269 * @private | 358 * @private |
270 */ | 359 */ |
271 SlideMode.prototype.setSelectedIndex_ = function(index) { | 360 SlideMode.prototype.loadSelectedItem_ = function() { |
272 this.selectedIndex_ = index; | 361 var slideHint = this.slideHint_; |
273 cr.dispatchSimpleEvent(this, 'selection'); | 362 this.slideHint_ = undefined; |
274 | 363 |
275 // For once edited image, disallow the 'overwrite' setting change. | 364 var index = this.getSelectedIndex(); |
276 ImageUtil.setAttribute(this.options_, 'saved', | 365 if (index == this.displayedIndex_) |
277 !this.getSelectedItem().isOriginal()); | 366 return; // Do not reselect. |
278 }; | |
279 | 367 |
280 /** | 368 var step = slideHint || (index - this.displayedIndex_); |
281 * Perform the actual selection change. | |
282 * | |
283 * @param {number} index New selected index. | |
284 * @param {number} opt_slideDir Slide animation direction (-1|0|1). | |
285 * Derived from the new and the old selected indices if omitted. | |
286 * @param {function} opt_callback Callback. | |
287 * @private | |
288 */ | |
289 SlideMode.prototype.doSelect_ = function(index, opt_slideDir, opt_callback) { | |
290 if (index == this.selectedIndex_) | |
291 return; // Do not reselect | |
292 | |
293 var step = opt_slideDir != undefined ? | |
294 opt_slideDir : | |
295 (index - this.selectedIndex_); | |
296 | 369 |
297 if (Math.abs(step) != 1) { | 370 if (Math.abs(step) != 1) { |
298 // Long leap, the sequence is broken, we have no good prefetch candidate. | 371 // Long leap, the sequence is broken, we have no good prefetch candidate. |
299 this.sequenceDirection_ = 0; | 372 this.sequenceDirection_ = 0; |
300 this.sequenceLength_ = 0; | 373 this.sequenceLength_ = 0; |
301 } else if (this.sequenceDirection_ == step) { | 374 } else if (this.sequenceDirection_ == step) { |
302 // Keeping going in sequence. | 375 // Keeping going in sequence. |
303 this.sequenceLength_++; | 376 this.sequenceLength_++; |
304 } else { | 377 } else { |
305 // Reversed the direction. Reset the counter. | 378 // Reversed the direction. Reset the counter. |
306 this.sequenceDirection_ = step; | 379 this.sequenceDirection_ = step; |
307 this.sequenceLength_ = 1; | 380 this.sequenceLength_ = 1; |
308 } | 381 } |
309 | 382 |
310 if (this.sequenceLength_ <= 1) { | 383 if (this.sequenceLength_ <= 1) { |
311 // We have just broke the sequence. Touch the current image so that it stays | 384 // We have just broke the sequence. Touch the current image so that it stays |
312 // in the cache longer. | 385 // in the cache longer. |
313 this.editor_.prefetchImage(this.getSelectedItem().getUrl()); | 386 this.imageView_.prefetch(this.imageView_.contentID_); |
314 } | 387 } |
315 | 388 |
316 this.setSelectedIndex_(index); | 389 this.displayedIndex_ = index; |
317 | |
318 if (this.ribbon_) | |
319 this.ribbon_.update(this.items_, this.selectedIndex_); | |
320 | 390 |
321 function shouldPrefetch(loadType, step, sequenceLength) { | 391 function shouldPrefetch(loadType, step, sequenceLength) { |
322 // Never prefetch when selecting out of sequence. | 392 // Never prefetch when selecting out of sequence. |
323 if (Math.abs(step) != 1) | 393 if (Math.abs(step) != 1) |
324 return false; | 394 return false; |
325 | 395 |
326 // Never prefetch after a video load (decoding the next image can freeze | 396 // Never prefetch after a video load (decoding the next image can freeze |
327 // the UI for a second or two). | 397 // the UI for a second or two). |
328 if (loadType == ImageView.LOAD_TYPE_VIDEO_FILE) | 398 if (loadType == ImageView.LOAD_TYPE_VIDEO_FILE) |
329 return false; | 399 return false; |
330 | 400 |
331 // Always prefetch if the previous load was from cache. | 401 // Always prefetch if the previous load was from cache. |
332 if (loadType == ImageView.LOAD_TYPE_CACHED_FULL) | 402 if (loadType == ImageView.LOAD_TYPE_CACHED_FULL) |
333 return true; | 403 return true; |
334 | 404 |
335 // Prefetch if we have been going in the same direction for long enough. | 405 // Prefetch if we have been going in the same direction for long enough. |
336 return sequenceLength >= 3; | 406 return sequenceLength >= 3; |
337 } | 407 } |
338 | 408 |
339 var selectedItem = this.getSelectedItem(); | 409 var selectedItem = this.getSelectedItem(); |
340 var onMetadata = function(metadata) { | 410 var onMetadata = function(metadata) { |
341 if (selectedItem != this.getSelectedItem()) return; | 411 if (selectedItem != this.getSelectedItem()) return; |
342 this.loadItem_(selectedItem.getUrl(), metadata, step, | 412 this.loadItem_(selectedItem.getUrl(), metadata, step, |
343 function(loadType) { | 413 function(loadType) { |
344 if (selectedItem != this.getSelectedItem()) return; | 414 if (selectedItem != this.getSelectedItem()) return; |
345 if (shouldPrefetch(loadType, step, this.sequenceLength_)) { | 415 if (shouldPrefetch(loadType, step, this.sequenceLength_)) { |
346 this.requestPrefetch(step); | 416 this.requestPrefetch(step); |
347 } | 417 } |
348 if (opt_callback) opt_callback(); | 418 if (this.isSlideshowOn_()) |
| 419 this.scheduleNextSlide_(); |
349 }.bind(this)); | 420 }.bind(this)); |
350 }.bind(this); | 421 }.bind(this); |
351 this.metadataCache_.get( | 422 this.metadataCache_.get( |
352 selectedItem.getUrl(), Gallery.METADATA_TYPE, onMetadata); | 423 selectedItem.getUrl(), Gallery.METADATA_TYPE, onMetadata); |
353 }; | 424 }; |
354 | 425 |
355 /** | 426 /** |
| 427 * Unload the current image. |
| 428 * @private |
| 429 */ |
| 430 SlideMode.prototype.unloadImage_ = function() { |
| 431 this.imageView_.unload(); |
| 432 this.container_.removeAttribute('video'); |
| 433 }; |
| 434 |
| 435 /** |
| 436 * Data model 'splice' event handler. |
| 437 * @param {Event} event Event. |
| 438 * @private |
| 439 */ |
| 440 SlideMode.prototype.onSplice_ = function(event) { |
| 441 ImageUtil.setAttribute(this.arrowLeft_, 'active', this.getItemCount_() > 1); |
| 442 ImageUtil.setAttribute(this.arrowRight_, 'active', this.getItemCount_() > 1); |
| 443 |
| 444 if (event.removed.length != 1) |
| 445 return; |
| 446 |
| 447 // Delay the selection to let the ribbon splice handler work first. |
| 448 setTimeout(function() { |
| 449 if (event.index < this.dataModel_.length) { |
| 450 // There is the next item, select it. |
| 451 // The next item is now at the same index as the removed one, so we need |
| 452 // to correct displayIndex_ so that loadSelectedItem_ does not think |
| 453 // we are re-selecting the same item (and does right-to-left slide-in |
| 454 // animation). |
| 455 this.displayedIndex_ = event.index - 1; |
| 456 this.select(event.index); |
| 457 } else if (this.dataModel_.length) { |
| 458 // Removed item is the rightmost, but there are more items. |
| 459 this.select(event.index - 1); // Select the new last index. |
| 460 } else { |
| 461 // No items left. Unload the image and show the banner. |
| 462 this.commitItem_(function() { |
| 463 this.unloadImage_(); |
| 464 this.showErrorBanner_('NO_IMAGES'); |
| 465 }.bind(this)); |
| 466 } |
| 467 }.bind(this), 0); |
| 468 }; |
| 469 |
| 470 /** |
356 * @param {number} direction -1 for left, 1 for right. | 471 * @param {number} direction -1 for left, 1 for right. |
357 * @return {number} Next index in the gived direction, with wrapping. | 472 * @return {number} Next index in the gived direction, with wrapping. |
358 * @private | 473 * @private |
359 */ | 474 */ |
360 SlideMode.prototype.getNextSelectedIndex_ = function(direction) { | 475 SlideMode.prototype.getNextSelectedIndex_ = function(direction) { |
361 var index = this.selectedIndex_ + (direction > 0 ? 1 : -1); | 476 var index = this.getSelectedIndex() + (direction > 0 ? 1 : -1); |
362 if (index == -1) return this.items_.length - 1; | 477 if (index == -1) return this.getItemCount_() - 1; |
363 if (index == this.items_.length) return 0; | 478 if (index == this.getItemCount_()) return 0; |
364 return index; | 479 return index; |
365 }; | 480 }; |
366 | 481 |
367 /** | 482 /** |
368 * Select the next item. | 483 * Select the next item. |
369 * @param {number} direction -1 for left, 1 for right. | 484 * @param {number} direction -1 for left, 1 for right. |
370 * @param {function} opt_callback Callback. | |
371 */ | 485 */ |
372 SlideMode.prototype.selectNext = function(direction, opt_callback) { | 486 SlideMode.prototype.selectNext = function(direction) { |
373 this.select(this.getNextSelectedIndex_(direction), direction, opt_callback); | 487 this.select(this.getNextSelectedIndex_(direction), direction); |
374 }; | 488 }; |
375 | 489 |
376 /** | 490 /** |
377 * Select the first item. | 491 * Select the first item. |
378 */ | 492 */ |
379 SlideMode.prototype.selectFirst = function() { | 493 SlideMode.prototype.selectFirst = function() { |
380 this.select(0); | 494 this.select(0); |
381 }; | 495 }; |
382 | 496 |
383 /** | 497 /** |
384 * Select the last item. | 498 * Select the last item. |
385 */ | 499 */ |
386 SlideMode.prototype.selectLast = function() { | 500 SlideMode.prototype.selectLast = function() { |
387 this.select(this.items_.length - 1); | 501 this.select(this.getItemCount_() - 1); |
388 }; | 502 }; |
389 | 503 |
390 // Loading/unloading | 504 // Loading/unloading |
391 | 505 |
392 /** | 506 /** |
393 * Load and display an item. | 507 * Load and display an item. |
394 * | 508 * |
395 * @param {string} url Item url. | 509 * @param {string} url Item url. |
396 * @param {Object} metadata Item metadata. | 510 * @param {Object} metadata Item metadata. |
397 * @param {number} slide Slide animation direction (-1|0|1). | 511 * @param {number} slide Slide animation direction (-1|0|1). |
(...skipping 10 matching lines...) Expand all Loading... |
408 ImageUtil.setAttribute(this.container_, 'video', video); | 522 ImageUtil.setAttribute(this.container_, 'video', video); |
409 | 523 |
410 this.showSpinner_(false); | 524 this.showSpinner_(false); |
411 if (loadType == ImageView.LOAD_TYPE_ERROR) { | 525 if (loadType == ImageView.LOAD_TYPE_ERROR) { |
412 this.showErrorBanner_(video ? 'VIDEO_ERROR' : 'IMAGE_ERROR'); | 526 this.showErrorBanner_(video ? 'VIDEO_ERROR' : 'IMAGE_ERROR'); |
413 } else if (loadType == ImageView.LOAD_TYPE_OFFLINE) { | 527 } else if (loadType == ImageView.LOAD_TYPE_OFFLINE) { |
414 this.showErrorBanner_(video ? 'VIDEO_OFFLINE' : 'IMAGE_OFFLINE'); | 528 this.showErrorBanner_(video ? 'VIDEO_OFFLINE' : 'IMAGE_OFFLINE'); |
415 } | 529 } |
416 | 530 |
417 if (video) { | 531 if (video) { |
418 if (this.isEditing()) { | 532 // The editor toolbar does not make sense for video, hide it. |
419 // The editor toolbar does not make sense for video, hide it. | 533 this.stopEditing_(); |
420 this.onEdit_(); | |
421 } | |
422 this.mediaControls_.attachMedia(this.imageView_.getVideo()); | 534 this.mediaControls_.attachMedia(this.imageView_.getVideo()); |
423 //TODO(kaznacheev): Add metrics for video playback. | 535 //TODO(kaznacheev): Add metrics for video playback. |
424 } else { | 536 } else { |
425 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('View')); | 537 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('View')); |
426 | 538 |
427 function toMillions(number) { return Math.round(number / (1000 * 1000)) } | 539 function toMillions(number) { return Math.round(number / (1000 * 1000)) } |
428 | 540 |
429 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MB'), | 541 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MB'), |
430 toMillions(metadata.filesystem.size)); | 542 toMillions(metadata.filesystem.size)); |
431 | 543 |
432 var canvas = this.imageView_.getCanvas(); | 544 var canvas = this.imageView_.getCanvas(); |
433 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MPix'), | 545 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MPix'), |
434 toMillions(canvas.width * canvas.height)); | 546 toMillions(canvas.width * canvas.height)); |
435 | 547 |
436 var extIndex = url.lastIndexOf('.'); | 548 var extIndex = url.lastIndexOf('.'); |
437 var ext = extIndex < 0 ? '' : url.substr(extIndex + 1).toLowerCase(); | 549 var ext = extIndex < 0 ? '' : url.substr(extIndex + 1).toLowerCase(); |
438 if (ext == 'jpeg') ext = 'jpg'; | 550 if (ext == 'jpeg') ext = 'jpg'; |
439 ImageUtil.metrics.recordEnum( | 551 ImageUtil.metrics.recordEnum( |
440 ImageUtil.getMetricName('FileType'), ext, ImageUtil.FILE_TYPES); | 552 ImageUtil.getMetricName('FileType'), ext, ImageUtil.FILE_TYPES); |
441 } | 553 } |
442 | 554 |
| 555 // For once edited image, disallow the 'overwrite' setting change. |
| 556 ImageUtil.setAttribute(this.options_, 'saved', |
| 557 !this.getSelectedItem().isOriginal()); |
| 558 |
| 559 var times = SlideMode.OVERWRITE_BUBBLE_KEY in localStorage ? |
| 560 parseInt(localStorage[SlideMode.OVERWRITE_BUBBLE_KEY], 10) : 0; |
| 561 if (times < SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { |
| 562 this.bubble_.hidden = false; |
| 563 if (this.isEditing()) { |
| 564 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = times + 1; |
| 565 } |
| 566 } |
| 567 |
443 callback(loadType); | 568 callback(loadType); |
444 }.bind(this); | 569 }.bind(this); |
445 | 570 |
446 this.editor_.openSession( | 571 this.editor_.openSession( |
447 url, metadata, slide, this.saveCurrentImage_.bind(this), loadDone); | 572 url, metadata, slide, this.saveCurrentImage_.bind(this), loadDone); |
448 }; | 573 }; |
449 | 574 |
450 /** | 575 /** |
451 * Commit changes to the current item and reset all messages/indicators. | 576 * Commit changes to the current item and reset all messages/indicators. |
452 * | 577 * |
(...skipping 10 matching lines...) Expand all Loading... |
463 } | 588 } |
464 this.editor_.closeSession(callback); | 589 this.editor_.closeSession(callback); |
465 }; | 590 }; |
466 | 591 |
467 /** | 592 /** |
468 * Request a prefetch for the next image. | 593 * Request a prefetch for the next image. |
469 * | 594 * |
470 * @param {number} direction -1 or 1. | 595 * @param {number} direction -1 or 1. |
471 */ | 596 */ |
472 SlideMode.prototype.requestPrefetch = function(direction) { | 597 SlideMode.prototype.requestPrefetch = function(direction) { |
473 if (this.items_.length <= 1) return; | 598 if (this.getItemCount_() <= 1) return; |
474 | 599 |
475 var index = this.getNextSelectedIndex_(direction); | 600 var index = this.getNextSelectedIndex_(direction); |
476 var nextItemUrl = this.items_[index].getUrl(); | 601 var nextItemUrl = this.getItem(index).getUrl(); |
477 | 602 |
478 var selectedItem = this.getSelectedItem(); | 603 var selectedItem = this.getSelectedItem(); |
479 this.metadataCache_.get(nextItemUrl, Gallery.METADATA_TYPE, | 604 this.metadataCache_.get(nextItemUrl, Gallery.METADATA_TYPE, |
480 function(metadata) { | 605 function(metadata) { |
481 if (selectedItem != this.getSelectedItem()) return; | 606 if (selectedItem != this.getSelectedItem()) return; |
482 this.editor_.prefetchImage(nextItemUrl); | 607 this.editor_.prefetchImage(nextItemUrl); |
483 }.bind(this)); | 608 }.bind(this)); |
484 }; | 609 }; |
485 | 610 |
486 // Event handlers. | 611 // Event handlers. |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
534 return true; | 659 return true; |
535 | 660 |
536 switch (util.getKeyModifiers(event) + event.keyIdentifier) { | 661 switch (util.getKeyModifiers(event) + event.keyIdentifier) { |
537 case 'U+0020': // Space toggles the video playback. | 662 case 'U+0020': // Space toggles the video playback. |
538 if (this.isShowingVideo_()) { | 663 if (this.isShowingVideo_()) { |
539 this.mediaControls_.togglePlayStateWithFeedback(); | 664 this.mediaControls_.togglePlayStateWithFeedback(); |
540 } | 665 } |
541 break; | 666 break; |
542 | 667 |
543 case 'U+0045': // 'e' toggles the editor | 668 case 'U+0045': // 'e' toggles the editor |
544 this.onEdit_(); | 669 this.toggleEditor_(); |
545 break; | 670 break; |
546 | 671 |
547 case 'U+001B': // Escape | 672 case 'U+001B': // Escape |
548 if (!this.isEditing()) | 673 if (!this.isEditing()) |
549 return false; // Not handled. | 674 return false; // Not handled. |
550 this.onEdit_(); | 675 this.toggleEditor_(); |
551 break; | 676 break; |
552 | 677 |
553 case 'Ctrl-U+00DD': // Ctrl+] (cryptic on purpose). | 678 case 'Ctrl-U+00DD': // Ctrl+]. TODO(kaznacheev): Find a non-cryptic key. |
554 this.toggleSlideshow_(); | 679 this.toggleSlideshow_(SlideMode.SLIDESHOW_INTERVAL_FIRST); |
555 break; | 680 break; |
556 | 681 |
557 case 'Home': | 682 case 'Home': |
558 this.selectFirst(); | 683 this.selectFirst(); |
559 break; | 684 break; |
560 case 'End': | 685 case 'End': |
561 this.selectLast(); | 686 this.selectLast(); |
562 break; | 687 break; |
563 case 'Left': | 688 case 'Left': |
564 this.selectNext(-1); | 689 this.selectNext(-1); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
607 this.shouldOverwriteOriginal_(), | 732 this.shouldOverwriteOriginal_(), |
608 canvas, | 733 canvas, |
609 metadataEncoder, | 734 metadataEncoder, |
610 function(success) { | 735 function(success) { |
611 // TODO(kaznacheev): Implement write error handling. | 736 // TODO(kaznacheev): Implement write error handling. |
612 // Until then pretend that the save succeeded. | 737 // Until then pretend that the save succeeded. |
613 this.showSpinner_(false); | 738 this.showSpinner_(false); |
614 this.flashSavedLabel_(); | 739 this.flashSavedLabel_(); |
615 var newUrl = item.getUrl(); | 740 var newUrl = item.getUrl(); |
616 this.updateSelectedUrl_(oldUrl, newUrl); | 741 this.updateSelectedUrl_(oldUrl, newUrl); |
617 this.ribbon_.updateThumbnail( | 742 this.ribbon_.updateThumbnail(newUrl, this.selectedImageMetadata_); |
618 this.selectedIndex_, newUrl, this.selectedImageMetadata_); | 743 cr.dispatchSimpleEvent(this, 'content'); |
| 744 |
| 745 if (this.imageView_.getContentRevision() == 1) { // First edit. |
| 746 // Lock the 'Overwrite original' checkbox for this item. |
| 747 ImageUtil.setAttribute(this.options_, 'saved', true); |
| 748 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('Edit')); |
| 749 } |
| 750 |
| 751 if (oldUrl != newUrl) { |
| 752 this.displayedIndex_++; |
| 753 // This splice call will change the selection change event. SlideMode |
| 754 // will ignore it as the selected item is the same. |
| 755 // The ribbon will redraw while being obscured by the Editor toolbar, |
| 756 // so there is no need for nice animation here. |
| 757 this.dataModel_.splice( |
| 758 this.getSelectedIndex(), 0, new Gallery.Item(oldUrl)); |
| 759 } |
619 callback(); | 760 callback(); |
620 }.bind(this)); | 761 }.bind(this)); |
621 }; | 762 }; |
622 | 763 |
623 /** | 764 /** |
624 * Update caches when the selected item url has changed. | 765 * Update caches when the selected item url has changed. |
625 * | 766 * |
626 * @param {string} oldUrl Old url. | 767 * @param {string} oldUrl Old url. |
627 * @param {string} newUrl New url. | 768 * @param {string} newUrl New url. |
628 * @private | 769 * @private |
629 */ | 770 */ |
630 SlideMode.prototype.updateSelectedUrl_ = function(oldUrl, newUrl) { | 771 SlideMode.prototype.updateSelectedUrl_ = function(oldUrl, newUrl) { |
631 this.metadataCache_.clear(oldUrl, Gallery.METADATA_TYPE); | 772 this.metadataCache_.clear(oldUrl, Gallery.METADATA_TYPE); |
632 | 773 |
633 if (oldUrl == newUrl) | 774 if (oldUrl == newUrl) |
634 return; | 775 return; |
635 | 776 |
636 this.imageView_.changeUrl(newUrl); | 777 this.imageView_.changeUrl(newUrl); |
637 this.ribbon_.remapCache(oldUrl, newUrl); | 778 this.ribbon_.remapCache(oldUrl, newUrl); |
638 | |
639 // Let the gallery know that the selected item url has changed. | |
640 cr.dispatchSimpleEvent(this, 'selection'); | |
641 }; | 779 }; |
642 | 780 |
643 /** | 781 /** |
644 * Flash 'Saved' label briefly to indicate that the image has been saved. | 782 * Flash 'Saved' label briefly to indicate that the image has been saved. |
645 * @private | 783 * @private |
646 */ | 784 */ |
647 SlideMode.prototype.flashSavedLabel_ = function() { | 785 SlideMode.prototype.flashSavedLabel_ = function() { |
648 var setLabelHighlighted = | 786 var setLabelHighlighted = |
649 ImageUtil.setAttribute.bind(null, this.savedLabel_, 'highlighted'); | 787 ImageUtil.setAttribute.bind(null, this.savedLabel_, 'highlighted'); |
650 setTimeout(setLabelHighlighted.bind(null, true), 0); | 788 setTimeout(setLabelHighlighted.bind(null, true), 0); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
691 /** | 829 /** |
692 * Overwrite info bubble close handler. | 830 * Overwrite info bubble close handler. |
693 * @private | 831 * @private |
694 */ | 832 */ |
695 SlideMode.prototype.onCloseBubble_ = function() { | 833 SlideMode.prototype.onCloseBubble_ = function() { |
696 this.bubble_.hidden = true; | 834 this.bubble_.hidden = true; |
697 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = | 835 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = |
698 SlideMode.OVERWRITE_BUBBLE_MAX_TIMES; | 836 SlideMode.OVERWRITE_BUBBLE_MAX_TIMES; |
699 }; | 837 }; |
700 | 838 |
| 839 // Slideshow |
| 840 |
701 /** | 841 /** |
702 * Callback called when the image is edited. | 842 * Slideshow interval in ms. |
| 843 */ |
| 844 SlideMode.SLIDESHOW_INTERVAL = 5000; |
| 845 |
| 846 /** |
| 847 * First slideshow interval in ms. It should be shorter so that the user |
| 848 * is not guessing whether the button worked. |
| 849 */ |
| 850 SlideMode.SLIDESHOW_INTERVAL_FIRST = 1000; |
| 851 |
| 852 /** |
| 853 * @return {boolean} True if the slideshow is on. |
703 * @private | 854 * @private |
704 */ | 855 */ |
705 SlideMode.prototype.onImageContentChanged_ = function() { | 856 SlideMode.prototype.isSlideshowOn_ = function() { |
706 var revision = this.imageView_.getContentRevision(); | 857 return this.container_.hasAttribute('slideshow'); |
707 if (revision == 0) { | 858 }; |
708 // Just loaded. | 859 |
709 var times = SlideMode.OVERWRITE_BUBBLE_KEY in localStorage ? | 860 /** |
710 parseInt(localStorage[SlideMode.OVERWRITE_BUBBLE_KEY], 10) : 0; | 861 * Stop the slideshow if it is on. |
711 if (times < SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { | 862 * @private |
712 this.bubble_.hidden = false; | 863 */ |
713 if (this.isEditing()) { | 864 SlideMode.prototype.stopSlideshow_ = function() { |
714 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = times + 1; | 865 if (this.isSlideshowOn_()) |
715 } | 866 this.toggleSlideshow_(); |
716 } | 867 }; |
| 868 |
| 869 /** |
| 870 * Start/stop the slideshow. |
| 871 * |
| 872 * @param {number} opt_interval First interval in ms. |
| 873 * @param {Event} opt_event Event. |
| 874 * @private |
| 875 */ |
| 876 |
| 877 SlideMode.prototype.toggleSlideshow_ = function(opt_interval, opt_event) { |
| 878 if (opt_event) // Caused by user action, notify the Gallery. |
| 879 cr.dispatchSimpleEvent(this, 'useraction'); |
| 880 |
| 881 if (!this.active_) { |
| 882 // Enter the slide mode. Show the first image for the full interval. |
| 883 this.enter(this.toggleSlideshow_.bind(this, SlideMode.SLIDESHOW_INTERVAL)); |
| 884 return; |
717 } | 885 } |
718 | 886 |
719 if (revision == 1) { | 887 this.stopEditing_(); |
720 // First edit. | 888 ImageUtil.setAttribute(this.container_, 'slideshow', !this.isSlideshowOn_()); |
721 ImageUtil.setAttribute(this.options_, 'saved', true); | 889 ImageUtil.setAttribute( |
722 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('Edit')); | 890 this.slideShowButton_, 'pressed', this.isSlideshowOn_()); |
| 891 |
| 892 if (this.isSlideshowOn_()) { |
| 893 this.scheduleNextSlide_(opt_interval); |
| 894 } else { |
| 895 if (this.slideShowTimeout_) { |
| 896 clearInterval(this.slideShowTimeout_); |
| 897 this.slideShowTimeout_ = null; |
| 898 } |
723 } | 899 } |
724 }; | 900 }; |
725 | 901 |
726 // Misc | |
727 | |
728 /** | 902 /** |
729 * Start/stop the slide show. | 903 * @param {number} opt_interval Slideshow interval in ms. |
730 * @private | 904 * @private |
731 */ | 905 */ |
732 SlideMode.prototype.toggleSlideshow_ = function() { | 906 SlideMode.prototype.scheduleNextSlide_ = function(opt_interval) { |
733 if (this.slideShowTimeout_) { | 907 if (this.slideShowTimeout_) |
734 clearInterval(this.slideShowTimeout_); | 908 clearTimeout(this.slideShowTimeout_); |
735 this.slideShowTimeout_ = null; | 909 |
736 } else { | 910 this.slideShowTimeout_ = setTimeout(function() { |
737 var self = this; | 911 this.slideShowTimeout_ = null; |
738 function nextSlide() { | 912 this.selectNext(1); |
739 self.selectNext(1, | 913 }.bind(this), |
740 function() { self.slideShowTimeout_ = setTimeout(nextSlide, 5000) }); | 914 opt_interval || SlideMode.SLIDESHOW_INTERVAL); |
741 } | |
742 nextSlide(); | |
743 } | |
744 }; | 915 }; |
745 | 916 |
746 /** | 917 /** |
747 * @return {boolean} True if the editor is active. | 918 * @return {boolean} True if the editor is active. |
748 */ | 919 */ |
749 SlideMode.prototype.isEditing = function() { | 920 SlideMode.prototype.isEditing = function() { |
750 return this.container_.hasAttribute('editing'); | 921 return this.container_.hasAttribute('editing'); |
751 }; | 922 }; |
752 | 923 |
753 /** | 924 /** |
| 925 * Stop editing. |
| 926 * @private |
| 927 */ |
| 928 SlideMode.prototype.stopEditing_ = function() { |
| 929 if (this.isEditing()) |
| 930 this.toggleEditor_(); |
| 931 }; |
| 932 |
| 933 /** |
754 * Activate/deactivate editor. | 934 * Activate/deactivate editor. |
| 935 * @param {Event} opt_event Event. |
755 * @private | 936 * @private |
756 */ | 937 */ |
757 SlideMode.prototype.onEdit_ = function() { | 938 SlideMode.prototype.toggleEditor_ = function(opt_event) { |
| 939 if (opt_event) // Caused by user action, notify the Gallery. |
| 940 cr.dispatchSimpleEvent(this, 'useraction'); |
| 941 |
| 942 if (!this.active_) { |
| 943 this.enter(this.toggleEditor_.bind(this)); |
| 944 return; |
| 945 } |
| 946 |
| 947 this.stopSlideshow_(); |
758 if (!this.isEditing() && this.isShowingVideo_()) | 948 if (!this.isEditing() && this.isShowingVideo_()) |
759 return; // No editing for videos. | 949 return; // No editing for videos. |
760 | 950 |
761 ImageUtil.setAttribute(this.container_, 'editing', !this.isEditing()); | 951 ImageUtil.setAttribute(this.container_, 'editing', !this.isEditing()); |
762 | 952 |
763 if (this.isEditing()) { // isEditing_ has just been flipped to a new value. | 953 if (this.isEditing()) { // isEditing_ has just been flipped to a new value. |
764 if (this.context_.readonlyDirName) { | 954 if (this.context_.readonlyDirName) { |
765 this.editor_.getPrompt().showAt( | 955 this.editor_.getPrompt().showAt( |
766 'top', 'readonly_warning', 0, this.context_.readonlyDirName); | 956 'top', 'readonly_warning', 0, this.context_.readonlyDirName); |
767 } | 957 } |
768 } else { | 958 } else { |
769 this.editor_.getPrompt().hide(); | 959 this.editor_.getPrompt().hide(); |
770 } | 960 } |
771 | 961 |
772 ImageUtil.setAttribute(this.editButton_, 'pressed', this.isEditing()); | 962 ImageUtil.setAttribute(this.editButton_, 'pressed', this.isEditing()); |
773 | |
774 cr.dispatchSimpleEvent(this, 'edit'); | |
775 }; | 963 }; |
776 | 964 |
777 /** | 965 /** |
778 * Display the error banner. | 966 * Display the error banner. |
779 * @param {string} message Message. | 967 * @param {string} message Message. |
780 * @private | 968 * @private |
781 */ | 969 */ |
782 SlideMode.prototype.showErrorBanner_ = function(message) { | 970 SlideMode.prototype.showErrorBanner_ = function(message) { |
783 if (message) { | 971 if (message) { |
784 this.errorBanner_.textContent = this.displayStringFunction_(message); | 972 this.errorBanner_.textContent = this.displayStringFunction_(message); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
862 done = true; | 1050 done = true; |
863 } | 1051 } |
864 }.bind(this); | 1052 }.bind(this); |
865 }; | 1053 }; |
866 | 1054 |
867 /** | 1055 /** |
868 * If the user touched the image and moved the finger more than SWIPE_THRESHOLD | 1056 * If the user touched the image and moved the finger more than SWIPE_THRESHOLD |
869 * horizontally it's considered as a swipe gesture (change the current image). | 1057 * horizontally it's considered as a swipe gesture (change the current image). |
870 */ | 1058 */ |
871 SwipeOverlay.SWIPE_THRESHOLD = 100; | 1059 SwipeOverlay.SWIPE_THRESHOLD = 100; |
OLD | NEW |