Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/graphics/svggraphics.js
2868 views
1
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
16
/**
17
* @fileoverview SvgGraphics sub class that uses SVG to draw the graphics.
18
* @author [email protected] (Erik Arvidsson)
19
*/
20
21
goog.provide('goog.graphics.SvgGraphics');
22
23
goog.require('goog.Timer');
24
goog.require('goog.dom');
25
goog.require('goog.events.EventHandler');
26
goog.require('goog.events.EventType');
27
goog.require('goog.graphics.AbstractGraphics');
28
goog.require('goog.graphics.Font');
29
goog.require('goog.graphics.LinearGradient');
30
goog.require('goog.graphics.Path');
31
goog.require('goog.graphics.SolidFill');
32
goog.require('goog.graphics.Stroke');
33
goog.require('goog.graphics.SvgEllipseElement');
34
goog.require('goog.graphics.SvgGroupElement');
35
goog.require('goog.graphics.SvgImageElement');
36
goog.require('goog.graphics.SvgPathElement');
37
goog.require('goog.graphics.SvgRectElement');
38
goog.require('goog.graphics.SvgTextElement');
39
goog.require('goog.math');
40
goog.require('goog.math.Size');
41
goog.require('goog.style');
42
goog.require('goog.userAgent');
43
44
45
46
/**
47
* A Graphics implementation for drawing using SVG.
48
* @param {string|number} width The width in pixels. Strings
49
* expressing percentages of parent with (e.g. '80%') are also accepted.
50
* @param {string|number} height The height in pixels. Strings
51
* expressing percentages of parent with (e.g. '80%') are also accepted.
52
* @param {?number=} opt_coordWidth The coordinate width - if
53
* omitted or null, defaults to same as width.
54
* @param {?number=} opt_coordHeight The coordinate height - if
55
* omitted or null, defaults to same as height.
56
* @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
57
* document we want to render in.
58
* @constructor
59
* @extends {goog.graphics.AbstractGraphics}
60
* @deprecated goog.graphics is deprecated. It existed to abstract over browser
61
* differences before the canvas tag was widely supported. See
62
* http://en.wikipedia.org/wiki/Canvas_element for details.
63
* @final
64
*/
65
goog.graphics.SvgGraphics = function(
66
width, height, opt_coordWidth, opt_coordHeight, opt_domHelper) {
67
goog.graphics.AbstractGraphics.call(
68
this, width, height, opt_coordWidth, opt_coordHeight, opt_domHelper);
69
70
/**
71
* Map from def key to id of def root element.
72
* Defs are global "defines" of svg that are used to share common attributes,
73
* for example gradients.
74
* @type {Object}
75
* @private
76
*/
77
this.defs_ = {};
78
79
/**
80
* Whether to manually implement viewBox by using a coordinate transform.
81
* As of 1/11/08 this is necessary for Safari 3 but not for the nightly
82
* WebKit build. Apply to webkit versions < 526. 525 is the
83
* last version used by Safari 3.1.
84
* @type {boolean}
85
* @private
86
*/
87
this.useManualViewbox_ =
88
goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher(526);
89
90
/**
91
* Event handler.
92
* @type {goog.events.EventHandler<!goog.graphics.SvgGraphics>}
93
* @private
94
*/
95
this.handler_ = new goog.events.EventHandler(this);
96
};
97
goog.inherits(goog.graphics.SvgGraphics, goog.graphics.AbstractGraphics);
98
99
100
/**
101
* The SVG namespace URN
102
* @private
103
* @type {string}
104
*/
105
goog.graphics.SvgGraphics.SVG_NS_ = 'http://www.w3.org/2000/svg';
106
107
108
/**
109
* The name prefix for def entries
110
* @private
111
* @type {string}
112
*/
113
goog.graphics.SvgGraphics.DEF_ID_PREFIX_ = '_svgdef_';
114
115
116
/**
117
* The next available unique identifier for a def entry.
118
* This is a static variable, so that when multiple graphics are used in one
119
* document, the same def id can not be re-defined by another SvgGraphics.
120
* @type {number}
121
* @private
122
*/
123
goog.graphics.SvgGraphics.nextDefId_ = 0;
124
125
126
/**
127
* Svg element for definitions for other elements, e.g. linear gradients.
128
* @type {Element}
129
* @private
130
*/
131
goog.graphics.SvgGraphics.prototype.defsElement_;
132
133
134
/**
135
* Creates an SVG element. Used internally and by different SVG classes.
136
* @param {string} tagName The type of element to create.
137
* @param {Object=} opt_attributes Map of name-value pairs for attributes.
138
* @return {!Element} The created element.
139
* @private
140
*/
141
goog.graphics.SvgGraphics.prototype.createSvgElement_ = function(
142
tagName, opt_attributes) {
143
var element = this.dom_.getDocument().createElementNS(
144
goog.graphics.SvgGraphics.SVG_NS_, tagName);
145
146
if (opt_attributes) {
147
this.setElementAttributes(element, opt_attributes);
148
}
149
150
return element;
151
};
152
153
154
/**
155
* Sets properties to an SVG element. Used internally and by different
156
* SVG elements.
157
* @param {Element} element The svg element.
158
* @param {Object} attributes Map of name-value pairs for attributes.
159
*/
160
goog.graphics.SvgGraphics.prototype.setElementAttributes = function(
161
element, attributes) {
162
for (var key in attributes) {
163
element.setAttribute(key, attributes[key]);
164
}
165
};
166
167
168
/**
169
* Appends an element.
170
*
171
* @param {goog.graphics.Element} element The element wrapper.
172
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
173
* to append to. If not specified, appends to the main canvas.
174
* @private
175
*/
176
goog.graphics.SvgGraphics.prototype.append_ = function(element, opt_group) {
177
var parent = opt_group || this.canvasElement;
178
parent.getElement().appendChild(element.getElement());
179
};
180
181
182
/**
183
* Sets the fill of the given element.
184
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
185
* @param {goog.graphics.Fill?} fill The fill object.
186
* @override
187
*/
188
goog.graphics.SvgGraphics.prototype.setElementFill = function(element, fill) {
189
var svgElement = element.getElement();
190
if (fill instanceof goog.graphics.SolidFill) {
191
svgElement.setAttribute('fill', fill.getColor());
192
svgElement.setAttribute('fill-opacity', fill.getOpacity());
193
} else if (fill instanceof goog.graphics.LinearGradient) {
194
// create a def key which is just a concat of all the relevant fields
195
var defKey = 'lg-' + fill.getX1() + '-' + fill.getY1() + '-' +
196
fill.getX2() + '-' + fill.getY2() + '-' + fill.getColor1() + '-' +
197
fill.getColor2();
198
// It seems that the SVG version accepts opacity where the VML does not
199
200
var id = this.getDef(defKey);
201
202
if (!id) { // No def for this yet, create it
203
// Create the gradient def entry (only linear gradient are supported)
204
var gradient = this.createSvgElement_('linearGradient', {
205
'x1': fill.getX1(),
206
'y1': fill.getY1(),
207
'x2': fill.getX2(),
208
'y2': fill.getY2(),
209
'gradientUnits': 'userSpaceOnUse'
210
});
211
212
var gstyle = 'stop-color:' + fill.getColor1();
213
if (goog.isNumber(fill.getOpacity1())) {
214
gstyle += ';stop-opacity:' + fill.getOpacity1();
215
}
216
var stop1 =
217
this.createSvgElement_('stop', {'offset': '0%', 'style': gstyle});
218
gradient.appendChild(stop1);
219
220
// LinearGradients don't have opacity in VML so implement that before
221
// enabling the following code.
222
// if (fill.getOpacity() != null) {
223
// gstyles += 'opacity:' + fill.getOpacity() + ';'
224
// }
225
gstyle = 'stop-color:' + fill.getColor2();
226
if (goog.isNumber(fill.getOpacity2())) {
227
gstyle += ';stop-opacity:' + fill.getOpacity2();
228
}
229
var stop2 =
230
this.createSvgElement_('stop', {'offset': '100%', 'style': gstyle});
231
gradient.appendChild(stop2);
232
233
// LinearGradients don't have opacity in VML so implement that before
234
// enabling the following code.
235
// if (fill.getOpacity() != null) {
236
// gstyles += 'opacity:' + fill.getOpacity() + ';'
237
// }
238
239
id = this.addDef(defKey, gradient);
240
}
241
242
// Link element to linearGradient definition
243
svgElement.setAttribute('fill', 'url(#' + id + ')');
244
} else {
245
svgElement.setAttribute('fill', 'none');
246
}
247
};
248
249
250
/**
251
* Sets the stroke of the given element.
252
* @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
253
* @param {goog.graphics.Stroke?} stroke The stroke object.
254
* @override
255
*/
256
goog.graphics.SvgGraphics.prototype.setElementStroke = function(
257
element, stroke) {
258
var svgElement = element.getElement();
259
if (stroke) {
260
svgElement.setAttribute('stroke', stroke.getColor());
261
svgElement.setAttribute('stroke-opacity', stroke.getOpacity());
262
263
var width = stroke.getWidth();
264
if (goog.isString(width) && width.indexOf('px') != -1) {
265
svgElement.setAttribute(
266
'stroke-width', parseFloat(width) / this.getPixelScaleX());
267
} else {
268
svgElement.setAttribute('stroke-width', width);
269
}
270
} else {
271
svgElement.setAttribute('stroke', 'none');
272
}
273
};
274
275
276
/**
277
* Set the translation and rotation of an element.
278
*
279
* If a more general affine transform is needed than this provides
280
* (e.g. skew and scale) then use setElementAffineTransform.
281
* @param {goog.graphics.Element} element The element wrapper.
282
* @param {number} x The x coordinate of the translation transform.
283
* @param {number} y The y coordinate of the translation transform.
284
* @param {number} angle The angle of the rotation transform.
285
* @param {number} centerX The horizontal center of the rotation transform.
286
* @param {number} centerY The vertical center of the rotation transform.
287
* @override
288
*/
289
goog.graphics.SvgGraphics.prototype.setElementTransform = function(
290
element, x, y, angle, centerX, centerY) {
291
element.getElement().setAttribute(
292
'transform', 'translate(' + x + ',' + y + ') rotate(' + angle + ' ' +
293
centerX + ' ' + centerY + ')');
294
};
295
296
297
/**
298
* Set the transformation of an element.
299
* @param {goog.graphics.Element} element The element wrapper.
300
* @param {!goog.graphics.AffineTransform} affineTransform The
301
* transformation applied to this element.
302
* @override
303
*/
304
goog.graphics.SvgGraphics.prototype.setElementAffineTransform = function(
305
element, affineTransform) {
306
var t = affineTransform;
307
var substr = [
308
t.getScaleX(), t.getShearY(), t.getShearX(), t.getScaleY(),
309
t.getTranslateX(), t.getTranslateY()
310
].join(',');
311
element.getElement().setAttribute('transform', 'matrix(' + substr + ')');
312
};
313
314
315
/**
316
* Creates the DOM representation of the graphics area.
317
* @override
318
*/
319
goog.graphics.SvgGraphics.prototype.createDom = function() {
320
// Set up the standard attributes.
321
var attributes =
322
{'width': this.width, 'height': this.height, 'overflow': 'hidden'};
323
324
var svgElement = this.createSvgElement_('svg', attributes);
325
326
var groupElement = this.createSvgElement_('g');
327
328
this.defsElement_ = this.createSvgElement_('defs');
329
this.canvasElement = new goog.graphics.SvgGroupElement(groupElement, this);
330
331
svgElement.appendChild(this.defsElement_);
332
svgElement.appendChild(groupElement);
333
334
// Use the svgElement as the root element.
335
this.setElementInternal(svgElement);
336
337
// Set up the coordinate system.
338
this.setViewBox_();
339
};
340
341
342
/**
343
* Changes the coordinate system position.
344
* @param {number} left The coordinate system left bound.
345
* @param {number} top The coordinate system top bound.
346
* @override
347
*/
348
goog.graphics.SvgGraphics.prototype.setCoordOrigin = function(left, top) {
349
this.coordLeft = left;
350
this.coordTop = top;
351
352
this.setViewBox_();
353
};
354
355
356
/**
357
* Changes the coordinate size.
358
* @param {number} coordWidth The coordinate width.
359
* @param {number} coordHeight The coordinate height.
360
* @override
361
*/
362
goog.graphics.SvgGraphics.prototype.setCoordSize = function(
363
coordWidth, coordHeight) {
364
goog.graphics.SvgGraphics.superClass_.setCoordSize.apply(this, arguments);
365
this.setViewBox_();
366
};
367
368
369
/**
370
* @return {string} The view box string.
371
* @private
372
*/
373
goog.graphics.SvgGraphics.prototype.getViewBox_ = function() {
374
return this.coordLeft + ' ' + this.coordTop + ' ' +
375
(this.coordWidth ? this.coordWidth + ' ' + this.coordHeight : '');
376
};
377
378
379
/**
380
* Sets up the view box.
381
* @private
382
*/
383
goog.graphics.SvgGraphics.prototype.setViewBox_ = function() {
384
if (this.coordWidth || this.coordLeft || this.coordTop) {
385
this.getElement().setAttribute('preserveAspectRatio', 'none');
386
if (this.useManualViewbox_) {
387
this.updateManualViewBox_();
388
} else {
389
this.getElement().setAttribute('viewBox', this.getViewBox_());
390
}
391
}
392
};
393
394
395
/**
396
* Updates the transform of the root element to fake a viewBox. Should only
397
* be called when useManualViewbox_ is set.
398
* @private
399
*/
400
goog.graphics.SvgGraphics.prototype.updateManualViewBox_ = function() {
401
if (!this.isInDocument() ||
402
!(this.coordWidth || this.coordLeft || !this.coordTop)) {
403
return;
404
}
405
406
var size = this.getPixelSize();
407
if (size.width == 0) {
408
// In Safari, invisible SVG is sometimes shown. Explicitly hide it.
409
this.getElement().style.visibility = 'hidden';
410
return;
411
}
412
413
this.getElement().style.visibility = '';
414
415
var offsetX = -this.coordLeft;
416
var offsetY = -this.coordTop;
417
var scaleX = size.width / this.coordWidth;
418
var scaleY = size.height / this.coordHeight;
419
420
this.canvasElement.getElement().setAttribute(
421
'transform', 'scale(' + scaleX + ' ' + scaleY + ') ' +
422
'translate(' + offsetX + ' ' + offsetY + ')');
423
};
424
425
426
/**
427
* Change the size of the canvas.
428
* @param {number} pixelWidth The width in pixels.
429
* @param {number} pixelHeight The height in pixels.
430
* @override
431
*/
432
goog.graphics.SvgGraphics.prototype.setSize = function(
433
pixelWidth, pixelHeight) {
434
goog.style.setSize(this.getElement(), pixelWidth, pixelHeight);
435
};
436
437
438
/** @override */
439
goog.graphics.SvgGraphics.prototype.getPixelSize = function() {
440
if (!goog.userAgent.GECKO) {
441
return this.isInDocument() ?
442
goog.style.getSize(this.getElement()) :
443
goog.graphics.SvgGraphics.base(this, 'getPixelSize');
444
}
445
446
// In Gecko, goog.style.getSize does not work for SVG elements. We have to
447
// compute the size manually if it is percentage based.
448
var width = this.width;
449
var height = this.height;
450
var computeWidth = goog.isString(width) && width.indexOf('%') != -1;
451
var computeHeight = goog.isString(height) && height.indexOf('%') != -1;
452
453
if (!this.isInDocument() && (computeWidth || computeHeight)) {
454
return null;
455
}
456
457
var parent;
458
var parentSize;
459
460
if (computeWidth) {
461
parent = /** @type {Element} */ (this.getElement().parentNode);
462
parentSize = goog.style.getSize(parent);
463
width = parseFloat(/** @type {string} */ (width)) * parentSize.width / 100;
464
}
465
466
if (computeHeight) {
467
parent = parent || /** @type {Element} */ (this.getElement().parentNode);
468
parentSize = parentSize || goog.style.getSize(parent);
469
height =
470
parseFloat(/** @type {string} */ (height)) * parentSize.height / 100;
471
}
472
473
return new goog.math.Size(
474
/** @type {number} */ (width),
475
/** @type {number} */ (height));
476
};
477
478
479
/**
480
* Remove all drawing elements from the graphics.
481
* @override
482
*/
483
goog.graphics.SvgGraphics.prototype.clear = function() {
484
this.canvasElement.clear();
485
goog.dom.removeChildren(this.defsElement_);
486
this.defs_ = {};
487
};
488
489
490
/**
491
* Draw an ellipse.
492
*
493
* @param {number} cx Center X coordinate.
494
* @param {number} cy Center Y coordinate.
495
* @param {number} rx Radius length for the x-axis.
496
* @param {number} ry Radius length for the y-axis.
497
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
498
* stroke.
499
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
500
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
501
* to append to. If not specified, appends to the main canvas.
502
*
503
* @return {!goog.graphics.EllipseElement} The newly created element.
504
* @override
505
*/
506
goog.graphics.SvgGraphics.prototype.drawEllipse = function(
507
cx, cy, rx, ry, stroke, fill, opt_group) {
508
var element = this.createSvgElement_(
509
'ellipse', {'cx': cx, 'cy': cy, 'rx': rx, 'ry': ry});
510
var wrapper =
511
new goog.graphics.SvgEllipseElement(element, this, stroke, fill);
512
this.append_(wrapper, opt_group);
513
return wrapper;
514
};
515
516
517
/**
518
* Draw a rectangle.
519
*
520
* @param {number} x X coordinate (left).
521
* @param {number} y Y coordinate (top).
522
* @param {number} width Width of rectangle.
523
* @param {number} height Height of rectangle.
524
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
525
* stroke.
526
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
527
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
528
* to append to. If not specified, appends to the main canvas.
529
*
530
* @return {!goog.graphics.RectElement} The newly created element.
531
* @override
532
*/
533
goog.graphics.SvgGraphics.prototype.drawRect = function(
534
x, y, width, height, stroke, fill, opt_group) {
535
var element = this.createSvgElement_(
536
'rect', {'x': x, 'y': y, 'width': width, 'height': height});
537
var wrapper = new goog.graphics.SvgRectElement(element, this, stroke, fill);
538
this.append_(wrapper, opt_group);
539
return wrapper;
540
};
541
542
543
/**
544
* Draw an image.
545
*
546
* @param {number} x X coordinate (left).
547
* @param {number} y Y coordinate (top).
548
* @param {number} width Width of the image.
549
* @param {number} height Height of the image.
550
* @param {string} src The source fo the image.
551
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
552
* to append to. If not specified, appends to the main canvas.
553
*
554
* @return {!goog.graphics.ImageElement} The newly created image wrapped in a
555
* rectangle element.
556
*/
557
goog.graphics.SvgGraphics.prototype.drawImage = function(
558
x, y, width, height, src, opt_group) {
559
var element = this.createSvgElement_('image', {
560
'x': x,
561
'y': y,
562
'width': width,
563
'height': height,
564
'image-rendering': 'optimizeQuality',
565
'preserveAspectRatio': 'none'
566
});
567
element.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src);
568
var wrapper = new goog.graphics.SvgImageElement(element, this);
569
this.append_(wrapper, opt_group);
570
return wrapper;
571
};
572
573
574
/**
575
* Draw a text string vertically centered on a given line.
576
*
577
* @param {string} text The text to draw.
578
* @param {number} x1 X coordinate of start of line.
579
* @param {number} y1 Y coordinate of start of line.
580
* @param {number} x2 X coordinate of end of line.
581
* @param {number} y2 Y coordinate of end of line.
582
* @param {string} align Horizontal alignment: left (default), center, right.
583
* @param {goog.graphics.Font} font Font describing the font properties.
584
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
585
* stroke.
586
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
587
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
588
* to append to. If not specified, appends to the main canvas.
589
*
590
* @return {!goog.graphics.TextElement} The newly created element.
591
* @override
592
*/
593
goog.graphics.SvgGraphics.prototype.drawTextOnLine = function(
594
text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {
595
var angle = Math.round(goog.math.angle(x1, y1, x2, y2));
596
var dx = x2 - x1;
597
var dy = y2 - y1;
598
var lineLength = Math.round(Math.sqrt(dx * dx + dy * dy)); // Length of line
599
600
// SVG baseline is on the glyph's base line. We estimate it as 85% of the
601
// font height. This is just a rough estimate, but do not have a better way.
602
var fontSize = font.size;
603
var attributes = {'font-family': font.family, 'font-size': fontSize};
604
var baseline = Math.round(fontSize * 0.85);
605
var textY = Math.round(y1 - (fontSize / 2) + baseline);
606
var textX = x1;
607
if (align == 'center') {
608
textX += Math.round(lineLength / 2);
609
attributes['text-anchor'] = 'middle';
610
} else if (align == 'right') {
611
textX += lineLength;
612
attributes['text-anchor'] = 'end';
613
}
614
attributes['x'] = textX;
615
attributes['y'] = textY;
616
if (font.bold) {
617
attributes['font-weight'] = 'bold';
618
}
619
if (font.italic) {
620
attributes['font-style'] = 'italic';
621
}
622
if (angle != 0) {
623
attributes['transform'] = 'rotate(' + angle + ' ' + x1 + ' ' + y1 + ')';
624
}
625
626
var element = this.createSvgElement_('text', attributes);
627
element.appendChild(this.dom_.getDocument().createTextNode(text));
628
629
// Bypass a Firefox-Mac bug where text fill is ignored. If text has no stroke,
630
// set a stroke, otherwise the text will not be visible.
631
if (stroke == null && goog.userAgent.GECKO && goog.userAgent.MAC) {
632
var color = 'black';
633
// For solid fills, use the fill color
634
if (fill instanceof goog.graphics.SolidFill) {
635
color = fill.getColor();
636
}
637
stroke = new goog.graphics.Stroke(1, color);
638
}
639
640
var wrapper = new goog.graphics.SvgTextElement(element, this, stroke, fill);
641
this.append_(wrapper, opt_group);
642
return wrapper;
643
};
644
645
646
/**
647
* Draw a path.
648
*
649
* @param {!goog.graphics.Path} path The path object to draw.
650
* @param {goog.graphics.Stroke?} stroke Stroke object describing the
651
* stroke.
652
* @param {goog.graphics.Fill?} fill Fill object describing the fill.
653
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
654
* to append to. If not specified, appends to the main canvas.
655
*
656
* @return {!goog.graphics.PathElement} The newly created element.
657
* @override
658
*/
659
goog.graphics.SvgGraphics.prototype.drawPath = function(
660
path, stroke, fill, opt_group) {
661
662
var element = this.createSvgElement_(
663
'path', {'d': goog.graphics.SvgGraphics.getSvgPath(path)});
664
var wrapper = new goog.graphics.SvgPathElement(element, this, stroke, fill);
665
this.append_(wrapper, opt_group);
666
return wrapper;
667
};
668
669
670
/**
671
* Returns a string representation of a logical path suitable for use in
672
* an SVG element.
673
*
674
* @param {goog.graphics.Path} path The logical path.
675
* @return {string} The SVG path representation.
676
* @suppress {deprecated} goog.graphics is deprecated.
677
*/
678
goog.graphics.SvgGraphics.getSvgPath = function(path) {
679
var list = [];
680
path.forEachSegment(function(segment, args) {
681
switch (segment) {
682
case goog.graphics.Path.Segment.MOVETO:
683
list.push('M');
684
Array.prototype.push.apply(list, args);
685
break;
686
case goog.graphics.Path.Segment.LINETO:
687
list.push('L');
688
Array.prototype.push.apply(list, args);
689
break;
690
case goog.graphics.Path.Segment.CURVETO:
691
list.push('C');
692
Array.prototype.push.apply(list, args);
693
break;
694
case goog.graphics.Path.Segment.ARCTO:
695
var extent = args[3];
696
list.push(
697
'A', args[0], args[1], 0, Math.abs(extent) > 180 ? 1 : 0,
698
extent > 0 ? 1 : 0, args[4], args[5]);
699
break;
700
case goog.graphics.Path.Segment.CLOSE:
701
list.push('Z');
702
break;
703
}
704
});
705
return list.join(' ');
706
};
707
708
709
/**
710
* Create an empty group of drawing elements.
711
*
712
* @param {goog.graphics.GroupElement=} opt_group The group wrapper element
713
* to append to. If not specified, appends to the main canvas.
714
*
715
* @return {!goog.graphics.GroupElement} The newly created group.
716
* @override
717
*/
718
goog.graphics.SvgGraphics.prototype.createGroup = function(opt_group) {
719
var element = this.createSvgElement_('g');
720
var parent = opt_group || this.canvasElement;
721
parent.getElement().appendChild(element);
722
return new goog.graphics.SvgGroupElement(element, this);
723
};
724
725
726
/**
727
* Measure and return the width (in pixels) of a given text string.
728
* Text measurement is needed to make sure a text can fit in the allocated area.
729
* The way text length is measured is by writing it into a div that is after
730
* the visible area, measure the div width, and immediately erase the written
731
* value.
732
*
733
* @override
734
*/
735
goog.graphics.SvgGraphics.prototype.getTextWidth = function(text, font) {
736
// TODO(user) Implement
737
throw new Error("unimplemented method");
738
};
739
740
741
/**
742
* Adds a definition of an element to the global definitions.
743
* @param {string} defKey This is a key that should be unique in a way that
744
* if two definitions are equal the should have the same key.
745
* @param {Element} defElement DOM element to add as a definition. It must
746
* have an id attribute set.
747
* @return {string} The assigned id of the defElement.
748
*/
749
goog.graphics.SvgGraphics.prototype.addDef = function(defKey, defElement) {
750
if (defKey in this.defs_) {
751
return this.defs_[defKey];
752
}
753
var id = goog.graphics.SvgGraphics.DEF_ID_PREFIX_ +
754
goog.graphics.SvgGraphics.nextDefId_++;
755
defElement.setAttribute('id', id);
756
this.defs_[defKey] = id;
757
758
// Add the def defElement of the defs list.
759
var defs = this.defsElement_;
760
defs.appendChild(defElement);
761
return id;
762
};
763
764
765
/**
766
* Returns the id of a definition element.
767
* @param {string} defKey This is a key that should be unique in a way that
768
* if two definitions are equal the should have the same key.
769
* @return {?string} The id of the found definition element or null if
770
* not found.
771
*/
772
goog.graphics.SvgGraphics.prototype.getDef = function(defKey) {
773
return defKey in this.defs_ ? this.defs_[defKey] : null;
774
};
775
776
777
/**
778
* Removes a definition of an elemnt from the global definitions.
779
* @param {string} defKey This is a key that should be unique in a way that
780
* if two definitions are equal they should have the same key.
781
*/
782
goog.graphics.SvgGraphics.prototype.removeDef = function(defKey) {
783
var id = this.getDef(defKey);
784
if (id) {
785
var element = this.dom_.getElement(id);
786
this.defsElement_.removeChild(element);
787
delete this.defs_[defKey];
788
}
789
};
790
791
792
/** @override */
793
goog.graphics.SvgGraphics.prototype.enterDocument = function() {
794
var oldPixelSize = this.getPixelSize();
795
goog.graphics.SvgGraphics.superClass_.enterDocument.call(this);
796
797
// Dispatch a resize if this is the first time the size value is accurate.
798
if (!oldPixelSize) {
799
this.dispatchEvent(goog.events.EventType.RESIZE);
800
}
801
802
803
// For percentage based heights, listen for changes to size.
804
if (this.useManualViewbox_) {
805
var width = this.width;
806
var height = this.height;
807
808
if (typeof width == 'string' && width.indexOf('%') != -1 &&
809
typeof height == 'string' && height.indexOf('%') != -1) {
810
// SVG elements don't behave well with respect to size events, so we
811
// resort to polling.
812
this.handler_.listen(
813
goog.graphics.SvgGraphics.getResizeCheckTimer_(), goog.Timer.TICK,
814
this.updateManualViewBox_);
815
}
816
817
this.updateManualViewBox_();
818
}
819
};
820
821
822
/** @override */
823
goog.graphics.SvgGraphics.prototype.exitDocument = function() {
824
goog.graphics.SvgGraphics.superClass_.exitDocument.call(this);
825
826
// Stop polling.
827
if (this.useManualViewbox_) {
828
this.handler_.unlisten(
829
goog.graphics.SvgGraphics.getResizeCheckTimer_(), goog.Timer.TICK,
830
this.updateManualViewBox_);
831
}
832
};
833
834
835
/**
836
* Disposes of the component by removing event handlers, detacing DOM nodes from
837
* the document body, and removing references to them.
838
* @override
839
* @protected
840
*/
841
goog.graphics.SvgGraphics.prototype.disposeInternal = function() {
842
delete this.defs_;
843
delete this.defsElement_;
844
delete this.canvasElement;
845
this.handler_.dispose();
846
delete this.handler_;
847
goog.graphics.SvgGraphics.superClass_.disposeInternal.call(this);
848
};
849
850
851
/**
852
* The centralized resize checking timer.
853
* @type {goog.Timer|undefined}
854
* @private
855
*/
856
goog.graphics.SvgGraphics.resizeCheckTimer_;
857
858
859
/**
860
* @return {goog.Timer} The centralized timer object used for interval timing.
861
* @private
862
*/
863
goog.graphics.SvgGraphics.getResizeCheckTimer_ = function() {
864
if (!goog.graphics.SvgGraphics.resizeCheckTimer_) {
865
goog.graphics.SvgGraphics.resizeCheckTimer_ = new goog.Timer(400);
866
goog.graphics.SvgGraphics.resizeCheckTimer_.start();
867
}
868
869
return /** @type {goog.Timer} */ (
870
goog.graphics.SvgGraphics.resizeCheckTimer_);
871
};
872
873
874
/** @override */
875
goog.graphics.SvgGraphics.prototype.isDomClonable = function() {
876
return true;
877
};
878
879