1 | /**
|
---|
2 | * SVGTransformList
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2
|
---|
5 | *
|
---|
6 | * Copyright(c) 2010 Alexis Deveria
|
---|
7 | * Copyright(c) 2010 Jeff Schiller
|
---|
8 | */
|
---|
9 |
|
---|
10 | // Dependencies:
|
---|
11 | // 1) browser.js
|
---|
12 |
|
---|
13 | var svgedit = svgedit || {};
|
---|
14 |
|
---|
15 | (function() {
|
---|
16 |
|
---|
17 | if (!svgedit.transformlist) {
|
---|
18 | svgedit.transformlist = {};
|
---|
19 | }
|
---|
20 |
|
---|
21 | var svgroot = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
---|
22 |
|
---|
23 | // Helper function.
|
---|
24 | function transformToString(xform) {
|
---|
25 | var m = xform.matrix,
|
---|
26 | text = "";
|
---|
27 | switch(xform.type) {
|
---|
28 | case 1: // MATRIX
|
---|
29 | text = "matrix(" + [m.a,m.b,m.c,m.d,m.e,m.f].join(",") + ")";
|
---|
30 | break;
|
---|
31 | case 2: // TRANSLATE
|
---|
32 | text = "translate(" + m.e + "," + m.f + ")";
|
---|
33 | break;
|
---|
34 | case 3: // SCALE
|
---|
35 | if (m.a == m.d) text = "scale(" + m.a + ")";
|
---|
36 | else text = "scale(" + m.a + "," + m.d + ")";
|
---|
37 | break;
|
---|
38 | case 4: // ROTATE
|
---|
39 | var cx = 0, cy = 0;
|
---|
40 | // this prevents divide by zero
|
---|
41 | if (xform.angle != 0) {
|
---|
42 | var K = 1 - m.a;
|
---|
43 | cy = ( K * m.f + m.b*m.e ) / ( K*K + m.b*m.b );
|
---|
44 | cx = ( m.e - m.b * cy ) / K;
|
---|
45 | }
|
---|
46 | text = "rotate(" + xform.angle + " " + cx + "," + cy + ")";
|
---|
47 | break;
|
---|
48 | }
|
---|
49 | return text;
|
---|
50 | };
|
---|
51 |
|
---|
52 |
|
---|
53 | /**
|
---|
54 | * Map of SVGTransformList objects.
|
---|
55 | */
|
---|
56 | var listMap_ = {};
|
---|
57 |
|
---|
58 |
|
---|
59 | // **************************************************************************************
|
---|
60 | // SVGTransformList implementation for Webkit
|
---|
61 | // These methods do not currently raise any exceptions.
|
---|
62 | // These methods also do not check that transforms are being inserted. This is basically
|
---|
63 | // implementing as much of SVGTransformList that we need to get the job done.
|
---|
64 | //
|
---|
65 | // interface SVGEditTransformList {
|
---|
66 | // attribute unsigned long numberOfItems;
|
---|
67 | // void clear ( )
|
---|
68 | // SVGTransform initialize ( in SVGTransform newItem )
|
---|
69 | // SVGTransform getItem ( in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
---|
70 | // SVGTransform insertItemBefore ( in SVGTransform newItem, in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
---|
71 | // SVGTransform replaceItem ( in SVGTransform newItem, in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
---|
72 | // SVGTransform removeItem ( in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
---|
73 | // SVGTransform appendItem ( in SVGTransform newItem )
|
---|
74 | // NOT IMPLEMENTED: SVGTransform createSVGTransformFromMatrix ( in SVGMatrix matrix );
|
---|
75 | // NOT IMPLEMENTED: SVGTransform consolidate ( );
|
---|
76 | // }
|
---|
77 | // **************************************************************************************
|
---|
78 | svgedit.transformlist.SVGTransformList = function(elem) {
|
---|
79 | this._elem = elem || null;
|
---|
80 | this._xforms = [];
|
---|
81 | // TODO: how do we capture the undo-ability in the changed transform list?
|
---|
82 | this._update = function() {
|
---|
83 | var tstr = "";
|
---|
84 | var concatMatrix = svgroot.createSVGMatrix();
|
---|
85 | for (var i = 0; i < this.numberOfItems; ++i) {
|
---|
86 | var xform = this._list.getItem(i);
|
---|
87 | tstr += transformToString(xform) + " ";
|
---|
88 | }
|
---|
89 | this._elem.setAttribute("transform", tstr);
|
---|
90 | };
|
---|
91 | this._list = this;
|
---|
92 | this._init = function() {
|
---|
93 | // Transform attribute parser
|
---|
94 | var str = this._elem.getAttribute("transform");
|
---|
95 | if(!str) return;
|
---|
96 |
|
---|
97 | // TODO: Add skew support in future
|
---|
98 | var re = /\s*((scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/;
|
---|
99 | var arr = [];
|
---|
100 | var m = true;
|
---|
101 | while(m) {
|
---|
102 | m = str.match(re);
|
---|
103 | str = str.replace(re,'');
|
---|
104 | if(m && m[1]) {
|
---|
105 | var x = m[1];
|
---|
106 | var bits = x.split(/\s*\(/);
|
---|
107 | var name = bits[0];
|
---|
108 | var val_bits = bits[1].match(/\s*(.*?)\s*\)/);
|
---|
109 | val_bits[1] = val_bits[1].replace(/(\d)-/g, "$1 -");
|
---|
110 | var val_arr = val_bits[1].split(/[, ]+/);
|
---|
111 | var letters = 'abcdef'.split('');
|
---|
112 | var mtx = svgroot.createSVGMatrix();
|
---|
113 | $.each(val_arr, function(i, item) {
|
---|
114 | val_arr[i] = parseFloat(item);
|
---|
115 | if(name == 'matrix') {
|
---|
116 | mtx[letters[i]] = val_arr[i];
|
---|
117 | }
|
---|
118 | });
|
---|
119 | var xform = svgroot.createSVGTransform();
|
---|
120 | var fname = 'set' + name.charAt(0).toUpperCase() + name.slice(1);
|
---|
121 | var values = name=='matrix'?[mtx]:val_arr;
|
---|
122 |
|
---|
123 | if (name == 'scale' && values.length == 1) {
|
---|
124 | values.push(values[0]);
|
---|
125 | } else if (name == 'translate' && values.length == 1) {
|
---|
126 | values.push(0);
|
---|
127 | } else if (name == 'rotate' && values.length == 1) {
|
---|
128 | values.push(0);
|
---|
129 | values.push(0);
|
---|
130 | }
|
---|
131 | xform[fname].apply(xform, values);
|
---|
132 | this._list.appendItem(xform);
|
---|
133 | }
|
---|
134 | }
|
---|
135 | };
|
---|
136 | this._removeFromOtherLists = function(item) {
|
---|
137 | if (item) {
|
---|
138 | // Check if this transform is already in a transformlist, and
|
---|
139 | // remove it if so.
|
---|
140 | var found = false;
|
---|
141 | for (var id in listMap_) {
|
---|
142 | var tl = listMap_[id];
|
---|
143 | for (var i = 0, len = tl._xforms.length; i < len; ++i) {
|
---|
144 | if(tl._xforms[i] == item) {
|
---|
145 | found = true;
|
---|
146 | tl.removeItem(i);
|
---|
147 | break;
|
---|
148 | }
|
---|
149 | }
|
---|
150 | if (found) {
|
---|
151 | break;
|
---|
152 | }
|
---|
153 | }
|
---|
154 | }
|
---|
155 | };
|
---|
156 |
|
---|
157 | this.numberOfItems = 0;
|
---|
158 | this.clear = function() {
|
---|
159 | this.numberOfItems = 0;
|
---|
160 | this._xforms = [];
|
---|
161 | };
|
---|
162 |
|
---|
163 | this.initialize = function(newItem) {
|
---|
164 | this.numberOfItems = 1;
|
---|
165 | this._removeFromOtherLists(newItem);
|
---|
166 | this._xforms = [newItem];
|
---|
167 | };
|
---|
168 |
|
---|
169 | this.getItem = function(index) {
|
---|
170 | if (index < this.numberOfItems && index >= 0) {
|
---|
171 | return this._xforms[index];
|
---|
172 | }
|
---|
173 | throw {code: 1}; // DOMException with code=INDEX_SIZE_ERR
|
---|
174 | };
|
---|
175 |
|
---|
176 | this.insertItemBefore = function(newItem, index) {
|
---|
177 | var retValue = null;
|
---|
178 | if (index >= 0) {
|
---|
179 | if (index < this.numberOfItems) {
|
---|
180 | this._removeFromOtherLists(newItem);
|
---|
181 | var newxforms = new Array(this.numberOfItems + 1);
|
---|
182 | // TODO: use array copying and slicing
|
---|
183 | for ( var i = 0; i < index; ++i) {
|
---|
184 | newxforms[i] = this._xforms[i];
|
---|
185 | }
|
---|
186 | newxforms[i] = newItem;
|
---|
187 | for ( var j = i+1; i < this.numberOfItems; ++j, ++i) {
|
---|
188 | newxforms[j] = this._xforms[i];
|
---|
189 | }
|
---|
190 | this.numberOfItems++;
|
---|
191 | this._xforms = newxforms;
|
---|
192 | retValue = newItem;
|
---|
193 | this._list._update();
|
---|
194 | }
|
---|
195 | else {
|
---|
196 | retValue = this._list.appendItem(newItem);
|
---|
197 | }
|
---|
198 | }
|
---|
199 | return retValue;
|
---|
200 | };
|
---|
201 |
|
---|
202 | this.replaceItem = function(newItem, index) {
|
---|
203 | var retValue = null;
|
---|
204 | if (index < this.numberOfItems && index >= 0) {
|
---|
205 | this._removeFromOtherLists(newItem);
|
---|
206 | this._xforms[index] = newItem;
|
---|
207 | retValue = newItem;
|
---|
208 | this._list._update();
|
---|
209 | }
|
---|
210 | return retValue;
|
---|
211 | };
|
---|
212 |
|
---|
213 | this.removeItem = function(index) {
|
---|
214 | if (index < this.numberOfItems && index >= 0) {
|
---|
215 | var retValue = this._xforms[index];
|
---|
216 | var newxforms = new Array(this.numberOfItems - 1);
|
---|
217 | for (var i = 0; i < index; ++i) {
|
---|
218 | newxforms[i] = this._xforms[i];
|
---|
219 | }
|
---|
220 | for (var j = i; j < this.numberOfItems-1; ++j, ++i) {
|
---|
221 | newxforms[j] = this._xforms[i+1];
|
---|
222 | }
|
---|
223 | this.numberOfItems--;
|
---|
224 | this._xforms = newxforms;
|
---|
225 | this._list._update();
|
---|
226 | return retValue;
|
---|
227 | } else {
|
---|
228 | throw {code: 1}; // DOMException with code=INDEX_SIZE_ERR
|
---|
229 | }
|
---|
230 | };
|
---|
231 |
|
---|
232 | this.appendItem = function(newItem) {
|
---|
233 | this._removeFromOtherLists(newItem);
|
---|
234 | this._xforms.push(newItem);
|
---|
235 | this.numberOfItems++;
|
---|
236 | this._list._update();
|
---|
237 | return newItem;
|
---|
238 | };
|
---|
239 | };
|
---|
240 |
|
---|
241 |
|
---|
242 | svgedit.transformlist.resetListMap = function() {
|
---|
243 | listMap_ = {};
|
---|
244 | };
|
---|
245 |
|
---|
246 | /**
|
---|
247 | * Removes transforms of the given element from the map.
|
---|
248 | * Parameters:
|
---|
249 | * elem - a DOM Element
|
---|
250 | */
|
---|
251 | svgedit.transformlist.removeElementFromListMap = function(elem) {
|
---|
252 | if (elem.id && listMap_[elem.id]) {
|
---|
253 | delete listMap_[elem.id];
|
---|
254 | }
|
---|
255 | };
|
---|
256 |
|
---|
257 | // Function: getTransformList
|
---|
258 | // Returns an object that behaves like a SVGTransformList for the given DOM element
|
---|
259 | //
|
---|
260 | // Parameters:
|
---|
261 | // elem - DOM element to get a transformlist from
|
---|
262 | svgedit.transformlist.getTransformList = function(elem) {
|
---|
263 | if (!svgedit.browser.supportsNativeTransformLists()) {
|
---|
264 | var id = elem.id;
|
---|
265 | if(!id) {
|
---|
266 | // Get unique ID for temporary element
|
---|
267 | id = 'temp';
|
---|
268 | }
|
---|
269 | var t = listMap_[id];
|
---|
270 | if (!t || id == 'temp') {
|
---|
271 | listMap_[id] = new svgedit.transformlist.SVGTransformList(elem);
|
---|
272 | listMap_[id]._init();
|
---|
273 | t = listMap_[id];
|
---|
274 | }
|
---|
275 | return t;
|
---|
276 | }
|
---|
277 | else if (elem.transform) {
|
---|
278 | return elem.transform.baseVal;
|
---|
279 | }
|
---|
280 | else if (elem.gradientTransform) {
|
---|
281 | return elem.gradientTransform.baseVal;
|
---|
282 | }
|
---|
283 | else if (elem.patternTransform) {
|
---|
284 | return elem.patternTransform.baseVal;
|
---|
285 | }
|
---|
286 |
|
---|
287 | return null;
|
---|
288 | };
|
---|
289 |
|
---|
290 |
|
---|
291 | })(); |
---|