fabric-rails 1.0.12.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/CHANGELOG.md +4 -0
  2. data/README.md +1 -1
  3. data/lib/fabric/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/cufon.js +1226 -0
  5. data/vendor/assets/javascripts/event.js +20 -20
  6. data/vendor/assets/javascripts/excanvas.js +1464 -0
  7. data/vendor/assets/javascripts/fabric.js +56 -33
  8. data/vendor/assets/javascripts/fabric/HEADER.js +2 -4
  9. data/vendor/assets/javascripts/fabric/brushes/base_brush.class.js +96 -0
  10. data/vendor/assets/javascripts/fabric/brushes/circle_brush.class.js +99 -0
  11. data/vendor/assets/javascripts/fabric/brushes/pattern_brush.class.js +55 -0
  12. data/vendor/assets/javascripts/fabric/{freedrawing.class.js → brushes/pencil_brush.class.js} +73 -65
  13. data/vendor/assets/javascripts/fabric/brushes/spray_brush.class.js +157 -0
  14. data/vendor/assets/javascripts/fabric/canvas.class.js +154 -135
  15. data/vendor/assets/javascripts/fabric/color.class.js +195 -29
  16. data/vendor/assets/javascripts/fabric/filters/brightness_filter.class.js +70 -0
  17. data/vendor/assets/javascripts/fabric/filters/convolute_filter.class.js +122 -0
  18. data/vendor/assets/javascripts/fabric/filters/gradienttransparency_filter.class.js +69 -0
  19. data/vendor/assets/javascripts/fabric/filters/grayscale_filter.class.js +61 -0
  20. data/vendor/assets/javascripts/fabric/filters/invert_filter.class.js +57 -0
  21. data/vendor/assets/javascripts/fabric/filters/noise_filter.class.js +73 -0
  22. data/vendor/assets/javascripts/fabric/filters/pixelate_filter.class.js +98 -0
  23. data/vendor/assets/javascripts/fabric/filters/removewhite_filter.class.js +86 -0
  24. data/vendor/assets/javascripts/fabric/filters/sepia2_filter.class.js +61 -0
  25. data/vendor/assets/javascripts/fabric/filters/sepia_filter.class.js +58 -0
  26. data/vendor/assets/javascripts/fabric/filters/tint_filter.class.js +80 -0
  27. data/vendor/assets/javascripts/fabric/gradient.class.js +232 -80
  28. data/vendor/assets/javascripts/fabric/intersection.class.js +10 -28
  29. data/vendor/assets/javascripts/fabric/log.js +0 -2
  30. data/vendor/assets/javascripts/fabric/{canvas_animation.mixin.js → mixins/canvas_animation.mixin.js} +3 -6
  31. data/vendor/assets/javascripts/fabric/mixins/canvas_dataurl_exporter.mixin.js +137 -0
  32. data/vendor/assets/javascripts/fabric/{canvas_events.mixin.js → mixins/canvas_events.mixin.js} +97 -144
  33. data/vendor/assets/javascripts/fabric/{canvas_gestures.mixin.js → mixins/canvas_gestures.mixin.js} +4 -8
  34. data/vendor/assets/javascripts/fabric/{canvas_serialization.mixin.js → mixins/canvas_serialization.mixin.js} +19 -14
  35. data/vendor/assets/javascripts/fabric/mixins/collection.mixin.js +137 -0
  36. data/vendor/assets/javascripts/fabric/{object_geometry.mixin.js → mixins/object_geometry.mixin.js} +130 -47
  37. data/vendor/assets/javascripts/fabric/{object_interactivity.mixin.js → mixins/object_interactivity.mixin.js} +21 -102
  38. data/vendor/assets/javascripts/fabric/{object_origin.mixin.js → mixins/object_origin.mixin.js} +36 -26
  39. data/vendor/assets/javascripts/fabric/{object_straightening.mixin.js → mixins/object_straightening.mixin.js} +4 -9
  40. data/vendor/assets/javascripts/fabric/{observable.mixin.js → mixins/observable.mixin.js} +27 -35
  41. data/vendor/assets/javascripts/fabric/mixins/stateful.mixin.js +45 -0
  42. data/vendor/assets/javascripts/fabric/node.js +62 -26
  43. data/vendor/assets/javascripts/fabric/parser.js +181 -58
  44. data/vendor/assets/javascripts/fabric/pattern.class.js +43 -14
  45. data/vendor/assets/javascripts/fabric/point.class.js +4 -43
  46. data/vendor/assets/javascripts/fabric/shadow.class.js +19 -19
  47. data/vendor/assets/javascripts/fabric/{circle.class.js → shapes/circle.class.js} +32 -29
  48. data/vendor/assets/javascripts/fabric/{ellipse.class.js → shapes/ellipse.class.js} +45 -27
  49. data/vendor/assets/javascripts/fabric/{group.class.js → shapes/group.class.js} +67 -169
  50. data/vendor/assets/javascripts/fabric/{image.class.js → shapes/image.class.js} +134 -72
  51. data/vendor/assets/javascripts/fabric/{line.class.js → shapes/line.class.js} +67 -36
  52. data/vendor/assets/javascripts/fabric/{object.class.js → shapes/object.class.js} +394 -252
  53. data/vendor/assets/javascripts/fabric/{path.class.js → shapes/path.class.js} +89 -174
  54. data/vendor/assets/javascripts/fabric/{path_group.class.js → shapes/path_group.class.js} +12 -18
  55. data/vendor/assets/javascripts/fabric/{polygon.class.js → shapes/polygon.class.js} +64 -38
  56. data/vendor/assets/javascripts/fabric/{polyline.class.js → shapes/polyline.class.js} +64 -39
  57. data/vendor/assets/javascripts/fabric/{rect.class.js → shapes/rect.class.js} +78 -112
  58. data/vendor/assets/javascripts/fabric/{text.class.js → shapes/text.class.js} +264 -270
  59. data/vendor/assets/javascripts/fabric/shapes/text.cufon.js +79 -0
  60. data/vendor/assets/javascripts/fabric/{triangle.class.js → shapes/triangle.class.js} +46 -26
  61. data/vendor/assets/javascripts/fabric/static_canvas.class.js +134 -358
  62. data/vendor/assets/javascripts/fabric/util/anim_ease.js +2 -31
  63. data/vendor/assets/javascripts/fabric/util/dom_event.js +21 -7
  64. data/vendor/assets/javascripts/fabric/util/dom_misc.js +49 -39
  65. data/vendor/assets/javascripts/fabric/util/dom_request.js +1 -2
  66. data/vendor/assets/javascripts/fabric/util/dom_style.js +1 -2
  67. data/vendor/assets/javascripts/fabric/util/lang_array.js +19 -13
  68. data/vendor/assets/javascripts/fabric/util/lang_class.js +1 -2
  69. data/vendor/assets/javascripts/fabric/util/lang_function.js +3 -1
  70. data/vendor/assets/javascripts/fabric/util/lang_object.js +5 -5
  71. data/vendor/assets/javascripts/fabric/util/lang_string.js +7 -5
  72. data/vendor/assets/javascripts/fabric/util/misc.js +207 -42
  73. metadata +47 -29
  74. data/vendor/assets/javascripts/fabric/image_filters.js +0 -809
  75. data/vendor/assets/javascripts/fabric/scout.js +0 -45
  76. data/vendor/assets/javascripts/fabric/stateful.js +0 -88
@@ -1,16 +1,14 @@
1
- /**
2
- * @namespace
3
- */
4
- fabric.Observable = {
1
+ (function(){
5
2
 
6
3
  /**
7
4
  * Observes specified event
8
- * @method observe
9
- * @depracated Since 0.8.34. Use `on` instead.
5
+ * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
6
+ * @memberOf fabric.Observable
7
+ * @alias on
10
8
  * @param {String} eventName
11
9
  * @param {Function} handler
12
10
  */
13
- observe: function(eventName, handler) {
11
+ function observe(eventName, handler) {
14
12
  if (!this.__eventListeners) {
15
13
  this.__eventListeners = { };
16
14
  }
@@ -26,16 +24,17 @@ fabric.Observable = {
26
24
  }
27
25
  this.__eventListeners[eventName].push(handler);
28
26
  }
29
- },
27
+ }
30
28
 
31
29
  /**
32
30
  * Stops event observing for a particular event handler
33
- * @method stopObserving
34
- * @depracated Since 0.8.34. Use `off` instead.
31
+ * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
32
+ * @memberOf fabric.Observable
33
+ * @alias off
35
34
  * @param {String} eventName
36
35
  * @param {Function} handler
37
36
  */
38
- stopObserving: function(eventName, handler) {
37
+ function stopObserving(eventName, handler) {
39
38
  if (!this.__eventListeners) {
40
39
  this.__eventListeners = { };
41
40
  }
@@ -47,16 +46,17 @@ fabric.Observable = {
47
46
  this.__eventListeners[eventName].length = 0;
48
47
  }
49
48
  }
50
- },
49
+ }
51
50
 
52
51
  /**
53
52
  * Fires event with an optional options object
54
- * @deprecated since 1.0.7
55
- * @method fire
53
+ * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
54
+ * @memberOf fabric.Observable
55
+ * @alias trigger
56
56
  * @param {String} eventName
57
57
  * @param {Object} [options]
58
58
  */
59
- fire: function(eventName, options) {
59
+ function fire(eventName, options) {
60
60
  if (!this.__eventListeners) {
61
61
  this.__eventListeners = { };
62
62
  }
@@ -67,25 +67,17 @@ fabric.Observable = {
67
67
  listenersForEvent[i](options || { });
68
68
  }
69
69
  }
70
- };
71
-
72
- /**
73
- * Alias for observe
74
- * @method observe
75
- * @type function
76
- */
77
- fabric.Observable.on = fabric.Observable.observe;
78
70
 
79
- /**
80
- * Alias for stopObserving
81
- * @method off
82
- * @type function
83
- */
84
- fabric.Observable.off = fabric.Observable.stopObserving;
71
+ /**
72
+ * @namespace fabric.Observable
73
+ */
74
+ fabric.Observable = {
75
+ observe: observe,
76
+ stopObserving: stopObserving,
77
+ fire: fire,
85
78
 
86
- /**
87
- * Alias for fire
88
- * @method trigger
89
- * @type function
90
- */
91
- fabric.Observable.trigger = fabric.Observable.fire;
79
+ on: observe,
80
+ off: stopObserving,
81
+ trigger: fire
82
+ };
83
+ })();
@@ -0,0 +1,45 @@
1
+ /*
2
+ Depends on `stateProperties`
3
+ */
4
+ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
5
+
6
+ /**
7
+ * Returns true if object state (one of its state properties) was changed
8
+ * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called
9
+ */
10
+ hasStateChanged: function() {
11
+ return this.stateProperties.some(function(prop) {
12
+ return this[prop] !== this.originalState[prop];
13
+ }, this);
14
+ },
15
+
16
+ /**
17
+ * Saves state of an object
18
+ * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
19
+ * @return {fabric.Object} thisArg
20
+ */
21
+ saveState: function(options) {
22
+ this.stateProperties.forEach(function(prop) {
23
+ this.originalState[prop] = this.get(prop);
24
+ }, this);
25
+
26
+ if (options && options.stateProperties) {
27
+ options.stateProperties.forEach(function(prop) {
28
+ this.originalState[prop] = this.get(prop);
29
+ }, this);
30
+ }
31
+
32
+ return this;
33
+ },
34
+
35
+ /**
36
+ * Setups state of an object
37
+ * @return {fabric.Object} thisArg
38
+ */
39
+ setupState: function() {
40
+ this.originalState = { };
41
+ this.saveState();
42
+
43
+ return this;
44
+ }
45
+ });
@@ -7,27 +7,29 @@
7
7
  var DOMParser = new require('xmldom').DOMParser,
8
8
  URL = require('url'),
9
9
  HTTP = require('http'),
10
+ HTTPS = require('https'),
10
11
 
11
12
  Canvas = require('canvas'),
12
13
  Image = require('canvas').Image;
13
14
 
14
15
  /** @private */
15
16
  function request(url, encoding, callback) {
16
- var oURL = URL.parse(url),
17
- client = HTTP.createClient(oURL.port, oURL.hostname),
18
- req = client.request('GET', oURL.pathname, { 'host': oURL.hostname });
17
+ var oURL = URL.parse(url);
19
18
 
20
- client.addListener('error', function(err) {
21
- if (err.errno === process.ECONNREFUSED) {
22
- fabric.log('ECONNREFUSED: connection refused to ' + client.host + ':' + client.port);
23
- }
24
- else {
25
- fabric.log(err.message);
26
- }
27
- });
19
+ // detect if http or https is used
20
+ if ( !oURL.port ) {
21
+ oURL.port = ( oURL.protocol.indexOf('https:') === 0 ) ? 443 : 80;
22
+ }
28
23
 
29
- req.end();
30
- req.on('response', function (response) {
24
+ // assign request handler based on protocol
25
+ var reqHandler = ( oURL.port === 443 ) ? HTTPS : HTTP;
26
+
27
+ var req = reqHandler.request({
28
+ hostname: oURL.hostname,
29
+ port: oURL.port,
30
+ path: oURL.path,
31
+ method: 'GET'
32
+ }, function(response){
31
33
  var body = "";
32
34
  if (encoding) {
33
35
  response.setEncoding(encoding);
@@ -41,29 +43,64 @@
41
43
  }
42
44
  });
43
45
  });
46
+
47
+ req.on('error', function(err) {
48
+ if (err.errno === process.ECONNREFUSED) {
49
+ fabric.log('ECONNREFUSED: connection refused to ' + oURL.hostname + ':' + oURL.port);
50
+ }
51
+ else {
52
+ fabric.log(err.message);
53
+ }
54
+ });
55
+
56
+ req.end();
57
+ }
58
+
59
+ /** @private */
60
+ function request_fs(url, callback){
61
+ var fs = require('fs'),
62
+ stream = fs.createReadStream(url),
63
+ body = '';
64
+ stream.on('data', function(chunk){
65
+ body += chunk;
66
+ });
67
+ stream.on('end', function(){
68
+ callback(body);
69
+ });
44
70
  }
45
71
 
46
72
  fabric.util.loadImage = function(url, callback, context) {
73
+ var createImageAndCallBack = function(data){
74
+ img.src = new Buffer(data, 'binary');
75
+ // preserving original url, which seems to be lost in node-canvas
76
+ img._src = url;
77
+ callback && callback.call(context, img);
78
+ };
47
79
  var img = new Image();
48
- if (url && url.indexOf('data') === 0) {
80
+ if (url && (url instanceof Buffer || url.indexOf('data') === 0)) {
49
81
  img.src = img._src = url;
50
82
  callback && callback.call(context, img);
51
83
  }
84
+ else if (url && url.indexOf('http') !== 0) {
85
+ request_fs(url, createImageAndCallBack);
86
+ }
52
87
  else if (url) {
53
- request(url, 'binary', function(body) {
54
- img.src = new Buffer(body, 'binary');
55
- // preserving original url, which seems to be lost in node-canvas
56
- img._src = url;
57
- callback && callback.call(context, img);
58
- });
88
+ request(url, 'binary', createImageAndCallBack);
59
89
  }
60
90
  };
61
91
 
62
92
  fabric.loadSVGFromURL = function(url, callback) {
63
93
  url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
64
- request(url, '', function(body) {
65
- fabric.loadSVGFromString(body, callback);
66
- });
94
+ if (url.indexOf('http') !== 0) {
95
+ request_fs(url, function(body) {
96
+ fabric.loadSVGFromString(body, callback);
97
+ });
98
+ }
99
+ else {
100
+ request(url, '', function(body) {
101
+ fabric.loadSVGFromString(body, callback);
102
+ });
103
+ }
67
104
  };
68
105
 
69
106
  fabric.loadSVGFromString = function(string, callback) {
@@ -92,7 +129,6 @@
92
129
 
93
130
  /**
94
131
  * Only available when running fabric on node.js
95
- * @method createCanvasForNode
96
132
  * @param width Canvas width
97
133
  * @param height Canvas height
98
134
  * @return {Object} wrapped canvas instance
@@ -128,7 +164,7 @@
128
164
 
129
165
  var origSetWidth = fabric.StaticCanvas.prototype.setWidth;
130
166
  fabric.StaticCanvas.prototype.setWidth = function(width) {
131
- origSetWidth.call(this);
167
+ origSetWidth.call(this, width);
132
168
  this.nodeCanvas.width = width;
133
169
  return this;
134
170
  };
@@ -138,7 +174,7 @@
138
174
 
139
175
  var origSetHeight = fabric.StaticCanvas.prototype.setHeight;
140
176
  fabric.StaticCanvas.prototype.setHeight = function(height) {
141
- origSetHeight.call(this);
177
+ origSetHeight.call(this, height);
142
178
  this.nodeCanvas.height = height;
143
179
  return this;
144
180
  };
@@ -10,23 +10,42 @@
10
10
  var fabric = global.fabric || (global.fabric = { }),
11
11
  extend = fabric.util.object.extend,
12
12
  capitalize = fabric.util.string.capitalize,
13
- clone = fabric.util.object.clone;
13
+ clone = fabric.util.object.clone,
14
+ toFixed = fabric.util.toFixed,
15
+ multiplyTransformMatrices = fabric.util.multiplyTransformMatrices;
16
+
17
+ fabric.SHARED_ATTRIBUTES = [
18
+ "transform",
19
+ "fill", "fill-opacity", "fill-rule",
20
+ "opacity",
21
+ "stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width"
22
+ ];
14
23
 
15
24
  var attributesMap = {
25
+ 'fill-opacity': 'fillOpacity',
26
+ 'fill-rule': 'fillRule',
27
+ 'font-family': 'fontFamily',
28
+ 'font-size': 'fontSize',
29
+ 'font-style': 'fontStyle',
30
+ 'font-weight': 'fontWeight',
16
31
  'cx': 'left',
17
32
  'x': 'left',
18
- 'cy': 'top',
19
- 'y': 'top',
20
33
  'r': 'radius',
21
- 'fill-opacity': 'opacity',
22
- 'fill-rule': 'fillRule',
34
+ 'stroke-dasharray': 'strokeDashArray',
35
+ 'stroke-linecap': 'strokeLineCap',
36
+ 'stroke-linejoin': 'strokeLineJoin',
37
+ 'stroke-miterlimit':'strokeMiterLimit',
38
+ 'stroke-opacity': 'strokeOpacity',
23
39
  'stroke-width': 'strokeWidth',
24
- 'transform': 'transformMatrix',
25
40
  'text-decoration': 'textDecoration',
26
- 'font-size': 'fontSize',
27
- 'font-weight': 'fontWeight',
28
- 'font-style': 'fontStyle',
29
- 'font-family': 'fontFamily'
41
+ 'cy': 'top',
42
+ 'y': 'top',
43
+ 'transform': 'transformMatrix'
44
+ };
45
+
46
+ var colorAttributes = {
47
+ 'stroke': 'strokeOpacity',
48
+ 'fill': 'fillOpacity'
30
49
  };
31
50
 
32
51
  function normalizeAttr(attr) {
@@ -37,12 +56,57 @@
37
56
  return attr;
38
57
  }
39
58
 
59
+ function normalizeValue(attr, value, parentAttributes) {
60
+ var isArray;
61
+
62
+ if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
63
+ value = '';
64
+ }
65
+ else if (attr === 'fillRule') {
66
+ value = (value === 'evenodd') ? 'destination-over' : value;
67
+ }
68
+ else if (attr === 'strokeDashArray') {
69
+ value = value.replace(/,/g, ' ').split(/\s+/);
70
+ }
71
+ else if (attr === 'transformMatrix') {
72
+ if (parentAttributes && parentAttributes.transformMatrix) {
73
+ value = multiplyTransformMatrices(
74
+ parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
75
+ }
76
+ else {
77
+ value = fabric.parseTransformAttribute(value);
78
+ }
79
+ }
80
+
81
+ isArray = Object.prototype.toString.call(value) === '[object Array]';
82
+
83
+ // TODO: need to normalize em, %, pt, etc. to px (!)
84
+ var parsed = isArray ? value.map(parseFloat) : parseFloat(value);
85
+
86
+ return (!isArray && isNaN(parsed) ? value : parsed);
87
+ }
88
+
89
+ /**
90
+ * @private
91
+ * @param {Object} attributes Array of attributes to parse
92
+ */
93
+ function _setStrokeFillOpacity(attributes) {
94
+ for (var attr in colorAttributes) {
95
+ if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') continue;
96
+
97
+ var color = new fabric.Color(attributes[attr]);
98
+ attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
99
+
100
+ delete attributes[colorAttributes[attr]];
101
+ }
102
+ return attributes;
103
+ }
104
+
40
105
  /**
41
106
  * Returns an object of attributes' name/value, given element and an array of attribute names;
42
107
  * Parses parent "g" nodes recursively upwards.
43
108
  * @static
44
109
  * @memberOf fabric
45
- * @method parseAttributes
46
110
  * @param {DOMElement} element Element to parse
47
111
  * @param {Array} attributes Array of attributes to parse
48
112
  * @return {Object} object containing parsed attributes' names/values
@@ -54,7 +118,6 @@
54
118
  }
55
119
 
56
120
  var value,
57
- parsed,
58
121
  parentAttributes = { };
59
122
 
60
123
  // if there's a parent container (`g` node), parse its attributes recursively upwards
@@ -64,20 +127,11 @@
64
127
 
65
128
  var ownAttributes = attributes.reduce(function(memo, attr) {
66
129
  value = element.getAttribute(attr);
67
- parsed = parseFloat(value);
68
130
  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
131
  attr = normalizeAttr(attr);
80
- memo[attr] = isNaN(parsed) ? value : parsed;
132
+ value = normalizeValue(attr, value, parentAttributes);
133
+
134
+ memo[attr] = value;
81
135
  }
82
136
  return memo;
83
137
  }, { });
@@ -85,8 +139,9 @@
85
139
  // add values parsed from style, which take precedence over attributes
86
140
  // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
87
141
 
88
- ownAttributes = extend(ownAttributes, extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
89
- return extend(parentAttributes, ownAttributes);
142
+ ownAttributes = extend(ownAttributes,
143
+ extend(getGlobalStylesForElement(element), fabric.parseStyleAttribute(element)));
144
+ return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
90
145
  }
91
146
 
92
147
  /**
@@ -94,7 +149,6 @@
94
149
  * @static
95
150
  * @function
96
151
  * @memberOf fabric
97
- * @method parseTransformAttribute
98
152
  * @param attributeValue {String} string containing attribute value
99
153
  * @return {Array} array of 6 elements representing transformation matrix
100
154
  */
@@ -177,12 +231,13 @@
177
231
  reTransformList = new RegExp(transform_list),
178
232
  // == end transform regexp
179
233
 
180
- reTransform = new RegExp(transform);
234
+ reTransform = new RegExp(transform, 'g');
181
235
 
182
236
  return function(attributeValue) {
183
237
 
184
238
  // start with identity matrix
185
239
  var matrix = iMatrix.concat();
240
+ var matrices = [ ];
186
241
 
187
242
  // return if no argument was given or
188
243
  // an argument does not match transform attribute regexp
@@ -218,8 +273,19 @@
218
273
  matrix = args;
219
274
  break;
220
275
  }
276
+
277
+ // snapshot current matrix into matrices array
278
+ matrices.push(matrix.concat());
279
+ // reset
280
+ matrix = iMatrix.concat();
221
281
  });
222
- return matrix;
282
+
283
+ var combinedMatrix = matrices[0];
284
+ while (matrices.length > 1) {
285
+ matrices.shift();
286
+ combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
287
+ }
288
+ return combinedMatrix;
223
289
  };
224
290
  })();
225
291
 
@@ -227,7 +293,6 @@
227
293
  * Parses "points" attribute, returning an array of values
228
294
  * @static
229
295
  * @memberOf fabric
230
- * @method parsePointsAttribute
231
296
  * @param points {String} points attribute string
232
297
  * @return {Array} array of points
233
298
  */
@@ -267,38 +332,76 @@
267
332
  return parsedPoints;
268
333
  }
269
334
 
335
+ function parseFontDeclaration(value, oStyle) {
336
+
337
+ // TODO: support non-px font size
338
+ var match = value.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px\s+(.*)/);
339
+
340
+ if (!match) return;
341
+
342
+ var fontStyle = match[1];
343
+ // Font variant is not used
344
+ // var fontVariant = match[2];
345
+ var fontWeight = match[3];
346
+ var fontSize = match[4];
347
+ var fontFamily = match[5];
348
+
349
+ if (fontStyle) {
350
+ oStyle.fontStyle = fontStyle;
351
+ }
352
+ if (fontWeight) {
353
+ oStyle.fontSize = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
354
+ }
355
+ if (fontSize) {
356
+ oStyle.fontSize = parseFloat(fontSize);
357
+ }
358
+ if (fontFamily) {
359
+ oStyle.fontFamily = fontFamily;
360
+ }
361
+ }
362
+
270
363
  /**
271
364
  * Parses "style" attribute, retuning an object with values
272
365
  * @static
273
366
  * @memberOf fabric
274
- * @method parseStyleAttribute
275
367
  * @param {SVGElement} element Element to parse
276
368
  * @return {Object} Objects with values parsed from style attribute of an element
277
369
  */
278
370
  function parseStyleAttribute(element) {
279
371
  var oStyle = { },
280
- style = element.getAttribute('style');
372
+ style = element.getAttribute('style'),
373
+ attr, value;
281
374
 
282
375
  if (!style) return oStyle;
283
376
 
284
377
  if (typeof style === 'string') {
285
- style = style.replace(/;$/, '').split(';').forEach(function (current) {
378
+ style.replace(/;$/, '').split(';').forEach(function (chunk) {
379
+ var pair = chunk.split(':');
286
380
 
287
- var attr = current.split(':');
288
- var value = attr[1].trim();
381
+ attr = normalizeAttr(pair[0].trim().toLowerCase());
382
+ value = normalizeValue(attr, pair[1].trim());
289
383
 
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;
384
+ if (attr === 'font') {
385
+ parseFontDeclaration(value, oStyle);
386
+ }
387
+ else {
388
+ oStyle[attr] = value;
389
+ }
294
390
  });
295
391
  }
296
392
  else {
297
393
  for (var prop in style) {
298
394
  if (typeof style[prop] === 'undefined') continue;
299
395
 
300
- var parsed = parseFloat(style[prop]);
301
- oStyle[normalizeAttr(prop.toLowerCase())] = isNaN(parsed) ? style[prop] : parsed;
396
+ attr = normalizeAttr(prop.toLowerCase());
397
+ value = normalizeValue(attr, style[prop]);
398
+
399
+ if (attr === 'font') {
400
+ parseFontDeclaration(value, oStyle);
401
+ }
402
+ else {
403
+ oStyle[attr] = value;
404
+ }
302
405
  }
303
406
  }
304
407
 
@@ -325,7 +428,6 @@
325
428
  * Transforms an array of svg elements to corresponding fabric.* instances
326
429
  * @static
327
430
  * @memberOf fabric
328
- * @method parseElements
329
431
  * @param {Array} elements Array of elements to parse
330
432
  * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)
331
433
  * @param {Object} [options] Options object
@@ -356,7 +458,7 @@
356
458
  instances.splice(index, 0, obj);
357
459
  checkIfDone();
358
460
  };
359
- })(index), options);
461
+ })(index, el), options);
360
462
  }
361
463
  else {
362
464
  var obj = klass.fromElement(el, options);
@@ -380,7 +482,6 @@
380
482
  * @static
381
483
  * @function
382
484
  * @memberOf fabric
383
- * @method getCSSRules
384
485
  * @param {SVGDocument} doc SVG document to parse
385
486
  * @return {Object} CSS rules of this document
386
487
  */
@@ -451,7 +552,6 @@
451
552
  * @static
452
553
  * @function
453
554
  * @memberOf fabric
454
- * @method parseSVGDocument
455
555
  * @param {SVGDocument} doc SVG document to parse
456
556
  * @param {Function} callback Callback to call when parsing is finished; It's being passed an array of elements (parsed from a document).
457
557
  * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
@@ -546,15 +646,13 @@
546
646
  };
547
647
  })();
548
648
 
549
- /**
649
+ /**
550
650
  * Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
551
- * @property
552
651
  * @namespace
553
652
  */
554
653
  var svgCache = {
555
654
 
556
655
  /**
557
- * @method has
558
656
  * @param {String} name
559
657
  * @param {Function} callback
560
658
  */
@@ -563,7 +661,6 @@
563
661
  },
564
662
 
565
663
  /**
566
- * @method get
567
664
  * @param {String} url
568
665
  * @param {Function} callback
569
666
  */
@@ -572,7 +669,6 @@
572
669
  },
573
670
 
574
671
  /**
575
- * @method set
576
672
  * @param {String} url
577
673
  * @param {Object} object
578
674
  */
@@ -582,8 +678,7 @@
582
678
  };
583
679
 
584
680
  /**
585
- * Takes url corresponding to an SVG document, and parses it into a set of fabric objects
586
- * @method loadSVGFromURL
681
+ * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy)
587
682
  * @memberof fabric
588
683
  * @param {String} url
589
684
  * @param {Function} callback
@@ -630,8 +725,8 @@
630
725
  }
631
726
 
632
727
  /**
633
- * @method _enlivenCachedObject
634
- */
728
+ * @private
729
+ */
635
730
  function _enlivenCachedObject(cachedObject) {
636
731
 
637
732
  var objects = cachedObject.objects,
@@ -646,7 +741,6 @@
646
741
 
647
742
  /**
648
743
  * Takes string corresponding to an SVG document, and parses it into a set of fabric objects
649
- * @method loadSVGFromString
650
744
  * @memberof fabric
651
745
  * @param {String} string
652
746
  * @param {Function} callback
@@ -675,7 +769,6 @@
675
769
 
676
770
  /**
677
771
  * Creates markup containing SVG font faces
678
- * @method createSVGFontFacesMarkup
679
772
  * @param {Array} objects Array of fabric objects
680
773
  * @return {String}
681
774
  */
@@ -708,7 +801,6 @@
708
801
 
709
802
  /**
710
803
  * Creates markup containing SVG referenced elements like patterns, gradients etc.
711
- * @method createSVGRefElementsMarkup
712
804
  * @param {fabric.Canvas} canvas instance of fabric.Canvas
713
805
  * @return {String}
714
806
  */
@@ -732,6 +824,35 @@
732
824
  return markup;
733
825
  }
734
826
 
827
+ /**
828
+ * Parses an SVG document, returning all of the gradient declarations found in it
829
+ * @static
830
+ * @function
831
+ * @memberOf fabric
832
+ * @param {SVGDocument} doc SVG document to parse
833
+ * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element
834
+ */
835
+ function getGradientDefs(doc) {
836
+ var linearGradientEls = doc.getElementsByTagName('linearGradient'),
837
+ radialGradientEls = doc.getElementsByTagName('radialGradient'),
838
+ el, i,
839
+ gradientDefs = { };
840
+
841
+ i = linearGradientEls.length;
842
+ for (; i--; ) {
843
+ el = linearGradientEls[i];
844
+ gradientDefs[el.getAttribute('id')] = el;
845
+ }
846
+
847
+ i = radialGradientEls.length;
848
+ for (; i--; ) {
849
+ el = radialGradientEls[i];
850
+ gradientDefs[el.getAttribute('id')] = el;
851
+ }
852
+
853
+ return gradientDefs;
854
+ }
855
+
735
856
  extend(fabric, {
736
857
 
737
858
  parseAttributes: parseAttributes,
@@ -744,7 +865,9 @@
744
865
  loadSVGFromString: loadSVGFromString,
745
866
 
746
867
  createSVGFontFacesMarkup: createSVGFontFacesMarkup,
747
- createSVGRefElementsMarkup: createSVGRefElementsMarkup
868
+ createSVGRefElementsMarkup: createSVGRefElementsMarkup,
869
+
870
+ getGradientDefs: getGradientDefs
748
871
  });
749
872
 
750
873
  })(typeof exports !== 'undefined' ? exports : this);