raphael.svg.js 55 KB

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