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.
Files changed (56) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/README.md +1 -1
  3. data/lib/fabric/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/event.js +1909 -0
  5. data/vendor/assets/javascripts/fabric.js +64 -16464
  6. data/vendor/assets/javascripts/fabric/HEADER.js +31 -0
  7. data/vendor/assets/javascripts/fabric/canvas.class.js +1007 -0
  8. data/vendor/assets/javascripts/fabric/canvas_animation.mixin.js +113 -0
  9. data/vendor/assets/javascripts/fabric/canvas_events.mixin.js +482 -0
  10. data/vendor/assets/javascripts/fabric/canvas_gestures.mixin.js +79 -0
  11. data/vendor/assets/javascripts/fabric/canvas_serialization.mixin.js +311 -0
  12. data/vendor/assets/javascripts/fabric/circle.class.js +182 -0
  13. data/vendor/assets/javascripts/fabric/color.class.js +284 -0
  14. data/vendor/assets/javascripts/fabric/ellipse.class.js +169 -0
  15. data/vendor/assets/javascripts/fabric/freedrawing.class.js +256 -0
  16. data/vendor/assets/javascripts/fabric/gradient.class.js +211 -0
  17. data/vendor/assets/javascripts/fabric/group.class.js +556 -0
  18. data/vendor/assets/javascripts/fabric/image.class.js +418 -0
  19. data/vendor/assets/javascripts/fabric/image_filters.js +809 -0
  20. data/vendor/assets/javascripts/fabric/intersection.class.js +178 -0
  21. data/vendor/assets/javascripts/fabric/line.class.js +188 -0
  22. data/vendor/assets/javascripts/fabric/log.js +26 -0
  23. data/vendor/assets/javascripts/fabric/node.js +149 -0
  24. data/vendor/assets/javascripts/fabric/object.class.js +1068 -0
  25. data/vendor/assets/javascripts/fabric/object_geometry.mixin.js +308 -0
  26. data/vendor/assets/javascripts/fabric/object_interactivity.mixin.js +496 -0
  27. data/vendor/assets/javascripts/fabric/object_origin.mixin.js +207 -0
  28. data/vendor/assets/javascripts/fabric/object_straightening.mixin.js +94 -0
  29. data/vendor/assets/javascripts/fabric/observable.mixin.js +91 -0
  30. data/vendor/assets/javascripts/fabric/parser.js +750 -0
  31. data/vendor/assets/javascripts/fabric/path.class.js +794 -0
  32. data/vendor/assets/javascripts/fabric/path_group.class.js +240 -0
  33. data/vendor/assets/javascripts/fabric/pattern.class.js +69 -0
  34. data/vendor/assets/javascripts/fabric/point.class.js +327 -0
  35. data/vendor/assets/javascripts/fabric/polygon.class.js +184 -0
  36. data/vendor/assets/javascripts/fabric/polyline.class.js +157 -0
  37. data/vendor/assets/javascripts/fabric/rect.class.js +298 -0
  38. data/vendor/assets/javascripts/fabric/scout.js +45 -0
  39. data/vendor/assets/javascripts/fabric/shadow.class.js +70 -0
  40. data/vendor/assets/javascripts/fabric/stateful.js +88 -0
  41. data/vendor/assets/javascripts/fabric/static_canvas.class.js +1298 -0
  42. data/vendor/assets/javascripts/fabric/text.class.js +934 -0
  43. data/vendor/assets/javascripts/fabric/triangle.class.js +108 -0
  44. data/vendor/assets/javascripts/fabric/util/anim_ease.js +360 -0
  45. data/vendor/assets/javascripts/fabric/util/dom_event.js +237 -0
  46. data/vendor/assets/javascripts/fabric/util/dom_misc.js +245 -0
  47. data/vendor/assets/javascripts/fabric/util/dom_request.js +72 -0
  48. data/vendor/assets/javascripts/fabric/util/dom_style.js +71 -0
  49. data/vendor/assets/javascripts/fabric/util/lang_array.js +250 -0
  50. data/vendor/assets/javascripts/fabric/util/lang_class.js +97 -0
  51. data/vendor/assets/javascripts/fabric/util/lang_function.js +35 -0
  52. data/vendor/assets/javascripts/fabric/util/lang_object.js +34 -0
  53. data/vendor/assets/javascripts/fabric/util/lang_string.js +60 -0
  54. data/vendor/assets/javascripts/fabric/util/misc.js +406 -0
  55. data/vendor/assets/javascripts/json2.js +491 -0
  56. 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);