fabric-rails 1.0.12 → 1.0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +16 -0
- data/README.md +1 -1
- data/lib/fabric/rails/version.rb +2 -2
- data/vendor/assets/javascripts/event.js +1909 -0
- data/vendor/assets/javascripts/fabric.js +64 -16464
- data/vendor/assets/javascripts/fabric/HEADER.js +31 -0
- data/vendor/assets/javascripts/fabric/canvas.class.js +1007 -0
- data/vendor/assets/javascripts/fabric/canvas_animation.mixin.js +113 -0
- data/vendor/assets/javascripts/fabric/canvas_events.mixin.js +482 -0
- data/vendor/assets/javascripts/fabric/canvas_gestures.mixin.js +79 -0
- data/vendor/assets/javascripts/fabric/canvas_serialization.mixin.js +311 -0
- data/vendor/assets/javascripts/fabric/circle.class.js +182 -0
- data/vendor/assets/javascripts/fabric/color.class.js +284 -0
- data/vendor/assets/javascripts/fabric/ellipse.class.js +169 -0
- data/vendor/assets/javascripts/fabric/freedrawing.class.js +256 -0
- data/vendor/assets/javascripts/fabric/gradient.class.js +211 -0
- data/vendor/assets/javascripts/fabric/group.class.js +556 -0
- data/vendor/assets/javascripts/fabric/image.class.js +418 -0
- data/vendor/assets/javascripts/fabric/image_filters.js +809 -0
- data/vendor/assets/javascripts/fabric/intersection.class.js +178 -0
- data/vendor/assets/javascripts/fabric/line.class.js +188 -0
- data/vendor/assets/javascripts/fabric/log.js +26 -0
- data/vendor/assets/javascripts/fabric/node.js +149 -0
- data/vendor/assets/javascripts/fabric/object.class.js +1068 -0
- data/vendor/assets/javascripts/fabric/object_geometry.mixin.js +308 -0
- data/vendor/assets/javascripts/fabric/object_interactivity.mixin.js +496 -0
- data/vendor/assets/javascripts/fabric/object_origin.mixin.js +207 -0
- data/vendor/assets/javascripts/fabric/object_straightening.mixin.js +94 -0
- data/vendor/assets/javascripts/fabric/observable.mixin.js +91 -0
- data/vendor/assets/javascripts/fabric/parser.js +750 -0
- data/vendor/assets/javascripts/fabric/path.class.js +794 -0
- data/vendor/assets/javascripts/fabric/path_group.class.js +240 -0
- data/vendor/assets/javascripts/fabric/pattern.class.js +69 -0
- data/vendor/assets/javascripts/fabric/point.class.js +327 -0
- data/vendor/assets/javascripts/fabric/polygon.class.js +184 -0
- data/vendor/assets/javascripts/fabric/polyline.class.js +157 -0
- data/vendor/assets/javascripts/fabric/rect.class.js +298 -0
- data/vendor/assets/javascripts/fabric/scout.js +45 -0
- data/vendor/assets/javascripts/fabric/shadow.class.js +70 -0
- data/vendor/assets/javascripts/fabric/stateful.js +88 -0
- data/vendor/assets/javascripts/fabric/static_canvas.class.js +1298 -0
- data/vendor/assets/javascripts/fabric/text.class.js +934 -0
- data/vendor/assets/javascripts/fabric/triangle.class.js +108 -0
- data/vendor/assets/javascripts/fabric/util/anim_ease.js +360 -0
- data/vendor/assets/javascripts/fabric/util/dom_event.js +237 -0
- data/vendor/assets/javascripts/fabric/util/dom_misc.js +245 -0
- data/vendor/assets/javascripts/fabric/util/dom_request.js +72 -0
- data/vendor/assets/javascripts/fabric/util/dom_style.js +71 -0
- data/vendor/assets/javascripts/fabric/util/lang_array.js +250 -0
- data/vendor/assets/javascripts/fabric/util/lang_class.js +97 -0
- data/vendor/assets/javascripts/fabric/util/lang_function.js +35 -0
- data/vendor/assets/javascripts/fabric/util/lang_object.js +34 -0
- data/vendor/assets/javascripts/fabric/util/lang_string.js +60 -0
- data/vendor/assets/javascripts/fabric/util/misc.js +406 -0
- data/vendor/assets/javascripts/json2.js +491 -0
- metadata +53 -2
@@ -0,0 +1,207 @@
|
|
1
|
+
(function() {
|
2
|
+
|
3
|
+
var degreesToRadians = fabric.util.degreesToRadians;
|
4
|
+
|
5
|
+
fabric.util.object.extend(fabric.Object.prototype, /** @scope fabric.Object.prototype */ {
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Translates the coordinates from origin to center coordinates (based on the object's dimensions)
|
9
|
+
* @method translateToCenterPoint
|
10
|
+
* @param {fabric.Point} point The point which corresponds to the originX and originY params
|
11
|
+
* @param {string} enum('left', 'center', 'right') Horizontal origin
|
12
|
+
* @param {string} enum('top', 'center', 'bottom') Vertical origin
|
13
|
+
* @return {fabric.Point}
|
14
|
+
*/
|
15
|
+
translateToCenterPoint: function(point, originX, originY) {
|
16
|
+
var cx = point.x, cy = point.y;
|
17
|
+
|
18
|
+
if ( originX === "left" ) {
|
19
|
+
cx = point.x + this.getWidth() / 2;
|
20
|
+
}
|
21
|
+
else if ( originX === "right" ) {
|
22
|
+
cx = point.x - this.getWidth() / 2;
|
23
|
+
}
|
24
|
+
|
25
|
+
if ( originY === "top" ) {
|
26
|
+
cy = point.y + this.getHeight() / 2;
|
27
|
+
}
|
28
|
+
else if ( originY === "bottom" ) {
|
29
|
+
cy = point.y - this.getHeight() / 2;
|
30
|
+
}
|
31
|
+
|
32
|
+
// Apply the reverse rotation to the point (it's already scaled properly)
|
33
|
+
return fabric.util.rotatePoint(new fabric.Point(cx, cy), point, degreesToRadians(this.angle));
|
34
|
+
},
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Translates the coordinates from center to origin coordinates (based on the object's dimensions)
|
38
|
+
* @method translateToOriginPoint
|
39
|
+
* @param {fabric.Point} point The point which corresponds to center of the object
|
40
|
+
* @param {string} enum('left', 'center', 'right') Horizontal origin
|
41
|
+
* @param {string} enum('top', 'center', 'bottom') Vertical origin
|
42
|
+
* @return {fabric.Point}
|
43
|
+
*/
|
44
|
+
translateToOriginPoint: function(center, originX, originY) {
|
45
|
+
var x = center.x, y = center.y;
|
46
|
+
|
47
|
+
// Get the point coordinates
|
48
|
+
if ( originX === "left" ) {
|
49
|
+
x = center.x - this.getWidth() / 2;
|
50
|
+
}
|
51
|
+
else if ( originX === "right" ) {
|
52
|
+
x = center.x + this.getWidth() / 2;
|
53
|
+
}
|
54
|
+
if ( originY === "top" ) {
|
55
|
+
y = center.y - this.getHeight() / 2;
|
56
|
+
}
|
57
|
+
else if ( originY === "bottom" ) {
|
58
|
+
y = center.y + this.getHeight() / 2;
|
59
|
+
}
|
60
|
+
|
61
|
+
// Apply the rotation to the point (it's already scaled properly)
|
62
|
+
return fabric.util.rotatePoint(new fabric.Point(x, y), center, degreesToRadians(this.angle));
|
63
|
+
},
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Returns the real center coordinates of the object
|
67
|
+
* @method getCenterPoint
|
68
|
+
* @return {fabric.Point}
|
69
|
+
*/
|
70
|
+
getCenterPoint: function() {
|
71
|
+
return this.translateToCenterPoint(
|
72
|
+
new fabric.Point(this.left, this.top), this.originX, this.originY);
|
73
|
+
},
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Returns the coordinates of the object based on center coordinates
|
77
|
+
* @method getOriginPoint
|
78
|
+
* @param {fabric.Point} point The point which corresponds to the originX and originY params
|
79
|
+
* @return {fabric.Point}
|
80
|
+
*/
|
81
|
+
// getOriginPoint: function(center) {
|
82
|
+
// return this.translateToOriginPoint(center, this.originX, this.originY);
|
83
|
+
// },
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Returns the coordinates of the object as if it has a different origin
|
87
|
+
* @method getPointByOrigin
|
88
|
+
* @param {string} enum('left', 'center', 'right') Horizontal origin
|
89
|
+
* @param {string} enum('top', 'center', 'bottom') Vertical origin
|
90
|
+
* @return {fabric.Point}
|
91
|
+
*/
|
92
|
+
// getPointByOrigin: function(originX, originY) {
|
93
|
+
// var center = this.getCenterPoint();
|
94
|
+
|
95
|
+
// return this.translateToOriginPoint(center, originX, originY);
|
96
|
+
// },
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Returns the point in local coordinates
|
100
|
+
* @method toLocalPoint
|
101
|
+
* @param {fabric.Point} The point relative to the global coordinate system
|
102
|
+
* @return {fabric.Point}
|
103
|
+
*/
|
104
|
+
toLocalPoint: function(point, originX, originY) {
|
105
|
+
var center = this.getCenterPoint();
|
106
|
+
|
107
|
+
var x, y;
|
108
|
+
if (originX !== undefined && originY !== undefined) {
|
109
|
+
if ( originX === "left" ) {
|
110
|
+
x = center.x - this.getWidth() / 2;
|
111
|
+
}
|
112
|
+
else if ( originX === "right" ) {
|
113
|
+
x = center.x + this.getWidth() / 2;
|
114
|
+
}
|
115
|
+
else {
|
116
|
+
x = center.x;
|
117
|
+
}
|
118
|
+
|
119
|
+
if ( originY === "top" ) {
|
120
|
+
y = center.y - this.getHeight() / 2;
|
121
|
+
}
|
122
|
+
else if ( originY === "bottom" ) {
|
123
|
+
y = center.y + this.getHeight() / 2;
|
124
|
+
}
|
125
|
+
else {
|
126
|
+
y = center.y;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
x = this.left;
|
131
|
+
y = this.top;
|
132
|
+
}
|
133
|
+
|
134
|
+
return fabric.util.rotatePoint(new fabric.Point(point.x, point.y), center, -degreesToRadians(this.angle)).subtractEquals(new fabric.Point(x, y));
|
135
|
+
},
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Returns the point in global coordinates
|
139
|
+
* @method toGlobalPoint
|
140
|
+
* @param {fabric.Point} The point relative to the local coordinate system
|
141
|
+
* @return {fabric.Point}
|
142
|
+
*/
|
143
|
+
// toGlobalPoint: function(point) {
|
144
|
+
// return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top));
|
145
|
+
// },
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Sets the position of the object taking into consideration the object's origin
|
149
|
+
* @method setPositionByOrigin
|
150
|
+
* @param {fabric.Point} point The new position of the object
|
151
|
+
* @param {string} enum('left', 'center', 'right') Horizontal origin
|
152
|
+
* @param {string} enum('top', 'center', 'bottom') Vertical origin
|
153
|
+
* @return {void}
|
154
|
+
*/
|
155
|
+
setPositionByOrigin: function(pos, originX, originY) {
|
156
|
+
var center = this.translateToCenterPoint(pos, originX, originY);
|
157
|
+
var position = this.translateToOriginPoint(center, this.originX, this.originY);
|
158
|
+
|
159
|
+
this.set('left', position.x);
|
160
|
+
this.set('top', position.y);
|
161
|
+
},
|
162
|
+
|
163
|
+
/**
|
164
|
+
* @method adjustPosition
|
165
|
+
* @param {String} to One of left, center, right
|
166
|
+
*/
|
167
|
+
adjustPosition: function(to) {
|
168
|
+
|
169
|
+
var angle = degreesToRadians(this.angle);
|
170
|
+
|
171
|
+
var hypotHalf = this.getWidth() / 2;
|
172
|
+
var xHalf = Math.cos(angle) * hypotHalf;
|
173
|
+
var yHalf = Math.sin(angle) * hypotHalf;
|
174
|
+
|
175
|
+
var hypotFull = this.getWidth();
|
176
|
+
var xFull = Math.cos(angle) * hypotFull;
|
177
|
+
var yFull = Math.sin(angle) * hypotFull;
|
178
|
+
|
179
|
+
if (this.originX === 'center' && to === 'left' ||
|
180
|
+
this.originX === 'right' && to === 'center') {
|
181
|
+
// move half left
|
182
|
+
this.left -= xHalf;
|
183
|
+
this.top -= yHalf;
|
184
|
+
}
|
185
|
+
else if (this.originX === 'left' && to === 'center' ||
|
186
|
+
this.originX === 'center' && to === 'right') {
|
187
|
+
// move half right
|
188
|
+
this.left += xHalf;
|
189
|
+
this.top += yHalf;
|
190
|
+
}
|
191
|
+
else if (this.originX === 'left' && to === 'right') {
|
192
|
+
// move full right
|
193
|
+
this.left += xFull;
|
194
|
+
this.top += yFull;
|
195
|
+
}
|
196
|
+
else if (this.originX === 'right' && to === 'left') {
|
197
|
+
// move full left
|
198
|
+
this.left -= xFull;
|
199
|
+
this.top -= yFull;
|
200
|
+
}
|
201
|
+
|
202
|
+
this.setCoords();
|
203
|
+
this.originX = to;
|
204
|
+
}
|
205
|
+
});
|
206
|
+
|
207
|
+
})();
|
@@ -0,0 +1,94 @@
|
|
1
|
+
fabric.util.object.extend(fabric.Object.prototype, /** @scope fabric.Object.prototype */ {
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @private
|
5
|
+
* @method _getAngleValueForStraighten
|
6
|
+
* @return {Number} angle value
|
7
|
+
*/
|
8
|
+
_getAngleValueForStraighten: function() {
|
9
|
+
var angle = this.getAngle() % 360;
|
10
|
+
if (angle > 0) {
|
11
|
+
return Math.round((angle-1)/90) * 90;
|
12
|
+
}
|
13
|
+
return Math.round(angle/90) * 90;
|
14
|
+
},
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer)
|
18
|
+
* @method straighten
|
19
|
+
* @return {fabric.Object} thisArg
|
20
|
+
* @chainable
|
21
|
+
*/
|
22
|
+
straighten: function() {
|
23
|
+
this.setAngle(this._getAngleValueForStraighten());
|
24
|
+
return this;
|
25
|
+
},
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Same as {@link fabric.Object.prototype.straghten} but with animation
|
29
|
+
* @method fxStraighten
|
30
|
+
* @param {Object} callbacks
|
31
|
+
* - onComplete: invoked on completion
|
32
|
+
* - onChange: invoked on every step of animation
|
33
|
+
*
|
34
|
+
* @return {fabric.Object} thisArg
|
35
|
+
* @chainable
|
36
|
+
*/
|
37
|
+
fxStraighten: function(callbacks) {
|
38
|
+
callbacks = callbacks || { };
|
39
|
+
|
40
|
+
var empty = function() { },
|
41
|
+
onComplete = callbacks.onComplete || empty,
|
42
|
+
onChange = callbacks.onChange || empty,
|
43
|
+
_this = this;
|
44
|
+
|
45
|
+
fabric.util.animate({
|
46
|
+
startValue: this.get('angle'),
|
47
|
+
endValue: this._getAngleValueForStraighten(),
|
48
|
+
duration: this.FX_DURATION,
|
49
|
+
onChange: function(value) {
|
50
|
+
_this.setAngle(value);
|
51
|
+
onChange();
|
52
|
+
},
|
53
|
+
onComplete: function() {
|
54
|
+
_this.setCoords();
|
55
|
+
onComplete();
|
56
|
+
},
|
57
|
+
onStart: function() {
|
58
|
+
_this.setActive(false);
|
59
|
+
}
|
60
|
+
});
|
61
|
+
|
62
|
+
return this;
|
63
|
+
}
|
64
|
+
});
|
65
|
+
|
66
|
+
fabric.util.object.extend(fabric.StaticCanvas.prototype, {
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Straightens object, then rerenders canvas
|
70
|
+
* @method straightenObject
|
71
|
+
* @param {fabric.Object} object Object to straighten
|
72
|
+
* @return {fabric.Canvas} thisArg
|
73
|
+
* @chainable
|
74
|
+
*/
|
75
|
+
straightenObject: function (object) {
|
76
|
+
object.straighten();
|
77
|
+
this.renderAll();
|
78
|
+
return this;
|
79
|
+
},
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Same as {@link fabric.Canvas.prototype.straightenObject}, but animated
|
83
|
+
* @method fxStraightenObject
|
84
|
+
* @param {fabric.Object} object Object to straighten
|
85
|
+
* @return {fabric.Canvas} thisArg
|
86
|
+
* @chainable
|
87
|
+
*/
|
88
|
+
fxStraightenObject: function (object) {
|
89
|
+
object.fxStraighten({
|
90
|
+
onChange: this.renderAll.bind(this)
|
91
|
+
});
|
92
|
+
return this;
|
93
|
+
}
|
94
|
+
});
|
@@ -0,0 +1,91 @@
|
|
1
|
+
/**
|
2
|
+
* @namespace
|
3
|
+
*/
|
4
|
+
fabric.Observable = {
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Observes specified event
|
8
|
+
* @method observe
|
9
|
+
* @depracated Since 0.8.34. Use `on` instead.
|
10
|
+
* @param {String} eventName
|
11
|
+
* @param {Function} handler
|
12
|
+
*/
|
13
|
+
observe: function(eventName, handler) {
|
14
|
+
if (!this.__eventListeners) {
|
15
|
+
this.__eventListeners = { };
|
16
|
+
}
|
17
|
+
// one object with key/value pairs was passed
|
18
|
+
if (arguments.length === 1) {
|
19
|
+
for (var prop in eventName) {
|
20
|
+
this.on(prop, eventName[prop]);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
else {
|
24
|
+
if (!this.__eventListeners[eventName]) {
|
25
|
+
this.__eventListeners[eventName] = [ ];
|
26
|
+
}
|
27
|
+
this.__eventListeners[eventName].push(handler);
|
28
|
+
}
|
29
|
+
},
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Stops event observing for a particular event handler
|
33
|
+
* @method stopObserving
|
34
|
+
* @depracated Since 0.8.34. Use `off` instead.
|
35
|
+
* @param {String} eventName
|
36
|
+
* @param {Function} handler
|
37
|
+
*/
|
38
|
+
stopObserving: function(eventName, handler) {
|
39
|
+
if (!this.__eventListeners) {
|
40
|
+
this.__eventListeners = { };
|
41
|
+
}
|
42
|
+
if (this.__eventListeners[eventName]) {
|
43
|
+
if (handler) {
|
44
|
+
fabric.util.removeFromArray(this.__eventListeners[eventName], handler);
|
45
|
+
}
|
46
|
+
else {
|
47
|
+
this.__eventListeners[eventName].length = 0;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
},
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Fires event with an optional options object
|
54
|
+
* @deprecated since 1.0.7
|
55
|
+
* @method fire
|
56
|
+
* @param {String} eventName
|
57
|
+
* @param {Object} [options]
|
58
|
+
*/
|
59
|
+
fire: function(eventName, options) {
|
60
|
+
if (!this.__eventListeners) {
|
61
|
+
this.__eventListeners = { };
|
62
|
+
}
|
63
|
+
var listenersForEvent = this.__eventListeners[eventName];
|
64
|
+
if (!listenersForEvent) return;
|
65
|
+
for (var i = 0, len = listenersForEvent.length; i < len; i++) {
|
66
|
+
// avoiding try/catch for perf. reasons
|
67
|
+
listenersForEvent[i](options || { });
|
68
|
+
}
|
69
|
+
}
|
70
|
+
};
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Alias for observe
|
74
|
+
* @method observe
|
75
|
+
* @type function
|
76
|
+
*/
|
77
|
+
fabric.Observable.on = fabric.Observable.observe;
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Alias for stopObserving
|
81
|
+
* @method off
|
82
|
+
* @type function
|
83
|
+
*/
|
84
|
+
fabric.Observable.off = fabric.Observable.stopObserving;
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Alias for fire
|
88
|
+
* @method trigger
|
89
|
+
* @type function
|
90
|
+
*/
|
91
|
+
fabric.Observable.trigger = fabric.Observable.fire;
|
@@ -0,0 +1,750 @@
|
|
1
|
+
(function(global) {
|
2
|
+
|
3
|
+
"use strict";
|
4
|
+
|
5
|
+
/**
|
6
|
+
* @name fabric
|
7
|
+
* @namespace
|
8
|
+
*/
|
9
|
+
|
10
|
+
var fabric = global.fabric || (global.fabric = { }),
|
11
|
+
extend = fabric.util.object.extend,
|
12
|
+
capitalize = fabric.util.string.capitalize,
|
13
|
+
clone = fabric.util.object.clone;
|
14
|
+
|
15
|
+
var attributesMap = {
|
16
|
+
'cx': 'left',
|
17
|
+
'x': 'left',
|
18
|
+
'cy': 'top',
|
19
|
+
'y': 'top',
|
20
|
+
'r': 'radius',
|
21
|
+
'fill-opacity': 'opacity',
|
22
|
+
'fill-rule': 'fillRule',
|
23
|
+
'stroke-width': 'strokeWidth',
|
24
|
+
'transform': 'transformMatrix',
|
25
|
+
'text-decoration': 'textDecoration',
|
26
|
+
'font-size': 'fontSize',
|
27
|
+
'font-weight': 'fontWeight',
|
28
|
+
'font-style': 'fontStyle',
|
29
|
+
'font-family': 'fontFamily'
|
30
|
+
};
|
31
|
+
|
32
|
+
function normalizeAttr(attr) {
|
33
|
+
// transform attribute names
|
34
|
+
if (attr in attributesMap) {
|
35
|
+
return attributesMap[attr];
|
36
|
+
}
|
37
|
+
return attr;
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Returns an object of attributes' name/value, given element and an array of attribute names;
|
42
|
+
* Parses parent "g" nodes recursively upwards.
|
43
|
+
* @static
|
44
|
+
* @memberOf fabric
|
45
|
+
* @method parseAttributes
|
46
|
+
* @param {DOMElement} element Element to parse
|
47
|
+
* @param {Array} attributes Array of attributes to parse
|
48
|
+
* @return {Object} object containing parsed attributes' names/values
|
49
|
+
*/
|
50
|
+
function parseAttributes(element, attributes) {
|
51
|
+
|
52
|
+
if (!element) {
|
53
|
+
return;
|
54
|
+
}
|
55
|
+
|
56
|
+
var value,
|
57
|
+
parsed,
|
58
|
+
parentAttributes = { };
|
59
|
+
|
60
|
+
// if there's a parent container (`g` node), parse its attributes recursively upwards
|
61
|
+
if (element.parentNode && /^g$/i.test(element.parentNode.nodeName)) {
|
62
|
+
parentAttributes = fabric.parseAttributes(element.parentNode, attributes);
|
63
|
+
}
|
64
|
+
|
65
|
+
var ownAttributes = attributes.reduce(function(memo, attr) {
|
66
|
+
value = element.getAttribute(attr);
|
67
|
+
parsed = parseFloat(value);
|
68
|
+
if (value) {
|
69
|
+
// "normalize" attribute values
|
70
|
+
if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
|
71
|
+
value = '';
|
72
|
+
}
|
73
|
+
if (attr === 'fill-rule') {
|
74
|
+
value = (value === 'evenodd') ? 'destination-over' : value;
|
75
|
+
}
|
76
|
+
if (attr === 'transform') {
|
77
|
+
value = fabric.parseTransformAttribute(value);
|
78
|
+
}
|
79
|
+
attr = normalizeAttr(attr);
|
80
|
+
memo[attr] = isNaN(parsed) ? value : parsed;
|
81
|
+
}
|
82
|
+
return memo;
|
83
|
+
}, { });
|
84
|
+
|
85
|
+
// add values parsed from style, which take precedence over attributes
|
86
|
+
// (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
|
87
|
+
|
88
|
+
ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
|
89
|
+
return extend(parentAttributes, ownAttributes);
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Parses "transform" attribute, returning an array of values
|
94
|
+
* @static
|
95
|
+
* @function
|
96
|
+
* @memberOf fabric
|
97
|
+
* @method parseTransformAttribute
|
98
|
+
* @param attributeValue {String} string containing attribute value
|
99
|
+
* @return {Array} array of 6 elements representing transformation matrix
|
100
|
+
*/
|
101
|
+
fabric.parseTransformAttribute = (function() {
|
102
|
+
function rotateMatrix(matrix, args) {
|
103
|
+
var angle = args[0];
|
104
|
+
|
105
|
+
matrix[0] = Math.cos(angle);
|
106
|
+
matrix[1] = Math.sin(angle);
|
107
|
+
matrix[2] = -Math.sin(angle);
|
108
|
+
matrix[3] = Math.cos(angle);
|
109
|
+
}
|
110
|
+
|
111
|
+
function scaleMatrix(matrix, args) {
|
112
|
+
var multiplierX = args[0],
|
113
|
+
multiplierY = (args.length === 2) ? args[1] : args[0];
|
114
|
+
|
115
|
+
matrix[0] = multiplierX;
|
116
|
+
matrix[3] = multiplierY;
|
117
|
+
}
|
118
|
+
|
119
|
+
function skewXMatrix(matrix, args) {
|
120
|
+
matrix[2] = args[0];
|
121
|
+
}
|
122
|
+
|
123
|
+
function skewYMatrix(matrix, args) {
|
124
|
+
matrix[1] = args[0];
|
125
|
+
}
|
126
|
+
|
127
|
+
function translateMatrix(matrix, args) {
|
128
|
+
matrix[4] = args[0];
|
129
|
+
if (args.length === 2) {
|
130
|
+
matrix[5] = args[1];
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
// identity matrix
|
135
|
+
var iMatrix = [
|
136
|
+
1, // a
|
137
|
+
0, // b
|
138
|
+
0, // c
|
139
|
+
1, // d
|
140
|
+
0, // e
|
141
|
+
0 // f
|
142
|
+
],
|
143
|
+
|
144
|
+
// == begin transform regexp
|
145
|
+
number = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)',
|
146
|
+
comma_wsp = '(?:\\s+,?\\s*|,\\s*)',
|
147
|
+
|
148
|
+
skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
|
149
|
+
skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
|
150
|
+
rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' + comma_wsp + '(' + number + ')' + comma_wsp + '(' + number + '))?\\s*\\))',
|
151
|
+
scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' + comma_wsp + '(' + number + '))?\\s*\\))',
|
152
|
+
translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' + comma_wsp + '(' + number + '))?\\s*\\))',
|
153
|
+
|
154
|
+
matrix = '(?:(matrix)\\s*\\(\\s*' +
|
155
|
+
'(' + number + ')' + comma_wsp +
|
156
|
+
'(' + number + ')' + comma_wsp +
|
157
|
+
'(' + number + ')' + comma_wsp +
|
158
|
+
'(' + number + ')' + comma_wsp +
|
159
|
+
'(' + number + ')' + comma_wsp +
|
160
|
+
'(' + number + ')' +
|
161
|
+
'\\s*\\))',
|
162
|
+
|
163
|
+
transform = '(?:' +
|
164
|
+
matrix + '|' +
|
165
|
+
translate + '|' +
|
166
|
+
scale + '|' +
|
167
|
+
rotate + '|' +
|
168
|
+
skewX + '|' +
|
169
|
+
skewY +
|
170
|
+
')',
|
171
|
+
|
172
|
+
transforms = '(?:' + transform + '(?:' + comma_wsp + transform + ')*' + ')',
|
173
|
+
|
174
|
+
transform_list = '^\\s*(?:' + transforms + '?)\\s*$',
|
175
|
+
|
176
|
+
// http://www.w3.org/TR/SVG/coords.html#TransformAttribute
|
177
|
+
reTransformList = new RegExp(transform_list),
|
178
|
+
// == end transform regexp
|
179
|
+
|
180
|
+
reTransform = new RegExp(transform);
|
181
|
+
|
182
|
+
return function(attributeValue) {
|
183
|
+
|
184
|
+
// start with identity matrix
|
185
|
+
var matrix = iMatrix.concat();
|
186
|
+
|
187
|
+
// return if no argument was given or
|
188
|
+
// an argument does not match transform attribute regexp
|
189
|
+
if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) {
|
190
|
+
return matrix;
|
191
|
+
}
|
192
|
+
|
193
|
+
attributeValue.replace(reTransform, function(match) {
|
194
|
+
|
195
|
+
var m = new RegExp(transform).exec(match).filter(function (match) {
|
196
|
+
return (match !== '' && match != null);
|
197
|
+
}),
|
198
|
+
operation = m[1],
|
199
|
+
args = m.slice(2).map(parseFloat);
|
200
|
+
|
201
|
+
switch(operation) {
|
202
|
+
case 'translate':
|
203
|
+
translateMatrix(matrix, args);
|
204
|
+
break;
|
205
|
+
case 'rotate':
|
206
|
+
rotateMatrix(matrix, args);
|
207
|
+
break;
|
208
|
+
case 'scale':
|
209
|
+
scaleMatrix(matrix, args);
|
210
|
+
break;
|
211
|
+
case 'skewX':
|
212
|
+
skewXMatrix(matrix, args);
|
213
|
+
break;
|
214
|
+
case 'skewY':
|
215
|
+
skewYMatrix(matrix, args);
|
216
|
+
break;
|
217
|
+
case 'matrix':
|
218
|
+
matrix = args;
|
219
|
+
break;
|
220
|
+
}
|
221
|
+
});
|
222
|
+
return matrix;
|
223
|
+
};
|
224
|
+
})();
|
225
|
+
|
226
|
+
/**
|
227
|
+
* Parses "points" attribute, returning an array of values
|
228
|
+
* @static
|
229
|
+
* @memberOf fabric
|
230
|
+
* @method parsePointsAttribute
|
231
|
+
* @param points {String} points attribute string
|
232
|
+
* @return {Array} array of points
|
233
|
+
*/
|
234
|
+
function parsePointsAttribute(points) {
|
235
|
+
|
236
|
+
// points attribute is required and must not be empty
|
237
|
+
if (!points) return null;
|
238
|
+
|
239
|
+
points = points.trim();
|
240
|
+
var asPairs = points.indexOf(',') > -1;
|
241
|
+
|
242
|
+
points = points.split(/\s+/);
|
243
|
+
var parsedPoints = [ ], i, len;
|
244
|
+
|
245
|
+
// points could look like "10,20 30,40" or "10 20 30 40"
|
246
|
+
if (asPairs) {
|
247
|
+
i = 0;
|
248
|
+
len = points.length;
|
249
|
+
for (; i < len; i++) {
|
250
|
+
var pair = points[i].split(',');
|
251
|
+
parsedPoints.push({ x: parseFloat(pair[0]), y: parseFloat(pair[1]) });
|
252
|
+
}
|
253
|
+
}
|
254
|
+
else {
|
255
|
+
i = 0;
|
256
|
+
len = points.length;
|
257
|
+
for (; i < len; i+=2) {
|
258
|
+
parsedPoints.push({ x: parseFloat(points[i]), y: parseFloat(points[i+1]) });
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
// odd number of points is an error
|
263
|
+
if (parsedPoints.length % 2 !== 0) {
|
264
|
+
// return null;
|
265
|
+
}
|
266
|
+
|
267
|
+
return parsedPoints;
|
268
|
+
}
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Parses "style" attribute, retuning an object with values
|
272
|
+
* @static
|
273
|
+
* @memberOf fabric
|
274
|
+
* @method parseStyleAttribute
|
275
|
+
* @param {SVGElement} element Element to parse
|
276
|
+
* @return {Object} Objects with values parsed from style attribute of an element
|
277
|
+
*/
|
278
|
+
function parseStyleAttribute(element) {
|
279
|
+
var oStyle = { },
|
280
|
+
style = element.getAttribute('style');
|
281
|
+
|
282
|
+
if (!style) return oStyle;
|
283
|
+
|
284
|
+
if (typeof style === 'string') {
|
285
|
+
style = style.replace(/;$/, '').split(';').forEach(function (current) {
|
286
|
+
|
287
|
+
var attr = current.split(':');
|
288
|
+
var value = attr[1].trim();
|
289
|
+
|
290
|
+
// TODO: need to normalize em, %, pt, etc. to px (!)
|
291
|
+
var parsed = parseFloat(value);
|
292
|
+
|
293
|
+
oStyle[normalizeAttr(attr[0].trim().toLowerCase())] = isNaN(parsed) ? value : parsed;
|
294
|
+
});
|
295
|
+
}
|
296
|
+
else {
|
297
|
+
for (var prop in style) {
|
298
|
+
if (typeof style[prop] === 'undefined') continue;
|
299
|
+
|
300
|
+
var parsed = parseFloat(style[prop]);
|
301
|
+
oStyle[normalizeAttr(prop.toLowerCase())] = isNaN(parsed) ? style[prop] : parsed;
|
302
|
+
}
|
303
|
+
}
|
304
|
+
|
305
|
+
return oStyle;
|
306
|
+
}
|
307
|
+
|
308
|
+
function resolveGradients(instances) {
|
309
|
+
for (var i = instances.length; i--; ) {
|
310
|
+
var instanceFillValue = instances[i].get('fill');
|
311
|
+
|
312
|
+
if (/^url\(/.test(instanceFillValue)) {
|
313
|
+
|
314
|
+
var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
|
315
|
+
|
316
|
+
if (fabric.gradientDefs[gradientId]) {
|
317
|
+
instances[i].set('fill',
|
318
|
+
fabric.Gradient.fromElement(fabric.gradientDefs[gradientId], instances[i]));
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
/**
|
325
|
+
* Transforms an array of svg elements to corresponding fabric.* instances
|
326
|
+
* @static
|
327
|
+
* @memberOf fabric
|
328
|
+
* @method parseElements
|
329
|
+
* @param {Array} elements Array of elements to parse
|
330
|
+
* @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)
|
331
|
+
* @param {Object} [options] Options object
|
332
|
+
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
|
333
|
+
*/
|
334
|
+
function parseElements(elements, callback, options, reviver) {
|
335
|
+
var instances = new Array(elements.length), i = elements.length;
|
336
|
+
|
337
|
+
function checkIfDone() {
|
338
|
+
if (--i === 0) {
|
339
|
+
instances = instances.filter(function(el) {
|
340
|
+
return el != null;
|
341
|
+
});
|
342
|
+
resolveGradients(instances);
|
343
|
+
callback(instances);
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
for (var index = 0, el, len = elements.length; index < len; index++) {
|
348
|
+
el = elements[index];
|
349
|
+
var klass = fabric[capitalize(el.tagName)];
|
350
|
+
if (klass && klass.fromElement) {
|
351
|
+
try {
|
352
|
+
if (klass.async) {
|
353
|
+
klass.fromElement(el, (function(index, el) {
|
354
|
+
return function(obj) {
|
355
|
+
reviver && reviver(el, obj);
|
356
|
+
instances.splice(index, 0, obj);
|
357
|
+
checkIfDone();
|
358
|
+
};
|
359
|
+
})(index), options);
|
360
|
+
}
|
361
|
+
else {
|
362
|
+
var obj = klass.fromElement(el, options);
|
363
|
+
reviver && reviver(el, obj);
|
364
|
+
instances.splice(index, 0, obj);
|
365
|
+
checkIfDone();
|
366
|
+
}
|
367
|
+
}
|
368
|
+
catch(err) {
|
369
|
+
fabric.log(err);
|
370
|
+
}
|
371
|
+
}
|
372
|
+
else {
|
373
|
+
checkIfDone();
|
374
|
+
}
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
/**
|
379
|
+
* Returns CSS rules for a given SVG document
|
380
|
+
* @static
|
381
|
+
* @function
|
382
|
+
* @memberOf fabric
|
383
|
+
* @method getCSSRules
|
384
|
+
* @param {SVGDocument} doc SVG document to parse
|
385
|
+
* @return {Object} CSS rules of this document
|
386
|
+
*/
|
387
|
+
function getCSSRules(doc) {
|
388
|
+
var styles = doc.getElementsByTagName('style'),
|
389
|
+
allRules = { },
|
390
|
+
rules;
|
391
|
+
|
392
|
+
// very crude parsing of style contents
|
393
|
+
for (var i = 0, len = styles.length; i < len; i++) {
|
394
|
+
var styleContents = styles[0].textContent;
|
395
|
+
|
396
|
+
// remove comments
|
397
|
+
styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
|
398
|
+
|
399
|
+
rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
|
400
|
+
rules = rules.map(function(rule) { return rule.trim(); });
|
401
|
+
|
402
|
+
rules.forEach(function(rule) {
|
403
|
+
var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/);
|
404
|
+
rule = match[1];
|
405
|
+
var declaration = match[2].trim(),
|
406
|
+
propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
|
407
|
+
|
408
|
+
if (!allRules[rule]) {
|
409
|
+
allRules[rule] = { };
|
410
|
+
}
|
411
|
+
|
412
|
+
for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
|
413
|
+
var pair = propertyValuePairs[i].split(/\s*:\s*/),
|
414
|
+
property = pair[0],
|
415
|
+
value = pair[1];
|
416
|
+
|
417
|
+
allRules[rule][property] = value;
|
418
|
+
}
|
419
|
+
});
|
420
|
+
}
|
421
|
+
|
422
|
+
return allRules;
|
423
|
+
}
|
424
|
+
|
425
|
+
/**
|
426
|
+
* @private
|
427
|
+
*/
|
428
|
+
function getGlobalStylesForElement(element) {
|
429
|
+
var nodeName = element.nodeName,
|
430
|
+
className = element.getAttribute('class'),
|
431
|
+
id = element.getAttribute('id'),
|
432
|
+
styles = { };
|
433
|
+
|
434
|
+
for (var rule in fabric.cssRules) {
|
435
|
+
var ruleMatchesElement = (className && new RegExp('^\\.' + className).test(rule)) ||
|
436
|
+
(id && new RegExp('^#' + id).test(rule)) ||
|
437
|
+
(new RegExp('^' + nodeName).test(rule));
|
438
|
+
|
439
|
+
if (ruleMatchesElement) {
|
440
|
+
for (var property in fabric.cssRules[rule]) {
|
441
|
+
styles[property] = fabric.cssRules[rule][property];
|
442
|
+
}
|
443
|
+
}
|
444
|
+
}
|
445
|
+
|
446
|
+
return styles;
|
447
|
+
}
|
448
|
+
|
449
|
+
/**
|
450
|
+
* Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
|
451
|
+
* @static
|
452
|
+
* @function
|
453
|
+
* @memberOf fabric
|
454
|
+
* @method parseSVGDocument
|
455
|
+
* @param {SVGDocument} doc SVG document to parse
|
456
|
+
* @param {Function} callback Callback to call when parsing is finished; It's being passed an array of elements (parsed from a document).
|
457
|
+
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
|
458
|
+
*/
|
459
|
+
fabric.parseSVGDocument = (function() {
|
460
|
+
|
461
|
+
var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/;
|
462
|
+
|
463
|
+
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
464
|
+
// \d doesn't quite cut it (as we need to match an actual float number)
|
465
|
+
|
466
|
+
// matches, e.g.: +14.56e-12, etc.
|
467
|
+
var reNum = '(?:[-+]?\\d+(?:\\.\\d+)?(?:e[-+]?\\d+)?)';
|
468
|
+
|
469
|
+
var reViewBoxAttrValue = new RegExp(
|
470
|
+
'^' +
|
471
|
+
'\\s*(' + reNum + '+)\\s*,?' +
|
472
|
+
'\\s*(' + reNum + '+)\\s*,?' +
|
473
|
+
'\\s*(' + reNum + '+)\\s*,?' +
|
474
|
+
'\\s*(' + reNum + '+)\\s*' +
|
475
|
+
'$'
|
476
|
+
);
|
477
|
+
|
478
|
+
function hasAncestorWithNodeName(element, nodeName) {
|
479
|
+
while (element && (element = element.parentNode)) {
|
480
|
+
if (nodeName.test(element.nodeName)) {
|
481
|
+
return true;
|
482
|
+
}
|
483
|
+
}
|
484
|
+
return false;
|
485
|
+
}
|
486
|
+
|
487
|
+
return function(doc, callback, reviver) {
|
488
|
+
if (!doc) return;
|
489
|
+
|
490
|
+
var startTime = new Date(),
|
491
|
+
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
|
492
|
+
|
493
|
+
if (descendants.length === 0) {
|
494
|
+
// we're likely in node, where "o3-xml" library fails to gEBTN("*")
|
495
|
+
// https://github.com/ajaxorg/node-o3-xml/issues/21
|
496
|
+
descendants = doc.selectNodes("//*[name(.)!='svg']");
|
497
|
+
var arr = [ ];
|
498
|
+
for (var i = 0, len = descendants.length; i < len; i++) {
|
499
|
+
arr[i] = descendants[i];
|
500
|
+
}
|
501
|
+
descendants = arr;
|
502
|
+
}
|
503
|
+
|
504
|
+
var elements = descendants.filter(function(el) {
|
505
|
+
return reAllowedSVGTagNames.test(el.tagName) &&
|
506
|
+
!hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
|
507
|
+
});
|
508
|
+
|
509
|
+
if (!elements || (elements && !elements.length)) return;
|
510
|
+
|
511
|
+
var viewBoxAttr = doc.getAttribute('viewBox'),
|
512
|
+
widthAttr = doc.getAttribute('width'),
|
513
|
+
heightAttr = doc.getAttribute('height'),
|
514
|
+
width = null,
|
515
|
+
height = null,
|
516
|
+
minX,
|
517
|
+
minY;
|
518
|
+
|
519
|
+
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
|
520
|
+
minX = parseInt(viewBoxAttr[1], 10);
|
521
|
+
minY = parseInt(viewBoxAttr[2], 10);
|
522
|
+
width = parseInt(viewBoxAttr[3], 10);
|
523
|
+
height = parseInt(viewBoxAttr[4], 10);
|
524
|
+
}
|
525
|
+
|
526
|
+
// values of width/height attributes overwrite those extracted from viewbox attribute
|
527
|
+
width = widthAttr ? parseFloat(widthAttr) : width;
|
528
|
+
height = heightAttr ? parseFloat(heightAttr) : height;
|
529
|
+
|
530
|
+
var options = {
|
531
|
+
width: width,
|
532
|
+
height: height
|
533
|
+
};
|
534
|
+
|
535
|
+
fabric.gradientDefs = fabric.getGradientDefs(doc);
|
536
|
+
fabric.cssRules = getCSSRules(doc);
|
537
|
+
|
538
|
+
// Precedence of rules: style > class > attribute
|
539
|
+
|
540
|
+
fabric.parseElements(elements, function(instances) {
|
541
|
+
fabric.documentParsingTime = new Date() - startTime;
|
542
|
+
if (callback) {
|
543
|
+
callback(instances, options);
|
544
|
+
}
|
545
|
+
}, clone(options), reviver);
|
546
|
+
};
|
547
|
+
})();
|
548
|
+
|
549
|
+
/**
|
550
|
+
* Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
|
551
|
+
* @property
|
552
|
+
* @namespace
|
553
|
+
*/
|
554
|
+
var svgCache = {
|
555
|
+
|
556
|
+
/**
|
557
|
+
* @method has
|
558
|
+
* @param {String} name
|
559
|
+
* @param {Function} callback
|
560
|
+
*/
|
561
|
+
has: function (name, callback) {
|
562
|
+
callback(false);
|
563
|
+
},
|
564
|
+
|
565
|
+
/**
|
566
|
+
* @method get
|
567
|
+
* @param {String} url
|
568
|
+
* @param {Function} callback
|
569
|
+
*/
|
570
|
+
get: function () {
|
571
|
+
/* NOOP */
|
572
|
+
},
|
573
|
+
|
574
|
+
/**
|
575
|
+
* @method set
|
576
|
+
* @param {String} url
|
577
|
+
* @param {Object} object
|
578
|
+
*/
|
579
|
+
set: function () {
|
580
|
+
/* NOOP */
|
581
|
+
}
|
582
|
+
};
|
583
|
+
|
584
|
+
/**
|
585
|
+
* Takes url corresponding to an SVG document, and parses it into a set of fabric objects
|
586
|
+
* @method loadSVGFromURL
|
587
|
+
* @memberof fabric
|
588
|
+
* @param {String} url
|
589
|
+
* @param {Function} callback
|
590
|
+
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
|
591
|
+
*/
|
592
|
+
function loadSVGFromURL(url, callback, reviver) {
|
593
|
+
|
594
|
+
url = url.replace(/^\n\s*/, '').trim();
|
595
|
+
|
596
|
+
svgCache.has(url, function (hasUrl) {
|
597
|
+
if (hasUrl) {
|
598
|
+
svgCache.get(url, function (value) {
|
599
|
+
var enlivedRecord = _enlivenCachedObject(value);
|
600
|
+
callback(enlivedRecord.objects, enlivedRecord.options);
|
601
|
+
});
|
602
|
+
}
|
603
|
+
else {
|
604
|
+
new fabric.util.request(url, {
|
605
|
+
method: 'get',
|
606
|
+
onComplete: onComplete
|
607
|
+
});
|
608
|
+
}
|
609
|
+
});
|
610
|
+
|
611
|
+
function onComplete(r) {
|
612
|
+
|
613
|
+
var xml = r.responseXML;
|
614
|
+
if (!xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
|
615
|
+
xml = new ActiveXObject('Microsoft.XMLDOM');
|
616
|
+
xml.async = 'false';
|
617
|
+
//IE chokes on DOCTYPE
|
618
|
+
xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,''));
|
619
|
+
}
|
620
|
+
if (!xml.documentElement) return;
|
621
|
+
|
622
|
+
fabric.parseSVGDocument(xml.documentElement, function (results, options) {
|
623
|
+
svgCache.set(url, {
|
624
|
+
objects: fabric.util.array.invoke(results, 'toObject'),
|
625
|
+
options: options
|
626
|
+
});
|
627
|
+
callback(results, options);
|
628
|
+
}, reviver);
|
629
|
+
}
|
630
|
+
}
|
631
|
+
|
632
|
+
/**
|
633
|
+
* @method _enlivenCachedObject
|
634
|
+
*/
|
635
|
+
function _enlivenCachedObject(cachedObject) {
|
636
|
+
|
637
|
+
var objects = cachedObject.objects,
|
638
|
+
options = cachedObject.options;
|
639
|
+
|
640
|
+
objects = objects.map(function (o) {
|
641
|
+
return fabric[capitalize(o.type)].fromObject(o);
|
642
|
+
});
|
643
|
+
|
644
|
+
return ({ objects: objects, options: options });
|
645
|
+
}
|
646
|
+
|
647
|
+
/**
|
648
|
+
* Takes string corresponding to an SVG document, and parses it into a set of fabric objects
|
649
|
+
* @method loadSVGFromString
|
650
|
+
* @memberof fabric
|
651
|
+
* @param {String} string
|
652
|
+
* @param {Function} callback
|
653
|
+
* @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
|
654
|
+
*/
|
655
|
+
function loadSVGFromString(string, callback, reviver) {
|
656
|
+
string = string.trim();
|
657
|
+
var doc;
|
658
|
+
if (typeof DOMParser !== 'undefined') {
|
659
|
+
var parser = new DOMParser();
|
660
|
+
if (parser && parser.parseFromString) {
|
661
|
+
doc = parser.parseFromString(string, 'text/xml');
|
662
|
+
}
|
663
|
+
}
|
664
|
+
else if (fabric.window.ActiveXObject) {
|
665
|
+
doc = new ActiveXObject('Microsoft.XMLDOM');
|
666
|
+
doc.async = 'false';
|
667
|
+
//IE chokes on DOCTYPE
|
668
|
+
doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,''));
|
669
|
+
}
|
670
|
+
|
671
|
+
fabric.parseSVGDocument(doc.documentElement, function (results, options) {
|
672
|
+
callback(results, options);
|
673
|
+
}, reviver);
|
674
|
+
}
|
675
|
+
|
676
|
+
/**
|
677
|
+
* Creates markup containing SVG font faces
|
678
|
+
* @method createSVGFontFacesMarkup
|
679
|
+
* @param {Array} objects Array of fabric objects
|
680
|
+
* @return {String}
|
681
|
+
*/
|
682
|
+
function createSVGFontFacesMarkup(objects) {
|
683
|
+
var markup = '';
|
684
|
+
|
685
|
+
for (var i = 0, len = objects.length; i < len; i++) {
|
686
|
+
if (objects[i].type !== 'text' || !objects[i].path) continue;
|
687
|
+
|
688
|
+
markup += [
|
689
|
+
'@font-face {',
|
690
|
+
'font-family: ', objects[i].fontFamily, '; ',
|
691
|
+
'src: url(\'', objects[i].path, '\')',
|
692
|
+
'}'
|
693
|
+
].join('');
|
694
|
+
}
|
695
|
+
|
696
|
+
if (markup) {
|
697
|
+
markup = [
|
698
|
+
'<style type="text/css">',
|
699
|
+
'<![CDATA[',
|
700
|
+
markup,
|
701
|
+
']]>',
|
702
|
+
'</style>'
|
703
|
+
].join('');
|
704
|
+
}
|
705
|
+
|
706
|
+
return markup;
|
707
|
+
}
|
708
|
+
|
709
|
+
/**
|
710
|
+
* Creates markup containing SVG referenced elements like patterns, gradients etc.
|
711
|
+
* @method createSVGRefElementsMarkup
|
712
|
+
* @param {fabric.Canvas} canvas instance of fabric.Canvas
|
713
|
+
* @return {String}
|
714
|
+
*/
|
715
|
+
function createSVGRefElementsMarkup(canvas) {
|
716
|
+
var markup = '';
|
717
|
+
|
718
|
+
if (canvas.backgroundColor && canvas.backgroundColor.source) {
|
719
|
+
markup = [
|
720
|
+
'<pattern x="0" y="0" id="backgroundColorPattern" ',
|
721
|
+
'width="', canvas.backgroundColor.source.width,
|
722
|
+
'" height="', canvas.backgroundColor.source.height,
|
723
|
+
'" patternUnits="userSpaceOnUse">',
|
724
|
+
'<image x="0" y="0" ',
|
725
|
+
'width="', canvas.backgroundColor.source.width,
|
726
|
+
'" height="', canvas.backgroundColor.source.height,
|
727
|
+
'" xlink:href="', canvas.backgroundColor.source.src,
|
728
|
+
'"></image></pattern>'
|
729
|
+
].join('');
|
730
|
+
}
|
731
|
+
|
732
|
+
return markup;
|
733
|
+
}
|
734
|
+
|
735
|
+
extend(fabric, {
|
736
|
+
|
737
|
+
parseAttributes: parseAttributes,
|
738
|
+
parseElements: parseElements,
|
739
|
+
parseStyleAttribute: parseStyleAttribute,
|
740
|
+
parsePointsAttribute: parsePointsAttribute,
|
741
|
+
getCSSRules: getCSSRules,
|
742
|
+
|
743
|
+
loadSVGFromURL: loadSVGFromURL,
|
744
|
+
loadSVGFromString: loadSVGFromString,
|
745
|
+
|
746
|
+
createSVGFontFacesMarkup: createSVGFontFacesMarkup,
|
747
|
+
createSVGRefElementsMarkup: createSVGRefElementsMarkup
|
748
|
+
});
|
749
|
+
|
750
|
+
})(typeof exports !== 'undefined' ? exports : this);
|