Update wmts-hidpi, add nicer-api-docs

This commit is contained in:
Andreas Hocevar
2014-05-06 13:02:46 -05:00
parent b3ac1afd00
commit 1e25fc5585
2239 changed files with 3726515 additions and 37010 deletions

View File

@@ -0,0 +1,441 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Graphics utility functions and factory methods.
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.graphics.AbstractGraphics');
goog.require('goog.dom');
goog.require('goog.graphics.Path');
goog.require('goog.math.Coordinate');
goog.require('goog.math.Size');
goog.require('goog.style');
goog.require('goog.ui.Component');
/**
* Base class for the different graphics. You should never construct objects
* of this class. Instead us goog.graphics.createGraphics
* @param {number|string} width The width in pixels or percent.
* @param {number|string} height The height in pixels or percent.
* @param {?number=} opt_coordWidth Optional coordinate system width - if
* omitted or null, defaults to same as width.
* @param {?number=} opt_coordHeight Optional coordinate system height - if
* omitted or null, defaults to same as height.
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
* document we want to render in.
* @constructor
* @extends {goog.ui.Component}
*/
goog.graphics.AbstractGraphics = function(width, height,
opt_coordWidth, opt_coordHeight,
opt_domHelper) {
goog.ui.Component.call(this, opt_domHelper);
/**
* Width of graphics in pixels or percentage points.
* @type {number|string}
* @protected
*/
this.width = width;
/**
* Height of graphics in pixels or precentage points.
* @type {number|string}
* @protected
*/
this.height = height;
/**
* Width of coordinate system in units.
* @type {?number}
* @protected
*/
this.coordWidth = opt_coordWidth || null;
/**
* Height of coordinate system in units.
* @type {?number}
* @protected
*/
this.coordHeight = opt_coordHeight || null;
};
goog.inherits(goog.graphics.AbstractGraphics, goog.ui.Component);
/**
* The root level group element.
* @type {goog.graphics.GroupElement?}
* @protected
*/
goog.graphics.AbstractGraphics.prototype.canvasElement = null;
/**
* Left coordinate of the view box
* @type {number}
* @protected
*/
goog.graphics.AbstractGraphics.prototype.coordLeft = 0;
/**
* Top coordinate of the view box
* @type {number}
* @protected
*/
goog.graphics.AbstractGraphics.prototype.coordTop = 0;
/**
* @return {goog.graphics.GroupElement} The root level canvas element.
*/
goog.graphics.AbstractGraphics.prototype.getCanvasElement = function() {
return this.canvasElement;
};
/**
* Changes the coordinate size.
* @param {number} coordWidth The coordinate width.
* @param {number} coordHeight The coordinate height.
*/
goog.graphics.AbstractGraphics.prototype.setCoordSize = function(coordWidth,
coordHeight) {
this.coordWidth = coordWidth;
this.coordHeight = coordHeight;
};
/**
* @return {goog.math.Size} The coordinate size.
*/
goog.graphics.AbstractGraphics.prototype.getCoordSize = function() {
if (this.coordWidth) {
return new goog.math.Size(this.coordWidth,
/** @type {number} */ (this.coordHeight));
} else {
return this.getPixelSize();
}
};
/**
* Changes the coordinate system position.
* @param {number} left The coordinate system left bound.
* @param {number} top The coordinate system top bound.
*/
goog.graphics.AbstractGraphics.prototype.setCoordOrigin = goog.abstractMethod;
/**
* @return {goog.math.Coordinate} The coordinate system position.
*/
goog.graphics.AbstractGraphics.prototype.getCoordOrigin = function() {
return new goog.math.Coordinate(this.coordLeft, this.coordTop);
};
/**
* Change the size of the canvas.
* @param {number} pixelWidth The width in pixels.
* @param {number} pixelHeight The height in pixels.
*/
goog.graphics.AbstractGraphics.prototype.setSize = goog.abstractMethod;
/**
* @return {goog.math.Size} The size of canvas.
* @deprecated Use getPixelSize.
*/
goog.graphics.AbstractGraphics.prototype.getSize = function() {
return this.getPixelSize();
};
/**
* @return {goog.math.Size?} Returns the number of pixels spanned by the
* surface, or null if the size could not be computed due to the size being
* specified in percentage points and the component not being in the
* document.
*/
goog.graphics.AbstractGraphics.prototype.getPixelSize = function() {
if (this.isInDocument()) {
return goog.style.getSize(this.getElement());
}
if (goog.isNumber(this.width) && goog.isNumber(this.height)) {
return new goog.math.Size(this.width, this.height);
}
return null;
};
/**
* @return {number} Returns the number of pixels per unit in the x direction.
*/
goog.graphics.AbstractGraphics.prototype.getPixelScaleX = function() {
var pixelSize = this.getPixelSize();
return pixelSize ? pixelSize.width / this.getCoordSize().width : 0;
};
/**
* @return {number} Returns the number of pixels per unit in the y direction.
*/
goog.graphics.AbstractGraphics.prototype.getPixelScaleY = function() {
var pixelSize = this.getPixelSize();
return pixelSize ? pixelSize.height / this.getCoordSize().height : 0;
};
/**
* Remove all drawing elements from the graphics.
*/
goog.graphics.AbstractGraphics.prototype.clear = goog.abstractMethod;
/**
* Remove a single drawing element from the surface. The default implementation
* assumes a DOM based drawing surface.
* @param {goog.graphics.Element} element The element to remove.
*/
goog.graphics.AbstractGraphics.prototype.removeElement = function(element) {
goog.dom.removeNode(element.getElement());
};
/**
* Sets the fill for the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
* @param {goog.graphics.Fill?} fill The fill object.
*/
goog.graphics.AbstractGraphics.prototype.setElementFill = goog.abstractMethod;
/**
* Sets the stroke for the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
* @param {goog.graphics.Stroke?} stroke The stroke object.
*/
goog.graphics.AbstractGraphics.prototype.setElementStroke = goog.abstractMethod;
/**
* Set the transformation of an element.
* @param {goog.graphics.Element} element The element wrapper.
* @param {number} x The x coordinate of the translation transform.
* @param {number} y The y coordinate of the translation transform.
* @param {number} angle The angle of the rotation transform.
* @param {number} centerX The horizontal center of the rotation transform.
* @param {number} centerY The vertical center of the rotation transform.
*/
goog.graphics.AbstractGraphics.prototype.setElementTransform =
goog.abstractMethod;
/**
* Draw a circle
*
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @param {number} r Radius length.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
* append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.EllipseElement} The newly created element.
*/
goog.graphics.AbstractGraphics.prototype.drawCircle = function(
cx, cy, r, stroke, fill, opt_group) {
return this.drawEllipse(cx, cy, r, r, stroke, fill, opt_group);
};
/**
* Draw an ellipse
*
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
* append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.EllipseElement} The newly created element.
*/
goog.graphics.AbstractGraphics.prototype.drawEllipse = goog.abstractMethod;
/**
* Draw a rectangle
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
* append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.RectElement} The newly created element.
*/
goog.graphics.AbstractGraphics.prototype.drawRect = goog.abstractMethod;
/**
* Draw a text string within a rectangle (drawing is horizontal)
*
* @param {string} text The text to draw.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @param {string} align Horizontal alignment: left (default), center, right.
* @param {string} vAlign Vertical alignment: top (default), center, bottom.
* @param {goog.graphics.Font} font Font describing the font properties.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
* append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.TextElement} The newly created element.
*/
goog.graphics.AbstractGraphics.prototype.drawText = function(
text, x, y, width, height, align, vAlign, font, stroke, fill, opt_group) {
var baseline = font.size / 2; // Baseline is middle of line
var textY;
if (vAlign == 'bottom') {
textY = y + height - baseline;
} else if (vAlign == 'center') {
textY = y + height / 2;
} else {
textY = y + baseline;
}
return this.drawTextOnLine(text, x, textY, x + width, textY, align,
font, stroke, fill, opt_group);
};
/**
* Draw a text string vertically centered on a given line.
*
* @param {string} text The text to draw.
* @param {number} x1 X coordinate of start of line.
* @param {number} y1 Y coordinate of start of line.
* @param {number} x2 X coordinate of end of line.
* @param {number} y2 Y coordinate of end of line.
* @param {string} align Horizontal alingnment: left (default), center, right.
* @param {goog.graphics.Font} font Font describing the font properties.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
* append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.TextElement} The newly created element.
*/
goog.graphics.AbstractGraphics.prototype.drawTextOnLine = goog.abstractMethod;
/**
* Draw a path.
*
* @param {!goog.graphics.Path} path The path object to draw.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
* append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.PathElement} The newly created element.
*/
goog.graphics.AbstractGraphics.prototype.drawPath = goog.abstractMethod;
/**
* Create an empty group of drawing elements.
*
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
* append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.GroupElement} The newly created group.
*/
goog.graphics.AbstractGraphics.prototype.createGroup = goog.abstractMethod;
/**
* Create an empty path.
*
* @return {goog.graphics.Path} The path.
* @deprecated Use {@code new goog.graphics.Path()}.
*/
goog.graphics.AbstractGraphics.prototype.createPath = function() {
return new goog.graphics.Path();
};
/**
* Measure and return the width (in pixels) of a given text string.
* Text measurement is needed to make sure a text can fit in the allocated
* area. The way text length is measured is by writing it into a div that is
* after the visible area, measure the div width, and immediatly erase the
* written value.
*
* @param {string} text The text string to measure.
* @param {goog.graphics.Font} font The font object describing the font style.
*
* @return {number} The width in pixels of the text strings.
*/
goog.graphics.AbstractGraphics.prototype.getTextWidth = goog.abstractMethod;
/**
* @return {boolean} Whether the underlying element can be cloned resulting in
* an accurate reproduction of the graphics contents.
*/
goog.graphics.AbstractGraphics.prototype.isDomClonable = function() {
return false;
};
/**
* Start preventing redraws - useful for chaining large numbers of changes
* together. Not guaranteed to do anything - i.e. only use this for
* optimization of a single code path.
*/
goog.graphics.AbstractGraphics.prototype.suspend = function() {
};
/**
* Stop preventing redraws. If any redraws had been prevented, a redraw will
* be done now.
*/
goog.graphics.AbstractGraphics.prototype.resume = function() {
};

View File

@@ -0,0 +1,587 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Provides an object representation of an AffineTransform and
* methods for working with it.
*/
goog.provide('goog.graphics.AffineTransform');
goog.require('goog.math');
/**
* Creates a 2D affine transform. An affine transform performs a linear
* mapping from 2D coordinates to other 2D coordinates that preserves the
* "straightness" and "parallelness" of lines.
*
* Such a coordinate transformation can be represented by a 3 row by 3 column
* matrix with an implied last row of [ 0 0 1 ]. This matrix transforms source
* coordinates (x,y) into destination coordinates (x',y') by considering them
* to be a column vector and multiplying the coordinate vector by the matrix
* according to the following process:
* <pre>
* [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
* [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
* [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
* </pre>
*
* This class is optimized for speed and minimizes calculations based on its
* knowledge of the underlying matrix (as opposed to say simply performing
* matrix multiplication).
*
* @param {number=} opt_m00 The m00 coordinate of the transform.
* @param {number=} opt_m10 The m10 coordinate of the transform.
* @param {number=} opt_m01 The m01 coordinate of the transform.
* @param {number=} opt_m11 The m11 coordinate of the transform.
* @param {number=} opt_m02 The m02 coordinate of the transform.
* @param {number=} opt_m12 The m12 coordinate of the transform.
* @constructor
*/
goog.graphics.AffineTransform = function(opt_m00, opt_m10, opt_m01,
opt_m11, opt_m02, opt_m12) {
if (arguments.length == 6) {
this.setTransform(/** @type {number} */ (opt_m00),
/** @type {number} */ (opt_m10),
/** @type {number} */ (opt_m01),
/** @type {number} */ (opt_m11),
/** @type {number} */ (opt_m02),
/** @type {number} */ (opt_m12));
} else if (arguments.length != 0) {
throw Error('Insufficient matrix parameters');
} else {
this.m00_ = this.m11_ = 1;
this.m10_ = this.m01_ = this.m02_ = this.m12_ = 0;
}
};
/**
* @return {boolean} Whether this transform is the identity transform.
*/
goog.graphics.AffineTransform.prototype.isIdentity = function() {
return this.m00_ == 1 && this.m10_ == 0 && this.m01_ == 0 &&
this.m11_ == 1 && this.m02_ == 0 && this.m12_ == 0;
};
/**
* @return {!goog.graphics.AffineTransform} A copy of this transform.
*/
goog.graphics.AffineTransform.prototype.clone = function() {
return new goog.graphics.AffineTransform(this.m00_, this.m10_, this.m01_,
this.m11_, this.m02_, this.m12_);
};
/**
* Sets this transform to the matrix specified by the 6 values.
*
* @param {number} m00 The m00 coordinate of the transform.
* @param {number} m10 The m10 coordinate of the transform.
* @param {number} m01 The m01 coordinate of the transform.
* @param {number} m11 The m11 coordinate of the transform.
* @param {number} m02 The m02 coordinate of the transform.
* @param {number} m12 The m12 coordinate of the transform.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.setTransform = function(m00, m10, m01,
m11, m02, m12) {
if (!goog.isNumber(m00) || !goog.isNumber(m10) || !goog.isNumber(m01) ||
!goog.isNumber(m11) || !goog.isNumber(m02) || !goog.isNumber(m12)) {
throw Error('Invalid transform parameters');
}
this.m00_ = m00;
this.m10_ = m10;
this.m01_ = m01;
this.m11_ = m11;
this.m02_ = m02;
this.m12_ = m12;
return this;
};
/**
* Sets this transform to be identical to the given transform.
*
* @param {!goog.graphics.AffineTransform} tx The transform to copy.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.copyFrom = function(tx) {
this.m00_ = tx.m00_;
this.m10_ = tx.m10_;
this.m01_ = tx.m01_;
this.m11_ = tx.m11_;
this.m02_ = tx.m02_;
this.m12_ = tx.m12_;
return this;
};
/**
* Concatenates this transform with a scaling transformation.
*
* @param {number} sx The x-axis scaling factor.
* @param {number} sy The y-axis scaling factor.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.scale = function(sx, sy) {
this.m00_ *= sx;
this.m10_ *= sx;
this.m01_ *= sy;
this.m11_ *= sy;
return this;
};
/**
* Pre-concatenates this transform with a scaling transformation,
* i.e. calculates the following matrix product:
*
* <pre>
* [sx 0 0] [m00 m01 m02]
* [ 0 sy 0] [m10 m11 m12]
* [ 0 0 1] [ 0 0 1]
* </pre>
*
* @param {number} sx The x-axis scaling factor.
* @param {number} sy The y-axis scaling factor.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.preScale = function(sx, sy) {
this.m00_ *= sx;
this.m01_ *= sx;
this.m02_ *= sx;
this.m10_ *= sy;
this.m11_ *= sy;
this.m12_ *= sy;
return this;
};
/**
* Concatenates this transform with a translate transformation.
*
* @param {number} dx The distance to translate in the x direction.
* @param {number} dy The distance to translate in the y direction.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.translate = function(dx, dy) {
this.m02_ += dx * this.m00_ + dy * this.m01_;
this.m12_ += dx * this.m10_ + dy * this.m11_;
return this;
};
/**
* Pre-concatenates this transform with a translate transformation,
* i.e. calculates the following matrix product:
*
* <pre>
* [1 0 dx] [m00 m01 m02]
* [0 1 dy] [m10 m11 m12]
* [0 0 1] [ 0 0 1]
* </pre>
*
* @param {number} dx The distance to translate in the x direction.
* @param {number} dy The distance to translate in the y direction.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.preTranslate = function(dx, dy) {
this.m02_ += dx;
this.m12_ += dy;
return this;
};
/**
* Concatenates this transform with a rotation transformation around an anchor
* point.
*
* @param {number} theta The angle of rotation measured in radians.
* @param {number} x The x coordinate of the anchor point.
* @param {number} y The y coordinate of the anchor point.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.rotate = function(theta, x, y) {
return this.concatenate(
goog.graphics.AffineTransform.getRotateInstance(theta, x, y));
};
/**
* Pre-concatenates this transform with a rotation transformation around an
* anchor point.
*
* @param {number} theta The angle of rotation measured in radians.
* @param {number} x The x coordinate of the anchor point.
* @param {number} y The y coordinate of the anchor point.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.preRotate = function(theta, x, y) {
return this.preConcatenate(
goog.graphics.AffineTransform.getRotateInstance(theta, x, y));
};
/**
* Concatenates this transform with a shear transformation.
*
* @param {number} shx The x shear factor.
* @param {number} shy The y shear factor.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.shear = function(shx, shy) {
var m00 = this.m00_;
var m10 = this.m10_;
this.m00_ += shy * this.m01_;
this.m10_ += shy * this.m11_;
this.m01_ += shx * m00;
this.m11_ += shx * m10;
return this;
};
/**
* Pre-concatenates this transform with a shear transformation.
* i.e. calculates the following matrix product:
*
* <pre>
* [ 1 shx 0] [m00 m01 m02]
* [shy 1 0] [m10 m11 m12]
* [ 0 0 1] [ 0 0 1]
* </pre>
*
* @param {number} shx The x shear factor.
* @param {number} shy The y shear factor.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.preShear = function(shx, shy) {
var m00 = this.m00_;
var m01 = this.m01_;
var m02 = this.m02_;
this.m00_ += shx * this.m10_;
this.m01_ += shx * this.m11_;
this.m02_ += shx * this.m12_;
this.m10_ += shy * m00;
this.m11_ += shy * m01;
this.m12_ += shy * m02;
return this;
};
/**
* @return {string} A string representation of this transform. The format of
* of the string is compatible with SVG matrix notation, i.e.
* "matrix(a,b,c,d,e,f)".
* @override
*/
goog.graphics.AffineTransform.prototype.toString = function() {
return 'matrix(' +
[this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_].join(
',') +
')';
};
/**
* @return {number} The scaling factor in the x-direction (m00).
*/
goog.graphics.AffineTransform.prototype.getScaleX = function() {
return this.m00_;
};
/**
* @return {number} The scaling factor in the y-direction (m11).
*/
goog.graphics.AffineTransform.prototype.getScaleY = function() {
return this.m11_;
};
/**
* @return {number} The translation in the x-direction (m02).
*/
goog.graphics.AffineTransform.prototype.getTranslateX = function() {
return this.m02_;
};
/**
* @return {number} The translation in the y-direction (m12).
*/
goog.graphics.AffineTransform.prototype.getTranslateY = function() {
return this.m12_;
};
/**
* @return {number} The shear factor in the x-direction (m01).
*/
goog.graphics.AffineTransform.prototype.getShearX = function() {
return this.m01_;
};
/**
* @return {number} The shear factor in the y-direction (m10).
*/
goog.graphics.AffineTransform.prototype.getShearY = function() {
return this.m10_;
};
/**
* Concatenates an affine transform to this transform.
*
* @param {!goog.graphics.AffineTransform} tx The transform to concatenate.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.concatenate = function(tx) {
var m0 = this.m00_;
var m1 = this.m01_;
this.m00_ = tx.m00_ * m0 + tx.m10_ * m1;
this.m01_ = tx.m01_ * m0 + tx.m11_ * m1;
this.m02_ += tx.m02_ * m0 + tx.m12_ * m1;
m0 = this.m10_;
m1 = this.m11_;
this.m10_ = tx.m00_ * m0 + tx.m10_ * m1;
this.m11_ = tx.m01_ * m0 + tx.m11_ * m1;
this.m12_ += tx.m02_ * m0 + tx.m12_ * m1;
return this;
};
/**
* Pre-concatenates an affine transform to this transform.
*
* @param {!goog.graphics.AffineTransform} tx The transform to preconcatenate.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.preConcatenate = function(tx) {
var m0 = this.m00_;
var m1 = this.m10_;
this.m00_ = tx.m00_ * m0 + tx.m01_ * m1;
this.m10_ = tx.m10_ * m0 + tx.m11_ * m1;
m0 = this.m01_;
m1 = this.m11_;
this.m01_ = tx.m00_ * m0 + tx.m01_ * m1;
this.m11_ = tx.m10_ * m0 + tx.m11_ * m1;
m0 = this.m02_;
m1 = this.m12_;
this.m02_ = tx.m00_ * m0 + tx.m01_ * m1 + tx.m02_;
this.m12_ = tx.m10_ * m0 + tx.m11_ * m1 + tx.m12_;
return this;
};
/**
* Transforms an array of coordinates by this transform and stores the result
* into a destination array.
*
* @param {!Array.<number>} src The array containing the source points
* as x, y value pairs.
* @param {number} srcOff The offset to the first point to be transformed.
* @param {!Array.<number>} dst The array into which to store the transformed
* point pairs.
* @param {number} dstOff The offset of the location of the first transformed
* point in the destination array.
* @param {number} numPts The number of points to tranform.
*/
goog.graphics.AffineTransform.prototype.transform = function(src, srcOff, dst,
dstOff, numPts) {
var i = srcOff;
var j = dstOff;
var srcEnd = srcOff + 2 * numPts;
while (i < srcEnd) {
var x = src[i++];
var y = src[i++];
dst[j++] = x * this.m00_ + y * this.m01_ + this.m02_;
dst[j++] = x * this.m10_ + y * this.m11_ + this.m12_;
}
};
/**
* @return {number} The determinant of this transform.
*/
goog.graphics.AffineTransform.prototype.getDeterminant = function() {
return this.m00_ * this.m11_ - this.m01_ * this.m10_;
};
/**
* Returns whether the transform is invertible. A transform is not invertible
* if the determinant is 0 or any value is non-finite or NaN.
*
* @return {boolean} Whether the transform is invertible.
*/
goog.graphics.AffineTransform.prototype.isInvertible = function() {
var det = this.getDeterminant();
return goog.math.isFiniteNumber(det) &&
goog.math.isFiniteNumber(this.m02_) &&
goog.math.isFiniteNumber(this.m12_) &&
det != 0;
};
/**
* @return {!goog.graphics.AffineTransform} An AffineTransform object
* representing the inverse transformation.
*/
goog.graphics.AffineTransform.prototype.createInverse = function() {
var det = this.getDeterminant();
return new goog.graphics.AffineTransform(
this.m11_ / det,
-this.m10_ / det,
-this.m01_ / det,
this.m00_ / det,
(this.m01_ * this.m12_ - this.m11_ * this.m02_) / det,
(this.m10_ * this.m02_ - this.m00_ * this.m12_) / det);
};
/**
* Creates a transform representing a scaling transformation.
*
* @param {number} sx The x-axis scaling factor.
* @param {number} sy The y-axis scaling factor.
* @return {!goog.graphics.AffineTransform} A transform representing a scaling
* transformation.
*/
goog.graphics.AffineTransform.getScaleInstance = function(sx, sy) {
return new goog.graphics.AffineTransform().setToScale(sx, sy);
};
/**
* Creates a transform representing a translation transformation.
*
* @param {number} dx The distance to translate in the x direction.
* @param {number} dy The distance to translate in the y direction.
* @return {!goog.graphics.AffineTransform} A transform representing a
* translation transformation.
*/
goog.graphics.AffineTransform.getTranslateInstance = function(dx, dy) {
return new goog.graphics.AffineTransform().setToTranslation(dx, dy);
};
/**
* Creates a transform representing a shearing transformation.
*
* @param {number} shx The x-axis shear factor.
* @param {number} shy The y-axis shear factor.
* @return {!goog.graphics.AffineTransform} A transform representing a shearing
* transformation.
*/
goog.graphics.AffineTransform.getShearInstance = function(shx, shy) {
return new goog.graphics.AffineTransform().setToShear(shx, shy);
};
/**
* Creates a transform representing a rotation transformation.
*
* @param {number} theta The angle of rotation measured in radians.
* @param {number} x The x coordinate of the anchor point.
* @param {number} y The y coordinate of the anchor point.
* @return {!goog.graphics.AffineTransform} A transform representing a rotation
* transformation.
*/
goog.graphics.AffineTransform.getRotateInstance = function(theta, x, y) {
return new goog.graphics.AffineTransform().setToRotation(theta, x, y);
};
/**
* Sets this transform to a scaling transformation.
*
* @param {number} sx The x-axis scaling factor.
* @param {number} sy The y-axis scaling factor.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.setToScale = function(sx, sy) {
return this.setTransform(sx, 0, 0, sy, 0, 0);
};
/**
* Sets this transform to a translation transformation.
*
* @param {number} dx The distance to translate in the x direction.
* @param {number} dy The distance to translate in the y direction.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.setToTranslation = function(dx, dy) {
return this.setTransform(1, 0, 0, 1, dx, dy);
};
/**
* Sets this transform to a shearing transformation.
*
* @param {number} shx The x-axis shear factor.
* @param {number} shy The y-axis shear factor.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.setToShear = function(shx, shy) {
return this.setTransform(1, shy, shx, 1, 0, 0);
};
/**
* Sets this transform to a rotation transformation.
*
* @param {number} theta The angle of rotation measured in radians.
* @param {number} x The x coordinate of the anchor point.
* @param {number} y The y coordinate of the anchor point.
* @return {!goog.graphics.AffineTransform} This affine transform.
*/
goog.graphics.AffineTransform.prototype.setToRotation = function(theta, x, y) {
var cos = Math.cos(theta);
var sin = Math.sin(theta);
return this.setTransform(cos, sin, -sin, cos,
x - x * cos + y * sin, y - x * sin - y * cos);
};
/**
* Compares two affine transforms for equality.
*
* @param {goog.graphics.AffineTransform} tx The other affine transform.
* @return {boolean} whether the two transforms are equal.
*/
goog.graphics.AffineTransform.prototype.equals = function(tx) {
if (this == tx) {
return true;
}
if (!tx) {
return false;
}
return this.m00_ == tx.m00_ &&
this.m01_ == tx.m01_ &&
this.m02_ == tx.m02_ &&
this.m10_ == tx.m10_ &&
this.m11_ == tx.m11_ &&
this.m12_ == tx.m12_;
};

View File

@@ -0,0 +1,792 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Objects representing shapes drawn on a canvas.
* @author robbyw@google.com (Robby Walker)
* @author wcrosby@google.com (Wayne Crosby)
*/
goog.provide('goog.graphics.CanvasEllipseElement');
goog.provide('goog.graphics.CanvasGroupElement');
goog.provide('goog.graphics.CanvasImageElement');
goog.provide('goog.graphics.CanvasPathElement');
goog.provide('goog.graphics.CanvasRectElement');
goog.provide('goog.graphics.CanvasTextElement');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.graphics.EllipseElement');
goog.require('goog.graphics.GroupElement');
goog.require('goog.graphics.ImageElement');
goog.require('goog.graphics.Path');
goog.require('goog.graphics.PathElement');
goog.require('goog.graphics.RectElement');
goog.require('goog.graphics.TextElement');
goog.require('goog.math');
goog.require('goog.string');
/**
* Object representing a group of objects in a canvas.
* This is an implementation of the goog.graphics.GroupElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {goog.graphics.CanvasGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.graphics.GroupElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.CanvasGroupElement = function(graphics) {
goog.graphics.GroupElement.call(this, null, graphics);
/**
* Children contained by this group.
* @type {Array.<goog.graphics.Element>}
* @private
*/
this.children_ = [];
};
goog.inherits(goog.graphics.CanvasGroupElement, goog.graphics.GroupElement);
/**
* Remove all drawing elements from the group.
* @override
*/
goog.graphics.CanvasGroupElement.prototype.clear = function() {
if (this.children_.length) {
this.children_.length = 0;
this.getGraphics().redraw();
}
};
/**
* Set the size of the group element.
* @param {number|string} width The width of the group element.
* @param {number|string} height The height of the group element.
* @override
*/
goog.graphics.CanvasGroupElement.prototype.setSize = function(width, height) {
// Do nothing.
};
/**
* Append a child to the group. Does not draw it
* @param {goog.graphics.Element} element The child to append.
*/
goog.graphics.CanvasGroupElement.prototype.appendChild = function(element) {
this.children_.push(element);
};
/**
* Draw the group.
* @param {CanvasRenderingContext2D} ctx The context to draw the element in.
*/
goog.graphics.CanvasGroupElement.prototype.draw = function(ctx) {
for (var i = 0, len = this.children_.length; i < len; i++) {
this.getGraphics().drawElement(this.children_[i]);
}
};
/**
* Thin wrapper for canvas ellipse elements.
* This is an implementation of the goog.graphics.EllipseElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.CanvasGraphics} graphics The graphics creating
* this element.
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
* @param {goog.graphics.Stroke} stroke The stroke to use for this element.
* @param {goog.graphics.Fill} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.EllipseElement}
*/
goog.graphics.CanvasEllipseElement = function(element, graphics,
cx, cy, rx, ry, stroke, fill) {
goog.graphics.EllipseElement.call(this, element, graphics, stroke, fill);
/**
* X coordinate of the ellipse center.
* @type {number}
* @private
*/
this.cx_ = cx;
/**
* Y coordinate of the ellipse center.
* @type {number}
* @private
*/
this.cy_ = cy;
/**
* Radius length for the x-axis.
* @type {number}
* @private
*/
this.rx_ = rx;
/**
* Radius length for the y-axis.
* @type {number}
* @private
*/
this.ry_ = ry;
/**
* Internal path approximating an ellipse.
* @type {goog.graphics.Path}
* @private
*/
this.path_ = new goog.graphics.Path();
this.setUpPath_();
/**
* Internal path element that actually does the drawing.
* @type {goog.graphics.CanvasPathElement}
* @private
*/
this.pathElement_ = new goog.graphics.CanvasPathElement(null, graphics,
this.path_, stroke, fill);
};
goog.inherits(goog.graphics.CanvasEllipseElement, goog.graphics.EllipseElement);
/**
* Sets up the path.
* @private
*/
goog.graphics.CanvasEllipseElement.prototype.setUpPath_ = function() {
this.path_.clear();
this.path_.moveTo(this.cx_ + goog.math.angleDx(0, this.rx_),
this.cy_ + goog.math.angleDy(0, this.ry_));
this.path_.arcTo(this.rx_, this.ry_, 0, 360);
this.path_.close();
};
/**
* Update the center point of the ellipse.
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @override
*/
goog.graphics.CanvasEllipseElement.prototype.setCenter = function(cx, cy) {
this.cx_ = cx;
this.cy_ = cy;
this.setUpPath_();
this.pathElement_.setPath(/** @type {!goog.graphics.Path} */ (this.path_));
};
/**
* Update the radius of the ellipse.
* @param {number} rx Center X coordinate.
* @param {number} ry Center Y coordinate.
* @override
*/
goog.graphics.CanvasEllipseElement.prototype.setRadius = function(rx, ry) {
this.rx_ = rx;
this.ry_ = ry;
this.setUpPath_();
this.pathElement_.setPath(/** @type {!goog.graphics.Path} */ (this.path_));
};
/**
* Draw the ellipse. Should be treated as package scope.
* @param {CanvasRenderingContext2D} ctx The context to draw the element in.
*/
goog.graphics.CanvasEllipseElement.prototype.draw = function(ctx) {
this.pathElement_.draw(ctx);
};
/**
* Thin wrapper for canvas rectangle elements.
* This is an implementation of the goog.graphics.RectElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.CanvasGraphics} graphics The graphics creating
* this element.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} w Width of rectangle.
* @param {number} h Height of rectangle.
* @param {goog.graphics.Stroke} stroke The stroke to use for this element.
* @param {goog.graphics.Fill} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.RectElement}
*/
goog.graphics.CanvasRectElement = function(element, graphics, x, y, w, h,
stroke, fill) {
goog.graphics.RectElement.call(this, element, graphics, stroke, fill);
/**
* X coordinate of the top left corner.
* @type {number}
* @private
*/
this.x_ = x;
/**
* Y coordinate of the top left corner.
* @type {number}
* @private
*/
this.y_ = y;
/**
* Width of the rectangle.
* @type {number}
* @private
*/
this.w_ = w;
/**
* Height of the rectangle.
* @type {number}
* @private
*/
this.h_ = h;
};
goog.inherits(goog.graphics.CanvasRectElement, goog.graphics.RectElement);
/**
* Update the position of the rectangle.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @override
*/
goog.graphics.CanvasRectElement.prototype.setPosition = function(x, y) {
this.x_ = x;
this.y_ = y;
if (this.drawn_) {
this.getGraphics().redraw();
}
};
/**
* Whether the rectangle has been drawn yet.
* @type {boolean}
* @private
*/
goog.graphics.CanvasRectElement.prototype.drawn_ = false;
/**
* Update the size of the rectangle.
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @override
*/
goog.graphics.CanvasRectElement.prototype.setSize = function(width, height) {
this.w_ = width;
this.h_ = height;
if (this.drawn_) {
this.getGraphics().redraw();
}
};
/**
* Draw the rectangle. Should be treated as package scope.
* @param {CanvasRenderingContext2D} ctx The context to draw the element in.
*/
goog.graphics.CanvasRectElement.prototype.draw = function(ctx) {
this.drawn_ = true;
ctx.beginPath();
ctx.moveTo(this.x_, this.y_);
ctx.lineTo(this.x_, this.y_ + this.h_);
ctx.lineTo(this.x_ + this.w_, this.y_ + this.h_);
ctx.lineTo(this.x_ + this.w_, this.y_);
ctx.closePath();
};
/**
* Thin wrapper for canvas path elements.
* This is an implementation of the goog.graphics.PathElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.CanvasGraphics} graphics The graphics creating
* this element.
* @param {!goog.graphics.Path} path The path object to draw.
* @param {goog.graphics.Stroke} stroke The stroke to use for this element.
* @param {goog.graphics.Fill} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.PathElement}
*/
goog.graphics.CanvasPathElement = function(element, graphics, path, stroke,
fill) {
goog.graphics.PathElement.call(this, element, graphics, stroke, fill);
this.setPath(path);
};
goog.inherits(goog.graphics.CanvasPathElement, goog.graphics.PathElement);
/**
* Whether the shape has been drawn yet.
* @type {boolean}
* @private
*/
goog.graphics.CanvasPathElement.prototype.drawn_ = false;
/**
* The path to draw.
* @type {goog.graphics.Path}
* @private
*/
goog.graphics.CanvasPathElement.prototype.path_;
/**
* Update the underlying path.
* @param {!goog.graphics.Path} path The path object to draw.
* @override
*/
goog.graphics.CanvasPathElement.prototype.setPath = function(path) {
this.path_ = path.isSimple() ? path :
goog.graphics.Path.createSimplifiedPath(path);
if (this.drawn_) {
this.getGraphics().redraw();
}
};
/**
* Draw the path. Should be treated as package scope.
* @param {CanvasRenderingContext2D} ctx The context to draw the element in.
* @suppress {deprecated} goog.graphics is deprecated.
*/
goog.graphics.CanvasPathElement.prototype.draw = function(ctx) {
this.drawn_ = true;
ctx.beginPath();
this.path_.forEachSegment(function(segment, args) {
switch (segment) {
case goog.graphics.Path.Segment.MOVETO:
ctx.moveTo(args[0], args[1]);
break;
case goog.graphics.Path.Segment.LINETO:
for (var i = 0; i < args.length; i += 2) {
ctx.lineTo(args[i], args[i + 1]);
}
break;
case goog.graphics.Path.Segment.CURVETO:
for (var i = 0; i < args.length; i += 6) {
ctx.bezierCurveTo(args[i], args[i + 1], args[i + 2],
args[i + 3], args[i + 4], args[i + 5]);
}
break;
case goog.graphics.Path.Segment.ARCTO:
throw Error('Canvas paths cannot contain arcs');
case goog.graphics.Path.Segment.CLOSE:
ctx.closePath();
break;
}
});
};
/**
* Thin wrapper for canvas text elements.
* This is an implementation of the goog.graphics.TextElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {!goog.graphics.CanvasGraphics} graphics The graphics creating
* this element.
* @param {string} text The text to draw.
* @param {number} x1 X coordinate of start of line.
* @param {number} y1 Y coordinate of start of line.
* @param {number} x2 X coordinate of end of line.
* @param {number} y2 Y coordinate of end of line.
* @param {?string} align Horizontal alignment: left (default), center, right.
* @param {!goog.graphics.Font} font Font describing the font properties.
* @param {goog.graphics.Stroke} stroke The stroke to use for this element.
* @param {goog.graphics.Fill} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.TextElement}
*/
goog.graphics.CanvasTextElement = function(graphics, text, x1, y1, x2, y2,
align, font, stroke, fill) {
var element = goog.dom.createDom(goog.dom.TagName.DIV, {
'style': 'display:table;position:absolute;padding:0;margin:0;border:0'
});
goog.graphics.TextElement.call(this, element, graphics, stroke, fill);
/**
* The text to draw.
* @type {string}
* @private
*/
this.text_ = text;
/**
* X coordinate of the start of the line the text is drawn on.
* @type {number}
* @private
*/
this.x1_ = x1;
/**
* Y coordinate of the start of the line the text is drawn on.
* @type {number}
* @private
*/
this.y1_ = y1;
/**
* X coordinate of the end of the line the text is drawn on.
* @type {number}
* @private
*/
this.x2_ = x2;
/**
* Y coordinate of the end of the line the text is drawn on.
* @type {number}
* @private
*/
this.y2_ = y2;
/**
* Horizontal alignment: left (default), center, right.
* @type {string}
* @private
*/
this.align_ = align || 'left';
/**
* Font object describing the font properties.
* @type {goog.graphics.Font}
* @private
*/
this.font_ = font;
/**
* The inner element that contains the text.
* @type {Element}
* @private
*/
this.innerElement_ = goog.dom.createDom('DIV', {
'style': 'display:table-cell;padding: 0;margin: 0;border: 0'
});
this.updateStyle_();
this.updateText_();
// Append to the DOM.
graphics.getElement().appendChild(element);
element.appendChild(this.innerElement_);
};
goog.inherits(goog.graphics.CanvasTextElement, goog.graphics.TextElement);
/**
* Update the displayed text of the element.
* @param {string} text The text to draw.
* @override
*/
goog.graphics.CanvasTextElement.prototype.setText = function(text) {
this.text_ = text;
this.updateText_();
};
/**
* Sets the fill for this element.
* @param {goog.graphics.Fill} fill The fill object.
* @override
*/
goog.graphics.CanvasTextElement.prototype.setFill = function(fill) {
this.fill = fill;
var element = this.getElement();
if (element) {
element.style.color = fill.getColor() || fill.getColor1();
}
};
/**
* Sets the stroke for this element.
* @param {goog.graphics.Stroke} stroke The stroke object.
* @override
*/
goog.graphics.CanvasTextElement.prototype.setStroke = function(stroke) {
// Ignore stroke
};
/**
* Draw the text. Should be treated as package scope.
* @param {CanvasRenderingContext2D} ctx The context to draw the element in.
*/
goog.graphics.CanvasTextElement.prototype.draw = function(ctx) {
// Do nothing - the text is already drawn.
};
/**
* Update the styles of the DIVs.
* @private
*/
goog.graphics.CanvasTextElement.prototype.updateStyle_ = function() {
var x1 = this.x1_;
var x2 = this.x2_;
var y1 = this.y1_;
var y2 = this.y2_;
var align = this.align_;
var font = this.font_;
var style = this.getElement().style;
var scaleX = this.getGraphics().getPixelScaleX();
var scaleY = this.getGraphics().getPixelScaleY();
if (x1 == x2) {
// Special case vertical text
style.lineHeight = '90%';
this.innerElement_.style.verticalAlign = align == 'center' ? 'middle' :
align == 'left' ? (y1 < y2 ? 'top' : 'bottom') :
y1 < y2 ? 'bottom' : 'top';
style.textAlign = 'center';
var w = font.size * scaleX;
style.top = Math.round(Math.min(y1, y2) * scaleY) + 'px';
style.left = Math.round((x1 - w / 2) * scaleX) + 'px';
style.width = Math.round(w) + 'px';
style.height = Math.abs(y1 - y2) * scaleY + 'px';
style.fontSize = font.size * 0.6 * scaleY + 'pt';
} else {
style.lineHeight = '100%';
this.innerElement_.style.verticalAlign = 'top';
style.textAlign = align;
style.top = Math.round(((y1 + y2) / 2 - font.size * 2 / 3) * scaleY) + 'px';
style.left = Math.round(x1 * scaleX) + 'px';
style.width = Math.round(Math.abs(x2 - x1) * scaleX) + 'px';
style.height = 'auto';
style.fontSize = font.size * scaleY + 'pt';
}
style.fontWeight = font.bold ? 'bold' : 'normal';
style.fontStyle = font.italic ? 'italic' : 'normal';
style.fontFamily = font.family;
var fill = this.getFill();
style.color = fill.getColor() || fill.getColor1();
};
/**
* Update the text content.
* @private
*/
goog.graphics.CanvasTextElement.prototype.updateText_ = function() {
if (this.x1_ == this.x2_) {
// Special case vertical text
this.innerElement_.innerHTML =
goog.array.map(this.text_.split(''),
function(entry) { return goog.string.htmlEscape(entry); }).
join('<br>');
} else {
this.innerElement_.innerHTML = goog.string.htmlEscape(this.text_);
}
};
/**
* Thin wrapper for canvas image elements.
* This is an implementation of the goog.graphics.ImageElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.CanvasGraphics} graphics The graphics creating
* this element.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} w Width of rectangle.
* @param {number} h Height of rectangle.
* @param {string} src Source of the image.
* @constructor
* @extends {goog.graphics.ImageElement}
*/
goog.graphics.CanvasImageElement = function(element, graphics, x, y, w, h,
src) {
goog.graphics.ImageElement.call(this, element, graphics);
/**
* X coordinate of the top left corner.
* @type {number}
* @private
*/
this.x_ = x;
/**
* Y coordinate of the top left corner.
* @type {number}
* @private
*/
this.y_ = y;
/**
* Width of the rectangle.
* @type {number}
* @private
*/
this.w_ = w;
/**
* Height of the rectangle.
* @type {number}
* @private
*/
this.h_ = h;
/**
* URL of the image source.
* @type {string}
* @private
*/
this.src_ = src;
};
goog.inherits(goog.graphics.CanvasImageElement, goog.graphics.ImageElement);
/**
* Whether the image has been drawn yet.
* @type {boolean}
* @private
*/
goog.graphics.CanvasImageElement.prototype.drawn_ = false;
/**
* Update the position of the image.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @override
*/
goog.graphics.CanvasImageElement.prototype.setPosition = function(x, y) {
this.x_ = x;
this.y_ = y;
if (this.drawn_) {
this.getGraphics().redraw();
}
};
/**
* Update the size of the image.
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @override
*/
goog.graphics.CanvasImageElement.prototype.setSize = function(width, height) {
this.w_ = width;
this.h_ = height;
if (this.drawn_) {
this.getGraphics().redraw();
}
};
/**
* Update the source of the image.
* @param {string} src Source of the image.
* @override
*/
goog.graphics.CanvasImageElement.prototype.setSource = function(src) {
this.src_ = src;
if (this.drawn_) {
// TODO(robbyw): Probably need to reload the image here.
this.getGraphics().redraw();
}
};
/**
* Draw the image. Should be treated as package scope.
* @param {CanvasRenderingContext2D} ctx The context to draw the element in.
*/
goog.graphics.CanvasImageElement.prototype.draw = function(ctx) {
if (this.img_) {
if (this.w_ && this.h_) {
// If the image is already loaded, draw it.
ctx.drawImage(this.img_, this.x_, this.y_, this.w_, this.h_);
}
this.drawn_ = true;
} else {
// Otherwise, load it.
var img = new Image();
img.onload = goog.bind(this.handleImageLoad_, this, img);
// TODO(robbyw): Handle image load errors.
img.src = this.src_;
}
};
/**
* Handle an image load.
* @param {Element} img The image element that finished loading.
* @private
*/
goog.graphics.CanvasImageElement.prototype.handleImageLoad_ = function(img) {
this.img_ = img;
// TODO(robbyw): Add a small delay to catch batched images
this.getGraphics().redraw();
};

View File

@@ -0,0 +1,665 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview CanvasGraphics sub class that uses the canvas tag for drawing.
* @author robbyw@google.com (Robby Walker)
* @author wcrosby@google.com (Wayne Crosby)
*/
goog.provide('goog.graphics.CanvasGraphics');
goog.require('goog.events.EventType');
goog.require('goog.graphics.AbstractGraphics');
goog.require('goog.graphics.CanvasEllipseElement');
goog.require('goog.graphics.CanvasGroupElement');
goog.require('goog.graphics.CanvasImageElement');
goog.require('goog.graphics.CanvasPathElement');
goog.require('goog.graphics.CanvasRectElement');
goog.require('goog.graphics.CanvasTextElement');
goog.require('goog.graphics.SolidFill');
goog.require('goog.math.Size');
goog.require('goog.style');
/**
* A Graphics implementation for drawing using canvas.
* @param {string|number} width The (non-zero) width in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {string|number} height The (non-zero) height in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {?number=} opt_coordWidth The coordinate width - if
* omitted or null, defaults to same as width.
* @param {?number=} opt_coordHeight The coordinate height - if
* omitted or null, defaults to same as height.
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
* document we want to render in.
* @constructor
* @extends {goog.graphics.AbstractGraphics}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.CanvasGraphics = function(width, height,
opt_coordWidth, opt_coordHeight,
opt_domHelper) {
goog.graphics.AbstractGraphics.call(this, width, height,
opt_coordWidth, opt_coordHeight,
opt_domHelper);
};
goog.inherits(goog.graphics.CanvasGraphics, goog.graphics.AbstractGraphics);
/**
* Sets the fill for the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element
* wrapper.
* @param {goog.graphics.Fill} fill The fill object.
* @override
*/
goog.graphics.CanvasGraphics.prototype.setElementFill = function(element,
fill) {
this.redraw();
};
/**
* Sets the stroke for the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element
* wrapper.
* @param {goog.graphics.Stroke} stroke The stroke object.
* @override
*/
goog.graphics.CanvasGraphics.prototype.setElementStroke = function(
element, stroke) {
this.redraw();
};
/**
* Set the transformation of an element.
* @param {goog.graphics.Element} element The element wrapper.
* @param {number} x The x coordinate of the translation transform.
* @param {number} y The y coordinate of the translation transform.
* @param {number} angle The angle of the rotation transform.
* @param {number} centerX The horizontal center of the rotation transform.
* @param {number} centerY The vertical center of the rotation transform.
* @override
*/
goog.graphics.CanvasGraphics.prototype.setElementTransform = function(element,
x, y, angle, centerX, centerY) {
this.redraw();
};
/**
* Push an element transform on to the transform stack.
* @param {goog.graphics.Element} element The transformed element.
*/
goog.graphics.CanvasGraphics.prototype.pushElementTransform = function(
element) {
var ctx = this.getContext();
ctx.save();
var transform = element.getTransform();
// TODO(robbyw): Test for unsupported transforms i.e. skews.
var tx = transform.getTranslateX();
var ty = transform.getTranslateY();
if (tx || ty) {
ctx.translate(tx, ty);
}
var sinTheta = transform.getShearY();
if (sinTheta) {
ctx.rotate(Math.asin(sinTheta));
}
};
/**
* Pop an element transform off of the transform stack.
*/
goog.graphics.CanvasGraphics.prototype.popElementTransform = function() {
this.getContext().restore();
};
/**
* Creates the DOM representation of the graphics area.
* @override
*/
goog.graphics.CanvasGraphics.prototype.createDom = function() {
var element = this.dom_.createDom('div',
{'style': 'position:relative;overflow:hidden'});
this.setElementInternal(element);
this.canvas_ = this.dom_.createDom('canvas');
element.appendChild(this.canvas_);
/**
* The main canvas element.
* @type {goog.graphics.CanvasGroupElement}
*/
this.canvasElement = new goog.graphics.CanvasGroupElement(this);
this.lastGroup_ = this.canvasElement;
this.redrawTimeout_ = 0;
this.updateSize();
};
/**
* Clears the drawing context object in response to actions that make the old
* context invalid - namely resize of the canvas element.
* @private
*/
goog.graphics.CanvasGraphics.prototype.clearContext_ = function() {
this.context_ = null;
};
/**
* Returns the drawing context.
* @return {Object} The canvas element rendering context.
*/
goog.graphics.CanvasGraphics.prototype.getContext = function() {
if (!this.getElement()) {
this.createDom();
}
if (!this.context_) {
this.context_ = this.canvas_.getContext('2d');
this.context_.save();
}
return this.context_;
};
/**
* Changes the coordinate system position.
* @param {number} left The coordinate system left bound.
* @param {number} top The coordinate system top bound.
* @override
*/
goog.graphics.CanvasGraphics.prototype.setCoordOrigin = function(left, top) {
this.coordLeft = left;
this.coordTop = top;
this.redraw();
};
/**
* Changes the coordinate size.
* @param {number} coordWidth The coordinate width.
* @param {number} coordHeight The coordinate height.
* @override
*/
goog.graphics.CanvasGraphics.prototype.setCoordSize = function(coordWidth,
coordHeight) {
goog.graphics.CanvasGraphics.superClass_.setCoordSize.apply(this, arguments);
this.redraw();
};
/**
* Change the size of the canvas.
* @param {number} pixelWidth The width in pixels.
* @param {number} pixelHeight The height in pixels.
* @override
*/
goog.graphics.CanvasGraphics.prototype.setSize = function(pixelWidth,
pixelHeight) {
this.width = pixelWidth;
this.height = pixelHeight;
this.updateSize();
this.redraw();
};
/** @override */
goog.graphics.CanvasGraphics.prototype.getPixelSize = function() {
// goog.style.getSize does not work for Canvas elements. We
// have to compute the size manually if it is percentage based.
var width = this.width;
var height = this.height;
var computeWidth = goog.isString(width) && width.indexOf('%') != -1;
var computeHeight = goog.isString(height) && height.indexOf('%') != -1;
if (!this.isInDocument() && (computeWidth || computeHeight)) {
return null;
}
var parent;
var parentSize;
if (computeWidth) {
parent = /** @type {Element} */ (this.getElement().parentNode);
parentSize = goog.style.getSize(parent);
width = parseFloat(/** @type {string} */ (width)) * parentSize.width / 100;
}
if (computeHeight) {
parent = parent || /** @type {Element} */ (this.getElement().parentNode);
parentSize = parentSize || goog.style.getSize(parent);
height = parseFloat(/** @type {string} */ (height)) * parentSize.height /
100;
}
return new goog.math.Size(/** @type {number} */ (width),
/** @type {number} */ (height));
};
/**
* Update the size of the canvas.
*/
goog.graphics.CanvasGraphics.prototype.updateSize = function() {
goog.style.setSize(this.getElement(), this.width, this.height);
var pixels = this.getPixelSize();
if (pixels) {
goog.style.setSize(this.canvas_,
/** @type {number} */ (pixels.width),
/** @type {number} */ (pixels.height));
this.canvas_.width = pixels.width;
this.canvas_.height = pixels.height;
this.clearContext_();
}
};
/**
* Reset the canvas.
*/
goog.graphics.CanvasGraphics.prototype.reset = function() {
var ctx = this.getContext();
ctx.restore();
var size = this.getPixelSize();
if (size.width && size.height) {
ctx.clearRect(0, 0, size.width, size.height);
}
ctx.save();
};
/**
* Remove all drawing elements from the graphics.
* @override
*/
goog.graphics.CanvasGraphics.prototype.clear = function() {
this.reset();
this.canvasElement.clear();
var el = this.getElement();
// Remove all children (text nodes) except the canvas (which is at index 0)
while (el.childNodes.length > 1) {
el.removeChild(el.lastChild);
}
};
/**
* Redraw the entire canvas.
*/
goog.graphics.CanvasGraphics.prototype.redraw = function() {
if (this.preventRedraw_) {
this.needsRedraw_ = true;
return;
}
if (this.isInDocument()) {
this.reset();
if (this.coordWidth) {
var pixels = this.getPixelSize();
this.getContext().scale(pixels.width / this.coordWidth,
pixels.height / this.coordHeight);
}
if (this.coordLeft || this.coordTop) {
this.getContext().translate(-this.coordLeft, -this.coordTop);
}
this.pushElementTransform(this.canvasElement);
this.canvasElement.draw(this.context_);
this.popElementTransform();
}
};
/**
* Draw an element, including any stroke or fill.
* @param {goog.graphics.Element} element The element to draw.
*/
goog.graphics.CanvasGraphics.prototype.drawElement = function(element) {
if (element instanceof goog.graphics.CanvasTextElement) {
// Don't draw text since that is not implemented using canvas.
return;
}
var ctx = this.getContext();
this.pushElementTransform(element);
if (!element.getFill || !element.getStroke) {
// Draw without stroke or fill (e.g. the element is an image or group).
element.draw(ctx);
this.popElementTransform();
return;
}
var fill = element.getFill();
if (fill) {
if (fill instanceof goog.graphics.SolidFill) {
if (fill.getOpacity() != 0) {
ctx.globalAlpha = fill.getOpacity();
ctx.fillStyle = fill.getColor();
element.draw(ctx);
ctx.fill();
ctx.globalAlpha = 1;
}
} else { // (fill instanceof goog.graphics.LinearGradient)
var linearGradient = ctx.createLinearGradient(fill.getX1(), fill.getY1(),
fill.getX2(), fill.getY2());
linearGradient.addColorStop(0.0, fill.getColor1());
linearGradient.addColorStop(1.0, fill.getColor2());
ctx.fillStyle = linearGradient;
element.draw(ctx);
ctx.fill();
}
}
var stroke = element.getStroke();
if (stroke) {
element.draw(ctx);
ctx.strokeStyle = stroke.getColor();
var width = stroke.getWidth();
if (goog.isString(width) && width.indexOf('px') != -1) {
width = parseFloat(width) / this.getPixelScaleX();
}
ctx.lineWidth = width;
ctx.stroke();
}
this.popElementTransform();
};
/**
* Append an element.
*
* @param {goog.graphics.Element} element The element to draw.
* @param {goog.graphics.CanvasGroupElement|undefined} group The group to draw
* it in. If null or undefined, defaults to the root group.
* @private
* @deprecated Use append instead.
*/
goog.graphics.CanvasGraphics.prototype.append_ = function(element, group) {
this.append(element, group);
};
/**
* Append an element.
*
* @param {goog.graphics.Element} element The element to draw.
* @param {goog.graphics.GroupElement|undefined} group The group to draw
* it in. If null or undefined, defaults to the root group.
* @protected
*/
goog.graphics.CanvasGraphics.prototype.append = function(element, group) {
group = group || this.canvasElement;
group.appendChild(element);
if (this.isDrawable(group)) {
this.drawElement(element);
}
};
/**
* Draw an ellipse.
*
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
* @param {goog.graphics.Stroke} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper
* element to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.EllipseElement} The newly created element.
* @override
*/
goog.graphics.CanvasGraphics.prototype.drawEllipse = function(cx, cy, rx, ry,
stroke, fill, opt_group) {
var element = new goog.graphics.CanvasEllipseElement(null, this,
cx, cy, rx, ry, stroke, fill);
this.append(element, opt_group);
return element;
};
/**
* Draw a rectangle.
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @param {goog.graphics.Stroke} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper
* element to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.RectElement} The newly created element.
* @override
*/
goog.graphics.CanvasGraphics.prototype.drawRect = function(x, y, width, height,
stroke, fill, opt_group) {
var element = new goog.graphics.CanvasRectElement(null, this,
x, y, width, height, stroke, fill);
this.append(element, opt_group);
return element;
};
/**
* Draw an image.
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of image.
* @param {number} height Height of image.
* @param {string} src Source of the image.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper
* element to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.ImageElement} The newly created element.
*/
goog.graphics.CanvasGraphics.prototype.drawImage = function(x, y, width, height,
src, opt_group) {
var element = new goog.graphics.CanvasImageElement(null, this, x, y, width,
height, src);
this.append(element, opt_group);
return element;
};
/**
* Draw a text string vertically centered on a given line.
*
* @param {string} text The text to draw.
* @param {number} x1 X coordinate of start of line.
* @param {number} y1 Y coordinate of start of line.
* @param {number} x2 X coordinate of end of line.
* @param {number} y2 Y coordinate of end of line.
* @param {?string} align Horizontal alignment: left (default), center, right.
* @param {goog.graphics.Font} font Font describing the font properties.
* @param {goog.graphics.Stroke} stroke Stroke object describing the stroke.
* @param {goog.graphics.Fill} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper
* element to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.TextElement} The newly created element.
* @override
*/
goog.graphics.CanvasGraphics.prototype.drawTextOnLine = function(
text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {
var element = new goog.graphics.CanvasTextElement(this,
text, x1, y1, x2, y2, align, /** @type {!goog.graphics.Font} */ (font),
stroke, fill);
this.append(element, opt_group);
return element;
};
/**
* Draw a path.
* @param {!goog.graphics.Path} path The path object to draw.
* @param {goog.graphics.Stroke} stroke Stroke object describing the stroke.
* @param {goog.graphics.Fill} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper
* element to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.PathElement} The newly created element.
* @override
*/
goog.graphics.CanvasGraphics.prototype.drawPath = function(path, stroke, fill,
opt_group) {
var element = new goog.graphics.CanvasPathElement(null, this,
path, stroke, fill);
this.append(element, opt_group);
return element;
};
/**
* @param {goog.graphics.GroupElement} group The group to possibly
* draw to.
* @return {boolean} Whether drawing can occur now.
*/
goog.graphics.CanvasGraphics.prototype.isDrawable = function(group) {
return this.isInDocument() && !this.redrawTimeout_ &&
!this.isRedrawRequired(group);
};
/**
* Returns true if drawing to the given group means a redraw is required.
* @param {goog.graphics.GroupElement} group The group to draw to.
* @return {boolean} Whether drawing to this group should force a redraw.
*/
goog.graphics.CanvasGraphics.prototype.isRedrawRequired = function(group) {
// TODO(robbyw): Moving up to any parent of lastGroup should not force redraw.
return group != this.canvasElement && group != this.lastGroup_;
};
/**
* Create an empty group of drawing elements.
*
* @param {goog.graphics.GroupElement=} opt_group The group wrapper
* element to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.CanvasGroupElement} The newly created group.
* @override
*/
goog.graphics.CanvasGraphics.prototype.createGroup = function(opt_group) {
var group = new goog.graphics.CanvasGroupElement(this);
opt_group = opt_group || this.canvasElement;
// TODO(robbyw): Moving up to any parent group should not force redraw.
if (opt_group == this.canvasElement || opt_group == this.lastGroup_) {
this.lastGroup_ = group;
}
this.append(group, opt_group);
return group;
};
/**
* Measure and return the width (in pixels) of a given text string.
* Text measurement is needed to make sure a text can fit in the allocated
* area. The way text length is measured is by writing it into a div that is
* after the visible area, measure the div width, and immediatly erase the
* written value.
*
* @param {string} text The text string to measure.
* @param {goog.graphics.Font} font The font object describing the font style.
* @override
*/
goog.graphics.CanvasGraphics.prototype.getTextWidth = goog.abstractMethod;
/**
* Disposes of the component by removing event handlers, detacing DOM nodes from
* the document body, and removing references to them.
* @override
* @protected
*/
goog.graphics.CanvasGraphics.prototype.disposeInternal = function() {
this.context_ = null;
goog.graphics.CanvasGraphics.superClass_.disposeInternal.call(this);
};
/** @override */
goog.graphics.CanvasGraphics.prototype.enterDocument = function() {
var oldPixelSize = this.getPixelSize();
goog.graphics.CanvasGraphics.superClass_.enterDocument.call(this);
if (!oldPixelSize) {
this.updateSize();
this.dispatchEvent(goog.events.EventType.RESIZE);
}
this.redraw();
};
/**
* Start preventing redraws - useful for chaining large numbers of changes
* together. Not guaranteed to do anything - i.e. only use this for
* optimization of a single code path.
* @override
*/
goog.graphics.CanvasGraphics.prototype.suspend = function() {
this.preventRedraw_ = true;
};
/**
* Stop preventing redraws. If any redraws had been prevented, a redraw will
* be done now.
* @override
*/
goog.graphics.CanvasGraphics.prototype.resume = function() {
this.preventRedraw_ = false;
if (this.needsRedraw_) {
this.redraw();
this.needsRedraw_ = false;
}
};

View File

@@ -0,0 +1,151 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element returned from
* the different draw methods of the graphics implementation, and
* all interfaces that the various element types support.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.Element');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.events.Listenable');
goog.require('goog.graphics.AffineTransform');
goog.require('goog.math');
/**
* Base class for a thin wrapper around the DOM element returned from
* the different draw methods of the graphics.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.events.EventTarget}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.Element = function(element, graphics) {
goog.events.EventTarget.call(this);
this.element_ = element;
this.graphics_ = graphics;
// Overloading EventTarget field to state that this is not a custom event.
// TODO(user) Should be handled in EventTarget.js (see bug 846824).
this[goog.events.Listenable.IMPLEMENTED_BY_PROP] = false;
};
goog.inherits(goog.graphics.Element, goog.events.EventTarget);
/**
* The graphics object that contains this element.
* @type {goog.graphics.AbstractGraphics?}
* @private
*/
goog.graphics.Element.prototype.graphics_ = null;
/**
* The native browser element this class wraps.
* @type {Element}
* @private
*/
goog.graphics.Element.prototype.element_ = null;
/**
* The transformation applied to this element.
* @type {goog.graphics.AffineTransform?}
* @private
*/
goog.graphics.Element.prototype.transform_ = null;
/**
* Returns the underlying object.
* @return {Element} The underlying element.
*/
goog.graphics.Element.prototype.getElement = function() {
return this.element_;
};
/**
* Returns the graphics.
* @return {goog.graphics.AbstractGraphics} The graphics that created the
* element.
*/
goog.graphics.Element.prototype.getGraphics = function() {
return this.graphics_;
};
/**
* Set the transformation of the element.
* @param {number} x The x coordinate of the translation transform.
* @param {number} y The y coordinate of the translation transform.
* @param {number} rotate The angle of the rotation transform.
* @param {number} centerX The horizontal center of the rotation transform.
* @param {number} centerY The vertical center of the rotation transform.
*/
goog.graphics.Element.prototype.setTransformation = function(x, y, rotate,
centerX, centerY) {
// TODO(robbyw): Add skew and scale.
this.transform_ = goog.graphics.AffineTransform.getRotateInstance(
goog.math.toRadians(rotate), centerX, centerY).translate(x, y);
this.getGraphics().setElementTransform(this, x, y, rotate, centerX, centerY);
};
/**
* @return {goog.graphics.AffineTransform} The transformation applied to
* this element.
*/
goog.graphics.Element.prototype.getTransform = function() {
return this.transform_ ? this.transform_.clone() :
new goog.graphics.AffineTransform();
};
/** @override */
goog.graphics.Element.prototype.addEventListener = function(
type, handler, opt_capture, opt_handlerScope) {
goog.events.listen(this.element_, type, handler, opt_capture,
opt_handlerScope);
};
/** @override */
goog.graphics.Element.prototype.removeEventListener = function(
type, handler, opt_capture, opt_handlerScope) {
goog.events.unlisten(this.element_, type, handler, opt_capture,
opt_handlerScope);
};
/** @override */
goog.graphics.Element.prototype.disposeInternal = function() {
goog.graphics.Element.superClass_.disposeInternal.call(this);
goog.events.removeAll(this.element_);
};

View File

@@ -0,0 +1,64 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element for ellipses.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.EllipseElement');
goog.require('goog.graphics.StrokeAndFillElement');
/**
* Interface for a graphics ellipse element.
* You should not construct objects from this constructor. The graphics
* will return an implementation of this interface for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.StrokeAndFillElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.EllipseElement = function(element, graphics, stroke, fill) {
goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
fill);
};
goog.inherits(goog.graphics.EllipseElement, goog.graphics.StrokeAndFillElement);
/**
* Update the center point of the ellipse.
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
*/
goog.graphics.EllipseElement.prototype.setCenter = goog.abstractMethod;
/**
* Update the radius of the ellipse.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
*/
goog.graphics.EllipseElement.prototype.setRadius = goog.abstractMethod;

View File

@@ -0,0 +1,159 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Graphics utility functions for advanced coordinates.
*
* This file assists the use of advanced coordinates in goog.graphics. Coords
* can be specified as simple numbers which will correspond to units in the
* graphics element's coordinate space. Alternately, coords can be expressed
* in pixels, meaning no matter what tranformations or coordinate system changes
* are present, the number of pixel changes will remain constant. Coords can
* also be expressed as percentages of their parent's size.
*
* This file also allows for elements to have margins, expressable in any of
* the ways described above.
*
* Additional pieces of advanced coordinate functionality can (soon) be found in
* element.js and groupelement.js.
*
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.coordinates');
goog.require('goog.string');
/**
* Cache of boolean values. For a given string (key), is it special? (value)
* @type {Object}
* @private
*/
goog.graphics.ext.coordinates.specialCoordinateCache_ = {};
/**
* Determines if the given coordinate is a percent based coordinate or an
* expression with a percent based component.
* @param {string} coord The coordinate to test.
* @return {boolean} Whether the coordinate contains the string '%'.
* @private
*/
goog.graphics.ext.coordinates.isPercent_ = function(coord) {
return goog.string.contains(coord, '%');
};
/**
* Determines if the given coordinate is a pixel based coordinate or an
* expression with a pixel based component.
* @param {string} coord The coordinate to test.
* @return {boolean} Whether the coordinate contains the string 'px'.
* @private
*/
goog.graphics.ext.coordinates.isPixels_ = function(coord) {
return goog.string.contains(coord, 'px');
};
/**
* Determines if the given coordinate is special - i.e. not just a number.
* @param {string|number|null} coord The coordinate to test.
* @return {boolean} Whether the coordinate is special.
*/
goog.graphics.ext.coordinates.isSpecial = function(coord) {
var cache = goog.graphics.ext.coordinates.specialCoordinateCache_;
if (!(coord in cache)) {
cache[coord] = goog.isString(coord) && (
goog.graphics.ext.coordinates.isPercent_(coord) ||
goog.graphics.ext.coordinates.isPixels_(coord));
}
return cache[coord];
};
/**
* Returns the value of the given expression in the given context.
*
* Should be treated as package scope.
*
* @param {string|number} coord The coordinate to convert.
* @param {number} size The size of the parent element.
* @param {number} scale The ratio of pixels to units.
* @return {number} The number of coordinate space units that corresponds to
* this coordinate.
*/
goog.graphics.ext.coordinates.computeValue = function(coord, size, scale) {
var number = parseFloat(String(coord));
if (goog.isString(coord)) {
if (goog.graphics.ext.coordinates.isPercent_(coord)) {
return number * size / 100;
} else if (goog.graphics.ext.coordinates.isPixels_(coord)) {
return number / scale;
}
}
return number;
};
/**
* Converts the given coordinate to a number value in units.
*
* Should be treated as package scope.
*
* @param {string|number} coord The coordinate to retrieve the value for.
* @param {boolean|undefined} forMaximum Whether we are computing the largest
* value this coordinate would be in a parent of no size. The container
* size in this case should be set to the size of the current element.
* @param {number} containerSize The unit value of the size of the container of
* this element. Should be set to the minimum width of this element if
* forMaximum is true.
* @param {number} scale The ratio of pixels to units.
* @param {Object=} opt_cache Optional (but highly recommend) object to store
* cached computations in. The calling class should manage clearing out
* the cache when the scale or containerSize changes.
* @return {number} The correct number of coordinate space units.
*/
goog.graphics.ext.coordinates.getValue = function(coord, forMaximum,
containerSize, scale, opt_cache) {
if (!goog.isNumber(coord)) {
var cacheString = opt_cache && ((forMaximum ? 'X' : '') + coord);
if (opt_cache && cacheString in opt_cache) {
coord = opt_cache[cacheString];
} else {
if (goog.graphics.ext.coordinates.isSpecial(
/** @type {string} */ (coord))) {
coord = goog.graphics.ext.coordinates.computeValue(coord,
containerSize, scale);
} else {
// Simple coordinates just need to be converted from a string to a
// number.
coord = parseFloat(/** @type {string} */ (coord));
}
// Cache the result.
if (opt_cache) {
opt_cache[cacheString] = coord;
}
}
}
return coord;
};

View File

@@ -0,0 +1,968 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thicker wrapper around the DOM element returned from
* the different draw methods of the graphics implementation, and
* all interfaces that the various element types support.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Element');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.functions');
goog.require('goog.graphics');
goog.require('goog.graphics.ext.coordinates');
/**
* Base class for a wrapper around the goog.graphics wrapper that enables
* more advanced functionality.
* @param {goog.graphics.ext.Group?} group Parent for this element.
* @param {goog.graphics.Element} wrapper The thin wrapper to wrap.
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.graphics.ext.Element = function(group, wrapper) {
goog.events.EventTarget.call(this);
this.wrapper_ = wrapper;
this.graphics_ = group ? group.getGraphics() : this;
this.xPosition_ = new goog.graphics.ext.Element.Position_(this, true);
this.yPosition_ = new goog.graphics.ext.Element.Position_(this, false);
// Handle parent / child relationships.
if (group) {
this.parent_ = group;
this.parent_.addChild(this);
}
};
goog.inherits(goog.graphics.ext.Element, goog.events.EventTarget);
/**
* The graphics object that contains this element.
* @type {goog.graphics.ext.Graphics|goog.graphics.ext.Element}
* @private
*/
goog.graphics.ext.Element.prototype.graphics_;
/**
* The goog.graphics wrapper this class wraps.
* @type {goog.graphics.Element}
* @private
*/
goog.graphics.ext.Element.prototype.wrapper_;
/**
* The group or surface containing this element.
* @type {goog.graphics.ext.Group|undefined}
* @private
*/
goog.graphics.ext.Element.prototype.parent_;
/**
* Whether or not computation of this element's position or size depends on its
* parent's size.
* @type {boolean}
* @private
*/
goog.graphics.ext.Element.prototype.parentDependent_ = false;
/**
* Whether the element has pending transformations.
* @type {boolean}
* @private
*/
goog.graphics.ext.Element.prototype.needsTransform_ = false;
/**
* The current angle of rotation, expressed in degrees.
* @type {number}
* @private
*/
goog.graphics.ext.Element.prototype.rotation_ = 0;
/**
* Object representing the x position and size of the element.
* @type {goog.graphics.ext.Element.Position_}
* @private
*/
goog.graphics.ext.Element.prototype.xPosition_;
/**
* Object representing the y position and size of the element.
* @type {goog.graphics.ext.Element.Position_}
* @private
*/
goog.graphics.ext.Element.prototype.yPosition_;
/**
* @return {goog.graphics.Element} The underlying thin wrapper.
* @protected
*/
goog.graphics.ext.Element.prototype.getWrapper = function() {
return this.wrapper_;
};
/**
* @return {goog.graphics.ext.Element|goog.graphics.ext.Graphics} The graphics
* surface the element is a part of.
*/
goog.graphics.ext.Element.prototype.getGraphics = function() {
return this.graphics_;
};
/**
* Returns the graphics implementation.
* @return {goog.graphics.AbstractGraphics} The underlying graphics
* implementation drawing this element's wrapper.
* @protected
*/
goog.graphics.ext.Element.prototype.getGraphicsImplementation = function() {
return this.graphics_.getImplementation();
};
/**
* @return {goog.graphics.ext.Group|undefined} The parent of this element.
*/
goog.graphics.ext.Element.prototype.getParent = function() {
return this.parent_;
};
// GENERAL POSITIONING
/**
* Internal convenience method for setting position - either as a left/top,
* center/middle, or right/bottom value. Only one should be specified.
* @param {goog.graphics.ext.Element.Position_} position The position object to
* set the value on.
* @param {number|string} value The value of the coordinate.
* @param {goog.graphics.ext.Element.PositionType_} type The type of the
* coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
* @private
*/
goog.graphics.ext.Element.prototype.setPosition_ = function(position, value,
type, opt_chain) {
position.setPosition(value, type);
this.computeIsParentDependent_(position);
this.needsTransform_ = true;
if (!opt_chain) {
this.transform();
}
};
/**
* Sets the width/height of the element.
* @param {goog.graphics.ext.Element.Position_} position The position object to
* set the value on.
* @param {string|number} size The new width/height value.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
* @private
*/
goog.graphics.ext.Element.prototype.setSize_ = function(position, size,
opt_chain) {
if (position.setSize(size)) {
this.needsTransform_ = true;
this.computeIsParentDependent_(position);
if (!opt_chain) {
this.reset();
}
} else if (!opt_chain && this.isPendingTransform()) {
this.reset();
}
};
/**
* Sets the minimum width/height of the element.
* @param {goog.graphics.ext.Element.Position_} position The position object to
* set the value on.
* @param {string|number} minSize The minimum width/height of the element.
* @private
*/
goog.graphics.ext.Element.prototype.setMinSize_ = function(position, minSize) {
position.setMinSize(minSize);
this.needsTransform_ = true;
this.computeIsParentDependent_(position);
};
// HORIZONTAL POSITIONING
/**
* @return {number} The distance from the left edge of this element to the left
* edge of its parent, specified in units of the parent's coordinate system.
*/
goog.graphics.ext.Element.prototype.getLeft = function() {
return this.xPosition_.getStart();
};
/**
* Sets the left coordinate of the element. Overwrites any previous value of
* left, center, or right for this element.
* @param {string|number} left The left coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setLeft = function(left, opt_chain) {
this.setPosition_(this.xPosition_,
left,
goog.graphics.ext.Element.PositionType_.START,
opt_chain);
};
/**
* @return {number} The right coordinate of the element, in units of the
* parent's coordinate system.
*/
goog.graphics.ext.Element.prototype.getRight = function() {
return this.xPosition_.getEnd();
};
/**
* Sets the right coordinate of the element. Overwrites any previous value of
* left, center, or right for this element.
* @param {string|number} right The right coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setRight = function(right, opt_chain) {
this.setPosition_(this.xPosition_,
right,
goog.graphics.ext.Element.PositionType_.END,
opt_chain);
};
/**
* @return {number} The center coordinate of the element, in units of the
* parent's coordinate system.
*/
goog.graphics.ext.Element.prototype.getCenter = function() {
return this.xPosition_.getMiddle();
};
/**
* Sets the center coordinate of the element. Overwrites any previous value of
* left, center, or right for this element.
* @param {string|number} center The center coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setCenter = function(center, opt_chain) {
this.setPosition_(this.xPosition_,
center,
goog.graphics.ext.Element.PositionType_.MIDDLE,
opt_chain);
};
// VERTICAL POSITIONING
/**
* @return {number} The distance from the top edge of this element to the top
* edge of its parent, specified in units of the parent's coordinate system.
*/
goog.graphics.ext.Element.prototype.getTop = function() {
return this.yPosition_.getStart();
};
/**
* Sets the top coordinate of the element. Overwrites any previous value of
* top, middle, or bottom for this element.
* @param {string|number} top The top coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setTop = function(top, opt_chain) {
this.setPosition_(this.yPosition_,
top,
goog.graphics.ext.Element.PositionType_.START,
opt_chain);
};
/**
* @return {number} The bottom coordinate of the element, in units of the
* parent's coordinate system.
*/
goog.graphics.ext.Element.prototype.getBottom = function() {
return this.yPosition_.getEnd();
};
/**
* Sets the bottom coordinate of the element. Overwrites any previous value of
* top, middle, or bottom for this element.
* @param {string|number} bottom The bottom coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setBottom = function(bottom, opt_chain) {
this.setPosition_(this.yPosition_,
bottom,
goog.graphics.ext.Element.PositionType_.END,
opt_chain);
};
/**
* @return {number} The middle coordinate of the element, in units of the
* parent's coordinate system.
*/
goog.graphics.ext.Element.prototype.getMiddle = function() {
return this.yPosition_.getMiddle();
};
/**
* Sets the middle coordinate of the element. Overwrites any previous value of
* top, middle, or bottom for this element
* @param {string|number} middle The middle coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setMiddle = function(middle, opt_chain) {
this.setPosition_(this.yPosition_,
middle,
goog.graphics.ext.Element.PositionType_.MIDDLE,
opt_chain);
};
// DIMENSIONS
/**
* @return {number} The width of the element, in units of the parent's
* coordinate system.
*/
goog.graphics.ext.Element.prototype.getWidth = function() {
return this.xPosition_.getSize();
};
/**
* Sets the width of the element.
* @param {string|number} width The new width value.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setWidth = function(width, opt_chain) {
this.setSize_(this.xPosition_, width, opt_chain);
};
/**
* @return {number} The minimum width of the element, in units of the parent's
* coordinate system.
*/
goog.graphics.ext.Element.prototype.getMinWidth = function() {
return this.xPosition_.getMinSize();
};
/**
* Sets the minimum width of the element.
* @param {string|number} minWidth The minimum width of the element.
*/
goog.graphics.ext.Element.prototype.setMinWidth = function(minWidth) {
this.setMinSize_(this.xPosition_, minWidth);
};
/**
* @return {number} The height of the element, in units of the parent's
* coordinate system.
*/
goog.graphics.ext.Element.prototype.getHeight = function() {
return this.yPosition_.getSize();
};
/**
* Sets the height of the element.
* @param {string|number} height The new height value.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setHeight = function(height, opt_chain) {
this.setSize_(this.yPosition_, height, opt_chain);
};
/**
* @return {number} The minimum height of the element, in units of the parent's
* coordinate system.
*/
goog.graphics.ext.Element.prototype.getMinHeight = function() {
return this.yPosition_.getMinSize();
};
/**
* Sets the minimum height of the element.
* @param {string|number} minHeight The minimum height of the element.
*/
goog.graphics.ext.Element.prototype.setMinHeight = function(minHeight) {
this.setMinSize_(this.yPosition_, minHeight);
};
// BOUNDS SHORTCUTS
/**
* Shortcut for setting the left and top position.
* @param {string|number} left The left coordinate.
* @param {string|number} top The top coordinate.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setPosition = function(left, top,
opt_chain) {
this.setLeft(left, true);
this.setTop(top, opt_chain);
};
/**
* Shortcut for setting the width and height.
* @param {string|number} width The new width value.
* @param {string|number} height The new height value.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setSize = function(width, height,
opt_chain) {
this.setWidth(width, true);
this.setHeight(height, opt_chain);
};
/**
* Shortcut for setting the left, top, width, and height.
* @param {string|number} left The left coordinate.
* @param {string|number} top The top coordinate.
* @param {string|number} width The new width value.
* @param {string|number} height The new height value.
* @param {boolean=} opt_chain Optional flag to specify this function is part
* of a chain of calls and therefore transformations should be set as
* pending but not yet performed.
*/
goog.graphics.ext.Element.prototype.setBounds = function(left, top, width,
height, opt_chain) {
this.setLeft(left, true);
this.setTop(top, true);
this.setWidth(width, true);
this.setHeight(height, opt_chain);
};
// MAXIMUM BOUNDS
/**
* @return {number} An estimate of the maximum x extent this element would have
* in a parent of no width.
*/
goog.graphics.ext.Element.prototype.getMaxX = function() {
return this.xPosition_.getMaxPosition();
};
/**
* @return {number} An estimate of the maximum y extent this element would have
* in a parent of no height.
*/
goog.graphics.ext.Element.prototype.getMaxY = function() {
return this.yPosition_.getMaxPosition();
};
// RESET
/**
* Reset the element. This is called when the element changes size, or when
* the coordinate system changes in a way that would affect pixel based
* rendering
*/
goog.graphics.ext.Element.prototype.reset = function() {
this.xPosition_.resetCache();
this.yPosition_.resetCache();
this.redraw();
this.needsTransform_ = true;
this.transform();
};
/**
* Overridable function for subclass specific reset.
* @protected
*/
goog.graphics.ext.Element.prototype.redraw = goog.nullFunction;
// PARENT DEPENDENCY
/**
* Computes whether the element is still parent dependent.
* @param {goog.graphics.ext.Element.Position_} position The recently changed
* position object.
* @private
*/
goog.graphics.ext.Element.prototype.computeIsParentDependent_ = function(
position) {
this.parentDependent_ = position.isParentDependent() ||
this.xPosition_.isParentDependent() ||
this.yPosition_.isParentDependent() ||
this.checkParentDependent();
};
/**
* Returns whether this element's bounds depend on its parents.
*
* This function should be treated as if it has package scope.
* @return {boolean} Whether this element's bounds depend on its parents.
*/
goog.graphics.ext.Element.prototype.isParentDependent = function() {
return this.parentDependent_;
};
/**
* Overridable function for subclass specific parent dependency.
* @return {boolean} Whether this shape's bounds depends on its parent's.
* @protected
*/
goog.graphics.ext.Element.prototype.checkParentDependent =
goog.functions.FALSE;
// ROTATION
/**
* Set the rotation of this element.
* @param {number} angle The angle of rotation, in degrees.
*/
goog.graphics.ext.Element.prototype.setRotation = function(angle) {
if (this.rotation_ != angle) {
this.rotation_ = angle;
this.needsTransform_ = true;
this.transform();
}
};
/**
* @return {number} The angle of rotation of this element, in degrees.
*/
goog.graphics.ext.Element.prototype.getRotation = function() {
return this.rotation_;
};
// TRANSFORMS
/**
* Called by the parent when the parent has transformed.
*
* Should be treated as package scope.
*/
goog.graphics.ext.Element.prototype.parentTransform = function() {
this.needsTransform_ = this.needsTransform_ || this.parentDependent_;
};
/**
* @return {boolean} Whether this element has pending transforms.
*/
goog.graphics.ext.Element.prototype.isPendingTransform = function() {
return this.needsTransform_;
};
/**
* Performs a pending transform.
* @protected
*/
goog.graphics.ext.Element.prototype.transform = function() {
if (this.isPendingTransform()) {
this.needsTransform_ = false;
this.wrapper_.setTransformation(
this.getLeft(),
this.getTop(),
this.rotation_,
(this.getWidth() || 1) / 2,
(this.getHeight() || 1) / 2);
// TODO(robbyw): this._fireEvent('transform', [ this ]);
}
};
// PIXEL SCALE
/**
* @return {number} Returns the number of pixels per unit in the x direction.
*/
goog.graphics.ext.Element.prototype.getPixelScaleX = function() {
return this.getGraphics().getPixelScaleX();
};
/**
* @return {number} Returns the number of pixels per unit in the y direction.
*/
goog.graphics.ext.Element.prototype.getPixelScaleY = function() {
return this.getGraphics().getPixelScaleY();
};
// EVENT HANDLING
/** @override */
goog.graphics.ext.Element.prototype.disposeInternal = function() {
goog.graphics.ext.Element.superClass_.disposeInternal.call();
this.wrapper_.dispose();
};
// INTERNAL POSITION OBJECT
/**
* Position specification types. Start corresponds to left/top, middle to
* center/middle, and end to right/bottom.
* @enum {number}
* @private
*/
goog.graphics.ext.Element.PositionType_ = {
START: 0,
MIDDLE: 1,
END: 2
};
/**
* Manages a position and size, either horizontal or vertical.
* @param {goog.graphics.ext.Element} element The element the position applies
* to.
* @param {boolean} horizontal Whether the position is horizontal or vertical.
* @constructor
* @private
*/
goog.graphics.ext.Element.Position_ = function(element, horizontal) {
this.element_ = element;
this.horizontal_ = horizontal;
};
/**
* @return {Object} The coordinate value computation cache.
* @private
*/
goog.graphics.ext.Element.Position_.prototype.getCoordinateCache_ = function() {
return this.coordinateCache_ || (this.coordinateCache_ = {});
};
/**
* @return {number} The size of the parent's coordinate space.
* @private
*/
goog.graphics.ext.Element.Position_.prototype.getParentSize_ = function() {
var parent = this.element_.getParent();
return this.horizontal_ ?
parent.getCoordinateWidth() :
parent.getCoordinateHeight();
};
/**
* @return {number} The minimum width/height of the element.
*/
goog.graphics.ext.Element.Position_.prototype.getMinSize = function() {
return this.getValue_(this.minSize_);
};
/**
* Sets the minimum width/height of the element.
* @param {string|number} minSize The minimum width/height of the element.
*/
goog.graphics.ext.Element.Position_.prototype.setMinSize = function(minSize) {
this.minSize_ = minSize;
this.resetCache();
};
/**
* @return {number} The width/height of the element.
*/
goog.graphics.ext.Element.Position_.prototype.getSize = function() {
return Math.max(this.getValue_(this.size_), this.getMinSize());
};
/**
* Sets the width/height of the element.
* @param {string|number} size The width/height of the element.
* @return {boolean} Whether the value was changed.
*/
goog.graphics.ext.Element.Position_.prototype.setSize = function(size) {
if (size != this.size_) {
this.size_ = size;
this.resetCache();
return true;
}
return false;
};
/**
* Converts the given x coordinate to a number value in units.
* @param {string|number} v The coordinate to retrieve the value for.
* @param {boolean=} opt_forMaximum Whether we are computing the largest value
* this coordinate would be in a parent of no size.
* @return {number} The correct number of coordinate space units.
* @private
*/
goog.graphics.ext.Element.Position_.prototype.getValue_ = function(v,
opt_forMaximum) {
if (!goog.graphics.ext.coordinates.isSpecial(v)) {
return parseFloat(String(v));
}
var cache = this.getCoordinateCache_();
var scale = this.horizontal_ ?
this.element_.getPixelScaleX() :
this.element_.getPixelScaleY();
var containerSize;
if (opt_forMaximum) {
containerSize = goog.graphics.ext.coordinates.computeValue(
this.size_ || 0, 0, scale);
} else {
var parent = this.element_.getParent();
containerSize = this.horizontal_ ? parent.getWidth() : parent.getHeight();
}
return goog.graphics.ext.coordinates.getValue(v, opt_forMaximum,
containerSize, scale, cache);
};
/**
* @return {number} The distance from the left/top edge of this element to the
* left/top edge of its parent, specified in units of the parent's
* coordinate system.
*/
goog.graphics.ext.Element.Position_.prototype.getStart = function() {
if (this.cachedValue_ == null) {
var value = this.getValue_(this.distance_);
if (this.distanceType_ == goog.graphics.ext.Element.PositionType_.START) {
this.cachedValue_ = value;
} else if (this.distanceType_ ==
goog.graphics.ext.Element.PositionType_.MIDDLE) {
this.cachedValue_ = value + (this.getParentSize_() - this.getSize()) / 2;
} else {
this.cachedValue_ = this.getParentSize_() - value - this.getSize();
}
}
return this.cachedValue_;
};
/**
* @return {number} The middle coordinate of the element, in units of the
* parent's coordinate system.
*/
goog.graphics.ext.Element.Position_.prototype.getMiddle = function() {
return this.distanceType_ == goog.graphics.ext.Element.PositionType_.MIDDLE ?
this.getValue_(this.distance_) :
(this.getParentSize_() - this.getSize()) / 2 - this.getStart();
};
/**
* @return {number} The end coordinate of the element, in units of the
* parent's coordinate system.
*/
goog.graphics.ext.Element.Position_.prototype.getEnd = function() {
return this.distanceType_ == goog.graphics.ext.Element.PositionType_.END ?
this.getValue_(this.distance_) :
this.getParentSize_() - this.getStart() - this.getSize();
};
/**
* Sets the position, either as a left/top, center/middle, or right/bottom
* value.
* @param {number|string} value The value of the coordinate.
* @param {goog.graphics.ext.Element.PositionType_} type The type of the
* coordinate.
*/
goog.graphics.ext.Element.Position_.prototype.setPosition = function(value,
type) {
this.distance_ = value;
this.distanceType_ = type;
// Clear cached value.
this.cachedValue_ = null;
};
/**
* @return {number} An estimate of the maximum x/y extent this element would
* have in a parent of no width/height.
*/
goog.graphics.ext.Element.Position_.prototype.getMaxPosition = function() {
// TODO(robbyw): Handle transformed or rotated coordinates
// TODO(robbyw): Handle pixel based sizes?
return this.getValue_(this.distance_ || 0) + (
goog.graphics.ext.coordinates.isSpecial(this.size_) ? 0 : this.getSize());
};
/**
* Resets the caches of position values and coordinate values.
*/
goog.graphics.ext.Element.Position_.prototype.resetCache = function() {
this.coordinateCache_ = null;
this.cachedValue_ = null;
};
/**
* @return {boolean} Whether the size or position of this element depends on
* the size of the parent element.
*/
goog.graphics.ext.Element.Position_.prototype.isParentDependent = function() {
return this.distanceType_ != goog.graphics.ext.Element.PositionType_.START ||
goog.graphics.ext.coordinates.isSpecial(this.size_) ||
goog.graphics.ext.coordinates.isSpecial(this.minSize_) ||
goog.graphics.ext.coordinates.isSpecial(this.distance_);
};
/**
* The lazy loaded distance from the parent's top/left edge to this element's
* top/left edge expressed in the parent's coordinate system. We cache this
* because it is most freqeuently requested by the element and it is easy to
* compute middle and end values from it.
* @type {?number}
* @private
*/
goog.graphics.ext.Element.Position_.prototype.cachedValue_ = null;
/**
* A cache of computed x coordinates.
* @type {Object}
* @private
*/
goog.graphics.ext.Element.Position_.prototype.coordinateCache_ = null;
/**
* The minimum width/height of this element, as specified by the caller.
* @type {string|number}
* @private
*/
goog.graphics.ext.Element.Position_.prototype.minSize_ = 0;
/**
* The width/height of this object, as specified by the caller.
* @type {string|number}
* @private
*/
goog.graphics.ext.Element.Position_.prototype.size_ = 0;
/**
* The coordinate of this object, as specified by the caller. The type of
* coordinate is specified by distanceType_.
* @type {string|number}
* @private
*/
goog.graphics.ext.Element.Position_.prototype.distance_ = 0;
/**
* The coordinate type specified by distance_.
* @type {goog.graphics.ext.Element.PositionType_}
* @private
*/
goog.graphics.ext.Element.Position_.prototype.distanceType_ =
goog.graphics.ext.Element.PositionType_.START;

View File

@@ -0,0 +1,59 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thick wrapper around ellipses.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Ellipse');
goog.require('goog.graphics.ext.StrokeAndFillElement');
/**
* Wrapper for a graphics ellipse element.
* @param {goog.graphics.ext.Group} group Parent for this element.
* @constructor
* @extends {goog.graphics.ext.StrokeAndFillElement}
*/
goog.graphics.ext.Ellipse = function(group) {
// Initialize with some stock values.
var wrapper = group.getGraphicsImplementation().drawEllipse(1, 1, 2, 2, null,
null, group.getWrapper());
goog.graphics.ext.StrokeAndFillElement.call(this, group, wrapper);
};
goog.inherits(goog.graphics.ext.Ellipse,
goog.graphics.ext.StrokeAndFillElement);
/**
* Redraw the ellipse. Called when the coordinate system is changed.
* @protected
* @override
*/
goog.graphics.ext.Ellipse.prototype.redraw = function() {
goog.graphics.ext.Ellipse.superClass_.redraw.call(this);
// Our position is already transformed in transform_, but because this is an
// ellipse we need to position the center.
var xRadius = this.getWidth() / 2;
var yRadius = this.getHeight() / 2;
var wrapper = this.getWrapper();
wrapper.setCenter(xRadius, yRadius);
wrapper.setRadius(xRadius, yRadius);
};

View File

@@ -0,0 +1,29 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Extended graphics namespace.
*/
goog.provide('goog.graphics.ext');
goog.require('goog.graphics.ext.Ellipse');
goog.require('goog.graphics.ext.Graphics');
goog.require('goog.graphics.ext.Group');
goog.require('goog.graphics.ext.Image');
goog.require('goog.graphics.ext.Rectangle');
goog.require('goog.graphics.ext.Shape');
goog.require('goog.graphics.ext.coordinates');

View File

@@ -0,0 +1,215 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Graphics surface type.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Graphics');
goog.require('goog.events.EventType');
goog.require('goog.graphics.ext.Group');
/**
* Wrapper for a graphics surface.
* @param {string|number} width The width in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {string|number} height The height in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {?number=} opt_coordWidth The coordinate width - if
* omitted or null, defaults to same as width.
* @param {?number=} opt_coordHeight The coordinate height. - if
* omitted or null, defaults to same as height.
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
* document we want to render in.
* @param {boolean=} opt_isSimple Flag used to indicate the graphics object will
* be drawn to in a single pass, and the fastest implementation for this
* scenario should be favored. NOTE: Setting to true may result in
* degradation of text support.
* @constructor
* @extends {goog.graphics.ext.Group}
*/
goog.graphics.ext.Graphics = function(width, height, opt_coordWidth,
opt_coordHeight, opt_domHelper, opt_isSimple) {
var surface = opt_isSimple ?
goog.graphics.createSimpleGraphics(width, height,
opt_coordWidth, opt_coordHeight, opt_domHelper) :
goog.graphics.createGraphics(width, height,
opt_coordWidth, opt_coordHeight, opt_domHelper);
this.implementation_ = surface;
goog.graphics.ext.Group.call(this, null, surface.getCanvasElement());
goog.events.listen(surface, goog.events.EventType.RESIZE,
this.updateChildren, false, this);
};
goog.inherits(goog.graphics.ext.Graphics, goog.graphics.ext.Group);
/**
* The root level graphics implementation.
* @type {goog.graphics.AbstractGraphics}
* @private
*/
goog.graphics.ext.Graphics.prototype.implementation_;
/**
* @return {goog.graphics.AbstractGraphics} The graphics implementation layer.
*/
goog.graphics.ext.Graphics.prototype.getImplementation = function() {
return this.implementation_;
};
/**
* Changes the coordinate size.
* @param {number} coordWidth The coordinate width.
* @param {number} coordHeight The coordinate height.
*/
goog.graphics.ext.Graphics.prototype.setCoordSize = function(coordWidth,
coordHeight) {
this.implementation_.setCoordSize(coordWidth, coordHeight);
goog.graphics.ext.Graphics.superClass_.setSize.call(this, coordWidth,
coordHeight);
};
/**
* @return {goog.math.Size} The coordinate size.
*/
goog.graphics.ext.Graphics.prototype.getCoordSize = function() {
return this.implementation_.getCoordSize();
};
/**
* Changes the coordinate system position.
* @param {number} left The coordinate system left bound.
* @param {number} top The coordinate system top bound.
*/
goog.graphics.ext.Graphics.prototype.setCoordOrigin = function(left, top) {
this.implementation_.setCoordOrigin(left, top);
};
/**
* @return {goog.math.Coordinate} The coordinate system position.
*/
goog.graphics.ext.Graphics.prototype.getCoordOrigin = function() {
return this.implementation_.getCoordOrigin();
};
/**
* Change the size of the canvas.
* @param {number} pixelWidth The width in pixels.
* @param {number} pixelHeight The height in pixels.
*/
goog.graphics.ext.Graphics.prototype.setPixelSize = function(pixelWidth,
pixelHeight) {
this.implementation_.setSize(pixelWidth, pixelHeight);
var coordSize = this.getCoordSize();
goog.graphics.ext.Graphics.superClass_.setSize.call(this, coordSize.width,
coordSize.height);
};
/**
* @return {goog.math.Size?} Returns the number of pixels spanned by the
* surface, or null if the size could not be computed due to the size being
* specified in percentage points and the component not being in the
* document.
*/
goog.graphics.ext.Graphics.prototype.getPixelSize = function() {
return this.implementation_.getPixelSize();
};
/**
* @return {number} The coordinate width of the canvas.
* @override
*/
goog.graphics.ext.Graphics.prototype.getWidth = function() {
return this.implementation_.getCoordSize().width;
};
/**
* @return {number} The coordinate width of the canvas.
* @override
*/
goog.graphics.ext.Graphics.prototype.getHeight = function() {
return this.implementation_.getCoordSize().height;
};
/**
* @return {number} Returns the number of pixels per unit in the x direction.
* @override
*/
goog.graphics.ext.Graphics.prototype.getPixelScaleX = function() {
return this.implementation_.getPixelScaleX();
};
/**
* @return {number} Returns the number of pixels per unit in the y direction.
* @override
*/
goog.graphics.ext.Graphics.prototype.getPixelScaleY = function() {
return this.implementation_.getPixelScaleY();
};
/**
* @return {Element} The root element of the graphics surface.
*/
goog.graphics.ext.Graphics.prototype.getElement = function() {
return this.implementation_.getElement();
};
/**
* Renders the underlying graphics.
*
* @param {Element} parentElement Parent element to render the component into.
*/
goog.graphics.ext.Graphics.prototype.render = function(parentElement) {
this.implementation_.render(parentElement);
};
/**
* Never transform a surface.
* @override
*/
goog.graphics.ext.Graphics.prototype.transform = goog.nullFunction;
/**
* Called from the parent class, this method resets any pre-computed positions
* and sizes.
* @protected
* @override
*/
goog.graphics.ext.Graphics.prototype.redraw = function() {
this.transformChildren();
};

View File

@@ -0,0 +1,215 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thicker wrapper around graphics groups.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Group');
goog.require('goog.graphics.ext.Element');
/**
* Wrapper for a graphics group.
* @param {goog.graphics.ext.Group} group Parent for this element. Can
* be null if this is a Graphics instance.
* @param {goog.graphics.GroupElement=} opt_wrapper The thin wrapper
* to wrap. If omitted, a new group will be created. Must be included
* when group is null.
* @constructor
* @extends {goog.graphics.ext.Element}
*/
goog.graphics.ext.Group = function(group, opt_wrapper) {
opt_wrapper = opt_wrapper || group.getGraphicsImplementation().createGroup(
group.getWrapper());
goog.graphics.ext.Element.call(this, group, opt_wrapper);
/**
* Array of child elements this group contains.
* @type {Array.<goog.graphics.ext.Element>}
* @private
*/
this.children_ = [];
};
goog.inherits(goog.graphics.ext.Group, goog.graphics.ext.Element);
/**
* Add an element to the group. This should be treated as package local, as
* it is called by the draw* methods.
* @param {!goog.graphics.ext.Element} element The element to add.
* @param {boolean=} opt_chain Whether this addition is part of a longer set
* of element additions.
*/
goog.graphics.ext.Group.prototype.addChild = function(element, opt_chain) {
if (!goog.array.contains(this.children_, element)) {
this.children_.push(element);
}
var transformed = this.growToFit_(element);
if (element.isParentDependent()) {
element.parentTransform();
}
if (!opt_chain && element.isPendingTransform()) {
element.reset();
}
if (transformed) {
this.reset();
}
};
/**
* Remove an element from the group.
* @param {goog.graphics.ext.Element} element The element to remove.
*/
goog.graphics.ext.Group.prototype.removeChild = function(element) {
goog.array.remove(this.children_, element);
// TODO(robbyw): shape.fireEvent('delete')
this.getGraphicsImplementation().removeElement(element.getWrapper());
};
/**
* Calls the given function on each of this component's children in order. If
* {@code opt_obj} is provided, it will be used as the 'this' object in the
* function when called. The function should take two arguments: the child
* component and its 0-based index. The return value is ignored.
* @param {Function} f The function to call for every child component; should
* take 2 arguments (the child and its index).
* @param {Object=} opt_obj Used as the 'this' object in f when called.
*/
goog.graphics.ext.Group.prototype.forEachChild = function(f, opt_obj) {
if (this.children_) {
goog.array.forEach(this.children_, f, opt_obj);
}
};
/**
* @return {goog.graphics.GroupElement} The underlying thin wrapper.
* @protected
*/
goog.graphics.ext.Group.prototype.getWrapper;
/**
* Reset the element.
* @override
*/
goog.graphics.ext.Group.prototype.reset = function() {
goog.graphics.ext.Group.superClass_.reset.call(this);
this.updateChildren();
};
/**
* Called from the parent class, this method resets any pre-computed positions
* and sizes.
* @protected
* @override
*/
goog.graphics.ext.Group.prototype.redraw = function() {
this.getWrapper().setSize(this.getWidth(), this.getHeight());
this.transformChildren();
};
/**
* Transform the children that need to be transformed.
* @protected
*/
goog.graphics.ext.Group.prototype.transformChildren = function() {
this.forEachChild(function(child) {
if (child.isParentDependent()) {
child.parentTransform();
}
});
};
/**
* As part of the reset process, update child elements.
*/
goog.graphics.ext.Group.prototype.updateChildren = function() {
this.forEachChild(function(child) {
if (child.isParentDependent() || child.isPendingTransform()) {
child.reset();
} else if (child.updateChildren) {
child.updateChildren();
}
});
};
/**
* When adding an element, grow this group's bounds to fit it.
* @param {!goog.graphics.ext.Element} element The added element.
* @return {boolean} Whether the size of this group changed.
* @private
*/
goog.graphics.ext.Group.prototype.growToFit_ = function(element) {
var transformed = false;
var x = element.getMaxX();
if (x > this.getWidth()) {
this.setMinWidth(x);
transformed = true;
}
var y = element.getMaxY();
if (y > this.getHeight()) {
this.setMinHeight(y);
transformed = true;
}
return transformed;
};
/**
* @return {number} The width of the element's coordinate space.
*/
goog.graphics.ext.Group.prototype.getCoordinateWidth = function() {
return this.getWidth();
};
/**
* @return {number} The height of the element's coordinate space.
*/
goog.graphics.ext.Group.prototype.getCoordinateHeight = function() {
return this.getHeight();
};
/**
* Remove all drawing elements from the group.
*/
goog.graphics.ext.Group.prototype.clear = function() {
while (this.children_.length) {
this.removeChild(this.children_[0]);
}
};

View File

@@ -0,0 +1,63 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thick wrapper around images.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Image');
goog.require('goog.graphics.ext.Element');
/**
* Wrapper for a graphics image element.
* @param {goog.graphics.ext.Group} group Parent for this element.
* @param {string} src The path to the image to display.
* @constructor
* @extends {goog.graphics.ext.Element}
*/
goog.graphics.ext.Image = function(group, src) {
// Initialize with some stock values.
var wrapper = group.getGraphicsImplementation().drawImage(0, 0, 1, 1, src,
group.getWrapper());
goog.graphics.ext.Element.call(this, group, wrapper);
};
goog.inherits(goog.graphics.ext.Image, goog.graphics.ext.Element);
/**
* Redraw the image. Called when the coordinate system is changed.
* @protected
* @override
*/
goog.graphics.ext.Image.prototype.redraw = function() {
goog.graphics.ext.Image.superClass_.redraw.call(this);
// Our position is already handled bu transform_.
this.getWrapper().setSize(this.getWidth(), this.getHeight());
};
/**
* Update the source of the image.
* @param {string} src Source of the image.
*/
goog.graphics.ext.Image.prototype.setSource = function(src) {
this.getWrapper().setSource(src);
};

View File

@@ -0,0 +1,142 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thick wrapper around paths.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Path');
goog.require('goog.graphics.AffineTransform');
goog.require('goog.graphics.Path');
goog.require('goog.math');
goog.require('goog.math.Rect');
/**
* Creates a path object
* @constructor
* @extends {goog.graphics.Path}
*/
goog.graphics.ext.Path = function() {
goog.graphics.Path.call(this);
};
goog.inherits(goog.graphics.ext.Path, goog.graphics.Path);
/**
* Optional cached or user specified bounding box. A user may wish to
* precompute a bounding box to save time and include more accurate
* computations.
* @type {goog.math.Rect?}
* @private
*/
goog.graphics.ext.Path.prototype.bounds_ = null;
/**
* Clones the path.
* @return {!goog.graphics.ext.Path} A clone of this path.
* @override
*/
goog.graphics.ext.Path.prototype.clone = function() {
var output = /** @type {goog.graphics.ext.Path} */
(goog.graphics.ext.Path.superClass_.clone.call(this));
output.bounds_ = this.bounds_ && this.bounds_.clone();
return output;
};
/**
* Transforms the path. Only simple paths are transformable. Attempting
* to transform a non-simple path will throw an error.
* @param {!goog.graphics.AffineTransform} tx The transformation to perform.
* @return {!goog.graphics.ext.Path} The path itself.
* @override
*/
goog.graphics.ext.Path.prototype.transform = function(tx) {
goog.graphics.ext.Path.superClass_.transform.call(this, tx);
// Make sure the precomputed bounds are cleared when the path is transformed.
this.bounds_ = null;
return this;
};
/**
* Modify the bounding box of the path. This may cause the path to be
* simplified (i.e. arcs converted to curves) as a side-effect.
* @param {number} deltaX How far to translate the x coordinates.
* @param {number} deltaY How far to translate the y coordinates.
* @param {number} xFactor After translation, all x coordinates are multiplied
* by this number.
* @param {number} yFactor After translation, all y coordinates are multiplied
* by this number.
* @return {goog.graphics.ext.Path} The path itself.
*/
goog.graphics.ext.Path.prototype.modifyBounds = function(deltaX, deltaY,
xFactor, yFactor) {
if (!this.isSimple()) {
var simple = goog.graphics.Path.createSimplifiedPath(this);
this.clear();
this.appendPath(simple);
}
return this.transform(goog.graphics.AffineTransform.getScaleInstance(
xFactor, yFactor).translate(deltaX, deltaY));
};
/**
* Set the precomputed bounds.
* @param {goog.math.Rect?} bounds The bounds to use, or set to null to clear
* and recompute on the next call to getBoundingBox.
*/
goog.graphics.ext.Path.prototype.useBoundingBox = function(bounds) {
this.bounds_ = bounds && bounds.clone();
};
/**
* @return {goog.math.Rect?} The bounding box of the path, or null if the
* path is empty.
*/
goog.graphics.ext.Path.prototype.getBoundingBox = function() {
if (!this.bounds_ && !this.isEmpty()) {
var minY;
var minX = minY = Number.POSITIVE_INFINITY;
var maxY;
var maxX = maxY = Number.NEGATIVE_INFINITY;
var simplePath = this.isSimple() ? this :
goog.graphics.Path.createSimplifiedPath(this);
simplePath.forEachSegment(function(type, points) {
for (var i = 0, len = points.length; i < len; i += 2) {
minX = Math.min(minX, points[i]);
maxX = Math.max(maxX, points[i]);
minY = Math.min(minY, points[i + 1]);
maxY = Math.max(maxY, points[i + 1]);
}
});
this.bounds_ = new goog.math.Rect(minX, minY, maxX - minX, maxY - minY);
}
return this.bounds_;
};

View File

@@ -0,0 +1,54 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thick wrapper around rectangles.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Rectangle');
goog.require('goog.graphics.ext.StrokeAndFillElement');
/**
* Wrapper for a graphics rectangle element.
* @param {goog.graphics.ext.Group} group Parent for this element.
* @constructor
* @extends {goog.graphics.ext.StrokeAndFillElement}
*/
goog.graphics.ext.Rectangle = function(group) {
// Initialize with some stock values.
var wrapper = group.getGraphicsImplementation().drawRect(0, 0, 1, 1, null,
null, group.getWrapper());
goog.graphics.ext.StrokeAndFillElement.call(this, group, wrapper);
};
goog.inherits(goog.graphics.ext.Rectangle,
goog.graphics.ext.StrokeAndFillElement);
/**
* Redraw the rectangle. Called when the coordinate system is changed.
* @protected
* @override
*/
goog.graphics.ext.Rectangle.prototype.redraw = function() {
goog.graphics.ext.Rectangle.superClass_.redraw.call(this);
// Our position is already handled by transform_.
this.getWrapper().setSize(this.getWidth(), this.getHeight());
};

View File

@@ -0,0 +1,146 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thick wrapper around shapes with custom paths.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.Shape');
goog.require('goog.graphics.ext.Path');
goog.require('goog.graphics.ext.StrokeAndFillElement');
goog.require('goog.math.Rect');
/**
* Wrapper for a graphics shape element.
* @param {goog.graphics.ext.Group} group Parent for this element.
* @param {!goog.graphics.ext.Path} path The path to draw.
* @param {boolean=} opt_autoSize Optional flag to specify the path should
* automatically resize to fit the element. Defaults to false.
* @constructor
* @extends {goog.graphics.ext.StrokeAndFillElement}
*/
goog.graphics.ext.Shape = function(group, path, opt_autoSize) {
this.autoSize_ = !!opt_autoSize;
var graphics = group.getGraphicsImplementation();
var wrapper = graphics.drawPath(path, null, null,
group.getWrapper());
goog.graphics.ext.StrokeAndFillElement.call(this, group, wrapper);
this.setPath(path);
};
goog.inherits(goog.graphics.ext.Shape, goog.graphics.ext.StrokeAndFillElement);
/**
* Whether or not to automatically resize the shape's path when the element
* itself is resized.
* @type {boolean}
* @private
*/
goog.graphics.ext.Shape.prototype.autoSize_ = false;
/**
* The original path, specified by the caller.
* @type {goog.graphics.Path}
* @private
*/
goog.graphics.ext.Shape.prototype.path_;
/**
* The bounding box of the original path.
* @type {goog.math.Rect?}
* @private
*/
goog.graphics.ext.Shape.prototype.boundingBox_ = null;
/**
* The scaled path.
* @type {goog.graphics.Path}
* @private
*/
goog.graphics.ext.Shape.prototype.scaledPath_;
/**
* Get the path drawn by this shape.
* @return {goog.graphics.Path?} The path drawn by this shape.
*/
goog.graphics.ext.Shape.prototype.getPath = function() {
return this.path_;
};
/**
* Set the path to draw.
* @param {goog.graphics.ext.Path} path The path to draw.
*/
goog.graphics.ext.Shape.prototype.setPath = function(path) {
this.path_ = path;
if (this.autoSize_) {
this.boundingBox_ = path.getBoundingBox();
}
this.scaleAndSetPath_();
};
/**
* Scale the internal path to fit.
* @private
*/
goog.graphics.ext.Shape.prototype.scaleAndSetPath_ = function() {
this.scaledPath_ = this.boundingBox_ ? this.path_.clone().modifyBounds(
-this.boundingBox_.left, -this.boundingBox_.top,
this.getWidth() / (this.boundingBox_.width || 1),
this.getHeight() / (this.boundingBox_.height || 1)) : this.path_;
var wrapper = this.getWrapper();
if (wrapper) {
wrapper.setPath(this.scaledPath_);
}
};
/**
* Redraw the ellipse. Called when the coordinate system is changed.
* @protected
* @override
*/
goog.graphics.ext.Shape.prototype.redraw = function() {
goog.graphics.ext.Shape.superClass_.redraw.call(this);
if (this.autoSize_) {
this.scaleAndSetPath_();
}
};
/**
* @return {boolean} Whether the shape is parent dependent.
* @protected
* @override
*/
goog.graphics.ext.Shape.prototype.checkParentDependent = function() {
return this.autoSize_ ||
goog.graphics.ext.Shape.superClass_.checkParentDependent.call(this);
};

View File

@@ -0,0 +1,70 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thick wrapper around elements with stroke and fill.
* @author robbyw@google.com (Robby Walker)
*/
goog.provide('goog.graphics.ext.StrokeAndFillElement');
goog.require('goog.graphics.ext.Element');
/**
* Interface for a graphics element that has a stroke and fill.
* This is the base interface for ellipse, rectangle and other
* shape interfaces.
* You should not construct objects from this constructor. Use a subclass.
* @param {goog.graphics.ext.Group} group Parent for this element.
* @param {goog.graphics.StrokeAndFillElement} wrapper The thin wrapper to wrap.
* @constructor
* @extends {goog.graphics.ext.Element}
*/
goog.graphics.ext.StrokeAndFillElement = function(group, wrapper) {
goog.graphics.ext.Element.call(this, group, wrapper);
};
goog.inherits(goog.graphics.ext.StrokeAndFillElement,
goog.graphics.ext.Element);
/**
* Sets the fill for this element.
* @param {goog.graphics.Fill?} fill The fill object.
*/
goog.graphics.ext.StrokeAndFillElement.prototype.setFill = function(fill) {
this.getWrapper().setFill(fill);
};
/**
* Sets the stroke for this element.
* @param {goog.graphics.Stroke?} stroke The stroke object.
*/
goog.graphics.ext.StrokeAndFillElement.prototype.setStroke = function(stroke) {
this.getWrapper().setStroke(stroke);
};
/**
* Redraw the rectangle. Called when the coordinate system is changed.
* @protected
* @override
*/
goog.graphics.ext.StrokeAndFillElement.prototype.redraw = function() {
this.getWrapper().reapplyStroke();
};

View File

@@ -0,0 +1,46 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a fill goog.graphics.
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.graphics.Fill');
/**
* Creates a fill object
* @constructor
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.Fill = function() {};
/**
* @return {string} The start color of a gradient fill.
*/
goog.graphics.Fill.prototype.getColor1 = goog.abstractMethod;
/**
* @return {string} The end color of a gradient fill.
*/
goog.graphics.Fill.prototype.getColor2 = goog.abstractMethod;

View File

@@ -0,0 +1,63 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a font to be used with a Renderer.
* @author arv@google.com (Erik Arvidsson)
* @see ../demos/graphics/basicelements.html
*/
goog.provide('goog.graphics.Font');
/**
* This class represents a font to be used with a renderer.
* @param {number} size The font size.
* @param {string} family The font family.
* @constructor
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.Font = function(size, family) {
/**
* Font size.
* @type {number}
*/
this.size = size;
// TODO(arv): Is this in pixels or drawing units based on the coord size?
/**
* The name of the font family to use, can be a comma separated string.
* @type {string}
*/
this.family = family;
};
/**
* Indication if text should be bolded
* @type {boolean}
*/
goog.graphics.Font.prototype.bold = false;
/**
* Indication if text should be in italics
* @type {boolean}
*/
goog.graphics.Font.prototype.italic = false;

View File

@@ -0,0 +1,135 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Graphics utility functions and factory methods.
* @author arv@google.com (Erik Arvidsson)
* @see ../demos/graphics/advancedcoordinates.html
* @see ../demos/graphics/advancedcoordinates2.html
* @see ../demos/graphics/basicelements.html
* @see ../demos/graphics/events.html
* @see ../demos/graphics/modifyelements.html
* @see ../demos/graphics/tiger.html
*/
goog.provide('goog.graphics');
goog.require('goog.graphics.CanvasGraphics');
goog.require('goog.graphics.SvgGraphics');
goog.require('goog.graphics.VmlGraphics');
goog.require('goog.userAgent');
/**
* Returns an instance of goog.graphics.AbstractGraphics that knows how to draw
* for the current platform (A factory for the proper Graphics implementation)
* @param {string|number} width The width in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {string|number} height The height in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {?number=} opt_coordWidth The optional coordinate width - if
* omitted or null, defaults to same as width.
* @param {?number=} opt_coordHeight The optional coordinate height - if
* omitted or null, defaults to same as height.
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
* document we want to render in.
* @return {goog.graphics.AbstractGraphics} The created instance.
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.createGraphics = function(width, height, opt_coordWidth,
opt_coordHeight, opt_domHelper) {
var graphics;
if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) {
graphics = new goog.graphics.VmlGraphics(width, height,
opt_coordWidth, opt_coordHeight, opt_domHelper);
} else if (goog.userAgent.WEBKIT &&
(!goog.userAgent.isVersionOrHigher('420') ||
goog.userAgent.MOBILE)) {
graphics = new goog.graphics.CanvasGraphics(width, height,
opt_coordWidth, opt_coordHeight, opt_domHelper);
} else {
graphics = new goog.graphics.SvgGraphics(width, height,
opt_coordWidth, opt_coordHeight, opt_domHelper);
}
// Create the dom now, because all drawing methods require that the
// main dom element (the canvas) has been already created.
graphics.createDom();
return graphics;
};
/**
* Returns an instance of goog.graphics.AbstractGraphics that knows how to draw
* for the current platform (A factory for the proper Graphics implementation)
* @param {string|number} width The width in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {string|number} height The height in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {?number=} opt_coordWidth The optional coordinate width, defaults to
* same as width.
* @param {?number=} opt_coordHeight The optional coordinate height, defaults to
* same as height.
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
* document we want to render in.
* @return {goog.graphics.AbstractGraphics} The created instance.
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.createSimpleGraphics = function(width, height,
opt_coordWidth, opt_coordHeight, opt_domHelper) {
if (goog.userAgent.MAC && goog.userAgent.GECKO &&
!goog.userAgent.isVersionOrHigher('1.9a')) {
// Canvas is 6x faster than SVG on Mac FF 2.0
var graphics = new goog.graphics.CanvasGraphics(
width, height, opt_coordWidth, opt_coordHeight,
opt_domHelper);
graphics.createDom();
return graphics;
}
// Otherwise, defer to normal graphics object creation.
return goog.graphics.createGraphics(width, height, opt_coordWidth,
opt_coordHeight, opt_domHelper);
};
/**
* Static function to check if the current browser has Graphics support.
* @return {boolean} True if the current browser has Graphics support.
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.isBrowserSupported = function() {
if (goog.userAgent.IE) {
return goog.userAgent.isVersionOrHigher('5.5');
}
if (goog.userAgent.GECKO) {
return goog.userAgent.isVersionOrHigher('1.8');
}
if (goog.userAgent.OPERA) {
return goog.userAgent.isVersionOrHigher('9.0');
}
if (goog.userAgent.WEBKIT) {
return goog.userAgent.isVersionOrHigher('412');
}
return false;
};

View File

@@ -0,0 +1,59 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element for graphics groups.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.GroupElement');
goog.require('goog.graphics.Element');
/**
* Interface for a graphics group element.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.graphics.Element}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.GroupElement = function(element, graphics) {
goog.graphics.Element.call(this, element, graphics);
};
goog.inherits(goog.graphics.GroupElement, goog.graphics.Element);
/**
* Remove all drawing elements from the group.
*/
goog.graphics.GroupElement.prototype.clear = goog.abstractMethod;
/**
* Set the size of the group element.
* @param {number|string} width The width of the group element.
* @param {number|string} height The height of the group element.
*/
goog.graphics.GroupElement.prototype.setSize = goog.abstractMethod;

View File

@@ -0,0 +1,70 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element for images.
*/
goog.provide('goog.graphics.ImageElement');
goog.require('goog.graphics.Element');
/**
* Interface for a graphics image element.
* You should not construct objects from this constructor. Instead,
* you should use {@code goog.graphics.Graphics.drawImage} and it
* will return an implementation of this interface for you.
*
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.graphics.Element}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.ImageElement = function(element, graphics) {
goog.graphics.Element.call(this, element, graphics);
};
goog.inherits(goog.graphics.ImageElement, goog.graphics.Element);
/**
* Update the position of the image.
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
*/
goog.graphics.ImageElement.prototype.setPosition = goog.abstractMethod;
/**
* Update the size of the image.
*
* @param {number} width Width of image.
* @param {number} height Height of image.
*/
goog.graphics.ImageElement.prototype.setSize = goog.abstractMethod;
/**
* Update the source of the image.
* @param {string} src Source of the image.
*/
goog.graphics.ImageElement.prototype.setSource = goog.abstractMethod;

View File

@@ -0,0 +1,174 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a gradient to be used with a Graphics implementor.
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.graphics.LinearGradient');
goog.require('goog.asserts');
goog.require('goog.graphics.Fill');
/**
* Creates an immutable linear gradient fill object.
*
* @param {number} x1 Start X position of the gradient.
* @param {number} y1 Start Y position of the gradient.
* @param {number} x2 End X position of the gradient.
* @param {number} y2 End Y position of the gradient.
* @param {string} color1 Start color of the gradient.
* @param {string} color2 End color of the gradient.
* @param {?number=} opt_opacity1 Start opacity of the gradient, both or neither
* of opt_opacity1 and opt_opacity2 have to be set.
* @param {?number=} opt_opacity2 End opacity of the gradient.
* @constructor
* @extends {goog.graphics.Fill}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.LinearGradient =
function(x1, y1, x2, y2, color1, color2, opt_opacity1, opt_opacity2) {
/**
* Start X position of the gradient.
* @type {number}
* @private
*/
this.x1_ = x1;
/**
* Start Y position of the gradient.
* @type {number}
* @private
*/
this.y1_ = y1;
/**
* End X position of the gradient.
* @type {number}
* @private
*/
this.x2_ = x2;
/**
* End Y position of the gradient.
* @type {number}
* @private
*/
this.y2_ = y2;
/**
* Start color of the gradient.
* @type {string}
* @private
*/
this.color1_ = color1;
/**
* End color of the gradient.
* @type {string}
* @private
*/
this.color2_ = color2;
goog.asserts.assert(
goog.isNumber(opt_opacity1) == goog.isNumber(opt_opacity2),
'Both or neither of opt_opacity1 and opt_opacity2 have to be set.');
/**
* Start opacity of the gradient.
* @type {?number}
* @private
*/
this.opacity1_ = goog.isDef(opt_opacity1) ? opt_opacity1 : null;
/**
* End opacity of the gradient.
* @type {?number}
* @private
*/
this.opacity2_ = goog.isDef(opt_opacity2) ? opt_opacity2 : null;
};
goog.inherits(goog.graphics.LinearGradient, goog.graphics.Fill);
/**
* @return {number} The start X position of the gradient.
*/
goog.graphics.LinearGradient.prototype.getX1 = function() {
return this.x1_;
};
/**
* @return {number} The start Y position of the gradient.
*/
goog.graphics.LinearGradient.prototype.getY1 = function() {
return this.y1_;
};
/**
* @return {number} The end X position of the gradient.
*/
goog.graphics.LinearGradient.prototype.getX2 = function() {
return this.x2_;
};
/**
* @return {number} The end Y position of the gradient.
*/
goog.graphics.LinearGradient.prototype.getY2 = function() {
return this.y2_;
};
/**
* @override
*/
goog.graphics.LinearGradient.prototype.getColor1 = function() {
return this.color1_;
};
/**
* @override
*/
goog.graphics.LinearGradient.prototype.getColor2 = function() {
return this.color2_;
};
/**
* @return {?number} The start opacity of the gradient.
*/
goog.graphics.LinearGradient.prototype.getOpacity1 = function() {
return this.opacity1_;
};
/**
* @return {?number} The end opacity of the gradient.
*/
goog.graphics.LinearGradient.prototype.getOpacity2 = function() {
return this.opacity2_;
};

View File

@@ -0,0 +1,512 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a path used with a Graphics implementation.
* @author arv@google.com (Erik Arvidsson)
* @author glenning@google.com (Anthony Glenning)
*/
goog.provide('goog.graphics.Path');
goog.provide('goog.graphics.Path.Segment');
goog.require('goog.array');
goog.require('goog.math');
/**
* Creates a path object. A path is a sequence of segments and may be open or
* closed. Path uses the EVEN-ODD fill rule for determining the interior of the
* path. A path must start with a moveTo command.
*
* A "simple" path does not contain any arcs and may be transformed using
* the {@code transform} method.
*
* @constructor
*/
goog.graphics.Path = function() {
/**
* The segment types that constitute this path.
* @type {!Array.<number>}
* @private
*/
this.segments_ = [];
/**
* The number of repeated segments of the current type.
* @type {!Array.<number>}
* @private
*/
this.count_ = [];
/**
* The arguments corresponding to each of the segments.
* @type {!Array.<number>}
* @private
*/
this.arguments_ = [];
};
/**
* The coordinates of the point which closes the path (the point of the
* last moveTo command).
* @type {Array.<number>?}
* @private
*/
goog.graphics.Path.prototype.closePoint_ = null;
/**
* The coordinates most recently added to the end of the path.
* @type {Array.<number>?}
* @private
*/
goog.graphics.Path.prototype.currentPoint_ = null;
/**
* Flag for whether this is a simple path (contains no arc segments).
* @type {boolean}
* @private
*/
goog.graphics.Path.prototype.simple_ = true;
/**
* Path segment types.
* @enum {number}
*/
goog.graphics.Path.Segment = {
MOVETO: 0,
LINETO: 1,
CURVETO: 2,
ARCTO: 3,
CLOSE: 4
};
/**
* The number of points for each segment type.
* @type {!Array.<number>}
* @private
*/
goog.graphics.Path.segmentArgCounts_ = (function() {
var counts = [];
counts[goog.graphics.Path.Segment.MOVETO] = 2;
counts[goog.graphics.Path.Segment.LINETO] = 2;
counts[goog.graphics.Path.Segment.CURVETO] = 6;
counts[goog.graphics.Path.Segment.ARCTO] = 6;
counts[goog.graphics.Path.Segment.CLOSE] = 0;
return counts;
})();
/**
* Returns the number of points for a segment type.
*
* @param {number} segment The segment type.
* @return {number} The number of points.
*/
goog.graphics.Path.getSegmentCount = function(segment) {
return goog.graphics.Path.segmentArgCounts_[segment];
};
/**
* Appends another path to the end of this path.
*
* @param {!goog.graphics.Path} path The path to append.
* @return {!goog.graphics.Path} This path.
*/
goog.graphics.Path.prototype.appendPath = function(path) {
if (path.currentPoint_) {
Array.prototype.push.apply(this.segments_, path.segments_);
Array.prototype.push.apply(this.count_, path.count_);
Array.prototype.push.apply(this.arguments_, path.arguments_);
this.currentPoint_ = path.currentPoint_.concat();
this.closePoint_ = path.closePoint_.concat();
this.simple_ = this.simple_ && path.simple_;
}
return this;
};
/**
* Clears the path.
*
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.clear = function() {
this.segments_.length = 0;
this.count_.length = 0;
this.arguments_.length = 0;
delete this.closePoint_;
delete this.currentPoint_;
delete this.simple_;
return this;
};
/**
* Adds a point to the path by moving to the specified point. Repeated moveTo
* commands are collapsed into a single moveTo.
*
* @param {number} x X coordinate of destination point.
* @param {number} y Y coordinate of destination point.
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.moveTo = function(x, y) {
if (goog.array.peek(this.segments_) == goog.graphics.Path.Segment.MOVETO) {
this.arguments_.length -= 2;
} else {
this.segments_.push(goog.graphics.Path.Segment.MOVETO);
this.count_.push(1);
}
this.arguments_.push(x, y);
this.currentPoint_ = this.closePoint_ = [x, y];
return this;
};
/**
* Adds points to the path by drawing a straight line to each point.
*
* @param {...number} var_args The coordinates of each destination point as x, y
* value pairs.
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.lineTo = function(var_args) {
var lastSegment = goog.array.peek(this.segments_);
if (lastSegment == null) {
throw Error('Path cannot start with lineTo');
}
if (lastSegment != goog.graphics.Path.Segment.LINETO) {
this.segments_.push(goog.graphics.Path.Segment.LINETO);
this.count_.push(0);
}
for (var i = 0; i < arguments.length; i += 2) {
var x = arguments[i];
var y = arguments[i + 1];
this.arguments_.push(x, y);
}
this.count_[this.count_.length - 1] += i / 2;
this.currentPoint_ = [x, y];
return this;
};
/**
* Adds points to the path by drawing cubic Bezier curves. Each curve is
* specified using 3 points (6 coordinates) - two control points and the end
* point of the curve.
*
* @param {...number} var_args The coordinates specifiying each curve in sets of
* 6 points: {@code [x1, y1]} the first control point, {@code [x2, y2]} the
* second control point and {@code [x, y]} the end point.
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.curveTo = function(var_args) {
var lastSegment = goog.array.peek(this.segments_);
if (lastSegment == null) {
throw Error('Path cannot start with curve');
}
if (lastSegment != goog.graphics.Path.Segment.CURVETO) {
this.segments_.push(goog.graphics.Path.Segment.CURVETO);
this.count_.push(0);
}
for (var i = 0; i < arguments.length; i += 6) {
var x = arguments[i + 4];
var y = arguments[i + 5];
this.arguments_.push(arguments[i], arguments[i + 1],
arguments[i + 2], arguments[i + 3], x, y);
}
this.count_[this.count_.length - 1] += i / 6;
this.currentPoint_ = [x, y];
return this;
};
/**
* Adds a path command to close the path by connecting the
* last point to the first point.
*
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.close = function() {
var lastSegment = goog.array.peek(this.segments_);
if (lastSegment == null) {
throw Error('Path cannot start with close');
}
if (lastSegment != goog.graphics.Path.Segment.CLOSE) {
this.segments_.push(goog.graphics.Path.Segment.CLOSE);
this.count_.push(1);
this.currentPoint_ = this.closePoint_;
}
return this;
};
/**
* Adds a path command to draw an arc centered at the point {@code (cx, cy)}
* with radius {@code rx} along the x-axis and {@code ry} along the y-axis from
* {@code startAngle} through {@code extent} degrees. Positive rotation is in
* the direction from positive x-axis to positive y-axis.
*
* @param {number} cx X coordinate of center of ellipse.
* @param {number} cy Y coordinate of center of ellipse.
* @param {number} rx Radius of ellipse on x axis.
* @param {number} ry Radius of ellipse on y axis.
* @param {number} fromAngle Starting angle measured in degrees from the
* positive x-axis.
* @param {number} extent The span of the arc in degrees.
* @param {boolean} connect If true, the starting point of the arc is connected
* to the current point.
* @return {!goog.graphics.Path} The path itself.
* @deprecated Use {@code arcTo} or {@code arcToAsCurves} instead.
*/
goog.graphics.Path.prototype.arc = function(cx, cy, rx, ry,
fromAngle, extent, connect) {
var startX = cx + goog.math.angleDx(fromAngle, rx);
var startY = cy + goog.math.angleDy(fromAngle, ry);
if (connect) {
if (!this.currentPoint_ || startX != this.currentPoint_[0] ||
startY != this.currentPoint_[1]) {
this.lineTo(startX, startY);
}
} else {
this.moveTo(startX, startY);
}
return this.arcTo(rx, ry, fromAngle, extent);
};
/**
* Adds a path command to draw an arc starting at the path's current point,
* with radius {@code rx} along the x-axis and {@code ry} along the y-axis from
* {@code startAngle} through {@code extent} degrees. Positive rotation is in
* the direction from positive x-axis to positive y-axis.
*
* This method makes the path non-simple.
*
* @param {number} rx Radius of ellipse on x axis.
* @param {number} ry Radius of ellipse on y axis.
* @param {number} fromAngle Starting angle measured in degrees from the
* positive x-axis.
* @param {number} extent The span of the arc in degrees.
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.arcTo = function(rx, ry, fromAngle, extent) {
var cx = this.currentPoint_[0] - goog.math.angleDx(fromAngle, rx);
var cy = this.currentPoint_[1] - goog.math.angleDy(fromAngle, ry);
var ex = cx + goog.math.angleDx(fromAngle + extent, rx);
var ey = cy + goog.math.angleDy(fromAngle + extent, ry);
this.segments_.push(goog.graphics.Path.Segment.ARCTO);
this.count_.push(1);
this.arguments_.push(rx, ry, fromAngle, extent, ex, ey);
this.simple_ = false;
this.currentPoint_ = [ex, ey];
return this;
};
/**
* Same as {@code arcTo}, but approximates the arc using bezier curves.
.* As a result, this method does not affect the simplified status of this path.
* The algorithm is adapted from {@code java.awt.geom.ArcIterator}.
*
* @param {number} rx Radius of ellipse on x axis.
* @param {number} ry Radius of ellipse on y axis.
* @param {number} fromAngle Starting angle measured in degrees from the
* positive x-axis.
* @param {number} extent The span of the arc in degrees.
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.arcToAsCurves = function(
rx, ry, fromAngle, extent) {
var cx = this.currentPoint_[0] - goog.math.angleDx(fromAngle, rx);
var cy = this.currentPoint_[1] - goog.math.angleDy(fromAngle, ry);
var extentRad = goog.math.toRadians(extent);
var arcSegs = Math.ceil(Math.abs(extentRad) / Math.PI * 2);
var inc = extentRad / arcSegs;
var angle = goog.math.toRadians(fromAngle);
for (var j = 0; j < arcSegs; j++) {
var relX = Math.cos(angle);
var relY = Math.sin(angle);
var z = 4 / 3 * Math.sin(inc / 2) / (1 + Math.cos(inc / 2));
var c0 = cx + (relX - z * relY) * rx;
var c1 = cy + (relY + z * relX) * ry;
angle += inc;
relX = Math.cos(angle);
relY = Math.sin(angle);
this.curveTo(c0, c1,
cx + (relX + z * relY) * rx,
cy + (relY - z * relX) * ry,
cx + relX * rx,
cy + relY * ry);
}
return this;
};
/**
* Iterates over the path calling the supplied callback once for each path
* segment. The arguments to the callback function are the segment type and
* an array of its arguments.
*
* The {@code LINETO} and {@code CURVETO} arrays can contain multiple
* segments of the same type. The number of segments is the length of the
* array divided by the segment length (2 for lines, 6 for curves).
*
* As a convenience the {@code ARCTO} segment also includes the end point as the
* last two arguments: {@code rx, ry, fromAngle, extent, x, y}.
*
* @param {function(number, Array)} callback The function to call with each
* path segment.
*/
goog.graphics.Path.prototype.forEachSegment = function(callback) {
var points = this.arguments_;
var index = 0;
for (var i = 0, length = this.segments_.length; i < length; i++) {
var seg = this.segments_[i];
var n = goog.graphics.Path.segmentArgCounts_[seg] * this.count_[i];
callback(seg, points.slice(index, index + n));
index += n;
}
};
/**
* Returns the coordinates most recently added to the end of the path.
*
* @return {Array.<number>?} An array containing the ending coordinates of the
* path of the form {@code [x, y]}.
*/
goog.graphics.Path.prototype.getCurrentPoint = function() {
return this.currentPoint_ && this.currentPoint_.concat();
};
/**
* @return {!goog.graphics.Path} A copy of this path.
*/
goog.graphics.Path.prototype.clone = function() {
var path = new this.constructor();
path.segments_ = this.segments_.concat();
path.count_ = this.count_.concat();
path.arguments_ = this.arguments_.concat();
path.closePoint_ = this.closePoint_ && this.closePoint_.concat();
path.currentPoint_ = this.currentPoint_ && this.currentPoint_.concat();
path.simple_ = this.simple_;
return path;
};
/**
* Returns true if this path contains no arcs. Simplified paths can be
* created using {@code createSimplifiedPath}.
*
* @return {boolean} True if the path contains no arcs.
*/
goog.graphics.Path.prototype.isSimple = function() {
return this.simple_;
};
/**
* A map from segment type to the path function to call to simplify a path.
* @type {!Object}
* @private
* @suppress {deprecated} goog.graphics.Path is deprecated.
*/
goog.graphics.Path.simplifySegmentMap_ = (function() {
var map = {};
map[goog.graphics.Path.Segment.MOVETO] = goog.graphics.Path.prototype.moveTo;
map[goog.graphics.Path.Segment.LINETO] = goog.graphics.Path.prototype.lineTo;
map[goog.graphics.Path.Segment.CLOSE] = goog.graphics.Path.prototype.close;
map[goog.graphics.Path.Segment.CURVETO] =
goog.graphics.Path.prototype.curveTo;
map[goog.graphics.Path.Segment.ARCTO] =
goog.graphics.Path.prototype.arcToAsCurves;
return map;
})();
/**
* Creates a copy of the given path, replacing {@code arcTo} with
* {@code arcToAsCurves}. The resulting path is simplified and can
* be transformed.
*
* @param {!goog.graphics.Path} src The path to simplify.
* @return {!goog.graphics.Path} A new simplified path.
* @suppress {deprecated} goog.graphics is deprecated.
*/
goog.graphics.Path.createSimplifiedPath = function(src) {
if (src.isSimple()) {
return src.clone();
}
var path = new goog.graphics.Path();
src.forEachSegment(function(segment, args) {
goog.graphics.Path.simplifySegmentMap_[segment].apply(path, args);
});
return path;
};
// TODO(chrisn): Delete this method
/**
* Creates a transformed copy of this path. The path is simplified
* {@see #createSimplifiedPath} prior to transformation.
*
* @param {!goog.graphics.AffineTransform} tx The transformation to perform.
* @return {!goog.graphics.Path} A new, transformed path.
*/
goog.graphics.Path.prototype.createTransformedPath = function(tx) {
var path = goog.graphics.Path.createSimplifiedPath(this);
path.transform(tx);
return path;
};
/**
* Transforms the path. Only simple paths are transformable. Attempting
* to transform a non-simple path will throw an error.
*
* @param {!goog.graphics.AffineTransform} tx The transformation to perform.
* @return {!goog.graphics.Path} The path itself.
*/
goog.graphics.Path.prototype.transform = function(tx) {
if (!this.isSimple()) {
throw Error('Non-simple path');
}
tx.transform(this.arguments_, 0, this.arguments_, 0,
this.arguments_.length / 2);
if (this.closePoint_) {
tx.transform(this.closePoint_, 0, this.closePoint_, 0, 1);
}
if (this.currentPoint_ && this.closePoint_ != this.currentPoint_) {
tx.transform(this.currentPoint_, 0, this.currentPoint_, 0, 1);
}
return this;
};
/**
* @return {boolean} Whether the path is empty.
*/
goog.graphics.Path.prototype.isEmpty = function() {
return this.segments_.length == 0;
};

View File

@@ -0,0 +1,55 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element for paths.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.PathElement');
goog.require('goog.graphics.StrokeAndFillElement');
/**
* Interface for a graphics path element.
* You should not construct objects from this constructor. The graphics
* will return an implementation of this interface for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.StrokeAndFillElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.PathElement = function(element, graphics, stroke, fill) {
goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
fill);
};
goog.inherits(goog.graphics.PathElement, goog.graphics.StrokeAndFillElement);
/**
* Update the underlying path.
* @param {!goog.graphics.Path} path The path object to draw.
*/
goog.graphics.PathElement.prototype.setPath = goog.abstractMethod;

View File

@@ -0,0 +1,86 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Factories for common path types.
* @author nicksantos@google.com (Nick Santos)
*/
goog.provide('goog.graphics.paths');
goog.require('goog.graphics.Path');
goog.require('goog.math.Coordinate');
/**
* Defines a regular n-gon by specifing the center, a vertex, and the total
* number of vertices.
* @param {goog.math.Coordinate} center The center point.
* @param {goog.math.Coordinate} vertex The vertex, which implicitly defines
* a radius as well.
* @param {number} n The number of vertices.
* @return {!goog.graphics.Path} The path.
*/
goog.graphics.paths.createRegularNGon = function(center, vertex, n) {
var path = new goog.graphics.Path();
path.moveTo(vertex.x, vertex.y);
var startAngle = Math.atan2(vertex.y - center.y, vertex.x - center.x);
var radius = goog.math.Coordinate.distance(center, vertex);
for (var i = 1; i < n; i++) {
var angle = startAngle + 2 * Math.PI * (i / n);
path.lineTo(center.x + radius * Math.cos(angle),
center.y + radius * Math.sin(angle));
}
path.close();
return path;
};
/**
* Defines an arrow.
* @param {goog.math.Coordinate} a Point A.
* @param {goog.math.Coordinate} b Point B.
* @param {?number} aHead The size of the arrow head at point A.
* 0 omits the head.
* @param {?number} bHead The size of the arrow head at point B.
* 0 omits the head.
* @return {!goog.graphics.Path} The path.
*/
goog.graphics.paths.createArrow = function(a, b, aHead, bHead) {
var path = new goog.graphics.Path();
path.moveTo(a.x, a.y);
path.lineTo(b.x, b.y);
var angle = Math.atan2(b.y - a.y, b.x - a.x);
if (aHead) {
path.appendPath(
goog.graphics.paths.createRegularNGon(
new goog.math.Coordinate(
a.x + aHead * Math.cos(angle),
a.y + aHead * Math.sin(angle)),
a, 3));
}
if (bHead) {
path.appendPath(
goog.graphics.paths.createRegularNGon(
new goog.math.Coordinate(
b.x + bHead * Math.cos(angle + Math.PI),
b.y + bHead * Math.sin(angle + Math.PI)),
b, 3));
}
return path;
};

View File

@@ -0,0 +1,64 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element for rectangles.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.RectElement');
goog.require('goog.graphics.StrokeAndFillElement');
/**
* Interface for a graphics rectangle element.
* You should not construct objects from this constructor. The graphics
* will return an implementation of this interface for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.StrokeAndFillElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.RectElement = function(element, graphics, stroke, fill) {
goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
fill);
};
goog.inherits(goog.graphics.RectElement, goog.graphics.StrokeAndFillElement);
/**
* Update the position of the rectangle.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
*/
goog.graphics.RectElement.prototype.setPosition = goog.abstractMethod;
/**
* Update the size of the rectangle.
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
*/
goog.graphics.RectElement.prototype.setSize = goog.abstractMethod;

View File

@@ -0,0 +1,74 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a solid color fill goog.graphics.
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.graphics.SolidFill');
goog.require('goog.graphics.Fill');
/**
* Creates an immutable solid color fill object.
*
* @param {string} color The color of the background.
* @param {number=} opt_opacity The opacity of the background fill. The value
* must be greater than or equal to zero (transparent) and less than or
* equal to 1 (opaque).
* @constructor
* @extends {goog.graphics.Fill}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.SolidFill = function(color, opt_opacity) {
/**
* The color with which to fill.
* @type {string}
* @private
*/
this.color_ = color;
/**
* The opacity of the fill.
* @type {number}
* @private
*/
this.opacity_ = opt_opacity == null ? 1.0 : opt_opacity;
};
goog.inherits(goog.graphics.SolidFill, goog.graphics.Fill);
/**
* @return {string} The color of this fill.
*/
goog.graphics.SolidFill.prototype.getColor = function() {
return this.color_;
};
/**
* @return {number} The opacity of this fill.
*/
goog.graphics.SolidFill.prototype.getOpacity = function() {
return this.opacity_;
};

View File

@@ -0,0 +1,67 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Represents a stroke object for goog.graphics.
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.graphics.Stroke');
/**
* Creates an immutable stroke object.
*
* @param {number|string} width The width of the stroke.
* @param {string} color The color of the stroke.
* @constructor
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.Stroke = function(width, color) {
/**
* The width of the stroke.
* @type {number|string}
* @private
*/
this.width_ = width;
/**
* The color with which to fill.
* @type {string}
* @private
*/
this.color_ = color;
};
/**
* @return {number|string} The width of this stroke.
*/
goog.graphics.Stroke.prototype.getWidth = function() {
return this.width_;
};
/**
* @return {string} The color of this stroke.
*/
goog.graphics.Stroke.prototype.getColor = function() {
return this.color_;
};

View File

@@ -0,0 +1,115 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element for elements with a
* stroke and fill.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.StrokeAndFillElement');
goog.require('goog.graphics.Element');
/**
* Interface for a graphics element with a stroke and fill.
* This is the base interface for ellipse, rectangle and other
* shape interfaces.
* You should not construct objects from this constructor. The graphics
* will return an implementation of this interface for you.
*
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.Element}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.StrokeAndFillElement = function(element, graphics, stroke, fill) {
goog.graphics.Element.call(this, element, graphics);
this.setStroke(stroke);
this.setFill(fill);
};
goog.inherits(goog.graphics.StrokeAndFillElement, goog.graphics.Element);
/**
* The latest fill applied to this element.
* @type {goog.graphics.Fill?}
* @protected
*/
goog.graphics.StrokeAndFillElement.prototype.fill = null;
/**
* The latest stroke applied to this element.
* @type {goog.graphics.Stroke?}
* @private
*/
goog.graphics.StrokeAndFillElement.prototype.stroke_ = null;
/**
* Sets the fill for this element.
* @param {goog.graphics.Fill?} fill The fill object.
*/
goog.graphics.StrokeAndFillElement.prototype.setFill = function(fill) {
this.fill = fill;
this.getGraphics().setElementFill(this, fill);
};
/**
* @return {goog.graphics.Fill?} fill The fill object.
*/
goog.graphics.StrokeAndFillElement.prototype.getFill = function() {
return this.fill;
};
/**
* Sets the stroke for this element.
* @param {goog.graphics.Stroke?} stroke The stroke object.
*/
goog.graphics.StrokeAndFillElement.prototype.setStroke = function(stroke) {
this.stroke_ = stroke;
this.getGraphics().setElementStroke(this, stroke);
};
/**
* @return {goog.graphics.Stroke?} stroke The stroke object.
*/
goog.graphics.StrokeAndFillElement.prototype.getStroke = function() {
return this.stroke_;
};
/**
* Re-strokes the element to react to coordinate size changes.
*/
goog.graphics.StrokeAndFillElement.prototype.reapplyStroke = function() {
if (this.stroke_) {
this.setStroke(this.stroke_);
}
};

View File

@@ -0,0 +1,279 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Thin wrappers around the DOM element returned from
* the different draw methods of the graphics. This is the SVG implementation.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.SvgEllipseElement');
goog.provide('goog.graphics.SvgGroupElement');
goog.provide('goog.graphics.SvgImageElement');
goog.provide('goog.graphics.SvgPathElement');
goog.provide('goog.graphics.SvgRectElement');
goog.provide('goog.graphics.SvgTextElement');
goog.require('goog.dom');
goog.require('goog.graphics.EllipseElement');
goog.require('goog.graphics.GroupElement');
goog.require('goog.graphics.ImageElement');
goog.require('goog.graphics.PathElement');
goog.require('goog.graphics.RectElement');
goog.require('goog.graphics.TextElement');
/**
* Thin wrapper for SVG group elements.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.SvgGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.graphics.GroupElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.SvgGroupElement = function(element, graphics) {
goog.graphics.GroupElement.call(this, element, graphics);
};
goog.inherits(goog.graphics.SvgGroupElement, goog.graphics.GroupElement);
/**
* Remove all drawing elements from the group.
* @override
*/
goog.graphics.SvgGroupElement.prototype.clear = function() {
goog.dom.removeChildren(this.getElement());
};
/**
* Set the size of the group element.
* @param {number|string} width The width of the group element.
* @param {number|string} height The height of the group element.
* @override
*/
goog.graphics.SvgGroupElement.prototype.setSize = function(width, height) {
this.getGraphics().setElementAttributes(this.getElement(),
{'width': width, 'height': height});
};
/**
* Thin wrapper for SVG ellipse elements.
* This is an implementation of the goog.graphics.EllipseElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.SvgGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.EllipseElement}
*/
goog.graphics.SvgEllipseElement = function(element, graphics, stroke, fill) {
goog.graphics.EllipseElement.call(this, element, graphics, stroke, fill);
};
goog.inherits(goog.graphics.SvgEllipseElement, goog.graphics.EllipseElement);
/**
* Update the center point of the ellipse.
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @override
*/
goog.graphics.SvgEllipseElement.prototype.setCenter = function(cx, cy) {
this.getGraphics().setElementAttributes(this.getElement(),
{'cx': cx, 'cy': cy});
};
/**
* Update the radius of the ellipse.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
* @override
*/
goog.graphics.SvgEllipseElement.prototype.setRadius = function(rx, ry) {
this.getGraphics().setElementAttributes(this.getElement(),
{'rx': rx, 'ry': ry});
};
/**
* Thin wrapper for SVG rectangle elements.
* This is an implementation of the goog.graphics.RectElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.SvgGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.RectElement}
*/
goog.graphics.SvgRectElement = function(element, graphics, stroke, fill) {
goog.graphics.RectElement.call(this, element, graphics, stroke, fill);
};
goog.inherits(goog.graphics.SvgRectElement, goog.graphics.RectElement);
/**
* Update the position of the rectangle.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @override
*/
goog.graphics.SvgRectElement.prototype.setPosition = function(x, y) {
this.getGraphics().setElementAttributes(this.getElement(), {'x': x, 'y': y});
};
/**
* Update the size of the rectangle.
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @override
*/
goog.graphics.SvgRectElement.prototype.setSize = function(width, height) {
this.getGraphics().setElementAttributes(this.getElement(),
{'width': width, 'height': height});
};
/**
* Thin wrapper for SVG path elements.
* This is an implementation of the goog.graphics.PathElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.SvgGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.PathElement}
*/
goog.graphics.SvgPathElement = function(element, graphics, stroke, fill) {
goog.graphics.PathElement.call(this, element, graphics, stroke, fill);
};
goog.inherits(goog.graphics.SvgPathElement, goog.graphics.PathElement);
/**
* Update the underlying path.
* @param {!goog.graphics.Path} path The path object to draw.
* @override
*/
goog.graphics.SvgPathElement.prototype.setPath = function(path) {
this.getGraphics().setElementAttributes(this.getElement(),
{'d': /** @suppress {missingRequire} */
goog.graphics.SvgGraphics.getSvgPath(path)});
};
/**
* Thin wrapper for SVG text elements.
* This is an implementation of the goog.graphics.TextElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.SvgGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.TextElement}
*/
goog.graphics.SvgTextElement = function(element, graphics, stroke, fill) {
goog.graphics.TextElement.call(this, element, graphics, stroke, fill);
};
goog.inherits(goog.graphics.SvgTextElement, goog.graphics.TextElement);
/**
* Update the displayed text of the element.
* @param {string} text The text to draw.
* @override
*/
goog.graphics.SvgTextElement.prototype.setText = function(text) {
this.getElement().firstChild.data = text;
};
/**
* Thin wrapper for SVG image elements.
* This is an implementation of the goog.graphics.ImageElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.SvgGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.graphics.ImageElement}
*/
goog.graphics.SvgImageElement = function(element, graphics) {
goog.graphics.ImageElement.call(this, element, graphics);
};
goog.inherits(goog.graphics.SvgImageElement, goog.graphics.ImageElement);
/**
* Update the position of the image.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @override
*/
goog.graphics.SvgImageElement.prototype.setPosition = function(x, y) {
this.getGraphics().setElementAttributes(this.getElement(), {'x': x, 'y': y});
};
/**
* Update the size of the image.
* @param {number} width Width of image.
* @param {number} height Height of image.
* @override
*/
goog.graphics.SvgImageElement.prototype.setSize = function(width, height) {
this.getGraphics().setElementAttributes(this.getElement(),
{'width': width, 'height': height});
};
/**
* Update the source of the image.
* @param {string} src Source of the image.
* @override
*/
goog.graphics.SvgImageElement.prototype.setSource = function(src) {
this.getGraphics().setElementAttributes(this.getElement(),
{'xlink:href': src});
};

View File

@@ -0,0 +1,857 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview SvgGraphics sub class that uses SVG to draw the graphics.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.SvgGraphics');
goog.require('goog.Timer');
goog.require('goog.dom');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventType');
goog.require('goog.graphics.AbstractGraphics');
goog.require('goog.graphics.LinearGradient');
goog.require('goog.graphics.Path');
goog.require('goog.graphics.SolidFill');
goog.require('goog.graphics.Stroke');
goog.require('goog.graphics.SvgEllipseElement');
goog.require('goog.graphics.SvgGroupElement');
goog.require('goog.graphics.SvgImageElement');
goog.require('goog.graphics.SvgPathElement');
goog.require('goog.graphics.SvgRectElement');
goog.require('goog.graphics.SvgTextElement');
goog.require('goog.math');
goog.require('goog.math.Size');
goog.require('goog.style');
goog.require('goog.userAgent');
/**
* A Graphics implementation for drawing using SVG.
* @param {string|number} width The width in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {string|number} height The height in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {?number=} opt_coordWidth The coordinate width - if
* omitted or null, defaults to same as width.
* @param {?number=} opt_coordHeight The coordinate height - if
* omitted or null, defaults to same as height.
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
* document we want to render in.
* @constructor
* @extends {goog.graphics.AbstractGraphics}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.SvgGraphics = function(width, height,
opt_coordWidth, opt_coordHeight,
opt_domHelper) {
goog.graphics.AbstractGraphics.call(this, width, height,
opt_coordWidth, opt_coordHeight,
opt_domHelper);
/**
* Map from def key to id of def root element.
* Defs are global "defines" of svg that are used to share common attributes,
* for example gradients.
* @type {Object}
* @private
*/
this.defs_ = {};
/**
* Whether to manually implement viewBox by using a coordinate transform.
* As of 1/11/08 this is necessary for Safari 3 but not for the nightly
* WebKit build. Apply to webkit versions < 526. 525 is the
* last version used by Safari 3.1.
* @type {boolean}
* @private
*/
this.useManualViewbox_ = goog.userAgent.WEBKIT &&
!goog.userAgent.isVersionOrHigher(526);
/**
* Event handler.
* @type {goog.events.EventHandler}
* @private
*/
this.handler_ = new goog.events.EventHandler(this);
};
goog.inherits(goog.graphics.SvgGraphics, goog.graphics.AbstractGraphics);
/**
* The SVG namespace URN
* @private
* @type {string}
*/
goog.graphics.SvgGraphics.SVG_NS_ = 'http://www.w3.org/2000/svg';
/**
* The name prefix for def entries
* @private
* @type {string}
*/
goog.graphics.SvgGraphics.DEF_ID_PREFIX_ = '_svgdef_';
/**
* The next available unique identifier for a def entry.
* This is a static variable, so that when multiple graphics are used in one
* document, the same def id can not be re-defined by another SvgGraphics.
* @type {number}
* @private
*/
goog.graphics.SvgGraphics.nextDefId_ = 0;
/**
* Svg element for definitions for other elements, e.g. linear gradients.
* @type {Element}
* @private
*/
goog.graphics.SvgGraphics.prototype.defsElement_;
/**
* Creates an SVG element. Used internally and by different SVG classes.
* @param {string} tagName The type of element to create.
* @param {Object=} opt_attributes Map of name-value pairs for attributes.
* @return {Element} The created element.
* @private
*/
goog.graphics.SvgGraphics.prototype.createSvgElement_ = function(tagName,
opt_attributes) {
var element = this.dom_.getDocument().createElementNS(
goog.graphics.SvgGraphics.SVG_NS_, tagName);
if (opt_attributes) {
this.setElementAttributes(element, opt_attributes);
}
return element;
};
/**
* Sets properties to an SVG element. Used internally and by different
* SVG elements.
* @param {Element} element The svg element.
* @param {Object} attributes Map of name-value pairs for attributes.
*/
goog.graphics.SvgGraphics.prototype.setElementAttributes = function(element,
attributes) {
for (var key in attributes) {
element.setAttribute(key, attributes[key]);
}
};
/**
* Appends an element.
*
* @param {goog.graphics.Element} element The element wrapper.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
* @private
*/
goog.graphics.SvgGraphics.prototype.append_ = function(element, opt_group) {
var parent = opt_group || this.canvasElement;
parent.getElement().appendChild(element.getElement());
};
/**
* Sets the fill of the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
* @param {goog.graphics.Fill?} fill The fill object.
* @override
*/
goog.graphics.SvgGraphics.prototype.setElementFill = function(element, fill) {
var svgElement = element.getElement();
if (fill instanceof goog.graphics.SolidFill) {
svgElement.setAttribute('fill', fill.getColor());
svgElement.setAttribute('fill-opacity', fill.getOpacity());
} else if (fill instanceof goog.graphics.LinearGradient) {
// create a def key which is just a concat of all the relevant fields
var defKey = 'lg-' +
fill.getX1() + '-' + fill.getY1() + '-' +
fill.getX2() + '-' + fill.getY2() + '-' +
fill.getColor1() + '-' + fill.getColor2();
// It seems that the SVG version accepts opacity where the VML does not
var id = this.getDef(defKey);
if (!id) { // No def for this yet, create it
// Create the gradient def entry (only linear gradient are supported)
var gradient = this.createSvgElement_('linearGradient', {
'x1': fill.getX1(),
'y1': fill.getY1(),
'x2': fill.getX2(),
'y2': fill.getY2(),
'gradientUnits': 'userSpaceOnUse'
});
var gstyle = 'stop-color:' + fill.getColor1();
if (goog.isNumber(fill.getOpacity1())) {
gstyle += ';stop-opacity:' + fill.getOpacity1();
}
var stop1 = this.createSvgElement_(
'stop', {'offset': '0%', 'style': gstyle});
gradient.appendChild(stop1);
// LinearGradients don't have opacity in VML so implement that before
// enabling the following code.
// if (fill.getOpacity() != null) {
// gstyles += 'opacity:' + fill.getOpacity() + ';'
// }
gstyle = 'stop-color:' + fill.getColor2();
if (goog.isNumber(fill.getOpacity2())) {
gstyle += ';stop-opacity:' + fill.getOpacity2();
}
var stop2 = this.createSvgElement_(
'stop', {'offset': '100%', 'style': gstyle});
gradient.appendChild(stop2);
// LinearGradients don't have opacity in VML so implement that before
// enabling the following code.
// if (fill.getOpacity() != null) {
// gstyles += 'opacity:' + fill.getOpacity() + ';'
// }
id = this.addDef(defKey, gradient);
}
// Link element to linearGradient definition
svgElement.setAttribute('fill', 'url(#' + id + ')');
} else {
svgElement.setAttribute('fill', 'none');
}
};
/**
* Sets the stroke of the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
* @param {goog.graphics.Stroke?} stroke The stroke object.
* @override
*/
goog.graphics.SvgGraphics.prototype.setElementStroke = function(element,
stroke) {
var svgElement = element.getElement();
if (stroke) {
svgElement.setAttribute('stroke', stroke.getColor());
var width = stroke.getWidth();
if (goog.isString(width) && width.indexOf('px') != -1) {
svgElement.setAttribute('stroke-width',
parseFloat(width) / this.getPixelScaleX());
} else {
svgElement.setAttribute('stroke-width', width);
}
} else {
svgElement.setAttribute('stroke', 'none');
}
};
/**
* Set the transformation of an element.
* @param {goog.graphics.Element} element The element wrapper.
* @param {number} x The x coordinate of the translation transform.
* @param {number} y The y coordinate of the translation transform.
* @param {number} angle The angle of the rotation transform.
* @param {number} centerX The horizontal center of the rotation transform.
* @param {number} centerY The vertical center of the rotation transform.
* @override
*/
goog.graphics.SvgGraphics.prototype.setElementTransform = function(element, x,
y, angle, centerX, centerY) {
element.getElement().setAttribute('transform', 'translate(' + x + ',' + y +
') rotate(' + angle + ' ' + centerX + ' ' + centerY + ')');
};
/**
* Creates the DOM representation of the graphics area.
* @override
*/
goog.graphics.SvgGraphics.prototype.createDom = function() {
// Set up the standard attributes.
var attributes = {
'width': this.width,
'height': this.height,
'overflow': 'hidden'
};
var svgElement = this.createSvgElement_('svg', attributes);
var groupElement = this.createSvgElement_('g');
this.defsElement_ = this.createSvgElement_('defs');
this.canvasElement = new goog.graphics.SvgGroupElement(groupElement, this);
svgElement.appendChild(this.defsElement_);
svgElement.appendChild(groupElement);
// Use the svgElement as the root element.
this.setElementInternal(svgElement);
// Set up the coordinate system.
this.setViewBox_();
};
/**
* Changes the coordinate system position.
* @param {number} left The coordinate system left bound.
* @param {number} top The coordinate system top bound.
* @override
*/
goog.graphics.SvgGraphics.prototype.setCoordOrigin = function(left, top) {
this.coordLeft = left;
this.coordTop = top;
this.setViewBox_();
};
/**
* Changes the coordinate size.
* @param {number} coordWidth The coordinate width.
* @param {number} coordHeight The coordinate height.
* @override
*/
goog.graphics.SvgGraphics.prototype.setCoordSize = function(coordWidth,
coordHeight) {
goog.graphics.SvgGraphics.superClass_.setCoordSize.apply(
this, arguments);
this.setViewBox_();
};
/**
* @return {string} The view box string.
* @private
*/
goog.graphics.SvgGraphics.prototype.getViewBox_ = function() {
return this.coordLeft + ' ' + this.coordTop + ' ' +
(this.coordWidth ? this.coordWidth + ' ' + this.coordHeight : '');
};
/**
* Sets up the view box.
* @private
*/
goog.graphics.SvgGraphics.prototype.setViewBox_ = function() {
if (this.coordWidth || this.coordLeft || this.coordTop) {
this.getElement().setAttribute('preserveAspectRatio', 'none');
if (this.useManualViewbox_) {
this.updateManualViewBox_();
} else {
this.getElement().setAttribute('viewBox', this.getViewBox_());
}
}
};
/**
* Updates the transform of the root element to fake a viewBox. Should only
* be called when useManualViewbox_ is set.
* @private
*/
goog.graphics.SvgGraphics.prototype.updateManualViewBox_ = function() {
if (!this.isInDocument() ||
!(this.coordWidth || this.coordLeft || !this.coordTop)) {
return;
}
var size = this.getPixelSize();
if (size.width == 0) {
// In Safari, invisible SVG is sometimes shown. Explicitly hide it.
this.getElement().style.visibility = 'hidden';
return;
}
this.getElement().style.visibility = '';
var offsetX = - this.coordLeft;
var offsetY = - this.coordTop;
var scaleX = size.width / this.coordWidth;
var scaleY = size.height / this.coordHeight;
this.canvasElement.getElement().setAttribute('transform',
'scale(' + scaleX + ' ' + scaleY + ') ' +
'translate(' + offsetX + ' ' + offsetY + ')');
};
/**
* Change the size of the canvas.
* @param {number} pixelWidth The width in pixels.
* @param {number} pixelHeight The height in pixels.
* @override
*/
goog.graphics.SvgGraphics.prototype.setSize = function(pixelWidth,
pixelHeight) {
goog.style.setSize(this.getElement(), pixelWidth, pixelHeight);
};
/** @override */
goog.graphics.SvgGraphics.prototype.getPixelSize = function() {
if (!goog.userAgent.GECKO) {
return this.isInDocument() ?
goog.style.getSize(this.getElement()) :
goog.base(this, 'getPixelSize');
}
// In Gecko, goog.style.getSize does not work for SVG elements. We have to
// compute the size manually if it is percentage based.
var width = this.width;
var height = this.height;
var computeWidth = goog.isString(width) && width.indexOf('%') != -1;
var computeHeight = goog.isString(height) && height.indexOf('%') != -1;
if (!this.isInDocument() && (computeWidth || computeHeight)) {
return null;
}
var parent;
var parentSize;
if (computeWidth) {
parent = /** @type {Element} */ (this.getElement().parentNode);
parentSize = goog.style.getSize(parent);
width = parseFloat(/** @type {string} */ (width)) * parentSize.width / 100;
}
if (computeHeight) {
parent = parent || /** @type {Element} */ (this.getElement().parentNode);
parentSize = parentSize || goog.style.getSize(parent);
height = parseFloat(/** @type {string} */ (height)) * parentSize.height /
100;
}
return new goog.math.Size(/** @type {number} */ (width),
/** @type {number} */ (height));
};
/**
* Remove all drawing elements from the graphics.
* @override
*/
goog.graphics.SvgGraphics.prototype.clear = function() {
this.canvasElement.clear();
goog.dom.removeChildren(this.defsElement_);
this.defs_ = {};
};
/**
* Draw an ellipse.
*
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.EllipseElement} The newly created element.
* @override
*/
goog.graphics.SvgGraphics.prototype.drawEllipse = function(
cx, cy, rx, ry, stroke, fill, opt_group) {
var element = this.createSvgElement_('ellipse',
{'cx': cx, 'cy': cy, 'rx': rx, 'ry': ry});
var wrapper = new goog.graphics.SvgEllipseElement(element, this, stroke,
fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw a rectangle.
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.RectElement} The newly created element.
* @override
*/
goog.graphics.SvgGraphics.prototype.drawRect = function(x, y, width, height,
stroke, fill, opt_group) {
var element = this.createSvgElement_('rect',
{'x': x, 'y': y, 'width': width, 'height': height});
var wrapper = new goog.graphics.SvgRectElement(element, this, stroke, fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw an image.
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of the image.
* @param {number} height Height of the image.
* @param {string} src The source fo the image.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.ImageElement} The newly created image wrapped in a
* rectangle element.
*/
goog.graphics.SvgGraphics.prototype.drawImage = function(x, y, width, height,
src, opt_group) {
var element = this.createSvgElement_('image', {
'x': x,
'y': y,
'width': width,
'height': height,
'image-rendering': 'optimizeQuality',
'preserveAspectRatio': 'none'
});
element.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src);
var wrapper = new goog.graphics.SvgImageElement(element, this);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw a text string vertically centered on a given line.
*
* @param {string} text The text to draw.
* @param {number} x1 X coordinate of start of line.
* @param {number} y1 Y coordinate of start of line.
* @param {number} x2 X coordinate of end of line.
* @param {number} y2 Y coordinate of end of line.
* @param {string} align Horizontal alignment: left (default), center, right.
* @param {goog.graphics.Font} font Font describing the font properties.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.TextElement} The newly created element.
* @override
*/
goog.graphics.SvgGraphics.prototype.drawTextOnLine = function(
text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {
var angle = Math.round(goog.math.angle(x1, y1, x2, y2));
var dx = x2 - x1;
var dy = y2 - y1;
var lineLength = Math.round(Math.sqrt(dx * dx + dy * dy)); // Length of line
// SVG baseline is on the glyph's base line. We estimate it as 85% of the
// font height. This is just a rough estimate, but do not have a better way.
var fontSize = font.size;
var attributes = {'font-family': font.family, 'font-size': fontSize};
var baseline = Math.round(fontSize * 0.85);
var textY = Math.round(y1 - (fontSize / 2) + baseline);
var textX = x1;
if (align == 'center') {
textX += Math.round(lineLength / 2);
attributes['text-anchor'] = 'middle';
} else if (align == 'right') {
textX += lineLength;
attributes['text-anchor'] = 'end';
}
attributes['x'] = textX;
attributes['y'] = textY;
if (font.bold) {
attributes['font-weight'] = 'bold';
}
if (font.italic) {
attributes['font-style'] = 'italic';
}
if (angle != 0) {
attributes['transform'] = 'rotate(' + angle + ' ' + x1 + ' ' + y1 + ')';
}
var element = this.createSvgElement_('text', attributes);
element.appendChild(this.dom_.getDocument().createTextNode(text));
// Bypass a Firefox-Mac bug where text fill is ignored. If text has no stroke,
// set a stroke, otherwise the text will not be visible.
if (stroke == null && goog.userAgent.GECKO && goog.userAgent.MAC) {
var color = 'black';
// For solid fills, use the fill color
if (fill instanceof goog.graphics.SolidFill) {
color = fill.getColor();
}
stroke = new goog.graphics.Stroke(1, color);
}
var wrapper = new goog.graphics.SvgTextElement(element, this, stroke, fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw a path.
*
* @param {!goog.graphics.Path} path The path object to draw.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.PathElement} The newly created element.
* @override
*/
goog.graphics.SvgGraphics.prototype.drawPath = function(
path, stroke, fill, opt_group) {
var element = this.createSvgElement_('path',
{'d': goog.graphics.SvgGraphics.getSvgPath(path)});
var wrapper = new goog.graphics.SvgPathElement(element, this, stroke, fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Returns a string representation of a logical path suitable for use in
* an SVG element.
*
* @param {goog.graphics.Path} path The logical path.
* @return {string} The SVG path representation.
* @suppress {deprecated} goog.graphics is deprecated.
*/
goog.graphics.SvgGraphics.getSvgPath = function(path) {
var list = [];
path.forEachSegment(function(segment, args) {
switch (segment) {
case goog.graphics.Path.Segment.MOVETO:
list.push('M');
Array.prototype.push.apply(list, args);
break;
case goog.graphics.Path.Segment.LINETO:
list.push('L');
Array.prototype.push.apply(list, args);
break;
case goog.graphics.Path.Segment.CURVETO:
list.push('C');
Array.prototype.push.apply(list, args);
break;
case goog.graphics.Path.Segment.ARCTO:
var extent = args[3];
var toAngle = args[2] + extent;
list.push('A', args[0], args[1],
0, Math.abs(extent) > 180 ? 1 : 0, extent > 0 ? 1 : 0,
args[4], args[5]);
break;
case goog.graphics.Path.Segment.CLOSE:
list.push('Z');
break;
}
});
return list.join(' ');
};
/**
* Create an empty group of drawing elements.
*
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.GroupElement} The newly created group.
* @override
*/
goog.graphics.SvgGraphics.prototype.createGroup = function(opt_group) {
var element = this.createSvgElement_('g');
var parent = opt_group || this.canvasElement;
parent.getElement().appendChild(element);
return new goog.graphics.SvgGroupElement(element, this);
};
/**
* Measure and return the width (in pixels) of a given text string.
* Text measurement is needed to make sure a text can fit in the allocated area.
* The way text length is measured is by writing it into a div that is after
* the visible area, measure the div width, and immediatly erase the written
* value.
*
* @param {string} text The text string to measure.
* @param {goog.graphics.Font} font The font object describing the font style.
* @override
*/
goog.graphics.SvgGraphics.prototype.getTextWidth = function(text, font) {
// TODO(user) Implement
};
/**
* Adds a defintion of an element to the global definitions.
* @param {string} defKey This is a key that should be unique in a way that
* if two definitions are equal the should have the same key.
* @param {Element} defElement DOM element to add as a definition. It must
* have an id attribute set.
* @return {string} The assigned id of the defElement.
*/
goog.graphics.SvgGraphics.prototype.addDef = function(defKey, defElement) {
if (defKey in this.defs_) {
return this.defs_[defKey];
}
var id = goog.graphics.SvgGraphics.DEF_ID_PREFIX_ +
goog.graphics.SvgGraphics.nextDefId_++;
defElement.setAttribute('id', id);
this.defs_[defKey] = id;
// Add the def defElement of the defs list.
var defs = this.defsElement_;
defs.appendChild(defElement);
return id;
};
/**
* Returns the id of a definition element.
* @param {string} defKey This is a key that should be unique in a way that
* if two definitions are equal the should have the same key.
* @return {?string} The id of the found definition element or null if
* not found.
*/
goog.graphics.SvgGraphics.prototype.getDef = function(defKey) {
return defKey in this.defs_ ? this.defs_[defKey] : null;
};
/**
* Removes a definition of an elemnt from the global definitions.
* @param {string} defKey This is a key that should be unique in a way that
* if two definitions are equal they should have the same key.
*/
goog.graphics.SvgGraphics.prototype.removeDef = function(defKey) {
var id = this.getDef(defKey);
if (id) {
var element = this.dom_.getElement(id);
this.defsElement_.removeChild(element);
delete this.defs_[defKey];
}
};
/** @override */
goog.graphics.SvgGraphics.prototype.enterDocument = function() {
var oldPixelSize = this.getPixelSize();
goog.graphics.SvgGraphics.superClass_.enterDocument.call(this);
// Dispatch a resize if this is the first time the size value is accurate.
if (!oldPixelSize) {
this.dispatchEvent(goog.events.EventType.RESIZE);
}
// For percentage based heights, listen for changes to size.
if (this.useManualViewbox_) {
var width = this.width;
var height = this.height;
if (typeof width == 'string' && width.indexOf('%') != -1 &&
typeof height == 'string' && height.indexOf('%') != -1) {
// SVG elements don't behave well with respect to size events, so we
// resort to polling.
this.handler_.listen(goog.graphics.SvgGraphics.getResizeCheckTimer_(),
goog.Timer.TICK, this.updateManualViewBox_);
}
this.updateManualViewBox_();
}
};
/** @override */
goog.graphics.SvgGraphics.prototype.exitDocument = function() {
goog.graphics.SvgGraphics.superClass_.exitDocument.call(this);
// Stop polling.
if (this.useManualViewbox_) {
this.handler_.unlisten(goog.graphics.SvgGraphics.getResizeCheckTimer_(),
goog.Timer.TICK, this.updateManualViewBox_);
}
};
/**
* Disposes of the component by removing event handlers, detacing DOM nodes from
* the document body, and removing references to them.
* @override
* @protected
*/
goog.graphics.SvgGraphics.prototype.disposeInternal = function() {
delete this.defs_;
delete this.defsElement_;
delete this.canvasElement;
goog.graphics.SvgGraphics.superClass_.disposeInternal.call(this);
};
/**
* The centralized resize checking timer.
* @type {goog.Timer|undefined}
* @private
*/
goog.graphics.SvgGraphics.resizeCheckTimer_;
/**
* @return {goog.Timer} The centralized timer object used for interval timing.
* @private
*/
goog.graphics.SvgGraphics.getResizeCheckTimer_ = function() {
if (!goog.graphics.SvgGraphics.resizeCheckTimer_) {
goog.graphics.SvgGraphics.resizeCheckTimer_ = new goog.Timer(400);
goog.graphics.SvgGraphics.resizeCheckTimer_.start();
}
return /** @type {goog.Timer} */ (
goog.graphics.SvgGraphics.resizeCheckTimer_);
};
/** @override */
goog.graphics.SvgGraphics.prototype.isDomClonable = function() {
return true;
};

View File

@@ -0,0 +1,56 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A thin wrapper around the DOM element for text elements.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.TextElement');
goog.require('goog.graphics.StrokeAndFillElement');
/**
* Interface for a graphics text element.
* You should not construct objects from this constructor. The graphics
* will return an implementation of this interface for you.
*
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.AbstractGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.StrokeAndFillElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.TextElement = function(element, graphics, stroke, fill) {
goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
fill);
};
goog.inherits(goog.graphics.TextElement, goog.graphics.StrokeAndFillElement);
/**
* Update the displayed text of the element.
* @param {string} text The text to draw.
*/
goog.graphics.TextElement.prototype.setText = goog.abstractMethod;

View File

@@ -0,0 +1,433 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Thin wrappers around the DOM element returned from
* the different draw methods of the graphics. This is the VML implementation.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.VmlEllipseElement');
goog.provide('goog.graphics.VmlGroupElement');
goog.provide('goog.graphics.VmlImageElement');
goog.provide('goog.graphics.VmlPathElement');
goog.provide('goog.graphics.VmlRectElement');
goog.provide('goog.graphics.VmlTextElement');
goog.require('goog.dom');
goog.require('goog.graphics.EllipseElement');
goog.require('goog.graphics.GroupElement');
goog.require('goog.graphics.ImageElement');
goog.require('goog.graphics.PathElement');
goog.require('goog.graphics.RectElement');
goog.require('goog.graphics.TextElement');
/**
* Returns the VML element corresponding to this object. This method is added
* to several classes below. Note that the return value of this method may
* change frequently in IE8, so it should not be cached externally.
* @return {Element} The VML element corresponding to this object.
* @this {goog.graphics.VmlGroupElement|goog.graphics.VmlEllipseElement|
* goog.graphics.VmlRectElement|goog.graphics.VmlPathElement|
* goog.graphics.VmlTextElement|goog.graphics.VmlImageElement}
* @private
*/
goog.graphics.vmlGetElement_ = function() {
this.element_ = this.getGraphics().getVmlElement(this.id_) || this.element_;
return this.element_;
};
/**
* Thin wrapper for VML group elements.
* This is an implementation of the goog.graphics.GroupElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.VmlGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.graphics.GroupElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.VmlGroupElement = function(element, graphics) {
this.id_ = element.id;
goog.graphics.GroupElement.call(this, element, graphics);
};
goog.inherits(goog.graphics.VmlGroupElement, goog.graphics.GroupElement);
/** @override */
goog.graphics.VmlGroupElement.prototype.getElement =
goog.graphics.vmlGetElement_;
/**
* Remove all drawing elements from the group.
* @override
*/
goog.graphics.VmlGroupElement.prototype.clear = function() {
goog.dom.removeChildren(this.getElement());
};
/**
* @return {boolean} True if this group is the root canvas element.
* @private
*/
goog.graphics.VmlGroupElement.prototype.isRootElement_ = function() {
return this.getGraphics().getCanvasElement() == this;
};
/**
* Set the size of the group element.
* @param {number|string} width The width of the group element.
* @param {number|string} height The height of the group element.
* @override
*/
goog.graphics.VmlGroupElement.prototype.setSize = function(width, height) {
var element = this.getElement();
var style = element.style;
style.width = /** @suppress {missingRequire} */ (
goog.graphics.VmlGraphics.toSizePx(width));
style.height = /** @suppress {missingRequire} */ (
goog.graphics.VmlGraphics.toSizePx(height));
element.coordsize = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toSizeCoord(width) +
' ' +
/** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toSizeCoord(height);
// Don't overwrite the root element's origin.
if (!this.isRootElement_()) {
element.coordorigin = '0 0';
}
};
/**
* Thin wrapper for VML ellipse elements.
* This is an implementation of the goog.graphics.EllipseElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.VmlGraphics} graphics The graphics creating
* this element.
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.EllipseElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.VmlEllipseElement = function(element, graphics,
cx, cy, rx, ry, stroke, fill) {
this.id_ = element.id;
goog.graphics.EllipseElement.call(this, element, graphics, stroke, fill);
// Store center and radius for future calls to setRadius or setCenter.
/**
* X coordinate of the ellipse center.
* @type {number}
*/
this.cx = cx;
/**
* Y coordinate of the ellipse center.
* @type {number}
*/
this.cy = cy;
/**
* Radius length for the x-axis.
* @type {number}
*/
this.rx = rx;
/**
* Radius length for the y-axis.
* @type {number}
*/
this.ry = ry;
};
goog.inherits(goog.graphics.VmlEllipseElement, goog.graphics.EllipseElement);
/** @override */
goog.graphics.VmlEllipseElement.prototype.getElement =
goog.graphics.vmlGetElement_;
/**
* Update the center point of the ellipse.
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @override
*/
goog.graphics.VmlEllipseElement.prototype.setCenter = function(cx, cy) {
this.cx = cx;
this.cy = cy;
/** @suppress {missingRequire} */
goog.graphics.VmlGraphics.setPositionAndSize(this.getElement(),
cx - this.rx, cy - this.ry, this.rx * 2, this.ry * 2);
};
/**
* Update the radius of the ellipse.
* @param {number} rx Center X coordinate.
* @param {number} ry Center Y coordinate.
* @override
*/
goog.graphics.VmlEllipseElement.prototype.setRadius = function(rx, ry) {
this.rx = rx;
this.ry = ry;
/** @suppress {missingRequire} */
goog.graphics.VmlGraphics.setPositionAndSize(this.getElement(),
this.cx - rx, this.cy - ry, rx * 2, ry * 2);
};
/**
* Thin wrapper for VML rectangle elements.
* This is an implementation of the goog.graphics.RectElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.VmlGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.RectElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.VmlRectElement = function(element, graphics, stroke, fill) {
this.id_ = element.id;
goog.graphics.RectElement.call(this, element, graphics, stroke, fill);
};
goog.inherits(goog.graphics.VmlRectElement, goog.graphics.RectElement);
/** @override */
goog.graphics.VmlRectElement.prototype.getElement =
goog.graphics.vmlGetElement_;
/**
* Update the position of the rectangle.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @override
*/
goog.graphics.VmlRectElement.prototype.setPosition = function(x, y) {
var style = this.getElement().style;
style.left = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toPosPx(x);
style.top = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toPosPx(y);
};
/**
* Update the size of the rectangle.
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @override
*/
goog.graphics.VmlRectElement.prototype.setSize = function(width, height) {
var style = this.getElement().style;
style.width = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toSizePx(width);
style.height = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toSizePx(height);
};
/**
* Thin wrapper for VML path elements.
* This is an implementation of the goog.graphics.PathElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.VmlGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.PathElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.VmlPathElement = function(element, graphics, stroke, fill) {
this.id_ = element.id;
goog.graphics.PathElement.call(this, element, graphics, stroke, fill);
};
goog.inherits(goog.graphics.VmlPathElement, goog.graphics.PathElement);
/** @override */
goog.graphics.VmlPathElement.prototype.getElement =
goog.graphics.vmlGetElement_;
/**
* Update the underlying path.
* @param {!goog.graphics.Path} path The path object to draw.
* @override
*/
goog.graphics.VmlPathElement.prototype.setPath = function(path) {
/** @suppress {missingRequire} */
goog.graphics.VmlGraphics.setAttribute(
this.getElement(), 'path',
/** @suppress {missingRequire} */
goog.graphics.VmlGraphics.getVmlPath(path));
};
/**
* Thin wrapper for VML text elements.
* This is an implementation of the goog.graphics.TextElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.VmlGraphics} graphics The graphics creating
* this element.
* @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
* @param {goog.graphics.Fill?} fill The fill to use for this element.
* @constructor
* @extends {goog.graphics.TextElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.VmlTextElement = function(element, graphics, stroke, fill) {
this.id_ = element.id;
goog.graphics.TextElement.call(this, element, graphics, stroke, fill);
};
goog.inherits(goog.graphics.VmlTextElement, goog.graphics.TextElement);
/** @override */
goog.graphics.VmlTextElement.prototype.getElement =
goog.graphics.vmlGetElement_;
/**
* Update the displayed text of the element.
* @param {string} text The text to draw.
* @override
*/
goog.graphics.VmlTextElement.prototype.setText = function(text) {
/** @suppress {missingRequire} */
goog.graphics.VmlGraphics.setAttribute(this.getElement().childNodes[1],
'string', text);
};
/**
* Thin wrapper for VML image elements.
* This is an implementation of the goog.graphics.ImageElement interface.
* You should not construct objects from this constructor. The graphics
* will return the object for you.
* @param {Element} element The DOM element to wrap.
* @param {goog.graphics.VmlGraphics} graphics The graphics creating
* this element.
* @constructor
* @extends {goog.graphics.ImageElement}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.VmlImageElement = function(element, graphics) {
this.id_ = element.id;
goog.graphics.ImageElement.call(this, element, graphics);
};
goog.inherits(goog.graphics.VmlImageElement, goog.graphics.ImageElement);
/** @override */
goog.graphics.VmlImageElement.prototype.getElement =
goog.graphics.vmlGetElement_;
/**
* Update the position of the image.
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @override
*/
goog.graphics.VmlImageElement.prototype.setPosition = function(x, y) {
var style = this.getElement().style;
style.left = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toPosPx(x);
style.top = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toPosPx(y);
};
/**
* Update the size of the image.
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @override
*/
goog.graphics.VmlImageElement.prototype.setSize = function(width, height) {
var style = this.getElement().style;
style.width = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toPosPx(width);
style.height = /** @suppress {missingRequire} */
goog.graphics.VmlGraphics.toPosPx(height);
};
/**
* Update the source of the image.
* @param {string} src Source of the image.
* @override
*/
goog.graphics.VmlImageElement.prototype.setSource = function(src) {
/** @suppress {missingRequire} */
goog.graphics.VmlGraphics.setAttribute(this.getElement(), 'src', src);
};

View File

@@ -0,0 +1,893 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview VmlGraphics sub class that uses VML to draw the graphics.
* @author arv@google.com (Erik Arvidsson)
* @author yoah@google.com (Yoah Bar-David)
*/
goog.provide('goog.graphics.VmlGraphics');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventType');
goog.require('goog.graphics.AbstractGraphics');
goog.require('goog.graphics.LinearGradient');
goog.require('goog.graphics.Path');
goog.require('goog.graphics.SolidFill');
goog.require('goog.graphics.VmlEllipseElement');
goog.require('goog.graphics.VmlGroupElement');
goog.require('goog.graphics.VmlImageElement');
goog.require('goog.graphics.VmlPathElement');
goog.require('goog.graphics.VmlRectElement');
goog.require('goog.graphics.VmlTextElement');
goog.require('goog.math');
goog.require('goog.math.Size');
goog.require('goog.string');
goog.require('goog.style');
/**
* A Graphics implementation for drawing using VML.
* @param {string|number} width The (non-zero) width in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {string|number} height The (non-zero) height in pixels. Strings
* expressing percentages of parent with (e.g. '80%') are also accepted.
* @param {?number=} opt_coordWidth The coordinate width - if
* omitted or null, defaults to same as width.
* @param {?number=} opt_coordHeight The coordinate height - if
* omitted or null, defaults to same as height.
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
* document we want to render in.
* @constructor
* @extends {goog.graphics.AbstractGraphics}
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
* differences before the canvas tag was widely supported. See
* http://en.wikipedia.org/wiki/Canvas_element for details.
*/
goog.graphics.VmlGraphics = function(width, height,
opt_coordWidth, opt_coordHeight,
opt_domHelper) {
goog.graphics.AbstractGraphics.call(this, width, height,
opt_coordWidth, opt_coordHeight,
opt_domHelper);
this.handler_ = new goog.events.EventHandler(this);
};
goog.inherits(goog.graphics.VmlGraphics, goog.graphics.AbstractGraphics);
/**
* The prefix to use for VML elements
* @private
* @type {string}
*/
goog.graphics.VmlGraphics.VML_PREFIX_ = 'g_vml_';
/**
* The VML namespace URN
* @private
* @type {string}
*/
goog.graphics.VmlGraphics.VML_NS_ = 'urn:schemas-microsoft-com:vml';
/**
* The VML behavior URL.
* @private
* @type {string}
*/
goog.graphics.VmlGraphics.VML_IMPORT_ = '#default#VML';
/**
* Whether the document is using IE8 standards mode, and therefore needs hacks.
* @private
* @type {boolean}
*/
goog.graphics.VmlGraphics.IE8_MODE_ = document.documentMode &&
document.documentMode >= 8;
/**
* The coordinate multiplier to allow sub-pixel rendering
* @type {number}
*/
goog.graphics.VmlGraphics.COORD_MULTIPLIER = 100;
/**
* Converts the given size to a css size. If it is a percentage, leaves it
* alone. Otherwise assumes px.
*
* @param {number|string} size The size to use.
* @return {string} The position adjusted for COORD_MULTIPLIER.
*/
goog.graphics.VmlGraphics.toCssSize = function(size) {
return goog.isString(size) && goog.string.endsWith(size, '%') ?
size : parseFloat(size.toString()) + 'px';
};
/**
* Multiplies positioning coordinates by COORD_MULTIPLIER to allow sub-pixel
* coordinates. Also adds a half pixel offset to match SVG.
*
* This function is internal for the VML supporting classes, and
* should not be used externally.
*
* @param {number|string} number A position in pixels.
* @return {number} The position adjusted for COORD_MULTIPLIER.
*/
goog.graphics.VmlGraphics.toPosCoord = function(number) {
return Math.round((parseFloat(number.toString()) - 0.5) *
goog.graphics.VmlGraphics.COORD_MULTIPLIER);
};
/**
* Add a "px" suffix to a number of pixels, and multiplies all coordinates by
* COORD_MULTIPLIER to allow sub-pixel coordinates.
*
* This function is internal for the VML supporting classes, and
* should not be used externally.
*
* @param {number|string} number A position in pixels.
* @return {string} The position with suffix 'px'.
*/
goog.graphics.VmlGraphics.toPosPx = function(number) {
return goog.graphics.VmlGraphics.toPosCoord(number) + 'px';
};
/**
* Multiplies the width or height coordinate by COORD_MULTIPLIER to allow
* sub-pixel coordinates.
*
* This function is internal for the VML supporting classes, and
* should not be used externally.
*
* @param {string|number} number A size in units.
* @return {number} The size multiplied by the correct factor.
*/
goog.graphics.VmlGraphics.toSizeCoord = function(number) {
return Math.round(parseFloat(number.toString()) *
goog.graphics.VmlGraphics.COORD_MULTIPLIER);
};
/**
* Add a "px" suffix to a number of pixels, and multiplies all coordinates by
* COORD_MULTIPLIER to allow sub-pixel coordinates.
*
* This function is internal for the VML supporting classes, and
* should not be used externally.
*
* @param {number|string} number A size in pixels.
* @return {string} The size with suffix 'px'.
*/
goog.graphics.VmlGraphics.toSizePx = function(number) {
return goog.graphics.VmlGraphics.toSizeCoord(number) + 'px';
};
/**
* Sets an attribute on the given VML element, in the way best suited to the
* current version of IE. Should only be used in the goog.graphics package.
* @param {Element} element The element to set an attribute
* on.
* @param {string} name The name of the attribute to set.
* @param {string} value The value to set it to.
*/
goog.graphics.VmlGraphics.setAttribute = function(element, name, value) {
if (goog.graphics.VmlGraphics.IE8_MODE_) {
element[name] = value;
} else {
element.setAttribute(name, value);
}
};
/**
* Event handler.
* @type {goog.events.EventHandler}
* @private
*/
goog.graphics.VmlGraphics.prototype.handler_;
/**
* Creates a VML element. Used internally and by different VML classes.
* @param {string} tagName The type of element to create.
* @return {Element} The created element.
*/
goog.graphics.VmlGraphics.prototype.createVmlElement = function(tagName) {
var element =
this.dom_.createElement(goog.graphics.VmlGraphics.VML_PREFIX_ + ':' +
tagName);
element.id = goog.string.createUniqueString();
return element;
};
/**
* Returns the VML element with the given id that is a child of this graphics
* object.
* Should be considered package private, and not used externally.
* @param {string} id The element id to find.
* @return {Element} The element with the given id, or null if none is found.
*/
goog.graphics.VmlGraphics.prototype.getVmlElement = function(id) {
return this.dom_.getElement(id);
};
/**
* Resets the graphics so they will display properly on IE8. Noop in older
* versions.
* @private
*/
goog.graphics.VmlGraphics.prototype.updateGraphics_ = function() {
if (goog.graphics.VmlGraphics.IE8_MODE_ && this.isInDocument()) {
this.getElement().innerHTML = this.getElement().innerHTML;
}
};
/**
* Appends an element.
*
* @param {goog.graphics.Element} element The element wrapper.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
* @private
*/
goog.graphics.VmlGraphics.prototype.append_ = function(element, opt_group) {
var parent = opt_group || this.canvasElement;
parent.getElement().appendChild(element.getElement());
this.updateGraphics_();
};
/**
* Sets the fill for the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
* @param {goog.graphics.Fill?} fill The fill object.
* @override
*/
goog.graphics.VmlGraphics.prototype.setElementFill = function(element, fill) {
var vmlElement = element.getElement();
this.removeFill(vmlElement);
if (fill instanceof goog.graphics.SolidFill) {
// NOTE(arv): VML does not understand 'transparent' so hard code support
// for it.
if (fill.getColor() == 'transparent') {
vmlElement.filled = false;
} else if (fill.getOpacity() != 1) {
vmlElement.filled = true;
// Set opacity (number 0-1 is translated to percent)
var fillNode = this.createVmlElement('fill');
fillNode.opacity = Math.round(fill.getOpacity() * 100) + '%';
fillNode.color = fill.getColor();
vmlElement.appendChild(fillNode);
} else {
vmlElement.filled = true;
vmlElement.fillcolor = fill.getColor();
}
} else if (fill instanceof goog.graphics.LinearGradient) {
vmlElement.filled = true;
// Add a 'fill' element
var gradient = this.createVmlElement('fill');
gradient.color = fill.getColor1();
gradient.color2 = fill.getColor2();
if (goog.isNumber(fill.getOpacity1())) {
gradient.opacity = fill.getOpacity1();
}
if (goog.isNumber(fill.getOpacity2())) {
gradient.opacity2 = fill.getOpacity2();
}
var angle = goog.math.angle(fill.getX1(), fill.getY1(),
fill.getX2(), fill.getY2());
// Our angles start from 0 to the right, and grow clockwise.
// MSIE starts from 0 to top, and grows anti-clockwise.
angle = Math.round(goog.math.standardAngle(270 - angle));
gradient.angle = angle;
gradient.type = 'gradient';
vmlElement.appendChild(gradient);
} else {
vmlElement.filled = false;
}
this.updateGraphics_();
};
/**
* Sets the stroke for the given element.
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
* @param {goog.graphics.Stroke?} stroke The stroke object.
* @override
*/
goog.graphics.VmlGraphics.prototype.setElementStroke = function(element,
stroke) {
var vmlElement = element.getElement();
if (stroke) {
vmlElement.stroked = true;
var width = stroke.getWidth();
if (goog.isString(width) && width.indexOf('px') == -1) {
width = parseFloat(width);
} else {
width = width * this.getPixelScaleX();
}
var strokeElement = vmlElement.getElementsByTagName('stroke')[0];
if (width < 1) {
strokeElement = strokeElement || this.createVmlElement('stroke');
strokeElement.opacity = width;
strokeElement.weight = '1px';
strokeElement.color = stroke.getColor();
vmlElement.appendChild(strokeElement);
} else {
if (strokeElement) {
vmlElement.removeChild(strokeElement);
}
vmlElement.strokecolor = stroke.getColor();
vmlElement.strokeweight = width + 'px';
}
} else {
vmlElement.stroked = false;
}
this.updateGraphics_();
};
/**
* Set the transformation of an element.
* @param {goog.graphics.Element} element The element wrapper.
* @param {number} x The x coordinate of the translation transform.
* @param {number} y The y coordinate of the translation transform.
* @param {number} angle The angle of the rotation transform.
* @param {number} centerX The horizontal center of the rotation transform.
* @param {number} centerY The vertical center of the rotation transform.
* @override
*/
goog.graphics.VmlGraphics.prototype.setElementTransform = function(element, x,
y, angle, centerX, centerY) {
var el = element.getElement();
el.style.left = goog.graphics.VmlGraphics.toPosPx(x);
el.style.top = goog.graphics.VmlGraphics.toPosPx(y);
if (angle || el.rotation) {
el.rotation = angle;
el.coordsize = goog.graphics.VmlGraphics.toSizeCoord(centerX * 2) + ' ' +
goog.graphics.VmlGraphics.toSizeCoord(centerY * 2);
}
};
/**
* Removes the fill information from a dom element.
* @param {Element} element DOM element.
*/
goog.graphics.VmlGraphics.prototype.removeFill = function(element) {
element.fillcolor = '';
var v = element.childNodes.length;
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (child.tagName == 'fill') {
element.removeChild(child);
}
}
};
/**
* Set top, left, width and height for an element.
* This function is internal for the VML supporting classes, and
* should not be used externally.
*
* @param {Element} element DOM element.
* @param {number} left Left ccordinate in pixels.
* @param {number} top Top ccordinate in pixels.
* @param {number} width Width in pixels.
* @param {number} height Height in pixels.
*/
goog.graphics.VmlGraphics.setPositionAndSize = function(
element, left, top, width, height) {
var style = element.style;
style.position = 'absolute';
style.left = goog.graphics.VmlGraphics.toPosPx(left);
style.top = goog.graphics.VmlGraphics.toPosPx(top);
style.width = goog.graphics.VmlGraphics.toSizePx(width);
style.height = goog.graphics.VmlGraphics.toSizePx(height);
if (element.tagName == 'shape') {
element.coordsize = goog.graphics.VmlGraphics.toSizeCoord(width) + ' ' +
goog.graphics.VmlGraphics.toSizeCoord(height);
}
};
/**
* Creates an element spanning the surface.
*
* @param {string} type The type of element to create.
* @return {Element} The created, positioned, and sized element.
* @private
*/
goog.graphics.VmlGraphics.prototype.createFullSizeElement_ = function(type) {
var element = this.createVmlElement(type);
var size = this.getCoordSize();
goog.graphics.VmlGraphics.setPositionAndSize(element, 0, 0, size.width,
size.height);
return element;
};
/**
* IE magic - if this "no-op" line is not here, the if statement below will
* fail intermittently. The eval is used to prevent the JsCompiler from
* stripping this piece of code, which it quite reasonably thinks is doing
* nothing. Put it in try-catch block to prevent "Unspecified Error" when
* this statement is executed in a defer JS in IE.
* More info here:
* http://www.mail-archive.com/users@openlayers.org/msg01838.html
*/
try {
eval('document.namespaces');
} catch (ex) {}
/**
* Creates the DOM representation of the graphics area.
* @override
*/
goog.graphics.VmlGraphics.prototype.createDom = function() {
var doc = this.dom_.getDocument();
// Add the namespace.
if (!doc.namespaces[goog.graphics.VmlGraphics.VML_PREFIX_]) {
if (goog.graphics.VmlGraphics.IE8_MODE_) {
doc.namespaces.add(goog.graphics.VmlGraphics.VML_PREFIX_,
goog.graphics.VmlGraphics.VML_NS_,
goog.graphics.VmlGraphics.VML_IMPORT_);
} else {
doc.namespaces.add(goog.graphics.VmlGraphics.VML_PREFIX_,
goog.graphics.VmlGraphics.VML_NS_);
}
// We assume that we only need to add the CSS if the namespace was not
// present
var ss = doc.createStyleSheet();
ss.cssText = goog.graphics.VmlGraphics.VML_PREFIX_ + '\\:*' +
'{behavior:url(#default#VML)}';
}
// Outer a DIV with overflow hidden for clipping.
// All inner elements are absolutly positioned on-top of this div.
var pixelWidth = this.width;
var pixelHeight = this.height;
var divElement = this.dom_.createDom('div', {
'style': 'overflow:hidden;position:relative;width:' +
goog.graphics.VmlGraphics.toCssSize(pixelWidth) + ';height:' +
goog.graphics.VmlGraphics.toCssSize(pixelHeight)
});
this.setElementInternal(divElement);
var group = this.createVmlElement('group');
var style = group.style;
style.position = 'absolute';
style.left = style.top = 0;
style.width = this.width;
style.height = this.height;
if (this.coordWidth) {
group.coordsize =
goog.graphics.VmlGraphics.toSizeCoord(this.coordWidth) + ' ' +
goog.graphics.VmlGraphics.toSizeCoord(
/** @type {number} */ (this.coordHeight));
} else {
group.coordsize = goog.graphics.VmlGraphics.toSizeCoord(pixelWidth) + ' ' +
goog.graphics.VmlGraphics.toSizeCoord(pixelHeight);
}
if (goog.isDef(this.coordLeft)) {
group.coordorigin = goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) +
' ' + goog.graphics.VmlGraphics.toSizeCoord(this.coordTop);
} else {
group.coordorigin = '0 0';
}
divElement.appendChild(group);
this.canvasElement = new goog.graphics.VmlGroupElement(group, this);
goog.events.listen(divElement, goog.events.EventType.RESIZE, goog.bind(
this.handleContainerResize_, this));
};
/**
* Changes the canvas element size to match the container element size.
* @private
*/
goog.graphics.VmlGraphics.prototype.handleContainerResize_ = function() {
var size = goog.style.getSize(this.getElement());
var style = this.canvasElement.getElement().style;
if (size.width) {
style.width = size.width + 'px';
style.height = size.height + 'px';
} else {
var current = this.getElement();
while (current && current.currentStyle &&
current.currentStyle.display != 'none') {
current = current.parentNode;
}
if (current && current.currentStyle) {
this.handler_.listen(current, 'propertychange',
this.handleContainerResize_);
}
}
this.dispatchEvent(goog.events.EventType.RESIZE);
};
/**
* Handle property changes on hidden ancestors.
* @param {goog.events.BrowserEvent} e The browser event.
* @private
*/
goog.graphics.VmlGraphics.prototype.handlePropertyChange_ = function(e) {
var prop = e.getBrowserEvent().propertyName;
if (prop == 'display' || prop == 'className') {
this.handler_.unlisten(/** @type {Element} */(e.target),
'propertychange', this.handlePropertyChange_);
this.handleContainerResize_();
}
};
/**
* Changes the coordinate system position.
* @param {number} left The coordinate system left bound.
* @param {number} top The coordinate system top bound.
* @override
*/
goog.graphics.VmlGraphics.prototype.setCoordOrigin = function(left, top) {
this.coordLeft = left;
this.coordTop = top;
this.canvasElement.getElement().coordorigin =
goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) + ' ' +
goog.graphics.VmlGraphics.toSizeCoord(this.coordTop);
};
/**
* Changes the coordinate size.
* @param {number} coordWidth The coordinate width.
* @param {number} coordHeight The coordinate height.
* @override
*/
goog.graphics.VmlGraphics.prototype.setCoordSize = function(coordWidth,
coordHeight) {
goog.graphics.VmlGraphics.superClass_.setCoordSize.apply(this, arguments);
this.canvasElement.getElement().coordsize =
goog.graphics.VmlGraphics.toSizeCoord(coordWidth) + ' ' +
goog.graphics.VmlGraphics.toSizeCoord(coordHeight);
};
/**
* Change the size of the canvas.
* @param {number} pixelWidth The width in pixels.
* @param {number} pixelHeight The height in pixels.
* @override
*/
goog.graphics.VmlGraphics.prototype.setSize = function(pixelWidth,
pixelHeight) {
goog.style.setSize(this.getElement(), pixelWidth, pixelHeight);
};
/**
* @return {goog.math.Size} Returns the number of pixels spanned by the surface.
* @override
*/
goog.graphics.VmlGraphics.prototype.getPixelSize = function() {
var el = this.getElement();
// The following relies on the fact that the size can never be 0.
return new goog.math.Size(el.style.pixelWidth || el.offsetWidth || 1,
el.style.pixelHeight || el.offsetHeight || 1);
};
/**
* Remove all drawing elements from the graphics.
* @override
*/
goog.graphics.VmlGraphics.prototype.clear = function() {
this.canvasElement.clear();
};
/**
* Draw an ellipse.
*
* @param {number} cx Center X coordinate.
* @param {number} cy Center Y coordinate.
* @param {number} rx Radius length for the x-axis.
* @param {number} ry Radius length for the y-axis.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.EllipseElement} The newly created element.
* @override
*/
goog.graphics.VmlGraphics.prototype.drawEllipse = function(cx, cy, rx, ry,
stroke, fill, opt_group) {
var element = this.createVmlElement('oval');
goog.graphics.VmlGraphics.setPositionAndSize(element, cx - rx, cy - ry,
rx * 2, ry * 2);
var wrapper = new goog.graphics.VmlEllipseElement(element, this,
cx, cy, rx, ry, stroke, fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw a rectangle.
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of rectangle.
* @param {number} height Height of rectangle.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
* stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.RectElement} The newly created element.
* @override
*/
goog.graphics.VmlGraphics.prototype.drawRect = function(x, y, width, height,
stroke, fill, opt_group) {
var element = this.createVmlElement('rect');
goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height);
var wrapper = new goog.graphics.VmlRectElement(element, this, stroke, fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw an image.
*
* @param {number} x X coordinate (left).
* @param {number} y Y coordinate (top).
* @param {number} width Width of image.
* @param {number} height Height of image.
* @param {string} src Source of the image.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.ImageElement} The newly created element.
*/
goog.graphics.VmlGraphics.prototype.drawImage = function(x, y, width, height,
src, opt_group) {
var element = this.createVmlElement('image');
goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height);
goog.graphics.VmlGraphics.setAttribute(element, 'src', src);
var wrapper = new goog.graphics.VmlImageElement(element, this);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw a text string vertically centered on a given line.
*
* @param {string} text The text to draw.
* @param {number} x1 X coordinate of start of line.
* @param {number} y1 Y coordinate of start of line.
* @param {number} x2 X coordinate of end of line.
* @param {number} y2 Y coordinate of end of line.
* @param {?string} align Horizontal alignment: left (default), center, right.
* @param {goog.graphics.Font} font Font describing the font properties.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.TextElement} The newly created element.
* @override
*/
goog.graphics.VmlGraphics.prototype.drawTextOnLine = function(
text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {
var shape = this.createFullSizeElement_('shape');
var pathElement = this.createVmlElement('path');
var path = 'M' + goog.graphics.VmlGraphics.toPosCoord(x1) + ',' +
goog.graphics.VmlGraphics.toPosCoord(y1) + 'L' +
goog.graphics.VmlGraphics.toPosCoord(x2) + ',' +
goog.graphics.VmlGraphics.toPosCoord(y2) + 'E';
goog.graphics.VmlGraphics.setAttribute(pathElement, 'v', path);
goog.graphics.VmlGraphics.setAttribute(pathElement, 'textpathok', 'true');
var textPathElement = this.createVmlElement('textpath');
textPathElement.setAttribute('on', 'true');
var style = textPathElement.style;
style.fontSize = font.size * this.getPixelScaleX();
style.fontFamily = font.family;
if (align != null) {
style['v-text-align'] = align;
}
if (font.bold) {
style.fontWeight = 'bold';
}
if (font.italic) {
style.fontStyle = 'italic';
}
goog.graphics.VmlGraphics.setAttribute(textPathElement, 'string', text);
shape.appendChild(pathElement);
shape.appendChild(textPathElement);
var wrapper = new goog.graphics.VmlTextElement(shape, this, stroke, fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Draw a path.
*
* @param {!goog.graphics.Path} path The path object to draw.
* @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke.
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.PathElement} The newly created element.
* @override
*/
goog.graphics.VmlGraphics.prototype.drawPath = function(path, stroke, fill,
opt_group) {
var element = this.createFullSizeElement_('shape');
goog.graphics.VmlGraphics.setAttribute(element, 'path',
goog.graphics.VmlGraphics.getVmlPath(path));
var wrapper = new goog.graphics.VmlPathElement(element, this, stroke, fill);
this.append_(wrapper, opt_group);
return wrapper;
};
/**
* Returns a string representation of a logical path suitable for use in
* a VML element.
*
* @param {goog.graphics.Path} path The logical path.
* @return {string} The VML path representation.
* @suppress {deprecated} goog.graphics is deprecated.
*/
goog.graphics.VmlGraphics.getVmlPath = function(path) {
var list = [];
path.forEachSegment(function(segment, args) {
switch (segment) {
case goog.graphics.Path.Segment.MOVETO:
list.push('m');
Array.prototype.push.apply(list, goog.array.map(args,
goog.graphics.VmlGraphics.toSizeCoord));
break;
case goog.graphics.Path.Segment.LINETO:
list.push('l');
Array.prototype.push.apply(list, goog.array.map(args,
goog.graphics.VmlGraphics.toSizeCoord));
break;
case goog.graphics.Path.Segment.CURVETO:
list.push('c');
Array.prototype.push.apply(list, goog.array.map(args,
goog.graphics.VmlGraphics.toSizeCoord));
break;
case goog.graphics.Path.Segment.CLOSE:
list.push('x');
break;
case goog.graphics.Path.Segment.ARCTO:
var toAngle = args[2] + args[3];
var cx = goog.graphics.VmlGraphics.toSizeCoord(
args[4] - goog.math.angleDx(toAngle, args[0]));
var cy = goog.graphics.VmlGraphics.toSizeCoord(
args[5] - goog.math.angleDy(toAngle, args[1]));
var rx = goog.graphics.VmlGraphics.toSizeCoord(args[0]);
var ry = goog.graphics.VmlGraphics.toSizeCoord(args[1]);
// VML angles are in fd units (see http://www.w3.org/TR/NOTE-VML) and
// are positive counter-clockwise.
var fromAngle = Math.round(args[2] * -65536);
var extent = Math.round(args[3] * -65536);
list.push('ae', cx, cy, rx, ry, fromAngle, extent);
break;
}
});
return list.join(' ');
};
/**
* Create an empty group of drawing elements.
*
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
* to append to. If not specified, appends to the main canvas.
*
* @return {goog.graphics.GroupElement} The newly created group.
* @override
*/
goog.graphics.VmlGraphics.prototype.createGroup = function(opt_group) {
var element = this.createFullSizeElement_('group');
var parent = opt_group || this.canvasElement;
parent.getElement().appendChild(element);
return new goog.graphics.VmlGroupElement(element, this);
};
/**
* Measure and return the width (in pixels) of a given text string.
* Text measurement is needed to make sure a text can fit in the allocated
* area. The way text length is measured is by writing it into a div that is
* after the visible area, measure the div width, and immediatly erase the
* written value.
*
* @param {string} text The text string to measure.
* @param {goog.graphics.Font} font The font object describing the font style.
*
* @return {number} The width in pixels of the text strings.
* @override
*/
goog.graphics.VmlGraphics.prototype.getTextWidth = function(text, font) {
// TODO(arv): Implement
return 0;
};
/** @override */
goog.graphics.VmlGraphics.prototype.enterDocument = function() {
goog.graphics.VmlGraphics.superClass_.enterDocument.call(this);
this.handleContainerResize_();
this.updateGraphics_();
};
/**
* Disposes of the component by removing event handlers, detacing DOM nodes from
* the document body, and removing references to them.
* @override
* @protected
*/
goog.graphics.VmlGraphics.prototype.disposeInternal = function() {
this.canvasElement = null;
goog.graphics.VmlGraphics.superClass_.disposeInternal.call(this);
};