kineticjs.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. "use strict";
  2. define( [ "module", "vwf/view", "jquery", "vwf/utility", "vwf/utility/color" ],
  3. function( module, view, $, utility, color ) {
  4. var self;
  5. var stageContainer;
  6. var stageWidth = 800;
  7. var stageHeight = 600;
  8. function processEvent( e, node, propagate ) {
  9. var returnData = { eventData: undefined, eventNodeData: undefined };
  10. if ( !propagate ) {
  11. // For the "dragend" event, kinetic sometimes sends us an event object that doesn't
  12. // have all the expected functions and properties attached
  13. e.evt.stopPropagation && e.evt.stopPropagation();
  14. }
  15. var eventPosition;
  16. var isTouchEvent = ( e.evt instanceof TouchEvent );
  17. if ( isTouchEvent ) {
  18. eventPosition = e.evt.changedTouches[ 0 ];
  19. } else {
  20. eventPosition = e.evt;
  21. }
  22. var stage = node && node.stage;
  23. returnData.eventData = [ convertBrowserEventDataToVwf( eventPosition, stage ) ];
  24. var eventDataElement = returnData.eventData[ 0 ];
  25. eventDataElement.button = e.evt.button;
  26. eventDataElement.timeStamp = e.evt.timeStamp;
  27. eventDataElement.shiftKey = e.evt.shiftKey;
  28. eventDataElement.ctrlKey = e.evt.ctrlKey;
  29. eventDataElement.altKey = e.evt.altKey;
  30. eventDataElement.metaKey = e.evt.metaKey;
  31. if ( isTouchEvent ) {
  32. returnData.eventData[ 0 ].touches = [];
  33. for ( var i = 0; i < e.evt.touches.length; i++ ) {
  34. returnData.eventData[ 0 ].touches[ i ] = convertBrowserEventDataToVwf(
  35. e.evt.touches[ i ],
  36. stage
  37. );
  38. }
  39. }
  40. if ( propagate ) {
  41. var stageId = stage && stage.getId();
  42. var pointerPickID = e.targetNode ? e.targetNode.getId() : stageId;
  43. returnData.eventNodeData = { "": [ {
  44. pickID: pointerPickID,
  45. } ] };
  46. if ( self && self.state.nodes[ pointerPickID ] ) {
  47. var childID = pointerPickID;
  48. var child = self.state.nodes[ childID ];
  49. var parentID = child.parentID;
  50. var parent = self.state.nodes[ child.parentID ];
  51. while ( child ) {
  52. returnData.eventNodeData[ childID ] = [ {
  53. pickID: pointerPickID,
  54. } ];
  55. childID = parentID;
  56. child = self.state.nodes[ childID ];
  57. parentID = child ? child.parentID : undefined;
  58. parent = parentID ? self.state.nodes[ child.parentID ] : undefined;
  59. }
  60. }
  61. }
  62. return returnData;
  63. }
  64. function convertBrowserEventDataToVwf( browserEventData, stage ) {
  65. var vwfEventData = {
  66. "location": [ browserEventData.x, browserEventData.y ],
  67. "stageRelative": [ browserEventData.pageX, browserEventData.pageY ],
  68. "client": [ browserEventData.clientX, browserEventData.clientY ],
  69. "screen": [ browserEventData.screenX, browserEventData.screenY ],
  70. "layer": [ browserEventData.layerX, browserEventData.layerY ],
  71. "page": [ browserEventData.pageX, browserEventData.pageY ],
  72. "offset": [ browserEventData.offsetX, browserEventData.offsetY ],
  73. "movement": [ browserEventData.webkitMovementX, browserEventData.webkitMovementY ]
  74. };
  75. if ( stage ) {
  76. vwfEventData.stage = [ stage.x(), stage.y() ];
  77. vwfEventData.stageRelative = [
  78. ( browserEventData.pageX - stage.x() ) / stage.scaleX(),
  79. ( browserEventData.pageY - stage.y() ) / stage.scaleY()
  80. ];
  81. }
  82. return vwfEventData;
  83. }
  84. function attachMouseEvents( node ) {
  85. var mouseDown = false;
  86. node.kineticObj.on( "mousemove", function( evt ) {
  87. var eData = processEvent( evt, node, false );
  88. self.kernel.fireEvent( node.ID, 'pointerMove', eData.eventData );
  89. } );
  90. node.kineticObj.on( "mouseout", function( evt ) {
  91. var eData = processEvent( evt, node, false );
  92. self.kernel.fireEvent( node.ID, 'pointerOut', eData.eventData );
  93. } );
  94. node.kineticObj.on( "mouseenter", function( evt ) {
  95. var eData = processEvent( evt, node, false );
  96. self.kernel.fireEvent( node.ID, 'pointerEnter', eData.eventData );
  97. } );
  98. node.kineticObj.on( "mouseleave", function( evt ) {
  99. var eData = processEvent( evt, node, false );
  100. self.kernel.fireEvent( node.ID, 'pointerLeave', eData.eventData );
  101. } );
  102. node.kineticObj.on( "mousedown", function( evt ) {
  103. var eData = processEvent( evt, node, false );
  104. mouseDown = true;
  105. self.kernel.fireEvent( node.ID, 'pointerDown', eData.eventData );
  106. } );
  107. node.kineticObj.on( "mouseup", function( evt ) {
  108. var eData = processEvent( evt, node, false );
  109. mouseDown = false;
  110. self.kernel.fireEvent( node.ID, 'pointerUp', eData.eventData );
  111. if ( node.kineticObj.mouseDragging ) {
  112. self.kernel.fireEvent( node.ID, 'dragEnd', eData.eventData );
  113. node.kineticObj.mouseDragging = false;
  114. }
  115. } );
  116. node.kineticObj.on( "click", function( evt ) {
  117. var eData = processEvent( evt, node, false );
  118. self.kernel.fireEvent( node.ID, 'pointerClick', eData.eventData );
  119. } );
  120. node.kineticObj.on( "dblclick", function( evt ) {
  121. var eData = processEvent( evt, node, false );
  122. self.kernel.fireEvent( node.ID, 'pointerDoubleClick', eData.eventData );
  123. } );
  124. node.kineticObj.on( "dragstart", function( evt ) {
  125. var eData = processEvent( evt, node, false );
  126. self.kernel.fireEvent( node.ID, 'dragStart', eData.eventData );
  127. node.kineticObj.mouseDragging = true;
  128. } );
  129. node.kineticObj.on( "dragmove", function( evt ) {
  130. var eData = processEvent( evt, node, false );
  131. self.kernel.fireEvent( node.ID, 'dragMove', eData.eventData );
  132. } );
  133. }
  134. function attachTouchEvents( node ) {
  135. var TOUCH_EVENT = true;
  136. node.kineticObj.on( "touchstart", function( evt ) {
  137. var eData = processEvent( evt, node, false );
  138. self.kernel.fireEvent( node.ID, 'touchStart', eData.eventData );
  139. } );
  140. node.kineticObj.on( "touchmove", function( evt ) {
  141. var eData = processEvent( evt, node, false );
  142. self.kernel.fireEvent( node.ID, 'touchMove', eData.eventData );
  143. } );
  144. node.kineticObj.on( "touchend", function( evt ) {
  145. var eData = processEvent( evt, node, false );
  146. self.kernel.fireEvent( node.ID, 'touchEnd', eData.eventData );
  147. } );
  148. node.kineticObj.on( "tap", function( evt ) {
  149. var eData = processEvent( evt, node, false );
  150. self.kernel.fireEvent( node.ID, 'tap', eData.eventData );
  151. } );
  152. node.kineticObj.on( "dbltap", function( evt ) {
  153. var eData = processEvent( evt, node, false );
  154. self.kernel.fireEvent( node.ID, 'doubleTap', eData.eventData );
  155. } );
  156. }
  157. return view.load( module, {
  158. initialize: function( options ) {
  159. self = this;
  160. this.arguments = Array.prototype.slice.call( arguments );
  161. this.options = options || {};
  162. if ( window && window.innerWidth ) {
  163. stageWidth = window.innerWidth - 20;
  164. }
  165. if ( window && window.innerHeight ) {
  166. stageHeight = window.innerHeight - 20;
  167. }
  168. stageContainer = this.options.container || 'vwf-root';
  169. stageWidth = this.options.width || stageWidth;
  170. stageHeight = this.options.height || stageHeight;
  171. },
  172. createdNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
  173. childSource, childType, childIndex, childName, callback ) {
  174. var node = this.state.nodes[ childID ];
  175. // If the "nodes" object does not have this object in it, it must not be one that
  176. // this driver cares about
  177. if ( !node ) {
  178. return;
  179. }
  180. var protos = node.prototypes;
  181. if ( self.state.isKineticClass( protos, [ "kinetic", "stage", "vwf" ] ) ) {
  182. var stage = this.state.stage = node.kineticObj;
  183. }
  184. },
  185. initializedNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
  186. childSource, childType, childIndex, childName, callback ) {
  187. var node = this.state.nodes[ childID ];
  188. // If the "nodes" object does not have this object in it, it must not be one that
  189. // this driver cares about
  190. if ( !node ) {
  191. //var stage = this.state.stages[ childID ];
  192. //renderScene( stage );
  193. return;
  194. }
  195. if ( node.kineticObj ) {
  196. // Attach the mouse and/or touch events based on property settings
  197. vwf_view.kernel.getProperty( childID, "supportMouseEvents" );
  198. vwf_view.kernel.getProperty( childID, "supportTouchEvents" );
  199. }
  200. },
  201. // -- deletedNode ------------------------------------------------------------------------------
  202. //deletedNode: function( nodeID ) { },
  203. // -- addedChild -------------------------------------------------------------------------------
  204. //addedChild: function( nodeID, childID, childName ) { },
  205. // -- removedChild -----------------------------------------------------------------------------
  206. //removedChild: function( nodeID, childID ) { },
  207. // -- createdProperty --------------------------------------------------------------------------
  208. //createdProperty: function (nodeID, propertyName, propertyValue) { },
  209. // -- initializedProperty ----------------------------------------------------------------------
  210. //initializedProperty: function (nodeID, propertyName, propertyValue) { },
  211. // TODO: deletedProperty
  212. // -- satProperty ------------------------------------------------------------------------------
  213. satProperty: function( nodeID, propertyName, propertyValue ) {
  214. var node = this.state.nodes[ nodeID ];
  215. // If we don't have a record of this node, it is not a kinetic node, and we ignore it
  216. if ( !( node && node.kineticObj ) ) {
  217. return;
  218. }
  219. var kineticObj = node.kineticObj;
  220. switch ( propertyName ) {
  221. case "supportMouseEvents":
  222. if ( propertyValue === true ) {
  223. attachMouseEvents( node );
  224. }
  225. break;
  226. case "supportTouchEvents":
  227. if ( propertyValue === true ) {
  228. attachTouchEvents( node );
  229. }
  230. break;
  231. case "enableEvents":
  232. var mouseDown = false;
  233. var touch = false;
  234. var mouseDownTime = null;
  235. var mouseDownId = undefined;
  236. var touchId = undefined;
  237. var timer = new Date();
  238. var protos = node.prototypes;
  239. if ( self.state.isKineticClass( protos, [ "kinetic", "stage", "vwf" ] ) ) {
  240. var stage = kineticObj;
  241. var TOUCH_EVENT = true;
  242. // these are the events for the global space, ie the stage
  243. // we did originally implement the mouse events this way
  244. // but then we added the events for the individual objects
  245. // which appeared to work better. We were getting duplicate events
  246. // which is why I moved these events down inside a property
  247. // just in case they were needed for another apllication that
  248. // is set up differently
  249. if ( Boolean( propertyValue ) ) {
  250. // defined handlers
  251. stage.on( 'contentMousedown', function( evt ) {
  252. var node = evt.targetNode;
  253. mouseDownId = ( node !== undefined ) ? node.getId() : stage.getId();
  254. mouseDown = true;
  255. mouseDownTime = timer.getTime();
  256. var eData = processEvent( evt, self.state.nodes[ mouseDownId ], true ); // false might be more corret here
  257. self.kernel.dispatchEvent( mouseDownId, 'pointerDown', eData.eventData, eData.eventNodeData );
  258. });
  259. stage.on( 'contentMousemove', function( evt ) {
  260. var node = evt.targetNode;
  261. var eData = processEvent( evt, self.state.nodes[ mouseDownId ], true ); // false might be more corret here
  262. self.kernel.dispatchEvent( mouseDownId ? mouseDownId : stage.getId(), 'pointerMove', eData.eventData, eData.eventNodeData );
  263. });
  264. stage.on( 'contentMouseup', function( evt ) {
  265. var node = evt.targetNode;
  266. mouseDown = false;
  267. var eData = processEvent( evt, self.state.nodes[ mouseDownId ], true ); // false might be more corret here
  268. if ( timer.getTime() - mouseDownTime < 700.0 ) {
  269. self.kernel.dispatchEvent( mouseDownId, 'pointerClick', eData.eventData, eData.eventNodeData );
  270. }
  271. self.kernel.dispatchEvent( mouseDownId ? mouseDownId : stage.getId(), 'pointerUp', eData.eventData, eData.eventNodeData );
  272. mouseDownTime = null;
  273. mouseDownId = null;
  274. } );
  275. } else {
  276. // remove handlers
  277. stage.off( 'contentMousedown' );
  278. stage.off( 'contentMousemove' );
  279. stage.off( 'contentMouseup' );
  280. // stage.off( 'contentTouchstart' );
  281. // stage.off( 'contentTouchmove' );
  282. // stage.off( 'contentTouchend' );
  283. // stage.off( 'contentTap' );
  284. }
  285. }
  286. break;
  287. case "scale":
  288. if ( !node.uniqueInView ) {
  289. kineticObj.scale( {
  290. "x": kineticObj.modelScaleX,
  291. "y": kineticObj.modelScaleY
  292. } );
  293. }
  294. break;
  295. case "scaleX":
  296. if ( !node.uniqueInView ) {
  297. kineticObj.scaleX( kineticObj.modelScaleX );
  298. }
  299. break;
  300. case "scaleY":
  301. if ( !node.uniqueInView ) {
  302. kineticObj.scaleY( kineticObj.modelScaleY );
  303. }
  304. break;
  305. }
  306. },
  307. gotProperty: function( nodeID, propertyName, propertyValue ) {
  308. var node = this.state.nodes[ nodeID ];
  309. var eventsAdded = false;
  310. // If we don't have a record of this node, it is not a kinetic node, and we ignore it
  311. if ( !( node && node.kineticObj ) ) {
  312. return eventsAdded;
  313. }
  314. switch ( propertyName ) {
  315. case "supportMouseEvents":
  316. if ( ( propertyValue === true ) && ( !node.hasMouseEvents ) ) {
  317. attachMouseEvents( node );
  318. node.hasMouseEvents = true;
  319. eventsAdded = node.hasMouseEvents;
  320. }
  321. break;
  322. case "supportTouchEvents":
  323. if ( ( propertyValue === true ) && ( !node.hasTouchEvents ) ) {
  324. attachTouchEvents( node );
  325. node.hasTouchEvents = true;
  326. eventsAdded = node.hasTouchEvents;
  327. }
  328. break;
  329. default:
  330. break;
  331. }
  332. return eventsAdded;
  333. },
  334. firedEvent: function( nodeID, eventName ) {
  335. if ( eventName == "draggingFromView" ) {
  336. var clientThatSatProperty = self.kernel.client();
  337. var me = self.kernel.moniker();
  338. // If the transform property was initially updated by this view....
  339. if ( clientThatSatProperty == me ) {
  340. // Tell the model not to update the view on the next position update because
  341. // kinetic has already done so
  342. // (this event is fired right before this driver sets the model property, so we
  343. // can be sure that the very next set of the position property is from us)
  344. var node = this.state.nodes[ nodeID ];
  345. node.viewIgnoreNextPositionUpdate = true;
  346. }
  347. }
  348. },
  349. ticked: function( vwfTime ) {
  350. var nodeIDs = Object.keys( this.state.nodes );
  351. for ( var i = 0; i < nodeIDs.length; i++ ) {
  352. var nodeID = nodeIDs[ i ];
  353. var node = this.state.nodes[ nodeID ];
  354. // If users can drag this node and all clients should stay synchronized, we must
  355. // pull the new node position out of kinetic and update the model with it
  356. if ( node.kineticObj.draggable() && !node.uniqueInView ) {
  357. var kineticX = node.kineticObj.x();
  358. var kineticY = node.kineticObj.y();
  359. // If the position of this node has changes since it's last model value, set the
  360. // model property with the new value
  361. if ( ( node.kineticObj.modelX !== kineticX ) ||
  362. ( node.kineticObj.modelY !== kineticY ) ) {
  363. // Fire this event to notify the model that kinetic has already updated the
  364. // view and it doesn't need to (if the model set the value, it would risk
  365. // having the model set the view back to an old value, which results in
  366. // jitter while the user is dragging the node)
  367. vwf_view.kernel.fireEvent( nodeID, "draggingFromView" );
  368. vwf_view.kernel.setProperty( nodeID, "position", [ kineticX, kineticY ] );
  369. }
  370. }
  371. }
  372. for ( var id in self.state.stages ){
  373. renderScene( self.state.stages[ id ] );
  374. }
  375. }
  376. } );
  377. function renderScene( stage ) {
  378. //window.requestAnimationFrame( renderScene( stage ) );
  379. if ( stage !== undefined ) {
  380. stage.batchDraw();
  381. }
  382. }
  383. });