Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Marc Galang 2014-08-20 15:58:28 +03:00
commit 7111e6f2cc
56 changed files with 1855 additions and 1478 deletions

View file

@ -1,6 +1,6 @@
language: node_js
node_js:
- "0.10"
- "0.10.29"
script: 'npm run-script build && npm test'
before_install:
- sudo apt-get update -qq

992
dist/fabric.js vendored

File diff suppressed because it is too large Load diff

14
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.

992
dist/fabric.require.js vendored

File diff suppressed because it is too large Load diff

View file

@ -71,7 +71,9 @@ fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype
* @private
*/
_setShadow: function() {
if (!this.shadow) return;
if (!this.shadow) {
return;
}
var ctx = this.canvas.contextTop;

View file

@ -339,7 +339,9 @@
* @param {fabric.Object} target
*/
_shouldCenterTransform: function (e, target) {
if (!target) return;
if (!target) {
return;
}
var t = this._currentTransform,
centerTransform;
@ -403,7 +405,9 @@
* @param {fabric.Object} target
*/
_setupCurrentTransform: function (e, target) {
if (!target) return;
if (!target) {
return;
}
var pointer = this.getPointer(e),
corner = target._findTargetCorner(this.getPointer(e, true)),
@ -470,9 +474,12 @@
var t = this._currentTransform,
target = t.target,
lockScalingX = target.get('lockScalingX'),
lockScalingY = target.get('lockScalingY');
lockScalingY = target.get('lockScalingY'),
lockScalingFlip = target.get('lockScalingFlip');
if (lockScalingX && lockScalingY) return;
if (lockScalingX && lockScalingY) {
return;
}
// Get the constraint point
var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
@ -481,7 +488,7 @@
this._setLocalMouse(localMouse, t);
// Actually scale the object
this._setObjectScale(localMouse, t, lockScalingX, lockScalingY, by);
this._setObjectScale(localMouse, t, lockScalingX, lockScalingY, by, lockScalingFlip);
// Make sure the constraints apply
target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
@ -490,32 +497,36 @@
/**
* @private
*/
_setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by) {
var target = transform.target;
_setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip) {
var target = transform.target, forbidScalingX = false, forbidScalingY = false;
transform.newScaleX = target.scaleX;
transform.newScaleY = target.scaleY;
transform.newScaleX = localMouse.x / (target.width + target.strokeWidth);
transform.newScaleY = localMouse.y / (target.height + target.strokeWidth);
if (lockScalingFlip && transform.newScaleX <= 0 && transform.newScaleX < target.scaleX) {
forbidScalingX = true;
}
if (lockScalingFlip && transform.newScaleY <= 0 && transform.newScaleY < target.scaleY) {
forbidScalingY = true;
}
if (by === 'equally' && !lockScalingX && !lockScalingY) {
this._scaleObjectEqually(localMouse, target, transform);
forbidScalingX || forbidScalingY || this._scaleObjectEqually(localMouse, target, transform);
}
else if (!by) {
transform.newScaleX = localMouse.x / (target.width + target.strokeWidth);
transform.newScaleY = localMouse.y / (target.height + target.strokeWidth);
lockScalingX || target.set('scaleX', transform.newScaleX);
lockScalingY || target.set('scaleY', transform.newScaleY);
forbidScalingX || lockScalingX || target.set('scaleX', transform.newScaleX);
forbidScalingY || lockScalingY || target.set('scaleY', transform.newScaleY);
}
else if (by === 'x' && !target.get('lockUniScaling')) {
transform.newScaleX = localMouse.x / (target.width + target.strokeWidth);
lockScalingX || target.set('scaleX', transform.newScaleX);
forbidScalingX || lockScalingX || target.set('scaleX', transform.newScaleX);
}
else if (by === 'y' && !target.get('lockUniScaling')) {
transform.newScaleY = localMouse.y / (target.height + target.strokeWidth);
lockScalingY || target.set('scaleY', transform.newScaleY);
forbidScalingY || lockScalingY || target.set('scaleY', transform.newScaleY);
}
this._flipObject(transform);
forbidScalingX || forbidScalingY || this._flipObject(transform);
},
/**
@ -623,7 +634,9 @@
var t = this._currentTransform;
if (t.target.get('lockRotation')) return;
if (t.target.get('lockRotation')) {
return;
}
var lastAngle = atan2(t.ey - t.top, t.ex - t.left),
curAngle = atan2(y - t.top, x - t.left),
@ -722,7 +735,9 @@
* @param {Boolean} skipGroup when true, group is skipped and only objects are traversed through
*/
findTarget: function (e, skipGroup) {
if (this.skipTargetFind) return;
if (this.skipTargetFind) {
return;
}
if (this._isLastRenderedObject(e)) {
return this.lastRenderedObjectWithControlsAboveOverlay;
@ -1102,7 +1117,9 @@
*/
_drawObjectsControls: function(ctx) {
for (var i = 0, len = this._objects.length; i < len; ++i) {
if (!this._objects[i] || !this._objects[i].active) continue;
if (!this._objects[i] || !this._objects[i].active) {
continue;
}
this._objects[i]._renderControls(ctx);
this.lastRenderedObjectWithControlsAboveOverlay = this._objects[i];
}

View file

@ -394,7 +394,9 @@
*/
fabric.Color.sourceFromHsl = function(color) {
var match = color.match(Color.reHSLa);
if (!match) return;
if (!match) {
return;
}
var h = (((parseFloat(match[1]) % 360) + 360) % 360) / 360,
s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1),

View file

@ -43,6 +43,8 @@ fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
}
else {
var obj = klass.fromElement(el, this.options);
this.resolveGradient(obj, 'fill');
this.resolveGradient(obj, 'stroke');
this.reviver && this.reviver(el, obj);
this.instances[index] = obj;
this.checkIfDone();
@ -52,18 +54,32 @@ fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
fabric.ElementsParser.prototype.createCallback = function(index, el) {
var _this = this;
return function(obj) {
_this.resolveGradient(obj, 'fill');
_this.resolveGradient(obj, 'stroke');
_this.reviver && _this.reviver(el, obj);
_this.instances[index] = obj;
_this.checkIfDone();
};
};
fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
var instanceFillValue = obj.get(property);
if (!(/^url\(/).test(instanceFillValue)) {
return;
}
var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
if (fabric.gradientDefs[gradientId]) {
obj.set(property,
fabric.Gradient.fromElement(fabric.gradientDefs[gradientId], obj));
}
};
fabric.ElementsParser.prototype.checkIfDone = function() {
if (--this.numElements === 0) {
this.instances = this.instances.filter(function(el) {
return el != null;
});
fabric.resolveGradients(this.instances);
this.callback(this.instances);
}
};

View file

@ -38,12 +38,12 @@
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data,
tr, tg, tb,
r, g, b,
source,
tr, tg, tb,
r, g, b,
source,
isImage = false;
if(this.image){
if (this.image) {
// Blend images
isImage = true;
@ -51,12 +51,13 @@
_el.width = this.image.width;
_el.height = this.image.height;
var _tmp_canvas = new fabric.StaticCanvas(_el);
_tmp_canvas.add(this.image);
var context2 = _tmp_canvas.getContext('2d');
source = context2.getImageData(0, 0, _tmp_canvas.width, _tmp_canvas.height).data;
} else {
// Blend color
var tmpCanvas = new fabric.StaticCanvas(_el);
tmpCanvas.add(this.image);
var context2 = tmpCanvas.getContext('2d');
source = context2.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height).data;
}
else {
// Blend color
source = new fabric.Color(this.color).getSource();
tr = source[0] * this.alpha;
@ -70,22 +71,22 @@
g = data[i + 1];
b = data[i + 2];
if(isImage){
if (isImage) {
tr = source[i] * this.alpha;
tg = source[i + 1] * this.alpha;
tb = source[i + 2] * this.alpha;
}
switch(this.mode){
switch (this.mode) {
case 'multiply':
data[i] = r * tr / 255;
data[i + 1] = g * tg / 255;
data[i + 2] = b * tb / 255;
break;
case 'screen':
data[i] = 1 - (1-r) * (1-tr);
data[i + 1] = 1 - (1-g) * (1-tg);
data[i + 2] = 1 - (1-b) * (1-tb);
data[i] = 1 - (1 - r) * (1 - tr);
data[i + 1] = 1 - (1 - g) * (1 - tg);
data[i + 2] = 1 - (1 - b) * (1 - tb);
break;
case 'add':
data[i] = Math.min(255, r + tr);

View file

@ -125,7 +125,9 @@
scx = sx + cx - halfSide;
/* jshint maxdepth:5 */
if (scy < 0 || scy > sh || scx < 0 || scx > sw) continue;
if (scy < 0 || scy > sh || scx < 0 || scx > sw) {
continue;
}
var srcOff = (scy * sw + scx) * 4,
wt = weights[cy * side + cx];

View file

@ -41,7 +41,9 @@
* @param {Object} canvasEl Canvas element to apply filter to
*/
applyTo: function(canvasEl) {
if (!this.mask) return;
if (!this.mask) {
return;
}
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),

View file

@ -4,7 +4,7 @@
function getColorStop(el) {
var style = el.getAttribute('style'),
offset = el.getAttribute('offset'),
color, opacity;
color, colorAlpha, opacity;
// convert percents to absolute values
offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
@ -38,13 +38,15 @@
opacity = el.getAttribute('stop-opacity');
}
// convert rgba color to rgb color - alpha value has no affect in svg
color = new fabric.Color(color).toRgb();
color = new fabric.Color(color);
colorAlpha = color.getAlpha();
opacity = isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity);
opacity *= colorAlpha;
return {
offset: offset,
color: color,
opacity: isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity)
color: color.toRgb(),
opacity: opacity
};
}
@ -76,6 +78,19 @@
* @see {@link fabric.Gradient#initialize} for constructor definition
*/
fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
/*
* Stores the original position of the gradient when we convert from % to fixed values, for objectBoundingBox case.
* @type Number
* @default 0
*/
origX: 0,
/*
* Stores the original position of the gradient when we convert from % to fixed values, for objectBoundingBox case.
* @type Number
* @default 0
*/
origY: 0,
/**
* Constructor
@ -105,6 +120,11 @@
this.coords = coords;
this.gradientUnits = options.gradientUnits || 'objectBoundingBox';
this.colorStops = options.colorStops.slice();
if (options.gradientTransform) {
this.gradientTransform = options.gradientTransform;
}
this.origX = options.left || this.origX;
this.origY = options.top || this.origY;
},
/**
@ -146,7 +166,7 @@
*/
toSVG: function(object, normalize) {
var coords = fabric.util.object.clone(this.coords),
markup;
markup, commonAttributes;
// colorStops must be sorted ascending
this.colorStops.sort(function(a, b) {
@ -162,44 +182,52 @@
else if (this.gradientUnits === 'objectBoundingBox') {
_convertValuesToPercentUnits(object, coords);
}
commonAttributes = 'id="SVGID_' + this.id +
'" gradientUnits="' + this.gradientUnits + '"';
if (this.gradientTransform) {
commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
}
if (this.type === 'linear') {
markup = [
//jscs:disable validateIndentation
'<linearGradient ',
'id="SVGID_', this.id,
'" gradientUnits="', this.gradientUnits,
'" x1="', coords.x1,
commonAttributes,
' x1="', coords.x1,
'" y1="', coords.y1,
'" x2="', coords.x2,
'" y2="', coords.y2,
'">'
'">\n'
//jscs:enable validateIndentation
];
}
else if (this.type === 'radial') {
markup = [
//jscs:disable validateIndentation
'<radialGradient ',
'id="SVGID_', this.id,
'" gradientUnits="', this.gradientUnits,
'" cx="', coords.x2,
commonAttributes,
' cx="', coords.x2,
'" cy="', coords.y2,
'" r="', coords.r2,
'" fx="', coords.x1,
'" fy="', coords.y1,
'">'
'">\n'
//jscs:enable validateIndentation
];
}
for (var i = 0; i < this.colorStops.length; i++) {
markup.push(
//jscs:disable validateIndentation
'<stop ',
'offset="', (this.colorStops[i].offset * 100) + '%',
'" style="stop-color:', this.colorStops[i].color,
(this.colorStops[i].opacity ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
'"/>'
(this.colorStops[i].opacity != null ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
'"/>\n'
//jscs:enable validateIndentation
);
}
markup.push((this.type === 'linear' ? '</linearGradient>' : '</radialGradient>'));
markup.push((this.type === 'linear' ? '</linearGradient>\n' : '</radialGradient>\n'));
return markup.join('');
},
@ -213,7 +241,9 @@
toLive: function(ctx) {
var gradient;
if (!this.type) return;
if (!this.type) {
return;
}
if (this.type === 'linear') {
gradient = ctx.createLinearGradient(
@ -290,6 +320,7 @@
var colorStopEls = el.getElementsByTagName('stop'),
type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
gradientTransform = el.getAttribute('gradientTransform'),
colorStops = [],
coords = { };
@ -306,12 +337,18 @@
_convertPercentUnitsToValues(instance, coords);
return new fabric.Gradient({
var gradient = new fabric.Gradient({
type: type,
coords: coords,
gradientUnits: gradientUnits,
colorStops: colorStops
});
if (gradientTransform) {
gradient.gradientTransform = fabric.parseTransformAttribute(gradientTransform);
}
return gradient;
},
/* _FROM_SVG_END_ */
@ -337,23 +374,12 @@
if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
var percents = parseFloat(options[prop], 10);
if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
options[prop] = fabric.util.toFixed(object.width * percents / 100, 2);
options[prop] = fabric.util.toFixed(object.width * percents / 100, 2) + object.left;
}
else if (prop === 'y1' || prop === 'y2') {
options[prop] = fabric.util.toFixed(object.height * percents / 100, 2);
options[prop] = fabric.util.toFixed(object.height * percents / 100, 2) + object.top;
}
}
normalize(options, prop, object);
}
}
// normalize rendering point (should be from top/left corner rather than center of the shape)
function normalize(options, prop, object) {
if (prop === 'x1' || prop === 'x2') {
options[prop] -= fabric.util.toFixed(object.width / 2, 2);
}
else if (prop === 'y1' || prop === 'y2') {
options[prop] -= fabric.util.toFixed(object.height / 2, 2);
}
}
@ -363,15 +389,12 @@
*/
function _convertValuesToPercentUnits(object, options) {
for (var prop in options) {
normalize(options, prop, object);
// convert to percent units
//convert to percent units
if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
options[prop] = fabric.util.toFixed(options[prop] / object.width * 100, 2) + '%';
options[prop] = fabric.util.toFixed((options[prop] - object.fill.origX) / object.width * 100, 2) + '%';
}
else if (prop === 'y1' || prop === 'y2') {
options[prop] = fabric.util.toFixed(options[prop] / object.height * 100, 2) + '%';
options[prop] = fabric.util.toFixed((options[prop] - object.fill.origY) / object.height * 100, 2) + '%';
}
}
}

View file

@ -1,12 +1,12 @@
/**
* Wrapper around `console.log` (when available)
* @param {Any} values Values to log
* @param {Any} [values] Values to log
*/
fabric.log = function() { };
/**
* Wrapper around `console.warn` (when available)
* @param {Any} Values to log as a warning
* @param {Any} [values] Values to log as a warning
*/
fabric.warn = function() { };

View file

@ -119,8 +119,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
/**
* Animates object's properties
* @param {String|Object} property to animate (if string) or properties to animate (if object)
* @param {Number|Object} value to animate property to (if string was given first) or options object
* @param {String|Object} property Property to animate (if string) or properties to animate (if object)
* @param {Number|Object} value Value to animate property to (if string was given first) or options object
* @return {fabric.Object} thisArg
* @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#animation}
* @chainable
@ -208,11 +208,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
else {
_this.set(property, value);
}
if (skipCallbacks) return;
if (skipCallbacks) {
return;
}
options.onChange && options.onChange();
},
onComplete: function() {
if (skipCallbacks) return;
if (skipCallbacks) {
return;
}
_this.setCoords();
options.onComplete && options.onComplete();

View file

@ -379,7 +379,9 @@
// accept only left clicks
var isLeftClick = 'which' in e ? e.which === 1 : e.button === 1;
if (!isLeftClick && !fabric.isTouchSupported) return;
if (!isLeftClick && !fabric.isTouchSupported) {
return;
}
if (this.isDrawingMode) {
this._onMouseDownInDrawingMode(e);
@ -387,7 +389,9 @@
}
// ignore if some object is being transformed at this moment
if (this._currentTransform) return;
if (this._currentTransform) {
return;
}
var target = this.findTarget(e),
pointer = this.getPointer(e, true);

View file

@ -70,7 +70,9 @@
lockScalingX = target.get('lockScalingX'),
lockScalingY = target.get('lockScalingY');
if (lockScalingX && lockScalingY) return;
if (lockScalingX && lockScalingY) {
return;
}
target._scaling = true;
@ -97,7 +99,9 @@
_rotateObjectByAngle: function(curAngle) {
var t = this._currentTransform;
if (t.target.get('lockRotation')) return;
if (t.target.get('lockRotation')) {
return;
}
t.target.angle = radiansToDegrees(degreesToRadians(curAngle) + t.theta);
}
});

View file

@ -156,7 +156,9 @@
for (var i = this._objects.length; i--; ) {
currentObject = this._objects[i];
if (!currentObject || !currentObject.selectable || !currentObject.visible) continue;
if (!currentObject || !currentObject.selectable || !currentObject.visible) {
continue;
}
if (currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2) ||
currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2) ||
@ -167,7 +169,9 @@
group.push(currentObject);
// only add one object if it's a click
if (isClick) break;
if (isClick) {
break;
}
}
}

View file

@ -39,7 +39,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* });
*/
loadFromJSON: function (json, callback, reviver) {
if (!json) return;
if (!json) {
return;
}
// serialize if it wasn't already
var serialized = (typeof json === 'string')

View file

@ -71,6 +71,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
_createTextCharBg: function(styleDecl, lineLeftOffset, lineTopOffset, heightOfLine, charWidth, charOffset) {
return [
//jscs:disable validateIndentation
'<rect fill="', styleDecl.textBackgroundColor,
'" transform="translate(',
-this.width / 2, ' ',
@ -80,6 +81,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
'" width="', charWidth,
'" height="', heightOfLine,
'"></rect>'
//jscs:enable validateIndentation
].join('');
},
@ -96,6 +98,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
}, styleDecl));
return [
//jscs:disable validateIndentation
'<tspan x="', lineLeftOffset + charOffset, '" ',
yProp, '="', lineTopOffset, '" ',
@ -108,6 +111,7 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
fabric.util.string.escapeXml(_char),
'</tspan>'
//jscs:enable validateIndentation
].join('');
}
});

View file

@ -61,11 +61,12 @@
* @private
*/
_tick: function() {
if (this._abortCursorAnimation) {
return;
}
var _this = this;
if (this._abortCursorAnimation) return;
this.animate('_currentCursorOpacity', 1, {
duration: this.cursorDuration,
@ -88,7 +89,9 @@
* @private
*/
_onTickComplete: function() {
if (this._abortCursorAnimation) return;
if (this._abortCursorAnimation) {
return;
}
var _this = this;
if (this._cursorTimeout1) {
@ -315,7 +318,9 @@
* @chainable
*/
enterEditing: function() {
if (this.isEditing || !this.editable) return;
if (this.isEditing || !this.editable) {
return;
}
this.exitEditingOnOthers();
@ -364,7 +369,9 @@
* @private
*/
_updateTextarea: function() {
if (!this.hiddenTextarea) return;
if (!this.hiddenTextarea) {
return;
}
this.hiddenTextarea.value = this.text;
this.hiddenTextarea.selectionStart = this.selectionStart;
@ -389,7 +396,9 @@
* @private
*/
_restoreEditingProps: function() {
if (!this._savedProps) return;
if (!this._savedProps) {
return;
}
this.hoverCursor = this._savedProps.overCursor;
this.hasControls = this._savedProps.hasControls;
@ -575,7 +584,9 @@
insertStyleObjects: function(_chars, isEndOfLine, styles) {
// short-circuit
if (this.isEmptyStyles()) return;
if (this.isEmptyStyles()) {
return;
}
var cursorLocation = this.get2DCursorLocation(),
lineIndex = cursorLocation.lineIndex,

View file

@ -112,7 +112,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
initMousemoveHandler: function() {
this.on('mousemove', function(options) {
if (!this.__isMousedown || !this.isEditing) return;
if (!this.__isMousedown || !this.isEditing) {
return;
}
var newSelectionStart = this.getSelectionStartFromPointer(options.e);
@ -143,7 +145,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
initMouseupHandler: function() {
this.on('mouseup', function(options) {
this.__isMousedown = false;
if (this._isObjectMoved(options.e)) return;
if (this._isObjectMoved(options.e)) {
return;
}
if (this.__lastSelected) {
this.enterEditing();

View file

@ -53,7 +53,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e Event object
*/
onKeyDown: function(e) {
if (!this.isEditing) return;
if (!this.isEditing) {
return;
}
if (e.keyCode in this._keysMap) {
this[this._keysMap[e.keyCode]](e);
@ -442,7 +444,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e Event object
*/
moveCursorLeft: function(e) {
if (this.selectionStart === 0 && this.selectionEnd === 0) return;
if (this.selectionStart === 0 && this.selectionEnd === 0) {
return;
}
this.abortCursorAnimation();
this._currentCursorOpacity = 1;
@ -528,7 +532,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e Event object
*/
moveCursorRight: function(e) {
if (this.selectionStart >= this.text.length && this.selectionEnd >= this.text.length) return;
if (this.selectionStart >= this.text.length && this.selectionEnd >= this.text.length) {
return;
}
this.abortCursorAnimation();
this._currentCursorOpacity = 1;

View file

@ -10,7 +10,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
var fill = this.fill
? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
: 'none',
fillRule = (this.fillRule === 'destination-over' ? 'evenodd' : this.fillRule),
stroke = this.stroke
? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
: 'none',
@ -33,6 +33,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
'stroke-linejoin: ', strokeLineJoin, '; ',
'stroke-miterlimit: ', strokeMiterLimit, '; ',
'fill: ', fill, '; ',
'fill-rule: ', fillRule, '; ',
'opacity: ', opacity, ';',
filter,
visibility
@ -44,6 +45,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
* @return {String}
*/
getSvgTransform: function() {
if (this.group) return '';
var toFixed = fabric.util.toFixed,
angle = this.getAngle(),
vpt = this.getViewportTransform(),
@ -51,7 +53,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
translatePart = 'translate(' +
translatePart = this.type === 'path-group' ? '' : 'translate(' +
toFixed(center.x, NUM_FRACTION_DIGITS) +
' ' +
toFixed(center.y, NUM_FRACTION_DIGITS) +
@ -68,16 +70,24 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
' ' +
toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
')'),
flipXPart = this.flipX ? 'matrix(-1 0 0 1 0 0) ' : '',
flipYPart = this.flipY ? 'matrix(1 0 0 -1 0 0)' : '';
addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0,
flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '',
addTranslateY = this.type === 'path-group' ? this.height * vpt[3] : 0,
flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : '';
return [
translatePart, anglePart, scalePart, flipXPart, flipYPart
].join('');
},
/**
* Returns transform-string for svg-export from the transform matrix of single elements
* @return {String}
*/
getSvgTransformMatrix: function() {
return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
},
/**
* @private
*/

View file

@ -1,7 +1,9 @@
(function(){
var degreesToRadians = fabric.util.degreesToRadians,
//jscs:disable requireCamelCaseOrUpperCaseIdentifiers
isVML = function() { return typeof G_vmlCanvasManager !== 'undefined'; };
//jscs:enable requireCamelCaseOrUpperCaseIdentifiers
fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
@ -262,7 +264,9 @@
* @chainable
*/
drawBorders: function(ctx) {
if (!this.hasBorders) return this;
if (!this.hasBorders) {
return this;
}
var padding = this.padding,
padding2 = padding * 2,
@ -337,7 +341,9 @@
* @chainable
*/
drawControls: function(ctx) {
if (!this.hasControls) return this;
if (!this.hasControls) {
return this;
}
var size = this.cornerSize,
size2 = size / 2,

View file

@ -6,7 +6,9 @@
* @param {Function} handler
*/
function _removeEventListener(eventName, handler) {
if (!this.__eventListeners[eventName]) return;
if (!this.__eventListeners[eventName]) {
return;
}
if (handler) {
fabric.util.removeFromArray(this.__eventListeners[eventName], handler);
@ -57,7 +59,9 @@
* @chainable
*/
function stopObserving(eventName, handler) {
if (!this.__eventListeners) return;
if (!this.__eventListeners) {
return;
}
// remove all key/value pairs (event name -> event handler)
if (arguments.length === 0) {
@ -86,10 +90,15 @@
* @chainable
*/
function fire(eventName, options) {
if (!this.__eventListeners) return;
if (!this.__eventListeners) {
return;
}
var listenersForEvent = this.__eventListeners[eventName];
if (!listenersForEvent) return;
if (!listenersForEvent) {
return;
}
for (var i = 0, len = listenersForEvent.length; i < len; i++) {
// avoiding try/catch for perf. reasons
listenersForEvent[i].call(this, options || { });

View file

@ -101,9 +101,13 @@
function _setStrokeFillOpacity(attributes) {
for (var attr in colorAttributes) {
if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') continue;
if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') {
continue;
}
if (attributes[attr].indexOf('url(') === 0) continue;
if (attributes[attr].indexOf('url(') === 0) {
continue;
}
var color = new fabric.Color(attributes[attr]);
attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
@ -271,7 +275,9 @@
// TODO: support non-px font size
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(?:\/(normal|[\d\.]+))?\s+(.*)/);
if (!match) return;
if (!match) {
return;
}
var fontStyle = match[1],
// font variant is not used
@ -324,7 +330,9 @@
function parseStyleObject(style, oStyle) {
var attr, value;
for (var prop in style) {
if (typeof style[prop] === 'undefined') continue;
if (typeof style[prop] === 'undefined') {
continue;
}
attr = normalizeAttr(prop.toLowerCase());
value = normalizeValue(attr, style[prop]);
@ -343,7 +351,7 @@
*/
function getGlobalStylesForElement(element) {
var styles = { };
for (var rule in fabric.cssRules) {
if (elementMatchesRule(element, rule.split(' '))) {
for (var property in fabric.cssRules[rule]) {
@ -419,7 +427,9 @@
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') continue;
if (attr.nodeName === 'x' || attr.nodeName === 'y' || attr.nodeName === 'xlink:href') {
continue;
}
if (attr.nodeName === 'transform') {
currentTrans = currentTrans + ' ' + attr.nodeValue;
@ -441,7 +451,9 @@
*/
function addSvgTransform(doc, matrix) {
matrix[3] = matrix[0] = (matrix[0] > matrix[3] ? matrix[3] : matrix[0]);
if (!(matrix[0] !== 1 || matrix[3] !== 1 || matrix[4] !== 0 || matrix[5] !== 0)) return;
if (!(matrix[0] !== 1 || matrix[3] !== 1 || matrix[4] !== 0 || matrix[5] !== 0)) {
return;
}
// default is to preserve aspect ratio
// preserveAspectRatio attribute to be implemented
var el = doc.ownerDocument.createElement('g');
@ -490,14 +502,16 @@
}
return function(doc, callback, reviver) {
if (!doc) return;
if (!doc) {
return;
}
var startTime = new Date();
parseUseDirectives(doc);
var viewBoxAttr = doc.getAttribute('viewBox'),
widthAttr = parseFloat(doc.getAttribute('width')),
heightAttr = parseFloat(doc.getAttribute('height')),
widthAttr = parseUnit(doc.getAttribute('width')),
heightAttr = parseUnit(doc.getAttribute('height')),
viewBoxWidth,
viewBoxHeight;
@ -513,7 +527,7 @@
if (heightAttr && heightAttr !== viewBoxHeight) {
scaleY = heightAttr / viewBoxHeight;
}
addSvgTransform(doc, [scaleX, 0, 0, scaleY, -minX, -minY]);
addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
}
var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
@ -618,25 +632,6 @@
extend(fabric, {
/**
* Initializes gradients on instances, according to gradients parsed from a document
* @param {Array} instances
*/
resolveGradients: function(instances) {
for (var i = instances.length; i--; ) {
var instanceFillValue = instances[i].get('fill');
if (!(/^url\(/).test(instanceFillValue)) continue;
var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
if (fabric.gradientDefs[gradientId]) {
instances[i].set('fill',
fabric.Gradient.fromElement(fabric.gradientDefs[gradientId], instances[i]));
}
}
},
/**
* Parses an SVG document, returning all of the gradient declarations found in it
* @static
@ -648,21 +643,36 @@
getGradientDefs: function(doc) {
var linearGradientEls = doc.getElementsByTagName('linearGradient'),
radialGradientEls = doc.getElementsByTagName('radialGradient'),
el, i,
gradientDefs = { };
el, i, j = 0, id, xlink, elList = [ ],
gradientDefs = { }, idsToXlinkMap = { };
elList.length = linearGradientEls.length + radialGradientEls.length;
i = linearGradientEls.length;
for (; i--; ) {
el = linearGradientEls[i];
gradientDefs[el.getAttribute('id')] = el;
while (i--) {
elList[j++] = linearGradientEls[i];
}
i = radialGradientEls.length;
for (; i--; ) {
el = radialGradientEls[i];
gradientDefs[el.getAttribute('id')] = el;
while (i--) {
elList[j++] = radialGradientEls[i];
}
while (j--) {
el = elList[j];
xlink = el.getAttribute('xlink:href');
id = el.getAttribute('id');
if (xlink) {
idsToXlinkMap[id] = xlink.substr(1);
}
gradientDefs[id] = el;
}
for (id in idsToXlinkMap) {
var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
el = gradientDefs[id];
while (el2.firstChild) {
el.appendChild(el2.firstChild);
}
}
return gradientDefs;
},
@ -684,8 +694,8 @@
var value,
parentAttributes = { };
// if there's a parent container (`g` node), parse its attributes recursively upwards
if (element.parentNode && /^[g|a]$/i.test(element.parentNode.nodeName)) {
// if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards
if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
parentAttributes = fabric.parseAttributes(element.parentNode, attributes);
}
@ -756,7 +766,9 @@
parsePointsAttribute: function(points) {
// points attribute is required and must not be empty
if (!points) return null;
if (!points) {
return null;
}
// replace commas with whitespace and remove bookending whitespace
points = points.replace(/,/g, ' ').trim();
@ -858,7 +870,9 @@
//IE chokes on DOCTYPE
xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,''));
}
if (!xml || !xml.documentElement) return;
if (!xml || !xml.documentElement) {
return;
}
fabric.parseSVGDocument(xml.documentElement, function (results, options) {
svgCache.set(url, {
@ -907,23 +921,29 @@
var markup = '';
for (var i = 0, len = objects.length; i < len; i++) {
if (objects[i].type !== 'text' || !objects[i].path) continue;
if (objects[i].type !== 'text' || !objects[i].path) {
continue;
}
markup += [
//jscs:disable validateIndentation
'@font-face {',
'font-family: ', objects[i].fontFamily, '; ',
'src: url(\'', objects[i].path, '\')',
'}'
//jscs:enable validateIndentation
].join('');
}
if (markup) {
markup = [
//jscs:disable validateIndentation
'<style type="text/css">',
'<![CDATA[',
markup,
']]>',
'</style>'
//jscs:enable validateIndentation
].join('');
}

View file

@ -79,15 +79,19 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
if (this.group) {
x = this.left + this.radius;
y = this.top + this.radius;
}
markup.push(
'<circle ',
'cx="0" cy="0" ',
'cx="' + x + '" cy="' + y + '" ',
'r="', this.radius,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@ -101,11 +105,9 @@
*/
_render: function(ctx, noTransform) {
ctx.beginPath();
// 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.arc(noTransform ? this.left + this.radius : 0, noTransform ? this.top + this.radius : 0, this.radius, 0, piBy2, false);
this._renderFill(ctx);
this.stroke && this._renderStroke(ctx);
this._renderStroke(ctx);
},
/**
@ -169,22 +171,13 @@
throw new Error('value of `r` attribute is required and can not be negative');
}
if (!('left' in parsedAttributes)) {
parsedAttributes.left = 0;
}
if (!('top' in parsedAttributes)) {
parsedAttributes.top = 0;
}
if (!('transformMatrix' in parsedAttributes)) {
parsedAttributes.left -= options.width ? (options.width / 2) : 0;
parsedAttributes.top -= options.height ? (options.height / 2) : 0;
}
parsedAttributes.left = parsedAttributes.left || 0;
parsedAttributes.top = parsedAttributes.top || 0;
var obj = new fabric.Circle(extend(parsedAttributes, options));
obj.cx = parseFloat(element.getAttribute('cx')) || 0;
obj.cy = parseFloat(element.getAttribute('cy')) || 0;
obj.left -= obj.radius;
obj.top -= obj.radius;
return obj;
};

View file

@ -77,32 +77,26 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
if (this.group) {
x = this.left + this.rx;
y = this.top + this.ry;
}
markup.push(
'<ellipse ',
'rx="', this.get('rx'),
'" ry="', this.get('ry'),
'cx="', x, '" cy="', y, '" ',
'rx="', this.rx,
'" ry="', this.ry,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
},
/* _TO_SVG_END_ */
/**
* Renders this instance on a given context
* @param {CanvasRenderingContext2D} ctx context to render on
* @param {Boolean} [noTransform] When true, context is not transformed
*/
render: function(ctx, noTransform) {
// do not use `get` for perf. reasons
if (this.rx === 0 || this.ry === 0) return;
return this.callSuper('render', ctx, noTransform);
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx context to render on
@ -110,10 +104,9 @@
*/
_render: function(ctx, noTransform) {
ctx.beginPath();
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
ctx.save();
ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
ctx.arc(noTransform ? this.left : 0, noTransform ? this.top * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
ctx.arc(noTransform ? this.left + this.rx : 0, noTransform ? (this.top + this.ry) * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
ctx.restore();
this._renderFill(ctx);
this._renderStroke(ctx);
@ -150,21 +143,13 @@
var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
if (!('left' in parsedAttributes)) {
parsedAttributes.left = 0;
}
if (!('top' in parsedAttributes)) {
parsedAttributes.top = 0;
}
if (!('transformMatrix' in parsedAttributes)) {
parsedAttributes.left -= options.width ? (options.width / 2) : 0;
parsedAttributes.top -= options.height ? (options.height / 2) : 0;
}
parsedAttributes.left = parsedAttributes.left || 0;
parsedAttributes.top = parsedAttributes.top || 0;
var ellipse = new fabric.Ellipse(extend(parsedAttributes, options));
ellipse.cx = parseFloat(element.getAttribute('cx')) || 0;
ellipse.cy = parseFloat(element.getAttribute('cy')) || 0;
ellipse.top -= ellipse.ry;
ellipse.left -= ellipse.rx;
return ellipse;
};
/* _FROM_SVG_END_ */

View file

@ -115,8 +115,8 @@
addWithUpdate: function(object) {
this._restoreObjectsState();
if (object) {
this._objects.push(object);
object.group = this;
this._objects.push(object);
object.group = this;
}
// since _restoreObjectsState set objects inactive
this.forEachObject(this._setObjectActive, this);
@ -218,7 +218,9 @@
*/
render: function(ctx) {
// do not render if object is not visible
if (!this.visible) return;
if (!this.visible) {
return;
}
ctx.save();
this.clipTo && fabric.util.clipContext(this, ctx);
@ -252,7 +254,9 @@
var originalHasRotatingPoint = object.hasRotatingPoint;
// do not render if object is not visible
if (!object.visible) return;
if (!object.visible) {
return;
}
object.hasRotatingPoint = false;
@ -475,16 +479,18 @@
*/
toSVG: function(reviver) {
var markup = [
//jscs:disable validateIndentation
'<g ',
'transform="', this.getSvgTransform(),
'">'
'">\n'
//jscs:enable validateIndentation
];
for (var i = 0, len = this._objects.length; i < len; i++) {
markup.push(this._objects[i].toSVG(reviver));
}
markup.push('</g>');
markup.push('</g>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},

View file

@ -112,44 +112,6 @@
};
},
/**
* Renders image on a specified context
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Boolean} [noTransform] When true, context is not transformed
*/
render: function(ctx, noTransform) {
// do not render if object is not visible
if (!this.visible) return;
ctx.save();
var m = this.transformMatrix,
isInPathGroup = this.group && this.group.type === 'path-group';
// this._resetWidthHeight();
if (isInPathGroup) {
ctx.translate(-this.group.width/2, -this.group.height/2);
}
if (m) {
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
if (!noTransform) {
this.transform(ctx);
}
if (isInPathGroup) {
ctx.translate(this.width/2, this.height/2);
}
this._setShadow(ctx);
this.clipTo && fabric.util.clipContext(this, ctx);
this._render(ctx);
if (this.shadow && !this.shadow.affectStroke) {
this._removeShadow(ctx);
}
this._renderStroke(ctx);
this.clipTo && ctx.restore();
ctx.restore();
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@ -207,20 +169,23 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = [];
var markup = [], x = -this.width / 2, y = -this.height / 2;
if (this.group) {
x = this.left;
y = this.top;
}
markup.push(
'<g transform="', this.getSvgTransform(), '">',
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
'<image xlink:href="', this.getSvgSrc(),
'" x="', x, '" y="', y,
'" style="', this.getSvgStyles(),
// we're essentially moving origin of transformation from top/left corner to the center of the shape
// by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
// so that object's center aligns with container's left/top
'" transform="translate(' + (-this.width/2) + ' ' + (-this.height/2) + ')',
'" width="', this.width,
'" height="', this.height,
'" preserveAspectRatio="none"',
'></image>'
'></image>\n'
);
if (this.stroke || this.strokeDashArray) {
@ -228,15 +193,15 @@
this.fill = null;
markup.push(
'<rect ',
'x="', (-1 * this.width / 2), '" y="', (-1 * this.height / 2),
'x="', x, '" y="', y,
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'"/>'
'"/>\n'
);
this.fill = origFill;
}
markup.push('</g>');
markup.push('</g>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},
@ -330,15 +295,16 @@
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
_render: function(ctx, noTransform) {
this._element &&
ctx.drawImage(
this._element,
-this.width / 2,
-this.height / 2,
noTransform ? this.left : -this.width/2,
noTransform ? this.top : -this.height/2,
this.width,
this.height
);
this._renderStroke(ctx);
},
/**

View file

@ -201,7 +201,9 @@
* Returns true if object has no styling
*/
isEmptyStyles: function() {
if (!this.styles) return true;
if (!this.styles) {
return true;
}
var obj = this.styles;
for (var p1 in obj) {
@ -313,7 +315,9 @@
* Renders cursor or selection (depending on what exists)
*/
renderCursorOrSelection: function() {
if (!this.active) return;
if (!this.active) {
return;
}
var chars = this.text.split(''),
boundaries;
@ -729,7 +733,9 @@
fontSize = (styleDeclaration ? styleDeclaration.fontSize : null) || this.fontSize;
if (!textDecoration) return;
if (!textDecoration) {
return;
}
if (textDecoration.indexOf('underline') > -1) {
this._renderCharDecorationAtOffset(
@ -800,7 +806,9 @@
* @param {Array} textLines Array of all text lines
*/
_renderTextLinesBackground: function(ctx, textLines) {
if (!this.textBackgroundColor && !this.styles) return;
if (!this.textBackgroundColor && !this.styles) {
return;
}
ctx.save();
@ -1101,7 +1109,9 @@
* @private
*/
_renderTextBoxBackground: function(ctx) {
if (!this.backgroundColor) return;
if (!this.backgroundColor) {
return;
}
ctx.save();
ctx.fillStyle = this.backgroundColor;

View file

@ -150,11 +150,10 @@
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
_render: function(ctx, noTransform) {
ctx.beginPath();
var isInPathGroup = this.group && this.group.type === 'path-group';
if (isInPathGroup) {
if (noTransform) {
// Line coords are distances from left-top of canvas to origin of line.
//
// To render line in a path-group, we need to translate them to
@ -164,7 +163,6 @@
cp.x,
cp.y
);
if (!this.transformMatrix) ctx.translate(-this.group.width / 2, -this.group.height / 2);
}
if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
@ -232,16 +230,22 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), addTranslate = '';
if (!this.group) {
var x = - this.width / 2 - (this.x1 > this.x2 ? this.x2 : this.x1),
y = - this.height / 2 - (this.y1 > this.y2 ? this.y2 : this.y1);
addTranslate = 'translate(' + x + ', ' + y + ') ';
}
markup.push(
'<line ',
'x1="', this.get('x1'),
'" y1="', this.get('y1'),
'" x2="', this.get('x2'),
'" y2="', this.get('y2'),
'x1="', this.x1,
'" y1="', this.y1,
'" x2="', this.x2,
'" y2="', this.y2,
'" style="', this.getSvgStyles(),
'"/>'
'" transform="', this.getSvgTransform(), addTranslate,
this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');

View file

@ -658,6 +658,13 @@
*/
lockUniScaling: false,
/**
* When `true`, object cannot be flipped by scaling into negative values
* @type Boolean
* @default
*/
lockScalingFlip: false,
/**
* List of properties to consider when checking if state
* of an object is changed (fabric.Object#hasStateChanged)
@ -708,7 +715,9 @@
* @param {Object} [options] Options object
*/
_initClipping: function(options) {
if (!options.clipTo || typeof options.clipTo !== 'string') return;
if (!options.clipTo || typeof options.clipTo !== 'string') {
return;
}
var functionBody = fabric.util.getFunctionBody(options.clipTo);
if (typeof functionBody !== 'undefined') {
@ -932,8 +941,9 @@
* @return {Boolean} flipY value // TODO
*/
getViewportTransform: function() {
if (this.canvas && this.canvas.viewportTransform)
if (this.canvas && this.canvas.viewportTransform) {
return this.canvas.viewportTransform;
}
return [1, 0, 0, 1, 0, 0];
},
@ -944,7 +954,9 @@
*/
render: function(ctx, noTransform) {
// do not render if width/height are zeros or object is not visible
if (this.width === 0 || this.height === 0 || !this.visible) return;
if (this.width === 0 || this.height === 0 || !this.visible) {
return;
}
ctx.save();
@ -955,12 +967,14 @@
this._setStrokeStyles(ctx);
this._setFillStyles(ctx);
var m = this.transformMatrix;
if (m && this.group) {
if (this.group && this.group.type === 'path-group') {
ctx.translate(-this.group.width/2, -this.group.height/2);
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
var m = this.transformMatrix;
if (m) {
ctx.transform.apply(ctx, m);
}
}
ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
this._setShadow(ctx);
this.clipTo && fabric.util.clipContext(this, ctx);
this._render(ctx, noTransform);
@ -975,7 +989,7 @@
var m = this.transformMatrix;
if (m && !this.group) {
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
ctx.setTransform.apply(ctx, m);
}
if (!noTransform) {
this.transform(ctx);
@ -1036,7 +1050,9 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_setShadow: function(ctx) {
if (!this.shadow) return;
if (!this.shadow) {
return;
}
ctx.shadowColor = this.shadow.color;
ctx.shadowBlur = this.shadow.blur;
@ -1049,7 +1065,9 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_removeShadow: function(ctx) {
if (!this.shadow) return;
if (!this.shadow) {
return;
}
ctx.shadowColor = '';
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
@ -1060,23 +1078,27 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderFill: function(ctx) {
if (!this.fill) return;
if (!this.fill) {
return;
}
ctx.save();
if (this.fill.toLive) {
ctx.save();
ctx.translate(
-this.width / 2 + this.fill.offsetX || 0,
-this.height / 2 + this.fill.offsetY || 0);
}
if (this.fill.gradientTransform) {
var g = this.fill.gradientTransform;
ctx.transform.apply(ctx, g);
}
if (this.fillRule === 'destination-over') {
ctx.fill('evenodd');
}
else {
ctx.fill();
}
if (this.fill.toLive) {
ctx.restore();
}
ctx.restore();
if (this.shadow && !this.shadow.affectStroke) {
this._removeShadow(ctx);
}
@ -1087,7 +1109,9 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderStroke: function(ctx) {
if (!this.stroke || this.strokeWidth === 0) return;
if (!this.stroke || this.strokeWidth === 0) {
return;
}
ctx.save();
if (this.strokeDashArray) {
@ -1106,6 +1130,10 @@
ctx.stroke();
}
else {
if (this.stroke.gradientTransform) {
var g = this.stroke.gradientTransform;
ctx.transform.apply(ctx, g);
}
this._stroke ? this._stroke(ctx) : ctx.stroke();
}
this._removeShadow(ctx);

View file

@ -94,7 +94,9 @@
// one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
: path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
if (!this.path) return;
if (!this.path) {
return;
}
if (!fromArray) {
this.path = this._parsePath();
@ -455,12 +457,14 @@
*/
render: function(ctx, noTransform) {
// do not render if object is not visible
if (!this.visible) return;
if (!this.visible) {
return;
}
ctx.save();
if (noTransform) {
ctx.translate(-this.width/2, -this.height/2);
}
ctx.translate(-this.width/2, -this.height/2);
}
var m = this.transformMatrix;
if (m) {
@ -541,14 +545,14 @@
var path = chunks.join(' ');
markup.push(
'<g transform="', (this.group ? '' : this.getSvgTransform()), '">',
'<path ',
'd="', path,
'" style="', this.getSvgStyles(),
'" transform="translate(', (-this.width / 2), ' ', (-this.height/2), ')',
'" stroke-linecap="round" ',
'/>',
'</g>'
//jscs:disable validateIndentation
'<path ',
'd="', path,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
'/>\n'
//jscs:enable validateIndentation
);
return reviver ? reviver(markup.join('')) : markup.join('');

View file

@ -72,7 +72,9 @@
*/
render: function(ctx) {
// do not render if object is not visible
if (!this.visible) return;
if (!this.visible) {
return;
}
ctx.save();
@ -147,17 +149,20 @@
*/
toSVG: function(reviver) {
var objects = this.getObjects(),
translatePart = 'translate(' + this.left + ' ' + this.top + ')',
markup = [
//jscs:disable validateIndentation
'<g ',
'style="', this.getSvgStyles(), '" ',
'transform="', this.getSvgTransform(), '" ',
'>'
'transform="', translatePart, this.getSvgTransform(), '" ',
'>\n'
//jscs:enable validateIndentation
];
for (var i = 0, len = objects.length; i < len; i++) {
markup.push(objects[i].toSVG(reviver));
}
markup.push('</g>');
markup.push('</g>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},

View file

@ -67,7 +67,9 @@
this.minX = minX;
this.minY = minY;
if (skipOffset) return;
if (skipOffset) {
return;
}
var halfWidth = this.width / 2 + this.minX,
halfHeight = this.height / 2 + this.minY;
@ -109,7 +111,8 @@
'points="', points.join(''),
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@ -123,7 +126,6 @@
_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];
@ -182,14 +184,16 @@
if (!element) {
return null;
}
options || (options = { });
var points = fabric.parsePointsAttribute(element.getAttribute('points')),
parsedAttributes = fabric.parseAttributes(element, fabric.Polygon.ATTRIBUTE_NAMES);
if (!('transformMatrix' in parsedAttributes)) {
fabric.util.normalizePoints(points, options);
if (points === null) {
return null;
}
return new fabric.Polygon(points, extend(parsedAttributes, options), true);
};
/* _FROM_SVG_END_ */

View file

@ -95,7 +95,8 @@
'points="', points.join(''),
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>'
' ', this.getSvgTransformMatrix(),
'"/>\n'
);
return reviver ? reviver(markup.join('')) : markup.join('');
@ -169,9 +170,10 @@
var points = fabric.parsePointsAttribute(element.getAttribute('points')),
parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
if (!('transformMatrix' in parsedAttributes)) {
fabric.util.normalizePoints(points, options);
if (points === null) {
return null;
}
return new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options), true);
};
/* _FROM_SVG_END_ */

View file

@ -50,17 +50,6 @@
*/
ry: 0,
/**
* @type Number
* @default
*/
x: 0,
/**
* @type Number
* @default
*/
y: 0,
/**
* Used to specify dash pattern for stroke on this object
@ -79,8 +68,6 @@
this.callSuper('initialize', options);
this._initRxRy();
this.x = options.x || 0;
this.y = options.y || 0;
},
/**
@ -100,7 +87,7 @@
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
_render: function(ctx, noTransform) {
// optimize 1x1 case (used in spray brush)
if (this.width === 1 && this.height === 1) {
@ -112,24 +99,15 @@
ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
w = this.width,
h = this.height,
x = -w / 2,
y = -h / 2,
isInPathGroup = this.group && this.group.type === 'path-group',
x = noTransform ? this.left : 0,
y = noTransform ? this.top : 0,
isRounded = rx !== 0 || ry !== 0,
k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
ctx.beginPath();
ctx.globalAlpha = isInPathGroup ? (ctx.globalAlpha * this.opacity) : this.opacity;
if (this.transformMatrix && isInPathGroup) {
ctx.translate(
this.width / 2 + this.x,
this.height / 2 + this.y);
}
if (!this.transformMatrix && isInPathGroup) {
ctx.translate(
-this.group.width / 2 + this.width / 2 + this.x,
-this.group.height / 2 + this.height / 2 + this.y);
if (!noTransform) {
ctx.translate(-this.width / 2, -this.height / 2);
}
ctx.moveTo(x + rx, y);
@ -170,22 +148,6 @@
ctx.closePath();
},
/**
* Since coordinate system differs from that of SVG
* @private
*/
_normalizeLeftTopProperties: function(parsedAttributes) {
if ('left' in parsedAttributes) {
this.set('left', parsedAttributes.left + this.getWidth() / 2);
}
this.set('x', parsedAttributes.left || 0);
if ('top' in parsedAttributes) {
this.set('top', parsedAttributes.top + this.getHeight() / 2);
}
this.set('y', parsedAttributes.top || 0);
return this;
},
/**
* Returns object representation of an instance
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
@ -194,9 +156,7 @@
toObject: function(propertiesToInclude) {
var object = extend(this.callSuper('toObject', propertiesToInclude), {
rx: this.get('rx') || 0,
ry: this.get('ry') || 0,
x: this.get('x'),
y: this.get('y')
ry: this.get('ry') || 0
});
if (!this.includeDefaultValues) {
this._removeDefaultValues(object);
@ -211,16 +171,20 @@
* @return {String} svg representation of an instance
*/
toSVG: function(reviver) {
var markup = this._createBaseSVGMarkup();
var markup = this._createBaseSVGMarkup(), x = this.left, y = this.top;
if (!this.group) {
x = -this.width / 2;
y = -this.height / 2;
}
markup.push(
'<rect ',
'x="', (-1 * this.width / 2), '" y="', (-1 * this.height / 2),
'x="', x, '" y="', y,
'" rx="', this.get('rx'), '" ry="', this.get('ry'),
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'" transform="', this.getSvgTransform(),
'"/>');
this.getSvgTransformMatrix(),
'"/>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
},
@ -244,15 +208,6 @@
*/
fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));
/**
* @private
*/
function _setDefaultLeftTopValues(attributes) {
attributes.left = attributes.left || 0;
attributes.top = attributes.top || 0;
return attributes;
}
/**
* Returns {@link fabric.Rect} instance from an SVG element
* @static
@ -265,14 +220,14 @@
if (!element) {
return null;
}
options = options || { };
var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
parsedAttributes = _setDefaultLeftTopValues(parsedAttributes);
var rect = new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
rect._normalizeLeftTopProperties(parsedAttributes);
return rect;
parsedAttributes.left = parsedAttributes.left || 0;
parsedAttributes.top = parsedAttributes.top || 0;
return new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
};
/* _FROM_SVG_END_ */

View file

@ -326,7 +326,9 @@
* @private
*/
_initDimensions: function() {
if (this.__skipDimension) return;
if (this.__skipDimension) {
return;
}
var canvasEl = fabric.util.createCanvasElement();
this._render(canvasEl.getContext('2d'));
},
@ -550,7 +552,9 @@
* @param {Array} textLines Array of all text lines
*/
_renderTextFill: function(ctx, textLines) {
if (!this.fill && !this._skipFillStrokeCheck) return;
if (!this.fill && !this._skipFillStrokeCheck) {
return;
}
this._boundaries = [ ];
var lineHeights = 0;
@ -576,7 +580,9 @@
* @param {Array} textLines Array of all text lines
*/
_renderTextStroke: function(ctx, textLines) {
if ((!this.stroke || this.strokeWidth === 0) && !this._skipFillStrokeCheck) return;
if ((!this.stroke || this.strokeWidth === 0) && !this._skipFillStrokeCheck) {
return;
}
var lineHeights = 0;
@ -626,7 +632,9 @@
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderTextBoxBackground: function(ctx) {
if (!this.backgroundColor) return;
if (!this.backgroundColor) {
return;
}
ctx.save();
ctx.fillStyle = this.backgroundColor;
@ -647,7 +655,9 @@
* @param {Array} textLines Array of all text lines
*/
_renderTextLinesBackground: function(ctx, textLines) {
if (!this.textBackgroundColor) return;
if (!this.textBackgroundColor) {
return;
}
ctx.save();
ctx.fillStyle = this.textBackgroundColor;
@ -703,7 +713,9 @@
* @param {Array} textLines Array of all text lines
*/
_renderTextDecoration: function(ctx, textLines) {
if (!this.textDecoration) return;
if (!this.textDecoration) {
return;
}
// var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2,
@ -754,13 +766,16 @@
*/
render: function(ctx, noTransform) {
// do not render if object is not visible
if (!this.visible) return;
if (!this.visible) {
return;
}
ctx.save();
this._transform(ctx, noTransform);
var m = this.transformMatrix;
var isInPathGroup = this.group && this.group.type === 'path-group';
var m = this.transformMatrix,
isInPathGroup = this.group && this.group.type === 'path-group';
if (isInPathGroup) {
ctx.translate(-this.group.width/2, -this.group.height/2);
}
@ -834,8 +849,8 @@
: (this.height/2) - (textLines.length * this.fontSize) - this._totalLineHeight;
return {
textLeft: textLeft,
textTop: textTop,
textLeft: textLeft + (this.group ? this.left : 0),
textTop: textTop + (this.group ? this.top : 0),
lineTop: lineTop
};
},
@ -845,7 +860,7 @@
*/
_wrapSVGTextAndBg: function(markup, textAndBg, shadowSpans, offsets) {
markup.push(
'<g transform="', this.getSvgTransform(), '">',
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
textAndBg.textBgRects.join(''),
'<text ',
(this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g,'\'') + '" ': ''),
@ -858,8 +873,8 @@
'transform="translate(', toFixed(offsets.textLeft, 2), ' ', toFixed(offsets.textTop, 2), ')">',
shadowSpans.join(''),
textAndBg.textSpans.join(''),
'</text>',
'</g>'
'</text>\n',
'</g>\n'
);
},
@ -931,7 +946,9 @@
lineTopOffsetMultiplier++;
}
if (!this.textBackgroundColor || !this._boundaries) continue;
if (!this.textBackgroundColor || !this._boundaries) {
continue;
}
this._setSVGTextLineBg(textBgRects, i, textLeftOffset, lineHeight);
}
@ -975,7 +992,7 @@
toFixed(this._boundaries[i].width, 2),
'" height="',
toFixed(this._boundaries[i].height, 2),
'"></rect>');
'"></rect>\n');
},
_setSVGBg: function(textBgRects) {
@ -1087,14 +1104,14 @@
options.originX = 'left';
}
var text = new fabric.Text(element.textContent, options);
var text = new fabric.Text(element.textContent, options),
/*
Adjust positioning:
x/y attributes in SVG correspond to the bottom-left corner of text bounding box
top/left properties in Fabric correspond to center point of text bounding box
*/
offX = 0;
/*
Adjust positioning:
x/y attributes in SVG correspond to the bottom-left corner of text bounding box
top/left properties in Fabric correspond to center point of text bounding box
*/
var offX = 0;
if (text.originX === 'left') {
offX = text.getWidth() / 2;
}

View file

@ -59,9 +59,11 @@ fabric.util.object.extend(fabric.Text.prototype, {
// Cufon doesn't play nice with textDecoration=underline if element doesn't have a parent
container.appendChild(el);
//jscs:disable requireCamelCaseOrUpperCaseIdentifiers
if (typeof G_vmlCanvasManager === 'undefined') {
el.innerHTML = this.text;
}
//jscs:enable requireCamelCaseOrUpperCaseIdentifiers
else {
// IE 7 & 8 drop newlines and white space on text nodes
// see: http://web.student.tuwien.ac.at/~e0226430/innerHtmlQuirk.html

View file

@ -421,7 +421,9 @@
this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
if (!this.lowerCanvasEl.style) return;
if (!this.lowerCanvasEl.style) {
return;
}
this.lowerCanvasEl.width = this.width;
this.lowerCanvasEl.height = this.height;
@ -468,7 +470,7 @@
/**
* Sets width of this canvas instance
* @param {Number|String} width value to set width to
* @param {Number|String} value Value to set width to
* @param {Object} [options] Options object
* @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
* @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
@ -481,7 +483,7 @@
/**
* Sets height of this canvas instance
* @param {Number|String} height value to set height to
* @param {Number|String} value Value to set height to
* @param {Object} [options] Options object
* @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
* @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions
@ -693,14 +695,18 @@
* @private
*/
_draw: function (ctx, object) {
if (!object) return;
if (!object) {
return;
}
ctx.save();
var v = this.viewportTransform;
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
object.render(ctx);
ctx.restore();
if (!this.controlsAboveOverlay) object._renderControls(ctx);
if (!this.controlsAboveOverlay) {
object._renderControls(ctx);
}
},
/**

View file

@ -2,132 +2,107 @@
var arcToSegmentsCache = { },
segmentToBezierCache = { },
_join = Array.prototype.join,
argsString;
// Generous contribution by Raph Levien, from libsvg-0.1.0.tar.gz
function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
argsString = _join.call(arguments);
_join = Array.prototype.join;
/* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
* by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
* http://mozilla.org/MPL/2.0/
*/
function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
var argsString = _join.call(arguments);
if (arcToSegmentsCache[argsString]) {
return arcToSegmentsCache[argsString];
}
var coords = getXYCoords(rotateX, rx, ry, ox, oy, x, y),
d = (coords.x1 - coords.x0) * (coords.x1 - coords.x0) +
(coords.y1 - coords.y0) * (coords.y1 - coords.y0),
sfactorSq = 1 / d - 0.25;
if (sfactorSq < 0) {
sfactorSq = 0;
}
var sfactor = Math.sqrt(sfactorSq);
if (sweep === large) {
sfactor = -sfactor;
}
var xc = 0.5 * (coords.x0 + coords.x1) - sfactor * (coords.y1 - coords.y0),
yc = 0.5 * (coords.y0 + coords.y1) + sfactor * (coords.x1 - coords.x0),
th0 = Math.atan2(coords.y0 - yc, coords.x0 - xc),
th1 = Math.atan2(coords.y1 - yc, coords.x1 - xc),
thArc = th1 - th0;
if (thArc < 0 && sweep === 1) {
thArc += 2 * Math.PI;
}
else if (thArc > 0 && sweep === 0) {
thArc -= 2 * Math.PI;
}
var segments = Math.ceil(Math.abs(thArc / (Math.PI * 0.5 + 0.001))),
result = [];
for (var i = 0; i < segments; i++) {
var th2 = th0 + i * thArc / segments,
th3 = th0 + (i + 1) * thArc / segments;
result[i] = [xc, yc, th2, th3, rx, ry, coords.sinTh, coords.cosTh];
}
arcToSegmentsCache[argsString] = result;
return result;
}
function getXYCoords(rotateX, rx, ry, ox, oy, x, y) {
var th = rotateX * (Math.PI / 180),
var PI = Math.PI, th = rotateX * (PI / 180),
sinTh = Math.sin(th),
cosTh = Math.cos(th);
cosTh = Math.cos(th),
fromX = 0, fromY = 0;
rx = Math.abs(rx);
ry = Math.abs(ry);
var px = cosTh * (ox - x) + sinTh * (oy - y),
py = cosTh * (oy - y) - sinTh * (ox - x),
pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
var px = -cosTh * toX - sinTh * toY,
py = -cosTh * toY + sinTh * toX,
rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
pl = 4 * rx2 * ry2 - rx2 * py2 - ry2 * px2,
root = 0;
pl *= 0.25;
if (pl > 1) {
pl = Math.sqrt(pl);
rx *= pl;
ry *= pl;
if (pl < 0) {
var s = Math.sqrt(1 - 0.25 * pl/(rx2 * ry2));
rx *= s;
ry *= s;
} else {
root = (large === sweep ? -0.5 : 0.5) *
Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
}
var a00 = cosTh / rx,
a01 = sinTh / rx,
a10 = (-sinTh) / ry,
a11 = (cosTh) / ry;
var cx = root * rx * py / ry,
cy = -root * ry * px / rx,
cx1 = cosTh * cx - sinTh * cy + toX / 2,
cy1 = sinTh * cx + cosTh * cy + toY / 2,
mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px -cx) / rx, (-py -cy) / ry);
return {
x0: a00 * ox + a01 * oy,
y0: a10 * ox + a11 * oy,
x1: a00 * x + a01 * y,
y1: a10 * x + a11 * y,
sinTh: sinTh,
cosTh: cosTh
};
if (sweep === 0 && dtheta > 0) {
dtheta -= 2 * PI;
} else if (sweep === 1 && dtheta < 0) {
dtheta += 2 * PI;
}
// Convert into cubic bezier segments <= 90deg
var segments = Math.ceil(Math.abs(dtheta / (PI * 0.5))),
result = [], mDelta = dtheta / segments,
mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
th3 = mTheta + mDelta;
for (var i = 0; i < segments; i++) {
result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
fromX = result[i][4];
fromY = result[i][5];
mTheta += mDelta;
th3 += mDelta;
}
arcToSegmentsCache[argsString] = result;
return result;
}
function segmentToBezier(cx, cy, th0, th1, rx, ry, sinTh, cosTh) {
argsString = _join.call(arguments);
if (segmentToBezierCache[argsString]) {
return segmentToBezierCache[argsString];
function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
var argsString2 = _join.call(arguments);
if (segmentToBezierCache[argsString2]) {
return segmentToBezierCache[argsString2];
}
var costh2 = Math.cos(th2),
sinth2 = Math.sin(th2),
costh3 = Math.cos(th3),
sinth3 = Math.sin(th3),
toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
cp1X = fromX + mT * ( - cosTh * rx * sinth2 - sinTh * ry * costh2),
cp1Y = fromY + mT * ( - sinTh * rx * sinth2 + cosTh * ry * costh2),
cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
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.25 * (th1 - th0),
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,
a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
a00 * x3 + a01 * y3, a10 * x3 + a11 * y3
segmentToBezierCache[argsString2] = [
cp1X, cp1Y,
cp2X, cp2Y,
toX, toY
];
return segmentToBezierCache[argsString2];
}
return segmentToBezierCache[argsString];
/*
* Private
*/
function calcVectorAngle(ux, uy, vx, vy) {
var ta = Math.atan2(uy, ux),
tb = Math.atan2(vy, vx);
if (tb >= ta) {
return tb - ta;
} else {
return 2 * Math.PI - (ta - tb);
}
}
/**
@ -137,18 +112,24 @@
* @param {Number} y
* @param {Array} coords
*/
fabric.util.drawArc = function(ctx, x, y, coords) {
fabric.util.drawArc = function(ctx, fx, fy, coords) {
var rx = coords[0],
ry = coords[1],
rot = coords[2],
large = coords[3],
sweep = coords[4],
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);
tx = coords[5],
ty = coords[6],
segs = [[ ], [ ], [ ], [ ]],
segs_norm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
for (var i = 0; i < segs_norm.length; i++) {
segs[i][0] = segs_norm[i][0] + fx;
segs[i][1] = segs_norm[i][1] + fy;
segs[i][2] = segs_norm[i][2] + fx;
segs[i][3] = segs_norm[i][3] + fy;
segs[i][4] = segs_norm[i][4] + fx;
segs[i][5] = segs_norm[i][5] + fy;
ctx.bezierCurveTo.apply(ctx, segs[i]);
}
};
})();

View file

@ -276,7 +276,9 @@
if (loading) {
if (typeof this.readyState === 'string' &&
this.readyState !== 'loaded' &&
this.readyState !== 'complete') return;
this.readyState !== 'complete') {
return;
}
loading = false;
callback(e || fabric.window.event);
scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;

View file

@ -215,7 +215,9 @@
* @private
*/
function find(array, byProperty, condition) {
if (!array || array.length === 0) return;
if (!array || array.length === 0) {
return;
}
var i = array.length - 1,
result = byProperty ? array[i][byProperty] : array[i];

View file

@ -387,9 +387,11 @@
*/
createCanvasElement: function(canvasEl) {
canvasEl || (canvasEl = fabric.document.createElement('canvas'));
//jscs:disable requireCamelCaseOrUpperCaseIdentifiers
if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
G_vmlCanvasManager.initElement(canvasEl);
}
//jscs:enable requireCamelCaseOrUpperCaseIdentifiers
return canvasEl;
},
@ -503,26 +505,6 @@
return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
},
/**
* Normalizes polygon/polyline points according to their dimensions
* @param {Array} points
* @param {Object} options
*/
normalizePoints: function(points, options) {
var minX = fabric.util.array.min(points, 'x'),
minY = fabric.util.array.min(points, 'y');
minX = minX < 0 ? minX : 0;
minY = minX < 0 ? minY : 0;
for (var i = 0, len = points.length; i < len; i++) {
// normalize coordinates, according to containing box
// (dimensions of which are passed via `options`)
points[i].x -= (options.width / 2 + minX) || 0;
points[i].y -= (options.height / 2 + minY) || 0;
}
},
/**
* Returns true if context has transparent pixel
* at specified location (taking tolerance into account)
@ -557,7 +539,9 @@
for (var i = 3, l = imageData.data.length; i < l; i += 4) {
var temp = imageData.data[i];
_isTransparent = temp <= 0;
if (_isTransparent === false) break; // Stop if colour found
if (_isTransparent === false) {
break; // Stop if colour found
}
}
imageData = null;

View file

@ -46,7 +46,7 @@
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,'+
'"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0,"x":0,"y":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
'"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
var el = fabric.document.createElement('canvas');
el.width = 600; el.height = 600;

View file

@ -33,12 +33,12 @@
var RECT_JSON = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":10,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0,"x":0,"y":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
var RECT_JSON_WITH_PADDING = '{"objects":[{"type":"rect","originX":"left","originY":"top","left":0,"top":0,"width":10,"height":20,"fill":"rgb(0,0,0)",'+
'"stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,'+
'"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,'+
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","padding":123,"foo":"bar","rx":0,"ry":0,"x":0,"y":0}],"background":""}';
'"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","padding":123,"foo":"bar","rx":0,"ry":0}],"background":""}';
function getAbsolutePath(path) {
var isAbsolute = /^https?:/.test(path);

View file

@ -152,8 +152,8 @@
ok(oCircle instanceof fabric.Circle);
equal(oCircle.get('radius'), radius);
equal(oCircle.get('left'), left);
equal(oCircle.get('top'), top);
equal(oCircle.get('left'), left - radius);
equal(oCircle.get('top'), top - radius);
equal(oCircle.get('fill'), fill);
equal(oCircle.get('opacity'), opacity);
equal(oCircle.get('strokeWidth'), strokeWidth);

View file

@ -111,8 +111,8 @@
equal(oEllipse.get('rx'), rx);
equal(oEllipse.get('ry'), ry);
equal(oEllipse.get('left'), left);
equal(oEllipse.get('top'), top);
equal(oEllipse.get('left'), left - rx);
equal(oEllipse.get('top'), top - ry);
equal(oEllipse.get('fill'), fill);
equal(oEllipse.get('opacity'), opacity);
equal(oEllipse.get('strokeWidth'), strokeWidth);

View file

@ -162,8 +162,8 @@
// TODO: need to double check these values
equal(gradient.coords.x1, 0);
equal(gradient.coords.y1, 0);
equal(gradient.coords.x1, 50);
equal(gradient.coords.y1, 50);
//equal(gradient.coords.x2, 100);
//equal(gradient.coords.y2, 100);
@ -200,8 +200,8 @@
// TODO: need to double check these values
equal(gradient.coords.x1, 0);
equal(gradient.coords.y1, 0);
equal(gradient.coords.x1, 50);
equal(gradient.coords.y1, 50);
//equal(gradient.coords.x2, 100);
//equal(gradient.coords.y2, 100);
@ -213,6 +213,43 @@
equal(gradient.colorStops[1].color, 'rgb(255,255,255)');
});
test('fromElement radialGradient with transform', function() {
ok(typeof fabric.Gradient.fromElement == 'function');
var element = fabric.document.createElement('radialGradient');
var stop1 = fabric.document.createElement('stop');
var stop2 = fabric.document.createElement('stop');
stop1.setAttribute('offset', '0%');
stop1.setAttribute('stop-color', 'white');
stop2.setAttribute('offset', '100%');
stop2.setAttribute('stop-color', 'black');
element.appendChild(stop1);
element.appendChild(stop2);
element.setAttribute('gradientTransform', 'matrix(3.321 -0.6998 0.4077 1.9347 -440.9168 -408.0598)');
var object = new fabric.Object({ width: 100, height: 100 });
var gradient = fabric.Gradient.fromElement(element, object);
ok(gradient instanceof fabric.Gradient);
// TODO: need to double check these values
equal(gradient.coords.x1, 50);
equal(gradient.coords.y1, 50);
//equal(gradient.coords.x2, 100);
//equal(gradient.coords.y2, 100);
equal(gradient.colorStops[0].offset, 1);
equal(gradient.colorStops[1].offset, 0);
equal(gradient.colorStops[0].color, 'rgb(0,0,0)');
equal(gradient.colorStops[1].color, 'rgb(255,255,255)');
deepEqual(gradient.gradientTransform, [ 3.321, -0.6998, 0.4077, 1.9347, -440.9168, -408.0598 ]);
})
test('fromElement linearGradient colorStop attributes/styles', function() {
ok(typeof fabric.Gradient.fromElement == 'function');
@ -251,8 +288,8 @@
// TODO: need to double check these values
equal(gradient.coords.x1, 0);
equal(gradient.coords.y1, 0);
equal(gradient.coords.x1, 50);
equal(gradient.coords.y1, 50);
//equal(gradient.coords.x2, 100);
//equal(gradient.coords.y2, 100);
@ -311,8 +348,8 @@
// TODO: need to double check these values
equal(gradient.coords.x1, 0);
equal(gradient.coords.y1, 0);
equal(gradient.coords.x1, 50);
equal(gradient.coords.y1, 50);
//equal(gradient.coords.x2, 100);
//equal(gradient.coords.y2, 100);

View file

@ -384,7 +384,7 @@ test('toObject without default values', function() {
var group = makeGroupWith2Objects();
ok(typeof group.toSVG == 'function');
var expectedSVG = '<g transform="translate(130 160)"><rect x="-15" y="-5" rx="0" ry="0" width="30" height="10" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(25 -25)"/><rect x="-5" y="-20" rx="0" ry="0" width="10" height="40" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(-35 10)"/></g>';
var expectedSVG = '<g transform="translate(130 160)">\n<rect x="10" y="-30" rx="0" ry="0" width="30" height="10" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform=""/>\n<rect x="-40" y="-10" rx="0" ry="0" width="10" height="40" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform=""/>\n</g>\n';
equal(group.toSVG(), expectedSVG);
});

View file

@ -128,14 +128,12 @@
var elPolygonWithoutPoints = fabric.document.createElement('polygon');
var error;
try {
fabric.Polygon.fromElement(elPolygonWithoutPoints);
}
catch(err) {
error = err;
}
ok(error, 'missing points attribute should result in error');
equal(fabric.Polygon.fromElement(elPolygonWithoutPoints), null);
var elPolygonWithEmptyPoints = fabric.document.createElement('polygon');
elPolygonWithEmptyPoints.setAttribute('points', '');
equal(fabric.Polygon.fromElement(elPolygonWithEmptyPoints), null);
equal(fabric.Polygon.fromElement(), null);
});

View file

@ -116,16 +116,13 @@
deepEqual(polylineWithAttrs.get('transformMatrix'), [ 2, 0, 0, 2, -10, -20 ]);
var elPolylineWithoutPoints = fabric.document.createElement('polyline');
equal(fabric.Polyline.fromElement(elPolylineWithoutPoints), null);
var error;
try {
fabric.Polyline.fromElement(elPolylineWithoutPoints);
}
catch(err) {
error = err;
}
var elPolylineWithEmptyPoints = fabric.document.createElement('polyline');
elPolylineWithEmptyPoints.setAttribute('points', '');
ok(typeof error !== 'undefined', 'missing points attribute should result in error');
equal(fabric.Polyline.fromElement(), null);
equal(fabric.Polyline.fromElement(elPolylineWithEmptyPoints), null);
equal(fabric.Polyline.fromElement(), null);
});
})();

View file

@ -27,8 +27,6 @@
'clipTo': null,
'rx': 0,
'ry': 0,
'x': 0,
'y': 0
};
QUnit.module('fabric.Rect');
@ -99,8 +97,8 @@
ok(rectWithAttrs instanceof fabric.Rect);
var expectedObject = fabric.util.object.extend(REFERENCE_RECT, {
left: 121,
top: 186.5,
left: 10,
top: 20,
width: 222,
height: 333,
fill: 'rgb(255,255,255)',
@ -112,9 +110,7 @@
strokeLineJoin: 'bevil',
strokeMiterLimit: 5,
rx: 11,
ry: 12,
x: 10,
y: 20
ry: 12
});
deepEqual(rectWithAttrs.toObject(), expectedObject);
});
@ -135,7 +131,7 @@
var rect = new fabric.Rect({ width: 100, height: 100, rx: 20, ry: 30 });
var svg = rect.toSVG();
equal(svg, '<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(50 50)"/>');
equal(svg, '<rect x="-50" y="-50" rx="20" ry="30" width="100" height="100" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform="translate(50 50)"/>\n');
});
test('toObject without default values', function() {

View file

@ -46,7 +46,7 @@
'useNative': true
};
var TEXT_SVG = '<g transform="translate(10 26)"><text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); opacity: 1;" transform="translate(-10 39)"><tspan x="0" y="-26" fill="rgb(0,0,0)">x</tspan></text></g>';
var TEXT_SVG = '<g transform="translate(10 26)">\n<text font-family="Times New Roman" font-size="40" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: source-over; opacity: 1;" transform="translate(-10 39)"><tspan x="0" y="-26" fill="rgb(0,0,0)">x</tspan></text>\n</g>\n';
test('constructor', function() {
ok(fabric.Text);