Reusable Accessible Mapping Platform

API Docs for: 5.0.0
Show:

File: src\js\RAMP\Modules\basemapSelector.js

  1. /*global define, $, esri, tmpl, RAMP, i18n, TimelineLite, window, console */
  2. /*jslint white: true */
  3.  
  4. /**
  5. *
  6. * @module RAMP
  7. */
  8.  
  9. /**
  10. * Populates the BasemapGallery widget located in the maps toolbar with items found in the application configuration.
  11. * This module also handles all the event needed to change the map basemap and update the UI
  12. *
  13. *
  14. * @class BaseMapSelector
  15. * @static
  16. * @uses dojo/_base/array
  17. * @uses dojo/_base/lang
  18. * @uses dojo/dom-attr
  19. * @uses dojo/query
  20. * @uses dojo/topic
  21. * @uses templates/basemap_selector_template.json
  22. * @uses GlobalStorage
  23. * @uses Map
  24. * @uses EventManager
  25. * @uses esri/dijit/BasemapGallery
  26. * @uses utils/popupManager
  27. * @uses utils/thmplHelper
  28. */
  29.  
  30. define([
  31. // Dojo
  32. "dojo/_base/array", "dojo/_base/lang", "dojo/dom-attr", "dojo/query", "dojo/topic",
  33. // Templates
  34. "dojo/text!./templates/basemap_selector_template.json",
  35. // Ramp
  36. "ramp/globalStorage", "ramp/map", "ramp/eventManager",
  37. // Esri
  38. "esri/dijit/BasemapGallery",
  39. // Util
  40. "utils/dictionary", "utils/popupManager", "utils/util", "utils/tmplHelper"],
  41.  
  42. function (
  43. // Dojo
  44. dojoArray, dojoLang, domAttr, query, topic,
  45. // Templates
  46. basemapselectorTemplate,
  47. // Ramp
  48. GlobalStorage, RampMap, EventManager,
  49. // Esri
  50. BasemapGallery,
  51. // Util
  52. Dictionary, PopupManager, UtilMisc, TmplHelper) {
  53. "use strict";
  54.  
  55. var basemapGallery,
  56.  
  57. currentBasemapId,
  58. currentTileSchema,
  59. basemaps,
  60.  
  61. placementAnchorId = "basemapGallery",
  62.  
  63. ui = (function () {
  64. var baseMapControls,
  65. baseMapToggle,
  66.  
  67. selectorSectionContainer,
  68. selectorSection,
  69.  
  70. selectorPopup,
  71. projectionPopup,
  72. basemapPopup,
  73.  
  74. selectorOpenTimeline = new TimelineLite({ paused: true }),
  75.  
  76. transitionDuration = 0.4,
  77.  
  78. cssButtonPressedClass = "button-pressed";
  79.  
  80. function createSelectorOpenTL() {
  81. var time = selectorOpenTimeline.time();
  82.  
  83. selectorOpenTimeline
  84. .clear()
  85. //.set(selectorSectionContainer, { display: "block" }, 0)
  86. .fromTo(selectorSection, transitionDuration,
  87. { top: -selectorSectionContainer.find(".basemapselector-section").outerHeight() - 20 },
  88. { top: 0, ease: "easeOutCirc" }, 0)
  89. .seek(time);
  90. }
  91.  
  92. function setTooltips() {
  93. // set tooltips on the overflowing spans with basemap/projection names
  94.  
  95. if (selectorPopup.isOpen()) {
  96. selectorSection
  97. .find("span[title]:visible")
  98. .each(function () {
  99. var node = $(this);
  100. if (node.attr("title")) {
  101. if (node.isOverflowed()) {
  102. node.tooltipster({ theme: '.tooltipster-shadow' });
  103. } else {
  104. node.removeAttr("title");
  105. }
  106. }
  107. });
  108. }
  109. }
  110.  
  111. return {
  112. /**
  113. * Initiates additional UI components of the widget, setting listeners and registering the popup functionality
  114. *
  115. * @method init
  116. * @private
  117. * @return {object} itself
  118. *
  119. */
  120. init: function (basemapId, tileSchema) {
  121. var data = [],
  122. pj = {},
  123. basemapControl,
  124. projectionControl;
  125.  
  126. baseMapControls = $("#basemapControls");
  127. baseMapToggle = $("#baseMapToggle");
  128.  
  129. // group basemaps by projection
  130. basemaps.forEach(function (m) {
  131. if (!pj[m.tileSchema]) {
  132. pj[m.tileSchema] = [];
  133. }
  134. pj[m.tileSchema].push(m);
  135. });
  136. Dictionary.forEachEntry(pj, function (k, p) {
  137. data.push(
  138. {
  139. isActive: k === tileSchema,
  140. id: k,
  141. tileShema: tileSchema,
  142. name: k,
  143. maps: p
  144. }
  145. );
  146. });
  147.  
  148. // load JSON templates for basemap and skin every node under the basemap selector
  149. tmpl.templates = JSON.parse(TmplHelper.stringifyTemplate(basemapselectorTemplate));
  150.  
  151. baseMapControls.append(tmpl("basemapselector", data));
  152. selectorSectionContainer = baseMapControls.find("#basemapselector-section-container");
  153. selectorSection = selectorSectionContainer.find(".basemapselector-section");
  154.  
  155. // turn on the opening and closing of the basemap selector section
  156. selectorPopup = PopupManager.registerPopup(baseMapControls, "hoverIntent",
  157. function (d) {
  158. baseMapToggle.addClass("button-pressed");
  159. createSelectorOpenTL();
  160.  
  161. selectorOpenTimeline.eventCallback("onComplete", function () {
  162. d.resolve();
  163. setTooltips();
  164. });
  165.  
  166. selectorSectionContainer.show();
  167. selectorOpenTimeline.play();
  168. },
  169. {
  170. activeClass: cssButtonPressedClass,
  171. target: selectorSectionContainer,
  172. closeHandler: function (d) {
  173. createSelectorOpenTL();
  174.  
  175. selectorOpenTimeline.eventCallback("onReverseComplete", function () {
  176. baseMapToggle.removeClass("button-pressed");
  177. selectorSectionContainer.hide();
  178. d.resolve();
  179. });
  180.  
  181. selectorOpenTimeline.reverse();
  182. },
  183. timeout: 500
  184. }
  185. );
  186.  
  187. // show/hide basemap lists based on what projection group is active
  188. projectionPopup = PopupManager.registerPopup(selectorSectionContainer, "click",
  189. function (d) {
  190. var fromHeight,
  191. toHeight,
  192. heightTimeline;
  193.  
  194. if (!this.isOpen()) {
  195. fromHeight = selectorSection.height();
  196. toHeight = this.target.height();
  197. heightTimeline = new TimelineLite({
  198. onComplete: setTooltips
  199. });
  200.  
  201. projectionPopup.close();
  202.  
  203. // animate resizing of the selector when switching between projection groups
  204. heightTimeline
  205. .set(this.target, { display: "block" }, 0)
  206. .fromTo(this.target, transitionDuration, { height: fromHeight }, { height: toHeight }, 0)
  207. .to(selectorSection, transitionDuration, { height: toHeight, ease: "easeOutCirc" }, 0);
  208. }
  209.  
  210. d.resolve();
  211. },
  212. {
  213. closeHandler: function (d) {
  214. this.target.hide();
  215. d.resolve();
  216. },
  217. openOnly: true,
  218. activeClass: cssButtonPressedClass,
  219. handleSelector: ".projection-button",
  220. containerSelector: ".projection-list-item",
  221. targetSelector: ".basemap-list-pane"
  222. }
  223. );
  224.  
  225. // listen to clicks on basemap list and switch basemap accordingly
  226. basemapPopup = PopupManager.registerPopup(selectorSectionContainer, "click",
  227. function (d) {
  228. if (!this.isOpen()) {
  229. basemapPopup.close();
  230. selectBasemap(this.target.data("basemap-id"), this.target.data("tileshema"));
  231. }
  232.  
  233. d.resolve();
  234. },
  235. {
  236. closeHandler: function (d) {
  237. d.resolve();
  238. },
  239. openOnly: true,
  240. handleSelector: ".basemap-button",
  241. activeClass: cssButtonPressedClass
  242. }
  243. );
  244.  
  245. basemapControl = selectorSectionContainer.find("button[data-basemap-id='" + basemapId + "']");
  246. projectionControl = selectorSectionContainer.find("button[data-projection-id='" + tileSchema + "']");
  247.  
  248. basemapPopup.open(basemapControl);
  249. projectionPopup.open(projectionControl);
  250.  
  251. selectorSectionContainer.hide(); // hide baseselector after it's initiated
  252.  
  253. topic.publish(EventManager.BasemapSelector.UI_COMPLETE);
  254.  
  255. return this;
  256. },
  257. /*
  258. * Changes the text shown on the toolbar to match the currently selected basemap's title
  259. * @method updateToggleLabel
  260. * @private
  261. *
  262. */
  263. updateToggleLabel: function () {
  264. baseMapToggle.find("span:first").text(basemapGallery.getSelected().title);
  265. }
  266. };
  267. }());
  268.  
  269. /**
  270. * Initializes functions that publish events.
  271. *
  272. * @method initTopics
  273. * @private
  274. */
  275. function initTopics() {
  276. /* PUBLISH */
  277. basemapGallery.on("selection-change", function () {
  278. var basemap = basemapGallery.getSelected();
  279.  
  280. ui.updateToggleLabel();
  281. topic.publish(EventManager.BasemapSelector.BASEMAP_CHANGED, {
  282. id: basemap.id,
  283. cssStyle: basemap.scaleCssClass
  284. });
  285. });
  286. }
  287.  
  288. /**
  289. * Initializes class listeners.
  290. *
  291. * @method initListeners
  292. * @private
  293. */
  294. function initListeners() {
  295. /* SUBSCRIBE */
  296. topic.subscribe(EventManager.BasemapSelector.TOGGLE, function (eventArg) {
  297. selectBasemap(eventArg.id);
  298. });
  299. }
  300.  
  301. /**
  302. * Selects a basemap in the basemapgallery based on the supplied basemap id. If the tileShema is different from the current one, reload the page.
  303. *
  304. * @method selectBasemap
  305. * @param {String} basemapId a basemap id used to select a basemap in the basemapgallery
  306. * @param {String} tileSchema a tileShema of the selected basemap
  307. * @private
  308. */
  309. function selectBasemap(basemapId, tileSchema) {
  310. if (currentBasemapId !== basemapId) {
  311. if (currentTileSchema === tileSchema) {
  312. currentBasemapId = basemapId;
  313. basemapGallery.select(currentBasemapId);
  314. } else {
  315. //we need to generate a bookmark and reload the page
  316.  
  317. //set listner for bookmark complete
  318. topic.subscribe(EventManager.BookmarkLink.BOOKMARK_GENERATED, function (eventArg) {
  319. //run away, load new page
  320. window.location.href = eventArg.link;
  321. });
  322.  
  323. // trigger bookmark generation. don't care about cssStyle as we are going to reload the site anyways
  324. topic.publish(EventManager.BasemapSelector.BASEMAP_CHANGED, {
  325. id: basemapId,
  326. cssStyle: ""
  327. });
  328. }
  329. }
  330. }
  331.  
  332. return {
  333. /*
  334. * Adds all of the basemaps specified in the application configuration to the basemap selector widget and then calls function to initializes event handling
  335. * @method init
  336. * @constructor
  337. *
  338. */
  339. init: function () {
  340. var initialBasemap,
  341. esriBasemaps = [],
  342. basemapId;
  343.  
  344. basemaps = RAMP.config.basemaps;
  345.  
  346. RAMP.basemapIndex = {};
  347.  
  348. dojoArray.forEach(basemaps, function (basemap, i) {
  349. var basemapDijit,
  350. basemapLayers = [];
  351.  
  352. // iterate over basemap layers and create layer objects for each;
  353. // these objects can have any of the properties of the Basemap param constructor object here: https://developers.arcgis.com/javascript/jsapi/basemaplayer-amd.html#basemaplayer1
  354. basemap.layers.forEach(function (layer) {
  355. //console.log(layer);
  356. basemapLayers.push(
  357. new esri.dijit.BasemapLayer(layer)
  358. );
  359. });
  360.  
  361. basemapDijit = new esri.dijit.Basemap({
  362. id: basemap.id,
  363. layers: basemapLayers, // shovel all the layers into the basemap
  364. title: String.format("{0}, {1}", basemap.name, i18n.t('config.tileSchema.' + basemap.tileSchema)),
  365. thumbnailUrl: basemap.thumbnail
  366. });
  367. basemapDijit.scaleCssClass = basemap.scaleCssClass;
  368.  
  369. esriBasemaps.push(basemapDijit);
  370.  
  371. //store index in lookup for bookmark module
  372. RAMP.basemapIndex[basemap.id] = i;
  373. });
  374.  
  375. //Create and start the selector
  376. basemapGallery = new BasemapGallery({
  377. showArcGISBasemaps: false,
  378. basemaps: esriBasemaps,
  379. map: RampMap.getMap()
  380. }, placementAnchorId);
  381.  
  382. basemapGallery.on('error', function (evt) {
  383. //TODO handle this in a nicer way
  384. console.log('Error occurred when loading basemap: ' + evt.message);
  385. });
  386.  
  387. basemapGallery.startup();
  388.  
  389. initialBasemap = basemaps[RAMP.config.initialBasemapIndex];
  390. // currentBasemapId is not specified from the start because the basemap hasn't been selected yet through the basemapgallery
  391. basemapId = initialBasemap.id;
  392. currentTileSchema = initialBasemap.tileSchema;
  393.  
  394. initTopics();
  395. initListeners();
  396.  
  397. ui
  398. .init(basemapId, currentTileSchema)
  399. .updateToggleLabel();
  400. }
  401. };
  402. });