4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js">/*
19 * @class Ext.draw.Draw
20 * Base Drawing class. Provides base drawing functions.
23 Ext.define('Ext.draw.Draw', {
24 /* Begin Definitions */
28 requires: ['Ext.draw.Color'],
32 pathToStringRE: /,?([achlmqrstvxz]),?/gi,
33 pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
34 pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
36 radian: Math.PI / 180,
39 along: "along",
41 "clip-rect": "csv",
44 fill: "color",
45 "fill-opacity": null,
46 "font-size": null,
49 path: "path",
51 rotation: "csv",
54 scale: "csv",
55 stroke: "color",
56 "stroke-opacity": null,
57 "stroke-width": null,
58 translation: "csv",
64 is: function(o, type) {
65 type = String(type).toLowerCase();
66 return (type == "object" && o === Object(o)) ||
67 (type == "undefined" && typeof o == type) ||
68 (type == "null" && o === null) ||
69 (type == "array" && Array.isArray && Array.isArray(o)) ||
70 (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
73 ellipsePath: function(sprite) {
74 var attr = sprite.attr;
75 return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry);
78 rectPath: function(sprite) {
79 var attr = sprite.attr;
81 return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height);
84 return Ext.String.format("M{0},{1}l{2},0,0,{3},{4},0z", attr.x, attr.y, attr.width, attr.height, -attr.width);
88 // To be deprecated, converts itself (an arrayPath) to a proper SVG path string
89 path2string: function () {
90 return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
93 // Convert the passed arrayPath to a proper SVG path string (d attribute)
94 pathToString: function(arrayPath) {
95 return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
98 parsePathString: function (pathString) {
102 var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
105 if (me.is(pathString, "array") && me.is(pathString[0], "array")) { // rough assumption
106 data = me.pathClone(pathString);
109 String(pathString).replace(me.pathCommandRE, function (a, b, c) {
111 name = b.toLowerCase();
112 c.replace(me.pathValuesRE, function (a, b) {
113 b && params.push(+b);
115 if (name == "m" && params.length > 2) {
116 data.push([b].concat(params.splice(0, 2)));
117 name = "l";
118 b = (b == "m") ? "l" : "L";
120 while (params.length >= paramCounts[name]) {
121 data.push([b].concat(params.splice(0, paramCounts[name])));
122 if (!paramCounts[name]) {
128 data.toString = me.path2string;
132 mapPath: function (path, matrix) {
136 var x, y, i, ii, j, jj, pathi;
137 path = this.path2curve(path);
138 for (i = 0, ii = path.length; i < ii; i++) {
140 for (j = 1, jj = pathi.length; j < jj-1; j += 2) {
141 x = matrix.x(pathi[j], pathi[j + 1]);
142 y = matrix.y(pathi[j], pathi[j + 1]);
150 pathClone: function(pathArray) {
153 if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption
154 pathArray = this.parsePathString(pathArray);
156 for (i = 0, ii = pathArray.length; i < ii; i++) {
158 for (j = 0, jj = pathArray[i].length; j < jj; j++) {
159 res[i][j] = pathArray[i][j];
162 res.toString = this.path2string;
166 pathToAbsolute: function (pathArray) {
167 if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption
168 pathArray = this.parsePathString(pathArray);
176 ln = pathArray.length,
177 r, pathSegment, j, ln2;
178 // MoveTo initial x/y position
179 if (ln && pathArray[0][0] == "M") {
180 x = +pathArray[0][1];
181 y = +pathArray[0][2];
185 res[0] = ["M", x, y];
187 for (; i < ln; i++) {
189 pathSegment = pathArray[i];
190 if (pathSegment[0] != pathSegment[0].toUpperCase()) {
191 r[0] = pathSegment[0].toUpperCase();
195 r[1] = pathSegment[1];
196 r[2] = pathSegment[2];
197 r[3] = pathSegment[3];
198 r[4] = pathSegment[4];
199 r[5] = pathSegment[5];
200 r[6] = +(pathSegment[6] + x);
201 r[7] = +(pathSegment[7] + y);
205 r[1] = +pathSegment[1] + y;
209 r[1] = +pathSegment[1] + x;
213 mx = +pathSegment[1] + x;
214 my = +pathSegment[2] + y;
217 ln2 = pathSegment.length;
218 for (; j < ln2; j++) {
219 r[j] = +pathSegment[j] + ((j % 2) ? x : y);
225 ln2 = pathSegment.length;
226 for (; j < ln2; j++) {
227 res[i][j] = pathSegment[j];
246 pathSegment = res[i];
247 ln2 = pathSegment.length;
248 mx = pathSegment[ln2 - 2];
249 my = pathSegment[ln2 - 1];
251 pathSegment = res[i];
252 ln2 = pathSegment.length;
253 x = pathSegment[ln2 - 2];
254 y = pathSegment[ln2 - 1];
257 res.toString = this.path2string;
262 pathToRelative: function (pathArray) {
263 if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
264 pathArray = this.parsePathString(pathArray);
272 if (pathArray[0][0] == "M") {
278 res.push(["M", x, y]);
280 for (var i = start, ii = pathArray.length; i < ii; i++) {
283 if (pa[0] != pa[0].toLowerCase()) {
284 r[0] = pa[0].toLowerCase();
292 r[6] = +(pa[6] - x).toFixed(3);
293 r[7] = +(pa[7] - y).toFixed(3);
296 r[1] = +(pa[1] - y).toFixed(3);
302 for (var j = 1, jj = pa.length; j < jj; j++) {
303 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
308 if (pa[0] == "m") {
312 for (var k = 0, kk = pa.length; k < kk; k++) {
316 var len = res[i].length;
323 x += +res[i][len - 1];
326 y += +res[i][len - 1];
329 x += +res[i][len - 2];
330 y += +res[i][len - 1];
333 res.toString = this.path2string;
337 // Returns a path converted to a set of curveto commands
338 path2curve: function (path) {
340 points = me.pathToAbsolute(path),
342 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
343 i, seg, segLn, point;
345 for (i = 0; i < ln; i++) {
346 points[i] = me.command2curve(points[i], attrs);
347 if (points[i].length > 7) {
350 while (point.length) {
351 points.splice(i++, 0, ["C"].concat(point.splice(0, 6)));
358 attrs.x = seg[segLn - 2];
359 attrs.y = seg[segLn - 1];
360 attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
361 attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
366 interpolatePaths: function (path, path2) {
368 p = me.pathToAbsolute(path),
369 p2 = me.pathToAbsolute(path2),
370 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
371 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
372 fixArc = function (pp, i) {
373 if (pp[i].length > 7) {
377 pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6)));
380 ii = Math.max(p.length, p2.length || 0);
383 fixM = function (path1, path2, a1, a2, i) {
384 if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
385 path2.splice(i, 0, ["M", a2.x, a2.y]);
390 ii = Math.max(p.length, p2.length || 0);
393 for (var i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) {
394 p[i] = me.command2curve(p[i], attrs);
396 (p2[i] = me.command2curve(p2[i], attrs2));
398 fixM(p, p2, attrs, attrs2, i);
399 fixM(p2, p, attrs2, attrs, i);
403 seg2len = seg2.length;
404 attrs.x = seg[seglen - 2];
405 attrs.y = seg[seglen - 1];
406 attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
407 attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
408 attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
409 attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
410 attrs2.x = seg2[seg2len - 2];
411 attrs2.y = seg2[seg2len - 1];
416 //Returns any path command as a curveto command based on the attrs passed
417 command2curve: function (pathCommand, d) {
420 return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
422 if (pathCommand[0] != "T" && pathCommand[0] != "Q") {
425 switch (pathCommand[0]) {
427 d.X = pathCommand[1];
428 d.Y = pathCommand[2];
431 pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
434 pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
437 d.qx = d.x + (d.x - (d.qx || d.x));
438 d.qy = d.y + (d.y - (d.qy || d.y));
439 pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
442 d.qx = pathCommand[1];
443 d.qy = pathCommand[2];
444 pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
447 pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
450 pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
453 pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
456 pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
462 quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
475 rotate: function (x, y, rad) {
476 var cos = Math.cos(rad),
478 X = x * cos - y * sin,
479 Y = x * sin + y * cos;
483 arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
484 // for more information of where this Math came from visit:
485 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
489 _120 = PI * 120 / 180,
490 rad = radian * (+angle || 0),
498 xy, cos, sin, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
499 t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
501 xy = me.rotate(x1, y1, -rad);
504 xy = me.rotate(x2, y2, -rad);
507 cos = mcos(radian * angle);
508 sin = msin(radian * angle);
511 h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
519 k = (large_arc_flag == sweep_flag ? -1 : 1) *
520 msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
521 cx = k * rx * y / ry + (x1 + x2) / 2;
522 cy = k * -ry * x / rx + (y1 + y2) / 2;
523 f1 = masin(((y1 - cy) / ry).toFixed(7));
524 f2 = masin(((y2 - cy) / ry).toFixed(7));
526 f1 = x1 < cx ? PI - f1 : f1;
527 f2 = x2 < cx ? PI - f2 : f2;
534 if (sweep_flag && f1 > f2) {
537 if (!sweep_flag && f2 > f1) {
548 if (mabs(df) > _120) {
552 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
553 x2 = cx + rx * mcos(f2);
554 y2 = cy + ry * msin(f2);
555 res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
562 t = math.tan(df / 4);
566 m2 = [x1 + hx * s1, y1 - hy * c1];
567 m3 = [x2 + hx * s2, y2 - hy * c2];
569 m2[0] = 2 * m1[0] - m2[0];
570 m2[1] = 2 * m1[1] - m2[1];
572 return [m2, m3, m4].concat(res);
575 res = [m2, m3, m4].concat(res).join().split(",");
578 for (i = 0; i < ln; i++) {
579 newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
586 rotateAndTranslatePath: function (sprite) {
587 var alpha = sprite.rotation.degrees,
588 cx = sprite.rotation.x,
589 cy = sprite.rotation.y,
590 dx = sprite.translation.x,
591 dy = sprite.translation.y,
598 if (!alpha && !dx && !dy) {
599 return this.pathToAbsolute(sprite.attr.path);
603 path = this.pathToAbsolute(sprite.attr.path);
604 for (i = path.length; i--;) {
605 p = res[i] = path[i].slice();
606 if (p[0] == "A") {
607 xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
612 while (p[j + 1] != null) {
613 xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
615 p[j + 1] = xy.y + dy;
624 rotatePoint: function (x, y, alpha, cx, cy) {
635 alpha = alpha * this.radian;
636 var cos = Math.cos(alpha),
637 sin = Math.sin(alpha);
639 x: x * cos - y * sin + cx,
640 y: x * sin + y * cos + cy
644 pathDimensions: function (path) {
645 if (!path || !(path + "")) {
646 return {x: 0, y: 0, width: 0, height: 0};
648 path = this.path2curve(path);
656 for (; i < ln; i++) {
658 if (p[0] == "M") {
665 dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
666 X = X.concat(dim.min.x, dim.max.x);
667 Y = Y.concat(dim.min.y, dim.max.y);
672 xmin = Math.min.apply(0, X);
673 ymin = Math.min.apply(0, Y);
678 width: Math.max.apply(0, X) - xmin,
679 height: Math.max.apply(0, Y) - ymin
683 intersectInside: function(path, cp1, cp2) {
684 return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) > (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
687 intersectIntersection: function(s, e, cp1, cp2) {
689 dcx = cp1[0] - cp2[0],
690 dcy = cp1[1] - cp2[1],
693 n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
694 n2 = s[0] * e[1] - s[1] * e[0],
695 n3 = 1 / (dcx * dpy - dcy * dpx);
697 p[0] = (n1 * dpx - n2 * dcx) * n3;
698 p[1] = (n1 * dpy - n2 * dcy) * n3;
702 intersect: function(subjectPolygon, clipPolygon) {
705 ln = clipPolygon.length,
706 cp1 = clipPolygon[ln - 1],
707 outputList = subjectPolygon,
708 cp2, s, e, point, ln2, inputList, j;
709 for (; i < ln; ++i) {
710 cp2 = clipPolygon[i];
711 inputList = outputList;
713 s = inputList[inputList.length - 1];
715 ln2 = inputList.length;
716 for (; j < ln2; j++) {
718 if (me.intersectInside(e, cp1, cp2)) {
719 if (!me.intersectInside(s, cp1, cp2)) {
720 outputList.push(me.intersectIntersection(s, e, cp1, cp2));
724 else if (me.intersectInside(s, cp1, cp2)) {
725 outputList.push(me.intersectIntersection(s, e, cp1, cp2));
734 curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
735 var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
736 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
738 t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
739 t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
743 if (Math.abs(t1) > 1e12) {
746 if (Math.abs(t2) > 1e12) {
749 if (t1 > 0 && t1 < 1) {
750 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
754 if (t2 > 0 && t2 < 1) {
755 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
759 a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
760 b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
762 t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
763 t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
764 if (Math.abs(t1) > 1e12) {
767 if (Math.abs(t2) > 1e12) {
770 if (t1 > 0 && t1 < 1) {
771 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
775 if (t2 > 0 && t2 < 1) {
776 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
781 min: {x: Math.min.apply(0, x), y: Math.min.apply(0, y)},
782 max: {x: Math.max.apply(0, x), y: Math.max.apply(0, y)}
786 getAnchors: function (p1x, p1y, p2x, p2y, p3x, p3y, value) {
788 var l = Math.min(Math.sqrt(Math.pow(p1x - p2x, 2) + Math.pow(p1y - p2y, 2)) / value, Math.sqrt(Math.pow(p3x - p2x, 2) + Math.pow(p3y - p2y, 2)) / value),
789 a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)),
790 b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y)),
792 a = p1y < p2y ? pi - a : a;
793 b = p3y < p2y ? pi - b : b;
794 var alpha = pi / 2 - ((a + b) % (pi * 2)) / 2;
795 alpha > pi / 2 && (alpha -= pi);
796 var dx1 = l * Math.sin(alpha + a),
797 dy1 = l * Math.cos(alpha + a),
798 dx2 = l * Math.sin(alpha + b),
799 dy2 = l * Math.cos(alpha + b),
809 /* Smoothing function for a path. Converts a path into cubic beziers. Value defines the divider of the distance between points.
810 * Defaults to a value of 4.
812 smooth: function (originalPath, value) {
813 var path = this.path2curve(originalPath),
826 for (; i < ii; i++) {
828 pathil = pathi.length,
829 pathim = path[i - 1],
830 pathiml = pathim.length,
831 pathip = path[i + 1],
832 pathipl = pathip && pathip.length;
833 if (pathi[0] == "M") {
837 while (path[j][0] != "C") {
842 newp.push(["M", mx, my]);
848 if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) {
849 var begl = newp[beg].length;
850 points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
851 newp[beg][1] = points.x2;
852 newp[beg][2] = points.y2;
854 else if (!pathip || pathip[0] == "M") {
856 x1: pathi[pathil - 2],
857 y1: pathi[pathil - 1]
860 points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
862 newp.push(["C", x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
869 findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
872 x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
873 y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
877 snapEnds: function (from, to, stepsMax) {
878 var step = (to - from) / stepsMax,
879 level = Math.floor(Math.log(step) / Math.LN10) + 1,
880 m = Math.pow(10, level),
882 modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
883 interval = [[0, 15], [20, 4], [30, 2], [40, 4], [50, 9], [60, 4], [70, 2], [80, 4], [100, 15]],
890 ln = interval.length;
891 cur = from = Math.floor(from / m) * m;
892 for (i = 0; i < ln; i++) {
893 value = interval[i][0];
894 weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1];
895 if (weight < topWeight) {
900 step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);
901 while (cur < to) {
905 to = +cur.toFixed(10);
915 sorter: function (a, b) {
916 return a.offset - b.offset;
919 rad: function(degrees) {
920 return degrees % 360 * Math.PI / 180;
923 degrees: function(radian) {
924 return radian * 180 / Math.PI % 360;
927 withinBox: function(x, y, bbox) {
929 return (x >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (bbox.y + bbox.height));
932 parseGradient: function(gradient) {
934 type = gradient.type || 'linear',
935 angle = gradient.angle || 0,
937 stops = gradient.stops,
944 if (type == 'linear') {
945 vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
946 max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
949 if (vector[2] < 0) {
950 vector[0] = -vector[2];
953 if (vector[3] < 0) {
954 vector[1] = -vector[3];
959 for (stop in stops) {
960 if (stops.hasOwnProperty(stop) && me.stopsRE.test(stop)) {
962 offset: parseInt(stop, 10),
963 color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
964 opacity: stops[stop].opacity || 1
966 stopsArr.push(stopObj);
969 // Sort by pct property
970 Ext.Array.sort(stopsArr, me.sorter);
971 if (type == 'linear') {
983 centerX: gradient.centerX,
984 centerY: gradient.centerY,
985 focalX: gradient.focalX,
986 focalY: gradient.focalY,
987 radius: gradient.radius,