[BACK_INCOMPAT] Custom fabric build is now created with node.js and is much more flexible. Sprockets is no longer required to build fabric. Parser and node are now optional modules, which means that parseSVGDocument is no longer included in default fabric build.

This commit is contained in:
kangax 2011-08-14 17:35:36 -04:00
parent a41fd5dffd
commit b53d794af6
17 changed files with 4327 additions and 3918 deletions

View file

@ -38,25 +38,27 @@ Fabric.js started as a foundation for design editor on [printio.ru](http://print
<h3 id="fabric-building">Building</h3>
1. Install [Sprockets](http://github.com/sstephenson/sprockets)
1. [Install Node.js](https://github.com/joyent/node/wiki/Installation)
$ gem install --remote sprockets
2. Build distribution file **[~76K minified, ~22K gzipped]**
2. Build distribution file **[~80K minified, ~24K gzipped]**
$ node build.js
$ sprocketize fabric.js > dist/all.js
- Build distribution with support for text (`fabric.Text`) **[~99K minified, 30K gzipped]**
$ sprocketize fabric+text.js > dist/all.js
- Build distribution with support for serialization (`loadFromJSON`, `clone`, etc.) **[~86K minified, 26K gzipped]**
$ sprocketize fabric+serialization.js > dist/all.js
- Or build a custom distribution file, by passing (comma separated) module names to be included.<br>
- Build distribution with support for text AND serialization **[~105K minified, 32K gzipped]**
$ sprocketize fabric+text+serialization.js > dist/all.js
$ node build.js modules=text,serialization,parser
// or
$ node build.js modules=text
// or
$ node build.js modules=parser,text
// etc.
By default (when none of the modules are specified) only basic functionality is included.
See the list of modules below for more information on each one of them.
- You can also include all modules like so:
$ node build.js modules=ALL
3. Create a minified distribution file
@ -82,6 +84,15 @@ Documentation is always available at [http://kangax.github.com/fabric.js/docs/](
Also see [presentation from BK.js](http://www.slideshare.net/kangax/fabricjs-building-acanvaslibrarybk) and [presentation from Falsy Values](http://www.slideshare.net/kangax/fabric-falsy-values-8067834) for an overview of fabric.js, how it works, and its features.
### Optional modules
These are the optional modules that could be specified for inclusion, when building custom version of fabric:
- **text** — Adds support for `fabric.Text`
- **serialization** — Adds support for `loadFromJSON`, `loadFromDatalessJSON`, and `clone` methods on `fabric.Canvas`
- **parser** — Adds support for `fabric.parseSVGDocument`, `fabric.loadSVGFromURL`, and `fabric.loadSVGFromString`
- **node** — Adds support for running fabric under node.js, with help of [jsdom](https://github.com/tmpvar/jsdom) and [node-canvas](https://github.com/learnboost/node-canvas) libraries.
### Examples of use
#### Adding red rectangle to canvas

97
build.js Normal file
View file

@ -0,0 +1,97 @@
var fs = require('fs'),
childProcess = require('child_process');
var modules = process.argv.slice(2)[0];
modules = modules ? modules.split('=')[1].split(',') : [ ];
var includeAllModules = modules.length === 1 && modules[0] === 'ALL';
var distFileContents = '';
function appendFileContents(fileNames, callback) {
(function readNextFile() {
if (fileNames.length <= 0) {
return callback();
}
var fileName = fileNames.shift();
if (!fileName) {
return readNextFile();
}
fs.readFile(__dirname + '/' + fileName, function (err, data) {
if (err) throw err;
distFileContents += (data + '\n');
readNextFile();
});
})();
}
function ifSpecifiedInclude(moduleName, fileName) {
return (modules.indexOf(moduleName) > -1 || includeAllModules) ? fileName : '';
}
var filesToInclude = [
'HEADER.js',
ifSpecifiedInclude('serialization', 'lib/json2.js'),
ifSpecifiedInclude('text', 'lib/cufon.js'),
'src/log.js',
'src/observable.js',
'src/util/misc.js',
'src/util/lang_array.js',
'src/util/lang_object.js',
'src/util/lang_string.js',
'src/util/lang_function.js',
'src/util/lang_class.js',
'src/util/dom_event.js',
'src/util/dom_style.js',
'src/util/dom_misc.js',
'src/util/dom_request.js',
ifSpecifiedInclude('parser', 'src/parser.js'),
'src/gradient.js',
'src/point.class.js',
'src/intersection.class.js',
'src/color.class.js',
'src/canvas.class.js',
'src/canvas.animation.js',
ifSpecifiedInclude('serialization', 'src/canvas.serialization.js'),
'src/object.class.js',
'src/line.class.js',
'src/circle.class.js',
'src/triangle.class.js',
'src/ellipse.class.js',
'src/rect.class.js',
'src/polyline.class.js',
'src/polygon.class.js',
'src/path.class.js',
'src/path_group.class.js',
'src/group.class.js',
'src/image.class.js',
ifSpecifiedInclude('text', 'src/text.class.js'),
ifSpecifiedInclude('node', 'lib/fabric_node.js')
];
appendFileContents(filesToInclude, function() {
fs.writeFile('dist/all.js', distFileContents, function (err) {
if (err) {
console.log(err);
throw err;
};
console.log('All done');
});
});

6757
dist/all.js vendored

File diff suppressed because it is too large Load diff

4
dist/all.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,28 +0,0 @@
//= require "HEADER"
//= require "lib/json2"
//= require "src/log"
//= require "src/util"
//= require "src/parser"
//= require "src/gradient"
//= require "src/point.class"
//= require "src/intersection.class"
//= require "src/color.class"
//= require "src/canvas.class"
//= require "src/canvas.animation"
//= require "src/canvas.serialization"
//= require "src/object.class"
//= require "src/line.class"
//= require "src/circle.class"
//= require "src/triangle.class"
//= require "src/ellipse.class"
//= require "src/rect.class"
//= require "src/polyline.class"
//= require "src/polygon.class"
//= require "src/path.class"
//= require "src/path_group.class"
//= require "src/group.class"
//= require "src/image.class"

View file

@ -1,30 +0,0 @@
//= require "HEADER"
//= require "lib/cufon"
//= require "lib/json2"
//= require "src/log"
//= require "src/util"
//= require "src/parser"
//= require "src/gradient"
//= require "src/point.class"
//= require "src/intersection.class"
//= require "src/color.class"
//= require "src/canvas.class"
//= require "src/canvas.animation"
//= require "src/canvas.serialization"
//= require "src/object.class"
//= require "src/line.class"
//= require "src/circle.class"
//= require "src/triangle.class"
//= require "src/ellipse.class"
//= require "src/rect.class"
//= require "src/polyline.class"
//= require "src/polygon.class"
//= require "src/path.class"
//= require "src/path_group.class"
//= require "src/group.class"
//= require "src/text.class"
//= require "src/image.class"

View file

@ -1,28 +0,0 @@
//= require "HEADER"
//= require "lib/cufon"
//= require "src/log"
//= require "src/util"
//= require "src/parser"
//= require "src/gradient"
//= require "src/point.class"
//= require "src/intersection.class"
//= require "src/color.class"
//= require "src/canvas.class"
//= require "src/canvas.animation"
//= require "src/object.class"
//= require "src/line.class"
//= require "src/circle.class"
//= require "src/triangle.class"
//= require "src/ellipse.class"
//= require "src/rect.class"
//= require "src/polyline.class"
//= require "src/polygon.class"
//= require "src/path.class"
//= require "src/path_group.class"
//= require "src/group.class"
//= require "src/text.class"
//= require "src/image.class"

View file

@ -1,25 +0,0 @@
//= require "HEADER"
//= require "src/log"
//= require "src/util"
//= require "src/parser"
//= require "src/gradient"
//= require "src/point.class"
//= require "src/intersection.class"
//= require "src/color.class"
//= require "src/canvas.class"
//= require "src/canvas.animation"
//= require "src/object.class"
//= require "src/line.class"
//= require "src/circle.class"
//= require "src/triangle.class"
//= require "src/ellipse.class"
//= require "src/rect.class"
//= require "src/polyline.class"
//= require "src/polygon.class"
//= require "src/path.class"
//= require "src/path_group.class"
//= require "src/group.class"
//= require "src/image.class"

View file

@ -1,68 +1,76 @@
var XML = require('o3-xml'),
URL = require('url'),
HTTP = require('http'),
Image = require('canvas').Image,
fabric = require('../dist/all.js').fabric;
(function() {
if (typeof document != 'undefined' && typeof window != 'undefined') {
return;
}
var XML = require('o3-xml'),
URL = require('url'),
HTTP = require('http'),
function request(url, encoding, callback) {
var oURL = URL.parse(url),
client = HTTP.createClient(80, oURL.hostname),
request = client.request('GET', oURL.pathname, { 'host': oURL.hostname });
Image = require('canvas').Image,
fabric = require('../dist/all.js').fabric;
request.end();
request.on('response', function (response) {
var body = "";
if (encoding) {
response.setEncoding(encoding);
}
response.on('end', function () {
callback(body);
});
response.on('data', function (chunk) {
if (response.statusCode == 200) {
body += chunk;
function request(url, encoding, callback) {
var oURL = URL.parse(url),
client = HTTP.createClient(80, oURL.hostname),
request = client.request('GET', oURL.pathname, { 'host': oURL.hostname });
request.end();
request.on('response', function (response) {
var body = "";
if (encoding) {
response.setEncoding(encoding);
}
response.on('end', function () {
callback(body);
});
response.on('data', function (chunk) {
if (response.statusCode == 200) {
body += chunk;
}
});
});
});
}
}
fabric.Canvas.prototype.loadImageFromURL = function(url, callback) {
request(url, 'binary', function(body) {
var img = new Image();
img.src = new Buffer(body, 'binary');
callback(new fabric.Image(img));
});
};
fabric.loadSVGFromURL = function(url, callback) {
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
request(url, '', function(body) {
var doc = XML.parseFromString(body);
fabric.parseSVGDocument(doc.documentElement, function(results, options) {
callback(results, options);
fabric.Canvas.prototype.loadImageFromURL = function(url, callback) {
request(url, 'binary', function(body) {
var img = new Image();
img.src = new Buffer(body, 'binary');
callback(new fabric.Image(img));
});
});
};
};
fabric.Image.fromObject = function(object, callback) {
fabric.Canvas.prototype.loadImageFromURL(object.src, function(oImg) {
oImg._initConfig(object);
callback(oImg);
});
};
fabric.loadSVGFromURL = function(url, callback) {
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
request(url, '', function(body) {
var doc = XML.parseFromString(body);
fabric.parseSVGDocument(doc.documentElement, function(results, options) {
callback(results, options);
});
});
};
fabric.createCanvasForNode = function(nodeCanvas) {
var canvasEl = fabric.document.createElement('canvas');
fabric.Image.fromObject = function(object, callback) {
fabric.Canvas.prototype.loadImageFromURL(object.src, function(oImg) {
oImg._initConfig(object);
callback(oImg);
});
};
// jsdom doesn't create style on canvas element, so here be temp. workaround
canvasEl.style = { };
fabric.createCanvasForNode = function(nodeCanvas) {
var canvasEl = fabric.document.createElement('canvas');
// jsdom doesn't create style on canvas element, so here be temp. workaround
canvasEl.style = { };
canvasEl.width = nodeCanvas.width;
canvasEl.height = nodeCanvas.height;
var fabricCanvas = new fabric.Canvas(canvasEl);
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
return fabricCanvas;
};
canvasEl.width = nodeCanvas.width;
canvasEl.height = nodeCanvas.height;
var fabricCanvas = new fabric.Canvas(canvasEl);
fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
return fabricCanvas;
};
})();

View file

@ -513,12 +513,136 @@
};
})();
/**
* Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
* @property
* @namespace
*/
var svgCache = {
/**
* @method has
* @param {String} name
* @param {Function} callback
*/
has: function (name, callback) {
callback(false);
},
/**
* @method get
* @param {String} url
* @param {Function} callback
*/
get: function (url, callback) {
/* NOOP */
},
/**
* @method set
* @param {String} url
* @param {Object} object
*/
set: function (url, object) {
/* NOOP */
}
};
/**
* Takes url corresponding to an SVG document, and parses it into a set of fabric objects
* @method loadSVGFromURL
* @param {String} url
* @param {Function} callback
*/
function loadSVGFromURL(url, callback) {
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
svgCache.has(url, function (hasUrl) {
if (hasUrl) {
svgCache.get(url, function (value) {
var enlivedRecord = _enlivenCachedObject(value);
callback(enlivedRecord.objects, enlivedRecord.options);
});
}
else {
new fabric.util.request(url, {
method: 'get',
onComplete: onComplete
});
}
});
function onComplete(r) {
var xml = r.responseXML;
if (!xml) return;
var doc = xml.documentElement;
if (!doc) return;
fabric.parseSVGDocument(doc, function (results, options) {
svgCache.set(url, {
objects: fabric.util.array.invoke(results, 'toObject'),
options: options
});
callback(results, options);
});
}
}
/**
* @method _enlivenCachedObject
*/
function _enlivenCachedObject(cachedObject) {
var objects = cachedObject.objects,
options = cachedObject.options;
objects = objects.map(function (o) {
return fabric[capitalize(o.type)].fromObject(o);
});
return ({ objects: objects, options: options });
}
/**
* Takes string corresponding to an SVG document, and parses it into a set of fabric objects
* @method loadSVGFromString
* @param {String} string
* @param {Function} callback
*/
function loadSVGFromString(string, callback) {
string = string.trim();
var doc;
if (typeof DOMParser !== 'undefined') {
var parser = new DOMParser();
if (parser && parser.parseFromString) {
doc = parser.parseFromString(string, 'text/xml');
}
}
else if (fabric.window.ActiveXObject) {
var doc = new ActiveXObject('Microsoft.XMLDOM');
if (doc && doc.loadXML) {
doc.async = 'false';
doc.loadXML(string);
}
}
fabric.parseSVGDocument(doc.documentElement, function (results, options) {
callback(results, options);
});
}
extend(fabric, {
parseAttributes: parseAttributes,
parseElements: parseElements,
parseStyleAttribute: parseStyleAttribute,
parsePointsAttribute: parsePointsAttribute,
getCSSRules: getCSSRules
getCSSRules: getCSSRules,
loadSVGFromURL: loadSVGFromURL,
loadSVGFromString: loadSVGFromString
});
})(typeof exports != 'undefined' ? exports : this);

View file

@ -1,28 +0,0 @@
(function (global) {
"use strict";
var fabric = global.fabric || (global.fabric = { }),
slice = Array.prototype.slice,
apply = Function.prototype.apply;
/** @namespace */
fabric.util = { };
//= require "observable"
//= require "util/misc"
//= require "util/lang_array"
//= require "util/lang_object"
//= require "util/lang_string"
//= require "util/lang_function"
//= require "util/lang_class"
//= require "util/dom_event"
//= require "util/dom_style"
//= require "util/dom_misc"
//= require "util/dom_request"
})(typeof exports != 'undefined' ? exports : this);

View file

@ -1,205 +1,199 @@
var _slice = Array.prototype.slice;
/**
* Takes id and returns an element with that id (if one exists in a document)
* @method getById
* @memberOf fabric.util
* @param {String|HTMLElement} id
* @return {HTMLElement|null}
*/
function getById(id) {
return typeof id === 'string' ? fabric.document.getElementById(id) : id;
}
/**
* Converts an array-like object (e.g. arguments or NodeList) to an array
* @method toArray
* @memberOf fabric.util
* @param {Object} arrayLike
* @return {Array}
*/
function toArray(arrayLike) {
return _slice.call(arrayLike, 0);
}
try {
var sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
}
catch(err) { }
if (!sliceCanConvertNodelists) {
toArray = function(arrayLike) {
var arr = new Array(arrayLike.length), i = arrayLike.length;
while (i--) {
arr[i] = arrayLike[i];
}
return arr;
};
}
/**
* Creates specified element with specified attributes
* @method makeElement
* @memberOf fabric.util
* @param {String} tagName Type of an element to create
* @param {Object} [attributes] Attributes to set on an element
* @return {HTMLElement} Newly created element
*/
function makeElement(tagName, attributes) {
var el = fabric.document.createElement(tagName);
for (var prop in attributes) {
if (prop === 'class') {
el.className = attributes[prop];
}
else if (prop === 'for') {
el.htmlFor = attributes[prop];
}
else {
el.setAttribute(prop, attributes[prop]);
}
}
return el;
}
/**
* Adds class to an element
* @method addClass
* @memberOf fabric.util
* @param {HTMLElement} element Element to add class to
* @param {String} className Class to add to an element
*/
function addClass(element, className) {
if ((' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
element.className += (element.className ? ' ' : '') + className;
}
}
/**
* Wraps element with another element
* @method wrapElement
* @memberOf fabric.util
* @param {HTMLElement} element Element to wrap
* @param {HTMLElement|String} wrapper Element to wrap with
* @param {Object} [attributes] Attributes to set on a wrapper
* @return {HTMLElement} wrapper
*/
function wrapElement(element, wrapper, attributes) {
if (typeof wrapper === 'string') {
wrapper = makeElement(wrapper, attributes);
}
if (element.parentNode) {
element.parentNode.replaceChild(wrapper, element);
}
wrapper.appendChild(element);
return wrapper;
}
/**
* Returns offset for a given element
* @method getElementOffset
* @function
* @memberOf fabric.util
* @param {HTMLElement} element Element to get offset for
* @return {Object} Object with "left" and "top" properties
*/
function getElementOffset(element) {
// TODO (kangax): need to fix this method
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
}
while (element);
return ({ left: valueL, top: valueT });
}
(function () {
var style = fabric.document.documentElement.style;
var selectProp = 'userSelect' in style
? 'userSelect'
: 'MozUserSelect' in style
? 'MozUserSelect'
: 'WebkitUserSelect' in style
? 'WebkitUserSelect'
: 'KhtmlUserSelect' in style
? 'KhtmlUserSelect'
: '';
/**
* Makes element unselectable
* @method makeElementUnselectable
* @memberOf fabric.util
* @param {HTMLElement} element Element to make unselectable
* @return {HTMLElement} Element that was passed in
*/
function makeElementUnselectable(element) {
if (typeof element.onselectstart !== 'undefined') {
element.onselectstart = fabric.util.falseFunction;
}
if (selectProp) {
element.style[selectProp] = 'none';
}
else if (typeof element.unselectable == 'string') {
element.unselectable = 'on';
}
return element;
}
fabric.util.makeElementUnselectable = makeElementUnselectable;
})();
(function() {
var _slice = Array.prototype.slice;
/**
* Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
* @method getScript
* Takes id and returns an element with that id (if one exists in a document)
* @method getById
* @memberOf fabric.util
* @param {String} url URL of a script to load
* @param {Function} callback Callback to execute when script is finished loading
* @param {String|HTMLElement} id
* @return {HTMLElement|null}
*/
function getScript(url, callback) {
var headEl = fabric.document.getElementsByTagName("head")[0],
scriptEl = fabric.document.createElement('script'),
loading = true;
function getById(id) {
return typeof id === 'string' ? fabric.document.getElementById(id) : id;
}
scriptEl.type = 'text/javascript';
scriptEl.setAttribute('runat', 'server');
/** @ignore */
scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
if (loading) {
if (typeof this.readyState == 'string' &&
this.readyState !== 'loaded' &&
this.readyState !== 'complete') return;
loading = false;
callback(e || fabric.window.event);
scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
}
};
scriptEl.src = url;
headEl.appendChild(scriptEl);
// causes issue in Opera
// headEl.removeChild(scriptEl);
/**
* Converts an array-like object (e.g. arguments or NodeList) to an array
* @method toArray
* @memberOf fabric.util
* @param {Object} arrayLike
* @return {Array}
*/
function toArray(arrayLike) {
return _slice.call(arrayLike, 0);
}
function getScriptJaxer(url, callback) {
Jaxer.load(url);
callback();
}
fabric.util.getScript = getScript;
var Jaxer = global.Jaxer;
if (Jaxer && Jaxer.load) {
fabric.util.getScript = getScriptJaxer;
}
})();
fabric.util.getById = getById;
fabric.util.toArray = toArray;
fabric.util.makeElement = makeElement;
fabric.util.addClass = addClass;
fabric.util.wrapElement = wrapElement;
fabric.util.getElementOffset = getElementOffset;
try {
var sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
}
catch(err) { }
if (!sliceCanConvertNodelists) {
toArray = function(arrayLike) {
var arr = new Array(arrayLike.length), i = arrayLike.length;
while (i--) {
arr[i] = arrayLike[i];
}
return arr;
};
}
/**
* Creates specified element with specified attributes
* @method makeElement
* @memberOf fabric.util
* @param {String} tagName Type of an element to create
* @param {Object} [attributes] Attributes to set on an element
* @return {HTMLElement} Newly created element
*/
function makeElement(tagName, attributes) {
var el = fabric.document.createElement(tagName);
for (var prop in attributes) {
if (prop === 'class') {
el.className = attributes[prop];
}
else if (prop === 'for') {
el.htmlFor = attributes[prop];
}
else {
el.setAttribute(prop, attributes[prop]);
}
}
return el;
}
/**
* Adds class to an element
* @method addClass
* @memberOf fabric.util
* @param {HTMLElement} element Element to add class to
* @param {String} className Class to add to an element
*/
function addClass(element, className) {
if ((' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
element.className += (element.className ? ' ' : '') + className;
}
}
/**
* Wraps element with another element
* @method wrapElement
* @memberOf fabric.util
* @param {HTMLElement} element Element to wrap
* @param {HTMLElement|String} wrapper Element to wrap with
* @param {Object} [attributes] Attributes to set on a wrapper
* @return {HTMLElement} wrapper
*/
function wrapElement(element, wrapper, attributes) {
if (typeof wrapper === 'string') {
wrapper = makeElement(wrapper, attributes);
}
if (element.parentNode) {
element.parentNode.replaceChild(wrapper, element);
}
wrapper.appendChild(element);
return wrapper;
}
/**
* Returns offset for a given element
* @method getElementOffset
* @function
* @memberOf fabric.util
* @param {HTMLElement} element Element to get offset for
* @return {Object} Object with "left" and "top" properties
*/
function getElementOffset(element) {
// TODO (kangax): need to fix this method
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
}
while (element);
return ({ left: valueL, top: valueT });
}
(function () {
var style = fabric.document.documentElement.style;
var selectProp = 'userSelect' in style
? 'userSelect'
: 'MozUserSelect' in style
? 'MozUserSelect'
: 'WebkitUserSelect' in style
? 'WebkitUserSelect'
: 'KhtmlUserSelect' in style
? 'KhtmlUserSelect'
: '';
/**
* Makes element unselectable
* @method makeElementUnselectable
* @memberOf fabric.util
* @param {HTMLElement} element Element to make unselectable
* @return {HTMLElement} Element that was passed in
*/
function makeElementUnselectable(element) {
if (typeof element.onselectstart !== 'undefined') {
element.onselectstart = fabric.util.falseFunction;
}
if (selectProp) {
element.style[selectProp] = 'none';
}
else if (typeof element.unselectable == 'string') {
element.unselectable = 'on';
}
return element;
}
fabric.util.makeElementUnselectable = makeElementUnselectable;
})();
(function() {
/**
* Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
* @method getScript
* @memberOf fabric.util
* @param {String} url URL of a script to load
* @param {Function} callback Callback to execute when script is finished loading
*/
function getScript(url, callback) {
var headEl = fabric.document.getElementsByTagName("head")[0],
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) {
if (typeof this.readyState == 'string' &&
this.readyState !== 'loaded' &&
this.readyState !== 'complete') return;
loading = false;
callback(e || fabric.window.event);
scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
}
};
scriptEl.src = url;
headEl.appendChild(scriptEl);
// causes issue in Opera
// headEl.removeChild(scriptEl);
}
fabric.util.getScript = getScript;
})();
fabric.util.getById = getById;
fabric.util.toArray = toArray;
fabric.util.makeElement = makeElement;
fabric.util.addClass = addClass;
fabric.util.wrapElement = wrapElement;
fabric.util.getElementOffset = getElementOffset;
})();

View file

@ -1,183 +1,189 @@
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(value, from) {
var len = this.length >>> 0;
from = Number(from) || 0;
from = Math[from < 0 ? 'ceil' : 'floor'](from);
if (from < 0) {
from += len;
}
for (; from < len; from++) {
if (from in this && this[from] === value) {
return from;
(function(){
var slice = Array.prototype.slice;
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(value, from) {
var len = this.length >>> 0;
from = Number(from) || 0;
from = Math[from < 0 ? 'ceil' : 'floor'](from);
if (from < 0) {
from += len;
}
}
return -1;
};
}
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
fn.call(context, this[i], i, this);
}
}
};
}
if (!Array.prototype.map) {
Array.prototype.map = function(fn, context) {
var result = [ ];
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
result[i] = fn.call(context, this[i], i, this);
}
}
return result;
};
}
if (!Array.prototype.every) {
Array.prototype.every = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && !fn.call(context, this[i], i, this)) {
return false;
}
}
return true;
};
}
if (!Array.prototype.some) {
Array.prototype.some = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && fn.call(context, this[i], i, this)) {
return true;
}
}
return false;
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function(fn, context) {
var result = [ ], val;
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
val = this[i]; // in case fn mutates this
if (fn.call(context, val, i, this)) {
result.push(val);
for (; from < len; from++) {
if (from in this && this[from] === value) {
return from;
}
}
return -1;
};
}
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
fn.call(context, this[i], i, this);
}
}
};
}
if (!Array.prototype.map) {
Array.prototype.map = function(fn, context) {
var result = [ ];
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
result[i] = fn.call(context, this[i], i, this);
}
}
return result;
};
}
if (!Array.prototype.every) {
Array.prototype.every = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && !fn.call(context, this[i], i, this)) {
return false;
}
}
return true;
};
}
if (!Array.prototype.some) {
Array.prototype.some = function(fn, context) {
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this && fn.call(context, this[i], i, this)) {
return true;
}
}
return false;
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function(fn, context) {
var result = [ ], val;
for (var i = 0, len = this.length >>> 0; i < len; i++) {
if (i in this) {
val = this[i]; // in case fn mutates this
if (fn.call(context, val, i, this)) {
result.push(val);
}
}
}
return result;
};
}
if (!Array.prototype.reduce) {
Array.prototype.reduce = function(fn /*, initial*/) {
var len = this.length >>> 0,
i = 0,
rv;
if (arguments.length > 1) {
rv = arguments[1];
}
else {
do {
if (i in this) {
rv = this[i++];
break;
}
// if array contains no values, no initial value to return
if (++i >= len) {
throw new TypeError();
}
}
while (true);
}
for (; i < len; i++) {
if (i in this) {
rv = fn.call(null, rv, this[i], i, this);
}
}
return rv;
};
}
/**
* Invokes method on all items in a given array
* @method invoke
* @memberOf fabric.util.array
* @param {Array} array Array to iterate over
* @param {String} method Name of a method to invoke
*/
function invoke(array, method) {
var args = slice.call(arguments, 2), result = [ ];
for (var i = 0, len = array.length; i < len; i++) {
result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
}
return result;
};
}
}
if (!Array.prototype.reduce) {
Array.prototype.reduce = function(fn /*, initial*/) {
var len = this.length >>> 0,
i = 0,
rv;
if (arguments.length > 1) {
rv = arguments[1];
/**
* Finds maximum value in array (not necessarily "first" one)
* @method max
* @memberOf fabric.util.array
* @param {Array} array Array to iterate over
* @param {String} byProperty
*/
function max(array, byProperty) {
var i = array.length - 1,
result = byProperty ? array[i][byProperty] : array[i];
if (byProperty) {
while (i--) {
if (array[i][byProperty] >= result) {
result = array[i][byProperty];
}
}
}
else {
do {
if (i in this) {
rv = this[i++];
break;
}
// if array contains no values, no initial value to return
if (++i >= len) {
throw new TypeError();
while (i--) {
if (array[i] >= result) {
result = array[i];
}
}
while (true);
}
for (; i < len; i++) {
if (i in this) {
rv = fn.call(null, rv, this[i], i, this);
return result;
}
/**
* Finds minimum value in array (not necessarily "first" one)
* @method min
* @memberOf fabric.util.array
* @param {Array} array Array to iterate over
* @param {String} byProperty
*/
function min(array, byProperty) {
var i = array.length - 1,
result = byProperty ? array[i][byProperty] : array[i];
if (byProperty) {
while (i--) {
if (array[i][byProperty] < result) {
result = array[i][byProperty];
}
}
}
return rv;
else {
while (i--) {
if (array[i] < result) {
result = array[i];
}
}
}
return result;
}
/** @namespace */
fabric.util.array = {
invoke: invoke,
min: min,
max: max
};
}
/**
* Invokes method on all items in a given array
* @method invoke
* @memberOf fabric.util.array
* @param {Array} array Array to iterate over
* @param {String} method Name of a method to invoke
*/
function invoke(array, method) {
var args = slice.call(arguments, 2), result = [ ];
for (var i = 0, len = array.length; i < len; i++) {
result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
}
return result;
}
/**
* Finds maximum value in array (not necessarily "first" one)
* @method max
* @memberOf fabric.util.array
* @param {Array} array Array to iterate over
* @param {String} byProperty
*/
function max(array, byProperty) {
var i = array.length - 1,
result = byProperty ? array[i][byProperty] : array[i];
if (byProperty) {
while (i--) {
if (array[i][byProperty] >= result) {
result = array[i][byProperty];
}
}
}
else {
while (i--) {
if (array[i] >= result) {
result = array[i];
}
}
}
return result;
}
/**
* Finds minimum value in array (not necessarily "first" one)
* @method min
* @memberOf fabric.util.array
* @param {Array} array Array to iterate over
* @param {String} byProperty
*/
function min(array, byProperty) {
var i = array.length - 1,
result = byProperty ? array[i][byProperty] : array[i];
if (byProperty) {
while (i--) {
if (array[i][byProperty] < result) {
result = array[i][byProperty];
}
}
}
else {
while (i--) {
if (array[i] < result) {
result = array[i];
}
}
}
return result;
}
/** @namespace */
fabric.util.array = {
invoke: invoke,
min: min,
max: max
};
})();

View file

@ -1,5 +1,7 @@
(function() {
var slice = Array.prototype.slice;
var IS_DONTENUM_BUGGY = (function(){
for (var p in { toString: 1 }) {
if (p === 'toString') return false;

View file

@ -1,16 +1,22 @@
if (!Function.prototype.bind) {
/**
* Cross-browser approximation of ES5 Function.prototype.bind (not fully spec conforming)
* @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind">Function#bind on MDN</a>
* @param {Object} thisArg Object to bind function to
* @param {Any[]} [...] Values to pass to a bound function
* @return {Function}
*/
Function.prototype.bind = function(thisArg) {
var fn = this, args = slice.call(arguments, 1);
return args.length
? function() { return apply.call(fn, thisArg, args.concat(slice.call(arguments))); }
: function() { return apply.call(fn, thisArg, arguments) };
};
}
(function() {
var slice = Array.prototype.slice,
apply = Function.prototype.apply;
if (!Function.prototype.bind) {
/**
* Cross-browser approximation of ES5 Function.prototype.bind (not fully spec conforming)
* @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind">Function#bind on MDN</a>
* @param {Object} thisArg Object to bind function to
* @param {Any[]} [...] Values to pass to a bound function
* @return {Function}
*/
Function.prototype.bind = function(thisArg) {
var fn = this, args = slice.call(arguments, 1);
return args.length
? function() { return apply.call(fn, thisArg, args.concat(slice.call(arguments))); }
: function() { return apply.call(fn, thisArg, arguments) };
};
}
})();

View file

@ -1,30 +1,34 @@
/**
* Copies all enumerable properties of one object to another
* @memberOf fabric.util.object
* @method extend
* @param {Object} destination Where to copy to
* @param {Object} source Where to copy from
*/
function extend(destination, source) {
// JScript DontEnum bug is not taken care of
for (var property in source) {
destination[property] = source[property];
(function(){
/**
* Copies all enumerable properties of one object to another
* @memberOf fabric.util.object
* @method extend
* @param {Object} destination Where to copy to
* @param {Object} source Where to copy from
*/
function extend(destination, source) {
// JScript DontEnum bug is not taken care of
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
return destination;
}
/**
* Creates an empty object and copies all enumerable properties of another object to it
* @method clone
* @memberOf fabric.util.object
* @param {Object} object Object to clone
*/
function clone(object) {
return extend({ }, object);
}
/**
* Creates an empty object and copies all enumerable properties of another object to it
* @method clone
* @memberOf fabric.util.object
* @param {Object} object Object to clone
*/
function clone(object) {
return extend({ }, object);
}
/** @namespace fabric.util.object */
fabric.util.object = {
extend: extend,
clone: clone
};
/** @namespace fabric.util.object */
fabric.util.object = {
extend: extend,
clone: clone
};
})();

View file

@ -1,5 +1,7 @@
(function() {
fabric.util = { };
/**
* Removes value from an array.
* Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
@ -111,128 +113,6 @@
return interval;
}
/**
* Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
* @property
* @namespace
*/
var svgCache = {
/**
* @method has
* @param {String} name
* @param {Function} callback
*/
has: function (name, callback) {
callback(false);
},
/**
* @method get
* @param {String} url
* @param {Function} callback
*/
get: function (url, callback) {
/* NOOP */
},
/**
* @method set
* @param {String} url
* @param {Object} object
*/
set: function (url, object) {
/* NOOP */
}
};
/**
* Takes url corresponding to an SVG document, and parses it into a set of fabric objects
* @method loadSVGFromURL
* @param {String} url
* @param {Function} callback
*/
function loadSVGFromURL(url, callback) {
url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
svgCache.has(url, function (hasUrl) {
if (hasUrl) {
svgCache.get(url, function (value) {
var enlivedRecord = _enlivenCachedObject(value);
callback(enlivedRecord.objects, enlivedRecord.options);
});
}
else {
new fabric.util.request(url, {
method: 'get',
onComplete: onComplete
});
}
});
function onComplete(r) {
var xml = r.responseXML;
if (!xml) return;
var doc = xml.documentElement;
if (!doc) return;
fabric.parseSVGDocument(doc, function (results, options) {
svgCache.set(url, {
objects: fabric.util.array.invoke(results, 'toObject'),
options: options
});
callback(results, options);
});
}
}
/**
* @method _enlivenCachedObject
*/
function _enlivenCachedObject(cachedObject) {
var objects = cachedObject.objects,
options = cachedObject.options;
objects = objects.map(function (o) {
return fabric[capitalize(o.type)].fromObject(o);
});
return ({ objects: objects, options: options });
}
/**
* Takes string corresponding to an SVG document, and parses it into a set of fabric objects
* @method loadSVGFromString
* @param {String} string
* @param {Function} callback
*/
function loadSVGFromString(string, callback) {
string = string.trim();
var doc;
if (typeof DOMParser !== 'undefined') {
var parser = new DOMParser();
if (parser && parser.parseFromString) {
doc = parser.parseFromString(string, 'text/xml');
}
}
else if (fabric.window.ActiveXObject) {
var doc = new ActiveXObject('Microsoft.XMLDOM');
if (doc && doc.loadXML) {
doc.async = 'false';
doc.loadXML(string);
}
}
fabric.parseSVGDocument(doc.documentElement, function (results, options) {
callback(results, options);
});
}
fabric.util.removeFromArray = removeFromArray;
fabric.util.degreesToRadians = degreesToRadians;
@ -241,7 +121,4 @@
fabric.util.falseFunction = falseFunction;
fabric.util.animate = animate;
fabric.loadSVGFromURL = loadSVGFromURL;
fabric.loadSVGFromString = loadSVGFromString;
})();