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_. | |
dgozman
2012/08/27 15:07:43
I think, this is done for nice animation? Needs a
| |
453 this.displayedIndex_ = event.index - 1; | |
454 this.select(event.index); | |
455 } else if (this.dataModel_.length) { | |
456 // Removed item is the rightmost, but there are more items. | |
457 this.select(event.index - 1); // Select the new last index. | |
458 } else { | |
459 // No items left. Unload the image and show the banner. | |
460 this.commitItem_(function() { | |
461 this.unloadImage_(); | |
462 this.showErrorBanner_('NO_IMAGES'); | |
463 }.bind(this)); | |
464 } | |
465 }.bind(this), 0); | |
466 }; | |
467 | |
468 /** | |
356 * @param {number} direction -1 for left, 1 for right. | 469 * @param {number} direction -1 for left, 1 for right. |
357 * @return {number} Next index in the gived direction, with wrapping. | 470 * @return {number} Next index in the gived direction, with wrapping. |
358 * @private | 471 * @private |
359 */ | 472 */ |
360 SlideMode.prototype.getNextSelectedIndex_ = function(direction) { | 473 SlideMode.prototype.getNextSelectedIndex_ = function(direction) { |
361 var index = this.selectedIndex_ + (direction > 0 ? 1 : -1); | 474 var index = this.getSelectedIndex() + (direction > 0 ? 1 : -1); |
362 if (index == -1) return this.items_.length - 1; | 475 if (index == -1) return this.getItemCount_() - 1; |
363 if (index == this.items_.length) return 0; | 476 if (index == this.getItemCount_()) return 0; |
364 return index; | 477 return index; |
365 }; | 478 }; |
366 | 479 |
367 /** | 480 /** |
368 * Select the next item. | 481 * Select the next item. |
369 * @param {number} direction -1 for left, 1 for right. | 482 * @param {number} direction -1 for left, 1 for right. |
370 * @param {function} opt_callback Callback. | |
371 */ | 483 */ |
372 SlideMode.prototype.selectNext = function(direction, opt_callback) { | 484 SlideMode.prototype.selectNext = function(direction) { |
373 this.select(this.getNextSelectedIndex_(direction), direction, opt_callback); | 485 this.select(this.getNextSelectedIndex_(direction), direction); |
374 }; | 486 }; |
375 | 487 |
376 /** | 488 /** |
377 * Select the first item. | 489 * Select the first item. |
378 */ | 490 */ |
379 SlideMode.prototype.selectFirst = function() { | 491 SlideMode.prototype.selectFirst = function() { |
380 this.select(0); | 492 this.select(0); |
381 }; | 493 }; |
382 | 494 |
383 /** | 495 /** |
384 * Select the last item. | 496 * Select the last item. |
385 */ | 497 */ |
386 SlideMode.prototype.selectLast = function() { | 498 SlideMode.prototype.selectLast = function() { |
387 this.select(this.items_.length - 1); | 499 this.select(this.getItemCount_() - 1); |
388 }; | 500 }; |
389 | 501 |
390 // Loading/unloading | 502 // Loading/unloading |
391 | 503 |
392 /** | 504 /** |
393 * Load and display an item. | 505 * Load and display an item. |
394 * | 506 * |
395 * @param {string} url Item url. | 507 * @param {string} url Item url. |
396 * @param {Object} metadata Item metadata. | 508 * @param {Object} metadata Item metadata. |
397 * @param {number} slide Slide animation direction (-1|0|1). | 509 * @param {number} slide Slide animation direction (-1|0|1). |
(...skipping 10 matching lines...) Expand all Loading... | |
408 ImageUtil.setAttribute(this.container_, 'video', video); | 520 ImageUtil.setAttribute(this.container_, 'video', video); |
409 | 521 |
410 this.showSpinner_(false); | 522 this.showSpinner_(false); |
411 if (loadType == ImageView.LOAD_TYPE_ERROR) { | 523 if (loadType == ImageView.LOAD_TYPE_ERROR) { |
412 this.showErrorBanner_(video ? 'VIDEO_ERROR' : 'IMAGE_ERROR'); | 524 this.showErrorBanner_(video ? 'VIDEO_ERROR' : 'IMAGE_ERROR'); |
413 } else if (loadType == ImageView.LOAD_TYPE_OFFLINE) { | 525 } else if (loadType == ImageView.LOAD_TYPE_OFFLINE) { |
414 this.showErrorBanner_(video ? 'VIDEO_OFFLINE' : 'IMAGE_OFFLINE'); | 526 this.showErrorBanner_(video ? 'VIDEO_OFFLINE' : 'IMAGE_OFFLINE'); |
415 } | 527 } |
416 | 528 |
417 if (video) { | 529 if (video) { |
418 if (this.isEditing()) { | 530 // The editor toolbar does not make sense for video, hide it. |
419 // The editor toolbar does not make sense for video, hide it. | 531 this.stopEditing_(); |
420 this.onEdit_(); | |
421 } | |
422 this.mediaControls_.attachMedia(this.imageView_.getVideo()); | 532 this.mediaControls_.attachMedia(this.imageView_.getVideo()); |
423 //TODO(kaznacheev): Add metrics for video playback. | 533 //TODO(kaznacheev): Add metrics for video playback. |
424 } else { | 534 } else { |
425 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('View')); | 535 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('View')); |
426 | 536 |
427 function toMillions(number) { return Math.round(number / (1000 * 1000)) } | 537 function toMillions(number) { return Math.round(number / (1000 * 1000)) } |
428 | 538 |
429 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MB'), | 539 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MB'), |
430 toMillions(metadata.filesystem.size)); | 540 toMillions(metadata.filesystem.size)); |
431 | 541 |
432 var canvas = this.imageView_.getCanvas(); | 542 var canvas = this.imageView_.getCanvas(); |
433 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MPix'), | 543 ImageUtil.metrics.recordSmallCount(ImageUtil.getMetricName('Size.MPix'), |
434 toMillions(canvas.width * canvas.height)); | 544 toMillions(canvas.width * canvas.height)); |
435 | 545 |
436 var extIndex = url.lastIndexOf('.'); | 546 var extIndex = url.lastIndexOf('.'); |
437 var ext = extIndex < 0 ? '' : url.substr(extIndex + 1).toLowerCase(); | 547 var ext = extIndex < 0 ? '' : url.substr(extIndex + 1).toLowerCase(); |
438 if (ext == 'jpeg') ext = 'jpg'; | 548 if (ext == 'jpeg') ext = 'jpg'; |
439 ImageUtil.metrics.recordEnum( | 549 ImageUtil.metrics.recordEnum( |
440 ImageUtil.getMetricName('FileType'), ext, ImageUtil.FILE_TYPES); | 550 ImageUtil.getMetricName('FileType'), ext, ImageUtil.FILE_TYPES); |
441 } | 551 } |
442 | 552 |
553 // For once edited image, disallow the 'overwrite' setting change. | |
554 ImageUtil.setAttribute(this.options_, 'saved', | |
555 !this.getSelectedItem().isOriginal()); | |
556 | |
557 var times = SlideMode.OVERWRITE_BUBBLE_KEY in localStorage ? | |
558 parseInt(localStorage[SlideMode.OVERWRITE_BUBBLE_KEY], 10) : 0; | |
559 if (times < SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { | |
560 this.bubble_.hidden = false; | |
561 if (this.isEditing()) { | |
562 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = times + 1; | |
563 } | |
564 } | |
565 | |
443 callback(loadType); | 566 callback(loadType); |
444 }.bind(this); | 567 }.bind(this); |
445 | 568 |
446 this.editor_.openSession( | 569 this.editor_.openSession( |
447 url, metadata, slide, this.saveCurrentImage_.bind(this), loadDone); | 570 url, metadata, slide, this.saveCurrentImage_.bind(this), loadDone); |
448 }; | 571 }; |
449 | 572 |
450 /** | 573 /** |
451 * Commit changes to the current item and reset all messages/indicators. | 574 * Commit changes to the current item and reset all messages/indicators. |
452 * | 575 * |
(...skipping 10 matching lines...) Expand all Loading... | |
463 } | 586 } |
464 this.editor_.closeSession(callback); | 587 this.editor_.closeSession(callback); |
465 }; | 588 }; |
466 | 589 |
467 /** | 590 /** |
468 * Request a prefetch for the next image. | 591 * Request a prefetch for the next image. |
469 * | 592 * |
470 * @param {number} direction -1 or 1. | 593 * @param {number} direction -1 or 1. |
471 */ | 594 */ |
472 SlideMode.prototype.requestPrefetch = function(direction) { | 595 SlideMode.prototype.requestPrefetch = function(direction) { |
473 if (this.items_.length <= 1) return; | 596 if (this.getItemCount_() <= 1) return; |
474 | 597 |
475 var index = this.getNextSelectedIndex_(direction); | 598 var index = this.getNextSelectedIndex_(direction); |
476 var nextItemUrl = this.items_[index].getUrl(); | 599 var nextItemUrl = this.getItem(index).getUrl(); |
477 | 600 |
478 var selectedItem = this.getSelectedItem(); | 601 var selectedItem = this.getSelectedItem(); |
479 this.metadataCache_.get(nextItemUrl, Gallery.METADATA_TYPE, | 602 this.metadataCache_.get(nextItemUrl, Gallery.METADATA_TYPE, |
480 function(metadata) { | 603 function(metadata) { |
481 if (selectedItem != this.getSelectedItem()) return; | 604 if (selectedItem != this.getSelectedItem()) return; |
482 this.editor_.prefetchImage(nextItemUrl); | 605 this.editor_.prefetchImage(nextItemUrl); |
483 }.bind(this)); | 606 }.bind(this)); |
484 }; | 607 }; |
485 | 608 |
486 // Event handlers. | 609 // Event handlers. |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
534 return true; | 657 return true; |
535 | 658 |
536 switch (util.getKeyModifiers(event) + event.keyIdentifier) { | 659 switch (util.getKeyModifiers(event) + event.keyIdentifier) { |
537 case 'U+0020': // Space toggles the video playback. | 660 case 'U+0020': // Space toggles the video playback. |
538 if (this.isShowingVideo_()) { | 661 if (this.isShowingVideo_()) { |
539 this.mediaControls_.togglePlayStateWithFeedback(); | 662 this.mediaControls_.togglePlayStateWithFeedback(); |
540 } | 663 } |
541 break; | 664 break; |
542 | 665 |
543 case 'U+0045': // 'e' toggles the editor | 666 case 'U+0045': // 'e' toggles the editor |
544 this.onEdit_(); | 667 this.toggleEditor_(); |
545 break; | 668 break; |
546 | 669 |
547 case 'U+001B': // Escape | 670 case 'U+001B': // Escape |
548 if (!this.isEditing()) | 671 if (!this.isEditing()) |
549 return false; // Not handled. | 672 return false; // Not handled. |
550 this.onEdit_(); | 673 this.toggleEditor_(); |
551 break; | 674 break; |
552 | 675 |
553 case 'Ctrl-U+00DD': // Ctrl+] (cryptic on purpose). | 676 case 'Ctrl-U+00DD': // Ctrl+]. TODO(kaznacheev): Find a non-cryptic key. |
554 this.toggleSlideshow_(); | 677 this.toggleSlideshow_(SlideMode.SLIDESHOW_INTERVAL_FIRST); |
555 break; | 678 break; |
556 | 679 |
557 case 'Home': | 680 case 'Home': |
558 this.selectFirst(); | 681 this.selectFirst(); |
559 break; | 682 break; |
560 case 'End': | 683 case 'End': |
561 this.selectLast(); | 684 this.selectLast(); |
562 break; | 685 break; |
563 case 'Left': | 686 case 'Left': |
564 this.selectNext(-1); | 687 this.selectNext(-1); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
607 this.shouldOverwriteOriginal_(), | 730 this.shouldOverwriteOriginal_(), |
608 canvas, | 731 canvas, |
609 metadataEncoder, | 732 metadataEncoder, |
610 function(success) { | 733 function(success) { |
611 // TODO(kaznacheev): Implement write error handling. | 734 // TODO(kaznacheev): Implement write error handling. |
612 // Until then pretend that the save succeeded. | 735 // Until then pretend that the save succeeded. |
613 this.showSpinner_(false); | 736 this.showSpinner_(false); |
614 this.flashSavedLabel_(); | 737 this.flashSavedLabel_(); |
615 var newUrl = item.getUrl(); | 738 var newUrl = item.getUrl(); |
616 this.updateSelectedUrl_(oldUrl, newUrl); | 739 this.updateSelectedUrl_(oldUrl, newUrl); |
617 this.ribbon_.updateThumbnail( | 740 this.ribbon_.updateThumbnail(newUrl, this.selectedImageMetadata_); |
618 this.selectedIndex_, newUrl, this.selectedImageMetadata_); | 741 cr.dispatchSimpleEvent(this, 'content'); |
742 | |
743 if (this.imageView_.getContentRevision() == 1) { // First edit. | |
744 // Lock the 'Overwrite original' checkbox for this item. | |
745 ImageUtil.setAttribute(this.options_, 'saved', true); | |
746 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('Edit')); | |
747 } | |
748 | |
749 if (oldUrl != newUrl) { | |
750 this.displayedIndex_++; | |
751 // This splice call will change the selection change event. SlideMode | |
752 // will ignore it as the selected item is the same. | |
753 // The ribbon will redraw while being obscured by the Editor toolbar, | |
754 // so there is no need for nice animation here. | |
755 this.dataModel_.splice( | |
756 this.getSelectedIndex(), 0, new Gallery.Item(oldUrl)); | |
757 } | |
619 callback(); | 758 callback(); |
620 }.bind(this)); | 759 }.bind(this)); |
621 }; | 760 }; |
622 | 761 |
623 /** | 762 /** |
624 * Update caches when the selected item url has changed. | 763 * Update caches when the selected item url has changed. |
625 * | 764 * |
626 * @param {string} oldUrl Old url. | 765 * @param {string} oldUrl Old url. |
627 * @param {string} newUrl New url. | 766 * @param {string} newUrl New url. |
628 * @private | 767 * @private |
629 */ | 768 */ |
630 SlideMode.prototype.updateSelectedUrl_ = function(oldUrl, newUrl) { | 769 SlideMode.prototype.updateSelectedUrl_ = function(oldUrl, newUrl) { |
631 this.metadataCache_.clear(oldUrl, Gallery.METADATA_TYPE); | 770 this.metadataCache_.clear(oldUrl, Gallery.METADATA_TYPE); |
632 | 771 |
633 if (oldUrl == newUrl) | 772 if (oldUrl == newUrl) |
634 return; | 773 return; |
635 | 774 |
636 this.imageView_.changeUrl(newUrl); | 775 this.imageView_.changeUrl(newUrl); |
637 this.ribbon_.remapCache(oldUrl, newUrl); | 776 this.ribbon_.remapCache(oldUrl, newUrl); |
638 | |
639 // Let the gallery know that the selected item url has changed. | |
640 cr.dispatchSimpleEvent(this, 'selection'); | |
641 }; | 777 }; |
642 | 778 |
643 /** | 779 /** |
644 * Flash 'Saved' label briefly to indicate that the image has been saved. | 780 * Flash 'Saved' label briefly to indicate that the image has been saved. |
645 * @private | 781 * @private |
646 */ | 782 */ |
647 SlideMode.prototype.flashSavedLabel_ = function() { | 783 SlideMode.prototype.flashSavedLabel_ = function() { |
648 var setLabelHighlighted = | 784 var setLabelHighlighted = |
649 ImageUtil.setAttribute.bind(null, this.savedLabel_, 'highlighted'); | 785 ImageUtil.setAttribute.bind(null, this.savedLabel_, 'highlighted'); |
650 setTimeout(setLabelHighlighted.bind(null, true), 0); | 786 setTimeout(setLabelHighlighted.bind(null, true), 0); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
691 /** | 827 /** |
692 * Overwrite info bubble close handler. | 828 * Overwrite info bubble close handler. |
693 * @private | 829 * @private |
694 */ | 830 */ |
695 SlideMode.prototype.onCloseBubble_ = function() { | 831 SlideMode.prototype.onCloseBubble_ = function() { |
696 this.bubble_.hidden = true; | 832 this.bubble_.hidden = true; |
697 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = | 833 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = |
698 SlideMode.OVERWRITE_BUBBLE_MAX_TIMES; | 834 SlideMode.OVERWRITE_BUBBLE_MAX_TIMES; |
699 }; | 835 }; |
700 | 836 |
837 // Slideshow | |
838 | |
701 /** | 839 /** |
702 * Callback called when the image is edited. | 840 * Slideshow interval in ms. |
841 */ | |
842 SlideMode.SLIDESHOW_INTERVAL = 5000; | |
843 | |
844 /** | |
845 * First slideshow interval in ms. It should be shorter so that the user | |
846 * is not guessing whether the button worked. | |
847 */ | |
848 SlideMode.SLIDESHOW_INTERVAL_FIRST = 1000; | |
849 | |
850 /** | |
851 * @return {boolean} True if the slideshow is on. | |
703 * @private | 852 * @private |
704 */ | 853 */ |
705 SlideMode.prototype.onImageContentChanged_ = function() { | 854 SlideMode.prototype.isSlideshowOn_ = function() { |
706 var revision = this.imageView_.getContentRevision(); | 855 return this.container_.hasAttribute('slideshow'); |
707 if (revision == 0) { | 856 }; |
708 // Just loaded. | 857 |
709 var times = SlideMode.OVERWRITE_BUBBLE_KEY in localStorage ? | 858 /** |
710 parseInt(localStorage[SlideMode.OVERWRITE_BUBBLE_KEY], 10) : 0; | 859 * Stop the slideshow if it is on. |
711 if (times < SlideMode.OVERWRITE_BUBBLE_MAX_TIMES) { | 860 * @private |
712 this.bubble_.hidden = false; | 861 */ |
713 if (this.isEditing()) { | 862 SlideMode.prototype.stopSlideshow_ = function() { |
714 localStorage[SlideMode.OVERWRITE_BUBBLE_KEY] = times + 1; | 863 if (this.isSlideshowOn_()) |
715 } | 864 this.toggleSlideshow_(); |
716 } | 865 }; |
866 | |
867 /** | |
868 * Start/stop the slideshow. | |
869 * | |
870 * @param {number} opt_interval First interval in ms. | |
871 * @param {Event} opt_event Event. | |
872 * @private | |
873 */ | |
874 | |
875 SlideMode.prototype.toggleSlideshow_ = function(opt_interval, opt_event) { | |
876 if (opt_event) // Caused by user action, notify the Gallery. | |
877 cr.dispatchSimpleEvent(this, 'useraction'); | |
878 | |
879 if (!this.active_) { | |
880 // Enter the slide mode. Show the first image for the full interval. | |
881 this.enter(this.toggleSlideshow_.bind(this, SlideMode.SLIDESHOW_INTERVAL)); | |
882 return; | |
717 } | 883 } |
718 | 884 |
719 if (revision == 1) { | 885 this.stopEditing_(); |
720 // First edit. | 886 ImageUtil.setAttribute(this.container_, 'slideshow', !this.isSlideshowOn_()); |
721 ImageUtil.setAttribute(this.options_, 'saved', true); | 887 ImageUtil.setAttribute( |
722 ImageUtil.metrics.recordUserAction(ImageUtil.getMetricName('Edit')); | 888 this.slideShowButton_, 'pressed', this.isSlideshowOn_()); |
889 | |
890 if (this.isSlideshowOn_()) { | |
891 this.scheduleNextSlide_(opt_interval); | |
892 } else { | |
893 if (this.slideShowTimeout_) { | |
894 clearInterval(this.slideShowTimeout_); | |
895 this.slideShowTimeout_ = null; | |
896 } | |
723 } | 897 } |
724 }; | 898 }; |
725 | 899 |
726 // Misc | |
727 | |
728 /** | 900 /** |
729 * Start/stop the slide show. | 901 * @param {number} opt_interval Slideshow interval in ms. |
730 * @private | 902 * @private |
731 */ | 903 */ |
732 SlideMode.prototype.toggleSlideshow_ = function() { | 904 SlideMode.prototype.scheduleNextSlide_ = function(opt_interval) { |
733 if (this.slideShowTimeout_) { | 905 if (this.slideShowTimeout_) |
734 clearInterval(this.slideShowTimeout_); | 906 clearTimeout(this.slideShowTimeout_); |
735 this.slideShowTimeout_ = null; | 907 |
736 } else { | 908 this.slideShowTimeout_ = setTimeout(function() { |
737 var self = this; | 909 this.slideShowTimeout_ = null; |
738 function nextSlide() { | 910 this.selectNext(1); |
739 self.selectNext(1, | 911 }.bind(this), |
740 function() { self.slideShowTimeout_ = setTimeout(nextSlide, 5000) }); | 912 opt_interval || SlideMode.SLIDESHOW_INTERVAL); |
741 } | |
742 nextSlide(); | |
743 } | |
744 }; | 913 }; |
745 | 914 |
746 /** | 915 /** |
747 * @return {boolean} True if the editor is active. | 916 * @return {boolean} True if the editor is active. |
748 */ | 917 */ |
749 SlideMode.prototype.isEditing = function() { | 918 SlideMode.prototype.isEditing = function() { |
750 return this.container_.hasAttribute('editing'); | 919 return this.container_.hasAttribute('editing'); |
751 }; | 920 }; |
752 | 921 |
753 /** | 922 /** |
923 * Stop editing. | |
924 * @private | |
925 */ | |
926 SlideMode.prototype.stopEditing_ = function() { | |
927 if (this.isEditing()) | |
928 this.toggleEditor_(); | |
929 }; | |
930 | |
931 /** | |
754 * Activate/deactivate editor. | 932 * Activate/deactivate editor. |
933 * @param {Event} opt_event Event. | |
755 * @private | 934 * @private |
756 */ | 935 */ |
757 SlideMode.prototype.onEdit_ = function() { | 936 SlideMode.prototype.toggleEditor_ = function(opt_event) { |
937 if (opt_event) // Caused by user action, notify the Gallery. | |
938 cr.dispatchSimpleEvent(this, 'useraction'); | |
939 | |
940 if (!this.active_) { | |
941 this.enter(this.toggleEditor_.bind(this)); | |
942 return; | |
943 } | |
944 | |
945 this.stopSlideshow_(); | |
758 if (!this.isEditing() && this.isShowingVideo_()) | 946 if (!this.isEditing() && this.isShowingVideo_()) |
759 return; // No editing for videos. | 947 return; // No editing for videos. |
760 | 948 |
761 ImageUtil.setAttribute(this.container_, 'editing', !this.isEditing()); | 949 ImageUtil.setAttribute(this.container_, 'editing', !this.isEditing()); |
762 | 950 |
763 if (this.isEditing()) { // isEditing_ has just been flipped to a new value. | 951 if (this.isEditing()) { // isEditing_ has just been flipped to a new value. |
764 if (this.context_.readonlyDirName) { | 952 if (this.context_.readonlyDirName) { |
765 this.editor_.getPrompt().showAt( | 953 this.editor_.getPrompt().showAt( |
766 'top', 'readonly_warning', 0, this.context_.readonlyDirName); | 954 'top', 'readonly_warning', 0, this.context_.readonlyDirName); |
767 } | 955 } |
768 } else { | 956 } else { |
769 this.editor_.getPrompt().hide(); | 957 this.editor_.getPrompt().hide(); |
770 } | 958 } |
771 | 959 |
772 ImageUtil.setAttribute(this.editButton_, 'pressed', this.isEditing()); | 960 ImageUtil.setAttribute(this.editButton_, 'pressed', this.isEditing()); |
773 | |
774 cr.dispatchSimpleEvent(this, 'edit'); | |
775 }; | 961 }; |
776 | 962 |
777 /** | 963 /** |
778 * Display the error banner. | 964 * Display the error banner. |
779 * @param {string} message Message. | 965 * @param {string} message Message. |
780 * @private | 966 * @private |
781 */ | 967 */ |
782 SlideMode.prototype.showErrorBanner_ = function(message) { | 968 SlideMode.prototype.showErrorBanner_ = function(message) { |
783 if (message) { | 969 if (message) { |
784 this.errorBanner_.textContent = this.displayStringFunction_(message); | 970 this.errorBanner_.textContent = this.displayStringFunction_(message); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
862 done = true; | 1048 done = true; |
863 } | 1049 } |
864 }.bind(this); | 1050 }.bind(this); |
865 }; | 1051 }; |
866 | 1052 |
867 /** | 1053 /** |
868 * If the user touched the image and moved the finger more than SWIPE_THRESHOLD | 1054 * 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). | 1055 * horizontally it's considered as a swipe gesture (change the current image). |
870 */ | 1056 */ |
871 SwipeOverlay.SWIPE_THRESHOLD = 100; | 1057 SwipeOverlay.SWIPE_THRESHOLD = 100; |
OLD | NEW |