view cropper.js @ 47:cd63f47c09d9 default tip

Added tag rough-draft for changeset 89f1b54973b1
author Atul Varma <varmaa@toolness.com>
date Mon, 14 Dec 2009 11:35:43 -0800
parents 90e484302486
children
line wrap: on
line source

function ObserverManager(subject) {
  this.subject = subject;
  this.observers = {};

  var self = this;
  this.subject.addObserver = function() {
    self.add.apply(self, arguments);
  };
}

ObserverManager.prototype = {
  add: function add(topic, observer) {
    if (typeof(observer) == "function")
      observer = {observe: observer};
    if (!(topic in this.observers))
      this.observers[topic] = [];
    this.observers[topic].push(observer);
  },

  notify: function notify(topic) {
    if (!(topic in this.observers))
      this.observers[topic] = [];
    for (var i = 0; i < this.observers[topic].length; i++)
      this.observers[topic][i].observe(this.subject, topic);
  }
};

function Thumbnail(selectableImage, canvas, size) {
  canvas.width = size;
  canvas.height = size;

  this.selectableImage = selectableImage;
  this.canvas = canvas;
  this.size = size;
  this.selectableImage.addObserver("selection-changed", this);
}

Thumbnail.prototype = {
  observe: function observe(subject, topic) {
    var selection = this.selectableImage.getSelection();
    var ctx = this.canvas.getContext("2d");
    if (selection.width == 0 || selection.height == 0) {
      ctx.fillStyle = "rgb(255,255,255)";
      ctx.fillRect(0, 0, this.size, this.size);
    } else
      ctx.drawImage(this.selectableImage.img,
                    selection.left,
                    selection.top,
                    selection.width,
                    selection.height,
                    0,
                    0,
                    this.size,
                    this.size);
  }
};

function SelectableImage(url, canvas) {
  var img = new Image();

  this._isLoading = true;
  img._parent = this;
  img.onload = this.onImgLoad;
  img.src = url;

  canvas._parent = this;

  this.isDirty = false;
  this.canvas = canvas;
  this.img = img;
  this._selection = {top: 0,
                     left: 0,
                     width: 0,
                     height: 0};
  this._observers = new ObserverManager(this);
}

SelectableImage.prototype = {
  onImgLoad: function onImgLoad(evt) {
    var self = evt.target._parent;

    self._isLoading = false;
    self.canvas.width = self.img.width;
    self.canvas.height = self.img.height;
    $(self.canvas).mousedown(self.onDragStart);
    $(self.canvas).mouseup(self.onDragStop);
    self._markDirty();
    self._observers.notify("selection-changed");
  },

  onDragStart: function onDragStart(evt) {
    var self = evt.target._parent;
    var point = self._getCanvasPoint(evt);

    self._selection = {top: point.y,
                       left: point.x,
                       width: 0,
                       height: 0};
    $(self.canvas).bind("mousemove", self.onDragging);
  },

  onDragging: function onDragging(evt) {
    var self = evt.target._parent;
    var point = self._getCanvasPoint(evt);

    var width = point.x - self._selection.left;
    var height = point.y - self._selection.top;

    if (evt.shiftKey) {
      // Constrain dimensions to square.
      var maxDiff = Math.max(Math.abs(width),
                             Math.abs(height));
      if (width > 0)
        width = maxDiff;
      else
        width = -maxDiff;

      if (height > 0)
        height = maxDiff;
      else
        height = -maxDiff;
    }

    self._selection.width = width;
    self._selection.height = height;
    self._markDirty();
  },

  onDragStop: function onDragStop(evt) {
    var self = evt.target._parent;

    $(self.canvas).unbind("mousemove", self.onDragging);
    self._markDirty();
    self._observers.notify("selection-changed");
  },

  setSelection: function setSelection(selection) {
    this._selection = selection;
    if (!this._isLoading) {
      this._markDirty();
      this._observers.notify("selection-changed");
    }
  },

  getSelection: function getSelection() {
    var x = this._selection.left;
    var y = this._selection.top;
    var width = this._selection.width;
    var height = this._selection.height;

    if (width < 0) {
      x = x + width;
      width = -width;
    }
    if (height < 0) {
      y = y + height;
      height = -height;
    }

    return {top: y,
            left: x,
            width: width,
            height: height};
  },

  _getCanvasPoint: function _getCanvasPoint(evt) {
    var ofs = $(this.canvas).offset();
    return {x: Math.floor(evt.pageX - ofs.left),
            y: Math.floor(evt.pageY - ofs.top)};
  },

  _markDirty: function _markDirty() {
    var self = this;

    if (!this._isDirty) {
      this._isDirty = true;
      window.setTimeout(function() { self._render(); }, 0);
    }
  },

  _render: function _render() {
    var ctx = this.canvas.getContext("2d");

    this._isDirty = false;
    ctx.drawImage(this.img, 0, 0);

    var norm = this.getSelection();

    ctx.fillStyle = "rgba(0,0,0,0.75)";
    ctx.strokeStyle = "rgb(255,255,255)";
    ctx.lineWidth = 1.0;
    ctx.lineJoin = "bevel";
    ctx.fillRect(norm.left, norm.top,
                 norm.width, norm.height);
    ctx.strokeRect(norm.left, norm.top,
                   norm.width, norm.height);
  }
};

function CropperInterface(options) {
  var canvas = document.createElement("canvas");
  $(options.selectableContainer).append(canvas);
  this.selectableImage = new SelectableImage(options.url, canvas);

  var thumbnailSize = (options.thumbnailSize ||
                       $(options.thumbnailContainer).width());
  var thumbnailCanvas = document.createElement("canvas");
  $(options.thumbnailContainer).append(thumbnailCanvas);
  this.thumbnail = new Thumbnail(this.selectableImage,
                                 thumbnailCanvas,
                                 thumbnailSize);
}

CropperInterface.prototype = {
};