From: Dmitry Baranovskiy Date: Wed, 13 Apr 2011 00:05:45 +0000 (+1000) Subject: Added split method to matrix, fixed caching a bit, first iteration of fix for correct... X-Git-Tag: v2.0.0~24 X-Git-Url: http://git.roojs.org/?p=raphael;a=commitdiff_plain;h=9962a268042cde75d4e9d9ccb0f6641ed99a6f72 Added split method to matrix, fixed caching a bit, first iteration of fix for correct transformation in IE for gradient and images. --- diff --git a/raphael.js b/raphael.js index 0a1ee2c..1a66634 100644 --- a/raphael.js +++ b/raphael.js @@ -659,6 +659,11 @@ R._path2string = function () { return this.join(",").replace(p2s, "$1"); }; + function repush(array, item) { + for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) { + return array.push(array.splice(i, 1)[0]); + } + } function cacher(f, scope, postprocessor) { function newf() { var arg = Array.prototype.slice.call(arguments, 0), @@ -666,6 +671,7 @@ cache = newf.cache = newf.cache || {}, count = newf.count = newf.count || []; if (cache[has](args)) { + repush(count, args); return postprocessor ? postprocessor(cache[args]) : cache[args]; } count.length >= 1e3 && delete cache[count.shift()]; @@ -1650,6 +1656,7 @@ } }, equaliseTransform = function (t1, t2) { + t2 = Str(t2).replace(/\.{3}|\u2026/g, t1); t1 = R.parseTransformString(t1) || []; t2 = R.parseTransformString(t2) || []; var maxlength = mmax(t1.length, t2.length), @@ -1660,9 +1667,9 @@ for (; i < maxlength; i++) { tt1 = t1[i] || getEmpty(t2[i]); tt2 = t2[i] || getEmpty(tt1); - if ( (tt1[0] != tt2[0]) || - (tt1[0] == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || - (tt1[0] == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) + if ((tt1[0] != tt2[0]) || + (tt1[0] == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || + (tt1[0] == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) ) { return; } @@ -1738,80 +1745,123 @@ this.f = 0; } } - var matrixproto = Matrix.prototype; - matrixproto.add = function (a, b, c, d, e, f) { - var out = [[], [], []], - m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]], - matrix = [[a, c, e], [b, d, f], [0, 0, 1]], - x, y, z, res; + (function (matrixproto) { + matrixproto.add = function (a, b, c, d, e, f) { + var out = [[], [], []], + m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]], + matrix = [[a, c, e], [b, d, f], [0, 0, 1]], + x, y, z, res; - if (a && a instanceof Matrix) { - matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]]; - } + if (a && a instanceof Matrix) { + matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]]; + } - for (x = 0; x < 3; x++) { - for (y = 0; y < 3; y++) { - res = 0; - for (z = 0; z < 3; z++) { - res += m[x][z] * matrix[z][y]; + for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) { + res = 0; + for (z = 0; z < 3; z++) { + res += m[x][z] * matrix[z][y]; + } + out[x][y] = res; } - out[x][y] = res; } + this.a = out[0][0]; + this.b = out[1][0]; + this.c = out[0][1]; + this.d = out[1][1]; + this.e = out[0][2]; + this.f = out[1][2]; + }; + matrixproto.invert = function () { + var me = this, + x = me.a * me.d - me.b * me.c; + return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x); + }; + matrixproto.clone = function () { + return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f); + }; + matrixproto.translate = function (x, y) { + this.add(1, 0, 0, 1, x, y); + }; + matrixproto.scale = function (x, y, cx, cy) { + y == null && (y = x); + this.add(1, 0, 0, 1, cx, cy); + this.add(x, 0, 0, y, 0, 0); + this.add(1, 0, 0, 1, -cx, -cy); + }; + matrixproto.rotate = function (a, x, y) { + a = R.rad(a); + var cos = +math.cos(a).toFixed(9), + sin = +math.sin(a).toFixed(9); + this.add(cos, sin, -sin, cos, x, y); + this.add(1, 0, 0, 1, -x, -y); + }; + matrixproto.x = function (x, y) { + return x * this.a + y * this.c + this.e; + }; + matrixproto.y = function (x, y) { + return x * this.b + y * this.d + this.f; + }; + matrixproto.get = function (i) { + return +this[Str.fromCharCode(97 + i)].toFixed(4); + }; + matrixproto.toString = function () { + return R.svg ? + "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" : + [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join(); + }; + matrixproto.toFilter = function () { + return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) + + ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) + + ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')"; + }; + matrixproto.offset = function () { + return [this.e.toFixed(4), this.f.toFixed(4)]; + }; + function norm(a) { + return a[0] * a[0] + a[1] * a[1]; } - this.a = out[0][0]; - this.b = out[1][0]; - this.c = out[0][1]; - this.d = out[1][1]; - this.e = out[0][2]; - this.f = out[1][2]; - }; - matrixproto.invert = function () { - var me = this, - x = me.a * me.d - me.b * me.c; - return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x); - }; - matrixproto.clone = function () { - return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f); - }; - matrixproto.translate = function (x, y) { - this.add(1, 0, 0, 1, x, y); - }; - matrixproto.scale = function (x, y, cx, cy) { - y == null && (y = x); - this.add(1, 0, 0, 1, cx, cy); - this.add(x, 0, 0, y, 0, 0); - this.add(1, 0, 0, 1, -cx, -cy); - }; - matrixproto.rotate = function (a, x, y) { - a = R.rad(a); - var cos = +math.cos(a).toFixed(9), - sin = +math.sin(a).toFixed(9); - this.add(cos, sin, -sin, cos, x, y); - this.add(1, 0, 0, 1, -x, -y); - }; - matrixproto.x = function (x, y) { - return x * this.a + y * this.c + this.e; - }; - matrixproto.y = function (x, y) { - return x * this.b + y * this.d + this.f; - }; - matrixproto.get = function (i) { - return +this[Str.fromCharCode(97 + i)].toFixed(4); - }; - matrixproto.toString = function () { - return R.svg ? - "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" : - [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join(); - }; - matrixproto.toFilter = function () { - return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) + - ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) + - ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmedthod='auto expand')"; - }; - matrixproto.offset = function () { - return [this.e.toFixed(4), this.f.toFixed(4)]; - }; + function normalize(a) { + var mag = math.sqrt(norm(a)); + a[0] /= mag; + a[1] /= mag; + } + matrixproto.split = function () { + var out = {}; + // translation + out.dx = this.e; + out.dy = this.f; + this.e = this.f = 0; + + // scale and shear + var row = [[this.a, this.c], [this.b, this.d]]; + out.scalex = math.sqrt(norm(row[0])); + normalize(row[0]); + + out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1]; + row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear]; + + out.scaley = math.sqrt(norm(row[1])); + normalize(row[1]); + out.shear /= out.scaley; + // rotation + out.rotate = R.deg(math.asin(-row[0][1])); + + out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate); + + return out; + }; + matrixproto.toTransformString = function () { + var s = this.split(); + if (s.isSimple) { + return "t" + [s.dx, s.dy] + "s" + [s.scalex, s.scaley, 0, 0] + "r" + [s.rotate, 0, 0]; + } else { + return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)]; + } + }; + })(Matrix.prototype); + // SVG if (R.svg) { var xlink = "http://www.w3.org/1999/xlink", @@ -2609,7 +2659,6 @@ extractTransform(this, tstr); this.clip && $(this.clip, {transform: this.matrix.invert()}); - // this.gradient && $(this.gradient, {gradientTransform: this.matrix.invert()}); this.pattern && updatePosition(this); this.node && $(this.node, {transform: this.matrix}); @@ -3154,18 +3203,19 @@ dy: m.y(dx, dy) }; }, - setCoords = function (p) { + setCoords = function (p, sx, sy, dx, dy, deg) { var _ = p._, - sx = _.sx, - sy = _.sy, - deg = _.deg, - dx = _.dx, - dy = _.dy, + m = p.matrix, + // sp = m.split(), + // sx = sp.scalex, + // sy = sp.scaley, + // deg = sp.rotate, + // dx = sp.dx, + // dy = sp.dy, fillpos = _.fillpos, o = p.node, s = o.style, y = 1, - m = p.matrix, flip = "", dxdy, kx = zoom / sx, @@ -3330,7 +3380,7 @@ fill.on = false; } if (fill.on && params.fill) { - var isURL = params.fill.match(ISURL); + var isURL = Str(params.fill).match(ISURL); if (isURL) { fill.parentNode == node && node.removeChild(fill); fill.rotate = true; @@ -3534,50 +3584,34 @@ extractTransform(this, tstr); var matrix = this.matrix.clone(), vbs = this.paper._viewBoxShift, - skew = this.skew; + skew = this.skew, + o = this.node; matrix.translate(-.5, -.5); if (vbs) { matrix.scale(vbs.scale, vbs.scale, -1, -1); - matrix.add(1, 0, 0, 1, vbs.dx, vbs.dy); - } - if (this.type == "image") { - if (Str(tstr).indexOf("m") + 1) { - this.node.style.filter = matrix.toFilter(); + matrix.translate(vbs.dx, vbs.dy); + } + if (Str(this.attrs.fill).indexOf("-") + 1 || this.type == "image") { + skew.matrix = "1 0 0 1"; + skew.offset = "0 0"; + var split = matrix.split(); + if (!split.isSimple) { + o.style.filter = matrix.toFilter(); var bb = this.getBBox(), bbt = this.getBBox(1), - im = matrix.invert(), - dx = im.x(bb.x, bb.y) - im.x(bbt.x, bbt.y), - dy = im.y(bb.x, bb.y) - im.y(bbt.x, bbt.y); - // skew.offset = dx + S + dy; - // this.node.getElementsByTagName(fillString)[0].position = skew.offset; + dx = bb.x - bbt.x, + dy = bb.y - bbt.y; + o.coordorigin = (dx * -zoom) + S + (dy * -zoom); + setCoords(this, 1, 1, dx, dy, 0); } else { - this.node.style.filter = E; - setCoords(this); + o.style.filter = E; + alert("clean"); + setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate); } } else { - // o = this.node, - // _ = this._, - // fillpos = _.fillpos, - // deg, - // matrix = this.matrix; - // fill = o.getElementsByTagName(fillString)[0], - // angle = fill.angle; - - this.node.style.filter = E; + o.style.filter = E; skew.matrix = Str(matrix); skew.offset = matrix.offset(); - - // if (0&&angle) { - // angle = R.rad(270 - angle); - // var dx = 100 * math.cos(angle), - // dy = 100 * math.sin(angle), - // zx = matrix.x(0, 0), - // zy = matrix.y(0, 0), - // mx = matrix.x(dx, dy), - // my = matrix.y(dx, dy); - // angle = R.angle(zx, zy, mx, my); - // fill.angle = (270 - angle) % 360; - // } } return this; }; @@ -3692,6 +3726,7 @@ res[i] = this.attrs[i]; } res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; + res.transform = this._.transform; return res; } if (value == null && R.is(name, "string")) { @@ -3878,7 +3913,7 @@ res._.fillpos = [x, y]; res._.fillsize = [w, h]; node.appendChild(fill); - setCoords(res); + setCoords(res, 1, 1, 0, 0, 0); return res; }; theText = function (vml, x, y, text) { @@ -3892,7 +3927,7 @@ path.textpathok = true; o.string = Str(text); o.on = true; - el.style.cssText = "position:absolute;left:0;top:0;width:1;height:1"; + el.style.cssText = "position:absolute;left:0;top:0;width:1px;height:1px"; el.coordsize = zoom + S + zoom; el.coordorigin = "0 0"; var p = new Element(el, vml), @@ -5074,15 +5109,13 @@ R.is(f, "function") && f.call(el); }); })(e.callback, that, e.anim); - if (--e.repeat) { - that.attr(e.origin); - e.start = Now; - } else { - that.attr(to); - animationElements.splice(l--, 1); + that.attr(to); + animationElements.splice(l--, 1); + if (e.repeat > 1 && !e.next) { + runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1); } if (e.next && !e.stop) { - runAnimation(e.anim, e.el, e.next, null, e.totalOrigin); + runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat); } } } @@ -5090,7 +5123,7 @@ animationElements.length && requestAnimFrame(animation); }, upto255 = function (color) { - return mmax(mmin(color, 255), 0); + return color > 255 ? 255 : color < 0 ? 0 : color; }; /*\ * Element.animateWith @@ -5228,7 +5261,7 @@ a.times = math.floor(mmax(times, 0)) || 1; return a; }; - function runAnimation(anim, element, percent, status, totalOrigin) { + function runAnimation(anim, element, percent, status, totalOrigin, times) { percent = toFloat(percent); var params, isInAnim, @@ -5319,25 +5352,25 @@ } } } else { - var m = (element.matrix || new Matrix).m, + var m = (element.matrix || new Matrix), to2 = {_:{transform: _.transform}, getBBox: function () { return element.getBBox(); }}; from[attr] = [ - m[0][0], - m[1][0], - m[0][1], - m[1][1], - m[0][2], - m[1][2] + m.a, + m.b, + m.c, + m.d, + m.e, + m.f ]; extractTransform(to2, to[attr]); to[attr] = to2._.transform; diff[attr] = [ - (to2.matrix.m[0][0] - m[0][0]) / ms, - (to2.matrix.m[1][0] - m[1][0]) / ms, - (to2.matrix.m[0][1] - m[0][1]) / ms, - (to2.matrix.m[1][1] - m[1][1]) / ms, - (to2.matrix.m[0][2] - m[0][2]) / ms, - (to2.matrix.m[1][2] - m[1][2]) / ms + (to2.matrix.a - m.a) / ms, + (to2.matrix.b - m.b) / ms, + (to2.matrix.c - m.c) / ms, + (to2.matrix.d - m.d) / ms, + (to2.matrix.e - m.e) / ms, + (to2.matrix.e - m.f) / ms ]; // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy]; // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }}; @@ -5407,7 +5440,7 @@ callback: params.callback, prev: prev, next: next, - repeat: anim.times, + repeat: times || anim.times, origin: element.attr(), totalOrigin: totalOrigin }; @@ -5909,34 +5942,103 @@ oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R); /* - * Eve 0.2.1 - JavaScript Events Library + * Eve 0.2.3 - JavaScript Events Library * * Copyright (c) 2010 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. */ var eve = R.eve = (function () { - var version = "0.2.1", + var version = "0.2.3", has = "hasOwnProperty", separator = /[\.\/]/, wildcard = "*", + fun = function () {}, + numsort = function (a, b) { + return a - b; + }, + current_event, events = {n: {}}, + /*\ + * eve + [ method ] + ** + * Fires event with given `name`, given scope and other parameters. + ** + > Arguments + ** + - name (string) name of the event, dot (`.`) or slash (`/`) separated + - scope (object) context for the event handlers + - varargs (...) the rest of arguments will be sent to event handlers + ** + = (array) array of errors, if any. Each element of the array is in format: + o { + o error (string) error message + o func (function) handler that caused error + o } + \*/ eve = function (name, scope) { var e = events, args = Array.prototype.slice.call(arguments, 2), listeners = eve.listeners(name), + z = 0, + f = false, + l, + indexed = [], + queue = {}, errors = []; - for (var i = 0, ii = listeners.length; i < ii; i++) { - try { - listeners[i].apply(scope, args); - } catch (ex) { - errors.push({error: ex && ex.message || ex, func: listeners[i]}); + current_event = name; + for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) { + indexed.push(listeners[i].zIndex); + if (listeners[i].zIndex < 0) { + queue[listeners[i].zIndex] = listeners[i]; + } + } + indexed.sort(numsort); + while (indexed[z] < 0) { + l = queue[indexed[z++]]; + if (l.apply(scope, args) === f) { + return f; } } - if (errors.length) { - return errors; + for (i = 0; i < ii; i++) { + l = listeners[i]; + if ("zIndex" in l) { + if (l.zIndex == indexed[z]) { + if (l.apply(scope, args) === f) { + return f; + } + do { + z++; + l = queue[indexed[z]]; + if (l) { + if (l.apply(scope, args) === f) { + return f; + } + } + } while (l) + } else { + queue[l.zIndex] = l; + } + } else { + if (l.apply(scope, args) === f) { + return f; + } + } } }; + /*\ + * eve.listeners + [ method ] + ** + * Internal method which gives you array of all event handlers that will be triggered by the given `name`. + ** + > Arguments + ** + - name (string) name of the event, dot (`.`) or slash (`/`) separated + ** + = (array) array of event handlers + \*/ eve.listeners = function (name) { var names = name.split(separator), e = events, @@ -5968,6 +6070,30 @@ } return out; }; + + /*\ + * eve.on + [ method ] + ** + * Binds given event handler with a given name. You can use wildcards “`*`” for the names: + | eve.on("*.under.*", f); + | eve("mouse.under.floor"); // triggers f + * Use @eve to trigger the listener. + ** + > Arguments + ** + - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards + - f (function) event handler function + ** + = (function) returned function accept one number parameter that represents z-index of the handler. It is optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment. + > Example: + | eve.on("mouse", eat)(2); + | eve.on("mouse", scream); + | eve.on("mouse", catch)(1); + * This will ensure that `catch` function will be called before `eat`. + * If you want to put you hadler before not indexed handlers specify negative value. + * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”. + \*/ eve.on = function (name, f) { var names = name.split(separator), e = events; @@ -5978,10 +6104,46 @@ } e.f = e.f || []; for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) { - return false; + return fun; } e.f.push(f); + return function (zIndex) { + if (+zIndex == +zIndex) { + f.zIndex = +zIndex; + } + }; }; + /*\ + * eve.nt + [ method ] + ** + * Could be used inside event handler to figure out actual name of the event. + ** + > Arguments + ** + - subname (string) #optional subname of the event + ** + = (string) name of the event, if `subname` is not specified + * or + = (boolean) `true`, if current event’s name contains `subname` + \*/ + eve.nt = function (subname) { + if (subname) { + return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event); + } + return current_event; + }; + /*\ + * eve.unbind + [ method ] + ** + * Removes given function from the list of event listeners assigned to given name. + ** + > Arguments + ** + - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards + - f (function) event handler function + \*/ eve.unbind = function (name, f) { var names = name.split(separator), e, @@ -6032,8 +6194,13 @@ e = e.n; } } - return true; }; + /*\ + * eve.version + [ property (string) ] + ** + * Current version of the library. + \*/ eve.version = version; eve.toString = function () { return "You are running Eve " + version;