raphael.svg.js 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428
  1. // ┌─────────────────────────────────────────────────────────────────────┐ \\
  2. // │ Raphaël @@VERSION - JavaScript Vector Library │ \\
  3. // ├─────────────────────────────────────────────────────────────────────┤ \\
  4. // │ SVG Module │ \\
  5. // ├─────────────────────────────────────────────────────────────────────┤ \\
  6. // │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com) │ \\
  7. // │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com) │ \\
  8. // │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
  9. // └─────────────────────────────────────────────────────────────────────┘ \\
  10. define(["./raphael.core"], function(R) {
  11. if (R && !R.svg) {
  12. return;
  13. }
  14. var has = "hasOwnProperty",
  15. Str = String,
  16. toFloat = parseFloat,
  17. toInt = parseInt,
  18. math = Math,
  19. mmax = math.max,
  20. abs = math.abs,
  21. pow = math.pow,
  22. separator = /[, ]+/,
  23. eve = R.eve,
  24. E = "",
  25. S = " ";
  26. var xlink = "http://www.w3.org/1999/xlink",
  27. markers = {
  28. block: "M5,0 0,2.5 5,5z",
  29. classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
  30. diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
  31. open: "M6,1 1,3.5 6,6",
  32. oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
  33. },
  34. markerCounter = {};
  35. R.toString = function () {
  36. return "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
  37. };
  38. var $ = function (el, attr) {
  39. if (attr) {
  40. if (typeof el == "string") {
  41. el = $(el);
  42. }
  43. for (var key in attr) if (attr[has](key)) {
  44. if (key.substring(0, 6) == "xlink:") {
  45. el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
  46. } else {
  47. el.setAttribute(key, Str(attr[key]));
  48. }
  49. }
  50. } else {
  51. el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
  52. el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
  53. }
  54. return el;
  55. },
  56. addGradientFill = function (element, gradient) {
  57. var type = "linear",
  58. id = element.id + gradient,
  59. fx = .5, fy = .5,
  60. o = element.node,
  61. SVG = element.paper,
  62. s = o.style,
  63. el = R._g.doc.getElementById(id);
  64. if (!el) {
  65. gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
  66. type = "radial";
  67. if (_fx && _fy) {
  68. fx = toFloat(_fx);
  69. fy = toFloat(_fy);
  70. var dir = ((fy > .5) * 2 - 1);
  71. pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
  72. (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
  73. fy != .5 &&
  74. (fy = fy.toFixed(5) - 1e-5 * dir);
  75. }
  76. return E;
  77. });
  78. gradient = gradient.split(/\s*\-\s*/);
  79. if (type == "linear") {
  80. var angle = gradient.shift();
  81. angle = -toFloat(angle);
  82. if (isNaN(angle)) {
  83. return null;
  84. }
  85. var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
  86. max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
  87. vector[2] *= max;
  88. vector[3] *= max;
  89. if (vector[2] < 0) {
  90. vector[0] = -vector[2];
  91. vector[2] = 0;
  92. }
  93. if (vector[3] < 0) {
  94. vector[1] = -vector[3];
  95. vector[3] = 0;
  96. }
  97. }
  98. var dots = R._parseDots(gradient);
  99. if (!dots) {
  100. return null;
  101. }
  102. id = id.replace(/[\(\)\s,\xb0#]/g, "_");
  103. if (element.gradient && id != element.gradient.id) {
  104. SVG.defs.removeChild(element.gradient);
  105. delete element.gradient;
  106. }
  107. if (!element.gradient) {
  108. el = $(type + "Gradient", {id: id});
  109. element.gradient = el;
  110. $(el, type == "radial" ? {
  111. fx: fx,
  112. fy: fy
  113. } : {
  114. x1: vector[0],
  115. y1: vector[1],
  116. x2: vector[2],
  117. y2: vector[3],
  118. gradientTransform: element.matrix.invert()
  119. });
  120. SVG.defs.appendChild(el);
  121. for (var i = 0, ii = dots.length; i < ii; i++) {
  122. el.appendChild($("stop", {
  123. offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
  124. "stop-color": dots[i].color || "#fff",
  125. "stop-opacity": isFinite(dots[i].opacity) ? dots[i].opacity : 1
  126. }));
  127. }
  128. }
  129. }
  130. $(o, {
  131. fill: fillurl(id),
  132. opacity: 1,
  133. "fill-opacity": 1
  134. });
  135. s.fill = E;
  136. s.opacity = 1;
  137. s.fillOpacity = 1;
  138. return 1;
  139. },
  140. isIE9or10 = function () {
  141. var mode = document.documentMode;
  142. return mode && (mode === 9 || mode === 10);
  143. },
  144. fillurl = function (id) {
  145. if (isIE9or10()) {
  146. return "url('#" + id + "')";
  147. }
  148. var location = document.location;
  149. var locationString = (
  150. location.protocol + '//' +
  151. location.host +
  152. location.pathname +
  153. location.search
  154. );
  155. return "url('" + locationString + "#" + id + "')";
  156. },
  157. updatePosition = function (o) {
  158. var bbox = o.getBBox(1);
  159. $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
  160. },
  161. addArrow = function (o, value, isEnd) {
  162. if (o.type == "path") {
  163. var values = Str(value).toLowerCase().split("-"),
  164. p = o.paper,
  165. se = isEnd ? "end" : "start",
  166. node = o.node,
  167. attrs = o.attrs,
  168. stroke = attrs["stroke-width"],
  169. i = values.length,
  170. type = "classic",
  171. from,
  172. to,
  173. dx,
  174. refX,
  175. attr,
  176. w = 3,
  177. h = 3,
  178. t = 5;
  179. while (i--) {
  180. switch (values[i]) {
  181. case "block":
  182. case "classic":
  183. case "oval":
  184. case "diamond":
  185. case "open":
  186. case "none":
  187. type = values[i];
  188. break;
  189. case "wide": h = 5; break;
  190. case "narrow": h = 2; break;
  191. case "long": w = 5; break;
  192. case "short": w = 2; break;
  193. }
  194. }
  195. if (type == "open") {
  196. w += 2;
  197. h += 2;
  198. t += 2;
  199. dx = 1;
  200. refX = isEnd ? 4 : 1;
  201. attr = {
  202. fill: "none",
  203. stroke: attrs.stroke
  204. };
  205. } else {
  206. refX = dx = w / 2;
  207. attr = {
  208. fill: attrs.stroke,
  209. stroke: "none"
  210. };
  211. }
  212. if (o._.arrows) {
  213. if (isEnd) {
  214. o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
  215. o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
  216. } else {
  217. o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
  218. o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
  219. }
  220. } else {
  221. o._.arrows = {};
  222. }
  223. if (type != "none") {
  224. var pathId = "raphael-marker-" + type,
  225. markerId = "raphael-marker-" + se + type + w + h + "-obj" + o.id;
  226. if (!R._g.doc.getElementById(pathId)) {
  227. p.defs.appendChild($($("path"), {
  228. "stroke-linecap": "round",
  229. d: markers[type],
  230. id: pathId
  231. }));
  232. markerCounter[pathId] = 1;
  233. } else {
  234. markerCounter[pathId]++;
  235. }
  236. var marker = R._g.doc.getElementById(markerId),
  237. use;
  238. if (!marker) {
  239. marker = $($("marker"), {
  240. id: markerId,
  241. markerHeight: h,
  242. markerWidth: w,
  243. orient: "auto",
  244. refX: refX,
  245. refY: h / 2
  246. });
  247. use = $($("use"), {
  248. "xlink:href": "#" + pathId,
  249. transform: (isEnd ? "rotate(180 " + w / 2 + " " + h / 2 + ") " : E) + "scale(" + w / t + "," + h / t + ")",
  250. "stroke-width": (1 / ((w / t + h / t) / 2)).toFixed(4)
  251. });
  252. marker.appendChild(use);
  253. p.defs.appendChild(marker);
  254. markerCounter[markerId] = 1;
  255. } else {
  256. markerCounter[markerId]++;
  257. use = marker.getElementsByTagName("use")[0];
  258. }
  259. $(use, attr);
  260. var delta = dx * (type != "diamond" && type != "oval");
  261. if (isEnd) {
  262. from = o._.arrows.startdx * stroke || 0;
  263. to = R.getTotalLength(attrs.path) - delta * stroke;
  264. } else {
  265. from = delta * stroke;
  266. to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
  267. }
  268. attr = {};
  269. attr["marker-" + se] = "url(#" + markerId + ")";
  270. if (to || from) {
  271. attr.d = R.getSubpath(attrs.path, from, to);
  272. }
  273. $(node, attr);
  274. o._.arrows[se + "Path"] = pathId;
  275. o._.arrows[se + "Marker"] = markerId;
  276. o._.arrows[se + "dx"] = delta;
  277. o._.arrows[se + "Type"] = type;
  278. o._.arrows[se + "String"] = value;
  279. } else {
  280. if (isEnd) {
  281. from = o._.arrows.startdx * stroke || 0;
  282. to = R.getTotalLength(attrs.path) - from;
  283. } else {
  284. from = 0;
  285. to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
  286. }
  287. o._.arrows[se + "Path"] && $(node, {d: R.getSubpath(attrs.path, from, to)});
  288. delete o._.arrows[se + "Path"];
  289. delete o._.arrows[se + "Marker"];
  290. delete o._.arrows[se + "dx"];
  291. delete o._.arrows[se + "Type"];
  292. delete o._.arrows[se + "String"];
  293. }
  294. for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
  295. var item = R._g.doc.getElementById(attr);
  296. item && item.parentNode.removeChild(item);
  297. }
  298. }
  299. },
  300. dasharray = {
  301. "-": [3, 1],
  302. ".": [1, 1],
  303. "-.": [3, 1, 1, 1],
  304. "-..": [3, 1, 1, 1, 1, 1],
  305. ". ": [1, 3],
  306. "- ": [4, 3],
  307. "--": [8, 3],
  308. "- .": [4, 3, 1, 3],
  309. "--.": [8, 3, 1, 3],
  310. "--..": [8, 3, 1, 3, 1, 3]
  311. },
  312. addDashes = function (o, value, params) {
  313. value = dasharray[Str(value).toLowerCase()];
  314. if (value) {
  315. var width = o.attrs["stroke-width"] || "1",
  316. butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
  317. dashes = [],
  318. i = value.length;
  319. while (i--) {
  320. dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
  321. }
  322. $(o.node, {"stroke-dasharray": dashes.join(",")});
  323. }
  324. else {
  325. $(o.node, {"stroke-dasharray": "none"});
  326. }
  327. },
  328. setFillAndStroke = function (o, params) {
  329. var node = o.node,
  330. attrs = o.attrs,
  331. vis = node.style.visibility;
  332. node.style.visibility = "hidden";
  333. for (var att in params) {
  334. if (params[has](att)) {
  335. if (!R._availableAttrs[has](att)) {
  336. continue;
  337. }
  338. var value = params[att];
  339. attrs[att] = value;
  340. switch (att) {
  341. case "blur":
  342. o.blur(value);
  343. break;
  344. case "title":
  345. var title = node.getElementsByTagName("title");
  346. // Use the existing <title>.
  347. if (title.length && (title = title[0])) {
  348. title.firstChild.nodeValue = value;
  349. } else {
  350. title = $("title");
  351. var val = R._g.doc.createTextNode(value);
  352. title.appendChild(val);
  353. node.appendChild(title);
  354. }
  355. break;
  356. case "href":
  357. case "target":
  358. var pn = node.parentNode;
  359. if (pn.tagName.toLowerCase() != "a") {
  360. var hl = $("a");
  361. pn.insertBefore(hl, node);
  362. hl.appendChild(node);
  363. pn = hl;
  364. }
  365. if (att == "target") {
  366. pn.setAttributeNS(xlink, "show", value == "blank" ? "new" : value);
  367. } else {
  368. pn.setAttributeNS(xlink, att, value);
  369. }
  370. break;
  371. case "cursor":
  372. node.style.cursor = value;
  373. break;
  374. case "transform":
  375. o.transform(value);
  376. break;
  377. case "arrow-start":
  378. addArrow(o, value);
  379. break;
  380. case "arrow-end":
  381. addArrow(o, value, 1);
  382. break;
  383. case "clip-rect":
  384. var rect = Str(value).split(separator);
  385. if (rect.length == 4) {
  386. o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
  387. var el = $("clipPath"),
  388. rc = $("rect");
  389. el.id = R.createUUID();
  390. $(rc, {
  391. x: rect[0],
  392. y: rect[1],
  393. width: rect[2],
  394. height: rect[3]
  395. });
  396. el.appendChild(rc);
  397. o.paper.defs.appendChild(el);
  398. $(node, {"clip-path": "url(#" + el.id + ")"});
  399. o.clip = rc;
  400. }
  401. if (!value) {
  402. var path = node.getAttribute("clip-path");
  403. if (path) {
  404. var clip = R._g.doc.getElementById(path.replace(/(^url\(#|\)$)/g, E));
  405. clip && clip.parentNode.removeChild(clip);
  406. $(node, {"clip-path": E});
  407. delete o.clip;
  408. }
  409. }
  410. break;
  411. case "path":
  412. if (o.type == "path") {
  413. $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"});
  414. o._.dirty = 1;
  415. if (o._.arrows) {
  416. "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
  417. "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
  418. }
  419. }
  420. break;
  421. case "width":
  422. node.setAttribute(att, value);
  423. o._.dirty = 1;
  424. if (attrs.fx) {
  425. att = "x";
  426. value = attrs.x;
  427. } else {
  428. break;
  429. }
  430. case "x":
  431. if (attrs.fx) {
  432. value = -attrs.x - (attrs.width || 0);
  433. }
  434. case "rx":
  435. if (att == "rx" && o.type == "rect") {
  436. break;
  437. }
  438. case "cx":
  439. node.setAttribute(att, value);
  440. o.pattern && updatePosition(o);
  441. o._.dirty = 1;
  442. break;
  443. case "height":
  444. node.setAttribute(att, value);
  445. o._.dirty = 1;
  446. if (attrs.fy) {
  447. att = "y";
  448. value = attrs.y;
  449. } else {
  450. break;
  451. }
  452. case "y":
  453. if (attrs.fy) {
  454. value = -attrs.y - (attrs.height || 0);
  455. }
  456. case "ry":
  457. if (att == "ry" && o.type == "rect") {
  458. break;
  459. }
  460. case "cy":
  461. node.setAttribute(att, value);
  462. o.pattern && updatePosition(o);
  463. o._.dirty = 1;
  464. break;
  465. case "r":
  466. if (o.type == "rect") {
  467. $(node, {rx: value, ry: value});
  468. } else {
  469. node.setAttribute(att, value);
  470. }
  471. o._.dirty = 1;
  472. break;
  473. case "src":
  474. if (o.type == "image") {
  475. node.setAttributeNS(xlink, "href", value);
  476. }
  477. break;
  478. case "stroke-width":
  479. if (o._.sx != 1 || o._.sy != 1) {
  480. value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
  481. }
  482. node.setAttribute(att, value);
  483. if (attrs["stroke-dasharray"]) {
  484. addDashes(o, attrs["stroke-dasharray"], params);
  485. }
  486. if (o._.arrows) {
  487. "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
  488. "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
  489. }
  490. break;
  491. case "stroke-dasharray":
  492. addDashes(o, value, params);
  493. break;
  494. case "fill":
  495. var isURL = Str(value).match(R._ISURL);
  496. if (isURL) {
  497. el = $("pattern");
  498. var ig = $("image");
  499. el.id = R.createUUID();
  500. $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
  501. $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
  502. el.appendChild(ig);
  503. (function (el) {
  504. R._preload(isURL[1], function () {
  505. var w = this.offsetWidth,
  506. h = this.offsetHeight;
  507. $(el, {width: w, height: h});
  508. $(ig, {width: w, height: h});
  509. });
  510. })(el);
  511. o.paper.defs.appendChild(el);
  512. $(node, {fill: "url(#" + el.id + ")"});
  513. o.pattern = el;
  514. o.pattern && updatePosition(o);
  515. break;
  516. }
  517. var clr = R.getRGB(value);
  518. if (!clr.error) {
  519. delete params.gradient;
  520. delete attrs.gradient;
  521. !R.is(attrs.opacity, "undefined") &&
  522. R.is(params.opacity, "undefined") &&
  523. $(node, {opacity: attrs.opacity});
  524. !R.is(attrs["fill-opacity"], "undefined") &&
  525. R.is(params["fill-opacity"], "undefined") &&
  526. $(node, {"fill-opacity": attrs["fill-opacity"]});
  527. } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
  528. if ("opacity" in attrs || "fill-opacity" in attrs) {
  529. var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
  530. if (gradient) {
  531. var stops = gradient.getElementsByTagName("stop");
  532. $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)});
  533. }
  534. }
  535. attrs.gradient = value;
  536. attrs.fill = "none";
  537. break;
  538. }
  539. clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
  540. case "stroke":
  541. clr = R.getRGB(value);
  542. node.setAttribute(att, clr.hex);
  543. att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
  544. if (att == "stroke" && o._.arrows) {
  545. "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
  546. "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
  547. }
  548. break;
  549. case "gradient":
  550. (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
  551. break;
  552. case "opacity":
  553. if (attrs.gradient && !attrs[has]("stroke-opacity")) {
  554. $(node, {"stroke-opacity": value > 1 ? value / 100 : value});
  555. }
  556. // fall
  557. case "fill-opacity":
  558. if (attrs.gradient) {
  559. gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
  560. if (gradient) {
  561. stops = gradient.getElementsByTagName("stop");
  562. $(stops[stops.length - 1], {"stop-opacity": value});
  563. }
  564. break;
  565. }
  566. default:
  567. att == "font-size" && (value = toInt(value, 10) + "px");
  568. var cssrule = att.replace(/(\-.)/g, function (w) {
  569. return w.substring(1).toUpperCase();
  570. });
  571. node.style[cssrule] = value;
  572. o._.dirty = 1;
  573. node.setAttribute(att, value);
  574. break;
  575. }
  576. }
  577. }
  578. tuneText(o, params);
  579. node.style.visibility = vis;
  580. },
  581. leading = 1.2,
  582. tuneText = function (el, params) {
  583. if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
  584. return;
  585. }
  586. var a = el.attrs,
  587. node = el.node,
  588. fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
  589. if (params[has]("text")) {
  590. a.text = params.text;
  591. while (node.firstChild) {
  592. node.removeChild(node.firstChild);
  593. }
  594. var texts = Str(params.text).split("\n"),
  595. tspans = [],
  596. tspan;
  597. for (var i = 0, ii = texts.length; i < ii; i++) {
  598. tspan = $("tspan");
  599. i && $(tspan, {dy: fontSize * leading, x: a.x});
  600. tspan.appendChild(R._g.doc.createTextNode(texts[i]));
  601. node.appendChild(tspan);
  602. tspans[i] = tspan;
  603. }
  604. } else {
  605. tspans = node.getElementsByTagName("tspan");
  606. for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
  607. $(tspans[i], {dy: fontSize * leading, x: a.x});
  608. } else {
  609. $(tspans[0], {dy: 0});
  610. }
  611. }
  612. $(node, {x: a.x, y: a.y});
  613. el._.dirty = 1;
  614. var bb = el._getBBox(),
  615. dif = a.y - (bb.y + bb.height / 2);
  616. dif && R.is(dif, "finite") && $(tspans[0], {dy: dif});
  617. },
  618. getRealNode = function (node) {
  619. if (node.parentNode && node.parentNode.tagName.toLowerCase() === "a") {
  620. return node.parentNode;
  621. } else {
  622. return node;
  623. }
  624. },
  625. Element = function (node, svg) {
  626. var X = 0,
  627. Y = 0;
  628. /*\
  629. * Element.node
  630. [ property (object) ]
  631. **
  632. * Gives you a reference to the DOM object, so you can assign event handlers or just mess around.
  633. **
  634. * Note: Don’t mess with it.
  635. > Usage
  636. | // draw a circle at coordinate 10,10 with radius of 10
  637. | var c = paper.circle(10, 10, 10);
  638. | c.node.onclick = function () {
  639. | c.attr("fill", "red");
  640. | };
  641. \*/
  642. this[0] = this.node = node;
  643. /*\
  644. * Element.raphael
  645. [ property (object) ]
  646. **
  647. * Internal reference to @Raphael object. In case it is not available.
  648. > Usage
  649. | Raphael.el.red = function () {
  650. | var hsb = this.paper.raphael.rgb2hsb(this.attr("fill"));
  651. | hsb.h = 1;
  652. | this.attr({fill: this.paper.raphael.hsb2rgb(hsb).hex});
  653. | }
  654. \*/
  655. node.raphael = true;
  656. /*\
  657. * Element.id
  658. [ property (number) ]
  659. **
  660. * Unique id of the element. Especially useful when you want to listen to events of the element,
  661. * because all events are fired in format `<module>.<action>.<id>`. Also useful for @Paper.getById method.
  662. \*/
  663. this.id = R._oid++;
  664. node.raphaelid = this.id;
  665. this.matrix = R.matrix();
  666. this.realPath = null;
  667. /*\
  668. * Element.paper
  669. [ property (object) ]
  670. **
  671. * Internal reference to “paper” where object drawn. Mainly for use in plugins and element extensions.
  672. > Usage
  673. | Raphael.el.cross = function () {
  674. | this.attr({fill: "red"});
  675. | this.paper.path("M10,10L50,50M50,10L10,50")
  676. | .attr({stroke: "red"});
  677. | }
  678. \*/
  679. this.paper = svg;
  680. this.attrs = this.attrs || {};
  681. this._ = {
  682. transform: [],
  683. sx: 1,
  684. sy: 1,
  685. deg: 0,
  686. dx: 0,
  687. dy: 0,
  688. dirty: 1
  689. };
  690. !svg.bottom && (svg.bottom = this);
  691. /*\
  692. * Element.prev
  693. [ property (object) ]
  694. **
  695. * Reference to the previous element in the hierarchy.
  696. \*/
  697. this.prev = svg.top;
  698. svg.top && (svg.top.next = this);
  699. svg.top = this;
  700. /*\
  701. * Element.next
  702. [ property (object) ]
  703. **
  704. * Reference to the next element in the hierarchy.
  705. \*/
  706. this.next = null;
  707. },
  708. elproto = R.el;
  709. Element.prototype = elproto;
  710. elproto.constructor = Element;
  711. R._engine.path = function (pathString, SVG) {
  712. var el = $("path");
  713. SVG.canvas && SVG.canvas.appendChild(el);
  714. var p = new Element(el, SVG);
  715. p.type = "path";
  716. setFillAndStroke(p, {
  717. fill: "none",
  718. stroke: "#000",
  719. path: pathString
  720. });
  721. return p;
  722. };
  723. /*\
  724. * Element.rotate
  725. [ method ]
  726. **
  727. * Deprecated! Use @Element.transform instead.
  728. * Adds rotation by given angle around given point to the list of
  729. * transformations of the element.
  730. > Parameters
  731. - deg (number) angle in degrees
  732. - cx (number) #optional x coordinate of the centre of rotation
  733. - cy (number) #optional y coordinate of the centre of rotation
  734. * If cx & cy aren’t specified centre of the shape is used as a point of rotation.
  735. = (object) @Element
  736. \*/
  737. elproto.rotate = function (deg, cx, cy) {
  738. if (this.removed) {
  739. return this;
  740. }
  741. deg = Str(deg).split(separator);
  742. if (deg.length - 1) {
  743. cx = toFloat(deg[1]);
  744. cy = toFloat(deg[2]);
  745. }
  746. deg = toFloat(deg[0]);
  747. (cy == null) && (cx = cy);
  748. if (cx == null || cy == null) {
  749. var bbox = this.getBBox(1);
  750. cx = bbox.x + bbox.width / 2;
  751. cy = bbox.y + bbox.height / 2;
  752. }
  753. this.transform(this._.transform.concat([["r", deg, cx, cy]]));
  754. return this;
  755. };
  756. /*\
  757. * Element.scale
  758. [ method ]
  759. **
  760. * Deprecated! Use @Element.transform instead.
  761. * Adds scale by given amount relative to given point to the list of
  762. * transformations of the element.
  763. > Parameters
  764. - sx (number) horisontal scale amount
  765. - sy (number) vertical scale amount
  766. - cx (number) #optional x coordinate of the centre of scale
  767. - cy (number) #optional y coordinate of the centre of scale
  768. * If cx & cy aren’t specified centre of the shape is used instead.
  769. = (object) @Element
  770. \*/
  771. elproto.scale = function (sx, sy, cx, cy) {
  772. if (this.removed) {
  773. return this;
  774. }
  775. sx = Str(sx).split(separator);
  776. if (sx.length - 1) {
  777. sy = toFloat(sx[1]);
  778. cx = toFloat(sx[2]);
  779. cy = toFloat(sx[3]);
  780. }
  781. sx = toFloat(sx[0]);
  782. (sy == null) && (sy = sx);
  783. (cy == null) && (cx = cy);
  784. if (cx == null || cy == null) {
  785. var bbox = this.getBBox(1);
  786. }
  787. cx = cx == null ? bbox.x + bbox.width / 2 : cx;
  788. cy = cy == null ? bbox.y + bbox.height / 2 : cy;
  789. this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
  790. return this;
  791. };
  792. /*\
  793. * Element.translate
  794. [ method ]
  795. **
  796. * Deprecated! Use @Element.transform instead.
  797. * Adds translation by given amount to the list of transformations of the element.
  798. > Parameters
  799. - dx (number) horisontal shift
  800. - dy (number) vertical shift
  801. = (object) @Element
  802. \*/
  803. elproto.translate = function (dx, dy) {
  804. if (this.removed) {
  805. return this;
  806. }
  807. dx = Str(dx).split(separator);
  808. if (dx.length - 1) {
  809. dy = toFloat(dx[1]);
  810. }
  811. dx = toFloat(dx[0]) || 0;
  812. dy = +dy || 0;
  813. this.transform(this._.transform.concat([["t", dx, dy]]));
  814. return this;
  815. };
  816. /*\
  817. * Element.transform
  818. [ method ]
  819. **
  820. * Adds transformation to the element which is separate to other attributes,
  821. * i.e. translation doesn’t change `x` or `y` of the rectange. The format
  822. * of transformation string is similar to the path string syntax:
  823. | "t100,100r30,100,100s2,2,100,100r45s1.5"
  824. * Each letter is a command. There are four commands: `t` is for translate, `r` is for rotate, `s` is for
  825. * scale and `m` is for matrix.
  826. *
  827. * There are also alternative “absolute” translation, rotation and scale: `T`, `R` and `S`. They will not take previous transformation into account. For example, `...T100,0` will always move element 100 px horisontally, while `...t100,0` could move it vertically if there is `r90` before. Just compare results of `r90t100,0` and `r90T100,0`.
  828. *
  829. * So, the example line above could be read like “translate by 100, 100; rotate 30° around 100, 100; scale twice around 100, 100;
  830. * rotate 45° around centre; scale 1.5 times relative to centre”. As you can see rotate and scale commands have origin
  831. * coordinates as optional parameters, the default is the centre point of the element.
  832. * Matrix accepts six parameters.
  833. > Usage
  834. | var el = paper.rect(10, 20, 300, 200);
  835. | // translate 100, 100, rotate 45°, translate -100, 0
  836. | el.transform("t100,100r45t-100,0");
  837. | // if you want you can append or prepend transformations
  838. | el.transform("...t50,50");
  839. | el.transform("s2...");
  840. | // or even wrap
  841. | el.transform("t50,50...t-50-50");
  842. | // to reset transformation call method with empty string
  843. | el.transform("");
  844. | // to get current value call it without parameters
  845. | console.log(el.transform());
  846. > Parameters
  847. - tstr (string) #optional transformation string
  848. * If tstr isn’t specified
  849. = (string) current transformation string
  850. * else
  851. = (object) @Element
  852. \*/
  853. elproto.transform = function (tstr) {
  854. var _ = this._;
  855. if (tstr == null) {
  856. return _.transform;
  857. }
  858. R._extractTransform(this, tstr);
  859. this.clip && $(this.clip, {transform: this.matrix.invert()});
  860. this.pattern && updatePosition(this);
  861. this.node && $(this.node, {transform: this.matrix});
  862. if (_.sx != 1 || _.sy != 1) {
  863. var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
  864. this.attr({"stroke-width": sw});
  865. }
  866. //Reduce transform string
  867. _.transform = this.matrix.toTransformString();
  868. return this;
  869. };
  870. /*\
  871. * Element.hide
  872. [ method ]
  873. **
  874. * Makes element invisible. See @Element.show.
  875. = (object) @Element
  876. \*/
  877. elproto.hide = function () {
  878. if(!this.removed) this.node.style.display = "none";
  879. return this;
  880. };
  881. /*\
  882. * Element.show
  883. [ method ]
  884. **
  885. * Makes element visible. See @Element.hide.
  886. = (object) @Element
  887. \*/
  888. elproto.show = function () {
  889. if(!this.removed) this.node.style.display = "";
  890. return this;
  891. };
  892. /*\
  893. * Element.remove
  894. [ method ]
  895. **
  896. * Removes element from the paper.
  897. \*/
  898. elproto.remove = function () {
  899. var node = getRealNode(this.node);
  900. if (this.removed || !node.parentNode) {
  901. return;
  902. }
  903. var paper = this.paper;
  904. paper.__set__ && paper.__set__.exclude(this);
  905. eve.unbind("raphael.*.*." + this.id);
  906. if (this.gradient) {
  907. paper.defs.removeChild(this.gradient);
  908. }
  909. R._tear(this, paper);
  910. node.parentNode.removeChild(node);
  911. // Remove custom data for element
  912. this.removeData();
  913. for (var i in this) {
  914. this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
  915. }
  916. this.removed = true;
  917. };
  918. elproto._getBBox = function () {
  919. if (this.node.style.display == "none") {
  920. this.show();
  921. var hide = true;
  922. }
  923. var canvasHidden = false,
  924. containerStyle;
  925. if (this.paper.canvas.parentElement) {
  926. containerStyle = this.paper.canvas.parentElement.style;
  927. } //IE10+ can't find parentElement
  928. else if (this.paper.canvas.parentNode) {
  929. containerStyle = this.paper.canvas.parentNode.style;
  930. }
  931. if(containerStyle && containerStyle.display == "none") {
  932. canvasHidden = true;
  933. containerStyle.display = "";
  934. }
  935. var bbox = {};
  936. try {
  937. bbox = this.node.getBBox();
  938. } catch(e) {
  939. // Firefox 3.0.x, 25.0.1 (probably more versions affected) play badly here - possible fix
  940. bbox = {
  941. x: this.node.clientLeft,
  942. y: this.node.clientTop,
  943. width: this.node.clientWidth,
  944. height: this.node.clientHeight
  945. }
  946. } finally {
  947. bbox = bbox || {};
  948. if(canvasHidden){
  949. containerStyle.display = "none";
  950. }
  951. }
  952. hide && this.hide();
  953. return bbox;
  954. };
  955. /*\
  956. * Element.attr
  957. [ method ]
  958. **
  959. * Sets the attributes of the element.
  960. > Parameters
  961. - attrName (string) attribute’s name
  962. - value (string) value
  963. * or
  964. - params (object) object of name/value pairs
  965. * or
  966. - attrName (string) attribute’s name
  967. * or
  968. - attrNames (array) in this case method returns array of current values for given attribute names
  969. = (object) @Element if attrsName & value or params are passed in.
  970. = (...) value of the attribute if only attrsName is passed in.
  971. = (array) array of values of the attribute if attrsNames is passed in.
  972. = (object) object of attributes if nothing is passed in.
  973. > Possible parameters
  974. # <p>Please refer to the <a href="http://www.w3.org/TR/SVG/" title="The W3C Recommendation for the SVG language describes these properties in detail.">SVG specification</a> for an explanation of these parameters.</p>
  975. o arrow-end (string) arrowhead on the end of the path. The format for string is `<type>[-<width>[-<length>]]`. Possible types: `classic`, `block`, `open`, `oval`, `diamond`, `none`, width: `wide`, `narrow`, `medium`, length: `long`, `short`, `midium`.
  976. o clip-rect (string) comma or space separated values: x, y, width and height
  977. o cursor (string) CSS type of the cursor
  978. o cx (number) the x-axis coordinate of the center of the circle, or ellipse
  979. o cy (number) the y-axis coordinate of the center of the circle, or ellipse
  980. o fill (string) colour, gradient or image
  981. o fill-opacity (number)
  982. o font (string)
  983. o font-family (string)
  984. o font-size (number) font size in pixels
  985. o font-weight (string)
  986. o height (number)
  987. o href (string) URL, if specified element behaves as hyperlink
  988. o opacity (number)
  989. o path (string) SVG path string format
  990. o r (number) radius of the circle, ellipse or rounded corner on the rect
  991. o rx (number) horisontal radius of the ellipse
  992. o ry (number) vertical radius of the ellipse
  993. o src (string) image URL, only works for @Element.image element
  994. o stroke (string) stroke colour
  995. o stroke-dasharray (string) [“”, “none”, “`-`”, “`.`”, “`-.`”, “`-..`”, “`. `”, “`- `”, “`--`”, “`- .`”, “`--.`”, “`--..`”]
  996. o stroke-linecap (string) [“`butt`”, “`square`”, “`round`”]
  997. o stroke-linejoin (string) [“`bevel`”, “`round`”, “`miter`”]
  998. o stroke-miterlimit (number)
  999. o stroke-opacity (number)
  1000. o stroke-width (number) stroke width in pixels, default is '1'
  1001. o target (string) used with href
  1002. o text (string) contents of the text element. Use `\n` for multiline text
  1003. o text-anchor (string) [“`start`”, “`middle`”, “`end`”], default is “`middle`”
  1004. o title (string) will create tooltip with a given text
  1005. o transform (string) see @Element.transform
  1006. o width (number)
  1007. o x (number)
  1008. o y (number)
  1009. > Gradients
  1010. * Linear gradient format: “`‹angle›-‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`90-#fff-#000`” – 90°
  1011. * gradient from white to black or “`0-#fff-#f00:20-#000`” – 0° gradient from white via red (at 20%) to black.
  1012. *
  1013. * radial gradient: “`r[(‹fx›, ‹fy›)]‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`r#fff-#000`” –
  1014. * gradient from white to black or “`r(0.25, 0.75)#fff-#000`” – gradient from white to black with focus point
  1015. * at 0.25, 0.75. Focus point coordinates are in 0..1 range. Radial gradients can only be applied to circles and ellipses.
  1016. > Path String
  1017. # <p>Please refer to <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path’s data attribute’s format are described in the SVG specification.">SVG documentation regarding path string</a>. Raphaël fully supports it.</p>
  1018. > Colour Parsing
  1019. # <ul>
  1020. # <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
  1021. # <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
  1022. # <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
  1023. # <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
  1024. # <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
  1025. # <li>rgba(•••, •••, •••, •••) — red, green and blue channels’ values: (“<code>rgba(200,&nbsp;100,&nbsp;0, .5)</code>”)</li>
  1026. # <li>rgba(•••%, •••%, •••%, •••%) — same as above, but in %: (“<code>rgba(100%,&nbsp;175%,&nbsp;0%, 50%)</code>”)</li>
  1027. # <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
  1028. # <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
  1029. # <li>hsba(•••, •••, •••, •••) — same as above, but with opacity</li>
  1030. # <li>hsl(•••, •••, •••) — almost the same as hsb, see <a href="http://en.wikipedia.org/wiki/HSL_and_HSV" title="HSL and HSV - Wikipedia, the free encyclopedia">Wikipedia page</a></li>
  1031. # <li>hsl(•••%, •••%, •••%) — same as above, but in %</li>
  1032. # <li>hsla(•••, •••, •••, •••) — same as above, but with opacity</li>
  1033. # <li>Optionally for hsb and hsl you could specify hue as a degree: “<code>hsl(240deg,&nbsp;1,&nbsp;.5)</code>” or, if you want to go fancy, “<code>hsl(240°,&nbsp;1,&nbsp;.5)</code>”</li>
  1034. # </ul>
  1035. \*/
  1036. elproto.attr = function (name, value) {
  1037. if (this.removed) {
  1038. return this;
  1039. }
  1040. if (name == null) {
  1041. var res = {};
  1042. for (var a in this.attrs) if (this.attrs[has](a)) {
  1043. res[a] = this.attrs[a];
  1044. }
  1045. res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
  1046. res.transform = this._.transform;
  1047. return res;
  1048. }
  1049. if (value == null && R.is(name, "string")) {
  1050. if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
  1051. return this.attrs.gradient;
  1052. }
  1053. if (name == "transform") {
  1054. return this._.transform;
  1055. }
  1056. var names = name.split(separator),
  1057. out = {};
  1058. for (var i = 0, ii = names.length; i < ii; i++) {
  1059. name = names[i];
  1060. if (name in this.attrs) {
  1061. out[name] = this.attrs[name];
  1062. } else if (R.is(this.paper.customAttributes[name], "function")) {
  1063. out[name] = this.paper.customAttributes[name].def;
  1064. } else {
  1065. out[name] = R._availableAttrs[name];
  1066. }
  1067. }
  1068. return ii - 1 ? out : out[names[0]];
  1069. }
  1070. if (value == null && R.is(name, "array")) {
  1071. out = {};
  1072. for (i = 0, ii = name.length; i < ii; i++) {
  1073. out[name[i]] = this.attr(name[i]);
  1074. }
  1075. return out;
  1076. }
  1077. if (value != null) {
  1078. var params = {};
  1079. params[name] = value;
  1080. } else if (name != null && R.is(name, "object")) {
  1081. params = name;
  1082. }
  1083. for (var key in params) {
  1084. eve("raphael.attr." + key + "." + this.id, this, params[key]);
  1085. }
  1086. for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
  1087. var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
  1088. this.attrs[key] = params[key];
  1089. for (var subkey in par) if (par[has](subkey)) {
  1090. params[subkey] = par[subkey];
  1091. }
  1092. }
  1093. setFillAndStroke(this, params);
  1094. return this;
  1095. };
  1096. /*\
  1097. * Element.toFront
  1098. [ method ]
  1099. **
  1100. * Moves the element so it is the closest to the viewer’s eyes, on top of other elements.
  1101. = (object) @Element
  1102. \*/
  1103. elproto.toFront = function () {
  1104. if (this.removed) {
  1105. return this;
  1106. }
  1107. var node = getRealNode(this.node);
  1108. node.parentNode.appendChild(node);
  1109. var svg = this.paper;
  1110. svg.top != this && R._tofront(this, svg);
  1111. return this;
  1112. };
  1113. /*\
  1114. * Element.toBack
  1115. [ method ]
  1116. **
  1117. * Moves the element so it is the furthest from the viewer’s eyes, behind other elements.
  1118. = (object) @Element
  1119. \*/
  1120. elproto.toBack = function () {
  1121. if (this.removed) {
  1122. return this;
  1123. }
  1124. var node = getRealNode(this.node);
  1125. var parentNode = node.parentNode;
  1126. parentNode.insertBefore(node, parentNode.firstChild);
  1127. R._toback(this, this.paper);
  1128. var svg = this.paper;
  1129. return this;
  1130. };
  1131. /*\
  1132. * Element.insertAfter
  1133. [ method ]
  1134. **
  1135. * Inserts current object after the given one.
  1136. = (object) @Element
  1137. \*/
  1138. elproto.insertAfter = function (element) {
  1139. if (this.removed || !element) {
  1140. return this;
  1141. }
  1142. var node = getRealNode(this.node);
  1143. var afterNode = getRealNode(element.node || element[element.length - 1].node);
  1144. if (afterNode.nextSibling) {
  1145. afterNode.parentNode.insertBefore(node, afterNode.nextSibling);
  1146. } else {
  1147. afterNode.parentNode.appendChild(node);
  1148. }
  1149. R._insertafter(this, element, this.paper);
  1150. return this;
  1151. };
  1152. /*\
  1153. * Element.insertBefore
  1154. [ method ]
  1155. **
  1156. * Inserts current object before the given one.
  1157. = (object) @Element
  1158. \*/
  1159. elproto.insertBefore = function (element) {
  1160. if (this.removed || !element) {
  1161. return this;
  1162. }
  1163. var node = getRealNode(this.node);
  1164. var beforeNode = getRealNode(element.node || element[0].node);
  1165. beforeNode.parentNode.insertBefore(node, beforeNode);
  1166. R._insertbefore(this, element, this.paper);
  1167. return this;
  1168. };
  1169. elproto.blur = function (size) {
  1170. // Experimental. No Safari support. Use it on your own risk.
  1171. var t = this;
  1172. if (+size !== 0) {
  1173. var fltr = $("filter"),
  1174. blur = $("feGaussianBlur");
  1175. t.attrs.blur = size;
  1176. fltr.id = R.createUUID();
  1177. $(blur, {stdDeviation: +size || 1.5});
  1178. fltr.appendChild(blur);
  1179. t.paper.defs.appendChild(fltr);
  1180. t._blur = fltr;
  1181. $(t.node, {filter: "url(#" + fltr.id + ")"});
  1182. } else {
  1183. if (t._blur) {
  1184. t._blur.parentNode.removeChild(t._blur);
  1185. delete t._blur;
  1186. delete t.attrs.blur;
  1187. }
  1188. t.node.removeAttribute("filter");
  1189. }
  1190. return t;
  1191. };
  1192. R._engine.circle = function (svg, x, y, r) {
  1193. var el = $("circle");
  1194. svg.canvas && svg.canvas.appendChild(el);
  1195. var res = new Element(el, svg);
  1196. res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
  1197. res.type = "circle";
  1198. $(el, res.attrs);
  1199. return res;
  1200. };
  1201. R._engine.rect = function (svg, x, y, w, h, r) {
  1202. var el = $("rect");
  1203. svg.canvas && svg.canvas.appendChild(el);
  1204. var res = new Element(el, svg);
  1205. res.attrs = {x: x, y: y, width: w, height: h, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
  1206. res.type = "rect";
  1207. $(el, res.attrs);
  1208. return res;
  1209. };
  1210. R._engine.ellipse = function (svg, x, y, rx, ry) {
  1211. var el = $("ellipse");
  1212. svg.canvas && svg.canvas.appendChild(el);
  1213. var res = new Element(el, svg);
  1214. res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
  1215. res.type = "ellipse";
  1216. $(el, res.attrs);
  1217. return res;
  1218. };
  1219. R._engine.image = function (svg, src, x, y, w, h) {
  1220. var el = $("image");
  1221. $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
  1222. el.setAttributeNS(xlink, "href", src);
  1223. svg.canvas && svg.canvas.appendChild(el);
  1224. var res = new Element(el, svg);
  1225. res.attrs = {x: x, y: y, width: w, height: h, src: src};
  1226. res.type = "image";
  1227. return res;
  1228. };
  1229. R._engine.text = function (svg, x, y, text) {
  1230. var el = $("text");
  1231. svg.canvas && svg.canvas.appendChild(el);
  1232. var res = new Element(el, svg);
  1233. res.attrs = {
  1234. x: x,
  1235. y: y,
  1236. "text-anchor": "middle",
  1237. text: text,
  1238. "font-family": R._availableAttrs["font-family"],
  1239. "font-size": R._availableAttrs["font-size"],
  1240. stroke: "none",
  1241. fill: "#000"
  1242. };
  1243. res.type = "text";
  1244. setFillAndStroke(res, res.attrs);
  1245. return res;
  1246. };
  1247. R._engine.setSize = function (width, height) {
  1248. this.width = width || this.width;
  1249. this.height = height || this.height;
  1250. this.canvas.setAttribute("width", this.width);
  1251. this.canvas.setAttribute("height", this.height);
  1252. if (this._viewBox) {
  1253. this.setViewBox.apply(this, this._viewBox);
  1254. }
  1255. return this;
  1256. };
  1257. R._engine.create = function () {
  1258. var con = R._getContainer.apply(0, arguments),
  1259. container = con && con.container,
  1260. x = con.x,
  1261. y = con.y,
  1262. width = con.width,
  1263. height = con.height;
  1264. if (!container) {
  1265. throw new Error("SVG container not found.");
  1266. }
  1267. var cnvs = $("svg"),
  1268. css = "overflow:hidden;",
  1269. isFloating;
  1270. x = x || 0;
  1271. y = y || 0;
  1272. width = width || 512;
  1273. height = height || 342;
  1274. $(cnvs, {
  1275. height: height,
  1276. version: 1.1,
  1277. width: width,
  1278. xmlns: "http://www.w3.org/2000/svg",
  1279. "xmlns:xlink": "http://www.w3.org/1999/xlink"
  1280. });
  1281. if (container == 1) {
  1282. cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
  1283. R._g.doc.body.appendChild(cnvs);
  1284. isFloating = 1;
  1285. } else {
  1286. cnvs.style.cssText = css + "position:relative";
  1287. if (container.firstChild) {
  1288. container.insertBefore(cnvs, container.firstChild);
  1289. } else {
  1290. container.appendChild(cnvs);
  1291. }
  1292. }
  1293. container = new R._Paper;
  1294. container.width = width;
  1295. container.height = height;
  1296. container.canvas = cnvs;
  1297. container.clear();
  1298. container._left = container._top = 0;
  1299. isFloating && (container.renderfix = function () {});
  1300. container.renderfix();
  1301. return container;
  1302. };
  1303. R._engine.setViewBox = function (x, y, w, h, fit) {
  1304. eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]);
  1305. var paperSize = this.getSize(),
  1306. size = mmax(w / paperSize.width, h / paperSize.height),
  1307. top = this.top,
  1308. aspectRatio = fit ? "xMidYMid meet" : "xMinYMin",
  1309. vb,
  1310. sw;
  1311. if (x == null) {
  1312. if (this._vbSize) {
  1313. size = 1;
  1314. }
  1315. delete this._vbSize;
  1316. vb = "0 0 " + this.width + S + this.height;
  1317. } else {
  1318. this._vbSize = size;
  1319. vb = x + S + y + S + w + S + h;
  1320. }
  1321. $(this.canvas, {
  1322. viewBox: vb,
  1323. preserveAspectRatio: aspectRatio
  1324. });
  1325. while (size && top) {
  1326. sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
  1327. top.attr({"stroke-width": sw});
  1328. top._.dirty = 1;
  1329. top._.dirtyT = 1;
  1330. top = top.prev;
  1331. }
  1332. this._viewBox = [x, y, w, h, !!fit];
  1333. return this;
  1334. };
  1335. /*\
  1336. * Paper.renderfix
  1337. [ method ]
  1338. **
  1339. * Fixes the issue of Firefox and IE9 regarding subpixel rendering. If paper is dependant
  1340. * on other elements after reflow it could shift half pixel which cause for lines to lost their crispness.
  1341. * This method fixes the issue.
  1342. **
  1343. Special thanks to Mariusz Nowak (http://www.medikoo.com/) for this method.
  1344. \*/
  1345. R.prototype.renderfix = function () {
  1346. var cnvs = this.canvas,
  1347. s = cnvs.style,
  1348. pos;
  1349. try {
  1350. pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix();
  1351. } catch (e) {
  1352. pos = cnvs.createSVGMatrix();
  1353. }
  1354. var left = -pos.e % 1,
  1355. top = -pos.f % 1;
  1356. if (left || top) {
  1357. if (left) {
  1358. this._left = (this._left + left) % 1;
  1359. s.left = this._left + "px";
  1360. }
  1361. if (top) {
  1362. this._top = (this._top + top) % 1;
  1363. s.top = this._top + "px";
  1364. }
  1365. }
  1366. };
  1367. /*\
  1368. * Paper.clear
  1369. [ method ]
  1370. **
  1371. * Clears the paper, i.e. removes all the elements.
  1372. \*/
  1373. R.prototype.clear = function () {
  1374. R.eve("raphael.clear", this);
  1375. var c = this.canvas;
  1376. while (c.firstChild) {
  1377. c.removeChild(c.firstChild);
  1378. }
  1379. this.bottom = this.top = null;
  1380. (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version));
  1381. c.appendChild(this.desc);
  1382. c.appendChild(this.defs = $("defs"));
  1383. };
  1384. /*\
  1385. * Paper.remove
  1386. [ method ]
  1387. **
  1388. * Removes the paper from the DOM.
  1389. \*/
  1390. R.prototype.remove = function () {
  1391. eve("raphael.remove", this);
  1392. this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
  1393. for (var i in this) {
  1394. this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
  1395. }
  1396. };
  1397. var setproto = R.st;
  1398. for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
  1399. setproto[method] = (function (methodname) {
  1400. return function () {
  1401. var arg = arguments;
  1402. return this.forEach(function (el) {
  1403. el[methodname].apply(el, arg);
  1404. });
  1405. };
  1406. })(method);
  1407. }
  1408. });