canvasWithEvents.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. define("dojox/gfx/canvasWithEvents", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", "dojo/_base/Color", "dojo/dom",
  2. "dojo/dom-geometry", "./_base","./canvas", "./shape", "./matrix"],
  3. function(lang, declare, hub, Color, dom, domGeom, g, canvas, shapeLib, m){
  4. /*=====
  5. dojox.gfx.canvasWithEvents = {
  6. // module:
  7. // dojox/gfx/canvasWithEvents
  8. // summary:
  9. // This the graphics rendering bridge for W3C Canvas compliant browsers which extends
  10. // the basic canvas drawing renderer bridge to add additional support for graphics events
  11. // on Shapes.
  12. // Since Canvas is an immediate mode graphics api, with no object graph or
  13. // eventing capabilities, use of the canvas module alone will only add in drawing support.
  14. // This additional module, canvasWithEvents extends this module with additional support
  15. // for handling events on Canvas. By default, the support for events is now included
  16. // however, if only drawing capabilities are needed, canvas event module can be disabled
  17. // using the dojoConfig option, canvasEvents:true|false.
  18. // The id of the Canvas renderer is 'canvasWithEvents'. This id can be used when switch Dojo's
  19. // graphics context between renderer implementations. See dojox.gfx._base switchRenderer
  20. // API.
  21. };
  22. g = dojox.gfx;
  23. canvas.Shape = dojox.gfx.canvas.Shape;
  24. canvas.Group = dojox.gfx.canvas.Group;
  25. canvas.Image = dojox.gfx.canvas.Image;
  26. canvas.Text = dojox.gfx.canvas.Text;
  27. canvas.Rect = dojox.gfx.canvas.Rect;
  28. canvas.Circle = dojox.gfx.canvas.Circle;
  29. canvas.Ellipse = dojox.gfx.canvas.Ellipse;
  30. canvas.Line = dojox.gfx.canvas.Line;
  31. canvas.PolyLine = dojox.gfx.canvas.PolyLine;
  32. canvas.TextPath = dojox.gfx.canvas.TextPath;
  33. canvas.Path = dojox.gfx.canvas.Path;
  34. canvas.Surface = dojox.gfx.canvas.Surface;
  35. canvasEvent.Shape = dojox.gfx.canvasWithEvents.Shape;
  36. =====*/
  37. var canvasEvent = g.canvasWithEvents = {};
  38. declare("dojox.gfx.canvasWithEvents.Shape", canvas.Shape, {
  39. _testInputs: function(/* Object */ctx, /* Array */ pos){
  40. if (!this.canvasFill && this.strokeStyle) {
  41. // pixel-based until a getStrokedPath-like api is available on the path
  42. this._hitTestPixel(ctx, pos);
  43. } else {
  44. this._renderShape(ctx);
  45. var cnt = pos.length, t = this.getTransform();
  46. for (var i = 0; i < pos.length; ++i) {
  47. var input = pos[i];
  48. // already hit
  49. if (input.target)
  50. continue;
  51. var x = input.x, y = input.y;
  52. var p = t ? m.multiplyPoint(m.invert(t), x, y) : {
  53. x: x,
  54. y: y
  55. };
  56. input.target = this._hitTestGeometry(ctx, p.x, p.y);
  57. }
  58. }
  59. },
  60. _hitTestPixel: function(/* Object */ctx, /* Array */ pos){
  61. for (var i = 0; i < pos.length; ++i) {
  62. var input = pos[i];
  63. if (input.target)
  64. continue;
  65. var x = input.x, y = input.y;
  66. ctx.clearRect(0,0,1,1);
  67. ctx.save();
  68. ctx.translate(-x, -y);
  69. this._render(ctx, true);
  70. input.target = ctx.getImageData(0, 0, 1, 1).data[0] ? this : null;
  71. ctx.restore();
  72. }
  73. },
  74. _hitTestGeometry: function(ctx, x, y){
  75. return ctx.isPointInPath(x, y) ? this : null;
  76. },
  77. _renderFill: function(/* Object */ ctx, /* Boolean */ apply){
  78. // summary:
  79. // render fill for the shape
  80. // ctx:
  81. // a canvas context object
  82. // apply:
  83. // whether ctx.fill() shall be called
  84. if(ctx.pickingMode){
  85. if("canvasFill" in this && apply){ ctx.fill(); }
  86. return;
  87. }
  88. this.inherited(arguments);
  89. },
  90. _renderStroke: function(/* Object */ ctx, /* Boolean */ apply){
  91. // summary:
  92. // render stroke for the shape
  93. // ctx:
  94. // a canvas context object
  95. // apply:
  96. // whether ctx.stroke() shall be called
  97. if (this.strokeStyle && ctx.pickingMode) {
  98. var c = this.strokeStyle.color;
  99. try {
  100. this.strokeStyle.color = new Color(ctx.strokeStyle);
  101. this.inherited(arguments);
  102. } finally {
  103. this.strokeStyle.color = c;
  104. }
  105. } else{
  106. this.inherited(arguments);
  107. }
  108. },
  109. // events
  110. getEventSource: function(){
  111. // summary: returns this gfx shape event source, which is the surface rawnode in the case of canvas.
  112. return this.surface.getEventSource();
  113. },
  114. connect: function(name, object, method){
  115. // summary: connects a handler to an event on this shape
  116. this.surface._setupEvents(name); // setup events on demand
  117. // No need to fix callback. The listeners registered by
  118. // '_setupEvents()' are always invoked first and they
  119. // already 'fix' the event
  120. return arguments.length > 2 ? // Object
  121. hub.connect(this, name, object, method) : hub.connect(this, name, object);
  122. },
  123. disconnect: function(token){
  124. // summary: disconnects an event handler
  125. hub.disconnect(token);
  126. },
  127. // connect hook
  128. oncontextmenu: function(){},
  129. onclick: function(){},
  130. ondblclick: function(){},
  131. onmouseenter: function(){},
  132. onmouseleave: function(){},
  133. onmouseout: function(){},
  134. onmousedown: function(){},
  135. ontouchstart: function(){},
  136. touchstart: function(){},
  137. onmouseup: function(){},
  138. ontouchend: function(){},
  139. touchend: function(){},
  140. onmouseover: function(){},
  141. onmousemove: function(){},
  142. ontouchmove: function(){},
  143. touchmove: function(){},
  144. onkeydown: function(){},
  145. onkeyup: function(){}
  146. });
  147. declare("dojox.gfx.canvasWithEvents.Group", [canvasEvent.Shape, canvas.Group], {
  148. _testInputs: function(/*Object*/ctx, /*Array*/ pos){
  149. var children = this.children, t = this.getTransform(), i, j;
  150. if(children.length == 0){
  151. return;
  152. }
  153. var posbk = [];
  154. for(i = 0; i < pos.length; ++i){
  155. var input = pos[i];
  156. // backup position before transform applied
  157. posbk[i] = {
  158. x: input.x,
  159. y: input.y
  160. };
  161. if(input.target) continue;
  162. var x = input.x, y = input.y;
  163. var p = t ? m.multiplyPoint(m.invert(t), x, y) : {
  164. x: x,
  165. y: y
  166. };
  167. input.x = p.x;
  168. input.y = p.y;
  169. }
  170. for(i = children.length - 1; i >= 0; --i){
  171. children[i]._testInputs(ctx, pos);
  172. // does it need more hit tests ?
  173. var allFound = true;
  174. for(j = 0; j < pos.length; ++j){
  175. if(pos[j].target == null){
  176. allFound = false;
  177. break;
  178. }
  179. }
  180. if(allFound){
  181. break;
  182. }
  183. }
  184. for(i = 0; i < pos.length; ++i){
  185. pos[i].x = posbk[i].x;
  186. pos[i].y = posbk[i].y;
  187. }
  188. }
  189. });
  190. declare("dojox.gfx.canvasWithEvents.Image", [canvasEvent.Shape, canvas.Image], {
  191. _renderShape: function(/* Object */ ctx){
  192. // summary:
  193. // render image
  194. // ctx:
  195. // a canvas context object
  196. var s = this.shape;
  197. if(ctx.pickingMode){
  198. ctx.fillRect(s.x, s.y, s.width, s.height);
  199. }else{
  200. this.inherited(arguments);
  201. }
  202. },
  203. _hitTestGeometry: function(ctx, x, y){
  204. // TODO: improve hit testing to take into account transparency
  205. var s = this.shape;
  206. return x >= s.x && x <= s.x + s.width && y >= s.y && y <= s.y + s.height ? this : null;
  207. }
  208. });
  209. declare("dojox.gfx.canvasWithEvents.Text", [canvasEvent.Shape, canvas.Text], {
  210. _testInputs: function(ctx, pos){
  211. return this._hitTestPixel(ctx, pos);
  212. }
  213. });
  214. declare("dojox.gfx.canvasWithEvents.Rect", [canvasEvent.Shape, canvas.Rect], {});
  215. declare("dojox.gfx.canvasWithEvents.Circle", [canvasEvent.Shape, canvas.Circle], {});
  216. declare("dojox.gfx.canvasWithEvents.Ellipse", [canvasEvent.Shape, canvas.Ellipse],{});
  217. declare("dojox.gfx.canvasWithEvents.Line", [canvasEvent.Shape, canvas.Line],{});
  218. declare("dojox.gfx.canvasWithEvents.Polyline", [canvasEvent.Shape, canvas.Polyline],{});
  219. declare("dojox.gfx.canvasWithEvents.Path", [canvasEvent.Shape, canvas.Path],{});
  220. declare("dojox.gfx.canvasWithEvents.TextPath", [canvasEvent.Shape, canvas.TextPath],{});
  221. // a map that redirects shape-specific events to the canvas event handler that deals with these events
  222. var _eventsRedirectMap = {
  223. onmouseenter : 'onmousemove',
  224. onmouseleave : 'onmousemove',
  225. onmouseout : 'onmousemove',
  226. onmouseover : 'onmousemove',
  227. touchstart : 'ontouchstart',
  228. touchend : 'ontouchend',
  229. touchmove : 'ontouchmove'
  230. };
  231. var _eventsShortNameMap = {
  232. ontouchstart : 'touchstart',
  233. ontouchend : 'touchend',
  234. ontouchmove : 'touchmove'
  235. };
  236. var uagent = navigator.userAgent.toLowerCase(),
  237. isiOS = uagent.search('iphone') > -1 ||
  238. uagent.search('ipad') > -1 ||
  239. uagent.search('ipod') > -1;
  240. declare("dojox.gfx.canvasWithEvents.Surface", canvas.Surface, {
  241. constructor:function(){
  242. this._pick = { curr: null, last: null };
  243. this._pickOfMouseDown = null;
  244. this._pickOfMouseUp = null;
  245. },
  246. connect: function(/*String*/name, /*Object*/object, /*Function|String*/method){
  247. // summary: connects a handler to an event on this surface
  248. // name : String
  249. // The event name
  250. // object: Object
  251. // The object that method will receive as "this".
  252. // method: Function
  253. // A function reference, or name of a function in context.
  254. if (name.indexOf('touch') !== -1) {
  255. // in case of surface.connect('touchXXX'...), we must root the handler to the
  256. // specific touch event processing (done in fireTouchEvents) so that the event is properly configured.
  257. // So, we activate the shape-level event processing calling _setupEvents,
  258. // and connect to the _ontouchXXXImpl_ hooks that are called back by invokeHandler()
  259. this._setupEvents(name);
  260. name = "_on" + name + "Impl_";
  261. return hub.connect(this, name, object, method);
  262. } else {
  263. this._initMirrorCanvas();
  264. return hub.connect(this.getEventSource(), name, null,
  265. shapeLib.fixCallback(this, g.fixTarget, object, method));
  266. }
  267. },
  268. // connection hooks for touch events connect
  269. _ontouchstartImpl_: function(){},
  270. _ontouchendImpl_: function(){},
  271. _ontouchmoveImpl_: function(){},
  272. _initMirrorCanvas: function(){
  273. if (!this.mirrorCanvas) {
  274. var p = this._parent, mirror = p.ownerDocument.createElement("canvas");
  275. mirror.width = 1;
  276. mirror.height = 1;
  277. mirror.style.position = 'absolute';
  278. mirror.style.left = '-99999px';
  279. mirror.style.top = '-99999px';
  280. p.appendChild(mirror);
  281. this.mirrorCanvas = mirror;
  282. }
  283. },
  284. _setupEvents: function(eventName){
  285. // summary:
  286. // setup event listeners if not yet
  287. // onmouseenter and onmouseleave shape events are handled in the onmousemove surface handler
  288. if (eventName in _eventsRedirectMap)
  289. eventName = _eventsRedirectMap[eventName];
  290. if (this._eventsH && this._eventsH[eventName]) {
  291. // the required listener has already been connected
  292. return;
  293. }
  294. // a mirror canvas for shape picking
  295. this._initMirrorCanvas();
  296. if (!this._eventsH)
  297. this._eventsH = {};
  298. // register event hooks if not done yet
  299. this._eventsH[eventName] = hub.connect(this.getEventSource(), eventName,
  300. shapeLib.fixCallback(this, g.fixTarget, this, "_" + eventName));
  301. if (eventName === 'onclick' || eventName==='ondblclick') {
  302. if(!this._eventsH['onmousedown']){
  303. this._eventsH['onmousedown'] = hub.connect(this.getEventSource(),
  304. 'onmousedown', shapeLib.fixCallback(this, g.fixTarget, this, "_onmousedown"));
  305. }
  306. if(!this._eventsH['onmouseup']){
  307. this._eventsH['onmouseup'] = hub.connect(this.getEventSource(),
  308. 'onmouseup', shapeLib.fixCallback(this, g.fixTarget, this, "_onmouseup"));
  309. }
  310. }
  311. },
  312. destroy: function(){
  313. // summary: stops the move, deletes all references, so the object can be garbage-collected
  314. canvas.Surface.destroy.apply(this);
  315. // destroy events and objects
  316. for(var i in this._eventsH){
  317. hub.disconnect(this._eventsH[i]);
  318. }
  319. this._eventsH = this.mirrorCanvas = null;
  320. },
  321. // events
  322. getEventSource: function(){
  323. // summary: returns the canvas DOM node for surface-level events
  324. return this.rawNode;
  325. },
  326. // internal handlers used to implement shape-level event notification
  327. _invokeHandler: function(base, method, event){
  328. // Invokes handler function
  329. var handler = base[method];
  330. if(handler && handler.after){
  331. handler.apply(base, [event]);
  332. }else if (method in _eventsShortNameMap){
  333. // there may be a synonym event name (touchstart -> ontouchstart)
  334. handler = base[_eventsShortNameMap[method]];
  335. if(handler && handler.after){
  336. handler.apply(base, [event]);
  337. }
  338. }
  339. if(!handler && method.indexOf('touch') !== -1){
  340. // special case for surface touch handlers
  341. method = "_" + method + "Impl_";
  342. handler = base[method];
  343. if(handler){
  344. handler.apply(base, [event]);
  345. }
  346. }
  347. // Propagates event up in the DOM hierarchy only if event
  348. // has not been stopped (event.cancelBubble is true)
  349. if (!isEventStopped(event) && base.parent) {
  350. this._invokeHandler(base.parent, method, event);
  351. }
  352. },
  353. _oncontextmenu: function(e){
  354. // summary: triggers onclick
  355. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  356. if(this._pick.curr){
  357. this._invokeHandler(this._pick.curr, 'oncontextmenu', e);
  358. }
  359. },
  360. _ondblclick: function(e){
  361. // summary: triggers onclick
  362. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  363. if(this._pickOfMouseUp){
  364. this._invokeHandler(this._pickOfMouseUp, 'ondblclick', e);
  365. }
  366. },
  367. _onclick: function(e){
  368. // summary: triggers onclick
  369. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  370. if(this._pickOfMouseUp && this._pickOfMouseUp == this._pickOfMouseDown){
  371. this._invokeHandler(this._pickOfMouseUp, 'onclick', e);
  372. }
  373. },
  374. _onmousedown: function(e){
  375. // summary: triggers onmousedown
  376. this._pickOfMouseDown = this._pick.curr;
  377. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  378. if(this._pick.curr){
  379. this._invokeHandler(this._pick.curr, 'onmousedown', e);
  380. }
  381. },
  382. _ontouchstart: function(e){
  383. // summary: triggers ontouchstart
  384. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  385. if (this._pick.curr) {
  386. this._fireTouchEvent(e);
  387. }
  388. },
  389. _onmouseup: function(e){
  390. // summary: triggers onmouseup
  391. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  392. this._pickOfMouseUp = this._pick.curr;
  393. if(this._pick.curr){
  394. this._invokeHandler(this._pick.curr, 'onmouseup', e);
  395. }
  396. },
  397. _ontouchend: function(e){
  398. // summary: triggers ontouchend
  399. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  400. if(this._pick.curr){
  401. for(var i = 0; i < this._pick.curr.length; ++i){
  402. if(this._pick.curr[i].target){
  403. e.gfxTarget = this._pick.curr[i].target;
  404. this._invokeHandler(this._pick.curr[i].target, 'ontouchend', e);
  405. }
  406. }
  407. }
  408. },
  409. _onmousemove: function(e){
  410. // summary: triggers onmousemove, onmouseenter, onmouseleave
  411. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  412. if(this._pick.last && this._pick.last != this._pick.curr){
  413. this._invokeHandler(this._pick.last, 'onmouseleave', e);
  414. this._invokeHandler(this._pick.last, 'onmouseout', e);
  415. }
  416. if(this._pick.curr){
  417. if(this._pick.last == this._pick.curr){
  418. this._invokeHandler(this._pick.curr, 'onmousemove', e);
  419. }else{
  420. this._invokeHandler(this._pick.curr, 'onmouseenter', e);
  421. this._invokeHandler(this._pick.curr, 'onmouseover', e);
  422. }
  423. }
  424. },
  425. _ontouchmove: function(e){
  426. // summary: triggers ontouchmove
  427. if(this._pick.curr){
  428. this._fireTouchEvent(e);
  429. }
  430. },
  431. _fireTouchEvent: function(e){
  432. // this._pick.curr = an array of target for touch event, one target instance for mouse events
  433. var toFire = []; // the per-shape events list to fire
  434. // for each positive picking:
  435. // .group all pickings by target
  436. // .collect all touches for the picking target
  437. for(var i = 0; i < this._pick.curr.length; ++i){
  438. var pick = this._pick.curr[i];
  439. if(pick.target){
  440. // touches for this target
  441. var gfxtt = pick.target.__gfxtt;
  442. if(!gfxtt){
  443. gfxtt = [];
  444. pick.target.__gfxtt = gfxtt;
  445. }
  446. // store the touch that yielded to this picking
  447. gfxtt.push(pick.t);
  448. // if the target has not been added yet, add it
  449. if(!pick.target.__inToFire){
  450. toFire.push(pick.target);
  451. pick.target.__inToFire=true;
  452. }
  453. }
  454. }
  455. if(toFire.length === 0){
  456. // no target, invokes the surface handler
  457. this._invokeHandler(this, 'on' + e.type, e);
  458. }else{
  459. for(i = 0; i < toFire.length; ++i){
  460. (function(){
  461. var targetTouches = toFire[i].__gfxtt;
  462. // fires the original event BUT with our own targetTouches array.
  463. // Note for iOS:
  464. var evt = lang.delegate(e, {gfxTarget: toFire[i]});
  465. if(isiOS){
  466. // must use the original preventDefault function or iOS will throw a TypeError
  467. evt.preventDefault = function(){e.preventDefault();};
  468. evt.stopPropagation = function(){e.stopPropagation();};
  469. }
  470. // override targetTouches with the filtered one
  471. evt.__defineGetter__('targetTouches', function(){return targetTouches;});
  472. // clean up
  473. delete toFire[i].__gfxtt;
  474. delete toFire[i].__inToFire;
  475. // fire event
  476. this._invokeHandler(toFire[i], 'on' + e.type, evt);
  477. }).call(this);
  478. }
  479. }
  480. },
  481. _onkeydown: function(){}, // needed?
  482. _onkeyup: function(){}, // needed?
  483. _whatsUnderEvent: function(evt){
  484. // summary: returns the shape under the mouse event
  485. // evt: mouse event
  486. var surface = this, i,
  487. pos = domGeom.position(surface.rawNode, true),
  488. inputs = [], changedTouches = evt.changedTouches, touches = evt.touches;
  489. // collect input events targets
  490. if(changedTouches){
  491. for(i = 0; i < changedTouches.length; ++i){
  492. inputs.push({
  493. t: changedTouches[i],
  494. x: changedTouches[i].pageX - pos.x,
  495. y: changedTouches[i].pageY - pos.y
  496. });
  497. }
  498. }else if(touches){
  499. for(i = 0; i < touches.length; ++i){
  500. inputs.push({
  501. t: touches[i],
  502. x: touches[i].pageX - pos.x,
  503. y: touches[i].pageY - pos.y
  504. });
  505. }
  506. }else{
  507. inputs.push({
  508. x : evt.pageX - pos.x,
  509. y : evt.pageY - pos.y
  510. });
  511. }
  512. var mirror = surface.mirrorCanvas,
  513. ctx = mirror.getContext('2d'),
  514. children = surface.children;
  515. ctx.clearRect(0, 0, mirror.width, mirror.height);
  516. ctx.save();
  517. ctx.strokeStyle = "rgba(127,127,127,1.0)";
  518. ctx.fillStyle = "rgba(127,127,127,1.0)";
  519. ctx.pickingMode = true;
  520. var pick = null;
  521. // process the inputs to find the target.
  522. for(i = children.length-1; i >= 0; i--){
  523. children[i]._testInputs(ctx, inputs);
  524. // does it need more hit tests ?
  525. var allFound = true;
  526. for(j = 0; j < inputs.length; ++j){
  527. if(inputs[j].target == null){
  528. allFound = false;
  529. break;
  530. }
  531. }
  532. if(allFound){
  533. break;
  534. }
  535. }
  536. ctx.restore();
  537. // touch event handlers expect an array of target, mouse handlers one target
  538. return (touches || changedTouches) ? inputs : inputs[0].target;
  539. }
  540. });
  541. canvasEvent.createSurface = function(parentNode, width, height){
  542. // summary: creates a surface (Canvas)
  543. // parentNode: Node: a parent node
  544. // width: String: width of surface, e.g., "100px"
  545. // height: String: height of surface, e.g., "100px"
  546. if(!width && !height){
  547. var pos = domGeom.position(parentNode);
  548. width = width || pos.w;
  549. height = height || pos.h;
  550. }
  551. if(typeof width == "number"){
  552. width = width + "px";
  553. }
  554. if(typeof height == "number"){
  555. height = height + "px";
  556. }
  557. var s = new canvasEvent.Surface(),
  558. p = dom.byId(parentNode),
  559. c = p.ownerDocument.createElement("canvas");
  560. c.width = g.normalizedLength(width); // in pixels
  561. c.height = g.normalizedLength(height); // in pixels
  562. p.appendChild(c);
  563. s.rawNode = c;
  564. s._parent = p;
  565. s.surface = s;
  566. return s; // dojox.gfx.Surface
  567. };
  568. // Mouse/Touch event
  569. var isEventStopped = function(/*Event*/ evt){
  570. // summary:
  571. // queries whether an event has been stopped or not
  572. // evt: Event
  573. // The event object.
  574. if(evt.cancelBubble !== undefined){
  575. return evt.cancelBubble;
  576. }
  577. return false;
  578. };
  579. canvasEvent.fixTarget = function(event, gfxElement){
  580. // summary:
  581. // Adds the gfxElement to event.gfxTarget if none exists. This new
  582. // property will carry the GFX element associated with this event.
  583. // event: Object
  584. // The current input event (MouseEvent or TouchEvent)
  585. // gfxElement: Object
  586. // The GFX target element (a Surface in this case)
  587. if(isEventStopped(event)){
  588. return false;
  589. }
  590. if(!event.gfxTarget){
  591. gfxElement._pick.last = gfxElement._pick.curr;
  592. gfxElement._pick.curr = gfxElement._whatsUnderEvent(event);
  593. if (!lang.isArray(gfxElement._pick.curr))
  594. event.gfxTarget = gfxElement._pick.curr;
  595. }
  596. return true;
  597. };
  598. return canvasEvent;
  599. });