Reusable Accessible Mapping Platform

API Docs for: 5.0.0
Show:

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

  1. /* global define, i18n, jQuery, console, $, document, RAMP */
  2.  
  3. /**
  4. * BookmarkLink submodule
  5. *
  6. * Keeps track of the current state of the map and updates the GetLinkPanel's textbook accordingly. On page load, if any
  7. * parameters are detected in the URL, this module will attempt to recreate the map according to the parameters. Internally,
  8. * this module subscribes to all events that change the state of the map (e.g. extent-change, layers toggled on/off). It contains
  9. * a dictionary that map event names to an object that contains the minimum information needed to reconstruct the map for that particular
  10. * event. For example, if an extent change occurred, this module will add a key "map/extent-change" (or update if the entry already exists)
  11. * and put an object that contains the minimum information needed to reconstruct the map to that extent (in this case it would be
  12. * xmin, ymin, xmax, ymax. Spatial reference is not needed since the map always has the same spatial reference).
  13. *
  14. * @module RAMP
  15. * @submodule BookmarkLink
  16. * @main BookmarkLink
  17. */
  18.  
  19. /**
  20. * BookmarkLink class.
  21. *
  22. *
  23. * ### Steps for making the bookmark link update with an event
  24. *
  25. * 1. Subscribe to the event of interest (e.g. map extent-change)
  26. * 2. Create an object containing fields that will contain the necessary
  27. * information for reconstructing the map to the same state. (e.g. for
  28. * map extent-change, a useful object might be one that represents the
  29. * current extent of the map: `{ xmin : 123, xmax : 456, ymin : 789, ymax : 000}).`
  30. *
  31. * __IMPORTANT__: the object must be serializable since it will be added to the URL and
  32. * should serialize to a reasonable length String. If the fields contain
  33. * non-primitives, e.g. array, object, one must manually serialize the field first.
  34. * Also only use anonymous objects with custom fields, do not use class objects
  35. * (e.g. use an anonymous { } object to store map extent instead of ESRI's map
  36. * {{#crossLink "Esri/geometry/Extent"}}{{/crossLink}} object, since it will contain other fields and methods that will also
  37. * be serialized).
  38. *
  39. * 3. Call updateURL, passing it a name (e.g. "newExtent") and the object
  40. * (a name is required for efficiency, this way the URL will only need to
  41. * serialize and update the given object instead of all objects).
  42. *
  43. * @class BookmarkLink
  44. * @static
  45. * @uses dojo/_base/declare
  46. * @uses require
  47. * @uses dojo/dom-construct
  48. * @uses dojo/io-query
  49. * @uses dojo/_base/lang
  50. * @uses dojo/dom
  51. * @uses dojo/_base/array
  52. * @uses dojo/topic
  53. * @uses dijit/form/TextBox
  54. * @uses dijit/TitlePane
  55. * @uses esri/geometry/Extent
  56. * @uses GlobalStorage
  57. * @uses Map
  58. * @uses EventManager
  59. * @uses Url
  60. * @uses Util
  61. * @uses Dictionary
  62. * @uses PopupManager
  63. */
  64.  
  65. define([
  66. // Dojo
  67. "dojo/_base/declare", "require", "dojo/dom-construct", "dojo/io-query", "dojo/_base/lang",
  68. "dojo/dom", "dojo/_base/array", "dojo/topic", "dijit/form/TextBox", "dijit/TitlePane",
  69. // Esri
  70. "esri/geometry/Extent",
  71. // Ramp
  72. "ramp/globalStorage", "ramp/map", "ramp/eventManager", "ramp/ramp",
  73. // Util
  74. "utils/url", "utils/util", "utils/dictionary", "utils/array", "utils/popupManager"
  75. ],
  76.  
  77. function (
  78. // Dojo
  79. declare, dojoRequire, dojoDomConstruct, dojoQuery, dojoLang,
  80. dojoDom, dojoArray, topic, TextBox, TitlePane,
  81. // Esri
  82. Extent,
  83. // Ramp
  84. GlobalStorage, RampMap, EventManager, Ramp,
  85. // Util
  86. Url, UtilMisc, UtilDict, UtilArray, PopupManager) {
  87. "use strict";
  88.  
  89. // Using constants so we can have intellisense and not make silly typos
  90. var EVENT_EXTENT_CHANGE = "extentChange",
  91. EVENT_FULLSCREEN = "fullscreen",
  92. EVENT_PANEL_CHANGE = "panelChange",
  93. EVENT_TAB_CHANGE = "selectedTab",
  94. EVENT_BASEMAP_CHANGED = "basemapChange",
  95. PARAM = {
  96. FILTER: {
  97. VISIBLE_LAYERS: "visibleLayers",
  98. HIDDEN_LAYERS: "hiddenLayers",
  99. VISIBLE_BOXES: "visibleBoxes",
  100. HIDDEN_BOXES: "hiddenBoxes"
  101. }
  102. },
  103.  
  104. // defines any standard url param keys we are expecting
  105. URL_KEYS = {
  106. PANEL_VISIBLE: "pv",
  107. FULL_SCREEN: "fs",
  108. XMIN: "xmin",
  109. YMIN: "ymin",
  110. XMAX: "xmax",
  111. YMAX: "ymax",
  112. SPATIAL_REF: "sr",
  113. BASEMAP: "bm",
  114. LAYER_TRANSPARENCY: "lt",
  115. SELECT_TAB: "st",
  116. VISIBLE_LAYERS: "vl",
  117. HIDDEN_LAYERS: "hl",
  118. VISIBLE_BOXES: "vb",
  119. HIDDEN_BOXES: "hb"
  120. },
  121.  
  122. HREF_MAILTO_TEMPLATE = "mailto:?subject={0}&body={1}%0D%0A%0D%0A{2}",
  123.  
  124. config,
  125.  
  126. queryObject,
  127.  
  128. linkPaneTextbox,
  129.  
  130. getlinkPopup,
  131.  
  132. getlinkToggle,
  133. getlinkSectionContainer,
  134. getlinkSection,
  135.  
  136. getlinkShortenButton,
  137. getlinkShortenButtonLabel,
  138. getlinkEmailButton,
  139.  
  140. getlinkloadinganimation,
  141.  
  142. cssButtonPressedClass = "button-pressed",
  143.  
  144. isShortLinkMode = false,
  145.  
  146. baseUrl,
  147.  
  148. /**
  149. * A dictionary mapping names (String) to query parameter (String) of the URL. The query parameter is
  150. * what ends up in the url. The key can be any arbitrary name (best to name them to describe the query
  151. * parameter).
  152. *
  153. * @private
  154. * @property parameters
  155. * @type {Object}
  156. */
  157. parameters = {},
  158.  
  159. /**
  160. * A dictionary mapping names (String) to anchors (String) used at the end of the URL.
  161. *
  162. * @private
  163. * @property anchors
  164. * @type {Object}
  165. */
  166. anchors = {},
  167.  
  168. /**
  169. * A dictionary containing layer id (String) as key and layer visibility (boolean) as value
  170. *
  171. * @private
  172. * @property layerVisibility
  173. * @type {Object}
  174. */
  175. layerVisibility = {},
  176.  
  177. /**
  178. * A dictionary containing layer id (String) as key and bounding box visibility (boolean) as value
  179. *
  180. * @private
  181. * @property boundingBoxVisibility
  182. * @type {Object}
  183. */
  184. boundingBoxVisibility = {},
  185.  
  186. /**
  187. * A dictionary with the layer id as key, and the transparency as value.
  188. *
  189. * @private
  190. * @property layerTransparency
  191. * @type {Object}
  192. */
  193. layerTransparency = {},
  194.  
  195. ui = {
  196. /**
  197. * Initiates additional UI components of the widget, setting listeners and other stuff.
  198. *
  199. * @method init
  200. * @private
  201. * @constructor
  202. */
  203. init: function () {
  204. getlinkloadinganimation = $("#getlink-section .loadingAnimation");
  205.  
  206. getlinkEmailButton = $(".getlink-email-button");
  207.  
  208. getlinkToggle = $("#getlink-toggle");
  209. getlinkSectionContainer = $("#getlink-section-container");
  210. getlinkSection = $("#getlink-section");
  211.  
  212. // select link when user focuses on the textbox http://stackoverflow.com/a/22102081
  213. linkPaneTextbox =
  214. $("#getlink-input")
  215. .on('focus', function () {
  216. var $this = $(this)
  217. .one('mouseup.mouseupSelect', function () {
  218. $this.select();
  219. return false;
  220. })
  221. .one('mousedown', function () {
  222. // compensate for untriggered 'mouseup' caused by focus via tab
  223. $this.off('mouseup.mouseupSelect');
  224. })
  225. .select();
  226. });
  227.  
  228. // toggle short/long link mode on click
  229. getlinkShortenButton =
  230. $(".getlink-shorten-button")
  231. .on("click", toggleShortLinkMode);
  232.  
  233. getlinkShortenButtonLabel = getlinkShortenButton.find("span.on-right");
  234.  
  235. getlinkPopup = PopupManager.registerPopup(getlinkToggle, "click",
  236. function (d) {
  237. topic.publish(EventManager.BookmarkLink.GETLINK_PANEL_CHANGED, { visible: true });
  238. topic.publish(EventManager.GUI.TOOLBAR_SECTION_OPEN, { id: "get-link-section" });
  239. console.log(EventManager.BookmarkLink.GETLINK_PANEL_CHANGED + " visible:", true);
  240.  
  241. // close this panel if any other panel is opened
  242. UtilMisc.subscribeOnce(EventManager.GUI.TOOLBAR_SECTION_OPEN, dojoLang.hitch(this,
  243. function () {
  244. if (this.isOpen()) {
  245. this.close();
  246. }
  247. })
  248. );
  249.  
  250. getlinkSectionContainer.slideDown("fast", function () {
  251. d.resolve();
  252. });
  253. },
  254. {
  255. activeClass: cssButtonPressedClass,
  256. target: getlinkSectionContainer,
  257. closeHandler: function (d) {
  258. topic.publish(EventManager.BookmarkLink.GETLINK_PANEL_CHANGED, { visible: false });
  259. topic.publish(EventManager.GUI.TOOLBAR_SECTION_CLOSE, { id: "get-link-section" });
  260. console.log(EventManager.BookmarkLink.GETLINK_PANEL_CHANGED + " visible:", false);
  261.  
  262. getlinkSectionContainer.slideUp("fast", function () {
  263. toggleShortLinkMode(false);
  264. d.resolve();
  265. });
  266. },
  267. resetFocusOnClose: true
  268. }
  269. );
  270.  
  271. /*topic.subscribe(EventManager.GUI.HELP_PANEL_CHANGE, function (attr) {
  272. if (getlinkPopup.isOpen() && attr.visible) {
  273. getlinkPopup.close();
  274. }
  275. });*/
  276. }
  277. };
  278.  
  279. /**
  280. * Update the parameter dictionary with the new values for the parameter. If paramObj is set to null,
  281. * essentially removes the given paramKey from the URL.
  282. *
  283. * @method addParameter
  284. * @private
  285. * @param {String} paramKey the parameter (e.g. extent) that was changed
  286. * @param {Object} paramObj an object representing data that can be serialized into the query parameter
  287. * of the URL (can be null, in which case the parameter will NOT be included in the URL)
  288. *
  289. */
  290. function addParameter(paramKey, paramObj) {
  291. if (paramObj === null) {
  292. parameters[paramKey] = null;
  293. } else {
  294. parameters[paramKey] = dojoQuery.objectToQuery(paramObj);
  295. }
  296. }
  297.  
  298. /*
  299. * Adds an anchor object to a tracking array
  300. * @method addAnchor
  301. * @param {String} anchorName a key value for the anchor
  302. * @param {Object} anchorObj an anchor object
  303. */
  304. function addAnchor(anchorName, anchorObj) {
  305. anchors[anchorName] = anchorObj;
  306. }
  307.  
  308. /*
  309. * Appends information to the current map's URL to create a mail-to link. Set the email buttons target URL.
  310. * @method setNewUrl
  311. * @param {String} url the base URL that defines the current state of the map
  312. */
  313. function setNewUrl(url) {
  314. var mailToHref = String.format(HREF_MAILTO_TEMPLATE,
  315. i18n.t("bookmarkLink.emailUrlSubject"),
  316. i18n.t("bookmarkLink.emailUrlBody"),
  317. encodeURIComponent(url));
  318.  
  319. linkPaneTextbox.val(url);
  320. getlinkEmailButton.attr("href", mailToHref);
  321. }
  322.  
  323. /**
  324. * Updates the link displayed in the textbox. This function should be called whenever
  325. * one of the objects that are in the URL query is modified.
  326. *
  327. * @method updateURL
  328. * @private
  329. */
  330. function updateURL() {
  331. var link = baseUrl,
  332. delim = "?";
  333.  
  334. /* Appends all the query parameters to the link (a query parameter can be
  335. * excluded by setting it to null) */
  336. UtilDict.forEachEntry(parameters, function (key, value) {
  337. if (value) { // Value cannot be null or the empty String
  338. link += delim + value;
  339. if (delim === "?") {
  340. //first param has a question mark in front of it. all others have an &
  341. delim = "&";
  342. }
  343. }
  344. });
  345.  
  346. // Need to add an extra "&" to the query if we have an anchor, otherwise
  347. // the last query will contain the anchors
  348. if (!UtilDict.isEmpty(anchors)) {
  349. link += "&";
  350. }
  351.  
  352. // Anchors have to be at the end
  353. UtilDict.forEachEntry(anchors, function (key, value) {
  354. link += "#" + value;
  355. });
  356.  
  357. if (isShortLinkMode) {
  358. if (linkPaneTextbox.is(":visible")) {
  359. getlinkloadinganimation.show();
  360.  
  361. jQuery.urlShortener({
  362. longUrl: link,
  363. success: function (shortUrl) {
  364. setNewUrl(shortUrl);
  365. getlinkloadinganimation.hide();
  366. },
  367. error: function (err) {
  368. console.error(JSON.stringify(err));
  369. getlinkloadinganimation.hide();
  370. }
  371. });
  372. }
  373. } else {
  374. setNewUrl(link);
  375. }
  376.  
  377. //update lang href with current bookmark url
  378. updateLangHref(link);
  379.  
  380. //trigger event indicating bookmark is complete. pass bookmark as arg
  381. topic.publish(EventManager.BookmarkLink.BOOKMARK_GENERATED, {
  382. link: link
  383. });
  384. }
  385.  
  386. /**
  387. * update Href on language button based on link provided from bookmarklink
  388. *
  389. * @method updateHref
  390. * @param {String} link bookmark url
  391. * @private
  392. */
  393. function updateLangHref(link) {
  394. var pos = link.indexOf('?');
  395. if (pos <= 0) {
  396. return;
  397. }
  398.  
  399. var paras = link.split('?')[1],
  400. element = $("#wb-lng").find("li a"),
  401. url = element.attr("href");
  402.  
  403. url = url.split('?')[0] + "?" + paras;
  404.  
  405. element.attr("href", url);
  406. }
  407.  
  408. /**
  409. * If a co-ordinate has a big value before the decimal point, drop the precision after the decimal
  410. *
  411. * @method slimCoord
  412. * @param {Object} value co-ordinate to potentially slim down
  413. * @private
  414. */
  415. function slimCoord(value) {
  416. var sVal = value.toString(),
  417. decIndex = sVal.indexOf("."),
  418. cutSize;
  419.  
  420. if (sVal.substring(0, 1) === "-") {
  421. cutSize = 7;
  422. } else {
  423. cutSize = 6;
  424. }
  425.  
  426. if (decIndex < cutSize) {
  427. //number has 5 or less numbers before the decimal, or no decimal point. return input as is
  428. return sVal;
  429. } else {
  430. //trim that decimal!
  431. return sVal.substring(0, decIndex);
  432. }
  433. }
  434.  
  435. /**
  436. * Toggle the short/long link mode and change the label accordingly
  437. *
  438. * @method toggleShortLinkMode
  439. * @param {Object} value true - shortLinkMode; false - !shortlinkMore; undefined - toggle;
  440. * @private
  441. */
  442. function toggleShortLinkMode(value) {
  443. var label;
  444.  
  445. isShortLinkMode = value === true ? true : (value === false ? false : !isShortLinkMode);
  446. label = isShortLinkMode ? i18n.t("bookmarkLink.longLink") : i18n.t("bookmarkLink.shortLink");
  447. getlinkShortenButtonLabel.text(label);
  448. updateURL();
  449. }
  450.  
  451. /**
  452. * Process the URL. If there are any parameters that came from a short-link, apply them to the config or the RAMP application
  453. *
  454. * @method updateConfig
  455. * @param {String} homePage the page name of the ramp site (e.g. index.html, map.html)
  456. * @private
  457. */
  458. function updateConfig(homePage) {
  459. var event,
  460. urlObj = new Url(dojoRequire.toUrl(document.location));
  461.  
  462. config = RAMP.config;
  463. baseUrl = urlObj.uri;
  464. queryObject = dojoQuery.queryToObject(urlObj.query);
  465.  
  466. //adds homePage (e.g. default.aspx or rampmap.aspx) if not present;
  467. //used in getlink or else the link would be invalid.
  468. if (baseUrl.indexOf(homePage) === -1) {
  469. baseUrl += homePage;
  470. }
  471.  
  472. // Move the API key to config.json??
  473. jQuery.urlShortener.settings.apiKey = 'AIzaSyB52ByjsXrOYlXxc2Q9GVpClLDwt0Lw6pc';
  474.  
  475. // Toggle the main panel
  476. if (queryObject[URL_KEYS.PANEL_VISIBLE]) {
  477. event = {
  478. pv: UtilMisc.parseBool(queryObject[URL_KEYS.PANEL_VISIBLE])
  479. };
  480.  
  481. addParameter(EVENT_PANEL_CHANGE, event);
  482. RAMP.state.ui.sidePanelOpened = event.pv;
  483. }
  484.  
  485. // Toggle fullscreen mode
  486. if (queryObject[URL_KEYS.FULL_SCREEN]) {
  487. event = {
  488. fs: UtilMisc.parseBool(queryObject[URL_KEYS.FULL_SCREEN])
  489. };
  490. addParameter(EVENT_FULLSCREEN, event);
  491. RAMP.state.ui.fullscreen = event.fs;
  492. }
  493.  
  494. // Check for map extent queries
  495.  
  496. if (queryObject[URL_KEYS.XMIN]) {
  497. event = {
  498. xmin: parseFloat(queryObject[URL_KEYS.XMIN].replace(/,/g, "")),
  499. ymin: parseFloat(queryObject[URL_KEYS.YMIN].replace(/,/g, "")),
  500. xmax: parseFloat(queryObject[URL_KEYS.XMAX].replace(/,/g, "")),
  501. ymax: parseFloat(queryObject[URL_KEYS.YMAX].replace(/,/g, "")),
  502. sr: queryObject[URL_KEYS.SPATIAL_REF]
  503. };
  504.  
  505. addParameter(EVENT_EXTENT_CHANGE, event);
  506.  
  507. //we call the spatial refernce "sr" in the url to save on characters. However, the internal config object uses the ESRI standard
  508. //name of "spatialReference" (so we can serialize to a valid esri SR object)
  509. var configExtent = {
  510. xmin: event.xmin,
  511. ymin: event.ymin,
  512. xmax: event.xmax,
  513. ymax: event.ymax,
  514. spatialReference: JSON.parse(event.sr)
  515. };
  516.  
  517. config.extents.defaultExtent = configExtent;
  518.  
  519. // Wait for things such as fullscreen or panel collapse
  520. // to finish before doing an extent change.
  521. }
  522.  
  523. // Select the correct basemap
  524. if (queryObject[URL_KEYS.BASEMAP]) {
  525. event = {
  526. bm: queryObject[URL_KEYS.BASEMAP]
  527. };
  528. addParameter(EVENT_BASEMAP_CHANGED, event);
  529.  
  530. config.initialBasemapIndex = parseInt(queryObject[URL_KEYS.BASEMAP]);
  531. }
  532.  
  533. // Modify the layer transparency
  534. if (queryObject[URL_KEYS.LAYER_TRANSPARENCY]) {
  535. addParameter(EventManager.FilterManager.LAYER_TRANSPARENCY_CHANGED, {
  536. lt: queryObject[URL_KEYS.LAYER_TRANSPARENCY]
  537. });
  538.  
  539. UtilDict.forEachEntry(JSON.parse(queryObject[URL_KEYS.LAYER_TRANSPARENCY]), function (key, value) {
  540. var layerConfig = UtilArray.find(config.layers.feature.concat(config.layers.wms), function (layer) {
  541. return layer.id === key;
  542. });
  543. layerConfig.settings.opacity.default = value;
  544. });
  545. }
  546.  
  547. // check for selected tab queries
  548. if (queryObject[URL_KEYS.SELECT_TAB]) {
  549. // Just add the parameter, no need to do anything else
  550. // since we're using anchor tags
  551. addParameter(EVENT_TAB_CHANGE, {
  552. index: queryObject[URL_KEYS.SELECT_TAB]
  553. });
  554. }
  555.  
  556. var layerIds;
  557.  
  558. if (queryObject[URL_KEYS.VISIBLE_LAYERS]) {
  559. layerIds = queryObject[URL_KEYS.VISIBLE_LAYERS].split("+");
  560.  
  561. layerIds.forEach(function (layerId) {
  562. var layerConfig = Ramp.getLayerConfigWithId(layerId);
  563. // make sure not null
  564. if (layerConfig !== null) {
  565. layerConfig.settings.visible = true;
  566.  
  567. layerVisibility[layerId] = true;
  568. }
  569. });
  570.  
  571. addParameter(PARAM.FILTER.VISIBLE_LAYERS, {
  572. vl: queryObject[URL_KEYS.VISIBLE_LAYERS]
  573. });
  574. }
  575.  
  576. if (queryObject[URL_KEYS.HIDDEN_LAYERS]) {
  577. layerIds = queryObject[URL_KEYS.HIDDEN_LAYERS].split("+");
  578.  
  579. layerIds.forEach(function (layerId) {
  580. var layerConfig = Ramp.getLayerConfigWithId(layerId);
  581.  
  582. if (layerConfig !== null) {
  583. layerConfig.settings.visible = false;
  584.  
  585. layerVisibility[layerId] = false;
  586. }
  587. });
  588.  
  589. addParameter(PARAM.FILTER.HIDDEN_LAYERS, {
  590. hl: queryObject[URL_KEYS.HIDDEN_LAYERS]
  591. });
  592. }
  593.  
  594. if (queryObject[URL_KEYS.VISIBLE_BOXES]) {
  595. layerIds = queryObject[URL_KEYS.VISIBLE_BOXES].split("+");
  596.  
  597. layerIds.forEach(function (layerId) {
  598. var layerConfig = Ramp.getLayerConfigWithId(layerId);
  599. if (layerConfig !== null) {
  600. layerConfig.settings.boundingBoxVisible = true;
  601. boundingBoxVisibility[layerId] = true;
  602. }
  603. });
  604.  
  605. addParameter(PARAM.FILTER.VISIBLE_BOXES, {
  606. vb: queryObject[URL_KEYS.VISIBLE_BOXES]
  607. });
  608. }
  609.  
  610. if (queryObject[URL_KEYS.HIDDEN_BOXES]) {
  611. layerIds = queryObject[URL_KEYS.HIDDEN_BOXES].split("+");
  612.  
  613. layerIds.forEach(function (layerId) {
  614. var layerConfig = Ramp.getLayerConfigWithId(layerId);
  615.  
  616. if (layerConfig !== null) {
  617. layerConfig.settings.boundingBoxVisible = false;
  618. boundingBoxVisibility[layerId] = false;
  619. }
  620. });
  621.  
  622. addParameter(PARAM.FILTER.HIDDEN_BOXES, {
  623. hb: queryObject[URL_KEYS.HIDDEN_BOXES]
  624. });
  625. }
  626.  
  627. //check for any additional unknown parameters that were on the url. add them to our URL
  628. var paramName, paramObj, knownNames = [];
  629. //get list of known keys in a searchable list
  630. for (paramName in URL_KEYS) {
  631. if (URL_KEYS.hasOwnProperty(paramName)) {
  632. knownNames.push(URL_KEYS[paramName]);
  633. }
  634. }
  635. for (paramName in queryObject) {
  636. if (queryObject.hasOwnProperty(paramName)) {
  637. if (knownNames.indexOf(paramName) === -1) {
  638. //found a param that is not known. add it to the collection
  639. paramObj = {};
  640. paramObj[paramName] = queryObject[paramName];
  641. addParameter(paramName, paramObj);
  642. }
  643. }
  644. }
  645. }
  646.  
  647. /**
  648. * Figures out if a layer is valid to be in the bookmark
  649. *
  650. * @method isBookmarkLayer
  651. * @param {String} layerId layers id to check
  652. * @private
  653. * @returns {Boolean} true if layer should be included in the bookmark
  654. */
  655. function isBookmarkLayer(layerId) {
  656. var layer = RampMap.getMap().getLayer(layerId);
  657.  
  658. if (UtilMisc.isUndefined(layer)) {
  659. return false;
  660. } else if (layer.ramp.user) {
  661. //we do not store user-added layers in the bookmark link (as they will not be reloaded).
  662. return false;
  663. } else {
  664. return true;
  665. }
  666. }
  667.  
  668. return {
  669. /**
  670. * Instantiates a BookmarkLink. Subscribes to all the events that causes
  671. * the bookmark link to update (e.g. map extent change or feature layer visibility
  672. * change).
  673. *
  674. * @method init
  675. * {String} homePage a string denoting the name of the homePage (e.g. usually "Default.aspx" or "index.html")
  676. */
  677. createUI: function () {
  678. ui.init();
  679. },
  680.  
  681. updateConfig: updateConfig,
  682.  
  683. /**
  684. * Subscribe to map state changes so the URL displayed can be changed accordingly.
  685. * SUBSCRIBES TO:
  686. * map "extent-change"
  687. * Updates URL when map extent changes
  688. *
  689. * EventManager.GUI.FULLSCREEN_CHANGE
  690. * Updates URL when map goes into fullscreen mode
  691. *
  692. * EventManager.GUI.TAB_SELECTED
  693. * Updates URL when tabs are selected
  694. *
  695. * EventManager.GUI.PANEL_CHANGE
  696. * Updates URL when panel opens/closes
  697. *
  698. * EventManager.BasemapSelector.BASEMAP_CHANGED
  699. * Updates URL when basemap changed
  700. *
  701. * * ================================================================
  702. * Subscribe to updates
  703. * ================================================================
  704. * To include more information into the query string, do not get the information
  705. * directly from the object/module of interest, but rather make it publish an
  706. * event with data to include and subscribe to this event here.
  707. * @method subscribeAndUpdate
  708. * @private
  709. */
  710. subscribeAndUpdate: function () {
  711. topic.subscribe(EventManager.Map.EXTENT_CHANGE, function (event) {
  712. // Event fields: extent, delta, levelChange, lod;
  713. addParameter(EVENT_EXTENT_CHANGE, {
  714. xmin: slimCoord(event.extent.xmin),
  715. ymin: slimCoord(event.extent.ymin),
  716. xmax: slimCoord(event.extent.xmax),
  717. ymax: slimCoord(event.extent.ymax),
  718. sr: JSON.stringify(event.extent.spatialReference)
  719. });
  720. updateURL();
  721. });
  722.  
  723. topic.subscribe(EventManager.GUI.FULLSCREEN_CHANGE, function (event) {
  724. addParameter(EVENT_FULLSCREEN, {
  725. fs: event.visible
  726. });
  727. updateURL();
  728. });
  729.  
  730. topic.subscribe(EventManager.GUI.TAB_SELECTED, function (event) {
  731. // Need to remove the "-link" part for the id to work
  732. // since when the page first loads, if the tab is deselected,
  733. // it's id will be missing the "-link" at the end.
  734. addAnchor(EVENT_TAB_CHANGE, event.id.replace("-link", ""));
  735. updateURL();
  736. });
  737.  
  738. topic.subscribe(EventManager.GUI.PANEL_CHANGE, function (event) {
  739. addParameter(EVENT_PANEL_CHANGE, {
  740. pv: event.visible
  741. });
  742. updateURL();
  743. });
  744.  
  745. topic.subscribe(EventManager.BasemapSelector.BASEMAP_CHANGED, function (event) {
  746. //lookup index from config. don't store id
  747.  
  748. addParameter(EVENT_BASEMAP_CHANGED, {
  749. bm: RAMP.basemapIndex[event.id]
  750. });
  751. updateURL();
  752. });
  753.  
  754. topic.subscribe(EventManager.FilterManager.LAYER_VISIBILITY_TOGGLED, function (event) {
  755. var layerId = event.id;
  756.  
  757. if (!isBookmarkLayer(layerId)) {
  758. //we do not store user-added layers in the bookmark link (as they will not be reloaded).
  759. return;
  760. }
  761.  
  762. layerVisibility[layerId] = event.state;
  763.  
  764. // Only keep attributes that are different from the default config
  765. var visibleLayers = UtilDict.filter(layerVisibility, function (key, layerVisible) {
  766. return layerVisible && !Ramp.getLayerConfigWithId(key).settings.visible;
  767. }),
  768. hiddenLayers = UtilDict.filter(layerVisibility, function (key, boxVisible) {
  769. return !boxVisible && Ramp.getLayerConfigWithId(key).settings.visible;
  770. });
  771.  
  772. addParameter(PARAM.FILTER.HIDDEN_LAYERS, UtilDict.isEmpty(hiddenLayers) ? null : {
  773. // Convert an array of string into a "+" delimited string
  774. hl: Object.keys(hiddenLayers).join("+")
  775. });
  776.  
  777. addParameter(PARAM.FILTER.VISIBLE_LAYERS, UtilDict.isEmpty(visibleLayers) ? null : {
  778. // Convert an array of string into a "+" delimited string
  779. vl: Object.keys(visibleLayers).join("+")
  780. });
  781.  
  782. updateURL();
  783. });
  784.  
  785. topic.subscribe(EventManager.FilterManager.BOX_VISIBILITY_TOGGLED, function (event) {
  786. var layerId = event.id;
  787.  
  788. if (!isBookmarkLayer(layerId)) {
  789. //we do not store user-added layers in the bookmark link (as they will not be reloaded).
  790. return;
  791. }
  792.  
  793. boundingBoxVisibility[layerId] = event.state;
  794.  
  795. // Only keep attributes that are different from the default config
  796. var visibleBoxes = UtilDict.filter(boundingBoxVisibility, function (key, boxVisible) {
  797. return boxVisible && !Ramp.getLayerConfigWithId(key).settings.boundingBoxVisible;
  798. }),
  799. hiddenBoxes = UtilDict.filter(boundingBoxVisibility, function (key, boxVisible) {
  800. return !boxVisible && Ramp.getLayerConfigWithId(key).settings.boundingBoxVisible;
  801. });
  802.  
  803. addParameter(PARAM.FILTER.HIDDEN_BOXES, UtilDict.isEmpty(hiddenBoxes) ? null : {
  804. // Convert an array of string into a "+" delimited string
  805. hb: Object.keys(hiddenBoxes).join("+")
  806. });
  807.  
  808. addParameter(PARAM.FILTER.VISIBLE_BOXES, UtilDict.isEmpty(visibleBoxes) ? null : {
  809. // Convert an array of string into a "+" delimited string
  810. vb: Object.keys(visibleBoxes).join("+")
  811. });
  812.  
  813. updateURL();
  814. });
  815.  
  816. topic.subscribe(EventManager.FilterManager.LAYER_TRANSPARENCY_CHANGED, function (event) {
  817. if (!isBookmarkLayer(event.layerId)) {
  818. //we do not store user-added layers in the bookmark link (as they will not be reloaded).
  819. return;
  820. }
  821.  
  822. addParameter(EventManager.FilterManager.LAYER_TRANSPARENCY_CHANGED, event);
  823. layerTransparency[event.layerId] = Math.round(event.value * 100) / 100;
  824.  
  825. addParameter(EventManager.FilterManager.LAYER_TRANSPARENCY_CHANGED, {
  826. lt: JSON.stringify(layerTransparency)
  827. });
  828.  
  829. updateURL();
  830. });
  831.  
  832. // This call is necessary to fill in the URL in the bookmark link
  833. // if this call is removed, there will be no URL in the bookmark link
  834. // until one of the above events fires
  835. updateURL();
  836. }
  837. };
  838. });