diff options
author | hirono <hirono@chromium.org> | 2014-08-24 19:13:37 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-08-25 02:14:23 +0000 |
commit | 50c47f96e2df25685da141dc8acdf4a1ef631a74 (patch) | |
tree | 48c45effdeb701f39af4dcda1cb65821fdbf9fa0 | |
parent | 3cd167db1ebbbe61fc62405fd70763e35cd9efda (diff) | |
download | chromium_src-50c47f96e2df25685da141dc8acdf4a1ef631a74.zip chromium_src-50c47f96e2df25685da141dc8acdf4a1ef631a74.tar.gz chromium_src-50c47f96e2df25685da141dc8acdf4a1ef631a74.tar.bz2 |
Gallery.app: Remove old gallery from Files.app.
BUG=None
TEST=None
Review URL: https://codereview.chromium.org/491223002
Cr-Commit-Position: refs/heads/master@{#291633}
120 files changed, 5 insertions, 12855 deletions
diff --git a/android_webview/tools/third_party_files_whitelist.txt b/android_webview/tools/third_party_files_whitelist.txt index 2bc9488..f42bf14 100644 --- a/android_webview/tools/third_party_files_whitelist.txt +++ b/android_webview/tools/third_party_files_whitelist.txt @@ -163,8 +163,6 @@ ui/base/dragdrop/os_exchange_data_provider_win.cc # up from webkit/. ui/events/keycodes/keyboard_codes_posix.h # String 'copyright' used in code. -ui/file_manager/file_manager/foreground/js/photo/gallery_scripts.js -# String 'copyright' used in code. ui/file_manager/file_manager/foreground/js/main_scripts.js # String 'copyright' used in code. ui/file_manager/gallery/js/gallery_scripts.js diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 2aae879..55039e2 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp @@ -85,12 +85,6 @@ Press any key to continue exploring. <message name="IDS_FLAGS_ENABLE_FILE_MANAGER_MTP_DESCRIPTION" desc="Description of the about:flag option to the enable MTP support in file manager."> Show MTP devices as a file storage in the file manager. </message> - <message name="IDS_FLAGS_FILE_MANAGER_ENABLE_NEW_GALLERY_NAME" desc="Name of the about:flag option to enable the new gallery app."> - Enable the new gallery - </message> - <message name="IDS_FLAGS_FILE_MANAGER_ENABLE_NEW_GALLERY_DESCRIPTION" desc="Description of the about:flag option to the enable new gallery app."> - Use the experimental new gallery instead of the stable one. - </message> <message name="IDS_FLAGS_ENABLE_VIDEO_PLAYER_CHROMECAST_SUPPORT_NAME" desc="Name of the about:flag option to enable the chromecast support for video app."> Experimental Chromecast support for Video Player </message> diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 33dbc1e..2a6e59a 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -425,14 +425,6 @@ const Experiment::Choice kEnableFileManagerMTPChoices[] = { { IDS_GENERIC_EXPERIMENT_CHOICE_DISABLED, chromeos::switches::kEnableFileManagerMTP, "false" } }; - -const Experiment::Choice kEnableFileManagerNewGalleryChoices[] = { - { IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", ""}, - { IDS_GENERIC_EXPERIMENT_CHOICE_ENABLED, - chromeos::switches::kFileManagerEnableNewGallery, "true"}, - { IDS_GENERIC_EXPERIMENT_CHOICE_DISABLED, - chromeos::switches::kFileManagerEnableNewGallery, "false"} -}; #endif const Experiment::Choice kEnableSettingsWindowChoices[] = { @@ -995,13 +987,6 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(chromeos::switches::kDisableBootAnimation), }, { - "enable-new-gallery", - IDS_FLAGS_FILE_MANAGER_ENABLE_NEW_GALLERY_NAME, - IDS_FLAGS_FILE_MANAGER_ENABLE_NEW_GALLERY_DESCRIPTION, - kOsCrOS, - MULTI_VALUE_TYPE(kEnableFileManagerNewGalleryChoices) - }, - { "enable-video-player-chromecast-support", IDS_FLAGS_ENABLE_VIDEO_PLAYER_CHROMECAST_SUPPORT_NAME, IDS_FLAGS_ENABLE_VIDEO_PLAYER_CHROMECAST_SUPPORT_DESCRIPTION, diff --git a/chrome/browser/chromeos/file_manager/file_tasks.cc b/chrome/browser/chromeos/file_manager/file_tasks.cc index a04a7d1..e4184b5 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks.cc +++ b/chrome/browser/chromeos/file_manager/file_tasks.cc @@ -6,7 +6,6 @@ #include "apps/launcher.h" #include "base/bind.h" -#include "base/command_line.h" #include "base/prefs/pref_service.h" #include "base/prefs/scoped_user_pref_update.h" #include "base/strings/stringprintf.h" @@ -26,7 +25,6 @@ #include "chrome/common/extensions/api/file_browser_private.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/pref_names.h" -#include "chromeos/chromeos_switches.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" @@ -105,28 +103,6 @@ void KeepOnlyFileManagerInternalTasks(std::vector<FullTaskDescriptor>* tasks) { tasks->swap(filtered); } -void ChooseSuitableGalleryHandler(std::vector<FullTaskDescriptor>* task_list) { - const bool disable_new_gallery = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - chromeos::switches::kFileManagerEnableNewGallery) == "false"; - std::vector<FullTaskDescriptor>::iterator it = task_list->begin(); - while (it != task_list->end()) { - if (disable_new_gallery) { - if (it->task_descriptor().app_id == kGalleryAppId) - it = task_list->erase(it); - else - ++it; - } else { - if (it->task_descriptor().app_id == kFileManagerAppId && - it->task_descriptor().action_id == "gallery") { - it = task_list->erase(it); - } else { - ++it; - } - } - } -} - // Returns true if the given task is a handler by built-in apps like Files.app // itself or QuickOffice etc. They are used as the initial default app. bool IsFallbackFileHandler(const file_tasks::TaskDescriptor& task) { @@ -504,7 +480,6 @@ void FindAllTypesOfTasks( if (ContainsGoogleDocument(path_mime_set)) KeepOnlyFileManagerInternalTasks(result_list); - ChooseSuitableGalleryHandler(result_list); ChooseAndSetDefaultTask(*profile->GetPrefs(), path_mime_set, result_list); } diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc index ac0e50c..53cd7e9 100644 --- a/chromeos/chromeos_switches.cc +++ b/chromeos/chromeos_switches.cc @@ -131,10 +131,6 @@ const char kEnterpriseEnrollmentModulusLimit[] = const char kEnterpriseEnrollmentSkipRobotAuth[] = "enterprise-enrollment-skip-robot-auth"; -// Enables the new gallery in the Files.app. -const char kFileManagerEnableNewGallery[] = - "file-manager-enable-new-gallery"; - // Enables the chromecast support for video player app. const char kEnableVideoPlayerChromecastSupport[] = "enable-video-player-chromecast-support"; diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h index 4dc5a2c..745e0f4 100644 --- a/chromeos/chromeos_switches.h +++ b/chromeos/chromeos_switches.h @@ -56,7 +56,6 @@ CHROMEOS_EXPORT extern const char kEnterpriseEnableForcedReEnrollment[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentInitialModulus[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentModulusLimit[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentSkipRobotAuth[]; -CHROMEOS_EXPORT extern const char kFileManagerEnableNewGallery[]; CHROMEOS_EXPORT extern const char kFirstExecAfterBoot[]; CHROMEOS_EXPORT extern const char kForceFirstRunUI[]; CHROMEOS_EXPORT extern const char kForceLoginManagerInTests[]; diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css index f34644e..93e9e1f 100644 --- a/ui/file_manager/file_manager/foreground/css/file_manager.css +++ b/ui/file_manager/file_manager/foreground/css/file_manager.css @@ -1261,24 +1261,6 @@ body[drive] .dialog-container[connection='offline'] .dim-offline { opacity: 0.4; } -/* Overlay pane covering the entire file manager window (e.g. image editor)*/ -.overlay-pane { - -webkit-app-region: no-drag; - border: none; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1000; /* Must be above all elements in file manager container. */ -} - -/* When the overlay pane is visible hide everything else so that the tab order - is not confused. */ -body[overlay-visible] > :not(.overlay-pane) { - display: none !important; -} - /* Invisible container for elements representing files while dragging. */ #drag-container { left: 0; @@ -1605,17 +1587,6 @@ list.autocomplete-suggestions > [lead] > div.detail-text em { -webkit-margin-end: 50px; } -#iframe-drag-area { - -webkit-app-region: drag; - height: 48px; - left: 64px; - position: absolute; - right: 134px; - top: 0; - width: auto; - z-index: 101; -} - #suggest-app-dialog { background-color: #fff; border: 0; diff --git a/ui/file_manager/file_manager/foreground/css/gallery.css b/ui/file_manager/file_manager/foreground/css/gallery.css deleted file mode 100644 index 1a62e4d..0000000 --- a/ui/file_manager/file_manager/foreground/css/gallery.css +++ /dev/null @@ -1,1381 +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. */ - -body { - -webkit-user-select: none; - font-family: Open Sans, Droid Sans Fallback, sans-serif; - font-size: 84%; - margin: 0; -} - -.gallery, -.gallery .content { - bottom: 0; - left: 0; - overflow: hidden; - position: absolute; - right: 0; - top: 0; -} - -/* Common background for both mosaic and slide mode. */ -.gallery .content { - background-color: black; -} - -/* Close button */ - -/* We actually want (left,top) to be (0,0) but for some weird reason - this triggers :hover style on page reload which is ugly. */ -.gallery > .back-button { - cursor: pointer; - left: 1px; - position: absolute; - top: 1px; - z-index: 200; -} - -/* The close icon is in a nested div so that its opacity can be manipulated - independently from its parent (which can be dimmed when the crop frame - overlaps it) */ -.gallery > .back-button div { - background-image: -webkit-image-set( - url('../images/gallery/back_to_files.png') 1x, - url('../images/gallery/2x/back_to_files.png') 2x); - background-position: center center; - background-repeat: no-repeat; - height: 40px; - opacity: 0; - width: 64px; -} - -.gallery[tools] > .back-button div { - opacity: 0.5; -} - -.gallery[tools] > .back-button div:hover { - opacity: 1; -} - -/* Image container and canvas elements */ - -.gallery .image-container { - cursor: none; /* Only visible when the toolbar is active */ - height: 100%; - position: absolute; - width: 100%; -} - -.gallery[tools] .image-container[cursor='default'] { - cursor: default; -} - -.gallery[tools] .image-container[cursor='move'] { - cursor: -webkit-image-set( - url('../images/gallery/cursor_move.png') 1x, - url('../images/gallery/2x/cursor_move.png') 2x) 15 15, auto; -} - -.gallery[tools] .image-container[cursor='crop'] { - cursor: -webkit-image-set( - url('../images/gallery/cursor_crop.png') 1x, - url('../images/gallery/2x/cursor_crop.png') 2x) 15 15, auto; -} - -.gallery[tools] .image-container[cursor='n-resize'], -.gallery[tools] .image-container[cursor='s-resize'] { - cursor: -webkit-image-set( - url('../images/gallery/cursor_updown.png') 1x, - url('../images/gallery/2x/cursor_updown.png') 2x) 15 15, auto; -} - -.gallery[tools] .image-container[cursor='e-resize'], -.gallery[tools] .image-container[cursor='w-resize'] { - cursor: -webkit-image-set( - url('../images/gallery/cursor_leftright.png') 1x, - url('../images/gallery/2x/cursor_leftright.png') 2x) 15 15, auto; -} - -.gallery[tools] .image-container[cursor='nw-resize'], -.gallery[tools] .image-container[cursor='se-resize'] { - cursor: -webkit-image-set( - url('../images/gallery/cursor_nwse.png') 1x, - url('../images/gallery/2x/cursor_nwse.png') 2x) 15 15, auto; -} - -.gallery[tools] .image-container[cursor='ne-resize'], -.gallery[tools] .image-container[cursor='sw-resize'] { - cursor: -webkit-image-set( - url('../images/gallery/cursor_swne.png') 1x, - url('../images/gallery/2x/cursor_swne.png') 2x) 15 15, auto; -} - -.gallery .image-container > .image { - pointer-events: none; - position: absolute; - /* Duration and timing function are set in Javascript. */ - transition-property: -webkit-transform, opacity; -} - -.gallery .image-container > .image[fade] { - opacity: 0; -} - -/* Full resolution image is invisible unless printing. */ -.gallery .image-container > canvas.fullres { - display: none; -} - -@media print { - /* Do not print anything but the image content. */ - .gallery > :not(.content) { - display: none !important; - } - - /* Center the printed image. */ - .gallery .image-container { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - } - - /* Do not print the screen resolution image. */ - .gallery .image-container > canvas.image { - display: none !important; - } - - /* Print the full resolution image instead. */ - .gallery .image-container > canvas.fullres { - display: block !important; - max-height: 100%; - max-width: 100%; - } - - /* Print video at the center of the page */ - .gallery .image-container > video.image { - position: auto !important; - } -} - -/* Toolbar */ - -.gallery > .header, -.gallery > .toolbar { - -webkit-box-align: stretch; - -webkit-box-orient: horizontal; - -webkit-box-pack: start; - background-color: rgba(30, 30, 30, 0.8); - display: -webkit-box; - left: 0; - opacity: 0; - padding: 0 10px; - pointer-events: none; - position: absolute; - right: 0; - transition: opacity 300ms ease; -} - -.gallery > .header { - -webkit-box-align: center; - -webkit-box-pack: end; - border-bottom: 1px solid rgba(50, 50, 50, 0.8); - display: -webkit-box; - height: 45px; - top: 0; -} - -.gallery > .toolbar { - border-top: 1px solid rgba(50, 50, 50, 0.8); - bottom: 0; - height: 55px; - min-width: 800px; -} - -.gallery[tools]:not([slideshow]) > .header, -.gallery[tools]:not([slideshow]) > .toolbar { - opacity: 1; - pointer-events: auto; -} - -/* Hide immediately when entering the slideshow. */ -.gallery[tools][slideshow] > .toolbar { - transition-duration: 0; -} - -.gallery[tools][locked] > .toolbar { - pointer-events: none; -} - -.gallery .arrow-box { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - height: 100%; - pointer-events: none; - position: absolute; - width: 100%; - z-index: 100; -} - -.gallery .arrow-box .arrow { - opacity: 0; - pointer-events: none; -} - -.gallery .arrow-box .arrow-spacer { - -webkit-box-flex: 1; - pointer-events: none; -} - -.gallery[tools] .arrow-box[active] .arrow { - cursor: pointer; - opacity: 1; - pointer-events: auto; -} - -/* The arrow icons are in nested divs so that their opacity can be manipulated - * independently from their parent (which can be dimmed when the crop frame - * overlaps it) */ -.gallery .arrow div { - background-position: center center; - background-repeat: no-repeat; - height: 193px; - opacity: 0; - width: 105px; -} - -.gallery[tools] .arrow-box[active] .arrow div { - opacity: 0.25; -} - -.gallery[tools] .arrow-box[active] .arrow div:hover { - opacity: 1; -} - -.gallery .arrow.left div { - background-image: -webkit-image-set( - url('../images/gallery/arrow_left.png') 1x, - url('../images/gallery/2x/arrow_left.png') 2x); -} - -.gallery .arrow.right div { - background-image: -webkit-image-set( - url('../images/gallery/arrow_right.png') 1x, - url('../images/gallery/2x/arrow_right.png') 2x); -} - -/* Special behavior on mouse drag. - Redundant .gallery attributes included to make the rules more specific */ - -/* Everything but the image container should become mouse-transparent */ -.gallery[tools][editing][mousedrag] * { - pointer-events: none; -} - -.gallery[tools][editing][mousedrag] .image-container { - pointer-events: auto; -} - -/* The editor marks elements with 'dimmed' attribute to get them out of the way - of the crop frame */ -.gallery[tools][editing] [dimmed], -.gallery[tools][editing] [dimmed] * { - pointer-events: none; -} - -.gallery[tools][editing] [dimmed] { - opacity: 0.2; -} - -/* Filename */ - -.gallery .filename-spacer { - position: relative; - width: 270px; -} - -.gallery .filename-spacer > * { - background-color: transparent; - overflow: hidden; - position: absolute; - transition: visibility 0 linear 180ms, all 180ms linear; - width: 260px; -} - -.gallery .filename-spacer * { - color: white; -} - -.gallery .filename-spacer .namebox { - height: 22px; - top: 15px; -} - -.gallery[editing] .filename-spacer .namebox { - height: 21px; - top: 5px; -} - - -.gallery .filename-spacer .namebox { - background-color: transparent; - border: none; - box-sizing: border-box; - cursor: pointer; - display: block; - font-size: 120%; - outline: none; - overflow: hidden; - padding: 0 3px; - position: absolute; - text-overflow: ellipsis; - white-space: nowrap; -} - -.gallery .filename-spacer .namebox[disabled] { - -webkit-user-select: none; - cursor: default; -} - -.gallery .filename-spacer .namebox:not([disabled]):not(:focus):hover { - background-color: rgba(48, 48, 48, 1.0); -} - -.gallery .filename-spacer .namebox:focus { - background-color: white; - color: black; - cursor: text; -} - -.gallery .filename-spacer .options { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: start; - display: -webkit-box; - opacity: 0; - top: 50px; - visibility: hidden; -} - -.gallery[editing] .filename-spacer .options { - opacity: 1; - top: 28px; - visibility: visible; -} - -.gallery .filename-spacer .saved, -.gallery .filename-spacer .overwrite-original { - cursor: inherit; - font-size: 90%; - margin-left: 3px; - margin-right: 18px; - opacity: 0; - pointer-events: none; - transition: all linear 120ms; -} - -.gallery[editing] .filename-spacer .saved { - color: white; - opacity: 0.5; -} - -.gallery[editing] .filename-spacer .overwrite-original, -.gallery[editing] .filename-spacer .overwrite-original > * { - cursor: pointer; - opacity: 1; - pointer-events: auto; -} - -.gallery[editing] .options[saved] .overwrite-original { - opacity: 0.5; -} - -.gallery[editing] .options[saved] .overwrite-original, -.gallery[editing] .options[saved] .overwrite-original > * { - cursor: default; - pointer-events: none; -} - -.gallery .filename-spacer .overwrite-original input { - margin-bottom: -2px; - margin-right: 6px; -} - -.gallery .filename-spacer .saved[highlighted] { - -webkit-transform: scaleX(1.1) scaleY(1.1) rotate(0); - opacity: 1; -} - -/* Bubble */ -.gallery .toolbar .bubble { - bottom: 65px; - font-size: 85%; - left: 50px; - position: absolute; - width: 220px; -} - -.gallery:not([editing]) .toolbar .bubble { - display: none; -} - -/* Toolbar buttons */ - -.gallery .button-spacer { - -webkit-box-flex: 1; - display: -webkit-box; -} - -/* Thumbnails */ - -.gallery .ribbon-spacer { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - height: 100%; - left: 280px; - position: absolute; - right: 280px; -} - -.gallery .toolbar .ribbon { - -webkit-box-flex: 0; - -webkit-box-orient: horizontal; - -webkit-box-pack: start; - display: -webkit-box; - height: 100%; - overflow: hidden; - transition: opacity 180ms linear, visibility 0 linear; - z-index: 0; -} - -.gallery[editing] .toolbar .ribbon { - opacity: 0; - transition-delay: 0, 180ms; - visibility: hidden; -} - -.gallery .ribbon-image { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - border: 2px solid rgba(255, 255, 255, 0); /* transparent white */ - cursor: pointer; - display: -webkit-box; - height: 47px; - margin: 2px; - overflow: hidden; - transition: all 180ms linear; - width: 47px; -} - -.ribbon-image[vanishing='smooth'] { - border-left-width: 0; - border-right-width: 0; - margin-left: 0; - margin-right: 0; - width: 0; -} - -.gallery .ribbon-image[selected] { - border: 2px solid rgba(255, 233, 168, 1); -} - -.gallery .toolbar .ribbon.fade-left { - -webkit-mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0, - rgba(0, 0, 0, 1) 40px); -} - -.gallery .toolbar .ribbon.fade-right { - -webkit-mask-image: linear-gradient(to left, rgba(0, 0, 0, 0) 0, - rgba(0, 0, 0, 1) 40px); -} - -.gallery .toolbar .ribbon.fade-left.fade-right { - -webkit-mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0, - rgba(0, 0, 0, 1) 40px, - rgba(0, 0, 0, 1) 230px, - rgba(0, 0, 0, 0) 100%); -} - -.gallery .image-wrapper { - background-size: 45px 45px; - border: 1px solid rgba(0, 0, 0, 0); /* transparent black */ - height: 45px; - overflow: hidden; - position: relative; - width: 45px; -} - -.gallery .image-wrapper > img { - position: absolute; -} - -.gallery .image-wrapper > img:not(.cached) { - -webkit-animation: fadeIn 500ms ease-in; -} - -/* Editor buttons */ - -.gallery .edit-bar-spacer { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - height: 100%; - left: 280px; - opacity: 0; - position: absolute; - right: 280px; - transition: opacity 180ms linear, visibility 0 linear 180ms; - visibility: hidden; -} - -.gallery .toolbar .edit-main { - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - color: white; - display: -webkit-box; - height: 55px; - overflow: visible; -} - -.gallery[editing] .edit-bar-spacer { - opacity: 1.0; - pointer-events: auto; - transition-delay: 100ms, 100ms; - visibility: visible; -} - -.gallery .header button, -.gallery .toolbar button, -.gallery .header button[disabled], -.gallery .toolbar button[disabled] { - -webkit-box-align: center; - -webkit-box-flex: 0; - -webkit-box-orient: horizontal; - -webkit-box-pack: end; - background-color: rgba(0, 0, 0, 0); - background-position: center; - background-repeat: no-repeat; - border: none; - box-shadow: none; - color: white; - cursor: pointer; - display: -webkit-box; - opacity: 0.99; /* Workaround for http://crosbug.com/21065 */ - padding: 1px; /* Instead of a border. */ - position: relative; - z-index: 10; -} - -.gallery .header button, -.gallery .toolbar button { - height: 40px; - margin: 6px 0; - min-width: 40px; /* Reset. */ - width: 40px; -} - -/* By default, labels are hidden. */ -.gallery > .toolbar button span { - display: none; -} - -/* Show labels if there is enough space. */ -@media (min-width: 1180px) { - - .gallery .edit-main button, - .gallery .edit-main button[disabled] { - background-position: 5px center; - max-width: 60px; - min-width: 0; /* Reset. */ - padding: 0 10px 0 35px; - width: auto; - } - - .gallery > .toolbar button span { - display: inline; - } - -} - -.gallery .header button:hover, -.gallery .toolbar button:hover { - background-color: rgba(31, 31, 31, 1); - color: white; -} - -.gallery .header button:active, -.gallery .toolbar button:active, -.gallery .header button[pressed], -.gallery .toolbar button[pressed], -.gallery .header button[pressed]:hover, -.gallery .toolbar button[pressed]:hover { - background-color: rgba(240, 240, 240, 1); - color: black; -} - -.gallery > .toolbar button.autofix { - background-image: -webkit-image-set( - url('../images/gallery/icon_autofix.png') 1x, - url('../images/gallery/2x/icon_autofix.png') 2x); -} - -.gallery > .toolbar button.autofix:active, -.gallery > .toolbar button.autofix[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_autofix_selected.png') 1x, - url('../images/gallery/2x/icon_autofix_selected.png') 2x); -} - -.gallery > .toolbar button.crop { - background-image: -webkit-image-set( - url('../images/gallery/icon_crop.png') 1x, - url('../images/gallery/2x/icon_crop.png') 2x); -} - -.gallery > .toolbar button.crop:active, -.gallery > .toolbar button.crop[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_crop_selected.png') 1x, - url('../images/gallery/2x/icon_crop_selected.png') 2x); -} - -.gallery > .toolbar button.exposure { - background-image: -webkit-image-set( - url('../images/gallery/icon_brightness.png') 1x, - url('../images/gallery/2x/icon_brightness.png') 2x); -} - -.gallery > .toolbar button.exposure:active, -.gallery > .toolbar button.exposure[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_brightness_selected.png') 1x, - url('../images/gallery/2x/icon_brightness_selected.png') 2x); -} - -.gallery > .toolbar button.rotate_right { - background-image: -webkit-image-set( - url('../images/gallery/icon_rotate.png') 1x, - url('../images/gallery/2x/icon_rotate.png') 2x); -} - -.gallery > .toolbar button.rotate_right:active, -.gallery > .toolbar button.rotate_right[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_rotate_selected.png') 1x, - url('../images/gallery/2x/icon_rotate_selected.png') 2x); -} - -.gallery > .toolbar button.rotate_left { - background-image: -webkit-image-set( - url('../images/gallery/icon_rotate_left.png') 1x, - url('../images/gallery/2x/icon_rotate_left.png') 2x); -} - -.gallery > .toolbar button.rotate_left:active, -.gallery > .toolbar button.rotate_left[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_rotate_left_selected.png') 1x, - url('../images/gallery/2x/icon_rotate_left_selected.png') 2x); -} - -.gallery > .toolbar button.undo { - background-image: -webkit-image-set( - url('../images/gallery/icon_undo.png') 1x, - url('../images/gallery/2x/icon_undo.png') 2x); -} - -.gallery > .toolbar button.undo:active, -.gallery > .toolbar button.undo[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_undo_selected.png') 1x, - url('../images/gallery/2x/icon_undo_selected.png') 2x); -} - -.gallery > .toolbar button.redo { - background-image: -webkit-image-set( - url('../images/gallery/icon_redo.png') 1x, - url('../images/gallery/2x/icon_redo.png') 2x); - position: absolute; /* Exclude from center-packing*/ -} - -.gallery > .toolbar button.redo:active, -.gallery > .toolbar button.redo[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_redo_selected.png') 1x, - url('../images/gallery/2x/icon_redo_selected.png') 2x); -} - -.gallery > .toolbar button[disabled], -.gallery[tools][locked] > .toolbar button { - opacity: 0.5; - pointer-events: none; -} - -.gallery > .toolbar button[hidden] { - display: none; -} - -.gallery[mode='slide'] > .toolbar > button.mode { - background-image: -webkit-image-set( - url('../images/gallery/icon_mosaic.png') 1x, - url('../images/gallery/2x/icon_mosaic.png') 2x); -} - -.gallery[mode='slide'] > .toolbar > button.mode:active { - background-image: -webkit-image-set( - url('../images/gallery/icon_mosaic_selected.png') 1x, - url('../images/gallery/2x/icon_mosaic_selected.png') 2x); -} - -.gallery[mode='mosaic'] > .toolbar > button.mode { - background-image: -webkit-image-set( - url('../images/gallery/icon_1up.png') 1x, - url('../images/gallery/2x/icon_1up.png') 2x); -} - -.gallery[mode='mosaic'] > .toolbar > button.mode:active { - background-image: -webkit-image-set( - url('../images/gallery/icon_1up_selected.png') 1x, - url('../images/gallery/2x/icon_1up_selected.png') 2x); -} - -.gallery > .toolbar > button.slideshow { - background-image: -webkit-image-set( - url('../images/gallery/icon_slideshow.png') 1x, - url('../images/gallery/2x/icon_slideshow.png') 2x); -} - -.gallery > .toolbar > button.slideshow:active, -.gallery > .toolbar > button.slideshow[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_slideshow_selected.png') 1x, - url('../images/gallery/2x/icon_slideshow_selected.png') 2x); -} - -.gallery > .toolbar > button.delete { - background-image: -webkit-image-set( - url('../images/gallery/icon_delete.png') 1x, - url('../images/gallery/2x/icon_delete.png') 2x); -} - -.gallery > .toolbar > button.delete:active { - background-image: -webkit-image-set( - url('../images/gallery/icon_delete_selected.png') 1x, - url('../images/gallery/2x/icon_delete_selected.png') 2x); -} - -.gallery > .toolbar > button.edit { - background-image: -webkit-image-set( - url('../images/gallery/icon_edit.png') 1x, - url('../images/gallery/2x/icon_edit.png') 2x); -} - -.gallery > .toolbar > button.edit:active, -.gallery > .toolbar > button.edit[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_edit_selected.png') 1x, - url('../images/gallery/2x/icon_edit_selected.png') 2x); -} - -.gallery > .toolbar > button.print { - background-image: -webkit-image-set( - url('../images/gallery/icon_print.png') 1x, - url('../images/gallery/2x/icon_print.png') 2x); -} - -.gallery > .toolbar > button.print:active, -.gallery > .toolbar > button.print[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_print_selected.png') 1x, - url('../images/gallery/2x/icon_print_selected.png') 2x); -} - -.gallery > .toolbar > button.share { - background-image: -webkit-image-set( - url('../images/gallery/icon_share.png') 1x, - url('../images/gallery/2x/icon_share.png') 2x); -} - -.gallery > .toolbar > button.share:active, -.gallery > .toolbar > button.share[pressed] { - background-image: -webkit-image-set( - url('../images/gallery/icon_share_selected.png') 1x, - url('../images/gallery/2x/icon_share_selected.png') 2x); -} - -.gallery > .toolbar > button.share[disabled] { - display: none; -} - -/* Secondary toolbar (mode-specific tools) */ - -.gallery .edit-modal { - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - bottom: 80px; - display: -webkit-box; - height: 40px; - pointer-events: none; - position: absolute; - width: 100%; -} - -.gallery .edit-modal-wrapper[hidden] { - display: none; -} - -.gallery .edit-modal-wrapper { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - background-color: rgba(0, 0, 0, 0.75); - color: white; - display: -webkit-box; - padding-right: 5px; - pointer-events: auto; -} - -.gallery .edit-modal .label { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - background-position: 20px center; - background-repeat: no-repeat; - display: -webkit-box; - height: 20px; - padding-left: 50px; - padding-right: 10px; -} - -.gallery .edit-modal .label.brightness { - background-image: -webkit-image-set( - url('../images/gallery/icon_brightness.png') 1x, - url('../images/gallery/2x/icon_brightness.png') 2x); -} - -.gallery .edit-modal .label.contrast { - background-image: -webkit-image-set( - url('../images/gallery/icon_contrast.png') 1x, - url('../images/gallery/2x/icon_contrast.png') 2x); - height: 24px; - margin-left: 15px; -} - -.gallery .edit-modal .range { - -webkit-appearance: none !important; - height: 3px; - margin-right: 10px; - margin-top: 1px; -} - -.gallery .edit-modal .range::-webkit-slider-thumb { - -webkit-appearance: none; - background-image: -webkit-image-set( - url('../images/gallery/slider_thumb.png') 1x, - url('../images/gallery/2x/slider_thumb.png') 2x); - height: 29px; - width: 16px; -} - -/* Crop frame */ - -.gallery .crop-overlay { - -webkit-box-orient: vertical; - display: -webkit-box; - pointer-events: none; - position: absolute; -} - -.gallery .crop-overlay .shadow { - background-color: rgba(0, 0, 0, 0.65); -} - -.gallery .crop-overlay .middle-box { - -webkit-box-flex: 1; - -webkit-box-orient: horizontal; - display: -webkit-box; -} - -.gallery .crop-frame { - -webkit-box-flex: 1; - display: -webkit-box; - position: relative; -} - -.gallery .crop-frame div { - background-color: rgba(255, 255, 255, 1); - box-shadow: 0 0 3px rgba(0, 0, 0, 0.75); - position: absolute; -} - -.gallery .crop-frame .horizontal { - height: 1px; - left: 7px; - right: 7px; -} - -.gallery .crop-frame .horizontal.top { - top: 0; -} - -.gallery .crop-frame .horizontal.bottom { - bottom: 0; -} - -.gallery .crop-frame .vertical { - bottom: 7px; - top: 7px; - width: 1px; -} - -.gallery .crop-frame .vertical.left { - left: 0; -} - -.gallery .crop-frame .vertical.right { - right: 0; -} - -.gallery .crop-frame .corner { - border-radius: 6px; - height: 13px; - width: 13px; -} - -.gallery .crop-frame .corner.left { - left: -6px; -} - -.gallery .crop-frame .corner.right { - right: -6px; -} - -.gallery .crop-frame .corner.top { - top: -6px; -} - -.gallery .crop-frame .corner.bottom { - bottom: -6px; -} - -/* Prompt/notification panel */ - -.gallery .prompt-wrapper { - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - height: 100%; - pointer-events: none; - position: absolute; - width: 100%; -} - -.gallery .prompt-wrapper[pos=top] { - -webkit-box-align: start; -} - -.gallery .prompt-wrapper[pos=center] { - -webkit-box-align: center; -} - -.gallery .prompt-wrapper[pos=center] .back-button { - display: none; -} - -.gallery .prompt-wrapper > div.dimmable { - opacity: 1; - transition: opacity 220ms ease; -} - -.gallery .prompt { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - background-color: rgba(0, 0, 0, 0.8); - color: white; - display: -webkit-box; - font-size: 120%; - height: 40px; - opacity: 0; - padding: 0 20px; - position: relative; - top: 5px; - transition: all 180ms ease; -} - -.gallery .prompt[state='fadein'] { - opacity: 1; - top: 0; -} - -.gallery .prompt[state='fadeout'] { - opacity: 0; - top: 0; -} - -.gallery .prompt-wrapper[pos=top] .prompt { - padding-right: 10px; -} - -.gallery .prompt .back-button { - background-image: -webkit-image-set( - url('../images/gallery/butterbar_close_button.png') 1x, - url('../images/gallery/2x/butterbar_close_button.png') 2x); - background-position: center center; - background-repeat: no-repeat; - height: 16px; - margin-left: 16px; - opacity: 0.65; - pointer-events: auto; - width: 16px; -} - -.gallery .prompt .back-button:hover { - background-color: rgba(81, 81, 81, 1); - opacity: 1.0; -} - -.gallery .share-menu { - -webkit-box-align: stretch; - -webkit-box-orient: vertical; - -webkit-box-pack: start; - background-color: white; - border: 1px solid #7f7f7f; - border-radius: 1px; - bottom: 60px; - display: -webkit-box; - opacity: 1.0; - padding: 8px; - position: absolute; - right: 10px; - transition: opacity 500ms ease-in-out; -} - -.gallery .share-menu .bubble-point { - background-image: -webkit-image-set( - url('../images/gallery/bubble_point.png') 1x, - url('../images/gallery/2x/bubble_point.png') 2x); - background-position: center top; - background-repeat: no-repeat; - bottom: -8px; - height: 8px; - padding: 0; - position: absolute; - right: 20px; - width: 20px; -} - -.gallery .share-menu[hidden] { - bottom: -100%; /* Offscreen so that 'dimmed' attribute does not show it. */ - opacity: 0; - pointer-events: none; -} - -.gallery .share-menu > .item { - background-color: rgba(0, 0, 0, 0); - background-position: 5px center; - background-repeat: no-repeat; - cursor: pointer; - padding: 5px; - padding-left: 26px; -} - -.gallery .share-menu > .item:hover { - background-color: rgba(240, 240, 240, 1); -} - -.gallery .share-menu > div > img { - display: block; - margin-right: 5px; -} - -/* Load spinner and error banner. */ - -.gallery .spinner { - background-image: url(../images/common/spinner.svg); - background-size: 100%; - height: 16px; - left: 50%; - margin-left: -8px; - margin-top: -8px; - position: absolute; - top: 50%; - width: 16px; -} - -.gallery:not([spinner]) .spinner { - display: none; -} - -.gallery .error-banner { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - background-color: rgba(24, 24, 24, 1); - background-image: -webkit-image-set( - url('../images/media/error.png') 1x, - url('../images/media/2x/error.png') 2x); - background-position: 25px center; - background-repeat: no-repeat; - color: white; - display: -webkit-box; - height: 54px; - padding-left: 70px; - padding-right: 35px; -} - -.gallery:not([error]) .error-banner { - display: none; -} - -/* Video playback support. */ - -.gallery video { - height: 100%; - position: absolute; - width: 100%; -} - -.gallery .video-controls-spacer { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - bottom: 60px; /* Just above the toolbar */ - display: -webkit-box; - height: 30px; - left: 0; - opacity: 0; - pointer-events: none; - position: absolute; - right: 0; -} - -.gallery[video] .video-controls-spacer { - /* Animate opacity on 'tools' attribute toggle. */ - /* Change opacity immediately on 'video' attribute change. */ - transition: opacity 280ms ease; -} - -.gallery[video][tools] .video-controls-spacer { - opacity: 1; -} - -.gallery .video-controls { - display: none; - max-width: 800px; -} - -.gallery[video] .video-controls { - -webkit-box-flex: 1; - display: -webkit-box; -} - -.gallery[video] > .toolbar .edit-main { - display: none; -} - -/* Mosaic view. */ -.mosaic { - bottom: 55px; /* Toolbar height. */ - left: 0; - overflow-x: scroll; - overflow-y: hidden; - position: absolute; - right: 0; - top: 0; - - /* transition-duration is set in Javascript. */ - transition-property: -webkit-transform; - transition-timing-function: linear; -} - -.mosaic::-webkit-scrollbar { - background: transparent; -} - -.mosaic::-webkit-scrollbar-thumb { - background: rgb(31, 31, 31); -} - -.gallery:not([mode='mosaic']) .mosaic::-webkit-scrollbar-thumb { - background: transparent; -} - -.mosaic-tile { - position: absolute; - /* Tile's zoom factor is animated on hover. We apply the transform to - the entire tile so that the image outline is included into the animation. */ - transition: -webkit-transform 150ms linear; -} - -/* Mosaic tile's opacity is controlled by |visible| attribute which changes - separately from .gallery[mode] */ -.mosaic:not([visible]) .mosaic-tile .img-border { - opacity: 0; -} - -/* Animate tile's opacity, except for the selected tile which should show/hide - instantly (this looks better when zooming to/from the slide mode). */ -.mosaic-tile:not([selected]) .img-border { - transition: opacity 350ms linear; -} - -/* Must be in sync with mosaic_mode.js. - Mosaic.Layout.SPACING should be equal to - top + bottom + border-top-width + border-bottom-width AND - left + right + border-left-width + border-right-width */ -.mosaic-tile .img-border { - border: 1px solid black; /* Space between the outline and the image. */ - bottom: 4px; - left: 4px; - outline: 2px solid transparent; - overflow: hidden; - position: absolute; - right: 4px; - top: 4px; -} - -/* Selected and hover state are only visible when zoom transition is over. */ -.mosaic[visible='normal'] .mosaic-tile[selected] .img-border { - outline-color: rgb(51, 153, 255); -} - -.mosaic[visible='normal'].hover-visible .mosaic-tile:hover { - -webkit-transform: scale(1.05); - z-index: 50; -} - -.mosaic[visible='normal'].hover-visible - .mosaic-tile:hover:not([selected]) .img-border { - outline-color: rgb(182, 212, 252); -} - -.mosaic-tile .img-wrapper { - bottom: 0; - left: 0; - position: absolute; - right: 0; - top: 0; -} - -.mosaic-tile .img-wrapper[generic-thumbnail], -.mosaic-tile .img-wrapper.animated:not([generic-thumbnail]) - canvas:not(.cached) { - -webkit-animation: fadeIn ease-in 1; - -webkit-animation-duration: 500ms; - -webkit-animation-fill-mode: forwards; -} - -@-webkit-keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -/* In order to do mode animated transitions smoothly we keep both mosaic and - image-container but transparent. */ -.gallery:not([mode='mosaic']) .mosaic, -.gallery:not([mode='slide']) .image-container { - pointer-events: none; -} - -.gallery:not([mode='slide']) .ribbon, -.gallery:not([mode='slide']) .arrow-box { - opacity: 0; - pointer-events: none; -} - -/* Temporary. Remove this along with the delete confirmation dialog - when Undo delete is implemented. */ -.cr-dialog-shield { - background-color: black; -} - -/* Slideshow controls */ - -.slideshow-toolbar { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - bottom: 0; - display: none; - left: 0; - padding-bottom: 6px; - pointer-events: none; - position: absolute; - right: 0; -} - -.gallery[tools][slideshow] .slideshow-toolbar { - display: -webkit-box; -} - -.slideshow-toolbar > div { - background-position: center; - background-repeat: no-repeat; - height: 68px; - opacity: 0.5; - pointer-events: auto; - width: 68px; -} - -.slideshow-toolbar > div:hover { - opacity: 1; -} - -.slideshow-toolbar > .slideshow-play { - background-image: -webkit-image-set( - url('../images/gallery/slideshow-play.png') 1x, - url('../images/gallery/2x/slideshow-play.png') 2x); - margin-right: -2px; -} - -.gallery[slideshow='playing'] .slideshow-toolbar > .slideshow-play { - background-image: -webkit-image-set( - url('../images/gallery/slideshow-pause.png') 1x, - url('../images/gallery/2x/slideshow-pause.png') 2x); -} - -.slideshow-toolbar > .slideshow-end { - background-image: -webkit-image-set( - url('../images/gallery/slideshow-end.png') 1x, - url('../images/gallery/2x/slideshow-end.png') 2x); - margin-left: -2px; -} - -.gallery > .header > button { - -webkit-margin-start: 10px; - cursor: default; - height: 32px; - min-width: 32px; - width: 32px; -} - -.gallery > .header > .minimize-button { - background: -webkit-image-set( - url('chrome://resources/images/apps/topbar_button_minimize.png') 1x, - url('chrome://resources/images/2x/apps/topbar_button_minimize.png') 2x) - center; -} - -.gallery > .header > .maximize-button { - background: -webkit-image-set( - url('chrome://resources/images/apps/topbar_button_maximize.png') 1x, - url('chrome://resources/images/2x/apps/topbar_button_maximize.png') 2x) - center; -} - -.gallery > .header > .close-button { - background: -webkit-image-set( - url('chrome://resources/images/apps/topbar_button_close.png') 1x, - url('chrome://resources/images/2x/apps/topbar_button_close.png') 2x) - center; -} diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/arrow_left.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/arrow_left.png Binary files differdeleted file mode 100644 index 6e4fb665..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/arrow_left.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/arrow_right.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/arrow_right.png Binary files differdeleted file mode 100644 index 22a6c73..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/arrow_right.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/back_to_files.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/back_to_files.png Binary files differdeleted file mode 100644 index e04d9d3..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/back_to_files.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/bubble_point.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/bubble_point.png Binary files differdeleted file mode 100644 index 161e4c0..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/bubble_point.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/butterbar_close_button.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/butterbar_close_button.png Binary files differdeleted file mode 100644 index aa6c816..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/butterbar_close_button.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_crop.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_crop.png Binary files differdeleted file mode 100644 index 6202fa9..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_crop.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_leftright.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_leftright.png Binary files differdeleted file mode 100644 index a7ee09c..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_leftright.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_move.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_move.png Binary files differdeleted file mode 100644 index faa3c8a..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_move.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_nwse.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_nwse.png Binary files differdeleted file mode 100644 index 0cd6399..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_nwse.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_swne.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_swne.png Binary files differdeleted file mode 100644 index 04d9dc0..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_swne.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_updown.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_updown.png Binary files differdeleted file mode 100644 index 1e9adfb..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/cursor_updown.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_1up.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_1up.png Binary files differdeleted file mode 100644 index 58cbc28..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_1up.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_1up_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_1up_selected.png Binary files differdeleted file mode 100644 index a0ca726..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_1up_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_autofix.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_autofix.png Binary files differdeleted file mode 100644 index 8ce4917..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_autofix.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_autofix_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_autofix_selected.png Binary files differdeleted file mode 100644 index 8838d5c..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_autofix_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_brightness.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_brightness.png Binary files differdeleted file mode 100644 index ffcd385..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_brightness.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_brightness_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_brightness_selected.png Binary files differdeleted file mode 100644 index 90bf03b..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_brightness_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_contrast.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_contrast.png Binary files differdeleted file mode 100644 index eec931d..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_contrast.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_crop.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_crop.png Binary files differdeleted file mode 100644 index 7c12fb5..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_crop.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_crop_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_crop_selected.png Binary files differdeleted file mode 100644 index bb2e9e6..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_crop_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_delete.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_delete.png Binary files differdeleted file mode 100644 index a55ac6c..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_delete.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_delete_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_delete_selected.png Binary files differdeleted file mode 100644 index af54168..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_delete_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_edit.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_edit.png Binary files differdeleted file mode 100644 index 288bc5b..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_edit.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_edit_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_edit_selected.png Binary files differdeleted file mode 100644 index bcf9933..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_edit_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_mosaic.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_mosaic.png Binary files differdeleted file mode 100644 index 3e1a621..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_mosaic.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_mosaic_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_mosaic_selected.png Binary files differdeleted file mode 100644 index d9e329d..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_mosaic_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_print.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_print.png Binary files differdeleted file mode 100644 index b5a9be0..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_print.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_print_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_print_selected.png Binary files differdeleted file mode 100644 index 048a341..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_print_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_redo.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_redo.png Binary files differdeleted file mode 100644 index 075275d..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_redo.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_redo_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_redo_selected.png Binary files differdeleted file mode 100644 index beed584..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_redo_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate.png Binary files differdeleted file mode 100644 index db2c0b0..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_left.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_left.png Binary files differdeleted file mode 100644 index da57329d..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_left.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_left_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_left_selected.png Binary files differdeleted file mode 100644 index d1b00a7..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_left_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_selected.png Binary files differdeleted file mode 100644 index b3a9bf6..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_rotate_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_share.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_share.png Binary files differdeleted file mode 100644 index b1da6d9..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_share.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_share_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_share_selected.png Binary files differdeleted file mode 100644 index b3cd00f..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_share_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_slideshow.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_slideshow.png Binary files differdeleted file mode 100644 index fec87c0..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_slideshow.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_slideshow_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_slideshow_selected.png Binary files differdeleted file mode 100644 index 4e1ed5a..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_slideshow_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_undo.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_undo.png Binary files differdeleted file mode 100644 index c51fd62..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_undo.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_undo_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_undo_selected.png Binary files differdeleted file mode 100644 index 92d3a01..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/icon_undo_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/slider_thumb.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/slider_thumb.png Binary files differdeleted file mode 100644 index e100da6..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/slider_thumb.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-end.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-end.png Binary files differdeleted file mode 100644 index 5e19f8d..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-end.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-pause.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-pause.png Binary files differdeleted file mode 100644 index 13457d9..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-pause.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-play.png b/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-play.png Binary files differdeleted file mode 100644 index aa9bd88..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/2x/slideshow-play.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/arrow_left.png b/ui/file_manager/file_manager/foreground/images/gallery/arrow_left.png Binary files differdeleted file mode 100644 index 85e687a..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/arrow_left.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/arrow_right.png b/ui/file_manager/file_manager/foreground/images/gallery/arrow_right.png Binary files differdeleted file mode 100644 index 0361556..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/arrow_right.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/back_to_files.png b/ui/file_manager/file_manager/foreground/images/gallery/back_to_files.png Binary files differdeleted file mode 100644 index 96e420a..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/back_to_files.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/bubble_point.png b/ui/file_manager/file_manager/foreground/images/gallery/bubble_point.png Binary files differdeleted file mode 100644 index a4a44e0..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/bubble_point.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/butterbar_close_button.png b/ui/file_manager/file_manager/foreground/images/gallery/butterbar_close_button.png Binary files differdeleted file mode 100644 index 3c65c23..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/butterbar_close_button.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/cursor_crop.png b/ui/file_manager/file_manager/foreground/images/gallery/cursor_crop.png Binary files differdeleted file mode 100644 index 6084188..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/cursor_crop.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/cursor_leftright.png b/ui/file_manager/file_manager/foreground/images/gallery/cursor_leftright.png Binary files differdeleted file mode 100644 index 30eeb03..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/cursor_leftright.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/cursor_move.png b/ui/file_manager/file_manager/foreground/images/gallery/cursor_move.png Binary files differdeleted file mode 100644 index c5026d1..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/cursor_move.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/cursor_nwse.png b/ui/file_manager/file_manager/foreground/images/gallery/cursor_nwse.png Binary files differdeleted file mode 100644 index 87fb564..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/cursor_nwse.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/cursor_swne.png b/ui/file_manager/file_manager/foreground/images/gallery/cursor_swne.png Binary files differdeleted file mode 100644 index 5e34475..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/cursor_swne.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/cursor_updown.png b/ui/file_manager/file_manager/foreground/images/gallery/cursor_updown.png Binary files differdeleted file mode 100644 index f3a4224..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/cursor_updown.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_1up.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_1up.png Binary files differdeleted file mode 100644 index 546e87a..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_1up.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_1up_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_1up_selected.png Binary files differdeleted file mode 100644 index a3043a8..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_1up_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_autofix.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_autofix.png Binary files differdeleted file mode 100644 index 0fb5b82..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_autofix.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_autofix_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_autofix_selected.png Binary files differdeleted file mode 100644 index fb5972d..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_autofix_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_brightness.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_brightness.png Binary files differdeleted file mode 100644 index ec9c114..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_brightness.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_brightness_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_brightness_selected.png Binary files differdeleted file mode 100644 index 88ee722..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_brightness_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_contrast.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_contrast.png Binary files differdeleted file mode 100644 index 0188d48..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_contrast.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_crop.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_crop.png Binary files differdeleted file mode 100644 index efff5ba..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_crop.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_crop_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_crop_selected.png Binary files differdeleted file mode 100644 index 18b8317..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_crop_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_delete.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_delete.png Binary files differdeleted file mode 100644 index efb132a..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_delete.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_delete_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_delete_selected.png Binary files differdeleted file mode 100644 index f2f88d8..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_delete_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_edit.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_edit.png Binary files differdeleted file mode 100644 index fc72ecf..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_edit.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_edit_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_edit_selected.png Binary files differdeleted file mode 100644 index 61540b5..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_edit_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_mosaic.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_mosaic.png Binary files differdeleted file mode 100644 index 6e49d3c..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_mosaic.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_mosaic_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_mosaic_selected.png Binary files differdeleted file mode 100644 index 86edb6e..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_mosaic_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_print.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_print.png Binary files differdeleted file mode 100644 index b235536..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_print.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_print_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_print_selected.png Binary files differdeleted file mode 100644 index 657b9c8..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_print_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_redo.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_redo.png Binary files differdeleted file mode 100644 index 7b4703b..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_redo.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_redo_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_redo_selected.png Binary files differdeleted file mode 100644 index 0022a13..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_redo_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate.png Binary files differdeleted file mode 100644 index c60f258..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_left.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_left.png Binary files differdeleted file mode 100644 index ef2f21f..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_left.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_left_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_left_selected.png Binary files differdeleted file mode 100644 index 1e4c1d6..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_left_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_selected.png Binary files differdeleted file mode 100644 index 445350f..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_rotate_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_share.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_share.png Binary files differdeleted file mode 100644 index 36bb221..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_share.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_share_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_share_selected.png Binary files differdeleted file mode 100644 index 438e8a2..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_share_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_slideshow.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_slideshow.png Binary files differdeleted file mode 100644 index 72763d4..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_slideshow.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_slideshow_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_slideshow_selected.png Binary files differdeleted file mode 100644 index 4f80a48..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_slideshow_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_undo.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_undo.png Binary files differdeleted file mode 100644 index 79e3fdd..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_undo.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/icon_undo_selected.png b/ui/file_manager/file_manager/foreground/images/gallery/icon_undo_selected.png Binary files differdeleted file mode 100644 index d5d13a7..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/icon_undo_selected.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/slider_thumb.png b/ui/file_manager/file_manager/foreground/images/gallery/slider_thumb.png Binary files differdeleted file mode 100644 index cb2d712..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/slider_thumb.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/slideshow-end.png b/ui/file_manager/file_manager/foreground/images/gallery/slideshow-end.png Binary files differdeleted file mode 100644 index f4371115..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/slideshow-end.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/slideshow-pause.png b/ui/file_manager/file_manager/foreground/images/gallery/slideshow-pause.png Binary files differdeleted file mode 100644 index 2170ce9..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/slideshow-pause.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/images/gallery/slideshow-play.png b/ui/file_manager/file_manager/foreground/images/gallery/slideshow-play.png Binary files differdeleted file mode 100644 index f949121..0000000 --- a/ui/file_manager/file_manager/foreground/images/gallery/slideshow-play.png +++ /dev/null diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js index 67fd2c7..99d86d7 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager.js @@ -860,8 +860,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; this.dialogDom_.ownerDocument.defaultView.addEventListener( 'resize', this.onResize_.bind(this)); - this.filePopup_ = null; - this.searchBoxWrapper_ = this.ui_.searchBox.element; this.searchBox_ = this.ui_.searchBox.inputElement; this.searchBox_.addEventListener( @@ -1578,29 +1576,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; return; var task = null; - // Handle restoring after crash, or the gallery action. - // TODO(mtomasz): Use the gallery action instead of just the gallery - // field. - if (this.params_.gallery || - this.params_.action === 'gallery' || - this.params_.action === 'gallery-video') { - if (!opt_selectionEntry) { - // Non-existent file or a directory. - // Reloading while the Gallery is open with empty or multiple - // selection. Open the Gallery when the directory is scanned. - task = function() { - new FileTasks(this, this.params_).openGallery([]); - }.bind(this); - } else { - // The file or the directory exists. - task = function() { - new FileTasks(this, this.params_).openGallery([opt_selectionEntry]); - }.bind(this); - } - } else { - // TODO(mtomasz): Implement remounting archives after crash. - // See: crbug.com/333139 - } + + // TODO(mtomasz): Implement remounting archives after crash. + // See: crbug.com/333139 // If there is a task to be run, run it after the scan is completed. if (task) { @@ -1876,64 +1854,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; }; /** - * Shows a modal-like file viewer/editor on top of the File Manager UI. - * - * @param {HTMLElement} popup Popup element. - * @param {function()} closeCallback Function to call after the popup is - * closed. - */ - FileManager.prototype.openFilePopup = function(popup, closeCallback) { - this.closeFilePopup(); - this.filePopup_ = popup; - this.filePopupCloseCallback_ = closeCallback; - this.dialogDom_.insertBefore( - this.filePopup_, this.dialogDom_.querySelector('#iframe-drag-area')); - this.filePopup_.focus(); - this.document_.body.setAttribute('overlay-visible', ''); - this.document_.querySelector('#iframe-drag-area').hidden = false; - }; - - /** - * Closes the modal-like file viewer/editor popup. - */ - FileManager.prototype.closeFilePopup = function() { - if (this.filePopup_) { - this.document_.body.removeAttribute('overlay-visible'); - this.document_.querySelector('#iframe-drag-area').hidden = true; - // The window resize would not be processed properly while the relevant - // divs had 'display:none', force resize after the layout fired. - setTimeout(this.onResize_.bind(this), 0); - if (this.filePopup_.contentWindow && - this.filePopup_.contentWindow.unload) { - this.filePopup_.contentWindow.unload(); - } - - if (this.filePopupCloseCallback_) { - this.filePopupCloseCallback_(); - this.filePopupCloseCallback_ = null; - } - - // These operations have to be in the end, otherwise v8 crashes on an - // assert. See: crbug.com/224174. - this.dialogDom_.removeChild(this.filePopup_); - this.filePopup_ = null; - } - }; - - /** - * Updates visibility of the draggable app region in the modal-like file - * viewer/editor. - * - * @param {boolean} visible True for visible, false otherwise. - */ - FileManager.prototype.onFilePopupAppRegionChanged = function(visible) { - if (!this.filePopup_) - return; - - this.document_.querySelector('#iframe-drag-area').hidden = !visible; - }; - - /** * @return {Array.<Entry>} List of all entries in the current directory. */ FileManager.prototype.getAllEntriesInCurrentDirectory = function() { @@ -2430,10 +2350,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52; this.directoryModel_.dispose(); if (this.volumeManager_) this.volumeManager_.dispose(); - if (this.filePopup_ && - this.filePopup_.contentWindow && - this.filePopup_.contentWindow.unload) - this.filePopup_.contentWindow.unload(true /* exiting */); if (this.progressCenterPanel_) this.backgroundPage_.background.progressCenter.removePanel( this.progressCenterPanel_); diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js index d430047..e26c696 100644 --- a/ui/file_manager/file_manager/foreground/js/file_tasks.js +++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js @@ -192,9 +192,7 @@ FileTasks.isInternalTask_ = function(taskId) { return (appId === chrome.runtime.id && taskType === 'file' && (actionId === 'play' || - actionId === 'mount-archive' || - actionId === 'gallery' || - actionId === 'gallery-video')); + actionId === 'mount-archive')); }; /** @@ -236,10 +234,6 @@ FileTasks.prototype.processTasks_ = function(tasks) { } else if (taskParts[2] === 'mount-archive') { task.iconType = 'archive'; task.title = loadTimeData.getString('MOUNT_ARCHIVE'); - } else if (taskParts[2] === 'gallery' || - taskParts[2] === 'gallery-video') { - task.iconType = 'image'; - task.title = loadTimeData.getString('ACTION_OPEN'); } else if (taskParts[2] === 'open-hosted-generic') { if (this.entries_.length > 1) task.iconType = 'generic'; @@ -560,11 +554,6 @@ FileTasks.prototype.executeInternalTask_ = function(id, entries) { return; } - if (id === 'gallery' || id === 'gallery-video') { - this.openGalleryInternal_(entries); - return; - } - console.error('Unexpected action ID: ' + id); }; @@ -623,103 +612,6 @@ FileTasks.prototype.mountArchivesInternal_ = function(entries) { }; /** - * Open the Gallery. - * - * @param {Array.<Entry>} entries List of selected entries. - */ -FileTasks.prototype.openGallery = function(entries) { - FileTasks.recordViewingFileTypeUMA_(entries); - this.openGalleryInternal_(entries); -}; - -/** - * The core implementation to open the Gallery. - * - * @param {Array.<Entry>} entries List of selected entries. - * @private - */ -FileTasks.prototype.openGalleryInternal_ = function(entries) { - var fm = this.fileManager_; - - var allEntries = - fm.getAllEntriesInCurrentDirectory().filter(FileType.isImageOrVideo); - - var galleryFrame = fm.document_.createElement('iframe'); - galleryFrame.className = 'overlay-pane'; - galleryFrame.scrolling = 'no'; - galleryFrame.setAttribute('webkitallowfullscreen', true); - - if (this.params_ && this.params_.gallery) { - // Remove the Gallery state from the location, we do not need it any more. - // TODO(mtomasz): Consider keeping the selection path. - util.updateAppState( - null, /* keep current directory */ - '', /* remove current selection */ - '' /* remove search. */); - } - - var savedAppState = JSON.parse(JSON.stringify(window.appState)); - var savedTitle = document.title; - - // Push a temporary state which will be replaced every time the selection - // changes in the Gallery and popped when the Gallery is closed. - util.updateAppState(); - - var onBack = function(selectedEntries) { - fm.directoryModel.selectEntries(selectedEntries); - fm.closeFilePopup(); // Will call Gallery.unload. - window.appState = savedAppState; - util.saveAppState(); - document.title = savedTitle; - }; - - var onAppRegionChanged = function(visible) { - fm.onFilePopupAppRegionChanged(visible); - }; - - galleryFrame.onload = function() { - galleryFrame.contentWindow.ImageUtil.metrics = metrics; - - // TODO(haruki): isOnReadonlyDirectory() only checks the permission for the - // root. We should check more granular permission to know whether the file - // is writable or not. - var readonly = fm.isOnReadonlyDirectory(); - var currentDir = fm.getCurrentDirectoryEntry(); - var downloadsVolume = fm.volumeManager.getCurrentProfileVolumeInfo( - VolumeManagerCommon.RootType.DOWNLOADS); - var downloadsDir = downloadsVolume && downloadsVolume.fileSystem.root; - - // TODO(mtomasz): Pass Entry instead of localized name. Conversion to a - // display string should be done in gallery.js. - var readonlyDirName = null; - if (readonly && currentDir) - readonlyDirName = util.getEntryLabel(fm.volumeManager, currentDir); - - var context = { - // We show the root label in readonly warning (e.g. archive name). - readonlyDirName: readonlyDirName, - curDirEntry: currentDir, - saveDirEntry: readonly ? downloadsDir : null, - searchResults: fm.directoryModel.isSearching(), - metadataCache: fm.metadataCache_, - pageState: this.params_, - appWindow: chrome.app.window.current(), - onBack: onBack, - onClose: fm.onClose.bind(fm), - onMaximize: fm.onMaximize.bind(fm), - onMinimize: fm.onMinimize.bind(fm), - onAppRegionChanged: onAppRegionChanged, - loadTimeData: fm.backgroundPage.background.stringData - }; - galleryFrame.contentWindow.Gallery.open( - context, fm.volumeManager, allEntries, entries); - }.bind(this); - - galleryFrame.src = 'gallery.html'; - fm.openFilePopup(galleryFrame, fm.updateTitle_.bind(fm)); -}; - -/** * Displays the list of tasks in a task picker combobutton. * * @param {cr.ui.ComboButton} combobutton The task picker element. diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/commands.js b/ui/file_manager/file_manager/foreground/js/image_editor/commands.js deleted file mode 100644 index e67907d..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/commands.js +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright 2014 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'; - -/** - * Command queue is the only way to modify images. - * Supports undo/redo. - * Command execution is asynchronous (callback-based). - * - * @param {Document} document Document to create canvases in. - * @param {HTMLCanvasElement} canvas The canvas with the original image. - * @param {function(callback)} saveFunction Function to save the image. - * @constructor - */ -function CommandQueue(document, canvas, saveFunction) { - this.document_ = document; - this.undo_ = []; - this.redo_ = []; - this.subscribers_ = []; - this.currentImage_ = canvas; - - // Current image may be null or not-null but with width = height = 0. - // Copying an image with zero dimensions causes js errors. - if (this.currentImage_) { - this.baselineImage_ = document.createElement('canvas'); - this.baselineImage_.width = this.currentImage_.width; - this.baselineImage_.height = this.currentImage_.height; - if (this.currentImage_.width > 0 && this.currentImage_.height > 0) { - var context = this.baselineImage_.getContext('2d'); - context.drawImage(this.currentImage_, 0, 0); - } - } else { - this.baselineImage_ = null; - } - - this.previousImage_ = document.createElement('canvas'); - this.previousImageAvailable_ = false; - - this.saveFunction_ = saveFunction; - this.busy_ = false; - this.UIContext_ = {}; -} - -/** - * Attach the UI elements to the command queue. - * Once the UI is attached the results of image manipulations are displayed. - * - * @param {ImageView} imageView The ImageView object to display the results. - * @param {ImageEditor.Prompt} prompt Prompt to use with this CommandQueue. - * @param {function(boolean)} lock Function to enable/disable buttons etc. - */ -CommandQueue.prototype.attachUI = function(imageView, prompt, lock) { - this.UIContext_ = { - imageView: imageView, - prompt: prompt, - lock: lock - }; -}; - -/** - * Execute the action when the queue is not busy. - * @param {function} callback Callback. - */ -CommandQueue.prototype.executeWhenReady = function(callback) { - if (this.isBusy()) - this.subscribers_.push(callback); - else - setTimeout(callback, 0); -}; - -/** - * @return {boolean} True if the command queue is busy. - */ -CommandQueue.prototype.isBusy = function() { return this.busy_ }; - -/** - * Set the queue state to busy. Lock the UI. - * @private - */ -CommandQueue.prototype.setBusy_ = function() { - if (this.busy_) - throw new Error('CommandQueue already busy'); - - this.busy_ = true; - - if (this.UIContext_.lock) - this.UIContext_.lock(true); - - ImageUtil.trace.resetTimer('command-busy'); -}; - -/** - * Set the queue state to not busy. Unlock the UI and execute pending actions. - * @private - */ -CommandQueue.prototype.clearBusy_ = function() { - if (!this.busy_) - throw new Error('Inconsistent CommandQueue already not busy'); - - this.busy_ = false; - - // Execute the actions requested while the queue was busy. - while (this.subscribers_.length) - this.subscribers_.shift()(); - - if (this.UIContext_.lock) - this.UIContext_.lock(false); - - ImageUtil.trace.reportTimer('command-busy'); -}; - -/** - * Commit the image change: save and unlock the UI. - * @param {number=} opt_delay Delay in ms (to avoid disrupting the animation). - * @private - */ -CommandQueue.prototype.commit_ = function(opt_delay) { - setTimeout(this.saveFunction_.bind(null, this.clearBusy_.bind(this)), - opt_delay || 0); -}; - -/** - * Internal function to execute the command in a given context. - * - * @param {Command} command The command to execute. - * @param {Object} uiContext The UI context. - * @param {function} callback Completion callback. - * @private - */ -CommandQueue.prototype.doExecute_ = function(command, uiContext, callback) { - if (!this.currentImage_) - throw new Error('Cannot operate on null image'); - - // Remember one previous image so that the first undo is as fast as possible. - this.previousImage_.width = this.currentImage_.width; - this.previousImage_.height = this.currentImage_.height; - this.previousImageAvailable_ = true; - var context = this.previousImage_.getContext('2d'); - context.drawImage(this.currentImage_, 0, 0); - - command.execute( - this.document_, - this.currentImage_, - function(result, opt_delay) { - this.currentImage_ = result; - callback(opt_delay); - }.bind(this), - uiContext); -}; - -/** - * Executes the command. - * - * @param {Command} command Command to execute. - * @param {boolean=} opt_keep_redo True if redo stack should not be cleared. - */ -CommandQueue.prototype.execute = function(command, opt_keep_redo) { - this.setBusy_(); - - if (!opt_keep_redo) - this.redo_ = []; - - this.undo_.push(command); - - this.doExecute_(command, this.UIContext_, this.commit_.bind(this)); -}; - -/** - * @return {boolean} True if Undo is applicable. - */ -CommandQueue.prototype.canUndo = function() { - return this.undo_.length != 0; -}; - -/** - * Undo the most recent command. - */ -CommandQueue.prototype.undo = function() { - if (!this.canUndo()) - throw new Error('Cannot undo'); - - this.setBusy_(); - - var command = this.undo_.pop(); - this.redo_.push(command); - - var self = this; - - function complete() { - var delay = command.revertView( - self.currentImage_, self.UIContext_.imageView); - self.commit_(delay); - } - - if (this.previousImageAvailable_) { - // First undo after an execute call. - this.currentImage_.width = this.previousImage_.width; - this.currentImage_.height = this.previousImage_.height; - var context = this.currentImage_.getContext('2d'); - context.drawImage(this.previousImage_, 0, 0); - - // Free memory. - this.previousImage_.width = 0; - this.previousImage_.height = 0; - this.previousImageAvailable_ = false; - - complete(); - // TODO(kaznacheev) Consider recalculating previousImage_ right here - // by replaying the commands in the background. - } else { - this.currentImage_.width = this.baselineImage_.width; - this.currentImage_.height = this.baselineImage_.height; - var context = this.currentImage_.getContext('2d'); - context.drawImage(this.baselineImage_, 0, 0); - - var replay = function(index) { - if (index < self.undo_.length) - self.doExecute_(self.undo_[index], {}, replay.bind(null, index + 1)); - else { - complete(); - } - }; - - replay(0); - } -}; - -/** - * @return {boolean} True if Redo is applicable. - */ -CommandQueue.prototype.canRedo = function() { - return this.redo_.length != 0; -}; - -/** - * Repeat the command that was recently un-done. - */ -CommandQueue.prototype.redo = function() { - if (!this.canRedo()) - throw new Error('Cannot redo'); - - this.execute(this.redo_.pop(), true); -}; - -/** - * Closes internal buffers. Call to ensure, that internal buffers are freed - * as soon as possible. - */ -CommandQueue.prototype.close = function() { - // Free memory used by the undo buffer. - this.previousImage_.width = 0; - this.previousImage_.height = 0; - this.previousImageAvailable_ = false; - - if (this.baselineImage_) { - this.baselineImage_.width = 0; - this.baselineImage_.height = 0; - } -}; - -/** - * Command object encapsulates an operation on an image and a way to visualize - * its result. - * - * @param {string} name Command name. - * @constructor - */ -function Command(name) { - this.name_ = name; -} - -/** - * @return {string} String representation of the command. - */ -Command.prototype.toString = function() { - return 'Command ' + this.name_; -}; - -/** - * Execute the command and visualize its results. - * - * The two actions are combined into one method because sometimes it is nice - * to be able to show partial results for slower operations. - * - * @param {Document} document Document on which to execute command. - * @param {HTMLCanvasElement} srcCanvas Canvas to execute on. - * @param {function(HTMLCanvasElement, number)} callback Callback to call on - * completion. - * @param {Object} uiContext Context to work in. - */ -Command.prototype.execute = function(document, srcCanvas, callback, uiContext) { - console.error('Command.prototype.execute not implemented'); -}; - -/** - * Visualize reversion of the operation. - * - * @param {HTMLCanvasElement} canvas Image data to use. - * @param {ImageView} imageView ImageView to revert. - * @return {number} Animation duration in ms. - */ -Command.prototype.revertView = function(canvas, imageView) { - imageView.replace(canvas); - return 0; -}; - -/** - * Creates canvas to render on. - * - * @param {Document} document Document to create canvas in. - * @param {HTMLCanvasElement} srcCanvas to copy optional dimensions from. - * @param {number=} opt_width new canvas width. - * @param {number=} opt_height new canvas height. - * @return {HTMLCanvasElement} Newly created canvas. - * @private - */ -Command.prototype.createCanvas_ = function( - document, srcCanvas, opt_width, opt_height) { - var result = document.createElement('canvas'); - result.width = opt_width || srcCanvas.width; - result.height = opt_height || srcCanvas.height; - return result; -}; - - -/** - * Rotate command - * @param {number} rotate90 Rotation angle in 90 degree increments (signed). - * @constructor - * @extends {Command} - */ -Command.Rotate = function(rotate90) { - Command.call(this, 'rotate(' + rotate90 * 90 + 'deg)'); - this.rotate90_ = rotate90; -}; - -Command.Rotate.prototype = { __proto__: Command.prototype }; - -/** @override */ -Command.Rotate.prototype.execute = function( - document, srcCanvas, callback, uiContext) { - var result = this.createCanvas_( - document, - srcCanvas, - (this.rotate90_ & 1) ? srcCanvas.height : srcCanvas.width, - (this.rotate90_ & 1) ? srcCanvas.width : srcCanvas.height); - ImageUtil.drawImageTransformed( - result, srcCanvas, 1, 1, this.rotate90_ * Math.PI / 2); - var delay; - if (uiContext.imageView) { - delay = uiContext.imageView.replaceAndAnimate(result, null, this.rotate90_); - } - setTimeout(callback, 0, result, delay); -}; - -/** @override */ -Command.Rotate.prototype.revertView = function(canvas, imageView) { - return imageView.replaceAndAnimate(canvas, null, -this.rotate90_); -}; - - -/** - * Crop command. - * - * @param {Rect} imageRect Crop rectangle in image coordinates. - * @constructor - * @extends {Command} - */ -Command.Crop = function(imageRect) { - Command.call(this, 'crop' + imageRect.toString()); - this.imageRect_ = imageRect; -}; - -Command.Crop.prototype = { __proto__: Command.prototype }; - -/** @override */ -Command.Crop.prototype.execute = function( - document, srcCanvas, callback, uiContext) { - var result = this.createCanvas_( - document, srcCanvas, this.imageRect_.width, this.imageRect_.height); - Rect.drawImage(result.getContext('2d'), srcCanvas, null, this.imageRect_); - var delay; - if (uiContext.imageView) { - delay = uiContext.imageView.replaceAndAnimate(result, this.imageRect_, 0); - } - setTimeout(callback, 0, result, delay); -}; - -/** @override */ -Command.Crop.prototype.revertView = function(canvas, imageView) { - return imageView.animateAndReplace(canvas, this.imageRect_); -}; - - -/** - * Filter command. - * - * @param {string} name Command name. - * @param {function(ImageData,ImageData,number,number)} filter Filter function. - * @param {string} message Message to display when done. - * @constructor - * @extends {Command} - */ -Command.Filter = function(name, filter, message) { - Command.call(this, name); - this.filter_ = filter; - this.message_ = message; -}; - -Command.Filter.prototype = { __proto__: Command.prototype }; - -/** @override */ -Command.Filter.prototype.execute = function( - document, srcCanvas, callback, uiContext) { - var result = this.createCanvas_(document, srcCanvas); - - var self = this; - - var previousRow = 0; - - function onProgressVisible(updatedRow, rowCount) { - if (updatedRow == rowCount) { - uiContext.imageView.replace(result); - if (self.message_) - uiContext.prompt.show(self.message_, 2000); - callback(result); - } else { - var viewport = uiContext.imageView.viewport_; - - var imageStrip = new Rect(viewport.getImageBounds()); - imageStrip.top = previousRow; - imageStrip.height = updatedRow - previousRow; - - var screenStrip = new Rect(viewport.getImageBoundsOnScreen()); - screenStrip.top = Math.round(viewport.imageToScreenY(previousRow)); - screenStrip.height = - Math.round(viewport.imageToScreenY(updatedRow)) - screenStrip.top; - - uiContext.imageView.paintDeviceRect( - viewport.screenToDeviceRect(screenStrip), result, imageStrip); - previousRow = updatedRow; - } - } - - function onProgressInvisible(updatedRow, rowCount) { - if (updatedRow == rowCount) { - callback(result); - } - } - - filter.applyByStrips(result, srcCanvas, this.filter_, - uiContext.imageView ? onProgressVisible : onProgressInvisible); -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/exif_encoder.js b/ui/file_manager/file_manager/foreground/js/image_editor/exif_encoder.js deleted file mode 100644 index 3787958..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/exif_encoder.js +++ /dev/null @@ -1,569 +0,0 @@ -// Copyright 2014 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'; - -// TODO:(kaznacheev) Share the EXIF constants with exif_parser.js -var EXIF_MARK_SOS = 0xffda; // Start of "stream" (the actual image data). -var EXIF_MARK_SOI = 0xffd8; // Start of image data. -var EXIF_MARK_EOI = 0xffd9; // End of image data. - -var EXIF_MARK_APP0 = 0xffe0; // APP0 block, most commonly JFIF data. -var EXIF_MARK_EXIF = 0xffe1; // Start of exif block. - -var EXIF_ALIGN_LITTLE = 0x4949; // Indicates little endian exif data. -var EXIF_ALIGN_BIG = 0x4d4d; // Indicates big endian exif data. - -var EXIF_TAG_TIFF = 0x002a; // First directory containing TIFF data. -var EXIF_TAG_GPSDATA = 0x8825; // Pointer from TIFF to the GPS directory. -var EXIF_TAG_EXIFDATA = 0x8769; // Pointer from TIFF to the EXIF IFD. - -var EXIF_TAG_JPG_THUMB_OFFSET = 0x0201; // Pointer from TIFF to thumbnail. -var EXIF_TAG_JPG_THUMB_LENGTH = 0x0202; // Length of thumbnail data. - -var EXIF_TAG_IMAGE_WIDTH = 0x0100; -var EXIF_TAG_IMAGE_HEIGHT = 0x0101; - -var EXIF_TAG_ORIENTATION = 0x0112; -var EXIF_TAG_X_DIMENSION = 0xA002; -var EXIF_TAG_Y_DIMENSION = 0xA003; - -/** - * The Exif metadata encoder. - * Uses the metadata format as defined by ExifParser. - * @param {Object} original_metadata Metadata to encode. - * @constructor - * @extends {ImageEncoder.MetadataEncoder} - */ -function ExifEncoder(original_metadata) { - ImageEncoder.MetadataEncoder.apply(this, arguments); - - this.ifd_ = this.metadata_.ifd; - if (!this.ifd_) - this.ifd_ = this.metadata_.ifd = {}; -} - -ExifEncoder.prototype = {__proto__: ImageEncoder.MetadataEncoder.prototype}; - -ImageEncoder.registerMetadataEncoder(ExifEncoder, 'image/jpeg'); - -/** - * @param {HTMLCanvasElement|Object} canvas Canvas or anything with - * width and height properties. - */ -ExifEncoder.prototype.setImageData = function(canvas) { - var image = this.ifd_.image; - if (!image) - image = this.ifd_.image = {}; - - // Only update width/height in this directory if they are present. - if (image[EXIF_TAG_IMAGE_WIDTH] && image[EXIF_TAG_IMAGE_HEIGHT]) { - image[EXIF_TAG_IMAGE_WIDTH].value = canvas.width; - image[EXIF_TAG_IMAGE_HEIGHT].value = canvas.height; - } - - var exif = this.ifd_.exif; - if (!exif) - exif = this.ifd_.exif = {}; - ExifEncoder.findOrCreateTag(image, EXIF_TAG_EXIFDATA); - ExifEncoder.findOrCreateTag(exif, EXIF_TAG_X_DIMENSION).value = canvas.width; - ExifEncoder.findOrCreateTag(exif, EXIF_TAG_Y_DIMENSION).value = canvas.height; - - this.metadata_.width = canvas.width; - this.metadata_.height = canvas.height; - - // Always save in default orientation. - delete this.metadata_.imageTransform; - ExifEncoder.findOrCreateTag(image, EXIF_TAG_ORIENTATION).value = 1; -}; - - -/** - * @param {HTMLCanvasElement} canvas Thumbnail canvas. - * @param {number} quality (0..1] Thumbnail encoding quality. - */ -ExifEncoder.prototype.setThumbnailData = function(canvas, quality) { - // Empirical formula with reasonable behavior: - // 10K for 1Mpix, 30K for 5Mpix, 50K for 9Mpix and up. - var pixelCount = this.metadata_.width * this.metadata_.height; - var maxEncodedSize = 5000 * Math.min(10, 1 + pixelCount / 1000000); - - var DATA_URL_PREFIX = 'data:' + this.mimeType + ';base64,'; - var BASE64_BLOAT = 4 / 3; - var maxDataURLLength = - DATA_URL_PREFIX.length + Math.ceil(maxEncodedSize * BASE64_BLOAT); - - for (;; quality *= 0.8) { - ImageEncoder.MetadataEncoder.prototype.setThumbnailData.call( - this, canvas, quality); - if (this.metadata_.thumbnailURL.length <= maxDataURLLength || quality < 0.2) - break; - } - - if (this.metadata_.thumbnailURL.length <= maxDataURLLength) { - var thumbnail = this.ifd_.thumbnail; - if (!thumbnail) - thumbnail = this.ifd_.thumbnail = {}; - - ExifEncoder.findOrCreateTag(thumbnail, EXIF_TAG_IMAGE_WIDTH).value = - canvas.width; - - ExifEncoder.findOrCreateTag(thumbnail, EXIF_TAG_IMAGE_HEIGHT).value = - canvas.height; - - // The values for these tags will be set in ExifWriter.encode. - ExifEncoder.findOrCreateTag(thumbnail, EXIF_TAG_JPG_THUMB_OFFSET); - ExifEncoder.findOrCreateTag(thumbnail, EXIF_TAG_JPG_THUMB_LENGTH); - - // Always save in default orientation. - ExifEncoder.findOrCreateTag(thumbnail, EXIF_TAG_ORIENTATION).value = 1; - } else { - console.warn( - 'Thumbnail URL too long: ' + this.metadata_.thumbnailURL.length); - // Delete thumbnail ifd so that it is not written out to a file, but - // keep thumbnailURL for display purposes. - if (this.ifd_.thumbnail) { - delete this.ifd_.thumbnail; - } - } - delete this.metadata_.thumbnailTransform; -}; - -/** - * Return a range where the metadata is (or should be) located. - * @param {string} encodedImage Raw image data to look for metadata. - * @return {Object} An object with from and to properties. - */ -ExifEncoder.prototype.findInsertionRange = function(encodedImage) { - function getWord(pos) { - if (pos + 2 > encodedImage.length) - throw 'Reading past the buffer end @' + pos; - return encodedImage.charCodeAt(pos) << 8 | encodedImage.charCodeAt(pos + 1); - } - - if (getWord(0) != EXIF_MARK_SOI) - throw new Error('Jpeg data starts from 0x' + getWord(0).toString(16)); - - var sectionStart = 2; - - // Default: an empty range right after SOI. - // Will be returned in absence of APP0 or Exif sections. - var range = {from: sectionStart, to: sectionStart}; - - for (;;) { - var tag = getWord(sectionStart); - - if (tag == EXIF_MARK_SOS) - break; - - var nextSectionStart = sectionStart + 2 + getWord(sectionStart + 2); - if (nextSectionStart <= sectionStart || - nextSectionStart > encodedImage.length) - throw new Error('Invalid section size in jpeg data'); - - if (tag == EXIF_MARK_APP0) { - // Assert that we have not seen the Exif section yet. - if (range.from != range.to) - throw new Error('APP0 section found after EXIF section'); - // An empty range right after the APP0 segment. - range.from = range.to = nextSectionStart; - } else if (tag == EXIF_MARK_EXIF) { - // A range containing the existing EXIF section. - range.from = sectionStart; - range.to = nextSectionStart; - } - sectionStart = nextSectionStart; - } - - return range; -}; - -/** - * @return {ArrayBuffer} serialized metadata ready to write to an image file. - */ -ExifEncoder.prototype.encode = function() { - var HEADER_SIZE = 10; - - // Allocate the largest theoretically possible size. - var bytes = new Uint8Array(0x10000); - - // Serialize header - var hw = new ByteWriter(bytes.buffer, 0, HEADER_SIZE); - hw.writeScalar(EXIF_MARK_EXIF, 2); - hw.forward('size', 2); - hw.writeString('Exif\0\0'); // Magic string. - - // First serialize the content of the exif section. - // Use a ByteWriter starting at HEADER_SIZE offset so that tell() positions - // can be directly mapped to offsets as encoded in the dictionaries. - var bw = new ByteWriter(bytes.buffer, HEADER_SIZE); - - if (this.metadata_.littleEndian) { - bw.setByteOrder(ByteWriter.LITTLE_ENDIAN); - bw.writeScalar(EXIF_ALIGN_LITTLE, 2); - } else { - bw.setByteOrder(ByteWriter.BIG_ENDIAN); - bw.writeScalar(EXIF_ALIGN_BIG, 2); - } - - bw.writeScalar(EXIF_TAG_TIFF, 2); - - bw.forward('image-dir', 4); // The pointer should point right after itself. - bw.resolveOffset('image-dir'); - - ExifEncoder.encodeDirectory(bw, this.ifd_.image, - [EXIF_TAG_EXIFDATA, EXIF_TAG_GPSDATA], 'thumb-dir'); - - if (this.ifd_.exif) { - bw.resolveOffset(EXIF_TAG_EXIFDATA); - ExifEncoder.encodeDirectory(bw, this.ifd_.exif); - } else { - if (EXIF_TAG_EXIFDATA in this.ifd_.image) - throw new Error('Corrupt exif dictionary reference'); - } - - if (this.ifd_.gps) { - bw.resolveOffset(EXIF_TAG_GPSDATA); - ExifEncoder.encodeDirectory(bw, this.ifd_.gps); - } else { - if (EXIF_TAG_GPSDATA in this.ifd_.image) - throw new Error('Missing gps dictionary reference'); - } - - if (this.ifd_.thumbnail) { - bw.resolveOffset('thumb-dir'); - ExifEncoder.encodeDirectory( - bw, - this.ifd_.thumbnail, - [EXIF_TAG_JPG_THUMB_OFFSET, EXIF_TAG_JPG_THUMB_LENGTH]); - - var thumbnailDecoded = - ImageEncoder.decodeDataURL(this.metadata_.thumbnailURL); - bw.resolveOffset(EXIF_TAG_JPG_THUMB_OFFSET); - bw.resolve(EXIF_TAG_JPG_THUMB_LENGTH, thumbnailDecoded.length); - bw.writeString(thumbnailDecoded); - } else { - bw.resolve('thumb-dir', 0); - } - - bw.checkResolved(); - - var totalSize = HEADER_SIZE + bw.tell(); - hw.resolve('size', totalSize - 2); // The marker is excluded. - hw.checkResolved(); - - var subarray = new Uint8Array(totalSize); - for (var i = 0; i != totalSize; i++) { - subarray[i] = bytes[i]; - } - return subarray.buffer; -}; - -/* - * Static methods. - */ - -/** - * Write the contents of an IFD directory. - * @param {ByteWriter} bw ByteWriter to use. - * @param {Object} directory A directory map as created by ExifParser. - * @param {Array} resolveLater An array of tag ids for which the values will be - * resolved later. - * @param {string} nextDirPointer A forward key for the pointer to the next - * directory. If omitted the pointer is set to 0. - */ -ExifEncoder.encodeDirectory = function( - bw, directory, resolveLater, nextDirPointer) { - - var longValues = []; - - bw.forward('dir-count', 2); - var count = 0; - - for (var key in directory) { - var tag = directory[key]; - bw.writeScalar(tag.id, 2); - bw.writeScalar(tag.format, 2); - bw.writeScalar(tag.componentCount, 4); - - var width = ExifEncoder.getComponentWidth(tag) * tag.componentCount; - - if (resolveLater && (resolveLater.indexOf(tag.id) >= 0)) { - // The actual value depends on further computations. - if (tag.componentCount != 1 || width > 4) - throw new Error('Cannot forward the pointer for ' + tag.id); - bw.forward(tag.id, width); - } else if (width <= 4) { - // The value fits into 4 bytes, write it immediately. - ExifEncoder.writeValue(bw, tag); - } else { - // The value does not fit, forward the 4 byte offset to the actual value. - width = 4; - bw.forward(tag.id, width); - longValues.push(tag); - } - bw.skip(4 - width); // Align so that the value take up exactly 4 bytes. - count++; - } - - bw.resolve('dir-count', count); - - if (nextDirPointer) { - bw.forward(nextDirPointer, 4); - } else { - bw.writeScalar(0, 4); - } - - // Write out the long values and resolve pointers. - for (var i = 0; i != longValues.length; i++) { - var longValue = longValues[i]; - bw.resolveOffset(longValue.id); - ExifEncoder.writeValue(bw, longValue); - } -}; - -/** - * @param {{format:number, id:number}} tag EXIF tag object. - * @return {number} Width in bytes of the data unit associated with this tag. - * TODO(kaznacheev): Share with ExifParser? - */ -ExifEncoder.getComponentWidth = function(tag) { - switch (tag.format) { - case 1: // Byte - case 2: // String - case 7: // Undefined - return 1; - - case 3: // Short - return 2; - - case 4: // Long - case 9: // Signed Long - return 4; - - case 5: // Rational - case 10: // Signed Rational - return 8; - - default: // ??? - console.warn('Unknown tag format 0x' + - Number(tag.id).toString(16) + ': ' + tag.format); - return 4; - } -}; - -/** - * Writes out the tag value. - * @param {ByteWriter} bw Writer to use. - * @param {Object} tag Tag, which value to write. - */ -ExifEncoder.writeValue = function(bw, tag) { - if (tag.format == 2) { // String - if (tag.componentCount != tag.value.length) { - throw new Error( - 'String size mismatch for 0x' + Number(tag.id).toString(16)); - } - bw.writeString(tag.value); - } else { // Scalar or rational - var width = ExifEncoder.getComponentWidth(tag); - - var writeComponent = function(value, signed) { - if (width == 8) { - bw.writeScalar(value[0], 4, signed); - bw.writeScalar(value[1], 4, signed); - } else { - bw.writeScalar(value, width, signed); - } - }; - - var signed = (tag.format == 9 || tag.format == 10); - if (tag.componentCount == 1) { - writeComponent(tag.value, signed); - } else { - for (var i = 0; i != tag.componentCount; i++) { - writeComponent(tag.value[i], signed); - } - } - } -}; - -/** - * @param {{Object.<number,Object>}} directory EXIF directory. - * @param {number} id Tag id. - * @param {number} format Tag format - * (used in {@link ExifEncoder#getComponentWidth}). - * @param {number} componentCount Number of components in this tag. - * @return {{id:number, format:number, componentCount:number}} - * Tag found or created. - */ -ExifEncoder.findOrCreateTag = function(directory, id, format, componentCount) { - if (!(id in directory)) { - directory[id] = { - id: id, - format: format || 3, // Short - componentCount: componentCount || 1 - }; - } - return directory[id]; -}; - -/** - * ByteWriter class. - * @param {ArrayBuffer} arrayBuffer Underlying buffer to use. - * @param {number} offset Offset at which to start writing. - * @param {number} length Maximum length to use. - * @class - * @constructor - */ -function ByteWriter(arrayBuffer, offset, length) { - length = length || (arrayBuffer.byteLength - offset); - this.view_ = new DataView(arrayBuffer, offset, length); - this.littleEndian_ = false; - this.pos_ = 0; - this.forwards_ = {}; -} - -/** - * Little endian byte order. - * @type {number} - */ -ByteWriter.LITTLE_ENDIAN = 0; - -/** - * Bug endian byte order. - * @type {number} - */ -ByteWriter.BIG_ENDIAN = 1; - -/** - * Set the byte ordering for future writes. - * @param {number} order ByteOrder to use {ByteWriter.LITTLE_ENDIAN} - * or {ByteWriter.BIG_ENDIAN}. - */ -ByteWriter.prototype.setByteOrder = function(order) { - this.littleEndian_ = (order == ByteWriter.LITTLE_ENDIAN); -}; - -/** - * @return {number} the current write position. - */ -ByteWriter.prototype.tell = function() { return this.pos_ }; - -/** - * Skips desired amount of bytes in output stream. - * @param {number} count Byte count to skip. - */ -ByteWriter.prototype.skip = function(count) { - this.validateWrite(count); - this.pos_ += count; -}; - -/** - * Check if the buffer has enough room to read 'width' bytes. Throws an error - * if it has not. - * @param {number} width Amount of bytes to check. - */ -ByteWriter.prototype.validateWrite = function(width) { - if (this.pos_ + width > this.view_.byteLength) - throw new Error('Writing past the end of the buffer'); -}; - -/** - * Writes scalar value to output stream. - * @param {number} value Value to write. - * @param {number} width Desired width of written value. - * @param {boolean=} opt_signed True if value represents signed number. - */ -ByteWriter.prototype.writeScalar = function(value, width, opt_signed) { - var method; -// The below switch is so verbose for two reasons: -// 1. V8 is faster on method names which are 'symbols'. -// 2. Method names are discoverable by full text search. - switch (width) { - case 1: - method = opt_signed ? 'setInt8' : 'setUint8'; - break; - - case 2: - method = opt_signed ? 'setInt16' : 'setUint16'; - break; - - case 4: - method = opt_signed ? 'setInt32' : 'setUint32'; - break; - - case 8: - method = opt_signed ? 'setInt64' : 'setUint64'; - break; - - default: - throw new Error('Invalid width: ' + width); - break; - } - - this.validateWrite(width); - this.view_[method](this.pos_, value, this.littleEndian_); - this.pos_ += width; -}; - -/** - * Writes string. - * @param {string} str String to write. - */ -ByteWriter.prototype.writeString = function(str) { - this.validateWrite(str.length); - for (var i = 0; i != str.length; i++) { - this.view_.setUint8(this.pos_++, str.charCodeAt(i)); - } -}; - -/** - * Allocate the space for 'width' bytes for the value that will be set later. - * To be followed by a 'resolve' call with the same key. - * @param {string} key A key to identify the value. - * @param {number} width Width of the value in bytes. - */ -ByteWriter.prototype.forward = function(key, width) { - if (key in this.forwards_) - throw new Error('Duplicate forward key ' + key); - this.validateWrite(width); - this.forwards_[key] = { - pos: this.pos_, - width: width - }; - this.pos_ += width; -}; - -/** - * Set the value previously allocated with a 'forward' call. - * @param {string} key A key to identify the value. - * @param {number} value value to write in pre-allocated space. - */ -ByteWriter.prototype.resolve = function(key, value) { - if (!(key in this.forwards_)) - throw new Error('Undeclared forward key ' + key.toString(16)); - var forward = this.forwards_[key]; - var curPos = this.pos_; - this.pos_ = forward.pos; - this.writeScalar(value, forward.width); - this.pos_ = curPos; - delete this.forwards_[key]; -}; - -/** - * A shortcut to resolve the value to the current write position. - * @param {string} key A key to identify pre-allocated position. - */ -ByteWriter.prototype.resolveOffset = function(key) { - this.resolve(key, this.tell()); -}; - -/** - * Check if every forward has been resolved, throw and error if not. - */ -ByteWriter.prototype.checkResolved = function() { - for (var key in this.forwards_) { - throw new Error('Unresolved forward pointer ' + key.toString(16)); - } -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/filter.js b/ui/file_manager/file_manager/foreground/js/image_editor/filter.js deleted file mode 100644 index a1a95dc1..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/filter.js +++ /dev/null @@ -1,612 +0,0 @@ -// Copyright 2014 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'; - -/** - * A namespace for image filter utilities. - */ -var filter = {}; - -/** - * Create a filter from name and options. - * - * @param {string} name Maps to a filter method name. - * @param {Object} options A map of filter-specific options. - * @return {function(ImageData,ImageData,number,number)} created function. - */ -filter.create = function(name, options) { - var filterFunc = filter[name](options); - return function() { - var time = Date.now(); - filterFunc.apply(null, arguments); - var dst = arguments[0]; - var mPixPerSec = dst.width * dst.height / 1000 / (Date.now() - time); - ImageUtil.trace.report(name, Math.round(mPixPerSec * 10) / 10 + 'Mps'); - } -}; - -/** - * Apply a filter to a image by splitting it into strips. - * - * To be used with large images to avoid freezing up the UI. - * - * @param {HTMLCanvasElement} dstCanvas Destination canvas. - * @param {HTMLCanvasElement} srcCanvas Source canvas. - * @param {function(ImageData,ImageData,number,number)} filterFunc Filter. - * @param {function(number, number)} progressCallback Progress callback. - * @param {number} maxPixelsPerStrip Pixel number to process at once. - */ -filter.applyByStrips = function( - dstCanvas, srcCanvas, filterFunc, progressCallback, maxPixelsPerStrip) { - var dstContext = dstCanvas.getContext('2d'); - var srcContext = srcCanvas.getContext('2d'); - var source = srcContext.getImageData(0, 0, srcCanvas.width, srcCanvas.height); - - var stripCount = Math.ceil(srcCanvas.width * srcCanvas.height / - (maxPixelsPerStrip || 1000000)); // 1 Mpix is a reasonable default. - - var strip = srcContext.getImageData(0, 0, - srcCanvas.width, Math.ceil(srcCanvas.height / stripCount)); - - var offset = 0; - - function filterStrip() { - // If the strip overlaps the bottom of the source image we cannot shrink it - // and we cannot fill it partially (since canvas.putImageData always draws - // the entire buffer). - // Instead we move the strip up several lines (converting those lines - // twice is a small price to pay). - if (offset > source.height - strip.height) { - offset = source.height - strip.height; - } - - filterFunc(strip, source, 0, offset); - dstContext.putImageData(strip, 0, offset); - - offset += strip.height; - - if (offset < source.height) { - setTimeout(filterStrip, 0); - } else { - ImageUtil.trace.reportTimer('filter-commit'); - } - - progressCallback(offset, source.height); - } - - ImageUtil.trace.resetTimer('filter-commit'); - filterStrip(); -}; - -/** - * Return a color histogram for an image. - * - * @param {HTMLCanvasElement|ImageData} source Image data to analyze. - * @return {{r: Array.<number>, g: Array.<number>, b: Array.<number>}} - * histogram. - */ -filter.getHistogram = function(source) { - var imageData; - if (source.constructor.name == 'HTMLCanvasElement') { - imageData = source.getContext('2d'). - getImageData(0, 0, source.width, source.height); - } else { - imageData = source; - } - - var r = []; - var g = []; - var b = []; - - for (var i = 0; i != 256; i++) { - r.push(0); - g.push(0); - b.push(0); - } - - var data = imageData.data; - var maxIndex = 4 * imageData.width * imageData.height; - for (var index = 0; index != maxIndex;) { - r[data[index++]]++; - g[data[index++]]++; - b[data[index++]]++; - index++; - } - - return { r: r, g: g, b: b }; -}; - -/** - * Compute the function for every integer value from 0 up to maxArg. - * - * Rounds and clips the results to fit the [0..255] range. - * Useful to speed up pixel manipulations. - * - * @param {number} maxArg Maximum argument value (inclusive). - * @param {function(number): number} func Function to precompute. - * @return {Uint8Array} Computed results. - */ -filter.precompute = function(maxArg, func) { - var results = new Uint8Array(maxArg + 1); - for (var arg = 0; arg <= maxArg; arg++) { - results[arg] = Math.max(0, Math.min(0xFF, Math.round(func(arg)))); - } - return results; -}; - -/** - * Convert pixels by applying conversion tables to each channel individually. - * - * @param {Array.<number>} rMap Red channel conversion table. - * @param {Array.<number>} gMap Green channel conversion table. - * @param {Array.<number>} bMap Blue channel conversion table. - * @param {ImageData} dst Destination image data. Can be smaller than the - * source, must completely fit inside the source. - * @param {ImageData} src Source image data. - * @param {number} offsetX Horizontal offset of dst relative to src. - * @param {number} offsetY Vertical offset of dst relative to src. - */ -filter.mapPixels = function(rMap, gMap, bMap, dst, src, offsetX, offsetY) { - var dstData = dst.data; - var dstWidth = dst.width; - var dstHeight = dst.height; - - var srcData = src.data; - var srcWidth = src.width; - var srcHeight = src.height; - - if (offsetX < 0 || offsetX + dstWidth > srcWidth || - offsetY < 0 || offsetY + dstHeight > srcHeight) - throw new Error('Invalid offset'); - - var dstIndex = 0; - for (var y = 0; y != dstHeight; y++) { - var srcIndex = (offsetX + (offsetY + y) * srcWidth) * 4; - for (var x = 0; x != dstWidth; x++) { - dstData[dstIndex++] = rMap[srcData[srcIndex++]]; - dstData[dstIndex++] = gMap[srcData[srcIndex++]]; - dstData[dstIndex++] = bMap[srcData[srcIndex++]]; - dstIndex++; - srcIndex++; - } - } -}; - -/** - * Number of digits after period(in binary form) to preserve. - * @type {number} - */ -filter.FIXED_POINT_SHIFT = 16; - -/** - * Maximum value that can be represented in fixed point without overflow. - * @type {number} - */ -filter.MAX_FLOAT_VALUE = 0x7FFFFFFF >> filter.FIXED_POINT_SHIFT; - -/** - * Converts floating point to fixed. - * @param {number} x Number to convert. - * @return {number} Converted number. - */ -filter.floatToFixedPoint = function(x) { - // Math.round on negative arguments causes V8 to deoptimize the calling - // function, so we are using >> 0 instead. - return (x * (1 << filter.FIXED_POINT_SHIFT)) >> 0; -}; - -/** - * Perform an image convolution with a symmetrical 5x5 matrix: - * - * 0 0 w3 0 0 - * 0 w2 w1 w2 0 - * w3 w1 w0 w1 w3 - * 0 w2 w1 w2 0 - * 0 0 w3 0 0 - * - * @param {Array.<number>} weights See the picture above. - * @param {ImageData} dst Destination image data. Can be smaller than the - * source, must completely fit inside the source. - * @param {ImageData} src Source image data. - * @param {number} offsetX Horizontal offset of dst relative to src. - * @param {number} offsetY Vertical offset of dst relative to src. - */ -filter.convolve5x5 = function(weights, dst, src, offsetX, offsetY) { - var w0 = filter.floatToFixedPoint(weights[0]); - var w1 = filter.floatToFixedPoint(weights[1]); - var w2 = filter.floatToFixedPoint(weights[2]); - var w3 = filter.floatToFixedPoint(weights[3]); - - var dstData = dst.data; - var dstWidth = dst.width; - var dstHeight = dst.height; - var dstStride = dstWidth * 4; - - var srcData = src.data; - var srcWidth = src.width; - var srcHeight = src.height; - var srcStride = srcWidth * 4; - var srcStride2 = srcStride * 2; - - if (offsetX < 0 || offsetX + dstWidth > srcWidth || - offsetY < 0 || offsetY + dstHeight > srcHeight) - throw new Error('Invalid offset'); - - // Javascript is not very good at inlining constants. - // We inline manually and assert that the constant is equal to the variable. - if (filter.FIXED_POINT_SHIFT != 16) - throw new Error('Wrong fixed point shift'); - - var margin = 2; - - var startX = Math.max(0, margin - offsetX); - var endX = Math.min(dstWidth, srcWidth - margin - offsetX); - - var startY = Math.max(0, margin - offsetY); - var endY = Math.min(dstHeight, srcHeight - margin - offsetY); - - for (var y = startY; y != endY; y++) { - var dstIndex = y * dstStride + startX * 4; - var srcIndex = (y + offsetY) * srcStride + (startX + offsetX) * 4; - - for (var x = startX; x != endX; x++) { - for (var c = 0; c != 3; c++) { - var sum = w0 * srcData[srcIndex] + - w1 * (srcData[srcIndex - 4] + - srcData[srcIndex + 4] + - srcData[srcIndex - srcStride] + - srcData[srcIndex + srcStride]) + - w2 * (srcData[srcIndex - srcStride - 4] + - srcData[srcIndex + srcStride - 4] + - srcData[srcIndex - srcStride + 4] + - srcData[srcIndex + srcStride + 4]) + - w3 * (srcData[srcIndex - 8] + - srcData[srcIndex + 8] + - srcData[srcIndex - srcStride2] + - srcData[srcIndex + srcStride2]); - if (sum < 0) - dstData[dstIndex++] = 0; - else if (sum > 0xFF0000) - dstData[dstIndex++] = 0xFF; - else - dstData[dstIndex++] = sum >> 16; - srcIndex++; - } - srcIndex++; - dstIndex++; - } - } -}; - -/** - * Compute the average color for the image. - * - * @param {ImageData} imageData Image data to analyze. - * @return {{r: number, g: number, b: number}} average color. - */ -filter.getAverageColor = function(imageData) { - var data = imageData.data; - var width = imageData.width; - var height = imageData.height; - - var total = 0; - var r = 0; - var g = 0; - var b = 0; - - var maxIndex = 4 * width * height; - for (var i = 0; i != maxIndex;) { - total++; - r += data[i++]; - g += data[i++]; - b += data[i++]; - i++; - } - if (total == 0) return { r: 0, g: 0, b: 0 }; - return { r: r / total, g: g / total, b: b / total }; -}; - -/** - * Compute the average color with more weight given to pixes at the center. - * - * @param {ImageData} imageData Image data to analyze. - * @return {{r: number, g: number, b: number}} weighted average color. - */ -filter.getWeightedAverageColor = function(imageData) { - var data = imageData.data; - var width = imageData.width; - var height = imageData.height; - - var total = 0; - var r = 0; - var g = 0; - var b = 0; - - var center = Math.floor(width / 2); - var maxDist = center * Math.sqrt(2); - maxDist *= 2; // Weaken the effect of distance - - var i = 0; - for (var x = 0; x != width; x++) { - for (var y = 0; y != height; y++) { - var dist = Math.sqrt( - (x - center) * (x - center) + (y - center) * (y - center)); - var weight = (maxDist - dist) / maxDist; - - total += weight; - r += data[i++] * weight; - g += data[i++] * weight; - b += data[i++] * weight; - i++; - } - } - if (total == 0) return { r: 0, g: 0, b: 0 }; - return { r: r / total, g: g / total, b: b / total }; -}; - -/** - * Copy part of src image to dst, applying matrix color filter on-the-fly. - * - * The copied part of src should completely fit into dst (there is no clipping - * on either side). - * - * @param {Array.<number>} matrix 3x3 color matrix. - * @param {ImageData} dst Destination image data. - * @param {ImageData} src Source image data. - * @param {number} offsetX X offset in source to start processing. - * @param {number} offsetY Y offset in source to start processing. - */ -filter.colorMatrix3x3 = function(matrix, dst, src, offsetX, offsetY) { - var c11 = filter.floatToFixedPoint(matrix[0]); - var c12 = filter.floatToFixedPoint(matrix[1]); - var c13 = filter.floatToFixedPoint(matrix[2]); - var c21 = filter.floatToFixedPoint(matrix[3]); - var c22 = filter.floatToFixedPoint(matrix[4]); - var c23 = filter.floatToFixedPoint(matrix[5]); - var c31 = filter.floatToFixedPoint(matrix[6]); - var c32 = filter.floatToFixedPoint(matrix[7]); - var c33 = filter.floatToFixedPoint(matrix[8]); - - var dstData = dst.data; - var dstWidth = dst.width; - var dstHeight = dst.height; - - var srcData = src.data; - var srcWidth = src.width; - var srcHeight = src.height; - - if (offsetX < 0 || offsetX + dstWidth > srcWidth || - offsetY < 0 || offsetY + dstHeight > srcHeight) - throw new Error('Invalid offset'); - - // Javascript is not very good at inlining constants. - // We inline manually and assert that the constant is equal to the variable. - if (filter.FIXED_POINT_SHIFT != 16) - throw new Error('Wrong fixed point shift'); - - var dstIndex = 0; - for (var y = 0; y != dstHeight; y++) { - var srcIndex = (offsetX + (offsetY + y) * srcWidth) * 4; - for (var x = 0; x != dstWidth; x++) { - var r = srcData[srcIndex++]; - var g = srcData[srcIndex++]; - var b = srcData[srcIndex++]; - srcIndex++; - - var rNew = r * c11 + g * c12 + b * c13; - var gNew = r * c21 + g * c22 + b * c23; - var bNew = r * c31 + g * c32 + b * c33; - - if (rNew < 0) { - dstData[dstIndex++] = 0; - } else if (rNew > 0xFF0000) { - dstData[dstIndex++] = 0xFF; - } else { - dstData[dstIndex++] = rNew >> 16; - } - - if (gNew < 0) { - dstData[dstIndex++] = 0; - } else if (gNew > 0xFF0000) { - dstData[dstIndex++] = 0xFF; - } else { - dstData[dstIndex++] = gNew >> 16; - } - - if (bNew < 0) { - dstData[dstIndex++] = 0; - } else if (bNew > 0xFF0000) { - dstData[dstIndex++] = 0xFF; - } else { - dstData[dstIndex++] = bNew >> 16; - } - - dstIndex++; - } - } -}; - -/** - * Return a convolution filter function bound to specific weights. - * - * @param {Array.<number>} weights Weights for the convolution matrix - * (not normalized). - * @return {function(ImageData,ImageData,number,number)} Convolution filter. - */ -filter.createConvolutionFilter = function(weights) { - // Normalize the weights to sum to 1. - var total = 0; - for (var i = 0; i != weights.length; i++) { - total += weights[i] * (i ? 4 : 1); - } - - var normalized = []; - for (i = 0; i != weights.length; i++) { - normalized.push(weights[i] / total); - } - for (; i < 4; i++) { - normalized.push(0); - } - - var maxWeightedSum = 0xFF * - Math.abs(normalized[0]) + - Math.abs(normalized[1]) * 4 + - Math.abs(normalized[2]) * 4 + - Math.abs(normalized[3]) * 4; - if (maxWeightedSum > filter.MAX_FLOAT_VALUE) - throw new Error('convolve5x5 cannot convert the weights to fixed point'); - - return filter.convolve5x5.bind(null, normalized); -}; - -/** - * Creates matrix filter. - * @param {Array.<number>} matrix Color transformation matrix. - * @return {function(ImageData,ImageData,number,number)} Matrix filter. - */ -filter.createColorMatrixFilter = function(matrix) { - for (var r = 0; r != 3; r++) { - var maxRowSum = 0; - for (var c = 0; c != 3; c++) { - maxRowSum += 0xFF * Math.abs(matrix[r * 3 + c]); - } - if (maxRowSum > filter.MAX_FLOAT_VALUE) - throw new Error( - 'colorMatrix3x3 cannot convert the matrix to fixed point'); - } - return filter.colorMatrix3x3.bind(null, matrix); -}; - -/** - * Return a blur filter. - * @param {Object} options Blur options. - * @return {function(ImageData,ImageData,number,number)} Blur filter. - */ -filter.blur = function(options) { - if (options.radius == 1) - return filter.createConvolutionFilter( - [1, options.strength]); - else if (options.radius == 2) - return filter.createConvolutionFilter( - [1, options.strength, options.strength]); - else - return filter.createConvolutionFilter( - [1, options.strength, options.strength, options.strength]); -}; - -/** - * Return a sharpen filter. - * @param {Object} options Sharpen options. - * @return {function(ImageData,ImageData,number,number)} Sharpen filter. - */ -filter.sharpen = function(options) { - if (options.radius == 1) - return filter.createConvolutionFilter( - [5, -options.strength]); - else if (options.radius == 2) - return filter.createConvolutionFilter( - [10, -options.strength, -options.strength]); - else - return filter.createConvolutionFilter( - [15, -options.strength, -options.strength, -options.strength]); -}; - -/** - * Return an exposure filter. - * @param {Object} options exposure options. - * @return {function(ImageData,ImageData,number,number)} Exposure filter. - */ -filter.exposure = function(options) { - var pixelMap = filter.precompute( - 255, - function(value) { - if (options.brightness > 0) { - value *= (1 + options.brightness); - } else { - value += (0xFF - value) * options.brightness; - } - return 0x80 + - (value - 0x80) * Math.tan((options.contrast + 1) * Math.PI / 4); - }); - - return filter.mapPixels.bind(null, pixelMap, pixelMap, pixelMap); -}; - -/** - * Return a color autofix filter. - * @param {Object} options Histogram for autofix. - * @return {function(ImageData,ImageData,number,number)} Autofix filter. - */ -filter.autofix = function(options) { - return filter.mapPixels.bind(null, - filter.autofix.stretchColors(options.histogram.r), - filter.autofix.stretchColors(options.histogram.g), - filter.autofix.stretchColors(options.histogram.b)); -}; - -/** - * Return a conversion table that stretches the range of colors used - * in the image to 0..255. - * @param {Array.<number>} channelHistogram Histogram to calculate range. - * @return {Uint8Array} Color mapping array. - */ -filter.autofix.stretchColors = function(channelHistogram) { - var range = filter.autofix.getRange(channelHistogram); - return filter.precompute( - 255, - function(x) { - return (x - range.first) / (range.last - range.first) * 255; - } - ); -}; - -/** - * Return a range that encloses non-zero elements values in a histogram array. - * @param {Array.<number>} channelHistogram Histogram to analyze. - * @return {{first: number, last: number}} Channel range in histogram. - */ -filter.autofix.getRange = function(channelHistogram) { - var first = 0; - while (first < channelHistogram.length && channelHistogram[first] == 0) - first++; - - var last = channelHistogram.length - 1; - while (last >= 0 && channelHistogram[last] == 0) - last--; - - if (first >= last) // Stretching does not make sense - return {first: 0, last: channelHistogram.length - 1}; - else - return {first: first, last: last}; -}; - -/** - * Minimum channel offset that makes visual difference. If autofix calculated - * offset is less than SENSITIVITY, probably autofix is not needed. - * Reasonable empirical value. - * @type {number} - */ -filter.autofix.SENSITIVITY = 8; - -/** - * @param {Array.<number>} channelHistogram Histogram to analyze. - * @return {boolean} True if stretching this range to 0..255 would make - * a visible difference. - */ -filter.autofix.needsStretching = function(channelHistogram) { - var range = filter.autofix.getRange(channelHistogram); - return (range.first >= filter.autofix.SENSITIVITY || - range.last <= 255 - filter.autofix.SENSITIVITY); -}; - -/** - * @param {{r: Array.<number>, g: Array.<number>, b: Array.<number>}} histogram - * @return {boolean} True if the autofix would make a visible difference. - */ -filter.autofix.isApplicable = function(histogram) { - return filter.autofix.needsStretching(histogram.r) || - filter.autofix.needsStretching(histogram.g) || - filter.autofix.needsStretching(histogram.b); -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/image_adjust.js b/ui/file_manager/file_manager/foreground/js/image_editor/image_adjust.js deleted file mode 100644 index 6c48fae..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/image_adjust.js +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2014 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'; - -/** - * The base class for simple filters that only modify the image content - * but do not modify the image dimensions. - * @constructor - * @extends ImageEditor.Mode - */ -ImageEditor.Mode.Adjust = function() { - ImageEditor.Mode.apply(this, arguments); - this.implicitCommit = true; - this.doneMessage_ = null; - this.viewportGeneration_ = 0; -}; - -ImageEditor.Mode.Adjust.prototype = {__proto__: ImageEditor.Mode.prototype}; - -/** @override */ -ImageEditor.Mode.Adjust.prototype.getCommand = function() { - if (!this.filter_) return null; - - return new Command.Filter(this.name, this.filter_, this.doneMessage_); -}; - -/** @override */ -ImageEditor.Mode.Adjust.prototype.cleanUpUI = function() { - ImageEditor.Mode.prototype.cleanUpUI.apply(this, arguments); - this.hidePreview(); -}; - -/** - * TODO(JSDOC) - */ -ImageEditor.Mode.Adjust.prototype.hidePreview = function() { - if (this.canvas_) { - this.canvas_.parentNode.removeChild(this.canvas_); - this.canvas_ = null; - } -}; - -/** - * TODO(JSDOC) - */ -ImageEditor.Mode.Adjust.prototype.cleanUpCaches = function() { - this.filter_ = null; - this.previewImageData_ = null; -}; - -/** - * TODO(JSDOC) - */ -ImageEditor.Mode.Adjust.prototype.reset = function() { - ImageEditor.Mode.prototype.reset.call(this); - this.hidePreview(); - this.cleanUpCaches(); -}; - -/** - * TODO(JSDOC) - * @param {Object} options // TODO(JSDOC). - */ -ImageEditor.Mode.Adjust.prototype.update = function(options) { - ImageEditor.Mode.prototype.update.apply(this, arguments); - - // We assume filter names are used in the UI directly. - // This will have to change with i18n. - this.filter_ = this.createFilter(options); - this.updatePreviewImage(); - ImageUtil.trace.resetTimer('preview'); - this.filter_(this.previewImageData_, this.originalImageData, 0, 0); - ImageUtil.trace.reportTimer('preview'); - this.canvas_.getContext('2d').putImageData( - this.previewImageData_, 0, 0); -}; - -/** - * Copy the source image data for the preview. - * Use the cached copy if the viewport has not changed. - */ -ImageEditor.Mode.Adjust.prototype.updatePreviewImage = function() { - if (!this.previewImageData_ || - this.viewportGeneration_ != this.getViewport().getCacheGeneration()) { - this.viewportGeneration_ = this.getViewport().getCacheGeneration(); - - if (!this.canvas_) { - this.canvas_ = this.getImageView().createOverlayCanvas(); - } - - this.getImageView().setupDeviceBuffer(this.canvas_); - - this.originalImageData = this.getImageView().copyScreenImageData(); - this.previewImageData_ = this.getImageView().copyScreenImageData(); - } -}; - -/* - * Own methods - */ - -/** - * TODO(JSDOC) - * @param {Object} options // TODO(JSDOC). - * @return {function(ImageData,ImageData,number,number)} Created function. - */ -ImageEditor.Mode.Adjust.prototype.createFilter = function(options) { - return filter.create(this.name, options); -}; - -/** - * A base class for color filters that are scale independent. - * @constructor - */ -ImageEditor.Mode.ColorFilter = function() { - ImageEditor.Mode.Adjust.apply(this, arguments); -}; - -ImageEditor.Mode.ColorFilter.prototype = - {__proto__: ImageEditor.Mode.Adjust.prototype}; - -/** - * TODO(JSDOC) - * @return {{r: Array.<number>, g: Array.<number>, b: Array.<number>}} - * histogram. - */ -ImageEditor.Mode.ColorFilter.prototype.getHistogram = function() { - return filter.getHistogram(this.getImageView().getThumbnail()); -}; - -/** - * Exposure/contrast filter. - * @constructor - */ -ImageEditor.Mode.Exposure = function() { - ImageEditor.Mode.ColorFilter.call(this, 'exposure', 'GALLERY_EXPOSURE'); -}; - -ImageEditor.Mode.Exposure.prototype = - {__proto__: ImageEditor.Mode.ColorFilter.prototype}; - -/** - * TODO(JSDOC) - * @param {ImageEditor.Toolbar} toolbar The toolbar to populate. - */ -ImageEditor.Mode.Exposure.prototype.createTools = function(toolbar) { - toolbar.addRange('brightness', 'GALLERY_BRIGHTNESS', -1, 0, 1, 100); - toolbar.addRange('contrast', 'GALLERY_CONTRAST', -1, 0, 1, 100); -}; - -/** - * Autofix. - * @constructor - */ -ImageEditor.Mode.Autofix = function() { - ImageEditor.Mode.ColorFilter.call(this, 'autofix', 'GALLERY_AUTOFIX'); - this.doneMessage_ = 'GALLERY_FIXED'; -}; - -ImageEditor.Mode.Autofix.prototype = - {__proto__: ImageEditor.Mode.ColorFilter.prototype}; - -/** - * TODO(JSDOC) - * @param {ImageEditor.Toolbar} toolbar The toolbar to populate. - */ -ImageEditor.Mode.Autofix.prototype.createTools = function(toolbar) { - var self = this; - toolbar.addButton('Apply', this.apply.bind(this)); -}; - -/** - * TODO(JSDOC) - * @return {boolean} // TODO(JSDOC). - */ -ImageEditor.Mode.Autofix.prototype.isApplicable = function() { - return this.getImageView().hasValidImage() && - filter.autofix.isApplicable(this.getHistogram()); -}; - -/** - * TODO(JSDOC) - */ -ImageEditor.Mode.Autofix.prototype.apply = function() { - this.update({histogram: this.getHistogram()}); -}; - -/** - * Instant Autofix. - * @constructor - */ -ImageEditor.Mode.InstantAutofix = function() { - ImageEditor.Mode.Autofix.apply(this, arguments); - this.instant = true; -}; - -ImageEditor.Mode.InstantAutofix.prototype = - {__proto__: ImageEditor.Mode.Autofix.prototype}; - -/** - * TODO(JSDOC) - */ -ImageEditor.Mode.InstantAutofix.prototype.setUp = function() { - ImageEditor.Mode.Autofix.prototype.setUp.apply(this, arguments); - this.apply(); -}; - -/** - * Blur filter. - * @constructor - */ -ImageEditor.Mode.Blur = function() { - ImageEditor.Mode.Adjust.call(this, 'blur'); -}; - -ImageEditor.Mode.Blur.prototype = - {__proto__: ImageEditor.Mode.Adjust.prototype}; - -/** - * TODO(JSDOC) - * @param {ImageEditor.Toolbar} toolbar The toolbar to populate. - */ -ImageEditor.Mode.Blur.prototype.createTools = function(toolbar) { - toolbar.addRange('strength', 'GALLERY_STRENGTH', 0, 0, 1, 100); - toolbar.addRange('radius', 'GALLERY_RADIUS', 1, 1, 3); -}; - -/** - * Sharpen filter. - * @constructor - */ -ImageEditor.Mode.Sharpen = function() { - ImageEditor.Mode.Adjust.call(this, 'sharpen'); -}; - -ImageEditor.Mode.Sharpen.prototype = - {__proto__: ImageEditor.Mode.Adjust.prototype}; - -/** - * TODO(JSDOC) - * @param {ImageEditor.Toolbar} toolbar The toolbar to populate. - */ -ImageEditor.Mode.Sharpen.prototype.createTools = function(toolbar) { - toolbar.addRange('strength', 'GALLERY_STRENGTH', 0, 0, 1, 100); - toolbar.addRange('radius', 'GALLERY_RADIUS', 1, 1, 3); -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/image_buffer.js b/ui/file_manager/file_manager/foreground/js/image_editor/image_buffer.js deleted file mode 100644 index 1d1ac3a..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/image_buffer.js +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2014 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'; - -/** - * A stack of overlays that display itself and handle mouse events. - * TODO(kaznacheev) Consider disbanding this class and moving - * the functionality to individual objects that display anything or handle - * mouse events. - * @constructor - */ -function ImageBuffer() { - this.overlays_ = []; -} - -/** - * TODO(JSDOC). - * @param {ImageBuffer.Overlay} overlay // TODO(JSDOC). - */ -ImageBuffer.prototype.addOverlay = function(overlay) { - var zIndex = overlay.getZIndex(); - // Store the overlays in the ascending Z-order. - var i; - for (i = 0; i != this.overlays_.length; i++) { - if (zIndex < this.overlays_[i].getZIndex()) break; - } - this.overlays_.splice(i, 0, overlay); -}; - -/** - * TODO(JSDOC). - * @param {ImageBuffer.Overlay} overlay // TODO(JSDOC). - */ -ImageBuffer.prototype.removeOverlay = function(overlay) { - for (var i = 0; i != this.overlays_.length; i++) { - if (this.overlays_[i] == overlay) { - this.overlays_.splice(i, 1); - return; - } - } - throw new Error('Cannot remove overlay ' + overlay); -}; - -/** - * Draws overlays in the ascending Z-order. - */ -ImageBuffer.prototype.draw = function() { - for (var i = 0; i != this.overlays_.length; i++) { - this.overlays_[i].draw(); - } -}; - -/** - * Searches for a cursor style in the descending Z-order. - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {string} A value for style.cursor CSS property. - */ -ImageBuffer.prototype.getCursorStyle = function(x, y, mouseDown) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - var style = this.overlays_[i].getCursorStyle(x, y, mouseDown); - if (style) return style; - } - return 'default'; -}; - -/** - * Searches for a click handler in the descending Z-order. - * @param {number} x X coordinate for click event. - * @param {number} y Y coordinate for click event. - * @return {boolean} True if handled. - */ -ImageBuffer.prototype.onClick = function(x, y) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - if (this.overlays_[i].onClick(x, y)) return true; - } - return false; -}; - -/** - * Searches for a drag handler in the descending Z-order. - * @param {number} x Event X coordinate. - * @param {number} y Event Y coordinate. - * @param {boolean} touch True if it's a touch event, false if mouse. - * @return {function(number,number)} A function to be called on mouse drag. - */ -ImageBuffer.prototype.getDragHandler = function(x, y, touch) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - var handler = this.overlays_[i].getDragHandler(x, y, touch); - if (handler) - return handler; - } - return null; -}; - -/** - * Searches for an action for the double tap enumerating - * layers in the descending Z-order. - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @return {ImageBuffer.DoubleTapAction} Action to perform as result. - */ -ImageBuffer.prototype.getDoubleTapAction = function(x, y) { - for (var i = this.overlays_.length - 1; i >= 0; i--) { - var action = this.overlays_[i].getDoubleTapAction(x, y); - if (action != ImageBuffer.DoubleTapAction.NOTHING) - return action; - } - return ImageBuffer.DoubleTapAction.NOTHING; -}; - -/** - * Possible double tap actions. - * @enum - */ -ImageBuffer.DoubleTapAction = { - NOTHING: 0, - COMMIT: 1, - CANCEL: 2 -}; - -/** - * ImageBuffer.Overlay is a pluggable extension that modifies the outlook - * and the behavior of the ImageBuffer instance. - * @class - */ -ImageBuffer.Overlay = function() {}; - -/** - * TODO(JSDOC). - * @return {number} // TODO(JSDOC). - */ -ImageBuffer.Overlay.prototype.getZIndex = function() { return 0 }; - -/** - * TODO(JSDOC). - */ -ImageBuffer.Overlay.prototype.draw = function() {}; - -/** - * TODO(JSDOC). - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {?string} A value for style.cursor CSS property or null for - * default. - */ -ImageBuffer.Overlay.prototype.getCursorStyle = function(x, y, mouseDown) { - return null; -}; - -/** - * TODO(JSDOC). - * @param {number} x // TODO(JSDOC). - * @param {number} y // TODO(JSDOC). - * @return {boolean} // TODO(JSDOC). - */ -ImageBuffer.Overlay.prototype.onClick = function(x, y) { - return false; -}; - -/** - * TODO(JSDOC). - * @param {number} x Event X coordinate. - * @param {number} y Event Y coordinate. - * @param {boolean} touch True if it's a touch event, false if mouse. - * @return {function(number,number)} A function to be called on mouse drag. - */ -ImageBuffer.Overlay.prototype.getDragHandler = function(x, y, touch) { - return null; -}; - -/** - * TODO(JSDOC). - * @param {number} x // TODO(JSDOC). - * @param {number} y // TODO(JSDOC). - * @return {ImageBuffer.DoubleTapAction} // TODO(JSDOC). - */ -ImageBuffer.Overlay.prototype.getDoubleTapAction = function(x, y) { - return ImageBuffer.DoubleTapAction.NOTHING; -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/image_editor.js b/ui/file_manager/file_manager/foreground/js/image_editor/image_editor.js deleted file mode 100644 index 517b274..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/image_editor.js +++ /dev/null @@ -1,1178 +0,0 @@ -// Copyright 2014 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'; - -/** - * ImageEditor is the top level object that holds together and connects - * everything needed for image editing. - * - * @param {Viewport} viewport The viewport. - * @param {ImageView} imageView The ImageView containing the images to edit. - * @param {ImageEditor.Prompt} prompt Prompt instance. - * @param {Object} DOMContainers Various DOM containers required for the editor. - * @param {Array.<ImageEditor.Mode>} modes Available editor modes. - * @param {function} displayStringFunction String formatting function. - * @param {function()} onToolsVisibilityChanged Callback to be called, when - * some of the UI elements have been dimmed or revealed. - * @constructor - */ -function ImageEditor( - viewport, imageView, prompt, DOMContainers, modes, displayStringFunction, - onToolsVisibilityChanged) { - this.rootContainer_ = DOMContainers.root; - this.container_ = DOMContainers.image; - this.modes_ = modes; - this.displayStringFunction_ = displayStringFunction; - this.onToolsVisibilityChanged_ = onToolsVisibilityChanged; - - ImageUtil.removeChildren(this.container_); - - var document = this.container_.ownerDocument; - - this.viewport_ = viewport; - this.viewport_.sizeByFrame(this.container_); - - this.buffer_ = new ImageBuffer(); - this.viewport_.addRepaintCallback(this.buffer_.draw.bind(this.buffer_)); - - this.imageView_ = imageView; - this.imageView_.addContentCallback(this.onContentUpdate_.bind(this)); - this.buffer_.addOverlay(this.imageView_); - - this.panControl_ = new ImageEditor.MouseControl( - this.rootContainer_, this.container_, this.getBuffer()); - - this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this)); - - this.mainToolbar_ = new ImageEditor.Toolbar( - DOMContainers.toolbar, displayStringFunction); - - this.modeToolbar_ = new ImageEditor.Toolbar( - DOMContainers.mode, displayStringFunction, - this.onOptionsChange.bind(this)); - - this.prompt_ = prompt; - - this.createToolButtons(); - - this.commandQueue_ = null; -} - -/** - * @return {boolean} True if no user commands are to be accepted. - */ -ImageEditor.prototype.isLocked = function() { - return !this.commandQueue_ || this.commandQueue_.isBusy(); -}; - -/** - * @return {boolean} True if the command queue is busy. - */ -ImageEditor.prototype.isBusy = function() { - return this.commandQueue_ && this.commandQueue_.isBusy(); -}; - -/** - * Reflect the locked state of the editor in the UI. - * @param {boolean} on True if locked. - */ -ImageEditor.prototype.lockUI = function(on) { - ImageUtil.setAttribute(this.rootContainer_, 'locked', on); -}; - -/** - * Report the tool use to the metrics subsystem. - * @param {string} name Action name. - */ -ImageEditor.prototype.recordToolUse = function(name) { - ImageUtil.metrics.recordEnum( - ImageUtil.getMetricName('Tool'), name, this.actionNames_); -}; - -/** - * Content update handler. - * @private - */ -ImageEditor.prototype.onContentUpdate_ = function() { - for (var i = 0; i != this.modes_.length; i++) { - var mode = this.modes_[i]; - ImageUtil.setAttribute(mode.button_, 'disabled', !mode.isApplicable()); - } -}; - -/** - * Open the editing session for a new image. - * - * @param {string} url Image url. - * @param {Object} metadata Metadata. - * @param {Object} effect Transition effect object. - * @param {function(function)} saveFunction Image save function. - * @param {function} displayCallback Display callback. - * @param {function} loadCallback Load callback. - */ -ImageEditor.prototype.openSession = function( - url, metadata, effect, saveFunction, displayCallback, loadCallback) { - if (this.commandQueue_) - throw new Error('Session not closed'); - - this.lockUI(true); - - var self = this; - this.imageView_.load( - url, metadata, effect, displayCallback, function(loadType, delay, error) { - self.lockUI(false); - self.commandQueue_ = new CommandQueue( - self.container_.ownerDocument, - self.imageView_.getCanvas(), - saveFunction); - self.commandQueue_.attachUI( - self.getImageView(), self.getPrompt(), self.lockUI.bind(self)); - self.updateUndoRedo(); - loadCallback(loadType, delay, error); - }); -}; - -/** - * Close the current image editing session. - * @param {function} callback Callback. - */ -ImageEditor.prototype.closeSession = function(callback) { - this.getPrompt().hide(); - if (this.imageView_.isLoading()) { - if (this.commandQueue_) { - console.warn('Inconsistent image editor state'); - this.commandQueue_ = null; - } - this.imageView_.cancelLoad(); - this.lockUI(false); - callback(); - return; - } - if (!this.commandQueue_) { - // Session is already closed. - callback(); - return; - } - - this.executeWhenReady(callback); - this.commandQueue_.close(); - this.commandQueue_ = null; -}; - -/** - * Commit the current operation and execute the action. - * - * @param {function} callback Callback. - */ -ImageEditor.prototype.executeWhenReady = function(callback) { - if (this.commandQueue_) { - this.leaveModeGently(); - this.commandQueue_.executeWhenReady(callback); - } else { - if (!this.imageView_.isLoading()) - console.warn('Inconsistent image editor state'); - callback(); - } -}; - -/** - * @return {boolean} True if undo queue is not empty. - */ -ImageEditor.prototype.canUndo = function() { - return this.commandQueue_ && this.commandQueue_.canUndo(); -}; - -/** - * Undo the recently executed command. - */ -ImageEditor.prototype.undo = function() { - if (this.isLocked()) return; - this.recordToolUse('undo'); - - // First undo click should dismiss the uncommitted modifications. - if (this.currentMode_ && this.currentMode_.isUpdated()) { - this.currentMode_.reset(); - return; - } - - this.getPrompt().hide(); - this.leaveMode(false); - this.commandQueue_.undo(); - this.updateUndoRedo(); -}; - -/** - * Redo the recently un-done command. - */ -ImageEditor.prototype.redo = function() { - if (this.isLocked()) return; - this.recordToolUse('redo'); - this.getPrompt().hide(); - this.leaveMode(false); - this.commandQueue_.redo(); - this.updateUndoRedo(); -}; - -/** - * Update Undo/Redo buttons state. - */ -ImageEditor.prototype.updateUndoRedo = function() { - var canUndo = this.commandQueue_ && this.commandQueue_.canUndo(); - var canRedo = this.commandQueue_ && this.commandQueue_.canRedo(); - ImageUtil.setAttribute(this.undoButton_, 'disabled', !canUndo); - this.redoButton_.hidden = !canRedo; -}; - -/** - * @return {HTMLCanvasElement} The current image canvas. - */ -ImageEditor.prototype.getCanvas = function() { - return this.getImageView().getCanvas(); -}; - -/** - * @return {ImageBuffer} ImageBuffer instance. - */ -ImageEditor.prototype.getBuffer = function() { return this.buffer_ }; - -/** - * @return {ImageView} ImageView instance. - */ -ImageEditor.prototype.getImageView = function() { return this.imageView_ }; - -/** - * @return {Viewport} Viewport instance. - */ -ImageEditor.prototype.getViewport = function() { return this.viewport_ }; - -/** - * @return {ImageEditor.Prompt} Prompt instance. - */ -ImageEditor.prototype.getPrompt = function() { return this.prompt_ }; - -/** - * Handle the toolbar controls update. - * @param {Object} options A map of options. - */ -ImageEditor.prototype.onOptionsChange = function(options) { - ImageUtil.trace.resetTimer('update'); - if (this.currentMode_) { - this.currentMode_.update(options); - } - ImageUtil.trace.reportTimer('update'); -}; - -/** - * ImageEditor.Mode represents a modal state dedicated to a specific operation. - * Inherits from ImageBuffer. Overlay to simplify the drawing of mode-specific - * tools. - * - * @param {string} name The mode name. - * @param {string} title The mode title. - * @constructor - */ - -ImageEditor.Mode = function(name, title) { - this.name = name; - this.title = title; - this.message_ = 'GALLERY_ENTER_WHEN_DONE'; -}; - -ImageEditor.Mode.prototype = {__proto__: ImageBuffer.Overlay.prototype }; - -/** - * @return {Viewport} Viewport instance. - */ -ImageEditor.Mode.prototype.getViewport = function() { return this.viewport_ }; - -/** - * @return {ImageView} ImageView instance. - */ -ImageEditor.Mode.prototype.getImageView = function() { return this.imageView_ }; - -/** - * @return {string} The mode-specific message to be displayed when entering. - */ -ImageEditor.Mode.prototype.getMessage = function() { return this.message_ }; - -/** - * @return {boolean} True if the mode is applicable in the current context. - */ -ImageEditor.Mode.prototype.isApplicable = function() { return true }; - -/** - * Called once after creating the mode button. - * - * @param {ImageEditor} editor The editor instance. - * @param {HTMLElement} button The mode button. - */ - -ImageEditor.Mode.prototype.bind = function(editor, button) { - this.editor_ = editor; - this.editor_.registerAction_(this.name); - this.button_ = button; - this.viewport_ = editor.getViewport(); - this.imageView_ = editor.getImageView(); -}; - -/** - * Called before entering the mode. - */ -ImageEditor.Mode.prototype.setUp = function() { - this.editor_.getBuffer().addOverlay(this); - this.updated_ = false; -}; - -/** - * Create mode-specific controls here. - * @param {ImageEditor.Toolbar} toolbar The toolbar to populate. - */ -ImageEditor.Mode.prototype.createTools = function(toolbar) {}; - -/** - * Called before exiting the mode. - */ -ImageEditor.Mode.prototype.cleanUpUI = function() { - this.editor_.getBuffer().removeOverlay(this); -}; - -/** - * Called after exiting the mode. - */ -ImageEditor.Mode.prototype.cleanUpCaches = function() {}; - -/** - * Called when any of the controls changed its value. - * @param {Object} options A map of options. - */ -ImageEditor.Mode.prototype.update = function(options) { - this.markUpdated(); -}; - -/** - * Mark the editor mode as updated. - */ -ImageEditor.Mode.prototype.markUpdated = function() { - this.updated_ = true; -}; - -/** - * @return {boolean} True if the mode controls changed. - */ -ImageEditor.Mode.prototype.isUpdated = function() { return this.updated_ }; - -/** - * Resets the mode to a clean state. - */ -ImageEditor.Mode.prototype.reset = function() { - this.editor_.modeToolbar_.reset(); - this.updated_ = false; -}; - -/** - * One-click editor tool, requires no interaction, just executes the command. - * - * @param {string} name The mode name. - * @param {string} title The mode title. - * @param {Command} command The command to execute on click. - * @constructor - */ -ImageEditor.Mode.OneClick = function(name, title, command) { - ImageEditor.Mode.call(this, name, title); - this.instant = true; - this.command_ = command; -}; - -ImageEditor.Mode.OneClick.prototype = {__proto__: ImageEditor.Mode.prototype}; - -/** - * @return {Command} command. - */ -ImageEditor.Mode.OneClick.prototype.getCommand = function() { - return this.command_; -}; - -/** - * Register the action name. Required for metrics reporting. - * @param {string} name Button name. - * @private - */ -ImageEditor.prototype.registerAction_ = function(name) { - this.actionNames_.push(name); -}; - -/** - * Populate the toolbar. - */ -ImageEditor.prototype.createToolButtons = function() { - this.mainToolbar_.clear(); - this.actionNames_ = []; - - var self = this; - function createButton(name, title, handler) { - return self.mainToolbar_.addButton(name, - title, - handler, - name /* opt_className */); - } - - for (var i = 0; i != this.modes_.length; i++) { - var mode = this.modes_[i]; - mode.bind(this, createButton(mode.name, - mode.title, - this.enterMode.bind(this, mode))); - } - - this.undoButton_ = createButton('undo', - 'GALLERY_UNDO', - this.undo.bind(this)); - this.registerAction_('undo'); - - this.redoButton_ = createButton('redo', - 'GALLERY_REDO', - this.redo.bind(this)); - this.registerAction_('redo'); -}; - -/** - * @return {ImageEditor.Mode} The current mode. - */ -ImageEditor.prototype.getMode = function() { return this.currentMode_ }; - -/** - * The user clicked on the mode button. - * - * @param {ImageEditor.Mode} mode The new mode. - */ -ImageEditor.prototype.enterMode = function(mode) { - if (this.isLocked()) return; - - if (this.currentMode_ == mode) { - // Currently active editor tool clicked, commit if modified. - this.leaveMode(this.currentMode_.updated_); - return; - } - - this.recordToolUse(mode.name); - - this.leaveModeGently(); - // The above call could have caused a commit which might have initiated - // an asynchronous command execution. Wait for it to complete, then proceed - // with the mode set up. - this.commandQueue_.executeWhenReady(this.setUpMode_.bind(this, mode)); -}; - -/** - * Set up the new editing mode. - * - * @param {ImageEditor.Mode} mode The mode. - * @private - */ -ImageEditor.prototype.setUpMode_ = function(mode) { - this.currentTool_ = mode.button_; - - ImageUtil.setAttribute(this.currentTool_, 'pressed', true); - - this.currentMode_ = mode; - this.currentMode_.setUp(); - - if (this.currentMode_.instant) { // Instant tool. - this.leaveMode(true); - return; - } - - this.getPrompt().show(this.currentMode_.getMessage()); - - this.modeToolbar_.clear(); - this.currentMode_.createTools(this.modeToolbar_); - this.modeToolbar_.show(true); -}; - -/** - * The user clicked on 'OK' or 'Cancel' or on a different mode button. - * @param {boolean} commit True if commit is required. - */ -ImageEditor.prototype.leaveMode = function(commit) { - if (!this.currentMode_) return; - - if (!this.currentMode_.instant) { - this.getPrompt().hide(); - } - - this.modeToolbar_.show(false); - - this.currentMode_.cleanUpUI(); - if (commit) { - var self = this; - var command = this.currentMode_.getCommand(); - if (command) { // Could be null if the user did not do anything. - this.commandQueue_.execute(command); - this.updateUndoRedo(); - } - } - this.currentMode_.cleanUpCaches(); - this.currentMode_ = null; - - ImageUtil.setAttribute(this.currentTool_, 'pressed', false); - this.currentTool_ = null; -}; - -/** - * Leave the mode, commit only if required by the current mode. - */ -ImageEditor.prototype.leaveModeGently = function() { - this.leaveMode(this.currentMode_ && - this.currentMode_.updated_ && - this.currentMode_.implicitCommit); -}; - -/** - * Enter the editor mode with the given name. - * - * @param {string} name Mode name. - * @private - */ -ImageEditor.prototype.enterModeByName_ = function(name) { - for (var i = 0; i != this.modes_.length; i++) { - var mode = this.modes_[i]; - if (mode.name == name) { - if (!mode.button_.hasAttribute('disabled')) - this.enterMode(mode); - return; - } - } - console.error('Mode "' + name + '" not found.'); -}; - -/** - * Key down handler. - * @param {Event} event The keydown event. - * @return {boolean} True if handled. - */ -ImageEditor.prototype.onKeyDown = function(event) { - switch (util.getKeyModifiers(event) + event.keyIdentifier) { - case 'U+001B': // Escape - case 'Enter': - if (this.getMode()) { - this.leaveMode(event.keyIdentifier == 'Enter'); - return true; - } - break; - - case 'Ctrl-U+005A': // Ctrl+Z - if (this.commandQueue_.canUndo()) { - this.undo(); - return true; - } - break; - - case 'Ctrl-U+0059': // Ctrl+Y - if (this.commandQueue_.canRedo()) { - this.redo(); - return true; - } - break; - - case 'U+0041': // 'a' - this.enterModeByName_('autofix'); - return true; - - case 'U+0042': // 'b' - this.enterModeByName_('exposure'); - return true; - - case 'U+0043': // 'c' - this.enterModeByName_('crop'); - return true; - - case 'U+004C': // 'l' - this.enterModeByName_('rotate_left'); - return true; - - case 'U+0052': // 'r' - this.enterModeByName_('rotate_right'); - return true; - } - return false; -}; - -/** - * Double tap handler. - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @private - */ -ImageEditor.prototype.onDoubleTap_ = function(x, y) { - if (this.getMode()) { - var action = this.buffer_.getDoubleTapAction(x, y); - if (action == ImageBuffer.DoubleTapAction.COMMIT) - this.leaveMode(true); - else if (action == ImageBuffer.DoubleTapAction.CANCEL) - this.leaveMode(false); - } -}; - -/** - * Hide the tools that overlap the given rectangular frame. - * - * @param {Rect} frame Hide the tool that overlaps this rect. - * @param {Rect} transparent But do not hide the tool that is completely inside - * this rect. - */ -ImageEditor.prototype.hideOverlappingTools = function(frame, transparent) { - var tools = this.rootContainer_.ownerDocument.querySelectorAll('.dimmable'); - var changed = false; - for (var i = 0; i != tools.length; i++) { - var tool = tools[i]; - var toolRect = tool.getBoundingClientRect(); - var overlapping = - (frame && frame.intersects(toolRect)) && - !(transparent && transparent.contains(toolRect)); - if (overlapping && !tool.hasAttribute('dimmed') || - !overlapping && tool.hasAttribute('dimmed')) { - ImageUtil.setAttribute(tool, 'dimmed', overlapping); - changed = true; - } - } - if (changed) - this.onToolsVisibilityChanged_(); -}; - -/** - * A helper object for panning the ImageBuffer. - * - * @param {HTMLElement} rootContainer The top-level container. - * @param {HTMLElement} container The container for mouse events. - * @param {ImageBuffer} buffer Image buffer. - * @constructor - */ -ImageEditor.MouseControl = function(rootContainer, container, buffer) { - this.rootContainer_ = rootContainer; - this.container_ = container; - this.buffer_ = buffer; - - var handlers = { - 'touchstart': this.onTouchStart, - 'touchend': this.onTouchEnd, - 'touchcancel': this.onTouchCancel, - 'touchmove': this.onTouchMove, - 'mousedown': this.onMouseDown, - 'mouseup': this.onMouseUp - }; - - for (var eventName in handlers) { - container.addEventListener( - eventName, handlers[eventName].bind(this), false); - } - - // Mouse move handler has to be attached to the window to receive events - // from outside of the window. See: http://crbug.com/155705 - window.addEventListener('mousemove', this.onMouseMove.bind(this), false); -}; - -/** - * Maximum movement for touch to be detected as a tap (in pixels). - * @private - */ -ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_ = 8; - -/** - * Maximum time for touch to be detected as a tap (in milliseconds). - * @private - */ -ImageEditor.MouseControl.MAX_TAP_DURATION_ = 500; - -/** - * Maximum distance from the first tap to the second tap to be considered - * as a double tap. - * @private - */ -ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_ = 32; - -/** - * Maximum time for touch to be detected as a double tap (in milliseconds). - * @private - */ -ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_ = 1000; - -/** - * Returns an event's position. - * - * @param {MouseEvent|Touch} e Pointer position. - * @return {Object} A pair of x,y in page coordinates. - * @private - */ -ImageEditor.MouseControl.getPosition_ = function(e) { - return { - x: e.pageX, - y: e.pageY - }; -}; - -/** - * Returns touch position or null if there is more than one touch position. - * - * @param {TouchEvent} e Event. - * @return {object?} A pair of x,y in page coordinates. - * @private - */ -ImageEditor.MouseControl.prototype.getTouchPosition_ = function(e) { - if (e.targetTouches.length == 1) - return ImageEditor.MouseControl.getPosition_(e.targetTouches[0]); - else - return null; -}; - -/** - * Touch start handler. - * @param {TouchEvent} e Event. - */ -ImageEditor.MouseControl.prototype.onTouchStart = function(e) { - var position = this.getTouchPosition_(e); - if (position) { - this.touchStartInfo_ = { - x: position.x, - y: position.y, - time: Date.now() - }; - this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y, - true /* touch */); - this.dragHappened_ = false; - e.preventDefault(); - } -}; - -/** - * Touch end handler. - * @param {TouchEvent} e Event. - */ -ImageEditor.MouseControl.prototype.onTouchEnd = function(e) { - if (!this.dragHappened_ && Date.now() - this.touchStartInfo_.time <= - ImageEditor.MouseControl.MAX_TAP_DURATION_) { - this.buffer_.onClick(this.touchStartInfo_.x, this.touchStartInfo_.y); - if (this.previousTouchStartInfo_ && - Date.now() - this.previousTouchStartInfo_.time < - ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_) { - var prevTouchCircle = new Circle( - this.previousTouchStartInfo_.x, - this.previousTouchStartInfo_.y, - ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_); - if (prevTouchCircle.inside(this.touchStartInfo_.x, - this.touchStartInfo_.y)) { - this.doubleTapCallback_(this.touchStartInfo_.x, this.touchStartInfo_.y); - } - } - this.previousTouchStartInfo_ = this.touchStartInfo_; - } else { - this.previousTouchStartInfo_ = null; - } - this.onTouchCancel(e); -}; - -/** - * Default double tap handler. - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @private - */ -ImageEditor.MouseControl.prototype.doubleTapCallback_ = function(x, y) {}; - -/** - * Sets callback to be called when double tap detected. - * @param {function(number, number)} callback New double tap callback. - */ -ImageEditor.MouseControl.prototype.setDoubleTapCallback = function(callback) { - this.doubleTapCallback_ = callback; -}; - -/** - * Touch cancel handler. - */ -ImageEditor.MouseControl.prototype.onTouchCancel = function() { - this.dragHandler_ = null; - this.dragHappened_ = false; - this.touchStartInfo_ = null; - this.lockMouse_(false); -}; - -/** - * Touch move handler. - * @param {TouchEvent} e Event. - */ -ImageEditor.MouseControl.prototype.onTouchMove = function(e) { - var position = this.getTouchPosition_(e); - if (!position) - return; - - if (this.touchStartInfo_ && !this.dragHappened_) { - var tapCircle = new Circle(this.touchStartInfo_.x, this.touchStartInfo_.y, - ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_); - this.dragHappened_ = !tapCircle.inside(position.x, position.y); - } - if (this.dragHandler_ && this.dragHappened_) { - this.dragHandler_(position.x, position.y); - this.lockMouse_(true); - } -}; - -/** - * Mouse down handler. - * @param {MouseEvent} e Event. - */ -ImageEditor.MouseControl.prototype.onMouseDown = function(e) { - var position = ImageEditor.MouseControl.getPosition_(e); - - this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y, - false /* mouse */); - this.dragHappened_ = false; - this.updateCursor_(position); -}; - -/** - * Mouse up handler. - * @param {MouseEvent} e Event. - */ -ImageEditor.MouseControl.prototype.onMouseUp = function(e) { - var position = ImageEditor.MouseControl.getPosition_(e); - - if (!this.dragHappened_) { - this.buffer_.onClick(position.x, position.y); - } - this.dragHandler_ = null; - this.dragHappened_ = false; - this.lockMouse_(false); -}; - -/** - * Mouse move handler. - * @param {MouseEvent} e Event. - */ -ImageEditor.MouseControl.prototype.onMouseMove = function(e) { - var position = ImageEditor.MouseControl.getPosition_(e); - - if (this.dragHandler_ && !e.which) { - // mouseup must have happened while the mouse was outside our window. - this.dragHandler_ = null; - this.lockMouse_(false); - } - - this.updateCursor_(position); - if (this.dragHandler_) { - this.dragHandler_(position.x, position.y); - this.dragHappened_ = true; - this.lockMouse_(true); - } -}; - -/** - * Update the UI to reflect mouse drag state. - * @param {boolean} on True if dragging. - * @private - */ -ImageEditor.MouseControl.prototype.lockMouse_ = function(on) { - ImageUtil.setAttribute(this.rootContainer_, 'mousedrag', on); -}; - -/** - * Update the cursor. - * - * @param {Object} position An object holding x and y properties. - * @private - */ -ImageEditor.MouseControl.prototype.updateCursor_ = function(position) { - var oldCursor = this.container_.getAttribute('cursor'); - var newCursor = this.buffer_.getCursorStyle( - position.x, position.y, !!this.dragHandler_); - if (newCursor != oldCursor) // Avoid flicker. - this.container_.setAttribute('cursor', newCursor); -}; - -/** - * A toolbar for the ImageEditor. - * @param {HTMLElement} parent The parent element. - * @param {function} displayStringFunction A string formatting function. - * @param {function} updateCallback The callback called when controls change. - * @constructor - */ -ImageEditor.Toolbar = function(parent, displayStringFunction, updateCallback) { - this.wrapper_ = parent; - this.displayStringFunction_ = displayStringFunction; - this.updateCallback_ = updateCallback; -}; - -/** - * Clear the toolbar. - */ -ImageEditor.Toolbar.prototype.clear = function() { - ImageUtil.removeChildren(this.wrapper_); -}; - -/** - * Create a control. - * @param {string} tagName The element tag name. - * @return {HTMLElement} The created control element. - * @private - */ -ImageEditor.Toolbar.prototype.create_ = function(tagName) { - return this.wrapper_.ownerDocument.createElement(tagName); -}; - -/** - * Add a control. - * @param {HTMLElement} element The control to add. - * @return {HTMLElement} The added element. - */ -ImageEditor.Toolbar.prototype.add = function(element) { - this.wrapper_.appendChild(element); - return element; -}; - -/** - * Add a text label. - * @param {string} name Label name. - * @return {HTMLElement} The added label. - */ -ImageEditor.Toolbar.prototype.addLabel = function(name) { - var label = this.create_('span'); - label.textContent = this.displayStringFunction_(name); - return this.add(label); -}; - -/** - * Add a button. - * - * @param {string} name Button name. - * @param {string} title Button title. - * @param {function} handler onClick handler. - * @param {string=} opt_class Extra class name. - * @return {HTMLElement} The added button. - */ -ImageEditor.Toolbar.prototype.addButton = function( - name, title, handler, opt_class) { - var button = this.create_('button'); - if (opt_class) button.classList.add(opt_class); - var label = this.create_('span'); - label.textContent = this.displayStringFunction_(title); - button.appendChild(label); - button.label = this.displayStringFunction_(title); - button.addEventListener('click', handler, false); - return this.add(button); -}; - -/** - * Add a range control (scalar value picker). - * - * @param {string} name An option name. - * @param {string} title An option title. - * @param {number} min Min value of the option. - * @param {number} value Default value of the option. - * @param {number} max Max value of the options. - * @param {number} scale A number to multiply by when setting - * min/value/max in DOM. - * @param {boolean=} opt_showNumeric True if numeric value should be displayed. - * @return {HTMLElement} Range element. - */ -ImageEditor.Toolbar.prototype.addRange = function( - name, title, min, value, max, scale, opt_showNumeric) { - var self = this; - - scale = scale || 1; - - var range = this.create_('input'); - - range.className = 'range'; - range.type = 'range'; - range.name = name; - range.min = Math.ceil(min * scale); - range.max = Math.floor(max * scale); - - var numeric = this.create_('div'); - numeric.className = 'numeric'; - function mirror() { - numeric.textContent = Math.round(range.getValue() * scale) / scale; - } - - range.setValue = function(newValue) { - range.value = Math.round(newValue * scale); - mirror(); - }; - - range.getValue = function() { - return Number(range.value) / scale; - }; - - range.reset = function() { - range.setValue(value); - }; - - range.addEventListener('change', - function() { - mirror(); - self.updateCallback_(self.getOptions()); - }, - false); - - range.setValue(value); - - var label = this.create_('div'); - label.textContent = this.displayStringFunction_(title); - label.className = 'label ' + name; - this.add(label); - this.add(range); - - if (opt_showNumeric) - this.add(numeric); - - // Swallow the left and right keys, so they are not handled by other - // listeners. - range.addEventListener('keydown', function(e) { - if (e.keyIdentifier === 'Left' || e.keyIdentifier === 'Right') - e.stopPropagation(); - }); - - return range; -}; - -/** - * @return {Object} options A map of options. - */ -ImageEditor.Toolbar.prototype.getOptions = function() { - var values = {}; - for (var child = this.wrapper_.firstChild; child; child = child.nextSibling) { - if (child.name) - values[child.name] = child.getValue(); - } - return values; -}; - -/** - * Reset the toolbar. - */ -ImageEditor.Toolbar.prototype.reset = function() { - for (var child = this.wrapper_.firstChild; child; child = child.nextSibling) { - if (child.reset) child.reset(); - } -}; - -/** - * Show/hide the toolbar. - * @param {boolean} on True if show. - */ -ImageEditor.Toolbar.prototype.show = function(on) { - if (!this.wrapper_.firstChild) - return; // Do not show empty toolbar; - - this.wrapper_.hidden = !on; -}; - -/** A prompt panel for the editor. - * - * @param {HTMLElement} container Container element. - * @param {function} displayStringFunction A formatting function. - * @constructor - */ -ImageEditor.Prompt = function(container, displayStringFunction) { - this.container_ = container; - this.displayStringFunction_ = displayStringFunction; -}; - -/** - * Reset the prompt. - */ -ImageEditor.Prompt.prototype.reset = function() { - this.cancelTimer(); - if (this.wrapper_) { - this.container_.removeChild(this.wrapper_); - this.wrapper_ = null; - this.prompt_ = null; - } -}; - -/** - * Cancel the delayed action. - */ -ImageEditor.Prompt.prototype.cancelTimer = function() { - if (this.timer_) { - clearTimeout(this.timer_); - this.timer_ = null; - } -}; - -/** - * Schedule the delayed action. - * @param {function} callback Callback. - * @param {number} timeout Timeout. - */ -ImageEditor.Prompt.prototype.setTimer = function(callback, timeout) { - this.cancelTimer(); - var self = this; - this.timer_ = setTimeout(function() { - self.timer_ = null; - callback(); - }, timeout); -}; - -/** - * Show the prompt. - * - * @param {string} text The prompt text. - * @param {number} timeout Timeout in ms. - * @param {Object} formatArgs varArgs for the formatting function. - */ -ImageEditor.Prompt.prototype.show = function(text, timeout, formatArgs) { - this.showAt.apply(this, - ['center'].concat(Array.prototype.slice.call(arguments))); -}; - -/** - * - * @param {string} pos The 'pos' attribute value. - * @param {string} text The prompt text. - * @param {number} timeout Timeout in ms. - * @param {Object} formatArgs varArgs for the formatting function. - */ -ImageEditor.Prompt.prototype.showAt = function(pos, text, timeout, formatArgs) { - this.reset(); - if (!text) return; - - var document = this.container_.ownerDocument; - this.wrapper_ = document.createElement('div'); - this.wrapper_.className = 'prompt-wrapper'; - this.wrapper_.setAttribute('pos', pos); - this.container_.appendChild(this.wrapper_); - - this.prompt_ = document.createElement('div'); - this.prompt_.className = 'prompt'; - - // Create an extra wrapper which opacity can be manipulated separately. - var tool = document.createElement('div'); - tool.className = 'dimmable'; - this.wrapper_.appendChild(tool); - tool.appendChild(this.prompt_); - - var args = [text].concat(Array.prototype.slice.call(arguments, 3)); - this.prompt_.textContent = this.displayStringFunction_.apply(null, args); - - var close = document.createElement('div'); - close.className = 'close'; - close.addEventListener('click', this.hide.bind(this)); - this.prompt_.appendChild(close); - - setTimeout( - this.prompt_.setAttribute.bind(this.prompt_, 'state', 'fadein'), 0); - - if (timeout) - this.setTimer(this.hide.bind(this), timeout); -}; - -/** - * Hide the prompt. - */ -ImageEditor.Prompt.prototype.hide = function() { - if (!this.prompt_) return; - this.prompt_.setAttribute('state', 'fadeout'); - // Allow some time for the animation to play out. - this.setTimer(this.reset.bind(this), 500); -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/image_encoder.js b/ui/file_manager/file_manager/foreground/js/image_editor/image_encoder.js deleted file mode 100644 index f1817b9..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/image_encoder.js +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2014 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'; - -/** - * A namespace class for image encoding functions. All methods are static. - */ -function ImageEncoder() {} - -/** - * @type {Array.<Object>} - */ -ImageEncoder.metadataEncoders = {}; - -/** - * @param {function(new:ImageEncoder.MetadataEncoder)} constructor - * // TODO(JSDOC). - * @param {string} mimeType // TODO(JSDOC). - */ -ImageEncoder.registerMetadataEncoder = function(constructor, mimeType) { - ImageEncoder.metadataEncoders[mimeType] = constructor; -}; - -/** - * Create a metadata encoder. - * - * The encoder will own and modify a copy of the original metadata. - * - * @param {Object} metadata Original metadata. - * @return {ImageEncoder.MetadataEncoder} Created metadata encoder. - */ -ImageEncoder.createMetadataEncoder = function(metadata) { - var constructor = - (metadata && ImageEncoder.metadataEncoders[metadata.mimeType]) || - ImageEncoder.MetadataEncoder; - return new constructor(metadata); -}; - - -/** - * Create a metadata encoder object holding a copy of metadata - * modified according to the properties of the supplied image. - * - * @param {Object} metadata Original metadata. - * @param {HTMLCanvasElement} canvas Canvas to use for metadata. - * @param {number} quality Encoding quality (defaults to 1). - * @return {ImageEncoder.MetadataEncoder} Encoder with encoded metadata. - */ -ImageEncoder.encodeMetadata = function(metadata, canvas, quality) { - var encoder = ImageEncoder.createMetadataEncoder(metadata); - encoder.setImageData(canvas); - encoder.setThumbnailData(ImageEncoder.createThumbnail(canvas), quality || 1); - return encoder; -}; - - -/** - * Return a blob with the encoded image with metadata inserted. - * @param {HTMLCanvasElement} canvas The canvas with the image to be encoded. - * @param {ImageEncoder.MetadataEncoder} metadataEncoder Encoder to use. - * @param {number} quality (0..1], Encoding quality, defaults to 0.9. - * @return {Blob} encoded data. - */ -ImageEncoder.getBlob = function(canvas, metadataEncoder, quality) { - // Contrary to what one might think 1.0 is not a good default. Opening and - // saving an typical photo taken with consumer camera increases its file size - // by 50-100%. - // Experiments show that 0.9 is much better. It shrinks some photos a bit, - // keeps others about the same size, but does not visibly lower the quality. - quality = quality || 0.9; - - ImageUtil.trace.resetTimer('dataurl'); - // WebKit does not support canvas.toBlob yet so canvas.toDataURL is - // the only way to use the Chrome built-in image encoder. - var dataURL = - canvas.toDataURL(metadataEncoder.getMetadata().mimeType, quality); - ImageUtil.trace.reportTimer('dataurl'); - - var encodedImage = ImageEncoder.decodeDataURL(dataURL); - - var encodedMetadata = metadataEncoder.encode(); - - var slices = []; - - // TODO(kaznacheev): refactor |stringToArrayBuffer| and |encode| to return - // arrays instead of array buffers. - function appendSlice(arrayBuffer) { - slices.push(new DataView(arrayBuffer)); - } - - ImageUtil.trace.resetTimer('blob'); - if (encodedMetadata.byteLength != 0) { - var metadataRange = metadataEncoder.findInsertionRange(encodedImage); - appendSlice(ImageEncoder.stringToArrayBuffer( - encodedImage, 0, metadataRange.from)); - - appendSlice(metadataEncoder.encode()); - - appendSlice(ImageEncoder.stringToArrayBuffer( - encodedImage, metadataRange.to, encodedImage.length)); - } else { - appendSlice(ImageEncoder.stringToArrayBuffer( - encodedImage, 0, encodedImage.length)); - } - var blob = new Blob(slices, {type: metadataEncoder.getMetadata().mimeType}); - ImageUtil.trace.reportTimer('blob'); - return blob; -}; - -/** - * Decode a dataURL into a binary string containing the encoded image. - * - * Why return a string? Calling atob and having the rest of the code deal - * with a string is several times faster than decoding base64 in Javascript. - * - * @param {string} dataURL Data URL to decode. - * @return {string} A binary string (char codes are the actual byte values). - */ -ImageEncoder.decodeDataURL = function(dataURL) { - // Skip the prefix ('data:image/<type>;base64,') - var base64string = dataURL.substring(dataURL.indexOf(',') + 1); - return atob(base64string); -}; - -/** - * Return a thumbnail for an image. - * @param {HTMLCanvasElement} canvas Original image. - * @param {number=} opt_shrinkage Thumbnail should be at least this much smaller - * than the original image (in each dimension). - * @return {HTMLCanvasElement} Thumbnail canvas. - */ -ImageEncoder.createThumbnail = function(canvas, opt_shrinkage) { - var MAX_THUMBNAIL_DIMENSION = 320; - - opt_shrinkage = Math.max(opt_shrinkage || 4, - canvas.width / MAX_THUMBNAIL_DIMENSION, - canvas.height / MAX_THUMBNAIL_DIMENSION); - - var thumbnailCanvas = canvas.ownerDocument.createElement('canvas'); - thumbnailCanvas.width = Math.round(canvas.width / opt_shrinkage); - thumbnailCanvas.height = Math.round(canvas.height / opt_shrinkage); - - var context = thumbnailCanvas.getContext('2d'); - context.drawImage(canvas, - 0, 0, canvas.width, canvas.height, - 0, 0, thumbnailCanvas.width, thumbnailCanvas.height); - - return thumbnailCanvas; -}; - -/** - * TODO(JSDOC) - * @param {string} string // TODO(JSDOC). - * @param {number} from // TODO(JSDOC). - * @param {number} to // TODO(JSDOC). - * @return {ArrayBuffer} // TODO(JSDOC). - */ -ImageEncoder.stringToArrayBuffer = function(string, from, to) { - var size = to - from; - var array = new Uint8Array(size); - for (var i = 0; i != size; i++) { - array[i] = string.charCodeAt(from + i); - } - return array.buffer; -}; - -/** - * A base class for a metadata encoder. - * - * Serves as a default metadata encoder for images that none of the metadata - * parsers recognized. - * - * @param {Object} original_metadata Starting metadata. - * @constructor - */ -ImageEncoder.MetadataEncoder = function(original_metadata) { - this.metadata_ = MetadataCache.cloneMetadata(original_metadata) || {}; - if (this.metadata_.mimeType != 'image/jpeg') { - // Chrome can only encode JPEG and PNG. Force PNG mime type so that we - // can save to file and generate a thumbnail. - this.metadata_.mimeType = 'image/png'; - } -}; - -/** - * TODO(JSDOC) - * @return {Object} // TODO(JSDOC). - */ -ImageEncoder.MetadataEncoder.prototype.getMetadata = function() { - return this.metadata_; -}; - -/** - * @param {HTMLCanvasElement|Object} canvas Canvas or or anything with - * width and height properties. - */ -ImageEncoder.MetadataEncoder.prototype.setImageData = function(canvas) { - this.metadata_.width = canvas.width; - this.metadata_.height = canvas.height; -}; - -/** - * @param {HTMLCanvasElement} canvas Canvas to use as thumbnail. - * @param {number} quality Thumbnail quality. - */ -ImageEncoder.MetadataEncoder.prototype.setThumbnailData = - function(canvas, quality) { - this.metadata_.thumbnailURL = - canvas.toDataURL(this.metadata_.mimeType, quality); - delete this.metadata_.thumbnailTransform; -}; - -/** - * Return a range where the metadata is (or should be) located. - * @param {string} encodedImage // TODO(JSDOC). - * @return {Object} An object with from and to properties. - */ -ImageEncoder.MetadataEncoder.prototype. - findInsertionRange = function(encodedImage) { return {from: 0, to: 0}; }; - -/** - * Return serialized metadata ready to write to an image file. - * The return type is optimized for passing to Blob.append. - * @return {ArrayBuffer} // TODO(JSDOC). - */ -ImageEncoder.MetadataEncoder.prototype.encode = function() { - return new Uint8Array(0).buffer; -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/image_transform.js b/ui/file_manager/file_manager/foreground/js/image_editor/image_transform.js deleted file mode 100644 index 776b9be..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/image_transform.js +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright 2014 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'; - -/** - * Crop mode. - * @constructor - */ -ImageEditor.Mode.Crop = function() { - ImageEditor.Mode.call(this, 'crop', 'GALLERY_CROP'); -}; - -ImageEditor.Mode.Crop.prototype = {__proto__: ImageEditor.Mode.prototype}; - -/** - * TODO(JSDOC). - */ -ImageEditor.Mode.Crop.prototype.setUp = function() { - ImageEditor.Mode.prototype.setUp.apply(this, arguments); - - var container = this.getImageView().container_; - var doc = container.ownerDocument; - - this.domOverlay_ = doc.createElement('div'); - this.domOverlay_.className = 'crop-overlay'; - container.appendChild(this.domOverlay_); - - this.shadowTop_ = doc.createElement('div'); - this.shadowTop_.className = 'shadow'; - this.domOverlay_.appendChild(this.shadowTop_); - - this.middleBox_ = doc.createElement('div'); - this.middleBox_.className = 'middle-box'; - this.domOverlay_.appendChild(this.middleBox_); - - this.shadowLeft_ = doc.createElement('div'); - this.shadowLeft_.className = 'shadow'; - this.middleBox_.appendChild(this.shadowLeft_); - - this.cropFrame_ = doc.createElement('div'); - this.cropFrame_.className = 'crop-frame'; - this.middleBox_.appendChild(this.cropFrame_); - - this.shadowRight_ = doc.createElement('div'); - this.shadowRight_.className = 'shadow'; - this.middleBox_.appendChild(this.shadowRight_); - - this.shadowBottom_ = doc.createElement('div'); - this.shadowBottom_.className = 'shadow'; - this.domOverlay_.appendChild(this.shadowBottom_); - - var cropFrame = this.cropFrame_; - function addCropFrame(className) { - var div = doc.createElement('div'); - div.className = className; - cropFrame.appendChild(div); - } - - addCropFrame('left top corner'); - addCropFrame('top horizontal'); - addCropFrame('right top corner'); - addCropFrame('left vertical'); - addCropFrame('right vertical'); - addCropFrame('left bottom corner'); - addCropFrame('bottom horizontal'); - addCropFrame('right bottom corner'); - - this.onResizedBound_ = this.onResized_.bind(this); - window.addEventListener('resize', this.onResizedBound_); - - this.createDefaultCrop(); -}; - -/** - * Handles resizing of the window and updates the crop rectangle. - * @private - */ -ImageEditor.Mode.Crop.prototype.onResized_ = function() { - this.positionDOM(); -}; - -/** - * TODO(JSDOC). - */ -ImageEditor.Mode.Crop.prototype.reset = function() { - ImageEditor.Mode.prototype.reset.call(this); - this.createDefaultCrop(); -}; - -/** - * TODO(JSDOC). - */ -ImageEditor.Mode.Crop.prototype.positionDOM = function() { - var screenClipped = this.viewport_.getScreenClipped(); - - var screenCrop = this.viewport_.imageToScreenRect(this.cropRect_.getRect()); - var delta = ImageEditor.Mode.Crop.MOUSE_GRAB_RADIUS; - this.editor_.hideOverlappingTools( - screenCrop.inflate(delta, delta), - screenCrop.inflate(-delta, -delta)); - - this.domOverlay_.style.left = screenClipped.left + 'px'; - this.domOverlay_.style.top = screenClipped.top + 'px'; - this.domOverlay_.style.width = screenClipped.width + 'px'; - this.domOverlay_.style.height = screenClipped.height + 'px'; - - this.shadowLeft_.style.width = screenCrop.left - screenClipped.left + 'px'; - - this.shadowTop_.style.height = screenCrop.top - screenClipped.top + 'px'; - - this.shadowRight_.style.width = screenClipped.left + screenClipped.width - - (screenCrop.left + screenCrop.width) + 'px'; - - this.shadowBottom_.style.height = screenClipped.top + screenClipped.height - - (screenCrop.top + screenCrop.height) + 'px'; -}; - -/** - * TODO(JSDOC). - */ -ImageEditor.Mode.Crop.prototype.cleanUpUI = function() { - ImageEditor.Mode.prototype.cleanUpUI.apply(this, arguments); - this.domOverlay_.parentNode.removeChild(this.domOverlay_); - this.domOverlay_ = null; - this.editor_.hideOverlappingTools(); - window.removeEventListener(this.onResizedBound_); - this.onResizedBound_ = null; -}; - -/** - * @const - * @type {number} - */ -ImageEditor.Mode.Crop.MOUSE_GRAB_RADIUS = 6; -/** - * @const - * @type {number} - */ -ImageEditor.Mode.Crop.TOUCH_GRAB_RADIUS = 20; - -/** - * TODO(JSDOC). - * @return {Command.Crop} // TODO(JSDOC). - */ -ImageEditor.Mode.Crop.prototype.getCommand = function() { - var cropImageRect = this.cropRect_.getRect(); - return new Command.Crop(cropImageRect); -}; - -/** - * TODO(JSDOC). - */ -ImageEditor.Mode.Crop.prototype.createDefaultCrop = function() { - var rect = new Rect(this.getViewport().getImageClipped()); - rect = rect.inflate( - -Math.round(rect.width / 6), -Math.round(rect.height / 6)); - this.cropRect_ = new DraggableRect(rect, this.getViewport()); - this.positionDOM(); -}; - -/** - * TODO(JSDOC). - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {string} A value for style.cursor CSS property. - */ -ImageEditor.Mode.Crop.prototype.getCursorStyle = function(x, y, mouseDown) { - return this.cropRect_.getCursorStyle(x, y, mouseDown); -}; - -/** - * TODO(JSDOC). - * @param {number} x Event X coordinate. - * @param {number} y Event Y coordinate. - * @param {boolean} touch True if it's a touch event, false if mouse. - * @return {function(number,number)} A function to be called on mouse drag. - */ -ImageEditor.Mode.Crop.prototype.getDragHandler = function(x, y, touch) { - var cropDragHandler = this.cropRect_.getDragHandler(x, y, touch); - if (!cropDragHandler) return null; - - var self = this; - return function(x, y) { - cropDragHandler(x, y); - self.markUpdated(); - self.positionDOM(); - }; -}; - -/** - * TODO(JSDOC). - * @param {number} x X coordinate of the event. - * @param {number} y Y coordinate of the event. - * @return {ImageBuffer.DoubleTapAction} Action to perform as result. - */ -ImageEditor.Mode.Crop.prototype.getDoubleTapAction = function(x, y) { - return this.cropRect_.getDoubleTapAction(x, y); -}; - -/* - * A draggable rectangle over the image. - * @param {Rect} rect // TODO(JSDOC). - * @param {Viewport} viewport // TODO(JSDOC). - * @constructor - */ -function DraggableRect(rect, viewport) { - // The bounds are not held in a regular rectangle (with width/height). - // left/top/right/bottom held instead for convenience. - this.bounds_ = {}; - this.bounds_[DraggableRect.LEFT] = rect.left; - this.bounds_[DraggableRect.RIGHT] = rect.left + rect.width; - this.bounds_[DraggableRect.TOP] = rect.top; - this.bounds_[DraggableRect.BOTTOM] = rect.top + rect.height; - - this.viewport_ = viewport; - - this.oppositeSide_ = {}; - this.oppositeSide_[DraggableRect.LEFT] = DraggableRect.RIGHT; - this.oppositeSide_[DraggableRect.RIGHT] = DraggableRect.LEFT; - this.oppositeSide_[DraggableRect.TOP] = DraggableRect.BOTTOM; - this.oppositeSide_[DraggableRect.BOTTOM] = DraggableRect.TOP; - - // Translation table to form CSS-compatible cursor style. - this.cssSide_ = {}; - this.cssSide_[DraggableRect.LEFT] = 'w'; - this.cssSide_[DraggableRect.TOP] = 'n'; - this.cssSide_[DraggableRect.RIGHT] = 'e'; - this.cssSide_[DraggableRect.BOTTOM] = 's'; - this.cssSide_[DraggableRect.NONE] = ''; -} - -// Static members to simplify reflective access to the bounds. -/** - * @const - * @type {string} - */ -DraggableRect.LEFT = 'left'; -/** - * @const - * @type {string} - */ -DraggableRect.RIGHT = 'right'; -/** - * @const - * @type {string} - */ -DraggableRect.TOP = 'top'; -/** - * @const - * @type {string} - */ -DraggableRect.BOTTOM = 'bottom'; -/** - * @const - * @type {string} - */ -DraggableRect.NONE = 'none'; - -/** - * TODO(JSDOC) - * @return {number} // TODO(JSDOC). - */ -DraggableRect.prototype.getLeft = function() { - return this.bounds_[DraggableRect.LEFT]; -}; - -/** - * TODO(JSDOC) - * @return {number} // TODO(JSDOC). - */ -DraggableRect.prototype.getRight = function() { - return this.bounds_[DraggableRect.RIGHT]; -}; - -/** - * TODO(JSDOC) - * @return {number} // TODO(JSDOC). - */ -DraggableRect.prototype.getTop = function() { - return this.bounds_[DraggableRect.TOP]; -}; - -/** - * TODO(JSDOC) - * @return {number} // TODO(JSDOC). - */ -DraggableRect.prototype.getBottom = function() { - return this.bounds_[DraggableRect.BOTTOM]; -}; - -/** - * TODO(JSDOC) - * @return {Rect} // TODO(JSDOC). - */ -DraggableRect.prototype.getRect = function() { - return new Rect(this.bounds_); -}; - -/** - * TODO(JSDOC) - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} touch // TODO(JSDOC). - * @return {Object} // TODO(JSDOC). - */ -DraggableRect.prototype.getDragMode = function(x, y, touch) { - var result = { - xSide: DraggableRect.NONE, - ySide: DraggableRect.NONE - }; - - var bounds = this.bounds_; - var R = this.viewport_.screenToImageSize( - touch ? ImageEditor.Mode.Crop.TOUCH_GRAB_RADIUS : - ImageEditor.Mode.Crop.MOUSE_GRAB_RADIUS); - - var circle = new Circle(x, y, R); - - var xBetween = ImageUtil.between(bounds.left, x, bounds.right); - var yBetween = ImageUtil.between(bounds.top, y, bounds.bottom); - - if (circle.inside(bounds.left, bounds.top)) { - result.xSide = DraggableRect.LEFT; - result.ySide = DraggableRect.TOP; - } else if (circle.inside(bounds.left, bounds.bottom)) { - result.xSide = DraggableRect.LEFT; - result.ySide = DraggableRect.BOTTOM; - } else if (circle.inside(bounds.right, bounds.top)) { - result.xSide = DraggableRect.RIGHT; - result.ySide = DraggableRect.TOP; - } else if (circle.inside(bounds.right, bounds.bottom)) { - result.xSide = DraggableRect.RIGHT; - result.ySide = DraggableRect.BOTTOM; - } else if (yBetween && Math.abs(x - bounds.left) <= R) { - result.xSide = DraggableRect.LEFT; - } else if (yBetween && Math.abs(x - bounds.right) <= R) { - result.xSide = DraggableRect.RIGHT; - } else if (xBetween && Math.abs(y - bounds.top) <= R) { - result.ySide = DraggableRect.TOP; - } else if (xBetween && Math.abs(y - bounds.bottom) <= R) { - result.ySide = DraggableRect.BOTTOM; - } else if (xBetween && yBetween) { - result.whole = true; - } else { - result.newcrop = true; - result.xSide = DraggableRect.RIGHT; - result.ySide = DraggableRect.BOTTOM; - } - - return result; -}; - -/** - * TODO(JSDOC) - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} mouseDown If mouse button is down. - * @return {string} // TODO(JSDOC). - */ -DraggableRect.prototype.getCursorStyle = function(x, y, mouseDown) { - var mode; - if (mouseDown) { - mode = this.dragMode_; - } else { - mode = this.getDragMode( - this.viewport_.screenToImageX(x), this.viewport_.screenToImageY(y)); - } - if (mode.whole) return 'move'; - if (mode.newcrop) return 'crop'; - return this.cssSide_[mode.ySide] + this.cssSide_[mode.xSide] + '-resize'; -}; - -/** - * TODO(JSDOC) - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} touch // TODO(JSDOC). - * @return {function(number,number)} // TODO(JSDOC). - */ -DraggableRect.prototype.getDragHandler = function(x, y, touch) { - x = this.viewport_.screenToImageX(x); - y = this.viewport_.screenToImageY(y); - - var clipRect = this.viewport_.getImageClipped(); - if (!clipRect.inside(x, y)) return null; - - this.dragMode_ = this.getDragMode(x, y, touch); - - var self = this; - - var mouseBiasX; - var mouseBiasY; - - var fixedWidth = 0; - var fixedHeight = 0; - - var resizeFuncX; - var resizeFuncY; - - if (this.dragMode_.whole) { - mouseBiasX = this.bounds_.left - x; - fixedWidth = this.bounds_.right - this.bounds_.left; - resizeFuncX = function(x) { - self.bounds_.left = x; - self.bounds_.right = self.bounds_.left + fixedWidth; - }; - mouseBiasY = this.bounds_.top - y; - fixedHeight = this.bounds_.bottom - this.bounds_.top; - resizeFuncY = function(y) { - self.bounds_.top = y; - self.bounds_.bottom = self.bounds_.top + fixedHeight; - }; - } else { - var checkNewCrop = function() { - if (self.dragMode_.newcrop) { - self.dragMode_.newcrop = false; - self.bounds_.left = self.bounds_.right = x; - self.bounds_.top = self.bounds_.bottom = y; - mouseBiasX = 0; - mouseBiasY = 0; - } - }; - - var flipSide = function(side) { - var opposite = self.oppositeSide_[side]; - var temp = self.bounds_[side]; - self.bounds_[side] = self.bounds_[opposite]; - self.bounds_[opposite] = temp; - return opposite; - }; - - if (this.dragMode_.xSide != DraggableRect.NONE) { - mouseBiasX = self.bounds_[this.dragMode_.xSide] - x; - resizeFuncX = function(x) { - checkNewCrop(); - self.bounds_[self.dragMode_.xSide] = x; - if (self.bounds_.left > self.bounds_.right) { - self.dragMode_.xSide = flipSide(self.dragMode_.xSide); - } - }; - } - if (this.dragMode_.ySide != DraggableRect.NONE) { - mouseBiasY = self.bounds_[this.dragMode_.ySide] - y; - resizeFuncY = function(y) { - checkNewCrop(); - self.bounds_[self.dragMode_.ySide] = y; - if (self.bounds_.top > self.bounds_.bottom) { - self.dragMode_.ySide = flipSide(self.dragMode_.ySide); - } - }; - } - } - - function convertX(x) { - return ImageUtil.clamp( - clipRect.left, - self.viewport_.screenToImageX(x) + mouseBiasX, - clipRect.left + clipRect.width - fixedWidth); - } - - function convertY(y) { - return ImageUtil.clamp( - clipRect.top, - self.viewport_.screenToImageY(y) + mouseBiasY, - clipRect.top + clipRect.height - fixedHeight); - } - - return function(x, y) { - if (resizeFuncX) resizeFuncX(convertX(x)); - if (resizeFuncY) resizeFuncY(convertY(y)); - }; -}; - -/** - * TODO(JSDOC) - * @param {number} x X coordinate for cursor. - * @param {number} y Y coordinate for cursor. - * @param {boolean} touch // TODO(JSDOC). - * @return {ImageBuffer.DoubleTapAction} // TODO(JSDOC). - */ -DraggableRect.prototype.getDoubleTapAction = function(x, y, touch) { - x = this.viewport_.screenToImageX(x); - y = this.viewport_.screenToImageY(y); - - var clipRect = this.viewport_.getImageClipped(); - if (clipRect.inside(x, y)) - return ImageBuffer.DoubleTapAction.COMMIT; - else - return ImageBuffer.DoubleTapAction.NOTHING; -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/image_util.js b/ui/file_manager/file_manager/foreground/js/image_editor/image_util.js deleted file mode 100644 index cad2f159..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/image_util.js +++ /dev/null @@ -1,678 +0,0 @@ -// Copyright 2014 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'; - - -// Namespace object for the utilities. -function ImageUtil() {} - -/** - * Performance trace. - */ -ImageUtil.trace = (function() { - function PerformanceTrace() { - this.lines_ = {}; - this.timers_ = {}; - this.container_ = null; - } - - PerformanceTrace.prototype.bindToDOM = function(container) { - this.container_ = container; - }; - - PerformanceTrace.prototype.report = function(key, value) { - if (!(key in this.lines_)) { - if (this.container_) { - var div = this.lines_[key] = document.createElement('div'); - this.container_.appendChild(div); - } else { - this.lines_[key] = {}; - } - } - this.lines_[key].textContent = key + ': ' + value; - if (ImageUtil.trace.log) this.dumpLine(key); - }; - - PerformanceTrace.prototype.resetTimer = function(key) { - this.timers_[key] = Date.now(); - }; - - PerformanceTrace.prototype.reportTimer = function(key) { - this.report(key, (Date.now() - this.timers_[key]) + 'ms'); - }; - - PerformanceTrace.prototype.dump = function() { - for (var key in this.lines_) - this.dumpLine(key); - }; - - PerformanceTrace.prototype.dumpLine = function(key) { - console.log('trace.' + this.lines_[key].textContent); - }; - - return new PerformanceTrace(); -})(); - -/** - * @param {number} min Minimum value. - * @param {number} value Value to adjust. - * @param {number} max Maximum value. - * @return {number} The closest to the |value| number in span [min, max]. - */ -ImageUtil.clamp = function(min, value, max) { - return Math.max(min, Math.min(max, value)); -}; - -/** - * @param {number} min Minimum value. - * @param {number} value Value to check. - * @param {number} max Maximum value. - * @return {boolean} True if value is between. - */ -ImageUtil.between = function(min, value, max) { - return (value - min) * (value - max) <= 0; -}; - -/** - * Rectangle class. - */ - -/** - * Rectangle constructor takes 0, 1, 2 or 4 arguments. - * Supports following variants: - * new Rect(left, top, width, height) - * new Rect(width, height) - * new Rect(rect) // anything with left, top, width, height properties - * new Rect(bounds) // anything with left, top, right, bottom properties - * new Rect(canvas|image) // anything with width and height properties. - * new Rect() // empty rectangle. - * @constructor - */ -function Rect() { - switch (arguments.length) { - case 4: - this.left = arguments[0]; - this.top = arguments[1]; - this.width = arguments[2]; - this.height = arguments[3]; - return; - - case 2: - this.left = 0; - this.top = 0; - this.width = arguments[0]; - this.height = arguments[1]; - return; - - case 1: { - var source = arguments[0]; - if ('left' in source && 'top' in source) { - this.left = source.left; - this.top = source.top; - if ('right' in source && 'bottom' in source) { - this.width = source.right - source.left; - this.height = source.bottom - source.top; - return; - } - } else { - this.left = 0; - this.top = 0; - } - if ('width' in source && 'height' in source) { - this.width = source.width; - this.height = source.height; - return; - } - break; // Fall through to the error message. - } - - case 0: - this.left = 0; - this.top = 0; - this.width = 0; - this.height = 0; - return; - } - console.error('Invalid Rect constructor arguments:', - Array.apply(null, arguments)); -} - -/** - * @param {number} factor Factor to scale. - * @return {Rect} A rectangle with every dimension scaled. - */ -Rect.prototype.scale = function(factor) { - return new Rect( - this.left * factor, - this.top * factor, - this.width * factor, - this.height * factor); -}; - -/** - * @param {number} dx Difference in X. - * @param {number} dy Difference in Y. - * @return {Rect} A rectangle shifted by (dx,dy), same size. - */ -Rect.prototype.shift = function(dx, dy) { - return new Rect(this.left + dx, this.top + dy, this.width, this.height); -}; - -/** - * @param {number} x Coordinate of the left top corner. - * @param {number} y Coordinate of the left top corner. - * @return {Rect} A rectangle with left==x and top==y, same size. - */ -Rect.prototype.moveTo = function(x, y) { - return new Rect(x, y, this.width, this.height); -}; - -/** - * @param {number} dx Difference in X. - * @param {number} dy Difference in Y. - * @return {Rect} A rectangle inflated by (dx, dy), same center. - */ -Rect.prototype.inflate = function(dx, dy) { - return new Rect( - this.left - dx, this.top - dy, this.width + 2 * dx, this.height + 2 * dy); -}; - -/** - * @param {number} x Coordinate of the point. - * @param {number} y Coordinate of the point. - * @return {boolean} True if the point lies inside the rectangle. - */ -Rect.prototype.inside = function(x, y) { - return this.left <= x && x < this.left + this.width && - this.top <= y && y < this.top + this.height; -}; - -/** - * @param {Rect} rect Rectangle to check. - * @return {boolean} True if this rectangle intersects with the |rect|. - */ -Rect.prototype.intersects = function(rect) { - return (this.left + this.width) > rect.left && - (rect.left + rect.width) > this.left && - (this.top + this.height) > rect.top && - (rect.top + rect.height) > this.top; -}; - -/** - * @param {Rect} rect Rectangle to check. - * @return {boolean} True if this rectangle containing the |rect|. - */ -Rect.prototype.contains = function(rect) { - return (this.left <= rect.left) && - (rect.left + rect.width) <= (this.left + this.width) && - (this.top <= rect.top) && - (rect.top + rect.height) <= (this.top + this.height); -}; - -/** - * @return {boolean} True if rectangle is empty. - */ -Rect.prototype.isEmpty = function() { - return this.width === 0 || this.height === 0; -}; - -/** - * Clamp the rectangle to the bounds by moving it. - * Decrease the size only if necessary. - * @param {Rect} bounds Bounds. - * @return {Rect} Calculated rectangle. - */ -Rect.prototype.clamp = function(bounds) { - var rect = new Rect(this); - - if (rect.width > bounds.width) { - rect.left = bounds.left; - rect.width = bounds.width; - } else if (rect.left < bounds.left) { - rect.left = bounds.left; - } else if (rect.left + rect.width > - bounds.left + bounds.width) { - rect.left = bounds.left + bounds.width - rect.width; - } - - if (rect.height > bounds.height) { - rect.top = bounds.top; - rect.height = bounds.height; - } else if (rect.top < bounds.top) { - rect.top = bounds.top; - } else if (rect.top + rect.height > - bounds.top + bounds.height) { - rect.top = bounds.top + bounds.height - rect.height; - } - - return rect; -}; - -/** - * @return {string} String representation. - */ -Rect.prototype.toString = function() { - return '(' + this.left + ',' + this.top + '):' + - '(' + (this.left + this.width) + ',' + (this.top + this.height) + ')'; -}; -/* - * Useful shortcuts for drawing (static functions). - */ - -/** - * Draw the image in context with appropriate scaling. - * @param {CanvasRenderingContext2D} context Context to draw. - * @param {Image} image Image to draw. - * @param {Rect=} opt_dstRect Rectangle in the canvas (whole canvas by default). - * @param {Rect=} opt_srcRect Rectangle in the image (whole image by default). - */ -Rect.drawImage = function(context, image, opt_dstRect, opt_srcRect) { - opt_dstRect = opt_dstRect || new Rect(context.canvas); - opt_srcRect = opt_srcRect || new Rect(image); - if (opt_dstRect.isEmpty() || opt_srcRect.isEmpty()) - return; - context.drawImage(image, - opt_srcRect.left, opt_srcRect.top, opt_srcRect.width, opt_srcRect.height, - opt_dstRect.left, opt_dstRect.top, opt_dstRect.width, opt_dstRect.height); -}; - -/** - * Draw a box around the rectangle. - * @param {CanvasRenderingContext2D} context Context to draw. - * @param {Rect} rect Rectangle. - */ -Rect.outline = function(context, rect) { - context.strokeRect( - rect.left - 0.5, rect.top - 0.5, rect.width + 1, rect.height + 1); -}; - -/** - * Fill the rectangle. - * @param {CanvasRenderingContext2D} context Context to draw. - * @param {Rect} rect Rectangle. - */ -Rect.fill = function(context, rect) { - context.fillRect(rect.left, rect.top, rect.width, rect.height); -}; - -/** - * Fills the space between the two rectangles. - * @param {CanvasRenderingContext2D} context Context to draw. - * @param {Rect} inner Inner rectangle. - * @param {Rect} outer Outer rectangle. - */ -Rect.fillBetween = function(context, inner, outer) { - var innerRight = inner.left + inner.width; - var innerBottom = inner.top + inner.height; - var outerRight = outer.left + outer.width; - var outerBottom = outer.top + outer.height; - if (inner.top > outer.top) { - context.fillRect( - outer.left, outer.top, outer.width, inner.top - outer.top); - } - if (inner.left > outer.left) { - context.fillRect( - outer.left, inner.top, inner.left - outer.left, inner.height); - } - if (inner.width < outerRight) { - context.fillRect( - innerRight, inner.top, outerRight - innerRight, inner.height); - } - if (inner.height < outerBottom) { - context.fillRect( - outer.left, innerBottom, outer.width, outerBottom - innerBottom); - } -}; - -/** - * Circle class. - * @param {number} x X coordinate of circle center. - * @param {number} y Y coordinate of circle center. - * @param {number} r Radius. - * @constructor - */ -function Circle(x, y, r) { - this.x = x; - this.y = y; - this.squaredR = r * r; -} - -/** - * Check if the point is inside the circle. - * @param {number} x X coordinate of the point. - * @param {number} y Y coordinate of the point. - * @return {boolean} True if the point is inside. - */ -Circle.prototype.inside = function(x, y) { - x -= this.x; - y -= this.y; - return x * x + y * y <= this.squaredR; -}; - -/** - * Copy an image applying scaling and rotation. - * - * @param {HTMLCanvasElement} dst Destination. - * @param {HTMLCanvasElement|HTMLImageElement} src Source. - * @param {number} scaleX Y scale transformation. - * @param {number} scaleY X scale transformation. - * @param {number} angle (in radians). - */ -ImageUtil.drawImageTransformed = function(dst, src, scaleX, scaleY, angle) { - var context = dst.getContext('2d'); - context.save(); - context.translate(context.canvas.width / 2, context.canvas.height / 2); - context.rotate(angle); - context.scale(scaleX, scaleY); - context.drawImage(src, -src.width / 2, -src.height / 2); - context.restore(); -}; - -/** - * Adds or removes an attribute to/from an HTML element. - * @param {HTMLElement} element To be applied to. - * @param {string} attribute Name of attribute. - * @param {boolean} on True if add, false if remove. - */ -ImageUtil.setAttribute = function(element, attribute, on) { - if (on) - element.setAttribute(attribute, ''); - else - element.removeAttribute(attribute); -}; - -/** - * Adds or removes CSS class to/from an HTML element. - * @param {HTMLElement} element To be applied to. - * @param {string} className Name of CSS class. - * @param {boolean} on True if add, false if remove. - */ -ImageUtil.setClass = function(element, className, on) { - var cl = element.classList; - if (on) - cl.add(className); - else - cl.remove(className); -}; - -/** - * ImageLoader loads an image from a given Entry into a canvas in two steps: - * 1. Loads the image into an HTMLImageElement. - * 2. Copies pixels from HTMLImageElement to HTMLCanvasElement. This is done - * stripe-by-stripe to avoid freezing up the UI. The transform is taken into - * account. - * - * @param {HTMLDocument} document Owner document. - * @param {MetadataCache=} opt_metadataCache Metadata cache. Required for - * caching. If not passed, caching will be disabled. - * @constructor - */ -ImageUtil.ImageLoader = function(document, opt_metadataCache) { - this.document_ = document; - this.metadataCache_ = opt_metadataCache || null; - this.image_ = new Image(); - this.generation_ = 0; -}; - -/** - * Loads an image. - * TODO(mtomasz): Simplify, or even get rid of this class and merge with the - * ThumbnaiLoader class. - * - * @param {FileEntry} entry Image entry to be loaded. - * @param {function(function(object))} transformFetcher function to get - * the image transform (which we need for the image orientation). - * @param {function(HTMLCanvasElement, string=)} callback Callback to be - * called when loaded. The second optional argument is an error identifier. - * @param {number=} opt_delay Load delay in milliseconds, useful to let the - * animations play out before the computation heavy image loading starts. - */ -ImageUtil.ImageLoader.prototype.load = function( - entry, transformFetcher, callback, opt_delay) { - this.cancel(); - - this.entry_ = entry; - this.callback_ = callback; - - // The transform fetcher is not cancellable so we need a generation counter. - var generation = ++this.generation_; - var onTransform = function(image, transform) { - if (generation === this.generation_) { - this.convertImage_( - image, transform || { scaleX: 1, scaleY: 1, rotate90: 0}); - } - }; - - var onError = function(opt_error) { - this.image_.onerror = null; - this.image_.onload = null; - var tmpCallback = this.callback_; - this.callback_ = null; - var emptyCanvas = this.document_.createElement('canvas'); - emptyCanvas.width = 0; - emptyCanvas.height = 0; - tmpCallback(emptyCanvas, opt_error); - }.bind(this); - - var loadImage = function(opt_metadata) { - ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime')); - this.timeout_ = null; - - this.image_.onload = function(e) { - this.image_.onerror = null; - this.image_.onload = null; - - transformFetcher(entry, onTransform.bind(this, e.target)); - }.bind(this); - - // The error callback has an optional error argument, which in case of a - // general error should not be specified - this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR'); - - // Load the image directly. The query parameter is workaround for - // crbug.com/379678, which force to update the contents of the image. - this.image_.src = entry.toURL() + "?nocache=" + Date.now(); - }.bind(this); - - // Loads the image. If already loaded, then forces a reload. - var startLoad = this.resetImage_.bind(this, function() { - // Fetch metadata to detect last modification time for the caching purpose. - if (this.metadataCache_) - this.metadataCache_.getOne(entry, 'filesystem', loadImage); - else - loadImage(); - }.bind(this), onError); - - if (opt_delay) { - this.timeout_ = setTimeout(startLoad, opt_delay); - } else { - startLoad(); - } -}; - -/** - * Resets the image by forcing the garbage collection and clearing the src - * attribute. - * - * @param {function()} onSuccess Success callback. - * @param {function(opt_string)} onError Failure callback with an optional - * error identifier. - * @private - */ -ImageUtil.ImageLoader.prototype.resetImage_ = function(onSuccess, onError) { - var clearSrc = function() { - this.image_.onload = onSuccess; - this.image_.onerror = onSuccess; - this.image_.src = ''; - }.bind(this); - - var emptyImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' + - 'AAABAAEAAAICTAEAOw=='; - - if (this.image_.src !== emptyImage) { - // Load an empty image, then clear src. - this.image_.onload = clearSrc; - this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR'); - this.image_.src = emptyImage; - } else { - // Empty image already loaded, so clear src immediately. - clearSrc(); - } -}; - -/** - * @return {boolean} True if an image is loading. - */ -ImageUtil.ImageLoader.prototype.isBusy = function() { - return !!this.callback_; -}; - -/** - * @param {Entry} entry Image entry. - * @return {boolean} True if loader loads this image. - */ -ImageUtil.ImageLoader.prototype.isLoading = function(entry) { - return this.isBusy() && util.isSameEntry(this.entry_, entry); -}; - -/** - * @param {function} callback To be called when the image loaded. - */ -ImageUtil.ImageLoader.prototype.setCallback = function(callback) { - this.callback_ = callback; -}; - -/** - * Stops loading image. - */ -ImageUtil.ImageLoader.prototype.cancel = function() { - if (!this.callback_) return; - this.callback_ = null; - if (this.timeout_) { - clearTimeout(this.timeout_); - this.timeout_ = null; - } - if (this.image_) { - this.image_.onload = function() {}; - this.image_.onerror = function() {}; - this.image_.src = ''; - } - this.generation_++; // Silence the transform fetcher if it is in progress. -}; - -/** - * @param {HTMLImageElement} image Image to be transformed. - * @param {Object} transform transformation description to apply to the image. - * @private - */ -ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) { - var canvas = this.document_.createElement('canvas'); - - if (transform.rotate90 & 1) { // Rotated +/-90deg, swap the dimensions. - canvas.width = image.height; - canvas.height = image.width; - } else { - canvas.width = image.width; - canvas.height = image.height; - } - - var context = canvas.getContext('2d'); - context.save(); - context.translate(canvas.width / 2, canvas.height / 2); - context.rotate(transform.rotate90 * Math.PI / 2); - context.scale(transform.scaleX, transform.scaleY); - - var stripCount = Math.ceil(image.width * image.height / (1 << 21)); - var step = Math.max(16, Math.ceil(image.height / stripCount)) & 0xFFFFF0; - - this.copyStrip_(context, image, 0, step); -}; - -/** - * @param {CanvasRenderingContext2D} context Context to draw. - * @param {HTMLImageElement} image Image to draw. - * @param {number} firstRow Number of the first pixel row to draw. - * @param {number} rowCount Count of pixel rows to draw. - * @private - */ -ImageUtil.ImageLoader.prototype.copyStrip_ = function( - context, image, firstRow, rowCount) { - var lastRow = Math.min(firstRow + rowCount, image.height); - - context.drawImage( - image, 0, firstRow, image.width, lastRow - firstRow, - -image.width / 2, firstRow - image.height / 2, - image.width, lastRow - firstRow); - - if (lastRow === image.height) { - context.restore(); - if (this.entry_.toURL().substr(0, 5) !== 'data:') { // Ignore data urls. - ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime')); - } - try { - setTimeout(this.callback_, 0, context.canvas); - } catch (e) { - console.error(e); - } - this.callback_ = null; - } else { - var self = this; - this.timeout_ = setTimeout( - function() { - self.timeout_ = null; - self.copyStrip_(context, image, lastRow, rowCount); - }, 0); - } -}; - -/** - * @param {HTMLElement} element To remove children from. - */ -ImageUtil.removeChildren = function(element) { - element.textContent = ''; -}; - -/** - * @param {string} name File name (with extension). - * @return {string} File name without extension. - */ -ImageUtil.getDisplayNameFromName = function(name) { - var index = name.lastIndexOf('.'); - if (index !== -1) - return name.substr(0, index); - else - return name; -}; - -/** - * @param {string} name File name. - * @return {string} File extension. - */ -ImageUtil.getExtensionFromFullName = function(name) { - var index = name.lastIndexOf('.'); - if (index !== -1) - return name.substring(index); - else - return ''; -}; - -/** - * Metrics (from metrics.js) itnitialized by the File Manager from owner frame. - * @type {Object?} - */ -ImageUtil.metrics = null; - -/** - * @param {string} name Local name. - * @return {string} Full name. - */ -ImageUtil.getMetricName = function(name) { - return 'PhotoEditor.' + name; -}; - -/** - * Used for metrics reporting, keep in sync with the histogram description. - */ -ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp']; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/image_view.js b/ui/file_manager/file_manager/foreground/js/image_editor/image_view.js deleted file mode 100644 index f899341..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/image_view.js +++ /dev/null @@ -1,1076 +0,0 @@ -// Copyright 2014 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'; - -/** - * The overlay displaying the image. - * - * @param {HTMLElement} container The container element. - * @param {Viewport} viewport The viewport. - * @param {MetadataCache} metadataCache The metadataCache. - * @constructor - */ -function ImageView(container, viewport, metadataCache) { - this.container_ = container; - this.viewport_ = viewport; - this.document_ = container.ownerDocument; - this.contentGeneration_ = 0; - this.displayedContentGeneration_ = 0; - this.displayedViewportGeneration_ = 0; - - this.imageLoader_ = new ImageUtil.ImageLoader(this.document_, metadataCache); - // We have a separate image loader for prefetch which does not get cancelled - // when the selection changes. - this.prefetchLoader_ = new ImageUtil.ImageLoader( - this.document_, metadataCache); - - // The content cache is used for prefetching the next image when going - // through the images sequentially. The real life photos can be large - // (18Mpix = 72Mb pixel array) so we want only the minimum amount of caching. - this.contentCache_ = new ImageView.Cache(2); - - // We reuse previously generated screen-scale images so that going back to - // a recently loaded image looks instant even if the image is not in - // the content cache any more. Screen-scale images are small (~1Mpix) - // so we can afford to cache more of them. - this.screenCache_ = new ImageView.Cache(5); - this.contentCallbacks_ = []; - - /** - * The element displaying the current content. - * - * @type {HTMLCanvasElement|HTMLVideoElement} - * @private - */ - this.screenImage_ = null; - - this.localImageTransformFetcher_ = function(entry, callback) { - metadataCache.getOne(entry, 'fetchedMedia', function(fetchedMedia) { - callback(fetchedMedia.imageTransform); - }); - }; -} - -/** - * Duration of transition between modes in ms. - */ -ImageView.MODE_TRANSITION_DURATION = 350; - -/** - * If the user flips though images faster than this interval we do not apply - * the slide-in/slide-out transition. - */ -ImageView.FAST_SCROLL_INTERVAL = 300; - -/** - * Image load type: full resolution image loaded from cache. - */ -ImageView.LOAD_TYPE_CACHED_FULL = 0; - -/** - * Image load type: screen resolution preview loaded from cache. - */ -ImageView.LOAD_TYPE_CACHED_SCREEN = 1; - -/** - * Image load type: image read from file. - */ -ImageView.LOAD_TYPE_IMAGE_FILE = 2; - -/** - * Image load type: video loaded. - */ -ImageView.LOAD_TYPE_VIDEO_FILE = 3; - -/** - * Image load type: error occurred. - */ -ImageView.LOAD_TYPE_ERROR = 4; - -/** - * Image load type: the file contents is not available offline. - */ -ImageView.LOAD_TYPE_OFFLINE = 5; - -/** - * The total number of load types. - */ -ImageView.LOAD_TYPE_TOTAL = 6; - -ImageView.prototype = {__proto__: ImageBuffer.Overlay.prototype}; - -/** - * Draws below overlays with the default zIndex. - * @return {number} Z-index. - */ -ImageView.prototype.getZIndex = function() { return -1 }; - -/** - * Draws the image on screen. - */ -ImageView.prototype.draw = function() { - if (!this.contentCanvas_) // Do nothing if the image content is not set. - return; - - var forceRepaint = false; - - if (this.displayedViewportGeneration_ !== - this.viewport_.getCacheGeneration()) { - this.displayedViewportGeneration_ = this.viewport_.getCacheGeneration(); - - this.setupDeviceBuffer(this.screenImage_); - - forceRepaint = true; - } - - if (forceRepaint || - this.displayedContentGeneration_ !== this.contentGeneration_) { - this.displayedContentGeneration_ = this.contentGeneration_; - - ImageUtil.trace.resetTimer('paint'); - this.paintDeviceRect(this.viewport_.getDeviceClipped(), - this.contentCanvas_, this.viewport_.getImageClipped()); - ImageUtil.trace.reportTimer('paint'); - } -}; - -/** - * @param {number} x X pointer position. - * @param {number} y Y pointer position. - * @param {boolean} mouseDown True if mouse is down. - * @return {string} CSS cursor style. - */ -ImageView.prototype.getCursorStyle = function(x, y, mouseDown) { - // Indicate that the image is draggable. - if (this.viewport_.isClipped() && - this.viewport_.getScreenClipped().inside(x, y)) - return 'move'; - - return null; -}; - -/** - * @param {number} x X pointer position. - * @param {number} y Y pointer position. - * @return {function} The closure to call on drag. - */ -ImageView.prototype.getDragHandler = function(x, y) { - var cursor = this.getCursorStyle(x, y); - if (cursor === 'move') { - // Return the handler that drags the entire image. - return this.viewport_.createOffsetSetter(x, y); - } - - return null; -}; - -/** - * @return {number} The cache generation. - */ -ImageView.prototype.getCacheGeneration = function() { - return this.contentGeneration_; -}; - -/** - * Invalidates the caches to force redrawing the screen canvas. - */ -ImageView.prototype.invalidateCaches = function() { - this.contentGeneration_++; -}; - -/** - * @return {HTMLCanvasElement} The content canvas element. - */ -ImageView.prototype.getCanvas = function() { return this.contentCanvas_ }; - -/** - * @return {boolean} True if the a valid image is currently loaded. - */ -ImageView.prototype.hasValidImage = function() { - return !this.preview_ && this.contentCanvas_ && this.contentCanvas_.width; -}; - -/** - * @return {HTMLVideoElement} The video element. - */ -ImageView.prototype.getVideo = function() { return this.videoElement_ }; - -/** - * @return {HTMLCanvasElement} The cached thumbnail image. - */ -ImageView.prototype.getThumbnail = function() { return this.thumbnailCanvas_ }; - -/** - * @return {number} The content revision number. - */ -ImageView.prototype.getContentRevision = function() { - return this.contentRevision_; -}; - -/** - * Copies an image fragment from a full resolution canvas to a device resolution - * canvas. - * - * @param {Rect} deviceRect Rectangle in the device coordinates. - * @param {HTMLCanvasElement} canvas Full resolution canvas. - * @param {Rect} imageRect Rectangle in the full resolution canvas. - */ -ImageView.prototype.paintDeviceRect = function(deviceRect, canvas, imageRect) { - // Map screen canvas (0,0) to (deviceBounds.left, deviceBounds.top) - var deviceBounds = this.viewport_.getDeviceClipped(); - deviceRect = deviceRect.shift(-deviceBounds.left, -deviceBounds.top); - - // The source canvas may have different physical size than the image size - // set at the viewport. Adjust imageRect accordingly. - var bounds = this.viewport_.getImageBounds(); - var scaleX = canvas.width / bounds.width; - var scaleY = canvas.height / bounds.height; - imageRect = new Rect(imageRect.left * scaleX, imageRect.top * scaleY, - imageRect.width * scaleX, imageRect.height * scaleY); - Rect.drawImage( - this.screenImage_.getContext('2d'), canvas, deviceRect, imageRect); -}; - -/** - * Creates an overlay canvas with properties similar to the screen canvas. - * Useful for showing quick feedback when editing. - * - * @return {HTMLCanvasElement} Overlay canvas. - */ -ImageView.prototype.createOverlayCanvas = function() { - var canvas = this.document_.createElement('canvas'); - canvas.className = 'image'; - this.container_.appendChild(canvas); - return canvas; -}; - -/** - * Sets up the canvas as a buffer in the device resolution. - * - * @param {HTMLCanvasElement} canvas The buffer canvas. - */ -ImageView.prototype.setupDeviceBuffer = function(canvas) { - var deviceRect = this.viewport_.getDeviceClipped(); - - // Set the canvas position and size in device pixels. - if (canvas.width !== deviceRect.width) - canvas.width = deviceRect.width; - - if (canvas.height !== deviceRect.height) - canvas.height = deviceRect.height; - - canvas.style.left = deviceRect.left + 'px'; - canvas.style.top = deviceRect.top + 'px'; - - // Scale the canvas down to screen pixels. - this.setTransform(canvas); -}; - -/** - * @return {ImageData} A new ImageData object with a copy of the content. - */ -ImageView.prototype.copyScreenImageData = function() { - return this.screenImage_.getContext('2d').getImageData( - 0, 0, this.screenImage_.width, this.screenImage_.height); -}; - -/** - * @return {boolean} True if the image is currently being loaded. - */ -ImageView.prototype.isLoading = function() { - return this.imageLoader_.isBusy(); -}; - -/** - * Cancels the current image loading operation. The callbacks will be ignored. - */ -ImageView.prototype.cancelLoad = function() { - this.imageLoader_.cancel(); -}; - -/** - * Loads and display a new image. - * - * Loads the thumbnail first, then replaces it with the main image. - * Takes into account the image orientation encoded in the metadata. - * - * @param {FileEntry} entry Image entry. - * @param {Object} metadata Metadata. - * @param {Object} effect Transition effect object. - * @param {function(number} displayCallback Called when the image is displayed - * (possibly as a prevew). - * @param {function(number} loadCallback Called when the image is fully loaded. - * The parameter is the load type. - */ -ImageView.prototype.load = function(entry, metadata, effect, - displayCallback, loadCallback) { - if (effect) { - // Skip effects when reloading repeatedly very quickly. - var time = Date.now(); - if (this.lastLoadTime_ && - (time - this.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) { - effect = null; - } - this.lastLoadTime_ = time; - } - - metadata = metadata || {}; - - ImageUtil.metrics.startInterval(ImageUtil.getMetricName('DisplayTime')); - - var self = this; - - this.contentEntry_ = entry; - this.contentRevision_ = -1; - - var loadingVideo = FileType.getMediaType(entry) === 'video'; - if (loadingVideo) { - var video = this.document_.createElement('video'); - var videoPreview = !!(metadata.thumbnail && metadata.thumbnail.url); - if (videoPreview) { - var thumbnailLoader = new ThumbnailLoader( - entry, - ThumbnailLoader.LoaderType.CANVAS, - metadata); - thumbnailLoader.loadDetachedImage(function(success) { - if (success) { - var canvas = thumbnailLoader.getImage(); - video.setAttribute('poster', canvas.toDataURL('image/jpeg')); - this.replace(video, effect); // Show the poster immediately. - if (displayCallback) displayCallback(); - } - }.bind(this)); - } - - var onVideoLoad = function(error) { - video.removeEventListener('loadedmetadata', onVideoLoadSuccess); - video.removeEventListener('error', onVideoLoadError); - displayMainImage(ImageView.LOAD_TYPE_VIDEO_FILE, videoPreview, video, - error); - }; - var onVideoLoadError = onVideoLoad.bind(this, 'GALLERY_VIDEO_ERROR'); - var onVideoLoadSuccess = onVideoLoad.bind(this, null); - - video.addEventListener('loadedmetadata', onVideoLoadSuccess); - video.addEventListener('error', onVideoLoadError); - - video.src = entry.toURL(); - video.load(); - return; - } - - // Cache has to be evicted in advance, so the returned cached image is not - // evicted later by the prefetched image. - this.contentCache_.evictLRU(); - - var cached = this.contentCache_.getItem(this.contentEntry_); - if (cached) { - displayMainImage(ImageView.LOAD_TYPE_CACHED_FULL, - false /* no preview */, cached); - } else { - var cachedScreen = this.screenCache_.getItem(this.contentEntry_); - var imageWidth = metadata.media && metadata.media.width || - metadata.drive && metadata.drive.imageWidth; - var imageHeight = metadata.media && metadata.media.height || - metadata.drive && metadata.drive.imageHeight; - if (cachedScreen) { - // We have a cached screen-scale canvas, use it instead of a thumbnail. - displayThumbnail(ImageView.LOAD_TYPE_CACHED_SCREEN, cachedScreen); - // As far as the user can tell the image is loaded. We still need to load - // the full res image to make editing possible, but we can report now. - ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); - } else if ((effect && effect.constructor.name === 'Slide') && - (metadata.thumbnail && metadata.thumbnail.url)) { - // Only show thumbnails if there is no effect or the effect is Slide. - // Also no thumbnail if the image is too large to be loaded. - var thumbnailLoader = new ThumbnailLoader( - entry, - ThumbnailLoader.LoaderType.CANVAS, - metadata); - thumbnailLoader.loadDetachedImage(function(success) { - displayThumbnail(ImageView.LOAD_TYPE_IMAGE_FILE, - success ? thumbnailLoader.getImage() : null); - }); - } else { - loadMainImage(ImageView.LOAD_TYPE_IMAGE_FILE, entry, - false /* no preview*/, 0 /* delay */); - } - } - - function displayThumbnail(loadType, canvas) { - if (canvas) { - var width = null; - var height = null; - if (metadata.media) { - width = metadata.media.width; - height = metadata.media.height; - } - // If metadata.drive.present is true, the image data is loaded directly - // from local cache, whose size may be out of sync with the drive - // metadata. - if (metadata.drive && !metadata.drive.present) { - width = metadata.drive.imageWidth; - height = metadata.drive.imageHeight; - } - self.replace( - canvas, - effect, - width, - height, - true /* preview */); - if (displayCallback) displayCallback(); - } - loadMainImage(loadType, entry, !!canvas, - (effect && canvas) ? effect.getSafeInterval() : 0); - } - - function loadMainImage(loadType, contentEntry, previewShown, delay) { - if (self.prefetchLoader_.isLoading(contentEntry)) { - // The image we need is already being prefetched. Initiating another load - // would be a waste. Hijack the load instead by overriding the callback. - self.prefetchLoader_.setCallback( - displayMainImage.bind(null, loadType, previewShown)); - - // Swap the loaders so that the self.isLoading works correctly. - var temp = self.prefetchLoader_; - self.prefetchLoader_ = self.imageLoader_; - self.imageLoader_ = temp; - return; - } - self.prefetchLoader_.cancel(); // The prefetch was doing something useless. - - self.imageLoader_.load( - contentEntry, - self.localImageTransformFetcher_, - displayMainImage.bind(null, loadType, previewShown), - delay); - } - - function displayMainImage(loadType, previewShown, content, opt_error) { - if (opt_error) - loadType = ImageView.LOAD_TYPE_ERROR; - - // If we already displayed the preview we should not replace the content if: - // 1. The full content failed to load. - // or - // 2. We are loading a video (because the full video is displayed in the - // same HTML element as the preview). - var animationDuration = 0; - if (!(previewShown && - (loadType === ImageView.LOAD_TYPE_ERROR || - loadType === ImageView.LOAD_TYPE_VIDEO_FILE))) { - var replaceEffect = previewShown ? null : effect; - animationDuration = replaceEffect ? replaceEffect.getSafeInterval() : 0; - self.replace(content, replaceEffect); - if (!previewShown && displayCallback) displayCallback(); - } - - if (loadType !== ImageView.LOAD_TYPE_ERROR && - loadType !== ImageView.LOAD_TYPE_CACHED_SCREEN) { - ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); - } - ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('LoadMode'), - loadType, ImageView.LOAD_TYPE_TOTAL); - - if (loadType === ImageView.LOAD_TYPE_ERROR && - !navigator.onLine && metadata.streaming) { - // |streaming| is set only when the file is not locally cached. - loadType = ImageView.LOAD_TYPE_OFFLINE; - } - if (loadCallback) loadCallback(loadType, animationDuration, opt_error); - } -}; - -/** - * Prefetches an image. - * @param {FileEntry} entry The image entry. - * @param {number} delay Image load delay in ms. - */ -ImageView.prototype.prefetch = function(entry, delay) { - var self = this; - function prefetchDone(canvas) { - if (canvas.width) - self.contentCache_.putItem(entry, canvas); - } - - var cached = this.contentCache_.getItem(entry); - if (cached) { - prefetchDone(cached); - } else if (FileType.getMediaType(entry) === 'image') { - // Evict the LRU item before we allocate the new canvas to avoid unneeded - // strain on memory. - this.contentCache_.evictLRU(); - - this.prefetchLoader_.load( - entry, - this.localImageTransformFetcher_, - prefetchDone, - delay); - } -}; - -/** - * Renames the current image. - * @param {FileEntry} newEntry The new image Entry. - */ -ImageView.prototype.changeEntry = function(newEntry) { - this.contentCache_.renameItem(this.contentEntry_, newEntry); - this.screenCache_.renameItem(this.contentEntry_, newEntry); - this.contentEntry_ = newEntry; -}; - -/** - * Unloads content. - * @param {Rect} zoomToRect Target rectangle for zoom-out-effect. - */ -ImageView.prototype.unload = function(zoomToRect) { - if (this.unloadTimer_) { - clearTimeout(this.unloadTimer_); - this.unloadTimer_ = null; - } - if (zoomToRect && this.screenImage_) { - var effect = this.createZoomEffect(zoomToRect); - this.setTransform(this.screenImage_, effect); - this.screenImage_.setAttribute('fade', true); - this.unloadTimer_ = setTimeout(function() { - this.unloadTimer_ = null; - this.unload(null /* force unload */); - }.bind(this), - effect.getSafeInterval()); - return; - } - this.container_.textContent = ''; - this.contentCanvas_ = null; - this.screenImage_ = null; - this.videoElement_ = null; -}; - -/** - * @param {HTMLCanvasElement|HTMLVideoElement} content The image element. - * @param {number=} opt_width Image width. - * @param {number=} opt_height Image height. - * @param {boolean=} opt_preview True if the image is a preview (not full res). - * @private - */ -ImageView.prototype.replaceContent_ = function( - content, opt_width, opt_height, opt_preview) { - - if (this.contentCanvas_ && this.contentCanvas_.parentNode === this.container_) - this.container_.removeChild(this.contentCanvas_); - - if (content.constructor.name === 'HTMLVideoElement') { - this.contentCanvas_ = null; - this.videoElement_ = content; - this.screenImage_ = content; - this.screenImage_.className = 'image'; - this.container_.appendChild(this.screenImage_); - this.videoElement_.play(); - return; - } - - this.screenImage_ = this.document_.createElement('canvas'); - this.screenImage_.className = 'image'; - - this.videoElement_ = null; - this.contentCanvas_ = content; - this.invalidateCaches(); - this.viewport_.setImageSize( - opt_width || this.contentCanvas_.width, - opt_height || this.contentCanvas_.height); - this.viewport_.fitImage(); - this.viewport_.update(); - this.draw(); - - this.container_.appendChild(this.screenImage_); - - this.preview_ = opt_preview; - // If this is not a thumbnail, cache the content and the screen-scale image. - if (this.hasValidImage()) { - // Insert the full resolution canvas into DOM so that it can be printed. - this.container_.appendChild(this.contentCanvas_); - this.contentCanvas_.classList.add('fullres'); - - this.contentCache_.putItem(this.contentEntry_, this.contentCanvas_, true); - this.screenCache_.putItem(this.contentEntry_, this.screenImage_); - - // TODO(kaznacheev): It is better to pass screenImage_ as it is usually - // much smaller than contentCanvas_ and still contains the entire image. - // Once we implement zoom/pan we should pass contentCanvas_ instead. - this.updateThumbnail_(this.screenImage_); - - this.contentRevision_++; - for (var i = 0; i !== this.contentCallbacks_.length; i++) { - try { - this.contentCallbacks_[i](); - } catch (e) { - console.error(e); - } - } - } -}; - -/** - * Adds a listener for content changes. - * @param {function} callback Callback. - */ -ImageView.prototype.addContentCallback = function(callback) { - this.contentCallbacks_.push(callback); -}; - -/** - * Updates the cached thumbnail image. - * - * @param {HTMLCanvasElement} canvas The source canvas. - * @private - */ -ImageView.prototype.updateThumbnail_ = function(canvas) { - ImageUtil.trace.resetTimer('thumb'); - var pixelCount = 10000; - var downScale = - Math.max(1, Math.sqrt(canvas.width * canvas.height / pixelCount)); - - this.thumbnailCanvas_ = canvas.ownerDocument.createElement('canvas'); - this.thumbnailCanvas_.width = Math.round(canvas.width / downScale); - this.thumbnailCanvas_.height = Math.round(canvas.height / downScale); - Rect.drawImage(this.thumbnailCanvas_.getContext('2d'), canvas); - ImageUtil.trace.reportTimer('thumb'); -}; - -/** - * Replaces the displayed image, possibly with slide-in animation. - * - * @param {HTMLCanvasElement|HTMLVideoElement} content The image element. - * @param {Object=} opt_effect Transition effect object. - * @param {number=} opt_width Image width. - * @param {number=} opt_height Image height. - * @param {boolean=} opt_preview True if the image is a preview (not full res). - */ -ImageView.prototype.replace = function( - content, opt_effect, opt_width, opt_height, opt_preview) { - var oldScreenImage = this.screenImage_; - - this.replaceContent_(content, opt_width, opt_height, opt_preview); - if (!opt_effect) { - if (oldScreenImage) - oldScreenImage.parentNode.removeChild(oldScreenImage); - return; - } - - var newScreenImage = this.screenImage_; - - if (oldScreenImage) - ImageUtil.setAttribute(newScreenImage, 'fade', true); - this.setTransform(newScreenImage, opt_effect, 0 /* instant */); - - setTimeout(function() { - this.setTransform(newScreenImage, null, - opt_effect && opt_effect.getDuration()); - if (oldScreenImage) { - ImageUtil.setAttribute(newScreenImage, 'fade', false); - ImageUtil.setAttribute(oldScreenImage, 'fade', true); - console.assert(opt_effect.getReverse, 'Cannot revert an effect.'); - var reverse = opt_effect.getReverse(); - this.setTransform(oldScreenImage, reverse); - setTimeout(function() { - if (oldScreenImage.parentNode) - oldScreenImage.parentNode.removeChild(oldScreenImage); - }, reverse.getSafeInterval()); - } - }.bind(this), 0); -}; - -/** - * @param {HTMLCanvasElement|HTMLVideoElement} element The element to transform. - * @param {ImageView.Effect=} opt_effect The effect to apply. - * @param {number=} opt_duration Transition duration. - */ -ImageView.prototype.setTransform = function(element, opt_effect, opt_duration) { - if (!opt_effect) - opt_effect = new ImageView.Effect.None(); - if (typeof opt_duration !== 'number') - opt_duration = opt_effect.getDuration(); - element.style.webkitTransitionDuration = opt_duration + 'ms'; - element.style.webkitTransitionTimingFunction = opt_effect.getTiming(); - element.style.webkitTransform = opt_effect.transform(element, this.viewport_); -}; - -/** - * @param {Rect} screenRect Target rectangle in screen coordinates. - * @return {ImageView.Effect.Zoom} Zoom effect object. - */ -ImageView.prototype.createZoomEffect = function(screenRect) { - return new ImageView.Effect.Zoom( - this.viewport_.screenToDeviceRect(screenRect), - null /* use viewport */, - ImageView.MODE_TRANSITION_DURATION); -}; - -/** - * Visualizes crop or rotate operation. Hide the old image instantly, animate - * the new image to visualize the operation. - * - * @param {HTMLCanvasElement} canvas New content canvas. - * @param {Rect} imageCropRect The crop rectangle in image coordinates. - * Null for rotation operations. - * @param {number} rotate90 Rotation angle in 90 degree increments. - * @return {number} Animation duration. - */ -ImageView.prototype.replaceAndAnimate = function( - canvas, imageCropRect, rotate90) { - var oldScale = this.viewport_.getScale(); - var deviceCropRect = imageCropRect && this.viewport_.screenToDeviceRect( - this.viewport_.imageToScreenRect(imageCropRect)); - - var oldScreenImage = this.screenImage_; - this.replaceContent_(canvas); - var newScreenImage = this.screenImage_; - - // Display the new canvas, initially transformed. - var deviceFullRect = this.viewport_.getDeviceClipped(); - - var effect = rotate90 ? - new ImageView.Effect.Rotate( - oldScale / this.viewport_.getScale(), -rotate90) : - new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect); - - this.setTransform(newScreenImage, effect, 0 /* instant */); - - oldScreenImage.parentNode.appendChild(newScreenImage); - oldScreenImage.parentNode.removeChild(oldScreenImage); - - // Let the layout fire, then animate back to non-transformed state. - setTimeout( - this.setTransform.bind( - this, newScreenImage, null, effect.getDuration()), - 0); - - return effect.getSafeInterval(); -}; - -/** - * Visualizes "undo crop". Shrink the current image to the given crop rectangle - * while fading in the new image. - * - * @param {HTMLCanvasElement} canvas New content canvas. - * @param {Rect} imageCropRect The crop rectangle in image coordinates. - * @return {number} Animation duration. - */ -ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { - var deviceFullRect = this.viewport_.getDeviceClipped(); - var oldScale = this.viewport_.getScale(); - - var oldScreenImage = this.screenImage_; - this.replaceContent_(canvas); - var newScreenImage = this.screenImage_; - - var deviceCropRect = this.viewport_.screenToDeviceRect( - this.viewport_.imageToScreenRect(imageCropRect)); - - var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade'); - setFade(true); - oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage); - - var effect = new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect); - // Animate to the transformed state. - this.setTransform(oldScreenImage, effect); - - setTimeout(setFade.bind(null, false), 0); - - setTimeout(function() { - if (oldScreenImage.parentNode) - oldScreenImage.parentNode.removeChild(oldScreenImage); - }, effect.getSafeInterval()); - - return effect.getSafeInterval(); -}; - - -/** - * Generic cache with a limited capacity and LRU eviction. - * @param {number} capacity Maximum number of cached item. - * @constructor - */ -ImageView.Cache = function(capacity) { - this.capacity_ = capacity; - this.map_ = {}; - this.order_ = []; -}; - -/** - * Fetches the item from the cache. - * @param {FileEntry} entry The entry. - * @return {Object} The cached item. - */ -ImageView.Cache.prototype.getItem = function(entry) { - return this.map_[entry.toURL()]; -}; - -/** - * Puts the item into the cache. - * - * @param {FileEntry} entry The entry. - * @param {Object} item The item object. - * @param {boolean=} opt_keepLRU True if the LRU order should not be modified. - */ -ImageView.Cache.prototype.putItem = function(entry, item, opt_keepLRU) { - var pos = this.order_.indexOf(entry.toURL()); - - if ((pos >= 0) !== (entry.toURL() in this.map_)) - throw new Error('Inconsistent cache state'); - - if (entry.toURL() in this.map_) { - if (!opt_keepLRU) { - // Move to the end (most recently used). - this.order_.splice(pos, 1); - this.order_.push(entry.toURL()); - } - } else { - this.evictLRU(); - this.order_.push(entry.toURL()); - } - - if ((pos >= 0) && (item !== this.map_[entry.toURL()])) - this.deleteItem_(this.map_[entry.toURL()]); - this.map_[entry.toURL()] = item; - - if (this.order_.length > this.capacity_) - throw new Error('Exceeded cache capacity'); -}; - -/** - * Evicts the least recently used items. - */ -ImageView.Cache.prototype.evictLRU = function() { - if (this.order_.length === this.capacity_) { - var url = this.order_.shift(); - this.deleteItem_(this.map_[url]); - delete this.map_[url]; - } -}; - -/** - * Changes the Entry. - * @param {FileEntry} oldEntry The old Entry. - * @param {FileEntry} newEntry The new Entry. - */ -ImageView.Cache.prototype.renameItem = function(oldEntry, newEntry) { - if (util.isSameEntry(oldEntry, newEntry)) - return; // No need to rename. - - var pos = this.order_.indexOf(oldEntry.toURL()); - if (pos < 0) - return; // Not cached. - - this.order_[pos] = newEntry.toURL(); - this.map_[newEntry.toURL()] = this.map_[oldEntry.toURL()]; - delete this.map_[oldEntry.toURL()]; -}; - -/** - * Disposes an object. - * - * @param {Object} item The item object. - * @private - */ -ImageView.Cache.prototype.deleteItem_ = function(item) { - // Trick to reduce memory usage without waiting for gc. - if (item instanceof HTMLCanvasElement) { - // If the canvas is being used somewhere else (eg. displayed on the screen), - // it will be cleared. - item.width = 0; - item.height = 0; - } -}; - -/* Transition effects */ - -/** - * Base class for effects. - * - * @param {number} duration Duration in ms. - * @param {string=} opt_timing CSS transition timing function name. - * @constructor - */ -ImageView.Effect = function(duration, opt_timing) { - this.duration_ = duration; - this.timing_ = opt_timing || 'linear'; -}; - -/** - * - */ -ImageView.Effect.DEFAULT_DURATION = 180; - -/** - * - */ -ImageView.Effect.MARGIN = 100; - -/** - * @return {number} Effect duration in ms. - */ -ImageView.Effect.prototype.getDuration = function() { return this.duration_ }; - -/** - * @return {number} Delay in ms since the beginning of the animation after which - * it is safe to perform CPU-heavy operations without disrupting the animation. - */ -ImageView.Effect.prototype.getSafeInterval = function() { - return this.getDuration() + ImageView.Effect.MARGIN; -}; - -/** - * @return {string} CSS transition timing function name. - */ -ImageView.Effect.prototype.getTiming = function() { return this.timing_ }; - -/** - * @param {HTMLCanvasElement|HTMLVideoElement} element Element. - * @return {number} Preferred pixel ration to use with this element. - * @private - */ -ImageView.Effect.getPixelRatio_ = function(element) { - if (element.constructor.name === 'HTMLCanvasElement') - return Viewport.getDevicePixelRatio(); - else - return 1; -}; - -/** - * Default effect. It is not a no-op as it needs to adjust a canvas scale - * for devicePixelRatio. - * - * @constructor - */ -ImageView.Effect.None = function() { - ImageView.Effect.call(this, 0); -}; - -/** - * Inherits from ImageView.Effect. - */ -ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @param {HTMLCanvasElement|HTMLVideoElement} element Element. - * @return {string} Transform string. - */ -ImageView.Effect.None.prototype.transform = function(element) { - var ratio = ImageView.Effect.getPixelRatio_(element); - return 'scale(' + (1 / ratio) + ')'; -}; - -/** - * Slide effect. - * - * @param {number} direction -1 for left, 1 for right. - * @param {boolean=} opt_slow True if slow (as in slideshow). - * @constructor - */ -ImageView.Effect.Slide = function Slide(direction, opt_slow) { - ImageView.Effect.call(this, - opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-in-out'); - this.direction_ = direction; - this.slow_ = opt_slow; - this.shift_ = opt_slow ? 100 : 40; - if (this.direction_ < 0) this.shift_ = -this.shift_; -}; - -/** - * Inherits from ImageView.Effect. - */ -ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @return {ImageView.Effect.Slide} Reverse Slide effect. - */ -ImageView.Effect.Slide.prototype.getReverse = function() { - return new ImageView.Effect.Slide(-this.direction_, this.slow_); -}; - -/** - * @param {HTMLCanvasElement|HTMLVideoElement} element Element. - * @return {string} Transform string. - */ -ImageView.Effect.Slide.prototype.transform = function(element) { - var ratio = ImageView.Effect.getPixelRatio_(element); - return 'scale(' + (1 / ratio) + ') translate(' + this.shift_ + 'px, 0px)'; -}; - -/** - * Zoom effect. - * - * Animates the original rectangle to the target rectangle. Both parameters - * should be given in device coordinates (accounting for devicePixelRatio). - * - * @param {Rect} deviceTargetRect Target rectangle. - * @param {Rect=} opt_deviceOriginalRect Original rectangle. If omitted, - * the full viewport will be used at the time of |transform| call. - * @param {number=} opt_duration Duration in ms. - * @constructor - */ -ImageView.Effect.Zoom = function( - deviceTargetRect, opt_deviceOriginalRect, opt_duration) { - ImageView.Effect.call(this, - opt_duration || ImageView.Effect.DEFAULT_DURATION); - this.target_ = deviceTargetRect; - this.original_ = opt_deviceOriginalRect; -}; - -/** - * Inherits from ImageView.Effect. - */ -ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @param {HTMLCanvasElement|HTMLVideoElement} element Element. - * @param {Viewport} viewport Viewport. - * @return {string} Transform string. - */ -ImageView.Effect.Zoom.prototype.transform = function(element, viewport) { - if (!this.original_) - this.original_ = viewport.getDeviceClipped(); - - var ratio = ImageView.Effect.getPixelRatio_(element); - - var dx = (this.target_.left + this.target_.width / 2) - - (this.original_.left + this.original_.width / 2); - var dy = (this.target_.top + this.target_.height / 2) - - (this.original_.top + this.original_.height / 2); - - var scaleX = this.target_.width / this.original_.width; - var scaleY = this.target_.height / this.original_.height; - - return 'translate(' + (dx / ratio) + 'px,' + (dy / ratio) + 'px) ' + - 'scaleX(' + (scaleX / ratio) + ') scaleY(' + (scaleY / ratio) + ')'; -}; - -/** - * Rotate effect. - * - * @param {number} scale Scale. - * @param {number} rotate90 Rotation in 90 degrees increments. - * @constructor - */ -ImageView.Effect.Rotate = function(scale, rotate90) { - ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION); - this.scale_ = scale; - this.rotate90_ = rotate90; -}; - -/** - * Inherits from ImageView.Effect. - */ -ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype }; - -/** - * @param {HTMLCanvasElement|HTMLVideoElement} element Element. - * @return {string} Transform string. - */ -ImageView.Effect.Rotate.prototype.transform = function(element) { - var ratio = ImageView.Effect.getPixelRatio_(element); - return 'rotate(' + (this.rotate90_ * 90) + 'deg) ' + - 'scale(' + (this.scale_ / ratio) + ')'; -}; diff --git a/ui/file_manager/file_manager/foreground/js/image_editor/viewport.js b/ui/file_manager/file_manager/foreground/js/image_editor/viewport.js deleted file mode 100644 index 9e723df..0000000 --- a/ui/file_manager/file_manager/foreground/js/image_editor/viewport.js +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright 2014 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'; - -/** - * Viewport class controls the way the image is displayed (scale, offset etc). - * @constructor - */ -function Viewport() { - this.imageBounds_ = new Rect(); - this.screenBounds_ = new Rect(); - - this.scale_ = 1; - this.offsetX_ = 0; - this.offsetY_ = 0; - - this.generation_ = 0; - - this.scaleControl_ = null; - this.repaintCallbacks_ = []; - this.update(); -} - -/* - * Viewport modification. - */ - -/** - * @param {Object} scaleControl The UI object responsible for scaling. - */ -Viewport.prototype.setScaleControl = function(scaleControl) { - this.scaleControl_ = scaleControl; -}; - -/** - * @param {number} width Image width. - * @param {number} height Image height. - */ -Viewport.prototype.setImageSize = function(width, height) { - this.imageBounds_ = new Rect(width, height); - if (this.scaleControl_) this.scaleControl_.displayImageSize(width, height); - this.invalidateCaches(); -}; - -/** - * @param {number} width Screen width. - * @param {number} height Screen height. - */ -Viewport.prototype.setScreenSize = function(width, height) { - this.screenBounds_ = new Rect(width, height); - if (this.scaleControl_) - this.scaleControl_.setMinScale(this.getFittingScale()); - this.invalidateCaches(); -}; - -/** - * Set the size by an HTML element. - * - * @param {HTMLElement} frame The element acting as the "screen". - */ -Viewport.prototype.sizeByFrame = function(frame) { - this.setScreenSize(frame.clientWidth, frame.clientHeight); -}; - -/** - * Set the size and scale to fit an HTML element. - * - * @param {HTMLElement} frame The element acting as the "screen". - */ -Viewport.prototype.sizeByFrameAndFit = function(frame) { - var wasFitting = this.getScale() == this.getFittingScale(); - this.sizeByFrame(frame); - var minScale = this.getFittingScale(); - if (wasFitting || (this.getScale() < minScale)) { - this.setScale(minScale, true); - } -}; - -/** - * @return {number} Scale. - */ -Viewport.prototype.getScale = function() { return this.scale_ }; - -/** - * @param {number} scale The new scale. - * @param {boolean} notify True if the change should be reflected in the UI. - */ -Viewport.prototype.setScale = function(scale, notify) { - if (this.scale_ == scale) return; - this.scale_ = scale; - if (notify && this.scaleControl_) this.scaleControl_.displayScale(scale); - this.invalidateCaches(); -}; - -/** - * @return {number} Best scale to fit the current image into the current screen. - */ -Viewport.prototype.getFittingScale = function() { - var scaleX = this.screenBounds_.width / this.imageBounds_.width; - var scaleY = this.screenBounds_.height / this.imageBounds_.height; - // Scales > (1 / this.getDevicePixelRatio()) do not look good. Also they are - // not really useful as we do not have any pixel-level operations. - return Math.min(1 / Viewport.getDevicePixelRatio(), scaleX, scaleY); -}; - -/** - * Set the scale to fit the image into the screen. - */ -Viewport.prototype.fitImage = function() { - var scale = this.getFittingScale(); - if (this.scaleControl_) this.scaleControl_.setMinScale(scale); - this.setScale(scale, true); -}; - -/** - * @return {number} X-offset of the viewport. - */ -Viewport.prototype.getOffsetX = function() { return this.offsetX_ }; - -/** - * @return {number} Y-offset of the viewport. - */ -Viewport.prototype.getOffsetY = function() { return this.offsetY_ }; - -/** - * Set the image offset in the viewport. - * @param {number} x X-offset. - * @param {number} y Y-offset. - * @param {boolean} ignoreClipping True if no clipping should be applied. - */ -Viewport.prototype.setOffset = function(x, y, ignoreClipping) { - if (!ignoreClipping) { - x = this.clampOffsetX_(x); - y = this.clampOffsetY_(y); - } - if (this.offsetX_ == x && this.offsetY_ == y) return; - this.offsetX_ = x; - this.offsetY_ = y; - this.invalidateCaches(); -}; - -/** - * Return a closure that can be called to pan the image. - * Useful for implementing non-trivial variants of panning (overview etc). - * @param {number} originalX The x coordinate on the screen canvas that - * corresponds to zero change to offsetX. - * @param {number} originalY The y coordinate on the screen canvas that - * corresponds to zero change to offsetY. - * @param {function():number} scaleFunc returns the image to screen scale. - * @param {function(number,number):boolean} hitFunc returns true if (x,y) is - * in the valid region. - * @return {function} The closure to pan the image. - */ -Viewport.prototype.createOffsetSetter = function( - originalX, originalY, scaleFunc, hitFunc) { - var originalOffsetX = this.offsetX_; - var originalOffsetY = this.offsetY_; - if (!hitFunc) hitFunc = function() { return true }; - if (!scaleFunc) scaleFunc = this.getScale.bind(this); - - var self = this; - return function(x, y) { - if (hitFunc(x, y)) { - var scale = scaleFunc(); - self.setOffset( - originalOffsetX + (x - originalX) / scale, - originalOffsetY + (y - originalY) / scale); - self.repaint(); - } - }; -}; - -/* - * Access to the current viewport state. - */ - -/** - * @return {Rect} The image bounds in image coordinates. - */ -Viewport.prototype.getImageBounds = function() { return this.imageBounds_ }; - -/** -* @return {Rect} The screen bounds in screen coordinates. -*/ -Viewport.prototype.getScreenBounds = function() { return this.screenBounds_ }; - -/** - * @return {Rect} The visible part of the image, in image coordinates. - */ -Viewport.prototype.getImageClipped = function() { return this.imageClipped_ }; - -/** - * @return {Rect} The visible part of the image, in screen coordinates. - */ -Viewport.prototype.getScreenClipped = function() { return this.screenClipped_ }; - -/** - * A counter that is incremented with each viewport state change. - * Clients that cache anything that depends on the viewport state should keep - * track of this counter. - * @return {number} counter. - */ -Viewport.prototype.getCacheGeneration = function() { return this.generation_ }; - -/** - * Called on event view port state change (even if repaint has not been called). - */ -Viewport.prototype.invalidateCaches = function() { this.generation_++ }; - -/** - * @return {Rect} The image bounds in screen coordinates. - */ -Viewport.prototype.getImageBoundsOnScreen = function() { - return this.imageOnScreen_; -}; - -/* - * Conversion between the screen and image coordinate spaces. - */ - -/** - * @param {number} size Size in screen coordinates. - * @return {number} Size in image coordinates. - */ -Viewport.prototype.screenToImageSize = function(size) { - return size / this.getScale(); -}; - -/** - * @param {number} x X in screen coordinates. - * @return {number} X in image coordinates. - */ -Viewport.prototype.screenToImageX = function(x) { - return Math.round((x - this.imageOnScreen_.left) / this.getScale()); -}; - -/** - * @param {number} y Y in screen coordinates. - * @return {number} Y in image coordinates. - */ -Viewport.prototype.screenToImageY = function(y) { - return Math.round((y - this.imageOnScreen_.top) / this.getScale()); -}; - -/** - * @param {Rect} rect Rectangle in screen coordinates. - * @return {Rect} Rectangle in image coordinates. - */ -Viewport.prototype.screenToImageRect = function(rect) { - return new Rect( - this.screenToImageX(rect.left), - this.screenToImageY(rect.top), - this.screenToImageSize(rect.width), - this.screenToImageSize(rect.height)); -}; - -/** - * @param {number} size Size in image coordinates. - * @return {number} Size in screen coordinates. - */ -Viewport.prototype.imageToScreenSize = function(size) { - return size * this.getScale(); -}; - -/** - * @param {number} x X in image coordinates. - * @return {number} X in screen coordinates. - */ -Viewport.prototype.imageToScreenX = function(x) { - return Math.round(this.imageOnScreen_.left + x * this.getScale()); -}; - -/** - * @param {number} y Y in image coordinates. - * @return {number} Y in screen coordinates. - */ -Viewport.prototype.imageToScreenY = function(y) { - return Math.round(this.imageOnScreen_.top + y * this.getScale()); -}; - -/** - * @param {Rect} rect Rectangle in image coordinates. - * @return {Rect} Rectangle in screen coordinates. - */ -Viewport.prototype.imageToScreenRect = function(rect) { - return new Rect( - this.imageToScreenX(rect.left), - this.imageToScreenY(rect.top), - Math.round(this.imageToScreenSize(rect.width)), - Math.round(this.imageToScreenSize(rect.height))); -}; - -/** - * @return {number} The number of physical pixels in one CSS pixel. - */ -Viewport.getDevicePixelRatio = function() { return window.devicePixelRatio }; - -/** - * Convert a rectangle from screen coordinates to 'device' coordinates. - * - * This conversion enlarges the original rectangle devicePixelRatio times - * with the screen center as a fixed point. - * - * @param {Rect} rect Rectangle in screen coordinates. - * @return {Rect} Rectangle in device coordinates. - */ -Viewport.prototype.screenToDeviceRect = function(rect) { - var ratio = Viewport.getDevicePixelRatio(); - var screenCenterX = Math.round( - this.screenBounds_.left + this.screenBounds_.width / 2); - var screenCenterY = Math.round( - this.screenBounds_.top + this.screenBounds_.height / 2); - return new Rect(screenCenterX + (rect.left - screenCenterX) * ratio, - screenCenterY + (rect.top - screenCenterY) * ratio, - rect.width * ratio, - rect.height * ratio); -}; - -/** - * @return {Rect} The visible part of the image, in device coordinates. - */ -Viewport.prototype.getDeviceClipped = function() { - return this.screenToDeviceRect(this.getScreenClipped()); -}; - -/** - * @return {boolean} True if some part of the image is clipped by the screen. - */ -Viewport.prototype.isClipped = function() { - return this.getMarginX_() < 0 || this.getMarginY_() < 0; -}; - -/** - * @return {number} Horizontal margin. - * Negative if the image is clipped horizontally. - * @private - */ -Viewport.prototype.getMarginX_ = function() { - return Math.round( - (this.screenBounds_.width - this.imageBounds_.width * this.scale_) / 2); -}; - -/** - * @return {number} Vertical margin. - * Negative if the image is clipped vertically. - * @private - */ -Viewport.prototype.getMarginY_ = function() { - return Math.round( - (this.screenBounds_.height - this.imageBounds_.height * this.scale_) / 2); -}; - -/** - * @param {number} x X-offset. - * @return {number} X-offset clamped to the valid range. - * @private - */ -Viewport.prototype.clampOffsetX_ = function(x) { - var limit = Math.round(Math.max(0, -this.getMarginX_() / this.getScale())); - return ImageUtil.clamp(-limit, x, limit); -}; - -/** - * @param {number} y Y-offset. - * @return {number} Y-offset clamped to the valid range. - * @private - */ -Viewport.prototype.clampOffsetY_ = function(y) { - var limit = Math.round(Math.max(0, -this.getMarginY_() / this.getScale())); - return ImageUtil.clamp(-limit, y, limit); -}; - -/** - * Recalculate the viewport parameters. - */ -Viewport.prototype.update = function() { - var scale = this.getScale(); - - // Image bounds in screen coordinates. - this.imageOnScreen_ = new Rect( - this.getMarginX_(), - this.getMarginY_(), - Math.round(this.imageBounds_.width * scale), - Math.round(this.imageBounds_.height * scale)); - - // A visible part of the image in image coordinates. - this.imageClipped_ = new Rect(this.imageBounds_); - - // A visible part of the image in screen coordinates. - this.screenClipped_ = new Rect(this.screenBounds_); - - // Adjust for the offset. - if (this.imageOnScreen_.left < 0) { - this.imageOnScreen_.left += - Math.round(this.clampOffsetX_(this.offsetX_) * scale); - this.imageClipped_.left = Math.round(-this.imageOnScreen_.left / scale); - this.imageClipped_.width = Math.round(this.screenBounds_.width / scale); - } else { - this.screenClipped_.left = this.imageOnScreen_.left; - this.screenClipped_.width = this.imageOnScreen_.width; - } - - if (this.imageOnScreen_.top < 0) { - this.imageOnScreen_.top += - Math.round(this.clampOffsetY_(this.offsetY_) * scale); - this.imageClipped_.top = Math.round(-this.imageOnScreen_.top / scale); - this.imageClipped_.height = Math.round(this.screenBounds_.height / scale); - } else { - this.screenClipped_.top = this.imageOnScreen_.top; - this.screenClipped_.height = this.imageOnScreen_.height; - } -}; - -/** - * @param {function} callback Repaint callback. - */ -Viewport.prototype.addRepaintCallback = function(callback) { - this.repaintCallbacks_.push(callback); -}; - -/** - * Repaint all clients. - */ -Viewport.prototype.repaint = function() { - this.update(); - for (var i = 0; i != this.repaintCallbacks_.length; i++) - this.repaintCallbacks_[i](); -}; diff --git a/ui/file_manager/file_manager/foreground/js/photo/gallery.js b/ui/file_manager/file_manager/foreground/js/photo/gallery.js deleted file mode 100644 index bdd4121..0000000 --- a/ui/file_manager/file_manager/foreground/js/photo/gallery.js +++ /dev/null @@ -1,890 +0,0 @@ -// Copyright 2014 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'; - -/** - * Called from the main frame when unloading. - * @param {boolean=} opt_exiting True if the app is exiting. - */ -function unload(opt_exiting) { Gallery.instance.onUnload(opt_exiting); } - -/** - * Gallery for viewing and editing image files. - * - * @param {Object} context Object containing the following: - * {function(string)} onNameChange Called every time a selected - * item name changes (on rename and on selection change). - * {AppWindow} appWindow - * {function(string)} onBack - * {function()} onClose - * {function()} onMaximize - * {function()} onMinimize - * {function(boolean)} onAppRegionChanged - * {MetadataCache} metadataCache - * {Array.<Object>} shareActions - * {string} readonlyDirName Directory name for readonly warning or null. - * {DirEntry} saveDirEntry Directory to save to. - * {function(string)} displayStringFunction. - * @param {VolumeManagerWrapper} volumeManager The VolumeManager instance of - * the system. - * @class - * @constructor - */ -function Gallery(context, volumeManager) { - this.container_ = document.querySelector('.gallery'); - this.document_ = document; - this.context_ = context; - this.metadataCache_ = context.metadataCache; - this.volumeManager_ = volumeManager; - this.selectedEntry_ = null; - this.metadataCacheObserverId_ = null; - this.onExternallyUnmountedBound_ = this.onExternallyUnmounted_.bind(this); - - this.dataModel_ = new cr.ui.ArrayDataModel([]); - this.selectionModel_ = new cr.ui.ListSelectionModel(); - loadTimeData.data = context.loadTimeData; - - this.initDom_(); - this.initListeners_(); -} - -/** - * Gallery extends cr.EventTarget. - */ -Gallery.prototype.__proto__ = cr.EventTarget.prototype; - -/** - * Creates and initializes a Gallery object based on a context. - * - * @param {Object} context Gallery context. - * @param {VolumeManagerWrapper} volumeManager VolumeManager of the system. - * @param {Array.<Entry>} entries Array of entries. - * @param {Array.<Entry>} selectedEntries Array of selected entries. - */ -Gallery.open = function(context, volumeManager, entries, selectedEntries) { - Gallery.instance = new Gallery(context, volumeManager); - Gallery.instance.load(entries, selectedEntries); -}; - -/** - * Tools fade-out timeout in milliseconds. - * @const - * @type {number} - */ -Gallery.FADE_TIMEOUT = 3000; - -/** - * First time tools fade-out timeout in milliseconds. - * @const - * @type {number} - */ -Gallery.FIRST_FADE_TIMEOUT = 1000; - -/** - * Time until mosaic is initialized in the background. Used to make gallery - * in the slide mode load faster. In miiliseconds. - * @const - * @type {number} - */ -Gallery.MOSAIC_BACKGROUND_INIT_DELAY = 1000; - -/** - * Types of metadata Gallery uses (to query the metadata cache). - * @const - * @type {string} - */ -Gallery.METADATA_TYPE = 'thumbnail|filesystem|media|streaming|drive'; - -/** - * Initializes listeners. - * @private - */ -Gallery.prototype.initListeners_ = function() { - this.document_.oncontextmenu = function(e) { e.preventDefault(); }; - this.keyDownBound_ = this.onKeyDown_.bind(this); - this.document_.body.addEventListener('keydown', this.keyDownBound_); - - this.inactivityWatcher_ = new MouseInactivityWatcher( - this.container_, Gallery.FADE_TIMEOUT, this.hasActiveTool.bind(this)); - - // Search results may contain files from different subdirectories so - // the observer is not going to work. - if (!this.context_.searchResults && this.context_.curDirEntry) { - this.metadataCacheObserverId_ = this.metadataCache_.addObserver( - this.context_.curDirEntry, - MetadataCache.CHILDREN, - 'thumbnail', - this.updateThumbnails_.bind(this)); - } - this.volumeManager_.addEventListener( - 'externally-unmounted', this.onExternallyUnmountedBound_); -}; - -/** - * Closes gallery when a volume containing the selected item is unmounted. - * @param {!Event} event The unmount event. - * @private - */ -Gallery.prototype.onExternallyUnmounted_ = function(event) { - if (!this.selectedEntry_) - return; - - if (this.volumeManager_.getVolumeInfo(this.selectedEntry_) === - event.volumeInfo) { - this.onBack_(); - } -}; - -/** - * Unloads the Gallery. - * @param {boolean} exiting True if the app is exiting. - */ -Gallery.prototype.onUnload = function(exiting) { - if (this.metadataCacheObserverId_ !== null) - this.metadataCache_.removeObserver(this.metadataCacheObserverId_); - this.volumeManager_.removeEventListener( - 'externally-unmounted', this.onExternallyUnmountedBound_); - this.slideMode_.onUnload(exiting); -}; - -/** - * Initializes DOM UI - * @private - */ -Gallery.prototype.initDom_ = function() { - // Initialize the dialog label. - cr.ui.dialogs.BaseDialog.OK_LABEL = str('GALLERY_OK_LABEL'); - cr.ui.dialogs.BaseDialog.CANCEL_LABEL = str('GALLERY_CANCEL_LABEL'); - - var content = util.createChild(this.container_, 'content'); - content.addEventListener('click', this.onContentClick_.bind(this)); - - this.header_ = util.createChild(this.container_, 'header tool dimmable'); - this.toolbar_ = util.createChild(this.container_, 'toolbar tool dimmable'); - - var backButton = util.createChild(this.container_, - 'back-button tool dimmable'); - util.createChild(backButton); - backButton.addEventListener('click', this.onBack_.bind(this)); - - var preventDefault = function(event) { event.preventDefault(); }; - - var minimizeButton = util.createChild(this.header_, - 'minimize-button tool dimmable', - 'button'); - minimizeButton.tabIndex = -1; - minimizeButton.addEventListener('click', this.onMinimize_.bind(this)); - minimizeButton.addEventListener('mousedown', preventDefault); - - var maximizeButton = util.createChild(this.header_, - 'maximize-button tool dimmable', - 'button'); - maximizeButton.tabIndex = -1; - maximizeButton.addEventListener('click', this.onMaximize_.bind(this)); - maximizeButton.addEventListener('mousedown', preventDefault); - - var closeButton = util.createChild(this.header_, - 'close-button tool dimmable', - 'button'); - closeButton.tabIndex = -1; - closeButton.addEventListener('click', this.onClose_.bind(this)); - closeButton.addEventListener('mousedown', preventDefault); - - this.filenameSpacer_ = util.createChild(this.toolbar_, 'filename-spacer'); - this.filenameEdit_ = util.createChild(this.filenameSpacer_, - 'namebox', 'input'); - - this.filenameEdit_.setAttribute('type', 'text'); - this.filenameEdit_.addEventListener('blur', - this.onFilenameEditBlur_.bind(this)); - - this.filenameEdit_.addEventListener('focus', - this.onFilenameFocus_.bind(this)); - - this.filenameEdit_.addEventListener('keydown', - this.onFilenameEditKeydown_.bind(this)); - - util.createChild(this.toolbar_, 'button-spacer'); - - this.prompt_ = new ImageEditor.Prompt(this.container_, str); - - this.modeButton_ = util.createChild(this.toolbar_, 'button mode', 'button'); - this.modeButton_.addEventListener('click', - this.toggleMode_.bind(this, null)); - - this.mosaicMode_ = new MosaicMode(content, - this.dataModel_, - this.selectionModel_, - this.metadataCache_, - this.volumeManager_, - this.toggleMode_.bind(this, null)); - - this.slideMode_ = new SlideMode(this.container_, - content, - this.toolbar_, - this.prompt_, - this.dataModel_, - this.selectionModel_, - this.context_, - this.toggleMode_.bind(this), - str); - - this.slideMode_.addEventListener('image-displayed', function() { - cr.dispatchSimpleEvent(this, 'image-displayed'); - }.bind(this)); - this.slideMode_.addEventListener('image-saved', function() { - cr.dispatchSimpleEvent(this, 'image-saved'); - }.bind(this)); - - var deleteButton = this.createToolbarButton_('delete', 'GALLERY_DELETE'); - deleteButton.addEventListener('click', this.delete_.bind(this)); - - this.shareButton_ = this.createToolbarButton_('share', 'GALLERY_SHARE'); - this.shareButton_.setAttribute('disabled', ''); - this.shareButton_.addEventListener('click', this.toggleShare_.bind(this)); - - this.shareMenu_ = util.createChild(this.container_, 'share-menu'); - this.shareMenu_.hidden = true; - util.createChild(this.shareMenu_, 'bubble-point'); - - this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); - this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); - - this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); - this.slideMode_.addEventListener('useraction', this.onUserAction_.bind(this)); -}; - -/** - * Creates toolbar button. - * - * @param {string} className Class to add. - * @param {string} title Button title. - * @return {!HTMLElement} Newly created button. - * @private - */ -Gallery.prototype.createToolbarButton_ = function(className, title) { - var button = util.createChild(this.toolbar_, className, 'button'); - button.title = str(title); - return button; -}; - -/** - * Loads the content. - * - * @param {!Array.<Entry>} entries Array of entries. - * @param {!Array.<Entry>} selectedEntries Array of selected entries. - */ -Gallery.prototype.load = function(entries, selectedEntries) { - var items = []; - for (var index = 0; index < entries.length; ++index) { - items.push(new Gallery.Item(entries[index])); - } - this.dataModel_.push.apply(this.dataModel_, items); - - this.selectionModel_.adjustLength(this.dataModel_.length); - - // Comparing Entries by reference is not safe. Therefore we have to use URLs. - var entryIndexesByURLs = {}; - for (var index = 0; index < entries.length; index++) { - entryIndexesByURLs[entries[index].toURL()] = index; - } - - for (var i = 0; i !== selectedEntries.length; i++) { - var selectedIndex = entryIndexesByURLs[selectedEntries[i].toURL()]; - if (selectedIndex !== undefined) - this.selectionModel_.setIndexSelected(selectedIndex, true); - else - console.error('Cannot select ' + selectedEntries[i]); - } - - if (this.selectionModel_.selectedIndexes.length === 0) - this.onSelection_(); - - var mosaic = this.mosaicMode_ && this.mosaicMode_.getMosaic(); - - // Mosaic view should show up if most of the selected files are images. - var imagesCount = 0; - for (var i = 0; i !== selectedEntries.length; i++) { - if (FileType.getMediaType(selectedEntries[i]) === 'image') - imagesCount++; - } - var mostlyImages = imagesCount > (selectedEntries.length / 2.0); - - var forcedMosaic = (this.context_.pageState && - this.context_.pageState.gallery === 'mosaic'); - - var showMosaic = (mostlyImages && selectedEntries.length > 1) || forcedMosaic; - if (mosaic && showMosaic) { - this.setCurrentMode_(this.mosaicMode_); - mosaic.init(); - mosaic.show(); - this.inactivityWatcher_.check(); // Show the toolbar. - cr.dispatchSimpleEvent(this, 'loaded'); - } else { - this.setCurrentMode_(this.slideMode_); - var maybeLoadMosaic = function() { - if (mosaic) - mosaic.init(); - cr.dispatchSimpleEvent(this, 'loaded'); - }.bind(this); - /* TODO: consider nice blow-up animation for the first image */ - this.slideMode_.enter(null, function() { - // Flash the toolbar briefly to show it is there. - this.inactivityWatcher_.kick(Gallery.FIRST_FADE_TIMEOUT); - }.bind(this), - maybeLoadMosaic); - } -}; - -/** - * Closes the Gallery and go to Files.app. - * @private - */ -Gallery.prototype.back_ = function() { - if (util.isFullScreen(this.context_.appWindow)) { - util.toggleFullScreen(this.context_.appWindow, - false); // Leave the full screen mode. - } - this.context_.onBack(this.getSelectedEntries()); -}; - -/** - * Handles user's 'Back' action (Escape or a click on the X icon). - * @private - */ -Gallery.prototype.onBack_ = function() { - this.executeWhenReady(this.back_.bind(this)); -}; - -/** - * Handles user's 'Close' action. - * @private - */ -Gallery.prototype.onClose_ = function() { - this.executeWhenReady(this.context_.onClose); -}; - -/** - * Handles user's 'Maximize' action (Escape or a click on the X icon). - * @private - */ -Gallery.prototype.onMaximize_ = function() { - this.executeWhenReady(this.context_.onMaximize); -}; - -/** - * Handles user's 'Maximize' action (Escape or a click on the X icon). - * @private - */ -Gallery.prototype.onMinimize_ = function() { - this.executeWhenReady(this.context_.onMinimize); -}; - -/** - * Executes a function when the editor is done with the modifications. - * @param {function} callback Function to execute. - */ -Gallery.prototype.executeWhenReady = function(callback) { - this.currentMode_.executeWhenReady(callback); -}; - -/** - * @return {Object} File browser private API. - */ -Gallery.getFileBrowserPrivate = function() { - return chrome.fileBrowserPrivate || window.top.chrome.fileBrowserPrivate; -}; - -/** - * @return {boolean} True if some tool is currently active. - */ -Gallery.prototype.hasActiveTool = function() { - return this.currentMode_.hasActiveTool() || - this.isSharing_() || this.isRenaming_(); -}; - -/** -* External user action event handler. -* @private -*/ -Gallery.prototype.onUserAction_ = function() { - this.closeShareMenu_(); - // Show the toolbar and hide it after the default timeout. - this.inactivityWatcher_.kick(); -}; - -/** - * Sets the current mode, update the UI. - * @param {Object} mode Current mode. - * @private - */ -Gallery.prototype.setCurrentMode_ = function(mode) { - if (mode !== this.slideMode_ && mode !== this.mosaicMode_) - console.error('Invalid Gallery mode'); - - this.currentMode_ = mode; - this.container_.setAttribute('mode', this.currentMode_.getName()); - this.updateSelectionAndState_(); - this.updateButtons_(); -}; - -/** - * Mode toggle event handler. - * @param {function=} opt_callback Callback. - * @param {Event=} opt_event Event that caused this call. - * @private - */ -Gallery.prototype.toggleMode_ = function(opt_callback, opt_event) { - if (!this.modeButton_) - return; - - if (this.changingMode_) // Do not re-enter while changing the mode. - return; - - if (opt_event) - this.onUserAction_(); - - this.changingMode_ = true; - - var onModeChanged = function() { - this.changingMode_ = false; - if (opt_callback) opt_callback(); - }.bind(this); - - var tileIndex = Math.max(0, this.selectionModel_.selectedIndex); - - var mosaic = this.mosaicMode_.getMosaic(); - var tileRect = mosaic.getTileRect(tileIndex); - - if (this.currentMode_ === this.slideMode_) { - this.setCurrentMode_(this.mosaicMode_); - mosaic.transform( - tileRect, this.slideMode_.getSelectedImageRect(), true /* instant */); - this.slideMode_.leave( - tileRect, - function() { - // Animate back to normal position. - mosaic.transform(); - mosaic.show(); - onModeChanged(); - }.bind(this)); - } else { - this.setCurrentMode_(this.slideMode_); - this.slideMode_.enter( - tileRect, - function() { - // Animate to zoomed position. - mosaic.transform(tileRect, this.slideMode_.getSelectedImageRect()); - mosaic.hide(); - }.bind(this), - onModeChanged); - } -}; - -/** - * Deletes the selected items. - * @private - */ -Gallery.prototype.delete_ = function() { - this.onUserAction_(); - - // Clone the sorted selected indexes array. - var indexesToRemove = this.selectionModel_.selectedIndexes.slice(); - if (!indexesToRemove.length) - return; - - /* TODO(dgozman): Implement Undo delete, Remove the confirmation dialog. */ - - var itemsToRemove = this.getSelectedItems(); - var plural = itemsToRemove.length > 1; - var param = plural ? itemsToRemove.length : itemsToRemove[0].getFileName(); - - function deleteNext() { - if (!itemsToRemove.length) - return; // All deleted. - - // TODO(hirono): Use fileOperationManager. - var entry = itemsToRemove.pop().getEntry(); - entry.remove(deleteNext, function() { - util.flog('Error deleting: ' + entry.name, deleteNext); - }); - } - - // Prevent the Gallery from handling Esc and Enter. - this.document_.body.removeEventListener('keydown', this.keyDownBound_); - var restoreListener = function() { - this.document_.body.addEventListener('keydown', this.keyDownBound_); - }.bind(this); - - - var confirm = new cr.ui.dialogs.ConfirmDialog(this.container_); - confirm.setOkLabel(str('DELETE_BUTTON_LABEL')); - confirm.show(strf(plural ? - 'GALLERY_CONFIRM_DELETE_SOME' : 'GALLERY_CONFIRM_DELETE_ONE', param), - function() { - restoreListener(); - this.selectionModel_.unselectAll(); - this.selectionModel_.leadIndex = -1; - // Remove items from the data model, starting from the highest index. - while (indexesToRemove.length) - this.dataModel_.splice(indexesToRemove.pop(), 1); - // Delete actual files. - deleteNext(); - }.bind(this), - function() { - // Restore the listener after a timeout so that ESC is processed. - setTimeout(restoreListener, 0); - }); -}; - -/** - * @return {Array.<Gallery.Item>} Current selection. - */ -Gallery.prototype.getSelectedItems = function() { - return this.selectionModel_.selectedIndexes.map( - this.dataModel_.item.bind(this.dataModel_)); -}; - -/** - * @return {Array.<Entry>} Array of currently selected entries. - */ -Gallery.prototype.getSelectedEntries = function() { - return this.selectionModel_.selectedIndexes.map(function(index) { - return this.dataModel_.item(index).getEntry(); - }.bind(this)); -}; - -/** - * @return {?Gallery.Item} Current single selection. - */ -Gallery.prototype.getSingleSelectedItem = function() { - var items = this.getSelectedItems(); - if (items.length > 1) { - console.error('Unexpected multiple selection'); - return null; - } - return items[0]; -}; - -/** - * Selection change event handler. - * @private - */ -Gallery.prototype.onSelection_ = function() { - this.updateSelectionAndState_(); - this.updateShareMenu_(); -}; - -/** - * Data model splice event handler. - * @private - */ -Gallery.prototype.onSplice_ = function() { - this.selectionModel_.adjustLength(this.dataModel_.length); -}; - -/** - * Content change event handler. - * @param {Event} event Event. - * @private -*/ -Gallery.prototype.onContentChange_ = function(event) { - var index = this.dataModel_.indexOf(event.item); - if (index !== this.selectionModel_.selectedIndex) - console.error('Content changed for unselected item'); - this.updateSelectionAndState_(); -}; - -/** - * Keydown handler. - * - * @param {Event} event Event. - * @private - */ -Gallery.prototype.onKeyDown_ = function(event) { - var wasSharing = this.isSharing_(); - this.closeShareMenu_(); - - if (this.currentMode_.onKeyDown(event)) - return; - - switch (util.getKeyModifiers(event) + event.keyIdentifier) { - case 'U+0008': // Backspace. - // The default handler would call history.back and close the Gallery. - event.preventDefault(); - break; - - case 'U+001B': // Escape - // Swallow Esc if it closed the Share menu, otherwise close the Gallery. - if (!wasSharing) - this.onBack_(); - break; - - case 'U+004D': // 'm' switches between Slide and Mosaic mode. - this.toggleMode_(null, event); - break; - - case 'U+0056': // 'v' - this.slideMode_.startSlideshow(SlideMode.SLIDESHOW_INTERVAL_FIRST, event); - break; - - case 'U+007F': // Delete - case 'Shift-U+0033': // Shift+'3' (Delete key might be missing). - this.delete_(); - break; - } -}; - -// Name box and rename support. - -/** - * Updates the UI related to the selected item and the persistent state. - * - * @private - */ -Gallery.prototype.updateSelectionAndState_ = function() { - var numSelectedItems = this.selectionModel_.selectedIndexes.length; - var displayName = ''; - var selectedEntryURL = null; - - // If it's selecting something, update the variable values. - if (numSelectedItems) { - var selectedItem = - this.dataModel_.item(this.selectionModel_.selectedIndex); - this.selectedEntry_ = selectedItem.getEntry(); - selectedEntryURL = this.selectedEntry_.toURL(); - - if (numSelectedItems === 1) { - window.top.document.title = this.selectedEntry_.name; - displayName = ImageUtil.getDisplayNameFromName(this.selectedEntry_.name); - } else if (this.context_.curDirEntry) { - // If the Gallery was opened on search results the search query will not - // be recorded in the app state and the relaunch will just open the - // gallery in the curDirEntry directory. - window.top.document.title = this.context_.curDirEntry.name; - displayName = strf('GALLERY_ITEMS_SELECTED', numSelectedItems); - } - } - - window.top.util.updateAppState( - null, // Keep the current directory. - selectedEntryURL, // Update the selection. - {gallery: (this.currentMode_ === this.mosaicMode_ ? 'mosaic' : 'slide')}); - - // We can't rename files in readonly directory. - // We can only rename a single file. - this.filenameEdit_.disabled = numSelectedItems !== 1 || - this.context_.readonlyDirName; - this.filenameEdit_.value = displayName; -}; - -/** - * Click event handler on filename edit box - * @private - */ -Gallery.prototype.onFilenameFocus_ = function() { - ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', true); - this.filenameEdit_.originalValue = this.filenameEdit_.value; - setTimeout(this.filenameEdit_.select.bind(this.filenameEdit_), 0); - this.onUserAction_(); -}; - -/** - * Blur event handler on filename edit box. - * - * @param {Event} event Blur event. - * @return {boolean} if default action should be prevented. - * @private - */ -Gallery.prototype.onFilenameEditBlur_ = function(event) { - if (this.filenameEdit_.value && this.filenameEdit_.value[0] === '.') { - this.prompt_.show('GALLERY_FILE_HIDDEN_NAME', 5000); - this.filenameEdit_.focus(); - event.stopPropagation(); - event.preventDefault(); - return false; - } - - var item = this.getSingleSelectedItem(); - if (item) { - var oldEntry = item.getEntry(); - - var onFileExists = function() { - this.prompt_.show('GALLERY_FILE_EXISTS', 3000); - this.filenameEdit_.value = name; - this.filenameEdit_.focus(); - }.bind(this); - - var onSuccess = function() { - var event = new Event('content'); - event.item = item; - event.oldEntry = oldEntry; - event.metadata = null; // Metadata unchanged. - this.dataModel_.dispatchEvent(event); - }.bind(this); - - if (this.filenameEdit_.value) { - item.rename( - this.filenameEdit_.value, onSuccess, onFileExists); - } - } - - ImageUtil.setAttribute(this.filenameSpacer_, 'renaming', false); - this.onUserAction_(); -}; - -/** - * Keydown event handler on filename edit box - * @private - */ -Gallery.prototype.onFilenameEditKeydown_ = function() { - switch (event.keyCode) { - case 27: // Escape - this.filenameEdit_.value = this.filenameEdit_.originalValue; - this.filenameEdit_.blur(); - break; - - case 13: // Enter - this.filenameEdit_.blur(); - break; - } - event.stopPropagation(); -}; - -/** - * @return {boolean} True if file renaming is currently in progress. - * @private - */ -Gallery.prototype.isRenaming_ = function() { - return this.filenameSpacer_.hasAttribute('renaming'); -}; - -/** - * Content area click handler. - * @private - */ -Gallery.prototype.onContentClick_ = function() { - this.closeShareMenu_(); - this.filenameEdit_.blur(); -}; - -// Share button support. - -/** - * @return {boolean} True if the Share menu is active. - * @private - */ -Gallery.prototype.isSharing_ = function() { - return !this.shareMenu_.hidden; -}; - -/** - * Close Share menu if it is open. - * @private - */ -Gallery.prototype.closeShareMenu_ = function() { - if (this.isSharing_()) - this.toggleShare_(); -}; - -/** - * Share button handler. - * @private - */ -Gallery.prototype.toggleShare_ = function() { - if (!this.shareButton_.hasAttribute('disabled')) - this.shareMenu_.hidden = !this.shareMenu_.hidden; - this.inactivityWatcher_.check(); -}; - -/** - * Updates available actions list based on the currently selected urls. - * @private. - */ -Gallery.prototype.updateShareMenu_ = function() { - var entries = this.getSelectedEntries(); - - function isShareAction(task) { - var taskParts = task.taskId.split('|'); - return taskParts[0] !== chrome.runtime.id; - } - - var api = Gallery.getFileBrowserPrivate(); - - var createShareMenu = function(tasks) { - var wasHidden = this.shareMenu_.hidden; - this.shareMenu_.hidden = true; - var items = this.shareMenu_.querySelectorAll('.item'); - for (var i = 0; i !== items.length; i++) { - items[i].parentNode.removeChild(items[i]); - } - - for (var t = 0; t !== tasks.length; t++) { - var task = tasks[t]; - if (!isShareAction(task)) continue; - - var item = util.createChild(this.shareMenu_, 'item'); - item.textContent = task.title; - item.style.backgroundImage = 'url(' + task.iconUrl + ')'; - item.addEventListener('click', function(taskId) { - this.toggleShare_(); // Hide the menu. - // TODO(hirono): Use entries instead of URLs. - this.executeWhenReady( - api.executeTask.bind( - api, - taskId, - util.entriesToURLs(entries), - function(result) { - var alertDialog = - new cr.ui.dialogs.AlertDialog(this.container_); - util.isTeleported(window).then(function(teleported) { - if (teleported) - util.showOpenInOtherDesktopAlert(alertDialog, entries); - }.bind(this)); - }.bind(this))); - }.bind(this, task.taskId)); - } - - var empty = this.shareMenu_.querySelector('.item') === null; - ImageUtil.setAttribute(this.shareButton_, 'disabled', empty); - this.shareMenu_.hidden = wasHidden || empty; - }.bind(this); - - // Create or update the share menu with a list of sharing tasks and show - // or hide the share button. - // TODO(mtomasz): Pass Entries directly, instead of URLs. - if (!entries.length) - createShareMenu([]); // Empty list of tasks, since there is no selection. - else - api.getFileTasks(util.entriesToURLs(entries), createShareMenu); -}; - -/** - * Updates thumbnails. - * @private - */ -Gallery.prototype.updateThumbnails_ = function() { - if (this.currentMode_ === this.slideMode_) - this.slideMode_.updateThumbnails(); - - if (this.mosaicMode_) { - var mosaic = this.mosaicMode_.getMosaic(); - if (mosaic.isInitialized()) - mosaic.reload(); - } -}; - -/** - * Updates buttons. - * @private - */ -Gallery.prototype.updateButtons_ = function() { - if (this.modeButton_) { - var oppositeMode = - this.currentMode_ === this.slideMode_ ? this.mosaicMode_ : - this.slideMode_; - this.modeButton_.title = str(oppositeMode.getTitle()); - } -}; diff --git a/ui/file_manager/file_manager/foreground/js/photo/gallery_item.js b/ui/file_manager/file_manager/foreground/js/photo/gallery_item.js deleted file mode 100644 index 813d87c..0000000 --- a/ui/file_manager/file_manager/foreground/js/photo/gallery_item.js +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2014 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'; - -/** - * Object representing an image item (a photo or a video). - * - * @param {FileEntry} entry Image entry. - * @constructor - */ -Gallery.Item = function(entry) { - this.entry_ = entry; - this.original_ = true; -}; - -/** - * @return {FileEntry} Image entry. - */ -Gallery.Item.prototype.getEntry = function() { return this.entry_ }; - -/** - * @return {string} File name. - */ -Gallery.Item.prototype.getFileName = function() { - return this.entry_.name; -}; - -/** - * @return {boolean} True if this image has not been created in this session. - */ -Gallery.Item.prototype.isOriginal = function() { return this.original_ }; - -// TODO: Localize? -/** - * @type {string} Suffix for a edited copy file name. - */ -Gallery.Item.COPY_SIGNATURE = ' - Edited'; - -/** - * Regular expression to match '... - Edited'. - * @type {RegExp} - */ -Gallery.Item.REGEXP_COPY_0 = - new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + '$'); - -/** - * Regular expression to match '... - Edited (N)'. - * @type {RegExp} - */ -Gallery.Item.REGEXP_COPY_N = - new RegExp('^(.+)' + Gallery.Item.COPY_SIGNATURE + ' \\((\\d+)\\)$'); - -/** - * Creates a name for an edited copy of the file. - * - * @param {Entry} dirEntry Entry. - * @param {function} callback Callback. - * @private - */ -Gallery.Item.prototype.createCopyName_ = function(dirEntry, callback) { - var name = this.getFileName(); - - // If the item represents a file created during the current Gallery session - // we reuse it for subsequent saves instead of creating multiple copies. - if (!this.original_) { - callback(name); - return; - } - - var ext = ''; - var index = name.lastIndexOf('.'); - if (index != -1) { - ext = name.substr(index); - name = name.substr(0, index); - } - - if (!ext.match(/jpe?g/i)) { - // Chrome can natively encode only two formats: JPEG and PNG. - // All non-JPEG images are saved in PNG, hence forcing the file extension. - ext = '.png'; - } - - function tryNext(tries) { - // All the names are used. Let's overwrite the last one. - if (tries == 0) { - setTimeout(callback, 0, name + ext); - return; - } - - // If the file name contains the copy signature add/advance the sequential - // number. - var matchN = Gallery.Item.REGEXP_COPY_N.exec(name); - var match0 = Gallery.Item.REGEXP_COPY_0.exec(name); - if (matchN && matchN[1] && matchN[2]) { - var copyNumber = parseInt(matchN[2], 10) + 1; - name = matchN[1] + Gallery.Item.COPY_SIGNATURE + ' (' + copyNumber + ')'; - } else if (match0 && match0[1]) { - name = match0[1] + Gallery.Item.COPY_SIGNATURE + ' (1)'; - } else { - name += Gallery.Item.COPY_SIGNATURE; - } - - dirEntry.getFile(name + ext, {create: false, exclusive: false}, - tryNext.bind(null, tries - 1), - callback.bind(null, name + ext)); - } - - tryNext(10); -}; - -/** - * Writes the new item content to the file. - * - * @param {Entry} overrideDir Directory to save to. If null, save to the same - * directory as the original. - * @param {boolean} overwrite True if overwrite, false if copy. - * @param {HTMLCanvasElement} canvas Source canvas. - * @param {ImageEncoder.MetadataEncoder} metadataEncoder MetadataEncoder. - * @param {function(boolean)=} opt_callback Callback accepting true for success. - */ -Gallery.Item.prototype.saveToFile = function( - overrideDir, overwrite, canvas, metadataEncoder, opt_callback) { - ImageUtil.metrics.startInterval(ImageUtil.getMetricName('SaveTime')); - - var name = this.getFileName(); - - var onSuccess = function(entry) { - ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 1, 2); - ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('SaveTime')); - this.entry_ = entry; - if (opt_callback) opt_callback(true); - }.bind(this); - - function onError(error) { - console.error('Error saving from gallery', name, error); - ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('SaveResult'), 0, 2); - if (opt_callback) opt_callback(false); - } - - function doSave(newFile, fileEntry) { - fileEntry.createWriter(function(fileWriter) { - function writeContent() { - fileWriter.onwriteend = onSuccess.bind(null, fileEntry); - fileWriter.write(ImageEncoder.getBlob(canvas, metadataEncoder)); - } - fileWriter.onerror = function(error) { - onError(error); - // Disable all callbacks on the first error. - fileWriter.onerror = null; - fileWriter.onwriteend = null; - }; - if (newFile) { - writeContent(); - } else { - fileWriter.onwriteend = writeContent; - fileWriter.truncate(0); - } - }, onError); - } - - function getFile(dir, newFile) { - dir.getFile(name, {create: newFile, exclusive: newFile}, - doSave.bind(null, newFile), onError); - } - - function checkExistence(dir) { - dir.getFile(name, {create: false, exclusive: false}, - getFile.bind(null, dir, false /* existing file */), - getFile.bind(null, dir, true /* create new file */)); - } - - var saveToDir = function(dir) { - if (overwrite) { - checkExistence(dir); - } else { - this.createCopyName_(dir, function(copyName) { - this.original_ = false; - name = copyName; - checkExistence(dir); - }.bind(this)); - } - }.bind(this); - - if (overrideDir) { - saveToDir(overrideDir); - } else { - this.entry_.getParent(saveToDir, onError); - } -}; - -/** - * Renames the file. - * - * @param {string} displayName New display name (without the extension). - * @param {function()} onSuccess Success callback. - * @param {function()} onExists Called if the file with the new name exists. - */ -Gallery.Item.prototype.rename = function(displayName, onSuccess, onExists) { - var newFileName = this.entry_.name.replace( - ImageUtil.getDisplayNameFromName(this.entry_.name), displayName); - - if (newFileName === this.entry_.name) - return; - - var onRenamed = function(entry) { - this.entry_ = entry; - onSuccess(); - }.bind(this); - - var onError = function() { - console.error( - 'Rename error: "' + this.entry_.name + '" to "' + newFileName + '"'); - }; - - var moveIfDoesNotExist = function(parentDir) { - parentDir.getFile( - newFileName, - {create: false, exclusive: false}, - onExists, - function() { - this.entry_.moveTo(parentDir, newFileName, onRenamed, onError); - }.bind(this)); - }.bind(this); - - this.entry_.getParent(moveIfDoesNotExist, onError); -}; diff --git a/ui/file_manager/file_manager/foreground/js/photo/gallery_scripts.js b/ui/file_manager/file_manager/foreground/js/photo/gallery_scripts.js deleted file mode 100644 index 6c1205d..0000000 --- a/ui/file_manager/file_manager/foreground/js/photo/gallery_scripts.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2014 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. - -// The include directives are put into Javascript-style comments to prevent -// parsing errors in non-flattened mode. The flattener still sees them. -// Note that this makes the flattener to comment out the first line of the -// included file but that's all right since any javascript file should start -// with a copyright comment anyway. - -//<include src="../metrics.js"> - -//<include src="../../../../image_loader/image_loader_client.js"/> - -//<include src="../../../../../webui/resources/js/cr.js"> -//<include src="../../../../../webui/resources/js/event_tracker.js"> -//<include src="../../../../../webui/resources/js/load_time_data.js"> - -//<include src="../../../../../webui/resources/js/cr/ui.js"> -//<include src="../../../../../webui/resources/js/cr/event_target.js"> -//<include src="../../../../../webui/resources/js/cr/ui/touch_handler.js"> -//<include src="../../../../../webui/resources/js/cr/ui/array_data_model.js"> -//<include src="../../../../../webui/resources/js/cr/ui/dialogs.js"> -//<include src="../../../../../webui/resources/js/cr/ui/list_item.js"> -//<include src="../../../../../webui/resources/js/cr/ui/list_selection_model.js"> -//<include src="../../../../../webui/resources/js/cr/ui/list_single_selection_model.js"> -//<include src="../../../../../webui/resources/js/cr/ui/list_selection_controller.js"> -//<include src="../../../../../webui/resources/js/cr/ui/list.js"> -//<include src="../../../../../webui/resources/js/cr/ui/grid.js"> - -(function() { -// 'strict mode' is invoked for this scope. - -//<include src="../../../common/js/async_util.js"> -//<include src="../../../common/js/util.js"> -//<include src="../../../common/js/volume_manager_common.js"> -//<include src="../file_type.js"> -//<include src="../thumbnail_loader.js"/> -//<include src="../volume_manager_wrapper.js"> - -//<include src="../image_editor/image_util.js"/> -//<include src="../image_editor/viewport.js"/> -//<include src="../image_editor/image_buffer.js"/> -//<include src="../image_editor/image_view.js"/> -//<include src="../image_editor/commands.js"/> -//<include src="../image_editor/image_editor.js"/> -//<include src="../image_editor/image_transform.js"/> -//<include src="../image_editor/image_adjust.js"/> -//<include src="../image_editor/filter.js"/> -//<include src="../image_editor/image_encoder.js"/> -//<include src="../image_editor/exif_encoder.js"/> - -//<include src="../media/media_controls.js"/> -//<include src="../media/mouse_inactivity_watcher.js"/> - -//<include src="../metadata/metadata_cache.js"/> - -//<include src="gallery.js"> -//<include src="gallery_item.js"> -//<include src="mosaic_mode.js"> -//<include src="slide_mode.js"> -//<include src="ribbon.js"> - -// Exports -window.ImageUtil = ImageUtil; -window.Gallery = Gallery; -window.unload = unload; - -})(); diff --git a/ui/file_manager/file_manager/foreground/js/photo/mosaic_mode.js b/ui/file_manager/file_manager/foreground/js/photo/mosaic_mode.js deleted file mode 100644 index e6b88c5..0000000 --- a/ui/file_manager/file_manager/foreground/js/photo/mosaic_mode.js +++ /dev/null @@ -1,2035 +0,0 @@ -// Copyright 2014 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'; - -/** - * @param {Element} container Content container. - * @param {cr.ui.ArrayDataModel} dataModel Data model. - * @param {cr.ui.ListSelectionModel} selectionModel Selection model. - * @param {MetadataCache} metadataCache Metadata cache. - * @param {VolumeManagerWrapper} volumeManager Volume manager. - * @param {function} toggleMode Function to switch to the Slide mode. - * @constructor - */ -function MosaicMode( - container, dataModel, selectionModel, metadataCache, volumeManager, - toggleMode) { - this.mosaic_ = new Mosaic( - container.ownerDocument, dataModel, selectionModel, metadataCache, - volumeManager); - container.appendChild(this.mosaic_); - - this.toggleMode_ = toggleMode; - this.mosaic_.addEventListener('dblclick', this.toggleMode_); - this.showingTimeoutID_ = null; -} - -/** - * @return {Mosaic} The mosaic control. - */ -MosaicMode.prototype.getMosaic = function() { return this.mosaic_ }; - -/** - * @return {string} Mode name. - */ -MosaicMode.prototype.getName = function() { return 'mosaic' }; - -/** - * @return {string} Mode title. - */ -MosaicMode.prototype.getTitle = function() { return 'GALLERY_MOSAIC' }; - -/** - * Execute an action (this mode has no busy state). - * @param {function} action Action to execute. - */ -MosaicMode.prototype.executeWhenReady = function(action) { action() }; - -/** - * @return {boolean} Always true (no toolbar fading in this mode). - */ -MosaicMode.prototype.hasActiveTool = function() { return true }; - -/** - * Keydown handler. - * - * @param {Event} event Event. - */ -MosaicMode.prototype.onKeyDown = function(event) { - switch (util.getKeyModifiers(event) + event.keyIdentifier) { - case 'Enter': - if (!document.activeElement || - document.activeElement.localName !== 'button') { - this.toggleMode_(); - event.preventDefault(); - } - return; - } - this.mosaic_.onKeyDown(event); -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * Mosaic control. - * - * @param {Document} document Document. - * @param {cr.ui.ArrayDataModel} dataModel Data model. - * @param {cr.ui.ListSelectionModel} selectionModel Selection model. - * @param {MetadataCache} metadataCache Metadata cache. - * @param {VolumeManagerWrapper} volumeManager Volume manager. - * @return {Element} Mosaic element. - * @constructor - */ -function Mosaic(document, dataModel, selectionModel, metadataCache, - volumeManager) { - var self = document.createElement('div'); - Mosaic.decorate( - self, dataModel, selectionModel, metadataCache, volumeManager); - return self; -} - -/** - * Inherits from HTMLDivElement. - */ -Mosaic.prototype.__proto__ = HTMLDivElement.prototype; - -/** - * Default layout delay in ms. - * @const - * @type {number} - */ -Mosaic.LAYOUT_DELAY = 200; - -/** - * Smooth scroll animation duration when scrolling using keyboard or - * clicking on a partly visible tile. In ms. - * @const - * @type {number} - */ -Mosaic.ANIMATED_SCROLL_DURATION = 500; - -/** - * Decorates a Mosaic instance. - * - * @param {Mosaic} self Self pointer. - * @param {cr.ui.ArrayDataModel} dataModel Data model. - * @param {cr.ui.ListSelectionModel} selectionModel Selection model. - * @param {MetadataCache} metadataCache Metadata cache. - * @param {VolumeManagerWrapper} volumeManager Volume manager. - */ -Mosaic.decorate = function( - self, dataModel, selectionModel, metadataCache, volumeManager) { - self.__proto__ = Mosaic.prototype; - self.className = 'mosaic'; - - self.dataModel_ = dataModel; - self.selectionModel_ = selectionModel; - self.metadataCache_ = metadataCache; - self.volumeManager_ = volumeManager; - - // Initialization is completed lazily on the first call to |init|. -}; - -/** - * Initializes the mosaic element. - */ -Mosaic.prototype.init = function() { - if (this.tiles_) - return; // Already initialized, nothing to do. - - this.layoutModel_ = new Mosaic.Layout(); - this.onResize_(); - - this.selectionController_ = - new Mosaic.SelectionController(this.selectionModel_, this.layoutModel_); - - this.tiles_ = []; - for (var i = 0; i !== this.dataModel_.length; i++) { - var locationInfo = - this.volumeManager_.getLocationInfo(this.dataModel_.item(i).getEntry()); - this.tiles_.push( - new Mosaic.Tile(this, this.dataModel_.item(i), locationInfo)); - } - - this.selectionModel_.selectedIndexes.forEach(function(index) { - this.tiles_[index].select(true); - }.bind(this)); - - this.initTiles_(this.tiles_); - - // The listeners might be called while some tiles are still loading. - this.initListeners_(); -}; - -/** - * @return {boolean} Whether mosaic is initialized. - */ -Mosaic.prototype.isInitialized = function() { - return !!this.tiles_; -}; - -/** - * Starts listening to events. - * - * We keep listening to events even when the mosaic is hidden in order to - * keep the layout up to date. - * - * @private - */ -Mosaic.prototype.initListeners_ = function() { - this.ownerDocument.defaultView.addEventListener( - 'resize', this.onResize_.bind(this)); - - var mouseEventBound = this.onMouseEvent_.bind(this); - this.addEventListener('mousemove', mouseEventBound); - this.addEventListener('mousedown', mouseEventBound); - this.addEventListener('mouseup', mouseEventBound); - this.addEventListener('scroll', this.onScroll_.bind(this)); - - this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); - this.selectionModel_.addEventListener('leadIndexChange', - this.onLeadChange_.bind(this)); - - this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); - this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); -}; - -/** - * Smoothly scrolls the container to the specified position using - * f(x) = sqrt(x) speed function normalized to animation duration. - * @param {number} targetPosition Horizontal scroll position in pixels. - */ -Mosaic.prototype.animatedScrollTo = function(targetPosition) { - if (this.scrollAnimation_) { - webkitCancelAnimationFrame(this.scrollAnimation_); - this.scrollAnimation_ = null; - } - - // Mouse move events are fired without touching the mouse because of scrolling - // the container. Therefore, these events have to be suppressed. - this.suppressHovering_ = true; - - // Calculates integral area from t1 to t2 of f(x) = sqrt(x) dx. - var integral = function(t1, t2) { - return 2.0 / 3.0 * Math.pow(t2, 3.0 / 2.0) - - 2.0 / 3.0 * Math.pow(t1, 3.0 / 2.0); - }; - - var delta = targetPosition - this.scrollLeft; - var factor = delta / integral(0, Mosaic.ANIMATED_SCROLL_DURATION); - var startTime = Date.now(); - var lastPosition = 0; - var scrollOffset = this.scrollLeft; - - var animationFrame = function() { - var position = Date.now() - startTime; - var step = factor * - integral(Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - position), - Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - lastPosition)); - scrollOffset += step; - - var oldScrollLeft = this.scrollLeft; - var newScrollLeft = Math.round(scrollOffset); - - if (oldScrollLeft !== newScrollLeft) - this.scrollLeft = newScrollLeft; - - if (step === 0 || this.scrollLeft !== newScrollLeft) { - this.scrollAnimation_ = null; - // Release the hovering lock after a safe delay to avoid hovering - // a tile because of altering |this.scrollLeft|. - setTimeout(function() { - if (!this.scrollAnimation_) - this.suppressHovering_ = false; - }.bind(this), 100); - } else { - // Continue the animation. - this.scrollAnimation_ = requestAnimationFrame(animationFrame); - } - - lastPosition = position; - }.bind(this); - - // Start the animation. - this.scrollAnimation_ = requestAnimationFrame(animationFrame); -}; - -/** - * @return {Mosaic.Tile} Selected tile or undefined if no selection. - */ -Mosaic.prototype.getSelectedTile = function() { - return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; -}; - -/** - * @param {number} index Tile index. - * @return {Rect} Tile's image rectangle. - */ -Mosaic.prototype.getTileRect = function(index) { - var tile = this.tiles_[index]; - return tile && tile.getImageRect(); -}; - -/** - * @param {number} index Tile index. - * Scroll the given tile into the viewport. - */ -Mosaic.prototype.scrollIntoView = function(index) { - var tile = this.tiles_[index]; - if (tile) tile.scrollIntoView(); -}; - -/** - * Initializes multiple tiles. - * - * @param {Array.<Mosaic.Tile>} tiles Array of tiles. - * @param {function()=} opt_callback Completion callback. - * @private - */ -Mosaic.prototype.initTiles_ = function(tiles, opt_callback) { - // We do not want to use tile indices in asynchronous operations because they - // do not survive data model splices. Copy tile references instead. - tiles = tiles.slice(); - - // Throttle the metadata access so that we do not overwhelm the file system. - var MAX_CHUNK_SIZE = 10; - - var loadChunk = function() { - if (!tiles.length) { - if (opt_callback) opt_callback(); - return; - } - var chunkSize = Math.min(tiles.length, MAX_CHUNK_SIZE); - var loaded = 0; - for (var i = 0; i !== chunkSize; i++) { - this.initTile_(tiles.shift(), function() { - if (++loaded === chunkSize) { - this.layout(); - loadChunk(); - } - }.bind(this)); - } - }.bind(this); - - loadChunk(); -}; - -/** - * Initializes a single tile. - * - * @param {Mosaic.Tile} tile Tile. - * @param {function()} callback Completion callback. - * @private - */ -Mosaic.prototype.initTile_ = function(tile, callback) { - var onImageMeasured = callback; - this.metadataCache_.getOne(tile.getItem().getEntry(), Gallery.METADATA_TYPE, - function(metadata) { - tile.init(metadata, onImageMeasured); - }); -}; - -/** - * Reloads all tiles. - */ -Mosaic.prototype.reload = function() { - this.layoutModel_.reset_(); - this.tiles_.forEach(function(t) { t.markUnloaded() }); - this.initTiles_(this.tiles_); -}; - -/** - * Layouts the tiles in the order of their indices. - * - * Starts where it last stopped (at #0 the first time). - * Stops when all tiles are processed or when the next tile is still loading. - */ -Mosaic.prototype.layout = function() { - if (this.layoutTimer_) { - clearTimeout(this.layoutTimer_); - this.layoutTimer_ = null; - } - while (true) { - var index = this.layoutModel_.getTileCount(); - if (index === this.tiles_.length) - break; // All tiles done. - var tile = this.tiles_[index]; - if (!tile.isInitialized()) - break; // Next layout will try to restart from here. - this.layoutModel_.add(tile, index + 1 === this.tiles_.length); - } - this.loadVisibleTiles_(); -}; - -/** - * Schedules the layout. - * - * @param {number=} opt_delay Delay in ms. - */ -Mosaic.prototype.scheduleLayout = function(opt_delay) { - if (!this.layoutTimer_) { - this.layoutTimer_ = setTimeout(function() { - this.layoutTimer_ = null; - this.layout(); - }.bind(this), opt_delay || 0); - } -}; - -/** - * Resize handler. - * - * @private - */ -Mosaic.prototype.onResize_ = function() { - this.layoutModel_.setViewportSize(this.clientWidth, this.clientHeight - - (Mosaic.Layout.PADDING_TOP + Mosaic.Layout.PADDING_BOTTOM)); - this.scheduleLayout(); -}; - -/** - * Mouse event handler. - * - * @param {Event} event Event. - * @private - */ -Mosaic.prototype.onMouseEvent_ = function(event) { - // Navigating with mouse, enable hover state. - if (!this.suppressHovering_) - this.classList.add('hover-visible'); - - if (event.type === 'mousemove') - return; - - var index = -1; - for (var target = event.target; - target && (target !== this); - target = target.parentNode) { - if (target.classList.contains('mosaic-tile')) { - index = this.dataModel_.indexOf(target.getItem()); - break; - } - } - this.selectionController_.handlePointerDownUp(event, index); -}; - -/** - * Scroll handler. - * @private - */ -Mosaic.prototype.onScroll_ = function() { - requestAnimationFrame(function() { - this.loadVisibleTiles_(); - }.bind(this)); -}; - -/** - * Selection change handler. - * - * @param {Event} event Event. - * @private - */ -Mosaic.prototype.onSelection_ = function(event) { - for (var i = 0; i !== event.changes.length; i++) { - var change = event.changes[i]; - var tile = this.tiles_[change.index]; - if (tile) tile.select(change.selected); - } -}; - -/** - * Leads item change handler. - * - * @param {Event} event Event. - * @private - */ -Mosaic.prototype.onLeadChange_ = function(event) { - var index = event.newValue; - if (index >= 0) { - var tile = this.tiles_[index]; - if (tile) tile.scrollIntoView(); - } -}; - -/** - * Splice event handler. - * - * @param {Event} event Event. - * @private - */ -Mosaic.prototype.onSplice_ = function(event) { - var index = event.index; - this.layoutModel_.invalidateFromTile_(index); - - if (event.removed.length) { - for (var t = 0; t !== event.removed.length; t++) { - // If the layout for the tile has not done yet, the parent is null. - // And the layout will not be done after onSplice_ because it is removed - // from this.tiles_. - if (this.tiles_[index + t].parentNode) - this.removeChild(this.tiles_[index + t]); - } - - this.tiles_.splice(index, event.removed.length); - this.scheduleLayout(Mosaic.LAYOUT_DELAY); - } - - if (event.added.length) { - var newTiles = []; - for (var t = 0; t !== event.added.length; t++) - newTiles.push(new Mosaic.Tile(this, this.dataModel_.item(index + t))); - - this.tiles_.splice.apply(this.tiles_, [index, 0].concat(newTiles)); - this.initTiles_(newTiles); - } - - if (this.tiles_.length !== this.dataModel_.length) - console.error('Mosaic is out of sync'); -}; - -/** - * Content change handler. - * - * @param {Event} event Event. - * @private - */ -Mosaic.prototype.onContentChange_ = function(event) { - if (!this.tiles_) - return; - - if (!event.metadata) - return; // Thumbnail unchanged, nothing to do. - - var index = this.dataModel_.indexOf(event.item); - if (index !== this.selectionModel_.selectedIndex) - console.error('Content changed for unselected item'); - - this.layoutModel_.invalidateFromTile_(index); - this.tiles_[index].init(event.metadata, function() { - this.tiles_[index].unload(); - this.tiles_[index].load( - Mosaic.Tile.LoadMode.HIGH_DPI, - this.scheduleLayout.bind(this, Mosaic.LAYOUT_DELAY)); - }.bind(this)); -}; - -/** - * Keydown event handler. - * - * @param {Event} event Event. - * @return {boolean} True if the event has been consumed. - */ -Mosaic.prototype.onKeyDown = function(event) { - this.selectionController_.handleKeyDown(event); - if (event.defaultPrevented) // Navigating with keyboard, hide hover state. - this.classList.remove('hover-visible'); - return event.defaultPrevented; -}; - -/** - * @return {boolean} True if the mosaic zoom effect can be applied. It is - * too slow if there are to many images. - * TODO(kaznacheev): Consider unloading the images that are out of the viewport. - */ -Mosaic.prototype.canZoom = function() { - return this.tiles_.length < 100; -}; - -/** - * Shows the mosaic. - */ -Mosaic.prototype.show = function() { - var duration = ImageView.MODE_TRANSITION_DURATION; - if (this.canZoom()) { - // Fade in in parallel with the zoom effect. - this.setAttribute('visible', 'zooming'); - } else { - // Mosaic is not animating but the large image is. Fade in the mosaic - // shortly before the large image animation is done. - duration -= 100; - } - this.showingTimeoutID_ = setTimeout(function() { - this.showingTimeoutID_ = null; - // Make the selection visible. - // If the mosaic is not animated it will start fading in now. - this.setAttribute('visible', 'normal'); - this.loadVisibleTiles_(); - }.bind(this), duration); -}; - -/** - * Hides the mosaic. - */ -Mosaic.prototype.hide = function() { - if (this.showingTimeoutID_ !== null) { - clearTimeout(this.showingTimeoutID_); - this.showingTimeoutID_ = null; - } - this.removeAttribute('visible'); -}; - -/** - * Checks if the mosaic view is visible. - * @return {boolean} True if visible, false otherwise. - * @private - */ -Mosaic.prototype.isVisible_ = function() { - return this.hasAttribute('visible'); -}; - -/** - * Loads visible tiles. Ignores consecutive calls. Does not reload already - * loaded images. - * @private - */ -Mosaic.prototype.loadVisibleTiles_ = function() { - if (this.loadVisibleTilesSuppressed_) { - this.loadVisibleTilesScheduled_ = true; - return; - } - - this.loadVisibleTilesSuppressed_ = true; - this.loadVisibleTilesScheduled_ = false; - setTimeout(function() { - this.loadVisibleTilesSuppressed_ = false; - if (this.loadVisibleTilesScheduled_) - this.loadVisibleTiles_(); - }.bind(this), 100); - - // Tiles only in the viewport (visible). - var visibleRect = new Rect(0, - 0, - this.clientWidth, - this.clientHeight); - - // Tiles in the viewport and also some distance on the left and right. - var renderableRect = new Rect(-this.clientWidth, - 0, - 3 * this.clientWidth, - this.clientHeight); - - // Unload tiles out of scope. - for (var index = 0; index < this.tiles_.length; index++) { - var tile = this.tiles_[index]; - var imageRect = tile.getImageRect(); - // Unload a thumbnail. - if (imageRect && !imageRect.intersects(renderableRect)) - tile.unload(); - } - - // Load the visible tiles first. - var allVisibleLoaded = true; - // Show high-dpi only when the mosaic view is visible. - var loadMode = this.isVisible_() ? Mosaic.Tile.LoadMode.HIGH_DPI : - Mosaic.Tile.LoadMode.LOW_DPI; - for (var index = 0; index < this.tiles_.length; index++) { - var tile = this.tiles_[index]; - var imageRect = tile.getImageRect(); - // Load a thumbnail. - if (!tile.isLoading(loadMode) && !tile.isLoaded(loadMode) && imageRect && - imageRect.intersects(visibleRect)) { - tile.load(loadMode, function() {}); - allVisibleLoaded = false; - } - } - - // Load also another, nearby, if the visible has been already loaded. - if (allVisibleLoaded) { - for (var index = 0; index < this.tiles_.length; index++) { - var tile = this.tiles_[index]; - var imageRect = tile.getImageRect(); - // Load a thumbnail. - if (!tile.isLoading() && !tile.isLoaded() && imageRect && - imageRect.intersects(renderableRect)) { - tile.load(Mosaic.Tile.LoadMode.LOW_DPI, function() {}); - } - } - } -}; - -/** - * Applies reset the zoom transform. - * - * @param {Rect} tileRect Tile rectangle. Reset the transform if null. - * @param {Rect} imageRect Large image rectangle. Reset the transform if null. - * @param {boolean=} opt_instant True of the transition should be instant. - */ -Mosaic.prototype.transform = function(tileRect, imageRect, opt_instant) { - if (opt_instant) { - this.style.webkitTransitionDuration = '0'; - } else { - this.style.webkitTransitionDuration = - ImageView.MODE_TRANSITION_DURATION + 'ms'; - } - - if (this.canZoom() && tileRect && imageRect) { - var scaleX = imageRect.width / tileRect.width; - var scaleY = imageRect.height / tileRect.height; - var shiftX = (imageRect.left + imageRect.width / 2) - - (tileRect.left + tileRect.width / 2); - var shiftY = (imageRect.top + imageRect.height / 2) - - (tileRect.top + tileRect.height / 2); - this.style.webkitTransform = - 'translate(' + shiftX * scaleX + 'px, ' + shiftY * scaleY + 'px)' + - 'scaleX(' + scaleX + ') scaleY(' + scaleY + ')'; - } else { - this.style.webkitTransform = ''; - } -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * Creates a selection controller that is to be used with grid. - * @param {cr.ui.ListSelectionModel} selectionModel The selection model to - * interact with. - * @param {Mosaic.Layout} layoutModel The layout model to use. - * @constructor - * @extends {!cr.ui.ListSelectionController} - */ -Mosaic.SelectionController = function(selectionModel, layoutModel) { - cr.ui.ListSelectionController.call(this, selectionModel); - this.layoutModel_ = layoutModel; -}; - -/** - * Extends cr.ui.ListSelectionController. - */ -Mosaic.SelectionController.prototype.__proto__ = - cr.ui.ListSelectionController.prototype; - -/** @override */ -Mosaic.SelectionController.prototype.getLastIndex = function() { - return this.layoutModel_.getLaidOutTileCount() - 1; -}; - -/** @override */ -Mosaic.SelectionController.prototype.getIndexBefore = function(index) { - return this.layoutModel_.getHorizontalAdjacentIndex(index, -1); -}; - -/** @override */ -Mosaic.SelectionController.prototype.getIndexAfter = function(index) { - return this.layoutModel_.getHorizontalAdjacentIndex(index, 1); -}; - -/** @override */ -Mosaic.SelectionController.prototype.getIndexAbove = function(index) { - return this.layoutModel_.getVerticalAdjacentIndex(index, -1); -}; - -/** @override */ -Mosaic.SelectionController.prototype.getIndexBelow = function(index) { - return this.layoutModel_.getVerticalAdjacentIndex(index, 1); -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * Mosaic layout. - * - * @param {string=} opt_mode Layout mode. - * @param {Mosaic.Density=} opt_maxDensity Layout density. - * @constructor - */ -Mosaic.Layout = function(opt_mode, opt_maxDensity) { - this.mode_ = opt_mode || Mosaic.Layout.MODE_TENTATIVE; - this.maxDensity_ = opt_maxDensity || Mosaic.Density.createHighest(); - this.reset_(); -}; - -/** - * Blank space at the top of the mosaic element. We do not do that in CSS - * to make transition effects easier. - */ -Mosaic.Layout.PADDING_TOP = 50; - -/** - * Blank space at the bottom of the mosaic element. - */ -Mosaic.Layout.PADDING_BOTTOM = 50; - -/** - * Horizontal and vertical spacing between images. Should be kept in sync - * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) - */ -Mosaic.Layout.SPACING = 10; - -/** - * Margin for scrolling using keyboard. Distance between a selected tile - * and window border. - */ -Mosaic.Layout.SCROLL_MARGIN = 30; - -/** - * Layout mode: commit to DOM immediately. - */ -Mosaic.Layout.MODE_FINAL = 'final'; - -/** - * Layout mode: do not commit layout to DOM until it is complete or the viewport - * overflows. - */ -Mosaic.Layout.MODE_TENTATIVE = 'tentative'; - -/** - * Layout mode: never commit layout to DOM. - */ -Mosaic.Layout.MODE_DRY_RUN = 'dry_run'; - -/** - * Resets the layout. - * - * @private - */ -Mosaic.Layout.prototype.reset_ = function() { - this.columns_ = []; - this.newColumn_ = null; - this.density_ = Mosaic.Density.createLowest(); - if (this.mode_ !== Mosaic.Layout.MODE_DRY_RUN) // DRY_RUN is sticky. - this.mode_ = Mosaic.Layout.MODE_TENTATIVE; -}; - -/** - * @param {number} width Viewport width. - * @param {number} height Viewport height. - */ -Mosaic.Layout.prototype.setViewportSize = function(width, height) { - this.viewportWidth_ = width; - this.viewportHeight_ = height; - this.reset_(); -}; - -/** - * @return {number} Total width of the layout. - */ -Mosaic.Layout.prototype.getWidth = function() { - var lastColumn = this.getLastColumn_(); - return lastColumn ? lastColumn.getRight() : 0; -}; - -/** - * @return {number} Total height of the layout. - */ -Mosaic.Layout.prototype.getHeight = function() { - var firstColumn = this.columns_[0]; - return firstColumn ? firstColumn.getHeight() : 0; -}; - -/** - * @return {Array.<Mosaic.Tile>} All tiles in the layout. - */ -Mosaic.Layout.prototype.getTiles = function() { - return Array.prototype.concat.apply([], - this.columns_.map(function(c) { return c.getTiles() })); -}; - -/** - * @return {number} Total number of tiles added to the layout. - */ -Mosaic.Layout.prototype.getTileCount = function() { - return this.getLaidOutTileCount() + - (this.newColumn_ ? this.newColumn_.getTileCount() : 0); -}; - -/** - * @return {Mosaic.Column} The last column or null for empty layout. - * @private - */ -Mosaic.Layout.prototype.getLastColumn_ = function() { - return this.columns_.length ? this.columns_[this.columns_.length - 1] : null; -}; - -/** - * @return {number} Total number of tiles in completed columns. - */ -Mosaic.Layout.prototype.getLaidOutTileCount = function() { - var lastColumn = this.getLastColumn_(); - return lastColumn ? lastColumn.getNextTileIndex() : 0; -}; - -/** - * Adds a tile to the layout. - * - * @param {Mosaic.Tile} tile The tile to be added. - * @param {boolean} isLast True if this tile is the last. - */ -Mosaic.Layout.prototype.add = function(tile, isLast) { - var layoutQueue = [tile]; - - // There are two levels of backtracking in the layout algorithm. - // |Mosaic.Layout.density_| tracks the state of the 'global' backtracking - // which aims to use as much of the viewport space as possible. - // It starts with the lowest density and increases it until the layout - // fits into the viewport. If it does not fit even at the highest density, - // the layout continues with the highest density. - // - // |Mosaic.Column.density_| tracks the state of the 'local' backtracking - // which aims to avoid producing unnaturally looking columns. - // It starts with the current global density and decreases it until the column - // looks nice. - - while (layoutQueue.length) { - if (!this.newColumn_) { - var lastColumn = this.getLastColumn_(); - this.newColumn_ = new Mosaic.Column( - this.columns_.length, - lastColumn ? lastColumn.getNextRowIndex() : 0, - lastColumn ? lastColumn.getNextTileIndex() : 0, - lastColumn ? lastColumn.getRight() : 0, - this.viewportHeight_, - this.density_.clone()); - } - - this.newColumn_.add(layoutQueue.shift()); - - var isFinalColumn = isLast && !layoutQueue.length; - - if (!this.newColumn_.prepareLayout(isFinalColumn)) - continue; // Column is incomplete. - - if (this.newColumn_.isSuboptimal()) { - layoutQueue = this.newColumn_.getTiles().concat(layoutQueue); - this.newColumn_.retryWithLowerDensity(); - continue; - } - - this.columns_.push(this.newColumn_); - this.newColumn_ = null; - - if (this.mode_ === Mosaic.Layout.MODE_FINAL) { - this.getLastColumn_().layout(); - continue; - } - - if (this.getWidth() > this.viewportWidth_) { - // Viewport completely filled. - if (this.density_.equals(this.maxDensity_)) { - // Max density reached, commit if tentative, just continue if dry run. - if (this.mode_ === Mosaic.Layout.MODE_TENTATIVE) - this.commit_(); - continue; - } - - // Rollback the entire layout, retry with higher density. - layoutQueue = this.getTiles().concat(layoutQueue); - this.columns_ = []; - this.density_.increase(); - continue; - } - - if (isFinalColumn && this.mode_ === Mosaic.Layout.MODE_TENTATIVE) { - // The complete tentative layout fits into the viewport. - var stretched = this.findHorizontalLayout_(); - if (stretched) - this.columns_ = stretched.columns_; - // Center the layout in the viewport and commit. - this.commit_((this.viewportWidth_ - this.getWidth()) / 2, - (this.viewportHeight_ - this.getHeight()) / 2); - } - } -}; - -/** - * Commits the tentative layout. - * - * @param {number=} opt_offsetX Horizontal offset. - * @param {number=} opt_offsetY Vertical offset. - * @private - */ -Mosaic.Layout.prototype.commit_ = function(opt_offsetX, opt_offsetY) { - console.assert(this.mode_ !== Mosaic.Layout.MODE_FINAL, - 'Did not expect final layout'); - for (var i = 0; i !== this.columns_.length; i++) { - this.columns_[i].layout(opt_offsetX, opt_offsetY); - } - this.mode_ = Mosaic.Layout.MODE_FINAL; -}; - -/** - * Finds the most horizontally stretched layout built from the same tiles. - * - * The main layout algorithm fills the entire available viewport height. - * If there is too few tiles this results in a layout that is unnaturally - * stretched in the vertical direction. - * - * This method tries a number of smaller heights and returns the most - * horizontally stretched layout that still fits into the viewport. - * - * @return {Mosaic.Layout} A horizontally stretched layout. - * @private - */ -Mosaic.Layout.prototype.findHorizontalLayout_ = function() { - // If the layout aspect ratio is not dramatically different from - // the viewport aspect ratio then there is no need to optimize. - if (this.getWidth() / this.getHeight() > - this.viewportWidth_ / this.viewportHeight_ * 0.9) - return null; - - var tiles = this.getTiles(); - if (tiles.length === 1) - return null; // Single tile layout is always the same. - - var tileHeights = tiles.map(function(t) { return t.getMaxContentHeight() }); - var minTileHeight = Math.min.apply(null, tileHeights); - - for (var h = minTileHeight; h < this.viewportHeight_; h += minTileHeight) { - var layout = new Mosaic.Layout( - Mosaic.Layout.MODE_DRY_RUN, this.density_.clone()); - layout.setViewportSize(this.viewportWidth_, h); - for (var t = 0; t !== tiles.length; t++) - layout.add(tiles[t], t + 1 === tiles.length); - - if (layout.getWidth() <= this.viewportWidth_) - return layout; - } - - return null; -}; - -/** - * Invalidates the layout after the given tile was modified (added, deleted or - * changed dimensions). - * - * @param {number} index Tile index. - * @private - */ -Mosaic.Layout.prototype.invalidateFromTile_ = function(index) { - var columnIndex = this.getColumnIndexByTile_(index); - if (columnIndex < 0) - return; // Index not in the layout, probably already invalidated. - - if (this.columns_[columnIndex].getLeft() >= this.viewportWidth_) { - // The columns to the right cover the entire viewport width, so there is no - // chance that the modified layout would fit into the viewport. - // No point in restarting the entire layout, keep the columns to the right. - console.assert(this.mode_ === Mosaic.Layout.MODE_FINAL, - 'Expected FINAL layout mode'); - this.columns_ = this.columns_.slice(0, columnIndex); - this.newColumn_ = null; - } else { - // There is a chance that the modified layout would fit into the viewport. - this.reset_(); - this.mode_ = Mosaic.Layout.MODE_TENTATIVE; - } -}; - -/** - * Gets the index of the tile to the left or to the right from the given tile. - * - * @param {number} index Tile index. - * @param {number} direction -1 for left, 1 for right. - * @return {number} Adjacent tile index. - */ -Mosaic.Layout.prototype.getHorizontalAdjacentIndex = function( - index, direction) { - var column = this.getColumnIndexByTile_(index); - if (column < 0) { - console.error('Cannot find column for tile #' + index); - return -1; - } - - var row = this.columns_[column].getRowByTileIndex(index); - if (!row) { - console.error('Cannot find row for tile #' + index); - return -1; - } - - var sameRowNeighbourIndex = index + direction; - if (row.hasTile(sameRowNeighbourIndex)) - return sameRowNeighbourIndex; - - var adjacentColumn = column + direction; - if (adjacentColumn < 0 || adjacentColumn === this.columns_.length) - return -1; - - return this.columns_[adjacentColumn]. - getEdgeTileIndex_(row.getCenterY(), -direction); -}; - -/** - * Gets the index of the tile to the top or to the bottom from the given tile. - * - * @param {number} index Tile index. - * @param {number} direction -1 for above, 1 for below. - * @return {number} Adjacent tile index. - */ -Mosaic.Layout.prototype.getVerticalAdjacentIndex = function( - index, direction) { - var column = this.getColumnIndexByTile_(index); - if (column < 0) { - console.error('Cannot find column for tile #' + index); - return -1; - } - - var row = this.columns_[column].getRowByTileIndex(index); - if (!row) { - console.error('Cannot find row for tile #' + index); - return -1; - } - - // Find the first item in the next row, or the last item in the previous row. - var adjacentRowNeighbourIndex = - row.getEdgeTileIndex_(direction) + direction; - - if (adjacentRowNeighbourIndex < 0 || - adjacentRowNeighbourIndex > this.getTileCount() - 1) - return -1; - - if (!this.columns_[column].hasTile(adjacentRowNeighbourIndex)) { - // It is not in the current column, so return it. - return adjacentRowNeighbourIndex; - } else { - // It is in the current column, so we have to find optically the closest - // tile in the adjacent row. - var adjacentRow = this.columns_[column].getRowByTileIndex( - adjacentRowNeighbourIndex); - var previousTileCenterX = row.getTileByIndex(index).getCenterX(); - - // Find the closest one. - var closestIndex = -1; - var closestDistance; - var adjacentRowTiles = adjacentRow.getTiles(); - for (var t = 0; t !== adjacentRowTiles.length; t++) { - var distance = - Math.abs(adjacentRowTiles[t].getCenterX() - previousTileCenterX); - if (closestIndex === -1 || distance < closestDistance) { - closestIndex = adjacentRow.getEdgeTileIndex_(-1) + t; - closestDistance = distance; - } - } - return closestIndex; - } -}; - -/** - * @param {number} index Tile index. - * @return {number} Index of the column containing the given tile. - * @private - */ -Mosaic.Layout.prototype.getColumnIndexByTile_ = function(index) { - for (var c = 0; c !== this.columns_.length; c++) { - if (this.columns_[c].hasTile(index)) - return c; - } - return -1; -}; - -/** - * Scales the given array of size values to satisfy 3 conditions: - * 1. The new sizes must be integer. - * 2. The new sizes must sum up to the given |total| value. - * 3. The relative proportions of the sizes should be as close to the original - * as possible. - * - * @param {Array.<number>} sizes Array of sizes. - * @param {number} newTotal New total size. - */ -Mosaic.Layout.rescaleSizesToNewTotal = function(sizes, newTotal) { - var total = 0; - - var partialTotals = [0]; - for (var i = 0; i !== sizes.length; i++) { - total += sizes[i]; - partialTotals.push(total); - } - - var scale = newTotal / total; - - for (i = 0; i !== sizes.length; i++) { - sizes[i] = Math.round(partialTotals[i + 1] * scale) - - Math.round(partialTotals[i] * scale); - } -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * Representation of the layout density. - * - * @param {number} horizontal Horizontal density, number tiles per row. - * @param {number} vertical Vertical density, frequency of rows forced to - * contain a single tile. - * @constructor - */ -Mosaic.Density = function(horizontal, vertical) { - this.horizontal = horizontal; - this.vertical = vertical; -}; - -/** - * Minimal horizontal density (tiles per row). - */ -Mosaic.Density.MIN_HORIZONTAL = 1; - -/** - * Minimal horizontal density (tiles per row). - */ -Mosaic.Density.MAX_HORIZONTAL = 3; - -/** - * Minimal vertical density: force 1 out of 2 rows to containt a single tile. - */ -Mosaic.Density.MIN_VERTICAL = 2; - -/** - * Maximal vertical density: force 1 out of 3 rows to containt a single tile. - */ -Mosaic.Density.MAX_VERTICAL = 3; - -/** - * @return {Mosaic.Density} Lowest density. - */ -Mosaic.Density.createLowest = function() { - return new Mosaic.Density( - Mosaic.Density.MIN_HORIZONTAL, - Mosaic.Density.MIN_VERTICAL /* ignored when horizontal is at min */); -}; - -/** - * @return {Mosaic.Density} Highest density. - */ -Mosaic.Density.createHighest = function() { - return new Mosaic.Density( - Mosaic.Density.MAX_HORIZONTAL, - Mosaic.Density.MAX_VERTICAL); -}; - -/** - * @return {Mosaic.Density} A clone of this density object. - */ -Mosaic.Density.prototype.clone = function() { - return new Mosaic.Density(this.horizontal, this.vertical); -}; - -/** - * @param {Mosaic.Density} that The other object. - * @return {boolean} True if equal. - */ -Mosaic.Density.prototype.equals = function(that) { - return this.horizontal === that.horizontal && - this.vertical === that.vertical; -}; - -/** - * Increases the density to the next level. - */ -Mosaic.Density.prototype.increase = function() { - if (this.horizontal === Mosaic.Density.MIN_HORIZONTAL || - this.vertical === Mosaic.Density.MAX_VERTICAL) { - console.assert(this.horizontal < Mosaic.Density.MAX_HORIZONTAL); - this.horizontal++; - this.vertical = Mosaic.Density.MIN_VERTICAL; - } else { - this.vertical++; - } -}; - -/** - * Decreases horizontal density. - */ -Mosaic.Density.prototype.decreaseHorizontal = function() { - console.assert(this.horizontal > Mosaic.Density.MIN_HORIZONTAL); - this.horizontal--; -}; - -/** - * @param {number} tileCount Number of tiles in the row. - * @param {number} rowIndex Global row index. - * @return {boolean} True if the row is complete. - */ -Mosaic.Density.prototype.isRowComplete = function(tileCount, rowIndex) { - return (tileCount === this.horizontal) || (rowIndex % this.vertical) === 0; -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * A column in a mosaic layout. Contains rows. - * - * @param {number} index Column index. - * @param {number} firstRowIndex Global row index. - * @param {number} firstTileIndex Index of the first tile in the column. - * @param {number} left Left edge coordinate. - * @param {number} maxHeight Maximum height. - * @param {Mosaic.Density} density Layout density. - * @constructor - */ -Mosaic.Column = function(index, firstRowIndex, firstTileIndex, left, maxHeight, - density) { - this.index_ = index; - this.firstRowIndex_ = firstRowIndex; - this.firstTileIndex_ = firstTileIndex; - this.left_ = left; - this.maxHeight_ = maxHeight; - this.density_ = density; - - this.reset_(); -}; - -/** - * Resets the layout. - * @private - */ -Mosaic.Column.prototype.reset_ = function() { - this.tiles_ = []; - this.rows_ = []; - this.newRow_ = null; -}; - -/** - * @return {number} Number of tiles in the column. - */ -Mosaic.Column.prototype.getTileCount = function() { return this.tiles_.length }; - -/** - * @return {number} Index of the last tile + 1. - */ -Mosaic.Column.prototype.getNextTileIndex = function() { - return this.firstTileIndex_ + this.getTileCount(); -}; - -/** - * @return {number} Global index of the last row + 1. - */ -Mosaic.Column.prototype.getNextRowIndex = function() { - return this.firstRowIndex_ + this.rows_.length; -}; - -/** - * @return {Array.<Mosaic.Tile>} Array of tiles in the column. - */ -Mosaic.Column.prototype.getTiles = function() { return this.tiles_ }; - -/** - * @param {number} index Tile index. - * @return {boolean} True if this column contains the tile with the given index. - */ -Mosaic.Column.prototype.hasTile = function(index) { - return this.firstTileIndex_ <= index && - index < (this.firstTileIndex_ + this.getTileCount()); -}; - -/** - * @param {number} y Y coordinate. - * @param {number} direction -1 for left, 1 for right. - * @return {number} Index of the tile lying on the edge of the column at the - * given y coordinate. - * @private - */ -Mosaic.Column.prototype.getEdgeTileIndex_ = function(y, direction) { - for (var r = 0; r < this.rows_.length; r++) { - if (this.rows_[r].coversY(y)) - return this.rows_[r].getEdgeTileIndex_(direction); - } - return -1; -}; - -/** - * @param {number} index Tile index. - * @return {Mosaic.Row} The row containing the tile with a given index. - */ -Mosaic.Column.prototype.getRowByTileIndex = function(index) { - for (var r = 0; r !== this.rows_.length; r++) - if (this.rows_[r].hasTile(index)) - return this.rows_[r]; - - return null; -}; - -/** - * Adds a tile to the column. - * - * @param {Mosaic.Tile} tile The tile to add. - */ -Mosaic.Column.prototype.add = function(tile) { - var rowIndex = this.getNextRowIndex(); - - if (!this.newRow_) - this.newRow_ = new Mosaic.Row(this.getNextTileIndex()); - - this.tiles_.push(tile); - this.newRow_.add(tile); - - if (this.density_.isRowComplete(this.newRow_.getTileCount(), rowIndex)) { - this.rows_.push(this.newRow_); - this.newRow_ = null; - } -}; - -/** - * Prepares the column layout. - * - * @param {boolean=} opt_force True if the layout must be performed even for an - * incomplete column. - * @return {boolean} True if the layout was performed. - */ -Mosaic.Column.prototype.prepareLayout = function(opt_force) { - if (opt_force && this.newRow_) { - this.rows_.push(this.newRow_); - this.newRow_ = null; - } - - if (this.rows_.length === 0) - return false; - - this.width_ = Math.min.apply( - null, this.rows_.map(function(row) { return row.getMaxWidth() })); - - this.height_ = 0; - - this.rowHeights_ = []; - for (var r = 0; r !== this.rows_.length; r++) { - var rowHeight = this.rows_[r].getHeightForWidth(this.width_); - this.height_ += rowHeight; - this.rowHeights_.push(rowHeight); - } - - var overflow = this.height_ / this.maxHeight_; - if (!opt_force && (overflow < 1)) - return false; - - if (overflow > 1) { - // Scale down the column width and height. - this.width_ = Math.round(this.width_ / overflow); - this.height_ = this.maxHeight_; - Mosaic.Layout.rescaleSizesToNewTotal(this.rowHeights_, this.maxHeight_); - } - - return true; -}; - -/** - * Retries the column layout with less tiles per row. - */ -Mosaic.Column.prototype.retryWithLowerDensity = function() { - this.density_.decreaseHorizontal(); - this.reset_(); -}; - -/** - * @return {number} Column left edge coordinate. - */ -Mosaic.Column.prototype.getLeft = function() { return this.left_ }; - -/** - * @return {number} Column right edge coordinate after the layout. - */ -Mosaic.Column.prototype.getRight = function() { - return this.left_ + this.width_; -}; - -/** - * @return {number} Column height after the layout. - */ -Mosaic.Column.prototype.getHeight = function() { return this.height_ }; - -/** - * Performs the column layout. - * @param {number=} opt_offsetX Horizontal offset. - * @param {number=} opt_offsetY Vertical offset. - */ -Mosaic.Column.prototype.layout = function(opt_offsetX, opt_offsetY) { - opt_offsetX = opt_offsetX || 0; - opt_offsetY = opt_offsetY || 0; - var rowTop = Mosaic.Layout.PADDING_TOP; - for (var r = 0; r !== this.rows_.length; r++) { - this.rows_[r].layout( - opt_offsetX + this.left_, - opt_offsetY + rowTop, - this.width_, - this.rowHeights_[r]); - rowTop += this.rowHeights_[r]; - } -}; - -/** - * Checks if the column layout is too ugly to be displayed. - * - * @return {boolean} True if the layout is suboptimal. - */ -Mosaic.Column.prototype.isSuboptimal = function() { - var tileCounts = - this.rows_.map(function(row) { return row.getTileCount() }); - - var maxTileCount = Math.max.apply(null, tileCounts); - if (maxTileCount === 1) - return false; // Every row has exactly 1 tile, as optimal as it gets. - - var sizes = - this.tiles_.map(function(tile) { return tile.getMaxContentHeight() }); - - // Ugly layout #1: all images are small and some are one the same row. - var allSmall = Math.max.apply(null, sizes) <= Mosaic.Tile.SMALL_IMAGE_SIZE; - if (allSmall) - return true; - - // Ugly layout #2: all images are large and none occupies an entire row. - var allLarge = Math.min.apply(null, sizes) > Mosaic.Tile.SMALL_IMAGE_SIZE; - var allCombined = Math.min.apply(null, tileCounts) !== 1; - if (allLarge && allCombined) - return true; - - // Ugly layout #3: some rows have too many tiles for the resulting width. - if (this.width_ / maxTileCount < 100) - return true; - - return false; -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * A row in a mosaic layout. Contains tiles. - * - * @param {number} firstTileIndex Index of the first tile in the row. - * @constructor - */ -Mosaic.Row = function(firstTileIndex) { - this.firstTileIndex_ = firstTileIndex; - this.tiles_ = []; -}; - -/** - * @param {Mosaic.Tile} tile The tile to add. - */ -Mosaic.Row.prototype.add = function(tile) { - console.assert(this.getTileCount() < Mosaic.Density.MAX_HORIZONTAL); - this.tiles_.push(tile); -}; - -/** - * @return {Array.<Mosaic.Tile>} Array of tiles in the row. - */ -Mosaic.Row.prototype.getTiles = function() { return this.tiles_ }; - -/** - * Gets a tile by index. - * @param {number} index Tile index. - * @return {Mosaic.Tile} Requested tile or null if not found. - */ -Mosaic.Row.prototype.getTileByIndex = function(index) { - if (!this.hasTile(index)) - return null; - return this.tiles_[index - this.firstTileIndex_]; -}; - -/** - * - * @return {number} Number of tiles in the row. - */ -Mosaic.Row.prototype.getTileCount = function() { return this.tiles_.length }; - -/** - * @param {number} index Tile index. - * @return {boolean} True if this row contains the tile with the given index. - */ -Mosaic.Row.prototype.hasTile = function(index) { - return this.firstTileIndex_ <= index && - index < (this.firstTileIndex_ + this.tiles_.length); -}; - -/** - * @param {number} y Y coordinate. - * @return {boolean} True if this row covers the given Y coordinate. - */ -Mosaic.Row.prototype.coversY = function(y) { - return this.top_ <= y && y < (this.top_ + this.height_); -}; - -/** - * @return {number} Y coordinate of the tile center. - */ -Mosaic.Row.prototype.getCenterY = function() { - return this.top_ + Math.round(this.height_ / 2); -}; - -/** - * Gets the first or the last tile. - * - * @param {number} direction -1 for the first tile, 1 for the last tile. - * @return {number} Tile index. - * @private - */ -Mosaic.Row.prototype.getEdgeTileIndex_ = function(direction) { - if (direction < 0) - return this.firstTileIndex_; - else - return this.firstTileIndex_ + this.getTileCount() - 1; -}; - -/** - * @return {number} Aspect ration of the combined content box of this row. - * @private - */ -Mosaic.Row.prototype.getTotalContentAspectRatio_ = function() { - var sum = 0; - for (var t = 0; t !== this.tiles_.length; t++) - sum += this.tiles_[t].getAspectRatio(); - return sum; -}; - -/** - * @return {number} Total horizontal spacing in this row. This includes - * the spacing between the tiles and both left and right margins. - * - * @private - */ -Mosaic.Row.prototype.getTotalHorizontalSpacing_ = function() { - return Mosaic.Layout.SPACING * this.getTileCount(); -}; - -/** - * @return {number} Maximum width that this row may have without overscaling - * any of the tiles. - */ -Mosaic.Row.prototype.getMaxWidth = function() { - var contentHeight = Math.min.apply(null, - this.tiles_.map(function(tile) { return tile.getMaxContentHeight() })); - - var contentWidth = - Math.round(contentHeight * this.getTotalContentAspectRatio_()); - return contentWidth + this.getTotalHorizontalSpacing_(); -}; - -/** - * Computes the height that best fits the supplied row width given - * aspect ratios of the tiles in this row. - * - * @param {number} width Row width. - * @return {number} Height. - */ -Mosaic.Row.prototype.getHeightForWidth = function(width) { - var contentWidth = width - this.getTotalHorizontalSpacing_(); - var contentHeight = - Math.round(contentWidth / this.getTotalContentAspectRatio_()); - return contentHeight + Mosaic.Layout.SPACING; -}; - -/** - * Positions the row in the mosaic. - * - * @param {number} left Left position. - * @param {number} top Top position. - * @param {number} width Width. - * @param {number} height Height. - */ -Mosaic.Row.prototype.layout = function(left, top, width, height) { - this.top_ = top; - this.height_ = height; - - var contentWidth = width - this.getTotalHorizontalSpacing_(); - var contentHeight = height - Mosaic.Layout.SPACING; - - var tileContentWidth = this.tiles_.map( - function(tile) { return tile.getAspectRatio() }); - - Mosaic.Layout.rescaleSizesToNewTotal(tileContentWidth, contentWidth); - - var tileLeft = left; - for (var t = 0; t !== this.tiles_.length; t++) { - var tileWidth = tileContentWidth[t] + Mosaic.Layout.SPACING; - this.tiles_[t].layout(tileLeft, top, tileWidth, height); - tileLeft += tileWidth; - } -}; - -//////////////////////////////////////////////////////////////////////////////// - -/** - * A single tile of the image mosaic. - * - * @param {Element} container Container element. - * @param {Gallery.Item} item Gallery item associated with this tile. - * @param {EntryLocation} locationInfo Location information for the tile. - * @return {Element} The new tile element. - * @constructor - */ -Mosaic.Tile = function(container, item, locationInfo) { - var self = container.ownerDocument.createElement('div'); - Mosaic.Tile.decorate(self, container, item, locationInfo); - return self; -}; - -/** - * @param {Element} self Self pointer. - * @param {Element} container Container element. - * @param {Gallery.Item} item Gallery item associated with this tile. - * @param {EntryLocation} locationInfo Location info for the tile image. - */ -Mosaic.Tile.decorate = function(self, container, item, locationInfo) { - self.__proto__ = Mosaic.Tile.prototype; - self.className = 'mosaic-tile'; - - self.container_ = container; - self.item_ = item; - self.left_ = null; // Mark as not laid out. - self.hidpiEmbedded_ = locationInfo && locationInfo.isDriveBased; -}; - -/** - * Load mode for the tile's image. - * @enum {number} - */ -Mosaic.Tile.LoadMode = { - LOW_DPI: 0, - HIGH_DPI: 1 -}; - -/** -* Inherit from HTMLDivElement. -*/ -Mosaic.Tile.prototype.__proto__ = HTMLDivElement.prototype; - -/** - * Minimum tile content size. - */ -Mosaic.Tile.MIN_CONTENT_SIZE = 64; - -/** - * Maximum tile content size. - */ -Mosaic.Tile.MAX_CONTENT_SIZE = 512; - -/** - * Default size for a tile with no thumbnail image. - */ -Mosaic.Tile.GENERIC_ICON_SIZE = 128; - -/** - * Max size of an image considered to be 'small'. - * Small images are laid out slightly differently. - */ -Mosaic.Tile.SMALL_IMAGE_SIZE = 160; - -/** - * @return {Gallery.Item} The Gallery item. - */ -Mosaic.Tile.prototype.getItem = function() { return this.item_; }; - -/** - * @return {number} Maximum content height that this tile can have. - */ -Mosaic.Tile.prototype.getMaxContentHeight = function() { - return this.maxContentHeight_; -}; - -/** - * @return {number} The aspect ratio of the tile image. - */ -Mosaic.Tile.prototype.getAspectRatio = function() { return this.aspectRatio_; }; - -/** - * @return {boolean} True if the tile is initialized. - */ -Mosaic.Tile.prototype.isInitialized = function() { - return !!this.maxContentHeight_; -}; - -/** - * Checks whether the image of specified (or better resolution) has been loaded. - * - * @param {Mosaic.Tile.LoadMode=} opt_loadMode Loading mode, default: LOW_DPI. - * @return {boolean} True if the tile is loaded with the specified dpi or - * better. - */ -Mosaic.Tile.prototype.isLoaded = function(opt_loadMode) { - var loadMode = opt_loadMode || Mosaic.Tile.LoadMode.LOW_DPI; - switch (loadMode) { - case Mosaic.Tile.LoadMode.LOW_DPI: - if (this.imagePreloaded_ || this.imageLoaded_) - return true; - break; - case Mosaic.Tile.LoadMode.HIGH_DPI: - if (this.imageLoaded_) - return true; - break; - } - return false; -}; - -/** - * Checks whether the image of specified (or better resolution) is being loaded. - * - * @param {Mosaic.Tile.LoadMode=} opt_loadMode Loading mode, default: LOW_DPI. - * @return {boolean} True if the tile is being loaded with the specified dpi or - * better. - */ -Mosaic.Tile.prototype.isLoading = function(opt_loadMode) { - var loadMode = opt_loadMode || Mosaic.Tile.LoadMode.LOW_DPI; - switch (loadMode) { - case Mosaic.Tile.LoadMode.LOW_DPI: - if (this.imagePreloading_ || this.imageLoading_) - return true; - break; - case Mosaic.Tile.LoadMode.HIGH_DPI: - if (this.imageLoading_) - return true; - break; - } - return false; -}; - -/** - * Marks the tile as not loaded to prevent it from participating in the layout. - */ -Mosaic.Tile.prototype.markUnloaded = function() { - this.maxContentHeight_ = 0; - if (this.thumbnailLoader_) { - this.thumbnailLoader_.cancel(); - this.imagePreloaded_ = false; - this.imagePreloading_ = false; - this.imageLoaded_ = false; - this.imageLoading_ = false; - } -}; - -/** - * Initializes the thumbnail in the tile. Does not load an image, but sets - * target dimensions using metadata. - * - * @param {Object} metadata Metadata object. - * @param {function()} onImageMeasured Image measured callback. - */ -Mosaic.Tile.prototype.init = function(metadata, onImageMeasured) { - this.markUnloaded(); - this.left_ = null; // Mark as not laid out. - - // Set higher priority for the selected elements to load them first. - var priority = this.getAttribute('selected') ? 2 : 3; - - // Use embedded thumbnails on Drive, since they have higher resolution. - this.thumbnailLoader_ = new ThumbnailLoader( - this.getItem().getEntry(), - ThumbnailLoader.LoaderType.CANVAS, - metadata, - undefined, // Media type. - this.hidpiEmbedded_ ? ThumbnailLoader.UseEmbedded.USE_EMBEDDED : - ThumbnailLoader.UseEmbedded.NO_EMBEDDED, - priority); - - // If no hidpi embedded thumbnail available, then use the low resolution - // for preloading. - if (!this.hidpiEmbedded_) { - this.thumbnailPreloader_ = new ThumbnailLoader( - this.getItem().getEntry(), - ThumbnailLoader.LoaderType.CANVAS, - metadata, - undefined, // Media type. - ThumbnailLoader.UseEmbedded.USE_EMBEDDED, - 2); // Preloaders have always higher priotity, so the preload images - // are loaded as soon as possible. - } - - var setDimensions = function(width, height) { - if (width > height) { - if (width > Mosaic.Tile.MAX_CONTENT_SIZE) { - height = Math.round(height * Mosaic.Tile.MAX_CONTENT_SIZE / width); - width = Mosaic.Tile.MAX_CONTENT_SIZE; - } - } else { - if (height > Mosaic.Tile.MAX_CONTENT_SIZE) { - width = Math.round(width * Mosaic.Tile.MAX_CONTENT_SIZE / height); - height = Mosaic.Tile.MAX_CONTENT_SIZE; - } - } - this.maxContentHeight_ = Math.max(Mosaic.Tile.MIN_CONTENT_SIZE, height); - this.aspectRatio_ = width / height; - onImageMeasured(); - }.bind(this); - - // Dimensions are always acquired from the metadata. For local files, it is - // extracted from headers. For Drive files, it is received via the Drive API. - // If the dimensions are not available, then the fallback dimensions will be - // used (same as for the generic icon). - if (metadata.media && metadata.media.width) { - setDimensions(metadata.media.width, metadata.media.height); - } else if (metadata.drive && metadata.drive.imageWidth && - metadata.drive.imageHeight) { - setDimensions(metadata.drive.imageWidth, metadata.drive.imageHeight); - } else { - // No dimensions in metadata, then use the generic dimensions. - setDimensions(Mosaic.Tile.GENERIC_ICON_SIZE, - Mosaic.Tile.GENERIC_ICON_SIZE); - } -}; - -/** - * Loads an image into the tile. - * - * The mode argument is a hint. Use low-dpi for faster response, and high-dpi - * for better output, but possibly affecting performance. - * - * If the mode is high-dpi, then a the high-dpi image is loaded, but also - * low-dpi image is loaded for preloading (if available). - * For the low-dpi mode, only low-dpi image is loaded. If not available, then - * the high-dpi image is loaded as a fallback. - * - * @param {Mosaic.Tile.LoadMode} loadMode Loading mode. - * @param {function(boolean)} onImageLoaded Callback when image is loaded. - * The argument is true for success, false for failure. - */ -Mosaic.Tile.prototype.load = function(loadMode, onImageLoaded) { - // Attaches the image to the tile and finalizes loading process for the - // specified loader. - var finalizeLoader = function(mode, success, loader) { - if (success && this.wrapper_) { - // Show the fade-in animation only when previously there was no image - // attached in this tile. - if (!this.imageLoaded_ && !this.imagePreloaded_) - this.wrapper_.classList.add('animated'); - else - this.wrapper_.classList.remove('animated'); - } - loader.attachImage(this.wrapper_, ThumbnailLoader.FillMode.OVER_FILL); - onImageLoaded(success); - switch (mode) { - case Mosaic.Tile.LoadMode.LOW_DPI: - this.imagePreloading_ = false; - this.imagePreloaded_ = true; - break; - case Mosaic.Tile.LoadMode.HIGH_DPI: - this.imageLoading_ = false; - this.imageLoaded_ = true; - break; - } - }.bind(this); - - // Always load the low-dpi image first if it is available for the fastest - // feedback. - if (!this.imagePreloading_ && this.thumbnailPreloader_) { - this.imagePreloading_ = true; - this.thumbnailPreloader_.loadDetachedImage(function(success) { - // Hi-dpi loaded first, ignore this call then. - if (this.imageLoaded_) - return; - finalizeLoader(Mosaic.Tile.LoadMode.LOW_DPI, - success, - this.thumbnailPreloader_); - }.bind(this)); - } - - // Load the high-dpi image only when it is requested, or the low-dpi is not - // available. - if (!this.imageLoading_ && - (loadMode === Mosaic.Tile.LoadMode.HIGH_DPI || !this.imagePreloading_)) { - this.imageLoading_ = true; - this.thumbnailLoader_.loadDetachedImage(function(success) { - // Cancel preloading, since the hi-dpi image is ready. - if (this.thumbnailPreloader_) - this.thumbnailPreloader_.cancel(); - finalizeLoader(Mosaic.Tile.LoadMode.HIGH_DPI, - success, - this.thumbnailLoader_); - }.bind(this)); - } -}; - -/** - * Unloads an image from the tile. - */ -Mosaic.Tile.prototype.unload = function() { - this.thumbnailLoader_.cancel(); - if (this.thumbnailPreloader_) - this.thumbnailPreloader_.cancel(); - this.imagePreloaded_ = false; - this.imageLoaded_ = false; - this.imagePreloading_ = false; - this.imageLoading_ = false; - this.wrapper_.innerText = ''; -}; - -/** - * Selects/unselects the tile. - * - * @param {boolean} on True if selected. - */ -Mosaic.Tile.prototype.select = function(on) { - if (on) - this.setAttribute('selected', true); - else - this.removeAttribute('selected'); -}; - -/** - * Positions the tile in the mosaic. - * - * @param {number} left Left position. - * @param {number} top Top position. - * @param {number} width Width. - * @param {number} height Height. - */ -Mosaic.Tile.prototype.layout = function(left, top, width, height) { - this.left_ = left; - this.top_ = top; - this.width_ = width; - this.height_ = height; - - this.style.left = left + 'px'; - this.style.top = top + 'px'; - this.style.width = width + 'px'; - this.style.height = height + 'px'; - - if (!this.wrapper_) { // First time, create DOM. - this.container_.appendChild(this); - var border = util.createChild(this, 'img-border'); - this.wrapper_ = util.createChild(border, 'img-wrapper'); - } - if (this.hasAttribute('selected')) - this.scrollIntoView(false); - - if (this.imageLoaded_) { - this.thumbnailLoader_.attachImage(this.wrapper_, - ThumbnailLoader.FillMode.OVER_FILL); - } -}; - -/** - * If the tile is not fully visible scroll the parent to make it fully visible. - * @param {boolean=} opt_animated True, if scroll should be animated, - * default: true. - */ -Mosaic.Tile.prototype.scrollIntoView = function(opt_animated) { - if (this.left_ === null) // Not laid out. - return; - - var targetPosition; - var tileLeft = this.left_ - Mosaic.Layout.SCROLL_MARGIN; - if (tileLeft < this.container_.scrollLeft) { - targetPosition = tileLeft; - } else { - var tileRight = this.left_ + this.width_ + Mosaic.Layout.SCROLL_MARGIN; - var scrollRight = this.container_.scrollLeft + this.container_.clientWidth; - if (tileRight > scrollRight) - targetPosition = tileRight - this.container_.clientWidth; - } - - if (targetPosition) { - if (opt_animated === false) - this.container_.scrollLeft = targetPosition; - else - this.container_.animatedScrollTo(targetPosition); - } -}; - -/** - * @return {Rect} Rectangle occupied by the tile's image, - * relative to the viewport. - */ -Mosaic.Tile.prototype.getImageRect = function() { - if (this.left_ === null) // Not laid out. - return null; - - var margin = Mosaic.Layout.SPACING / 2; - return new Rect(this.left_ - this.container_.scrollLeft, this.top_, - this.width_, this.height_).inflate(-margin, -margin); -}; - -/** - * @return {number} X coordinate of the tile center. - */ -Mosaic.Tile.prototype.getCenterX = function() { - return this.left_ + Math.round(this.width_ / 2); -}; diff --git a/ui/file_manager/file_manager/foreground/js/photo/ribbon.js b/ui/file_manager/file_manager/foreground/js/photo/ribbon.js deleted file mode 100644 index 32aaaca..0000000 --- a/ui/file_manager/file_manager/foreground/js/photo/ribbon.js +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2014 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'; - -/** - * Scrollable thumbnail ribbon at the bottom of the Gallery in the Slide mode. - * - * @param {Document} document Document. - * @param {MetadataCache} metadataCache MetadataCache instance. - * @param {cr.ui.ArrayDataModel} dataModel Data model. - * @param {cr.ui.ListSelectionModel} selectionModel Selection model. - * @return {Element} Ribbon element. - * @constructor - */ -function Ribbon(document, metadataCache, dataModel, selectionModel) { - var self = document.createElement('div'); - Ribbon.decorate(self, metadataCache, dataModel, selectionModel); - return self; -} - -/** - * Inherit from HTMLDivElement. - */ -Ribbon.prototype.__proto__ = HTMLDivElement.prototype; - -/** - * Decorate a Ribbon instance. - * - * @param {Ribbon} self Self pointer. - * @param {MetadataCache} metadataCache MetadataCache instance. - * @param {cr.ui.ArrayDataModel} dataModel Data model. - * @param {cr.ui.ListSelectionModel} selectionModel Selection model. - */ -Ribbon.decorate = function(self, metadataCache, dataModel, selectionModel) { - self.__proto__ = Ribbon.prototype; - self.metadataCache_ = metadataCache; - self.dataModel_ = dataModel; - self.selectionModel_ = selectionModel; - - self.className = 'ribbon'; -}; - -/** - * Max number of thumbnails in the ribbon. - * @type {number} - */ -Ribbon.ITEMS_COUNT = 5; - -/** - * Force redraw the ribbon. - */ -Ribbon.prototype.redraw = function() { - this.onSelection_(); -}; - -/** - * Clear all cached data to force full redraw on the next selection change. - */ -Ribbon.prototype.reset = function() { - this.renderCache_ = {}; - this.firstVisibleIndex_ = 0; - this.lastVisibleIndex_ = -1; // Zero thumbnails -}; - -/** - * Enable the ribbon. - */ -Ribbon.prototype.enable = function() { - this.onContentBound_ = this.onContentChange_.bind(this); - this.dataModel_.addEventListener('content', this.onContentBound_); - - this.onSpliceBound_ = this.onSplice_.bind(this); - this.dataModel_.addEventListener('splice', this.onSpliceBound_); - - this.onSelectionBound_ = this.onSelection_.bind(this); - this.selectionModel_.addEventListener('change', this.onSelectionBound_); - - this.reset(); - this.redraw(); -}; - -/** - * Disable ribbon. - */ -Ribbon.prototype.disable = function() { - this.dataModel_.removeEventListener('content', this.onContentBound_); - this.dataModel_.removeEventListener('splice', this.onSpliceBound_); - this.selectionModel_.removeEventListener('change', this.onSelectionBound_); - - this.removeVanishing_(); - this.textContent = ''; -}; - -/** - * Data model splice handler. - * @param {Event} event Event. - * @private - */ -Ribbon.prototype.onSplice_ = function(event) { - if (event.removed.length == 0) - return; - - if (event.removed.length > 1) { - console.error('Cannot remove multiple items'); - return; - } - - var removed = this.renderCache_[event.removed[0].getEntry().toURL()]; - if (!removed || !removed.parentNode || !removed.hasAttribute('selected')) { - console.error('Can only remove the selected item'); - return; - } - - var persistentNodes = this.querySelectorAll('.ribbon-image:not([vanishing])'); - if (this.lastVisibleIndex_ < this.dataModel_.length) { // Not at the end. - var lastNode = persistentNodes[persistentNodes.length - 1]; - if (lastNode.nextSibling) { - // Pull back a vanishing node from the right. - lastNode.nextSibling.removeAttribute('vanishing'); - } else { - // Push a new item at the right end. - this.appendChild(this.renderThumbnail_(this.lastVisibleIndex_)); - } - } else { - // No items to the right, move the window to the left. - this.lastVisibleIndex_--; - if (this.firstVisibleIndex_) { - this.firstVisibleIndex_--; - var firstNode = persistentNodes[0]; - if (firstNode.previousSibling) { - // Pull back a vanishing node from the left. - firstNode.previousSibling.removeAttribute('vanishing'); - } else { - // Push a new item at the left end. - var newThumbnail = this.renderThumbnail_(this.firstVisibleIndex_); - newThumbnail.style.marginLeft = -(this.clientHeight - 2) + 'px'; - this.insertBefore(newThumbnail, this.firstChild); - setTimeout(function() { - newThumbnail.style.marginLeft = '0'; - }, 0); - } - } - } - - removed.removeAttribute('selected'); - removed.setAttribute('vanishing', 'smooth'); - this.scheduleRemove_(); -}; - -/** - * Selection change handler. - * @private - */ -Ribbon.prototype.onSelection_ = function() { - var indexes = this.selectionModel_.selectedIndexes; - if (indexes.length == 0) - return; // Ignore temporary empty selection. - var selectedIndex = indexes[0]; - - var length = this.dataModel_.length; - - // TODO(dgozman): use margin instead of 2 here. - var itemWidth = this.clientHeight - 2; - var fullItems = Ribbon.ITEMS_COUNT; - fullItems = Math.min(fullItems, length); - var right = Math.floor((fullItems - 1) / 2); - - var fullWidth = fullItems * itemWidth; - this.style.width = fullWidth + 'px'; - - var lastIndex = selectedIndex + right; - lastIndex = Math.max(lastIndex, fullItems - 1); - lastIndex = Math.min(lastIndex, length - 1); - var firstIndex = lastIndex - fullItems + 1; - - if (this.firstVisibleIndex_ != firstIndex || - this.lastVisibleIndex_ != lastIndex) { - - if (this.lastVisibleIndex_ == -1) { - this.firstVisibleIndex_ = firstIndex; - this.lastVisibleIndex_ = lastIndex; - } - - this.removeVanishing_(); - - this.textContent = ''; - var startIndex = Math.min(firstIndex, this.firstVisibleIndex_); - // All the items except the first one treated equally. - for (var index = startIndex + 1; - index <= Math.max(lastIndex, this.lastVisibleIndex_); - ++index) { - // Only add items that are in either old or the new viewport. - if (this.lastVisibleIndex_ < index && index < firstIndex || - lastIndex < index && index < this.firstVisibleIndex_) - continue; - var box = this.renderThumbnail_(index); - box.style.marginLeft = '0'; - this.appendChild(box); - if (index < firstIndex || index > lastIndex) { - // If the node is not in the new viewport we only need it while - // the animation is playing out. - box.setAttribute('vanishing', 'slide'); - } - } - - var slideCount = this.childNodes.length + 1 - Ribbon.ITEMS_COUNT; - var margin = itemWidth * slideCount; - var startBox = this.renderThumbnail_(startIndex); - if (startIndex == firstIndex) { - // Sliding to the right. - startBox.style.marginLeft = -margin + 'px'; - if (this.firstChild) - this.insertBefore(startBox, this.firstChild); - else - this.appendChild(startBox); - setTimeout(function() { - startBox.style.marginLeft = '0'; - }, 0); - } else { - // Sliding to the left. Start item will become invisible and should be - // removed afterwards. - startBox.setAttribute('vanishing', 'slide'); - startBox.style.marginLeft = '0'; - if (this.firstChild) - this.insertBefore(startBox, this.firstChild); - else - this.appendChild(startBox); - setTimeout(function() { - startBox.style.marginLeft = -margin + 'px'; - }, 0); - } - - ImageUtil.setClass(this, 'fade-left', - firstIndex > 0 && selectedIndex != firstIndex); - - ImageUtil.setClass(this, 'fade-right', - lastIndex < length - 1 && selectedIndex != lastIndex); - - this.firstVisibleIndex_ = firstIndex; - this.lastVisibleIndex_ = lastIndex; - - this.scheduleRemove_(); - } - - var oldSelected = this.querySelector('[selected]'); - if (oldSelected) oldSelected.removeAttribute('selected'); - - var newSelected = - this.renderCache_[this.dataModel_.item(selectedIndex).getEntry().toURL()]; - if (newSelected) newSelected.setAttribute('selected', true); -}; - -/** - * Schedule the removal of thumbnails marked as vanishing. - * @private - */ -Ribbon.prototype.scheduleRemove_ = function() { - if (this.removeTimeout_) - clearTimeout(this.removeTimeout_); - - this.removeTimeout_ = setTimeout(function() { - this.removeTimeout_ = null; - this.removeVanishing_(); - }.bind(this), 200); -}; - -/** - * Remove all thumbnails marked as vanishing. - * @private - */ -Ribbon.prototype.removeVanishing_ = function() { - if (this.removeTimeout_) { - clearTimeout(this.removeTimeout_); - this.removeTimeout_ = 0; - } - var vanishingNodes = this.querySelectorAll('[vanishing]'); - for (var i = 0; i != vanishingNodes.length; i++) { - vanishingNodes[i].removeAttribute('vanishing'); - this.removeChild(vanishingNodes[i]); - } -}; - -/** - * Create a DOM element for a thumbnail. - * - * @param {number} index Item index. - * @return {Element} Newly created element. - * @private - */ -Ribbon.prototype.renderThumbnail_ = function(index) { - var item = this.dataModel_.item(index); - var url = item.getEntry().toURL(); - - var cached = this.renderCache_[url]; - if (cached) { - var img = cached.querySelector('img'); - if (img) - img.classList.add('cached'); - return cached; - } - - var thumbnail = this.ownerDocument.createElement('div'); - thumbnail.className = 'ribbon-image'; - thumbnail.addEventListener('click', function() { - var index = this.dataModel_.indexOf(item); - this.selectionModel_.unselectAll(); - this.selectionModel_.setIndexSelected(index, true); - }.bind(this)); - - util.createChild(thumbnail, 'image-wrapper'); - - this.metadataCache_.getOne(item.getEntry(), Gallery.METADATA_TYPE, - this.setThumbnailImage_.bind(this, thumbnail, item.getEntry())); - - // TODO: Implement LRU eviction. - // Never evict the thumbnails that are currently in the DOM because we rely - // on this cache to find them by URL. - this.renderCache_[url] = thumbnail; - return thumbnail; -}; - -/** - * Set the thumbnail image. - * - * @param {Element} thumbnail Thumbnail element. - * @param {FileEntry} entry Image Entry. - * @param {Object} metadata Metadata. - * @private - */ -Ribbon.prototype.setThumbnailImage_ = function(thumbnail, entry, metadata) { - new ThumbnailLoader(entry, ThumbnailLoader.LoaderType.IMAGE, metadata).load( - thumbnail.querySelector('.image-wrapper'), - ThumbnailLoader.FillMode.FILL /* fill */, - ThumbnailLoader.OptimizationMode.NEVER_DISCARD); -}; - -/** - * Content change handler. - * - * @param {Event} event Event. - * @private - */ -Ribbon.prototype.onContentChange_ = function(event) { - var url = event.item.getEntry().toURL(); - if (event.oldEntry.toURL() !== url) { - this.remapCache_(event.oldEntry.toURL(), url); - } else { - delete this.renderCache_[url]; - var index = this.dataModel_.indexOf(event.item); - this.renderThumbnail_(index); - } - - var thumbnail = this.renderCache_[url]; - if (thumbnail && event.metadata) - this.setThumbnailImage_(thumbnail, event.item.getEntry(), event.metadata); -}; - -/** - * Update the thumbnail element cache. - * - * @param {string} oldUrl Old url. - * @param {string} newUrl New url. - * @private - */ -Ribbon.prototype.remapCache_ = function(oldUrl, newUrl) { - if (oldUrl != newUrl && (oldUrl in this.renderCache_)) { - this.renderCache_[newUrl] = this.renderCache_[oldUrl]; - delete this.renderCache_[oldUrl]; - } -}; diff --git a/ui/file_manager/file_manager/foreground/js/photo/slide_mode.js b/ui/file_manager/file_manager/foreground/js/photo/slide_mode.js deleted file mode 100644 index 0d957ce..0000000 --- a/ui/file_manager/file_manager/foreground/js/photo/slide_mode.js +++ /dev/null @@ -1,1349 +0,0 @@ -// Copyright 2014 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.onToolsVisibilityChanged_.bind(this)); - - 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(); - // Show the selected item ASAP, then complete the initialization - // (loading the ribbon thumbnails can take some time). - this.metadataCache_.getOne(selectedItem.getEntry(), Gallery.METADATA_TYPE, - function(metadata) { - this.loadItem_(selectedItem.getEntry(), 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)); -}; - -/** - * Handles changes in tools visibility, and if the header is dimmed, then - * requests disabling the draggable app region. - * - * @private - */ -SlideMode.prototype.onToolsVisibilityChanged_ = function() { - var headerDimmed = - this.document_.querySelector('.header').hasAttribute('dimmed'); - this.context_.onAppRegionChanged(!headerDimmed); -}; - -/** - * 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_.contentEntry_); - } - - 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.getEntry(), 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_.getOne( - selectedItem.getEntry(), 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 {FileEntry} entry Item entry to be loaded. - * @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( - entry, 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_(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 = entry.name.lastIndexOf('.'); - var ext = extIndex < 0 ? '' : - entry.name.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(entry, 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 nextItemEntry = this.getItem(index).getEntry(); - this.imageView_.prefetch(nextItemEntry, 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); - } -}; - -/** - * 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 oldEntry = item.getEntry(); - var canvas = this.imageView_.getCanvas(); - - this.showSpinner_(true); - var metadataEncoder = ImageEncoder.encodeMetadata( - this.selectedImageMetadata_.media, canvas, 1 /* quality */); - var selectedImageMetadata = ContentProvider.ConvertContentMetadata( - metadataEncoder.getMetadata(), this.selectedImageMetadata_); - if (selectedImageMetadata.filesystem) - selectedImageMetadata.filesystem.modificationTime = new Date(); - this.selectedImageMetadata_ = selectedImageMetadata; - this.metadataCache_.set(oldEntry, - Gallery.METADATA_TYPE, - 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 event = new Event('content'); - event.item = item; - event.oldEntry = oldEntry; - event.metadata = selectedImageMetadata; - this.dataModel_.dispatchEvent(event); - - // 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 (!util.isSameEntry(oldEntry, item.getEntry())) { - this.dataModel_.splice( - this.getSelectedIndex(), 0, new Gallery.Item(oldEntry)); - // 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 has been renamed. - * @param {Event} event Event. - * @private - */ -SlideMode.prototype.onContentChange_ = function(event) { - var newEntry = event.item.getEntry(); - if (util.isSameEntry(newEntry, event.oldEntry)) - this.imageView_.changeEntry(newEntry); - this.metadataCache_.clear(event.oldEntry, 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', 'GALLERY_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; diff --git a/ui/file_manager/file_manager/gallery.html b/ui/file_manager/file_manager/gallery.html deleted file mode 100644 index 27aa851..0000000 --- a/ui/file_manager/file_manager/gallery.html +++ /dev/null @@ -1,79 +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. - --> -<html> -<head> - <link rel="stylesheet" href="foreground/css/list.css"> - <link rel="stylesheet" href="foreground/css/common.css"> - <link rel="stylesheet" href="foreground/css/file_types.css"> - <link rel="stylesheet" href="foreground/css/gallery.css"> - <link rel="stylesheet" href="foreground/css/media_controls.css"> - - <!-- Don't load gallery_scripts.js when flattening is disabled --> - <if expr="False"><!-- </if> - <script src="foreground/js/photo/gallery_scripts.js"></script> - <if expr="False"> --></if> - - <if expr="False"> - <!-- This section is used when the file manager is loaded with - 'filemgr-ext-path' command-line flag. --> - <!-- Keep the list in sync with gallery_scripts.js. --> - <script src="foreground/js/metrics.js"></script> - - <!-- Loads the client of the image loader extension --> - <script src="chrome-extension://pmfjbimdmchhbnneeidfognadeopoehp/image_loader_client.js"></script> - - <script src="chrome://resources/js/cr.js"></script> - <script src="chrome://resources/js/event_tracker.js"></script> - <script src="chrome://resources/js/load_time_data.js"></script> - - <script src="chrome://resources/js/cr/ui.js"></script> - <script src="chrome://resources/js/cr/event_target.js"></script> - <script src="chrome://resources/js/cr/ui/touch_handler.js"></script> - <script src="chrome://resources/js/cr/ui/array_data_model.js"></script> - <script src="chrome://resources/js/cr/ui/dialogs.js"></script> - <script src="chrome://resources/js/cr/ui/list_item.js"></script> - <script src="chrome://resources/js/cr/ui/list_selection_model.js"></script> - <script src="chrome://resources/js/cr/ui/list_single_selection_model.js"></script> - <script src="chrome://resources/js/cr/ui/list_selection_controller.js"></script> - <script src="chrome://resources/js/cr/ui/list.js"></script> - <script src="chrome://resources/js/cr/ui/grid.js"></script> - - <script src="common/js/async_util.js"></script> - <script src="common/js/util.js"></script> - <script src="common/js/volume_manager_common.js"></script> - - <script src="foreground/js/file_type.js"></script> - <script src="foreground/js/thumbnail_loader.js"></script> - <script src="foreground/js/volume_manager_wrapper.js"></script> - - <script src="foreground/js/image_editor/image_util.js"></script> - <script src="foreground/js/image_editor/viewport.js"></script> - <script src="foreground/js/image_editor/image_buffer.js"></script> - <script src="foreground/js/image_editor/image_view.js"></script> - <script src="foreground/js/image_editor/commands.js"></script> - <script src="foreground/js/image_editor/image_editor.js"></script> - <script src="foreground/js/image_editor/image_transform.js"></script> - <script src="foreground/js/image_editor/image_adjust.js"></script> - <script src="foreground/js/image_editor/filter.js"></script> - <script src="foreground/js/image_editor/image_encoder.js"></script> - <script src="foreground/js/image_editor/exif_encoder.js"></script> - - <script src="foreground/js/media/media_controls.js"></script> - <script src="foreground/js/media/mouse_inactivity_watcher.js"></script> - - <script src="foreground/js/metadata/metadata_cache.js"></script> - - <script src="foreground/js/photo/gallery.js"></script> - <script src="foreground/js/photo/gallery_item.js"></script> - <script src="foreground/js/photo/mosaic_mode.js"></script> - <script src="foreground/js/photo/slide_mode.js"></script> - <script src="foreground/js/photo/ribbon.js"></script> - </if> -</head> -<body> - <div class="gallery"></div> -</body> -</html> diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html index ad11fed..f1deb6c 100644 --- a/ui/file_manager/file_manager/main.html +++ b/ui/file_manager/file_manager/main.html @@ -409,6 +409,5 @@ </div> <div id="drag-container"></div> <iframe id="command-dispatcher" hidden></iframe> - <div class="overlay-pane" id="iframe-drag-area" hidden></div> </body> </html> diff --git a/ui/file_manager/file_manager/manifest.json b/ui/file_manager/file_manager/manifest.json index ec906f1..9eed6a1 100644 --- a/ui/file_manager/file_manager/manifest.json +++ b/ui/file_manager/file_manager/manifest.json @@ -94,21 +94,6 @@ ] }, { - "id": "gallery", - "default_title": "__MSG_OPEN_ACTION__", - "default_icon": "common/images/file_types/200/image.png", - "file_filters": [ - // Image formats - "filesystem:*.bmp", - "filesystem:*.gif", - "filesystem:*.ico", - "filesystem:*.jpg", - "filesystem:*.jpeg", - "filesystem:*.png", - "filesystem:*.webp" - ] - }, - { "id": "open-hosted-generic", "default_title": "__MSG_HOSTED__", "default_icon": "common/images/file_types/200generic.png", diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd index f5729c7..5f62c2a 100644 --- a/ui/file_manager/file_manager_resources.grd +++ b/ui/file_manager/file_manager_resources.grd @@ -17,9 +17,6 @@ <include name="IDR_FILE_MANAGER_MAIN_JS" file="file_manager/foreground/js/main_scripts.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_FILE_MANAGER_BKGND_JS" file="file_manager/background/js/background.js" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY" file="file_manager/gallery.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" /> - <include name="IDR_FILE_MANAGER_GALLERY_JS" file="file_manager/foreground/js/photo/gallery_scripts.js" flattenhtml="true" type="BINDATA" /> - <!-- Scripts working in background page. --> <include name="IDR_FILE_MANAGER_ERROR_UTIL_JS" file="file_manager/common/js/error_util.js" flattenhtml="false" type="BINDATA" /> <include name="IDR_FILE_MANAGER_ASYNC_UTIL_JS" file="file_manager/common/js/async_util.js" flattenhtml="false" type="BINDATA" /> diff --git a/ui/file_manager/gallery/js/image_editor/image_editor.js b/ui/file_manager/gallery/js/image_editor/image_editor.js index f9f5db4..7b0d5ef 100644 --- a/ui/file_manager/gallery/js/image_editor/image_editor.js +++ b/ui/file_manager/gallery/js/image_editor/image_editor.js @@ -1132,8 +1132,7 @@ ImageEditor.Prompt.prototype.setTimer = function(callback, timeout) { */ ImageEditor.Prompt.prototype.show = function(text, timeout, var_formatArgs) { var args = [text].concat(Array.prototype.slice.call(arguments, 2)); - var message = this.displayStringFunction_.apply( - null, [text].concat(args)); + var message = this.displayStringFunction_.apply(null, args); this.showStringAt('center', message, timeout); }; |