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 |
deleted file mode 100644 |
index fe07754df54b104cab0b57dfc8d1a201ee2e0b34..0000000000000000000000000000000000000000 |
--- a/chrome/browser/resources/file_manager/js/photo/slide_mode.js |
+++ /dev/null |
@@ -1,1343 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-'use strict'; |
- |
-/** |
- * Slide mode displays a single image and has a set of controls to navigate |
- * between the images and to edit an image. |
- * |
- * 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(function())} toggleMode Function to toggle the Gallery mode. |
- * @param {function(string):string} displayStringFunction String formatting |
- * function. |
- * @constructor |
- */ |
-function SlideMode(container, content, toolbar, prompt, |
- dataModel, selectionModel, context, |
- toggleMode, displayStringFunction) { |
- this.container_ = container; |
- 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.toggleMode_ = toggleMode; |
- this.displayStringFunction_ = displayStringFunction; |
- |
- this.onSelectionBound_ = this.onSelection_.bind(this); |
- this.onSpliceBound_ = this.onSplice_.bind(this); |
- this.onContentBound_ = this.onContentChange_.bind(this); |
- |
- // Unique numeric key, incremented per each load attempt used to discard |
- // old attempts. This can happen especially when changing selection fast or |
- // Internet connection is slow. |
- this.currentUniqueKey_ = 0; |
- |
- this.initListeners_(); |
- this.initDom_(); |
-} |
- |
-/** |
- * SlideMode extends cr.EventTarget. |
- */ |
-SlideMode.prototype.__proto__ = cr.EventTarget.prototype; |
- |
-/** |
- * List of available editor modes. |
- * @type {Array.<ImageEditor.Mode>} |
- */ |
-SlideMode.editorModes = [ |
- new ImageEditor.Mode.InstantAutofix(), |
- new ImageEditor.Mode.Crop(), |
- new ImageEditor.Mode.Exposure(), |
- new ImageEditor.Mode.OneClick( |
- 'rotate_left', 'GALLERY_ROTATE_LEFT', new Command.Rotate(-1)), |
- new ImageEditor.Mode.OneClick( |
- 'rotate_right', 'GALLERY_ROTATE_RIGHT', new Command.Rotate(1)) |
-]; |
- |
-/** |
- * @return {string} Mode name. |
- */ |
-SlideMode.prototype.getName = function() { return 'slide' }; |
- |
-/** |
- * @return {string} Mode title. |
- */ |
-SlideMode.prototype.getTitle = function() { return 'GALLERY_SLIDE' }; |
- |
-/** |
- * Initialize the listeners. |
- * @private |
- */ |
-SlideMode.prototype.initListeners_ = function() { |
- window.addEventListener('resize', this.onResize_.bind(this), false); |
-}; |
- |
-/** |
- * Initialize the UI. |
- * @private |
- */ |
-SlideMode.prototype.initDom_ = function() { |
- // Container for displayed image or video. |
- this.imageContainer_ = util.createChild( |
- this.document_.querySelector('.content'), 'image-container'); |
- this.imageContainer_.addEventListener('click', this.onClick_.bind(this)); |
- |
- this.document_.addEventListener('click', this.onDocumentClick_.bind(this)); |
- |
- // Overwrite options and info bubble. |
- this.options_ = util.createChild( |
- this.toolbar_.querySelector('.filename-spacer'), 'options'); |
- |
- this.savedLabel_ = util.createChild(this.options_, 'saved'); |
- this.savedLabel_.textContent = this.displayStringFunction_('GALLERY_SAVED'); |
- |
- var overwriteOriginalBox = |
- util.createChild(this.options_, 'overwrite-original'); |
- |
- this.overwriteOriginal_ = util.createChild( |
- overwriteOriginalBox, 'common white', 'input'); |
- this.overwriteOriginal_.type = 'checkbox'; |
- this.overwriteOriginal_.id = 'overwrite-checkbox'; |
- util.platform.getPreference(SlideMode.OVERWRITE_KEY, function(value) { |
- // Out-of-the box default is 'true' |
- this.overwriteOriginal_.checked = |
- (typeof value != 'string' || value == 'true'); |
- }.bind(this)); |
- this.overwriteOriginal_.addEventListener('click', |
- this.onOverwriteOriginalClick_.bind(this)); |
- |
- var overwriteLabel = util.createChild(overwriteOriginalBox, '', 'label'); |
- overwriteLabel.textContent = |
- this.displayStringFunction_('GALLERY_OVERWRITE_ORIGINAL'); |
- overwriteLabel.setAttribute('for', 'overwrite-checkbox'); |
- |
- this.bubble_ = util.createChild(this.toolbar_, 'bubble'); |
- this.bubble_.hidden = true; |
- |
- var bubbleContent = util.createChild(this.bubble_); |
- bubbleContent.innerHTML = this.displayStringFunction_( |
- 'GALLERY_OVERWRITE_BUBBLE'); |
- |
- util.createChild(this.bubble_, 'pointer bottom', 'span'); |
- |
- var bubbleClose = util.createChild(this.bubble_, 'close-x'); |
- bubbleClose.addEventListener('click', this.onCloseBubble_.bind(this)); |
- |
- // Video player controls. |
- this.mediaSpacer_ = |
- util.createChild(this.container_, 'video-controls-spacer'); |
- this.mediaToolbar_ = util.createChild(this.mediaSpacer_, 'tool'); |
- this.mediaControls_ = new VideoControls( |
- this.mediaToolbar_, |
- this.showErrorBanner_.bind(this, 'GALLERY_VIDEO_ERROR'), |
- this.displayStringFunction_.bind(this), |
- this.toggleFullScreen_.bind(this), |
- this.container_); |
- |
- // Ribbon and related controls. |
- this.arrowBox_ = util.createChild(this.container_, 'arrow-box'); |
- |
- this.arrowLeft_ = |
- util.createChild(this.arrowBox_, 'arrow left tool dimmable'); |
- this.arrowLeft_.addEventListener('click', |
- this.advanceManually.bind(this, -1)); |
- util.createChild(this.arrowLeft_); |
- |
- util.createChild(this.arrowBox_, 'arrow-spacer'); |
- |
- this.arrowRight_ = |
- util.createChild(this.arrowBox_, 'arrow right tool dimmable'); |
- this.arrowRight_.addEventListener('click', |
- this.advanceManually.bind(this, 1)); |
- 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'); |
- errorWrapper.setAttribute('pos', 'center'); |
- |
- this.errorBanner_ = util.createChild(errorWrapper, 'error-banner'); |
- |
- util.createChild(this.container_, 'spinner'); |
- |
- var slideShowButton = util.createChild(this.toolbar_, |
- 'button slideshow', 'button'); |
- slideShowButton.title = this.displayStringFunction_('GALLERY_SLIDESHOW'); |
- slideShowButton.addEventListener('click', |
- this.startSlideshow.bind(this, SlideMode.SLIDESHOW_INTERVAL_FIRST)); |
- |
- var slideShowToolbar = |
- util.createChild(this.container_, 'tool slideshow-toolbar'); |
- util.createChild(slideShowToolbar, 'slideshow-play'). |
- addEventListener('click', this.toggleSlideshowPause_.bind(this)); |
- util.createChild(slideShowToolbar, 'slideshow-end'). |
- addEventListener('click', this.stopSlideshow_.bind(this)); |
- |
- // Editor. |
- |
- this.editButton_ = util.createChild(this.toolbar_, 'button edit', 'button'); |
- this.editButton_.title = this.displayStringFunction_('GALLERY_EDIT'); |
- this.editButton_.setAttribute('disabled', ''); // Disabled by default. |
- this.editButton_.addEventListener('click', this.toggleEditor.bind(this)); |
- |
- this.printButton_ = util.createChild(this.toolbar_, 'button print', 'button'); |
- this.printButton_.title = this.displayStringFunction_('GALLERY_PRINT'); |
- this.printButton_.setAttribute('disabled', ''); // Disabled by default. |
- this.printButton_.addEventListener('click', this.print_.bind(this)); |
- |
- this.editBarSpacer_ = util.createChild(this.toolbar_, 'edit-bar-spacer'); |
- this.editBarMain_ = util.createChild(this.editBarSpacer_, 'edit-main'); |
- |
- this.editBarMode_ = util.createChild(this.container_, 'edit-modal'); |
- this.editBarModeWrapper_ = util.createChild( |
- this.editBarMode_, 'edit-modal-wrapper'); |
- this.editBarModeWrapper_.hidden = true; |
- |
- // Objects supporting image display and editing. |
- this.viewport_ = new Viewport(); |
- |
- this.imageView_ = new ImageView( |
- this.imageContainer_, |
- this.viewport_, |
- this.metadataCache_); |
- |
- this.editor_ = new ImageEditor( |
- this.viewport_, |
- this.imageView_, |
- this.prompt_, |
- { |
- root: this.container_, |
- image: this.imageContainer_, |
- toolbar: this.editBarMain_, |
- mode: this.editBarModeWrapper_ |
- }, |
- SlideMode.editorModes, |
- this.displayStringFunction_); |
- |
- this.editor_.getBuffer().addOverlay( |
- new SwipeOverlay(this.advanceManually.bind(this))); |
-}; |
- |
-/** |
- * Load items, display the selected item. |
- * @param {Rect} zoomFromRect Rectangle for zoom effect. |
- * @param {function} displayCallback Called when the image is displayed. |
- * @param {function} loadCallback Called when the image is displayed. |
- */ |
-SlideMode.prototype.enter = function( |
- zoomFromRect, displayCallback, loadCallback) { |
- this.sequenceDirection_ = 0; |
- this.sequenceLength_ = 0; |
- |
- var loadDone = function(loadType, delay) { |
- this.active_ = true; |
- |
- this.selectionModel_.addEventListener('change', this.onSelectionBound_); |
- this.dataModel_.addEventListener('splice', this.onSpliceBound_); |
- this.dataModel_.addEventListener('content', this.onContentBound_); |
- |
- ImageUtil.setAttribute(this.arrowBox_, 'active', this.getItemCount_() > 1); |
- this.ribbon_.enable(); |
- |
- // Wait 1000ms after the animation is done, then prefetch the next image. |
- this.requestPrefetch(1, delay + 1000); |
- |
- if (loadCallback) loadCallback(); |
- }.bind(this); |
- |
- // The latest |leave| call might have left the image animating. Remove it. |
- this.unloadImage_(); |
- |
- if (this.getItemCount_() == 0) { |
- this.displayedIndex_ = -1; |
- //TODO(kaznacheev) Show this message in the grid mode too. |
- this.showErrorBanner_('GALLERY_NO_IMAGES'); |
- loadDone(); |
- } else { |
- // Remember the selection if it is empty or multiple. It will be restored |
- // in |leave| if the user did not changing the selection manually. |
- var currentSelection = this.selectionModel_.selectedIndexes; |
- if (currentSelection.length == 1) |
- this.savedSelection_ = null; |
- else |
- this.savedSelection_ = currentSelection; |
- |
- // Ensure valid single selection. |
- // Note that the SlideMode object is not listening to selection change yet. |
- this.select(Math.max(0, this.getSelectedIndex())); |
- 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, |
- zoomFromRect && this.imageView_.createZoomEffect(zoomFromRect), |
- displayCallback, loadDone); |
- }.bind(this)); |
- |
- } |
-}; |
- |
-/** |
- * Leave the mode. |
- * @param {Rect} zoomToRect Rectangle for zoom effect. |
- * @param {function} callback Called when the image is committed and |
- * the zoom-out animation has started. |
- */ |
-SlideMode.prototype.leave = function(zoomToRect, callback) { |
- var commitDone = function() { |
- this.stopEditing_(); |
- this.stopSlideshow_(); |
- ImageUtil.setAttribute(this.arrowBox_, 'active', false); |
- this.selectionModel_.removeEventListener( |
- 'change', this.onSelectionBound_); |
- this.dataModel_.removeEventListener('splice', this.onSpliceBound_); |
- this.dataModel_.removeEventListener('content', this.onContentBound_); |
- this.ribbon_.disable(); |
- this.active_ = false; |
- if (this.savedSelection_) |
- this.selectionModel_.selectedIndexes = this.savedSelection_; |
- this.unloadImage_(zoomToRect); |
- callback(); |
- }.bind(this); |
- |
- if (this.getItemCount_() == 0) { |
- this.showErrorBanner_(false); |
- commitDone(); |
- } else { |
- this.commitItem_(commitDone); |
- } |
- |
- // Disable the slide-mode only buttons when leaving. |
- this.editButton_.setAttribute('disabled', ''); |
- this.printButton_.setAttribute('disabled', ''); |
-}; |
- |
- |
-/** |
- * Execute an action when the editor is not busy. |
- * |
- * @param {function} action Function to execute. |
- */ |
-SlideMode.prototype.executeWhenReady = function(action) { |
- this.editor_.executeWhenReady(action); |
-}; |
- |
-/** |
- * @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.getItemCount_ = function() { |
- return this.dataModel_.length; |
-}; |
- |
-/** |
- * @param {number} index Index. |
- * @return {Gallery.Item} Item. |
- */ |
-SlideMode.prototype.getItem = function(index) { |
- return this.dataModel_.item(index); |
-}; |
- |
-/** |
- * @return {Gallery.Item} Selected index. |
- */ |
-SlideMode.prototype.getSelectedIndex = function() { |
- return this.selectionModel_.selectedIndex; |
-}; |
- |
-/** |
- * @return {Rect} Screen rectangle of the selected image. |
- */ |
-SlideMode.prototype.getSelectedImageRect = function() { |
- if (this.getSelectedIndex() < 0) |
- return null; |
- else |
- return this.viewport_.getScreenClipped(); |
-}; |
- |
-/** |
- * @return {Gallery.Item} Selected item. |
- */ |
-SlideMode.prototype.getSelectedItem = function() { |
- return this.getItem(this.getSelectedIndex()); |
-}; |
- |
-/** |
- * Toggles the full screen mode. |
- * @private |
- */ |
-SlideMode.prototype.toggleFullScreen_ = function() { |
- util.toggleFullScreen(this.context_.appWindow, |
- !util.isFullScreen(this.context_.appWindow)); |
-}; |
- |
-/** |
- * Selection change handler. |
- * |
- * Commits the current image and displays the newly selected image. |
- * @private |
- */ |
-SlideMode.prototype.onSelection_ = function() { |
- if (this.selectionModel_.selectedIndexes.length == 0) |
- return; // Temporary empty selection. |
- |
- // Forget the saved selection if the user changed the selection manually. |
- if (!this.isSlideshowOn_()) |
- this.savedSelection_ = null; |
- |
- if (this.getSelectedIndex() == this.displayedIndex_) |
- return; // Do not reselect. |
- |
- this.commitItem_(this.loadSelectedItem_.bind(this)); |
-}; |
- |
-/** |
- * Change the selection. |
- * |
- * @param {number} index New selected index. |
- * @param {number=} opt_slideHint Slide animation direction (-1|1). |
- */ |
-SlideMode.prototype.select = function(index, opt_slideHint) { |
- this.slideHint_ = opt_slideHint; |
- this.selectionModel_.selectedIndex = index; |
- this.selectionModel_.leadIndex = index; |
-}; |
- |
-/** |
- * Load the selected item. |
- * |
- * @private |
- */ |
-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 = slideHint || (index - this.displayedIndex_); |
- |
- if (Math.abs(step) != 1) { |
- // Long leap, the sequence is broken, we have no good prefetch candidate. |
- this.sequenceDirection_ = 0; |
- this.sequenceLength_ = 0; |
- } else if (this.sequenceDirection_ == step) { |
- // Keeping going in sequence. |
- this.sequenceLength_++; |
- } else { |
- // Reversed the direction. Reset the counter. |
- this.sequenceDirection_ = step; |
- this.sequenceLength_ = 1; |
- } |
- |
- if (this.sequenceLength_ <= 1) { |
- // We have just broke the sequence. Touch the current image so that it stays |
- // in the cache longer. |
- this.imageView_.prefetch(this.imageView_.contentID_); |
- } |
- |
- this.displayedIndex_ = index; |
- |
- function shouldPrefetch(loadType, step, sequenceLength) { |
- // Never prefetch when selecting out of sequence. |
- if (Math.abs(step) != 1) |
- return false; |
- |
- // Never prefetch after a video load (decoding the next image can freeze |
- // the UI for a second or two). |
- if (loadType == ImageView.LOAD_TYPE_VIDEO_FILE) |
- return false; |
- |
- // Always prefetch if the previous load was from cache. |
- if (loadType == ImageView.LOAD_TYPE_CACHED_FULL) |
- return true; |
- |
- // Prefetch if we have been going in the same direction for long enough. |
- return sequenceLength >= 3; |
- } |
- |
- var selectedItem = this.getSelectedItem(); |
- this.currentUniqueKey_++; |
- var selectedUniqueKey = this.currentUniqueKey_; |
- var onMetadata = function(metadata) { |
- // Discard, since another load has been invoked after this one. |
- if (selectedUniqueKey != this.currentUniqueKey_) return; |
- this.loadItem_(selectedItem.getUrl(), metadata, |
- new ImageView.Effect.Slide(step, this.isSlideshowPlaying_()), |
- function() {} /* no displayCallback */, |
- function(loadType, delay) { |
- // Discard, since another load has been invoked after this one. |
- if (selectedUniqueKey != this.currentUniqueKey_) return; |
- if (shouldPrefetch(loadType, step, this.sequenceLength_)) { |
- this.requestPrefetch(step, delay); |
- } |
- if (this.isSlideshowPlaying_()) |
- this.scheduleNextSlide_(); |
- }.bind(this)); |
- }.bind(this); |
- this.metadataCache_.get( |
- selectedItem.getUrl(), Gallery.METADATA_TYPE, onMetadata); |
-}; |
- |
-/** |
- * Unload the current image. |
- * |
- * @param {Rect} zoomToRect Rectangle for zoom effect. |
- * @private |
- */ |
-SlideMode.prototype.unloadImage_ = function(zoomToRect) { |
- this.imageView_.unload(zoomToRect); |
- this.container_.removeAttribute('video'); |
-}; |
- |
-/** |
- * Data model 'splice' event handler. |
- * @param {Event} event Event. |
- * @private |
- */ |
-SlideMode.prototype.onSplice_ = function(event) { |
- ImageUtil.setAttribute(this.arrowBox_, 'active', this.getItemCount_() > 1); |
- |
- // Splice invalidates saved indices, drop the saved selection. |
- this.savedSelection_ = null; |
- |
- 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_ so that loadSelectedItem_ does not think |
- // we are re-selecting the same item (and does right-to-left slide-in |
- // animation). |
- 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_('GALLERY_NO_IMAGES'); |
- }.bind(this)); |
- } |
- }.bind(this), 0); |
-}; |
- |
-/** |
- * @param {number} direction -1 for left, 1 for right. |
- * @return {number} Next index in the given direction, with wrapping. |
- * @private |
- */ |
-SlideMode.prototype.getNextSelectedIndex_ = function(direction) { |
- function advance(index, limit) { |
- index += (direction > 0 ? 1 : -1); |
- if (index < 0) |
- return limit - 1; |
- if (index == limit) |
- return 0; |
- return index; |
- } |
- |
- // If the saved selection is multiple the Slideshow should cycle through |
- // the saved selection. |
- if (this.isSlideshowOn_() && |
- this.savedSelection_ && this.savedSelection_.length > 1) { |
- var pos = advance(this.savedSelection_.indexOf(this.getSelectedIndex()), |
- this.savedSelection_.length); |
- return this.savedSelection_[pos]; |
- } else { |
- return advance(this.getSelectedIndex(), this.getItemCount_()); |
- } |
-}; |
- |
-/** |
- * Advance the selection based on the pressed key ID. |
- * @param {string} keyID Key identifier. |
- */ |
-SlideMode.prototype.advanceWithKeyboard = function(keyID) { |
- this.advanceManually(keyID == 'Up' || keyID == 'Left' ? -1 : 1); |
-}; |
- |
-/** |
- * Advance the selection as a result of a user action (as opposed to an |
- * automatic change in the slideshow mode). |
- * @param {number} direction -1 for left, 1 for right. |
- */ |
-SlideMode.prototype.advanceManually = function(direction) { |
- if (this.isSlideshowPlaying_()) { |
- this.pauseSlideshow_(); |
- cr.dispatchSimpleEvent(this, 'useraction'); |
- } |
- this.selectNext(direction); |
-}; |
- |
-/** |
- * Select the next item. |
- * @param {number} direction -1 for left, 1 for right. |
- */ |
-SlideMode.prototype.selectNext = function(direction) { |
- this.select(this.getNextSelectedIndex_(direction), direction); |
-}; |
- |
-/** |
- * Select the first item. |
- */ |
-SlideMode.prototype.selectFirst = function() { |
- this.select(0); |
-}; |
- |
-/** |
- * Select the last item. |
- */ |
-SlideMode.prototype.selectLast = function() { |
- this.select(this.getItemCount_() - 1); |
-}; |
- |
-// Loading/unloading |
- |
-/** |
- * Load and display an item. |
- * |
- * @param {string} url Item url. |
- * @param {Object} metadata Item metadata. |
- * @param {Object} effect Transition effect object. |
- * @param {function} displayCallback Called when the image is displayed |
- * (which can happen before the image load due to caching). |
- * @param {function} loadCallback Called when the image is fully loaded. |
- * @private |
- */ |
-SlideMode.prototype.loadItem_ = function( |
- url, metadata, effect, displayCallback, loadCallback) { |
- this.selectedImageMetadata_ = MetadataCache.cloneMetadata(metadata); |
- |
- this.showSpinner_(true); |
- |
- var loadDone = function(loadType, delay, error) { |
- var video = this.isShowingVideo_(); |
- ImageUtil.setAttribute(this.container_, 'video', video); |
- |
- this.showSpinner_(false); |
- if (loadType == ImageView.LOAD_TYPE_ERROR) { |
- // if we have a specific error, then display it |
- if (error) { |
- this.showErrorBanner_('GALLERY_' + error); |
- } else { |
- // otherwise try to infer general error |
- this.showErrorBanner_( |
- video ? 'GALLERY_VIDEO_ERROR' : 'GALLERY_IMAGE_ERROR'); |
- } |
- } else if (loadType == ImageView.LOAD_TYPE_OFFLINE) { |
- this.showErrorBanner_( |
- video ? 'GALLERY_VIDEO_OFFLINE' : 'GALLERY_IMAGE_OFFLINE'); |
- } |
- |
- if (video) { |
- // 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 { |
- ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('View')); |
- |
- var toMillions = function(number) { |
- return Math.round(number / (1000 * 1000)); |
- }; |
- |
- ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MB'), |
- toMillions(metadata.filesystem.size)); |
- |
- var canvas = this.imageView_.getCanvas(); |
- ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MPix'), |
- toMillions(canvas.width * canvas.height)); |
- |
- var extIndex = url.lastIndexOf('.'); |
- var ext = extIndex < 0 ? '' : url.substr(extIndex + 1).toLowerCase(); |
- if (ext == 'jpeg') ext = 'jpg'; |
- ImageUtil.metrics.recordEnum( |
- ImageUtil.getMetricName('FileType'), ext, ImageUtil.FILE_TYPES); |
- } |
- |
- // Enable or disable buttons for editing and printing. |
- if (video || error) { |
- this.editButton_.setAttribute('disabled', ''); |
- this.printButton_.setAttribute('disabled', ''); |
- } else { |
- this.editButton_.removeAttribute('disabled'); |
- this.printButton_.removeAttribute('disabled'); |
- } |
- |
- // For once edited image, disallow the 'overwrite' setting change. |
- ImageUtil.setAttribute(this.options_, 'saved', |
- !this.getSelectedItem().isOriginal()); |
- |
- util.platform.getPreference(SlideMode.OVERWRITE_BUBBLE_KEY, |
- function(value) { |
- var times = typeof value == 'string' ? parseInt(value, 10) : 0; |
- if (times < SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { |
- this.bubble_.hidden = false; |
- if (this.isEditing()) { |
- util.platform.setPreference( |
- SlideMode.OVERWRITE_BUBBLE_KEY, times + 1); |
- } |
- } |
- }.bind(this)); |
- |
- loadCallback(loadType, delay); |
- }.bind(this); |
- |
- var displayDone = function() { |
- cr.dispatchSimpleEvent(this, 'image-displayed'); |
- displayCallback(); |
- }.bind(this); |
- |
- this.editor_.openSession(url, metadata, effect, |
- this.saveCurrentImage_.bind(this), displayDone, loadDone); |
-}; |
- |
-/** |
- * Commit changes to the current item and reset all messages/indicators. |
- * |
- * @param {function} callback Callback. |
- * @private |
- */ |
-SlideMode.prototype.commitItem_ = function(callback) { |
- this.showSpinner_(false); |
- this.showErrorBanner_(false); |
- this.editor_.getPrompt().hide(); |
- |
- // Detach any media attached to the controls. |
- if (this.mediaControls_.getMedia()) |
- this.mediaControls_.detachMedia(); |
- |
- // If showing the video, then pause it. Note, that it may not be attached |
- // to the media controls yet. |
- if (this.isShowingVideo_()) { |
- this.imageView_.getVideo().pause(); |
- // Force stop downloading, if uncached on Drive. |
- this.imageView_.getVideo().src = ''; |
- this.imageView_.getVideo().load(); |
- } |
- |
- this.editor_.closeSession(callback); |
-}; |
- |
-/** |
- * Request a prefetch for the next image. |
- * |
- * @param {number} direction -1 or 1. |
- * @param {number} delay Delay in ms. Used to prevent the CPU-heavy image |
- * loading from disrupting the animation that might be still in progress. |
- */ |
-SlideMode.prototype.requestPrefetch = function(direction, delay) { |
- if (this.getItemCount_() <= 1) return; |
- |
- var index = this.getNextSelectedIndex_(direction); |
- var nextItemUrl = this.getItem(index).getUrl(); |
- this.imageView_.prefetch(nextItemUrl, delay); |
-}; |
- |
-// Event handlers. |
- |
-/** |
- * Unload handler, to be called from the top frame. |
- * @param {boolean} exiting True if the app is exiting. |
- */ |
-SlideMode.prototype.onUnload = function(exiting) { |
- if (this.isShowingVideo_() && this.mediaControls_.isPlaying()) { |
- this.mediaControls_.savePosition(exiting); |
- } |
-}; |
- |
-/** |
- * beforeunload handler, to be called from the top frame. |
- * @return {string} Message to show if there are unsaved changes. |
- */ |
-SlideMode.prototype.onBeforeUnload = function() { |
- if (this.editor_.isBusy()) |
- return this.displayStringFunction_('GALLERY_UNSAVED_CHANGES'); |
- return null; |
-}; |
- |
-/** |
- * Click handler for the image container. |
- * |
- * @param {Event} event Mouse click event. |
- * @private |
- */ |
-SlideMode.prototype.onClick_ = function(event) { |
- if (!this.isShowingVideo_() || !this.mediaControls_.getMedia()) |
- return; |
- if (event.ctrlKey) { |
- this.mediaControls_.toggleLoopedModeWithFeedback(true); |
- if (!this.mediaControls_.isPlaying()) |
- this.mediaControls_.togglePlayStateWithFeedback(); |
- } else { |
- this.mediaControls_.togglePlayStateWithFeedback(); |
- } |
-}; |
- |
-/** |
- * Click handler for the entire document. |
- * @param {Event} e Mouse click event. |
- * @private |
- */ |
-SlideMode.prototype.onDocumentClick_ = function(e) { |
- // Close the bubble if clicked outside of it and if it is visible. |
- if (!this.bubble_.contains(e.target) && |
- !this.editButton_.contains(e.target) && |
- !this.arrowLeft_.contains(e.target) && |
- !this.arrowRight_.contains(e.target) && |
- !this.bubble_.hidden) { |
- this.bubble_.hidden = true; |
- } |
-}; |
- |
-/** |
- * Keydown handler. |
- * |
- * @param {Event} event Event. |
- * @return {boolean} True if handled. |
- */ |
-SlideMode.prototype.onKeyDown = function(event) { |
- var keyID = util.getKeyModifiers(event) + event.keyIdentifier; |
- |
- if (this.isSlideshowOn_()) { |
- switch (keyID) { |
- case 'U+001B': // Escape exits the slideshow. |
- this.stopSlideshow_(event); |
- break; |
- |
- case 'U+0020': // Space pauses/resumes the slideshow. |
- this.toggleSlideshowPause_(); |
- break; |
- |
- case 'Up': |
- case 'Down': |
- case 'Left': |
- case 'Right': |
- this.advanceWithKeyboard(keyID); |
- break; |
- } |
- return true; // Consume all keystrokes in the slideshow mode. |
- } |
- |
- if (this.isEditing() && this.editor_.onKeyDown(event)) |
- return true; |
- |
- switch (keyID) { |
- case 'U+0020': // Space toggles the video playback. |
- if (this.isShowingVideo_() && this.mediaControls_.getMedia()) |
- this.mediaControls_.togglePlayStateWithFeedback(); |
- break; |
- |
- case 'Ctrl-U+0050': // Ctrl+'p' prints the current image. |
- if (!this.printButton_.hasAttribute('disabled')) |
- this.print_(); |
- break; |
- |
- case 'U+0045': // 'e' toggles the editor. |
- if (!this.editButton_.hasAttribute('disabled')) |
- this.toggleEditor(event); |
- break; |
- |
- case 'U+001B': // Escape |
- if (!this.isEditing()) |
- return false; // Not handled. |
- this.toggleEditor(event); |
- break; |
- |
- case 'Home': |
- this.selectFirst(); |
- break; |
- case 'End': |
- this.selectLast(); |
- break; |
- case 'Up': |
- case 'Down': |
- case 'Left': |
- case 'Right': |
- this.advanceWithKeyboard(keyID); |
- break; |
- |
- default: return false; |
- } |
- |
- return true; |
-}; |
- |
-/** |
- * Resize handler. |
- * @private |
- */ |
-SlideMode.prototype.onResize_ = function() { |
- this.viewport_.sizeByFrameAndFit(this.container_); |
- this.viewport_.repaint(); |
-}; |
- |
-/** |
- * Update thumbnails. |
- */ |
-SlideMode.prototype.updateThumbnails = function() { |
- this.ribbon_.reset(); |
- if (this.active_) |
- this.ribbon_.redraw(); |
-}; |
- |
-// Saving |
- |
-/** |
- * Save the current image to a file. |
- * |
- * @param {function} callback Callback. |
- * @private |
- */ |
-SlideMode.prototype.saveCurrentImage_ = function(callback) { |
- var item = this.getSelectedItem(); |
- var oldUrl = item.getUrl(); |
- var canvas = this.imageView_.getCanvas(); |
- |
- this.showSpinner_(true); |
- var metadataEncoder = ImageEncoder.encodeMetadata( |
- this.selectedImageMetadata_.media, canvas, 1 /* quality */); |
- |
- this.selectedImageMetadata_ = ContentProvider.ConvertContentMetadata( |
- metadataEncoder.getMetadata(), this.selectedImageMetadata_); |
- |
- item.saveToFile( |
- this.context_.saveDirEntry, |
- this.shouldOverwriteOriginal_(), |
- canvas, |
- metadataEncoder, |
- function(success) { |
- // TODO(kaznacheev): Implement write error handling. |
- // Until then pretend that the save succeeded. |
- this.showSpinner_(false); |
- this.flashSavedLabel_(); |
- |
- var e = new Event('content'); |
- e.item = item; |
- e.oldUrl = oldUrl; |
- e.metadata = this.selectedImageMetadata_; |
- this.dataModel_.dispatchEvent(e); |
- |
- // Allow changing the 'Overwrite original' setting only if the user |
- // used Undo to restore the original image AND it is not a copy. |
- // Otherwise lock the setting in its current state. |
- var mayChangeOverwrite = !this.editor_.canUndo() && item.isOriginal(); |
- ImageUtil.setAttribute(this.options_, 'saved', !mayChangeOverwrite); |
- |
- if (this.imageView_.getContentRevision() == 1) { // First edit. |
- ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('Edit')); |
- } |
- |
- if (oldUrl != item.getUrl()) { |
- this.dataModel_.splice( |
- this.getSelectedIndex(), 0, new Gallery.Item(oldUrl)); |
- // The ribbon will ignore the splice above and redraw after the |
- // select call below (while being obscured by the Editor toolbar, |
- // so there is no need for nice animation here). |
- // SlideMode will ignore the selection change as the displayed item |
- // index has not changed. |
- this.select(++this.displayedIndex_); |
- } |
- callback(); |
- cr.dispatchSimpleEvent(this, 'image-saved'); |
- }.bind(this)); |
-}; |
- |
-/** |
- * Update caches when the selected item url has changed. |
- * |
- * @param {Event} event Event. |
- * @private |
- */ |
-SlideMode.prototype.onContentChange_ = function(event) { |
- var newUrl = event.item.getUrl(); |
- if (newUrl != event.oldUrl) { |
- this.imageView_.changeUrl(newUrl); |
- } |
- this.metadataCache_.clear(event.oldUrl, Gallery.METADATA_TYPE); |
-}; |
- |
-/** |
- * Flash 'Saved' label briefly to indicate that the image has been saved. |
- * @private |
- */ |
-SlideMode.prototype.flashSavedLabel_ = function() { |
- var setLabelHighlighted = |
- ImageUtil.setAttribute.bind(null, this.savedLabel_, 'highlighted'); |
- setTimeout(setLabelHighlighted.bind(null, true), 0); |
- setTimeout(setLabelHighlighted.bind(null, false), 300); |
-}; |
- |
-/** |
- * Local storage key for the 'Overwrite original' setting. |
- * @type {string} |
- */ |
-SlideMode.OVERWRITE_KEY = 'gallery-overwrite-original'; |
- |
-/** |
- * Local storage key for the number of times that |
- * the overwrite info bubble has been displayed. |
- * @type {string} |
- */ |
-SlideMode.OVERWRITE_BUBBLE_KEY = 'gallery-overwrite-bubble'; |
- |
-/** |
- * Max number that the overwrite info bubble is shown. |
- * @type {number} |
- */ |
-SlideMode.OVERWRITE_BUBBLE_MAX_TIMES = 5; |
- |
-/** |
- * @return {boolean} True if 'Overwrite original' is set. |
- * @private |
- */ |
-SlideMode.prototype.shouldOverwriteOriginal_ = function() { |
- return this.overwriteOriginal_.checked; |
-}; |
- |
-/** |
- * 'Overwrite original' checkbox handler. |
- * @param {Event} event Event. |
- * @private |
- */ |
-SlideMode.prototype.onOverwriteOriginalClick_ = function(event) { |
- util.platform.setPreference(SlideMode.OVERWRITE_KEY, event.target.checked); |
-}; |
- |
-/** |
- * Overwrite info bubble close handler. |
- * @private |
- */ |
-SlideMode.prototype.onCloseBubble_ = function() { |
- this.bubble_.hidden = true; |
- util.platform.setPreference(SlideMode.OVERWRITE_BUBBLE_KEY, |
- SlideMode.OVERWRITE_BUBBLE_MAX_TIMES); |
-}; |
- |
-// Slideshow |
- |
-/** |
- * Slideshow interval in ms. |
- */ |
-SlideMode.SLIDESHOW_INTERVAL = 5000; |
- |
-/** |
- * 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; |
- |
-/** |
- * Empirically determined duration of the fullscreen toggle animation. |
- */ |
-SlideMode.FULLSCREEN_TOGGLE_DELAY = 500; |
- |
-/** |
- * @return {boolean} True if the slideshow is on. |
- * @private |
- */ |
-SlideMode.prototype.isSlideshowOn_ = function() { |
- return this.container_.hasAttribute('slideshow'); |
-}; |
- |
-/** |
- * Start the slideshow. |
- * @param {number=} opt_interval First interval in ms. |
- * @param {Event=} opt_event Event. |
- */ |
-SlideMode.prototype.startSlideshow = function(opt_interval, opt_event) { |
- // Set the attribute early to prevent the toolbar from flashing when |
- // the slideshow is being started from the mosaic view. |
- this.container_.setAttribute('slideshow', 'playing'); |
- |
- if (this.active_) { |
- this.stopEditing_(); |
- } else { |
- // We are in the Mosaic mode. Toggle the mode but remember to return. |
- this.leaveAfterSlideshow_ = true; |
- this.toggleMode_(this.startSlideshow.bind( |
- this, SlideMode.SLIDESHOW_INTERVAL, opt_event)); |
- return; |
- } |
- |
- if (opt_event) // Caused by user action, notify the Gallery. |
- cr.dispatchSimpleEvent(this, 'useraction'); |
- |
- this.fullscreenBeforeSlideshow_ = util.isFullScreen(this.context_.appWindow); |
- if (!this.fullscreenBeforeSlideshow_) { |
- // Wait until the zoom animation from the mosaic mode is done. |
- setTimeout(this.toggleFullScreen_.bind(this), |
- ImageView.ZOOM_ANIMATION_DURATION); |
- opt_interval = (opt_interval || SlideMode.SLIDESHOW_INTERVAL) + |
- SlideMode.FULLSCREEN_TOGGLE_DELAY; |
- } |
- |
- this.resumeSlideshow_(opt_interval); |
-}; |
- |
-/** |
- * Stop the slideshow. |
- * @param {Event=} opt_event Event. |
- * @private |
- */ |
-SlideMode.prototype.stopSlideshow_ = function(opt_event) { |
- if (!this.isSlideshowOn_()) |
- return; |
- |
- if (opt_event) // Caused by user action, notify the Gallery. |
- cr.dispatchSimpleEvent(this, 'useraction'); |
- |
- this.pauseSlideshow_(); |
- this.container_.removeAttribute('slideshow'); |
- |
- // Do not restore fullscreen if we exited fullscreen while in slideshow. |
- var fullscreen = util.isFullScreen(this.context_.appWindow); |
- var toggleModeDelay = 0; |
- if (!this.fullscreenBeforeSlideshow_ && fullscreen) { |
- this.toggleFullScreen_(); |
- toggleModeDelay = SlideMode.FULLSCREEN_TOGGLE_DELAY; |
- } |
- if (this.leaveAfterSlideshow_) { |
- this.leaveAfterSlideshow_ = false; |
- setTimeout(this.toggleMode_.bind(this), toggleModeDelay); |
- } |
-}; |
- |
-/** |
- * @return {boolean} True if the slideshow is playing (not paused). |
- * @private |
- */ |
-SlideMode.prototype.isSlideshowPlaying_ = function() { |
- return this.container_.getAttribute('slideshow') == 'playing'; |
-}; |
- |
-/** |
- * Pause/resume the slideshow. |
- * @private |
- */ |
-SlideMode.prototype.toggleSlideshowPause_ = function() { |
- cr.dispatchSimpleEvent(this, 'useraction'); // Show the tools. |
- if (this.isSlideshowPlaying_()) { |
- this.pauseSlideshow_(); |
- } else { |
- this.resumeSlideshow_(SlideMode.SLIDESHOW_INTERVAL_FIRST); |
- } |
-}; |
- |
-/** |
- * @param {number=} opt_interval Slideshow interval in ms. |
- * @private |
- */ |
-SlideMode.prototype.scheduleNextSlide_ = function(opt_interval) { |
- console.assert(this.isSlideshowPlaying_(), 'Inconsistent slideshow state'); |
- |
- if (this.slideShowTimeout_) |
- clearTimeout(this.slideShowTimeout_); |
- |
- this.slideShowTimeout_ = setTimeout(function() { |
- this.slideShowTimeout_ = null; |
- this.selectNext(1); |
- }.bind(this), |
- opt_interval || SlideMode.SLIDESHOW_INTERVAL); |
-}; |
- |
-/** |
- * Resume the slideshow. |
- * @param {number=} opt_interval Slideshow interval in ms. |
- * @private |
- */ |
-SlideMode.prototype.resumeSlideshow_ = function(opt_interval) { |
- this.container_.setAttribute('slideshow', 'playing'); |
- this.scheduleNextSlide_(opt_interval); |
-}; |
- |
-/** |
- * Pause the slideshow. |
- * @private |
- */ |
-SlideMode.prototype.pauseSlideshow_ = function() { |
- this.container_.setAttribute('slideshow', 'paused'); |
- if (this.slideShowTimeout_) { |
- clearTimeout(this.slideShowTimeout_); |
- this.slideShowTimeout_ = null; |
- } |
-}; |
- |
-/** |
- * @return {boolean} True if the editor is active. |
- */ |
-SlideMode.prototype.isEditing = function() { |
- return this.container_.hasAttribute('editing'); |
-}; |
- |
-/** |
- * Stop editing. |
- * @private |
- */ |
-SlideMode.prototype.stopEditing_ = function() { |
- if (this.isEditing()) |
- this.toggleEditor(); |
-}; |
- |
-/** |
- * Activate/deactivate editor. |
- * @param {Event=} opt_event Event. |
- */ |
-SlideMode.prototype.toggleEditor = function(opt_event) { |
- if (opt_event) // Caused by user action, notify the Gallery. |
- cr.dispatchSimpleEvent(this, 'useraction'); |
- |
- if (!this.active_) { |
- this.toggleMode_(this.toggleEditor.bind(this)); |
- return; |
- } |
- |
- this.stopSlideshow_(); |
- if (!this.isEditing() && this.isShowingVideo_()) |
- return; // No editing for videos. |
- |
- ImageUtil.setAttribute(this.container_, 'editing', !this.isEditing()); |
- |
- if (this.isEditing()) { // isEditing has just been flipped to a new value. |
- if (this.context_.readonlyDirName) { |
- this.editor_.getPrompt().showAt( |
- 'top', 'readonly_warning', 0, this.context_.readonlyDirName); |
- } |
- } else { |
- this.editor_.getPrompt().hide(); |
- this.editor_.leaveModeGently(); |
- } |
-}; |
- |
-/** |
- * Prints the current item. |
- * @private |
- */ |
-SlideMode.prototype.print_ = function() { |
- cr.dispatchSimpleEvent(this, 'useraction'); |
- window.print(); |
-}; |
- |
-/** |
- * Display the error banner. |
- * @param {string} message Message. |
- * @private |
- */ |
-SlideMode.prototype.showErrorBanner_ = function(message) { |
- if (message) { |
- this.errorBanner_.textContent = this.displayStringFunction_(message); |
- } |
- ImageUtil.setAttribute(this.container_, 'error', !!message); |
-}; |
- |
-/** |
- * Show/hide the busy spinner. |
- * |
- * @param {boolean} on True if show, false if hide. |
- * @private |
- */ |
-SlideMode.prototype.showSpinner_ = function(on) { |
- if (this.spinnerTimer_) { |
- clearTimeout(this.spinnerTimer_); |
- this.spinnerTimer_ = null; |
- } |
- |
- if (on) { |
- this.spinnerTimer_ = setTimeout(function() { |
- this.spinnerTimer_ = null; |
- ImageUtil.setAttribute(this.container_, 'spinner', true); |
- }.bind(this), 1000); |
- } else { |
- ImageUtil.setAttribute(this.container_, 'spinner', false); |
- } |
-}; |
- |
-/** |
- * @return {boolean} True if the current item is a video. |
- * @private |
- */ |
-SlideMode.prototype.isShowingVideo_ = function() { |
- return !!this.imageView_.getVideo(); |
-}; |
- |
-/** |
- * Overlay that handles swipe gestures. Changes to the next or previous file. |
- * @param {function(number)} callback A callback accepting the swipe direction |
- * (1 means left, -1 right). |
- * @constructor |
- * @implements {ImageBuffer.Overlay} |
- */ |
-function SwipeOverlay(callback) { |
- this.callback_ = callback; |
-} |
- |
-/** |
- * Inherit ImageBuffer.Overlay. |
- */ |
-SwipeOverlay.prototype.__proto__ = ImageBuffer.Overlay.prototype; |
- |
-/** |
- * @param {number} x X pointer position. |
- * @param {number} y Y pointer position. |
- * @param {boolean} touch True if dragging caused by touch. |
- * @return {function} The closure to call on drag. |
- */ |
-SwipeOverlay.prototype.getDragHandler = function(x, y, touch) { |
- if (!touch) |
- return null; |
- var origin = x; |
- var done = false; |
- return function(x, y) { |
- if (!done && origin - x > SwipeOverlay.SWIPE_THRESHOLD) { |
- this.callback_(1); |
- done = true; |
- } else if (!done && x - origin > SwipeOverlay.SWIPE_THRESHOLD) { |
- this.callback_(-1); |
- done = true; |
- } |
- }.bind(this); |
-}; |
- |
-/** |
- * If the user touched the image and moved the finger more than SWIPE_THRESHOLD |
- * horizontally it's considered as a swipe gesture (change the current image). |
- */ |
-SwipeOverlay.SWIPE_THRESHOLD = 100; |