Chromium Code Reviews| Index: chrome/browser/resources/file_manager/js/photo/slide_mode.js |
| diff --git a/chrome/browser/resources/file_manager/js/photo/slide_mode.js b/chrome/browser/resources/file_manager/js/photo/slide_mode.js |
| index ceb026f463e640c4435db9dfde0e7c77c51ddf4b..90e049055ef79bab545ac8b977697452c47a8833 100644 |
| --- a/chrome/browser/resources/file_manager/js/photo/slide_mode.js |
| +++ b/chrome/browser/resources/file_manager/js/photo/slide_mode.js |
| @@ -6,23 +6,36 @@ |
| * Slide mode displays a single image and has a set of controls to navigate |
| * between the images and to edit an image. |
| * |
| - * @param {Element} container Container element. |
| + * TODO(kaznacheev): Introduce a parameter object. |
| + * |
| + * @param {Element} container Main container element. |
| + * @param {Element} content Content container element. |
| * @param {Element} toolbar Toolbar element. |
| * @param {ImageEditor.Prompt} prompt Prompt. |
| + * @param {cr.ui.ArrayDataModel} dataModel Data model. |
| + * @param {cr.ui.ListSelectionModel} selectionModel Selection model. |
| * @param {Object} context Context. |
| * @param {function(string):string} displayStringFunction String formatting |
| * function. |
| * @constructor |
| */ |
| -function SlideMode(container, toolbar, prompt, context, displayStringFunction) { |
| +function SlideMode(container, content, toolbar, prompt, |
| + dataModel, selectionModel, |
| + context, displayStringFunction) { |
| this.container_ = container; |
| - this.toolbar_ = toolbar; |
| this.document_ = container.ownerDocument; |
| + this.content = content; |
| + this.toolbar_ = toolbar; |
| this.prompt_ = prompt; |
| + this.dataModel_ = dataModel; |
| + this.selectionModel_ = selectionModel; |
| this.context_ = context; |
| this.metadataCache_ = context.metadataCache; |
| this.displayStringFunction_ = displayStringFunction; |
| + this.onSelectionBound_ = this.onSelection_.bind(this); |
| + this.onSpliceBound_ = this.onSplice_.bind(this); |
| + |
| this.initListeners_(); |
| this.initDom_(); |
| } |
| @@ -136,6 +149,9 @@ SlideMode.prototype.initDom_ = function() { |
| util.createChild(this.arrowRight_); |
| this.ribbonSpacer_ = util.createChild(this.toolbar_, 'ribbon-spacer'); |
| + this.ribbon_ = new Ribbon(this.document_, |
| + this.metadataCache_, this.dataModel_, this.selectionModel_); |
| + this.ribbonSpacer_.appendChild(this.ribbon_); |
| // Error indicator. |
| var errorWrapper = util.createChild(this.container_, 'prompt-wrapper'); |
| @@ -145,12 +161,16 @@ SlideMode.prototype.initDom_ = function() { |
| util.createChild(this.container_, 'spinner'); |
| + this.slideShowButton_ = util.createChild(this.toolbar_, 'button slideshow'); |
| + this.slideShowButton_.title = this.displayStringFunction_('slideshow'); |
| + this.slideShowButton_.addEventListener('click', |
| + this.toggleSlideshow_.bind(this, SlideMode.SLIDESHOW_INTERVAL_FIRST)); |
| - this.editButton_ = util.createChild(this.toolbar_, 'button edit'); |
| - this.editButton_.textContent = this.displayStringFunction_('edit'); |
| - this.editButton_.addEventListener('click', this.onEdit_.bind(this)); |
| + // Editor. |
| - // Editor toolbar. |
| + this.editButton_ = util.createChild(this.toolbar_, 'button edit'); |
| + this.editButton_.title = this.displayStringFunction_('edit'); |
| + this.editButton_.addEventListener('click', this.toggleEditor_.bind(this)); |
| this.editBar_ = util.createChild(this.toolbar_, 'edit-bar'); |
| this.editBarMain_ = util.createChild(this.editBar_, 'edit-main'); |
| @@ -168,8 +188,6 @@ SlideMode.prototype.initDom_ = function() { |
| this.viewport_, |
| this.metadataCache_); |
| - this.imageView_.addContentCallback(this.onImageContentChanged_.bind(this)); |
| - |
| this.editor_ = new ImageEditor( |
| this.viewport_, |
| this.imageView_, |
| @@ -190,109 +208,164 @@ SlideMode.prototype.initDom_ = function() { |
| /** |
| * Load items, display the selected item. |
| * |
| - * @param {Array.<Gallery.Item>} items Array of items. |
| - * @param {number} selectedIndex Selected index. |
| - * @param {function} callback Callback. |
| + * @param {function} opt_callback Callback. |
| */ |
| -SlideMode.prototype.load = function(items, selectedIndex, callback) { |
| - var selectedItem = items[selectedIndex]; |
| - if (!selectedItem) { |
| - this.showErrorBanner_('IMAGE_ERROR'); |
| - return; |
| - } |
| +SlideMode.prototype.enter = function(opt_callback) { |
| + this.container_.setAttribute('mode', 'slide'); |
| + |
| + this.sequenceDirection_ = 0; |
| + this.sequenceLength_ = 0; |
| var loadDone = function() { |
| - this.items_ = items; |
| - this.setSelectedIndex_(selectedIndex); |
| - this.setupNavigation_(); |
| - setTimeout(this.requestPrefetch.bind(this, 1 /* Next item */), 1000); |
| - callback(); |
| + this.active_ = true; |
| + |
| + this.selectionModel_.addEventListener('change', this.onSelectionBound_); |
| + this.dataModel_.addEventListener('splice', this.onSpliceBound_); |
| + |
| + this.ribbon_.enable(); |
| + |
| + this.prefetchTimer_ = setTimeout(function() { |
| + this.prefetchTimer_ = null; |
| + this.requestPrefetch(1); // Prefetch the next image. |
| + }.bind(this), 1000); |
| + if (opt_callback) opt_callback(); |
| }.bind(this); |
| - var selectedUrl = selectedItem.getUrl(); |
| - // Show the selected item ASAP, then complete the initialization |
| - // (loading the ribbon thumbnails can take some time). |
| - this.metadataCache_.get(selectedUrl, Gallery.METADATA_TYPE, |
| - function(metadata) { |
| - this.loadItem_(selectedUrl, metadata, 0, loadDone); |
| - }.bind(this)); |
| + if (this.getItemCount_() == 0) { |
| + this.displayedIndex_ = -1; |
| + //TODO(kaznacheev) Show this message in the grid mode too. |
| + this.showErrorBanner_('NO_IMAGES'); |
| + loadDone(); |
| + } else { |
| + // Ensure valid single selection. |
| + // Note that the SlideMode object is not listening to selection change yet. |
| + this.select(Math.max(0, this.getSelectedIndex())); |
| + cr.dispatchSimpleEvent(this, 'namechange'); // Update name in the UI. |
| + this.displayedIndex_ = this.getSelectedIndex(); |
| + |
| + var selectedItem = this.getSelectedItem(); |
| + var selectedUrl = selectedItem.getUrl(); |
| + // Show the selected item ASAP, then complete the initialization |
| + // (loading the ribbon thumbnails can take some time). |
| + this.metadataCache_.get(selectedUrl, Gallery.METADATA_TYPE, |
| + function(metadata) { |
| + this.loadItem_(selectedUrl, metadata, 0, loadDone); |
| + }.bind(this)); |
| + |
| + } |
| +}; |
| + |
| +/** |
| + * Leave the mode. |
| + * @param {function} opt_callback Callback. |
| + */ |
| +SlideMode.prototype.leave = function(opt_callback) { |
| + if (this.prefetchTimer_) { |
| + clearTimeout(this.prefetchTimer_); |
| + this.prefetchTimer_ = null; |
| + } |
| + |
| + var commitDone = function() { |
| + this.stopEditing_(); |
| + this.stopSlideshow_(); |
| + this.unloadImage_(); |
| + this.selectionModel_.removeEventListener( |
| + 'change', this.onSelectionBound_); |
| + this.dataModel_.removeEventListener('splice', this.onSpliceBound_); |
| + this.ribbon_.disable(); |
| + this.active_ = false; |
| + if (opt_callback) opt_callback(); |
| + }.bind(this); |
| + |
| + if (this.getItemCount_() == 0) { |
| + this.showErrorBanner_(false); |
| + commitDone(); |
| + } else { |
| + this.commitItem_(commitDone); |
| + } |
| }; |
| /** |
| - * Setup navigation controls. |
| + * @return {boolean} True if the mode has active tools (that should not fade). |
| + */ |
| +SlideMode.prototype.hasActiveTool = function() { |
| + return this.isEditing(); |
| +}; |
| + |
| +/** |
| + * @return {number} Item count. |
| * @private |
| */ |
| -SlideMode.prototype.setupNavigation_ = function() { |
| - this.sequenceDirection_ = 0; |
| - this.sequenceLength_ = 0; |
| +SlideMode.prototype.getItemCount_ = function() { |
| + return this.dataModel_.length; |
| +}; |
| - ImageUtil.setAttribute(this.arrowLeft_, 'active', this.items_.length > 1); |
| - ImageUtil.setAttribute(this.arrowRight_, 'active', this.items_.length > 1); |
| +/** |
| + * @param {number} index Index. |
| + * @return {Gallery.Item} Item. |
| + */ |
| +SlideMode.prototype.getItem = function(index) { |
| + return this.dataModel_.item(index); |
| +}; |
| - this.ribbon_ = new Ribbon( |
| - this.document_, this.metadataCache_, this.select.bind(this)); |
| - this.ribbonSpacer_.appendChild(this.ribbon_); |
| - this.ribbon_.update(this.items_, this.selectedIndex_); |
| +/** |
| + * @return {Gallery.Item} Selected index. |
| + */ |
| +SlideMode.prototype.getSelectedIndex = function() { |
| + return this.selectionModel_.selectedIndexes.length ? |
| + this.selectionModel_.selectedIndexes[0] : |
| + -1; |
| }; |
| /** |
| * @return {Gallery.Item} Selected item |
| */ |
| SlideMode.prototype.getSelectedItem = function() { |
| - return this.items_ && this.items_[this.selectedIndex_]; |
| + return this.getItem(this.getSelectedIndex()); |
| }; |
| /** |
| - * Change the selection. |
| - * |
| - * Commits the current image and changes the selection. |
| + * Selection change handler. |
| * |
| - * @param {number} index New selected index. |
| - * @param {number} opt_slideDir Slide animation direction (-1|0|1). |
| - * Derived from the new and the old selected indices if omitted. |
| - * @param {function} opt_callback Callback. |
| + * Commits the current image and displays the newly selected image. |
| + * @private |
| */ |
| -SlideMode.prototype.select = function(index, opt_slideDir, opt_callback) { |
| - if (!this.items_) |
| - return; // Not fully initialized, still loading the first image. |
| +SlideMode.prototype.onSelection_ = function() { |
| + if (this.selectionModel_.selectedIndexes.length == 0) |
| + return; // Temporary empty selection. |
| - if (index == this.selectedIndex_) |
| + if (this.getSelectedIndex() == this.displayedIndex_) |
| return; // Do not reselect. |
| - this.commitItem_( |
| - this.doSelect_.bind(this, index, opt_slideDir, opt_callback)); |
| + this.commitItem_(this.loadSelectedItem_.bind(this)); |
| }; |
| /** |
| - * Set the new selected index value. |
| + * Change the selection. |
| + * |
| * @param {number} index New selected index. |
| - * @private |
| + * @param {number} opt_slideHint Slide animation direction (-1|1). |
| */ |
| -SlideMode.prototype.setSelectedIndex_ = function(index) { |
| - this.selectedIndex_ = index; |
| - cr.dispatchSimpleEvent(this, 'selection'); |
| - |
| - // For once edited image, disallow the 'overwrite' setting change. |
| - ImageUtil.setAttribute(this.options_, 'saved', |
| - !this.getSelectedItem().isOriginal()); |
| +SlideMode.prototype.select = function(index, opt_slideHint) { |
| + this.slideHint_ = opt_slideHint; |
| + this.selectionModel_.unselectAll(); |
| + this.selectionModel_.setIndexSelected(index, true); |
| }; |
| /** |
| - * Perform the actual selection change. |
| + * Load the selected item. |
| * |
| - * @param {number} index New selected index. |
| - * @param {number} opt_slideDir Slide animation direction (-1|0|1). |
| - * Derived from the new and the old selected indices if omitted. |
| - * @param {function} opt_callback Callback. |
| * @private |
| */ |
| -SlideMode.prototype.doSelect_ = function(index, opt_slideDir, opt_callback) { |
| - if (index == this.selectedIndex_) |
| - return; // Do not reselect |
| +SlideMode.prototype.loadSelectedItem_ = function() { |
| + var slideHint = this.slideHint_; |
| + this.slideHint_ = undefined; |
| + |
| + var index = this.getSelectedIndex(); |
| + if (index == this.displayedIndex_) |
| + return; // Do not reselect. |
| - var step = opt_slideDir != undefined ? |
| - opt_slideDir : |
| - (index - this.selectedIndex_); |
| + var step = slideHint || (index - this.displayedIndex_); |
| if (Math.abs(step) != 1) { |
| // Long leap, the sequence is broken, we have no good prefetch candidate. |
| @@ -310,13 +383,10 @@ SlideMode.prototype.doSelect_ = function(index, opt_slideDir, opt_callback) { |
| if (this.sequenceLength_ <= 1) { |
| // We have just broke the sequence. Touch the current image so that it stays |
| // in the cache longer. |
| - this.editor_.prefetchImage(this.getSelectedItem().getUrl()); |
| + this.imageView_.prefetch(this.imageView_.contentID_); |
| } |
| - this.setSelectedIndex_(index); |
| - |
| - if (this.ribbon_) |
| - this.ribbon_.update(this.items_, this.selectedIndex_); |
| + this.displayedIndex_ = index; |
| function shouldPrefetch(loadType, step, sequenceLength) { |
| // Never prefetch when selecting out of sequence. |
| @@ -345,7 +415,8 @@ SlideMode.prototype.doSelect_ = function(index, opt_slideDir, opt_callback) { |
| if (shouldPrefetch(loadType, step, this.sequenceLength_)) { |
| this.requestPrefetch(step); |
| } |
| - if (opt_callback) opt_callback(); |
| + if (this.isSlideshowOn_()) |
| + this.scheduleNextSlide_(); |
| }.bind(this)); |
| }.bind(this); |
| this.metadataCache_.get( |
| @@ -353,24 +424,65 @@ SlideMode.prototype.doSelect_ = function(index, opt_slideDir, opt_callback) { |
| }; |
| /** |
| + * Unload the current image. |
| + * @private |
| + */ |
| +SlideMode.prototype.unloadImage_ = function() { |
| + this.imageView_.unload(); |
| + this.container_.removeAttribute('video'); |
| +}; |
| + |
| +/** |
| + * Data model 'splice' event handler. |
| + * @param {Event} event Event. |
| + * @private |
| + */ |
| +SlideMode.prototype.onSplice_ = function(event) { |
| + ImageUtil.setAttribute(this.arrowLeft_, 'active', this.getItemCount_() > 1); |
| + ImageUtil.setAttribute(this.arrowRight_, 'active', this.getItemCount_() > 1); |
| + |
| + if (event.removed.length != 1) |
| + return; |
| + |
| + // Delay the selection to let the ribbon splice handler work first. |
| + setTimeout(function() { |
| + if (event.index < this.dataModel_.length) { |
| + // There is the next item, select it. |
| + // The next item is now at the same index as the removed one, so we need |
| + // to correct displayIndex_. |
|
dgozman
2012/08/27 15:07:43
I think, this is done for nice animation? Needs a
|
| + this.displayedIndex_ = event.index - 1; |
| + this.select(event.index); |
| + } else if (this.dataModel_.length) { |
| + // Removed item is the rightmost, but there are more items. |
| + this.select(event.index - 1); // Select the new last index. |
| + } else { |
| + // No items left. Unload the image and show the banner. |
| + this.commitItem_(function() { |
| + this.unloadImage_(); |
| + this.showErrorBanner_('NO_IMAGES'); |
| + }.bind(this)); |
| + } |
| + }.bind(this), 0); |
| +}; |
| + |
| +/** |
| * @param {number} direction -1 for left, 1 for right. |
| * @return {number} Next index in the gived direction, with wrapping. |
| * @private |
| */ |
| SlideMode.prototype.getNextSelectedIndex_ = function(direction) { |
| - var index = this.selectedIndex_ + (direction > 0 ? 1 : -1); |
| - if (index == -1) return this.items_.length - 1; |
| - if (index == this.items_.length) return 0; |
| + var index = this.getSelectedIndex() + (direction > 0 ? 1 : -1); |
| + if (index == -1) return this.getItemCount_() - 1; |
| + if (index == this.getItemCount_()) return 0; |
| return index; |
| }; |
| /** |
| * Select the next item. |
| * @param {number} direction -1 for left, 1 for right. |
| - * @param {function} opt_callback Callback. |
| */ |
| -SlideMode.prototype.selectNext = function(direction, opt_callback) { |
| - this.select(this.getNextSelectedIndex_(direction), direction, opt_callback); |
| +SlideMode.prototype.selectNext = function(direction) { |
| + this.select(this.getNextSelectedIndex_(direction), direction); |
| }; |
| /** |
| @@ -384,7 +496,7 @@ SlideMode.prototype.selectFirst = function() { |
| * Select the last item. |
| */ |
| SlideMode.prototype.selectLast = function() { |
| - this.select(this.items_.length - 1); |
| + this.select(this.getItemCount_() - 1); |
| }; |
| // Loading/unloading |
| @@ -415,10 +527,8 @@ SlideMode.prototype.loadItem_ = function(url, metadata, slide, callback) { |
| } |
| if (video) { |
| - if (this.isEditing()) { |
| - // The editor toolbar does not make sense for video, hide it. |
| - this.onEdit_(); |
| - } |
| + // The editor toolbar does not make sense for video, hide it. |
| + this.stopEditing_(); |
| this.mediaControls_.attachMedia(this.imageView_.getVideo()); |
| //TODO(kaznacheev): Add metrics for video playback. |
| } else { |
| @@ -440,6 +550,19 @@ SlideMode.prototype.loadItem_ = function(url, metadata, slide, callback) { |
| ImageUtil.getMetricName('FileType'), ext, ImageUtil.FILE_TYPES); |
| } |
| + // For once edited image, disallow the 'overwrite' setting change. |
| + ImageUtil.setAttribute(this.options_, 'saved', |
| + !this.getSelectedItem().isOriginal()); |
| + |
| + var times = SlideMode.OVERWRITE_BUBBLE_KEY in localStorage ? |
| + parseInt(localStorage[SlideMode.OVERWRITE_BUBBLE_KEY], 10) : 0; |
| + if (times < SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { |
| + this.bubble_.hidden = false; |
| + if (this.isEditing()) { |
| + localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = times + 1; |
| + } |
| + } |
| + |
| callback(loadType); |
| }.bind(this); |
| @@ -470,10 +593,10 @@ SlideMode.prototype.commitItem_ = function(callback) { |
| * @param {number} direction -1 or 1. |
| */ |
| SlideMode.prototype.requestPrefetch = function(direction) { |
| - if (this.items_.length <= 1) return; |
| + if (this.getItemCount_() <= 1) return; |
| var index = this.getNextSelectedIndex_(direction); |
| - var nextItemUrl = this.items_[index].getUrl(); |
| + var nextItemUrl = this.getItem(index).getUrl(); |
| var selectedItem = this.getSelectedItem(); |
| this.metadataCache_.get(nextItemUrl, Gallery.METADATA_TYPE, |
| @@ -541,17 +664,17 @@ SlideMode.prototype.onKeyDown = function(event) { |
| break; |
| case 'U+0045': // 'e' toggles the editor |
| - this.onEdit_(); |
| + this.toggleEditor_(); |
| break; |
| case 'U+001B': // Escape |
| if (!this.isEditing()) |
| return false; // Not handled. |
| - this.onEdit_(); |
| + this.toggleEditor_(); |
| break; |
| - case 'Ctrl-U+00DD': // Ctrl+] (cryptic on purpose). |
| - this.toggleSlideshow_(); |
| + case 'Ctrl-U+00DD': // Ctrl+]. TODO(kaznacheev): Find a non-cryptic key. |
| + this.toggleSlideshow_(SlideMode.SLIDESHOW_INTERVAL_FIRST); |
| break; |
| case 'Home': |
| @@ -614,8 +737,24 @@ SlideMode.prototype.saveCurrentImage_ = function(callback) { |
| this.flashSavedLabel_(); |
| var newUrl = item.getUrl(); |
| this.updateSelectedUrl_(oldUrl, newUrl); |
| - this.ribbon_.updateThumbnail( |
| - this.selectedIndex_, newUrl, this.selectedImageMetadata_); |
| + this.ribbon_.updateThumbnail(newUrl, this.selectedImageMetadata_); |
| + cr.dispatchSimpleEvent(this, 'content'); |
| + |
| + if (this.imageView_.getContentRevision() == 1) { // First edit. |
| + // Lock the 'Overwrite original' checkbox for this item. |
| + ImageUtil.setAttribute(this.options_, 'saved', true); |
| + ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('Edit')); |
| + } |
| + |
| + if (oldUrl != newUrl) { |
| + this.displayedIndex_++; |
| + // This splice call will change the selection change event. SlideMode |
| + // will ignore it as the selected item is the same. |
| + // The ribbon will redraw while being obscured by the Editor toolbar, |
| + // so there is no need for nice animation here. |
| + this.dataModel_.splice( |
| + this.getSelectedIndex(), 0, new Gallery.Item(oldUrl)); |
| + } |
| callback(); |
| }.bind(this)); |
| }; |
| @@ -635,9 +774,6 @@ SlideMode.prototype.updateSelectedUrl_ = function(oldUrl, newUrl) { |
| this.imageView_.changeUrl(newUrl); |
| this.ribbon_.remapCache(oldUrl, newUrl); |
| - |
| - // Let the gallery know that the selected item url has changed. |
| - cr.dispatchSimpleEvent(this, 'selection'); |
| }; |
| /** |
| @@ -698,52 +834,85 @@ SlideMode.prototype.onCloseBubble_ = function() { |
| SlideMode.OVERWRITE_BUBBLE_MAX_TIMES; |
| }; |
| +// Slideshow |
| + |
| /** |
| - * Callback called when the image is edited. |
| - * @private |
| + * Slideshow interval in ms. |
| */ |
| -SlideMode.prototype.onImageContentChanged_ = function() { |
| - var revision = this.imageView_.getContentRevision(); |
| - if (revision == 0) { |
| - // Just loaded. |
| - var times = SlideMode.OVERWRITE_BUBBLE_KEY in localStorage ? |
| - parseInt(localStorage[SlideMode.OVERWRITE_BUBBLE_KEY], 10) : 0; |
| - if (times < SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { |
| - this.bubble_.hidden = false; |
| - if (this.isEditing()) { |
| - localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = times + 1; |
| - } |
| - } |
| - } |
| +SlideMode.SLIDESHOW_INTERVAL = 5000; |
| - if (revision == 1) { |
| - // First edit. |
| - ImageUtil.setAttribute(this.options_, 'saved', true); |
| - ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('Edit')); |
| - } |
| +/** |
| + * First slideshow interval in ms. It should be shorter so that the user |
| + * is not guessing whether the button worked. |
| + */ |
| +SlideMode.SLIDESHOW_INTERVAL_FIRST = 1000; |
| + |
| +/** |
| + * @return {boolean} True if the slideshow is on. |
| + * @private |
| + */ |
| +SlideMode.prototype.isSlideshowOn_ = function() { |
| + return this.container_.hasAttribute('slideshow'); |
| }; |
| -// Misc |
| +/** |
| + * Stop the slideshow if it is on. |
| + * @private |
| + */ |
| +SlideMode.prototype.stopSlideshow_ = function() { |
| + if (this.isSlideshowOn_()) |
| + this.toggleSlideshow_(); |
| +}; |
| /** |
| - * Start/stop the slide show. |
| + * Start/stop the slideshow. |
| + * |
| + * @param {number} opt_interval First interval in ms. |
| + * @param {Event} opt_event Event. |
| * @private |
| */ |
| -SlideMode.prototype.toggleSlideshow_ = function() { |
| - if (this.slideShowTimeout_) { |
| - clearInterval(this.slideShowTimeout_); |
| - this.slideShowTimeout_ = null; |
| + |
| +SlideMode.prototype.toggleSlideshow_ = function(opt_interval, opt_event) { |
| + if (opt_event) // Caused by user action, notify the Gallery. |
| + cr.dispatchSimpleEvent(this, 'useraction'); |
| + |
| + if (!this.active_) { |
| + // Enter the slide mode. Show the first image for the full interval. |
| + this.enter(this.toggleSlideshow_.bind(this, SlideMode.SLIDESHOW_INTERVAL)); |
| + return; |
| + } |
| + |
| + this.stopEditing_(); |
| + ImageUtil.setAttribute(this.container_, 'slideshow', !this.isSlideshowOn_()); |
| + ImageUtil.setAttribute( |
| + this.slideShowButton_, 'pressed', this.isSlideshowOn_()); |
| + |
| + if (this.isSlideshowOn_()) { |
| + this.scheduleNextSlide_(opt_interval); |
| } else { |
| - var self = this; |
| - function nextSlide() { |
| - self.selectNext(1, |
| - function() { self.slideShowTimeout_ = setTimeout(nextSlide, 5000) }); |
| + if (this.slideShowTimeout_) { |
| + clearInterval(this.slideShowTimeout_); |
| + this.slideShowTimeout_ = null; |
| } |
| - nextSlide(); |
| } |
| }; |
| /** |
| + * @param {number} opt_interval Slideshow interval in ms. |
| + * @private |
| + */ |
| +SlideMode.prototype.scheduleNextSlide_ = function(opt_interval) { |
| + if (this.slideShowTimeout_) |
| + clearTimeout(this.slideShowTimeout_); |
| + |
| + this.slideShowTimeout_ = setTimeout(function() { |
| + this.slideShowTimeout_ = null; |
| + this.selectNext(1); |
| + }.bind(this), |
| + opt_interval || SlideMode.SLIDESHOW_INTERVAL); |
| +}; |
| + |
| +/** |
| * @return {boolean} True if the editor is active. |
| */ |
| SlideMode.prototype.isEditing = function() { |
| @@ -751,10 +920,29 @@ SlideMode.prototype.isEditing = function() { |
| }; |
| /** |
| + * Stop editing. |
| + * @private |
| + */ |
| +SlideMode.prototype.stopEditing_ = function() { |
| + if (this.isEditing()) |
| + this.toggleEditor_(); |
| +}; |
| + |
| +/** |
| * Activate/deactivate editor. |
| + * @param {Event} opt_event Event. |
| * @private |
| */ |
| -SlideMode.prototype.onEdit_ = function() { |
| +SlideMode.prototype.toggleEditor_ = function(opt_event) { |
| + if (opt_event) // Caused by user action, notify the Gallery. |
| + cr.dispatchSimpleEvent(this, 'useraction'); |
| + |
| + if (!this.active_) { |
| + this.enter(this.toggleEditor_.bind(this)); |
| + return; |
| + } |
| + |
| + this.stopSlideshow_(); |
| if (!this.isEditing() && this.isShowingVideo_()) |
| return; // No editing for videos. |
| @@ -770,8 +958,6 @@ SlideMode.prototype.onEdit_ = function() { |
| } |
| ImageUtil.setAttribute(this.editButton_, 'pressed', this.isEditing()); |
| - |
| - cr.dispatchSimpleEvent(this, 'edit'); |
| }; |
| /** |