diff --git a/dist/docs/resources/js/paper.js b/dist/docs/resources/js/paper.js index e963d27f..e69de29b 100644 --- a/dist/docs/resources/js/paper.js +++ b/dist/docs/resources/js/paper.js @@ -1,7842 +0,0 @@ -/*! - * Paper.js v0.22 - * - * This file is part of Paper.js, a JavaScript Vector Graphics Library, - * based on Scriptographer.org and designed to be largely API compatible. - * http://paperjs.org/ - * http://scriptographer.org/ - * - * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey - * http://lehni.org/ & http://jonathanpuckey.com/ - * - * Distributed under the MIT license. See LICENSE file for details. - * - * All rights reserved. - * - * Date: Thu Nov 10 18:30:18 2011 +0100 - * - *** - * - * Bootstrap.js JavaScript Framework. - * http://bootstrapjs.org/ - * - * Copyright (c) 2006 - 2011 Juerg Lehni - * http://lehni.org/ - * - * Distributed under the MIT license. - * - *** - * - * Parse-js - * - * A JavaScript tokenizer / parser / generator, originally written in Lisp. - * Copyright (c) Marijn Haverbeke - * http://marijn.haverbeke.nl/parse-js/ - * - * Ported by to JavaScript by Mihai Bazon - * Copyright (c) 2010, Mihai Bazon - * http://mihai.bazon.net/blog/ - * - * Modifications and adaptions to browser (c) 2011, Juerg Lehni - * http://lehni.org/ - * - * Distributed under the BSD license. - */ - -var paper = new function() { - -var Base = new function() { - var fix = !this.__proto__, - hidden = /^(statics|generics|preserve|enumerable|prototype|__proto__|toString|valueOf)$/, - proto = Object.prototype, - has = fix - ? function(name) { - return name !== '__proto__' && this.hasOwnProperty(name); - } - : proto.hasOwnProperty, - toString = proto.toString, - proto = Array.prototype, - isArray = Array.isArray = Array.isArray || function(obj) { - return toString.call(obj) === '[object Array]'; - }, - slice = proto.slice, - forEach = proto.forEach = proto.forEach || function(iter, bind) { - for (var i = 0, l = this.length; i < l; i++) - iter.call(bind, this[i], i, this); - }, - forIn = function(iter, bind) { - for (var i in this) - if (this.hasOwnProperty(i)) - iter.call(bind, this[i], i, this); - }, - _define = Object.defineProperty, - _describe = Object.getOwnPropertyDescriptor; - - function define(obj, name, desc) { - if (_define) { - try { - delete obj[name]; - return _define(obj, name, desc); - } catch (e) {} - } - if ((desc.get || desc.set) && obj.__defineGetter__) { - desc.get && obj.__defineGetter__(name, desc.get); - desc.set && obj.__defineSetter__(name, desc.set); - } else { - obj[name] = desc.value; - } - return obj; - } - - function describe(obj, name) { - if (_describe) { - try { - return _describe(obj, name); - } catch (e) {} - } - var get = obj.__lookupGetter__ && obj.__lookupGetter__(name); - return get - ? { get: get, set: obj.__lookupSetter__(name), enumerable: true, - configurable: true } - : has.call(obj, name) - ? { value: obj[name], enumerable: true, configurable: true, - writable: true } - : null; - } - - function inject(dest, src, enumerable, base, preserve, generics) { - var beans, bean; - - function field(name, val, dontCheck, generics) { - var val = val || (val = describe(src, name)) - && (val.get ? val : val.value), - func = typeof val === 'function', - res = val, - prev = preserve || func - ? (val && val.get ? name in dest : dest[name]) : null; - if (generics && func && (!preserve || !generics[name])) { - generics[name] = function(bind) { - return bind && dest[name].apply(bind, - slice.call(arguments, 1)); - } - } - if ((dontCheck || val !== undefined && has.call(src, name)) - && (!preserve || !prev)) { - if (func) { - if (prev && /\bthis\.base\b/.test(val)) { - var fromBase = base && base[name] == prev; - res = function() { - var tmp = describe(this, 'base'); - define(this, 'base', { value: fromBase - ? base[name] : prev, configurable: true }); - try { - return val.apply(this, arguments); - } finally { - tmp ? define(this, 'base', tmp) - : delete this.base; - } - }; - res.toString = function() { - return val.toString(); - } - res.valueOf = function() { - return val.valueOf(); - } - } - if (beans && val.length == 0 - && (bean = name.match(/^(get|is)(([A-Z])(.*))$/))) - beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]); - } - if (!res || func || !res.get && !res.set) - res = { value: res, writable: true }; - if ((describe(dest, name) - || { configurable: true }).configurable) { - res.configurable = true; - res.enumerable = enumerable; - } - define(dest, name, res); - } - } - if (src) { - beans = []; - for (var name in src) - if (has.call(src, name) && !hidden.test(name)) - field(name, null, true, generics); - field('toString'); - field('valueOf'); - for (var i = 0, l = beans && beans.length; i < l; i++) - try { - var bean = beans[i], part = bean[1]; - field(bean[0], { - get: dest['get' + part] || dest['is' + part], - set: dest['set' + part] - }, true); - } catch (e) {} - } - return dest; - } - - function extend(obj) { - var ctor = function(dont) { - if (fix) define(this, '__proto__', { value: obj }); - if (this.initialize && dont !== ctor.dont) - return this.initialize.apply(this, arguments); - } - ctor.prototype = obj; - ctor.toString = function() { - return (this.prototype.initialize || function() {}).toString(); - } - return ctor; - } - - function iterator(iter) { - return !iter - ? function(val) { return val } - : typeof iter !== 'function' - ? function(val) { return val == iter } - : iter; - } - - function each(obj, iter, bind, asArray) { - try { - if (obj) - (asArray || asArray === undefined && isArray(obj) - ? forEach : forIn).call(obj, iterator(iter), - bind = bind || obj); - } catch (e) { - if (e !== Base.stop) throw e; - } - return bind; - } - - function clone(obj) { - return each(obj, function(val, i) { - this[i] = val; - }, new obj.constructor()); - } - - return inject(function() {}, { - inject: function(src) { - if (src) { - var proto = this.prototype, - base = proto.__proto__ && proto.__proto__.constructor, - statics = src.statics == true ? src : src.statics; - if (statics != src) - inject(proto, src, src.enumerable, base && base.prototype, - src.preserve, src.generics && this); - inject(this, statics, true, base, src.preserve); - } - for (var i = 1, l = arguments.length; i < l; i++) - this.inject(arguments[i]); - return this; - }, - - extend: function(src) { - var proto = new this(this.dont), - ctor = extend(proto); - define(proto, 'constructor', - { value: ctor, writable: true, configurable: true }); - ctor.dont = {}; - inject(ctor, this, true); - return arguments.length ? this.inject.apply(ctor, arguments) : ctor; - } - }, true).inject({ - has: has, - each: each, - - inject: function() { - for (var i = 0, l = arguments.length; i < l; i++) - inject(this, arguments[i]); - return this; - }, - - extend: function() { - var res = new (extend(this)); - return res.inject.apply(res, arguments); - }, - - each: function(iter, bind) { - return each(this, iter, bind); - }, - - clone: function() { - return clone(this); - }, - - statics: { - each: each, - clone: clone, - define: define, - describe: describe, - iterator: iterator, - - has: function(obj, name) { - return has.call(obj, name); - }, - - type: function(obj) { - return (obj || obj === 0) && (obj._type || typeof obj) || null; - }, - - check: function(obj) { - return !!(obj || obj === 0); - }, - - pick: function() { - for (var i = 0, l = arguments.length; i < l; i++) - if (arguments[i] !== undefined) - return arguments[i]; - return null; - }, - - stop: {} - } - }); -} - -this.Base = Base.inject({ - generics: true, - - clone: function() { - return new this.constructor(this); - }, - - toString: function() { - return '{ ' + Base.each(this, function(value, key) { - if (key.charAt(0) != '_') { - var type = typeof value; - this.push(key + ': ' + (type === 'number' - ? Base.formatNumber(value) - : type === 'string' ? "'" + value + "'" : value)); - } - }, []).join(', ') + ' }'; - }, - - statics: { - read: function(list, start, length) { - var start = start || 0, - length = length || list.length - start; - var obj = list[start]; - if (obj instanceof this - || this.prototype._readNull && obj == null && length <= 1) - return obj; - obj = new this(this.dont); - return obj.initialize.apply(obj, start > 0 || length < list.length - ? Array.prototype.slice.call(list, start, start + length) - : list) || obj; - }, - - readAll: function(list, start) { - var res = [], entry; - for (var i = start || 0, l = list.length; i < l; i++) { - res.push(Array.isArray(entry = list[i]) - ? this.read(entry, 0) - : this.read(list, i, 1)); - } - return res; - }, - - splice: function(list, items, index, remove) { - var amount = items && items.length, - append = index === undefined; - index = append ? list.length : index; - for (var i = 0; i < amount; i++) - items[i]._index = index + i; - if (append) { - list.push.apply(list, items); - return []; - } else { - var args = [index, remove]; - if (items) - args.push.apply(args, items); - var removed = list.splice.apply(list, args); - for (var i = 0, l = removed.length; i < l; i++) - delete removed[i]._index; - for (var i = index + amount, l = list.length; i < l; i++) - list[i]._index = i; - return removed; - } - }, - - merge: function() { - return Base.each(arguments, function(hash) { - Base.each(hash, function(value, key) { - this[key] = value; - }, this); - }, new Base(), true); - }, - - capitalize: function(str) { - return str.replace(/\b[a-z]/g, function(match) { - return match.toUpperCase(); - }); - }, - - camelize: function(str) { - return str.replace(/-(\w)/g, function(all, chr) { - return chr.toUpperCase(); - }); - }, - - hyphenate: function(str) { - return str.replace(/[a-z][A-Z0-9]|[0-9][a-zA-Z]|[A-Z]{2}[a-z]/g, - function(match) { - return match.charAt(0) + '-' + match.substring(1); - } - ).toLowerCase(); - }, - - formatNumber: function(num) { - return (Math.round(num * 100000) / 100000).toString(); - } - } -}); - -var PaperScope = this.PaperScope = Base.extend({ - - initialize: function(script) { - paper = this; - this.view = null; - this.views = []; - this.project = null; - this.projects = []; - this.tool = null; - this.tools = []; - this._id = script && (script.getAttribute('id') || script.src) - || ('paperscope-' + (PaperScope._id++)); - if (script) - script.setAttribute('id', this._id); - PaperScope._scopes[this._id] = this; - }, - - version: 0.22, - - evaluate: function(code) { - var res = PaperScript.evaluate(code, this); - View.updateFocus(); - return res; - }, - - install: function(scope) { - var that = this; - Base.each(['project', 'view', 'tool'], function(key) { - Base.define(scope, key, { - configurable: true, - writable: true, - get: function() { - return that[key]; - } - }); - }); - for (var key in this) { - if (!/^(version|_id|load)/.test(key) && !(key in scope)) - scope[key] = this[key]; - } - }, - - setup: function(canvas) { - paper = this; - this.project = new Project(); - if (canvas) - this.view = new View(canvas); - }, - - clear: function() { - for (var i = this.projects.length - 1; i >= 0; i--) - this.projects[i].remove(); - for (var i = this.views.length - 1; i >= 0; i--) - this.views[i].remove(); - for (var i = this.tools.length - 1; i >= 0; i--) - this.tools[i].remove(); - }, - - remove: function() { - this.clear(); - delete PaperScope._scopes[this._id]; - }, - - _needsRedraw: function() { - if (!this._redrawNotified) { - for (var i = this.views.length - 1; i >= 0; i--) - this.views[i]._redrawNeeded = true; - this._redrawNotified = true; - } - }, - - statics: { - _scopes: {}, - _id: 0, - - get: function(id) { - if (typeof id === 'object') - id = id.getAttribute('id'); - return this._scopes[id] || null; - }, - - each: function(iter) { - Base.each(this._scopes, iter); - } - } -}); - -var PaperScopeItem = Base.extend({ - - initialize: function(activate) { - this._scope = paper; - this._index = this._scope[this._list].push(this) - 1; - if (activate || !this._scope[this._reference]) - this.activate(); - }, - - activate: function() { - if (!this._scope) - return false; - this._scope[this._reference] = this; - return true; - }, - - remove: function() { - if (this._index == null) - return false; - Base.splice(this._scope[this._list], null, this._index, 1); - if (this._scope[this._reference] == this) - this._scope[this._reference] = null; - this._scope = null; - return true; - } -}); - -var Point = this.Point = Base.extend({ - initialize: function(arg0, arg1) { - if (arg1 !== undefined) { - this.x = arg0; - this.y = arg1; - } else if (arg0 !== undefined) { - if (arg0 == null) { - this.x = this.y = 0; - } else if (arg0.x !== undefined) { - this.x = arg0.x; - this.y = arg0.y; - } else if (arg0.width !== undefined) { - this.x = arg0.width; - this.y = arg0.height; - } else if (Array.isArray(arg0)) { - this.x = arg0[0]; - this.y = arg0.length > 1 ? arg0[1] : arg0[0]; - } else if (arg0.angle !== undefined) { - this.x = arg0.length; - this.y = 0; - this.setAngle(arg0.angle); - } else if (typeof arg0 === 'number') { - this.x = this.y = arg0; - } else { - this.x = this.y = 0; - } - } else { - this.x = this.y = 0; - } - }, - - set: function(x, y) { - this.x = x; - this.y = y; - return this; - }, - - clone: function() { - return Point.create(this.x, this.y); - }, - - toString: function() { - var format = Base.formatNumber; - return '{ x: ' + format(this.x) + ', y: ' + format(this.y) + ' }'; - }, - - add: function(point) { - point = Point.read(arguments); - return Point.create(this.x + point.x, this.y + point.y); - }, - - subtract: function(point) { - point = Point.read(arguments); - return Point.create(this.x - point.x, this.y - point.y); - }, - - multiply: function(point) { - point = Point.read(arguments); - return Point.create(this.x * point.x, this.y * point.y); - }, - - divide: function(point) { - point = Point.read(arguments); - return Point.create(this.x / point.x, this.y / point.y); - }, - - modulo: function(point) { - point = Point.read(arguments); - return Point.create(this.x % point.x, this.y % point.y); - }, - - negate: function() { - return Point.create(-this.x, -this.y); - }, - - transform: function(matrix) { - return matrix ? matrix._transformPoint(this) : this; - }, - - getDistance: function(point, squared) { - point = Point.read(arguments); - var x = point.x - this.x, - y = point.y - this.y, - d = x * x + y * y; - return squared ? d : Math.sqrt(d); - }, - - getLength: function() { - var l = this.x * this.x + this.y * this.y; - return arguments[0] ? l : Math.sqrt(l); - }, - - setLength: function(length) { - if (this.isZero()) { - var angle = this._angle || 0; - this.set( - Math.cos(angle) * length, - Math.sin(angle) * length - ); - } else { - var scale = length / this.getLength(); - if (scale == 0) - this.getAngle(); - this.set( - this.x * scale, - this.y * scale - ); - } - return this; - }, - - normalize: function(length) { - if (length === undefined) - length = 1; - var current = this.getLength(), - scale = current != 0 ? length / current : 0, - point = Point.create(this.x * scale, this.y * scale); - point._angle = this._angle; - return point; - }, - - getAngle: function() { - return this.getAngleInRadians(arguments[0]) * 180 / Math.PI; - }, - - setAngle: function(angle) { - angle = this._angle = angle * Math.PI / 180; - if (!this.isZero()) { - var length = this.getLength(); - this.set( - Math.cos(angle) * length, - Math.sin(angle) * length - ); - } - return this; - }, - - getAngleInRadians: function() { - if (arguments[0] === undefined) { - if (this._angle == null) - this._angle = Math.atan2(this.y, this.x); - return this._angle; - } else { - var point = Point.read(arguments), - div = this.getLength() * point.getLength(); - if (div == 0) { - return NaN; - } else { - return Math.acos(this.dot(point) / div); - } - } - }, - - getAngleInDegrees: function() { - return this.getAngle(arguments[0]); - }, - - getQuadrant: function() { - return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; - }, - - getDirectedAngle: function(point) { - point = Point.read(arguments); - return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI; - }, - - rotate: function(angle, center) { - angle = angle * Math.PI / 180; - var point = center ? this.subtract(center) : this, - s = Math.sin(angle), - c = Math.cos(angle); - point = Point.create( - point.x * c - point.y * s, - point.y * c + point.x * s - ); - return center ? point.add(center) : point; - }, - - equals: function(point) { - point = Point.read(arguments); - return this.x == point.x && this.y == point.y; - }, - - isInside: function(rect) { - return rect.contains(this); - }, - - isClose: function(point, tolerance) { - return this.getDistance(point) < tolerance; - }, - - isColinear: function(point) { - return this.cross(point) < Numerical.TOLERANCE; - }, - - isOrthogonal: function(point) { - return this.dot(point) < Numerical.TOLERANCE; - }, - - isZero: function() { - return this.x == 0 && this.y == 0; - }, - - isNaN: function() { - return isNaN(this.x) || isNaN(this.y); - }, - - dot: function(point) { - point = Point.read(arguments); - return this.x * point.x + this.y * point.y; - }, - - cross: function(point) { - point = Point.read(arguments); - return this.x * point.y - this.y * point.x; - }, - - project: function(point) { - point = Point.read(arguments); - if (point.isZero()) { - return Point.create(0, 0); - } else { - var scale = this.dot(point) / point.dot(point); - return Point.create( - point.x * scale, - point.y * scale - ); - } - }, - - statics: { - create: function(x, y) { - var point = new Point(Point.dont); - point.x = x; - point.y = y; - return point; - }, - - min: function(point1, point2) { - point1 = Point.read(arguments, 0, 1); - point2 = Point.read(arguments, 1, 1); - return Point.create( - Math.min(point1.x, point2.x), - Math.min(point1.y, point2.y) - ); - }, - - max: function(point1, point2) { - point1 = Point.read(arguments, 0, 1); - point2 = Point.read(arguments, 1, 1); - return Point.create( - Math.max(point1.x, point2.x), - Math.max(point1.y, point2.y) - ); - }, - - random: function() { - return Point.create(Math.random(), Math.random()); - } - } -}, new function() { - - return Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { - var op = Math[name]; - this[name] = function() { - return Point.create(op(this.x), op(this.y)); - }; - }, {}); -}); - -var LinkedPoint = Point.extend({ - set: function(x, y, dontNotify) { - this._x = x; - this._y = y; - if (!dontNotify) - this._owner[this._setter](this); - return this; - }, - - getX: function() { - return this._x; - }, - - setX: function(x) { - this._x = x; - this._owner[this._setter](this); - }, - - getY: function() { - return this._y; - }, - - setY: function(y) { - this._y = y; - this._owner[this._setter](this); - }, - - statics: { - create: function(owner, setter, x, y, dontLink) { - if (dontLink) - return Point.create(x, y); - var point = new LinkedPoint(LinkedPoint.dont); - point._x = x; - point._y = y; - point._owner = owner; - point._setter = setter; - return point; - } - } -}); - -var Size = this.Size = Base.extend({ - initialize: function(arg0, arg1) { - if (arg1 !== undefined) { - this.width = arg0; - this.height = arg1; - } else if (arg0 !== undefined) { - if (arg0 == null) { - this.width = this.height = 0; - } else if (arg0.width !== undefined) { - this.width = arg0.width; - this.height = arg0.height; - } else if (arg0.x !== undefined) { - this.width = arg0.x; - this.height = arg0.y; - } else if (Array.isArray(arg0)) { - this.width = arg0[0]; - this.height = arg0.length > 1 ? arg0[1] : arg0[0]; - } else if (typeof arg0 === 'number') { - this.width = this.height = arg0; - } else { - this.width = this.height = 0; - } - } else { - this.width = this.height = 0; - } - }, - - toString: function() { - var format = Base.formatNumber; - return '{ width: ' + format(this.width) - + ', height: ' + format(this.height) + ' }'; - }, - - set: function(width, height) { - this.width = width; - this.height = height; - return this; - }, - - clone: function() { - return Size.create(this.width, this.height); - }, - - add: function(size) { - size = Size.read(arguments); - return Size.create(this.width + size.width, this.height + size.height); - }, - - subtract: function(size) { - size = Size.read(arguments); - return Size.create(this.width - size.width, this.height - size.height); - }, - - multiply: function(size) { - size = Size.read(arguments); - return Size.create(this.width * size.width, this.height * size.height); - }, - - divide: function(size) { - size = Size.read(arguments); - return Size.create(this.width / size.width, this.height / size.height); - }, - - modulo: function(size) { - size = Size.read(arguments); - return Size.create(this.width % size.width, this.height % size.height); - }, - - negate: function() { - return Size.create(-this.width, -this.height); - }, - - equals: function(size) { - size = Size.read(arguments); - return this.width == size.width && this.height == size.height; - }, - - isZero: function() { - return this.width == 0 && this.height == 0; - }, - - isNaN: function() { - return isNaN(this.width) || isNaN(this.height); - }, - - statics: { - create: function(width, height) { - return new Size(Size.dont).set(width, height); - }, - - min: function(size1, size2) { - return Size.create( - Math.min(size1.width, size2.width), - Math.min(size1.height, size2.height)); - }, - - max: function(size1, size2) { - return Size.create( - Math.max(size1.width, size2.width), - Math.max(size1.height, size2.height)); - }, - - random: function() { - return Size.create(Math.random(), Math.random()); - } - } -}, new function() { - - return Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { - var op = Math[name]; - this[name] = function() { - return Size.create(op(this.width), op(this.height)); - }; - }, {}); -}); - -var LinkedSize = Size.extend({ - set: function(width, height, dontNotify) { - this._width = width; - this._height = height; - if (!dontNotify) - this._owner[this._setter](this); - return this; - }, - - getWidth: function() { - return this._width; - }, - - setWidth: function(width) { - this._width = width; - this._owner[this._setter](this); - }, - - getHeight: function() { - return this._height; - }, - - setHeight: function(height) { - this._height = height; - this._owner[this._setter](this); - }, - - statics: { - create: function(owner, setter, width, height, dontLink) { - if (dontLink) - return Size.create(width, height); - var size = new LinkedSize(LinkedSize.dont); - size._width = width; - size._height = height; - size._owner = owner; - size._setter = setter; - return size; - } - } -}); - -var Rectangle = this.Rectangle = Base.extend({ - initialize: function(arg0, arg1, arg2, arg3) { - if (arguments.length == 4) { - this.x = arg0; - this.y = arg1; - this.width = arg2; - this.height = arg3; - } else if (arguments.length == 2) { - if (arg1 && arg1.x !== undefined) { - var point1 = Point.read(arguments, 0, 1); - var point2 = Point.read(arguments, 1, 1); - this.x = point1.x; - this.y = point1.y; - this.width = point2.x - point1.x; - this.height = point2.y - point1.y; - if (this.width < 0) { - this.x = point2.x; - this.width = -this.width; - } - if (this.height < 0) { - this.y = point2.y; - this.height = -this.height; - } - } else { - var point = Point.read(arguments, 0, 1); - var size = Size.read(arguments, 1, 1); - this.x = point.x; - this.y = point.y; - this.width = size.width; - this.height = size.height; - } - } else if (arg0) { - this.x = arg0.x || 0; - this.y = arg0.y || 0; - this.width = arg0.width || 0; - this.height = arg0.height || 0; - } else { - this.x = this.y = this.width = this.height = 0; - } - }, - - set: function(x, y, width, height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - return this; - }, - - getPoint: function() { - return LinkedPoint.create(this, 'setPoint', this.x, this.y, - arguments[0]); - }, - - setPoint: function(point) { - point = Point.read(arguments); - this.x = point.x; - this.y = point.y; - return this; - }, - - getSize: function() { - return LinkedSize.create(this, 'setSize', this.width, this.height, - arguments[0]); - }, - - setSize: function(size) { - size = Size.read(arguments); - this.width = size.width; - this.height = size.height; - return this; - }, - - getLeft: function() { - return this.x; - }, - - setLeft: function(left) { - this.width -= left - this.x; - this.x = left; - return this; - }, - - getTop: function() { - return this.y; - }, - - setTop: function(top) { - this.height -= top - this.y; - this.y = top; - return this; - }, - - getRight: function() { - return this.x + this.width; - }, - - setRight: function(right) { - this.width = right - this.x; - return this; - }, - - getBottom: function() { - return this.y + this.height; - }, - - setBottom: function(bottom) { - this.height = bottom - this.y; - return this; - }, - - getCenterX: function() { - return this.x + this.width * 0.5; - }, - - setCenterX: function(x) { - this.x = x - this.width * 0.5; - return this; - }, - - getCenterY: function() { - return this.y + this.height * 0.5; - }, - - setCenterY: function(y) { - this.y = y - this.height * 0.5; - return this; - }, - - getCenter: function() { - return LinkedPoint.create(this, 'setCenter', - this.getCenterX(), this.getCenterY(), arguments[0]); - }, - - setCenter: function(point) { - point = Point.read(arguments); - return this.setCenterX(point.x).setCenterY(point.y); - }, - - equals: function(rect) { - rect = Rectangle.read(arguments); - return this.x == rect.x && this.y == rect.y - && this.width == rect.width && this.height == rect.height; - }, - - isEmpty: function() { - return this.width == 0 || this.height == 0; - }, - - toString: function() { - var format = Base.formatNumber; - return '{ x: ' + format(this.x) - + ', y: ' + format(this.y) - + ', width: ' + format(this.width) - + ', height: ' + format(this.height) - + ' }'; - }, - - contains: function(arg) { - return arg && arg.width !== undefined - || (Array.isArray(arg) ? arg : arguments).length == 4 - ? this._containsRectangle(Rectangle.read(arguments)) - : this._containsPoint(Point.read(arguments)); - }, - - _containsPoint: function(point) { - var x = point.x, - y = point.y; - return x >= this.x && y >= this.y - && x <= this.x + this.width - && y <= this.y + this.height; - }, - - _containsRectangle: function(rect) { - var x = rect.x, - y = rect.y; - return x >= this.x && y >= this.y - && x + rect.width <= this.x + this.width - && y + rect.height <= this.y + this.height; - }, - - intersects: function(rect) { - rect = Rectangle.read(arguments); - return rect.x + rect.width > this.x - && rect.y + rect.height > this.y - && rect.x < this.x + this.width - && rect.y < this.y + this.height; - }, - - intersect: function(rect) { - rect = Rectangle.read(arguments); - var x1 = Math.max(this.x, rect.x), - y1 = Math.max(this.y, rect.y), - x2 = Math.min(this.x + this.width, rect.x + rect.width), - y2 = Math.min(this.y + this.height, rect.y + rect.height); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - unite: function(rect) { - rect = Rectangle.read(arguments); - var x1 = Math.min(this.x, rect.x), - y1 = Math.min(this.y, rect.y), - x2 = Math.max(this.x + this.width, rect.x + rect.width), - y2 = Math.max(this.y + this.height, rect.y + rect.height); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - include: function(point) { - point = Point.read(arguments); - var x1 = Math.min(this.x, point.x), - y1 = Math.min(this.y, point.y), - x2 = Math.max(this.x + this.width, point.x), - y2 = Math.max(this.y + this.height, point.y); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - expand: function(hor, ver) { - if (ver === undefined) - ver = hor; - return Rectangle.create(this.x - hor / 2, this.y - ver / 2, - this.width + hor, this.height + ver); - }, - - scale: function(hor, ver) { - return this.expand(this.width * hor - this.width, - this.height * (ver === undefined ? hor : ver) - this.height); - }, - - statics: { - create: function(x, y, width, height) { - return new Rectangle(Rectangle.dont).set(x, y, width, height); - } - } -}, new function() { - return Base.each([ - ['Top', 'Left'], ['Top', 'Right'], - ['Bottom', 'Left'], ['Bottom', 'Right'], - ['Left', 'Center'], ['Top', 'Center'], - ['Right', 'Center'], ['Bottom', 'Center'] - ], - function(parts, index) { - var part = parts.join(''); - var xFirst = /^[RL]/.test(part); - if (index >= 4) - parts[1] += xFirst ? 'Y' : 'X'; - var x = parts[xFirst ? 0 : 1], - y = parts[xFirst ? 1 : 0], - getX = 'get' + x, - getY = 'get' + y, - setX = 'set' + x, - setY = 'set' + y, - get = 'get' + part, - set = 'set' + part; - this[get] = function() { - return LinkedPoint.create(this, set, - this[getX](), this[getY](), arguments[0]); - }; - this[set] = function(point) { - point = Point.read(arguments); - return this[setX](point.x)[setY](point.y); - }; - }, {}); -}); - -var LinkedRectangle = Rectangle.extend({ - set: function(x, y, width, height, dontNotify) { - this._x = x; - this._y = y; - this._width = width; - this._height = height; - if (!dontNotify) - this._owner[this._setter](this); - return this; - }, - - statics: { - create: function(owner, setter, x, y, width, height) { - var rect = new LinkedRectangle(LinkedRectangle.dont).set( - x, y, width, height, true); - rect._owner = owner; - rect._setter = setter; - return rect; - } - } -}, new function() { - var proto = Rectangle.prototype; - - return Base.each(['x', 'y', 'width', 'height'], function(key) { - var part = Base.capitalize(key); - var internal = '_' + key; - this['get' + part] = function() { - return this[internal]; - }; - - this['set' + part] = function(value) { - this[internal] = value; - if (!this._dontNotify) - this._owner[this._setter](this); - }; - }, Base.each(['Point', 'Size', 'Center', - 'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY', - 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', - 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], - function(key) { - var name = 'set' + key; - this[name] = function(value) { - this._dontNotify = true; - proto[name].apply(this, arguments); - delete this._dontNotify; - this._owner[this._setter](this); - return this; - }; - }, {}) - ); -}); - -var Matrix = this.Matrix = Base.extend({ - initialize: function(arg) { - var count = arguments.length, - ok = true; - if (count == 6) { - this.set.apply(this, arguments); - } else if (count == 1) { - if (arg instanceof Matrix) { - this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty); - } else if (Array.isArray(arg)) { - this.set.apply(this, arg); - } else { - ok = false; - } - } else if (count == 0) { - this._a = this._d = 1; - this._c = this._b = this._tx = this._ty = 0; - } else { - ok = false; - } - if (!ok) - throw new Error('Unsupported matrix parameters'); - }, - - clone: function() { - return Matrix.create(this._a, this._c, this._b, this._d, - this._tx, this._ty); - }, - - set: function(a, c, b, d, tx, ty) { - this._a = a; - this._c = c; - this._b = b; - this._d = d; - this._tx = tx; - this._ty = ty; - return this; - }, - - scale: function( hor, ver, center) { - if (arguments.length < 2 || typeof ver === 'object') { - center = Point.read(arguments, 1); - ver = hor; - } else { - center = Point.read(arguments, 2); - } - if (center) - this.translate(center); - this._a *= hor; - this._c *= hor; - this._b *= ver; - this._d *= ver; - if (center) - this.translate(center.negate()); - return this; - }, - - translate: function(point) { - point = Point.read(arguments); - var x = point.x, y = point.y; - this._tx += x * this._a + y * this._b; - this._ty += x * this._c + y * this._d; - return this; - }, - - rotate: function(angle, center) { - return this.concatenate( - Matrix.getRotateInstance.apply(Matrix, arguments)); - }, - - shear: function( hor, ver, center) { - if (arguments.length < 2 || typeof ver === 'object') { - center = Point.read(arguments, 1); - ver = hor; - } else { - center = Point.read(arguments, 2); - } - if (center) - this.translate(center); - var a = this._a, - c = this._c; - this._a += ver * this._b; - this._c += ver * this._d; - this._b += hor * a; - this._d += hor * c; - if (center) - this.translate(center.negate()); - return this; - }, - - toString: function() { - var format = Base.formatNumber; - return '[[' + [format(this._a), format(this._b), - format(this._tx)].join(', ') + '], [' - + [format(this._c), format(this._d), - format(this._ty)].join(', ') + ']]'; - }, - - getValues: function() { - return [ this._a, this._c, this._b, this._d, this._tx, this._ty ]; - }, - - concatenate: function(mx) { - var a = this._a, - b = this._b, - c = this._c, - d = this._d; - this._a = mx._a * a + mx._c * b; - this._b = mx._b * a + mx._d * b; - this._tx += mx._tx * a + mx._ty * b; - this._c = mx._a * c + mx._c * d; - this._d = mx._b * c + mx._d * d; - this._ty += mx._tx * c + mx._ty * d; - return this; - }, - - preConcatenate: function(mx) { - var a = this._a, - b = this._b, - c = this._c, - d = this._d, - tx = this._tx, - ty = this._ty; - this._a = mx._a * a + mx._b * c; - this._c = mx._c * a + mx._d * c; - this._b = mx._a * b + mx._b * d; - this._d = mx._c * b + mx._d * d; - this._tx = mx._a * tx + mx._b * ty + mx._tx; - this._ty = mx._c * tx + mx._d * ty + mx._ty; - return this; - }, - - transform: function( src, srcOff, dst, dstOff, numPts) { - return arguments.length < 5 - ? this._transformPoint(Point.read(arguments)) - : this._transformCoordinates(src, srcOff, dst, dstOff, numPts); - }, - - _transformPoint: function(point, dest, dontNotify) { - var x = point.x, - y = point.y; - if (!dest) - dest = new Point(Point.dont); - return dest.set( - x * this._a + y * this._b + this._tx, - x * this._c + y * this._d + this._ty, - dontNotify - ); - }, - - _transformCoordinates: function(src, srcOff, dst, dstOff, numPts) { - var i = srcOff, j = dstOff, - srcEnd = srcOff + 2 * numPts; - while (i < srcEnd) { - var x = src[i++]; - var y = src[i++]; - dst[j++] = x * this._a + y * this._b + this._tx; - dst[j++] = x * this._c + y * this._d + this._ty; - } - return dst; - }, - - _transformCorners: function(rect) { - var x1 = rect.x, - y1 = rect.y, - x2 = x1 + rect.width, - y2 = y1 + rect.height, - coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ]; - return this._transformCoordinates(coords, 0, coords, 0, 4); - }, - - _transformBounds: function(bounds) { - var coords = this._transformCorners(bounds), - min = coords.slice(0, 2), - max = coords.slice(0); - for (var i = 2; i < 8; i++) { - var val = coords[i], - j = i & 1; - if (val < min[j]) - min[j] = val; - else if (val > max[j]) - max[j] = val; - } - return Rectangle.create(min[0], min[1], - max[0] - min[0], max[1] - min[1]); - }, - - inverseTransform: function(point) { - return this._inverseTransform(Point.read(arguments)); - }, - - _getDeterminant: function() { - var det = this._a * this._d - this._b * this._c; - return isFinite(det) && Math.abs(det) > Numerical.EPSILON - && isFinite(this._tx) && isFinite(this._ty) - ? det : null; - }, - - _inverseTransform: function(point, dest, dontNotify) { - var det = this._getDeterminant(); - if (!det) - return null; - var x = point.x - this._tx, - y = point.y - this._ty; - if (!dest) - dest = new Point(Point.dont); - return dest.set( - (x * this._d - y * this._b) / det, - (y * this._a - x * this._c) / det, - dontNotify - ); - }, - - getTranslation: function() { - return new Point(this._tx, this._ty); - }, - - getScaling: function() { - var hor = Math.sqrt(this._a * this._a + this._c * this._c), - ver = Math.sqrt(this._b * this._b + this._d * this._d); - return new Point(this._a < 0 ? -hor : hor, this._b < 0 ? -ver : ver); - }, - - getRotation: function() { - var angle1 = -Math.atan2(this._b, this._d), - angle2 = Math.atan2(this._c, this._a); - return Math.abs(angle1 - angle2) < Numerical.TOLERANCE - ? angle1 * 180 / Math.PI : undefined; - }, - - isIdentity: function() { - return this._a == 1 && this._c == 0 && this._b == 0 && this._d == 1 - && this._tx == 0 && this._ty == 0; - }, - - isInvertible: function() { - return !!this._getDeterminant(); - }, - - isSingular: function() { - return !this._getDeterminant(); - }, - - createInverse: function() { - var det = this._getDeterminant(); - return det && Matrix.create( - this._d / det, - -this._c / det, - -this._b / det, - this._a / det, - (this._b * this._ty - this._d * this._tx) / det, - (this._c * this._tx - this._a * this._ty) / det); - }, - - createShiftless: function() { - return Matrix.create(this._a, this._c, this._b, this._d, 0, 0); - }, - - setToScale: function(hor, ver) { - return this.set(hor, 0, 0, ver, 0, 0); - }, - - setToTranslation: function(delta) { - delta = Point.read(arguments); - return this.set(1, 0, 0, 1, delta.x, delta.y); - }, - - setToShear: function(hor, ver) { - return this.set(1, ver, hor, 1, 0, 0); - }, - - setToRotation: function(angle, center) { - center = Point.read(arguments, 1); - angle = angle * Math.PI / 180; - var x = center.x, - y = center.y, - cos = Math.cos(angle), - sin = Math.sin(angle); - return this.set(cos, sin, -sin, cos, - x - x * cos + y * sin, - y - x * sin - y * cos); - }, - - applyToContext: function(ctx, reset) { - ctx[reset ? 'setTransform' : 'transform']( - this._a, this._c, this._b, this._d, this._tx, this._ty); - return this; - }, - - statics: { - create: function(a, c, b, d, tx, ty) { - return new Matrix(Matrix.dont).set(a, c, b, d, tx, ty); - }, - - getScaleInstance: function(hor, ver) { - var mx = new Matrix(); - return mx.setToScale.apply(mx, arguments); - }, - - getTranslateInstance: function(delta) { - var mx = new Matrix(); - return mx.setToTranslation.apply(mx, arguments); - }, - - getShearInstance: function(hor, ver, center) { - var mx = new Matrix(); - return mx.setToShear.apply(mx, arguments); - }, - - getRotateInstance: function(angle, center) { - var mx = new Matrix(); - return mx.setToRotation.apply(mx, arguments); - } - } -}, new function() { - return Base.each({ - scaleX: '_a', - scaleY: '_d', - translateX: '_tx', - translateY: '_ty', - shearX: '_b', - shearY: '_c' - }, function(prop, name) { - name = Base.capitalize(name); - this['get' + name] = function() { - return this[prop]; - }; - this['set' + name] = function(value) { - this[prop] = value; - }; - }, {}); -}); - -var Line = this.Line = Base.extend({ - initialize: function(point1, point2, infinite) { - point1 = Point.read(arguments, 0, 1); - point2 = Point.read(arguments, 1, 1); - if (arguments.length == 3) { - this.point = point1; - this.vector = point2.subtract(point1); - this.infinite = infinite; - } else { - this.point = point1; - this.vector = point2; - this.infinite = true; - } - }, - - intersect: function(line) { - var cross = this.vector.cross(line.vector); - if (Math.abs(cross) <= Numerical.EPSILON) - return null; - var v = line.point.subtract(this.point), - t1 = v.cross(line.vector) / cross, - t2 = v.cross(this.vector) / cross; - return (this.infinite || 0 <= t1 && t1 <= 1) - && (line.infinite || 0 <= t2 && t2 <= 1) - ? this.point.add(this.vector.multiply(t1)) : null; - }, - - getSide: function(point) { - var v1 = this.vector, - v2 = point.subtract(this.point), - ccw = v2.cross(v1); - if (ccw == 0) { - ccw = v2.dot(v1); - if (ccw > 0) { - ccw = v2.subtract(v1).dot(v1); - if (ccw < 0) - ccw = 0; - } - } - return ccw < 0 ? -1 : ccw > 0 ? 1 : 0; - }, - - getDistance: function(point) { - var m = this.vector.y / this.vector.x, - b = this.point.y - (m * this.point.x); - var dist = Math.abs(point.y - (m * point.x) - b) / Math.sqrt(m * m + 1); - return this.infinite ? dist : Math.min(dist, - point.getDistance(this.point), - point.getDistance(this.point.add(this.vector))); - } -}); - -var Project = this.Project = PaperScopeItem.extend({ - _list: 'projects', - _reference: 'project', - - initialize: function() { - this.base(true); - this._currentStyle = new PathStyle(); - this._selectedItems = {}; - this._selectedItemCount = 0; - this.layers = []; - this.symbols = []; - this.activeLayer = new Layer(); - }, - - _needsRedraw: function() { - if (this._scope) - this._scope._needsRedraw(); - }, - - getCurrentStyle: function() { - return this._currentStyle; - }, - - setCurrentStyle: function(style) { - this._currentStyle.initialize(style); - }, - - getIndex: function() { - return this._index; - }, - - getSelectedItems: function() { - var items = []; - Base.each(this._selectedItems, function(item) { - items.push(item); - }); - return items; - }, - - _updateSelection: function(item) { - if (item._selected) { - this._selectedItemCount++; - this._selectedItems[item.getId()] = item; - } else { - this._selectedItemCount--; - delete this._selectedItems[item.getId()]; - } - }, - - selectAll: function() { - for (var i = 0, l = this.layers.length; i < l; i++) - this.layers[i].setSelected(true); - }, - - deselectAll: function() { - for (var i in this._selectedItems) - this._selectedItems[i].setSelected(false); - }, - - hitTest: function(point, options) { - options = HitResult.getOptions(point, options); - point = options.point; - for (var i = this.layers.length - 1; i >= 0; i--) { - var res = this.layers[i].hitTest(point, options); - if (res) return res; - } - return null; - }, - - draw: function(ctx) { - ctx.save(); - var param = { offset: new Point(0, 0) }; - for (var i = 0, l = this.layers.length; i < l; i++) - Item.draw(this.layers[i], ctx, param); - ctx.restore(); - - if (this._selectedItemCount > 0) { - ctx.save(); - ctx.strokeWidth = 1; - ctx.strokeStyle = ctx.fillStyle = '#009dec'; - param = { selection: true }; - Base.each(this._selectedItems, function(item) { - item.draw(ctx, param); - }); - ctx.restore(); - } - } -}); - -var Symbol = this.Symbol = Base.extend({ - initialize: function(item) { - this.project = paper.project; - this.project.symbols.push(this); - this.setDefinition(item); - this._instances = {}; - }, - - _changed: function(flags) { - Base.each(this._instances, function(item) { - item._changed(flags); - }); - }, - - getDefinition: function() { - return this._definition; - }, - - setDefinition: function(item) { - if (item._parentSymbol) - item = item.clone(); - if (this._definition) - delete this._definition._parentSymbol; - this._definition = item; - item.remove(); - item.setPosition(new Point()); - item._parentSymbol = this; - this._changed(Change.GEOMETRY); - }, - - place: function(position) { - return new PlacedSymbol(this, position); - }, - - clone: function() { - return new Symbol(this._definition.clone()); - } -}); - -var ChangeFlag = { - APPEARANCE: 1, - HIERARCHY: 2, - GEOMETRY: 4, - STROKE: 8, - STYLE: 16, - ATTRIBUTE: 32, - CONTENT: 64, - PIXELS: 128, - CLIPPING: 256 -}; - -var Change = { - HIERARCHY: ChangeFlag.HIERARCHY | ChangeFlag.APPEARANCE, - GEOMETRY: ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE, - STROKE: ChangeFlag.STROKE | ChangeFlag.STYLE | ChangeFlag.APPEARANCE, - STYLE: ChangeFlag.STYLE | ChangeFlag.APPEARANCE, - ATTRIBUTE: ChangeFlag.ATTRIBUTE | ChangeFlag.APPEARANCE, - CONTENT: ChangeFlag.CONTENT | ChangeFlag.APPEARANCE, - PIXELS: ChangeFlag.PIXELS | ChangeFlag.APPEARANCE -}; - -var Item = this.Item = Base.extend({ - initialize: function() { - this._id = ++Item._id; - if (!this._project) - paper.project.activeLayer.addChild(this); - this._style = PathStyle.create(this); - this.setStyle(this._project.getCurrentStyle()); - }, - - _changed: function(flags) { - if (flags & ChangeFlag.GEOMETRY) { - delete this._bounds; - delete this._position; - } - if (flags & ChangeFlag.APPEARANCE) { - this._project._needsRedraw(); - } - if (this._parentSymbol) - this._parentSymbol._changed(flags); - if (this._project._changes) { - var entry = this._project._changesById[this._id]; - if (entry) { - entry.flags |= flags; - } else { - entry = { item: this, flags: flags }; - this._project._changesById[this._id] = entry; - this._project._changes.push(entry); - } - } - }, - - getId: function() { - return this._id; - }, - - getName: function() { - return this._name; - }, - - setName: function(name) { - - if (this._name) - this._removeFromNamed(); - this._name = name || undefined; - if (name) { - var children = this._parent._children, - namedChildren = this._parent._namedChildren; - (namedChildren[name] = namedChildren[name] || []).push(this); - children[name] = this; - } - this._changed(ChangeFlag.ATTRIBUTE); - }, - - getPosition: function() { - var pos = this._position - || (this._position = this.getBounds().getCenter()); - return LinkedPoint.create(this, 'setPosition', pos._x, pos._y); - }, - - setPosition: function(point) { - this.translate(Point.read(arguments).subtract(this.getPosition())); - }, - - getStyle: function() { - return this._style; - }, - - setStyle: function(style) { - this._style.initialize(style); - }, - - statics: { - _id: 0 - } -}, new function() { - return Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'], - function(name) { - var part = Base.capitalize(name), - name = '_' + name; - this['get' + part] = function() { - return this[name]; - }; - this['set' + part] = function(value) { - if (value != this[name]) { - this[name] = value; - this._changed(name === '_locked' - ? ChangeFlag.ATTRIBUTE : Change.ATTRIBUTE); - } - }; - }, {}); -}, { - - _locked: false, - - _visible: true, - - _blendMode: 'normal', - - _opacity: 1, - - _guide: false, - - isSelected: function() { - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) - if (this._children[i].isSelected()) - return true; - } - return this._selected; - }, - - setSelected: function(selected) { - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - this._children[i].setSelected(selected); - } - } else if ((selected = !!selected) != this._selected) { - this._selected = selected; - this._project._updateSelection(this); - this._changed(Change.ATTRIBUTE); - } - }, - - _selected: false, - - isFullySelected: function() { - if (this._children && this._selected) { - for (var i = 0, l = this._children.length; i < l; i++) - if (!this._children[i].isFullySelected()) - return false; - return true; - } - return this._selected; - }, - - setFullySelected: function(selected) { - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - this._children[i].setFullySelected(selected); - } - } - this.setSelected(selected); - }, - - isClipMask: function() { - return this._clipMask; - }, - - setClipMask: function(clipMask) { - if (this._clipMask != (clipMask = !!clipMask)) { - this._clipMask = clipMask; - if (clipMask) { - this.setFillColor(null); - this.setStrokeColor(null); - } - this._changed(Change.ATTRIBUTE); - if (this._parent) - this._parent._changed(ChangeFlag.CLIPPING); - } - }, - - _clipMask: false, - - getProject: function() { - return this._project; - }, - - _setProject: function(project) { - if (this._project != project) { - this._project = project; - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - this._children[i]._setProject(project); - } - } - } - }, - - getLayer: function() { - var parent = this; - while (parent = parent._parent) { - if (parent instanceof Layer) - return parent; - } - return null; - }, - - getParent: function() { - return this._parent; - }, - - getChildren: function() { - return this._children; - }, - - setChildren: function(items) { - this.removeChildren(); - this.addChildren(items); - }, - - getFirstChild: function() { - return this._children && this._children[0] || null; - }, - - getLastChild: function() { - return this._children && this._children[this._children.length - 1] - || null; - }, - - getNextSibling: function() { - return this._parent && this._parent._children[this._index + 1] || null; - }, - - getPreviousSibling: function() { - return this._parent && this._parent._children[this._index - 1] || null; - }, - - getIndex: function() { - return this._index; - }, - - clone: function() { - return this._clone(new this.constructor()); - }, - - _clone: function(copy) { - copy.setStyle(this._style); - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) - copy.addChild(this._children[i].clone()); - } - var keys = ['_locked', '_visible', '_blendMode', '_opacity', - '_clipMask', '_guide']; - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - if (this.hasOwnProperty(key)) - copy[key] = this[key]; - } - copy.setSelected(this._selected); - if (this._name) - copy.setName(this._name); - return copy; - }, - - copyTo: function(itemOrProject) { - var copy = this.clone(); - if (itemOrProject.layers) { - itemOrProject.activeLayer.addChild(copy); - } else { - itemOrProject.addChild(copy); - } - return copy; - }, - - rasterize: function(resolution) { - var bounds = this.getStrokeBounds(), - scale = (resolution || 72) / 72, - canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)), - ctx = canvas.getContext('2d'), - matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); - matrix.applyToContext(ctx); - this.draw(ctx, {}); - var raster = new Raster(canvas); - raster.setBounds(bounds); - return raster; - }, - - hitTest: function(point, options, matrix) { - options = HitResult.getOptions(point, options); - point = options.point; - if (!this._children && !this.getRoughBounds(matrix) - .expand(options.tolerance)._containsPoint(point)) - return null; - if ((options.center || options.bounds) && - !(this instanceof Layer && !this._parent)) { - var bounds = this.getBounds(), - that = this, - points = ['TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', - 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], - res; - function checkBounds(type, part) { - var pt = bounds['get' + part]().transform(matrix); - if (point.getDistance(pt) < options.tolerance) - return new HitResult(type, that, - { name: Base.hyphenate(part), point: pt }); - } - if (options.center && (res = checkBounds('center', 'Center'))) - return res; - if (options.bounds) { - for (var i = 0; i < 8; i++) - if (res = checkBounds('bounds', points[i])) - return res; - } - } - - return this._children || !(options.guides && !this._guide - || options.selected && !this._selected) - ? this._hitTest(point, options, matrix) : null; - }, - - _hitTest: function(point, options, matrix) { - if (this._children) { - for (var i = this._children.length - 1; i >= 0; i--) { - var res = this._children[i].hitTest(point, options, matrix); - if (res) return res; - } - } - }, - - addChild: function(item) { - return this.insertChild(undefined, item); - }, - - insertChild: function(index, item) { - if (this._children) { - item._remove(false, true); - Base.splice(this._children, [item], index, 0); - item._parent = this; - item._setProject(this._project); - if (item._name) - item.setName(item._name); - this._changed(Change.HIERARCHY); - return true; - } - return false; - }, - - addChildren: function(items) { - for (var i = 0, l = items && items.length; i < l; i++) - this.insertChild(undefined, items[i]); - }, - - insertChildren: function(index, items) { - for (var i = 0, l = items && items.length; i < l; i++) { - if (this.insertChild(index, items[i])) - index++; - } - }, - - insertAbove: function(item) { - return item._parent && item._parent.insertChild( - item._index + 1, this); - }, - - insertBelow: function(item) { - return item._parent && item._parent.insertChild( - item._index - 1, this); - }, - - appendTop: function(item) { - return this.addChild(item); - }, - - appendBottom: function(item) { - return this.insertChild(0, item); - }, - - moveAbove: function(item) { - return this.insertAbove(item); - }, - - moveBelow: function(item) { - return this.insertBelow(item); - }, - - _removeFromNamed: function() { - var children = this._parent._children, - namedChildren = this._parent._namedChildren, - name = this._name, - namedArray = namedChildren[name], - index = namedArray ? namedArray.indexOf(this) : -1; - if (index == -1) - return; - if (children[name] == this) - delete children[name]; - namedArray.splice(index, 1); - if (namedArray.length) { - children[name] = namedArray[namedArray.length - 1]; - } else { - delete namedChildren[name]; - } - }, - - _remove: function(deselect, notify) { - if (this._parent) { - if (deselect) - this.setSelected(false); - if (this._name) - this._removeFromNamed(); - Base.splice(this._parent._children, null, this._index, 1); - if (notify) - this._parent._changed(Change.HIERARCHY); - this._parent = null; - return true; - } - return false; - }, - - remove: function() { - return this._remove(true, true); - }, - - removeChildren: function(from, to) { - if (!this._children) - return null; - from = from || 0; - to = Base.pick(to, this._children.length); - var removed = this._children.splice(from, to - from); - for (var i = removed.length - 1; i >= 0; i--) - removed[i]._remove(true, false); - if (removed.length > 0) - this._changed(Change.HIERARCHY); - return removed; - }, - - reverseChildren: function() { - if (this._children) { - this._children.reverse(); - for (var i = 0, l = this._children.length; i < l; i++) - this._children[i]._index = i; - this._changed(Change.HIERARCHY); - } - }, - - isEditable: function() { - var item = this; - while (item) { - if (!item._visible || item._locked) - return false; - item = item._parent; - } - return true; - }, - - _getOrder: function(item) { - function getList(item) { - var list = []; - do { - list.unshift(item); - } while (item = item._parent) - return list; - } - var list1 = getList(this), - list2 = getList(item); - for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) { - if (list1[i] != list2[i]) { - return list1[i]._index < list2[i]._index ? 1 : -1; - } - } - return 0; - }, - - hasChildren: function() { - return this._children && this._children.length > 0; - }, - - isAbove: function(item) { - return this._getOrder(item) == -1; - }, - - isBelow: function(item) { - return this._getOrder(item) == 1; - }, - - isParent: function(item) { - return this._parent == item; - }, - - isChild: function(item) { - return item && item._parent == this; - }, - - isDescendant: function(item) { - var parent = this; - while (parent = parent._parent) { - if (parent == item) - return true; - } - return false; - }, - - isAncestor: function(item) { - return item ? item.isDescendant(this) : false; - }, - - isGroupedWith: function(item) { - var parent = this._parent; - while (parent) { - if (parent._parent - && (parent instanceof Group || parent instanceof CompoundPath) - && item.isDescendant(parent)) - return true; - parent = parent._parent; - } - return false; - }, - - _getBounds: function(getter, cacheName, args) { - var children = this._children; - if (!children || children.length == 0) - return new Rectangle(); - var x1 = Infinity, - x2 = -x1, - y1 = x1, - y2 = x2; - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - if (child._visible) { - var rect = child[getter](args[0]); - x1 = Math.min(rect.x, x1); - y1 = Math.min(rect.y, y1); - x2 = Math.max(rect.x + rect.width, x2); - y2 = Math.max(rect.y + rect.height, y2); - } - } - var bounds = Rectangle.create(x1, y1, x2 - x1, y2 - y1); - return getter == 'getBounds' ? this._createBounds(bounds) : bounds; - }, - - _createBounds: function(rect) { - return LinkedRectangle.create(this, 'setBounds', - rect.x, rect.y, rect.width, rect.height); - }, - - getBounds: function() { - return this._getBounds('getBounds', '_bounds', arguments); - }, - - setBounds: function(rect) { - rect = Rectangle.read(arguments); - var bounds = this.getBounds(), - matrix = new Matrix(), - center = rect.getCenter(); - matrix.translate(center); - if (rect.width != bounds.width || rect.height != bounds.height) { - matrix.scale( - bounds.width != 0 ? rect.width / bounds.width : 1, - bounds.height != 0 ? rect.height / bounds.height : 1); - } - center = bounds.getCenter(); - matrix.translate(-center.x, -center.y); - this.transform(matrix); - }, - - getStrokeBounds: function() { - return this._getBounds('getStrokeBounds', '_strokeBounds', arguments); - }, - - getHandleBounds: function() { - return this._getBounds('getHandleBounds', '_handleBounds', arguments); - }, - - getRoughBounds: function() { - return this._getBounds('getRoughBounds', '_roughBounds', arguments); - }, - - scale: function(hor, ver , center) { - if (arguments.length < 2 || typeof ver === 'object') { - center = ver; - ver = hor; - } - return this.transform(new Matrix().scale(hor, ver, - center || this.getPosition())); - }, - - translate: function(delta) { - var mx = new Matrix(); - return this.transform(mx.translate.apply(mx, arguments)); - }, - - rotate: function(angle, center) { - return this.transform(new Matrix().rotate(angle, - center || this.getPosition())); - }, - - shear: function(hor, ver, center) { - if (arguments.length < 2 || typeof ver === 'object') { - center = ver; - ver = hor; - } - return this.transform(new Matrix().shear(hor, ver, - center || this.getPosition())); - }, - - transform: function(matrix, flags) { - var bounds = this._bounds, - position = this._position, - children = this._children; - if (this._transform) { - this._transform(matrix, flags); - this._changed(Change.GEOMETRY); - } - if (bounds && matrix.getRotation() % 90 === 0) { - this._bounds = this._createBounds( - matrix._transformBounds(bounds)); - this._position = this._bounds.getCenter(); - } else if (position) { - this._position = matrix._transformPoint(position, position, true); - } - for (var i = 0, l = children && children.length; i < l; i++) - children[i].transform(matrix, flags); - return this; - }, - - fitBounds: function(rectangle, fill) { - rectangle = Rectangle.read(arguments); - var bounds = this.getBounds(), - itemRatio = bounds.height / bounds.width, - rectRatio = rectangle.height / rectangle.width, - scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio) - ? rectangle.width / bounds.width - : rectangle.height / bounds.height, - delta = rectangle.getCenter().subtract(bounds.getCenter()), - newBounds = new Rectangle(new Point(), - new Size(bounds.width * scale, bounds.height * scale)); - newBounds.setCenter(rectangle.getCenter()); - this.setBounds(newBounds); - }, - - toString: function() { - return (this.constructor._name || 'Item') + (this._name - ? " '" + this._name + "'" - : ' @' + this._id); - }, - - statics: { - drawSelectedBounds: function(bounds, ctx, matrix) { - var coords = matrix._transformCorners(bounds); - ctx.beginPath(); - for (var i = 0; i < 8; i++) - ctx[i == 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]); - ctx.closePath(); - ctx.stroke(); - for (var i = 0; i < 8; i++) { - ctx.beginPath(); - ctx.rect(coords[i] - 2, coords[++i] - 2, 4, 4); - ctx.fill(); - } - }, - - draw: function(item, ctx, param) { - if (!item._visible || item._opacity == 0) - return; - - var tempCanvas, parentCtx; - if (item._blendMode !== 'normal' - || item._opacity < 1 - && !(item._segments && (!item.getFillColor() - || !item.getStrokeColor()))) { - var bounds = item.getStrokeBounds() || item.getBounds(); - if (!bounds.width || !bounds.height) - return; - - var itemOffset = bounds.getTopLeft().floor(), - size = bounds.getSize().ceil().add(new Size(1, 1)); - tempCanvas = CanvasProvider.getCanvas(size); - - parentCtx = ctx; - - ctx = tempCanvas.getContext('2d'); - ctx.save(); - - ctx.translate(-itemOffset.x, -itemOffset.y); - } - var savedOffset; - if (itemOffset) { - savedOffset = param.offset; - param.offset = itemOffset; - } - item.draw(ctx, param); - if (itemOffset) - param.offset = savedOffset; - - if (tempCanvas) { - - ctx.restore(); - - if (item._blendMode !== 'normal') { - var pixelOffset = itemOffset.subtract(param.offset); - BlendMode.process(item._blendMode, ctx, parentCtx, - item._opacity, pixelOffset); - } else { - parentCtx.save(); - parentCtx.globalAlpha = item._opacity; - parentCtx.drawImage(tempCanvas, - itemOffset.x, itemOffset.y); - parentCtx.restore(); - } - - CanvasProvider.returnCanvas(tempCanvas); - } - } - } -}, new function() { - - var sets = { - down: {}, drag: {}, up: {}, move: {} - }; - - function removeAll(set) { - for (var id in set) { - var item = set[id]; - item.remove(); - for (var type in sets) { - var other = sets[type]; - if (other != set && other[item.getId()]) - delete other[item.getId()]; - } - } - } - - function installHandler(name) { - var handler = 'onMouse' + Base.capitalize(name); - var func = paper.tool[handler]; - if (!func || !func._installed) { - var hash = {}; - hash[handler] = function(event) { - if (name === 'up') - sets.drag = {}; - removeAll(sets[name]); - sets[name] = {}; - if (this.base) - this.base(event); - }; - paper.tool.inject(hash); - paper.tool[handler]._installed = true; - } - } - - return Base.each(['down', 'drag', 'up', 'move'], function(name) { - this['removeOn' + Base.capitalize(name)] = function() { - var hash = {}; - hash[name] = true; - return this.removeOn(hash); - }; - }, { - removeOn: function(obj) { - for (var name in obj) { - if (obj[name]) { - sets[name][this.getId()] = this; - if (name === 'drag') - installHandler('up'); - installHandler(name); - } - } - return this; - } - }); -}); - -var Group = this.Group = Item.extend({ - initialize: function(items) { - this.base(); - this._children = []; - this._namedChildren = {}; - this.addChildren(!items || !Array.isArray(items) - || typeof items[0] !== 'object' ? arguments : items); - }, - - _changed: function(flags) { - Item.prototype._changed.call(this, flags); - if (flags & (ChangeFlag.HIERARCHY | ChangeFlag.CLIPPING)) { - delete this._clipItem; - } - }, - - _getClipItem: function() { - if (this._clipItem !== undefined) - return this._clipItem; - for (var i = 0, l = this._children.length; i < l; i++) { - var child = this._children[i]; - if (child._clipMask) - return this._clipItem = child; - } - return this._clipItem = null; - }, - - isClipped: function() { - return !!this._getClipItem(); - }, - - setClipped: function(clipped) { - var child = this.getFirstChild(); - if (child) - child.setClipMask(clipped); - return this; - }, - - draw: function(ctx, param) { - var clipItem = this._getClipItem(); - if (clipItem) - Item.draw(clipItem, ctx, param); - for (var i = 0, l = this._children.length; i < l; i++) { - var item = this._children[i]; - if (item != clipItem) - Item.draw(item, ctx, param); - } - } -}); - -var Layer = this.Layer = Group.extend({ - initialize: function(items) { - this._project = paper.project; - this._index = this._project.layers.push(this) - 1; - this.base.apply(this, arguments); - this.activate(); - }, - - _remove: function(deselect, notify) { - if (this._parent) - return this.base(deselect, notify); - if (this._index != null) { - if (deselect) - this.setSelected(false); - Base.splice(this._project.layers, null, this._index, 1); - this._project._needsRedraw(); - return true; - } - return false; - }, - - getNextSibling: function() { - return this._parent ? this.base() - : this._project.layers[this._index + 1] || null; - }, - - getPreviousSibling: function() { - return this._parent ? this.base() - : this._project.layers[this._index - 1] || null; - }, - - activate: function() { - this._project.activeLayer = this; - } -}, new function () { - function insert(above) { - return function(item) { - if (item instanceof Layer && !item._parent - && this._remove(false, true)) { - Base.splice(item._project.layers, [this], - item._index + (above ? 1 : -1), 0); - this._setProject(item._project); - return true; - } - return this.base(item); - }; - } - - return { - insertAbove: insert(true), - - insertBelow: insert(false) - }; -}); - -var PlacedItem = this.PlacedItem = Item.extend({ - - _transform: function(matrix, flags) { - this._matrix.preConcatenate(matrix); - }, - - _changed: function(flags) { - Item.prototype._changed.call(this, flags); - if (flags & ChangeFlag.GEOMETRY) { - delete this._strokeBounds; - delete this._handleBounds; - delete this._roughBounds; - } - }, - - getMatrix: function() { - return this._matrix; - }, - - setMatrix: function(matrix) { - this._matrix = matrix.clone(); - this._changed(Change.GEOMETRY); - }, - - getBounds: function() { - var useCache = arguments[0] === undefined; - if (useCache && this._bounds) - return this._bounds; - var bounds = this.getStrokeBounds(arguments[0]); - if (useCache) - bounds = this._bounds = this._createBounds(bounds); - return bounds; - }, - - _getBounds: function(getter, cacheName, args) { - var matrix = args[0], - useCache = matrix === undefined; - if (useCache && this[cacheName]) - return this[cacheName]; - matrix = matrix ? matrix.clone().concatenate(this._matrix) - : this._matrix; - var bounds = this._calculateBounds(getter, matrix); - if (useCache) - this[cacheName] = bounds; - return bounds; - } -}); - -var Raster = this.Raster = PlacedItem.extend({ - initialize: function(object) { - this.base(); - if (object.getContext) { - this.setCanvas(object); - } else { - if (typeof object === 'string') - object = document.getElementById(object); - this.setImage(object); - } - this._matrix = new Matrix(); - }, - - clone: function() { - var image = this._image; - if (!image) { - image = CanvasProvider.getCanvas(this._size); - image.getContext('2d').drawImage(this._canvas, 0, 0); - } - var copy = new Raster(image); - copy._matrix = this._matrix.clone(); - return this._clone(copy); - }, - - getSize: function() { - return this._size; - }, - - setSize: function() { - var size = Size.read(arguments), - image = this.getImage(); - this.setCanvas(CanvasProvider.getCanvas(size)); - this.getContext(true).drawImage(image, 0, 0, size.width, size.height); - }, - - getWidth: function() { - return this._size.width; - }, - - getHeight: function() { - return this._size.height; - }, - - getPpi: function() { - var matrix = this._matrix, - orig = new Point(0, 0).transform(matrix), - u = new Point(1, 0).transform(matrix).subtract(orig), - v = new Point(0, 1).transform(matrix).subtract(orig); - return new Size( - 72 / u.getLength(), - 72 / v.getLength() - ); - }, - - getContext: function() { - if (!this._context) - this._context = this.getCanvas().getContext('2d'); - if (arguments[0]) - this._changed(Change.PIXELS); - return this._context; - }, - - setContext: function(context) { - this._context = context; - }, - - getCanvas: function() { - if (!this._canvas) { - this._canvas = CanvasProvider.getCanvas(this._size); - if (this._image) - this.getContext(true).drawImage(this._image, 0, 0); - } - return this._canvas; - }, - - setCanvas: function(canvas) { - if (this._canvas) - CanvasProvider.returnCanvas(this._canvas); - this._canvas = canvas; - this._size = new Size(canvas.width, canvas.height); - this._image = null; - this._context = null; - this._changed(Change.GEOMETRY); - }, - - getImage: function() { - return this._image || this.getCanvas(); - }, - - setImage: function(image) { - if (this._canvas) - CanvasProvider.returnCanvas(this._canvas); - this._image = image; - this._size = new Size(image.naturalWidth, image.naturalHeight); - this._canvas = null; - this._context = null; - this._changed(Change.GEOMETRY); - }, - - getSubImage: function(rect) { - rect = Rectangle.read(arguments); - var canvas = CanvasProvider.getCanvas(rect.getSize()); - canvas.getContext('2d').drawImage(this.getCanvas(), rect.x, rect.y, - canvas.width, canvas.height, 0, 0, canvas.width, canvas.height); - return canvas; - }, - - drawImage: function(image, point) { - point = Point.read(arguments, 1); - this.getContext(true).drawImage(image, point.x, point.y); - }, - - getAverageColor: function(object) { - if (!object) - object = this.getBounds(); - var bounds, path; - if (object instanceof PathItem) { - path = object; - bounds = object.getBounds(); - } else if (object.width) { - bounds = new Rectangle(object); - } else if (object.x) { - bounds = Rectangle.create(object.x - 0.5, object.y - 0.5, 1, 1); - } - var sampleSize = 32, - width = Math.min(bounds.width, sampleSize), - height = Math.min(bounds.height, sampleSize); - var ctx = Raster._sampleContext; - if (!ctx) { - ctx = Raster._sampleContext = CanvasProvider.getCanvas( - new Size(sampleSize)).getContext('2d'); - } else { - ctx.clearRect(0, 0, sampleSize, sampleSize); - } - ctx.save(); - ctx.scale(width / bounds.width, height / bounds.height); - ctx.translate(-bounds.x, -bounds.y); - if (path) - path.draw(ctx, { clip: true }); - this._matrix.applyToContext(ctx); - ctx.drawImage(this._canvas || this._image, - -this._size.width / 2, -this._size.height / 2); - ctx.restore(); - var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width), - Math.ceil(height)).data, - channels = [0, 0, 0], - total = 0; - for (var i = 0, l = pixels.length; i < l; i += 4) { - var alpha = pixels[i + 3]; - total += alpha; - alpha /= 255; - channels[0] += pixels[i] * alpha; - channels[1] += pixels[i + 1] * alpha; - channels[2] += pixels[i + 2] * alpha; - } - for (var i = 0; i < 3; i++) - channels[i] /= total; - return total ? Color.read(channels) : null; - }, - - getPixel: function(point) { - point = Point.read(arguments); - var pixels = this.getContext().getImageData(point.x, point.y, 1, 1).data, - channels = new Array(4); - for (var i = 0; i < 4; i++) - channels[i] = pixels[i] / 255; - return RgbColor.read(channels); - }, - - setPixel: function(point, color) { - var hasPoint = arguments.length == 2; - point = Point.read(arguments, 0, hasPoint ? 1 : 2); - color = Color.read(arguments, hasPoint ? 1 : 2); - var ctx = this.getContext(true), - imageData = ctx.createImageData(1, 1), - alpha = color.getAlpha(); - imageData.data[0] = color.getRed() * 255; - imageData.data[1] = color.getGreen() * 255; - imageData.data[2] = color.getBlue() * 255; - imageData.data[3] = alpha != null ? alpha * 255 : 255; - ctx.putImageData(imageData, point.x, point.y); - }, - - createData: function(size) { - size = Size.read(arguments); - return this.getContext().createImageData(size.width, size.height); - }, - - getData: function(rect) { - rect = Rectangle.read(arguments); - if (rect.isEmpty()) - rect = new Rectangle(this.getSize()); - return this.getContext().getImageData(rect.x, rect.y, - rect.width, rect.height); - }, - - setData: function(data, point) { - point = Point.read(arguments, 1); - this.getContext(true).putImageData(data, point.x, point.y); - }, - - _calculateBounds: function(getter, matrix) { - return matrix._transformBounds( - new Rectangle(this._size).setCenter(0, 0)); - }, - - getHandleBounds: function() { - return this.getStrokeBounds(arguments[0]); - }, - - getRoughBounds: function() { - return this.getStrokeBounds(arguments[0]); - }, - - draw: function(ctx, param) { - if (param.selection) { - var bounds = new Rectangle(this._size).setCenter(0, 0); - Item.drawSelectedBounds(bounds, ctx, this._matrix); - } else { - ctx.save(); - this._matrix.applyToContext(ctx); - ctx.drawImage(this._canvas || this._image, - -this._size.width / 2, -this._size.height / 2); - ctx.restore(); - } - } -}); - -var PlacedSymbol = this.PlacedSymbol = PlacedItem.extend({ - initialize: function(symbol, matrixOrOffset) { - this.base(); - this.setSymbol(symbol instanceof Symbol ? symbol : new Symbol(symbol)); - this._matrix = matrixOrOffset !== undefined - ? matrixOrOffset instanceof Matrix - ? matrixOrOffset - : new Matrix().translate(Point.read(arguments, 1)) - : new Matrix(); - }, - - getSymbol: function() { - return this._symbol; - }, - - setSymbol: function(symbol) { - if (this._symbol) - delete this._symbol._instances[this._id]; - this._symbol = symbol; - symbol._instances[this._id] = this; - }, - - clone: function() { - return this._clone(new PlacedSymbol(this.symbol, this._matrix.clone())); - }, - - _calculateBounds: function(getter, matrix) { - return this.symbol._definition[getter](matrix); - }, - - draw: function(ctx, param) { - if (param.selection) { - Item.drawSelectedBounds(this.symbol._definition.getStrokeBounds(), - ctx, this._matrix); - } else { - ctx.save(); - this._matrix.applyToContext(ctx); - Item.draw(this.symbol.getDefinition(), ctx, param); - ctx.restore(); - } - } - -}); - -HitResult = Base.extend({ - initialize: function(type, item, values) { - this.type = type; - this.item = item; - if (values) { - Base.each(values, function(value, key) { - this[key] = value; - }, this); - } - }, - - statics: { - getOptions: function(point, options) { - return options && options._merged ? options : Base.merge({ - point: Point.read(arguments, 0, 1), - type: null, - tolerance: 2, - fill: !options, - stroke: !options, - segments: !options, - handles: false, - ends: false, - center: false, - bounds: false, - guides: false, - selected: false, - _merged: true - }, options); - } - } -}); - -var Segment = this.Segment = Base.extend({ - initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) { - var count = arguments.length, - createPoint = SegmentPoint.create, - point, handleIn, handleOut; - if (count == 0) { - } else if (count == 1) { - if (arg0.point) { - point = arg0.point; - handleIn = arg0.handleIn; - handleOut = arg0.handleOut; - } else { - point = arg0; - } - } else if (count < 6) { - if (count == 2 && arg1.x === undefined) { - point = [ arg0, arg1 ]; - } else { - point = arg0; - handleIn = arg1; - handleOut = arg2; - } - } else if (count == 6) { - point = [ arg0, arg1 ]; - handleIn = [ arg2, arg3 ]; - handleOut = [ arg4, arg5 ]; - } - createPoint(this, '_point', point); - createPoint(this, '_handleIn', handleIn); - createPoint(this, '_handleOut', handleOut); - }, - - _changed: function(point) { - if (!this._path) - return; - var curve = this._path._curves && this.getCurve(), other; - if (curve) { - curve._changed(); - if (other = (curve[point == this._point - || point == this._handleIn && curve._segment1 == this - ? 'getPrevious' : 'getNext']())) { - other._changed(); - } - } - this._path._changed(Change.GEOMETRY); - }, - - getPoint: function() { - return this._point; - }, - - setPoint: function(point) { - point = Point.read(arguments); - this._point.set(point.x, point.y); - }, - - getHandleIn: function() { - return this._handleIn; - }, - - setHandleIn: function(point) { - point = Point.read(arguments); - this._handleIn.set(point.x, point.y); - }, - - getHandleOut: function() { - return this._handleOut; - }, - - setHandleOut: function(point) { - point = Point.read(arguments); - this._handleOut.set(point.x, point.y); - }, - - _isSelected: function(point) { - var state = this._selectionState; - return point == this._point ? !!(state & SelectionState.POINT) - : point == this._handleIn ? !!(state & SelectionState.HANDLE_IN) - : point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT) - : false; - }, - - _setSelected: function(point, selected) { - var path = this._path, - selected = !!selected, - state = this._selectionState || 0, - selection = [ - !!(state & SelectionState.POINT), - !!(state & SelectionState.HANDLE_IN), - !!(state & SelectionState.HANDLE_OUT) - ]; - if (point == this._point) { - if (selected) { - selection[1] = selection[2] = false; - } else { - var previous = this.getPrevious(), - next = this.getNext(); - selection[1] = previous && (previous._point.isSelected() - || previous._handleOut.isSelected()); - selection[2] = next && (next._point.isSelected() - || next._handleIn.isSelected()); - } - selection[0] = selected; - } else { - var index = point == this._handleIn ? 1 : 2; - if (selection[index] != selected) { - if (selected) - selection[0] = false; - selection[index] = selected; - path._changed(Change.ATTRIBUTE); - } - } - this._selectionState = (selection[0] ? SelectionState.POINT : 0) - | (selection[1] ? SelectionState.HANDLE_IN : 0) - | (selection[2] ? SelectionState.HANDLE_OUT : 0); - if (path && state != this._selectionState) - path._updateSelection(this, state, this._selectionState); - }, - - isSelected: function() { - return this._isSelected(this._point); - }, - - setSelected: function(selected) { - this._setSelected(this._point, selected); - }, - - getIndex: function() { - return this._index !== undefined ? this._index : null; - }, - - getPath: function() { - return this._path || null; - }, - - getCurve: function() { - if (this._path) { - var index = this._index; - if (!this._path._closed && index == this._path._segments.length - 1) - index--; - return this._path.getCurves()[index] || null; - } - return null; - }, - - getNext: function() { - var segments = this._path && this._path._segments; - return segments && (segments[this._index + 1] - || this._path._closed && segments[0]) || null; - }, - - getPrevious: function() { - var segments = this._path && this._path._segments; - return segments && (segments[this._index - 1] - || this._path._closed && segments[segments.length - 1]) || null; - }, - - reverse: function() { - return new Segment(this._point, this._handleOut, this._handleIn); - }, - - remove: function() { - return this._path ? !!this._path.removeSegment(this._index) : false; - }, - - toString: function() { - var parts = [ 'point: ' + this._point ]; - if (!this._handleIn.isZero()) - parts.push('handleIn: ' + this._handleIn); - if (!this._handleOut.isZero()) - parts.push('handleOut: ' + this._handleOut); - return '{ ' + parts.join(', ') + ' }'; - }, - - _transformCoordinates: function(matrix, coords, change) { - var point = this._point, - handleIn = !change || !this._handleIn.isZero() - ? this._handleIn : null, - handleOut = !change || !this._handleOut.isZero() - ? this._handleOut : null, - x = point._x, - y = point._y, - i = 2; - coords[0] = x; - coords[1] = y; - if (handleIn) { - coords[i++] = handleIn._x + x; - coords[i++] = handleIn._y + y; - } - if (handleOut) { - coords[i++] = handleOut._x + x; - coords[i++] = handleOut._y + y; - } - if (!matrix) - return; - matrix._transformCoordinates(coords, 0, coords, 0, i / 2); - x = coords[0]; - y = coords[1]; - if (change) { - point._x = x; - point._y = y; - i = 2; - if (handleIn) { - handleIn._x = coords[i++] - x; - handleIn._y = coords[i++] - y; - } - if (handleOut) { - handleOut._x = coords[i++] - x; - handleOut._y = coords[i++] - y; - } - } else { - if (!handleIn) { - coords[i++] = x; - coords[i++] = y; - } - if (!handleOut) { - coords[i++] = x; - coords[i++] = y; - } - } - } -}); - -var SegmentPoint = Point.extend({ - set: function(x, y) { - this._x = x; - this._y = y; - this._owner._changed(this); - return this; - }, - - getX: function() { - return this._x; - }, - - setX: function(x) { - this._x = x; - this._owner._changed(this); - }, - - getY: function() { - return this._y; - }, - - setY: function(y) { - this._y = y; - this._owner._changed(this); - }, - - isZero: function() { - return this._x == 0 && this._y == 0; - }, - - setSelected: function(selected) { - this._owner._setSelected(this, selected); - }, - - isSelected: function() { - return this._owner._isSelected(this); - }, - - statics: { - create: function(segment, key, pt) { - var point = new SegmentPoint(SegmentPoint.dont), - x, y, selected; - if (!pt) { - x = y = 0; - } else if ((x = pt[0]) !== undefined) { - y = pt[1]; - } else { - if ((x = pt.x) === undefined) { - pt = Point.read(arguments, 2, 1); - x = pt.x; - } - y = pt.y; - selected = pt.selected; - } - point._x = x; - point._y = y; - point._owner = segment; - segment[key] = point; - if (selected) - point.setSelected(true); - return point; - } - } -}); - -var SelectionState = { - HANDLE_IN: 1, - HANDLE_OUT: 2, - POINT: 4 -}; - -var Curve = this.Curve = Base.extend({ - initialize: function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { - var count = arguments.length; - if (count == 0) { - this._segment1 = new Segment(); - this._segment2 = new Segment(); - } else if (count == 1) { - this._segment1 = new Segment(arg0.segment1); - this._segment2 = new Segment(arg0.segment2); - } else if (count == 2) { - this._segment1 = new Segment(arg0); - this._segment2 = new Segment(arg1); - } else if (count == 4) { - this._segment1 = new Segment(arg0, null, arg1); - this._segment2 = new Segment(arg3, arg2, null); - } else if (count == 8) { - var p1 = Point.create(arg0, arg1), - p2 = Point.create(arg6, arg7); - this._segment1 = new Segment(p1, null, - Point.create(arg2, arg3).subtract(p1)); - this._segment2 = new Segment(p2, - Point.create(arg4, arg5).subtract(p2), null); - } - }, - - _changed: function() { - delete this._length; - }, - - getPoint1: function() { - return this._segment1._point; - }, - - setPoint1: function(point) { - point = Point.read(arguments); - this._segment1._point.set(point.x, point.y); - }, - - getPoint2: function() { - return this._segment2._point; - }, - - setPoint2: function(point) { - point = Point.read(arguments); - this._segment2._point.set(point.x, point.y); - }, - - getHandle1: function() { - return this._segment1._handleOut; - }, - - setHandle1: function(point) { - point = Point.read(arguments); - this._segment1._handleOut.set(point.x, point.y); - }, - - getHandle2: function() { - return this._segment2._handleIn; - }, - - setHandle2: function(point) { - point = Point.read(arguments); - this._segment2._handleIn.set(point.x, point.y); - }, - - getSegment1: function() { - return this._segment1; - }, - - getSegment2: function() { - return this._segment2; - }, - - getPath: function() { - return this._path; - }, - - getIndex: function() { - return this._segment1._index; - }, - - getNext: function() { - var curves = this._path && this._path._curves; - return curves && (curves[this._segment1._index + 1] - || this._path._closed && curves[0]) || null; - }, - - getPrevious: function() { - var curves = this._path && this._path._curves; - return curves && (curves[this._segment1._index - 1] - || this._path._closed && curves[curves.length - 1]) || null; - }, - - isSelected: function() { - return this.getHandle1().isSelected() && this.getHandle2().isSelected(); - }, - - setSelected: function(selected) { - this.getHandle1().setSelected(selected); - this.getHandle2().setSelected(selected); - }, - - getValues: function(matrix) { - return Curve.getValues(this._segment1, this._segment2, matrix); - }, - - getPoints: function(matrix) { - var coords = this.getValues(matrix), - points = []; - for (var i = 0; i < 8; i += 2) - points.push(Point.create(coords[i], coords[i + 1])); - return points; - }, - - getLength: function() { - var from = arguments[0], - to = arguments[1]; - fullLength = arguments.length == 0 || from == 0 && to == 1; - if (fullLength && this._length != null) - return this._length; - var length = Curve.getLength(this.getValues(), from, to); - if (fullLength) - this._length = length; - return length; - }, - - getPart: function(from, to) { - return new Curve(Curve.getPart(this.getValues(), from, to)); - }, - - isLinear: function() { - return this._segment1._handleOut.isZero() - && this._segment2._handleIn.isZero(); - }, - - getParameterAt: function(offset, start) { - return Curve.getParameterAt(this.getValues(), offset, - start !== undefined ? start : offset < 0 ? 1 : 0); - }, - - getPoint: function(parameter) { - return Curve.evaluate(this.getValues(), parameter, 0); - }, - - getTangent: function(parameter) { - return Curve.evaluate(this.getValues(), parameter, 1); - }, - - getNormal: function(parameter) { - return Curve.evaluate(this.getValues(), parameter, 2); - }, - - getParameter: function(point) { - point = Point.read(point); - return Curve.getParameter(this.getValues(), point.x, point.y); - }, - - getCrossings: function(point, matrix, roots) { - var vals = this.getValues(matrix), - num = Curve.solveCubic(vals, 1, point.y, roots), - crossings = 0; - for (var i = 0; i < num; i++) { - var t = roots[i]; - if (t >= 0 && t < 1 && Curve.evaluate(vals, t, 0).x > point.x) { - if (t < Numerical.TOLERANCE && Curve.evaluate( - this.getPrevious().getValues(matrix), 1, 1).y - * Curve.evaluate(vals, t, 1).y >= 0) - continue; - crossings++; - } - } - return crossings; - }, - - reverse: function() { - return new Curve(this._segment2.reverse(), this._segment1.reverse()); - }, - - clone: function() { - return new Curve(this._segment1, this._segment2); - }, - - toString: function() { - var parts = [ 'point1: ' + this._segment1._point ]; - if (!this._segment1._handleOut.isZero()) - parts.push('handle1: ' + this._segment1._handleOut); - if (!this._segment2._handleIn.isZero()) - parts.push('handle2: ' + this._segment2._handleIn); - parts.push('point2: ' + this._segment2._point); - return '{ ' + parts.join(', ') + ' }'; - }, - - statics: { - create: function(path, segment1, segment2) { - var curve = new Curve(Curve.dont); - curve._path = path; - curve._segment1 = segment1; - curve._segment2 = segment2; - return curve; - }, - - getValues: function(segment1, segment2, matrix) { - var p1 = segment1._point, - h1 = segment1._handleOut, - h2 = segment2._handleIn, - p2 = segment2._point, - coords = [ - p1._x, p1._y, - p1._x + h1._x, p1._y + h1._y, - p2._x + h2._x, p2._y + h2._y, - p2._x, p2._y - ]; - return matrix - ? matrix._transformCoordinates(coords, 0, coords, 0, 4) - : coords; - }, - - evaluate: function(v, t, type) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7], - x, y; - - if (type == 0 && (t == 0 || t == 1)) { - x = t == 0 ? p1x : p2x; - y = t == 0 ? p1y : p2y; - } else { - var tMin = Numerical.TOLERANCE; - if (t < tMin && c1x == p1x && c1y == p1y) - t = tMin; - else if (t > 1 - tMin && c2x == p2x && c2y == p2y) - t = 1 - tMin; - var cx = 3 * (c1x - p1x), - bx = 3 * (c2x - c1x) - cx, - ax = p2x - p1x - cx - bx, - - cy = 3 * (c1y - p1y), - by = 3 * (c2y - c1y) - cy, - ay = p2y - p1y - cy - by; - - switch (type) { - case 0: - x = ((ax * t + bx) * t + cx) * t + p1x; - y = ((ay * t + by) * t + cy) * t + p1y; - break; - case 1: - case 2: - x = (3 * ax * t + 2 * bx) * t + cx; - y = (3 * ay * t + 2 * by) * t + cy; - break; - } - } - return type == 2 ? new Point(y, -x) : new Point(x, y); - }, - - subdivide: function(v, t) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7]; - if (t === undefined) - t = 0.5; - var u = 1 - t, - p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y, - p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y, - p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y, - p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y, - p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y, - p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y; - return [ - [p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y], - [p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y] - ]; - }, - - solveCubic: function (v, coord, val, roots) { - var p1 = v[coord], - c1 = v[coord + 2], - c2 = v[coord + 4], - p2 = v[coord + 6], - c = 3 * (c1 - p1), - b = 3 * (c2 - c1) - c, - a = p2 - p1 - c - b; - return Numerical.solveCubic(a, b, c, p1 - val, roots, - Numerical.TOLERANCE); - }, - - getParameter: function(v, x, y) { - var txs = [], - tys = [], - sx = Curve.solveCubic(v, 0, x, txs), - sy = Curve.solveCubic(v, 1, y, tys), - tx, ty; - for (var cx = 0; sx == -1 || cx < sx;) { - if (sx == -1 || (tx = txs[cx++]) >= 0 && tx <= 1) { - for (var cy = 0; sy == -1 || cy < sy;) { - if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) { - if (sx == -1) tx = ty; - else if (sy == -1) ty = tx; - if (Math.abs(tx - ty) < Numerical.TOLERANCE) - return (tx + ty) * 0.5; - } - } - if (sx == -1) - break; - } - } - return null; - }, - - getPart: function(v, from, to) { - if (from > 0) - v = Curve.subdivide(v, from)[1]; - if (to < 1) - v = Curve.subdivide(v, (to - from) / (1 - from))[0]; - return v; - }, - - isFlatEnough: function(v) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7], - - a = p1y - p2y, - b = p2x - p1x, - c = p1x * p2y - p2x * p1y, - v1 = a * c1x + b * c1y + c, - v2 = a * c2x + b * c2y + c; - return Math.abs((v1 * v1 + v2 * v2) / (a * (a * a + b * b))) < 0.005; - } - } -}, new function() { - - function getLengthIntegrand(v) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7], - - ax = 9 * (c1x - c2x) + 3 * (p2x - p1x), - bx = 6 * (p1x + c2x) - 12 * c1x, - cx = 3 * (c1x - p1x), - - ay = 9 * (c1y - c2y) + 3 * (p2y - p1y), - by = 6 * (p1y + c2y) - 12 * c1y, - cy = 3 * (c1y - p1y); - - return function(t) { - var dx = (ax * t + bx) * t + cx, - dy = (ay * t + by) * t + cy; - return Math.sqrt(dx * dx + dy * dy); - }; - } - - function getIterations(a, b) { - return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32))); - } - - return { - statics: true, - - getLength: function(v, a, b) { - if (a === undefined) - a = 0; - if (b === undefined) - b = 1; - if (v[0] == v[2] && v[1] == v[3] && v[6] == v[4] && v[7] == v[5]) { - var dx = v[6] - v[0], - dy = v[7] - v[1]; - return (b - a) * Math.sqrt(dx * dx + dy * dy); - } - var ds = getLengthIntegrand(v); - return Numerical.integrate(ds, a, b, getIterations(a, b)); - }, - - getParameterAt: function(v, offset, start) { - if (offset == 0) - return start; - var forward = offset > 0, - a = forward ? start : 0, - b = forward ? 1 : start, - offset = Math.abs(offset), - ds = getLengthIntegrand(v), - rangeLength = Numerical.integrate(ds, a, b, - getIterations(a, b)); - if (offset >= rangeLength) - return forward ? b : a; - var guess = offset / rangeLength, - length = 0; - function f(t) { - var count = getIterations(start, t); - length += start < t - ? Numerical.integrate(ds, start, t, count) - : -Numerical.integrate(ds, t, start, count); - start = t; - return length - offset; - } - return Numerical.findRoot(f, ds, - forward ? a + guess : b - guess, - a, b, 16, Numerical.TOLERANCE); - } - }; -}, new function() { - - var maxDepth = 32, - epsilon = Math.pow(2, -maxDepth - 1); - - var zCubic = [ - [1.0, 0.6, 0.3, 0.1], - [0.4, 0.6, 0.6, 0.4], - [0.1, 0.3, 0.6, 1.0] - ]; - - var xAxis = new Line(new Point(0, 0), new Point(1, 0)); - - function toBezierForm(v, point) { - var n = 3, - degree = 5, - c = [], - d = [], - cd = [], - w = []; - for(var i = 0; i <= n; i++) { - c[i] = v[i].subtract(point); - if (i < n) - d[i] = v[i + 1].subtract(v[i]).multiply(n); - } - - for (var row = 0; row < n; row++) { - cd[row] = []; - for (var column = 0; column <= n; column++) - cd[row][column] = d[row].dot(c[column]); - } - - for (var i = 0; i <= degree; i++) - w[i] = new Point(i / degree, 0); - - for (k = 0; k <= degree; k++) { - var lb = Math.max(0, k - n + 1), - ub = Math.min(k, n); - for (var i = lb; i <= ub; i++) { - var j = k - i; - w[k].y += cd[j][i] * zCubic[j][i]; - } - } - - return w; - } - - function findRoots(w, depth) { - switch (countCrossings(w)) { - case 0: - return []; - case 1: - if (depth >= maxDepth) - return [0.5 * (w[0].x + w[5].x)]; - if (isFlatEnough(w)) { - var line = new Line(w[0], w[5], true); - return [ line.vector.getLength(true) <= Numerical.EPSILON - ? line.point.x - : xAxis.intersect(line).x ]; - } - } - - var p = [[]], - left = [], - right = []; - for (var j = 0; j <= 5; j++) - p[0][j] = new Point(w[j]); - - for (var i = 1; i <= 5; i++) { - p[i] = []; - for (var j = 0 ; j <= 5 - i; j++) - p[i][j] = p[i - 1][j].add(p[i - 1][j + 1]).multiply(0.5); - } - for (var j = 0; j <= 5; j++) { - left[j] = p[j][0]; - right[j] = p[5 - j][j]; - } - - return findRoots(left, depth + 1).concat(findRoots(right, depth + 1)); - } - - function countCrossings(v) { - var crossings = 0, - prevSign = null; - for (var i = 0, l = v.length; i < l; i++) { - var sign = v[i].y < 0 ? -1 : 1; - if (prevSign != null && sign != prevSign) - crossings++; - prevSign = sign; - } - return crossings; - } - - function isFlatEnough(v) { - - var n = v.length - 1, - a = v[0].y - v[n].y, - b = v[n].x - v[0].x, - c = v[0].x * v[n].y - v[n].x * v[0].y, - maxAbove = 0, - maxBelow = 0; - for (var i = 1; i < n; i++) { - var val = a * v[i].x + b * v[i].y + c, - dist = val * val; - if (val < 0 && dist > maxBelow) { - maxBelow = dist; - } else if (dist > maxAbove) { - maxAbove = dist; - } - } - return Math.abs((maxAbove + maxBelow) / (2 * a * (a * a + b * b))) - < epsilon; - } - - return { - getNearestLocation: function(point, matrix) { - var w = toBezierForm(this.getPoints(matrix), point); - var roots = findRoots(w, 0).concat([0, 1]); - var minDist = Infinity, - minT, - minPoint; - for (var i = 0; i < roots.length; i++) { - var pt = this.getPoint(roots[i]), - dist = point.getDistance(pt, true); - if (dist < minDist) { - minDist = dist; - minT = roots[i]; - minPoint = pt; - } - } - return new CurveLocation(this, minT, minPoint, Math.sqrt(minDist)); - }, - - getNearestPoint: function(point, matrix) { - return this.getNearestLocation(point, matrix).getPoint(); - } - }; -}); - -CurveLocation = Base.extend({ - initialize: function(curve, parameter, point, distance) { - this._curve = curve; - this._parameter = parameter; - this._point = point; - this._distance = distance; - }, - - getSegment: function() { - if (!this._segment) { - var curve = this._curve, - parameter = this.getParameter(); - if (parameter == 0) { - this._segment = curve._segment1; - } else if (parameter == 1) { - this._segment = curve._segment2; - } else if (parameter == null) { - return null; - } else { - this._segment = curve.getLength(0, parameter) - < curve.getLength(parameter, 1) - ? curve._segment1 - : curve._segment2; - } - } - return this._segment; - }, - - getCurve: function() { - return this._curve; - }, - - getPath: function() { - return this._curve && this._curve._path; - }, - - getIndex: function() { - return this._curve && this._curve.getIndex(); - }, - - getOffset: function() { - var path = this._curve && this._curve._path; - return path && path._getOffset(this); - }, - - getCurveOffset: function() { - var parameter = this.getParameter(); - return parameter != null && this._curve - && this._curve.getLength(0, parameter); - }, - - getParameter: function() { - if (this._parameter == null && this._curve && this._point) - this._parameter = this._curve.getParameterAt(this._point); - return this._parameter; - }, - - getPoint: function() { - if (!this._point && this._curve && this._parameter != null) - this._point = this._curve.getPoint(this._parameter); - return this._point; - }, - - getTangent: function() { - var parameter = this.getParameter(); - return parameter != null && this._curve - && this._curve.getTangent(parameter); - }, - - getNormal: function() { - var parameter = this.getParameter(); - return parameter != null && this._curve - && this._curve.getNormal(parameter); - }, - - getDistance: function() { - return this._distance; - }, - - toString: function() { - var parts = [], - point = this.getPoint(); - if (point) - parts.push('point: ' + point); - var index = this.getIndex(); - if (index != null) - parts.push('index: ' + index); - var parameter = this.getParameter(); - if (parameter != null) - parts.push('parameter: ' + Base.formatNumber(parameter)); - if (this._distance != null) - parts.push('distance: ' + Base.formatNumber(this._distance)); - return '{ ' + parts.join(', ') + ' }'; - } -}); - -var PathItem = this.PathItem = Item.extend({ - -}); - -var Path = this.Path = PathItem.extend({ - initialize: function(segments) { - this.base(); - this._closed = false; - this._selectedSegmentState = 0; - this.setSegments(!segments || !Array.isArray(segments) - || typeof segments[0] !== 'object' ? arguments : segments); - }, - - clone: function() { - var copy = this._clone(new Path(this._segments)); - copy._closed = this._closed; - if (this._clockwise !== undefined) - copy._clockwise = this._clockwise; - return copy; - }, - - _changed: function(flags) { - Item.prototype._changed.call(this, flags); - if (flags & ChangeFlag.GEOMETRY) { - delete this._strokeBounds; - delete this._handleBounds; - delete this._roughBounds; - delete this._length; - delete this._clockwise; - } else if (flags & ChangeFlag.STROKE) { - delete this._strokeBounds; - } - }, - - getSegments: function() { - return this._segments; - }, - - setSegments: function(segments) { - if (!this._segments) { - this._segments = []; - } else { - this._selectedSegmentState = 0; - this._segments.length = 0; - if (this._curves) - delete this._curves; - } - this._add(Segment.readAll(segments)); - }, - - getFirstSegment: function() { - return this._segments[0]; - }, - - getLastSegment: function() { - return this._segments[this._segments.length - 1]; - }, - - getCurves: function() { - if (!this._curves) { - var segments = this._segments, - length = segments.length; - if (!this._closed && length > 0) - length--; - this._curves = new Array(length); - for (var i = 0; i < length; i++) - this._curves[i] = Curve.create(this, segments[i], - segments[i + 1] || segments[0]); - } - return this._curves; - }, - - getFirstCurve: function() { - return this.getCurves()[0]; - }, - - getLastCurve: function() { - var curves = this.getCurves(); - return curves[curves.length - 1]; - }, - - getClosed: function() { - return this._closed; - }, - - setClosed: function(closed) { - if (this._closed != (closed = !!closed)) { - this._closed = closed; - if (this._curves) { - var length = this._segments.length, - i; - if (!closed && length > 0) - length--; - this._curves.length = length; - if (closed) - this._curves[i = length - 1] = Curve.create(this, - this._segments[i], this._segments[0]); - } - this._changed(Change.GEOMETRY); - } - }, - - _transform: function(matrix, flags) { - if (!matrix.isIdentity()) { - var coords = new Array(6); - for (var i = 0, l = this._segments.length; i < l; i++) { - this._segments[i]._transformCoordinates(matrix, coords, true); - } - var fillColor = this.getFillColor(), - strokeColor = this.getStrokeColor(); - if (fillColor && fillColor.transform) - fillColor.transform(matrix); - if (strokeColor && strokeColor.transform) - strokeColor.transform(matrix); - } - }, - - _add: function(segs, index) { - var segments = this._segments, - curves = this._curves, - amount = segs.length, - append = index == null, - index = append ? segments.length : index, - fullySelected = this.isFullySelected(); - for (var i = 0; i < amount; i++) { - var segment = segs[i]; - if (segment._path) { - segment = segs[i] = new Segment(segment); - } - segment._path = this; - segment._index = index + i; - if (fullySelected) - segment._selectionState = SelectionState.POINT; - if (segment._selectionState) - this._updateSelection(segment, 0, segment._selectionState); - } - if (append) { - segments.push.apply(segments, segs); - } else { - segments.splice.apply(segments, [index, 0].concat(segs)); - for (var i = index + amount, l = segments.length; i < l; i++) { - segments[i]._index = i; - } - } - if (curves && --index >= 0) { - curves.splice(index, 0, Curve.create(this, segments[index], - segments[index + 1])); - var curve = curves[index + amount]; - if (curve) { - curve._segment1 = segments[index + amount]; - } - } - this._changed(Change.GEOMETRY); - return segs; - }, - - add: function(segment1 ) { - return arguments.length > 1 && typeof segment1 !== 'number' - ? this._add(Segment.readAll(arguments)) - : this._add([ Segment.read(arguments) ])[0]; - }, - - insert: function(index, segment1 ) { - return arguments.length > 2 && typeof segment1 !== 'number' - ? this._add(Segment.readAll(arguments, 1), index) - : this._add([ Segment.read(arguments, 1) ], index)[0]; - }, - - addSegment: function(segment) { - return this._add([ Segment.read(arguments) ])[0]; - }, - - insertSegment: function(index, segment) { - return this._add([ Segment.read(arguments, 1) ], index)[0]; - }, - - addSegments: function(segments) { - return this._add(Segment.readAll(segments)); - }, - - insertSegments: function(index, segments) { - return this._add(Segment.readAll(segments), index); - }, - - removeSegment: function(index) { - var segments = this.removeSegments(index, index + 1); - return segments[0] || null; - }, - - removeSegments: function(from, to) { - from = from || 0; - to = Base.pick(to, this._segments.length); - var segments = this._segments, - curves = this._curves, - last = to >= segments.length, - removed = segments.splice(from, to - from), - amount = removed.length; - if (!amount) - return removed; - for (var i = 0; i < amount; i++) { - var segment = removed[i]; - if (segment._selectionState) - this._updateSelection(segment, segment._selectionState, 0); - removed._index = removed._path = undefined; - } - for (var i = from, l = segments.length; i < l; i++) - segments[i]._index = i; - if (curves) { - curves.splice(from, amount); - var curve; - if (curve = curves[from - 1]) - curve._segment2 = segments[from]; - if (curve = curves[from]) - curve._segment1 = segments[from]; - if (last && this._closed && (curve = curves[curves.length - 1])) - curve._segment2 = segments[0]; - } - this._changed(Change.GEOMETRY); - return removed; - }, - - isFullySelected: function() { - return this._selected && this._selectedSegmentState - == this._segments.length * SelectionState.POINT; - }, - - setFullySelected: function(selected) { - var length = this._segments.length; - this._selectedSegmentState = selected - ? length * SelectionState.POINT : 0; - for (var i = 0; i < length; i++) - this._segments[i]._selectionState = selected - ? SelectionState.POINT : 0; - this.setSelected(selected); - }, - - _updateSelection: function(segment, oldState, newState) { - segment._selectionState = newState; - var total = this._selectedSegmentState += newState - oldState; - if (total > 0) - this.setSelected(true); - }, - - flatten: function(maxDistance) { - var flattener = new PathFlattener(this), - pos = 0, - step = flattener.length / Math.ceil(flattener.length / maxDistance), - end = flattener.length + (this._closed ? -step : step) / 2; - var segments = []; - while (pos <= end) { - segments.push(new Segment(flattener.evaluate(pos, 0))); - pos += step; - } - this.setSegments(segments); - }, - - simplify: function(tolerance) { - if (this._segments.length > 2) { - var fitter = new PathFitter(this, tolerance || 2.5); - this.setSegments(fitter.fit()); - } - }, - - isClockwise: function() { - if (this._clockwise !== undefined) - return this._clockwise; - var sum = 0, - xPre, yPre; - function edge(x, y) { - if (xPre !== undefined) - sum += (xPre - x) * (y + yPre); - xPre = x; - yPre = y; - } - for (var i = 0, l = this._segments.length; i < l; i++) { - var seg1 = this._segments[i], - seg2 = this._segments[i + 1 < l ? i + 1 : 0], - point1 = seg1._point, - handle1 = seg1._handleOut, - handle2 = seg2._handleIn, - point2 = seg2._point; - edge(point1._x, point1._y); - edge(point1._x + handle1._x, point1._y + handle1._y); - edge(point2._x + handle2._x, point2._y + handle2._y); - edge(point2._x, point2._y); - } - return sum > 0; - }, - - setClockwise: function(clockwise) { - if (this.isClockwise() != (clockwise = !!clockwise)) { - this.reverse(); - this._clockwise = clockwise; - } - }, - - reverse: function() { - this._segments.reverse(); - for (var i = 0, l = this._segments.length; i < l; i++) { - var segment = this._segments[i]; - var handleIn = segment._handleIn; - segment._handleIn = segment._handleOut; - segment._handleOut = handleIn; - } - if (this._clockwise !== undefined) - this._clockwise = !this._clockwise; - }, - - join: function(path) { - if (path) { - var segments = path._segments, - last1 = this.getLastSegment(), - last2 = path.getLastSegment(); - if (last1._point.equals(last2._point)) - path.reverse(); - var first2 = path.getFirstSegment(); - if (last1._point.equals(first2._point)) { - last1.setHandleOut(first2._handleOut); - this._add(segments.slice(1)); - } else { - var first1 = this.getFirstSegment(); - if (first1._point.equals(first2._point)) - path.reverse(); - last2 = path.getLastSegment(); - if (first1._point.equals(last2._point)) { - first1.setHandleIn(last2._handleIn); - this._add(segments.slice(0, segments.length - 1), 0); - } else { - this._add(segments.slice(0)); - } - } - path.remove(); - var first1 = this.getFirstSegment(); - last1 = this.getLastSegment(); - if (last1._point.equals(first1._point)) { - first1.setHandleIn(last1._handleIn); - last1.remove(); - this.setClosed(true); - } - this._changed(Change.GEOMETRY); - return true; - } - return false; - }, - - getLength: function() { - if (this._length == null) { - var curves = this.getCurves(); - this._length = 0; - for (var i = 0, l = curves.length; i < l; i++) - this._length += curves[i].getLength(); - } - return this._length; - }, - - _getOffset: function(location) { - var index = location && location.getIndex(); - if (index != null) { - var curves = this.getCurves(), - offset = 0; - for (var i = 0; i < index; i++) - offset += curves[i].getLength(); - var curve = curves[index]; - return offset + curve.getLength(0, location.getParameter()); - } - return null; - }, - - getLocation: function(point) { - var curves = this.getCurves(); - for (var i = 0, l = curves.length; i < l; i++) { - var curve = curves[i]; - var t = curve.getParameter(point); - if (t != null) - return new CurveLocation(curve, t); - } - return null; - }, - - getLocationAt: function(offset, isParameter) { - var curves = this.getCurves(), - length = 0; - if (isParameter) { - var index = ~~offset; - return new CurveLocation(curves[index], offset - index); - } - for (var i = 0, l = curves.length; i < l; i++) { - var start = length, - curve = curves[i]; - length += curve.getLength(); - if (length >= offset) { - return new CurveLocation(curve, - curve.getParameterAt(offset - start)); - } - } - if (offset <= this.getLength()) - return new CurveLocation(curves[curves.length - 1], 1); - return null; - }, - - getPointAt: function(offset, isParameter) { - var loc = this.getLocationAt(offset, isParameter); - return loc && loc.getPoint(); - }, - - getTangentAt: function(offset, isParameter) { - var loc = this.getLocationAt(offset, isParameter); - return loc && loc.getTangent(); - }, - - getNormalAt: function(offset, isParameter) { - var loc = this.getLocationAt(offset, isParameter); - return loc && loc.getNormal(); - }, - - getNearestLocation: function(point, matrix) { - var curves = this.getCurves(), - minDist = Infinity, - minLoc = null; - for (var i = 0, l = curves.length; i < l; i++) { - var loc = curves[i].getNearestLocation(point, matrix); - if (loc._distance < minDist) { - minDist = loc._distance; - minLoc = loc; - } - } - return minLoc; - }, - - getNearestPoint: function(point, matrix) { - return this.getNearestLocation(point, matrix).getPoint(); - }, - - contains: function(point, matrix) { - point = Point.read(arguments); - if (!this._closed || !this.getRoughBounds(matrix)._containsPoint(point)) - return false; - var curves = this.getCurves(), - crossings = 0, - roots = []; - for (var i = 0, l = curves.length; i < l; i++) - crossings += curves[i].getCrossings(point, matrix, roots); - return (crossings & 1) == 1; - }, - - _hitTest: function(point, options, matrix) { - var tolerance = options.tolerance || 0, - radius = (options.stroke ? this.getStrokeWidth() / 2 : 0) + tolerance, - loc, - res; - var coords = [], - that = this; - function checkSegment(segment, ends) { - segment._transformCoordinates(matrix, coords); - for (var j = ends || options.segments ? 0 : 2, - m = !ends && options.handles ? 6 : 2; j < m; j += 2) { - if (point.getDistance(coords[j], coords[j + 1]) < tolerance) - return new HitResult(j == 0 ? 'segment' - : 'handle-' + (j == 2 ? 'in' : 'out'), - that, { segment: segment }); - } - } - if (options.ends && !options.segments && !this._closed) { - if (res = checkSegment(this.getFirstSegment(), true) - || checkSegment(this.getLastSegment(), true)) - return res; - } else if (options.segments || options.handles) { - for (var i = 0, l = this._segments.length; i < l; i++) { - if (res = checkSegment(this._segments[i])) - return res; - } - } - if (options.stroke && radius > 0) - loc = this.getNearestLocation(point, matrix); - if (!(loc && loc._distance <= radius) && options.fill - && this.getFillColor() && this.contains(point, matrix)) - return new HitResult('fill', this); - if (!loc && options.stroke && radius > 0) - loc = this.getNearestLocation(point, matrix); - if (loc && loc._distance <= radius) - return options.stroke - ? new HitResult('stroke', this, { location: loc }) - : new HitResult('fill', this); - } - -}, new function() { - - function drawHandles(ctx, segments) { - for (var i = 0, l = segments.length; i < l; i++) { - var segment = segments[i], - point = segment._point, - state = segment._selectionState, - selected = state & SelectionState.POINT; - if (selected || (state & SelectionState.HANDLE_IN)) - drawHandle(ctx, point, segment._handleIn); - if (selected || (state & SelectionState.HANDLE_OUT)) - drawHandle(ctx, point, segment._handleOut); - ctx.save(); - ctx.beginPath(); - ctx.rect(point._x - 2, point._y - 2, 4, 4); - ctx.fill(); - if (!selected) { - ctx.beginPath(); - ctx.rect(point._x - 1, point._y - 1, 2, 2); - ctx.fillStyle = '#ffffff'; - ctx.fill(); - } - ctx.restore(); - } - } - - function drawHandle(ctx, point, handle) { - if (!handle.isZero()) { - var handleX = point._x + handle._x, - handleY = point._y + handle._y; - ctx.beginPath(); - ctx.moveTo(point._x, point._y); - ctx.lineTo(handleX, handleY); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(handleX, handleY, 1.75, 0, Math.PI * 2, true); - ctx.fill(); - } - } - - function drawSegments(ctx, path) { - var segments = path._segments, - length = segments.length, - handleOut, outX, outY; - - function drawSegment(i) { - var segment = segments[i], - point = segment._point, - x = point._x, - y = point._y, - handleIn = segment._handleIn; - if (!handleOut) { - ctx.moveTo(x, y); - } else { - if (handleIn.isZero() && handleOut.isZero()) { - ctx.lineTo(x, y); - } else { - ctx.bezierCurveTo(outX, outY, - handleIn._x + x, handleIn._y + y, x, y); - } - } - handleOut = segment._handleOut; - outX = handleOut._x + x; - outY = handleOut._y + y; - } - - for (var i = 0; i < length; i++) - drawSegment(i); - if (path._closed && length > 1) - drawSegment(0); - } - - function drawDashes(ctx, path, dashArray, dashOffset) { - var flattener = new PathFlattener(path), - from = dashOffset, to, - i = 0; - while (from < flattener.length) { - to = from + dashArray[(i++) % dashArray.length]; - flattener.drawPart(ctx, from, to); - from = to + dashArray[(i++) % dashArray.length]; - } - } - - return { - draw: function(ctx, param) { - if (!param.compound) - ctx.beginPath(); - - var fillColor = this.getFillColor(), - strokeColor = this.getStrokeColor(), - dashArray = this.getDashArray() || [], - hasDash = !!dashArray.length; - - if (param.compound || param.selection || this._clipMask || fillColor - || strokeColor && !hasDash) { - drawSegments(ctx, this); - } - - if (param.selection) { - ctx.stroke(); - drawHandles(ctx, this._segments); - } else if (this._clipMask) { - ctx.clip(); - } else if (!param.compound && (fillColor || strokeColor)) { - ctx.save(); - this._setStyles(ctx); - if (!fillColor || !strokeColor) - ctx.globalAlpha = this._opacity; - if (fillColor) { - ctx.fillStyle = fillColor.getCanvasStyle(ctx); - ctx.fill(); - } - if (strokeColor) { - ctx.strokeStyle = strokeColor.getCanvasStyle(ctx); - if (hasDash) { - ctx.beginPath(); - drawDashes(ctx, this, dashArray, this.getDashOffset()); - } - ctx.stroke(); - } - ctx.restore(); - } - } - }; -}, new function() { - - function getFirstControlPoints(rhs) { - var n = rhs.length, - x = [], - tmp = [], - b = 2; - x[0] = rhs[0] / b; - for (var i = 1; i < n; i++) { - tmp[i] = 1 / b; - b = (i < n - 1 ? 4 : 2) - tmp[i]; - x[i] = (rhs[i] - x[i - 1]) / b; - } - for (var i = 1; i < n; i++) { - x[n - i - 1] -= tmp[n - i] * x[n - i]; - } - return x; - }; - - var styles = { - getStrokeWidth: 'lineWidth', - getStrokeJoin: 'lineJoin', - getStrokeCap: 'lineCap', - getMiterLimit: 'miterLimit' - }; - - return { - _setStyles: function(ctx) { - for (var i in styles) { - var style = this._style[i](); - if (style) - ctx[styles[i]] = style; - } - }, - - smooth: function() { - var segments = this._segments, - size = segments.length, - n = size, - overlap; - - if (size <= 2) - return; - - if (this._closed) { - overlap = Math.min(size, 4); - n += Math.min(size, overlap) * 2; - } else { - overlap = 0; - } - var knots = []; - for (var i = 0; i < size; i++) - knots[i + overlap] = segments[i]._point; - if (this._closed) { - for (var i = 0; i < overlap; i++) { - knots[i] = segments[i + size - overlap]._point; - knots[i + size + overlap] = segments[i]._point; - } - } else { - n--; - } - var rhs = []; - - for (var i = 1; i < n - 1; i++) - rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x; - rhs[0] = knots[0]._x + 2 * knots[1]._x; - rhs[n - 1] = 3 * knots[n - 1]._x; - var x = getFirstControlPoints(rhs); - - for (var i = 1; i < n - 1; i++) - rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y; - rhs[0] = knots[0]._y + 2 * knots[1]._y; - rhs[n - 1] = 3 * knots[n - 1]._y; - var y = getFirstControlPoints(rhs); - - if (this._closed) { - for (var i = 0, j = size; i < overlap; i++, j++) { - var f1 = (i / overlap); - var f2 = 1 - f1; - x[j] = x[i] * f1 + x[j] * f2; - y[j] = y[i] * f1 + y[j] * f2; - var ie = i + overlap, je = j + overlap; - x[je] = x[ie] * f2 + x[je] * f1; - y[je] = y[ie] * f2 + y[je] * f1; - } - n--; - } - var handleIn = null; - for (var i = overlap; i <= n - overlap; i++) { - var segment = segments[i - overlap]; - if (handleIn) - segment.setHandleIn(handleIn.subtract(segment._point)); - if (i < n) { - segment.setHandleOut( - new Point(x[i], y[i]).subtract(segment._point)); - if (i < n - 1) - handleIn = new Point( - 2 * knots[i + 1]._x - x[i + 1], - 2 * knots[i + 1]._y - y[i + 1]); - else - handleIn = new Point( - (knots[n]._x + x[n - 1]) / 2, - (knots[n]._y + y[n - 1]) / 2); - } - } - if (this._closed && handleIn) { - var segment = this._segments[0]; - segment.setHandleIn(handleIn.subtract(segment._point)); - } - } - }; -}, new function() { - function getCurrentSegment(that) { - var segments = that._segments; - if (segments.length == 0) - throw new Error('Use a moveTo() command first'); - return segments[segments.length - 1]; - } - - return { - moveTo: function(point) { - if (!this._segments.length) - this._add([ new Segment(Point.read(arguments)) ]); - }, - - moveBy: function(point) { - throw new Error('moveBy() is unsupported on Path items.'); - }, - - lineTo: function(point) { - this._add([ new Segment(Point.read(arguments)) ]); - }, - - cubicCurveTo: function(handle1, handle2, to) { - handle1 = Point.read(arguments, 0, 1); - handle2 = Point.read(arguments, 1, 1); - to = Point.read(arguments, 2, 1); - var current = getCurrentSegment(this); - current.setHandleOut(handle1.subtract(current._point)); - this._add([ new Segment(to, handle2.subtract(to)) ]); - }, - - quadraticCurveTo: function(handle, to) { - handle = Point.read(arguments, 0, 1); - to = Point.read(arguments, 1, 1); - var current = getCurrentSegment(this)._point; - this.cubicCurveTo( - handle.add(current.subtract(handle).multiply(1/3)), - handle.add(to.subtract(handle).multiply(1/3)), - to - ); - }, - - curveTo: function(through, to, parameter) { - through = Point.read(arguments, 0, 1); - to = Point.read(arguments, 1, 1); - var t = Base.pick(parameter, 0.5), - t1 = 1 - t, - current = getCurrentSegment(this)._point, - handle = through.subtract(current.multiply(t1 * t1)) - .subtract(to.multiply(t * t)).divide(2 * t * t1); - if (handle.isNaN()) - throw new Error( - 'Cannot put a curve through points with parameter = ' + t); - this.quadraticCurveTo(handle, to); - }, - - arcTo: function(to, clockwise ) { - var current = getCurrentSegment(this), - from = current._point, - through; - if (clockwise === undefined) - clockwise = true; - if (typeof clockwise === 'boolean') { - to = Point.read(arguments, 0, 1); - var middle = from.add(to).divide(2), - through = middle.add(middle.subtract(from).rotate( - clockwise ? -90 : 90)); - } else { - through = Point.read(arguments, 0, 1); - to = Point.read(arguments, 1, 1); - } - var l1 = new Line(from.add(through).divide(2), - through.subtract(from).rotate(90)), - l2 = new Line(through.add(to).divide(2), - to.subtract(through).rotate(90)), - center = l1.intersect(l2), - line = new Line(from, to, true), - throughSide = line.getSide(through); - if (!center) { - if (!throughSide) - return this.lineTo(to); - throw new Error("Cannot put an arc through the given points: " - + [from, through, to]); - } - var vector = from.subtract(center), - radius = vector.getLength(), - extent = vector.getDirectedAngle(to.subtract(center)), - centerSide = line.getSide(center); - if (centerSide == 0) { - extent = throughSide * Math.abs(extent); - } else if (throughSide == centerSide) { - extent -= 360 * (extent < 0 ? -1 : 1); - } - var ext = Math.abs(extent), - count = ext >= 360 ? 4 : Math.ceil(ext / 90), - inc = extent / count, - half = inc * Math.PI / 360, - z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)), - segments = []; - for (var i = 0; i <= count; i++) { - var pt = i < count ? center.add(vector) : to; - var out = i < count ? vector.rotate(90).multiply(z) : null; - if (i == 0) { - current.setHandleOut(out); - } else { - segments.push( - new Segment(pt, vector.rotate(-90).multiply(z), out)); - } - vector = vector.rotate(inc); - } - this._add(segments); - }, - - lineBy: function(vector) { - vector = Point.read(arguments); - var current = getCurrentSegment(this); - this.lineTo(current._point.add(vector)); - }, - - curveBy: function(throughVector, toVector, parameter) { - throughVector = Point.read(throughVector); - toVector = Point.read(toVector); - var current = getCurrentSegment(this)._point; - this.curveTo(current.add(throughVector), current.add(toVector), - parameter); - }, - - arcBy: function(throughVector, toVector) { - throughVector = Point.read(throughVector); - toVector = Point.read(toVector); - var current = getCurrentSegment(this)._point; - this.arcBy(current.add(throughVector), current.add(toVector)); - }, - - closePath: function() { - this.setClosed(true); - } - }; -}, new function() { - - function getBounds(that, matrix, strokePadding) { - var segments = that._segments, - first = segments[0]; - if (!first) - return null; - var coords = new Array(6), - prevCoords = new Array(6); - if (matrix && matrix.isIdentity()) - matrix = null; - first._transformCoordinates(matrix, prevCoords, false); - var min = prevCoords.slice(0, 2), - max = min.slice(0), - tMin = Numerical.TOLERANCE, - tMax = 1 - tMin; - function processSegment(segment) { - segment._transformCoordinates(matrix, coords, false); - - for (var i = 0; i < 2; i++) { - var v0 = prevCoords[i], - v1 = prevCoords[i + 4], - v2 = coords[i + 2], - v3 = coords[i]; - - function add(value, t) { - var padding = 0; - if (value == null) { - var u = 1 - t; - value = u * u * u * v0 - + 3 * u * u * t * v1 - + 3 * u * t * t * v2 - + t * t * t * v3; - padding = strokePadding ? strokePadding[i] : 0; - } - var left = value - padding, - right = value + padding; - if (left < min[i]) - min[i] = left; - if (right > max[i]) - max[i] = right; - - } - add(v3, null); - - var a = 3 * (v1 - v2) - v0 + v3, - b = 2 * (v0 + v2) - 4 * v1, - c = v1 - v0; - - if (a == 0) { - if (b == 0) - continue; - var t = -c / b; - if (tMin < t && t < tMax) - add(null, t); - continue; - } - - var q = b * b - 4 * a * c; - if (q < 0) - continue; - var sqrt = Math.sqrt(q), - f = -0.5 / a, - t1 = (b - sqrt) * f, - t2 = (b + sqrt) * f; - if (tMin < t1 && t1 < tMax) - add(null, t1); - if (tMin < t2 && t2 < tMax) - add(null, t2); - } - var tmp = prevCoords; - prevCoords = coords; - coords = tmp; - } - for (var i = 1, l = segments.length; i < l; i++) - processSegment(segments[i]); - if (that._closed) - processSegment(first); - return Rectangle.create(min[0], min[1], - max[0] - min[0], max[1] - min[1]); - } - - function getPenPadding(radius, matrix) { - if (!matrix) - return [radius, radius]; - var mx = matrix.createShiftless(), - hor = mx.transform(new Point(radius, 0)), - ver = mx.transform(new Point(0, radius)), - phi = hor.getAngleInRadians(), - a = hor.getLength(), - b = ver.getLength(); - var tx = - Math.atan(b * Math.tan(phi)), - ty = + Math.atan(b / Math.tan(phi)), - x = a * Math.cos(tx) * Math.cos(phi) - - b * Math.sin(tx) * Math.sin(phi), - y = b * Math.sin(ty) * Math.cos(phi) - + a * Math.cos(ty) * Math.sin(phi); - return [Math.abs(x), Math.abs(y)]; - } - - return { - getBounds: function() { - var useCache = arguments[0] === undefined; - if (useCache && this._bounds) - return this._bounds; - var bounds = this._createBounds(getBounds(this, arguments[0])); - if (useCache) - this._bounds = bounds; - return bounds; - }, - - getStrokeBounds: function() { - if (!this._style._strokeColor || !this._style._strokeWidth) - return this.getBounds.apply(this, arguments); - var useCache = arguments[0] === undefined; - if (useCache && this._strokeBounds) - return this._strokeBounds; - var matrix = arguments[0], - width = this.getStrokeWidth(), - radius = width / 2, - padding = getPenPadding(radius, matrix), - join = this.getStrokeJoin(), - cap = this.getStrokeCap(), - miter = this.getMiterLimit() * width / 2, - segments = this._segments, - length = segments.length, - bounds = getBounds(this, matrix, getPenPadding(radius)); - var joinBounds = new Rectangle(new Size(padding).multiply(2)); - - function add(point) { - bounds = bounds.include(matrix - ? matrix.transform(point) : point); - } - - function addBevelJoin(curve, t) { - var point = curve.getPoint(t), - normal = curve.getNormal(t).normalize(radius); - add(point.add(normal)); - add(point.subtract(normal)); - } - - function addJoin(segment, join) { - if (join === 'round' || !segment._handleIn.isZero() - && !segment._handleOut.isZero()) { - bounds = bounds.unite(joinBounds.setCenter(matrix - ? matrix.transform(segment._point) : segment._point)); - } else if (join == 'bevel') { - var curve = segment.getCurve(); - addBevelJoin(curve, 0); - addBevelJoin(curve.getPrevious(), 1); - } else if (join == 'miter') { - var curve2 = segment.getCurve(), - curve1 = curve2.getPrevious(), - point = curve2.getPoint(0), - normal1 = curve1.getNormal(1).normalize(radius), - normal2 = curve2.getNormal(0).normalize(radius), - line1 = new Line(point.subtract(normal1), - new Point(-normal1.y, normal1.x)), - line2 = new Line(point.subtract(normal2), - new Point(-normal2.y, normal2.x)), - corner = line1.intersect(line2); - if (!corner || point.getDistance(corner) > miter) { - addJoin(segment, 'bevel'); - } else { - add(corner); - } - } - } - - function addCap(segment, cap, t) { - switch (cap) { - case 'round': - return addJoin(segment, cap); - case 'butt': - case 'square': - var curve = segment.getCurve(), - point = curve.getPoint(t), - normal = curve.getNormal(t).normalize(radius); - if (cap === 'square') - point = point.add(normal.y, -normal.x); - add(point.add(normal)); - add(point.subtract(normal)); - break; - } - } - - for (var i = 1, l = length - (this._closed ? 0 : 1); i < l; i++) { - addJoin(segments[i], join); - } - if (this._closed) { - addJoin(segments[0], join); - } else { - addCap(segments[0], cap, 0); - addCap(segments[length - 1], cap, 1); - } - if (useCache) - this._strokeBounds = bounds; - return bounds; - }, - - getHandleBounds: function() { - var matrix = arguments[0], - useCache = matrix === undefined; - if (useCache && this._handleBounds) - return this._handleBounds; - var coords = new Array(6), - stroke = arguments[1] / 2 || 0, - join = arguments[2] / 2 || 0, - open = !this._closed, - x1 = Infinity, - x2 = -x1, - y1 = x1, - y2 = x2; - for (var i = 0, l = this._segments.length; i < l; i++) { - var segment = this._segments[i]; - segment._transformCoordinates(matrix, coords, false); - for (var j = 0; j < 6; j += 2) { - var padding = j == 0 ? join : stroke, - x = coords[j], - y = coords[j + 1], - xn = x - padding, - xx = x + padding, - yn = y - padding, - yx = y + padding; - if (xn < x1) x1 = xn; - if (xx > x2) x2 = xx; - if (yn < y1) y1 = yn; - if (yx > y2) y2 = yx; - } - } - var bounds = Rectangle.create(x1, y1, x2 - x1, y2 - y1); - if (useCache) - this._handleBounds = bounds; - return bounds; - }, - - getRoughBounds: function() { - var useCache = arguments[0] === undefined; - if (useCache && this._roughBounds) - return this._roughBounds; - var bounds = this.getHandleBounds(arguments[0], this.strokeWidth, - this.getStrokeJoin() == 'miter' - ? this.strokeWidth * this.getMiterLimit() - : this.strokeWidth); - if (useCache) - this._roughBounds = bounds; - return bounds; - } - }; -}); - -Path.inject({ statics: new function() { - var kappa = 2 / 3 * (Math.sqrt(2) - 1); - - var ovalSegments = [ - new Segment([0, 0.5], [0, kappa ], [0, -kappa]), - new Segment([0.5, 0], [-kappa, 0], [kappa, 0 ]), - new Segment([1, 0.5], [0, -kappa], [0, kappa ]), - new Segment([0.5, 1], [kappa, 0 ], [-kappa, 0]) - ]; - - return { - Line: function() { - var step = Math.floor(arguments.length / 2); - return new Path( - Segment.read(arguments, 0, step), - Segment.read(arguments, step, step) - ); - }, - - Rectangle: function(rect) { - rect = Rectangle.read(arguments); - var left = rect.x, - top = rect.y - right = left + rect.width, - bottom = top + rect.height, - path = new Path(); - path._add([ - new Segment(Point.create(left, bottom)), - new Segment(Point.create(left, top)), - new Segment(Point.create(right, top)), - new Segment(Point.create(right, bottom)) - ]); - path._closed = true; - return path; - }, - - RoundRectangle: function(rect, size) { - if (arguments.length == 2) { - rect = Rectangle.read(arguments, 0, 1); - size = Size.read(arguments, 1, 1); - } else if (arguments.length == 6) { - rect = Rectangle.read(arguments, 0, 4); - size = Size.read(arguments, 4, 2); - } - size = Size.min(size, rect.getSize(true).divide(2)); - var path = new Path(), - uSize = size.multiply(kappa * 2), - bl = rect.getBottomLeft(true), - tl = rect.getTopLeft(true), - tr = rect.getTopRight(true), - br = rect.getBottomRight(true); - path._add([ - new Segment(bl.add(size.width, 0), null, [-uSize.width, 0]), - new Segment(bl.subtract(0, size.height), [0, uSize.height], null), - - new Segment(tl.add(0, size.height), null, [0, -uSize.height]), - new Segment(tl.add(size.width, 0), [-uSize.width, 0], null), - - new Segment(tr.subtract(size.width, 0), null, [uSize.width, 0]), - new Segment(tr.add(0, size.height), [0, -uSize.height], null), - - new Segment(br.subtract(0, size.height), null, [0, uSize.height]), - new Segment(br.subtract(size.width, 0), [uSize.width, 0], null) - ]); - path._closed = true; - return path; - }, - - Oval: function(rect) { - rect = Rectangle.read(arguments); - var path = new Path(), - point = rect.getPoint(true), - size = rect.getSize(true), - segments = new Array(4); - for (var i = 0; i < 4; i++) { - var segment = ovalSegments[i]; - segments[i] = new Segment( - segment._point.multiply(size).add(point), - segment._handleIn.multiply(size), - segment._handleOut.multiply(size) - ); - } - path._add(segments); - path._closed = true; - return path; - }, - - Circle: function(center, radius) { - if (arguments.length == 3) { - center = Point.read(arguments, 0, 2); - radius = arguments[2]; - } else { - center = Point.read(arguments, 0, 1); - } - return Path.Oval(new Rectangle(center.subtract(radius), - Size.create(radius * 2, radius * 2))); - }, - - Arc: function(from, through, to) { - var path = new Path(); - path.moveTo(from); - path.arcTo(through, to); - return path; - }, - - RegularPolygon: function(center, numSides, radius) { - center = Point.read(arguments, 0, 1); - var path = new Path(), - step = 360 / numSides, - three = !(numSides % 3), - vector = new Point(0, three ? -radius : radius), - offset = three ? -1 : 0.5, - segments = new Array(numSides); - for (var i = 0; i < numSides; i++) { - segments[i] = new Segment(center.add( - vector.rotate((i + offset) * step))); - } - path._add(segments); - path._closed = true; - return path; - }, - - Star: function(center, numPoints, radius1, radius2) { - center = Point.read(arguments, 0, 1); - numPoints *= 2; - var path = new Path(), - step = 360 / numPoints, - vector = new Point(0, -1), - segments = new Array(numPoints); - for (var i = 0; i < numPoints; i++) { - segments[i] = new Segment(center.add( - vector.rotate(step * i).multiply(i % 2 ? radius2 : radius1))); - } - path._add(segments); - path._closed = true; - return path; - } - }; -}}); - -var CompoundPath = this.CompoundPath = PathItem.extend({ - initialize: function(paths) { - this.base(); - this._children = []; - this._namedChildren = {}; - var items = !paths || !Array.isArray(paths) - || typeof paths[0] !== 'object' ? arguments : paths; - this.addChildren(items); - }, - - insertChild: function(index, item) { - this.base(index, item); - if (item._clockwise === undefined) - item.setClockwise(item._index == 0); - }, - - simplify: function() { - if (this._children.length == 1) { - var child = this._children[0]; - child.insertAbove(this); - this.remove(); - return child; - } - return this; - }, - - smooth: function() { - for (var i = 0, l = this._children.length; i < l; i++) - this._children[i].smooth(); - }, - - draw: function(ctx, param) { - var l = this._children.length; - if (l == 0) { - return; - } - var firstChild = this._children[0]; - ctx.beginPath(); - param.compound = true; - for (var i = 0; i < l; i++) - Item.draw(this._children[i], ctx, param); - firstChild._setStyles(ctx); - var fillColor = firstChild.getFillColor(), - strokeColor = firstChild.getStrokeColor(); - if (fillColor) { - ctx.fillStyle = fillColor.getCanvasStyle(ctx); - ctx.fill(); - } - if (strokeColor) { - ctx.strokeStyle = strokeColor.getCanvasStyle(ctx); - ctx.stroke(); - } - param.compound = false; - } -}, new function() { - function getCurrentPath(that) { - if (!that._children.length) - throw new Error('Use a moveTo() command first'); - return that._children[that._children.length - 1]; - } - - var fields = { - moveTo: function(point) { - var path = new Path(); - this.addChild(path); - path.moveTo.apply(path, arguments); - }, - - moveBy: function(point) { - this.moveTo(getCurrentPath(this).getLastSegment()._point.add( - Point.read(arguments))); - }, - - closePath: function() { - getCurrentPath(this).setClosed(true); - } - }; - - Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', - 'arcTo', 'lineBy', 'curveBy', 'arcBy'], function(key) { - fields[key] = function() { - var path = getCurrentPath(this); - path[key].apply(path, arguments); - }; - }); - - return fields; -}); - -var PathFlattener = Base.extend({ - initialize: function(path) { - this.curves = []; - this.parts = []; - this.length = 0; - this.index = 0; - - var segments = path._segments, - segment1 = segments[0], - segment2, - that = this; - - function addCurve(segment1, segment2) { - var curve = Curve.getValues(segment1, segment2); - that.curves.push(curve); - that._computeParts(curve, segment1._index, 0, 1); - } - - for (var i = 1, l = segments.length; i < l; i++) { - segment2 = segments[i]; - addCurve(segment1, segment2); - segment1 = segment2; - } - if (path._closed) - addCurve(segment2, segments[0]); - }, - - _computeParts: function(curve, index, minT, maxT) { - if ((maxT - minT) > 1 / 32 && !Curve.isFlatEnough(curve)) { - var curves = Curve.subdivide(curve); - var halfT = (minT + maxT) / 2; - this._computeParts(curves[0], index, minT, halfT); - this._computeParts(curves[1], index, halfT, maxT); - } else { - var x = curve[6] - curve[0], - y = curve[7] - curve[1], - dist = Math.sqrt(x * x + y * y); - if (dist > Numerical.TOLERANCE) { - this.length += dist; - this.parts.push({ - offset: this.length, - value: maxT, - index: index - }); - } - } - }, - - getParameterAt: function(offset) { - var i, j = this.index; - for (;;) { - i = j; - if (j == 0 || this.parts[--j].offset < offset) - break; - } - for (var l = this.parts.length; i < l; i++) { - var part = this.parts[i]; - if (part.offset >= offset) { - this.index = i; - var prev = this.parts[i - 1]; - var prevVal = prev && prev.index == part.index ? prev.value : 0, - prevLen = prev ? prev.offset : 0; - return { - value: prevVal + (part.value - prevVal) - * (offset - prevLen) / (part.offset - prevLen), - index: part.index - }; - } - } - var part = this.parts[this.parts.length - 1]; - return { - value: 1, - index: part.index - }; - }, - - evaluate: function(offset, type) { - var param = this.getParameterAt(offset); - return Curve.evaluate(this.curves[param.index], param.value, type); - }, - - drawPart: function(ctx, from, to) { - from = this.getParameterAt(from); - to = this.getParameterAt(to); - for (var i = from.index; i <= to.index; i++) { - var curve = Curve.getPart(this.curves[i], - i == from.index ? from.value : 0, - i == to.index ? to.value : 1); - if (i == from.index) - ctx.moveTo(curve[0], curve[1]); - ctx.bezierCurveTo.apply(ctx, curve.slice(2)); - } - } -}); - -var PathFitter = Base.extend({ - initialize: function(path, error) { - this.points = []; - var segments = path._segments, - prev; - for (var i = 0, l = segments.length; i < l; i++) { - var point = segments[i].point.clone(); - if (!prev || !prev.equals(point)) { - this.points.push(point); - prev = point; - } - } - this.error = error; - }, - - fit: function() { - this.segments = [new Segment(this.points[0])]; - this.fitCubic(0, this.points.length - 1, - this.points[1].subtract(this.points[0]).normalize(), - this.points[this.points.length - 2].subtract( - this.points[this.points.length - 1]).normalize()); - return this.segments; - }, - - fitCubic: function(first, last, tan1, tan2) { - if (last - first == 1) { - var pt1 = this.points[first], - pt2 = this.points[last], - dist = pt1.getDistance(pt2) / 3; - this.addCurve([pt1, pt1.add(tan1.normalize(dist)), - pt2.add(tan2.normalize(dist)), pt2]); - return; - } - var uPrime = this.chordLengthParameterize(first, last), - maxError = Math.max(this.error, this.error * this.error), - error, - split; - for (var i = 0; i <= 4; i++) { - var curve = this.generateBezier(first, last, uPrime, tan1, tan2); - var max = this.findMaxError(first, last, curve, uPrime); - if (max.error < this.error) { - this.addCurve(curve); - return; - } - split = max.index; - if (max.error >= maxError) - break; - this.reparameterize(first, last, uPrime, curve); - maxError = max.error; - } - var V1 = this.points[split - 1].subtract(this.points[split]), - V2 = this.points[split].subtract(this.points[split + 1]), - tanCenter = V1.add(V2).divide(2).normalize(); - this.fitCubic(first, split, tan1, tanCenter); - this.fitCubic(split, last, tanCenter.negate(), tan2); - }, - - addCurve: function(curve) { - var prev = this.segments[this.segments.length - 1]; - prev.setHandleOut(curve[1].subtract(curve[0])); - this.segments.push( - new Segment(curve[3], curve[2].subtract(curve[3]))); - }, - - generateBezier: function(first, last, uPrime, tan1, tan2) { - var epsilon = Numerical.EPSILON, - pt1 = this.points[first], - pt2 = this.points[last], - C = [[0, 0], [0, 0]], - X = [0, 0]; - - for (var i = 0, l = last - first + 1; i < l; i++) { - var u = uPrime[i], - t = 1 - u, - b = 3 * u * t, - b0 = t * t * t, - b1 = b * t, - b2 = b * u, - b3 = u * u * u, - a1 = tan1.normalize(b1), - a2 = tan2.normalize(b2), - tmp = this.points[first + i] - .subtract(pt1.multiply(b0 + b1)) - .subtract(pt2.multiply(b2 + b3)); - C[0][0] += a1.dot(a1); - C[0][1] += a1.dot(a2); - C[1][0] = C[0][1]; - C[1][1] += a2.dot(a2); - X[0] += a1.dot(tmp); - X[1] += a2.dot(tmp); - } - - var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1], - alpha1, alpha2; - if (Math.abs(detC0C1) > epsilon) { - var detC0X = C[0][0] * X[1] - C[1][0] * X[0], - detXC1 = X[0] * C[1][1] - X[1] * C[0][1]; - alpha1 = detXC1 / detC0C1; - alpha2 = detC0X / detC0C1; - } else { - var c0 = C[0][0] + C[0][1], - c1 = C[1][0] + C[1][1]; - if (Math.abs(c0) > epsilon) { - alpha1 = alpha2 = X[0] / c0; - } else if (Math.abs(c0) > epsilon) { - alpha1 = alpha2 = X[1] / c1; - } else { - alpha1 = alpha2 = 0.; - } - } - - var segLength = pt2.getDistance(pt1); - epsilon *= segLength; - if (alpha1 < epsilon || alpha2 < epsilon) { - alpha1 = alpha2 = segLength / 3; - } - - return [pt1, pt1.add(tan1.normalize(alpha1)), - pt2.add(tan2.normalize(alpha2)), pt2]; - }, - - reparameterize: function(first, last, u, curve) { - for (var i = first; i <= last; i++) { - u[i - first] = this.findRoot(curve, this.points[i], u[i - first]); - } - }, - - findRoot: function(curve, point, u) { - var curve1 = [], - curve2 = []; - for (var i = 0; i <= 2; i++) { - curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3); - } - for (var i = 0; i <= 1; i++) { - curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2); - } - var pt = this.evaluate(3, curve, u), - pt1 = this.evaluate(2, curve1, u), - pt2 = this.evaluate(1, curve2, u), - diff = pt.subtract(point), - df = pt1.dot(pt1) + diff.dot(pt2); - if (Math.abs(df) < Numerical.TOLERANCE) - return u; - return u - diff.dot(pt1) / df; - }, - - evaluate: function(degree, curve, t) { - var tmp = curve.slice(); - for (var i = 1; i <= degree; i++) { - for (var j = 0; j <= degree - i; j++) { - tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t)); - } - } - return tmp[0]; - }, - - chordLengthParameterize: function(first, last) { - var u = [0]; - for (var i = first + 1; i <= last; i++) { - u[i - first] = u[i - first - 1] - + this.points[i].getDistance(this.points[i - 1]); - } - for (var i = 1, m = last - first; i <= m; i++) { - u[i] /= u[m]; - } - return u; - }, - - findMaxError: function(first, last, curve, u) { - var index = Math.floor((last - first + 1) / 2), - maxDist = 0; - for (var i = first + 1; i < last; i++) { - var P = this.evaluate(3, curve, u[i - first]); - var v = P.subtract(this.points[i]); - var dist = v.x * v.x + v.y * v.y; - if (dist >= maxDist) { - maxDist = dist; - index = i; - } - } - return { - error: maxDist, - index: index - }; - } -}); - -var TextItem = this.TextItem = Item.extend({ - initialize: function() { - this.base(); - this._content = ''; - this._characterStyle = CharacterStyle.create(this); - this.setCharacterStyle(this._project.getCurrentStyle()); - this._paragraphStyle = ParagraphStyle.create(this); - this.setParagraphStyle(); - }, - - _clone: function(copy) { - copy._content = this._content; - copy.setCharacterStyle(this._characterStyle); - copy.setParagraphStyle(this._paragraphStyle); - return this.base(copy); - }, - - getContent: function() { - return this._content; - }, - - setContent: function(content) { - this._changed(Change.CONTENT); - this._content = '' + content; - }, - - getCharacterStyle: function() { - return this._characterStyle; - }, - - setCharacterStyle: function(style) { - this._characterStyle.initialize(style); - }, - - getParagraphStyle: function() { - return this._paragraphStyle; - }, - - setParagraphStyle: function(style) { - this._paragraphStyle.initialize(style); - } -}); - -var PointText = this.PointText = TextItem.extend({ - initialize: function(point) { - this.base(); - this._point = Point.read(arguments).clone(); - this._matrix = new Matrix().translate(this._point); - }, - - clone: function() { - var copy = this._clone(new PointText(this._point)); - copy._matrix.initialize(this._matrix); - return copy; - }, - - getPoint: function() { - return LinkedPoint.create(this, 'setPoint', - this._point.x, this._point.y); - }, - - setPoint: function(point) { - this.translate(Point.read(arguments).subtract(this._point)); - }, - - getPosition: function() { - return this.getPoint(); - }, - - setPosition: function(point) { - this.setPoint.apply(this, arguments); - }, - - _transform: function(matrix, flags) { - this._matrix.preConcatenate(matrix); - matrix._transformPoint(this._point, this._point); - }, - - draw: function(ctx) { - if (!this._content) - return; - ctx.save(); - ctx.font = this.getFontSize() + 'pt ' + this.getFont(); - ctx.textAlign = this.getJustification(); - this._matrix.applyToContext(ctx); - - var fillColor = this.getFillColor(); - var strokeColor = this.getStrokeColor(); - if (!fillColor || !strokeColor) - ctx.globalAlpha = this._opacity; - if (fillColor) { - ctx.fillStyle = fillColor.getCanvasStyle(ctx); - ctx.fillText(this._content, 0, 0); - } - if (strokeColor) { - ctx.strokeStyle = strokeColor.getCanvasStyle(ctx); - ctx.strokeText(this._content, 0, 0); - } - ctx.restore(); - } -}); - -var Style = Item.extend({ - initialize: function(style) { - var clone = style instanceof Style; - return Base.each(this._defaults, function(value, key) { - value = style && style[key] || value; - this[key] = value && clone && value.clone - ? value.clone() : value; - }, this); - }, - - statics: { - create: function(item) { - var style = new this(this.dont); - style._item = item; - return style; - }, - - extend: function(src) { - var styleKey = src._style, - flags = src._flags || {}; - src._owner.inject(Base.each(src._defaults, function(value, key) { - var isColor = !!key.match(/Color$/), - part = Base.capitalize(key), - set = 'set' + part, - get = 'get' + part; - src[set] = function(value) { - var children = this._item && this._item._children; - value = isColor ? Color.read(arguments) : value; - if (children) { - for (var i = 0, l = children.length; i < l; i++) - children[i][styleKey][set](value); - } else { - var old = this['_' + key]; - if (old != value && !(old && old.equals - && old.equals(value))) { - this['_' + key] = value; - if (isColor) { - if (old) - old._removeOwner(this._item); - if (value) - value._addOwner(this._item); - } - if (this._item) - this._item._changed(flags[key] || Change.STYLE); - } - } - return this; - }; - src[get] = function() { - var children = this._item && this._item._children, - style; - if (!children) - return this['_' + key]; - for (var i = 0, l = children.length; i < l; i++) { - var childStyle = children[i][styleKey][get](); - if (!style) { - style = childStyle; - } else if (style != childStyle && !(style - && style.equals && style.equals(childStyle))) { - return undefined; - } - } - return style; - }; - this[set] = function(value) { - this[styleKey][set](value); - return this; - }; - this[get] = function() { - return this[styleKey][get](); - }; - }, {})); - return this.base(src); - } - } -}); - -var PathStyle = this.PathStyle = Style.extend({ - _defaults: { - fillColor: undefined, - strokeColor: undefined, - strokeWidth: 1, - strokeCap: 'butt', - strokeJoin: 'miter', - miterLimit: 10, - dashOffset: 0, - dashArray: [] - }, - _flags: { - strokeWidth: Change.STROKE, - strokeCap: Change.STROKE, - strokeJoin: Change.STROKE, - miterLimit: Change.STROKE - }, - _owner: Item, - _style: '_style' - -}); - -var ParagraphStyle = this.ParagraphStyle = Style.extend({ - _defaults: { - justification: 'left' - }, - _owner: TextItem, - _style: '_paragraphStyle' - -}); - -var CharacterStyle = this.CharacterStyle = PathStyle.extend({ - _defaults: Base.merge(PathStyle.prototype._defaults, { - fillColor: 'black', - fontSize: 10, - font: 'sans-serif' - }), - _owner: TextItem, - _style: '_characterStyle' - -}); - -var Color = this.Color = Base.extend(new function() { - - var components = { - gray: ['gray'], - rgb: ['red', 'green', 'blue'], - hsb: ['hue', 'saturation', 'brightness'], - hsl: ['hue', 'saturation', 'lightness'] - }; - - var colorCache = {}, - colorContext; - - function nameToRgbColor(name) { - var color = colorCache[name]; - if (color) - return color.clone(); - if (!colorContext) { - var canvas = CanvasProvider.getCanvas(Size.create(1, 1)); - colorContext = canvas.getContext('2d'); - colorContext.globalCompositeOperation = 'copy'; - } - colorContext.fillStyle = 'rgba(0,0,0,0)'; - colorContext.fillStyle = name; - colorContext.fillRect(0, 0, 1, 1); - var data = colorContext.getImageData(0, 0, 1, 1).data, - rgb = [data[0] / 255, data[1] / 255, data[2] / 255]; - return (colorCache[name] = RgbColor.read(rgb)).clone(); - } - - function hexToRgbColor(string) { - var hex = string.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); - if (hex.length >= 4) { - var rgb = new Array(3); - for (var i = 0; i < 3; i++) { - var channel = hex[i + 1]; - rgb[i] = parseInt(channel.length == 1 - ? channel + channel : channel, 16) / 255; - } - return RgbColor.read(rgb); - } - } - - var hsbIndices = [ - [0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2] - ]; - - var converters = { - 'rgb-hsb': function(color) { - var r = color._red, - g = color._green, - b = color._blue, - max = Math.max(r, g, b), - min = Math.min(r, g, b), - delta = max - min, - h = delta == 0 ? 0 - : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) - : max == g ? (b - r) / delta + 2 - : (r - g) / delta + 4) * 60, - s = max == 0 ? 0 : delta / max, - v = max; - return new HsbColor(h, s, v, color._alpha); - }, - - 'hsb-rgb': function(color) { - var h = (color._hue / 60) % 6, - s = color._saturation, - b = color._brightness, - i = Math.floor(h), - f = h - i, - i = hsbIndices[i], - v = [ - b, - b * (1 - s), - b * (1 - s * f), - b * (1 - s * (1 - f)) - ]; - return new RgbColor(v[i[0]], v[i[1]], v[i[2]], color._alpha); - }, - - 'rgb-hsl': function(color) { - var r = color._red, - g = color._green, - b = color._blue, - max = Math.max(r, g, b), - min = Math.min(r, g, b), - delta = max - min, - achromatic = delta == 0, - h = achromatic ? 0 - : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) - : max == g ? (b - r) / delta + 2 - : (r - g) / delta + 4) * 60, - l = (max + min) / 2, - s = achromatic ? 0 : l < 0.5 - ? delta / (max + min) - : delta / (2 - max - min); - return new HslColor(h, s, l, color._alpha); - }, - - 'hsl-rgb': function(color) { - var s = color._saturation, - h = color._hue / 360, - l = color._lightness, - t1, t2, c; - if (s == 0) - return new RgbColor(l, l, l, color._alpha); - var t3s = [ h + 1 / 3, h, h - 1 / 3 ], - t2 = l < 0.5 ? l * (1 + s) : l + s - l * s, - t1 = 2 * l - t2, - c = []; - for (var i = 0; i < 3; i++) { - var t3 = t3s[i]; - if (t3 < 0) t3 += 1; - if (t3 > 1) t3 -= 1; - c[i] = 6 * t3 < 1 - ? t1 + (t2 - t1) * 6 * t3 - : 2 * t3 < 1 - ? t2 - : 3 * t3 < 2 - ? t1 + (t2 - t1) * ((2 / 3) - t3) * 6 - : t1; - } - return new RgbColor(c[0], c[1], c[2], color._alpha); - }, - - 'rgb-gray': function(color) { - return new GrayColor(1 - (color._red * 0.2989 + color._green * 0.587 - + color._blue * 0.114), color._alpha); - }, - - 'gray-rgb': function(color) { - var comp = 1 - color._gray; - return new RgbColor(comp, comp, comp, color._alpha); - }, - - 'gray-hsb': function(color) { - return new HsbColor(0, 0, 1 - color._gray, color._alpha); - }, - - 'gray-hsl': function(color) { - return new HslColor(0, 0, 1 - color._gray, color._alpha); - } - }; - - var fields = { - _readNull: true, - - initialize: function(arg) { - var isArray = Array.isArray(arg), - type = this._colorType; - if (typeof arg === 'object' && !isArray) { - if (!type) { - return arg.red !== undefined - ? new RgbColor(arg.red, arg.green, arg.blue, arg.alpha) - : arg.gray !== undefined - ? new GrayColor(arg.gray, arg.alpha) - : arg.lightness !== undefined - ? new HslColor(arg.hue, arg.saturation, arg.lightness, - arg.alpha) - : arg.hue !== undefined - ? new HsbColor(arg.hue, arg.saturation, arg.brightness, - arg.alpha) - : new RgbColor(); - } else { - return Color.read(arguments).convert(type); - } - } else if (typeof arg === 'string') { - var rgbColor = arg.match(/^#[0-9a-f]{3,6}$/i) - ? hexToRgbColor(arg) - : nameToRgbColor(arg); - return type - ? rgbColor.convert(type) - : rgbColor; - } else { - var components = isArray ? arg - : Array.prototype.slice.call(arguments); - if (!type) { - if (components.length >= 3) - return new RgbColor(components); - return new GrayColor(components); - } else { - Base.each(this._components, - function(name, i) { - var value = components[i]; - this['_' + name] = value !== undefined - ? value : null; - }, - this); - } - } - }, - - clone: function() { - var ctor = this.constructor, - copy = new ctor(ctor.dont), - components = this._components; - for (var i = 0, l = components.length; i < l; i++) { - var key = '_' + components[i]; - copy[key] = this[key]; - } - return copy; - }, - - convert: function(type) { - var converter; - return this._colorType == type - ? this.clone() - : (converter = converters[this._colorType + '-' + type]) - ? converter(this) - : converters['rgb-' + type]( - converters[this._colorType + '-rgb'](this)); - }, - - statics: { - extend: function(src) { - src.beans = true; - if (src._colorType) { - var comps = components[src._colorType]; - src._components = comps.concat(['alpha']); - Base.each(comps, function(name) { - var isHue = name === 'hue', - part = Base.capitalize(name), - name = '_' + name; - this['get' + part] = function() { - return this[name]; - }; - this['set' + part] = function(value) { - this[name] = isHue - ? ((value % 360) + 360) % 360 - : Math.min(Math.max(value, 0), 1); - this._changed(); - return this; - }; - }, src); - } - return this.base(src); - } - } - }; - - Base.each(components, function(comps, type) { - Base.each(comps, function(component) { - var part = Base.capitalize(component); - fields['get' + part] = function() { - return this.convert(type)[component]; - }; - fields['set' + part] = function(value) { - var color = this.convert(type); - color[component] = value; - color = color.convert(this._colorType); - for (var i = 0, l = this._components.length; i < l; i++) { - var key = this._components[i]; - this[key] = color[key]; - } - }; - }); - }); - - return fields; -}, { - - _changed: function() { - this._cssString = null; - for (var i = 0, l = this._owners && this._owners.length; i < l; i++) - this._owners[i]._changed(Change.STYLE); - }, - - _addOwner: function(item) { - if (!this._owners) - this._owners = []; - this._owners.push(item); - }, - - _removeOwner: function(item) { - var index = this._owners ? this._owners.indexOf(item) : -1; - if (index != -1) { - this._owners.splice(index, 1); - if (this._owners.length == 0) - delete this._owners; - } - }, - - getType: function() { - return this._colorType; - }, - - getComponents: function() { - var length = this._components.length; - var comps = new Array(length); - for (var i = 0; i < length; i++) - comps[i] = this['_' + this._components[i]]; - return comps; - }, - - getAlpha: function() { - return this._alpha != null ? this._alpha : 1; - }, - - setAlpha: function(alpha) { - this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1); - this._changed(); - return this; - }, - - hasAlpha: function() { - return this._alpha != null; - }, - - equals: function(color) { - if (color && color._colorType === this._colorType) { - for (var i = 0, l = this._components.length; i < l; i++) { - var component = '_' + this._components[i]; - if (this[component] !== color[component]) - return false; - } - return true; - } - return false; - }, - - toString: function() { - var parts = [], - format = Base.formatNumber; - for (var i = 0, l = this._components.length; i < l; i++) { - var component = this._components[i], - value = this['_' + component]; - if (component === 'alpha' && value == null) - value = 1; - parts.push(component + ': ' + format(value)); - } - return '{ ' + parts.join(', ') + ' }'; - }, - - toCssString: function() { - if (!this._cssString) { - var color = this.convert('rgb'), - alpha = color.getAlpha(), - components = [ - Math.round(color._red * 255), - Math.round(color._green * 255), - Math.round(color._blue * 255), - alpha != null ? alpha : 1 - ]; - this._cssString = 'rgba(' + components.join(', ') + ')'; - } - return this._cssString; - }, - - getCanvasStyle: function() { - return this.toCssString(); - } - -}); - -var GrayColor = this.GrayColor = Color.extend({ - - _colorType: 'gray' -}); - -var RgbColor = this.RgbColor = this.RGBColor = Color.extend({ - - _colorType: 'rgb' -}); - -var HsbColor = this.HsbColor = this.HSBColor = Color.extend({ - - _colorType: 'hsb' -}); - -var HslColor = this.HslColor = this.HSLColor = Color.extend({ - - _colorType: 'hsl' -}); - -var GradientColor = this.GradientColor = Color.extend({ - - initialize: function(gradient, origin, destination, hilite) { - this.gradient = gradient || new Gradient(); - this.setOrigin(origin); - this.setDestination(destination); - if (hilite) - this.setHilite(hilite); - }, - - clone: function() { - return new GradientColor(this.gradient, this._origin, this._destination, - this._hilite); - }, - - getOrigin: function() { - return this._origin; - }, - - setOrigin: function(origin) { - origin = Point.read(arguments).clone(); - this._origin = origin; - if (this._destination) - this._radius = this._destination.getDistance(this._origin); - this._changed(); - return this; - }, - - getDestination: function() { - return this._destination; - }, - - setDestination: function(destination) { - destination = Point.read(arguments).clone(); - this._destination = destination; - this._radius = this._destination.getDistance(this._origin); - this._changed(); - return this; - }, - - getHilite: function() { - return this._hilite; - }, - - setHilite: function(hilite) { - hilite = Point.read(arguments).clone(); - var vector = hilite.subtract(this._origin); - if (vector.getLength() > this._radius) { - this._hilite = this._origin.add( - vector.normalize(this._radius - 0.1)); - } else { - this._hilite = hilite; - } - this._changed(); - return this; - }, - - getCanvasStyle: function(ctx) { - var gradient; - if (this.gradient.type === 'linear') { - gradient = ctx.createLinearGradient(this._origin.x, this._origin.y, - this._destination.x, this._destination.y); - } else { - var origin = this._hilite || this._origin; - gradient = ctx.createRadialGradient(origin.x, origin.y, - 0, this._origin.x, this._origin.y, this._radius); - } - for (var i = 0, l = this.gradient._stops.length; i < l; i++) { - var stop = this.gradient._stops[i]; - gradient.addColorStop(stop._rampPoint, stop._color.toCssString()); - } - return gradient; - }, - - equals: function(color) { - return color == this || color && color._colorType === this._colorType - && this.gradient.equals(color.gradient) - && this._origin.equals(color._origin) - && this._destination.equals(color._destination); - }, - - transform: function(matrix) { - matrix._transformPoint(this._origin, this._origin, true); - matrix._transformPoint(this._destination, this._destination, true); - if (this._hilite) - matrix._transformPoint(this._hilite, this._hilite, true); - this._radius = this._destination.getDistance(this._origin); - } -}); - -var Gradient = this.Gradient = Base.extend({ - initialize: function(stops, type) { - this.setStops(stops || ['white', 'black']); - this.type = type || 'linear'; - }, - - clone: function() { - var stops = []; - for (var i = 0, l = this._stops.length; i < l; i++) - stops[i] = this._stops[i].clone(); - return new Gradient(stops, this.type); - }, - - getStops: function() { - return this._stops; - }, - - setStops: function(stops) { - if (stops.length < 2) - throw new Error( - 'Gradient stop list needs to contain at least two stops.'); - this._stops = GradientStop.readAll(stops); - for (var i = 0, l = this._stops.length; i < l; i++) { - var stop = this._stops[i]; - if (stop._defaultRamp) - stop.setRampPoint(i / (l - 1)); - } - }, - - equals: function(gradient) { - if (gradient.type != this.type) - return false; - if (this._stops.length == gradient._stops.length) { - for (var i = 0, l = this._stops.length; i < l; i++) { - if (!this._stops[i].equals(gradient._stops[i])) - return false; - } - return true; - } - return false; - } -}); - -var GradientStop = this.GradientStop = Base.extend({ - initialize: function(arg0, arg1) { - if (arg1 === undefined && Array.isArray(arg0)) { - this.setColor(arg0[0]); - this.setRampPoint(arg0[1]); - } else if (arg0.color) { - this.setColor(arg0.color); - this.setRampPoint(arg0.rampPoint); - } else { - this.setColor(arg0); - this.setRampPoint(arg1); - } - }, - - clone: function() { - return new GradientStop(this._color.clone(), this._rampPoint); - }, - - getRampPoint: function() { - return this._rampPoint; - }, - - setRampPoint: function(rampPoint) { - this._defaultRamp = rampPoint == null; - this._rampPoint = rampPoint || 0; - }, - - getColor: function() { - return this._color; - }, - - setColor: function(color) { - this._color = Color.read(arguments); - }, - - equals: function(stop) { - return stop == this || stop instanceof GradientStop - && this._color.equals(stop._color) - && this._rampPoint == stop._rampPoint; - } -}); - -var DomElement = { - getBounds: function(el, viewport) { - var rect = el.getBoundingClientRect(), - doc = el.ownerDocument, - body = doc.body, - docEl = doc.documentElement, - x = rect.left - (docEl.clientLeft || body.clientLeft || 0), - y = rect.top - (docEl.clientTop || body.clientTop || 0); - if (!viewport) { - var win = DomElement.getViewport(doc); - x += win.pageXOffset || docEl.scrollLeft || body.scrollLeft; - y += win.pageYOffset || docEl.scrollTop || body.scrollTop; - } - return new Rectangle(x, y, rect.width, rect.height); - }, - - getOffset: function(el, viewport) { - return this.getBounds(el, viewport).getPoint(); - }, - - getSize: function(el) { - return this.getBounds(el, true).getSize(); - }, - - isInvisible: function(el) { - return this.getSize(el).equals([0, 0]); - }, - - isVisible: function(el) { - return !this.isInvisible(el) && this.getViewportBounds(el).intersects( - this.getBounds(el, true)); - }, - - getViewport: function(doc) { - return doc.defaultView || doc.parentWindow; - }, - - getViewportBounds: function(el) { - var doc = el.ownerDocument, - view = this.getViewport(doc), - body = doc.getElementsByTagName( - doc.compatMode === 'CSS1Compat' ? 'html' : 'body')[0]; - return Rectangle.create(0, 0, - view.innerWidth || body.clientWidth, - view.innerHeight || body.clientHeight - ); - }, - - getComputedStyle: function(el, name) { - if (el.currentStyle) - return el.currentStyle[Base.camelize(name)]; - var style = this.getViewport(el.ownerDocument) - .getComputedStyle(el, null); - return style ? style.getPropertyValue(Base.hyphenate(name)) : null; - } -}; - -var DomEvent = { - add: function(el, events) { - for (var type in events) { - var func = events[type]; - if (el.addEventListener) { - el.addEventListener(type, func, false); - } else if (el.attachEvent) { - el.attachEvent('on' + type, func.bound = function() { - func.call(el, window.event); - }); - } - } - }, - - remove: function(el, events) { - for (var type in events) { - var func = events[type]; - if (el.removeEventListener) { - el.removeEventListener(type, func, false); - } else if (el.detachEvent) { - el.detachEvent('on' + type, func.bound); - } - } - }, - - getPoint: function(event) { - var pos = event.targetTouches - ? event.targetTouches.length - ? event.targetTouches[0] - : event.changedTouches[0] - : event; - return Point.create( - pos.pageX || pos.clientX + document.documentElement.scrollLeft, - pos.pageY || pos.clientY + document.documentElement.scrollTop - ); - }, - - getTarget: function(event) { - return event.target || event.srcElement; - }, - - getOffset: function(event, target) { - return DomEvent.getPoint(event).subtract(DomElement.getOffset( - target || DomEvent.getTarget(event))); - }, - - preventDefault: function(event) { - if (event.preventDefault) { - event.preventDefault(); - } else { - event.returnValue = false; - } - }, - - stopPropagation: function(event) { - if (event.stopPropagation) { - event.stopPropagation(); - } else { - event.cancelBubble = true; - } - }, - - stop: function(event) { - DomEvent.stopPropagation(event); - DomEvent.preventDefault(event); - } -}; - -DomEvent.requestAnimationFrame = new function() { - var part = 'equestAnimationFrame', - request = window['r' + part] || window['webkitR' + part] - || window['mozR' + part] || window['oR' + part] - || window['msR' + part]; - if (request) { - request(function(time) { - if (time == undefined) - request = null; - }); - } - - var callbacks = [], - focused = true, - timer; - - DomEvent.add(window, { - focus: function() { - focused = true; - }, - blur: function() { - focused = false; - } - }); - - return function(callback, element) { - if (request) - return request(callback, element); - callbacks.push([callback, element]); - if (timer) - return; - timer = window.setInterval(function() { - for (var i = callbacks.length - 1; i >= 0; i--) { - var entry = callbacks[i], - func = entry[0], - el = entry[1]; - if (!el || (PaperScript.getAttribute(el, 'keepalive') == 'true' - || focused) && DomElement.isVisible(el)) { - callbacks.splice(i, 1); - func(Date.now()); - } - } - }, 1000 / 60); - }; -}; - -var View = this.View = PaperScopeItem.extend({ - _list: 'views', - _reference: 'view', - - initialize: function(canvas) { - this.base(); - var size; - - if (typeof canvas === 'string') - canvas = document.getElementById(canvas); - if (canvas instanceof HTMLCanvasElement) { - this._canvas = canvas; - if (PaperScript.hasAttribute(canvas, 'resize')) { - var offset = DomElement.getOffset(canvas, true), - that = this; - size = DomElement.getViewportBounds(canvas) - .getSize().subtract(offset); - canvas.width = size.width; - canvas.height = size.height; - DomEvent.add(window, { - resize: function(event) { - if (!DomElement.isInvisible(canvas)) - offset = DomElement.getOffset(canvas, true); - that.setViewSize(DomElement.getViewportBounds(canvas) - .getSize().subtract(offset)); - } - }); - } else { - size = DomElement.isInvisible(canvas) - ? Size.create(parseInt(canvas.getAttribute('width')), - parseInt(canvas.getAttribute('height'))) - : DomElement.getSize(canvas); - } - if (PaperScript.hasAttribute(canvas, 'stats')) { - this._stats = new Stats(); - var element = this._stats.domElement, - style = element.style, - offset = DomElement.getOffset(canvas); - style.position = 'absolute'; - style.left = offset.x + 'px'; - style.top = offset.y + 'px'; - document.body.appendChild(element); - } - } else { - size = Size.read(arguments, 1); - if (size.isZero()) - size = new Size(1024, 768); - this._canvas = CanvasProvider.getCanvas(size); - } - this._id = this._canvas.getAttribute('id'); - if (this._id == null) - this._canvas.setAttribute('id', this._id = 'canvas-' + View._id++); - - View._views[this._id] = this; - this._viewSize = LinkedSize.create(this, 'setViewSize', - size.width, size.height); - this._context = this._canvas.getContext('2d'); - this._matrix = new Matrix(); - this._zoom = 1; - - this._events = this._createEvents(); - DomEvent.add(this._canvas, this._events); - if (!View._focused) - View._focused = this; - - this._scope._redrawNotified = false; - }, - - remove: function() { - if (!this.base()) - return false; - if (View._focused == this) - View._focused = null; - delete View._views[this._id]; - DomEvent.remove(this._canvas, this._events); - this._canvas = this._events = this._onFrame = null; - return true; - }, - - _redraw: function() { - this._redrawNeeded = true; - if (this._onFrameCallback) { - this._onFrameCallback(0, true); - } else { - this.draw(); - } - }, - - _transform: function(matrix, flags) { - this._matrix.preConcatenate(matrix); - this._bounds = null; - this._inverse = null; - this._redraw(); - }, - - getCanvas: function() { - return this._canvas; - }, - - getViewSize: function() { - return this._viewSize; - }, - - setViewSize: function(size) { - size = Size.read(arguments); - var delta = size.subtract(this._viewSize); - if (delta.isZero()) - return; - this._canvas.width = size.width; - this._canvas.height = size.height; - this._viewSize.set(size.width, size.height, true); - this._bounds = null; - this._redrawNeeded = true; - if (this.onResize) { - this.onResize({ - size: size, - delta: delta - }); - } - this._redraw(); - }, - - getBounds: function() { - if (!this._bounds) - this._bounds = this._getInverse()._transformBounds( - new Rectangle(new Point(), this._viewSize)); - return this._bounds; - }, - - getSize: function() { - return this.getBounds().getSize(); - }, - - getCenter: function() { - return this.getBounds().getCenter(); - }, - - setCenter: function(center) { - this.scrollBy(Point.read(arguments).subtract(this.getCenter())); - }, - - getZoom: function() { - return this._zoom; - }, - - setZoom: function(zoom) { - this._transform(new Matrix().scale(zoom / this._zoom, this.getCenter())); - this._zoom = zoom; - }, - - isVisible: function() { - return DomElement.isVisible(this._canvas); - }, - - scrollBy: function(point) { - this._transform(new Matrix().translate(Point.read(arguments).negate())); - }, - - draw: function(checkRedraw) { - if (checkRedraw && !this._redrawNeeded) - return false; - if (this._stats) - this._stats.update(); - var ctx = this._context, - size = this._viewSize; - ctx.clearRect(0, 0, size._width + 1, size._height + 1); - - ctx.save(); - this._matrix.applyToContext(ctx); - this._scope.project.draw(ctx); - ctx.restore(); - if (this._redrawNeeded) { - this._redrawNeeded = false; - this._scope._redrawNotified = false; - } - return true; - }, - - projectToView: function(point) { - return this._matrix._transformPoint(Point.read(arguments)); - }, - - viewToProject: function(point) { - return this._getInverse()._transformPoint(Point.read(arguments)); - }, - - _getInverse: function() { - if (!this._inverse) - this._inverse = this._matrix.createInverse(); - return this._inverse; - }, - - getOnFrame: function() { - return this._onFrame; - }, - - setOnFrame: function(onFrame) { - this._onFrame = onFrame; - if (!onFrame) { - delete this._onFrameCallback; - return; - } - var that = this, - requested = false, - before, - time = 0, - count = 0; - this._onFrameCallback = function(param, dontRequest) { - requested = false; - if (!that._onFrame) - return; - paper = that._scope; - requested = true; - if (!dontRequest) { - DomEvent.requestAnimationFrame(that._onFrameCallback, - that._canvas); - } - var now = Date.now() / 1000, - delta = before ? now - before : 0; - that._onFrame(Base.merge({ - delta: delta, - time: time += delta, - count: count++ - })); - before = now; - that.draw(true); - }; - if (!requested) - this._onFrameCallback(); - }, - - onResize: null -}, { - statics: { - _views: {}, - _id: 0 - } -}, new function() { - var tool, - timer, - curPoint, - tempFocus, - dragging = false; - - function viewToProject(view, event) { - return view.viewToProject(DomEvent.getOffset(event, view._canvas)); - } - - function updateFocus() { - if (!View._focused || !View._focused.isVisible()) { - PaperScope.each(function(scope) { - for (var i = 0, l = scope.views.length; i < l; i++) { - var view = scope.views[i]; - if (view.isVisible()) { - View._focused = tempFocus = view; - throw Base.stop; - } - } - }); - } - } - - function mousemove(event) { - var view; - if (!dragging) { - view = View._views[DomEvent.getTarget(event).getAttribute('id')]; - if (view) { - View._focused = tempFocus = view; - } else if (tempFocus && tempFocus == View._focused) { - View._focused = null; - updateFocus(); - } - } - if (!(view = view || View._focused) || !(tool = view._scope.tool)) - return; - var point = event && viewToProject(view, event); - var onlyMove = !!(!tool.onMouseDrag && tool.onMouseMove); - if (dragging && !onlyMove) { - curPoint = point || curPoint; - if (curPoint && tool.onHandleEvent('mousedrag', curPoint, event)) { - view.draw(true); - DomEvent.stop(event); - } - } else if ((!dragging || onlyMove) - && tool.onHandleEvent('mousemove', point, event)) { - view.draw(true); - DomEvent.stop(event); - } - } - - function mouseup(event) { - var view = View._focused; - if (!view || !dragging) - return; - dragging = false; - curPoint = null; - if (tool) { - if (timer != null) - timer = clearInterval(timer); - if (tool.onHandleEvent('mouseup', viewToProject(view, event), event)) { - view.draw(true); - DomEvent.stop(event); - } - } - } - - function selectstart(event) { - if (dragging) - DomEvent.stop(event); - } - - DomEvent.add(document, { - mousemove: mousemove, - mouseup: mouseup, - touchmove: mousemove, - touchend: mouseup, - selectstart: selectstart, - scroll: updateFocus - }); - - DomEvent.add(window, { - load: updateFocus - }); - - return { - _createEvents: function() { - var view = this; - - function mousedown(event) { - View._focused = view; - if (!(tool = view._scope.tool)) - return; - curPoint = viewToProject(view, event); - if (tool.onHandleEvent('mousedown', curPoint, event)) - view.draw(true); - if (tool.eventInterval != null) - timer = setInterval(mousemove, tool.eventInterval); - dragging = true; - } - - return { - mousedown: mousedown, - touchstart: mousedown, - selectstart: selectstart - }; - }, - - statics: { - - updateFocus: updateFocus - } - }; -}); - -var Event = this.Event = Base.extend({ - initialize: function(event) { - this.event = event; - }, - - preventDefault: function() { - DomEvent.preventDefault(this.event); - }, - - stopPropagation: function() { - DomEvent.stopPropagation(this.event); - }, - - stop: function() { - DomEvent.stop(this.event); - }, - - getModifiers: function() { - return Key.modifiers; - } -}); - -var KeyEvent = this.KeyEvent = Event.extend(new function() { - return { - initialize: function(down, key, character, event) { - this.base(event); - this.type = down ? 'keydown' : 'keyup'; - this.key = key; - this.character = character; - }, - - toString: function() { - return '{ type: ' + this.type - + ', key: ' + this.key - + ', character: ' + this.character - + ', modifiers: ' + this.getModifiers() - + ' }'; - } - }; -}); - -var Key = this.Key = new function() { - - var keys = { - 8: 'backspace', - 13: 'enter', - 16: 'shift', - 17: 'control', - 18: 'option', - 19: 'pause', - 20: 'caps-lock', - 27: 'escape', - 32: 'space', - 35: 'end', - 36: 'home', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 46: 'delete', - 91: 'command', - 93: 'command', - 224: 'command' - }, - - modifiers = Base.merge({ - shift: false, - control: false, - option: false, - command: false, - capsLock: false - }), - - charCodeMap = {}, - keyMap = {}, - downCode; - - function handleKey(down, keyCode, charCode, event) { - var character = String.fromCharCode(charCode), - key = keys[keyCode] || character.toLowerCase(), - handler = down ? 'onKeyDown' : 'onKeyUp', - view = View._focused, - scope = view && view.isVisible() && view._scope, - tool = scope && scope.tool; - keyMap[key] = down; - if (tool && tool[handler]) { - var keyEvent = new KeyEvent(down, key, character, event); - if (tool[handler](keyEvent) === false) - keyEvent.preventDefault(); - if (view) - view.draw(true); - } - } - - DomEvent.add(document, { - keydown: function(event) { - var code = event.which || event.keyCode; - var key = keys[code], name; - if (key) { - if ((name = Base.camelize(key)) in modifiers) - modifiers[name] = true; - charCodeMap[code] = 0; - handleKey(true, code, null, event); - } else { - downCode = code; - } - }, - - keypress: function(event) { - if (downCode != null) { - var code = event.which || event.keyCode; - charCodeMap[downCode] = code; - handleKey(true, downCode, code, event); - downCode = null; - } - }, - - keyup: function(event) { - var code = event.which || event.keyCode, - key = keys[code], name; - if (key && (name = Base.camelize(key)) in modifiers) - modifiers[name] = false; - if (charCodeMap[code] != null) { - handleKey(false, code, charCodeMap[code], event); - delete charCodeMap[code]; - } - } - }); - - return { - modifiers: modifiers, - - isDown: function(key) { - return !!keyMap[key]; - } - }; -}; - -var ToolEvent = this.ToolEvent = Event.extend({ - initialize: function(tool, type, event) { - this.tool = tool; - this.type = type; - this.event = event; - }, - - _choosePoint: function(point, toolPoint) { - return point ? point : toolPoint ? toolPoint.clone() : null; - }, - - getPoint: function() { - return this._choosePoint(this._point, this.tool._point); - }, - - setPoint: function(point) { - this._point = point; - }, - - getLastPoint: function() { - return this._choosePoint(this._lastPoint, this.tool._lastPoint); - }, - - setLastPoint: function(lastPoint) { - this._lastPoint = lastPoint; - }, - - getDownPoint: function() { - return this._choosePoint(this._downPoint, this.tool._downPoint); - }, - - setDownPoint: function(downPoint) { - this._downPoint = downPoint; - }, - - getMiddlePoint: function() { - if (!this._middlePoint && this.tool._lastPoint) { - return this.tool._point.add(this.tool._lastPoint).divide(2); - } - return this.middlePoint; - }, - - setMiddlePoint: function(middlePoint) { - this._middlePoint = middlePoint; - }, - - getDelta: function() { - return !this._delta && this.tool._lastPoint - ? this.tool._point.subtract(this.tool._lastPoint) - : this._delta; - }, - - setDelta: function(delta) { - this._delta = delta; - }, - - getCount: function() { - return /^mouse(down|up)$/.test(this.type) - ? this.tool._downCount - : this.tool._count; - }, - - setCount: function(count) { - this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count'] - = count; - }, - - getItem: function() { - if (!this._item) { - var result = this.tool._scope.project.hitTest(this.getPoint()); - if (result) { - var item = result.item, - parent = item._parent; - while ((parent instanceof Group && !(parent instanceof Layer)) - || parent instanceof CompoundPath) { - item = parent; - parent = parent._parent; - } - this._item = item; - } - } - return this._item; - }, - setItem: function(item) { - this._item = item; - }, - - toString: function() { - return '{ type: ' + this.type - + ', point: ' + this.getPoint() - + ', count: ' + this.getCount() - + ', modifiers: ' + this.getModifiers() - + ' }'; - } -}); - -var Tool = this.Tool = PaperScopeItem.extend({ - _list: 'tools', - _reference: 'tool', - - initialize: function() { - this.base(); - this._firstMove = true; - this._count = 0; - this._downCount = 0; - }, - - eventInterval: null, - - getMinDistance: function() { - return this._minDistance; - }, - - setMinDistance: function(minDistance) { - this._minDistance = minDistance; - if (this._minDistance != null && this._maxDistance != null - && this._minDistance > this._maxDistance) { - this._maxDistance = this._minDistance; - } - }, - - getMaxDistance: function() { - return this._maxDistance; - }, - - setMaxDistance: function(maxDistance) { - this._maxDistance = maxDistance; - if (this._minDistance != null && this._maxDistance != null - && this._maxDistance < this._minDistance) { - this._minDistance = maxDistance; - } - }, - - getFixedDistance: function() { - return this._minDistance == this._maxDistance - ? this._minDistance : null; - }, - - setFixedDistance: function(distance) { - this._minDistance = distance; - this._maxDistance = distance; - }, - - updateEvent: function(type, pt, minDistance, maxDistance, start, - needsChange, matchMaxDistance) { - if (!start) { - if (minDistance != null || maxDistance != null) { - var minDist = minDistance != null ? minDistance : 0; - var vector = pt.subtract(this._point); - var distance = vector.getLength(); - if (distance < minDist) - return false; - var maxDist = maxDistance != null ? maxDistance : 0; - if (maxDist != 0) { - if (distance > maxDist) { - pt = this._point.add(vector.normalize(maxDist)); - } else if (matchMaxDistance) { - return false; - } - } - } - if (needsChange && pt.equals(this._point)) - return false; - } - this._lastPoint = start && type == 'mousemove' ? pt : this._point; - this._point = pt; - switch (type) { - case 'mousedown': - this._lastPoint = this._downPoint; - this._downPoint = this._point; - this._downCount++; - break; - case 'mouseup': - this._lastPoint = this._downPoint; - break; - } - this._count = start ? 0 : this._count + 1; - return true; - }, - - onHandleEvent: function(type, pt, event) { - paper = this._scope; - var called = false; - switch (type) { - case 'mousedown': - this.updateEvent(type, pt, null, null, true, false, false); - if (this.onMouseDown) { - this.onMouseDown(new ToolEvent(this, type, event)); - called = true; - } - break; - case 'mousedrag': - var needsChange = false, - matchMaxDistance = false; - while (this.updateEvent(type, pt, this.minDistance, - this.maxDistance, false, needsChange, matchMaxDistance)) { - if (this.onMouseDrag) { - this.onMouseDrag(new ToolEvent(this, type, event)); - called = true; - } - needsChange = true; - matchMaxDistance = true; - } - break; - case 'mouseup': - if ((this._point.x != pt.x || this._point.y != pt.y) - && this.updateEvent('mousedrag', pt, this.minDistance, - this.maxDistance, false, false, false)) { - if (this.onMouseDrag) { - this.onMouseDrag(new ToolEvent(this, type, event)); - called = true; - } - } - this.updateEvent(type, pt, null, this.maxDistance, false, - false, false); - if (this.onMouseUp) { - this.onMouseUp(new ToolEvent(this, type, event)); - called = true; - } - this.updateEvent(type, pt, null, null, true, false, false); - this._firstMove = true; - break; - case 'mousemove': - while (this.updateEvent(type, pt, this.minDistance, - this.maxDistance, this._firstMove, true, false)) { - if (this.onMouseMove) { - this.onMouseMove(new ToolEvent(this, type, event)); - called = true; - } - this._firstMove = false; - } - break; - } - return called; - } -}); - -var CanvasProvider = { - canvases: [], - getCanvas: function(size) { - if (this.canvases.length) { - var canvas = this.canvases.pop(); - if ((canvas.width != size.width) - || (canvas.height != size.height)) { - canvas.width = size.width; - canvas.height = size.height; - } else { - canvas.getContext('2d').clearRect(0, 0, - size.width + 1, size.height + 1); - } - return canvas; - } else { - var canvas = document.createElement('canvas'); - canvas.width = size.width; - canvas.height = size.height; - return canvas; - } - }, - - returnCanvas: function(canvas) { - this.canvases.push(canvas); - } -}; - -var Numerical = new function() { - - var abscissas = [ - [ 0.5773502691896257645091488], - [0,0.7745966692414833770358531], - [ 0.3399810435848562648026658,0.8611363115940525752239465], - [0,0.5384693101056830910363144,0.9061798459386639927976269], - [ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016], - [0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897], - [ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609], - [0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762], - [ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640], - [0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380], - [ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491], - [0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294], - [ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973], - [0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657], - [ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542] - ]; - - var weights = [ - [1], - [0.8888888888888888888888889,0.5555555555555555555555556], - [0.6521451548625461426269361,0.3478548451374538573730639], - [0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640], - [0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961], - [0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114], - [0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314], - [0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922], - [0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688], - [0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537], - [0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160], - [0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216], - [0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329], - [0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284], - [0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806] - ]; - - var abs = Math.abs, - sqrt = Math.sqrt, - cos = Math.cos, - PI = Math.PI; - - return { - TOLERANCE: 10e-6, - EPSILON: 10e-12, - - integrate: function(f, a, b, n) { - var x = abscissas[n - 2], - w = weights[n - 2], - A = 0.5 * (b - a), - B = A + a, - i = 0, - m = (n + 1) >> 1, - sum = n & 1 ? w[i++] * f(B) : 0; - while (i < m) { - var Ax = A * x[i]; - sum += w[i++] * (f(B + Ax) + f(B - Ax)); - } - return A * sum; - }, - - findRoot: function(f, df, x, a, b, n, tolerance) { - for (var i = 0; i < n; i++) { - var fx = f(x), - dx = fx / df(x); - if (abs(dx) < tolerance) - return x; - var nx = x - dx; - if (fx > 0) { - b = x; - x = nx <= a ? 0.5 * (a + b) : nx; - } else { - a = x; - x = nx >= b ? 0.5 * (a + b) : nx; - } - } - }, - - solveQuadratic: function(a, b, c, roots, tolerance) { - if (abs(a) < tolerance) { - if (abs(b) >= tolerance) { - roots[0] = -c / b; - return 1; - } - if (abs(c) < tolerance) - return -1; - return 0; - } - var q = b * b - 4 * a * c; - if (q < 0) - return 0; - q = sqrt(q); - if (b < 0) - q = -q; - q = (b + q) * -0.5; - var n = 0; - if (abs(q) >= tolerance) - roots[n++] = c / q; - if (abs(a) >= tolerance) - roots[n++] = q / a; - return n; - }, - - solveCubic: function(a, b, c, d, roots, tolerance) { - if (abs(a) < tolerance) - return Numerical.solveQuadratic(b, c, d, roots, tolerance); - b /= a; - c /= a; - d /= a; - var Q = (b * b - 3 * c) / 9, - R = (2 * b * b * b - 9 * b * c + 27 * d) / 54, - Q3 = Q * Q * Q, - R2 = R * R; - b /= 3; - if (R2 < Q3) { - var theta = Math.acos(R / sqrt(Q3)), - q = -2 * sqrt(Q); - roots[0] = q * cos(theta / 3) - b; - roots[1] = q * cos((theta + 2 * PI) / 3) - b; - roots[2] = q * cos((theta - 2 * PI) / 3) - b; - return 3; - } else { - var A = -Math.pow(abs(R) + sqrt(R2 - Q3), 1 / 3); - if (R < 0) A = -A; - var B = (abs(A) < tolerance) ? 0 : Q / A; - roots[0] = (A + B) - b; - return 1; - } - return 0; - } - }; -}; - -var BlendMode = { - process: function(blendMode, srcContext, dstContext, alpha, offset) { - var srcCanvas = srcContext.canvas, - dstData = dstContext.getImageData(offset.x, offset.y, - srcCanvas.width, srcCanvas.height), - dst = dstData.data, - src = srcContext.getImageData(0, 0, - srcCanvas.width, srcCanvas.height).data, - min = Math.min, - max = Math.max, - abs = Math.abs, - sr, sg, sb, sa, - br, bg, bb, ba, - dr, dg, db; - - function getLum(r, g, b) { - return 0.2989 * r + 0.587 * g + 0.114 * b; - } - - function setLum(r, g, b, l) { - var d = l - getLum(r, g, b); - dr = r + d; - dg = g + d; - db = b + d; - var l = getLum(dr, dg, db), - mn = min(dr, dg, db), - mx = max(dr, dg, db); - if (mn < 0) { - var lmn = l - mn; - dr = l + (dr - l) * l / lmn; - dg = l + (dg - l) * l / lmn; - db = l + (db - l) * l / lmn; - } - if (mx > 255) { - var ln = 255 - l, mxl = mx - l; - dr = l + (dr - l) * ln / mxl; - dg = l + (dg - l) * ln / mxl; - db = l + (db - l) * ln / mxl; - } - } - - function getSat(r, g, b) { - return max(r, g, b) - min(r, g, b); - } - - function setSat(r, g, b, s) { - var col = [r, g, b], - mx = max(r, g, b), - mn = min(r, g, b), - md; - mn = mn == r ? 0 : mn == g ? 1 : 2; - mx = mx == r ? 0 : mx == g ? 1 : 2; - md = min(mn, mx) == 0 ? max(mn, mx) == 1 ? 2 : 1 : 0; - if (col[mx] > col[mn]) { - col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]); - col[mx] = s; - } else { - col[md] = col[mx] = 0; - } - col[mn] = 0; - dr = col[0]; - dg = col[1]; - db = col[2]; - } - - var modes = { - multiply: function() { - dr = br * sr / 255; - dg = bg * sg / 255; - db = bb * sb / 255; - }, - - screen: function() { - dr = 255 - (255 - br) * (255 - sr) / 255; - dg = 255 - (255 - bg) * (255 - sg) / 255; - db = 255 - (255 - bb) * (255 - sb) / 255; - }, - - overlay: function() { - dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255; - dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255; - db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255; - }, - - 'soft-light': function() { - var t = sr * br / 255; - dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255; - t = sg * bg / 255; - dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255; - t = sb * bb / 255; - db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255; - }, - - 'hard-light': function() { - dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255; - dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255; - db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255; - }, - - 'color-dodge': function() { - dr = sr == 255 ? sr : min(255, br * 255 / (255 - sr)); - dg = sg == 255 ? sg : min(255, bg * 255 / (255 - sg)); - db = sb == 255 ? sb : min(255, bb * 255 / (255 - sb)); - }, - - 'color-burn': function() { - dr = sr == 0 ? 0 : max(255 - ((255 - br) * 255) / sr, 0); - dg = sg == 0 ? 0 : max(255 - ((255 - bg) * 255) / sg, 0); - db = sb == 0 ? 0 : max(255 - ((255 - bb) * 255) / sb, 0); - }, - - darken: function() { - dr = br < sr ? br : sr; - dg = bg < sg ? bg : sg; - db = bb < sb ? bb : sb; - }, - - lighten: function() { - dr = br > sr ? br : sr; - dg = bg > sg ? bg : sg; - db = bb > sb ? bb : sb; - }, - - difference: function() { - dr = br - sr; - if (dr < 0) - dr = -dr; - dg = bg - sg; - if (dg < 0) - dg = -dg; - db = bb - sb; - if (db < 0) - db = -db; - }, - - exclusion: function() { - dr = br + sr * (255 - br - br) / 255; - dg = bg + sg * (255 - bg - bg) / 255; - db = bb + sb * (255 - bb - bb) / 255; - }, - - hue: function() { - setSat(sr, sg, sb, getSat(br, bg, bb)); - setLum(dr, dg, db, getLum(br, bg, bb)); - }, - - saturation: function() { - setSat(br, bg, bb, getSat(sr, sg, sb)); - setLum(dr, dg, db, getLum(br, bg, bb)); - }, - - luminosity: function() { - setLum(br, bg, bb, getLum(sr, sg, sb)); - }, - - color: function() { - setLum(sr, sg, sb, getLum(br, bg, bb)); - }, - - add: function() { - dr = min(br + sr, 255); - dg = min(bg + sg, 255); - db = min(bb + sb, 255); - }, - - subtract: function() { - dr = max(br - sr, 0); - dg = max(bg - sg, 0); - db = max(bb - sb, 0); - }, - - average: function() { - dr = (br + sr) / 2; - dg = (bg + sg) / 2; - db = (bb + sb) / 2; - }, - - negation: function() { - dr = 255 - abs(255 - sr - br); - dg = 255 - abs(255 - sg - bg); - db = 255 - abs(255 - sb - bb); - } - }; - - var process = modes[blendMode]; - if (!process) - return; - - for (var i = 0, l = dst.length; i < l; i += 4) { - sr = src[i]; - br = dst[i]; - sg = src[i + 1]; - bg = dst[i + 1]; - sb = src[i + 2]; - bb = dst[i + 2]; - sa = src[i + 3]; - ba = dst[i + 3]; - process(); - var a1 = sa * alpha / 255, - a2 = 1 - a1; - dst[i] = a1 * dr + a2 * br; - dst[i + 1] = a1 * dg + a2 * bg; - dst[i + 2] = a1 * db + a2 * bb; - dst[i + 3] = sa * alpha + a2 * ba; - } - dstContext.putImageData(dstData, offset.x, offset.y); - } -}; - -var PaperScript = this.PaperScript = new function() { -var parse_js=new function(){function W(a,b,c){var d=[];for(var e=0;e0,g=j(function(){return h(a[0]?k(["case",z(a[0])+":"]):"default:")},.5)+(f?e+j(function(){return u(a[1]).join(e)}):"");!c&&f&&d0?h(a):a}).join(e)},block:w,"var":function(a){return"var "+l(W(a,x))+";"},"const":function(a){return"const "+l(W(a,x))+";"},"try":function(a,b,c){var d=["try",w(a)];b&&d.push("catch","("+b[0]+")",w(b[1])),c&&d.push("finally",w(c));return k(d)},"throw":function(a){return k(["throw",z(a)])+";"},"new":function(a,b){b=b.length>0?"("+l(W(b,z))+")":"";return k(["new",m(a,"seq","binary","conditional","assign",function(a){var b=N(),c={};try{b.with_walkers({call:function(){throw c},"function":function(){return this}},function(){b.walk(a)})}catch(d){if(d===c)return!0;throw d}})+b])},"switch":function(a,b){return k(["switch","("+z(a)+")",v(b)])},"break":function(a){var b="break";a!=null&&(b+=" "+g(a));return b+";"},"continue":function(a){var b="continue";a!=null&&(b+=" "+g(a));return b+";"},conditional:function(a,b,c){return k([m(a,"assign","seq","conditional"),"?",m(b,"seq"),":",m(c,"seq")])},assign:function(a,b,c){a&&a!==!0?a+="=":a="=";return k([z(b),a,m(c,"seq")])},dot:function(a){var b=z(a),c=1;a[0]=="num"?/\./.test(a[1])||(b+="."):o(a)&&(b="("+b+")");while(cB[b[1]])d="("+d+")";if(L(c[0],["assign","conditional","seq"])||c[0]=="binary"&&B[a]>=B[c[1]]&&(c[1]!=a||!L(a,["&&","||","*"])))e="("+e+")";return k([d,a,e])},"unary-prefix":function(a,b){var c=z(b);b[0]=="num"||b[0]=="unary-prefix"&&!M(i,a+b[1])||!o(b)||(c="("+c+")");return a+(p(a.charAt(0))?" ":"")+c},"unary-postfix":function(a,b){var c=z(b);b[0]=="num"||b[0]=="unary-postfix"&&!M(i,a+b[1])||!o(b)||(c="("+c+")");return c+a},sub:function(a,b){var c=z(a);o(a)&&(c="("+c+")");return c+"["+z(b)+"]"},object:function(a){return a.length==0?"{}":"{"+e+j(function(){return W(a,function(a){if(a.length==3)return h(t(a[0],a[1][2],a[1][3],a[2]));var d=a[0],e=z(a[1]);b.quote_keys?d=Q(d):(typeof d=="number"||!c&&+d+""==d)&&parseFloat(d)>=0?d=q(+d):V(d)||(d=Q(d));return h(k(c&&b.space_colon?[d,":",e]:[d+":",e]))}).join(","+e)})+e+h("}")},regexp:function(a,b){return"/"+a+"/"+b},array:function(a){return a.length==0?"[]":k(["[",l(W(a,function(a){return!c&&a[0]=="atom"&&a[1]=="undefined"?"":m(a,"seq")})),"]"])},stat:function(a){return z(a).replace(/;*\s*$/,";")},seq:function(){return l(W(J(arguments),z))},label:function(a,b){return k([g(a),":",z(b)])},"with":function(a,b){return k(["with","("+z(a)+")",z(b)])},atom:function(a){return g(a)}},y=[];return z(a)}function Q(a){var b=0,c=0;a=a.replace(/[\\\b\f\n\r\t\x22\x27]/g,function(a){switch(a){case"\\":return"\\\\";case"\b":return"\\b";case"\f":return"\\f";case"\n":return"\\n";case"\r":return"\\r";case"\t":return"\\t";case'"':++b;return'"';case"'":++c;return"'"}return a});return b>c?"'"+a.replace(/\x27/g,"\\'")+"'":'"'+a.replace(/\x22/g,'\\"')+'"'}function O(a){return!a||a[0]=="block"&&(!a[1]||a[1].length==0)}function N(){function g(a,b){var c={},e;for(e in a)M(a,e)&&(c[e]=d[e],d[e]=a[e]);var f=b();for(e in c)M(c,e)&&(c[e]?d[e]=c[e]:delete d[e]);return f}function f(a){if(a==null)return null;try{e.push(a);var b=a[0],f=d[b];if(f){var g=f.apply(a,a.slice(1));if(g!=null)return g}f=c[b];return f.apply(a,a.slice(1))}finally{e.pop()}}function b(a){var b=[this[0]];a!=null&&b.push(W(a,f));return b}function a(a){return[this[0],W(a,function(a){var b=[a[0]];a.length>1&&(b[1]=f(a[1]));return b})]}var c={string:function(a){return[this[0],a]},num:function(a){return[this[0],a]},name:function(a){return[this[0],a]},toplevel:function(a){return[this[0],W(a,f)]},block:b,splice:b,"var":a,"const":a,"try":function(a,b,c){return[this[0],W(a,f),b!=null?[b[0],W(b[1],f)]:null,c!=null?W(c,f):null]},"throw":function(a){return[this[0],f(a)]},"new":function(a,b){return[this[0],f(a),W(b,f)]},"switch":function(a,b){return[this[0],f(a),W(b,function(a){return[a[0]?f(a[0]):null,W(a[1],f)]})]},"break":function(a){return[this[0],a]},"continue":function(a){return[this[0],a]},conditional:function(a,b,c){return[this[0],f(a),f(b),f(c)]},assign:function(a,b,c){return[this[0],a,f(b),f(c)]},dot:function(a){return[this[0],f(a)].concat(J(arguments,1))},call:function(a,b){return[this[0],f(a),W(b,f)]},"function":function(a,b,c){return[this[0],a,b.slice(),W(c,f)]},defun:function(a,b,c){return[this[0],a,b.slice(),W(c,f)]},"if":function(a,b,c){return[this[0],f(a),f(b),f(c)]},"for":function(a,b,c,d){return[this[0],f(a),f(b),f(c),f(d)]},"for-in":function(a,b,c,d){return[this[0],f(a),f(b),f(c),f(d)]},"while":function(a,b){return[this[0],f(a),f(b)]},"do":function(a,b){return[this[0],f(a),f(b)]},"return":function(a){return[this[0],f(a)]},binary:function(a,b,c){return[this[0],a,f(b),f(c)]},"unary-prefix":function(a,b){return[this[0],a,f(b)]},"unary-postfix":function(a,b){return[this[0],a,f(b)]},sub:function(a,b){return[this[0],f(a),f(b)]},object:function(a){return[this[0],W(a,function(a){return a.length==2?[a[0],f(a[1])]:[a[0],f(a[1]),a[2]]})]},regexp:function(a,b){return[this[0],a,b]},array:function(a){return[this[0],W(a,f)]},stat:function(a){return[this[0],f(a)]},seq:function(){return[this[0]].concat(W(J(arguments),f))},label:function(a,b){return[this[0],a,f(b)]},"with":function(a,b){return[this[0],f(a),f(b)]},atom:function(a){return[this[0],a]}},d={},e=[];return{walk:f,with_walkers:g,parent:function(){return e[e.length-2]},stack:function(){return e}}}function M(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function L(a,b){for(var c=b.length;--c>=0;)if(b[c]===a)return!0;return!1}function K(a){return a.split("")}function J(a,b){return Array.prototype.slice.call(a,b||0)}function I(a){var b={};for(var c=0;c0;++b)arguments[b]();return a}function G(a){var b=J(arguments,1);return function(){return a.apply(this,b.concat(J(arguments)))}}function F(a,b,c){function bk(a){try{++d.in_loop;return a()}finally{--d.in_loop}}function bi(a){var b=bg(a),c=d.token.value;if(e("operator")&&M(A,c)){if(bh(b)){g();return p("assign",A[c],b,bi(a))}i("Invalid assignment")}return b}function bh(a){if(!b)return!0;switch(a[0]){case"dot":case"sub":case"new":case"call":return!0;case"name":return a[1]!="this"}}function bg(a){var b=bf(a);if(e("operator","?")){g();var c=bj(!1);m(":");return p("conditional",b,c,bj(!1,a))}return b}function bf(a){return be(Y(!0),0,a)}function be(a,b,c){var f=e("operator")?d.token.value:null;f&&f=="in"&&c&&(f=null);var h=f!=null?B[f]:null;if(h!=null&&h>b){g();var i=be(Y(!0),h,c);return be(p("binary",f,a,i),b,c)}return a}function bd(a,b,c){(b=="++"||b=="--")&&!bh(c)&&i("Invalid use of "+b+" operator");return p(a,b,c)}function bc(a,b){if(e("punc",".")){g();return bc(p("dot",a,bb()),b)}if(e("punc","[")){g();return bc(p("sub",a,H(bj,G(m,"]"))),b)}if(b&&e("punc","(")){g();return bc(p("call",a,Z(")")),!0)}return b&&e("operator")&&M(z,d.token.value)?H(G(bd,"unary-postfix",d.token.value,a),g):a}function bb(){switch(d.token.type){case"name":case"operator":case"keyword":case"atom":return H(d.token.value,g);default:k()}}function ba(){switch(d.token.type){case"num":case"string":return H(d.token.value,g)}return bb()}function _(){var a=!0,c=[];while(!e("punc","}")){a?a=!1:m(",");if(!b&&e("punc","}"))break;var f=d.token.type,h=ba();f!="name"||h!="get"&&h!="set"||!!e("punc",":")?(m(":"),c.push([h,bj(!1)])):c.push([bb(),P(!1),h])}g();return p("object",c)}function $(){return p("array",Z("]",!b,!0))}function Z(a,b,c){var d=!0,f=[];while(!e("punc",a)){d?d=!1:m(",");if(b&&e("punc",a))break;e("punc",",")&&c?f.push(["atom","undefined"]):f.push(bj(!1))}g();return f}function X(){var a=Y(!1),b;e("punc","(")?(g(),b=Z(")")):b=[];return bc(p("new",a,b),!0)}function W(){return p("const",U())}function V(a){return p("var",U(a))}function U(a){var b=[];for(;;){e("name")||k();var c=d.token.value;g(),e("operator","=")?(g(),b.push([c,bj(!1,a)])):b.push([c]);if(!e("punc",","))break;g()}return b}function T(){var a=R(),b,c;if(e("keyword","catch")){g(),m("("),e("name")||i("Name expected");var f=d.token.value;g(),m(")"),b=[f,R()]}e("keyword","finally")&&(g(),c=R()),!b&&!c&&i("Missing catch/finally blocks");return p("try",a,b,c)}function R(){m("{");var a=[];while(!e("punc","}"))e("eof")&&k(),a.push(t());g();return a}function Q(){var a=q(),b=t(),c;e("keyword","else")&&(g(),c=t());return p("if",a,b,c)}function O(a){var b=a[0]=="var"?p("name",a[1][0]):a;g();var c=bj();m(")");return p("for-in",a,b,c,bk(t))}function N(a){m(";");var b=e("punc",";")?null:bj();m(";");var c=e("punc",")")?null:bj();m(")");return p("for",a,b,c,bk(t))}function K(){m("(");var a=null;if(!e("punc",";")){a=e("keyword","var")?(g(),V(!0)):bj(!0,!0);if(e("operator","in"))return O(a)}return N(a)}function I(a){var b;n()||(b=e("name")?d.token.value:null),b!=null?(g(),L(b,d.labels)||i("Label "+b+" without matching loop or statement")):d.in_loop==0&&i(a+" not inside a loop or switch"),o();return p(a,b)}function F(){return p("stat",H(bj,o))}function w(a){d.labels.push(a);var c=d.token,e=t();b&&!M(C,e[0])&&k(c),d.labels.pop();return p("label",a,e)}function s(a){return c?function(){var b=d.token,c=a.apply(this,arguments);c[0]=r(c[0],b,h());return c}:a}function r(a,b,c){return a instanceof E?a:new E(a,b,c)}function q(){m("(");var a=bj();m(")");return a}function p(){return J(arguments)}function o(){e("punc",";")?g():n()||k()}function n(){return!b&&(d.token.nlb||e("eof")||e("punc","}"))}function m(a){return l("punc",a)}function l(a,b){if(e(a,b))return g();j(d.token,"Unexpected token "+d.token.type+", expected "+a)}function k(a){a==null&&(a=d.token),j(a,"Unexpected token: "+a.type+" ("+a.value+")")}function j(a,b){i(b,a.line,a.col)}function i(a,b,c,e){var f=d.input.context();u(a,b!=null?b:f.tokline,c!=null?c:f.tokcol,e!=null?e:f.tokpos)}function h(){return d.prev}function g(){d.prev=d.token,d.peeked?(d.token=d.peeked,d.peeked=null):d.token=d.input();return d.token}function f(){return d.peeked||(d.peeked=d.input())}function e(a,b){return v(d.token,a,b)}var d={input:typeof a=="string"?x(a,!0):a,token:null,prev:null,peeked:null,in_function:0,in_loop:0,labels:[]};d.token=g();var t=s(function(){e("operator","/")&&(d.peeked=null,d.token=d.input(!0));switch(d.token.type){case"num":case"string":case"regexp":case"operator":case"atom":return F();case"name":return v(f(),"punc",":")?w(H(d.token.value,g,g)):F();case"punc":switch(d.token.value){case"{":return p("block",R());case"[":case"(":return F();case";":g();return p("block");default:k()};case"keyword":switch(H(d.token.value,g)){case"break":return I("break");case"continue":return I("continue");case"debugger":o();return p("debugger");case"do":return function(a){l("keyword","while");return p("do",H(q,o),a)}(bk(t));case"for":return K();case"function":return P(!0);case"if":return Q();case"return":d.in_function==0&&i("'return' outside of function");return p("return",e("punc",";")?(g(),null):n()?null:H(bj,o));case"switch":return p("switch",q(),S());case"throw":return p("throw",H(bj,o));case"try":return T();case"var":return H(V,o);case"const":return H(W,o);case"while":return p("while",q(),bk(t));case"with":return p("with",q(),t());default:k()}}}),P=s(function(a){var b=e("name")?H(d.token.value,g):null;a&&!b&&k(),m("(");return p(a?"defun":"function",b,function(a,b){while(!e("punc",")"))a?a=!1:m(","),e("name")||k(),b.push(d.token.value),g();g();return b}(!0,[]),function(){++d.in_function;var a=d.in_loop;d.in_loop=0;var b=R();--d.in_function,d.in_loop=a;return b}())}),S=G(bk,function(){m("{");var a=[],b=null;while(!e("punc","}"))e("eof")&&k(),e("keyword","case")?(g(),b=[],a.push([bj(),b]),m(":")):e("keyword","default")?(g(),m(":"),b=[],a.push([null,b])):(b||k(),b.push(t()));g();return a}),Y=s(function(a){if(e("operator","new")){g();return X()}if(e("operator")&&M(y,d.token.value))return bd("unary-prefix",H(d.token.value,g),Y(a));if(e("punc")){switch(d.token.value){case"(":g();return bc(H(bj,G(m,")")),a);case"[":g();return bc($(),a);case"{":g();return bc(_(),a)}k()}if(e("keyword","function")){g();return bc(P(!1),a)}if(M(D,d.token.type)){var b=d.token.type=="regexp"?p("regexp",d.token.value[0],d.token.value[1]):p(d.token.type,d.token.value);return bc(H(b,g),a)}k()}),bj=s(function(a,b){arguments.length==0&&(a=!0);var c=bi(b);if(a&&e("punc",",")){g();return p("seq",c,bj(!0,b))}return c});return p("toplevel",function(a){while(!e("eof"))a.push(t());return a}([]))}function E(a,b,c){this.name=a,this.start=b,this.end=c}function x(b){function P(a){if(a)return I();y(),v();var b=g();if(!b)return x("eof");if(o(b))return C();if(b=='"'||b=="'")return F();if(M(l,b))return x("punc",h());if(b==".")return L();if(b=="/")return K();if(M(e,b))return J();if(b=="\\"||q(b))return N();B("Unexpected character '"+b+"'")}function O(a,b){try{return b()}catch(c){if(c===w)B(a);else throw c}}function N(){var b=A(r);return M(a,b)?M(i,b)?x("operator",b):M(d,b)?x("atom",b):x("keyword",b):x("name",b)}function L(){h();return o(g())?C("."):x("punc",".")}function K(){h();var a=f.regex_allowed;switch(g()){case"/":f.comments_before.push(G()),f.regex_allowed=a;return P();case"*":f.comments_before.push(H()),f.regex_allowed=a;return P()}return f.regex_allowed?I():J("/")}function J(a){function b(a){if(!g())return a;var c=a+g();if(M(i,c)){h();return b(c)}return a}return x("operator",b(a||h()))}function I(){return O("Unterminated regular expression",function(){var a=!1,b="",c,d=!1;while(c=h(!0))if(a)b+="\\"+c,a=!1;else if(c=="[")d=!0,b+=c;else if(c=="]"&&d)d=!1,b+=c;else{if(c=="/"&&!d)break;c=="\\"?a=!0:b+=c}var e=A(function(a){return M(m,a)});return x("regexp",[b,e])})}function H(){h();return O("Unterminated multiline comment",function(){var a=t("*/",!0),b=f.text.substring(f.pos,a),c=x("comment2",b,!0);f.pos=a+2,f.line+=b.split("\n").length-1,f.newline_before=b.indexOf("\n")>=0;return c})}function G(){h();var a=t("\n"),b;a==-1?(b=f.text.substr(f.pos),f.pos=f.text.length):(b=f.text.substring(f.pos,a),f.pos=a);return x("comment1",b,!0)}function F(){return O("Unterminated string constant",function(){var a=h(),b="";for(;;){var c=h(!0);if(c=="\\")c=D();else if(c==a)break;b+=c}return x("string",b)})}function E(a){var b=0;for(;a>0;--a){var c=parseInt(h(!0),16);isNaN(c)&&B("Invalid hex-character pattern in string"),b=b<<4|c}return b}function D(){var a=h(!0);switch(a){case"n":return"\n";case"r":return"\r";case"t":return"\t";case"b":return"\b";case"v":return" ";case"f":return"\f";case"0":return"";case"x":return String.fromCharCode(E(2));case"u":return String.fromCharCode(E(4));case"\n":return"";default:return a}}function C(a){var b=!1,c=!1,d=!1,e=a==".",f=A(function(f,g){if(f=="x"||f=="X")return d?!1:d=!0;if(!d&&(f=="E"||f=="e"))return b?!1:b=c=!0;if(f=="-")return c||g==0&&!a?!0:!1;if(f=="+")return c;c=!1;if(f==".")return!e&&!d?e=!0:!1;return p(f)});a&&(f=a+f);var g=s(f);if(!isNaN(g))return x("num",g);B("Invalid syntax: "+f)}function B(a){u(a,f.tokline,f.tokcol,f.tokpos)}function A(a){var b="",c=g(),d=0;while(c&&a(c,d++))b+=h(),c=g();return b}function y(){while(M(j,g()))h()}function x(a,b,d){f.regex_allowed=a=="operator"&&!M(z,b)||a=="keyword"&&M(c,b)||a=="punc"&&M(k,b);var e={type:a,value:b,line:f.tokline,col:f.tokcol,pos:f.tokpos,nlb:f.newline_before};d||(e.comments_before=f.comments_before,f.comments_before=[]),f.newline_before=!1;return e}function v(){f.tokline=f.line,f.tokcol=f.col,f.tokpos=f.pos}function t(a,b){var c=f.text.indexOf(a,f.pos);if(b&&c==-1)throw w;return c}function n(){return!f.peek()}function h(a){var b=f.text.charAt(f.pos++);if(a&&!b)throw w;b=="\n"?(f.newline_before=!0,++f.line,f.col=0):++f.col;return b}function g(){return f.text.charAt(f.pos)}var f={text:b.replace(/\r\n?|[\n\u2028\u2029]/g,"\n").replace(/^\uFEFF/,""),pos:0,tokpos:0,line:0,tokline:0,col:0,tokcol:0,newline_before:!1,regex_allowed:!1,comments_before:[]};P.context=function(a){a&&(f=a);return f};return P}function v(a,b,c){return a.type==b&&(c==null||a.value==c)}function u(a,b,c,d){throw new t(a,b,c,d)}function t(a,b,c,d){this.message=a,this.line=b,this.col=c,this.pos=d}function s(a){if(f.test(a))return parseInt(a.substr(2),16);if(g.test(a))return parseInt(a.substr(1),8);if(h.test(a))return parseFloat(a)}function r(a){return q(a)||o(a)}function q(a){return a=="$"||a=="_"||n(a)}function p(a){return o(a)||n(a)}function o(a){a=a.charCodeAt(0);return a>=48&&a<=57}function n(a){a=a.charCodeAt(0);return a>=65&&a<=90||a>=97&&a<=122}var a=I(["break","case","catch","const","continue","default","delete","do","else","finally","for","function","if","in","instanceof","new","return","switch","throw","try","typeof","var","void","while","with"]),b=I(["abstract","boolean","byte","char","class","debugger","double","enum","export","extends","final","float","goto","implements","import","int","interface","long","native","package","private","protected","public","short","static","super","synchronized","throws","transient","volatile"]),c=I(["return","new","delete","throw","else","case"]),d=I(["false","null","true","undefined"]),e=I(K("+-*&%=<>!?|~^")),f=/^0x[0-9a-f]+$/i,g=/^0[0-7]+$/,h=/^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i,i=I(["in","instanceof","typeof","new","void","delete","++","--","+","-","!","~","&","|","^","*","/","%",">>","<<",">>>","<",">","<=",">=","==","===","!=","!==","?","=","+=","-=","/=","*=","%=",">>=","<<=",">>>=","|=","^=","&=","&&","||"]),j=I(K(" \n\r\t")),k=I(K("[{}(,.;:")),l=I(K("[]{}(),;:")),m=I(K("gmsiy"));t.prototype.toString=function(){return this.message+" (line: "+this.line+", col: "+this.col+", pos: "+this.pos+")"};var w={},y=I(["typeof","void","delete","--","++","!","~","-","+"]),z=I(["--","++"]),A=function(a,b,c){while(c>=","<<=",">>>=","|=","^=","&="],{"=":!0},0),B=function(a,b){for(var c=0,d=1;c","<=",">=","in","instanceof"],[">>","<<",">>>"],["+","-"],["*","/","%"]],{}),C=I(["for","do","while","switch"]),D=I(["atom","num","string","regexp","name"]);E.prototype.toString=function(){return this.name};var P=I(["name","array","object","string","dot","sub","call","regexp"]),R=I(["if","while","do","for","for-in","with"]);return{parse:F,gen_code:S,tokenizer:x,ast_walker:N}} - - // Math Operators - - var operators = { - '+': 'add', - '-': 'subtract', - '*': 'multiply', - '/': 'divide', - '%': 'modulo', - '==': 'equals', - '!=': 'equals' - }; - - function $eval(left, operator, right) { - var handler = operators[operator]; - if (left && left[handler]) { - var res = left[handler](right); - return operator == '!=' ? !res : res; - } - switch (operator) { - case '+': return left + right; - case '-': return left - right; - case '*': return left * right; - case '/': return left / right; - case '%': return left % right; - case '==': return left == right; - case '!=': return left != right; - default: - throw new Error('Implement Operator: ' + operator); - } - }; - - // Sign Operators - - var signOperators = { - '-': 'negate' - }; - - function $sign(operator, value) { - var handler = signOperators[operator]; - if (value && value[handler]) { - return value[handler](); - } - switch (operator) { - case '+': return +value; - case '-': return -value; - default: - throw new Error('Implement Sign Operator: ' + operator); - } - } - - // AST Helpers - - function isDynamic(exp) { - var type = exp[0]; - return type != 'num' && type != 'string'; - } - - function handleOperator(operator, left, right) { - // Only replace operators with calls to $operator if the left hand side - // is potentially an object. - if (operators[operator] && isDynamic(left)) { - // Replace with call to $operator(left, operator, right): - return ['call', ['name', '$eval'], - [left, ['string', operator], right]]; - } - } - - /** - * Compiles PaperScript code into JavaScript code. - * - * @name PaperScript.compile - * @function - * @param {String} code The PaperScript code. - * @return {String} The compiled PaperScript as JavaScript code. - */ - function compile(code) { - // Use parse-js to translate the code into a AST structure which is then - // walked and parsed for operators to overload. The resulting AST is - // translated back to code and evaluated. - var ast = parse_js.parse(code), - walker = parse_js.ast_walker(), - walk = walker.walk; - - ast = walker.with_walkers({ - 'binary': function(operator, left, right) { - // Handle simple mathematical operators here: - return handleOperator(operator, left = walk(left), - right = walk(right)) - // Always return something since we're walking left and - || [this[0], operator, left, right]; - }, - - 'assign': function(operator, left, right) { - var res = handleOperator(operator, left = walk(left), - right = walk(right)); - if (res) - return [this[0], true, left, res]; - return [this[0], operator, left, right]; - }, - - 'unary-prefix': function(operator, exp) { - if (signOperators[operator] && isDynamic(exp)) { - return ['call', ['name', '$sign'], - [['string', operator], walk(exp)]]; - } - } - }, function() { - return walk(ast); - }); - - return parse_js.gen_code(ast, { - beautify: true - }); - } - - function evaluate(code, scope) { - paper = scope; - var view = scope.view, - tool = /on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code) - && new Tool(), - res; - with (scope) { - (function() { - var onEditOptions, onSelect, onDeselect, onReselect, onMouseDown, - onMouseUp, onMouseDrag, onMouseMove, onKeyDown, onKeyUp, - onFrame, onResize, - handlers = [ 'onEditOptions', 'onSelect', 'onDeselect', - 'onReselect', 'onMouseDown', 'onMouseUp', 'onMouseDrag', - 'onMouseMove', 'onKeyDown', 'onKeyUp']; - res = eval(compile(code)); - if (tool) { - Base.each(handlers, function(key) { - tool[key] = eval(key); - }); - } - if (view) { - view.onResize = onResize; - view.setOnFrame(onFrame); - view.draw(); - } - }).call(scope); - } - return res; - } - - function request(url, scope) { - var xhr = new (window.ActiveXObject || XMLHttpRequest)( - 'Microsoft.XMLHTTP'); - xhr.open('GET', url, true); - if (xhr.overrideMimeType) { - xhr.overrideMimeType('text/plain'); - } - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - return evaluate(xhr.responseText, scope); - } - }; - return xhr.send(null); - } - - function load() { - var scripts = document.getElementsByTagName('script'); - for (var i = 0, l = scripts.length; i < l; i++) { - var script = scripts[i]; - if (/^text\/(?:x-|)paperscript$/.test(script.type) - && !script.getAttribute('data-paper-loaded')) { - var scope = new PaperScope(script); - scope.setup(PaperScript.getAttribute(script, 'canvas')); - if (script.src) { - request(script.src, scope); - } else { - evaluate(script.innerHTML, scope); - } - script.setAttribute('data-paper-loaded', true); - } - } - } - - DomEvent.add(window, { load: load }); - - function handleAttribute(name) { - name += 'Attribute'; - return function(el, attr) { - return el[name](attr) || el[name]('data-paper-' + attr); - }; - } - - return { - compile: compile, - evaluate: evaluate, - load: load, - getAttribute: handleAttribute('get'), - hasAttribute: handleAttribute('has') - }; - -}; - -this.load = PaperScript.load; - -Base.each(this, function(val, key) { - if (val && val.prototype instanceof Base) { - val._name = key; - } -}); - -this.enumerable = true; -return new (PaperScope.inject(this)); -}; \ No newline at end of file diff --git a/dist/paper.js b/dist/paper.js index e459b4a4..e69de29b 100644 --- a/dist/paper.js +++ b/dist/paper.js @@ -1,8237 +0,0 @@ -/*! - * Paper.js v0.22 - * - * This file is part of Paper.js, a JavaScript Vector Graphics Library, - * based on Scriptographer.org and designed to be largely API compatible. - * http://paperjs.org/ - * http://scriptographer.org/ - * - * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey - * http://lehni.org/ & http://jonathanpuckey.com/ - * - * Distributed under the MIT license. See LICENSE file for details. - * - * All rights reserved. - * - * Date: Wed Apr 25 20:47:53 2012 +0200 - * - *** - * - * Bootstrap.js JavaScript Framework. - * http://bootstrapjs.org/ - * - * Copyright (c) 2006 - 2011 Juerg Lehni - * http://lehni.org/ - * - * Distributed under the MIT license. - * - *** - * - * Parse-js - * - * A JavaScript tokenizer / parser / generator, originally written in Lisp. - * Copyright (c) Marijn Haverbeke - * http://marijn.haverbeke.nl/parse-js/ - * - * Ported by to JavaScript by Mihai Bazon - * Copyright (c) 2010, Mihai Bazon - * http://mihai.bazon.net/blog/ - * - * Modifications and adaptions to browser (c) 2011, Juerg Lehni - * http://lehni.org/ - * - * Distributed under the BSD license. - */ - -var paper = new function() { - -var Base = new function() { - var fix = !this.__proto__, - hidden = /^(statics|generics|preserve|enumerable|prototype|__proto__|toString|valueOf)$/, - proto = Object.prototype, - has = fix - ? function(name) { - return name !== '__proto__' && this.hasOwnProperty(name); - } - : proto.hasOwnProperty, - toString = proto.toString, - proto = Array.prototype, - isArray = Array.isArray = Array.isArray || function(obj) { - return toString.call(obj) === '[object Array]'; - }, - slice = proto.slice, - forEach = proto.forEach || function(iter, bind) { - for (var i = 0, l = this.length; i < l; i++) - iter.call(bind, this[i], i, this); - }, - forIn = function(iter, bind) { - for (var i in this) - if (this.hasOwnProperty(i)) - iter.call(bind, this[i], i, this); - }, - _define = Object.defineProperty, - _describe = Object.getOwnPropertyDescriptor; - - function define(obj, name, desc) { - if (_define) { - try { - delete obj[name]; - return _define(obj, name, desc); - } catch (e) {} - } - if ((desc.get || desc.set) && obj.__defineGetter__) { - desc.get && obj.__defineGetter__(name, desc.get); - desc.set && obj.__defineSetter__(name, desc.set); - } else { - obj[name] = desc.value; - } - return obj; - } - - function describe(obj, name) { - if (_describe) { - try { - return _describe(obj, name); - } catch (e) {} - } - var get = obj.__lookupGetter__ && obj.__lookupGetter__(name); - return get - ? { get: get, set: obj.__lookupSetter__(name), enumerable: true, - configurable: true } - : has.call(obj, name) - ? { value: obj[name], enumerable: true, configurable: true, - writable: true } - : null; - } - - function inject(dest, src, enumerable, base, preserve, generics) { - var beans, bean; - - function field(name, val, dontCheck, generics) { - var val = val || (val = describe(src, name)) - && (val.get ? val : val.value), - func = typeof val === 'function', - res = val, - prev = preserve || func - ? (val && val.get ? name in dest : dest[name]) : null; - if (generics && func && (!preserve || !generics[name])) { - generics[name] = function(bind) { - return bind && dest[name].apply(bind, - slice.call(arguments, 1)); - } - } - if ((dontCheck || val !== undefined && has.call(src, name)) - && (!preserve || !prev)) { - if (func) { - if (prev && /\bthis\.base\b/.test(val)) { - var fromBase = base && base[name] == prev; - res = function() { - var tmp = describe(this, 'base'); - define(this, 'base', { value: fromBase - ? base[name] : prev, configurable: true }); - try { - return val.apply(this, arguments); - } finally { - tmp ? define(this, 'base', tmp) - : delete this.base; - } - }; - res.toString = function() { - return val.toString(); - } - res.valueOf = function() { - return val.valueOf(); - } - } - if (beans && val.length == 0 - && (bean = name.match(/^(get|is)(([A-Z])(.*))$/))) - beans.push([ bean[3].toLowerCase() + bean[4], bean[2] ]); - } - if (!res || func || !res.get) - res = { value: res, writable: true }; - if ((describe(dest, name) - || { configurable: true }).configurable) { - res.configurable = true; - res.enumerable = enumerable; - } - define(dest, name, res); - } - } - if (src) { - beans = []; - for (var name in src) - if (has.call(src, name) && !hidden.test(name)) - field(name, null, true, generics); - field('toString'); - field('valueOf'); - for (var i = 0, l = beans && beans.length; i < l; i++) - try { - var bean = beans[i], part = bean[1]; - field(bean[0], { - get: dest['get' + part] || dest['is' + part], - set: dest['set' + part] - }, true); - } catch (e) {} - } - return dest; - } - - function extend(obj) { - var ctor = function(dont) { - if (fix) define(this, '__proto__', { value: obj }); - if (this.initialize && dont !== ctor.dont) - return this.initialize.apply(this, arguments); - } - ctor.prototype = obj; - ctor.toString = function() { - return (this.prototype.initialize || function() {}).toString(); - } - return ctor; - } - - function iterator(iter) { - return !iter - ? function(val) { return val } - : typeof iter !== 'function' - ? function(val) { return val == iter } - : iter; - } - - function each(obj, iter, bind, asArray) { - try { - if (obj) - (asArray || asArray === undefined && isArray(obj) - ? forEach : forIn).call(obj, iterator(iter), - bind = bind || obj); - } catch (e) { - if (e !== Base.stop) throw e; - } - return bind; - } - - function clone(obj) { - return each(obj, function(val, i) { - this[i] = val; - }, new obj.constructor()); - } - - return inject(function() {}, { - inject: function(src) { - if (src) { - var proto = this.prototype, - base = proto.__proto__ && proto.__proto__.constructor, - statics = src.statics == true ? src : src.statics; - if (statics != src) - inject(proto, src, src.enumerable, base && base.prototype, - src.preserve, src.generics && this); - inject(this, statics, true, base, src.preserve); - } - for (var i = 1, l = arguments.length; i < l; i++) - this.inject(arguments[i]); - return this; - }, - - extend: function(src) { - var proto = new this(this.dont), - ctor = extend(proto); - define(proto, 'constructor', - { value: ctor, writable: true, configurable: true }); - ctor.dont = {}; - inject(ctor, this, true); - return arguments.length ? this.inject.apply(ctor, arguments) : ctor; - } - }, true).inject({ - has: has, - each: each, - - inject: function() { - for (var i = 0, l = arguments.length; i < l; i++) - inject(this, arguments[i]); - return this; - }, - - extend: function() { - var res = new (extend(this)); - return res.inject.apply(res, arguments); - }, - - each: function(iter, bind) { - return each(this, iter, bind); - }, - - clone: function() { - return clone(this); - }, - - statics: { - each: each, - clone: clone, - define: define, - describe: describe, - iterator: iterator, - - has: function(obj, name) { - return has.call(obj, name); - }, - - type: function(obj) { - return (obj || obj === 0) && (obj._type || typeof obj) || null; - }, - - check: function(obj) { - return !!(obj || obj === 0); - }, - - pick: function() { - for (var i = 0, l = arguments.length; i < l; i++) - if (arguments[i] !== undefined) - return arguments[i]; - return null; - }, - - stop: {} - } - }); -} - -this.Base = Base.inject({ - generics: true, - - clone: function() { - return new this.constructor(this); - }, - - toString: function() { - return '{ ' + Base.each(this, function(value, key) { - if (key.charAt(0) != '_') { - var type = typeof value; - this.push(key + ': ' + (type === 'number' - ? Base.formatNumber(value) - : type === 'string' ? "'" + value + "'" : value)); - } - }, []).join(', ') + ' }'; - }, - - statics: { - read: function(list, start, length) { - var start = start || 0, - length = length || list.length - start; - var obj = list[start]; - if (obj instanceof this - || this.prototype._readNull && obj == null && length <= 1) - return obj; - obj = new this(this.dont); - return obj.initialize.apply(obj, start > 0 || length < list.length - ? Array.prototype.slice.call(list, start, start + length) - : list) || obj; - }, - - readAll: function(list, start) { - var res = [], entry; - for (var i = start || 0, l = list.length; i < l; i++) { - res.push(Array.isArray(entry = list[i]) - ? this.read(entry, 0) - : this.read(list, i, 1)); - } - return res; - }, - - splice: function(list, items, index, remove) { - var amount = items && items.length, - append = index === undefined; - index = append ? list.length : index; - for (var i = 0; i < amount; i++) - items[i]._index = index + i; - if (append) { - list.push.apply(list, items); - return []; - } else { - var args = [index, remove]; - if (items) - args.push.apply(args, items); - var removed = list.splice.apply(list, args); - for (var i = 0, l = removed.length; i < l; i++) - delete removed[i]._index; - for (var i = index + amount, l = list.length; i < l; i++) - list[i]._index = i; - return removed; - } - }, - - merge: function() { - return Base.each(arguments, function(hash) { - Base.each(hash, function(value, key) { - this[key] = value; - }, this); - }, new Base(), true); - }, - - capitalize: function(str) { - return str.replace(/\b[a-z]/g, function(match) { - return match.toUpperCase(); - }); - }, - - camelize: function(str) { - return str.replace(/-(\w)/g, function(all, chr) { - return chr.toUpperCase(); - }); - }, - - hyphenate: function(str) { - return str.replace(/[a-z][A-Z0-9]|[0-9][a-zA-Z]|[A-Z]{2}[a-z]/g, - function(match) { - return match.charAt(0) + '-' + match.substring(1); - } - ).toLowerCase(); - }, - - formatNumber: function(num) { - return (Math.round(num * 100000) / 100000).toString(); - } - } -}); - -var PaperScope = this.PaperScope = Base.extend({ - - initialize: function(script) { - paper = this; - this.project = null; - this.projects = []; - this.tools = []; - this._id = script && (script.getAttribute('id') || script.src) - || ('paperscope-' + (PaperScope._id++)); - if (script) - script.setAttribute('id', this._id); - PaperScope._scopes[this._id] = this; - }, - - version: 0.22, - - getView: function() { - return this.project.view; - }, - - getTool: function() { - if (!this._tool) - this._tool = new Tool(); - return this._tool; - }, - - evaluate: function(code) { - var res = PaperScript.evaluate(code, this); - View.updateFocus(); - return res; - }, - - install: function(scope) { - var that = this; - Base.each(['project', 'view', 'tool'], function(key) { - Base.define(scope, key, { - configurable: true, - writable: true, - get: function() { - return that[key]; - } - }); - }); - for (var key in this) { - if (!/^(version|_id|load)/.test(key) && !(key in scope)) - scope[key] = this[key]; - } - }, - - setup: function(canvas) { - paper = this; - this.project = new Project(canvas); - }, - - clear: function() { - for (var i = this.projects.length - 1; i >= 0; i--) - this.projects[i].remove(); - for (var i = this.tools.length - 1; i >= 0; i--) - this.tools[i].remove(); - }, - - remove: function() { - this.clear(); - delete PaperScope._scopes[this._id]; - }, - - statics: { - _scopes: {}, - _id: 0, - - get: function(id) { - if (typeof id === 'object') - id = id.getAttribute('id'); - return this._scopes[id] || null; - } - } -}); - -var PaperScopeItem = Base.extend({ - - initialize: function(activate) { - this._scope = paper; - this._index = this._scope[this._list].push(this) - 1; - if (activate || !this._scope[this._reference]) - this.activate(); - }, - - activate: function() { - if (!this._scope) - return false; - this._scope[this._reference] = this; - return true; - }, - - remove: function() { - if (this._index == null) - return false; - Base.splice(this._scope[this._list], null, this._index, 1); - if (this._scope[this._reference] == this) - this._scope[this._reference] = null; - this._scope = null; - return true; - } -}); - -var Callback = { - attach: function(type, func) { - if (typeof type !== 'string') { - return Base.each(type, function(value, key) { - this.attach(key, value); - }, this); - } - var entry = this._eventTypes[type]; - if (!entry) - return this; - var handlers = this._handlers = this._handlers || {}; - handlers = handlers[type] = handlers[type] || []; - if (handlers.indexOf(func) == -1) { - handlers.push(func); - if (entry.install && handlers.length == 1) - entry.install.call(this, type); - } - return this; - }, - - detach: function(type, func) { - if (typeof type !== 'string') { - return Base.each(type, function(value, key) { - this.detach(key, value); - }, this); - } - var entry = this._eventTypes[type], - handlers = this._handlers && this._handlers[type], - index; - if (entry && handlers) { - if (!func || (index = handlers.indexOf(func)) != -1 - && handlers.length == 1) { - if (entry.uninstall) - entry.uninstall.call(this, type); - delete this._handlers[type]; - } else if (index != -1) { - handlers.splice(index, 1); - } - } - return this; - }, - - fire: function(type, event) { - var handlers = this._handlers && this._handlers[type]; - if (!handlers) - return false; - Base.each(handlers, function(func) { - if (func.call(this, event) === false && event && event.stop) - event.stop(); - }, this); - return true; - }, - - responds: function(type) { - return !!(this._handlers && this._handlers[type]); - }, - - statics: { - inject: function() { - for (var i = 0, l = arguments.length; i < l; i++) { - var src = arguments[i], - events = src._events; - if (events) { - var types = {}; - Base.each(events, function(entry, key) { - var isString = typeof entry === 'string', - name = isString ? entry : key, - part = Base.capitalize(name), - type = name.substring(2).toLowerCase(); - types[type] = isString ? {} : entry; - name = '_' + name; - src['get' + part] = function() { - return this[name]; - }; - src['set' + part] = function(func) { - if (func) { - this.attach(type, func); - } else if (this[name]) { - this.detach(type, this[name]); - } - this[name] = func; - }; - }); - src._eventTypes = types; - } - this.base(src); - } - return this; - } - } -}; - -var Point = this.Point = Base.extend({ - initialize: function(arg0, arg1) { - if (arg1 !== undefined) { - this.x = arg0; - this.y = arg1; - } else if (arg0 !== undefined) { - if (arg0 == null) { - this.x = this.y = 0; - } else if (arg0.x !== undefined) { - this.x = arg0.x; - this.y = arg0.y; - } else if (arg0.width !== undefined) { - this.x = arg0.width; - this.y = arg0.height; - } else if (Array.isArray(arg0)) { - this.x = arg0[0]; - this.y = arg0.length > 1 ? arg0[1] : arg0[0]; - } else if (arg0.angle !== undefined) { - this.x = arg0.length; - this.y = 0; - this.setAngle(arg0.angle); - } else if (typeof arg0 === 'number') { - this.x = this.y = arg0; - } else { - this.x = this.y = 0; - } - } else { - this.x = this.y = 0; - } - }, - - set: function(x, y) { - this.x = x; - this.y = y; - return this; - }, - - clone: function() { - return Point.create(this.x, this.y); - }, - - toString: function() { - var format = Base.formatNumber; - return '{ x: ' + format(this.x) + ', y: ' + format(this.y) + ' }'; - }, - - add: function(point) { - point = Point.read(arguments); - return Point.create(this.x + point.x, this.y + point.y); - }, - - subtract: function(point) { - point = Point.read(arguments); - return Point.create(this.x - point.x, this.y - point.y); - }, - - multiply: function(point) { - point = Point.read(arguments); - return Point.create(this.x * point.x, this.y * point.y); - }, - - divide: function(point) { - point = Point.read(arguments); - return Point.create(this.x / point.x, this.y / point.y); - }, - - modulo: function(point) { - point = Point.read(arguments); - return Point.create(this.x % point.x, this.y % point.y); - }, - - negate: function() { - return Point.create(-this.x, -this.y); - }, - - transform: function(matrix) { - return matrix ? matrix._transformPoint(this) : this; - }, - - getDistance: function(point, squared) { - point = Point.read(arguments); - var x = point.x - this.x, - y = point.y - this.y, - d = x * x + y * y; - return squared ? d : Math.sqrt(d); - }, - - getLength: function() { - var l = this.x * this.x + this.y * this.y; - return arguments[0] ? l : Math.sqrt(l); - }, - - setLength: function(length) { - if (this.isZero()) { - var angle = this._angle || 0; - this.set( - Math.cos(angle) * length, - Math.sin(angle) * length - ); - } else { - var scale = length / this.getLength(); - if (scale == 0) - this.getAngle(); - this.set( - this.x * scale, - this.y * scale - ); - } - return this; - }, - - normalize: function(length) { - if (length === undefined) - length = 1; - var current = this.getLength(), - scale = current != 0 ? length / current : 0, - point = Point.create(this.x * scale, this.y * scale); - point._angle = this._angle; - return point; - }, - - getAngle: function() { - return this.getAngleInRadians(arguments[0]) * 180 / Math.PI; - }, - - setAngle: function(angle) { - angle = this._angle = angle * Math.PI / 180; - if (!this.isZero()) { - var length = this.getLength(); - this.set( - Math.cos(angle) * length, - Math.sin(angle) * length - ); - } - return this; - }, - - getAngleInRadians: function() { - if (arguments[0] === undefined) { - if (this._angle == null) - this._angle = Math.atan2(this.y, this.x); - return this._angle; - } else { - var point = Point.read(arguments), - div = this.getLength() * point.getLength(); - if (div == 0) { - return NaN; - } else { - return Math.acos(this.dot(point) / div); - } - } - }, - - getAngleInDegrees: function() { - return this.getAngle(arguments[0]); - }, - - getQuadrant: function() { - return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; - }, - - getDirectedAngle: function(point) { - point = Point.read(arguments); - return Math.atan2(this.cross(point), this.dot(point)) * 180 / Math.PI; - }, - - rotate: function(angle, center) { - angle = angle * Math.PI / 180; - var point = center ? this.subtract(center) : this, - s = Math.sin(angle), - c = Math.cos(angle); - point = Point.create( - point.x * c - point.y * s, - point.y * c + point.x * s - ); - return center ? point.add(center) : point; - }, - - equals: function(point) { - point = Point.read(arguments); - return this.x == point.x && this.y == point.y; - }, - - isInside: function(rect) { - return rect.contains(this); - }, - - isClose: function(point, tolerance) { - return this.getDistance(point) < tolerance; - }, - - isColinear: function(point) { - return this.cross(point) < Numerical.TOLERANCE; - }, - - isOrthogonal: function(point) { - return this.dot(point) < Numerical.TOLERANCE; - }, - - isZero: function() { - return this.x == 0 && this.y == 0; - }, - - isNaN: function() { - return isNaN(this.x) || isNaN(this.y); - }, - - dot: function(point) { - point = Point.read(arguments); - return this.x * point.x + this.y * point.y; - }, - - cross: function(point) { - point = Point.read(arguments); - return this.x * point.y - this.y * point.x; - }, - - project: function(point) { - point = Point.read(arguments); - if (point.isZero()) { - return Point.create(0, 0); - } else { - var scale = this.dot(point) / point.dot(point); - return Point.create( - point.x * scale, - point.y * scale - ); - } - }, - - statics: { - create: function(x, y) { - var point = new Point(Point.dont); - point.x = x; - point.y = y; - return point; - }, - - min: function(point1, point2) { - point1 = Point.read(arguments, 0, 1); - point2 = Point.read(arguments, 1, 1); - return Point.create( - Math.min(point1.x, point2.x), - Math.min(point1.y, point2.y) - ); - }, - - max: function(point1, point2) { - point1 = Point.read(arguments, 0, 1); - point2 = Point.read(arguments, 1, 1); - return Point.create( - Math.max(point1.x, point2.x), - Math.max(point1.y, point2.y) - ); - }, - - random: function() { - return Point.create(Math.random(), Math.random()); - } - } -}, new function() { - - return Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { - var op = Math[name]; - this[name] = function() { - return Point.create(op(this.x), op(this.y)); - }; - }, {}); -}); - -var LinkedPoint = Point.extend({ - set: function(x, y, dontNotify) { - this._x = x; - this._y = y; - if (!dontNotify) - this._owner[this._setter](this); - return this; - }, - - getX: function() { - return this._x; - }, - - setX: function(x) { - this._x = x; - this._owner[this._setter](this); - }, - - getY: function() { - return this._y; - }, - - setY: function(y) { - this._y = y; - this._owner[this._setter](this); - }, - - statics: { - create: function(owner, setter, x, y, dontLink) { - if (dontLink) - return Point.create(x, y); - var point = new LinkedPoint(LinkedPoint.dont); - point._x = x; - point._y = y; - point._owner = owner; - point._setter = setter; - return point; - } - } -}); - -var Size = this.Size = Base.extend({ - initialize: function(arg0, arg1) { - if (arg1 !== undefined) { - this.width = arg0; - this.height = arg1; - } else if (arg0 !== undefined) { - if (arg0 == null) { - this.width = this.height = 0; - } else if (arg0.width !== undefined) { - this.width = arg0.width; - this.height = arg0.height; - } else if (arg0.x !== undefined) { - this.width = arg0.x; - this.height = arg0.y; - } else if (Array.isArray(arg0)) { - this.width = arg0[0]; - this.height = arg0.length > 1 ? arg0[1] : arg0[0]; - } else if (typeof arg0 === 'number') { - this.width = this.height = arg0; - } else { - this.width = this.height = 0; - } - } else { - this.width = this.height = 0; - } - }, - - toString: function() { - var format = Base.formatNumber; - return '{ width: ' + format(this.width) - + ', height: ' + format(this.height) + ' }'; - }, - - set: function(width, height) { - this.width = width; - this.height = height; - return this; - }, - - clone: function() { - return Size.create(this.width, this.height); - }, - - add: function(size) { - size = Size.read(arguments); - return Size.create(this.width + size.width, this.height + size.height); - }, - - subtract: function(size) { - size = Size.read(arguments); - return Size.create(this.width - size.width, this.height - size.height); - }, - - multiply: function(size) { - size = Size.read(arguments); - return Size.create(this.width * size.width, this.height * size.height); - }, - - divide: function(size) { - size = Size.read(arguments); - return Size.create(this.width / size.width, this.height / size.height); - }, - - modulo: function(size) { - size = Size.read(arguments); - return Size.create(this.width % size.width, this.height % size.height); - }, - - negate: function() { - return Size.create(-this.width, -this.height); - }, - - equals: function(size) { - size = Size.read(arguments); - return this.width == size.width && this.height == size.height; - }, - - isZero: function() { - return this.width == 0 && this.height == 0; - }, - - isNaN: function() { - return isNaN(this.width) || isNaN(this.height); - }, - - statics: { - create: function(width, height) { - return new Size(Size.dont).set(width, height); - }, - - min: function(size1, size2) { - return Size.create( - Math.min(size1.width, size2.width), - Math.min(size1.height, size2.height)); - }, - - max: function(size1, size2) { - return Size.create( - Math.max(size1.width, size2.width), - Math.max(size1.height, size2.height)); - }, - - random: function() { - return Size.create(Math.random(), Math.random()); - } - } -}, new function() { - - return Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { - var op = Math[name]; - this[name] = function() { - return Size.create(op(this.width), op(this.height)); - }; - }, {}); -}); - -var LinkedSize = Size.extend({ - set: function(width, height, dontNotify) { - this._width = width; - this._height = height; - if (!dontNotify) - this._owner[this._setter](this); - return this; - }, - - getWidth: function() { - return this._width; - }, - - setWidth: function(width) { - this._width = width; - this._owner[this._setter](this); - }, - - getHeight: function() { - return this._height; - }, - - setHeight: function(height) { - this._height = height; - this._owner[this._setter](this); - }, - - statics: { - create: function(owner, setter, width, height, dontLink) { - if (dontLink) - return Size.create(width, height); - var size = new LinkedSize(LinkedSize.dont); - size._width = width; - size._height = height; - size._owner = owner; - size._setter = setter; - return size; - } - } -}); - -var Rectangle = this.Rectangle = Base.extend({ - initialize: function(arg0, arg1, arg2, arg3) { - if (arguments.length == 4) { - this.x = arg0; - this.y = arg1; - this.width = arg2; - this.height = arg3; - } else if (arguments.length == 2) { - if (arg1 && arg1.x !== undefined) { - var point1 = Point.read(arguments, 0, 1); - var point2 = Point.read(arguments, 1, 1); - this.x = point1.x; - this.y = point1.y; - this.width = point2.x - point1.x; - this.height = point2.y - point1.y; - if (this.width < 0) { - this.x = point2.x; - this.width = -this.width; - } - if (this.height < 0) { - this.y = point2.y; - this.height = -this.height; - } - } else { - var point = Point.read(arguments, 0, 1); - var size = Size.read(arguments, 1, 1); - this.x = point.x; - this.y = point.y; - this.width = size.width; - this.height = size.height; - } - } else if (arg0) { - this.x = arg0.x || 0; - this.y = arg0.y || 0; - this.width = arg0.width || 0; - this.height = arg0.height || 0; - } else { - this.x = this.y = this.width = this.height = 0; - } - }, - - set: function(x, y, width, height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - return this; - }, - - getPoint: function() { - return LinkedPoint.create(this, 'setPoint', this.x, this.y, - arguments[0]); - }, - - setPoint: function(point) { - point = Point.read(arguments); - this.x = point.x; - this.y = point.y; - return this; - }, - - getSize: function() { - return LinkedSize.create(this, 'setSize', this.width, this.height, - arguments[0]); - }, - - setSize: function(size) { - size = Size.read(arguments); - this.width = size.width; - this.height = size.height; - return this; - }, - - getLeft: function() { - return this.x; - }, - - setLeft: function(left) { - this.width -= left - this.x; - this.x = left; - return this; - }, - - getTop: function() { - return this.y; - }, - - setTop: function(top) { - this.height -= top - this.y; - this.y = top; - return this; - }, - - getRight: function() { - return this.x + this.width; - }, - - setRight: function(right) { - this.width = right - this.x; - return this; - }, - - getBottom: function() { - return this.y + this.height; - }, - - setBottom: function(bottom) { - this.height = bottom - this.y; - return this; - }, - - getCenterX: function() { - return this.x + this.width * 0.5; - }, - - setCenterX: function(x) { - this.x = x - this.width * 0.5; - return this; - }, - - getCenterY: function() { - return this.y + this.height * 0.5; - }, - - setCenterY: function(y) { - this.y = y - this.height * 0.5; - return this; - }, - - getCenter: function() { - return LinkedPoint.create(this, 'setCenter', - this.getCenterX(), this.getCenterY(), arguments[0]); - }, - - setCenter: function(point) { - point = Point.read(arguments); - return this.setCenterX(point.x).setCenterY(point.y); - }, - - equals: function(rect) { - rect = Rectangle.read(arguments); - return this.x == rect.x && this.y == rect.y - && this.width == rect.width && this.height == rect.height; - }, - - isEmpty: function() { - return this.width == 0 || this.height == 0; - }, - - toString: function() { - var format = Base.formatNumber; - return '{ x: ' + format(this.x) - + ', y: ' + format(this.y) - + ', width: ' + format(this.width) - + ', height: ' + format(this.height) - + ' }'; - }, - - contains: function(arg) { - return arg && arg.width !== undefined - || (Array.isArray(arg) ? arg : arguments).length == 4 - ? this._containsRectangle(Rectangle.read(arguments)) - : this._containsPoint(Point.read(arguments)); - }, - - _containsPoint: function(point) { - var x = point.x, - y = point.y; - return x >= this.x && y >= this.y - && x <= this.x + this.width - && y <= this.y + this.height; - }, - - _containsRectangle: function(rect) { - var x = rect.x, - y = rect.y; - return x >= this.x && y >= this.y - && x + rect.width <= this.x + this.width - && y + rect.height <= this.y + this.height; - }, - - intersects: function(rect) { - rect = Rectangle.read(arguments); - return rect.x + rect.width > this.x - && rect.y + rect.height > this.y - && rect.x < this.x + this.width - && rect.y < this.y + this.height; - }, - - intersect: function(rect) { - rect = Rectangle.read(arguments); - var x1 = Math.max(this.x, rect.x), - y1 = Math.max(this.y, rect.y), - x2 = Math.min(this.x + this.width, rect.x + rect.width), - y2 = Math.min(this.y + this.height, rect.y + rect.height); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - unite: function(rect) { - rect = Rectangle.read(arguments); - var x1 = Math.min(this.x, rect.x), - y1 = Math.min(this.y, rect.y), - x2 = Math.max(this.x + this.width, rect.x + rect.width), - y2 = Math.max(this.y + this.height, rect.y + rect.height); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - include: function(point) { - point = Point.read(arguments); - var x1 = Math.min(this.x, point.x), - y1 = Math.min(this.y, point.y), - x2 = Math.max(this.x + this.width, point.x), - y2 = Math.max(this.y + this.height, point.y); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - expand: function(hor, ver) { - if (ver === undefined) - ver = hor; - return Rectangle.create(this.x - hor / 2, this.y - ver / 2, - this.width + hor, this.height + ver); - }, - - scale: function(hor, ver) { - return this.expand(this.width * hor - this.width, - this.height * (ver === undefined ? hor : ver) - this.height); - }, - - statics: { - create: function(x, y, width, height) { - return new Rectangle(Rectangle.dont).set(x, y, width, height); - } - } -}, new function() { - return Base.each([ - ['Top', 'Left'], ['Top', 'Right'], - ['Bottom', 'Left'], ['Bottom', 'Right'], - ['Left', 'Center'], ['Top', 'Center'], - ['Right', 'Center'], ['Bottom', 'Center'] - ], - function(parts, index) { - var part = parts.join(''); - var xFirst = /^[RL]/.test(part); - if (index >= 4) - parts[1] += xFirst ? 'Y' : 'X'; - var x = parts[xFirst ? 0 : 1], - y = parts[xFirst ? 1 : 0], - getX = 'get' + x, - getY = 'get' + y, - setX = 'set' + x, - setY = 'set' + y, - get = 'get' + part, - set = 'set' + part; - this[get] = function() { - return LinkedPoint.create(this, set, - this[getX](), this[getY](), arguments[0]); - }; - this[set] = function(point) { - point = Point.read(arguments); - return this[setX](point.x)[setY](point.y); - }; - }, {}); -}); - -var LinkedRectangle = Rectangle.extend({ - set: function(x, y, width, height, dontNotify) { - this._x = x; - this._y = y; - this._width = width; - this._height = height; - if (!dontNotify) - this._owner[this._setter](this); - return this; - }, - - statics: { - create: function(owner, setter, x, y, width, height) { - var rect = new LinkedRectangle(LinkedRectangle.dont).set( - x, y, width, height, true); - rect._owner = owner; - rect._setter = setter; - return rect; - } - } -}, new function() { - var proto = Rectangle.prototype; - - return Base.each(['x', 'y', 'width', 'height'], function(key) { - var part = Base.capitalize(key); - var internal = '_' + key; - this['get' + part] = function() { - return this[internal]; - }; - - this['set' + part] = function(value) { - this[internal] = value; - if (!this._dontNotify) - this._owner[this._setter](this); - }; - }, Base.each(['Point', 'Size', 'Center', - 'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY', - 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', - 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], - function(key) { - var name = 'set' + key; - this[name] = function(value) { - this._dontNotify = true; - proto[name].apply(this, arguments); - delete this._dontNotify; - this._owner[this._setter](this); - return this; - }; - }, {}) - ); -}); - -var Matrix = this.Matrix = Base.extend({ - initialize: function(arg) { - var count = arguments.length, - ok = true; - if (count == 6) { - this.set.apply(this, arguments); - } else if (count == 1) { - if (arg instanceof Matrix) { - this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty); - } else if (Array.isArray(arg)) { - this.set.apply(this, arg); - } else { - ok = false; - } - } else if (count == 0) { - this.setIdentity(); - } else { - ok = false; - } - if (!ok) - throw new Error('Unsupported matrix parameters'); - }, - - clone: function() { - return Matrix.create(this._a, this._c, this._b, this._d, - this._tx, this._ty); - }, - - set: function(a, c, b, d, tx, ty) { - this._a = a; - this._c = c; - this._b = b; - this._d = d; - this._tx = tx; - this._ty = ty; - return this; - }, - - setIdentity: function() { - this._a = this._d = 1; - this._c = this._b = this._tx = this._ty = 0; - return this; - }, - - scale: function( hor, ver, center) { - if (arguments.length < 2 || typeof ver === 'object') { - center = Point.read(arguments, 1); - ver = hor; - } else { - center = Point.read(arguments, 2); - } - if (center) - this.translate(center); - this._a *= hor; - this._c *= hor; - this._b *= ver; - this._d *= ver; - if (center) - this.translate(center.negate()); - return this; - }, - - translate: function(point) { - point = Point.read(arguments); - var x = point.x, y = point.y; - this._tx += x * this._a + y * this._b; - this._ty += x * this._c + y * this._d; - return this; - }, - - rotate: function(angle, center) { - return this.concatenate( - Matrix.getRotateInstance.apply(Matrix, arguments)); - }, - - shear: function( hor, ver, center) { - if (arguments.length < 2 || typeof ver === 'object') { - center = Point.read(arguments, 1); - ver = hor; - } else { - center = Point.read(arguments, 2); - } - if (center) - this.translate(center); - var a = this._a, - c = this._c; - this._a += ver * this._b; - this._c += ver * this._d; - this._b += hor * a; - this._d += hor * c; - if (center) - this.translate(center.negate()); - return this; - }, - - toString: function() { - var format = Base.formatNumber; - return '[[' + [format(this._a), format(this._b), - format(this._tx)].join(', ') + '], [' - + [format(this._c), format(this._d), - format(this._ty)].join(', ') + ']]'; - }, - - getValues: function() { - return [ this._a, this._c, this._b, this._d, this._tx, this._ty ]; - }, - - concatenate: function(mx) { - var a = this._a, - b = this._b, - c = this._c, - d = this._d; - this._a = mx._a * a + mx._c * b; - this._b = mx._b * a + mx._d * b; - this._c = mx._a * c + mx._c * d; - this._d = mx._b * c + mx._d * d; - this._tx += mx._tx * a + mx._ty * b; - this._ty += mx._tx * c + mx._ty * d; - return this; - }, - - preConcatenate: function(mx) { - var a = this._a, - b = this._b, - c = this._c, - d = this._d, - tx = this._tx, - ty = this._ty; - this._a = mx._a * a + mx._b * c; - this._b = mx._a * b + mx._b * d; - this._c = mx._c * a + mx._d * c; - this._d = mx._c * b + mx._d * d; - this._tx = mx._a * tx + mx._b * ty + mx._tx; - this._ty = mx._c * tx + mx._d * ty + mx._ty; - return this; - }, - - transform: function( src, srcOff, dst, dstOff, numPts) { - return arguments.length < 5 - ? this._transformPoint(Point.read(arguments)) - : this._transformCoordinates(src, srcOff, dst, dstOff, numPts); - }, - - _transformPoint: function(point, dest, dontNotify) { - var x = point.x, - y = point.y; - if (!dest) - dest = new Point(Point.dont); - return dest.set( - x * this._a + y * this._b + this._tx, - x * this._c + y * this._d + this._ty, - dontNotify - ); - }, - - _transformCoordinates: function(src, srcOff, dst, dstOff, numPts) { - var i = srcOff, j = dstOff, - srcEnd = srcOff + 2 * numPts; - while (i < srcEnd) { - var x = src[i++]; - var y = src[i++]; - dst[j++] = x * this._a + y * this._b + this._tx; - dst[j++] = x * this._c + y * this._d + this._ty; - } - return dst; - }, - - _transformCorners: function(rect) { - var x1 = rect.x, - y1 = rect.y, - x2 = x1 + rect.width, - y2 = y1 + rect.height, - coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ]; - return this._transformCoordinates(coords, 0, coords, 0, 4); - }, - - _transformBounds: function(bounds, dest, dontNotify) { - var coords = this._transformCorners(bounds), - min = coords.slice(0, 2), - max = coords.slice(0); - for (var i = 2; i < 8; i++) { - var val = coords[i], - j = i & 1; - if (val < min[j]) - min[j] = val; - else if (val > max[j]) - max[j] = val; - } - if (!dest) - dest = new Rectangle(Rectangle.dont); - return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1], - dontNotify); - }, - - inverseTransform: function(point) { - return this._inverseTransform(Point.read(arguments)); - }, - - _getDeterminant: function() { - var det = this._a * this._d - this._b * this._c; - return isFinite(det) && Math.abs(det) > Numerical.EPSILON - && isFinite(this._tx) && isFinite(this._ty) - ? det : null; - }, - - _inverseTransform: function(point, dest, dontNotify) { - var det = this._getDeterminant(); - if (!det) - return null; - var x = point.x - this._tx, - y = point.y - this._ty; - if (!dest) - dest = new Point(Point.dont); - return dest.set( - (x * this._d - y * this._b) / det, - (y * this._a - x * this._c) / det, - dontNotify - ); - }, - - getTranslation: function() { - return Point.create(this._tx, this._ty); - }, - - getScaling: function() { - var hor = Math.sqrt(this._a * this._a + this._c * this._c), - ver = Math.sqrt(this._b * this._b + this._d * this._d); - return Point.create(this._a < 0 ? -hor : hor, this._b < 0 ? -ver : ver); - }, - - getRotation: function() { - var angle1 = -Math.atan2(this._b, this._d), - angle2 = Math.atan2(this._c, this._a); - return Math.abs(angle1 - angle2) < Numerical.TOLERANCE - ? angle1 * 180 / Math.PI : undefined; - }, - - equals: function(mx) { - return this._a == mx._a && this._b == mx._b && this._c == mx._c - && this._d == mx._d && this._tx == mx._tx && this._ty == mx._ty; - }, - - isIdentity: function() { - return this._a == 1 && this._c == 0 && this._b == 0 && this._d == 1 - && this._tx == 0 && this._ty == 0; - }, - - isInvertible: function() { - return !!this._getDeterminant(); - }, - - isSingular: function() { - return !this._getDeterminant(); - }, - - createInverse: function() { - var det = this._getDeterminant(); - return det && Matrix.create( - this._d / det, - -this._c / det, - -this._b / det, - this._a / det, - (this._b * this._ty - this._d * this._tx) / det, - (this._c * this._tx - this._a * this._ty) / det); - }, - - createShiftless: function() { - return Matrix.create(this._a, this._c, this._b, this._d, 0, 0); - }, - - setToScale: function(hor, ver) { - return this.set(hor, 0, 0, ver, 0, 0); - }, - - setToTranslation: function(delta) { - delta = Point.read(arguments); - return this.set(1, 0, 0, 1, delta.x, delta.y); - }, - - setToShear: function(hor, ver) { - return this.set(1, ver, hor, 1, 0, 0); - }, - - setToRotation: function(angle, center) { - center = Point.read(arguments, 1); - angle = angle * Math.PI / 180; - var x = center.x, - y = center.y, - cos = Math.cos(angle), - sin = Math.sin(angle); - return this.set(cos, sin, -sin, cos, - x - x * cos + y * sin, - y - x * sin - y * cos); - }, - - applyToContext: function(ctx, reset) { - ctx[reset ? 'setTransform' : 'transform']( - this._a, this._c, this._b, this._d, this._tx, this._ty); - return this; - }, - - statics: { - create: function(a, c, b, d, tx, ty) { - return new Matrix(Matrix.dont).set(a, c, b, d, tx, ty); - }, - - getScaleInstance: function(hor, ver) { - var mx = new Matrix(); - return mx.setToScale.apply(mx, arguments); - }, - - getTranslateInstance: function(delta) { - var mx = new Matrix(); - return mx.setToTranslation.apply(mx, arguments); - }, - - getShearInstance: function(hor, ver, center) { - var mx = new Matrix(); - return mx.setToShear.apply(mx, arguments); - }, - - getRotateInstance: function(angle, center) { - var mx = new Matrix(); - return mx.setToRotation.apply(mx, arguments); - } - } -}, new function() { - return Base.each({ - scaleX: '_a', - scaleY: '_d', - translateX: '_tx', - translateY: '_ty', - shearX: '_b', - shearY: '_c' - }, function(prop, name) { - name = Base.capitalize(name); - this['get' + name] = function() { - return this[prop]; - }; - this['set' + name] = function(value) { - this[prop] = value; - }; - }, {}); -}); - -var Line = this.Line = Base.extend({ - initialize: function(point1, point2, infinite) { - point1 = Point.read(arguments, 0, 1); - point2 = Point.read(arguments, 1, 1); - if (arguments.length == 3) { - this.point = point1; - this.vector = point2.subtract(point1); - this.infinite = infinite; - } else { - this.point = point1; - this.vector = point2; - this.infinite = true; - } - }, - - intersect: function(line) { - var cross = this.vector.cross(line.vector); - if (Math.abs(cross) <= Numerical.EPSILON) - return null; - var v = line.point.subtract(this.point), - t1 = v.cross(line.vector) / cross, - t2 = v.cross(this.vector) / cross; - return (this.infinite || 0 <= t1 && t1 <= 1) - && (line.infinite || 0 <= t2 && t2 <= 1) - ? this.point.add(this.vector.multiply(t1)) : null; - }, - - getSide: function(point) { - var v1 = this.vector, - v2 = point.subtract(this.point), - ccw = v2.cross(v1); - if (ccw == 0) { - ccw = v2.dot(v1); - if (ccw > 0) { - ccw = v2.subtract(v1).dot(v1); - if (ccw < 0) - ccw = 0; - } - } - return ccw < 0 ? -1 : ccw > 0 ? 1 : 0; - }, - - getDistance: function(point) { - var m = this.vector.y / this.vector.x, - b = this.point.y - (m * this.point.x); - var dist = Math.abs(point.y - (m * point.x) - b) / Math.sqrt(m * m + 1); - return this.infinite ? dist : Math.min(dist, - point.getDistance(this.point), - point.getDistance(this.point.add(this.vector))); - } -}); - -var Project = this.Project = PaperScopeItem.extend({ - _list: 'projects', - _reference: 'project', - - initialize: function(view) { - this.base(true); - this._currentStyle = new PathStyle(); - this._selectedItems = {}; - this._selectedItemCount = 0; - this.layers = []; - this.symbols = []; - this.activeLayer = new Layer(); - if (view) - this.view = view instanceof View ? view : View.create(view); - }, - - _needsRedraw: function() { - if (this.view) - this.view._redrawNeeded = true; - }, - - remove: function() { - if (!this.base()) - return false; - if (this.view) - this.view.remove(); - return true; - }, - - getCurrentStyle: function() { - return this._currentStyle; - }, - - setCurrentStyle: function(style) { - this._currentStyle.initialize(style); - }, - - getIndex: function() { - return this._index; - }, - - getSelectedItems: function() { - var items = []; - Base.each(this._selectedItems, function(item) { - items.push(item); - }); - return items; - }, - - _updateSelection: function(item) { - if (item._selected) { - this._selectedItemCount++; - this._selectedItems[item._id] = item; - } else { - this._selectedItemCount--; - delete this._selectedItems[item._id]; - } - }, - - selectAll: function() { - for (var i = 0, l = this.layers.length; i < l; i++) - this.layers[i].setSelected(true); - }, - - deselectAll: function() { - for (var i in this._selectedItems) - this._selectedItems[i].setSelected(false); - }, - - hitTest: function(point, options) { - options = HitResult.getOptions(point, options); - point = options.point; - for (var i = this.layers.length - 1; i >= 0; i--) { - var res = this.layers[i].hitTest(point, options); - if (res) return res; - } - return null; - }, - - draw: function(ctx, matrix) { - ctx.save(); - if (!matrix.isIdentity()) - matrix.applyToContext(ctx); - var param = { offset: new Point(0, 0) }; - for (var i = 0, l = this.layers.length; i < l; i++) - Item.draw(this.layers[i], ctx, param); - ctx.restore(); - - if (this._selectedItemCount > 0) { - ctx.save(); - ctx.strokeWidth = 1; - ctx.strokeStyle = ctx.fillStyle = '#009dec'; - var matrices = {}; - function getGlobalMatrix(item, mx, cached) { - var cache = cached && matrices[item._id]; - if (cache) { - mx.concatenate(cache); - return mx; - } - if (item._parent) { - getGlobalMatrix(item._parent, mx, true); - if (!item._matrix.isIdentity()) - mx.concatenate(item._matrix); - } else { - mx.initialize(item._matrix); - } - if (cached) - matrices[item._id] = mx.clone(); - return mx; - } - for (var id in this._selectedItems) { - var item = this._selectedItems[id]; - item.drawSelected(ctx, getGlobalMatrix(item, matrix.clone())); - } - ctx.restore(); - } - } -}); - -var Symbol = this.Symbol = Base.extend({ - initialize: function(item) { - this.project = paper.project; - this.project.symbols.push(this); - this.setDefinition(item); - this._instances = {}; - }, - - _changed: function(flags) { - Base.each(this._instances, function(item) { - item._changed(flags); - }); - }, - - getDefinition: function() { - return this._definition; - }, - - setDefinition: function(item) { - if (item._parentSymbol) - item = item.clone(); - if (this._definition) - delete this._definition._parentSymbol; - this._definition = item; - item.remove(); - item.setPosition(new Point()); - item._parentSymbol = this; - this._changed(Change.GEOMETRY); - }, - - place: function(position) { - return new PlacedSymbol(this, position); - }, - - clone: function() { - return new Symbol(this._definition.clone()); - } -}); - -var ChangeFlag = { - APPEARANCE: 1, - HIERARCHY: 2, - GEOMETRY: 4, - STROKE: 8, - STYLE: 16, - ATTRIBUTE: 32, - CONTENT: 64, - PIXELS: 128, - CLIPPING: 256 -}; - -var Change = { - HIERARCHY: ChangeFlag.HIERARCHY | ChangeFlag.APPEARANCE, - GEOMETRY: ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE, - STROKE: ChangeFlag.STROKE | ChangeFlag.STYLE | ChangeFlag.APPEARANCE, - STYLE: ChangeFlag.STYLE | ChangeFlag.APPEARANCE, - ATTRIBUTE: ChangeFlag.ATTRIBUTE | ChangeFlag.APPEARANCE, - CONTENT: ChangeFlag.CONTENT | ChangeFlag.APPEARANCE, - PIXELS: ChangeFlag.PIXELS | ChangeFlag.APPEARANCE -}; - -var Item = this.Item = Base.extend(Callback, { - _events: new function() { - - var mouseFlags = { - mousedown: { - mousedown: 1, - mousedrag: 1, - click: 1, - doubleclick: 1 - }, - mouseup: { - mouseup: 1, - mousedrag: 1, - click: 1, - doubleclick: 1 - }, - mousemove: { - mousedrag: 1, - mousemove: 1, - mouseenter: 1, - mouseleave: 1 - } - }; - - var mouseEvent = { - install: function(type) { - var counters = this._project.view._eventCounters; - if (counters) { - for (var key in mouseFlags) { - counters[key] = (counters[key] || 0) - + (mouseFlags[key][type] || 0); - } - } - }, - uninstall: function(type) { - var counters = this._project.view._eventCounters; - if (counters) { - for (var key in mouseFlags) - counters[key] -= mouseFlags[key][type] || 0; - } - } - }; - - var onFrameItems = []; - function onFrame(event) { - for (var i = 0, l = onFrameItems.length; i < l; i++) - onFrameItems[i].fire('frame', event); - } - - return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick', - 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'], - function(name) { - this[name] = mouseEvent; - }, { - onFrame: { - install: function() { - if (!onFrameItems.length) - this._project.view.attach('frame', onFrame); - onFrameItems.push(this); - }, - uninstall: function() { - onFrameItems.splice(onFrameItems.indexOf(this), 1); - if (!onFrameItems.length) - this._project.view.detach('frame', onFrame); - } - } - }); - }, - - initialize: function(pointOrMatrix) { - this._id = ++Item._id; - if (!this._project) - paper.project.activeLayer.addChild(this); - if (!this._style) - this._style = PathStyle.create(this); - this.setStyle(this._project.getCurrentStyle()); - this._matrix = pointOrMatrix !== undefined - ? pointOrMatrix instanceof Matrix - ? pointOrMatrix.clone() - : new Matrix().translate(Point.read(arguments, 0)) - : new Matrix(); - }, - - _changed: function(flags) { - if (flags & ChangeFlag.GEOMETRY) { - delete this._bounds; - delete this._position; - } - if (this._parent - && (flags & (ChangeFlag.GEOMETRY | ChangeFlag.STROKE))) { - this._parent._clearBoundsCache(); - } - if (flags & ChangeFlag.HIERARCHY) { - this._clearBoundsCache(); - } - if (flags & ChangeFlag.APPEARANCE) { - this._project._needsRedraw(); - } - if (this._parentSymbol) - this._parentSymbol._changed(flags); - if (this._project._changes) { - var entry = this._project._changesById[this._id]; - if (entry) { - entry.flags |= flags; - } else { - entry = { item: this, flags: flags }; - this._project._changesById[this._id] = entry; - this._project._changes.push(entry); - } - } - }, - - getId: function() { - return this._id; - }, - - getName: function() { - return this._name; - }, - - setName: function(name) { - - if (this._name) - this._removeFromNamed(); - this._name = name || undefined; - if (name && this._parent) { - var children = this._parent._children, - namedChildren = this._parent._namedChildren; - (namedChildren[name] = namedChildren[name] || []).push(this); - children[name] = this; - } - this._changed(ChangeFlag.ATTRIBUTE); - }, - - statics: { - _id: 0 - } -}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'], - function(name) { - var part = Base.capitalize(name), - name = '_' + name; - this['get' + part] = function() { - return this[name]; - }; - this['set' + part] = function(value) { - if (value != this[name]) { - this[name] = value; - this._changed(name === '_locked' - ? ChangeFlag.ATTRIBUTE : Change.ATTRIBUTE); - } - }; -}, {}), { - - _locked: false, - - _visible: true, - - _blendMode: 'normal', - - _opacity: 1, - - _guide: false, - - isSelected: function() { - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) - if (this._children[i].isSelected()) - return true; - } - return this._selected; - }, - - setSelected: function(selected ) { - if (this._children && !arguments[1]) { - for (var i = 0, l = this._children.length; i < l; i++) - this._children[i].setSelected(selected); - } else if ((selected = !!selected) != this._selected) { - this._selected = selected; - this._project._updateSelection(this); - this._changed(Change.ATTRIBUTE); - } - }, - - _selected: false, - - isFullySelected: function() { - if (this._children && this._selected) { - for (var i = 0, l = this._children.length; i < l; i++) - if (!this._children[i].isFullySelected()) - return false; - return true; - } - return this._selected; - }, - - setFullySelected: function(selected) { - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) - this._children[i].setFullySelected(selected); - } - this.setSelected(selected, true); - }, - - isClipMask: function() { - return this._clipMask; - }, - - setClipMask: function(clipMask) { - if (this._clipMask != (clipMask = !!clipMask)) { - this._clipMask = clipMask; - if (clipMask) { - this.setFillColor(null); - this.setStrokeColor(null); - } - this._changed(Change.ATTRIBUTE); - if (this._parent) - this._parent._changed(ChangeFlag.CLIPPING); - } - }, - - _clipMask: false, - - getPosition: function() { - var pos = this._position - || (this._position = this.getBounds().getCenter(true)); - return arguments[0] ? pos - : LinkedPoint.create(this, 'setPosition', pos.x, pos.y); - }, - - setPosition: function(point) { - this.translate(Point.read(arguments).subtract(this.getPosition(true))); - }, - - getMatrix: function() { - return this._matrix; - }, - - setMatrix: function(matrix) { - this._matrix.initialize(matrix); - this._changed(Change.GEOMETRY); - } -}, Base.each(['bounds', 'strokeBounds', 'handleBounds', 'roughBounds'], -function(name) { - this['get' + Base.capitalize(name)] = function() { - var type = this._boundsType, - bounds = this._getCachedBounds( - typeof type == 'string' ? type : type && type[name] || name, - arguments[0]); - return name == 'bounds' ? LinkedRectangle.create(this, 'setBounds', - bounds.x, bounds.y, bounds.width, bounds.height) : bounds; - }; -}, { - _getCachedBounds: function(type, matrix, cacheItem) { - var cache = (!matrix || matrix.equals(this._matrix)) && type; - if (cacheItem && this._parent) { - var id = cacheItem._id, - ref = this._parent._boundsCache - = this._parent._boundsCache || { - ids: {}, - list: [] - }; - if (!ref.ids[id]) { - ref.list.push(cacheItem); - ref.ids[id] = cacheItem; - } - } - if (cache && this._bounds && this._bounds[cache]) - return this._bounds[cache]; - var identity = this._matrix.isIdentity(); - matrix = !matrix || matrix.isIdentity() - ? identity ? null : this._matrix - : identity ? matrix : matrix.clone().concatenate(this._matrix); - var bounds = this._getBounds(type, matrix, cache ? this : cacheItem); - if (cache) { - if (!this._bounds) - this._bounds = {}; - this._bounds[cache] = bounds.clone(); - } - return bounds; - }, - - _clearBoundsCache: function() { - if (this._boundsCache) { - for (var i = 0, list = this._boundsCache.list, l = list.length; - i < l; i++) { - var item = list[i]; - delete item._bounds; - if (item != this && item._boundsCache) - item._clearBoundsCache(); - } - delete this._boundsCache; - } - }, - - _getBounds: function(type, matrix, cacheItem) { - var children = this._children; - if (!children || children.length == 0) - return new Rectangle(); - var x1 = Infinity, - x2 = -x1, - y1 = x1, - y2 = x2; - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - if (child._visible) { - var rect = child._getCachedBounds(type, matrix, cacheItem); - x1 = Math.min(rect.x, x1); - y1 = Math.min(rect.y, y1); - x2 = Math.max(rect.x + rect.width, x2); - y2 = Math.max(rect.y + rect.height, y2); - } - } - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - setBounds: function(rect) { - rect = Rectangle.read(arguments); - var bounds = this.getBounds(), - matrix = new Matrix(), - center = rect.getCenter(); - matrix.translate(center); - if (rect.width != bounds.width || rect.height != bounds.height) { - matrix.scale( - bounds.width != 0 ? rect.width / bounds.width : 1, - bounds.height != 0 ? rect.height / bounds.height : 1); - } - center = bounds.getCenter(); - matrix.translate(-center.x, -center.y); - this.transform(matrix); - } - -}), { - getProject: function() { - return this._project; - }, - - _setProject: function(project) { - if (this._project != project) { - this._project = project; - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - this._children[i]._setProject(project); - } - } - } - }, - - getLayer: function() { - var parent = this; - while (parent = parent._parent) { - if (parent instanceof Layer) - return parent; - } - return null; - }, - - getParent: function() { - return this._parent; - }, - - getChildren: function() { - return this._children; - }, - - setChildren: function(items) { - this.removeChildren(); - this.addChildren(items); - }, - - getFirstChild: function() { - return this._children && this._children[0] || null; - }, - - getLastChild: function() { - return this._children && this._children[this._children.length - 1] - || null; - }, - - getNextSibling: function() { - return this._parent && this._parent._children[this._index + 1] || null; - }, - - getPreviousSibling: function() { - return this._parent && this._parent._children[this._index - 1] || null; - }, - - getIndex: function() { - return this._index; - }, - - clone: function() { - return this._clone(new this.constructor()); - }, - - _clone: function(copy) { - copy.setStyle(this._style); - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) - copy.addChild(this._children[i].clone()); - } - var keys = ['_locked', '_visible', '_blendMode', '_opacity', - '_clipMask', '_guide']; - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - if (this.hasOwnProperty(key)) - copy[key] = this[key]; - } - copy._matrix.initialize(this._matrix); - copy.setSelected(this._selected); - if (this._name) - copy.setName(this._name); - return copy; - }, - - copyTo: function(itemOrProject) { - var copy = this.clone(); - if (itemOrProject.layers) { - itemOrProject.activeLayer.addChild(copy); - } else { - itemOrProject.addChild(copy); - } - return copy; - }, - - rasterize: function(resolution) { - var bounds = this.getStrokeBounds(), - scale = (resolution || 72) / 72, - canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)), - ctx = canvas.getContext('2d'), - matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); - matrix.applyToContext(ctx); - this.draw(ctx, {}); - var raster = new Raster(canvas); - raster.setBounds(bounds); - return raster; - }, - - hitTest: function(point, options) { - options = HitResult.getOptions(point, options); - point = options.point = this._matrix._inverseTransform(options.point); - if (!this._children && !this.getRoughBounds() - .expand(options.tolerance)._containsPoint(point)) - return null; - if ((options.center || options.bounds) && - !(this instanceof Layer && !this._parent)) { - var bounds = this.getBounds(), - that = this, - points = ['TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', - 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'], - res; - function checkBounds(type, part) { - var pt = bounds['get' + part](); - if (point.getDistance(pt) < options.tolerance) - return new HitResult(type, that, - { name: Base.hyphenate(part), point: pt }); - } - if (options.center && (res = checkBounds('center', 'Center'))) - return res; - if (options.bounds) { - for (var i = 0; i < 8; i++) - if (res = checkBounds('bounds', points[i])) - return res; - } - } - - return this._children || !(options.guides && !this._guide - || options.selected && !this._selected) - ? this._hitTest(point, options) : null; - }, - - _hitTest: function(point, options) { - if (this._children) { - for (var i = this._children.length - 1; i >= 0; i--) { - var res = this._children[i].hitTest(point, options); - if (res) return res; - } - } - }, - - addChild: function(item) { - return this.insertChild(undefined, item); - }, - - insertChild: function(index, item) { - if (this._children) { - item._remove(false, true); - Base.splice(this._children, [item], index, 0); - item._parent = this; - item._setProject(this._project); - if (item._name) - item.setName(item._name); - this._changed(Change.HIERARCHY); - return true; - } - return false; - }, - - addChildren: function(items) { - for (var i = 0, l = items && items.length; i < l; i++) - this.insertChild(undefined, items[i]); - }, - - insertChildren: function(index, items) { - for (var i = 0, l = items && items.length; i < l; i++) { - if (this.insertChild(index, items[i])) - index++; - } - }, - - insertAbove: function(item) { - var index = item._index; - if (item._parent == this._parent && index < this._index) - index++; - return item._parent.insertChild(index, this); - }, - - insertBelow: function(item) { - var index = item._index; - if (item._parent == this._parent && index > this._index) - index--; - return item._parent.insertChild(index, this); - }, - - appendTop: function(item) { - return this.addChild(item); - }, - - appendBottom: function(item) { - return this.insertChild(0, item); - }, - - moveAbove: function(item) { - return this.insertAbove(item); - }, - - moveBelow: function(item) { - return this.insertBelow(item); - }, - - _removeFromNamed: function() { - var children = this._parent._children, - namedChildren = this._parent._namedChildren, - name = this._name, - namedArray = namedChildren[name], - index = namedArray ? namedArray.indexOf(this) : -1; - if (index == -1) - return; - if (children[name] == this) - delete children[name]; - namedArray.splice(index, 1); - if (namedArray.length) { - children[name] = namedArray[namedArray.length - 1]; - } else { - delete namedChildren[name]; - } - }, - - _remove: function(deselect, notify) { - if (this._parent) { - if (deselect) - this.setSelected(false); - if (this._name) - this._removeFromNamed(); - if (this._index != null) - Base.splice(this._parent._children, null, this._index, 1); - if (notify) - this._parent._changed(Change.HIERARCHY); - this._parent = null; - return true; - } - return false; - }, - - remove: function() { - return this._remove(true, true); - }, - - removeChildren: function(from, to) { - if (!this._children) - return null; - from = from || 0; - to = Base.pick(to, this._children.length); - var removed = Base.splice(this._children, null, from, to - from); - for (var i = removed.length - 1; i >= 0; i--) - removed[i]._remove(true, false); - if (removed.length > 0) - this._changed(Change.HIERARCHY); - return removed; - }, - - reverseChildren: function() { - if (this._children) { - this._children.reverse(); - for (var i = 0, l = this._children.length; i < l; i++) - this._children[i]._index = i; - this._changed(Change.HIERARCHY); - } - }, - - isEditable: function() { - var item = this; - while (item) { - if (!item._visible || item._locked) - return false; - item = item._parent; - } - return true; - }, - - _getOrder: function(item) { - function getList(item) { - var list = []; - do { - list.unshift(item); - } while (item = item._parent) - return list; - } - var list1 = getList(this), - list2 = getList(item); - for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) { - if (list1[i] != list2[i]) { - return list1[i]._index < list2[i]._index ? 1 : -1; - } - } - return 0; - }, - - hasChildren: function() { - return this._children && this._children.length > 0; - }, - - isAbove: function(item) { - return this._getOrder(item) == -1; - }, - - isBelow: function(item) { - return this._getOrder(item) == 1; - }, - - isParent: function(item) { - return this._parent == item; - }, - - isChild: function(item) { - return item && item._parent == this; - }, - - isDescendant: function(item) { - var parent = this; - while (parent = parent._parent) { - if (parent == item) - return true; - } - return false; - }, - - isAncestor: function(item) { - return item ? item.isDescendant(this) : false; - }, - - isGroupedWith: function(item) { - var parent = this._parent; - while (parent) { - if (parent._parent - && (parent instanceof Group || parent instanceof CompoundPath) - && item.isDescendant(parent)) - return true; - parent = parent._parent; - } - return false; - }, - - scale: function(hor, ver , center, apply) { - if (arguments.length < 2 || typeof ver === 'object') { - apply = center; - center = ver; - ver = hor; - } - return this.transform(new Matrix().scale(hor, ver, - center || this.getPosition(true)), apply); - }, - - translate: function(delta, apply) { - var mx = new Matrix(); - return this.transform(mx.translate.apply(mx, arguments), apply); - }, - - rotate: function(angle, center, apply) { - return this.transform(new Matrix().rotate(angle, - center || this.getPosition(true)), apply); - }, - - shear: function(hor, ver, center, apply) { - if (arguments.length < 2 || typeof ver === 'object') { - apply = center; - center = ver; - ver = hor; - } - return this.transform(new Matrix().shear(hor, ver, - center || this.getPosition(true)), apply); - }, - - transform: function(matrix, apply) { - var bounds = this._bounds, - position = this._position; - this._matrix.preConcatenate(matrix); - if (this._transform) - this._transform(matrix); - if (apply) - this.apply(); - this._changed(Change.GEOMETRY); - if (bounds && matrix.getRotation() % 90 === 0) { - for (var key in bounds) { - var rect = bounds[key]; - matrix._transformBounds(rect, rect); - } - var type = this._boundsType, - rect = bounds[type && type.bounds || 'bounds']; - if (rect) - this._position = rect.getCenter(true); - this._bounds = bounds; - } else if (position) { - this._position = matrix._transformPoint(position, position); - } - return this; - }, - - apply: function() { - if (this._apply(this._matrix)) { - this._matrix.setIdentity(); - } - }, - - _apply: function(matrix) { - if (this._children) { - for (var i = 0, l = this._children.length; i < l; i++) { - var child = this._children[i]; - child.transform(matrix); - child.apply(); - } - return true; - } - }, - - fitBounds: function(rectangle, fill) { - rectangle = Rectangle.read(arguments); - var bounds = this.getBounds(), - itemRatio = bounds.height / bounds.width, - rectRatio = rectangle.height / rectangle.width, - scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio) - ? rectangle.width / bounds.width - : rectangle.height / bounds.height, - newBounds = new Rectangle(new Point(), - Size.create(bounds.width * scale, bounds.height * scale)); - newBounds.setCenter(rectangle.getCenter()); - this.setBounds(newBounds); - }, - - toString: function() { - return (this.constructor._name || 'Item') + (this._name - ? " '" + this._name + "'" - : ' @' + this._id); - }, - - _setStyles: function(ctx) { - var style = this._style, - width = style._strokeWidth, - join = style._strokeJoin, - cap = style._strokeCap, - limit = style._miterLimit, - fillColor = style._fillColor, - strokeColor = style._strokeColor; - if (width != null) ctx.lineWidth = width; - if (join) ctx.lineJoin = join; - if (cap) ctx.lineCap = cap; - if (limit) ctx.miterLimit = limit; - if (fillColor) ctx.fillStyle = fillColor.getCanvasStyle(ctx); - if (strokeColor) ctx.strokeStyle = strokeColor.getCanvasStyle(ctx); - if (!fillColor || !strokeColor) - ctx.globalAlpha = this._opacity; - }, - - statics: { - drawSelectedBounds: function(bounds, ctx, matrix) { - var coords = matrix._transformCorners(bounds); - ctx.beginPath(); - for (var i = 0; i < 8; i++) - ctx[i == 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]); - ctx.closePath(); - ctx.stroke(); - for (var i = 0; i < 8; i++) { - ctx.beginPath(); - ctx.rect(coords[i] - 2, coords[++i] - 2, 4, 4); - ctx.fill(); - } - }, - - draw: function(item, ctx, param) { - if (!item._visible || item._opacity == 0) - return; - var tempCanvas, parentCtx, - itemOffset, prevOffset; - if (item._blendMode !== 'normal' || item._opacity < 1 - && !(item._segments - && (!item.getFillColor() || !item.getStrokeColor()))) { - var bounds = item.getStrokeBounds(); - if (!bounds.width || !bounds.height) - return; - prevOffset = param.offset; - parentCtx = ctx; - itemOffset = param.offset = bounds.getTopLeft().floor(); - tempCanvas = CanvasProvider.getCanvas( - bounds.getSize().ceil().add(Size.create(1, 1))); - ctx = tempCanvas.getContext('2d'); - } - if (!param.clipping) - ctx.save(); - if (tempCanvas) - ctx.translate(-itemOffset.x, -itemOffset.y); - item._matrix.applyToContext(ctx); - item.draw(ctx, param); - if (!param.clipping) - ctx.restore(); - if (tempCanvas) { - param.offset = prevOffset; - if (item._blendMode !== 'normal') { - BlendMode.process(item._blendMode, ctx, parentCtx, - item._opacity, itemOffset.subtract(prevOffset)); - } else { - parentCtx.save(); - parentCtx.globalAlpha = item._opacity; - parentCtx.drawImage(tempCanvas, itemOffset.x, itemOffset.y); - parentCtx.restore(); - } - CanvasProvider.returnCanvas(tempCanvas); - } - } - } -}, Base.each(['down', 'drag', 'up', 'move'], function(name) { - this['removeOn' + Base.capitalize(name)] = function() { - var hash = {}; - hash[name] = true; - return this.removeOn(hash); - }; -}, { - - removeOn: function(obj) { - for (var name in obj) { - if (obj[name]) { - var key = 'mouse' + name, - sets = Tool._removeSets = Tool._removeSets || {}; - sets[key] = sets[key] || {}; - sets[key][this._id] = this; - } - } - return this; - } -})); - -var Group = this.Group = Item.extend({ - initialize: function(items) { - this.base(); - this._children = []; - this._namedChildren = {}; - this.addChildren(!items || !Array.isArray(items) - || typeof items[0] !== 'object' ? arguments : items); - }, - - _changed: function(flags) { - Item.prototype._changed.call(this, flags); - if (flags & (ChangeFlag.HIERARCHY | ChangeFlag.CLIPPING)) { - delete this._clipItem; - } - }, - - _getClipItem: function() { - if (this._clipItem !== undefined) - return this._clipItem; - for (var i = 0, l = this._children.length; i < l; i++) { - var child = this._children[i]; - if (child._clipMask) - return this._clipItem = child; - } - return this._clipItem = null; - }, - - isClipped: function() { - return !!this._getClipItem(); - }, - - setClipped: function(clipped) { - var child = this.getFirstChild(); - if (child) - child.setClipMask(clipped); - return this; - }, - - draw: function(ctx, param) { - var clipItem = this._getClipItem(); - if (clipItem) { - param.clipping = true; - Item.draw(clipItem, ctx, param); - delete param.clipping; - } - for (var i = 0, l = this._children.length; i < l; i++) { - var item = this._children[i]; - if (item != clipItem) - Item.draw(item, ctx, param); - } - } -}); - -var Layer = this.Layer = Group.extend({ - initialize: function(items) { - this._project = paper.project; - this._index = this._project.layers.push(this) - 1; - this.base.apply(this, arguments); - this.activate(); - }, - - _remove: function(deselect, notify) { - if (this._parent) - return this.base(deselect, notify); - if (this._index != null) { - if (deselect) - this.setSelected(false); - Base.splice(this._project.layers, null, this._index, 1); - this._project._needsRedraw(); - return true; - } - return false; - }, - - getNextSibling: function() { - return this._parent ? this.base() - : this._project.layers[this._index + 1] || null; - }, - - getPreviousSibling: function() { - return this._parent ? this.base() - : this._project.layers[this._index - 1] || null; - }, - - activate: function() { - this._project.activeLayer = this; - } -}, new function () { - function insert(above) { - return function(item) { - if (item instanceof Layer && !item._parent - && this._remove(false, true)) { - Base.splice(item._project.layers, [this], - item._index + (above ? 1 : -1), 0); - this._setProject(item._project); - return true; - } - return this.base(item); - }; - } - - return { - insertAbove: insert(true), - - insertBelow: insert(false) - }; -}); - -var PlacedItem = this.PlacedItem = Item.extend({ - _boundsType: { bounds: 'strokeBounds' } -}); - -var Raster = this.Raster = PlacedItem.extend({ - _boundsType: 'bounds', - - initialize: function(object, pointOrMatrix) { - this.base(pointOrMatrix); - if (object.getContext) { - this.setCanvas(object); - } else { - if (typeof object === 'string') - object = document.getElementById(object); - this.setImage(object); - } - }, - - clone: function() { - var image = this._image; - if (!image) { - image = CanvasProvider.getCanvas(this._size); - image.getContext('2d').drawImage(this._canvas, 0, 0); - } - var copy = new Raster(image); - return this._clone(copy); - }, - - getSize: function() { - return this._size; - }, - - setSize: function() { - var size = Size.read(arguments), - image = this.getImage(); - this.setCanvas(CanvasProvider.getCanvas(size)); - this.getContext(true).drawImage(image, 0, 0, size.width, size.height); - }, - - getWidth: function() { - return this._size.width; - }, - - getHeight: function() { - return this._size.height; - }, - - getPpi: function() { - var matrix = this._matrix, - orig = new Point(0, 0).transform(matrix), - u = new Point(1, 0).transform(matrix).subtract(orig), - v = new Point(0, 1).transform(matrix).subtract(orig); - return Size.create( - 72 / u.getLength(), - 72 / v.getLength() - ); - }, - - getContext: function() { - if (!this._context) - this._context = this.getCanvas().getContext('2d'); - if (arguments[0]) - this._changed(Change.PIXELS); - return this._context; - }, - - setContext: function(context) { - this._context = context; - }, - - getCanvas: function() { - if (!this._canvas) { - this._canvas = CanvasProvider.getCanvas(this._size); - if (this._image) - this.getContext(true).drawImage(this._image, 0, 0); - } - return this._canvas; - }, - - setCanvas: function(canvas) { - if (this._canvas) - CanvasProvider.returnCanvas(this._canvas); - this._canvas = canvas; - this._size = Size.create(canvas.width, canvas.height); - this._image = null; - this._context = null; - this._changed(Change.GEOMETRY | Change.PIXELS); - }, - - getImage: function() { - return this._image || this.getCanvas(); - }, - - setImage: function(image) { - if (this._canvas) - CanvasProvider.returnCanvas(this._canvas); - this._image = image; - this._size = Size.create(image.naturalWidth, image.naturalHeight); - this._canvas = null; - this._context = null; - this._changed(Change.GEOMETRY); - }, - - getSubImage: function(rect) { - rect = Rectangle.read(arguments); - var canvas = CanvasProvider.getCanvas(rect.getSize()); - canvas.getContext('2d').drawImage(this.getCanvas(), rect.x, rect.y, - canvas.width, canvas.height, 0, 0, canvas.width, canvas.height); - return canvas; - }, - - drawImage: function(image, point) { - point = Point.read(arguments, 1); - this.getContext(true).drawImage(image, point.x, point.y); - }, - - getAverageColor: function(object) { - var bounds, path; - if (!object) { - bounds = this.getBounds(); - } else if (object instanceof PathItem) { - path = object; - bounds = object.getBounds(); - } else if (object.width) { - bounds = new Rectangle(object); - } else if (object.x) { - bounds = Rectangle.create(object.x - 0.5, object.y - 0.5, 1, 1); - } - var sampleSize = 32, - width = Math.min(bounds.width, sampleSize), - height = Math.min(bounds.height, sampleSize); - var ctx = Raster._sampleContext; - if (!ctx) { - ctx = Raster._sampleContext = CanvasProvider.getCanvas( - new Size(sampleSize)).getContext('2d'); - } else { - ctx.clearRect(0, 0, sampleSize, sampleSize); - } - ctx.save(); - ctx.scale(width / bounds.width, height / bounds.height); - ctx.translate(-bounds.x, -bounds.y); - if (path) - path.draw(ctx, { clip: true }); - this._matrix.applyToContext(ctx); - ctx.drawImage(this._canvas || this._image, - -this._size.width / 2, -this._size.height / 2); - ctx.restore(); - var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width), - Math.ceil(height)).data, - channels = [0, 0, 0], - total = 0; - for (var i = 0, l = pixels.length; i < l; i += 4) { - var alpha = pixels[i + 3]; - total += alpha; - alpha /= 255; - channels[0] += pixels[i] * alpha; - channels[1] += pixels[i + 1] * alpha; - channels[2] += pixels[i + 2] * alpha; - } - for (var i = 0; i < 3; i++) - channels[i] /= total; - return total ? Color.read(channels) : null; - }, - - getPixel: function(point) { - point = Point.read(arguments); - var pixels = this.getContext().getImageData(point.x, point.y, 1, 1).data, - channels = new Array(4); - for (var i = 0; i < 4; i++) - channels[i] = pixels[i] / 255; - return RgbColor.read(channels); - }, - - setPixel: function(point, color) { - var hasPoint = arguments.length == 2; - point = Point.read(arguments, 0, hasPoint ? 1 : 2); - color = Color.read(arguments, hasPoint ? 1 : 2); - var ctx = this.getContext(true), - imageData = ctx.createImageData(1, 1), - alpha = color.getAlpha(); - imageData.data[0] = color.getRed() * 255; - imageData.data[1] = color.getGreen() * 255; - imageData.data[2] = color.getBlue() * 255; - imageData.data[3] = alpha != null ? alpha * 255 : 255; - ctx.putImageData(imageData, point.x, point.y); - }, - - createData: function(size) { - size = Size.read(arguments); - return this.getContext().createImageData(size.width, size.height); - }, - - getData: function(rect) { - rect = Rectangle.read(arguments); - if (rect.isEmpty()) - rect = new Rectangle(this.getSize()); - return this.getContext().getImageData(rect.x, rect.y, - rect.width, rect.height); - }, - - setData: function(data, point) { - point = Point.read(arguments, 1); - this.getContext(true).putImageData(data, point.x, point.y); - }, - - _getBounds: function(type, matrix) { - var rect = new Rectangle(this._size).setCenter(0, 0); - return matrix ? matrix._transformBounds(rect) : rect; - }, - - _hitTest: function(point, options) { - if (point.isInside(this._getBounds())) { - var that = this; - return new HitResult('pixel', that, { - offset: point.add(that._size.divide(2)).round(), - getColor: function() { - return that.getPixel(this.offset); - } - }); - } - }, - - draw: function(ctx, param) { - ctx.drawImage(this._canvas || this._image, - -this._size.width / 2, -this._size.height / 2); - }, - - drawSelected: function(ctx, matrix) { - Item.drawSelectedBounds(new Rectangle(this._size).setCenter(0, 0), ctx, - matrix); - } -}); - -var PlacedSymbol = this.PlacedSymbol = PlacedItem.extend({ - initialize: function(symbol, pointOrMatrix) { - this.base(pointOrMatrix); - this.setSymbol(symbol instanceof Symbol ? symbol : new Symbol(symbol)); - }, - - getSymbol: function() { - return this._symbol; - }, - - setSymbol: function(symbol) { - if (this._symbol) - delete this._symbol._instances[this._id]; - this._symbol = symbol; - symbol._instances[this._id] = this; - }, - - clone: function() { - return this._clone(new PlacedSymbol(this.symbol, this._matrix.clone())); - }, - - _getBounds: function(type, matrix) { - return this.symbol._definition._getCachedBounds(type, matrix); - }, - - draw: function(ctx, param) { - Item.draw(this.symbol._definition, ctx, param); - }, - - drawSelected: function(ctx, matrix) { - Item.drawSelectedBounds(this.symbol._definition.getBounds(), ctx, - matrix); - } - -}); - -HitResult = Base.extend({ - initialize: function(type, item, values) { - this.type = type; - this.item = item; - if (values) - this.inject(values); - }, - - statics: { - getOptions: function(point, options) { - return options && options._merged ? options : Base.merge({ - point: Point.read(arguments, 0, 1), - type: null, - tolerance: 2, - fill: !options, - stroke: !options, - segments: !options, - handles: false, - ends: false, - center: false, - bounds: false, - guides: false, - selected: false, - _merged: true - }, options); - } - } -}); - -var Segment = this.Segment = Base.extend({ - initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) { - var count = arguments.length, - createPoint = SegmentPoint.create, - point, handleIn, handleOut; - if (count == 0) { - } else if (count == 1) { - if (arg0.point) { - point = arg0.point; - handleIn = arg0.handleIn; - handleOut = arg0.handleOut; - } else { - point = arg0; - } - } else if (count < 6) { - if (count == 2 && arg1.x === undefined) { - point = [ arg0, arg1 ]; - } else { - point = arg0; - handleIn = arg1; - handleOut = arg2; - } - } else if (count == 6) { - point = [ arg0, arg1 ]; - handleIn = [ arg2, arg3 ]; - handleOut = [ arg4, arg5 ]; - } - createPoint(this, '_point', point); - createPoint(this, '_handleIn', handleIn); - createPoint(this, '_handleOut', handleOut); - }, - - _changed: function(point) { - if (!this._path) - return; - var curve = this._path._curves && this.getCurve(), other; - if (curve) { - curve._changed(); - if (other = (curve[point == this._point - || point == this._handleIn && curve._segment1 == this - ? 'getPrevious' : 'getNext']())) { - other._changed(); - } - } - this._path._changed(Change.GEOMETRY); - }, - - getPoint: function() { - return this._point; - }, - - setPoint: function(point) { - point = Point.read(arguments); - this._point.set(point.x, point.y); - }, - - getHandleIn: function() { - return this._handleIn; - }, - - setHandleIn: function(point) { - point = Point.read(arguments); - this._handleIn.set(point.x, point.y); - }, - - getHandleOut: function() { - return this._handleOut; - }, - - setHandleOut: function(point) { - point = Point.read(arguments); - this._handleOut.set(point.x, point.y); - }, - - _isSelected: function(point) { - var state = this._selectionState; - return point == this._point ? !!(state & SelectionState.POINT) - : point == this._handleIn ? !!(state & SelectionState.HANDLE_IN) - : point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT) - : false; - }, - - _setSelected: function(point, selected) { - var path = this._path, - selected = !!selected, - state = this._selectionState || 0, - selection = [ - !!(state & SelectionState.POINT), - !!(state & SelectionState.HANDLE_IN), - !!(state & SelectionState.HANDLE_OUT) - ]; - if (point == this._point) { - if (selected) { - selection[1] = selection[2] = false; - } else { - var previous = this.getPrevious(), - next = this.getNext(); - selection[1] = previous && (previous._point.isSelected() - || previous._handleOut.isSelected()); - selection[2] = next && (next._point.isSelected() - || next._handleIn.isSelected()); - } - selection[0] = selected; - } else { - var index = point == this._handleIn ? 1 : 2; - if (selection[index] != selected) { - if (selected) - selection[0] = false; - selection[index] = selected; - path._changed(Change.ATTRIBUTE); - } - } - this._selectionState = (selection[0] ? SelectionState.POINT : 0) - | (selection[1] ? SelectionState.HANDLE_IN : 0) - | (selection[2] ? SelectionState.HANDLE_OUT : 0); - if (path && state != this._selectionState) - path._updateSelection(this, state, this._selectionState); - }, - - isSelected: function() { - return this._isSelected(this._point); - }, - - setSelected: function(selected) { - this._setSelected(this._point, selected); - }, - - getIndex: function() { - return this._index !== undefined ? this._index : null; - }, - - getPath: function() { - return this._path || null; - }, - - getCurve: function() { - if (this._path) { - var index = this._index; - if (!this._path._closed && index == this._path._segments.length - 1) - index--; - return this._path.getCurves()[index] || null; - } - return null; - }, - - getNext: function() { - var segments = this._path && this._path._segments; - return segments && (segments[this._index + 1] - || this._path._closed && segments[0]) || null; - }, - - getPrevious: function() { - var segments = this._path && this._path._segments; - return segments && (segments[this._index - 1] - || this._path._closed && segments[segments.length - 1]) || null; - }, - - reverse: function() { - return new Segment(this._point, this._handleOut, this._handleIn); - }, - - remove: function() { - return this._path ? !!this._path.removeSegment(this._index) : false; - }, - - clone: function() { - return new Segment(this._point, this._handleIn, this._handleOut); - }, - - equals: function(segment) { - return segment == this || segment - && this._point.equals(segment._point) - && this._handleIn.equals(segment._handleIn) - && this._handleOut.equals(segment._handleOut); - }, - - toString: function() { - var parts = [ 'point: ' + this._point ]; - if (!this._handleIn.isZero()) - parts.push('handleIn: ' + this._handleIn); - if (!this._handleOut.isZero()) - parts.push('handleOut: ' + this._handleOut); - return '{ ' + parts.join(', ') + ' }'; - }, - - _transformCoordinates: function(matrix, coords, change) { - var point = this._point, - handleIn = !change || !this._handleIn.isZero() - ? this._handleIn : null, - handleOut = !change || !this._handleOut.isZero() - ? this._handleOut : null, - x = point._x, - y = point._y, - i = 2; - coords[0] = x; - coords[1] = y; - if (handleIn) { - coords[i++] = handleIn._x + x; - coords[i++] = handleIn._y + y; - } - if (handleOut) { - coords[i++] = handleOut._x + x; - coords[i++] = handleOut._y + y; - } - if (!matrix) - return; - matrix._transformCoordinates(coords, 0, coords, 0, i / 2); - x = coords[0]; - y = coords[1]; - if (change) { - point._x = x; - point._y = y; - i = 2; - if (handleIn) { - handleIn._x = coords[i++] - x; - handleIn._y = coords[i++] - y; - } - if (handleOut) { - handleOut._x = coords[i++] - x; - handleOut._y = coords[i++] - y; - } - } else { - if (!handleIn) { - coords[i++] = x; - coords[i++] = y; - } - if (!handleOut) { - coords[i++] = x; - coords[i++] = y; - } - } - } -}); - -var SegmentPoint = Point.extend({ - set: function(x, y) { - this._x = x; - this._y = y; - this._owner._changed(this); - return this; - }, - - getX: function() { - return this._x; - }, - - setX: function(x) { - this._x = x; - this._owner._changed(this); - }, - - getY: function() { - return this._y; - }, - - setY: function(y) { - this._y = y; - this._owner._changed(this); - }, - - isZero: function() { - return this._x == 0 && this._y == 0; - }, - - setSelected: function(selected) { - this._owner._setSelected(this, selected); - }, - - isSelected: function() { - return this._owner._isSelected(this); - }, - - statics: { - create: function(segment, key, pt) { - var point = new SegmentPoint(SegmentPoint.dont), - x, y, selected; - if (!pt) { - x = y = 0; - } else if ((x = pt[0]) !== undefined) { - y = pt[1]; - } else { - if ((x = pt.x) === undefined) { - pt = Point.read(arguments, 2, 1); - x = pt.x; - } - y = pt.y; - selected = pt.selected; - } - point._x = x; - point._y = y; - point._owner = segment; - segment[key] = point; - if (selected) - point.setSelected(true); - return point; - } - } -}); - -var SelectionState = { - HANDLE_IN: 1, - HANDLE_OUT: 2, - POINT: 4 -}; - -var Curve = this.Curve = Base.extend({ - initialize: function(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { - var count = arguments.length; - if (count == 0) { - this._segment1 = new Segment(); - this._segment2 = new Segment(); - } else if (count == 1) { - this._segment1 = new Segment(arg0.segment1); - this._segment2 = new Segment(arg0.segment2); - } else if (count == 2) { - this._segment1 = new Segment(arg0); - this._segment2 = new Segment(arg1); - } else if (count == 4) { - this._segment1 = new Segment(arg0, null, arg1); - this._segment2 = new Segment(arg3, arg2, null); - } else if (count == 8) { - var p1 = Point.create(arg0, arg1), - p2 = Point.create(arg6, arg7); - this._segment1 = new Segment(p1, null, - Point.create(arg2, arg3).subtract(p1)); - this._segment2 = new Segment(p2, - Point.create(arg4, arg5).subtract(p2), null); - } - }, - - _changed: function() { - delete this._length; - }, - - getPoint1: function() { - return this._segment1._point; - }, - - setPoint1: function(point) { - point = Point.read(arguments); - this._segment1._point.set(point.x, point.y); - }, - - getPoint2: function() { - return this._segment2._point; - }, - - setPoint2: function(point) { - point = Point.read(arguments); - this._segment2._point.set(point.x, point.y); - }, - - getHandle1: function() { - return this._segment1._handleOut; - }, - - setHandle1: function(point) { - point = Point.read(arguments); - this._segment1._handleOut.set(point.x, point.y); - }, - - getHandle2: function() { - return this._segment2._handleIn; - }, - - setHandle2: function(point) { - point = Point.read(arguments); - this._segment2._handleIn.set(point.x, point.y); - }, - - getSegment1: function() { - return this._segment1; - }, - - getSegment2: function() { - return this._segment2; - }, - - getPath: function() { - return this._path; - }, - - getIndex: function() { - return this._segment1._index; - }, - - getNext: function() { - var curves = this._path && this._path._curves; - return curves && (curves[this._segment1._index + 1] - || this._path._closed && curves[0]) || null; - }, - - getPrevious: function() { - var curves = this._path && this._path._curves; - return curves && (curves[this._segment1._index - 1] - || this._path._closed && curves[curves.length - 1]) || null; - }, - - isSelected: function() { - return this.getHandle1().isSelected() && this.getHandle2().isSelected(); - }, - - setSelected: function(selected) { - this.getHandle1().setSelected(selected); - this.getHandle2().setSelected(selected); - }, - - getValues: function() { - return Curve.getValues(this._segment1, this._segment2); - }, - - getPoints: function() { - var coords = this.getValues(), - points = []; - for (var i = 0; i < 8; i += 2) - points.push(Point.create(coords[i], coords[i + 1])); - return points; - }, - - getLength: function() { - var from = arguments[0], - to = arguments[1]; - fullLength = arguments.length == 0 || from == 0 && to == 1; - if (fullLength && this._length != null) - return this._length; - var length = Curve.getLength(this.getValues(), from, to); - if (fullLength) - this._length = length; - return length; - }, - - getPart: function(from, to) { - return new Curve(Curve.getPart(this.getValues(), from, to)); - }, - - isLinear: function() { - return this._segment1._handleOut.isZero() - && this._segment2._handleIn.isZero(); - }, - - getParameterAt: function(offset, start) { - return Curve.getParameterAt(this.getValues(), offset, - start !== undefined ? start : offset < 0 ? 1 : 0); - }, - - getPoint: function(parameter) { - return Curve.evaluate(this.getValues(), parameter, 0); - }, - - getTangent: function(parameter) { - return Curve.evaluate(this.getValues(), parameter, 1); - }, - - getNormal: function(parameter) { - return Curve.evaluate(this.getValues(), parameter, 2); - }, - - getParameter: function(point) { - point = Point.read(point); - return Curve.getParameter(this.getValues(), point.x, point.y); - }, - - getCrossings: function(point, roots) { - var vals = this.getValues(), - num = Curve.solveCubic(vals, 1, point.y, roots), - crossings = 0; - for (var i = 0; i < num; i++) { - var t = roots[i]; - if (t >= 0 && t < 1 && Curve.evaluate(vals, t, 0).x > point.x) { - if (t < Numerical.TOLERANCE && Curve.evaluate( - this.getPrevious().getValues(), 1, 1).y - * Curve.evaluate(vals, t, 1).y >= 0) - continue; - crossings++; - } - } - return crossings; - }, - - reverse: function() { - return new Curve(this._segment2.reverse(), this._segment1.reverse()); - }, - - clone: function() { - return new Curve(this._segment1, this._segment2); - }, - - toString: function() { - var parts = [ 'point1: ' + this._segment1._point ]; - if (!this._segment1._handleOut.isZero()) - parts.push('handle1: ' + this._segment1._handleOut); - if (!this._segment2._handleIn.isZero()) - parts.push('handle2: ' + this._segment2._handleIn); - parts.push('point2: ' + this._segment2._point); - return '{ ' + parts.join(', ') + ' }'; - }, - - statics: { - create: function(path, segment1, segment2) { - var curve = new Curve(Curve.dont); - curve._path = path; - curve._segment1 = segment1; - curve._segment2 = segment2; - return curve; - }, - - getValues: function(segment1, segment2) { - var p1 = segment1._point, - h1 = segment1._handleOut, - h2 = segment2._handleIn, - p2 = segment2._point; - return [ - p1._x, p1._y, - p1._x + h1._x, p1._y + h1._y, - p2._x + h2._x, p2._y + h2._y, - p2._x, p2._y - ]; - }, - - evaluate: function(v, t, type) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7], - x, y; - - if (type == 0 && (t == 0 || t == 1)) { - x = t == 0 ? p1x : p2x; - y = t == 0 ? p1y : p2y; - } else { - var tMin = Numerical.TOLERANCE; - if (t < tMin && c1x == p1x && c1y == p1y) - t = tMin; - else if (t > 1 - tMin && c2x == p2x && c2y == p2y) - t = 1 - tMin; - var cx = 3 * (c1x - p1x), - bx = 3 * (c2x - c1x) - cx, - ax = p2x - p1x - cx - bx, - - cy = 3 * (c1y - p1y), - by = 3 * (c2y - c1y) - cy, - ay = p2y - p1y - cy - by; - - switch (type) { - case 0: - x = ((ax * t + bx) * t + cx) * t + p1x; - y = ((ay * t + by) * t + cy) * t + p1y; - break; - case 1: - case 2: - x = (3 * ax * t + 2 * bx) * t + cx; - y = (3 * ay * t + 2 * by) * t + cy; - break; - } - } - return type == 2 ? new Point(y, -x) : new Point(x, y); - }, - - subdivide: function(v, t) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7]; - if (t === undefined) - t = 0.5; - var u = 1 - t, - p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y, - p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y, - p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y, - p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y, - p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y, - p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y; - return [ - [p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y], - [p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y] - ]; - }, - - solveCubic: function (v, coord, val, roots) { - var p1 = v[coord], - c1 = v[coord + 2], - c2 = v[coord + 4], - p2 = v[coord + 6], - c = 3 * (c1 - p1), - b = 3 * (c2 - c1) - c, - a = p2 - p1 - c - b; - return Numerical.solveCubic(a, b, c, p1 - val, roots, - Numerical.TOLERANCE); - }, - - getParameter: function(v, x, y) { - var txs = [], - tys = [], - sx = Curve.solveCubic(v, 0, x, txs), - sy = Curve.solveCubic(v, 1, y, tys), - tx, ty; - for (var cx = 0; sx == -1 || cx < sx;) { - if (sx == -1 || (tx = txs[cx++]) >= 0 && tx <= 1) { - for (var cy = 0; sy == -1 || cy < sy;) { - if (sy == -1 || (ty = tys[cy++]) >= 0 && ty <= 1) { - if (sx == -1) tx = ty; - else if (sy == -1) ty = tx; - if (Math.abs(tx - ty) < Numerical.TOLERANCE) - return (tx + ty) * 0.5; - } - } - if (sx == -1) - break; - } - } - return null; - }, - - getPart: function(v, from, to) { - if (from > 0) - v = Curve.subdivide(v, from)[1]; - if (to < 1) - v = Curve.subdivide(v, (to - from) / (1 - from))[0]; - return v; - }, - - isFlatEnough: function(v) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7], - ux = 3 * c1x - 2 * p1x - p2x, - uy = 3 * c1y - 2 * p1y - p2y, - vx = 3 * c2x - 2 * p2x - p1x, - vy = 3 * c2y - 2 * p2y - p1y; - return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy) < 1; - } - } -}, new function() { - - function getLengthIntegrand(v) { - var p1x = v[0], p1y = v[1], - c1x = v[2], c1y = v[3], - c2x = v[4], c2y = v[5], - p2x = v[6], p2y = v[7], - - ax = 9 * (c1x - c2x) + 3 * (p2x - p1x), - bx = 6 * (p1x + c2x) - 12 * c1x, - cx = 3 * (c1x - p1x), - - ay = 9 * (c1y - c2y) + 3 * (p2y - p1y), - by = 6 * (p1y + c2y) - 12 * c1y, - cy = 3 * (c1y - p1y); - - return function(t) { - var dx = (ax * t + bx) * t + cx, - dy = (ay * t + by) * t + cy; - return Math.sqrt(dx * dx + dy * dy); - }; - } - - function getIterations(a, b) { - return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32))); - } - - return { - statics: true, - - getLength: function(v, a, b) { - if (a === undefined) - a = 0; - if (b === undefined) - b = 1; - if (v[0] == v[2] && v[1] == v[3] && v[6] == v[4] && v[7] == v[5]) { - var dx = v[6] - v[0], - dy = v[7] - v[1]; - return (b - a) * Math.sqrt(dx * dx + dy * dy); - } - var ds = getLengthIntegrand(v); - return Numerical.integrate(ds, a, b, getIterations(a, b)); - }, - - getParameterAt: function(v, offset, start) { - if (offset == 0) - return start; - var forward = offset > 0, - a = forward ? start : 0, - b = forward ? 1 : start, - offset = Math.abs(offset), - ds = getLengthIntegrand(v), - rangeLength = Numerical.integrate(ds, a, b, - getIterations(a, b)); - if (offset >= rangeLength) - return forward ? b : a; - var guess = offset / rangeLength, - length = 0; - function f(t) { - var count = getIterations(start, t); - length += start < t - ? Numerical.integrate(ds, start, t, count) - : -Numerical.integrate(ds, t, start, count); - start = t; - return length - offset; - } - return Numerical.findRoot(f, ds, - forward ? a + guess : b - guess, - a, b, 16, Numerical.TOLERANCE); - } - }; -}, new function() { - - var maxDepth = 32, - epsilon = Math.pow(2, -maxDepth - 1); - - var zCubic = [ - [1.0, 0.6, 0.3, 0.1], - [0.4, 0.6, 0.6, 0.4], - [0.1, 0.3, 0.6, 1.0] - ]; - - var xAxis = new Line(new Point(0, 0), new Point(1, 0)); - - function toBezierForm(v, point) { - var n = 3, - degree = 5, - c = [], - d = [], - cd = [], - w = []; - for(var i = 0; i <= n; i++) { - c[i] = v[i].subtract(point); - if (i < n) - d[i] = v[i + 1].subtract(v[i]).multiply(n); - } - - for (var row = 0; row < n; row++) { - cd[row] = []; - for (var column = 0; column <= n; column++) - cd[row][column] = d[row].dot(c[column]); - } - - for (var i = 0; i <= degree; i++) - w[i] = new Point(i / degree, 0); - - for (k = 0; k <= degree; k++) { - var lb = Math.max(0, k - n + 1), - ub = Math.min(k, n); - for (var i = lb; i <= ub; i++) { - var j = k - i; - w[k].y += cd[j][i] * zCubic[j][i]; - } - } - - return w; - } - - function findRoots(w, depth) { - switch (countCrossings(w)) { - case 0: - return []; - case 1: - if (depth >= maxDepth) - return [0.5 * (w[0].x + w[5].x)]; - if (isFlatEnough(w)) { - var line = new Line(w[0], w[5], true); - return [ line.vector.getLength(true) <= Numerical.EPSILON - ? line.point.x - : xAxis.intersect(line).x ]; - } - } - - var p = [[]], - left = [], - right = []; - for (var j = 0; j <= 5; j++) - p[0][j] = new Point(w[j]); - - for (var i = 1; i <= 5; i++) { - p[i] = []; - for (var j = 0 ; j <= 5 - i; j++) - p[i][j] = p[i - 1][j].add(p[i - 1][j + 1]).multiply(0.5); - } - for (var j = 0; j <= 5; j++) { - left[j] = p[j][0]; - right[j] = p[5 - j][j]; - } - - return findRoots(left, depth + 1).concat(findRoots(right, depth + 1)); - } - - function countCrossings(v) { - var crossings = 0, - prevSign = null; - for (var i = 0, l = v.length; i < l; i++) { - var sign = v[i].y < 0 ? -1 : 1; - if (prevSign != null && sign != prevSign) - crossings++; - prevSign = sign; - } - return crossings; - } - - function isFlatEnough(v) { - - var n = v.length - 1, - a = v[0].y - v[n].y, - b = v[n].x - v[0].x, - c = v[0].x * v[n].y - v[n].x * v[0].y, - maxAbove = 0, - maxBelow = 0; - for (var i = 1; i < n; i++) { - var val = a * v[i].x + b * v[i].y + c, - dist = val * val; - if (val < 0 && dist > maxBelow) { - maxBelow = dist; - } else if (dist > maxAbove) { - maxAbove = dist; - } - } - return Math.abs((maxAbove + maxBelow) / (2 * a * (a * a + b * b))) - < epsilon; - } - - return { - getNearestLocation: function(point) { - var w = toBezierForm(this.getPoints(), point); - var roots = findRoots(w, 0).concat([0, 1]); - var minDist = Infinity, - minT, - minPoint; - for (var i = 0; i < roots.length; i++) { - var pt = this.getPoint(roots[i]), - dist = point.getDistance(pt, true); - if (dist < minDist) { - minDist = dist; - minT = roots[i]; - minPoint = pt; - } - } - return new CurveLocation(this, minT, minPoint, Math.sqrt(minDist)); - }, - - getNearestPoint: function(point) { - return this.getNearestLocation(point).getPoint(); - } - }; -}); - -CurveLocation = Base.extend({ - initialize: function(curve, parameter, point, distance) { - this._curve = curve; - this._parameter = parameter; - this._point = point; - this._distance = distance; - }, - - getSegment: function() { - if (!this._segment) { - var curve = this._curve, - parameter = this.getParameter(); - if (parameter == 0) { - this._segment = curve._segment1; - } else if (parameter == 1) { - this._segment = curve._segment2; - } else if (parameter == null) { - return null; - } else { - this._segment = curve.getLength(0, parameter) - < curve.getLength(parameter, 1) - ? curve._segment1 - : curve._segment2; - } - } - return this._segment; - }, - - getCurve: function() { - return this._curve; - }, - - getPath: function() { - return this._curve && this._curve._path; - }, - - getIndex: function() { - return this._curve && this._curve.getIndex(); - }, - - getOffset: function() { - var path = this._curve && this._curve._path; - return path && path._getOffset(this); - }, - - getCurveOffset: function() { - var parameter = this.getParameter(); - return parameter != null && this._curve - && this._curve.getLength(0, parameter); - }, - - getParameter: function() { - if (this._parameter == null && this._curve && this._point) - this._parameter = this._curve.getParameterAt(this._point); - return this._parameter; - }, - - getPoint: function() { - if (!this._point && this._curve && this._parameter != null) - this._point = this._curve.getPoint(this._parameter); - return this._point; - }, - - getTangent: function() { - var parameter = this.getParameter(); - return parameter != null && this._curve - && this._curve.getTangent(parameter); - }, - - getNormal: function() { - var parameter = this.getParameter(); - return parameter != null && this._curve - && this._curve.getNormal(parameter); - }, - - getDistance: function() { - return this._distance; - }, - - toString: function() { - var parts = [], - point = this.getPoint(); - if (point) - parts.push('point: ' + point); - var index = this.getIndex(); - if (index != null) - parts.push('index: ' + index); - var parameter = this.getParameter(); - if (parameter != null) - parts.push('parameter: ' + Base.formatNumber(parameter)); - if (this._distance != null) - parts.push('distance: ' + Base.formatNumber(this._distance)); - return '{ ' + parts.join(', ') + ' }'; - } -}); - -var PathItem = this.PathItem = Item.extend({ - -}); - -var Path = this.Path = PathItem.extend({ - initialize: function(segments) { - this.base(); - this._closed = false; - this._selectedSegmentState = 0; - this.setSegments(!segments || !Array.isArray(segments) - || typeof segments[0] !== 'object' ? arguments : segments); - }, - - clone: function() { - var copy = this._clone(new Path(this._segments)); - copy._closed = this._closed; - if (this._clockwise !== undefined) - copy._clockwise = this._clockwise; - return copy; - }, - - _changed: function(flags) { - Item.prototype._changed.call(this, flags); - if (flags & ChangeFlag.GEOMETRY) { - delete this._length; - delete this._clockwise; - if (this._curves != null) { - for (var i = 0, l = this._curves.length; i < l; i++) { - this._curves[i]._changed(Change.GEOMETRY); - } - } - } else if (flags & ChangeFlag.STROKE) { - delete this._bounds; - } - }, - - getSegments: function() { - return this._segments; - }, - - setSegments: function(segments) { - if (!this._segments) { - this._segments = []; - } else { - this._selectedSegmentState = 0; - this._segments.length = 0; - if (this._curves) - delete this._curves; - } - this._add(Segment.readAll(segments)); - }, - - getFirstSegment: function() { - return this._segments[0]; - }, - - getLastSegment: function() { - return this._segments[this._segments.length - 1]; - }, - - getCurves: function() { - if (!this._curves) { - var segments = this._segments, - length = segments.length; - if (!this._closed && length > 0) - length--; - this._curves = new Array(length); - for (var i = 0; i < length; i++) - this._curves[i] = Curve.create(this, segments[i], - segments[i + 1] || segments[0]); - } - return this._curves; - }, - - getFirstCurve: function() { - return this.getCurves()[0]; - }, - - getLastCurve: function() { - var curves = this.getCurves(); - return curves[curves.length - 1]; - }, - - getClosed: function() { - return this._closed; - }, - - setClosed: function(closed) { - if (this._closed != (closed = !!closed)) { - this._closed = closed; - if (this._curves) { - var length = this._segments.length, - i; - if (!closed && length > 0) - length--; - this._curves.length = length; - if (closed) - this._curves[i = length - 1] = Curve.create(this, - this._segments[i], this._segments[0]); - } - this._changed(Change.GEOMETRY); - } - }, - - transform: function(matrix) { - return this.base(matrix, true); - }, - - getMatrix: function() { - return null; - }, - - setMatrix: function(matrix) { - }, - - _apply: function(matrix) { - var coords = new Array(6); - for (var i = 0, l = this._segments.length; i < l; i++) { - this._segments[i]._transformCoordinates(matrix, coords, true); - } - var style = this._style, - fillColor = style._fillColor, - strokeColor = style._strokeColor; - if (fillColor && fillColor.transform) - fillColor.transform(matrix); - if (strokeColor && strokeColor.transform) - strokeColor.transform(matrix); - return true; - }, - - _add: function(segs, index) { - var segments = this._segments, - curves = this._curves, - amount = segs.length, - append = index == null, - index = append ? segments.length : index, - fullySelected = this.isFullySelected(); - for (var i = 0; i < amount; i++) { - var segment = segs[i]; - if (segment._path) { - segment = segs[i] = new Segment(segment); - } - segment._path = this; - segment._index = index + i; - if (fullySelected) - segment._selectionState = SelectionState.POINT; - if (segment._selectionState) - this._updateSelection(segment, 0, segment._selectionState); - } - if (append) { - segments.push.apply(segments, segs); - } else { - segments.splice.apply(segments, [index, 0].concat(segs)); - for (var i = index + amount, l = segments.length; i < l; i++) { - segments[i]._index = i; - } - } - if (curves && --index >= 0) { - curves.splice(index, 0, Curve.create(this, segments[index], - segments[index + 1])); - var curve = curves[index + amount]; - if (curve) { - curve._segment1 = segments[index + amount]; - } - } - this._changed(Change.GEOMETRY); - return segs; - }, - - add: function(segment1 ) { - return arguments.length > 1 && typeof segment1 !== 'number' - ? this._add(Segment.readAll(arguments)) - : this._add([ Segment.read(arguments) ])[0]; - }, - - insert: function(index, segment1 ) { - return arguments.length > 2 && typeof segment1 !== 'number' - ? this._add(Segment.readAll(arguments, 1), index) - : this._add([ Segment.read(arguments, 1) ], index)[0]; - }, - - addSegment: function(segment) { - return this._add([ Segment.read(arguments) ])[0]; - }, - - insertSegment: function(index, segment) { - return this._add([ Segment.read(arguments, 1) ], index)[0]; - }, - - addSegments: function(segments) { - return this._add(Segment.readAll(segments)); - }, - - insertSegments: function(index, segments) { - return this._add(Segment.readAll(segments), index); - }, - - removeSegment: function(index) { - var segments = this.removeSegments(index, index + 1); - return segments[0] || null; - }, - - removeSegments: function(from, to) { - from = from || 0; - to = Base.pick(to, this._segments.length); - var segments = this._segments, - curves = this._curves, - last = to >= segments.length, - removed = segments.splice(from, to - from), - amount = removed.length; - if (!amount) - return removed; - for (var i = 0; i < amount; i++) { - var segment = removed[i]; - if (segment._selectionState) - this._updateSelection(segment, segment._selectionState, 0); - removed._index = removed._path = undefined; - } - for (var i = from, l = segments.length; i < l; i++) - segments[i]._index = i; - if (curves) { - curves.splice(from, amount); - var curve; - if (curve = curves[from - 1]) - curve._segment2 = segments[from]; - if (curve = curves[from]) - curve._segment1 = segments[from]; - if (last && this._closed && (curve = curves[curves.length - 1])) - curve._segment2 = segments[0]; - } - this._changed(Change.GEOMETRY); - return removed; - }, - - isFullySelected: function() { - return this._selected && this._selectedSegmentState - == this._segments.length * SelectionState.POINT; - }, - - setFullySelected: function(selected) { - var length = this._segments.length; - this._selectedSegmentState = selected - ? length * SelectionState.POINT : 0; - for (var i = 0; i < length; i++) - this._segments[i]._selectionState = selected - ? SelectionState.POINT : 0; - this.setSelected(selected); - }, - - _updateSelection: function(segment, oldState, newState) { - segment._selectionState = newState; - var total = this._selectedSegmentState += newState - oldState; - if (total > 0) - this.setSelected(true); - }, - - flatten: function(maxDistance) { - var flattener = new PathFlattener(this), - pos = 0, - step = flattener.length / Math.ceil(flattener.length / maxDistance), - end = flattener.length + (this._closed ? -step : step) / 2; - var segments = []; - while (pos <= end) { - segments.push(new Segment(flattener.evaluate(pos, 0))); - pos += step; - } - this.setSegments(segments); - }, - - simplify: function(tolerance) { - if (this._segments.length > 2) { - var fitter = new PathFitter(this, tolerance || 2.5); - this.setSegments(fitter.fit()); - } - }, - - isClockwise: function() { - if (this._clockwise !== undefined) - return this._clockwise; - var sum = 0, - xPre, yPre; - function edge(x, y) { - if (xPre !== undefined) - sum += (xPre - x) * (y + yPre); - xPre = x; - yPre = y; - } - for (var i = 0, l = this._segments.length; i < l; i++) { - var seg1 = this._segments[i], - seg2 = this._segments[i + 1 < l ? i + 1 : 0], - point1 = seg1._point, - handle1 = seg1._handleOut, - handle2 = seg2._handleIn, - point2 = seg2._point; - edge(point1._x, point1._y); - edge(point1._x + handle1._x, point1._y + handle1._y); - edge(point2._x + handle2._x, point2._y + handle2._y); - edge(point2._x, point2._y); - } - return sum > 0; - }, - - setClockwise: function(clockwise) { - if (this.isClockwise() != (clockwise = !!clockwise)) { - this.reverse(); - this._clockwise = clockwise; - } - }, - - reverse: function() { - this._segments.reverse(); - for (var i = 0, l = this._segments.length; i < l; i++) { - var segment = this._segments[i]; - var handleIn = segment._handleIn; - segment._handleIn = segment._handleOut; - segment._handleOut = handleIn; - segment._index = i; - } - if (this._clockwise !== undefined) - this._clockwise = !this._clockwise; - }, - - join: function(path) { - if (path) { - var segments = path._segments, - last1 = this.getLastSegment(), - last2 = path.getLastSegment(); - if (last1._point.equals(last2._point)) - path.reverse(); - var first2 = path.getFirstSegment(); - if (last1._point.equals(first2._point)) { - last1.setHandleOut(first2._handleOut); - this._add(segments.slice(1)); - } else { - var first1 = this.getFirstSegment(); - if (first1._point.equals(first2._point)) - path.reverse(); - last2 = path.getLastSegment(); - if (first1._point.equals(last2._point)) { - first1.setHandleIn(last2._handleIn); - this._add(segments.slice(0, segments.length - 1), 0); - } else { - this._add(segments.slice(0)); - } - } - path.remove(); - var first1 = this.getFirstSegment(); - last1 = this.getLastSegment(); - if (last1._point.equals(first1._point)) { - first1.setHandleIn(last1._handleIn); - last1.remove(); - this.setClosed(true); - } - this._changed(Change.GEOMETRY); - return true; - } - return false; - }, - - getLength: function() { - if (this._length == null) { - var curves = this.getCurves(); - this._length = 0; - for (var i = 0, l = curves.length; i < l; i++) - this._length += curves[i].getLength(); - } - return this._length; - }, - - _getOffset: function(location) { - var index = location && location.getIndex(); - if (index != null) { - var curves = this.getCurves(), - offset = 0; - for (var i = 0; i < index; i++) - offset += curves[i].getLength(); - var curve = curves[index]; - return offset + curve.getLength(0, location.getParameter()); - } - return null; - }, - - getLocation: function(point) { - var curves = this.getCurves(); - for (var i = 0, l = curves.length; i < l; i++) { - var curve = curves[i]; - var t = curve.getParameter(point); - if (t != null) - return new CurveLocation(curve, t); - } - return null; - }, - - getLocationAt: function(offset, isParameter) { - var curves = this.getCurves(), - length = 0; - if (isParameter) { - var index = ~~offset; - return new CurveLocation(curves[index], offset - index); - } - for (var i = 0, l = curves.length; i < l; i++) { - var start = length, - curve = curves[i]; - length += curve.getLength(); - if (length >= offset) { - return new CurveLocation(curve, - curve.getParameterAt(offset - start)); - } - } - if (offset <= this.getLength()) - return new CurveLocation(curves[curves.length - 1], 1); - return null; - }, - - getPointAt: function(offset, isParameter) { - var loc = this.getLocationAt(offset, isParameter); - return loc && loc.getPoint(); - }, - - getTangentAt: function(offset, isParameter) { - var loc = this.getLocationAt(offset, isParameter); - return loc && loc.getTangent(); - }, - - getNormalAt: function(offset, isParameter) { - var loc = this.getLocationAt(offset, isParameter); - return loc && loc.getNormal(); - }, - - getNearestLocation: function(point) { - var curves = this.getCurves(), - minDist = Infinity, - minLoc = null; - for (var i = 0, l = curves.length; i < l; i++) { - var loc = curves[i].getNearestLocation(point); - if (loc._distance < minDist) { - minDist = loc._distance; - minLoc = loc; - } - } - return minLoc; - }, - - getNearestPoint: function(point) { - return this.getNearestLocation(point).getPoint(); - }, - - contains: function(point) { - point = Point.read(arguments); - if (!this._closed || !this.getRoughBounds()._containsPoint(point)) - return false; - var curves = this.getCurves(), - crossings = 0, - roots = []; - for (var i = 0, l = curves.length; i < l; i++) - crossings += curves[i].getCrossings(point, roots); - return (crossings & 1) == 1; - }, - - _hitTest: function(point, options) { - var style = this._style, - tolerance = options.tolerance || 0, - radius = (options.stroke && style._strokeColor - ? style._strokeWidth / 2 : 0) + tolerance, - loc, - res; - var coords = [], - that = this; - function checkPoint(seg, pt, name) { - if (point.getDistance(pt) < tolerance) - return new HitResult(name, that, { segment: seg, point: pt }); - } - function checkSegment(seg, ends) { - var point = seg._point; - return (ends || options.segments) - && checkPoint(seg, point, 'segment') - || (!ends && options.handles) && ( - checkPoint(seg, point.add(seg._handleIn), 'handle-in') || - checkPoint(seg, point.add(seg._handleOut), 'handle-out')); - } - if (options.ends && !options.segments && !this._closed) { - if (res = checkSegment(this.getFirstSegment(), true) - || checkSegment(this.getLastSegment(), true)) - return res; - } else if (options.segments || options.handles) { - for (var i = 0, l = this._segments.length; i < l; i++) { - if (res = checkSegment(this._segments[i])) - return res; - } - } - if (options.stroke && radius > 0) - loc = this.getNearestLocation(point); - if (!(loc && loc._distance <= radius) && options.fill - && style._fillColor && this.contains(point)) - return new HitResult('fill', this); - if (!loc && options.stroke && radius > 0) - loc = this.getNearestLocation(point); - if (loc && loc._distance <= radius) - return options.stroke - ? new HitResult('stroke', this, { location: loc }) - : new HitResult('fill', this); - } - -}, new function() { - - function drawHandles(ctx, segments, matrix) { - var coords = new Array(6); - for (var i = 0, l = segments.length; i < l; i++) { - var segment = segments[i]; - segment._transformCoordinates(matrix, coords, false); - var state = segment._selectionState, - selected = state & SelectionState.POINT, - pX = coords[0], - pY = coords[1]; - - function drawHandle(index) { - var hX = coords[index], - hY = coords[index + 1]; - if (pX != hX || pY != hY) { - ctx.beginPath(); - ctx.moveTo(pX, pY); - ctx.lineTo(hX, hY); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(hX, hY, 1.75, 0, Math.PI * 2, true); - ctx.fill(); - } - } - - if (selected || (state & SelectionState.HANDLE_IN)) - drawHandle(2); - if (selected || (state & SelectionState.HANDLE_OUT)) - drawHandle(4); - ctx.save(); - ctx.beginPath(); - ctx.rect(pX - 2, pY - 2, 4, 4); - ctx.fill(); - if (!selected) { - ctx.beginPath(); - ctx.rect(pX - 1, pY - 1, 2, 2); - ctx.fillStyle = '#ffffff'; - ctx.fill(); - } - ctx.restore(); - } - } - - function drawSegments(ctx, path, matrix) { - var segments = path._segments, - length = segments.length, - coords = new Array(6), - first = true, - pX, pY, - inX, inY, - outX, outY; - - function drawSegment(i) { - var segment = segments[i]; - if (matrix) { - segment._transformCoordinates(matrix, coords, false); - pX = coords[0]; - pY = coords[1]; - } else { - var point = segment._point; - pX = point._x; - pY = point._y; - } - if (first) { - ctx.moveTo(pX, pY); - first = false; - } else { - if (matrix) { - inX = coords[2]; - inY = coords[3]; - } else { - var handle = segment._handleIn; - inX = pX + handle._x; - inY = pY + handle._y; - } - if (inX == pX && inY == pY && outX == pX && outY == pY) { - ctx.lineTo(pX, pY); - } else { - ctx.bezierCurveTo(outX, outY, inX, inY, pX, pY); - } - } - if (matrix) { - outX = coords[4]; - outY = coords[5]; - } else { - var handle = segment._handleOut; - outX = pX + handle._x; - outY = pY + handle._y; - } - } - - for (var i = 0; i < length; i++) - drawSegment(i); - if (path._closed && length > 1) - drawSegment(0); - } - - return { - draw: function(ctx, param) { - if (!param.compound) - ctx.beginPath(); - - var style = this._style, - fillColor = style._fillColor, - strokeColor = style._strokeColor, - dashArray = style._dashArray, - hasDash = strokeColor && dashArray && dashArray.length; - - if (param.compound || this._clipMask || fillColor - || strokeColor && !hasDash) { - drawSegments(ctx, this); - } - - if (this._closed) - ctx.closePath(); - - if (this._clipMask) { - ctx.clip(); - } else if (!param.compound && (fillColor || strokeColor)) { - ctx.save(); - this._setStyles(ctx); - if (fillColor) - ctx.fill(); - if (strokeColor) { - if (hasDash) { - ctx.beginPath(); - var flattener = new PathFlattener(this), - from = style._dashOffset, to, - i = 0; - while (from < flattener.length) { - to = from + dashArray[(i++) % dashArray.length]; - flattener.drawPart(ctx, from, to); - from = to + dashArray[(i++) % dashArray.length]; - } - } - ctx.stroke(); - } - ctx.restore(); - } - }, - - drawSelected: function(ctx, matrix) { - ctx.beginPath(); - drawSegments(ctx, this, matrix); - ctx.stroke(); - drawHandles(ctx, this._segments, matrix); - } - }; -}, new function() { - - function getFirstControlPoints(rhs) { - var n = rhs.length, - x = [], - tmp = [], - b = 2; - x[0] = rhs[0] / b; - for (var i = 1; i < n; i++) { - tmp[i] = 1 / b; - b = (i < n - 1 ? 4 : 2) - tmp[i]; - x[i] = (rhs[i] - x[i - 1]) / b; - } - for (var i = 1; i < n; i++) { - x[n - i - 1] -= tmp[n - i] * x[n - i]; - } - return x; - }; - - return { - smooth: function() { - var segments = this._segments, - size = segments.length, - n = size, - overlap; - - if (size <= 2) - return; - - if (this._closed) { - overlap = Math.min(size, 4); - n += Math.min(size, overlap) * 2; - } else { - overlap = 0; - } - var knots = []; - for (var i = 0; i < size; i++) - knots[i + overlap] = segments[i]._point; - if (this._closed) { - for (var i = 0; i < overlap; i++) { - knots[i] = segments[i + size - overlap]._point; - knots[i + size + overlap] = segments[i]._point; - } - } else { - n--; - } - var rhs = []; - - for (var i = 1; i < n - 1; i++) - rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x; - rhs[0] = knots[0]._x + 2 * knots[1]._x; - rhs[n - 1] = 3 * knots[n - 1]._x; - var x = getFirstControlPoints(rhs); - - for (var i = 1; i < n - 1; i++) - rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y; - rhs[0] = knots[0]._y + 2 * knots[1]._y; - rhs[n - 1] = 3 * knots[n - 1]._y; - var y = getFirstControlPoints(rhs); - - if (this._closed) { - for (var i = 0, j = size; i < overlap; i++, j++) { - var f1 = (i / overlap); - var f2 = 1 - f1; - x[j] = x[i] * f1 + x[j] * f2; - y[j] = y[i] * f1 + y[j] * f2; - var ie = i + overlap, je = j + overlap; - x[je] = x[ie] * f2 + x[je] * f1; - y[je] = y[ie] * f2 + y[je] * f1; - } - n--; - } - var handleIn = null; - for (var i = overlap; i <= n - overlap; i++) { - var segment = segments[i - overlap]; - if (handleIn) - segment.setHandleIn(handleIn.subtract(segment._point)); - if (i < n) { - segment.setHandleOut( - Point.create(x[i], y[i]).subtract(segment._point)); - if (i < n - 1) - handleIn = Point.create( - 2 * knots[i + 1]._x - x[i + 1], - 2 * knots[i + 1]._y - y[i + 1]); - else - handleIn = Point.create( - (knots[n]._x + x[n - 1]) / 2, - (knots[n]._y + y[n - 1]) / 2); - } - } - if (this._closed && handleIn) { - var segment = this._segments[0]; - segment.setHandleIn(handleIn.subtract(segment._point)); - } - } - }; -}, new function() { - function getCurrentSegment(that) { - var segments = that._segments; - if (segments.length == 0) - throw new Error('Use a moveTo() command first'); - return segments[segments.length - 1]; - } - - return { - moveTo: function(point) { - if (!this._segments.length) - this._add([ new Segment(Point.read(arguments)) ]); - }, - - moveBy: function(point) { - throw new Error('moveBy() is unsupported on Path items.'); - }, - - lineTo: function(point) { - this._add([ new Segment(Point.read(arguments)) ]); - }, - - cubicCurveTo: function(handle1, handle2, to) { - handle1 = Point.read(arguments, 0, 1); - handle2 = Point.read(arguments, 1, 1); - to = Point.read(arguments, 2, 1); - var current = getCurrentSegment(this); - current.setHandleOut(handle1.subtract(current._point)); - this._add([ new Segment(to, handle2.subtract(to)) ]); - }, - - quadraticCurveTo: function(handle, to) { - handle = Point.read(arguments, 0, 1); - to = Point.read(arguments, 1, 1); - var current = getCurrentSegment(this)._point; - this.cubicCurveTo( - handle.add(current.subtract(handle).multiply(1/3)), - handle.add(to.subtract(handle).multiply(1/3)), - to - ); - }, - - curveTo: function(through, to, parameter) { - through = Point.read(arguments, 0, 1); - to = Point.read(arguments, 1, 1); - var t = Base.pick(parameter, 0.5), - t1 = 1 - t, - current = getCurrentSegment(this)._point, - handle = through.subtract(current.multiply(t1 * t1)) - .subtract(to.multiply(t * t)).divide(2 * t * t1); - if (handle.isNaN()) - throw new Error( - 'Cannot put a curve through points with parameter = ' + t); - this.quadraticCurveTo(handle, to); - }, - - arcTo: function(to, clockwise ) { - var current = getCurrentSegment(this), - from = current._point, - through; - if (clockwise === undefined) - clockwise = true; - if (typeof clockwise === 'boolean') { - to = Point.read(arguments, 0, 1); - var middle = from.add(to).divide(2), - through = middle.add(middle.subtract(from).rotate( - clockwise ? -90 : 90)); - } else { - through = Point.read(arguments, 0, 1); - to = Point.read(arguments, 1, 1); - } - var l1 = new Line(from.add(through).divide(2), - through.subtract(from).rotate(90)), - l2 = new Line(through.add(to).divide(2), - to.subtract(through).rotate(90)), - center = l1.intersect(l2), - line = new Line(from, to, true), - throughSide = line.getSide(through); - if (!center) { - if (!throughSide) - return this.lineTo(to); - throw new Error("Cannot put an arc through the given points: " - + [from, through, to]); - } - var vector = from.subtract(center), - radius = vector.getLength(), - extent = vector.getDirectedAngle(to.subtract(center)), - centerSide = line.getSide(center); - if (centerSide == 0) { - extent = throughSide * Math.abs(extent); - } else if (throughSide == centerSide) { - extent -= 360 * (extent < 0 ? -1 : 1); - } - var ext = Math.abs(extent), - count = ext >= 360 ? 4 : Math.ceil(ext / 90), - inc = extent / count, - half = inc * Math.PI / 360, - z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)), - segments = []; - for (var i = 0; i <= count; i++) { - var pt = i < count ? center.add(vector) : to; - var out = i < count ? vector.rotate(90).multiply(z) : null; - if (i == 0) { - current.setHandleOut(out); - } else { - segments.push( - new Segment(pt, vector.rotate(-90).multiply(z), out)); - } - vector = vector.rotate(inc); - } - this._add(segments); - }, - - lineBy: function(vector) { - vector = Point.read(arguments); - var current = getCurrentSegment(this); - this.lineTo(current._point.add(vector)); - }, - - curveBy: function(throughVector, toVector, parameter) { - throughVector = Point.read(throughVector); - toVector = Point.read(toVector); - var current = getCurrentSegment(this)._point; - this.curveTo(current.add(throughVector), current.add(toVector), - parameter); - }, - - arcBy: function(throughVector, toVector) { - throughVector = Point.read(throughVector); - toVector = Point.read(toVector); - var current = getCurrentSegment(this)._point; - this.arcBy(current.add(throughVector), current.add(toVector)); - }, - - closePath: function() { - this.setClosed(true); - } - }; -}, new function() { - function getBounds(matrix, strokePadding) { - var segments = this._segments, - first = segments[0]; - if (!first) - return null; - var coords = new Array(6), - prevCoords = new Array(6); - first._transformCoordinates(matrix, prevCoords, false); - var min = prevCoords.slice(0, 2), - max = min.slice(0), - tMin = Numerical.TOLERANCE, - tMax = 1 - tMin; - function processSegment(segment) { - segment._transformCoordinates(matrix, coords, false); - - for (var i = 0; i < 2; i++) { - var v0 = prevCoords[i], - v1 = prevCoords[i + 4], - v2 = coords[i + 2], - v3 = coords[i]; - - function add(value, t) { - var padding = 0; - if (value == null) { - var u = 1 - t; - value = u * u * u * v0 - + 3 * u * u * t * v1 - + 3 * u * t * t * v2 - + t * t * t * v3; - padding = strokePadding ? strokePadding[i] : 0; - } - var left = value - padding, - right = value + padding; - if (left < min[i]) - min[i] = left; - if (right > max[i]) - max[i] = right; - - } - add(v3, null); - - var a = 3 * (v1 - v2) - v0 + v3, - b = 2 * (v0 + v2) - 4 * v1, - c = v1 - v0; - - if (a == 0) { - if (b == 0) - continue; - var t = -c / b; - if (tMin < t && t < tMax) - add(null, t); - continue; - } - - var q = b * b - 4 * a * c; - if (q < 0) - continue; - var sqrt = Math.sqrt(q), - f = -0.5 / a, - t1 = (b - sqrt) * f, - t2 = (b + sqrt) * f; - if (tMin < t1 && t1 < tMax) - add(null, t1); - if (tMin < t2 && t2 < tMax) - add(null, t2); - } - var tmp = prevCoords; - prevCoords = coords; - coords = tmp; - } - for (var i = 1, l = segments.length; i < l; i++) - processSegment(segments[i]); - if (this._closed) - processSegment(first); - return Rectangle.create(min[0], min[1], - max[0] - min[0], max[1] - min[1]); - } - - function getPenPadding(radius, matrix) { - if (!matrix) - return [radius, radius]; - var mx = matrix.createShiftless(), - hor = mx.transform(Point.create(radius, 0)), - ver = mx.transform(Point.create(0, radius)), - phi = hor.getAngleInRadians(), - a = hor.getLength(), - b = ver.getLength(); - var sin = Math.sin(phi), - cos = Math.cos(phi), - tan = Math.tan(phi), - tx = -Math.atan(b * tan / a), - ty = Math.atan(b / (tan * a)); - return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin), - Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)]; - } - - function getStrokeBounds(matrix) { - var style = this._style; - if (!style._strokeColor || !style._strokeWidth) - return getBounds.call(this, matrix); - var width = style._strokeWidth, - radius = width / 2, - padding = getPenPadding(radius, matrix), - join = style._strokeJoin, - cap = style._strokeCap, - miter = style._miterLimit * width / 2, - segments = this._segments, - length = segments.length, - bounds = getBounds.call(this, matrix, padding); - var joinBounds = new Rectangle(new Size(padding).multiply(2)); - - function add(point) { - bounds = bounds.include(matrix - ? matrix._transformPoint(point, point) : point); - } - - function addBevelJoin(curve, t) { - var point = curve.getPoint(t), - normal = curve.getNormal(t).normalize(radius); - add(point.add(normal)); - add(point.subtract(normal)); - } - - function addJoin(segment, join) { - if (join === 'round' || !segment._handleIn.isZero() - && !segment._handleOut.isZero()) { - bounds = bounds.unite(joinBounds.setCenter(matrix - ? matrix._transformPoint(segment._point) : segment._point)); - } else if (join == 'bevel') { - var curve = segment.getCurve(); - addBevelJoin(curve, 0); - addBevelJoin(curve.getPrevious(), 1); - } else if (join == 'miter') { - var curve2 = segment.getCurve(), - curve1 = curve2.getPrevious(), - point = curve2.getPoint(0), - normal1 = curve1.getNormal(1).normalize(radius), - normal2 = curve2.getNormal(0).normalize(radius), - line1 = new Line(point.subtract(normal1), - Point.create(-normal1.y, normal1.x)), - line2 = new Line(point.subtract(normal2), - Point.create(-normal2.y, normal2.x)), - corner = line1.intersect(line2); - if (!corner || point.getDistance(corner) > miter) { - addJoin(segment, 'bevel'); - } else { - add(corner); - } - } - } - - function addCap(segment, cap, t) { - switch (cap) { - case 'round': - return addJoin(segment, cap); - case 'butt': - case 'square': - var curve = segment.getCurve(), - point = curve.getPoint(t), - normal = curve.getNormal(t).normalize(radius); - if (cap === 'square') - point = point.add(normal.rotate(t == 0 ? -90 : 90)); - add(point.add(normal)); - add(point.subtract(normal)); - break; - } - } - - for (var i = 1, l = length - (this._closed ? 0 : 1); i < l; i++) { - addJoin(segments[i], join); - } - if (this._closed) { - addJoin(segments[0], join); - } else { - addCap(segments[0], cap, 0); - addCap(segments[length - 1], cap, 1); - } - return bounds; - } - - function getHandleBounds(matrix, stroke, join) { - var coords = new Array(6), - x1 = Infinity, - x2 = -x1, - y1 = x1, - y2 = x2; - stroke = stroke / 2 || 0; - join = join / 2 || 0; - for (var i = 0, l = this._segments.length; i < l; i++) { - var segment = this._segments[i]; - segment._transformCoordinates(matrix, coords, false); - for (var j = 0; j < 6; j += 2) { - var padding = j == 0 ? join : stroke, - x = coords[j], - y = coords[j + 1], - xn = x - padding, - xx = x + padding, - yn = y - padding, - yx = y + padding; - if (xn < x1) x1 = xn; - if (xx > x2) x2 = xx; - if (yn < y1) y1 = yn; - if (yx > y2) y2 = yx; - } - } - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - } - - function getRoughBounds(matrix) { - var style = this._style, - width = style._strokeWidth; - return getHandleBounds.call(this, matrix, width, - style._strokeJoin == 'miter' - ? width * style._miterLimit - : width); - } - - var get = { - bounds: getBounds, - strokeBounds: getStrokeBounds, - handleBounds: getHandleBounds, - roughBounds: getRoughBounds - }; - - return { - _getBounds: function(type, matrix) { - return get[type].call(this, matrix); - } - }; -}); - -Path.inject({ statics: new function() { - var kappa = 2 / 3 * (Math.sqrt(2) - 1); - - var ovalSegments = [ - new Segment([0, 0.5], [0, kappa ], [0, -kappa]), - new Segment([0.5, 0], [-kappa, 0], [kappa, 0 ]), - new Segment([1, 0.5], [0, -kappa], [0, kappa ]), - new Segment([0.5, 1], [kappa, 0 ], [-kappa, 0]) - ]; - - return { - Line: function() { - var step = Math.floor(arguments.length / 2); - return new Path( - Segment.read(arguments, 0, step), - Segment.read(arguments, step, step) - ); - }, - - Rectangle: function(rect) { - rect = Rectangle.read(arguments); - var left = rect.x, - top = rect.y, - right = left + rect.width, - bottom = top + rect.height, - path = new Path(); - path._add([ - new Segment(Point.create(left, bottom)), - new Segment(Point.create(left, top)), - new Segment(Point.create(right, top)), - new Segment(Point.create(right, bottom)) - ]); - path._closed = true; - return path; - }, - - RoundRectangle: function(rect, size) { - if (arguments.length == 2) { - rect = Rectangle.read(arguments, 0, 1); - size = Size.read(arguments, 1, 1); - } else if (arguments.length == 6) { - rect = Rectangle.read(arguments, 0, 4); - size = Size.read(arguments, 4, 2); - } - size = Size.min(size, rect.getSize(true).divide(2)); - var path = new Path(), - uSize = size.multiply(kappa * 2), - bl = rect.getBottomLeft(true), - tl = rect.getTopLeft(true), - tr = rect.getTopRight(true), - br = rect.getBottomRight(true); - path._add([ - new Segment(bl.add(size.width, 0), null, [-uSize.width, 0]), - new Segment(bl.subtract(0, size.height), [0, uSize.height], null), - - new Segment(tl.add(0, size.height), null, [0, -uSize.height]), - new Segment(tl.add(size.width, 0), [-uSize.width, 0], null), - - new Segment(tr.subtract(size.width, 0), null, [uSize.width, 0]), - new Segment(tr.add(0, size.height), [0, -uSize.height], null), - - new Segment(br.subtract(0, size.height), null, [0, uSize.height]), - new Segment(br.subtract(size.width, 0), [uSize.width, 0], null) - ]); - path._closed = true; - return path; - }, - - Oval: function(rect) { - rect = Rectangle.read(arguments); - var path = new Path(), - point = rect.getPoint(true), - size = rect.getSize(true), - segments = new Array(4); - for (var i = 0; i < 4; i++) { - var segment = ovalSegments[i]; - segments[i] = new Segment( - segment._point.multiply(size).add(point), - segment._handleIn.multiply(size), - segment._handleOut.multiply(size) - ); - } - path._add(segments); - path._closed = true; - return path; - }, - - Circle: function(center, radius) { - if (arguments.length == 3) { - center = Point.read(arguments, 0, 2); - radius = arguments[2]; - } else { - center = Point.read(arguments, 0, 1); - } - return Path.Oval(new Rectangle(center.subtract(radius), - Size.create(radius * 2, radius * 2))); - }, - - Arc: function(from, through, to) { - var path = new Path(); - path.moveTo(from); - path.arcTo(through, to); - return path; - }, - - RegularPolygon: function(center, numSides, radius) { - center = Point.read(arguments, 0, 1); - var path = new Path(), - step = 360 / numSides, - three = !(numSides % 3), - vector = new Point(0, three ? -radius : radius), - offset = three ? -1 : 0.5, - segments = new Array(numSides); - for (var i = 0; i < numSides; i++) { - segments[i] = new Segment(center.add( - vector.rotate((i + offset) * step))); - } - path._add(segments); - path._closed = true; - return path; - }, - - Star: function(center, numPoints, radius1, radius2) { - center = Point.read(arguments, 0, 1); - numPoints *= 2; - var path = new Path(), - step = 360 / numPoints, - vector = new Point(0, -1), - segments = new Array(numPoints); - for (var i = 0; i < numPoints; i++) { - segments[i] = new Segment(center.add( - vector.rotate(step * i).multiply(i % 2 ? radius2 : radius1))); - } - path._add(segments); - path._closed = true; - return path; - } - }; -}}); - -var CompoundPath = this.CompoundPath = PathItem.extend({ - initialize: function(paths) { - this.base(); - this._children = []; - this._namedChildren = {}; - var items = !paths || !Array.isArray(paths) - || typeof paths[0] !== 'object' ? arguments : paths; - this.addChildren(items); - }, - - insertChild: function(index, item) { - this.base(index, item); - if (item._clockwise === undefined) - item.setClockwise(item._index == 0); - }, - - simplify: function() { - if (this._children.length == 1) { - var child = this._children[0]; - child.insertAbove(this); - this.remove(); - return child; - } - return this; - }, - - smooth: function() { - for (var i = 0, l = this._children.length; i < l; i++) - this._children[i].smooth(); - }, - - draw: function(ctx, param) { - var children = this._children; - if (children.length == 0) - return; - var firstChild = children[0], - style = firstChild._style; - ctx.beginPath(); - param.compound = true; - for (var i = 0, l = children.length; i < l; i++) - Item.draw(children[i], ctx, param); - firstChild._setStyles(ctx); - if (style._fillColor) - ctx.fill(); - if (style._strokeColor) - ctx.stroke(); - param.compound = false; - } -}, new function() { - function getCurrentPath(that) { - if (!that._children.length) - throw new Error('Use a moveTo() command first'); - return that._children[that._children.length - 1]; - } - - var fields = { - moveTo: function(point) { - var path = new Path(); - this.addChild(path); - path.moveTo.apply(path, arguments); - }, - - moveBy: function(point) { - this.moveTo(getCurrentPath(this).getLastSegment()._point.add( - Point.read(arguments))); - }, - - closePath: function() { - getCurrentPath(this).setClosed(true); - } - }; - - Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', - 'arcTo', 'lineBy', 'curveBy', 'arcBy'], function(key) { - fields[key] = function() { - var path = getCurrentPath(this); - path[key].apply(path, arguments); - }; - }); - - return fields; -}); - -var PathFlattener = Base.extend({ - initialize: function(path) { - this.curves = []; - this.parts = []; - this.length = 0; - this.index = 0; - - var segments = path._segments, - segment1 = segments[0], - segment2, - that = this; - - function addCurve(segment1, segment2) { - var curve = Curve.getValues(segment1, segment2); - that.curves.push(curve); - that._computeParts(curve, segment1._index, 0, 1); - } - - for (var i = 1, l = segments.length; i < l; i++) { - segment2 = segments[i]; - addCurve(segment1, segment2); - segment1 = segment2; - } - if (path._closed) - addCurve(segment2, segments[0]); - }, - - _computeParts: function(curve, index, minT, maxT) { - if ((maxT - minT) > 1 / 32 && !Curve.isFlatEnough(curve)) { - var curves = Curve.subdivide(curve); - var halfT = (minT + maxT) / 2; - this._computeParts(curves[0], index, minT, halfT); - this._computeParts(curves[1], index, halfT, maxT); - } else { - var x = curve[6] - curve[0], - y = curve[7] - curve[1], - dist = Math.sqrt(x * x + y * y); - if (dist > Numerical.TOLERANCE) { - this.length += dist; - this.parts.push({ - offset: this.length, - value: maxT, - index: index - }); - } - } - }, - - getParameterAt: function(offset) { - var i, j = this.index; - for (;;) { - i = j; - if (j == 0 || this.parts[--j].offset < offset) - break; - } - for (var l = this.parts.length; i < l; i++) { - var part = this.parts[i]; - if (part.offset >= offset) { - this.index = i; - var prev = this.parts[i - 1]; - var prevVal = prev && prev.index == part.index ? prev.value : 0, - prevLen = prev ? prev.offset : 0; - return { - value: prevVal + (part.value - prevVal) - * (offset - prevLen) / (part.offset - prevLen), - index: part.index - }; - } - } - var part = this.parts[this.parts.length - 1]; - return { - value: 1, - index: part.index - }; - }, - - evaluate: function(offset, type) { - var param = this.getParameterAt(offset); - return Curve.evaluate(this.curves[param.index], param.value, type); - }, - - drawPart: function(ctx, from, to) { - from = this.getParameterAt(from); - to = this.getParameterAt(to); - for (var i = from.index; i <= to.index; i++) { - var curve = Curve.getPart(this.curves[i], - i == from.index ? from.value : 0, - i == to.index ? to.value : 1); - if (i == from.index) - ctx.moveTo(curve[0], curve[1]); - ctx.bezierCurveTo.apply(ctx, curve.slice(2)); - } - } -}); - -var PathFitter = Base.extend({ - initialize: function(path, error) { - this.points = []; - var segments = path._segments, - prev; - for (var i = 0, l = segments.length; i < l; i++) { - var point = segments[i].point.clone(); - if (!prev || !prev.equals(point)) { - this.points.push(point); - prev = point; - } - } - this.error = error; - }, - - fit: function() { - this.segments = [new Segment(this.points[0])]; - this.fitCubic(0, this.points.length - 1, - this.points[1].subtract(this.points[0]).normalize(), - this.points[this.points.length - 2].subtract( - this.points[this.points.length - 1]).normalize()); - return this.segments; - }, - - fitCubic: function(first, last, tan1, tan2) { - if (last - first == 1) { - var pt1 = this.points[first], - pt2 = this.points[last], - dist = pt1.getDistance(pt2) / 3; - this.addCurve([pt1, pt1.add(tan1.normalize(dist)), - pt2.add(tan2.normalize(dist)), pt2]); - return; - } - var uPrime = this.chordLengthParameterize(first, last), - maxError = Math.max(this.error, this.error * this.error), - error, - split; - for (var i = 0; i <= 4; i++) { - var curve = this.generateBezier(first, last, uPrime, tan1, tan2); - var max = this.findMaxError(first, last, curve, uPrime); - if (max.error < this.error) { - this.addCurve(curve); - return; - } - split = max.index; - if (max.error >= maxError) - break; - this.reparameterize(first, last, uPrime, curve); - maxError = max.error; - } - var V1 = this.points[split - 1].subtract(this.points[split]), - V2 = this.points[split].subtract(this.points[split + 1]), - tanCenter = V1.add(V2).divide(2).normalize(); - this.fitCubic(first, split, tan1, tanCenter); - this.fitCubic(split, last, tanCenter.negate(), tan2); - }, - - addCurve: function(curve) { - var prev = this.segments[this.segments.length - 1]; - prev.setHandleOut(curve[1].subtract(curve[0])); - this.segments.push( - new Segment(curve[3], curve[2].subtract(curve[3]))); - }, - - generateBezier: function(first, last, uPrime, tan1, tan2) { - var epsilon = Numerical.EPSILON, - pt1 = this.points[first], - pt2 = this.points[last], - C = [[0, 0], [0, 0]], - X = [0, 0]; - - for (var i = 0, l = last - first + 1; i < l; i++) { - var u = uPrime[i], - t = 1 - u, - b = 3 * u * t, - b0 = t * t * t, - b1 = b * t, - b2 = b * u, - b3 = u * u * u, - a1 = tan1.normalize(b1), - a2 = tan2.normalize(b2), - tmp = this.points[first + i] - .subtract(pt1.multiply(b0 + b1)) - .subtract(pt2.multiply(b2 + b3)); - C[0][0] += a1.dot(a1); - C[0][1] += a1.dot(a2); - C[1][0] = C[0][1]; - C[1][1] += a2.dot(a2); - X[0] += a1.dot(tmp); - X[1] += a2.dot(tmp); - } - - var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1], - alpha1, alpha2; - if (Math.abs(detC0C1) > epsilon) { - var detC0X = C[0][0] * X[1] - C[1][0] * X[0], - detXC1 = X[0] * C[1][1] - X[1] * C[0][1]; - alpha1 = detXC1 / detC0C1; - alpha2 = detC0X / detC0C1; - } else { - var c0 = C[0][0] + C[0][1], - c1 = C[1][0] + C[1][1]; - if (Math.abs(c0) > epsilon) { - alpha1 = alpha2 = X[0] / c0; - } else if (Math.abs(c1) > epsilon) { - alpha1 = alpha2 = X[1] / c1; - } else { - alpha1 = alpha2 = 0.; - } - } - - var segLength = pt2.getDistance(pt1); - epsilon *= segLength; - if (alpha1 < epsilon || alpha2 < epsilon) { - alpha1 = alpha2 = segLength / 3; - } - - return [pt1, pt1.add(tan1.normalize(alpha1)), - pt2.add(tan2.normalize(alpha2)), pt2]; - }, - - reparameterize: function(first, last, u, curve) { - for (var i = first; i <= last; i++) { - u[i - first] = this.findRoot(curve, this.points[i], u[i - first]); - } - }, - - findRoot: function(curve, point, u) { - var curve1 = [], - curve2 = []; - for (var i = 0; i <= 2; i++) { - curve1[i] = curve[i + 1].subtract(curve[i]).multiply(3); - } - for (var i = 0; i <= 1; i++) { - curve2[i] = curve1[i + 1].subtract(curve1[i]).multiply(2); - } - var pt = this.evaluate(3, curve, u), - pt1 = this.evaluate(2, curve1, u), - pt2 = this.evaluate(1, curve2, u), - diff = pt.subtract(point), - df = pt1.dot(pt1) + diff.dot(pt2); - if (Math.abs(df) < Numerical.TOLERANCE) - return u; - return u - diff.dot(pt1) / df; - }, - - evaluate: function(degree, curve, t) { - var tmp = curve.slice(); - for (var i = 1; i <= degree; i++) { - for (var j = 0; j <= degree - i; j++) { - tmp[j] = tmp[j].multiply(1 - t).add(tmp[j + 1].multiply(t)); - } - } - return tmp[0]; - }, - - chordLengthParameterize: function(first, last) { - var u = [0]; - for (var i = first + 1; i <= last; i++) { - u[i - first] = u[i - first - 1] - + this.points[i].getDistance(this.points[i - 1]); - } - for (var i = 1, m = last - first; i <= m; i++) { - u[i] /= u[m]; - } - return u; - }, - - findMaxError: function(first, last, curve, u) { - var index = Math.floor((last - first + 1) / 2), - maxDist = 0; - for (var i = first + 1; i < last; i++) { - var P = this.evaluate(3, curve, u[i - first]); - var v = P.subtract(this.points[i]); - var dist = v.x * v.x + v.y * v.y; - if (dist >= maxDist) { - maxDist = dist; - index = i; - } - } - return { - error: maxDist, - index: index - }; - } -}); - -var TextItem = this.TextItem = Item.extend({ - _boundsType: 'bounds', - - initialize: function(pointOrMatrix) { - this._style = CharacterStyle.create(this); - this._paragraphStyle = ParagraphStyle.create(this); - this.base(pointOrMatrix); - this.setParagraphStyle(); - this._content = ''; - this._lines = []; - }, - - _clone: function(copy) { - copy.setContent(this._content); - copy.setParagraphStyle(this._paragraphStyle); - return this.base(copy); - }, - - getContent: function() { - return this._content; - }, - - setContent: function(content) { - this._content = '' + content; - this._lines = this._content.split(/\r\n|\n|\r/mg); - this._changed(Change.CONTENT); - }, - - getCharacterStyle: function() { - return this.getStyle(); - }, - - setCharacterStyle: function(style) { - this.setStyle(style); - } - -}); - -var PointText = this.PointText = TextItem.extend({ - initialize: function(pointOrMatrix) { - this.base(pointOrMatrix); - this._point = this._matrix.getTranslation(); - }, - - clone: function() { - return this._clone(new PointText(this._matrix)); - }, - - getPoint: function() { - return LinkedPoint.create(this, 'setPoint', - this._point.x, this._point.y); - }, - - setPoint: function(point) { - this.translate(Point.read(arguments).subtract(this._point)); - }, - - _transform: function(matrix) { - matrix._transformPoint(this._point, this._point); - }, - - draw: function(ctx) { - if (!this._content) - return; - this._setStyles(ctx); - var style = this._style, - leading = this.getLeading(), - lines = this._lines; - ctx.font = style.getFontStyle(); - ctx.textAlign = this.getJustification(); - for (var i = 0, l = lines.length; i < l; i++) { - var line = lines[i]; - if (style._fillColor) - ctx.fillText(line, 0, 0); - if (style._strokeColor) - ctx.strokeText(line, 0, 0); - ctx.translate(0, leading); - } - } -}, new function() { - var context = null; - - return { - _getBounds: function(type, matrix) { - if (!context) - context = CanvasProvider.getCanvas( - Size.create(1, 1)).getContext('2d'); - var justification = this.getJustification(), - x = 0; - context.font = this._style.getFontStyle(); - var width = 0; - for (var i = 0, l = this._lines.length; i < l; i++) - width = Math.max(width, context.measureText( - this._lines[i]).width); - if (justification !== 'left') - x -= width / (justification === 'center' ? 2: 1); - var leading = this.getLeading(), - count = this._lines.length, - bounds = Rectangle.create(x, - count ? leading / 4 + (count - 1) * leading : 0, - width, -count * leading); - return matrix ? matrix._transformBounds(bounds, bounds) : bounds; - } - }; -}); - -var Style = Item.extend({ - initialize: function(style) { - var clone = style instanceof Style; - return Base.each(this._defaults, function(value, key) { - value = style && style[key] || value; - this[key] = value && clone && value.clone - ? value.clone() : value; - }, this); - }, - - statics: { - create: function(item) { - var style = new this(this.dont); - style._item = item; - return style; - }, - - extend: function(src) { - var styleKey = '_' + src._style, - stylePart = Base.capitalize(src._style), - flags = src._flags || {}, - owner = {}; - - owner['get' + stylePart] = function() { - return this[styleKey]; - }; - - owner['set' + stylePart] = function(style) { - this[styleKey].initialize(style); - }; - - Base.each(src._defaults, function(value, key) { - var isColor = !!key.match(/Color$/), - part = Base.capitalize(key), - set = 'set' + part, - get = 'get' + part; - src[set] = function(value) { - var children = this._item && this._item._children; - value = isColor ? Color.read(arguments) : value; - if (children) { - for (var i = 0, l = children.length; i < l; i++) - children[i][styleKey][set](value); - } else { - var old = this['_' + key]; - if (old != value && !(old && old.equals - && old.equals(value))) { - this['_' + key] = value; - if (isColor) { - if (old) - old._removeOwner(this._item); - if (value) - value._addOwner(this._item); - } - if (this._item) - this._item._changed(flags[key] || Change.STYLE); - } - } - return this; - }; - src[get] = function() { - var children = this._item && this._item._children, - style; - if (!children) - return this['_' + key]; - for (var i = 0, l = children.length; i < l; i++) { - var childStyle = children[i][styleKey][get](); - if (!style) { - style = childStyle; - } else if (style != childStyle && !(style - && style.equals && style.equals(childStyle))) { - return undefined; - } - } - return style; - }; - owner[set] = function(value) { - this[styleKey][set](value); - return this; - }; - owner[get] = function() { - return this[styleKey][get](); - }; - }); - src._owner.inject(owner); - return this.base.apply(this, arguments); - } - } -}); - -var PathStyle = this.PathStyle = Style.extend({ - _owner: Item, - _style: 'style', - _defaults: { - fillColor: undefined, - strokeColor: undefined, - strokeWidth: 1, - strokeCap: 'butt', - strokeJoin: 'miter', - miterLimit: 10, - dashOffset: 0, - dashArray: [] - }, - _flags: { - strokeWidth: Change.STROKE, - strokeCap: Change.STROKE, - strokeJoin: Change.STROKE, - miterLimit: Change.STROKE - } - -}); - -var ParagraphStyle = this.ParagraphStyle = Style.extend({ - _owner: TextItem, - _style: 'paragraphStyle', - _defaults: { - justification: 'left' - }, - _flags: { - justification: Change.GEOMETRY - } - -}); - -var CharacterStyle = this.CharacterStyle = PathStyle.extend({ - _owner: TextItem, - _style: 'style', - _defaults: Base.merge(PathStyle.prototype._defaults, { - fillColor: 'black', - fontSize: 12, - leading: null, - font: 'sans-serif' - }), - _flags: { - fontSize: Change.GEOMETRY, - leading: Change.GEOMETRY, - font: Change.GEOMETRY - } - -}, { - getLeading: function() { - var leading = this.base(); - return leading != null ? leading : this.getFontSize() * 1.2; - }, - - getFontStyle: function() { - return this._fontSize + 'px ' + this._font; - } -}); - -var Color = this.Color = Base.extend(new function() { - - var components = { - gray: ['gray'], - rgb: ['red', 'green', 'blue'], - hsb: ['hue', 'saturation', 'brightness'], - hsl: ['hue', 'saturation', 'lightness'] - }; - - var colorCache = {}, - colorContext; - - function nameToRgbColor(name) { - var color = colorCache[name]; - if (color) - return color.clone(); - if (!colorContext) { - var canvas = CanvasProvider.getCanvas(Size.create(1, 1)); - colorContext = canvas.getContext('2d'); - colorContext.globalCompositeOperation = 'copy'; - } - colorContext.fillStyle = 'rgba(0,0,0,0)'; - colorContext.fillStyle = name; - colorContext.fillRect(0, 0, 1, 1); - var data = colorContext.getImageData(0, 0, 1, 1).data, - rgb = [data[0] / 255, data[1] / 255, data[2] / 255]; - return (colorCache[name] = RgbColor.read(rgb)).clone(); - } - - function hexToRgbColor(string) { - var hex = string.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); - if (hex.length >= 4) { - var rgb = new Array(3); - for (var i = 0; i < 3; i++) { - var channel = hex[i + 1]; - rgb[i] = parseInt(channel.length == 1 - ? channel + channel : channel, 16) / 255; - } - return RgbColor.read(rgb); - } - } - - var hsbIndices = [ - [0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2] - ]; - - var converters = { - 'rgb-hsb': function(color) { - var r = color._red, - g = color._green, - b = color._blue, - max = Math.max(r, g, b), - min = Math.min(r, g, b), - delta = max - min, - h = delta == 0 ? 0 - : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) - : max == g ? (b - r) / delta + 2 - : (r - g) / delta + 4) * 60, - s = max == 0 ? 0 : delta / max, - v = max; - return new HsbColor(h, s, v, color._alpha); - }, - - 'hsb-rgb': function(color) { - var h = (color._hue / 60) % 6, - s = color._saturation, - b = color._brightness, - i = Math.floor(h), - f = h - i, - i = hsbIndices[i], - v = [ - b, - b * (1 - s), - b * (1 - s * f), - b * (1 - s * (1 - f)) - ]; - return new RgbColor(v[i[0]], v[i[1]], v[i[2]], color._alpha); - }, - - 'rgb-hsl': function(color) { - var r = color._red, - g = color._green, - b = color._blue, - max = Math.max(r, g, b), - min = Math.min(r, g, b), - delta = max - min, - achromatic = delta == 0, - h = achromatic ? 0 - : ( max == r ? (g - b) / delta + (g < b ? 6 : 0) - : max == g ? (b - r) / delta + 2 - : (r - g) / delta + 4) * 60, - l = (max + min) / 2, - s = achromatic ? 0 : l < 0.5 - ? delta / (max + min) - : delta / (2 - max - min); - return new HslColor(h, s, l, color._alpha); - }, - - 'hsl-rgb': function(color) { - var s = color._saturation, - h = color._hue / 360, - l = color._lightness, - t1, t2, c; - if (s == 0) - return new RgbColor(l, l, l, color._alpha); - var t3s = [ h + 1 / 3, h, h - 1 / 3 ], - t2 = l < 0.5 ? l * (1 + s) : l + s - l * s, - t1 = 2 * l - t2, - c = []; - for (var i = 0; i < 3; i++) { - var t3 = t3s[i]; - if (t3 < 0) t3 += 1; - if (t3 > 1) t3 -= 1; - c[i] = 6 * t3 < 1 - ? t1 + (t2 - t1) * 6 * t3 - : 2 * t3 < 1 - ? t2 - : 3 * t3 < 2 - ? t1 + (t2 - t1) * ((2 / 3) - t3) * 6 - : t1; - } - return new RgbColor(c[0], c[1], c[2], color._alpha); - }, - - 'rgb-gray': function(color) { - return new GrayColor(1 - (color._red * 0.2989 + color._green * 0.587 - + color._blue * 0.114), color._alpha); - }, - - 'gray-rgb': function(color) { - var comp = 1 - color._gray; - return new RgbColor(comp, comp, comp, color._alpha); - }, - - 'gray-hsb': function(color) { - return new HsbColor(0, 0, 1 - color._gray, color._alpha); - }, - - 'gray-hsl': function(color) { - return new HslColor(0, 0, 1 - color._gray, color._alpha); - } - }; - - var fields = { - _readNull: true, - - initialize: function(arg) { - var isArray = Array.isArray(arg), - type = this._colorType; - if (typeof arg === 'object' && !isArray) { - if (!type) { - return arg.red !== undefined - ? new RgbColor(arg.red, arg.green, arg.blue, arg.alpha) - : arg.gray !== undefined - ? new GrayColor(arg.gray, arg.alpha) - : arg.lightness !== undefined - ? new HslColor(arg.hue, arg.saturation, arg.lightness, - arg.alpha) - : arg.hue !== undefined - ? new HsbColor(arg.hue, arg.saturation, arg.brightness, - arg.alpha) - : new RgbColor(); - } else { - return Color.read(arguments).convert(type); - } - } else if (typeof arg === 'string') { - var rgbColor = arg.match(/^#[0-9a-f]{3,6}$/i) - ? hexToRgbColor(arg) - : nameToRgbColor(arg); - return type - ? rgbColor.convert(type) - : rgbColor; - } else { - var components = isArray ? arg - : Array.prototype.slice.call(arguments); - if (!type) { - if (components.length >= 3) - return new RgbColor(components); - return new GrayColor(components); - } else { - Base.each(this._components, - function(name, i) { - var value = components[i]; - this['_' + name] = value !== undefined - ? value : null; - }, - this); - } - } - }, - - clone: function() { - var ctor = this.constructor, - copy = new ctor(ctor.dont), - components = this._components; - for (var i = 0, l = components.length; i < l; i++) { - var key = '_' + components[i]; - copy[key] = this[key]; - } - return copy; - }, - - convert: function(type) { - var converter; - return this._colorType == type - ? this.clone() - : (converter = converters[this._colorType + '-' + type]) - ? converter(this) - : converters['rgb-' + type]( - converters[this._colorType + '-rgb'](this)); - }, - - statics: { - extend: function(src) { - if (src._colorType) { - var comps = components[src._colorType]; - src._components = comps.concat(['alpha']); - Base.each(comps, function(name) { - var isHue = name === 'hue', - part = Base.capitalize(name), - name = '_' + name; - this['get' + part] = function() { - return this[name]; - }; - this['set' + part] = function(value) { - this[name] = isHue - ? ((value % 360) + 360) % 360 - : Math.min(Math.max(value, 0), 1); - this._changed(); - return this; - }; - }, src); - } - return this.base(src); - } - } - }; - - Base.each(components, function(comps, type) { - Base.each(comps, function(component) { - var part = Base.capitalize(component); - fields['get' + part] = function() { - return this.convert(type)[component]; - }; - fields['set' + part] = function(value) { - var color = this.convert(type); - color[component] = value; - color = color.convert(this._colorType); - for (var i = 0, l = this._components.length; i < l; i++) { - var key = this._components[i]; - this[key] = color[key]; - } - }; - }); - }); - - return fields; -}, { - - _changed: function() { - this._cssString = null; - for (var i = 0, l = this._owners && this._owners.length; i < l; i++) - this._owners[i]._changed(Change.STYLE); - }, - - _addOwner: function(item) { - if (!this._owners) - this._owners = []; - this._owners.push(item); - }, - - _removeOwner: function(item) { - var index = this._owners ? this._owners.indexOf(item) : -1; - if (index != -1) { - this._owners.splice(index, 1); - if (this._owners.length == 0) - delete this._owners; - } - }, - - getType: function() { - return this._colorType; - }, - - getComponents: function() { - var length = this._components.length; - var comps = new Array(length); - for (var i = 0; i < length; i++) - comps[i] = this['_' + this._components[i]]; - return comps; - }, - - getAlpha: function() { - return this._alpha != null ? this._alpha : 1; - }, - - setAlpha: function(alpha) { - this._alpha = alpha == null ? null : Math.min(Math.max(alpha, 0), 1); - this._changed(); - return this; - }, - - hasAlpha: function() { - return this._alpha != null; - }, - - equals: function(color) { - if (color && color._colorType === this._colorType) { - for (var i = 0, l = this._components.length; i < l; i++) { - var component = '_' + this._components[i]; - if (this[component] !== color[component]) - return false; - } - return true; - } - return false; - }, - - toString: function() { - var parts = [], - format = Base.formatNumber; - for (var i = 0, l = this._components.length; i < l; i++) { - var component = this._components[i], - value = this['_' + component]; - if (component === 'alpha' && value == null) - value = 1; - parts.push(component + ': ' + format(value)); - } - return '{ ' + parts.join(', ') + ' }'; - }, - - toCssString: function() { - if (!this._cssString) { - var color = this.convert('rgb'), - alpha = color.getAlpha(), - components = [ - Math.round(color._red * 255), - Math.round(color._green * 255), - Math.round(color._blue * 255), - alpha != null ? alpha : 1 - ]; - this._cssString = 'rgba(' + components.join(', ') + ')'; - } - return this._cssString; - }, - - getCanvasStyle: function() { - return this.toCssString(); - } - -}); - -var GrayColor = this.GrayColor = Color.extend({ - - _colorType: 'gray' -}); - -var RgbColor = this.RgbColor = this.RGBColor = Color.extend({ - - _colorType: 'rgb' -}); - -var HsbColor = this.HsbColor = this.HSBColor = Color.extend({ - - _colorType: 'hsb' -}); - -var HslColor = this.HslColor = this.HSLColor = Color.extend({ - - _colorType: 'hsl' -}); - -var GradientColor = this.GradientColor = Color.extend({ - - initialize: function(gradient, origin, destination, hilite) { - this.gradient = gradient || new Gradient(); - this.gradient._addOwner(this); - this.setOrigin(origin); - this.setDestination(destination); - if (hilite) - this.setHilite(hilite); - }, - - clone: function() { - return new GradientColor(this.gradient, this._origin, this._destination, - this._hilite); - }, - - getOrigin: function() { - return this._origin; - }, - - setOrigin: function(origin) { - origin = Point.read(arguments).clone(); - this._origin = origin; - if (this._destination) - this._radius = this._destination.getDistance(this._origin); - this._changed(); - return this; - }, - - getDestination: function() { - return this._destination; - }, - - setDestination: function(destination) { - destination = Point.read(arguments).clone(); - this._destination = destination; - this._radius = this._destination.getDistance(this._origin); - this._changed(); - return this; - }, - - getHilite: function() { - return this._hilite; - }, - - setHilite: function(hilite) { - hilite = Point.read(arguments).clone(); - var vector = hilite.subtract(this._origin); - if (vector.getLength() > this._radius) { - this._hilite = this._origin.add( - vector.normalize(this._radius - 0.1)); - } else { - this._hilite = hilite; - } - this._changed(); - return this; - }, - - getCanvasStyle: function(ctx) { - var gradient; - if (this.gradient.type === 'linear') { - gradient = ctx.createLinearGradient(this._origin.x, this._origin.y, - this._destination.x, this._destination.y); - } else { - var origin = this._hilite || this._origin; - gradient = ctx.createRadialGradient(origin.x, origin.y, - 0, this._origin.x, this._origin.y, this._radius); - } - for (var i = 0, l = this.gradient._stops.length; i < l; i++) { - var stop = this.gradient._stops[i]; - gradient.addColorStop(stop._rampPoint, stop._color.toCssString()); - } - return gradient; - }, - - equals: function(color) { - return color == this || color && color._colorType === this._colorType - && this.gradient.equals(color.gradient) - && this._origin.equals(color._origin) - && this._destination.equals(color._destination); - }, - - transform: function(matrix) { - matrix._transformPoint(this._origin, this._origin, true); - matrix._transformPoint(this._destination, this._destination, true); - if (this._hilite) - matrix._transformPoint(this._hilite, this._hilite, true); - this._radius = this._destination.getDistance(this._origin); - } -}); - -var Gradient = this.Gradient = Base.extend({ - initialize: function(stops, type) { - this.setStops(stops || ['white', 'black']); - this.type = type || 'linear'; - }, - - _changed: function() { - for (var i = 0, l = this._owners && this._owners.length; i < l; i++) - this._owners[i]._changed(); - }, - - _addOwner: function(color) { - if (!this._owners) - this._owners = []; - this._owners.push(color); - }, - - _removeOwner: function(color) { - var index = this._owners ? this._owners.indexOf(color) : -1; - if (index != -1) { - this._owners.splice(index, 1); - if (this._owners.length == 0) - delete this._owners; - } - }, - - clone: function() { - var stops = []; - for (var i = 0, l = this._stops.length; i < l; i++) - stops[i] = this._stops[i].clone(); - return new Gradient(stops, this.type); - }, - - getStops: function() { - return this._stops; - }, - - setStops: function(stops) { - if (this.stops) { - for (var i = 0, l = this._stops.length; i < l; i++) { - this._stops[i]._removeOwner(this); - } - } - if (stops.length < 2) - throw new Error( - 'Gradient stop list needs to contain at least two stops.'); - this._stops = GradientStop.readAll(stops); - for (var i = 0, l = this._stops.length; i < l; i++) { - var stop = this._stops[i]; - stop._addOwner(this); - if (stop._defaultRamp) - stop.setRampPoint(i / (l - 1)); - } - this._changed(); - }, - - equals: function(gradient) { - if (gradient.type != this.type) - return false; - if (this._stops.length == gradient._stops.length) { - for (var i = 0, l = this._stops.length; i < l; i++) { - if (!this._stops[i].equals(gradient._stops[i])) - return false; - } - return true; - } - return false; - } -}); - -var GradientStop = this.GradientStop = Base.extend({ - initialize: function(arg0, arg1) { - if (arg1 === undefined && Array.isArray(arg0)) { - this.setColor(arg0[0]); - this.setRampPoint(arg0[1]); - } else if (arg0.color) { - this.setColor(arg0.color); - this.setRampPoint(arg0.rampPoint); - } else { - this.setColor(arg0); - this.setRampPoint(arg1); - } - }, - - clone: function() { - return new GradientStop(this._color.clone(), this._rampPoint); - }, - - _changed: function() { - for (var i = 0, l = this._owners && this._owners.length; i < l; i++) - this._owners[i]._changed(Change.STYLE); - }, - - _addOwner: function(gradient) { - if (!this._owners) - this._owners = []; - this._owners.push(gradient); - }, - - _removeOwner: function(gradient) { - var index = this._owners ? this._owners.indexOf(gradient) : -1; - if (index != -1) { - this._owners.splice(index, 1); - if (this._owners.length == 0) - delete this._owners; - } - }, - - getRampPoint: function() { - return this._rampPoint; - }, - - setRampPoint: function(rampPoint) { - this._defaultRamp = rampPoint == null; - this._rampPoint = rampPoint || 0; - this._changed(); - }, - - getColor: function() { - return this._color; - }, - - setColor: function(color) { - if (this._color) - this._color._removeOwner(this); - this._color = Color.read(arguments); - this._color._addOwner(this); - this._changed(); - }, - - equals: function(stop) { - return stop == this || stop instanceof GradientStop - && this._color.equals(stop._color) - && this._rampPoint == stop._rampPoint; - } -}); - -var DomElement = { - getBounds: function(el, viewport) { - var rect = el.getBoundingClientRect(), - doc = el.ownerDocument, - body = doc.body, - docEl = doc.documentElement, - x = rect.left - (docEl.clientLeft || body.clientLeft || 0), - y = rect.top - (docEl.clientTop || body.clientTop || 0); - if (!viewport) { - var win = DomElement.getViewport(doc); - x += win.pageXOffset || docEl.scrollLeft || body.scrollLeft; - y += win.pageYOffset || docEl.scrollTop || body.scrollTop; - } - return new Rectangle(x, y, rect.width, rect.height); - }, - - getOffset: function(el, viewport) { - return this.getBounds(el, viewport).getPoint(); - }, - - getSize: function(el) { - return this.getBounds(el, true).getSize(); - }, - - isInvisible: function(el) { - return this.getSize(el).equals([0, 0]); - }, - - isVisible: function(el) { - return !this.isInvisible(el) && this.getViewportBounds(el).intersects( - this.getBounds(el, true)); - }, - - getViewport: function(doc) { - return doc.defaultView || doc.parentWindow; - }, - - getViewportBounds: function(el) { - var doc = el.ownerDocument, - view = this.getViewport(doc), - body = doc.getElementsByTagName( - doc.compatMode === 'CSS1Compat' ? 'html' : 'body')[0]; - return Rectangle.create(0, 0, - view.innerWidth || body.clientWidth, - view.innerHeight || body.clientHeight - ); - }, - - getComputedStyle: function(el, name) { - if (el.currentStyle) - return el.currentStyle[Base.camelize(name)]; - var style = this.getViewport(el.ownerDocument) - .getComputedStyle(el, null); - return style ? style.getPropertyValue(Base.hyphenate(name)) : null; - } -}; - -var DomEvent = { - add: function(el, events) { - for (var type in events) { - var func = events[type]; - if (el.addEventListener) { - el.addEventListener(type, func, false); - } else if (el.attachEvent) { - el.attachEvent('on' + type, func.bound = function() { - func.call(el, window.event); - }); - } - } - }, - - remove: function(el, events) { - for (var type in events) { - var func = events[type]; - if (el.removeEventListener) { - el.removeEventListener(type, func, false); - } else if (el.detachEvent) { - el.detachEvent('on' + type, func.bound); - } - } - }, - - getPoint: function(event) { - var pos = event.targetTouches - ? event.targetTouches.length - ? event.targetTouches[0] - : event.changedTouches[0] - : event; - return Point.create( - pos.pageX || pos.clientX + document.documentElement.scrollLeft, - pos.pageY || pos.clientY + document.documentElement.scrollTop - ); - }, - - getTarget: function(event) { - return event.target || event.srcElement; - }, - - getOffset: function(event, target) { - return DomEvent.getPoint(event).subtract(DomElement.getOffset( - target || DomEvent.getTarget(event))); - }, - - preventDefault: function(event) { - if (event.preventDefault) { - event.preventDefault(); - } else { - event.returnValue = false; - } - }, - - stopPropagation: function(event) { - if (event.stopPropagation) { - event.stopPropagation(); - } else { - event.cancelBubble = true; - } - }, - - stop: function(event) { - DomEvent.stopPropagation(event); - DomEvent.preventDefault(event); - } -}; - -DomEvent.requestAnimationFrame = new function() { - var part = 'equestAnimationFrame', - request = window['r' + part] || window['webkitR' + part] - || window['mozR' + part] || window['oR' + part] - || window['msR' + part]; - if (request) { - request(function(time) { - if (time == undefined) - request = null; - }); - } - - var callbacks = [], - focused = true, - timer; - - DomEvent.add(window, { - focus: function() { - focused = true; - }, - blur: function() { - focused = false; - } - }); - - return function(callback, element) { - if (request) - return request(callback, element); - callbacks.push([callback, element]); - if (timer) - return; - timer = window.setInterval(function() { - for (var i = callbacks.length - 1; i >= 0; i--) { - var entry = callbacks[i], - func = entry[0], - el = entry[1]; - if (!el || (PaperScript.getAttribute(el, 'keepalive') == 'true' - || focused) && DomElement.isVisible(el)) { - callbacks.splice(i, 1); - func(Date.now()); - } - } - }, 1000 / 60); - }; -}; - -var View = this.View = Base.extend(Callback, { - _events: { - onFrame: { - install: function() { - var that = this, - requested = false, - before, - time = 0, - count = 0; - this._onFrameCallback = function(param, dontRequest) { - requested = false; - if (!that._onFrameCallback) - return; - paper = that._scope; - if (!dontRequest) { - requested = true; - DomEvent.requestAnimationFrame(that._onFrameCallback, - that._element); - } - var now = Date.now() / 1000, - delta = before ? now - before : 0; - that.fire('frame', Base.merge({ - delta: delta, - time: time += delta, - count: count++ - })); - before = now; - if (that._stats) - that._stats.update(); - that.draw(true); - }; - if (!requested) - this._onFrameCallback(); - }, - - uninstall: function() { - delete this._onFrameCallback; - } - }, - - onResize: {} - }, - - initialize: function(element) { - this._scope = paper; - this._project = paper.project; - this._element = element; - var size; - this._id = element.getAttribute('id'); - if (this._id == null) - element.setAttribute('id', this._id = 'view-' + View._id++); - DomEvent.add(element, this._handlers); - if (PaperScript.hasAttribute(element, 'resize')) { - var offset = DomElement.getOffset(element, true), - that = this; - size = DomElement.getViewportBounds(element) - .getSize().subtract(offset); - element.width = size.width; - element.height = size.height; - DomEvent.add(window, { - resize: function(event) { - if (!DomElement.isInvisible(element)) - offset = DomElement.getOffset(element, true); - that.setViewSize(DomElement.getViewportBounds(element) - .getSize().subtract(offset)); - } - }); - } else { - size = DomElement.isInvisible(element) - ? Size.create(parseInt(element.getAttribute('width')), - parseInt(element.getAttribute('height'))) - : DomElement.getSize(element); - } - if (PaperScript.hasAttribute(element, 'stats')) { - this._stats = new Stats(); - var stats = this._stats.domElement, - style = stats.style, - offset = DomElement.getOffset(element); - style.position = 'absolute'; - style.left = offset.x + 'px'; - style.top = offset.y + 'px'; - document.body.appendChild(stats); - } - View._views.push(this); - View._viewsById[this._id] = this; - this._viewSize = LinkedSize.create(this, 'setViewSize', - size.width, size.height); - this._matrix = new Matrix(); - this._zoom = 1; - if (!View._focused) - View._focused = this; - }, - - remove: function() { - if (!this._project) - return false; - if (View._focused == this) - View._focused = null; - View._views.splice(View._views.indexOf(this), 1); - delete View._viewsById[this._id]; - if (this._project.view == this) - this._project.view = null; - DomEvent.remove(this._element, this._handlers); - this._element = this._project = null; - this.detach('frame'); - return true; - }, - - _redraw: function() { - this._redrawNeeded = true; - if (this._onFrameCallback) { - this._onFrameCallback(0, true); - } else { - this.draw(); - } - }, - - _transform: function(matrix) { - this._matrix.preConcatenate(matrix); - this._bounds = null; - this._inverse = null; - this._redraw(); - }, - - getElement: function() { - return this._element; - }, - - getViewSize: function() { - return this._viewSize; - }, - - setViewSize: function(size) { - size = Size.read(arguments); - var delta = size.subtract(this._viewSize); - if (delta.isZero()) - return; - this._element.width = size.width; - this._element.height = size.height; - this._viewSize.set(size.width, size.height, true); - this._bounds = null; - this._redrawNeeded = true; - this.fire('resize', { - size: size, - delta: delta - }); - this._redraw(); - }, - - getBounds: function() { - if (!this._bounds) - this._bounds = this._getInverse()._transformBounds( - new Rectangle(new Point(), this._viewSize)); - return this._bounds; - }, - - getSize: function() { - return this.getBounds().getSize(); - }, - - getCenter: function() { - return this.getBounds().getCenter(); - }, - - setCenter: function(center) { - this.scrollBy(Point.read(arguments).subtract(this.getCenter())); - }, - - getZoom: function() { - return this._zoom; - }, - - setZoom: function(zoom) { - this._transform(new Matrix().scale(zoom / this._zoom, - this.getCenter())); - this._zoom = zoom; - }, - - isVisible: function() { - return DomElement.isVisible(this._element); - }, - - scrollBy: function(point) { - this._transform(new Matrix().translate(Point.read(arguments).negate())); - }, - - projectToView: function(point) { - return this._matrix._transformPoint(Point.read(arguments)); - }, - - viewToProject: function(point) { - return this._getInverse()._transformPoint(Point.read(arguments)); - }, - - _getInverse: function() { - if (!this._inverse) - this._inverse = this._matrix.createInverse(); - return this._inverse; - } - -}, { - statics: { - _views: [], - _viewsById: {}, - _id: 0, - - create: function(element) { - if (typeof element === 'string') - element = document.getElementById(element); - return new CanvasView(element); - } - } -}, new function() { - var tool, - curPoint, - tempFocus, - dragging = false; - - function getView(event) { - return View._viewsById[DomEvent.getTarget(event).getAttribute('id')]; - } - - function viewToProject(view, event) { - return view.viewToProject(DomEvent.getOffset(event, view._element)); - } - - function updateFocus() { - if (!View._focused || !View._focused.isVisible()) { - for (var i = 0, l = View._views.length; i < l; i++) { - var view = View._views[i]; - if (view && view.isVisible()) { - View._focused = tempFocus = view; - break; - } - } - } - } - - function mousedown(event) { - var view = View._focused = getView(event); - curPoint = viewToProject(view, event); - dragging = true; - if (view._onMouseDown) - view._onMouseDown(event, curPoint); - if (tool = view._scope.tool) - tool._onHandleEvent('mousedown', curPoint, event); - view.draw(true); - } - - function mousemove(event) { - var view; - if (!dragging) { - view = getView(event); - if (view) { - View._focused = tempFocus = view; - } else if (tempFocus && tempFocus == View._focused) { - View._focused = null; - updateFocus(); - } - } - if (!(view = view || View._focused)) - return; - var point = event && viewToProject(view, event); - if (view._onMouseMove) - view._onMouseMove(event, point); - if (tool = view._scope.tool) { - var onlyMove = !!(!tool.onMouseDrag && tool.onMouseMove); - if (dragging && !onlyMove) { - if ((curPoint = point || curPoint) - && tool._onHandleEvent('mousedrag', curPoint, event)) - DomEvent.stop(event); - } else if ((!dragging || onlyMove) - && tool._onHandleEvent('mousemove', point, event)) { - DomEvent.stop(event); - } - } - view.draw(true); - } - - function mouseup(event) { - var view = View._focused; - if (!view || !dragging) - return; - var point = viewToProject(view, event); - curPoint = null; - dragging = false; - if (view._onMouseUp) - view._onMouseUp(event, point); - if (tool && tool._onHandleEvent('mouseup', point, event)) - DomEvent.stop(event); - view.draw(true); - } - - function selectstart(event) { - if (dragging) - DomEvent.stop(event); - } - - DomEvent.add(document, { - mousemove: mousemove, - mouseup: mouseup, - touchmove: mousemove, - touchend: mouseup, - selectstart: selectstart, - scroll: updateFocus - }); - - DomEvent.add(window, { - load: updateFocus - }); - - return { - _handlers: { - mousedown: mousedown, - touchstart: mousedown, - selectstart: selectstart - }, - - statics: { - updateFocus: updateFocus - } - }; -}); - -var CanvasView = View.extend({ - initialize: function(canvas) { - if (!(canvas instanceof HTMLCanvasElement)) { - var size = Size.read(arguments, 1); - if (size.isZero()) - size = Size.create(1024, 768); - canvas = CanvasProvider.getCanvas(size); - } - this._context = canvas.getContext('2d'); - this._eventCounters = {}; - this.base(canvas); - }, - - draw: function(checkRedraw) { - if (checkRedraw && !this._redrawNeeded) - return false; - var ctx = this._context, - size = this._viewSize; - ctx.clearRect(0, 0, size._width + 1, size._height + 1); - this._project.draw(ctx, this._matrix); - this._redrawNeeded = false; - return true; - } -}, new function() { - - var hitOptions = { - fill: true, - stroke: true, - tolerance: 0 - }; - - var downPoint, - lastPoint, - overPoint, - downItem, - overItem, - hasDrag, - doubleClick, - clickTime; - - function callEvent(type, event, point, target, lastPoint, bubble) { - var item = target, - mouseEvent, - called = false; - while (item) { - if (item.responds(type)) { - if (!mouseEvent) - mouseEvent = new MouseEvent(type, event, point, target, - lastPoint ? point.subtract(lastPoint) : null); - called = item.fire(type, mouseEvent) || called; - if (called && (!bubble || mouseEvent._stopped)) - break; - } - item = item.getParent(); - } - return called; - } - - function handleEvent(view, type, event, point, lastPoint) { - if (view._eventCounters[type]) { - var hit = view._project.hitTest(point, hitOptions), - item = hit && hit.item; - if (item) { - if (type == 'mousemove' && item != overItem) - lastPoint = point; - if (type != 'mousemove' || !hasDrag) - callEvent(type, event, point, item, lastPoint); - return item; - } - } - } - - return { - _onMouseDown: function(event, point) { - var item = handleEvent(this, 'mousedown', event, point); - doubleClick = downItem == item && Date.now() - clickTime < 300; - downItem = item; - downPoint = lastPoint = overPoint = point; - hasDrag = downItem && downItem.responds('mousedrag'); - }, - - _onMouseUp: function(event, point) { - var item = handleEvent(this, 'mouseup', event, point); - if (hasDrag) { - if (lastPoint && !lastPoint.equals(point)) - callEvent('mousedrag', event, point, downItem, lastPoint); - if (item != downItem) { - overPoint = point; - callEvent('mousemove', event, point, item, overPoint); - } - } - if (item == downItem) { - clickTime = Date.now(); - callEvent(doubleClick ? 'doubleclick' : 'click', event, - downPoint, overItem); - doubleClick = false; - } - downItem = null; - hasDrag = false; - }, - - _onMouseMove: function(event, point) { - if (downItem) - callEvent('mousedrag', event, point, downItem, lastPoint); - var item = handleEvent(this, 'mousemove', event, point, overPoint); - lastPoint = overPoint = point; - if (item != overItem) { - callEvent('mouseleave', event, point, overItem); - overItem = item; - callEvent('mouseenter', event, point, item); - } - } - }; -}); - -var Event = this.Event = Base.extend({ - initialize: function(event) { - this.event = event; - }, - - preventDefault: function() { - this._prevented = true; - DomEvent.preventDefault(this.event); - return this; - }, - - stopPropagation: function() { - this._stopped = true; - DomEvent.stopPropagation(this.event); - return this; - }, - - stop: function() { - return this.stopPropagation().preventDefault(); - }, - - getModifiers: function() { - return Key.modifiers; - } -}); - -var KeyEvent = this.KeyEvent = Event.extend({ - initialize: function(down, key, character, event) { - this.base(event); - this.type = down ? 'keydown' : 'keyup'; - this.key = key; - this.character = character; - }, - - toString: function() { - return '{ type: ' + this.type - + ', key: ' + this.key - + ', character: ' + this.character - + ', modifiers: ' + this.getModifiers() - + ' }'; - } -}); - -var Key = this.Key = new function() { - - var keys = { - 8: 'backspace', - 13: 'enter', - 16: 'shift', - 17: 'control', - 18: 'option', - 19: 'pause', - 20: 'caps-lock', - 27: 'escape', - 32: 'space', - 35: 'end', - 36: 'home', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 46: 'delete', - 91: 'command', - 93: 'command', - 224: 'command' - }, - - modifiers = Base.merge({ - shift: false, - control: false, - option: false, - command: false, - capsLock: false - }), - - charCodeMap = {}, - keyMap = {}, - downCode; - - function handleKey(down, keyCode, charCode, event) { - var character = String.fromCharCode(charCode), - key = keys[keyCode] || character.toLowerCase(), - type = down ? 'keydown' : 'keyup', - view = View._focused, - scope = view && view.isVisible() && view._scope, - tool = scope && scope.tool; - keyMap[key] = down; - if (tool && tool.responds(type)) { - tool.fire(type, new KeyEvent(down, key, character, event)); - if (view) - view.draw(true); - } - } - - DomEvent.add(document, { - keydown: function(event) { - var code = event.which || event.keyCode; - var key = keys[code], name; - if (key) { - if ((name = Base.camelize(key)) in modifiers) - modifiers[name] = true; - charCodeMap[code] = 0; - handleKey(true, code, null, event); - } else { - downCode = code; - } - }, - - keypress: function(event) { - if (downCode != null) { - var code = event.which || event.keyCode; - charCodeMap[downCode] = code; - handleKey(true, downCode, code, event); - downCode = null; - } - }, - - keyup: function(event) { - var code = event.which || event.keyCode, - key = keys[code], name; - if (key && (name = Base.camelize(key)) in modifiers) - modifiers[name] = false; - if (charCodeMap[code] != null) { - handleKey(false, code, charCodeMap[code], event); - delete charCodeMap[code]; - } - } - }); - - return { - modifiers: modifiers, - - isDown: function(key) { - return !!keyMap[key]; - } - }; -}; - -var MouseEvent = this.MouseEvent = Event.extend({ - initialize: function(type, event, point, target, delta) { - this.base(event); - this.type = type; - this.point = point; - this.target = target; - this.delta = delta; - }, - - toString: function() { - return '{ type: ' + this.type - + ', point: ' + this.point - + ', target: ' + this.target - + (this.delta ? ', delta: ' + this.delta : '') - + ', modifiers: ' + this.getModifiers() - + ' }'; - } -}); - -var ToolEvent = this.ToolEvent = Event.extend({ - initialize: function(tool, type, event) { - this.tool = tool; - this.type = type; - this.event = event; - }, - - _choosePoint: function(point, toolPoint) { - return point ? point : toolPoint ? toolPoint.clone() : null; - }, - - getPoint: function() { - return this._choosePoint(this._point, this.tool._point); - }, - - setPoint: function(point) { - this._point = point; - }, - - getLastPoint: function() { - return this._choosePoint(this._lastPoint, this.tool._lastPoint); - }, - - setLastPoint: function(lastPoint) { - this._lastPoint = lastPoint; - }, - - getDownPoint: function() { - return this._choosePoint(this._downPoint, this.tool._downPoint); - }, - - setDownPoint: function(downPoint) { - this._downPoint = downPoint; - }, - - getMiddlePoint: function() { - if (!this._middlePoint && this.tool._lastPoint) { - return this.tool._point.add(this.tool._lastPoint).divide(2); - } - return this.middlePoint; - }, - - setMiddlePoint: function(middlePoint) { - this._middlePoint = middlePoint; - }, - - getDelta: function() { - return !this._delta && this.tool._lastPoint - ? this.tool._point.subtract(this.tool._lastPoint) - : this._delta; - }, - - setDelta: function(delta) { - this._delta = delta; - }, - - getCount: function() { - return /^mouse(down|up)$/.test(this.type) - ? this.tool._downCount - : this.tool._count; - }, - - setCount: function(count) { - this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count'] - = count; - }, - - getItem: function() { - if (!this._item) { - var result = this.tool._scope.project.hitTest(this.getPoint()); - if (result) { - var item = result.item, - parent = item._parent; - while ((parent instanceof Group && !(parent instanceof Layer)) - || parent instanceof CompoundPath) { - item = parent; - parent = parent._parent; - } - this._item = item; - } - } - return this._item; - }, - setItem: function(item) { - this._item = item; - }, - - toString: function() { - return '{ type: ' + this.type - + ', point: ' + this.getPoint() - + ', count: ' + this.getCount() - + ', modifiers: ' + this.getModifiers() - + ' }'; - } -}); - -var Tool = this.Tool = PaperScopeItem.extend(Callback, { - _list: 'tools', - _reference: '_tool', - _events: [ 'onEditOptions', 'onSelect', 'onDeselect', 'onReselect', - 'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove', - 'onKeyDown', 'onKeyUp' ], - - initialize: function() { - this.base(); - this._firstMove = true; - this._count = 0; - this._downCount = 0; - }, - - getMinDistance: function() { - return this._minDistance; - }, - - setMinDistance: function(minDistance) { - this._minDistance = minDistance; - if (this._minDistance != null && this._maxDistance != null - && this._minDistance > this._maxDistance) { - this._maxDistance = this._minDistance; - } - }, - - getMaxDistance: function() { - return this._maxDistance; - }, - - setMaxDistance: function(maxDistance) { - this._maxDistance = maxDistance; - if (this._minDistance != null && this._maxDistance != null - && this._maxDistance < this._minDistance) { - this._minDistance = maxDistance; - } - }, - - getFixedDistance: function() { - return this._minDistance == this._maxDistance - ? this._minDistance : null; - }, - - setFixedDistance: function(distance) { - this._minDistance = distance; - this._maxDistance = distance; - }, - - _updateEvent: function(type, pt, minDistance, maxDistance, start, - needsChange, matchMaxDistance) { - if (!start) { - if (minDistance != null || maxDistance != null) { - var minDist = minDistance != null ? minDistance : 0; - var vector = pt.subtract(this._point); - var distance = vector.getLength(); - if (distance < minDist) - return false; - var maxDist = maxDistance != null ? maxDistance : 0; - if (maxDist != 0) { - if (distance > maxDist) { - pt = this._point.add(vector.normalize(maxDist)); - } else if (matchMaxDistance) { - return false; - } - } - } - if (needsChange && pt.equals(this._point)) - return false; - } - this._lastPoint = start && type == 'mousemove' ? pt : this._point; - this._point = pt; - switch (type) { - case 'mousedown': - this._lastPoint = this._downPoint; - this._downPoint = this._point; - this._downCount++; - break; - case 'mouseup': - this._lastPoint = this._downPoint; - break; - } - this._count = start ? 0 : this._count + 1; - return true; - }, - - _onHandleEvent: function(type, pt, event) { - paper = this._scope; - var sets = Tool._removeSets; - if (sets) { - if (type === 'mouseup') - sets.mousedrag = null; - var set = sets[type]; - if (set) { - for (var id in set) { - var item = set[id]; - for (var key in sets) { - var other = sets[key]; - if (other && other != set && other[item._id]) - delete other[item._id]; - } - item.remove(); - } - sets[type] = null; - } - } - var called = false; - switch (type) { - case 'mousedown': - this._updateEvent(type, pt, null, null, true, false, false); - if (this.responds(type)) - called = this.fire(type, new ToolEvent(this, type, event)); - break; - case 'mousedrag': - var needsChange = false, - matchMaxDistance = false; - while (this._updateEvent(type, pt, this.minDistance, - this.maxDistance, false, needsChange, matchMaxDistance)) { - if (this.responds(type)) - called = this.fire(type, new ToolEvent(this, type, event)); - needsChange = true; - matchMaxDistance = true; - } - break; - case 'mouseup': - if ((this._point.x != pt.x || this._point.y != pt.y) - && this._updateEvent('mousedrag', pt, this.minDistance, - this.maxDistance, false, false, false)) { - if (this.responds('mousedrag')) - called = this.fire('mousedrag', - new ToolEvent(this, type, event)); - } - this._updateEvent(type, pt, null, this.maxDistance, false, - false, false); - if (this.responds(type)) - called = this.fire(type, new ToolEvent(this, type, event)); - this._updateEvent(type, pt, null, null, true, false, false); - this._firstMove = true; - break; - case 'mousemove': - while (this._updateEvent(type, pt, this.minDistance, - this.maxDistance, this._firstMove, true, false)) { - if (this.responds(type)) - called = this.fire(type, new ToolEvent(this, type, event)); - this._firstMove = false; - } - break; - } - return called; - } - -}); - -var CanvasProvider = { - canvases: [], - getCanvas: function(size) { - if (this.canvases.length) { - var canvas = this.canvases.pop(); - if ((canvas.width != size.width) - || (canvas.height != size.height)) { - canvas.width = size.width; - canvas.height = size.height; - } else { - canvas.getContext('2d').clearRect(0, 0, - size.width + 1, size.height + 1); - } - return canvas; - } else { - var canvas = document.createElement('canvas'); - canvas.width = size.width; - canvas.height = size.height; - return canvas; - } - }, - - returnCanvas: function(canvas) { - this.canvases.push(canvas); - } -}; - -var Numerical = new function() { - - var abscissas = [ - [ 0.5773502691896257645091488], - [0,0.7745966692414833770358531], - [ 0.3399810435848562648026658,0.8611363115940525752239465], - [0,0.5384693101056830910363144,0.9061798459386639927976269], - [ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016], - [0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897], - [ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609], - [0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762], - [ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640], - [0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380], - [ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491], - [0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294], - [ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973], - [0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657], - [ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542] - ]; - - var weights = [ - [1], - [0.8888888888888888888888889,0.5555555555555555555555556], - [0.6521451548625461426269361,0.3478548451374538573730639], - [0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640], - [0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961], - [0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114], - [0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314], - [0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922], - [0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688], - [0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537], - [0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160], - [0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216], - [0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329], - [0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284], - [0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806] - ]; - - var abs = Math.abs, - sqrt = Math.sqrt, - cos = Math.cos, - PI = Math.PI; - - return { - TOLERANCE: 10e-6, - EPSILON: 10e-12, - - integrate: function(f, a, b, n) { - var x = abscissas[n - 2], - w = weights[n - 2], - A = 0.5 * (b - a), - B = A + a, - i = 0, - m = (n + 1) >> 1, - sum = n & 1 ? w[i++] * f(B) : 0; - while (i < m) { - var Ax = A * x[i]; - sum += w[i++] * (f(B + Ax) + f(B - Ax)); - } - return A * sum; - }, - - findRoot: function(f, df, x, a, b, n, tolerance) { - for (var i = 0; i < n; i++) { - var fx = f(x), - dx = fx / df(x); - if (abs(dx) < tolerance) - return x; - var nx = x - dx; - if (fx > 0) { - b = x; - x = nx <= a ? 0.5 * (a + b) : nx; - } else { - a = x; - x = nx >= b ? 0.5 * (a + b) : nx; - } - } - }, - - solveQuadratic: function(a, b, c, roots, tolerance) { - if (abs(a) < tolerance) { - if (abs(b) >= tolerance) { - roots[0] = -c / b; - return 1; - } - if (abs(c) < tolerance) - return -1; - return 0; - } - var q = b * b - 4 * a * c; - if (q < 0) - return 0; - q = sqrt(q); - if (b < 0) - q = -q; - q = (b + q) * -0.5; - var n = 0; - if (abs(q) >= tolerance) - roots[n++] = c / q; - if (abs(a) >= tolerance) - roots[n++] = q / a; - return n; - }, - - solveCubic: function(a, b, c, d, roots, tolerance) { - if (abs(a) < tolerance) - return Numerical.solveQuadratic(b, c, d, roots, tolerance); - b /= a; - c /= a; - d /= a; - var Q = (b * b - 3 * c) / 9, - R = (2 * b * b * b - 9 * b * c + 27 * d) / 54, - Q3 = Q * Q * Q, - R2 = R * R; - b /= 3; - if (R2 < Q3) { - var theta = Math.acos(R / sqrt(Q3)), - q = -2 * sqrt(Q); - roots[0] = q * cos(theta / 3) - b; - roots[1] = q * cos((theta + 2 * PI) / 3) - b; - roots[2] = q * cos((theta - 2 * PI) / 3) - b; - return 3; - } else { - var A = -Math.pow(abs(R) + sqrt(R2 - Q3), 1 / 3); - if (R < 0) A = -A; - var B = (abs(A) < tolerance) ? 0 : Q / A; - roots[0] = (A + B) - b; - return 1; - } - return 0; - } - }; -}; - -var BlendMode = { - process: function(blendMode, srcContext, dstContext, alpha, offset) { - var srcCanvas = srcContext.canvas, - dstData = dstContext.getImageData(offset.x, offset.y, - srcCanvas.width, srcCanvas.height), - dst = dstData.data, - src = srcContext.getImageData(0, 0, - srcCanvas.width, srcCanvas.height).data, - min = Math.min, - max = Math.max, - abs = Math.abs, - sr, sg, sb, sa, - br, bg, bb, ba, - dr, dg, db; - - function getLum(r, g, b) { - return 0.2989 * r + 0.587 * g + 0.114 * b; - } - - function setLum(r, g, b, l) { - var d = l - getLum(r, g, b); - dr = r + d; - dg = g + d; - db = b + d; - var l = getLum(dr, dg, db), - mn = min(dr, dg, db), - mx = max(dr, dg, db); - if (mn < 0) { - var lmn = l - mn; - dr = l + (dr - l) * l / lmn; - dg = l + (dg - l) * l / lmn; - db = l + (db - l) * l / lmn; - } - if (mx > 255) { - var ln = 255 - l, mxl = mx - l; - dr = l + (dr - l) * ln / mxl; - dg = l + (dg - l) * ln / mxl; - db = l + (db - l) * ln / mxl; - } - } - - function getSat(r, g, b) { - return max(r, g, b) - min(r, g, b); - } - - function setSat(r, g, b, s) { - var col = [r, g, b], - mx = max(r, g, b), - mn = min(r, g, b), - md; - mn = mn == r ? 0 : mn == g ? 1 : 2; - mx = mx == r ? 0 : mx == g ? 1 : 2; - md = min(mn, mx) == 0 ? max(mn, mx) == 1 ? 2 : 1 : 0; - if (col[mx] > col[mn]) { - col[md] = (col[md] - col[mn]) * s / (col[mx] - col[mn]); - col[mx] = s; - } else { - col[md] = col[mx] = 0; - } - col[mn] = 0; - dr = col[0]; - dg = col[1]; - db = col[2]; - } - - var modes = { - multiply: function() { - dr = br * sr / 255; - dg = bg * sg / 255; - db = bb * sb / 255; - }, - - screen: function() { - dr = 255 - (255 - br) * (255 - sr) / 255; - dg = 255 - (255 - bg) * (255 - sg) / 255; - db = 255 - (255 - bb) * (255 - sb) / 255; - }, - - overlay: function() { - dr = br < 128 ? 2 * br * sr / 255 : 255 - 2 * (255 - br) * (255 - sr) / 255; - dg = bg < 128 ? 2 * bg * sg / 255 : 255 - 2 * (255 - bg) * (255 - sg) / 255; - db = bb < 128 ? 2 * bb * sb / 255 : 255 - 2 * (255 - bb) * (255 - sb) / 255; - }, - - 'soft-light': function() { - var t = sr * br / 255; - dr = t + br * (255 - (255 - br) * (255 - sr) / 255 - t) / 255; - t = sg * bg / 255; - dg = t + bg * (255 - (255 - bg) * (255 - sg) / 255 - t) / 255; - t = sb * bb / 255; - db = t + bb * (255 - (255 - bb) * (255 - sb) / 255 - t) / 255; - }, - - 'hard-light': function() { - dr = sr < 128 ? 2 * sr * br / 255 : 255 - 2 * (255 - sr) * (255 - br) / 255; - dg = sg < 128 ? 2 * sg * bg / 255 : 255 - 2 * (255 - sg) * (255 - bg) / 255; - db = sb < 128 ? 2 * sb * bb / 255 : 255 - 2 * (255 - sb) * (255 - bb) / 255; - }, - - 'color-dodge': function() { - dr = sr == 255 ? sr : min(255, br * 255 / (255 - sr)); - dg = sg == 255 ? sg : min(255, bg * 255 / (255 - sg)); - db = sb == 255 ? sb : min(255, bb * 255 / (255 - sb)); - }, - - 'color-burn': function() { - dr = sr == 0 ? 0 : max(255 - ((255 - br) * 255) / sr, 0); - dg = sg == 0 ? 0 : max(255 - ((255 - bg) * 255) / sg, 0); - db = sb == 0 ? 0 : max(255 - ((255 - bb) * 255) / sb, 0); - }, - - darken: function() { - dr = br < sr ? br : sr; - dg = bg < sg ? bg : sg; - db = bb < sb ? bb : sb; - }, - - lighten: function() { - dr = br > sr ? br : sr; - dg = bg > sg ? bg : sg; - db = bb > sb ? bb : sb; - }, - - difference: function() { - dr = br - sr; - if (dr < 0) - dr = -dr; - dg = bg - sg; - if (dg < 0) - dg = -dg; - db = bb - sb; - if (db < 0) - db = -db; - }, - - exclusion: function() { - dr = br + sr * (255 - br - br) / 255; - dg = bg + sg * (255 - bg - bg) / 255; - db = bb + sb * (255 - bb - bb) / 255; - }, - - hue: function() { - setSat(sr, sg, sb, getSat(br, bg, bb)); - setLum(dr, dg, db, getLum(br, bg, bb)); - }, - - saturation: function() { - setSat(br, bg, bb, getSat(sr, sg, sb)); - setLum(dr, dg, db, getLum(br, bg, bb)); - }, - - luminosity: function() { - setLum(br, bg, bb, getLum(sr, sg, sb)); - }, - - color: function() { - setLum(sr, sg, sb, getLum(br, bg, bb)); - }, - - add: function() { - dr = min(br + sr, 255); - dg = min(bg + sg, 255); - db = min(bb + sb, 255); - }, - - subtract: function() { - dr = max(br - sr, 0); - dg = max(bg - sg, 0); - db = max(bb - sb, 0); - }, - - average: function() { - dr = (br + sr) / 2; - dg = (bg + sg) / 2; - db = (bb + sb) / 2; - }, - - negation: function() { - dr = 255 - abs(255 - sr - br); - dg = 255 - abs(255 - sg - bg); - db = 255 - abs(255 - sb - bb); - } - }; - - var process = modes[blendMode]; - if (!process) - return; - - for (var i = 0, l = dst.length; i < l; i += 4) { - sr = src[i]; - br = dst[i]; - sg = src[i + 1]; - bg = dst[i + 1]; - sb = src[i + 2]; - bb = dst[i + 2]; - sa = src[i + 3]; - ba = dst[i + 3]; - process(); - var a1 = sa * alpha / 255, - a2 = 1 - a1; - dst[i] = a1 * dr + a2 * br; - dst[i + 1] = a1 * dg + a2 * bg; - dst[i + 2] = a1 * db + a2 * bb; - dst[i + 3] = sa * alpha + a2 * ba; - } - dstContext.putImageData(dstData, offset.x, offset.y); - } -}; - -var PaperScript = this.PaperScript = new function() { -var parse_js=new function(){function W(a,b,c){var d=[];for(var e=0;e0,g=j(function(){return h(a[0]?k(["case",z(a[0])+":"]):"default:")},.5)+(f?e+j(function(){return u(a[1]).join(e)}):"");!c&&f&&d0?h(a):a}).join(e)},block:w,"var":function(a){return"var "+l(W(a,x))+";"},"const":function(a){return"const "+l(W(a,x))+";"},"try":function(a,b,c){var d=["try",w(a)];b&&d.push("catch","("+b[0]+")",w(b[1])),c&&d.push("finally",w(c));return k(d)},"throw":function(a){return k(["throw",z(a)])+";"},"new":function(a,b){b=b.length>0?"("+l(W(b,z))+")":"";return k(["new",m(a,"seq","binary","conditional","assign",function(a){var b=N(),c={};try{b.with_walkers({call:function(){throw c},"function":function(){return this}},function(){b.walk(a)})}catch(d){if(d===c)return!0;throw d}})+b])},"switch":function(a,b){return k(["switch","("+z(a)+")",v(b)])},"break":function(a){var b="break";a!=null&&(b+=" "+g(a));return b+";"},"continue":function(a){var b="continue";a!=null&&(b+=" "+g(a));return b+";"},conditional:function(a,b,c){return k([m(a,"assign","seq","conditional"),"?",m(b,"seq"),":",m(c,"seq")])},assign:function(a,b,c){a&&a!==!0?a+="=":a="=";return k([z(b),a,m(c,"seq")])},dot:function(a){var b=z(a),c=1;a[0]=="num"?/\./.test(a[1])||(b+="."):o(a)&&(b="("+b+")");while(cB[b[1]])d="("+d+")";if(L(c[0],["assign","conditional","seq"])||c[0]=="binary"&&B[a]>=B[c[1]]&&(c[1]!=a||!L(a,["&&","||","*"])))e="("+e+")";return k([d,a,e])},"unary-prefix":function(a,b){var c=z(b);b[0]=="num"||b[0]=="unary-prefix"&&!M(i,a+b[1])||!o(b)||(c="("+c+")");return a+(p(a.charAt(0))?" ":"")+c},"unary-postfix":function(a,b){var c=z(b);b[0]=="num"||b[0]=="unary-postfix"&&!M(i,a+b[1])||!o(b)||(c="("+c+")");return c+a},sub:function(a,b){var c=z(a);o(a)&&(c="("+c+")");return c+"["+z(b)+"]"},object:function(a){return a.length==0?"{}":"{"+e+j(function(){return W(a,function(a){if(a.length==3)return h(t(a[0],a[1][2],a[1][3],a[2]));var d=a[0],e=z(a[1]);b.quote_keys?d=Q(d):(typeof d=="number"||!c&&+d+""==d)&&parseFloat(d)>=0?d=q(+d):V(d)||(d=Q(d));return h(k(c&&b.space_colon?[d,":",e]:[d+":",e]))}).join(","+e)})+e+h("}")},regexp:function(a,b){return"/"+a+"/"+b},array:function(a){return a.length==0?"[]":k(["[",l(W(a,function(a){return!c&&a[0]=="atom"&&a[1]=="undefined"?"":m(a,"seq")})),"]"])},stat:function(a){return z(a).replace(/;*\s*$/,";")},seq:function(){return l(W(J(arguments),z))},label:function(a,b){return k([g(a),":",z(b)])},"with":function(a,b){return k(["with","("+z(a)+")",z(b)])},atom:function(a){return g(a)}},y=[];return z(a)}function Q(a){var b=0,c=0;a=a.replace(/[\\\b\f\n\r\t\x22\x27]/g,function(a){switch(a){case"\\":return"\\\\";case"\b":return"\\b";case"\f":return"\\f";case"\n":return"\\n";case"\r":return"\\r";case"\t":return"\\t";case'"':++b;return'"';case"'":++c;return"'"}return a});return b>c?"'"+a.replace(/\x27/g,"\\'")+"'":'"'+a.replace(/\x22/g,'\\"')+'"'}function O(a){return!a||a[0]=="block"&&(!a[1]||a[1].length==0)}function N(){function g(a,b){var c={},e;for(e in a)M(a,e)&&(c[e]=d[e],d[e]=a[e]);var f=b();for(e in c)M(c,e)&&(c[e]?d[e]=c[e]:delete d[e]);return f}function f(a){if(a==null)return null;try{e.push(a);var b=a[0],f=d[b];if(f){var g=f.apply(a,a.slice(1));if(g!=null)return g}f=c[b];return f.apply(a,a.slice(1))}finally{e.pop()}}function b(a){var b=[this[0]];a!=null&&b.push(W(a,f));return b}function a(a){return[this[0],W(a,function(a){var b=[a[0]];a.length>1&&(b[1]=f(a[1]));return b})]}var c={string:function(a){return[this[0],a]},num:function(a){return[this[0],a]},name:function(a){return[this[0],a]},toplevel:function(a){return[this[0],W(a,f)]},block:b,splice:b,"var":a,"const":a,"try":function(a,b,c){return[this[0],W(a,f),b!=null?[b[0],W(b[1],f)]:null,c!=null?W(c,f):null]},"throw":function(a){return[this[0],f(a)]},"new":function(a,b){return[this[0],f(a),W(b,f)]},"switch":function(a,b){return[this[0],f(a),W(b,function(a){return[a[0]?f(a[0]):null,W(a[1],f)]})]},"break":function(a){return[this[0],a]},"continue":function(a){return[this[0],a]},conditional:function(a,b,c){return[this[0],f(a),f(b),f(c)]},assign:function(a,b,c){return[this[0],a,f(b),f(c)]},dot:function(a){return[this[0],f(a)].concat(J(arguments,1))},call:function(a,b){return[this[0],f(a),W(b,f)]},"function":function(a,b,c){return[this[0],a,b.slice(),W(c,f)]},defun:function(a,b,c){return[this[0],a,b.slice(),W(c,f)]},"if":function(a,b,c){return[this[0],f(a),f(b),f(c)]},"for":function(a,b,c,d){return[this[0],f(a),f(b),f(c),f(d)]},"for-in":function(a,b,c,d){return[this[0],f(a),f(b),f(c),f(d)]},"while":function(a,b){return[this[0],f(a),f(b)]},"do":function(a,b){return[this[0],f(a),f(b)]},"return":function(a){return[this[0],f(a)]},binary:function(a,b,c){return[this[0],a,f(b),f(c)]},"unary-prefix":function(a,b){return[this[0],a,f(b)]},"unary-postfix":function(a,b){return[this[0],a,f(b)]},sub:function(a,b){return[this[0],f(a),f(b)]},object:function(a){return[this[0],W(a,function(a){return a.length==2?[a[0],f(a[1])]:[a[0],f(a[1]),a[2]]})]},regexp:function(a,b){return[this[0],a,b]},array:function(a){return[this[0],W(a,f)]},stat:function(a){return[this[0],f(a)]},seq:function(){return[this[0]].concat(W(J(arguments),f))},label:function(a,b){return[this[0],a,f(b)]},"with":function(a,b){return[this[0],f(a),f(b)]},atom:function(a){return[this[0],a]}},d={},e=[];return{walk:f,with_walkers:g,parent:function(){return e[e.length-2]},stack:function(){return e}}}function M(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function L(a,b){for(var c=b.length;--c>=0;)if(b[c]===a)return!0;return!1}function K(a){return a.split("")}function J(a,b){return Array.prototype.slice.call(a,b||0)}function I(a){var b={};for(var c=0;c0;++b)arguments[b]();return a}function G(a){var b=J(arguments,1);return function(){return a.apply(this,b.concat(J(arguments)))}}function F(a,b,c){function bk(a){try{++d.in_loop;return a()}finally{--d.in_loop}}function bi(a){var b=bg(a),c=d.token.value;if(e("operator")&&M(A,c)){if(bh(b)){g();return p("assign",A[c],b,bi(a))}i("Invalid assignment")}return b}function bh(a){if(!b)return!0;switch(a[0]){case"dot":case"sub":case"new":case"call":return!0;case"name":return a[1]!="this"}}function bg(a){var b=bf(a);if(e("operator","?")){g();var c=bj(!1);m(":");return p("conditional",b,c,bj(!1,a))}return b}function bf(a){return be(Y(!0),0,a)}function be(a,b,c){var f=e("operator")?d.token.value:null;f&&f=="in"&&c&&(f=null);var h=f!=null?B[f]:null;if(h!=null&&h>b){g();var i=be(Y(!0),h,c);return be(p("binary",f,a,i),b,c)}return a}function bd(a,b,c){(b=="++"||b=="--")&&!bh(c)&&i("Invalid use of "+b+" operator");return p(a,b,c)}function bc(a,b){if(e("punc",".")){g();return bc(p("dot",a,bb()),b)}if(e("punc","[")){g();return bc(p("sub",a,H(bj,G(m,"]"))),b)}if(b&&e("punc","(")){g();return bc(p("call",a,Z(")")),!0)}return b&&e("operator")&&M(z,d.token.value)?H(G(bd,"unary-postfix",d.token.value,a),g):a}function bb(){switch(d.token.type){case"name":case"operator":case"keyword":case"atom":return H(d.token.value,g);default:k()}}function ba(){switch(d.token.type){case"num":case"string":return H(d.token.value,g)}return bb()}function _(){var a=!0,c=[];while(!e("punc","}")){a?a=!1:m(",");if(!b&&e("punc","}"))break;var f=d.token.type,h=ba();f!="name"||h!="get"&&h!="set"||!!e("punc",":")?(m(":"),c.push([h,bj(!1)])):c.push([bb(),P(!1),h])}g();return p("object",c)}function $(){return p("array",Z("]",!b,!0))}function Z(a,b,c){var d=!0,f=[];while(!e("punc",a)){d?d=!1:m(",");if(b&&e("punc",a))break;e("punc",",")&&c?f.push(["atom","undefined"]):f.push(bj(!1))}g();return f}function X(){var a=Y(!1),b;e("punc","(")?(g(),b=Z(")")):b=[];return bc(p("new",a,b),!0)}function W(){return p("const",U())}function V(a){return p("var",U(a))}function U(a){var b=[];for(;;){e("name")||k();var c=d.token.value;g(),e("operator","=")?(g(),b.push([c,bj(!1,a)])):b.push([c]);if(!e("punc",","))break;g()}return b}function T(){var a=R(),b,c;if(e("keyword","catch")){g(),m("("),e("name")||i("Name expected");var f=d.token.value;g(),m(")"),b=[f,R()]}e("keyword","finally")&&(g(),c=R()),!b&&!c&&i("Missing catch/finally blocks");return p("try",a,b,c)}function R(){m("{");var a=[];while(!e("punc","}"))e("eof")&&k(),a.push(t());g();return a}function Q(){var a=q(),b=t(),c;e("keyword","else")&&(g(),c=t());return p("if",a,b,c)}function O(a){var b=a[0]=="var"?p("name",a[1][0]):a;g();var c=bj();m(")");return p("for-in",a,b,c,bk(t))}function N(a){m(";");var b=e("punc",";")?null:bj();m(";");var c=e("punc",")")?null:bj();m(")");return p("for",a,b,c,bk(t))}function K(){m("(");var a=null;if(!e("punc",";")){a=e("keyword","var")?(g(),V(!0)):bj(!0,!0);if(e("operator","in"))return O(a)}return N(a)}function I(a){var b;n()||(b=e("name")?d.token.value:null),b!=null?(g(),L(b,d.labels)||i("Label "+b+" without matching loop or statement")):d.in_loop==0&&i(a+" not inside a loop or switch"),o();return p(a,b)}function F(){return p("stat",H(bj,o))}function w(a){d.labels.push(a);var c=d.token,e=t();b&&!M(C,e[0])&&k(c),d.labels.pop();return p("label",a,e)}function s(a){return c?function(){var b=d.token,c=a.apply(this,arguments);c[0]=r(c[0],b,h());return c}:a}function r(a,b,c){return a instanceof E?a:new E(a,b,c)}function q(){m("(");var a=bj();m(")");return a}function p(){return J(arguments)}function o(){e("punc",";")?g():n()||k()}function n(){return!b&&(d.token.nlb||e("eof")||e("punc","}"))}function m(a){return l("punc",a)}function l(a,b){if(e(a,b))return g();j(d.token,"Unexpected token "+d.token.type+", expected "+a)}function k(a){a==null&&(a=d.token),j(a,"Unexpected token: "+a.type+" ("+a.value+")")}function j(a,b){i(b,a.line,a.col)}function i(a,b,c,e){var f=d.input.context();u(a,b!=null?b:f.tokline,c!=null?c:f.tokcol,e!=null?e:f.tokpos)}function h(){return d.prev}function g(){d.prev=d.token,d.peeked?(d.token=d.peeked,d.peeked=null):d.token=d.input();return d.token}function f(){return d.peeked||(d.peeked=d.input())}function e(a,b){return v(d.token,a,b)}var d={input:typeof a=="string"?x(a,!0):a,token:null,prev:null,peeked:null,in_function:0,in_loop:0,labels:[]};d.token=g();var t=s(function(){e("operator","/")&&(d.peeked=null,d.token=d.input(!0));switch(d.token.type){case"num":case"string":case"regexp":case"operator":case"atom":return F();case"name":return v(f(),"punc",":")?w(H(d.token.value,g,g)):F();case"punc":switch(d.token.value){case"{":return p("block",R());case"[":case"(":return F();case";":g();return p("block");default:k()};case"keyword":switch(H(d.token.value,g)){case"break":return I("break");case"continue":return I("continue");case"debugger":o();return p("debugger");case"do":return function(a){l("keyword","while");return p("do",H(q,o),a)}(bk(t));case"for":return K();case"function":return P(!0);case"if":return Q();case"return":d.in_function==0&&i("'return' outside of function");return p("return",e("punc",";")?(g(),null):n()?null:H(bj,o));case"switch":return p("switch",q(),S());case"throw":return p("throw",H(bj,o));case"try":return T();case"var":return H(V,o);case"const":return H(W,o);case"while":return p("while",q(),bk(t));case"with":return p("with",q(),t());default:k()}}}),P=s(function(a){var b=e("name")?H(d.token.value,g):null;a&&!b&&k(),m("(");return p(a?"defun":"function",b,function(a,b){while(!e("punc",")"))a?a=!1:m(","),e("name")||k(),b.push(d.token.value),g();g();return b}(!0,[]),function(){++d.in_function;var a=d.in_loop;d.in_loop=0;var b=R();--d.in_function,d.in_loop=a;return b}())}),S=G(bk,function(){m("{");var a=[],b=null;while(!e("punc","}"))e("eof")&&k(),e("keyword","case")?(g(),b=[],a.push([bj(),b]),m(":")):e("keyword","default")?(g(),m(":"),b=[],a.push([null,b])):(b||k(),b.push(t()));g();return a}),Y=s(function(a){if(e("operator","new")){g();return X()}if(e("operator")&&M(y,d.token.value))return bd("unary-prefix",H(d.token.value,g),Y(a));if(e("punc")){switch(d.token.value){case"(":g();return bc(H(bj,G(m,")")),a);case"[":g();return bc($(),a);case"{":g();return bc(_(),a)}k()}if(e("keyword","function")){g();return bc(P(!1),a)}if(M(D,d.token.type)){var b=d.token.type=="regexp"?p("regexp",d.token.value[0],d.token.value[1]):p(d.token.type,d.token.value);return bc(H(b,g),a)}k()}),bj=s(function(a,b){arguments.length==0&&(a=!0);var c=bi(b);if(a&&e("punc",",")){g();return p("seq",c,bj(!0,b))}return c});return p("toplevel",function(a){while(!e("eof"))a.push(t());return a}([]))}function E(a,b,c){this.name=a,this.start=b,this.end=c}function x(b){function P(a){if(a)return I();y(),v();var b=g();if(!b)return x("eof");if(o(b))return C();if(b=='"'||b=="'")return F();if(M(l,b))return x("punc",h());if(b==".")return L();if(b=="/")return K();if(M(e,b))return J();if(b=="\\"||q(b))return N();B("Unexpected character '"+b+"'")}function O(a,b){try{return b()}catch(c){if(c===w)B(a);else throw c}}function N(){var b=A(r);return M(a,b)?M(i,b)?x("operator",b):M(d,b)?x("atom",b):x("keyword",b):x("name",b)}function L(){h();return o(g())?C("."):x("punc",".")}function K(){h();var a=f.regex_allowed;switch(g()){case"/":f.comments_before.push(G()),f.regex_allowed=a;return P();case"*":f.comments_before.push(H()),f.regex_allowed=a;return P()}return f.regex_allowed?I():J("/")}function J(a){function b(a){if(!g())return a;var c=a+g();if(M(i,c)){h();return b(c)}return a}return x("operator",b(a||h()))}function I(){return O("Unterminated regular expression",function(){var a=!1,b="",c,d=!1;while(c=h(!0))if(a)b+="\\"+c,a=!1;else if(c=="[")d=!0,b+=c;else if(c=="]"&&d)d=!1,b+=c;else{if(c=="/"&&!d)break;c=="\\"?a=!0:b+=c}var e=A(function(a){return M(m,a)});return x("regexp",[b,e])})}function H(){h();return O("Unterminated multiline comment",function(){var a=t("*/",!0),b=f.text.substring(f.pos,a),c=x("comment2",b,!0);f.pos=a+2,f.line+=b.split("\n").length-1,f.newline_before=b.indexOf("\n")>=0;return c})}function G(){h();var a=t("\n"),b;a==-1?(b=f.text.substr(f.pos),f.pos=f.text.length):(b=f.text.substring(f.pos,a),f.pos=a);return x("comment1",b,!0)}function F(){return O("Unterminated string constant",function(){var a=h(),b="";for(;;){var c=h(!0);if(c=="\\")c=D();else if(c==a)break;b+=c}return x("string",b)})}function E(a){var b=0;for(;a>0;--a){var c=parseInt(h(!0),16);isNaN(c)&&B("Invalid hex-character pattern in string"),b=b<<4|c}return b}function D(){var a=h(!0);switch(a){case"n":return"\n";case"r":return"\r";case"t":return"\t";case"b":return"\b";case"v":return" ";case"f":return"\f";case"0":return"";case"x":return String.fromCharCode(E(2));case"u":return String.fromCharCode(E(4));case"\n":return"";default:return a}}function C(a){var b=!1,c=!1,d=!1,e=a==".",f=A(function(f,g){if(f=="x"||f=="X")return d?!1:d=!0;if(!d&&(f=="E"||f=="e"))return b?!1:b=c=!0;if(f=="-")return c||g==0&&!a?!0:!1;if(f=="+")return c;c=!1;if(f==".")return!e&&!d?e=!0:!1;return p(f)});a&&(f=a+f);var g=s(f);if(!isNaN(g))return x("num",g);B("Invalid syntax: "+f)}function B(a){u(a,f.tokline,f.tokcol,f.tokpos)}function A(a){var b="",c=g(),d=0;while(c&&a(c,d++))b+=h(),c=g();return b}function y(){while(M(j,g()))h()}function x(a,b,d){f.regex_allowed=a=="operator"&&!M(z,b)||a=="keyword"&&M(c,b)||a=="punc"&&M(k,b);var e={type:a,value:b,line:f.tokline,col:f.tokcol,pos:f.tokpos,nlb:f.newline_before};d||(e.comments_before=f.comments_before,f.comments_before=[]),f.newline_before=!1;return e}function v(){f.tokline=f.line,f.tokcol=f.col,f.tokpos=f.pos}function t(a,b){var c=f.text.indexOf(a,f.pos);if(b&&c==-1)throw w;return c}function n(){return!f.peek()}function h(a){var b=f.text.charAt(f.pos++);if(a&&!b)throw w;b=="\n"?(f.newline_before=!0,++f.line,f.col=0):++f.col;return b}function g(){return f.text.charAt(f.pos)}var f={text:b.replace(/\r\n?|[\n\u2028\u2029]/g,"\n").replace(/^\uFEFF/,""),pos:0,tokpos:0,line:0,tokline:0,col:0,tokcol:0,newline_before:!1,regex_allowed:!1,comments_before:[]};P.context=function(a){a&&(f=a);return f};return P}function v(a,b,c){return a.type==b&&(c==null||a.value==c)}function u(a,b,c,d){throw new t(a,b,c,d)}function t(a,b,c,d){this.message=a,this.line=b,this.col=c,this.pos=d}function s(a){if(f.test(a))return parseInt(a.substr(2),16);if(g.test(a))return parseInt(a.substr(1),8);if(h.test(a))return parseFloat(a)}function r(a){return q(a)||o(a)}function q(a){return a=="$"||a=="_"||n(a)}function p(a){return o(a)||n(a)}function o(a){a=a.charCodeAt(0);return a>=48&&a<=57}function n(a){a=a.charCodeAt(0);return a>=65&&a<=90||a>=97&&a<=122}var a=I(["break","case","catch","const","continue","default","delete","do","else","finally","for","function","if","in","instanceof","new","return","switch","throw","try","typeof","var","void","while","with"]),b=I(["abstract","boolean","byte","char","class","debugger","double","enum","export","extends","final","float","goto","implements","import","int","interface","long","native","package","private","protected","public","short","static","super","synchronized","throws","transient","volatile"]),c=I(["return","new","delete","throw","else","case"]),d=I(["false","null","true","undefined"]),e=I(K("+-*&%=<>!?|~^")),f=/^0x[0-9a-f]+$/i,g=/^0[0-7]+$/,h=/^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i,i=I(["in","instanceof","typeof","new","void","delete","++","--","+","-","!","~","&","|","^","*","/","%",">>","<<",">>>","<",">","<=",">=","==","===","!=","!==","?","=","+=","-=","/=","*=","%=",">>=","<<=",">>>=","|=","^=","&=","&&","||"]),j=I(K(" \n\r\t")),k=I(K("[{}(,.;:")),l=I(K("[]{}(),;:")),m=I(K("gmsiy"));t.prototype.toString=function(){return this.message+" (line: "+this.line+", col: "+this.col+", pos: "+this.pos+")"};var w={},y=I(["typeof","void","delete","--","++","!","~","-","+"]),z=I(["--","++"]),A=function(a,b,c){while(c>=","<<=",">>>=","|=","^=","&="],{"=":!0},0),B=function(a,b){for(var c=0,d=1;c","<=",">=","in","instanceof"],[">>","<<",">>>"],["+","-"],["*","/","%"]],{}),C=I(["for","do","while","switch"]),D=I(["atom","num","string","regexp","name"]);E.prototype.toString=function(){return this.name};var P=I(["name","array","object","string","dot","sub","call","regexp"]),R=I(["if","while","do","for","for-in","with"]);return{parse:F,gen_code:S,tokenizer:x,ast_walker:N}} - - // Math Operators - - var operators = { - '+': 'add', - '-': 'subtract', - '*': 'multiply', - '/': 'divide', - '%': 'modulo', - '==': 'equals', - '!=': 'equals' - }; - - function $eval(left, operator, right) { - var handler = operators[operator]; - if (left && left[handler]) { - var res = left[handler](right); - return operator == '!=' ? !res : res; - } - switch (operator) { - case '+': return left + right; - case '-': return left - right; - case '*': return left * right; - case '/': return left / right; - case '%': return left % right; - case '==': return left == right; - case '!=': return left != right; - default: - throw new Error('Implement Operator: ' + operator); - } - }; - - // Sign Operators - - var signOperators = { - '-': 'negate' - }; - - function $sign(operator, value) { - var handler = signOperators[operator]; - if (value && value[handler]) { - return value[handler](); - } - switch (operator) { - case '+': return +value; - case '-': return -value; - default: - throw new Error('Implement Sign Operator: ' + operator); - } - } - - // AST Helpers - - function isDynamic(exp) { - var type = exp[0]; - return type != 'num' && type != 'string'; - } - - function handleOperator(operator, left, right) { - // Only replace operators with calls to $operator if the left hand side - // is potentially an object. - if (operators[operator] && isDynamic(left)) { - // Replace with call to $operator(left, operator, right): - return ['call', ['name', '$eval'], - [left, ['string', operator], right]]; - } - } - - /** - * Compiles PaperScript code into JavaScript code. - * - * @name PaperScript.compile - * @function - * @param {String} code The PaperScript code. - * @return {String} The compiled PaperScript as JavaScript code. - */ - function compile(code) { - // Use parse-js to translate the code into a AST structure which is then - // walked and parsed for operators to overload. The resulting AST is - // translated back to code and evaluated. - var ast = parse_js.parse(code), - walker = parse_js.ast_walker(), - walk = walker.walk; - - ast = walker.with_walkers({ - 'binary': function(operator, left, right) { - // Handle simple mathematical operators here: - return handleOperator(operator, left = walk(left), - right = walk(right)) - // Always return something since we're walking left and - || [this[0], operator, left, right]; - }, - - 'assign': function(operator, left, right) { - var res = handleOperator(operator, left = walk(left), - right = walk(right)); - if (res) - return [this[0], true, left, res]; - return [this[0], operator, left, right]; - }, - - 'unary-prefix': function(operator, exp) { - if (signOperators[operator] && isDynamic(exp)) { - return ['call', ['name', '$sign'], - [['string', operator], walk(exp)]]; - } - } - }, function() { - return walk(ast); - }); - - return parse_js.gen_code(ast, { - beautify: true - }); - } - - function evaluate(code, scope) { - paper = scope; - var view = scope.project.view, - res; - with (scope) { - (function() { - var onEditOptions, onSelect, onDeselect, onReselect, - onMouseDown, onMouseUp, onMouseDrag, onMouseMove, - onKeyDown, onKeyUp, onFrame, onResize; - res = eval(compile(code)); - if (/on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)) { - Base.each(Tool.prototype._events, function(key) { - var value = eval(key); - if (value) { - scope.getTool()[key] = value; - } - }); - } - if (view) { - view.setOnResize(onResize); - view.fire('resize', { - size: view.size, - delta: new Point() - }); - view.setOnFrame(onFrame); - view.draw(); - } - }).call(scope); - } - return res; - } - - function request(url, scope) { - var xhr = new (window.ActiveXObject || XMLHttpRequest)( - 'Microsoft.XMLHTTP'); - xhr.open('GET', url, true); - if (xhr.overrideMimeType) { - xhr.overrideMimeType('text/plain'); - } - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - return evaluate(xhr.responseText, scope); - } - }; - return xhr.send(null); - } - - function load() { - var scripts = document.getElementsByTagName('script'); - for (var i = 0, l = scripts.length; i < l; i++) { - var script = scripts[i]; - if (/^text\/(?:x-|)paperscript$/.test(script.type) - && !script.getAttribute('data-paper-loaded')) { - var scope = new PaperScope(script); - scope.setup(PaperScript.getAttribute(script, 'canvas')); - if (script.src) { - request(script.src, scope); - } else { - evaluate(script.innerHTML, scope); - } - script.setAttribute('data-paper-loaded', true); - } - } - } - - DomEvent.add(window, { load: load }); - - function handleAttribute(name) { - name += 'Attribute'; - return function(el, attr) { - return el[name](attr) || el[name]('data-paper-' + attr); - }; - } - - return { - compile: compile, - evaluate: evaluate, - load: load, - getAttribute: handleAttribute('get'), - hasAttribute: handleAttribute('has') - }; - -}; - -this.load = PaperScript.load; - -Base.each(this, function(val, key) { - if (val && val.prototype instanceof Base) { - val._name = key; - } -}); - -this.enumerable = true; -return new (PaperScope.inject(this)); -}; diff --git a/test/tests/ImportSVG.js b/test/tests/ImportSVG.js index a964224d..e7fd358e 100644 --- a/test/tests/ImportSVG.js +++ b/test/tests/ImportSVG.js @@ -12,11 +12,21 @@ * Distributed under the MIT license. See LICENSE file for details. * * All rights reserved. +* +* This test file created by Stetson-Team-Alpha */ module('ImportSVG'); -/** -*TODO: Create SVGs utilizing ImportSVG methods -* WILL ADD MORE AFTER LEARNING SVG -*/ +test('Make a circle', function() +{ + var svgDocument = evt.target.ownerDocument; + var svgcircle = svgDocument.createElementNS(svgns, "circle"); + svgCircle.setAttributeNS(null, "cx", 25); + svgCircle.setAttributeNS(null, "cy", 25); + svgCircle.setAttributeNS(null, "r", 20); + svgCircle.setAttributeNS(null, "fill", "green"); + var circle = new ImportSVG(svgCircle) + equals(circle); +}); +/