/examples/addonsgallery/public/excanvas.js

http://pyjamas.googlecode.com/ · JavaScript · 704 lines · 539 code · 92 blank · 73 comment · 60 complexity · 1f48564d9be1a2b986399b98af5e74ca MD5 · raw file

  1. // Copyright 2006 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // TODO: Patterns
  15. // TODO: Radial gradient
  16. // TODO: Clipping paths
  17. // TODO: Coordsize
  18. // TODO: Painting mode
  19. // TODO: Optimize
  20. // TODO: canvas width/height sets content size in moz, border size in ie
  21. // TODO: Painting outside the canvas should not be allowed
  22. // only add this code if we do not already have a canvas implementation
  23. if (!window.CanvasRenderingContext2D) {
  24. (function () {
  25. var G_vmlCanvasManager_ = {
  26. init: function (opt_doc) {
  27. var doc = opt_doc || document;
  28. if (/MSIE/.test(navigator.userAgent) && !window.opera) {
  29. var self = this;
  30. doc.attachEvent("onreadystatechange", function () {
  31. self.init_(doc);
  32. });
  33. }
  34. },
  35. init_: function (doc, e) {
  36. if (doc.readyState == "complete") {
  37. // create xmlns
  38. if (!doc.namespaces["g_vml_"]) {
  39. doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
  40. }
  41. // setup default css
  42. var ss = doc.createStyleSheet();
  43. ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
  44. "text-align:left;}" +
  45. "canvas *{behavior:url(#default#VML)}";
  46. // find all canvas elements
  47. var els = doc.getElementsByTagName("canvas");
  48. for (var i = 0; i < els.length; i++) {
  49. if (!els[i].getContext) {
  50. this.initElement(els[i]);
  51. }
  52. }
  53. }
  54. },
  55. fixElement_: function (el) {
  56. // in IE before version 5.5 we would need to add HTML: to the tag name
  57. // but we do not care about IE before version 6
  58. var outerHTML = el.outerHTML;
  59. var newEl = document.createElement(outerHTML);
  60. // if the tag is still open IE has created the children as siblings and
  61. // it has also created a tag with the name "/FOO"
  62. if (outerHTML.slice(-2) != "/>") {
  63. var tagName = "/" + el.tagName;
  64. var ns;
  65. // remove content
  66. while ((ns = el.nextSibling) && ns.tagName != tagName) {
  67. ns.removeNode();
  68. }
  69. // remove the incorrect closing tag
  70. if (ns) {
  71. ns.removeNode();
  72. }
  73. }
  74. el.parentNode.replaceChild(newEl, el);
  75. return newEl;
  76. },
  77. /**
  78. * Public initializes a canvas element so that it can be used as canvas
  79. * element from now on. This is called automatically before the page is
  80. * loaded but if you are creating elements using createElement yuo need to
  81. * make sure this is called on the element.
  82. * @param el {HTMLElement} The canvas element to initialize.
  83. */
  84. initElement: function (el) {
  85. el = this.fixElement_(el);
  86. el.getContext = function () {
  87. if (this.context_) {
  88. return this.context_;
  89. }
  90. return this.context_ = new CanvasRenderingContext2D_(this);
  91. };
  92. var self = this; //bind
  93. el.attachEvent("onpropertychange", function (e) {
  94. // we need to watch changes to width and height
  95. switch (e.propertyName) {
  96. case "width":
  97. case "height":
  98. // coord size changed?
  99. break;
  100. }
  101. });
  102. // if style.height is set
  103. var attrs = el.attributes;
  104. if (attrs.width && attrs.width.specified) {
  105. // TODO: use runtimeStyle and coordsize
  106. // el.getContext().setWidth_(attrs.width.nodeValue);
  107. el.style.width = attrs.width.nodeValue + "px";
  108. }
  109. if (attrs.height && attrs.height.specified) {
  110. // TODO: use runtimeStyle and coordsize
  111. // el.getContext().setHeight_(attrs.height.nodeValue);
  112. el.style.height = attrs.height.nodeValue + "px";
  113. }
  114. //el.getContext().setCoordsize_()
  115. }
  116. };
  117. G_vmlCanvasManager_.init();
  118. // precompute "00" to "FF"
  119. var dec2hex = [];
  120. for (var i = 0; i < 16; i++) {
  121. for (var j = 0; j < 16; j++) {
  122. dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
  123. }
  124. }
  125. function createMatrixIdentity() {
  126. return [
  127. [1, 0, 0],
  128. [0, 1, 0],
  129. [0, 0, 1]
  130. ];
  131. }
  132. function matrixMultiply(m1, m2) {
  133. var result = createMatrixIdentity();
  134. for (var x = 0; x < 3; x++) {
  135. for (var y = 0; y < 3; y++) {
  136. var sum = 0;
  137. for (var z = 0; z < 3; z++) {
  138. sum += m1[x][z] * m2[z][y];
  139. }
  140. result[x][y] = sum;
  141. }
  142. }
  143. return result;
  144. }
  145. function copyState(o1, o2) {
  146. o2.fillStyle = o1.fillStyle;
  147. o2.lineCap = o1.lineCap;
  148. o2.lineJoin = o1.lineJoin;
  149. o2.lineWidth = o1.lineWidth;
  150. o2.miterLimit = o1.miterLimit;
  151. o2.shadowBlur = o1.shadowBlur;
  152. o2.shadowColor = o1.shadowColor;
  153. o2.shadowOffsetX = o1.shadowOffsetX;
  154. o2.shadowOffsetY = o1.shadowOffsetY;
  155. o2.strokeStyle = o1.strokeStyle;
  156. }
  157. function processStyle(styleString) {
  158. var str, alpha = 1;
  159. styleString = String(styleString);
  160. if (styleString.substring(0, 3) == "rgb") {
  161. var start = styleString.indexOf("(", 3);
  162. var end = styleString.indexOf(")", start + 1);
  163. var guts = styleString.substring(start + 1, end).split(",");
  164. str = "#";
  165. for (var i = 0; i < 3; i++) {
  166. str += dec2hex[parseInt(guts[i])];
  167. }
  168. if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
  169. alpha = guts[3];
  170. }
  171. } else {
  172. str = styleString;
  173. }
  174. return [str, alpha];
  175. }
  176. function processLineCap(lineCap) {
  177. switch (lineCap) {
  178. case "butt":
  179. return "flat";
  180. case "round":
  181. return "round";
  182. case "square":
  183. default:
  184. return "square";
  185. }
  186. }
  187. /**
  188. * This class implements CanvasRenderingContext2D interface as described by
  189. * the WHATWG.
  190. * @param surfaceElement {HTMLElement} The element that the 2D context should
  191. * be associated with
  192. */
  193. function CanvasRenderingContext2D_(surfaceElement) {
  194. this.m_ = createMatrixIdentity();
  195. this.element_ = surfaceElement;
  196. this.mStack_ = [];
  197. this.aStack_ = [];
  198. this.currentPath_ = [];
  199. // Canvas context properties
  200. this.strokeStyle = "#000";
  201. this.fillStyle = "#ccc";
  202. this.lineWidth = 1;
  203. this.lineJoin = "miter";
  204. this.lineCap = "butt";
  205. this.miterLimit = 10;
  206. this.globalAlpha = 1;
  207. };
  208. var contextPrototype = CanvasRenderingContext2D_.prototype;
  209. contextPrototype.clearRect = function() {
  210. this.element_.innerHTML = "";
  211. this.currentPath_ = [];
  212. };
  213. contextPrototype.beginPath = function() {
  214. // TODO: Branch current matrix so that save/restore has no effect
  215. // as per safari docs.
  216. this.currentPath_ = [];
  217. };
  218. contextPrototype.moveTo = function(aX, aY) {
  219. this.currentPath_.push({type: "moveTo", x: aX, y: aY});
  220. };
  221. contextPrototype.lineTo = function(aX, aY) {
  222. this.currentPath_.push({type: "lineTo", x: aX, y: aY});
  223. };
  224. contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
  225. aCP2x, aCP2y,
  226. aX, aY) {
  227. this.currentPath_.push({type: "bezierCurveTo",
  228. cp1x: aCP1x,
  229. cp1y: aCP1y,
  230. cp2x: aCP2x,
  231. cp2y: aCP2y,
  232. x: aX,
  233. y: aY});
  234. };
  235. contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
  236. // VML's qb produces different output to Firefox's
  237. // FF's behaviour seems to have changed in 1.5.0.1, check this
  238. this.bezierCurveTo(aCPx, aCPy, aCPx, aCPy, aX, aY);
  239. };
  240. contextPrototype.arc = function(aX, aY, aRadius,
  241. aStartAngle, aEndAngle, aClockwise) {
  242. if (!aClockwise) {
  243. var t = aStartAngle;
  244. aStartAngle = aEndAngle;
  245. aEndAngle = t;
  246. }
  247. var xStart = aX + (Math.cos(aStartAngle) * aRadius);
  248. var yStart = aY + (Math.sin(aStartAngle) * aRadius);
  249. var xEnd = aX + (Math.cos(aEndAngle) * aRadius);
  250. var yEnd = aY + (Math.sin(aEndAngle) * aRadius);
  251. this.currentPath_.push({type: "arc",
  252. x: aX,
  253. y: aY,
  254. radius: aRadius,
  255. xStart: xStart,
  256. yStart: yStart,
  257. xEnd: xEnd,
  258. yEnd: yEnd});
  259. };
  260. contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
  261. this.moveTo(aX, aY);
  262. this.lineTo(aX + aWidth, aY);
  263. this.lineTo(aX + aWidth, aY + aHeight);
  264. this.lineTo(aX, aY + aHeight);
  265. this.closePath();
  266. };
  267. contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
  268. // Will destroy any existing path (same as FF behaviour)
  269. this.beginPath();
  270. this.moveTo(aX, aY);
  271. this.lineTo(aX + aWidth, aY);
  272. this.lineTo(aX + aWidth, aY + aHeight);
  273. this.lineTo(aX, aY + aHeight);
  274. this.closePath();
  275. this.stroke();
  276. };
  277. contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
  278. // Will destroy any existing path (same as FF behaviour)
  279. this.beginPath();
  280. this.moveTo(aX, aY);
  281. this.lineTo(aX + aWidth, aY);
  282. this.lineTo(aX + aWidth, aY + aHeight);
  283. this.lineTo(aX, aY + aHeight);
  284. this.closePath();
  285. this.fill();
  286. };
  287. contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
  288. var gradient = new CanvasGradient_("gradient");
  289. return gradient;
  290. };
  291. contextPrototype.createRadialGradient = function(aX0, aY0,
  292. aR0, aX1,
  293. aY1, aR1) {
  294. var gradient = new CanvasGradient_("gradientradial");
  295. gradient.radius1_ = aR0;
  296. gradient.radius2_ = aR1;
  297. gradient.focus_.x = aX0;
  298. gradient.focus_.y = aY0;
  299. return gradient;
  300. };
  301. contextPrototype.drawImage = function (image, var_args) {
  302. var dx, dy, dw, dh, sx, sy, sw, sh;
  303. var w = image.width;
  304. var h = image.height;
  305. if (arguments.length == 3) {
  306. dx = arguments[1];
  307. dy = arguments[2];
  308. sx = sy = 0;
  309. sw = dw = w;
  310. sh = dh = h;
  311. } else if (arguments.length == 5) {
  312. dx = arguments[1];
  313. dy = arguments[2];
  314. dw = arguments[3];
  315. dh = arguments[4];
  316. sx = sy = 0;
  317. sw = w;
  318. sh = h;
  319. } else if (arguments.length == 9) {
  320. sx = arguments[1];
  321. sy = arguments[2];
  322. sw = arguments[3];
  323. sh = arguments[4];
  324. dx = arguments[5];
  325. dy = arguments[6];
  326. dw = arguments[7];
  327. dh = arguments[8];
  328. } else {
  329. throw "Invalid number of arguments";
  330. }
  331. var d = this.getCoords_(dx, dy);
  332. var w2 = (sw / 2);
  333. var h2 = (sh / 2);
  334. var vmlStr = [];
  335. // For some reason that I've now forgotten, using divs didn't work
  336. vmlStr.push(' <g_vml_:group',
  337. ' coordsize="100,100"',
  338. ' coordorigin="0, 0"' ,
  339. ' style="width:100px;height:100px;position:absolute;');
  340. // If filters are necessary (rotation exists), create them
  341. // filters are bog-slow, so only create them if abbsolutely necessary
  342. // The following check doesn't account for skews (which don't exist
  343. // in the canvas spec (yet) anyway.
  344. if (this.m_[0][0] != 1 || this.m_[0][1]) {
  345. var filter = [];
  346. // Note the 12/21 reversal
  347. filter.push("M11='", this.m_[0][0], "',",
  348. "M12='", this.m_[1][0], "',",
  349. "M21='", this.m_[0][1], "',",
  350. "M22='", this.m_[1][1], "',",
  351. "Dx='", d.x, "',",
  352. "Dy='", d.y, "'");
  353. // Bounding box calculation (need to minimize displayed area so that
  354. // filters don't waste time on unused pixels.
  355. var max = d;
  356. var c2 = this.getCoords_(dx+dw, dy);
  357. var c3 = this.getCoords_(dx, dy+dh);
  358. var c4 = this.getCoords_(dx+dw, dy+dh);
  359. max.x = Math.max(max.x, c2.x, c3.x, c4.x);
  360. max.y = Math.max(max.y, c2.y, c3.y, c4.y);
  361. vmlStr.push(" padding:0 ", Math.floor(max.x), "px ", Math.floor(max.y),
  362. "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
  363. filter.join(""), ", sizingmethod='clip');")
  364. } else {
  365. vmlStr.push(" top:", d.y, "px;left:", d.x, "px;")
  366. }
  367. vmlStr.push(' ">' ,
  368. '<g_vml_:image src="', image.src, '"',
  369. ' style="width:', dw, ';',
  370. ' height:', dh, ';"',
  371. ' cropleft="', sx / w, '"',
  372. ' croptop="', sy / h, '"',
  373. ' cropright="', (w - sx - sw) / w, '"',
  374. ' cropbottom="', (h - sy - sh) / h, '"',
  375. ' />',
  376. '</g_vml_:group>');
  377. this.element_.insertAdjacentHTML("BeforeEnd",
  378. vmlStr.join(""));
  379. };
  380. contextPrototype.stroke = function(aFill) {
  381. var lineStr = [];
  382. var lineOpen = false;
  383. var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
  384. var color = a[0];
  385. var opacity = a[1] * this.globalAlpha;
  386. lineStr.push('<g_vml_:shape',
  387. ' fillcolor="', color, '"',
  388. ' filled="', Boolean(aFill), '"',
  389. ' style="position:absolute;width:10;height:10;"',
  390. ' coordorigin="0 0" coordsize="10 10"',
  391. ' stroked="', !aFill, '"',
  392. ' strokeweight="', this.lineWidth, '"',
  393. ' strokecolor="', color, '"',
  394. ' path="');
  395. var newSeq = false;
  396. var min = {x: null, y: null};
  397. var max = {x: null, y: null};
  398. for (var i = 0; i < this.currentPath_.length; i++) {
  399. var p = this.currentPath_[i];
  400. if (p.type == "moveTo") {
  401. lineStr.push(" m ");
  402. var c = this.getCoords_(p.x, p.y);
  403. lineStr.push(Math.floor(c.x), ",", Math.floor(c.y));
  404. } else if (p.type == "lineTo") {
  405. lineStr.push(" l ");
  406. var c = this.getCoords_(p.x, p.y);
  407. lineStr.push(Math.floor(c.x), ",", Math.floor(c.y));
  408. } else if (p.type == "close") {
  409. lineStr.push(" x ");
  410. } else if (p.type == "bezierCurveTo") {
  411. lineStr.push(" c ");
  412. var c = this.getCoords_(p.x, p.y);
  413. var c1 = this.getCoords_(p.cp1x, p.cp1y);
  414. var c2 = this.getCoords_(p.cp2x, p.cp2y);
  415. lineStr.push(Math.floor(c1.x), ",", Math.floor(c1.y), ",",
  416. Math.floor(c2.x), ",", Math.floor(c2.y), ",",
  417. Math.floor(c.x), ",", Math.floor(c.y));
  418. } else if (p.type == "arc") {
  419. lineStr.push(" ar ");
  420. var c = this.getCoords_(p.x, p.y);
  421. var cStart = this.getCoords_(p.xStart, p.yStart);
  422. var cEnd = this.getCoords_(p.xEnd, p.yEnd);
  423. // TODO: FIX (matricies (scale+rotation) now buggered this up)
  424. // VML arc also doesn't seem able to do rotated non-circular
  425. // arcs without parent grouping.
  426. var absXScale = this.m_[0][0];
  427. var absYScale = this.m_[1][1];
  428. lineStr.push(Math.floor(c.x - absXScale * p.radius), ",",
  429. Math.floor(c.y - absYScale * p.radius), " ",
  430. Math.floor(c.x + absXScale * p.radius), ",",
  431. Math.floor(c.y + absYScale * p.radius), " ",
  432. Math.floor(cStart.x), ",", Math.floor(cStart.y), " ",
  433. Math.floor(cEnd.x), ",", Math.floor(cEnd.y));
  434. }
  435. // TODO: Following is broken for curves due to
  436. // move to proper paths.
  437. // Figure out dimensions so we can do gradient fills
  438. // properly
  439. if(c) {
  440. if (min.x == null || c.x < min.x) {
  441. min.x = c.x;
  442. }
  443. if (max.x == null || c.x > max.x) {
  444. max.x = c.x;
  445. }
  446. if (min.y == null || c.y < min.y) {
  447. min.y = c.y;
  448. }
  449. if (max.y == null || c.y > max.y) {
  450. max.y = c.y;
  451. }
  452. }
  453. }
  454. lineStr.push(' ">');
  455. if (typeof this.fillStyle == "object") {
  456. var focus = {x: "50%", y: "50%"};
  457. var width = (max.x - min.x);
  458. var height = (max.y - min.y);
  459. var dimension = (width > height) ? width : height;
  460. focus.x = Math.floor((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
  461. focus.y = Math.floor((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
  462. var colors = [];
  463. // inside radius (%)
  464. if (this.fillStyle.type_ == "gradientradial") {
  465. var inside = (this.fillStyle.radius1_ / dimension * 100);
  466. // percentage that outside radius exceeds inside radius
  467. var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
  468. } else {
  469. var inside = 0;
  470. var expansion = 100;
  471. }
  472. var insidecolor = {offset: null, color: null};
  473. var outsidecolor = {offset: null, color: null};
  474. // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
  475. // won't interpret it correctly
  476. this.fillStyle.colors_.sort(function (cs1, cs2) {
  477. return cs1.offset - cs2.offset;
  478. });
  479. for (var i = 0; i < this.fillStyle.colors_.length; i++) {
  480. var fs = this.fillStyle.colors_[i];
  481. colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
  482. if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
  483. insidecolor.offset = fs.offset;
  484. insidecolor.color = fs.color;
  485. }
  486. if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
  487. outsidecolor.offset = fs.offset;
  488. outsidecolor.color = fs.color;
  489. }
  490. }
  491. colors.pop();
  492. lineStr.push('<g_vml_:fill',
  493. ' color="', outsidecolor.color, '"',
  494. ' color2="', insidecolor.color, '"',
  495. ' type="', this.fillStyle.type_, '"',
  496. ' focusposition="', focus.x, ', ', focus.y, '"',
  497. ' colors="', colors.join(""), '"',
  498. ' opacity="', opacity, '" />');
  499. } else if (aFill) {
  500. lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
  501. } else {
  502. lineStr.push(
  503. '<g_vml_:stroke',
  504. ' opacity="', opacity,'"',
  505. ' joinstyle="', this.lineJoin, '"',
  506. ' miterlimit="', this.miterLimit, '"',
  507. ' endcap="', processLineCap(this.lineCap) ,'"',
  508. ' weight="', this.lineWidth, 'px"',
  509. ' color="', color,'" />'
  510. );
  511. }
  512. lineStr.push("</g_vml_:shape>");
  513. this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
  514. this.currentPath_ = [];
  515. };
  516. contextPrototype.fill = function() {
  517. this.stroke(true);
  518. }
  519. contextPrototype.closePath = function() {
  520. this.currentPath_.push({type: "close"});
  521. };
  522. /**
  523. * @private
  524. */
  525. contextPrototype.getCoords_ = function(aX, aY) {
  526. return {
  527. x: (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]),
  528. y: (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1])
  529. }
  530. };
  531. contextPrototype.save = function() {
  532. var o = {};
  533. copyState(this, o);
  534. this.aStack_.push(o);
  535. this.mStack_.push(this.m_);
  536. this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
  537. };
  538. contextPrototype.restore = function() {
  539. copyState(this.aStack_.pop(), this);
  540. this.m_ = this.mStack_.pop();
  541. };
  542. contextPrototype.translate = function(aX, aY) {
  543. var m1 = [
  544. [1, 0, 0],
  545. [0, 1, 0],
  546. [aX, aY, 1]
  547. ];
  548. this.m_ = matrixMultiply(m1, this.m_);
  549. };
  550. contextPrototype.rotate = function(aRot) {
  551. var c = Math.cos(aRot);
  552. var s = Math.sin(aRot);
  553. var m1 = [
  554. [c, s, 0],
  555. [-s, c, 0],
  556. [0, 0, 1]
  557. ];
  558. this.m_ = matrixMultiply(m1, this.m_);
  559. };
  560. contextPrototype.scale = function(aX, aY) {
  561. var m1 = [
  562. [aX, 0, 0],
  563. [0, aY, 0],
  564. [0, 0, 1]
  565. ];
  566. this.m_ = matrixMultiply(m1, this.m_);
  567. };
  568. /******** STUBS ********/
  569. contextPrototype.clip = function() {
  570. // TODO: Implement
  571. };
  572. contextPrototype.arcTo = function() {
  573. // TODO: Implement
  574. };
  575. contextPrototype.createPattern = function() {
  576. return new CanvasPattern_;
  577. };
  578. // Gradient / Pattern Stubs
  579. function CanvasGradient_(aType) {
  580. this.type_ = aType;
  581. this.radius1_ = 0;
  582. this.radius2_ = 0;
  583. this.colors_ = [];
  584. this.focus_ = {x: 0, y: 0};
  585. }
  586. CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
  587. aColor = processStyle(aColor);
  588. this.colors_.push({offset: 1-aOffset, color: aColor});
  589. };
  590. function CanvasPattern_() {}
  591. // set up externs
  592. G_vmlCanvasManager = G_vmlCanvasManager_;
  593. CanvasRenderingContext2D = CanvasRenderingContext2D_;
  594. CanvasGradient = CanvasGradient_;
  595. CanvasPattern = CanvasPattern_;
  596. })();
  597. } // if