featureclick.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Events.js
  7. */
  8. /**
  9. * Class: OpenLayers.Events.featureclick
  10. *
  11. * Extension event type for handling feature click events, including overlapping
  12. * features.
  13. *
  14. * Event types provided by this extension:
  15. * - featureclick
  16. */
  17. OpenLayers.Events.featureclick = OpenLayers.Class({
  18. /**
  19. * Property: cache
  20. * {Object} A cache of features under the mouse.
  21. */
  22. cache: null,
  23. /**
  24. * Property: map
  25. * {<OpenLayers.Map>} The map to register browser events on.
  26. */
  27. map: null,
  28. /**
  29. * Property: provides
  30. * {Array(String)} The event types provided by this extension.
  31. */
  32. provides: ["featureclick", "nofeatureclick", "featureover", "featureout"],
  33. /**
  34. * Constructor: OpenLayers.Events.featureclick
  35. * Create a new featureclick event type.
  36. *
  37. * Parameters:
  38. * target - {<OpenLayers.Events>} The events instance to create the events
  39. * for.
  40. */
  41. initialize: function(target) {
  42. this.target = target;
  43. if (target.object instanceof OpenLayers.Map) {
  44. this.setMap(target.object);
  45. } else if (target.object instanceof OpenLayers.Layer.Vector) {
  46. if (target.object.map) {
  47. this.setMap(target.object.map);
  48. } else {
  49. target.object.events.register("added", this, function(evt) {
  50. this.setMap(target.object.map);
  51. });
  52. }
  53. } else {
  54. throw("Listeners for '" + this.provides.join("', '") +
  55. "' events can only be registered for OpenLayers.Layer.Vector " +
  56. "or OpenLayers.Map instances");
  57. }
  58. for (var i=this.provides.length-1; i>=0; --i) {
  59. target.extensions[this.provides[i]] = true;
  60. }
  61. },
  62. /**
  63. * Method: setMap
  64. *
  65. * Parameters:
  66. * map - {<OpenLayers.Map>} The map to register browser events on.
  67. */
  68. setMap: function(map) {
  69. this.map = map;
  70. this.cache = {};
  71. map.events.register("mousedown", this, this.start, {extension: true});
  72. map.events.register("mouseup", this, this.onClick, {extension: true});
  73. map.events.register("touchstart", this, this.start, {extension: true});
  74. map.events.register("touchmove", this, this.cancel, {extension: true});
  75. map.events.register("touchend", this, this.onClick, {extension: true});
  76. map.events.register("mousemove", this, this.onMousemove, {extension: true});
  77. },
  78. /**
  79. * Method: start
  80. * Sets startEvt = evt.
  81. *
  82. * Parameters:
  83. * evt - {<OpenLayers.Event>}
  84. */
  85. start: function(evt) {
  86. this.startEvt = evt;
  87. },
  88. /**
  89. * Method: cancel
  90. * Deletes the start event.
  91. *
  92. * Parameters:
  93. * evt - {<OpenLayers.Event>}
  94. */
  95. cancel: function(evt) {
  96. delete this.startEvt;
  97. },
  98. /**
  99. * Method: onClick
  100. * Listener for the click event.
  101. *
  102. * Parameters:
  103. * evt - {<OpenLayers.Event>}
  104. */
  105. onClick: function(evt) {
  106. if (!this.startEvt || evt.type !== "touchend" &&
  107. !OpenLayers.Event.isLeftClick(evt)) {
  108. return;
  109. }
  110. var features = this.getFeatures(this.startEvt);
  111. delete this.startEvt;
  112. // fire featureclick events
  113. var feature, layer, more, clicked = {};
  114. for (var i=0, len=features.length; i<len; ++i) {
  115. feature = features[i];
  116. layer = feature.layer;
  117. clicked[layer.id] = true;
  118. more = this.triggerEvent("featureclick", {feature: feature});
  119. if (more === false) {
  120. break;
  121. }
  122. }
  123. // fire nofeatureclick events on all vector layers with no targets
  124. for (i=0, len=this.map.layers.length; i<len; ++i) {
  125. layer = this.map.layers[i];
  126. if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) {
  127. this.triggerEvent("nofeatureclick", {layer: layer});
  128. }
  129. }
  130. },
  131. /**
  132. * Method: onMousemove
  133. * Listener for the mousemove event.
  134. *
  135. * Parameters:
  136. * evt - {<OpenLayers.Event>}
  137. */
  138. onMousemove: function(evt) {
  139. delete this.startEvt;
  140. var features = this.getFeatures(evt);
  141. var over = {}, newly = [], feature;
  142. for (var i=0, len=features.length; i<len; ++i) {
  143. feature = features[i];
  144. over[feature.id] = feature;
  145. if (!this.cache[feature.id]) {
  146. newly.push(feature);
  147. }
  148. }
  149. // check if already over features
  150. var out = [];
  151. for (var id in this.cache) {
  152. feature = this.cache[id];
  153. if (feature.layer && feature.layer.map) {
  154. if (!over[feature.id]) {
  155. out.push(feature);
  156. }
  157. } else {
  158. // removed
  159. delete this.cache[id];
  160. }
  161. }
  162. // fire featureover events
  163. var more;
  164. for (i=0, len=newly.length; i<len; ++i) {
  165. feature = newly[i];
  166. this.cache[feature.id] = feature;
  167. more = this.triggerEvent("featureover", {feature: feature});
  168. if (more === false) {
  169. break;
  170. }
  171. }
  172. // fire featureout events
  173. for (i=0, len=out.length; i<len; ++i) {
  174. feature = out[i];
  175. delete this.cache[feature.id];
  176. more = this.triggerEvent("featureout", {feature: feature});
  177. if (more === false) {
  178. break;
  179. }
  180. }
  181. },
  182. /**
  183. * Method: triggerEvent
  184. * Determines where to trigger the event and triggers it.
  185. *
  186. * Parameters:
  187. * type - {String} The event type to trigger
  188. * evt - {Object} The listener argument
  189. *
  190. * Returns:
  191. * {Boolean} The last listener return.
  192. */
  193. triggerEvent: function(type, evt) {
  194. var layer = evt.feature ? evt.feature.layer : evt.layer,
  195. object = this.target.object;
  196. if (object instanceof OpenLayers.Map || object === layer) {
  197. return this.target.triggerEvent(type, evt);
  198. }
  199. },
  200. /**
  201. * Method: getFeatures
  202. * Get all features at the given screen location.
  203. *
  204. * Parameters:
  205. * evt - {Object} Event object.
  206. *
  207. * Returns:
  208. * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point.
  209. */
  210. getFeatures: function(evt) {
  211. var x = evt.clientX, y = evt.clientY,
  212. features = [], targets = [], layers = [],
  213. layer, target, feature, i, len;
  214. // go through all layers looking for targets
  215. for (i=this.map.layers.length-1; i>=0; --i) {
  216. layer = this.map.layers[i];
  217. if (layer.div.style.display !== "none") {
  218. if (layer.renderer instanceof OpenLayers.Renderer.Elements) {
  219. if (layer instanceof OpenLayers.Layer.Vector) {
  220. target = document.elementFromPoint(x, y);
  221. while (target && target._featureId) {
  222. feature = layer.getFeatureById(target._featureId);
  223. if (feature) {
  224. features.push(feature);
  225. target.style.display = "none";
  226. targets.push(target);
  227. target = document.elementFromPoint(x, y);
  228. } else {
  229. // sketch, all bets off
  230. target = false;
  231. }
  232. }
  233. }
  234. layers.push(layer);
  235. layer.div.style.display = "none";
  236. } else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) {
  237. feature = layer.renderer.getFeatureIdFromEvent(evt);
  238. if (feature) {
  239. features.push(feature);
  240. layers.push(layer);
  241. }
  242. }
  243. }
  244. }
  245. // restore feature visibility
  246. for (i=0, len=targets.length; i<len; ++i) {
  247. targets[i].style.display = "";
  248. }
  249. // restore layer visibility
  250. for (i=layers.length-1; i>=0; --i) {
  251. layers[i].div.style.display = "block";
  252. }
  253. return features;
  254. },
  255. /**
  256. * APIMethod: destroy
  257. * Clean up.
  258. */
  259. destroy: function() {
  260. for (var i=this.provides.length-1; i>=0; --i) {
  261. delete this.target.extensions[this.provides[i]];
  262. }
  263. this.map.events.un({
  264. mousemove: this.onMousemove,
  265. mousedown: this.start,
  266. mouseup: this.onClick,
  267. touchstart: this.start,
  268. touchmove: this.cancel,
  269. touchend: this.onClick,
  270. scope: this
  271. });
  272. delete this.cache;
  273. delete this.map;
  274. delete this.target;
  275. }
  276. });
  277. /**
  278. * Class: OpenLayers.Events.nofeatureclick
  279. *
  280. * Extension event type for handling click events that do not hit a feature.
  281. *
  282. * Event types provided by this extension:
  283. * - nofeatureclick
  284. */
  285. OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick;
  286. /**
  287. * Class: OpenLayers.Events.featureover
  288. *
  289. * Extension event type for handling hovering over a feature.
  290. *
  291. * Event types provided by this extension:
  292. * - featureover
  293. */
  294. OpenLayers.Events.featureover = OpenLayers.Events.featureclick;
  295. /**
  296. * Class: OpenLayers.Events.featureout
  297. *
  298. * Extension event type for handling leaving a feature.
  299. *
  300. * Event types provided by this extension:
  301. * - featureout
  302. */
  303. OpenLayers.Events.featureout = OpenLayers.Events.featureclick;