canvas.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. if (!dojo._hasResource["dojox.gfx.canvas"]) { // _hasResource checks added by
  2. // build. Do not use
  3. // _hasResource directly in your
  4. // code.
  5. dojo._hasResource["dojox.gfx.canvas"] = true;
  6. dojo.provide("dojox.gfx.canvas");
  7. dojo.require("dojox.gfx._base");
  8. dojo.require("dojox.gfx.shape");
  9. dojo.require("dojox.gfx.path");
  10. dojo.require("dojox.gfx.arc");
  11. dojo.require("dojox.gfx.decompose");
  12. dojo.experimental("dojox.gfx.canvas");
  13. (function() {
  14. var g = dojox.gfx, gs = g.shape, ga = g.arc, m = g.matrix, mp = m.multiplyPoint, twoPI = 2
  15. * Math.PI;
  16. dojo.extend(g.Shape, {
  17. render : function(/* Object */ctx) {
  18. // summary: render the shape
  19. ctx.save();
  20. this._renderTransform(ctx);
  21. this._renderShape(ctx);
  22. this._renderFill(ctx, true);
  23. this._renderStroke(ctx, true);
  24. ctx.restore();
  25. },
  26. _renderTransform : function(/* Object */ctx) {
  27. if ("canvasTransform" in this) {
  28. var t = this.canvasTransform;
  29. ctx.translate(t.dx, t.dy);
  30. ctx.rotate(t.angle2);
  31. ctx.scale(t.sx, t.sy);
  32. ctx.rotate(t.angle1);
  33. // The future implementation when vendors catch up
  34. // with the spec:
  35. // var t = this.matrix;
  36. // ctx.transform(t.xx, t.yx, t.xy, t.yy, t.dx,
  37. // t.dy);
  38. }
  39. },
  40. _renderShape : function(/* Object */ctx) {
  41. // nothing
  42. },
  43. _renderFill : function(/* Object */ctx, /* Boolean */
  44. apply) {
  45. if ("canvasFill" in this) {
  46. if ("canvasFillImage" in this) {
  47. this.canvasFill = ctx.createPattern(
  48. this.canvasFillImage, "repeat");
  49. delete this.canvasFillImage;
  50. }
  51. ctx.fillStyle = this.canvasFill;
  52. if (apply) {
  53. ctx.fill();
  54. }
  55. } else {
  56. ctx.fillStyle = "rgba(0,0,0,0.0)";
  57. }
  58. },
  59. _renderStroke : function(/* Object */ctx, /* Boolean */
  60. apply) {
  61. var s = this.strokeStyle;
  62. if (s) {
  63. ctx.strokeStyle = s.color.toString();
  64. ctx.lineWidth = s.width;
  65. ctx.lineCap = s.cap;
  66. if (typeof s.join == "number") {
  67. ctx.lineJoin = "miter";
  68. ctx.miterLimit = s.join;
  69. } else {
  70. ctx.lineJoin = s.join;
  71. }
  72. if (apply) {
  73. ctx.stroke();
  74. }
  75. } else if (!apply) {
  76. ctx.strokeStyle = "rgba(0,0,0,0.0)";
  77. }
  78. },
  79. // events are not implemented
  80. getEventSource : function() {
  81. return null;
  82. },
  83. connect : function() {
  84. },
  85. disconnect : function() {
  86. }
  87. });
  88. var modifyMethod = function(shape, method, extra) {
  89. var old = shape.prototype[method];
  90. shape.prototype[method] = extra ? function() {
  91. this.surface.makeDirty();
  92. old.apply(this, arguments);
  93. extra.call(this);
  94. return this;
  95. } : function() {
  96. this.surface.makeDirty();
  97. return old.apply(this, arguments);
  98. };
  99. };
  100. modifyMethod(g.Shape, "setTransform", function() {
  101. // prepare Canvas-specific structures
  102. if (this.matrix) {
  103. this.canvasTransform = g.decompose(this.matrix);
  104. } else {
  105. delete this.canvasTransform;
  106. }
  107. });
  108. modifyMethod(g.Shape, "setFill", function() {
  109. // prepare Canvas-specific structures
  110. var fs = this.fillStyle, f;
  111. if (fs) {
  112. if (typeof(fs) == "object" && "type" in fs) {
  113. var ctx = this.surface.rawNode.getContext("2d");
  114. switch (fs.type) {
  115. case "linear" :
  116. case "radial" :
  117. f = fs.type == "linear" ? ctx
  118. .createLinearGradient(fs.x1, fs.y1,
  119. fs.x2, fs.y2) : ctx
  120. .createRadialGradient(fs.cx, fs.cy,
  121. 0, fs.cx, fs.cy, fs.r);
  122. dojo.forEach(fs.colors, function(step) {
  123. f
  124. .addColorStop(
  125. step.offset,
  126. g
  127. .normalizeColor(step.color)
  128. .toString());
  129. });
  130. break;
  131. case "pattern" :
  132. var img = new Image(fs.width, fs.height);
  133. this.surface.downloadImage(img, fs.src);
  134. this.canvasFillImage = img;
  135. }
  136. } else {
  137. // Set fill color using CSS RGBA func style
  138. f = fs.toString();
  139. }
  140. this.canvasFill = f;
  141. } else {
  142. delete this.canvasFill;
  143. }
  144. });
  145. modifyMethod(g.Shape, "setStroke");
  146. modifyMethod(g.Shape, "setShape");
  147. dojo.declare("dojox.gfx.Group", g.Shape, {
  148. // summary: a group shape (Canvas), which can be used
  149. // to logically group shapes (e.g, to propagate matricies)
  150. constructor : function() {
  151. gs.Container._init.call(this);
  152. },
  153. render : function(/* Object */ctx) {
  154. // summary: render the group
  155. ctx.save();
  156. this._renderTransform(ctx);
  157. this._renderFill(ctx);
  158. this._renderStroke(ctx);
  159. for (var i = 0; i < this.children.length; ++i) {
  160. this.children[i].render(ctx);
  161. }
  162. ctx.restore();
  163. }
  164. });
  165. dojo.declare("dojox.gfx.Rect", gs.Rect, {
  166. // summary: a rectangle shape (Canvas)
  167. _renderShape : function(/* Object */ctx) {
  168. var s = this.shape, r = Math
  169. .min(s.r, s.height / 2, s.width / 2), xl = s.x, xr = xl
  170. + s.width, yt = s.y, yb = yt + s.height, xl2 = xl + r, xr2 = xr
  171. - r, yt2 = yt + r, yb2 = yb - r;
  172. ctx.beginPath();
  173. ctx.moveTo(xl2, yt);
  174. ctx.lineTo(xr2, yt);
  175. if (r) {
  176. ctx.arcTo(xr, yt, xr, yt2, r);
  177. }
  178. ctx.lineTo(xr, yb2);
  179. if (r) {
  180. ctx.arcTo(xr, yb, xr2, yb, r);
  181. }
  182. ctx.lineTo(xl2, yb);
  183. if (r) {
  184. ctx.arcTo(xl, yb, xl, yb2, r);
  185. }
  186. ctx.lineTo(xl, yt2);
  187. if (r) {
  188. ctx.arcTo(xl, yt, xl2, yt, r);
  189. }
  190. ctx.closePath();
  191. }
  192. });
  193. var bezierCircle = [];
  194. (function() {
  195. var u = ga.curvePI4;
  196. bezierCircle.push(u.s, u.c1, u.c2, u.e);
  197. for (var a = 45; a < 360; a += 45) {
  198. var r = m.rotateg(a);
  199. bezierCircle.push(mp(r, u.c1), mp(r, u.c2), mp(r, u.e));
  200. }
  201. })();
  202. dojo.declare("dojox.gfx.Ellipse", gs.Ellipse, {
  203. // summary: an ellipse shape (Canvas)
  204. setShape : function() {
  205. g.Ellipse.superclass.setShape.apply(this, arguments);
  206. // prepare Canvas-specific structures
  207. var s = this.shape, t, c1, c2, r = [], M = m
  208. .normalize([m.translate(s.cx, s.cy),
  209. m.scale(s.rx, s.ry)]);
  210. t = mp(M, bezierCircle[0]);
  211. r.push([t.x, t.y]);
  212. for (var i = 1; i < bezierCircle.length; i += 3) {
  213. c1 = mp(M, bezierCircle[i]);
  214. c2 = mp(M, bezierCircle[i + 1]);
  215. t = mp(M, bezierCircle[i + 2]);
  216. r.push([c1.x, c1.y, c2.x, c2.y, t.x, t.y]);
  217. }
  218. this.canvasEllipse = r;
  219. return this;
  220. },
  221. _renderShape : function(/* Object */ctx) {
  222. var r = this.canvasEllipse;
  223. ctx.beginPath();
  224. ctx.moveTo.apply(ctx, r[0]);
  225. for (var i = 1; i < r.length; ++i) {
  226. ctx.bezierCurveTo.apply(ctx, r[i]);
  227. }
  228. ctx.closePath();
  229. }
  230. });
  231. dojo.declare("dojox.gfx.Circle", gs.Circle, {
  232. // summary: a circle shape (Canvas)
  233. _renderShape : function(/* Object */ctx) {
  234. var s = this.shape;
  235. ctx.beginPath();
  236. ctx.arc(s.cx, s.cy, s.r, 0, twoPI, 1);
  237. }
  238. });
  239. dojo.declare("dojox.gfx.Line", gs.Line, {
  240. // summary: a line shape (Canvas)
  241. _renderShape : function(/* Object */ctx) {
  242. var s = this.shape;
  243. ctx.beginPath();
  244. ctx.moveTo(s.x1, s.y1);
  245. ctx.lineTo(s.x2, s.y2);
  246. }
  247. });
  248. dojo.declare("dojox.gfx.Polyline", gs.Polyline, {
  249. // summary: a polyline/polygon shape (Canvas)
  250. setShape : function() {
  251. g.Polyline.superclass.setShape.apply(this, arguments);
  252. // prepare Canvas-specific structures
  253. var p = this.shape.points, f = p[0], r = [], c, i;
  254. if (p.length) {
  255. if (typeof f == "number") {
  256. r.push(f, p[1]);
  257. i = 2;
  258. } else {
  259. r.push(f.x, f.y);
  260. i = 1;
  261. }
  262. for (; i < p.length; ++i) {
  263. c = p[i];
  264. if (typeof c == "number") {
  265. r.push(c, p[++i]);
  266. } else {
  267. r.push(c.x, c.y);
  268. }
  269. }
  270. }
  271. this.canvasPolyline = r;
  272. return this;
  273. },
  274. _renderShape : function(/* Object */ctx) {
  275. var p = this.canvasPolyline;
  276. if (p.length) {
  277. ctx.beginPath();
  278. ctx.moveTo(p[0], p[1]);
  279. for (var i = 2; i < p.length; i += 2) {
  280. ctx.lineTo(p[i], p[i + 1]);
  281. }
  282. }
  283. }
  284. });
  285. dojo.declare("dojox.gfx.Image", gs.Image, {
  286. // summary: an image shape (Canvas)
  287. setShape : function() {
  288. g.Image.superclass.setShape.apply(this, arguments);
  289. // prepare Canvas-specific structures
  290. var img = new Image();
  291. this.surface.downloadImage(img, this.shape.src);
  292. this.canvasImage = img;
  293. return this;
  294. },
  295. _renderShape : function(/* Object */ctx) {
  296. var s = this.shape;
  297. ctx.drawImage(this.canvasImage, s.x, s.y, s.width,
  298. s.height);
  299. }
  300. });
  301. dojo.declare("dojox.gfx.Text", gs.Text, {
  302. // summary: a text shape (Canvas)
  303. _renderShape : function(/* Object */ctx) {
  304. var s = this.shape;
  305. // nothing for the moment
  306. }
  307. });
  308. modifyMethod(g.Text, "setFont");
  309. var pathRenderers = {
  310. M : "_moveToA",
  311. m : "_moveToR",
  312. L : "_lineToA",
  313. l : "_lineToR",
  314. H : "_hLineToA",
  315. h : "_hLineToR",
  316. V : "_vLineToA",
  317. v : "_vLineToR",
  318. C : "_curveToA",
  319. c : "_curveToR",
  320. S : "_smoothCurveToA",
  321. s : "_smoothCurveToR",
  322. Q : "_qCurveToA",
  323. q : "_qCurveToR",
  324. T : "_qSmoothCurveToA",
  325. t : "_qSmoothCurveToR",
  326. A : "_arcTo",
  327. a : "_arcTo",
  328. Z : "_closePath",
  329. z : "_closePath"
  330. };
  331. dojo.declare("dojox.gfx.Path", g.path.Path, {
  332. // summary: a path shape (Canvas)
  333. constructor : function() {
  334. this.last = {};
  335. this.lastControl = {};
  336. },
  337. setShape : function() {
  338. this.canvasPath = [];
  339. return g.Path.superclass.setShape.apply(this, arguments);
  340. },
  341. _updateWithSegment : function(segment) {
  342. var last = dojo.clone(this.last);
  343. this[pathRenderers[segment.action]](this.canvasPath,
  344. segment.action, segment.args);
  345. this.last = last;
  346. g.Path.superclass._updateWithSegment.apply(this, arguments);
  347. },
  348. _renderShape : function(/* Object */ctx) {
  349. var r = this.canvasPath;
  350. ctx.beginPath();
  351. for (var i = 0; i < r.length; i += 2) {
  352. ctx[r[i]].apply(ctx, r[i + 1]);
  353. }
  354. },
  355. _moveToA : function(result, action, args) {
  356. result.push("moveTo", [args[0], args[1]]);
  357. for (var i = 2; i < args.length; i += 2) {
  358. result.push("lineTo", [args[i], args[i + 1]]);
  359. }
  360. this.last.x = args[args.length - 2];
  361. this.last.y = args[args.length - 1];
  362. this.lastControl = {};
  363. },
  364. _moveToR : function(result, action, args) {
  365. if ("x" in this.last) {
  366. result.push("moveTo", [this.last.x += args[0],
  367. this.last.y += args[1]]);
  368. } else {
  369. result.push("moveTo", [this.last.x = args[0],
  370. this.last.y = args[1]]);
  371. }
  372. for (var i = 2; i < args.length; i += 2) {
  373. result.push("lineTo", [this.last.x += args[i],
  374. this.last.y += args[i + 1]]);
  375. }
  376. this.lastControl = {};
  377. },
  378. _lineToA : function(result, action, args) {
  379. for (var i = 0; i < args.length; i += 2) {
  380. result.push("lineTo", [args[i], args[i + 1]]);
  381. }
  382. this.last.x = args[args.length - 2];
  383. this.last.y = args[args.length - 1];
  384. this.lastControl = {};
  385. },
  386. _lineToR : function(result, action, args) {
  387. for (var i = 0; i < args.length; i += 2) {
  388. result.push("lineTo", [this.last.x += args[i],
  389. this.last.y += args[i + 1]]);
  390. }
  391. this.lastControl = {};
  392. },
  393. _hLineToA : function(result, action, args) {
  394. for (var i = 0; i < args.length; ++i) {
  395. result.push("lineTo", [args[i], this.last.y]);
  396. }
  397. this.last.x = args[args.length - 1];
  398. this.lastControl = {};
  399. },
  400. _hLineToR : function(result, action, args) {
  401. for (var i = 0; i < args.length; ++i) {
  402. result
  403. .push("lineTo", [this.last.x += args[i],
  404. this.last.y]);
  405. }
  406. this.lastControl = {};
  407. },
  408. _vLineToA : function(result, action, args) {
  409. for (var i = 0; i < args.length; ++i) {
  410. result.push("lineTo", [this.last.x, args[i]]);
  411. }
  412. this.last.y = args[args.length - 1];
  413. this.lastControl = {};
  414. },
  415. _vLineToR : function(result, action, args) {
  416. for (var i = 0; i < args.length; ++i) {
  417. result
  418. .push("lineTo", [this.last.x,
  419. this.last.y += args[i]]);
  420. }
  421. this.lastControl = {};
  422. },
  423. _curveToA : function(result, action, args) {
  424. for (var i = 0; i < args.length; i += 6) {
  425. result.push("bezierCurveTo", args.slice(i, i + 6));
  426. }
  427. this.last.x = args[args.length - 2];
  428. this.last.y = args[args.length - 1];
  429. this.lastControl.x = args[args.length - 4];
  430. this.lastControl.y = args[args.length - 3];
  431. this.lastControl.type = "C";
  432. },
  433. _curveToR : function(result, action, args) {
  434. for (var i = 0; i < args.length; i += 6) {
  435. result.push("bezierCurveTo", [
  436. this.last.x + args[i],
  437. this.last.y + args[i + 1],
  438. this.lastControl.x = this.last.x
  439. + args[i + 2],
  440. this.lastControl.y = this.last.y
  441. + args[i + 3],
  442. this.last.x + args[i + 4],
  443. this.last.y + args[i + 5]]);
  444. this.last.x += args[i + 4];
  445. this.last.y += args[i + 5];
  446. }
  447. this.lastControl.type = "C";
  448. },
  449. _smoothCurveToA : function(result, action, args) {
  450. for (var i = 0; i < args.length; i += 4) {
  451. var valid = this.lastControl.type == "C";
  452. result.push("bezierCurveTo", [
  453. valid ? 2 * this.last.x
  454. - this.lastControl.x : this.last.x,
  455. valid ? 2 * this.last.y
  456. - this.lastControl.y : this.last.y,
  457. args[i], args[i + 1], args[i + 2],
  458. args[i + 3]]);
  459. this.lastControl.x = args[i];
  460. this.lastControl.y = args[i + 1];
  461. this.lastControl.type = "C";
  462. }
  463. this.last.x = args[args.length - 2];
  464. this.last.y = args[args.length - 1];
  465. },
  466. _smoothCurveToR : function(result, action, args) {
  467. for (var i = 0; i < args.length; i += 4) {
  468. var valid = this.lastControl.type == "C";
  469. result.push("bezierCurveTo", [
  470. valid ? 2 * this.last.x
  471. - this.lastControl.x : this.last.x,
  472. valid ? 2 * this.last.y
  473. - this.lastControl.y : this.last.y,
  474. this.last.x + args[i],
  475. this.last.y + args[i + 1],
  476. this.last.x + args[i + 2],
  477. this.last.y + args[i + 3]]);
  478. this.lastControl.x = this.last.x + args[i];
  479. this.lastControl.y = this.last.y + args[i + 1];
  480. this.lastControl.type = "C";
  481. this.last.x += args[i + 2];
  482. this.last.y += args[i + 3];
  483. }
  484. },
  485. _qCurveToA : function(result, action, args) {
  486. for (var i = 0; i < args.length; i += 4) {
  487. result.push("quadraticCurveTo", args.slice(i, i + 4));
  488. }
  489. this.last.x = args[args.length - 2];
  490. this.last.y = args[args.length - 1];
  491. this.lastControl.x = args[args.length - 4];
  492. this.lastControl.y = args[args.length - 3];
  493. this.lastControl.type = "Q";
  494. },
  495. _qCurveToR : function(result, action, args) {
  496. for (var i = 0; i < args.length; i += 4) {
  497. result.push("quadraticCurveTo", [
  498. this.lastControl.x = this.last.x + args[i],
  499. this.lastControl.y = this.last.y
  500. + args[i + 1],
  501. this.last.x + args[i + 2],
  502. this.last.y + args[i + 3]]);
  503. this.last.x += args[i + 2];
  504. this.last.y += args[i + 3];
  505. }
  506. this.lastControl.type = "Q";
  507. },
  508. _qSmoothCurveToA : function(result, action, args) {
  509. for (var i = 0; i < args.length; i += 2) {
  510. var valid = this.lastControl.type == "Q";
  511. result
  512. .push(
  513. "quadraticCurveTo",
  514. [
  515. this.lastControl.x = valid
  516. ? 2
  517. * this.last.x
  518. - this.lastControl.x
  519. : this.last.x,
  520. this.lastControl.y = valid
  521. ? 2
  522. * this.last.y
  523. - this.lastControl.y
  524. : this.last.y, args[i],
  525. args[i + 1]]);
  526. this.lastControl.type = "Q";
  527. }
  528. this.last.x = args[args.length - 2];
  529. this.last.y = args[args.length - 1];
  530. },
  531. _qSmoothCurveToR : function(result, action, args) {
  532. for (var i = 0; i < args.length; i += 2) {
  533. var valid = this.lastControl.type == "Q";
  534. result.push("quadraticCurveTo", [
  535. this.lastControl.x = valid
  536. ? 2 * this.last.x
  537. - this.lastControl.x
  538. : this.last.x,
  539. this.lastControl.y = valid
  540. ? 2 * this.last.y
  541. - this.lastControl.y
  542. : this.last.y,
  543. this.last.x + args[i],
  544. this.last.y + args[i + 1]]);
  545. this.lastControl.type = "Q";
  546. this.last.x += args[i];
  547. this.last.y += args[i + 1];
  548. }
  549. },
  550. _arcTo : function(result, action, args) {
  551. var relative = action == "a";
  552. for (var i = 0; i < args.length; i += 7) {
  553. var x1 = args[i + 5], y1 = args[i + 6];
  554. if (relative) {
  555. x1 += this.last.x;
  556. y1 += this.last.y;
  557. }
  558. var arcs = ga.arcAsBezier(this.last, args[i], args[i + 1],
  559. args[i + 2], args[i + 3] ? 1 : 0, args[i + 4]
  560. ? 1
  561. : 0, x1, y1);
  562. dojo.forEach(arcs, function(p) {
  563. result.push("bezierCurveTo", p);
  564. });
  565. this.last.x = x1;
  566. this.last.y = y1;
  567. }
  568. this.lastControl = {};
  569. },
  570. _closePath : function(result, action, args) {
  571. result.push("closePath", []);
  572. this.lastControl = {};
  573. }
  574. });
  575. dojo.forEach(["moveTo", "lineTo", "hLineTo", "vLineTo", "curveTo",
  576. "smoothCurveTo", "qCurveTo", "qSmoothCurveTo", "arcTo",
  577. "closePath"], function(method) {
  578. modifyMethod(g.Path, method);
  579. });
  580. dojo.declare("dojox.gfx.TextPath", g.path.TextPath, {
  581. // summary: a text shape (Canvas)
  582. _renderShape : function(/* Object */ctx) {
  583. var s = this.shape;
  584. // nothing for the moment
  585. }
  586. });
  587. dojo.declare("dojox.gfx.Surface", gs.Surface, {
  588. // summary: a surface object to be used for drawings
  589. // (Canvas)
  590. constructor : function() {
  591. gs.Container._init.call(this);
  592. this.pendingImageCount = 0;
  593. this.makeDirty();
  594. },
  595. setDimensions : function(width, height) {
  596. // summary: sets the width and height of the rawNode
  597. // width: String: width of surface, e.g., "100px"
  598. // height: String: height of surface, e.g., "100px"
  599. this.width = g.normalizedLength(width); // in pixels
  600. this.height = g.normalizedLength(height); // in pixels
  601. if (!this.rawNode)
  602. return this;
  603. this.rawNode.width = width;
  604. this.rawNode.height = height;
  605. this.makeDirty();
  606. return this; // self
  607. },
  608. getDimensions : function() {
  609. // summary: returns an object with properties "width"
  610. // and "height"
  611. return this.rawNode ? {
  612. width : this.rawNode.width,
  613. height : this.rawNode.height
  614. } : null; // Object
  615. },
  616. render : function() {
  617. // summary: render the all shapes
  618. if (this.pendingImageCount) {
  619. return;
  620. }
  621. var ctx = this.rawNode.getContext("2d");
  622. ctx.save();
  623. ctx.clearRect(0, 0, this.rawNode.width,
  624. this.rawNode.height);
  625. for (var i = 0; i < this.children.length; ++i) {
  626. this.children[i].render(ctx);
  627. }
  628. ctx.restore();
  629. if ("pendingRender" in this) {
  630. clearTimeout(this.pendingRender);
  631. delete this.pendingRender;
  632. }
  633. },
  634. makeDirty : function() {
  635. // summary: internal method, which is called when we may
  636. // need to redraw
  637. if (!this.pendingImagesCount
  638. && !("pendingRender" in this)) {
  639. this.pendingRender = setTimeout(dojo.hitch(this,
  640. this.render), 0);
  641. }
  642. },
  643. downloadImage : function(img, url) {
  644. // summary:
  645. // internal method, which starts an image download and
  646. // renders, when it is ready
  647. // img: Image:
  648. // the image object
  649. // url: String:
  650. // the url of the image
  651. var handler = dojo.hitch(this, this.onImageLoad);
  652. if (!this.pendingImageCount++
  653. && "pendingRender" in this) {
  654. clearTimeout(this.pendingRender);
  655. delete this.pendingRender;
  656. }
  657. img.onload = handler;
  658. img.onerror = handler;
  659. img.onabort = handler;
  660. img.src = url;
  661. },
  662. onImageLoad : function() {
  663. if (!--this.pendingImageCount) {
  664. this.render();
  665. }
  666. },
  667. // events are not implemented
  668. getEventSource : function() {
  669. return null;
  670. },
  671. connect : function() {
  672. },
  673. disconnect : function() {
  674. }
  675. });
  676. g.createSurface = function(parentNode, width, height) {
  677. // summary: creates a surface (Canvas)
  678. // parentNode: Node: a parent node
  679. // width: String: width of surface, e.g., "100px"
  680. // height: String: height of surface, e.g., "100px"
  681. if (!width) {
  682. width = "100%";
  683. }
  684. if (!height) {
  685. height = "100%";
  686. }
  687. var s = new g.Surface(), p = dojo.byId(parentNode), c = p.ownerDocument
  688. .createElement("canvas");
  689. c.width = width;
  690. c.height = height;
  691. p.appendChild(c);
  692. s.rawNode = c;
  693. s.surface = s;
  694. return s; // dojox.gfx.Surface
  695. };
  696. // Extenders
  697. var C = gs.Container, Container = {
  698. add : function(shape) {
  699. this.surface.makeDirty();
  700. return C.add.apply(this, arguments);
  701. },
  702. remove : function(shape, silently) {
  703. this.surface.makeDirty();
  704. return C.remove.apply(this, arguments);
  705. },
  706. clear : function() {
  707. this.surface.makeDirty();
  708. return C.clear.apply(this, arguments);
  709. },
  710. _moveChildToFront : function(shape) {
  711. this.surface.makeDirty();
  712. return C._moveChildToFront.apply(this, arguments);
  713. },
  714. _moveChildToBack : function(shape) {
  715. this.surface.makeDirty();
  716. return C._moveChildToBack.apply(this, arguments);
  717. }
  718. };
  719. dojo.mixin(gs.Creator, {
  720. // summary: Canvas shape creators
  721. createObject : function(shapeType, rawShape) {
  722. // summary: creates an instance of the passed shapeType
  723. // class
  724. // shapeType: Function: a class constructor to create an
  725. // instance of
  726. // rawShape: Object: properties to be passed in to the
  727. // classes "setShape" method
  728. // overrideSize: Boolean: set the size explicitly, if
  729. // true
  730. var shape = new shapeType();
  731. shape.surface = this.surface;
  732. shape.setShape(rawShape);
  733. this.add(shape);
  734. return shape; // dojox.gfx.Shape
  735. }
  736. });
  737. dojo.extend(g.Group, Container);
  738. dojo.extend(g.Group, gs.Creator);
  739. dojo.extend(g.Surface, Container);
  740. dojo.extend(g.Surface, gs.Creator);
  741. })();
  742. }