fabric-rails 1.0.12 → 1.0.12.1

Sign up to get free protection for your applications and to get access to all the features.
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);