15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Copyright 2014 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)// Namespace object for the utilities.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function ImageUtil() {}
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Performance trace.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.trace = (function() {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function PerformanceTrace() {
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lines_ = {};
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.timers_ = {};
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_ = null;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PerformanceTrace.prototype.bindToDOM = function(container) {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_ = container;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PerformanceTrace.prototype.report = function(key, value) {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!(key in this.lines_)) {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (this.container_) {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var div = this.lines_[key] = document.createElement('div');
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.container_.appendChild(div);
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.lines_[key] = {};
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lines_[key].textContent = key + ': ' + value;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (ImageUtil.trace.log) this.dumpLine(key);
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PerformanceTrace.prototype.resetTimer = function(key) {
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.timers_[key] = Date.now();
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PerformanceTrace.prototype.reportTimer = function(key) {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.report(key, (Date.now() - this.timers_[key]) + 'ms');
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PerformanceTrace.prototype.dump = function() {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var key in this.lines_)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.dumpLine(key);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PerformanceTrace.prototype.dumpLine = function(key) {
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    console.log('trace.' + this.lines_[key].textContent);
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new PerformanceTrace();
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)})();
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} min Minimum value.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} value Value to adjust.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} max Maximum value.
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} The closest to the |value| number in span [min, max].
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.clamp = function(min, value, max) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Math.max(min, Math.min(max, value));
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} min Minimum value.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} value Value to check.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} max Maximum value.
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {boolean} True if value is between.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.between = function(min, value, max) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (value - min) * (value - max) <= 0;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Rectangle class.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Rectangle constructor takes 0, 1, 2 or 4 arguments.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Supports following variants:
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   new Rect(left, top, width, height)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   new Rect(width, height)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   new Rect(rect)         // anything with left, top, width, height properties
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   new Rect(bounds)       // anything with left, top, right, bottom properties
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   new Rect(canvas|image) // anything with width and height properties.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   new Rect()             // empty rectangle.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function Rect() {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (arguments.length) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 4:
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.left = arguments[0];
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.top = arguments[1];
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.width = arguments[2];
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.height = arguments[3];
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 2:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.left = 0;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.top = 0;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.width = arguments[0];
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.height = arguments[1];
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 1: {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var source = arguments[0];
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ('left' in source && 'top' in source) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.left = source.left;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.top = source.top;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ('right' in source && 'bottom' in source) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.width = source.right - source.left;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.height = source.bottom - source.top;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.left = 0;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.top = 0;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ('width' in source && 'height' in source) {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.width = source.width;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.height = source.height;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break; // Fall through to the error message.
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 0:
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.left = 0;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.top = 0;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.width = 0;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.height = 0;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  console.error('Invalid Rect constructor arguments:',
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                Array.apply(null, arguments));
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
142116680a4aac90f2aa7413d9095a592090648e557Ben MurdochRect.prototype = {
143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  /**
144116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   * Obtains the x coordinate of right edge. The most right pixels in the
145116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   * rectangle are (x = right - 1) and the pixels (x = right) are not included
146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   * in the rectangle.
147116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   * @return {number}
148116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   */
149116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  get right() {
150116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return this.left + this.width;
151116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  },
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
153116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  /**
154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   * Obtains the y coordinate of bottom edge. The most bottom pixels in the
1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)   * rectangle are (y = bottom - 1) and the pixels (y = bottom) are not included
156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   * in the rectangle.
157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   * @return {number}
158116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch   */
159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  get bottom() {
160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return this.top + this.height;
161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch};
163116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} factor Factor to scale.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Rect} A rectangle with every dimension scaled.
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.scale = function(factor) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new Rect(
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.left * factor,
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.top * factor,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.width * factor,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.height * factor);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} dx Difference in X.
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} dy Difference in Y.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Rect} A rectangle shifted by (dx,dy), same size.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.shift = function(dx, dy) {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new Rect(this.left + dx, this.top + dy, this.width, this.height);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x Coordinate of the left top corner.
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Coordinate of the left top corner.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Rect} A rectangle with left==x and top==y, same size.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.moveTo = function(x, y) {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new Rect(x, y, this.width, this.height);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} dx Difference in X.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} dy Difference in Y.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Rect} A rectangle inflated by (dx, dy), same center.
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.inflate = function(dx, dy) {
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new Rect(
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.left - dx, this.top - dy, this.width + 2 * dx, this.height + 2 * dy);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x Coordinate of the point.
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Coordinate of the point.
2074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @return {boolean} True if the point lies inside the rectangle.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.inside = function(x, y) {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.left <= x && x < this.left + this.width &&
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         this.top <= y && y < this.top + this.height;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} rect Rectangle to check.
2164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @return {boolean} True if this rectangle intersects with the |rect|.
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.intersects = function(rect) {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (this.left + this.width) > rect.left &&
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (rect.left + rect.width) > this.left &&
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (this.top + this.height) > rect.top &&
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (rect.top + rect.height) > this.top;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} rect Rectangle to check.
2274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @return {boolean} True if this rectangle containing the |rect|.
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.contains = function(rect) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (this.left <= rect.left) &&
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (rect.left + rect.width) <= (this.left + this.width) &&
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (this.top <= rect.top) &&
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (rect.top + rect.height) <= (this.top + this.height);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if rectangle is empty.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.isEmpty = function() {
240a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return this.width === 0 || this.height === 0;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Clamp the rectangle to the bounds by moving it.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Decrease the size only if necessary.
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} bounds Bounds.
2474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @return {Rect} Calculated rectangle.
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.clamp = function(bounds) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var rect = new Rect(this);
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rect.width > bounds.width) {
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.left = bounds.left;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.width = bounds.width;
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (rect.left < bounds.left) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.left = bounds.left;
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (rect.left + rect.width >
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             bounds.left + bounds.width) {
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.left = bounds.left + bounds.width - rect.width;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rect.height > bounds.height) {
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.top = bounds.top;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.height = bounds.height;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (rect.top < bounds.top) {
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.top = bounds.top;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (rect.top + rect.height >
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             bounds.top + bounds.height) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rect.top = bounds.top + bounds.height - rect.height;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rect;
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} String representation.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.prototype.toString = function() {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return '(' + this.left + ',' + this.top + '):' +
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         '(' + (this.left + this.width) + ',' + (this.top + this.height) + ')';
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Useful shortcuts for drawing (static functions).
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Draw the image in context with appropriate scaling.
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {CanvasRenderingContext2D} context Context to draw.
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Image} image Image to draw.
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Rect=} opt_dstRect Rectangle in the canvas (whole canvas by default).
2914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {Rect=} opt_srcRect Rectangle in the image (whole image by default).
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.drawImage = function(context, image, opt_dstRect, opt_srcRect) {
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  opt_dstRect = opt_dstRect || new Rect(context.canvas);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  opt_srcRect = opt_srcRect || new Rect(image);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_dstRect.isEmpty() || opt_srcRect.isEmpty())
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.drawImage(image,
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_srcRect.left, opt_srcRect.top, opt_srcRect.width, opt_srcRect.height,
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_dstRect.left, opt_dstRect.top, opt_dstRect.width, opt_dstRect.height);
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Draw a box around the rectangle.
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {CanvasRenderingContext2D} context Context to draw.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} rect Rectangle.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.outline = function(context, rect) {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.strokeRect(
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rect.left - 0.5, rect.top - 0.5, rect.width + 1, rect.height + 1);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Fill the rectangle.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {CanvasRenderingContext2D} context Context to draw.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} rect Rectangle.
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.fill = function(context, rect) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.fillRect(rect.left, rect.top, rect.width, rect.height);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Fills the space between the two rectangles.
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {CanvasRenderingContext2D} context Context to draw.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} inner Inner rectangle.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} outer Outer rectangle.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rect.fillBetween = function(context, inner, outer) {
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var innerRight = inner.left + inner.width;
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var innerBottom = inner.top + inner.height;
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var outerRight = outer.left + outer.width;
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var outerBottom = outer.top + outer.height;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (inner.top > outer.top) {
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context.fillRect(
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        outer.left, outer.top, outer.width, inner.top - outer.top);
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (inner.left > outer.left) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context.fillRect(
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        outer.left, inner.top, inner.left - outer.left, inner.height);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (inner.width < outerRight) {
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context.fillRect(
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        innerRight, inner.top, outerRight - innerRight, inner.height);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (inner.height < outerBottom) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context.fillRect(
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        outer.left, innerBottom, outer.width, outerBottom - innerBottom);
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Circle class.
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x X coordinate of circle center.
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y coordinate of circle center.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} r Radius.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function Circle(x, y, r) {
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.x = x;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.y = y;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.squaredR = r * r;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Check if the point is inside the circle.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x X coordinate of the point.
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y coordinate of the point.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the point is inside.
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Circle.prototype.inside = function(x, y) {
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  x -= this.x;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  y -= this.y;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return x * x + y * y <= this.squaredR;
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copy an image applying scaling and rotation.
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} dst Destination.
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLImageElement} src Source.
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} scaleX Y scale transformation.
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} scaleY X scale transformation.
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number} angle (in radians).
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.drawImageTransformed = function(dst, src, scaleX, scaleY, angle) {
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var context = dst.getContext('2d');
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.save();
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.translate(context.canvas.width / 2, context.canvas.height / 2);
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.rotate(angle);
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.scale(scaleX, scaleY);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.drawImage(src, -src.width / 2, -src.height / 2);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.restore();
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Adds or removes an attribute to/from an HTML element.
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} element To be applied to.
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} attribute Name of attribute.
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} on True if add, false if remove.
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.setAttribute = function(element, attribute, on) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (on)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    element.setAttribute(attribute, '');
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    element.removeAttribute(attribute);
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Adds or removes CSS class to/from an HTML element.
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} element To be applied to.
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} className Name of CSS class.
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} on True if add, false if remove.
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.setClass = function(element, className, on) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var cl = element.classList;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (on)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cl.add(className);
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cl.remove(className);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
423a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * ImageLoader loads an image from a given Entry into a canvas in two steps:
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1. Loads the image into an HTMLImageElement.
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 2. Copies pixels from HTMLImageElement to HTMLCanvasElement. This is done
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    stripe-by-stripe to avoid freezing up the UI. The transform is taken into
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    account.
4287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) *
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLDocument} document Owner document.
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
432116680a4aac90f2aa7413d9095a592090648e557Ben MurdochImageUtil.ImageLoader = function(document) {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.document_ = document;
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  this.image_ = new Image();
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.generation_ = 0;
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * Loads an image.
4407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * TODO(mtomasz): Simplify, or even get rid of this class and merge with the
4417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * ThumbnaiLoader class.
4427d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) *
4431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Gallery.Item} item Item representing the image to be loaded.
44490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {function(HTMLCanvasElement, string=)} callback Callback to be
44590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) *     called when loaded. The second optional argument is an error identifier.
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_delay Load delay in milliseconds, useful to let the
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *     animations play out before the computation heavy image loading starts.
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
449116680a4aac90f2aa7413d9095a592090648e557Ben MurdochImageUtil.ImageLoader.prototype.load = function(item, callback, opt_delay) {
450116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var entry = item.getEntry();
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
452116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.cancel();
453a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.entry_ = entry;
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.callback_ = callback;
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The transform fetcher is not cancellable so we need a generation counter.
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var generation = ++this.generation_;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var onTransform = function(image, transform) {
459a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (generation === this.generation_) {
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.convertImage_(
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          image, transform || { scaleX: 1, scaleY: 1, rotate90: 0});
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
463116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }.bind(this);
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  var onError = function(opt_error) {
46690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    this.image_.onerror = null;
46790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    this.image_.onload = null;
46890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    var tmpCallback = this.callback_;
46990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    this.callback_ = null;
47090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    var emptyCanvas = this.document_.createElement('canvas');
47190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    emptyCanvas.width = 0;
47290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    emptyCanvas.height = 0;
47390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    tmpCallback(emptyCanvas, opt_error);
47490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }.bind(this);
47590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
476116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var loadImage = function() {
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.metrics.startInterval(ImageUtil.getMetricName('LoadTime'));
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.timeout_ = null;
4797d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
480116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.image_.onload = function() {
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.image_.onerror = null;
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this.image_.onload = null;
483116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      item.getFetchedMedia().then(function(fetchedMediaMetadata) {
484116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        onTransform(this.image_, fetchedMediaMetadata.imageTransform);
485116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      }.bind(this)).catch(function(error) {
486116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        console.error(error.stack || error);
487116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      });
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }.bind(this);
48990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    // The error callback has an optional error argument, which in case of a
4917d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    // general error should not be specified
492a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR');
4937d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
49446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // Load the image directly. The query parameter is workaround for
49546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // crbug.com/379678, which force to update the contents of the image.
4961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    this.image_.src = entry.toURL() + '?nocache=' + Date.now();
49790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }.bind(this);
49890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
4997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Loads the image. If already loaded, then forces a reload.
5007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  var startLoad = this.resetImage_.bind(this, function() {
501116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    loadImage();
5027d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }.bind(this), onError);
50390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_delay) {
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.timeout_ = setTimeout(startLoad, opt_delay);
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    startLoad();
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * Resets the image by forcing the garbage collection and clearing the src
5137d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * attribute.
5147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) *
5157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * @param {function()} onSuccess Success callback.
5167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * @param {function(opt_string)} onError Failure callback with an optional
5177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) *     error identifier.
5187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) * @private
5197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) */
5207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)ImageUtil.ImageLoader.prototype.resetImage_ = function(onSuccess, onError) {
5217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  var clearSrc = function() {
5227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    this.image_.onload = onSuccess;
5237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    this.image_.onerror = onSuccess;
5247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    this.image_.src = '';
5257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }.bind(this);
5267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
5277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  var emptyImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' +
5287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      'AAABAAEAAAICTAEAOw==';
5297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
530a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (this.image_.src !== emptyImage) {
5317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    // Load an empty image, then clear src.
5327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    this.image_.onload = clearSrc;
533a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    this.image_.onerror = onError.bind(this, 'GALLERY_IMAGE_ERROR');
5347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    this.image_.src = emptyImage;
5357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  } else {
5367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    // Empty image already loaded, so clear src immediately.
5377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    clearSrc();
5387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
5397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)};
5407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
5417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)/**
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if an image is loading.
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.ImageLoader.prototype.isBusy = function() {
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !!this.callback_;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
549a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {Entry} entry Image entry.
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if loader loads this image.
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
552a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageUtil.ImageLoader.prototype.isLoading = function(entry) {
553a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return this.isBusy() && util.isSameEntry(this.entry_, entry);
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {function} callback To be called when the image loaded.
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.ImageLoader.prototype.setCallback = function(callback) {
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.callback_ = callback;
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Stops loading image.
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.ImageLoader.prototype.cancel = function() {
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.callback_) return;
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.callback_ = null;
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.timeout_) {
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearTimeout(this.timeout_);
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.timeout_ = null;
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.image_) {
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.image_.onload = function() {};
5752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.image_.onerror = function() {};
5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    this.image_.src = '';
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.generation_++;  // Silence the transform fetcher if it is in progress.
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLImageElement} image Image to be transformed.
5834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {Object} transform transformation description to apply to the image.
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.ImageLoader.prototype.convertImage_ = function(image, transform) {
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var canvas = this.document_.createElement('canvas');
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (transform.rotate90 & 1) {  // Rotated +/-90deg, swap the dimensions.
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas.width = image.height;
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas.height = image.width;
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas.width = image.width;
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas.height = image.height;
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var context = canvas.getContext('2d');
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.save();
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.translate(canvas.width / 2, canvas.height / 2);
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.rotate(transform.rotate90 * Math.PI / 2);
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.scale(transform.scaleX, transform.scaleY);
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var stripCount = Math.ceil(image.width * image.height / (1 << 21));
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var step = Math.max(16, Math.ceil(image.height / stripCount)) & 0xFFFFF0;
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.copyStrip_(context, image, 0, step);
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {CanvasRenderingContext2D} context Context to draw.
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLImageElement} image Image to draw.
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} firstRow Number of the first pixel row to draw.
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} rowCount Count of pixel rows to draw.
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.ImageLoader.prototype.copyStrip_ = function(
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context, image, firstRow, rowCount) {
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var lastRow = Math.min(firstRow + rowCount, image.height);
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context.drawImage(
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      image, 0, firstRow, image.width, lastRow - firstRow,
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      -image.width / 2, firstRow - image.height / 2,
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      image.width, lastRow - firstRow);
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (lastRow === image.height) {
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    context.restore();
627a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (this.entry_.toURL().substr(0, 5) !== 'data:') {  // Ignore data urls.
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('LoadTime'));
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try {
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTimeout(this.callback_, 0, context.canvas);
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } catch (e) {
633c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      console.error(e);
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.callback_ = null;
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var self = this;
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.timeout_ = setTimeout(
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        function() {
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.timeout_ = null;
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.copyStrip_(context, image, lastRow, rowCount);
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }, 0);
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} element To remove children from.
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.removeChildren = function(element) {
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  element.textContent = '';
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name File name (with extension).
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} File name without extension.
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
657a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageUtil.getDisplayNameFromName = function(name) {
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var index = name.lastIndexOf('.');
659a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (index !== -1)
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return name.substr(0, index);
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return name;
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name File name.
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} File extension.
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.getExtensionFromFullName = function(name) {
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var index = name.lastIndexOf('.');
671a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (index !== -1)
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return name.substring(index);
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '';
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Metrics (from metrics.js) itnitialized by the File Manager from owner frame.
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {Object?}
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.metrics = null;
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Local name.
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} Full name.
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.getMetricName = function(name) {
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 'PhotoEditor.' + name;
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Used for metrics reporting, keep in sync with the histogram description.
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageUtil.FILE_TYPES = ['jpg', 'png', 'gif', 'bmp', 'webp'];
695