Merge pull request #1 from kangax/master

Merged master branch
This commit is contained in:
Nathan Muir 2013-05-15 20:52:10 -07:00
commit fd22edb289
16 changed files with 206 additions and 137 deletions

View file

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

View file

@ -1,4 +1,4 @@
### Fabric
### Fabric
[![Build Status](https://secure.travis-ci.org/kangax/fabric.js.png?branch=master)](http://travis-ci.org/#!/kangax/fabric.js)
<a href="https://npmjs.org/package/fabric"><img src="https://badge.fury.io/js/fabric.png"></a>
@ -12,7 +12,7 @@ Using Fabric.js, you can create and populate objects on canvas; objects like sim
### Goals
- Unit tested (1500+ tests at the moment)
- Unit tested (1570+ tests at the moment)
- Modular (~40 small "classes", modules, mixins)
- Cross-browser
- [Fast](https://github.com/kangax/fabric.js/wiki/Focus-on-speed)
@ -106,6 +106,16 @@ These are the optional modules that could be specified for inclusion, when build
- **gestures** — Adds support for multitouch gestures with help of [Event.js](https://github.com/mudcube/Event.js)
- **object_straightening** — Adds support for rotating an object to one of 0, 90, 180, 270, etc. depending on which is angle is closer.
Additional flags for build script are:
- **no-strict** — Strips "use strict" directives from source
- **no-svg-export** — Removes svg exporting functionality
- **no-es5-compat** - Removes ES5 compat methods (Array.prototype.*, String.prototype.*, Function.prototype.*)
For example:
node build.js modules=ALL exclude=json no-strict no-svg-export
### Examples of use
#### Adding red rectangle to canvas

View file

@ -30,6 +30,7 @@ else if (minifier === 'uglifyjs') {
var includeAllModules = modulesToInclude.length === 1 && modulesToInclude[0] === 'ALL';
var noStrict = 'no-strict' in buildArgsAsObject;
var noSVGExport = 'no-svg-export' in buildArgsAsObject;
var noES5Compat = 'no-es5-compat' in buildArgsAsObject;
var distFileContents =
'/* build: `node build.js modules=' +
@ -37,6 +38,7 @@ var distFileContents =
(modulesToExclude.length ? (' exclude=' + modulesToExclude.join(',')) : '') +
(noStrict ? ' no-strict' : '') +
(noSVGExport ? ' no-svg-export' : '') +
(noES5Compat ? ' no-es5-compat' : '') +
'` */\n';
function appendFileContents(fileNames, callback) {
@ -62,6 +64,9 @@ function appendFileContents(fileNames, callback) {
if (noSVGExport) {
strData = strData.replace(/\/\* _TO_SVG_START_ \*\/[\s\S]*\/\* _TO_SVG_END_ \*\//, '');
}
if (noES5Compat) {
strData = strData.replace(/\/\* _ES5_COMPAT_START_ \*\/[\s\S]*\/\* _ES5_COMPAT_END_ \*\//, '');
}
distFileContents += (strData + '\n');
readNextFile();
});
@ -142,7 +147,8 @@ var filesToInclude = [
'src/object.class.js',
'src/object_origin.mixin.js',
'src/object_geometry.mixin.js',
'src/stateful.mixin.js',
ifSpecifiedInclude('stateful', 'src/stateful.mixin.js'),
ifSpecifiedInclude('interaction', 'src/object_interactivity.mixin.js'),

147
dist/all.js vendored
View file

@ -1,7 +1,7 @@
/* build: `node build.js modules=ALL exclude=gestures` */
/*! Fabric.js Copyright 2008-2013, Printio (Juriy Zaytsev, Maxim Chernyak) */
var fabric = fabric || { version: "1.1.13" };
var fabric = fabric || { version: "1.1.14" };
if (typeof exports !== 'undefined') {
exports.fabric = fabric;
@ -2462,6 +2462,8 @@ fabric.Collection = {
var slice = Array.prototype.slice;
/* _ES5_COMPAT_START_ */
if (!Array.prototype.indexOf) {
/**
* Finds index of an element in an array
@ -2626,6 +2628,8 @@ fabric.Collection = {
};
}
/* _ES5_COMPAT_END_ */
/**
* Invokes method on all items in a given array
* @memberOf fabric.util.array
@ -2748,6 +2752,7 @@ fabric.Collection = {
(function() {
/* _ES5_COMPAT_START_ */
if (!String.prototype.trim) {
/**
* Trims a string (removing whitespace from the beginning and the end)
@ -2759,6 +2764,7 @@ if (!String.prototype.trim) {
return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
};
}
/* _ES5_COMPAT_END_ */
/**
* Camelizes a string
@ -2807,6 +2813,7 @@ fabric.util.string = {
};
}());
/* _ES5_COMPAT_START_ */
(function() {
var slice = Array.prototype.slice,
@ -2842,6 +2849,8 @@ fabric.util.string = {
}
})();
/* _ES5_COMPAT_END_ */
(function() {
var slice = Array.prototype.slice, emptyFunction = function() { };
@ -3456,9 +3465,6 @@ fabric.util.string = {
scriptEl = fabric.document.createElement('script'),
loading = true;
scriptEl.type = 'text/javascript';
scriptEl.setAttribute('runat', 'server');
/** @ignore */
scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
if (loading) {
@ -4182,6 +4188,34 @@ fabric.util.string = {
return parsedPoints;
}
function parseFontDeclaration(value, oStyle) {
// 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\s+(.*)/);
if (!match) return;
var fontStyle = match[1];
// Font variant is not used
// var fontVariant = match[2];
var fontWeight = match[3];
var fontSize = match[4];
var fontFamily = match[5];
if (fontStyle) {
oStyle.fontStyle = fontStyle;
}
if (fontWeight) {
oStyle.fontSize = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
}
if (fontSize) {
oStyle.fontSize = parseFloat(fontSize);
}
if (fontFamily) {
oStyle.fontFamily = fontFamily;
}
}
/**
* Parses "style" attribute, retuning an object with values
* @static
@ -4196,16 +4230,20 @@ fabric.util.string = {
if (!style) return oStyle;
if (typeof style === 'string') {
style = style.replace(/;$/, '').split(';').forEach(function (current) {
style.replace(/;$/, '').split(';').forEach(function (chunk) {
var pair = current.split(':');
var pair = chunk.split(':');
var attr = normalizeAttr(pair[0].trim().toLowerCase());
var value = normalizeValue(attr, pair[1].trim());
// TODO: need to normalize em, %, pt, etc. to px (!)
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
if (attr === 'font') {
parseFontDeclaration(value, oStyle);
}
else {
// TODO: need to normalize em, %, pt, etc. to px (!)
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
}
});
}
else {
@ -4214,9 +4252,15 @@ fabric.util.string = {
var attr = normalizeAttr(prop.toLowerCase());
var value = normalizeValue(attr, style[prop]);
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
if (attr === 'font') {
parseFontDeclaration(value, oStyle);
}
else {
// TODO: need to normalize em, %, pt, etc. to px (!)
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
}
}
}
@ -8845,6 +8889,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onMouseDown: function (e) {
this.__onMouseDown(e);
@ -8861,6 +8906,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* @private
* @param {Event} e Event object fired on mouseup
*/
_onMouseUp: function (e) {
this.__onMouseUp(e);
@ -8877,6 +8923,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* @private
* @param {Event} e Event object fired on mousemove
*/
_onMouseMove: function (e) {
e.preventDefault && e.preventDefault();
@ -8969,7 +9016,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* canvas so the current image can be placed on the top canvas and the rest
* in on the container one.
* @private
* @param e {Event} Event object fired on mousedown
* @param {Event} e Event object fired on mousedown
*/
__onMouseDown: function (e) {
@ -9004,6 +9051,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.deactivateAllWithDispatch();
target && target.selectable && this.setActiveObject(target, e);
}
else if (this._shouldHandleGroupLogic(e, target)) {
this._handleGroupLogic(e, target);
target = this.getActiveGroup();
}
else {
// determine if it's a drag or rotate case
this.stateful && target.saveState();
@ -9012,15 +9063,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
this.onBeforeScaleRotate(target);
}
if (this._shouldHandleGroupLogic(e, target)) {
this._handleGroupLogic(e, target);
target = this.getActiveGroup();
}
else {
if (target !== this.getActiveGroup() && target !== this.getActiveObject()) {
this.deactivateAll();
this.setActiveObject(target, e);
}
if (target !== this.getActiveGroup() && target !== this.getActiveObject()) {
this.deactivateAll();
this.setActiveObject(target, e);
}
this._setupCurrentTransform(e, target);
@ -9047,7 +9092,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
* all any other type of action.
* In case of an image transformation only the top canvas will be rendered.
* @private
* @param e {Event} Event object fired on mousemove
* @param {Event} e Event object fired on mousemove
*/
__onMouseMove: function (e) {
@ -9174,8 +9219,8 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
/**
* Sets the cursor depending on where the canvas is being hovered.
* Note: very buggy in Opera
* @param e {Event} Event object
* @param target {Object} Object that the mouse is hovering, if so.
* @param {Event} e Event object
* @param {Object} target Object that the mouse is hovering, if so.
*/
_setCursorFromEvent: function (e, target) {
var s = this.upperCanvasEl.style;
@ -10909,38 +10954,29 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
* @param {String} to One of left, center, right
*/
adjustPosition: function(to) {
var angle = degreesToRadians(this.angle);
var hypotHalf = this.getWidth() / 2;
var xHalf = Math.cos(angle) * hypotHalf;
var yHalf = Math.sin(angle) * hypotHalf;
var hypotFull = this.getWidth();
var xFull = Math.cos(angle) * hypotFull;
var yFull = Math.sin(angle) * hypotFull;
if (this.originX === 'center' && to === 'left' ||
this.originX === 'right' && to === 'center') {
// move half left
this.left -= xHalf;
this.top -= yHalf;
}
else if (this.originX === 'left' && to === 'center' ||
this.originX === 'center' && to === 'right') {
// move half right
this.left += xHalf;
this.top += yHalf;
}
else if (this.originX === 'left' && to === 'right') {
// move full right
this.left += xFull;
this.top += yFull;
}
else if (this.originX === 'right' && to === 'left') {
// move full left
this.left -= xFull;
this.top -= yFull;
}
this.setCoords();
@ -10956,23 +10992,14 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
var hypotHalf = this.getWidth() / 2;
var xHalf = Math.cos(angle) * hypotHalf;
var yHalf = Math.sin(angle) * hypotHalf;
var hypotFull = this.getWidth();
var xFull = Math.cos(angle) * hypotFull;
var yFull = Math.sin(angle) * hypotFull;
var x = this.left;
var y = this.top;
if (this.originX === 'center') {
// move half left
if (this.originX === 'center' || this.originX === 'right') {
x -= xHalf;
y -= yHalf;
}
else if (this.originX === 'right') {
// move full left
x -= xFull;
y -= yFull;
if (this.originY === 'center' || this.originY === 'bottom') {
y -= yHalf;
}
return { x: x, y: y };
@ -15269,23 +15296,17 @@ fabric.Image.filters.Grayscale = fabric.util.createClass( /** @lends fabric.Imag
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data,
iLen = imageData.width,
jLen = imageData.height,
index, average, i, j;
for (i = 0; i < iLen; i++) {
for (j = 0; j < jLen; j++) {
index = (i * 4) * jLen + (j * 4);
average = (data[index] + data[index + 1] + data[index + 2]) / 3;
data[index] = average;
data[index + 1] = average;
data[index + 2] = average;
}
}
context.putImageData(imageData, 0, 0);
len = imageData.width * imageData.height * 4,
index = 0,
average;
while (index < len) {
average = (data[index] + data[index + 1] + data[index + 2]) / 3;
data[index] = average;
data[index + 1] = average;
data[index + 2] = average;
index += 4;
}
context.putImageData(imageData, 0, 0);
},
/**

12
dist/all.min.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/all.min.js.gz vendored

Binary file not shown.

View file

@ -1,7 +1,7 @@
{
"name": "fabric",
"description": "Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.",
"version": "1.1.13",
"version": "1.1.14",
"author": "Juriy Zaytsev <kangax@gmail.com>",
"keywords": ["canvas", "graphic", "graphics", "SVG", "node-canvas", "parser", "HTML5", "object model"],
"repository": "git://github.com/kangax/fabric.js",

View file

@ -50,6 +50,7 @@
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onMouseDown: function (e) {
this.__onMouseDown(e);
@ -66,6 +67,7 @@
/**
* @private
* @param {Event} e Event object fired on mouseup
*/
_onMouseUp: function (e) {
this.__onMouseUp(e);
@ -82,6 +84,7 @@
/**
* @private
* @param {Event} e Event object fired on mousemove
*/
_onMouseMove: function (e) {
e.preventDefault && e.preventDefault();
@ -174,7 +177,7 @@
* canvas so the current image can be placed on the top canvas and the rest
* in on the container one.
* @private
* @param e {Event} Event object fired on mousedown
* @param {Event} e Event object fired on mousedown
*/
__onMouseDown: function (e) {
@ -209,6 +212,10 @@
this.deactivateAllWithDispatch();
target && target.selectable && this.setActiveObject(target, e);
}
else if (this._shouldHandleGroupLogic(e, target)) {
this._handleGroupLogic(e, target);
target = this.getActiveGroup();
}
else {
// determine if it's a drag or rotate case
this.stateful && target.saveState();
@ -217,15 +224,9 @@
this.onBeforeScaleRotate(target);
}
if (this._shouldHandleGroupLogic(e, target)) {
this._handleGroupLogic(e, target);
target = this.getActiveGroup();
}
else {
if (target !== this.getActiveGroup() && target !== this.getActiveObject()) {
this.deactivateAll();
this.setActiveObject(target, e);
}
if (target !== this.getActiveGroup() && target !== this.getActiveObject()) {
this.deactivateAll();
this.setActiveObject(target, e);
}
this._setupCurrentTransform(e, target);
@ -252,7 +253,7 @@
* all any other type of action.
* In case of an image transformation only the top canvas will be rendered.
* @private
* @param e {Event} Event object fired on mousemove
* @param {Event} e Event object fired on mousemove
*/
__onMouseMove: function (e) {
@ -379,8 +380,8 @@
/**
* Sets the cursor depending on where the canvas is being hovered.
* Note: very buggy in Opera
* @param e {Event} Event object
* @param target {Object} Object that the mouse is hovering, if so.
* @param {Event} e Event object
* @param {Object} target Object that the mouse is hovering, if so.
*/
_setCursorFromEvent: function (e, target) {
var s = this.upperCanvasEl.style;

View file

@ -26,23 +26,17 @@ fabric.Image.filters.Grayscale = fabric.util.createClass( /** @lends fabric.Imag
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data,
iLen = imageData.width,
jLen = imageData.height,
index, average, i, j;
for (i = 0; i < iLen; i++) {
for (j = 0; j < jLen; j++) {
index = (i * 4) * jLen + (j * 4);
average = (data[index] + data[index + 1] + data[index + 2]) / 3;
data[index] = average;
data[index + 1] = average;
data[index + 2] = average;
}
}
context.putImageData(imageData, 0, 0);
len = imageData.width * imageData.height * 4,
index = 0,
average;
while (index < len) {
average = (data[index] + data[index + 1] + data[index + 2]) / 3;
data[index] = average;
data[index + 1] = average;
data[index + 2] = average;
index += 4;
}
context.putImageData(imageData, 0, 0);
},
/**

View file

@ -156,38 +156,29 @@
* @param {String} to One of left, center, right
*/
adjustPosition: function(to) {
var angle = degreesToRadians(this.angle);
var hypotHalf = this.getWidth() / 2;
var xHalf = Math.cos(angle) * hypotHalf;
var yHalf = Math.sin(angle) * hypotHalf;
var hypotFull = this.getWidth();
var xFull = Math.cos(angle) * hypotFull;
var yFull = Math.sin(angle) * hypotFull;
if (this.originX === 'center' && to === 'left' ||
this.originX === 'right' && to === 'center') {
// move half left
this.left -= xHalf;
this.top -= yHalf;
}
else if (this.originX === 'left' && to === 'center' ||
this.originX === 'center' && to === 'right') {
// move half right
this.left += xHalf;
this.top += yHalf;
}
else if (this.originX === 'left' && to === 'right') {
// move full right
this.left += xFull;
this.top += yFull;
}
else if (this.originX === 'right' && to === 'left') {
// move full left
this.left -= xFull;
this.top -= yFull;
}
this.setCoords();
@ -203,23 +194,14 @@
var hypotHalf = this.getWidth() / 2;
var xHalf = Math.cos(angle) * hypotHalf;
var yHalf = Math.sin(angle) * hypotHalf;
var hypotFull = this.getWidth();
var xFull = Math.cos(angle) * hypotFull;
var yFull = Math.sin(angle) * hypotFull;
var x = this.left;
var y = this.top;
if (this.originX === 'center') {
// move half left
if (this.originX === 'center' || this.originX === 'right') {
x -= xHalf;
y -= yHalf;
}
else if (this.originX === 'right') {
// move full left
x -= xFull;
y -= yFull;
if (this.originY === 'center' || this.originY === 'bottom') {
y -= yHalf;
}
return { x: x, y: y };

View file

@ -287,6 +287,34 @@
return parsedPoints;
}
function parseFontDeclaration(value, oStyle) {
// 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\s+(.*)/);
if (!match) return;
var fontStyle = match[1];
// Font variant is not used
// var fontVariant = match[2];
var fontWeight = match[3];
var fontSize = match[4];
var fontFamily = match[5];
if (fontStyle) {
oStyle.fontStyle = fontStyle;
}
if (fontWeight) {
oStyle.fontSize = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
}
if (fontSize) {
oStyle.fontSize = parseFloat(fontSize);
}
if (fontFamily) {
oStyle.fontFamily = fontFamily;
}
}
/**
* Parses "style" attribute, retuning an object with values
* @static
@ -301,16 +329,20 @@
if (!style) return oStyle;
if (typeof style === 'string') {
style = style.replace(/;$/, '').split(';').forEach(function (current) {
style.replace(/;$/, '').split(';').forEach(function (chunk) {
var pair = current.split(':');
var pair = chunk.split(':');
var attr = normalizeAttr(pair[0].trim().toLowerCase());
var value = normalizeValue(attr, pair[1].trim());
// TODO: need to normalize em, %, pt, etc. to px (!)
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
if (attr === 'font') {
parseFontDeclaration(value, oStyle);
}
else {
// TODO: need to normalize em, %, pt, etc. to px (!)
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
}
});
}
else {
@ -319,9 +351,15 @@
var attr = normalizeAttr(prop.toLowerCase());
var value = normalizeValue(attr, style[prop]);
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
if (attr === 'font') {
parseFontDeclaration(value, oStyle);
}
else {
// TODO: need to normalize em, %, pt, etc. to px (!)
var parsed = parseFloat(value);
oStyle[attr] = isNaN(parsed) ? value : parsed;
}
}
}

View file

@ -211,9 +211,6 @@
scriptEl = fabric.document.createElement('script'),
loading = true;
scriptEl.type = 'text/javascript';
scriptEl.setAttribute('runat', 'server');
/** @ignore */
scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
if (loading) {

View file

@ -2,6 +2,8 @@
var slice = Array.prototype.slice;
/* _ES5_COMPAT_START_ */
if (!Array.prototype.indexOf) {
/**
* Finds index of an element in an array
@ -166,6 +168,8 @@
};
}
/* _ES5_COMPAT_END_ */
/**
* Invokes method on all items in a given array
* @memberOf fabric.util.array

View file

@ -1,3 +1,4 @@
/* _ES5_COMPAT_START_ */
(function() {
var slice = Array.prototype.slice,
@ -32,4 +33,5 @@
};
}
})();
})();
/* _ES5_COMPAT_END_ */

View file

@ -1,5 +1,6 @@
(function() {
/* _ES5_COMPAT_START_ */
if (!String.prototype.trim) {
/**
* Trims a string (removing whitespace from the beginning and the end)
@ -11,6 +12,7 @@ if (!String.prototype.trim) {
return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
};
}
/* _ES5_COMPAT_END_ */
/**
* Camelizes a string

View file

@ -155,6 +155,18 @@
deepEqual(expectedObject, fabric.parseStyleAttribute(element));
});
test('parseStyleAttribute with short font declaration', function() {
var element = fabric.document.createElement('path');
element.setAttribute('style', 'font: italic 12px Arial,Helvetica,sans-serif');
var expectedObject = {
'fontSize': 12,
'fontStyle': 'italic',
'fontFamily': 'Arial,Helvetica,sans-serif'
};
deepEqual(expectedObject, fabric.parseStyleAttribute(element));
});
test('parseAttributes (style to have higher priority than attribute)', function() {
var element = fabric.document.createElement('path');
element.setAttribute('style', 'fill:red');