NavigationHistory.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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/Control.js
  7. * @requires OpenLayers/Control/Button.js
  8. */
  9. /**
  10. * Class: OpenLayers.Control.NavigationHistory
  11. * A navigation history control. This is a meta-control, that creates two
  12. * dependent controls: <previous> and <next>. Call the trigger method
  13. * on the <previous> and <next> controls to restore previous and next
  14. * history states. The previous and next controls will become active
  15. * when there are available states to restore and will become deactive
  16. * when there are no states to restore.
  17. *
  18. * Inherits from:
  19. * - <OpenLayers.Control>
  20. */
  21. OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {
  22. /**
  23. * Property: type
  24. * {String} Note that this control is not intended to be added directly
  25. * to a control panel. Instead, add the sub-controls previous and
  26. * next. These sub-controls are button type controls that activate
  27. * and deactivate themselves. If this parent control is added to
  28. * a panel, it will act as a toggle.
  29. */
  30. type: OpenLayers.Control.TYPE_TOGGLE,
  31. /**
  32. * APIProperty: previous
  33. * {<OpenLayers.Control>} A button type control whose trigger method restores
  34. * the previous state managed by this control.
  35. */
  36. previous: null,
  37. /**
  38. * APIProperty: previousOptions
  39. * {Object} Set this property on the options argument of the constructor
  40. * to set optional properties on the <previous> control.
  41. */
  42. previousOptions: null,
  43. /**
  44. * APIProperty: next
  45. * {<OpenLayers.Control>} A button type control whose trigger method restores
  46. * the next state managed by this control.
  47. */
  48. next: null,
  49. /**
  50. * APIProperty: nextOptions
  51. * {Object} Set this property on the options argument of the constructor
  52. * to set optional properties on the <next> control.
  53. */
  54. nextOptions: null,
  55. /**
  56. * APIProperty: limit
  57. * {Integer} Optional limit on the number of history items to retain. If
  58. * null, there is no limit. Default is 50.
  59. */
  60. limit: 50,
  61. /**
  62. * APIProperty: autoActivate
  63. * {Boolean} Activate the control when it is added to a map. Default is
  64. * true.
  65. */
  66. autoActivate: true,
  67. /**
  68. * Property: clearOnDeactivate
  69. * {Boolean} Clear the history when the control is deactivated. Default
  70. * is false.
  71. */
  72. clearOnDeactivate: false,
  73. /**
  74. * Property: registry
  75. * {Object} An object with keys corresponding to event types. Values
  76. * are functions that return an object representing the current state.
  77. */
  78. registry: null,
  79. /**
  80. * Property: nextStack
  81. * {Array} Array of items in the history.
  82. */
  83. nextStack: null,
  84. /**
  85. * Property: previousStack
  86. * {Array} List of items in the history. First item represents the current
  87. * state.
  88. */
  89. previousStack: null,
  90. /**
  91. * Property: listeners
  92. * {Object} An object containing properties corresponding to event types.
  93. * This object is used to configure the control and is modified on
  94. * construction.
  95. */
  96. listeners: null,
  97. /**
  98. * Property: restoring
  99. * {Boolean} Currently restoring a history state. This is set to true
  100. * before calling restore and set to false after restore returns.
  101. */
  102. restoring: false,
  103. /**
  104. * Constructor: OpenLayers.Control.NavigationHistory
  105. *
  106. * Parameters:
  107. * options - {Object} An optional object whose properties will be used
  108. * to extend the control.
  109. */
  110. initialize: function(options) {
  111. OpenLayers.Control.prototype.initialize.apply(this, [options]);
  112. this.registry = OpenLayers.Util.extend({
  113. "moveend": this.getState
  114. }, this.registry);
  115. var previousOptions = {
  116. trigger: OpenLayers.Function.bind(this.previousTrigger, this),
  117. displayClass: this.displayClass + " " + this.displayClass + "Previous"
  118. };
  119. OpenLayers.Util.extend(previousOptions, this.previousOptions);
  120. this.previous = new OpenLayers.Control.Button(previousOptions);
  121. var nextOptions = {
  122. trigger: OpenLayers.Function.bind(this.nextTrigger, this),
  123. displayClass: this.displayClass + " " + this.displayClass + "Next"
  124. };
  125. OpenLayers.Util.extend(nextOptions, this.nextOptions);
  126. this.next = new OpenLayers.Control.Button(nextOptions);
  127. this.clear();
  128. },
  129. /**
  130. * Method: onPreviousChange
  131. * Called when the previous history stack changes.
  132. *
  133. * Parameters:
  134. * state - {Object} An object representing the state to be restored
  135. * if previous is triggered again or null if no previous states remain.
  136. * length - {Integer} The number of remaining previous states that can
  137. * be restored.
  138. */
  139. onPreviousChange: function(state, length) {
  140. if(state && !this.previous.active) {
  141. this.previous.activate();
  142. } else if(!state && this.previous.active) {
  143. this.previous.deactivate();
  144. }
  145. },
  146. /**
  147. * Method: onNextChange
  148. * Called when the next history stack changes.
  149. *
  150. * Parameters:
  151. * state - {Object} An object representing the state to be restored
  152. * if next is triggered again or null if no next states remain.
  153. * length - {Integer} The number of remaining next states that can
  154. * be restored.
  155. */
  156. onNextChange: function(state, length) {
  157. if(state && !this.next.active) {
  158. this.next.activate();
  159. } else if(!state && this.next.active) {
  160. this.next.deactivate();
  161. }
  162. },
  163. /**
  164. * APIMethod: destroy
  165. * Destroy the control.
  166. */
  167. destroy: function() {
  168. OpenLayers.Control.prototype.destroy.apply(this);
  169. this.previous.destroy();
  170. this.next.destroy();
  171. this.deactivate();
  172. for(var prop in this) {
  173. this[prop] = null;
  174. }
  175. },
  176. /**
  177. * Method: setMap
  178. * Set the map property for the control and <previous> and <next> child
  179. * controls.
  180. *
  181. * Parameters:
  182. * map - {<OpenLayers.Map>}
  183. */
  184. setMap: function(map) {
  185. this.map = map;
  186. this.next.setMap(map);
  187. this.previous.setMap(map);
  188. },
  189. /**
  190. * Method: draw
  191. * Called when the control is added to the map.
  192. */
  193. draw: function() {
  194. OpenLayers.Control.prototype.draw.apply(this, arguments);
  195. this.next.draw();
  196. this.previous.draw();
  197. },
  198. /**
  199. * Method: previousTrigger
  200. * Restore the previous state. If no items are in the previous history
  201. * stack, this has no effect.
  202. *
  203. * Returns:
  204. * {Object} Item representing state that was restored. Undefined if no
  205. * items are in the previous history stack.
  206. */
  207. previousTrigger: function() {
  208. var current = this.previousStack.shift();
  209. var state = this.previousStack.shift();
  210. if(state != undefined) {
  211. this.nextStack.unshift(current);
  212. this.previousStack.unshift(state);
  213. this.restoring = true;
  214. this.restore(state);
  215. this.restoring = false;
  216. this.onNextChange(this.nextStack[0], this.nextStack.length);
  217. this.onPreviousChange(
  218. this.previousStack[1], this.previousStack.length - 1
  219. );
  220. } else {
  221. this.previousStack.unshift(current);
  222. }
  223. return state;
  224. },
  225. /**
  226. * APIMethod: nextTrigger
  227. * Restore the next state. If no items are in the next history
  228. * stack, this has no effect. The next history stack is populated
  229. * as states are restored from the previous history stack.
  230. *
  231. * Returns:
  232. * {Object} Item representing state that was restored. Undefined if no
  233. * items are in the next history stack.
  234. */
  235. nextTrigger: function() {
  236. var state = this.nextStack.shift();
  237. if(state != undefined) {
  238. this.previousStack.unshift(state);
  239. this.restoring = true;
  240. this.restore(state);
  241. this.restoring = false;
  242. this.onNextChange(this.nextStack[0], this.nextStack.length);
  243. this.onPreviousChange(
  244. this.previousStack[1], this.previousStack.length - 1
  245. );
  246. }
  247. return state;
  248. },
  249. /**
  250. * APIMethod: clear
  251. * Clear history.
  252. */
  253. clear: function() {
  254. this.previousStack = [];
  255. this.previous.deactivate();
  256. this.nextStack = [];
  257. this.next.deactivate();
  258. },
  259. /**
  260. * Method: getState
  261. * Get the current state and return it.
  262. *
  263. * Returns:
  264. * {Object} An object representing the current state.
  265. */
  266. getState: function() {
  267. return {
  268. center: this.map.getCenter(),
  269. resolution: this.map.getResolution(),
  270. projection: this.map.getProjectionObject(),
  271. units: this.map.getProjectionObject().getUnits() ||
  272. this.map.units || this.map.baseLayer.units
  273. };
  274. },
  275. /**
  276. * Method: restore
  277. * Update the state with the given object.
  278. *
  279. * Parameters:
  280. * state - {Object} An object representing the state to restore.
  281. */
  282. restore: function(state) {
  283. var center, zoom;
  284. if (this.map.getProjectionObject() == state.projection) {
  285. zoom = this.map.getZoomForResolution(state.resolution);
  286. center = state.center;
  287. } else {
  288. center = state.center.clone();
  289. center.transform(state.projection, this.map.getProjectionObject());
  290. var sourceUnits = state.units;
  291. var targetUnits = this.map.getProjectionObject().getUnits() ||
  292. this.map.units || this.map.baseLayer.units;
  293. var resolutionFactor = sourceUnits && targetUnits ?
  294. OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
  295. zoom = this.map.getZoomForResolution(resolutionFactor*state.resolution);
  296. }
  297. this.map.setCenter(center, zoom);
  298. },
  299. /**
  300. * Method: setListeners
  301. * Sets functions to be registered in the listeners object.
  302. */
  303. setListeners: function() {
  304. this.listeners = {};
  305. for(var type in this.registry) {
  306. this.listeners[type] = OpenLayers.Function.bind(function() {
  307. if(!this.restoring) {
  308. var state = this.registry[type].apply(this, arguments);
  309. this.previousStack.unshift(state);
  310. if(this.previousStack.length > 1) {
  311. this.onPreviousChange(
  312. this.previousStack[1], this.previousStack.length - 1
  313. );
  314. }
  315. if(this.previousStack.length > (this.limit + 1)) {
  316. this.previousStack.pop();
  317. }
  318. if(this.nextStack.length > 0) {
  319. this.nextStack = [];
  320. this.onNextChange(null, 0);
  321. }
  322. }
  323. return true;
  324. }, this);
  325. }
  326. },
  327. /**
  328. * APIMethod: activate
  329. * Activate the control. This registers any listeners.
  330. *
  331. * Returns:
  332. * {Boolean} Control successfully activated.
  333. */
  334. activate: function() {
  335. var activated = false;
  336. if(this.map) {
  337. if(OpenLayers.Control.prototype.activate.apply(this)) {
  338. if(this.listeners == null) {
  339. this.setListeners();
  340. }
  341. for(var type in this.listeners) {
  342. this.map.events.register(type, this, this.listeners[type]);
  343. }
  344. activated = true;
  345. if(this.previousStack.length == 0) {
  346. this.initStack();
  347. }
  348. }
  349. }
  350. return activated;
  351. },
  352. /**
  353. * Method: initStack
  354. * Called after the control is activated if the previous history stack is
  355. * empty.
  356. */
  357. initStack: function() {
  358. if(this.map.getCenter()) {
  359. this.listeners.moveend();
  360. }
  361. },
  362. /**
  363. * APIMethod: deactivate
  364. * Deactivate the control. This unregisters any listeners.
  365. *
  366. * Returns:
  367. * {Boolean} Control successfully deactivated.
  368. */
  369. deactivate: function() {
  370. var deactivated = false;
  371. if(this.map) {
  372. if(OpenLayers.Control.prototype.deactivate.apply(this)) {
  373. for(var type in this.listeners) {
  374. this.map.events.unregister(
  375. type, this, this.listeners[type]
  376. );
  377. }
  378. if(this.clearOnDeactivate) {
  379. this.clear();
  380. }
  381. deactivated = true;
  382. }
  383. }
  384. return deactivated;
  385. },
  386. CLASS_NAME: "OpenLayers.Control.NavigationHistory"
  387. });