Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / fx / PropertyHandler.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.fx.PropertyHandler
17  * @ignore
18  */
19 Ext.define('Ext.fx.PropertyHandler', {
20
21     /* Begin Definitions */
22
23     requires: ['Ext.draw.Draw'],
24
25     statics: {
26         defaultHandler: {
27             pixelDefaultsRE: /width|height|top$|bottom$|left$|right$/i,
28             unitRE: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/,
29             scrollRE: /^scroll/i,
30
31             computeDelta: function(from, end, damper, initial, attr) {
32                 damper = (typeof damper == 'number') ? damper : 1;
33                 var unitRE = this.unitRE,
34                     match = unitRE.exec(from),
35                     start, units;
36                 if (match) {
37                     from = match[1];
38                     units = match[2];
39                     if (!this.scrollRE.test(attr) && !units && this.pixelDefaultsRE.test(attr)) {
40                         units = 'px';
41                     }
42                 }
43                 from = +from || 0;
44
45                 match = unitRE.exec(end);
46                 if (match) {
47                     end = match[1];
48                     units = match[2] || units;
49                 }
50                 end = +end || 0;
51                 start = (initial != null) ? initial : from;
52                 return {
53                     from: from,
54                     delta: (end - start) * damper,
55                     units: units
56                 };
57             },
58
59             get: function(from, end, damper, initialFrom, attr) {
60                 var ln = from.length,
61                     out = [],
62                     i, initial, res, j, len;
63                 for (i = 0; i < ln; i++) {
64                     if (initialFrom) {
65                         initial = initialFrom[i][1].from;
66                     }
67                     if (Ext.isArray(from[i][1]) && Ext.isArray(end)) {
68                         res = [];
69                         j = 0;
70                         len = from[i][1].length;
71                         for (; j < len; j++) {
72                             res.push(this.computeDelta(from[i][1][j], end[j], damper, initial, attr));
73                         }
74                         out.push([from[i][0], res]);
75                     }
76                     else {
77                         out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]);
78                     }
79                 }
80                 return out;
81             },
82
83             set: function(values, easing) {
84                 var ln = values.length,
85                     out = [],
86                     i, val, res, len, j;
87                 for (i = 0; i < ln; i++) {
88                     val  = values[i][1];
89                     if (Ext.isArray(val)) {
90                         res = [];
91                         j = 0;
92                         len = val.length;
93                         for (; j < len; j++) {
94                             res.push(val[j].from + (val[j].delta * easing) + (val[j].units || 0));
95                         }
96                         out.push([values[i][0], res]);
97                     } else {
98                         out.push([values[i][0], val.from + (val.delta * easing) + (val.units || 0)]);
99                     }
100                 }
101                 return out;
102             }
103         },
104         color: {
105             rgbRE: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
106             hexRE: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
107             hex3RE: /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i,
108
109             parseColor : function(color, damper) {
110                 damper = (typeof damper == 'number') ? damper : 1;
111                 var base,
112                     out = false,
113                     match;
114
115                 Ext.each([this.hexRE, this.rgbRE, this.hex3RE], function(re, idx) {
116                     base = (idx % 2 == 0) ? 16 : 10;
117                     match = re.exec(color);
118                     if (match && match.length == 4) {
119                         if (idx == 2) {
120                             match[1] += match[1];
121                             match[2] += match[2];
122                             match[3] += match[3];
123                         }
124                         out = {
125                             red: parseInt(match[1], base),
126                             green: parseInt(match[2], base),
127                             blue: parseInt(match[3], base)
128                         };
129                         return false;
130                     }
131                 });
132                 return out || color;
133             },
134
135             computeDelta: function(from, end, damper, initial) {
136                 from = this.parseColor(from);
137                 end = this.parseColor(end, damper);
138                 var start = initial ? initial : from,
139                     tfrom = typeof start,
140                     tend = typeof end;
141                 //Extra check for when the color string is not recognized.
142                 if (tfrom == 'string' ||  tfrom == 'undefined' 
143                   || tend == 'string' || tend == 'undefined') {
144                     return end || start;
145                 }
146                 return {
147                     from:  from,
148                     delta: {
149                         red: Math.round((end.red - start.red) * damper),
150                         green: Math.round((end.green - start.green) * damper),
151                         blue: Math.round((end.blue - start.blue) * damper)
152                     }
153                 };
154             },
155
156             get: function(start, end, damper, initialFrom) {
157                 var ln = start.length,
158                     out = [],
159                     i, initial;
160                 for (i = 0; i < ln; i++) {
161                     if (initialFrom) {
162                         initial = initialFrom[i][1].from;
163                     }
164                     out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
165                 }
166                 return out;
167             },
168
169             set: function(values, easing) {
170                 var ln = values.length,
171                     out = [],
172                     i, val, parsedString, from, delta;
173                 for (i = 0; i < ln; i++) {
174                     val = values[i][1];
175                     if (val) {
176                         from = val.from;
177                         delta = val.delta;
178                         //multiple checks to reformat the color if it can't recognized by computeDelta.
179                         val = (typeof val == 'object' && 'red' in val)? 
180                                 'rgb(' + val.red + ', ' + val.green + ', ' + val.blue + ')' : val;
181                         val = (typeof val == 'object' && val.length)? val[0] : val;
182                         if (typeof val == 'undefined') {
183                             return [];
184                         }
185                         parsedString = typeof val == 'string'? val :
186                             'rgb(' + [
187                                   (from.red + Math.round(delta.red * easing)) % 256,
188                                   (from.green + Math.round(delta.green * easing)) % 256,
189                                   (from.blue + Math.round(delta.blue * easing)) % 256
190                               ].join(',') + ')';
191                         out.push([
192                             values[i][0],
193                             parsedString
194                         ]);
195                     }
196                 }
197                 return out;
198             }
199         },
200         object: {
201             interpolate: function(prop, damper) {
202                 damper = (typeof damper == 'number') ? damper : 1;
203                 var out = {},
204                     p;
205                 for(p in prop) {
206                     out[p] = parseInt(prop[p], 10) * damper;
207                 }
208                 return out;
209             },
210
211             computeDelta: function(from, end, damper, initial) {
212                 from = this.interpolate(from);
213                 end = this.interpolate(end, damper);
214                 var start = initial ? initial : from,
215                     delta = {},
216                     p;
217
218                 for(p in end) {
219                     delta[p] = end[p] - start[p];
220                 }
221                 return {
222                     from:  from,
223                     delta: delta
224                 };
225             },
226
227             get: function(start, end, damper, initialFrom) {
228                 var ln = start.length,
229                     out = [],
230                     i, initial;
231                 for (i = 0; i < ln; i++) {
232                     if (initialFrom) {
233                         initial = initialFrom[i][1].from;
234                     }
235                     out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
236                 }
237                 return out;
238             },
239
240             set: function(values, easing) {
241                 var ln = values.length,
242                     out = [],
243                     outObject = {},
244                     i, from, delta, val, p;
245                 for (i = 0; i < ln; i++) {
246                     val  = values[i][1];
247                     from = val.from;
248                     delta = val.delta;
249                     for (p in from) {
250                         outObject[p] = Math.round(from[p] + delta[p] * easing);
251                     }
252                     out.push([
253                         values[i][0],
254                         outObject
255                     ]);
256                 }
257                 return out;
258             }
259         },
260
261         path: {
262             computeDelta: function(from, end, damper, initial) {
263                 damper = (typeof damper == 'number') ? damper : 1;
264                 var start;
265                 from = +from || 0;
266                 end = +end || 0;
267                 start = (initial != null) ? initial : from;
268                 return {
269                     from: from,
270                     delta: (end - start) * damper
271                 };
272             },
273
274             forcePath: function(path) {
275                 if (!Ext.isArray(path) && !Ext.isArray(path[0])) {
276                     path = Ext.draw.Draw.parsePathString(path);
277                 }
278                 return path;
279             },
280
281             get: function(start, end, damper, initialFrom) {
282                 var endPath = this.forcePath(end),
283                     out = [],
284                     startLn = start.length,
285                     startPathLn, pointsLn, i, deltaPath, initial, j, k, path, startPath;
286                 for (i = 0; i < startLn; i++) {
287                     startPath = this.forcePath(start[i][1]);
288
289                     deltaPath = Ext.draw.Draw.interpolatePaths(startPath, endPath);
290                     startPath = deltaPath[0];
291                     endPath = deltaPath[1];
292
293                     startPathLn = startPath.length;
294                     path = [];
295                     for (j = 0; j < startPathLn; j++) {
296                         deltaPath = [startPath[j][0]];
297                         pointsLn = startPath[j].length;
298                         for (k = 1; k < pointsLn; k++) {
299                             initial = initialFrom && initialFrom[0][1][j][k].from;
300                             deltaPath.push(this.computeDelta(startPath[j][k], endPath[j][k], damper, initial));
301                         }
302                         path.push(deltaPath);
303                     }
304                     out.push([start[i][0], path]);
305                 }
306                 return out;
307             },
308
309             set: function(values, easing) {
310                 var ln = values.length,
311                     out = [],
312                     i, j, k, newPath, calcPath, deltaPath, deltaPathLn, pointsLn;
313                 for (i = 0; i < ln; i++) {
314                     deltaPath = values[i][1];
315                     newPath = [];
316                     deltaPathLn = deltaPath.length;
317                     for (j = 0; j < deltaPathLn; j++) {
318                         calcPath = [deltaPath[j][0]];
319                         pointsLn = deltaPath[j].length;
320                         for (k = 1; k < pointsLn; k++) {
321                             calcPath.push(deltaPath[j][k].from + deltaPath[j][k].delta * easing);
322                         }
323                         newPath.push(calcPath.join(','));
324                     }
325                     out.push([values[i][0], newPath.join(',')]);
326                 }
327                 return out;
328             }
329         }
330         /* End Definitions */
331     }
332 }, function() {
333     Ext.each([
334         'outlineColor',
335         'backgroundColor',
336         'borderColor',
337         'borderTopColor',
338         'borderRightColor', 
339         'borderBottomColor', 
340         'borderLeftColor',
341         'fill',
342         'stroke'
343     ], function(prop) {
344         this[prop] = this.color;
345     }, this);
346 });