15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'use strict';
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ImageEditor is the top level object that holds together and connects
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * everything needed for image editing.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Viewport} viewport The viewport.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageView} imageView The ImageView containing the images to edit.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageEditor.Prompt} prompt Prompt instance.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} DOMContainers Various DOM containers required for the editor.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Array.<ImageEditor.Mode>} modes Available editor modes.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} displayStringFunction String formatting function.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function ImageEditor(
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    viewport, imageView, prompt, DOMContainers, modes, displayStringFunction) {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.rootContainer_ = DOMContainers.root;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_ = DOMContainers.image;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.modes_ = modes;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.displayStringFunction_ = displayStringFunction;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.removeChildren(this.container_);
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var document = this.container_.ownerDocument;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_ = viewport;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_.sizeByFrame(this.container_);
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.buffer_ = new ImageBuffer();
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_.addRepaintCallback(this.buffer_.draw.bind(this.buffer_));
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.imageView_ = imageView;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.imageView_.addContentCallback(this.onContentUpdate_.bind(this));
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.buffer_.addOverlay(this.imageView_);
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.panControl_ = new ImageEditor.MouseControl(
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.rootContainer_, this.container_, this.getBuffer());
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.panControl_.setDoubleTapCallback(this.onDoubleTap_.bind(this));
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.mainToolbar_ = new ImageEditor.Toolbar(
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DOMContainers.toolbar, displayStringFunction);
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.modeToolbar_ = new ImageEditor.Toolbar(
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DOMContainers.mode, displayStringFunction,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.onOptionsChange.bind(this));
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.prompt_ = prompt;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.createToolButtons();
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.commandQueue_ = null;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if no user commands are to be accepted.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.isLocked = function() {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !this.commandQueue_ || this.commandQueue_.isBusy();
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the command queue is busy.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.isBusy = function() {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.commandQueue_ && this.commandQueue_.isBusy();
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Reflect the locked state of the editor in the UI.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} on True if locked.
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.lockUI = function(on) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.setAttribute(this.rootContainer_, 'locked', on);
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Report the tool use to the metrics subsystem.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Action name.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.recordToolUse = function(name) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.metrics.recordEnum(
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ImageUtil.getMetricName('Tool'), name, this.actionNames_);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Content update handler.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.onContentUpdate_ = function() {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i != this.modes_.length; i++) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var mode = this.modes_[i];
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.setAttribute(mode.button_, 'disabled', !mode.isApplicable());
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Open the editing session for a new image.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} url Image url.
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} metadata Metadata.
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} effect Transition effect object.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(function)} saveFunction Image save function.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} displayCallback Display callback.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} loadCallback Load callback.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.openSession = function(
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url, metadata, effect, saveFunction, displayCallback, loadCallback) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.commandQueue_)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throw new Error('Session not closed');
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.lockUI(true);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.imageView_.load(
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url, metadata, effect, displayCallback, function(loadType, delay, error) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.lockUI(false);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.commandQueue_ = new CommandQueue(
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.container_.ownerDocument,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.imageView_.getCanvas(),
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        saveFunction);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.commandQueue_.attachUI(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.getImageView(), self.getPrompt(), self.lockUI.bind(self));
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.updateUndoRedo();
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    loadCallback(loadType, delay, error);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  });
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Close the current image editing session.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} callback Callback.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.closeSession = function(callback) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.getPrompt().hide();
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.imageView_.isLoading()) {
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (this.commandQueue_) {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      console.warn('Inconsistent image editor state');
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.commandQueue_ = null;
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.imageView_.cancelLoad();
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lockUI(false);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    callback();
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!this.commandQueue_) {
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Session is already closed.
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    callback();
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.executeWhenReady(callback);
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.commandQueue_.close();
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.commandQueue_ = null;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Commit the current operation and execute the action.
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} callback Callback.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.executeWhenReady = function(callback) {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.commandQueue_) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.leaveModeGently();
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.commandQueue_.executeWhenReady(callback);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!this.imageView_.isLoading())
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      console.warn('Inconsistent image editor state');
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    callback();
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {boolean} True if undo queue is not empty.
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ImageEditor.prototype.canUndo = function() {
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return this.commandQueue_ && this.commandQueue_.canUndo();
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Undo the recently executed command.
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.undo = function() {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.isLocked()) return;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.recordToolUse('undo');
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First undo click should dismiss the uncommitted modifications.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.currentMode_ && this.currentMode_.isUpdated()) {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.currentMode_.reset();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.getPrompt().hide();
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.leaveMode(false);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.commandQueue_.undo();
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updateUndoRedo();
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Redo the recently un-done command.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.redo = function() {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.isLocked()) return;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.recordToolUse('redo');
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.getPrompt().hide();
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.leaveMode(false);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.commandQueue_.redo();
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updateUndoRedo();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update Undo/Redo buttons state.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.updateUndoRedo = function() {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var canUndo = this.commandQueue_ && this.commandQueue_.canUndo();
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var canRedo = this.commandQueue_ && this.commandQueue_.canRedo();
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.setAttribute(this.undoButton_, 'disabled', !canUndo);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.redoButton_.hidden = !canRedo;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLCanvasElement} The current image canvas.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.getCanvas = function() {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.getImageView().getCanvas();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageBuffer} ImageBuffer instance.
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.getBuffer = function() { return this.buffer_ };
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageView} ImageView instance.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.getImageView = function() { return this.imageView_ };
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Viewport} Viewport instance.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.getViewport = function() { return this.viewport_ };
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageEditor.Prompt} Prompt instance.
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.getPrompt = function() { return this.prompt_ };
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle the toolbar controls update.
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} options A map of options.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.onOptionsChange = function(options) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.trace.resetTimer('update');
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.currentMode_) {
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.currentMode_.update(options);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.trace.reportTimer('update');
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ImageEditor.Mode represents a modal state dedicated to a specific operation.
264eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Inherits from ImageBuffer. Overlay to simplify the drawing of mode-specific
265eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * tools.
266eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch *
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name The mode name.
268eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {string} title The mode title.
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
272eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochImageEditor.Mode = function(name, title) {
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.name = name;
274eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  this.title = title;
275eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  this.message_ = 'GALLERY_ENTER_WHEN_DONE';
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype = {__proto__: ImageBuffer.Overlay.prototype };
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Viewport} Viewport instance.
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.getViewport = function() { return this.viewport_ };
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageView} ImageView instance.
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.getImageView = function() { return this.imageView_ };
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} The mode-specific message to be displayed when entering.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.getMessage = function() { return this.message_ };
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the mode is applicable in the current context.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.isApplicable = function() { return true };
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Called once after creating the mode button.
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageEditor} editor The editor instance.
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} button The mode button.
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.bind = function(editor, button) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.editor_ = editor;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.editor_.registerAction_(this.name);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.button_ = button;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_ = editor.getViewport();
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.imageView_ = editor.getImageView();
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Called before entering the mode.
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.setUp = function() {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.editor_.getBuffer().addOverlay(this);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updated_ = false;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Create mode-specific controls here.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageEditor.Toolbar} toolbar The toolbar to populate.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.createTools = function(toolbar) {};
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Called before exiting the mode.
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.cleanUpUI = function() {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.editor_.getBuffer().removeOverlay(this);
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Called after exiting the mode.
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.cleanUpCaches = function() {};
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Called when any of the controls changed its value.
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} options A map of options.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.update = function(options) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.markUpdated();
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Mark the editor mode as updated.
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.markUpdated = function() {
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updated_ = true;
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the mode controls changed.
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.isUpdated = function() { return this.updated_ };
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Resets the mode to a clean state.
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.prototype.reset = function() {
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.editor_.modeToolbar_.reset();
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updated_ = false;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * One-click editor tool, requires no interaction, just executes the command.
371eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch *
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name The mode name.
373eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {string} title The mode title.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Command} command The command to execute on click.
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
377eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochImageEditor.Mode.OneClick = function(name, title, command) {
378eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ImageEditor.Mode.call(this, name, title);
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.instant = true;
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.command_ = command;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.OneClick.prototype = {__proto__: ImageEditor.Mode.prototype};
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {Command} command.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Mode.OneClick.prototype.getCommand = function() {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.command_;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Register the action name. Required for metrics reporting.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Button name.
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.registerAction_ = function(name) {
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.actionNames_.push(name);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Populate the toolbar.
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.createToolButtons = function() {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.mainToolbar_.clear();
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.actionNames_ = [];
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
409eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  function createButton(name, title, handler) {
410eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return self.mainToolbar_.addButton(name,
411eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                       title,
412eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                       handler,
413eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                       name /* opt_className */);
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i != this.modes_.length; i++) {
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var mode = this.modes_[i];
418eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    mode.bind(this, createButton(mode.name,
419eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                 mode.title,
420eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                 this.enterMode.bind(this, mode)));
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
423eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  this.undoButton_ = createButton('undo',
424eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  'GALLERY_UNDO',
425eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  this.undo.bind(this));
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.registerAction_('undo');
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
428eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  this.redoButton_ = createButton('redo',
429eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  'GALLERY_REDO',
430eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  this.redo.bind(this));
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.registerAction_('redo');
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageEditor.Mode} The current mode.
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.getMode = function() { return this.currentMode_ };
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The user clicked on the mode button.
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageEditor.Mode} mode The new mode.
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.enterMode = function(mode) {
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.isLocked()) return;
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.currentMode_ == mode) {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Currently active editor tool clicked, commit if modified.
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.leaveMode(this.currentMode_.updated_);
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.recordToolUse(mode.name);
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.leaveModeGently();
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The above call could have caused a commit which might have initiated
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an asynchronous command execution. Wait for it to complete, then proceed
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // with the mode set up.
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.commandQueue_.executeWhenReady(this.setUpMode_.bind(this, mode));
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Set up the new editing mode.
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageEditor.Mode} mode The mode.
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.setUpMode_ = function(mode) {
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentTool_ = mode.button_;
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.setAttribute(this.currentTool_, 'pressed', true);
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentMode_ = mode;
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentMode_.setUp();
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.currentMode_.instant) {  // Instant tool.
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.leaveMode(true);
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.getPrompt().show(this.currentMode_.getMessage());
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.modeToolbar_.clear();
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentMode_.createTools(this.modeToolbar_);
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.modeToolbar_.show(true);
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The user clicked on 'OK' or 'Cancel' or on a different mode button.
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} commit True if commit is required.
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.leaveMode = function(commit) {
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.currentMode_) return;
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.currentMode_.instant) {
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.getPrompt().hide();
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.modeToolbar_.show(false);
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentMode_.cleanUpUI();
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (commit) {
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var self = this;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var command = this.currentMode_.getCommand();
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (command) {  // Could be null if the user did not do anything.
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.commandQueue_.execute(command);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateUndoRedo();
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentMode_.cleanUpCaches();
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentMode_ = null;
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.setAttribute(this.currentTool_, 'pressed', false);
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.currentTool_ = null;
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Leave the mode, commit only if required by the current mode.
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.leaveModeGently = function() {
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.leaveMode(this.currentMode_ &&
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 this.currentMode_.updated_ &&
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 this.currentMode_.implicitCommit);
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Enter the editor mode with the given name.
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Mode name.
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.enterModeByName_ = function(name) {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i != this.modes_.length; i++) {
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var mode = this.modes_[i];
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (mode.name == name) {
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!mode.button_.hasAttribute('disabled'))
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.enterMode(mode);
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  console.error('Mode "' + name + '" not found.');
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Key down handler.
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Event} event The keydown event.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if handled.
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.onKeyDown = function(event) {
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (util.getKeyModifiers(event) + event.keyIdentifier) {
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'U+001B': // Escape
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'Enter':
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.getMode()) {
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.leaveMode(event.keyIdentifier == 'Enter');
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return true;
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'Ctrl-U+005A':  // Ctrl+Z
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.commandQueue_.canUndo()) {
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.undo();
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return true;
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'Ctrl-U+0059':  // Ctrl+Y
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.commandQueue_.canRedo()) {
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.redo();
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return true;
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'U+0041':  // 'a'
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.enterModeByName_('autofix');
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'U+0042':  // 'b'
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.enterModeByName_('exposure');
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'U+0043':  // 'c'
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.enterModeByName_('crop');
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'U+004C':  // 'l'
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.enterModeByName_('rotate_left');
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 'U+0052':  // 'r'
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.enterModeByName_('rotate_right');
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Double tap handler.
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x X coordinate of the event.
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y coordinate of the event.
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.onDoubleTap_ = function(x, y) {
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.getMode()) {
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var action = this.buffer_.getDoubleTapAction(x, y);
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (action == ImageBuffer.DoubleTapAction.COMMIT)
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.leaveMode(true);
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (action == ImageBuffer.DoubleTapAction.CANCEL)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.leaveMode(false);
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Hide the tools that overlap the given rectangular frame.
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} frame Hide the tool that overlaps this rect.
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} transparent But do not hide the tool that is completely inside
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *                           this rect.
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.prototype.hideOverlappingTools = function(frame, transparent) {
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var tools = this.rootContainer_.ownerDocument.querySelectorAll('.dimmable');
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i != tools.length; i++) {
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var tool = tools[i];
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var toolRect = tool.getBoundingClientRect();
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.setAttribute(tool, 'dimmed',
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (frame && frame.intersects(toolRect)) &&
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !(transparent && transparent.contains(toolRect)));
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A helper object for panning the ImageBuffer.
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} rootContainer The top-level container.
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} container The container for mouse events.
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ImageBuffer} buffer Image buffer.
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl = function(rootContainer, container, buffer) {
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.rootContainer_ = rootContainer;
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_ = container;
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.buffer_ = buffer;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var handlers = {
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'touchstart': this.onTouchStart,
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'touchend': this.onTouchEnd,
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'touchcancel': this.onTouchCancel,
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'touchmove': this.onTouchMove,
6492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    'mousedown': this.onMouseDown,
6502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    'mouseup': this.onMouseUp
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var eventName in handlers) {
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.addEventListener(
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        eventName, handlers[eventName].bind(this), false);
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Mouse move handler has to be attached to the window to receive events
6592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // from outside of the window. See: http://crbug.com/155705
6602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  window.addEventListener('mousemove', this.onMouseMove.bind(this), false);
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Maximum movement for touch to be detected as a tap (in pixels).
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_ = 8;
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Maximum time for touch to be detected as a tap (in milliseconds).
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.MAX_TAP_DURATION_ = 500;
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Maximum distance from the first tap to the second tap to be considered
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * as a double tap.
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_ = 32;
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Maximum time for touch to be detected as a double tap (in milliseconds).
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_ = 1000;
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
68990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Returns an event's position.
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MouseEvent|Touch} e Pointer position.
69290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @return {Object} A pair of x,y in page coordinates.
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.getPosition_ = function(e) {
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
69790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    x: e.pageX,
69890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    y: e.pageY
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns touch position or null if there is more than one touch position.
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {TouchEvent} e Event.
70690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @return {object?} A pair of x,y in page coordinates.
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.getTouchPosition_ = function(e) {
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (e.targetTouches.length == 1)
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ImageEditor.MouseControl.getPosition_(e.targetTouches[0]);
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return null;
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Touch start handler.
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {TouchEvent} e Event.
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchStart = function(e) {
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var position = this.getTouchPosition_(e);
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (position) {
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.touchStartInfo_ = {
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      x: position.x,
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      y: position.y,
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time: Date.now()
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y,
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                    true /* touch */);
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.dragHappened_ = false;
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  e.preventDefault();
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Touch end handler.
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {TouchEvent} e Event.
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchEnd = function(e) {
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.dragHappened_ && Date.now() - this.touchStartInfo_.time <=
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             ImageEditor.MouseControl.MAX_TAP_DURATION_) {
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.buffer_.onClick(this.touchStartInfo_.x, this.touchStartInfo_.y);
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.previousTouchStartInfo_ &&
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Date.now() - this.previousTouchStartInfo_.time <
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ImageEditor.MouseControl.MAX_DOUBLE_TAP_DURATION_) {
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var prevTouchCircle = new Circle(
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.previousTouchStartInfo_.x,
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.previousTouchStartInfo_.y,
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ImageEditor.MouseControl.MAX_DISTANCE_FOR_DOUBLE_TAP_);
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (prevTouchCircle.inside(this.touchStartInfo_.x,
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 this.touchStartInfo_.y)) {
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.doubleTapCallback_(this.touchStartInfo_.x, this.touchStartInfo_.y);
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.previousTouchStartInfo_ = this.touchStartInfo_;
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.previousTouchStartInfo_ = null;
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.onTouchCancel(e);
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  e.preventDefault();
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Default double tap handler.
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x X coordinate of the event.
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y coordinate of the event.
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.doubleTapCallback_ = function(x, y) {};
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Sets callback to be called when double tap detected.
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(number, number)} callback New double tap callback.
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.setDoubleTapCallback = function(callback) {
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.doubleTapCallback_ = callback;
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Touch chancel handler.
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchCancel = function() {
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dragHandler_ = null;
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dragHappened_ = false;
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.touchStartInfo_ = null;
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.lockMouse_(false);
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Touch move handler.
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {TouchEvent} e Event.
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.onTouchMove = function(e) {
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var position = this.getTouchPosition_(e);
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!position)
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.touchStartInfo_ && !this.dragHappened_) {
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var tapCircle = new Circle(this.touchStartInfo_.x, this.touchStartInfo_.y,
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    ImageEditor.MouseControl.MAX_MOVEMENT_FOR_TAP_);
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.dragHappened_ = !tapCircle.inside(position.x, position.y);
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.dragHandler_ && this.dragHappened_) {
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.dragHandler_(position.x, position.y);
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lockMouse_(true);
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  e.preventDefault();
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Mouse down handler.
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MouseEvent} e Event.
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.onMouseDown = function(e) {
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var position = ImageEditor.MouseControl.getPosition_(e);
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dragHandler_ = this.buffer_.getDragHandler(position.x, position.y,
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  false /* mouse */);
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dragHappened_ = false;
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updateCursor_(position);
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  e.preventDefault();
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Mouse up handler.
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MouseEvent} e Event.
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.onMouseUp = function(e) {
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var position = ImageEditor.MouseControl.getPosition_(e);
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.dragHappened_) {
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.buffer_.onClick(position.x, position.y);
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dragHandler_ = null;
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.dragHappened_ = false;
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.lockMouse_(false);
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  e.preventDefault();
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Mouse move handler.
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MouseEvent} e Event.
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.onMouseMove = function(e) {
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var position = ImageEditor.MouseControl.getPosition_(e);
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.dragHandler_ && !e.which) {
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // mouseup must have happened while the mouse was outside our window.
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.dragHandler_ = null;
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lockMouse_(false);
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updateCursor_(position);
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.dragHandler_) {
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.dragHandler_(position.x, position.y);
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.dragHappened_ = true;
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lockMouse_(true);
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  e.preventDefault();
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update the UI to reflect mouse drag state.
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} on True if dragging.
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.lockMouse_ = function(on) {
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.setAttribute(this.rootContainer_, 'mousedrag', on);
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Update the cursor.
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
8742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} position An object holding x and y properties.
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.MouseControl.prototype.updateCursor_ = function(position) {
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var oldCursor = this.container_.getAttribute('cursor');
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var newCursor = this.buffer_.getCursorStyle(
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      position.x, position.y, !!this.dragHandler_);
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (newCursor != oldCursor)  // Avoid flicker.
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_.setAttribute('cursor', newCursor);
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A toolbar for the ImageEditor.
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} parent The parent element.
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} displayStringFunction A string formatting function.
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} updateCallback The callback called when controls change.
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar = function(parent, displayStringFunction, updateCallback) {
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrapper_ = parent;
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.displayStringFunction_ = displayStringFunction;
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.updateCallback_ = updateCallback;
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Clear the toolbar.
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.clear = function() {
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.removeChildren(this.wrapper_);
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Create a control.
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} tagName The element tag name.
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLElement} The created control element.
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.create_ = function(tagName) {
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.wrapper_.ownerDocument.createElement(tagName);
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Add a control.
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} element The control to add.
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLElement} The added element.
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.add = function(element) {
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrapper_.appendChild(element);
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return element;
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Add a text label.
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Label name.
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLElement} The added label.
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.addLabel = function(name) {
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var label = this.create_('span');
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  label.textContent = this.displayStringFunction_(name);
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.add(label);
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Add a button.
938eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch *
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Button name.
940eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {string} title Button title.
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} handler onClick handler.
9422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string=} opt_class Extra class name.
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLElement} The added button.
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.addButton = function(
946eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    name, title, handler, opt_class) {
9472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var button = this.create_('button');
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_class) button.classList.add(opt_class);
9492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var label = this.create_('span');
950eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  label.textContent = this.displayStringFunction_(title);
9512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  button.appendChild(label);
952eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  button.label = this.displayStringFunction_(title);
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  button.addEventListener('click', handler, false);
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.add(button);
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Add a range control (scalar value picker).
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name An option name.
961eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @param {string} title An option title.
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} min Min value of the option.
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} value Default value of the option.
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} max Max value of the options.
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} scale A number to multiply by when setting
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *                       min/value/max in DOM.
9672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_showNumeric True if numeric value should be displayed.
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLElement} Range element.
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.addRange = function(
971eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    name, title, min, value, max, scale, opt_showNumeric) {
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scale = scale || 1;
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var range = this.create_('input');
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.className = 'range';
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.type = 'range';
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.name = name;
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.min = Math.ceil(min * scale);
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.max = Math.floor(max * scale);
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var numeric = this.create_('div');
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  numeric.className = 'numeric';
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function mirror() {
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    numeric.textContent = Math.round(range.getValue() * scale) / scale;
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.setValue = function(newValue) {
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    range.value = Math.round(newValue * scale);
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mirror();
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.getValue = function() {
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Number(range.value) / scale;
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.reset = function() {
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    range.setValue(value);
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.addEventListener('change',
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function() {
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mirror();
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.updateCallback_(self.getOptions());
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      false);
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  range.setValue(value);
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var label = this.create_('div');
1013eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  label.textContent = this.displayStringFunction_(title);
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  label.className = 'label ' + name;
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.add(label);
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.add(range);
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_showNumeric) this.add(numeric);
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return range;
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {Object} options A map of options.
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.getOptions = function() {
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var values = {};
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var child = this.wrapper_.firstChild; child; child = child.nextSibling) {
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (child.name)
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      values[child.name] = child.getValue();
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values;
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Reset the toolbar.
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.reset = function() {
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var child = this.wrapper_.firstChild; child; child = child.nextSibling) {
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (child.reset) child.reset();
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Show/hide the toolbar.
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} on True if show.
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Toolbar.prototype.show = function(on) {
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.wrapper_.firstChild)
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Do not show empty toolbar;
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrapper_.hidden = !on;
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** A prompt panel for the editor.
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} container Container element.
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} displayStringFunction A formatting function.
10582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @constructor
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Prompt = function(container, displayStringFunction) {
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_ = container;
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.displayStringFunction_ = displayStringFunction;
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Reset the prompt.
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Prompt.prototype.reset = function() {
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.cancelTimer();
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.wrapper_) {
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_.removeChild(this.wrapper_);
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.wrapper_ = null;
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.prompt_ = null;
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Cancel the delayed action.
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Prompt.prototype.cancelTimer = function() {
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.timer_) {
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearTimeout(this.timer_);
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.timer_ = null;
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Schedule the delayed action.
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} callback Callback.
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} timeout Timeout.
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Prompt.prototype.setTimer = function(callback, timeout) {
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.cancelTimer();
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.timer_ = setTimeout(function() {
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.timer_ = null;
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    callback();
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }, timeout);
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Show the prompt.
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} text The prompt text.
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} timeout Timeout in ms.
11062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} formatArgs varArgs for the formatting fuction.
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Prompt.prototype.show = function(text, timeout, formatArgs) {
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.showAt.apply(this,
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ['center'].concat(Array.prototype.slice.call(arguments)));
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} pos The 'pos' attribute value.
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} text The prompt text.
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} timeout Timeout in ms.
11182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} formatArgs varArgs for the formatting fuction.
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Prompt.prototype.showAt = function(pos, text, timeout, formatArgs) {
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.reset();
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!text) return;
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var document = this.container_.ownerDocument;
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrapper_ = document.createElement('div');
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrapper_.className = 'prompt-wrapper';
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrapper_.setAttribute('pos', pos);
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_.appendChild(this.wrapper_);
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.prompt_ = document.createElement('div');
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.prompt_.className = 'prompt';
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create an extra wrapper which opacity can be manipulated separately.
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var tool = document.createElement('div');
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tool.className = 'dimmable';
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.wrapper_.appendChild(tool);
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tool.appendChild(this.prompt_);
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var args = [text].concat(Array.prototype.slice.call(arguments, 3));
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.prompt_.textContent = this.displayStringFunction_.apply(null, args);
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var close = document.createElement('div');
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close.className = 'close';
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close.addEventListener('click', this.hide.bind(this));
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.prompt_.appendChild(close);
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setTimeout(
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.prompt_.setAttribute.bind(this.prompt_, 'state', 'fadein'), 0);
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (timeout)
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.setTimer(this.hide.bind(this), timeout);
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Hide the prompt.
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageEditor.Prompt.prototype.hide = function() {
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.prompt_) return;
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.prompt_.setAttribute('state', 'fadeout');
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Allow some time for the animation to play out.
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.setTimer(this.reset.bind(this), 500);
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1163