Merge remote-tracking branch 'upstream/master'

Conflicts:
	dist/fabric.min.js
	dist/fabric.min.js.gz
	dist/fabric.require.js
	src/canvas.class.js
This commit is contained in:
Tom French 2014-07-03 16:02:43 +01:00
commit a4129c31e8
34 changed files with 22281 additions and 16892 deletions

View file

@ -7,8 +7,6 @@
"requireParenthesesAroundIIFE": true,
"requireSpacesInsideObjectBrackets": "all",
"requireCommaBeforeLineBreak": true,
"requireRightStickedOperators": ["!"],
"requireLeftStickedOperators": [","],
"requireCamelCaseOrUpperCaseIdentifiers": true,
"requireKeywordsOnNewLine": ["else"],
"requireLineFeedAtFileEnd": true,

View file

@ -16,6 +16,7 @@
- Fix `setAngle` for different originX/originY (!= 'center')
- Change default/init noise/brightness value for `fabric.Image.filters.Noise` and `fabric.Image.filters.Brightness` from 100 to 0
- Add `fabric.Canvas#imageSmoothingEnabled`
- Add `copy/paste` support for iText (uses clipboardData)
**Version 1.4.0**

View file

@ -1,6 +1,6 @@
/*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */
var fabric = fabric || { version: "1.4.6" };
var fabric = fabric || { version: "1.4.7" };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
}

View file

@ -10,6 +10,8 @@
[![Dependency Status](https://david-dm.org/kangax/fabric.js.png?theme=shields.io)](https://david-dm.org/kangax/fabric.js)
[![devDependency Status](https://david-dm.org/kangax/fabric.js/dev-status.png?theme=shields.io)](https://david-dm.org/kangax/fabric.js#info=devDependencies)
[![Bountysource](https://api.bountysource.com/badge/tracker?tracker_id=23217)](https://www.bountysource.com/trackers/23217-fabric-js?utm_source=23217&utm_medium=shield&utm_campaign=TRACKER_BADGE)
**Fabric.js** is a framework that makes it easy to work with HTML5 canvas element. It is an **interactive object model** on top of canvas element. It is also an **SVG-to-canvas parser**.

View file

@ -1,6 +1,6 @@
{
"name": "fabric.js",
"version": "1.4.6",
"version": "1.4.7",
"homepage": "http://fabricjs.com",
"authors": [
"kangax", "Kienz"

4464
dist/fabric.js vendored

File diff suppressed because it is too large Load diff

19
dist/fabric.min.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/fabric.min.js.gz vendored

Binary file not shown.

34311
dist/fabric.require.js vendored

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
"name": "fabric",
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
"homepage": "http://fabricjs.com/",
"version": "1.4.6",
"version": "1.4.7",
"author": "Juriy Zaytsev <kangax@gmail.com>",
"keywords": [
"canvas",
@ -14,6 +14,11 @@
"HTML5",
"object model"
],
"browser" : {
"canvas" : false,
"jsdom": false,
"xmldom": false
},
"repository": {
"type": "git",
"url": "https://github.com/kangax/fabric.js"
@ -27,17 +32,17 @@
"test": "node test.js && jshint src"
},
"dependencies": {
"canvas": "1.0.x",
"jsdom": "0.10.x",
"canvas": "1.1.x",
"jsdom": "0.11.x",
"xmldom": "0.1.x"
},
"devDependencies": {
"execSync": "0.0.x",
"uglify-js": "2.4.x",
"jscs": "1.4.x",
"jscs": "1.5.x",
"jshint": "2.5.x",
"qunit": "0.6.x",
"istanbul": "0.2.6"
"istanbul": "0.2.x"
},
"engines": {
"node": ">=0.4.0 && <1.0.0"

View file

@ -148,7 +148,7 @@
this.box = this.getPathBoundingBox(this._points);
return this.convertPointsToSVGPath(
this._points, this.box.minx, this.box.maxx, this.box.miny, this.box.maxy);
},
},
/**
* Returns bounding box of a path based on given points

View file

@ -787,9 +787,8 @@
// Cache all targets where their bounding box contains point.
var target,
pointer = this.getPointer(e, true);
var i = this._objects.length;
pointer = this.getPointer(e, true),
i = this._objects.length;
while (i--) {
if (this._checkTarget(e, this._objects[i], pointer)){

View file

@ -59,10 +59,9 @@
source = new fabric.Color(this.color).getSource();
for (i = 0; i < iLen; i+=4) {
data[i] *= source[0]/255;
data[i + 1] *= source[1]/255;
data[i + 2] *= source[2]/255;
data[i] *= source[0] / 255;
data[i + 1] *= source[1] / 255;
data[i + 2] *= source[2] / 255;
}
context.putImageData(imageData, 0, 0);

View file

@ -153,7 +153,7 @@
if (e.type === 'touchstart') {
// Unbind mousedown to prevent double triggers from touch devices
removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
}
else {
addListener(fabric.document, 'mouseup', this._onMouseUp);

View file

@ -108,7 +108,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
fabric.util.string.escapeXml(_char),
'</tspan>'
].join('');
].join('');
}
});
/* _TO_SVG_END_ */

View file

@ -156,6 +156,7 @@
selectAll: function() {
this.selectionStart = 0;
this.selectionEnd = this.text.length;
this.fire('selection:changed');
this.canvas && this.canvas.fire('text:selection:changed', { target: this });
},

View file

@ -238,10 +238,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
mouseOffset, prevWidth, width, charIndex + i, jlen);
}
if (mouseOffset.y < height) {
return this._getNewSelectionStartFromOffset(
mouseOffset, prevWidth, width, charIndex + i, jlen, j);
}
if (mouseOffset.y < height) {
return this._getNewSelectionStartFromOffset(
mouseOffset, prevWidth, width, charIndex + i, jlen, j);
}
}
// clicked somewhere after all chars, so set at the end

View file

@ -13,6 +13,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));
fabric.util.addListener(this.hiddenTextarea, 'keypress', this.onKeyPress.bind(this));
fabric.util.addListener(this.hiddenTextarea, 'copy', this.copy.bind(this));
fabric.util.addListener(this.hiddenTextarea, 'paste', this.paste.bind(this));
if (!this._clickHandlerInitialized && this.canvas) {
fabric.util.addListener(this.canvas.upperCanvasEl, 'click', this.onClick.bind(this));
@ -38,8 +40,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_ctrlKeysMap: {
65: 'selectAll',
67: 'copy',
86: 'paste',
88: 'cut'
},
@ -65,7 +65,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
return;
}
e.preventDefault();
e.stopPropagation();
this.canvas && this.canvas.renderAll();
@ -83,9 +82,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/**
* Copies selected text
* @param {Event} e Event object
*/
copy: function() {
var selectedText = this.getSelectedText();
copy: function(e) {
var selectedText = this.getSelectedText(),
clipboardData = this._getClipboardData(e);
// Check for backward compatibility with old browsers
if (clipboardData) {
clipboardData.setData('text', selectedText);
}
this.copiedText = selectedText;
this.copiedStyles = this.getSelectionStyles(
this.selectionStart,
@ -94,21 +101,45 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
/**
* Pastes text
* @param {Event} e Event object
*/
paste: function() {
if (this.copiedText) {
this.insertChars(this.copiedText);
paste: function(e) {
var copiedText = null,
clipboardData = this._getClipboardData(e);
// Check for backward compatibility with old browsers
if (clipboardData) {
copiedText = clipboardData.getData('text');
} else {
copiedText = this.copiedText;
}
if (copiedText) {
this.insertChars(copiedText);
}
},
/**
* Cuts text
* @param {Event} e Event object
*/
cut: function(e) {
if (this.selectionStart === this.selectionEnd) {
return;
}
this.copy();
this.removeChars(e);
},
/**
* @private
* @param {Event} e Event object
*/
_getClipboardData: function(e) {
return e && (e.clipboardData || fabric.window.clipboardData);
},
/**
* Handles keypress event
* @param {Event} e Event object
@ -120,7 +151,6 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this.insertChars(String.fromCharCode(e.which));
e.preventDefault();
e.stopPropagation();
},

View file

@ -18,7 +18,9 @@
* @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
*/
_findTargetCorner: function(pointer) {
if (!this.hasControls || !this.active) return false;
if (!this.hasControls || !this.active) {
return false;
}
var ex = pointer.x,
ey = pointer.y,

View file

@ -76,7 +76,7 @@
else if (attr === 'visible') {
value = (value === 'none' || value === 'hidden') ? false : true;
// display=none on parent element always takes precedence over child element
if (parentAttributes.visible === false) {
if (parentAttributes && parentAttributes.visible === false) {
value = false;
}
}
@ -354,13 +354,44 @@
if (ruleMatchesElement) {
for (var property in fabric.cssRules[rule]) {
styles[property] = fabric.cssRules[rule][property];
var attr = normalizeAttr(property),
value = normalizeValue(attr, fabric.cssRules[rule][property]);
styles[attr] = value;
}
}
}
return styles;
}
/**
* @private
*/
function parseUseDirectives(doc) {
var nodelist = doc.querySelectorAll("use");
for (var i = 0; i < nodelist.length; i++) {
var el = nodelist[i];
var xlink = el.getAttribute('xlink:href').substr(1);
var x = el.getAttribute('x') || 0;
var y = el.getAttribute('y') || 0;
var el2 = doc.getElementById(xlink).cloneNode(true);
var currentTrans = (el.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')';
for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
var attr = attrs.item(j);
if (attr.nodeName !== 'x' && attr.nodeName !== 'y' && attr.nodeName !== 'xlink:href') {
if (attr.nodeName === 'transform') {
currentTrans = currentTrans + ' ' + attr.nodeValue;
} else {
el2.setAttribute(attr.nodeName, attr.nodeValue);
}
}
}
el2.setAttribute('transform', currentTrans);
el2.removeAttribute('id');
var pNode=el.parentNode;
pNode.replaceChild(el2, el);
}
}
/**
* Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
@ -401,9 +432,11 @@
return function(doc, callback, reviver) {
if (!doc) return;
var startTime = new Date(),
descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
var startTime = new Date();
parseUseDirectives(doc);
var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
if (descendants.length === 0 && fabric.isLikelyNode) {
// we're likely in node, where "o3-xml" library fails to gEBTN("*")
@ -462,7 +495,6 @@
fabric.gradientDefs = fabric.getGradientDefs(doc);
fabric.cssRules = fabric.getCSSRules(doc);
// Precedence of rules: style > class > attribute
fabric.parseElements(elements, function(instances) {
@ -478,46 +510,46 @@
* Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
* @namespace
*/
var svgCache = {
var svgCache = {
/**
* @param {String} name
* @param {Function} callback
*/
has: function (name, callback) {
callback(false);
},
/**
* @param {String} name
* @param {Function} callback
*/
has: function (name, callback) {
callback(false);
},
/**
* @param {String} url
* @param {Function} callback
*/
get: function () {
/* NOOP */
},
/**
* @param {String} url
* @param {Function} callback
*/
get: function () {
/* NOOP */
},
/**
* @param {String} url
* @param {Object} object
*/
set: function () {
/* NOOP */
}
};
/**
* @param {String} url
* @param {Object} object
*/
set: function () {
/* NOOP */
}
};
/**
* @private
*/
function _enlivenCachedObject(cachedObject) {
var objects = cachedObject.objects,
options = cachedObject.options;
var objects = cachedObject.objects,
options = cachedObject.options;
objects = objects.map(function (o) {
return fabric[capitalize(o.type)].fromObject(o);
});
objects = objects.map(function (o) {
return fabric[capitalize(o.type)].fromObject(o);
});
return ({ objects: objects, options: options });
return ({ objects: objects, options: options });
}
/**
@ -774,7 +806,6 @@
loadSVGFromURL: function(url, callback, reviver) {
url = url.replace(/^\n\s*/, '').trim();
svgCache.has(url, function (hasUrl) {
if (hasUrl) {
svgCache.get(url, function (value) {

View file

@ -64,6 +64,7 @@
* @return {fabric.Shadow} thisArg
*/
initialize: function(options) {
if (typeof options === 'string') {
options = this._parseShadow(options);
}

View file

@ -103,8 +103,6 @@
// multiply by currently set alpha (the one that was set by path group where this object is contained, for example)
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top : 0, this.radius, 0, piBy2, false);
ctx.closePath();
this._renderFill(ctx);
this.stroke && this._renderStroke(ctx);
},
@ -163,16 +161,24 @@
*/
fabric.Circle.fromElement = function(element, options) {
options || (options = { });
var parsedAttributes = fabric.parseAttributes(element, fabric.Circle.ATTRIBUTE_NAMES);
if (!isValidRadius(parsedAttributes)) {
throw new Error('value of `r` attribute is required and can not be negative');
}
if ('left' in parsedAttributes) {
parsedAttributes.left -= (options.width / 2) || 0;
if (!('left' in parsedAttributes)) {
parsedAttributes.left = 0;
}
if ('top' in parsedAttributes) {
parsedAttributes.top -= (options.height / 2) || 0;
if (!('top' in parsedAttributes)) {
parsedAttributes.top = 0
}
if (!('transformMatrix' in parsedAttributes)) {
parsedAttributes.left -= (options.width / 2);
parsedAttributes.top -= (options.height / 2);
}
var obj = new fabric.Circle(extend(parsedAttributes, options));
obj.cx = parseFloat(element.getAttribute('cx')) || 0;

View file

@ -111,15 +111,11 @@
ctx.beginPath();
ctx.save();
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
if (this.transformMatrix && this.group) {
ctx.translate(this.cx, this.cy);
}
ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top : 0, this.rx, 0, piBy2, false);
ctx.restore();
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
this._renderFill(ctx);
this._renderStroke(ctx);
ctx.restore();
},
/**
@ -151,21 +147,22 @@
fabric.Ellipse.fromElement = function(element, options) {
options || (options = { });
var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES),
cx = parsedAttributes.left,
cy = parsedAttributes.top;
var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
if ('left' in parsedAttributes) {
parsedAttributes.left -= (options.width / 2) || 0;
if (!('left' in parsedAttributes)) {
parsedAttributes.left = 0;
}
if ('top' in parsedAttributes) {
parsedAttributes.top -= (options.height / 2) || 0;
if (!('top' in parsedAttributes)) {
parsedAttributes.top = 0;
}
if (!('transformMatrix' in parsedAttributes)) {
parsedAttributes.left -= (options.width / 2);
parsedAttributes.top -= (options.height / 2);
}
var ellipse = new fabric.Ellipse(extend(parsedAttributes, options));
ellipse.cx = cx || 0;
ellipse.cy = cy || 0;
ellipse.cx = parseFloat(element.getAttribute('cx')) || 0;
ellipse.cy = parseFloat(element.getAttribute('cy')) || 0;
return ellipse;
};

View file

@ -168,10 +168,10 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderDashedStroke: function(ctx) {
var x = -this.width/2,
y = -this.height/2,
w = this.width,
h = this.height;
var x = -this.width / 2,
y = -this.height / 2,
w = this.width,
h = this.height;
ctx.save();
this._setStrokeStyles(ctx);
@ -331,13 +331,14 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
this._element && ctx.drawImage(
this._element,
-this.width / 2,
-this.height / 2,
this.width,
this.height
);
this._element &&
ctx.drawImage(
this._element,
-this.width / 2,
-this.height / 2,
this.width,
this.height
);
},
/**

View file

@ -9,6 +9,7 @@
* @mixes fabric.Observable
*
* @fires changed ("text:changed" when observing canvas)
* @fires selection:changed ("text:selection:changed" when observing canvas)
* @fires editing:entered ("text:editing:entered" when observing canvas)
* @fires editing:exited ("text:editing:exited" when observing canvas)
*
@ -219,6 +220,7 @@
*/
setSelectionStart: function(index) {
if (this.selectionStart !== index) {
this.fire('selection:changed');
this.canvas && this.canvas.fire('text:selection:changed', { target: this });
}
this.selectionStart = index;
@ -231,6 +233,7 @@
*/
setSelectionEnd: function(index) {
if (this.selectionEnd !== index) {
this.fire('selection:changed');
this.canvas && this.canvas.fire('text:selection:changed', { target: this });
}
this.selectionEnd = index;
@ -356,6 +359,8 @@
textBackgroundColor: style && style.textBackgroundColor || this.textBackgroundColor,
textDecoration: style && style.textDecoration || this.textDecoration,
fontFamily: style && style.fontFamily || this.fontFamily,
fontWeight: style && style.fontWeight || this.fontWeight,
fontStyle: style && style.fontStyle || this.fontStyle,
stroke: style && style.stroke || this.stroke,
strokeWidth: style && style.strokeWidth || this.strokeWidth
};
@ -695,6 +700,8 @@
prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor ||
prevStyle.textDecoration !== thisStyle.textDecoration ||
prevStyle.fontFamily !== thisStyle.fontFamily ||
prevStyle.fontWeight !== thisStyle.fontWeight ||
prevStyle.fontStyle !== thisStyle.fontStyle ||
prevStyle.stroke !== thisStyle.stroke ||
prevStyle.strokeWidth !== thisStyle.strokeWidth
);

View file

@ -1343,7 +1343,7 @@
* canvas.renderAll();
*/
setShadow: function(options) {
return this.set('shadow', new fabric.Shadow(options));
return this.set('shadow', options ? new fabric.Shadow(options) : null);
},
/**

View file

@ -158,6 +158,8 @@
_render: function(ctx) {
var current, // current instruction
previous = null,
subpathStartX = 0,
subpathStartY = 0,
x = 0, // current x
y = 0, // current y
controlX = 0, // current control point x
@ -167,8 +169,7 @@
tempControlX,
tempControlY,
l = -((this.width / 2) + this.pathOffset.x),
t = -((this.height / 2) + this.pathOffset.y),
methodName;
t = -((this.height / 2) + this.pathOffset.y);
for (var i = 0, len = this.path.length; i < len; ++i) {
@ -211,21 +212,17 @@
case 'm': // moveTo, relative
x += current[1];
y += current[2];
// draw a line if previous command was moveTo as well (otherwise, it will have no effect)
methodName = (previous && (previous[0] === 'm' || previous[0] === 'M'))
? 'lineTo'
: 'moveTo';
ctx[methodName](x + l, y + t);
subpathStartX = x;
subpathStartY = y;
ctx.moveTo(x + l, y + t);
break;
case 'M': // moveTo, absolute
x = current[1];
y = current[2];
// draw a line if previous command was moveTo as well (otherwise, it will have no effect)
methodName = (previous && (previous[0] === 'm' || previous[0] === 'M'))
? 'lineTo'
: 'moveTo';
ctx[methodName](x + l, y + t);
subpathStartX = x;
subpathStartY = y;
ctx.moveTo(x + l, y + t);
break;
case 'c': // bezierCurveTo, relative
@ -436,6 +433,8 @@
case 'z':
case 'Z':
x = subpathStartX;
y = subpathStartY;
ctx.closePath();
break;
}
@ -466,7 +465,7 @@
this._setShadow(ctx);
this.clipTo && fabric.util.clipContext(this, ctx);
ctx.beginPath();
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
this._render(ctx);
this._renderFill(ctx);
this._renderStroke(ctx);

View file

@ -123,6 +123,7 @@
_render: function(ctx) {
var point;
ctx.beginPath();
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
ctx.moveTo(this.points[0].x, this.points[0].y);
for (var i = 0, len = this.points.length; i < len; i++) {
point = this.points[i];

View file

@ -336,7 +336,7 @@
* @private
* @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
* or {@link fabric.StaticCanvas#overlayImage|overlayImage})
* @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set background or overlay to
* @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an image or null to set background or overlay to
* @param {Function} callback Callback to invoke when image is loaded and set as background or overlay
* @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.
*/
@ -359,11 +359,11 @@
* @private
* @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}
* or {@link fabric.StaticCanvas#overlayColor|overlayColor})
* @param {(Object|String)} color Object with pattern information or color value
* @param {(Object|String|null)} color Object with pattern information, color value or null
* @param {Function} [callback] Callback is invoked when color is set
*/
__setBgOverlayColor: function(property, color, callback) {
if (color.source) {
if (color && color.source) {
var _this = this;
fabric.util.loadImage(color.source, function(img) {
_this[property] = new fabric.Pattern({
@ -1022,7 +1022,6 @@
}
}
return data;
},

View file

@ -66,10 +66,12 @@
rx = Math.abs(rx);
ry = Math.abs(ry);
var px = cosTh * (ox - x) * 0.5 + sinTh * (oy - y) * 0.5,
py = cosTh * (oy - y) * 0.5 - sinTh * (ox - x) * 0.5,
var px = cosTh * (ox - x) + sinTh * (oy - y),
py = cosTh * (oy - y) - sinTh * (ox - x),
pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
pl *= 0.25;
if (pl > 1) {
pl = Math.sqrt(pl);
rx *= pl;
@ -98,20 +100,26 @@
return segmentToBezierCache[argsString];
}
var a00 = cosTh * rx,
var sinTh0 = Math.sin(th0),
cosTh0 = Math.cos(th0),
sinTh1 = Math.sin(th1),
cosTh1 = Math.cos(th1),
a00 = cosTh * rx,
a01 = -sinTh * ry,
a10 = sinTh * rx,
a11 = cosTh * ry,
thHalf = 0.5 * (th1 - th0),
t = (8 / 3) * Math.sin(thHalf * 0.5) *
Math.sin(thHalf * 0.5) / Math.sin(thHalf),
thHalf = 0.25 * (th1 - th0),
x1 = cx + Math.cos(th0) - t * Math.sin(th0),
y1 = cy + Math.sin(th0) + t * Math.cos(th0),
x3 = cx + Math.cos(th1),
y3 = cy + Math.sin(th1),
x2 = x3 + t * Math.sin(th1),
y2 = y3 - t * Math.cos(th1);
t = (8 / 3) * Math.sin(thHalf) *
Math.sin(thHalf) / Math.sin(thHalf * 2),
x1 = cx + cosTh0 - t * sinTh0,
y1 = cy + sinTh0 + t * cosTh0,
x3 = cx + cosTh1,
y3 = cy + sinTh1,
x2 = x3 + t * sinTh1,
y2 = y3 - t * cosTh1;
segmentToBezierCache[argsString] = [
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
@ -138,7 +146,6 @@
ex = coords[5],
ey = coords[6],
segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
for (var i = 0; i < segs.length; i++) {
var bez = segmentToBezier.apply(this, segs[i]);
ctx.bezierCurveTo.apply(ctx, bez);

View file

@ -9,7 +9,9 @@
t, i, len = methodNames.length;
for (i = 0; i < len; i++) {
t = typeof object[methodNames[i]];
if (!(/^(?:function|object|unknown)$/).test(t)) return false;
if (!(/^(?:function|object|unknown)$/).test(t)) {
return false;
}
}
return true;
}

View file

@ -4,7 +4,9 @@
IS_DONTENUM_BUGGY = (function(){
for (var p in { toString: 1 }) {
if (p === 'toString') return false;
if (p === 'toString') {
return false;
}
}
return true;
})(),

View file

@ -1075,6 +1075,11 @@ test('toDataURL & reference to canvas', function() {
equal(object.shadow.blur, 10);
equal(object.shadow.offsetX, 5);
equal(object.shadow.offsetY, 15);
equal(object.setShadow(null), object, 'should be chainable');
ok(!(object.shadow instanceof fabric.Shadow));
equal(object.shadow, null);
});
test('set shadow', function() {

View file

@ -224,6 +224,20 @@
});
});
asyncTest('multiple M/m commands preserved as M/m commands', function() {
var el = getPathElement('M100 100 M 200 200 M150 50 m 300 300 m 400 -50 m 50 100');
fabric.Path.fromElement(el, function(obj) {
deepEqual(obj.path[0], ['M', 100, 100]);
deepEqual(obj.path[1], ['M', 200, 200]);
deepEqual(obj.path[2], ['M', 150, 50]);
deepEqual(obj.path[3], ['m', 300, 300]);
deepEqual(obj.path[4], ['m', 400, -50]);
deepEqual(obj.path[5], ['m', 50, 100]);
start();
});
});
asyncTest('compressed path commands', function() {
var el = getPathElement('M56.224 84.12c-.047.132-.138.221-.322.215.046-.131.137-.221.322-.215z');
fabric.Path.fromElement(el, function(obj) {