1f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// found in the LICENSE file.
4f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)'use strict';
6f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
8f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * ImageEditor is the top level object that holds together and connects
9f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * everything needed for image editing.
10f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
11f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Viewport} viewport The viewport.
12f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageView} imageView The ImageView containing the images to edit.
13f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageEditor.Prompt} prompt Prompt instance.
14f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} DOMContainers Various DOM containers required for the editor.
15f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Array.<ImageEditor.Mode>} modes Available editor modes.
16f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} displayStringFunction String formatting function.
17f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function()} onToolsVisibilityChanged Callback to be called, when
18f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *     some of the UI elements have been dimmed or revealed.
19f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
20f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
21f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)function ImageEditor(
22f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    viewport, imageView, prompt, DOMContainers, modes, displayStringFunction,
23f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    onToolsVisibilityChanged) {
24f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.rootContainer_ = DOMContainers.root;
25f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.container_ = DOMContainers.image;
26f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.modes_ = modes;
27f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.displayStringFunction_ = displayStringFunction;
28f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.onToolsVisibilityChanged_ = onToolsVisibilityChanged;
29f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
30f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.removeChildren(this.container_);
31f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
32f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var document = this.container_.ownerDocument;
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
34f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.viewport_ = viewport;
35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.viewport_.sizeByFrame(this.container_);
36f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
37f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.buffer_ = new ImageBuffer();
38f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.viewport_.addRepaintCallback(this.buffer_.draw.bind(this.buffer_));
39f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.imageView_ = imageView;
41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.imageView_.addContentCallback(this.onContentUpdate_.bind(this));
42f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.buffer_.addOverlay(this.imageView_);
43f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
44f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.panControl_ = new ImageEditor.MouseControl(
45f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.rootContainer_, this.container_, this.getBuffer());
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this));
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.mainToolbar_ = new ImageEditor.Toolbar(
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      DOMContainers.toolbar, displayStringFunction);
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
52f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.modeToolbar_ = new ImageEditor.Toolbar(
53f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      DOMContainers.mode, displayStringFunction,
54f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.onOptionsChange.bind(this));
55f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
56f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.prompt_ = prompt;
57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.createToolButtons();
59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
60f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.commandQueue_ = null;
61f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
62f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
63f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
64f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if no user commands are to be accepted.
65f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
66f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.isLocked = function() {
67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return !this.commandQueue_ || this.commandQueue_.isBusy();
68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
70f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
71f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if the command queue is busy.
72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
73f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.isBusy = function() {
74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.commandQueue_ && this.commandQueue_.isBusy();
75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Reflect the locked state of the editor in the UI.
79f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {boolean} on True if locked.
80f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
81f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.lockUI = function(on) {
82f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.setAttribute(this.rootContainer_, 'locked', on);
83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
85f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
86f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Report the tool use to the metrics subsystem.
87f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name Action name.
88f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
89f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.recordToolUse = function(name) {
90f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.metrics.recordEnum(
91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      ImageUtil.getMetricName('Tool'), name, this.actionNames_);
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
93f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
94f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
95f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Content update handler.
96f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
97f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
98f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.onContentUpdate_ = function() {
99f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (var i = 0; i != this.modes_.length; i++) {
100f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var mode = this.modes_[i];
101f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    ImageUtil.setAttribute(mode.button_, 'disabled', !mode.isApplicable());
102f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
103f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
104f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
106f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Open the editing session for a new image.
107f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
108f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} url Image url.
109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} metadata Metadata.
110f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} effect Transition effect object.
111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function(function)} saveFunction Image save function.
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} displayCallback Display callback.
113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} loadCallback Load callback.
114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.openSession = function(
116f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    url, metadata, effect, saveFunction, displayCallback, loadCallback) {
117f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.commandQueue_)
118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    throw new Error('Session not closed');
119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.lockUI(true);
121f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
122f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var self = this;
123f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.imageView_.load(
124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      url, metadata, effect, displayCallback, function(loadType, delay, error) {
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    self.lockUI(false);
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    self.commandQueue_ = new CommandQueue(
127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        self.container_.ownerDocument,
128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        self.imageView_.getCanvas(),
129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        saveFunction);
130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    self.commandQueue_.attachUI(
131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        self.getImageView(), self.getPrompt(), self.lockUI.bind(self));
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    self.updateUndoRedo();
133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    loadCallback(loadType, delay, error);
134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  });
135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
136f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
137f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
138f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Close the current image editing session.
139f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} callback Callback.
140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
141f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.closeSession = function(callback) {
142f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.getPrompt().hide();
143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.imageView_.isLoading()) {
144f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (this.commandQueue_) {
145f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      console.warn('Inconsistent image editor state');
146f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.commandQueue_ = null;
147f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
148f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.imageView_.cancelLoad();
149f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.lockUI(false);
150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    callback();
151f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
152f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
153f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.commandQueue_) {
154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Session is already closed.
155f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    callback();
156f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
157f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
158f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
159f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.executeWhenReady(callback);
160f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.commandQueue_.close();
161f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.commandQueue_ = null;
162f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
163f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
165f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Commit the current operation and execute the action.
166f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
167f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} callback Callback.
168f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
169f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.executeWhenReady = function(callback) {
170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.commandQueue_) {
171f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.leaveModeGently();
172f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.commandQueue_.executeWhenReady(callback);
173f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!this.imageView_.isLoading())
175f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      console.warn('Inconsistent image editor state');
176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    callback();
177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
179f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if undo queue is not empty.
182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
183f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.canUndo = function() {
184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.commandQueue_ && this.commandQueue_.canUndo();
185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
186f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
187f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
188f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Undo the recently executed command.
189f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
190f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.undo = function() {
191f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.isLocked()) return;
192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.recordToolUse('undo');
193f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
194f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // First undo click should dismiss the uncommitted modifications.
195f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.currentMode_ && this.currentMode_.isUpdated()) {
196f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.currentMode_.reset();
197f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
199f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
200f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.getPrompt().hide();
201f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.leaveMode(false);
202f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.commandQueue_.undo();
203f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updateUndoRedo();
204f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
205f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
206f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
207f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Redo the recently un-done command.
208f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
209f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.redo = function() {
210f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.isLocked()) return;
211f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.recordToolUse('redo');
212f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.getPrompt().hide();
213f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.leaveMode(false);
214f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.commandQueue_.redo();
215f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updateUndoRedo();
216f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
217f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
218f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
219f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Update Undo/Redo buttons state.
220f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.updateUndoRedo = function() {
222f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var canUndo = this.commandQueue_ && this.commandQueue_.canUndo();
223f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var canRedo = this.commandQueue_ && this.commandQueue_.canRedo();
224f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.setAttribute(this.undoButton_, 'disabled', !canUndo);
225f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.redoButton_.hidden = !canRedo;
226f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
227f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
228f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
229f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {HTMLCanvasElement} The current image canvas.
230f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
231f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.getCanvas = function() {
232f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.getImageView().getCanvas();
233f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
234f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
235f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
236f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {ImageBuffer} ImageBuffer instance.
237f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
238f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.getBuffer = function() { return this.buffer_ };
239f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
240f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
241f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {ImageView} ImageView instance.
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.getImageView = function() { return this.imageView_ };
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {Viewport} Viewport instance.
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.getViewport = function() { return this.viewport_ };
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {ImageEditor.Prompt} Prompt instance.
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.getPrompt = function() { return this.prompt_ };
254f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
255f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
256f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Handle the toolbar controls update.
257f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} options A map of options.
258f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
259f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.onOptionsChange = function(options) {
260f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.trace.resetTimer('update');
261f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.currentMode_) {
262f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.currentMode_.update(options);
263f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
264f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.trace.reportTimer('update');
265f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
266f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
267f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
268f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * ImageEditor.Mode represents a modal state dedicated to a specific operation.
269f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Inherits from ImageBuffer. Overlay to simplify the drawing of mode-specific
270f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * tools.
271f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
272f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name The mode name.
273f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} title The mode title.
274f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
275f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
276f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
277f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode = function(name, title) {
278f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.name = name;
279f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.title = title;
280f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.message_ = 'GALLERY_ENTER_WHEN_DONE';
281f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
282f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
283f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype = {__proto__: ImageBuffer.Overlay.prototype };
284f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
285f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
286f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {Viewport} Viewport instance.
287f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
288f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.getViewport = function() { return this.viewport_ };
289f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
290f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
291f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {ImageView} ImageView instance.
292f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
293f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.getImageView = function() { return this.imageView_ };
294f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
295f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
296f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {string} The mode-specific message to be displayed when entering.
297f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
298f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.getMessage = function() { return this.message_ };
299f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
300f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
301f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if the mode is applicable in the current context.
302f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
303f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.isApplicable = function() { return true };
304f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
305f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
306f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Called once after creating the mode button.
307f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
308f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageEditor} editor The editor instance.
309f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLElement} button The mode button.
310f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
311f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
312f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.bind = function(editor, button) {
313f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.editor_ = editor;
314f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.editor_.registerAction_(this.name);
315f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.button_ = button;
316f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.viewport_ = editor.getViewport();
317f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.imageView_ = editor.getImageView();
318f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
319f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
320f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
321f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Called before entering the mode.
322f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
323f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.setUp = function() {
324f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.editor_.getBuffer().addOverlay(this);
325f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updated_ = false;
326f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
327f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
328f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
329f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Create mode-specific controls here.
330f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageEditor.Toolbar} toolbar The toolbar to populate.
331f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
332f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.createTools = function(toolbar) {};
333f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
334f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
335f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Called before exiting the mode.
336f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
337f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.cleanUpUI = function() {
338f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.editor_.getBuffer().removeOverlay(this);
339f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
340f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
341f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
342f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Called after exiting the mode.
343f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
344f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.cleanUpCaches = function() {};
345f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
346f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
347f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Called when any of the controls changed its value.
348f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} options A map of options.
349f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
350f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.update = function(options) {
351f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.markUpdated();
352f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
353f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
354f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
355f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Mark the editor mode as updated.
356f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
357f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.markUpdated = function() {
358f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updated_ = true;
359f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
360f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
361f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
362f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if the mode controls changed.
363f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
364f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.isUpdated = function() { return this.updated_ };
365f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
366f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
367f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Resets the mode to a clean state.
368f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
369f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.prototype.reset = function() {
370f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.editor_.modeToolbar_.reset();
371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updated_ = false;
372f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
373f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
374f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
375f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * One-click editor tool, requires no interaction, just executes the command.
376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
377f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name The mode name.
378f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} title The mode title.
379f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Command} command The command to execute on click.
380f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
381f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
382f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.OneClick = function(name, title, command) {
383f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageEditor.Mode.call(this, name, title);
384f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.instant = true;
385f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.command_ = command;
386f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
387f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
388f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.OneClick.prototype = {__proto__: ImageEditor.Mode.prototype};
389f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
390f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
391f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {Command} command.
392f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
393f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Mode.OneClick.prototype.getCommand = function() {
394f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.command_;
395f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
396f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
397f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
398f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Register the action name. Required for metrics reporting.
399f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name Button name.
400f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
401f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
402f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.registerAction_ = function(name) {
403f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.actionNames_.push(name);
404f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
405f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
406f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
407f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Populate the toolbar.
408f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
409f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.createToolButtons = function() {
410f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.mainToolbar_.clear();
411f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.actionNames_ = [];
412f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
413f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var self = this;
414f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  function createButton(name, title, handler) {
415f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return self.mainToolbar_.addButton(name,
416f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                       title,
417f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                       handler,
418f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                       name /* opt_className */);
419f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
420f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
421f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (var i = 0; i != this.modes_.length; i++) {
422f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var mode = this.modes_[i];
423f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    mode.bind(this, createButton(mode.name,
424f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                 mode.title,
425f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                 this.enterMode.bind(this, mode)));
426f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
427f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
428f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.undoButton_ = createButton('undo',
429f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                  'GALLERY_UNDO',
430f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                  this.undo.bind(this));
431f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.registerAction_('undo');
432f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
433f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.redoButton_ = createButton('redo',
434f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                  'GALLERY_REDO',
435f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                  this.redo.bind(this));
436f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.registerAction_('redo');
437f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
438f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
439f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
440f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {ImageEditor.Mode} The current mode.
441f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
442f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.getMode = function() { return this.currentMode_ };
443f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
444f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
445f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * The user clicked on the mode button.
446f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
447f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageEditor.Mode} mode The new mode.
448f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
449f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.enterMode = function(mode) {
450f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.isLocked()) return;
451f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
452f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.currentMode_ == mode) {
453f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Currently active editor tool clicked, commit if modified.
454f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.leaveMode(this.currentMode_.updated_);
455f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
456f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
457f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
458f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.recordToolUse(mode.name);
459f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
460f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.leaveModeGently();
461f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // The above call could have caused a commit which might have initiated
462f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // an asynchronous command execution. Wait for it to complete, then proceed
463f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // with the mode set up.
464f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.commandQueue_.executeWhenReady(this.setUpMode_.bind(this, mode));
465f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
466f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
467f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
468f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Set up the new editing mode.
469f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
470f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageEditor.Mode} mode The mode.
471f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
472f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
473f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.setUpMode_ = function(mode) {
474f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentTool_ = mode.button_;
475f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
476f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.setAttribute(this.currentTool_, 'pressed', true);
477f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
478f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentMode_ = mode;
479f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentMode_.setUp();
480f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
481f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.currentMode_.instant) {  // Instant tool.
482f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.leaveMode(true);
483f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
484f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
485f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
486f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.getPrompt().show(this.currentMode_.getMessage());
487f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
488f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.modeToolbar_.clear();
489f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentMode_.createTools(this.modeToolbar_);
490f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.modeToolbar_.show(true);
491f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
492f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
493f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
494f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * The user clicked on 'OK' or 'Cancel' or on a different mode button.
495f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {boolean} commit True if commit is required.
496f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
497f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.leaveMode = function(commit) {
498f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.currentMode_) return;
499f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
500f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.currentMode_.instant) {
501f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.getPrompt().hide();
502f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
503f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
504f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.modeToolbar_.show(false);
505f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
506f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentMode_.cleanUpUI();
507f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (commit) {
508f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var self = this;
509f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var command = this.currentMode_.getCommand();
510f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (command) {  // Could be null if the user did not do anything.
511f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.commandQueue_.execute(command);
512f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.updateUndoRedo();
513f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
514f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
515f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentMode_.cleanUpCaches();
516f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentMode_ = null;
517f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
518f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.setAttribute(this.currentTool_, 'pressed', false);
519f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.currentTool_ = null;
520f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
521f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
522f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
523f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Leave the mode, commit only if required by the current mode.
524f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
525f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.leaveModeGently = function() {
526f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.leaveMode(this.currentMode_ &&
527f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 this.currentMode_.updated_ &&
528f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 this.currentMode_.implicitCommit);
529f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
530f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
531f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
532f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Enter the editor mode with the given name.
533f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
534f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name Mode name.
535f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
536f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
537f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.enterModeByName_ = function(name) {
538f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (var i = 0; i != this.modes_.length; i++) {
539f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var mode = this.modes_[i];
540f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (mode.name == name) {
541f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (!mode.button_.hasAttribute('disabled'))
542f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        this.enterMode(mode);
543f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return;
544f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
545f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
546f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  console.error('Mode "' + name + '" not found.');
547f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
548f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
549f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
550f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Key down handler.
551f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Event} event The keydown event.
552f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {boolean} True if handled.
553f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
554f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.onKeyDown = function(event) {
555f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  switch (util.getKeyModifiers(event) + event.keyIdentifier) {
556f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'U+001B': // Escape
557f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'Enter':
558f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (this.getMode()) {
559f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        this.leaveMode(event.keyIdentifier == 'Enter');
560f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return true;
561f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
562f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
563f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
564f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'Ctrl-U+005A':  // Ctrl+Z
565f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (this.commandQueue_.canUndo()) {
566f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        this.undo();
567f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return true;
568f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
569f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
570f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
571f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'Ctrl-U+0059':  // Ctrl+Y
572f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (this.commandQueue_.canRedo()) {
573f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        this.redo();
574f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return true;
575f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
576f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
577f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
578f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'U+0041':  // 'a'
579f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.enterModeByName_('autofix');
580f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return true;
581f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
582f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'U+0042':  // 'b'
583f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.enterModeByName_('exposure');
584f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return true;
585f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
586f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'U+0043':  // 'c'
587f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.enterModeByName_('crop');
588f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return true;
589f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
590f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'U+004C':  // 'l'
591f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.enterModeByName_('rotate_left');
592f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return true;
593f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
594f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case 'U+0052':  // 'r'
595f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.enterModeByName_('rotate_right');
596f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return true;
597f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
598f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return false;
599f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
600f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
601f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
602f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Double tap handler.
603f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} x X coordinate of the event.
604f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} y Y coordinate of the event.
605f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
606f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
607f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.onDoubleTap_ = function(x, y) {
608f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.getMode()) {
609f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var action = this.buffer_.getDoubleTapAction(x, y);
610f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (action == ImageBuffer.DoubleTapAction.COMMIT)
611f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.leaveMode(true);
612f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    else if (action == ImageBuffer.DoubleTapAction.CANCEL)
613f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.leaveMode(false);
614f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
615f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
616f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
617f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
618f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Hide the tools that overlap the given rectangular frame.
619f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
620f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Rect} frame Hide the tool that overlaps this rect.
621f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Rect} transparent But do not hide the tool that is completely inside
622f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *                           this rect.
623f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
624f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.prototype.hideOverlappingTools = function(frame, transparent) {
625f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var tools = this.rootContainer_.ownerDocument.querySelectorAll('.dimmable');
626f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var changed = false;
627f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (var i = 0; i != tools.length; i++) {
628f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var tool = tools[i];
629f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var toolRect = tool.getBoundingClientRect();
630f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var overlapping =
631f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        (frame && frame.intersects(toolRect)) &&
632f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        !(transparent && transparent.contains(toolRect));
633f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (overlapping && !tool.hasAttribute('dimmed') ||
634f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        !overlapping && tool.hasAttribute('dimmed')) {
635f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      ImageUtil.setAttribute(tool, 'dimmed', overlapping);
636f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      changed = true;
637f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
638f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
639f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (changed)
640f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.onToolsVisibilityChanged_();
641f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
642f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
643f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
644f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * A helper object for panning the ImageBuffer.
645f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
646f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLElement} rootContainer The top-level container.
647f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLElement} container The container for mouse events.
648f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {ImageBuffer} buffer Image buffer.
649f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
650f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
651f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl = function(rootContainer, container, buffer) {
652f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.rootContainer_ = rootContainer;
653f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.container_ = container;
654f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.buffer_ = buffer;
655f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
656f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var handlers = {
657f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    'touchstart': this.onTouchStart,
658f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    'touchend': this.onTouchEnd,
659f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    'touchcancel': this.onTouchCancel,
660f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    'touchmove': this.onTouchMove,
661f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    'mousedown': this.onMouseDown,
662f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    'mouseup': this.onMouseUp
663f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  };
664f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
665f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (var eventName in handlers) {
666f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    container.addEventListener(
667f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        eventName, handlers[eventName].bind(this), false);
668f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
669f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
670f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Mouse move handler has to be attached to the window to receive events
671f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // from outside of the window. See: http://crbug.com/155705
672f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  window.addEventListener('mousemove', this.onMouseMove.bind(this), false);
673f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
674f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
675f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
676f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Maximum movement for touch to be detected as a tap (in pixels).
677f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
678f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
679f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_ = 8;
680f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
681f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
682f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Maximum time for touch to be detected as a tap (in milliseconds).
683f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
684f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
685f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.MAX_TAP_DURATION_ = 500;
686f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
687f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
688f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Maximum distance from the first tap to the second tap to be considered
689f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * as a double tap.
690f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
691f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
692f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_ = 32;
693f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
694f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
695f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Maximum time for touch to be detected as a double tap (in milliseconds).
696f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
697f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
698f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_ = 1000;
699f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
700f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
701f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Returns an event's position.
702f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
703f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {MouseEvent|Touch} e Pointer position.
704f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {Object} A pair of x,y in page coordinates.
705f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
706f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
707f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.getPosition_ = function(e) {
708f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return {
709f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    x: e.pageX,
710f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    y: e.pageY
711f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  };
712f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
713f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
714f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
715f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Returns touch position or null if there is more than one touch position.
716f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
717f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {TouchEvent} e Event.
718f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {object?} A pair of x,y in page coordinates.
719f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
720f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
721f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.getTouchPosition_ = function(e) {
722f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (e.targetTouches.length == 1)
723f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return ImageEditor.MouseControl.getPosition_(e.targetTouches[0]);
724f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  else
725f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return null;
726f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
727f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
728f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
729f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Touch start handler.
730f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {TouchEvent} e Event.
731f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
732f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchStart = function(e) {
733f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var position = this.getTouchPosition_(e);
734f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (position) {
735f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.touchStartInfo_ = {
736f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      x: position.x,
737f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      y: position.y,
738f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      time: Date.now()
739f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    };
740f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y,
741f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                    true /* touch */);
742f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.dragHappened_ = false;
743f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    e.preventDefault();
744f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
745f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
746f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
747f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
748f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Touch end handler.
749f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {TouchEvent} e Event.
750f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
751f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchEnd = function(e) {
752f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.dragHappened_ && Date.now() - this.touchStartInfo_.time <=
753f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                             ImageEditor.MouseControl.MAX_TAP_DURATION_) {
754f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.buffer_.onClick(this.touchStartInfo_.x, this.touchStartInfo_.y);
755f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (this.previousTouchStartInfo_ &&
756f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        Date.now() - this.previousTouchStartInfo_.time <
757f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_) {
758f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      var prevTouchCircle = new Circle(
759f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          this.previousTouchStartInfo_.x,
760f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          this.previousTouchStartInfo_.y,
761f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_);
762f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (prevTouchCircle.inside(this.touchStartInfo_.x,
763f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                 this.touchStartInfo_.y)) {
764f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        this.doubleTapCallback_(this.touchStartInfo_.x, this.touchStartInfo_.y);
765f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
766f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
767f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.previousTouchStartInfo_ = this.touchStartInfo_;
768f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
769f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.previousTouchStartInfo_ = null;
770f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
771f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.onTouchCancel(e);
772f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
773f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
774f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
775f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Default double tap handler.
776f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} x X coordinate of the event.
777f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} y Y coordinate of the event.
778f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
779f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
780f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.doubleTapCallback_ = function(x, y) {};
781f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
782f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
783f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Sets callback to be called when double tap detected.
784f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function(number, number)} callback New double tap callback.
785f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
786f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.setDoubleTapCallback = function(callback) {
787f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.doubleTapCallback_ = callback;
788f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
789f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
790f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
791f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Touch cancel handler.
792f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
793f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchCancel = function() {
794f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.dragHandler_ = null;
795f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.dragHappened_ = false;
796f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.touchStartInfo_ = null;
797f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.lockMouse_(false);
798f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
799f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
800f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
801f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Touch move handler.
802f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {TouchEvent} e Event.
803f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
804f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchMove = function(e) {
805f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var position = this.getTouchPosition_(e);
806f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!position)
807f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
808f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
809f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.touchStartInfo_ && !this.dragHappened_) {
810f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    var tapCircle = new Circle(this.touchStartInfo_.x, this.touchStartInfo_.y,
811f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                    ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_);
812f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.dragHappened_ = !tapCircle.inside(position.x, position.y);
813f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
814f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.dragHandler_ && this.dragHappened_) {
815f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.dragHandler_(position.x, position.y);
816f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.lockMouse_(true);
817f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
818f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
819f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
820f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
821f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Mouse down handler.
822f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {MouseEvent} e Event.
823f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
824f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.onMouseDown = function(e) {
825f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var position = ImageEditor.MouseControl.getPosition_(e);
826f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
827f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y,
828f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                  false /* mouse */);
829f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.dragHappened_ = false;
830f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updateCursor_(position);
831f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
832f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
833f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
834f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Mouse up handler.
835f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {MouseEvent} e Event.
836f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
837f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.onMouseUp = function(e) {
838f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var position = ImageEditor.MouseControl.getPosition_(e);
839f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
840f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.dragHappened_) {
841f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.buffer_.onClick(position.x, position.y);
842f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
843f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.dragHandler_ = null;
844f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.dragHappened_ = false;
845f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.lockMouse_(false);
846f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
847f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
848f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
849f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Mouse move handler.
850f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {MouseEvent} e Event.
851f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
852f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.onMouseMove = function(e) {
853f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var position = ImageEditor.MouseControl.getPosition_(e);
854f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
855f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.dragHandler_ && !e.which) {
856f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // mouseup must have happened while the mouse was outside our window.
857f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.dragHandler_ = null;
858f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.lockMouse_(false);
859f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
860f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
861f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updateCursor_(position);
862f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.dragHandler_) {
863f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.dragHandler_(position.x, position.y);
864f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.dragHappened_ = true;
865f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.lockMouse_(true);
866f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
867f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
868f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
869f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
870f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Update the UI to reflect mouse drag state.
871f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {boolean} on True if dragging.
872f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
873f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
874f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.lockMouse_ = function(on) {
875f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.setAttribute(this.rootContainer_, 'mousedrag', on);
876f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
877f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
878f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
879f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Update the cursor.
880f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
881f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} position An object holding x and y properties.
882f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
883f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
884f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.MouseControl.prototype.updateCursor_ = function(position) {
885f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var oldCursor = this.container_.getAttribute('cursor');
886f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var newCursor = this.buffer_.getCursorStyle(
887f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      position.x, position.y, !!this.dragHandler_);
888f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (newCursor != oldCursor)  // Avoid flicker.
889f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.container_.setAttribute('cursor', newCursor);
890f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
891f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
892f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
893f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * A toolbar for the ImageEditor.
894f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLElement} parent The parent element.
895f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} displayStringFunction A string formatting function.
896f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} updateCallback The callback called when controls change.
897f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
898f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
899f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar = function(parent, displayStringFunction, updateCallback) {
900f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.wrapper_ = parent;
901f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.displayStringFunction_ = displayStringFunction;
902f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.updateCallback_ = updateCallback;
903f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
904f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
905f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
906f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Clear the toolbar.
907f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
908f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.clear = function() {
909f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ImageUtil.removeChildren(this.wrapper_);
910f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
911f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
912f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
913f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Create a control.
914f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} tagName The element tag name.
915f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {HTMLElement} The created control element.
916f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @private
917f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
918f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.create_ = function(tagName) {
919f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.wrapper_.ownerDocument.createElement(tagName);
920f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
921f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
922f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
923f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Add a control.
924f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLElement} element The control to add.
925f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {HTMLElement} The added element.
926f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
927f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.add = function(element) {
928f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.wrapper_.appendChild(element);
929f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return element;
930f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
931f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
932f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
933f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Add a text label.
934f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name Label name.
935f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {HTMLElement} The added label.
936f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
937f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.addLabel = function(name) {
938f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var label = this.create_('span');
939f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  label.textContent = this.displayStringFunction_(name);
940f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.add(label);
941f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
942f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
943f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
944f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Add a button.
945f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
946f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name Button name.
947f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} title Button title.
948f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} handler onClick handler.
949f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string=} opt_class Extra class name.
950f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {HTMLElement} The added button.
951f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
952f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.addButton = function(
953f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    name, title, handler, opt_class) {
954f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var button = this.create_('button');
955f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (opt_class) button.classList.add(opt_class);
956f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var label = this.create_('span');
957f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  label.textContent = this.displayStringFunction_(title);
958f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  button.appendChild(label);
959f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  button.label = this.displayStringFunction_(title);
960f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  button.addEventListener('click', handler, false);
961f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return this.add(button);
962f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
963f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
964f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
965f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Add a range control (scalar value picker).
966f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
967f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} name An option name.
968f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} title An option title.
969f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} min Min value of the option.
970f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} value Default value of the option.
971f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} max Max value of the options.
972f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} scale A number to multiply by when setting
973f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *                       min/value/max in DOM.
974f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {boolean=} opt_showNumeric True if numeric value should be displayed.
975f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {HTMLElement} Range element.
976f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
977f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.addRange = function(
978f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    name, title, min, value, max, scale, opt_showNumeric) {
979f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var self = this;
980f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
981f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  scale = scale || 1;
982f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
983f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var range = this.create_('input');
984f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
985f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.className = 'range';
986f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.type = 'range';
987f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.name = name;
988f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.min = Math.ceil(min * scale);
989f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.max = Math.floor(max * scale);
990f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
991f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var numeric = this.create_('div');
992f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  numeric.className = 'numeric';
993f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  function mirror() {
994f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    numeric.textContent = Math.round(range.getValue() * scale) / scale;
995f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
996f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
997f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.setValue = function(newValue) {
998f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    range.value = Math.round(newValue * scale);
999f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    mirror();
1000f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  };
1001f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1002f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.getValue = function() {
1003f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return Number(range.value) / scale;
1004f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  };
1005f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1006f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.reset = function() {
1007f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    range.setValue(value);
1008f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  };
1009f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1010f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.addEventListener('change',
1011f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      function() {
1012f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        mirror();
1013f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        self.updateCallback_(self.getOptions());
1014f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      },
1015f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      false);
1016f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1017f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.setValue(value);
1018f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1019f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var label = this.create_('div');
1020f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  label.textContent = this.displayStringFunction_(title);
1021f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  label.className = 'label ' + name;
1022f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.add(label);
1023f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.add(range);
1024f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1025f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (opt_showNumeric)
1026f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.add(numeric);
1027f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1028f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Swallow the left and right keys, so they are not handled by other
1029f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // listeners.
1030f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  range.addEventListener('keydown', function(e) {
1031f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (e.keyIdentifier === 'Left' || e.keyIdentifier === 'Right')
1032f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      e.stopPropagation();
1033f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  });
1034f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1035f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return range;
1036f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1037f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1038f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1039f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @return {Object} options A map of options.
1040f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1041f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.getOptions = function() {
1042f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var values = {};
1043f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (var child = this.wrapper_.firstChild; child; child = child.nextSibling) {
1044f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (child.name)
1045f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      values[child.name] = child.getValue();
1046f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
1047f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return values;
1048f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1049f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1050f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1051f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Reset the toolbar.
1052f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1053f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.reset = function() {
1054f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (var child = this.wrapper_.firstChild; child; child = child.nextSibling) {
1055f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (child.reset) child.reset();
1056f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
1057f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1058f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1059f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1060f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Show/hide the toolbar.
1061f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {boolean} on True if show.
1062f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1063f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Toolbar.prototype.show = function(on) {
1064f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.wrapper_.firstChild)
1065f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;  // Do not show empty toolbar;
1066f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1067f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.wrapper_.hidden = !on;
1068f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1069f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1070f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/** A prompt panel for the editor.
1071f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
1072f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {HTMLElement} container Container element.
1073f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} displayStringFunction A formatting function.
1074f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @constructor
1075f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1076f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Prompt = function(container, displayStringFunction) {
1077f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.container_ = container;
1078f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.displayStringFunction_ = displayStringFunction;
1079f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1080f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1081f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1082f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Reset the prompt.
1083f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1084f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Prompt.prototype.reset = function() {
1085f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.cancelTimer();
1086f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.wrapper_) {
1087f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.container_.removeChild(this.wrapper_);
1088f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.wrapper_ = null;
1089f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.prompt_ = null;
1090f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
1091f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1092f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1093f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1094f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Cancel the delayed action.
1095f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1096f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Prompt.prototype.cancelTimer = function() {
1097f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (this.timer_) {
1098f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    clearTimeout(this.timer_);
1099f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.timer_ = null;
1100f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
1101f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1102f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1103f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1104f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Schedule the delayed action.
1105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function} callback Callback.
1106f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} timeout Timeout.
1107f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1108f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Prompt.prototype.setTimer = function(callback, timeout) {
1109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.cancelTimer();
1110f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var self = this;
1111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.timer_ = setTimeout(function() {
1112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    self.timer_ = null;
1113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    callback();
1114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }, timeout);
1115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1116f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1117f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Show the prompt.
1119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
1120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} text The prompt text.
1121f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} timeout Timeout in ms.
1122f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} formatArgs varArgs for the formatting function.
1123f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Prompt.prototype.show = function(text, timeout, formatArgs) {
1125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.showAt.apply(this,
1126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      ['center'].concat(Array.prototype.slice.call(arguments)));
1127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *
1131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} pos The 'pos' attribute value.
1132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {string} text The prompt text.
1133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number} timeout Timeout in ms.
1134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {Object} formatArgs varArgs for the formatting function.
1135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1136f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Prompt.prototype.showAt = function(pos, text, timeout, formatArgs) {
1137f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.reset();
1138f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!text) return;
1139f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var document = this.container_.ownerDocument;
1141f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.wrapper_ = document.createElement('div');
1142f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.wrapper_.className = 'prompt-wrapper';
1143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.wrapper_.setAttribute('pos', pos);
1144f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.container_.appendChild(this.wrapper_);
1145f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1146f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.prompt_ = document.createElement('div');
1147f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.prompt_.className = 'prompt';
1148f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1149f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Create an extra wrapper which opacity can be manipulated separately.
1150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var tool = document.createElement('div');
1151f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  tool.className = 'dimmable';
1152f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.wrapper_.appendChild(tool);
1153f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  tool.appendChild(this.prompt_);
1154f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1155f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var args = [text].concat(Array.prototype.slice.call(arguments, 3));
1156f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.prompt_.textContent = this.displayStringFunction_.apply(null, args);
1157f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1158f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var close = document.createElement('div');
1159f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  close.className = 'close';
1160f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  close.addEventListener('click', this.hide.bind(this));
1161f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.prompt_.appendChild(close);
1162f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1163f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  setTimeout(
1164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      this.prompt_.setAttribute.bind(this.prompt_, 'state', 'fadein'), 0);
1165f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1166f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (timeout)
1167f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    this.setTimer(this.hide.bind(this), timeout);
1168f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1169f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)/**
1171f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Hide the prompt.
1172f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) */
1173f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)ImageEditor.Prompt.prototype.hide = function() {
1174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!this.prompt_) return;
1175f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.prompt_.setAttribute('state', 'fadeout');
1176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Allow some time for the animation to play out.
1177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  this.setTimer(this.reset.bind(this), 500);
1178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
1179