InfoWindow.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /**
  2. * Class: SuperMap.InfoWindow
  3. * 信息弹窗类。在地图上可以打开或关闭,通常情况下点击一个 icon 打开弹窗,
  4. * 弹窗直接加载到map上,不需要创建图层,可用 SuperMap.Map.addPopup 方法在地图上添加使用。
  5. */
  6. SuperMap.InfoWindow = SuperMap.Class({
  7. /**
  8. * Property: events
  9. * {<SuperMap.Events>} 自定义事件经理
  10. */
  11. events: null,
  12. /*
  13. * APIProperty: title
  14. * {String} 弹窗的标题名
  15. * */
  16. title: '详细信息',
  17. /*
  18. * APIProperty: titleBox
  19. * {Boolean} 在弹出的窗口中是否显示标题栏
  20. * */
  21. titleBox: true,
  22. /** Property: id
  23. * {String} 弹窗的ID号
  24. */
  25. id: "",
  26. /**
  27. * APIProperty: lonLat
  28. * {<SuperMap.LonLat>} 弹窗在地图上的地理坐标
  29. */
  30. lonLat: null,
  31. /**
  32. * Property: div
  33. * {DOMElement} 弹窗容器div
  34. */
  35. div: null,
  36. /**
  37. * APIProperty: size
  38. * {<SuperMap.Size>} 弹窗div的宽与高
  39. */
  40. size: null,
  41. /**
  42. * APIProperty: contentSize
  43. * {<SuperMap.Size>} 弹窗内容div的宽与高
  44. */
  45. contentSize: null,
  46. /**
  47. * Property: default_width
  48. * {<SuperMap.Size>} 弹窗内容div的默认宽度
  49. */
  50. default_width: 300,
  51. /**
  52. * Property: default_height
  53. * {<SuperMap.Size>} 弹窗内容div的默认高度
  54. */
  55. default_height: 200,
  56. /**
  57. * APIProperty: default_backgroundColor
  58. * {String} 弹窗的默认背景颜色
  59. */
  60. default_backgroundColor: 'white',
  61. /**
  62. * APIProperty: default_opacity
  63. * {float} 弹窗的默认透明度 (between 0.0 and 1.0)
  64. */
  65. default_opacity: 1,
  66. /**
  67. * APIProperty: default_border
  68. * {String} 弹窗的默认边框大小 (eg 2px)
  69. */
  70. default_border: 0,
  71. /**
  72. * APIProperty: contentHTML
  73. * {String} 弹窗内容
  74. */
  75. contentHTML: null,
  76. /**
  77. * APIProperty: closeOnMove
  78. * {Boolean} 当地图平移时,关闭弹窗。
  79. * 默认为false。
  80. */
  81. closeOnMove: false,
  82. /**
  83. * Property: closeBox
  84. * {Boolean} 在弹出窗口的里面是否显示关闭窗。
  85. */
  86. closeBox: true,
  87. /**
  88. * Property: closeBoxCallback
  89. * {Function} 关闭弹窗触发该回调函数。
  90. */
  91. closeBoxCallback: null,
  92. /*
  93. * APIProperty:content
  94. * {Object} 用户自定义信息
  95. * */
  96. content: null,
  97. /*
  98. * APIProperty:content
  99. * {SuperMap.Feature} 要素信息
  100. * */
  101. feature: null,
  102. /*
  103. * APIProperty:content
  104. * {Object} 弹窗偏移量
  105. * */
  106. offset: {x:0,y:0},
  107. /**
  108. * Constructor: SuperMap.InfoWindow
  109. * 创建弹窗。
  110. * 例如:
  111. * (code)
  112. * var InfoWindow = new SuperMap.InfoWindow("chicken",
  113. * new SuperMap.Size(200,200),
  114. * "元素信息",
  115. * "example InfoWindow",
  116. * true);
  117. * InfoWindow.closeOnMove = true;
  118. * InfoWindow.setLonLat(new SuperMap.LonLat(5,40),);
  119. * map.addPopup(InfoWindow);
  120. * (end)
  121. *
  122. * Parameters:
  123. * id - {String} 弹窗的唯一标识,如设为null,则将会自动生成。
  124. * title - {String} 弹窗标题名
  125. * contentHTML - {String} 弹窗中显示的一个HTML要素的字符串。
  126. */
  127. initialize: function (id, title, options) {
  128. if (id == null) {
  129. id = SuperMap.Util.createUniqueID(this.CLASS_NAME + "_");
  130. }
  131. this.id = id;
  132. this.title = (title != null) ? title : this.title;
  133. this.contentSize = options && options.contentSize;
  134. if(!this.contentSize) {
  135. this.contentSize = new SuperMap.Size(this.default_width, this.default_height);
  136. }
  137. if(options && options.size) {
  138. this.size= options.size
  139. }else {
  140. this.size = new SuperMap.Size(this.default_width, this.default_height+37);
  141. }
  142. if(options && options.map) {
  143. this.map = options.map;
  144. }
  145. if(options && options.titleBox) {
  146. this.titleBox = options.titleBox;
  147. }
  148. if(options && options.closeBox) {
  149. this.closeBox = options.closeBox;
  150. }
  151. this.render();
  152. },
  153. /**
  154. * APIMethod: render
  155. * 渲染弹窗
  156. */
  157. render: function () {
  158. this.div = SuperMap.Util.createDiv(this.id, null, null,
  159. null, null, null, "visible");
  160. this.div.className = "pop-container";
  161. //标题栏
  162. this.header = SuperMap.Util.createDiv(this.id + "_header", null, null,
  163. null, null, null, "visible");
  164. if (this.titleBox) {
  165. this.header.className = "pop-header";
  166. this.header.innerHTML = "<label class='pop-titlename'>" + this.title + "</label>";
  167. }
  168. else {
  169. this.header.className = "theme-pop-header";
  170. }
  171. //弹窗内容
  172. this.contentDiv = SuperMap.Util.createDiv(this.id + "_contentDiv", null, null,
  173. null, null, null, "auto");
  174. //this.contentDiv.className = "nano pop-content";
  175. this.contentDiv.style.overflowX = "hidden";
  176. this.groupDiv = SuperMap.Util.createDiv(this.id + "_groupDiv", null, null,
  177. null, null);
  178. //this.groupDiv.className = "nano-content";
  179. this.contentDiv.appendChild(this.groupDiv);
  180. //弹窗指向箭头
  181. this.arrow = SuperMap.Util.createDiv(this.id + "_arrow", null, null,
  182. null, null, null, "hidden");
  183. this.arrow.className = "arrow-container";
  184. this.arrow.innerHTML = '<div class="arrow"></div>';
  185. //弹窗关闭按钮
  186. if (this.closeBox) {
  187. this.addCloseBox(this.closeBoxCallback);
  188. }
  189. this.div.appendChild(this.header);
  190. this.div.appendChild(this.contentDiv);
  191. this.div.appendChild(this.arrow);
  192. this.registerEvents();
  193. },
  194. /**
  195. * APIMethod: destroy
  196. * 清除弹窗
  197. */
  198. destroy: function () {
  199. this.id = null;
  200. this.lonLat = null;
  201. this.size = null;
  202. this.contentHTML = null;
  203. this.backgroundColor = null;
  204. this.opacity = null;
  205. this.border = null;
  206. if (this.closeOnMove && this.map) {
  207. this.map.events.unregister("movestart", this, this.hide);
  208. }
  209. if (this.closeDiv) {
  210. SuperMap.Event.stopObservingElement(this.closeDiv);
  211. this.header.removeChild(this.closeDiv);
  212. }
  213. this.closeDiv = null;
  214. if (this.map != null) {
  215. this.map.removePopup(this);
  216. }
  217. this.map = null;
  218. this.div = null;
  219. },
  220. /**
  221. * Method: draw
  222. * 制作弹窗
  223. *
  224. * Parameters:
  225. * px - {<SuperMap.Pixel>} 弹窗的像素坐标
  226. *
  227. * Returns:
  228. * {DOMElement} 返回一个弹窗div
  229. */
  230. draw: function (px) {
  231. if (px == null) {
  232. if ((this.lonLat != null) && (this.map != null)) {
  233. px = this.map.getLayerPxFromLonLat(this.lonLat);
  234. }
  235. }
  236. if (this.closeOnMove) {
  237. this.map.events.register("movestart", this, this.hide);
  238. }
  239. this.setSize(this.contentSize);
  240. this.moveTo(px);
  241. this.setBackgroundColor();
  242. this.setOpacity();
  243. this.setBorder();
  244. this.setLonLat();
  245. return this.div;
  246. },
  247. /**
  248. * Method: updatePosition
  249. * 如果地图改变了弹窗的地理坐标,弹窗需要改变自己的位置
  250. */
  251. updatePosition: function () {
  252. if ((this.lonLat) && (this.map)) {
  253. var px = this.map.getLayerPxFromLonLat(this.lonLat);
  254. if (px) {
  255. this.moveTo(px);
  256. }
  257. }
  258. },
  259. /**
  260. * APIMethod: setLonLat
  261. * 允许用户自定义弹窗的地理坐标
  262. *
  263. * Parameters:
  264. * lonLat - {String} 弹窗的地理坐标
  265. * offset - {Object} 弹窗偏移量
  266. */
  267. setLonLat: function (lonlat,offset) {
  268. if(offset) {
  269. this.offset = offset;
  270. }
  271. if (lonlat) {
  272. this.lonLat = lonlat;
  273. if (this.map) {
  274. this.updatePosition();
  275. }
  276. }
  277. },
  278. /**
  279. * Method: moveTo
  280. *
  281. * Parameters:
  282. * px - {<SuperMap.Pixel>} 弹窗div的 top 与 left 值
  283. */
  284. moveTo: function (px) {
  285. if ((px != null) && (this.div != null)) {
  286. this.div.style.left = px.x - this.size.w * 0.5 - this.offset.x + "px";
  287. this.div.style.top = px.y - this.size.h - 25 - this.offset.y + "px";
  288. }
  289. },
  290. /**
  291. * APIMethod: hide
  292. * 隐藏弹窗
  293. */
  294. hide: function () {
  295. if (this.div) {
  296. this.div.style.display = 'none';
  297. }
  298. },
  299. /**
  300. * APIMethod: visible
  301. *
  302. * Returns:
  303. * {Boolean} 返回弹窗元素的可见性
  304. */
  305. visible: function () {
  306. return SuperMap.Element.visible(this.div);
  307. },
  308. /**
  309. * APIMethod: toggle
  310. * 转换弹窗的显示与隐藏
  311. */
  312. toggle: function () {
  313. if (this.visible()) {
  314. this.hide();
  315. } else {
  316. this.show(this.feature);
  317. }
  318. },
  319. /**
  320. * APIMethod: show
  321. * 显示弹窗
  322. *
  323. * Parameters:
  324. * titleArray 显示弹窗的要素
  325. * content 用户自定义弹窗的内容
  326. */
  327. show: function (titleArray,content) {
  328. this.setContentHTML(titleArray,content);
  329. if (this.div) {
  330. this.div.style.display = 'block';
  331. }
  332. },
  333. /**
  334. * APIMethod: setSize
  335. * 设置弹窗的大小
  336. *
  337. * Parameters:
  338. * contentSize - {<SuperMap.Size>} 弹窗内容的大小
  339. */
  340. setSize: function (contentSize) {
  341. this.size = contentSize.clone();
  342. this.size.h += 37;
  343. if (this.div != null) {
  344. this.div.style.width = this.size.w + "px";
  345. this.div.style.height = this.size.h + "px";
  346. }
  347. if (this.arrow != null) {
  348. this.arrow.style.left = this.size.w * 0.5 - 20 + "px";
  349. }
  350. if (this.contentDiv != null) {
  351. this.contentDiv.style.width = contentSize.w + "px";
  352. this.contentDiv.style.height = contentSize.h + "px";
  353. if (this.titleBox) {
  354. this.contentDiv.style.top = 36 + 'px';
  355. }
  356. else {
  357. this.contentDiv.style.top = 25 + 'px';
  358. }
  359. }
  360. },
  361. /**
  362. * APIMethod: setBackgroundColor
  363. * 设置弹出框的背景颜色.
  364. *
  365. * Parameters:
  366. * color - {String} 背景颜色. 如 "#FFBBBB"
  367. */
  368. setBackgroundColor: function (color) {
  369. if (color != undefined) {
  370. this.default_backgroundColor = color;
  371. }
  372. if (this.div != null) {
  373. this.div.style.backgroundColor = this.default_backgroundColor;
  374. this.arrow.firstChild.style.backgroundColor = this.default_backgroundColor;
  375. }
  376. },
  377. /**
  378. * APIMethod: setOpacity
  379. * 设置弹出框的透明度.
  380. *
  381. * Parameters:
  382. * opacity - {float} 该值在0.0(完全透明)到1.0(不透明)之间.
  383. */
  384. setOpacity: function (opacity) {
  385. if (opacity != undefined) {
  386. this.default_opacity = opacity;
  387. }
  388. if (this.div != null) {
  389. // for Mozilla and Safari
  390. this.div.style.opacity = this.default_opacity;
  391. // for IE
  392. this.div.style.filter = 'alpha(opacity=' + this.default_opacity * 100 + ')';
  393. }
  394. },
  395. /**
  396. * APIMethod: setBorder
  397. * 设置弹出窗体的边框样式.
  398. * 对应页面元素style的border属性
  399. * Parameters:
  400. * border - {String} 边框的样式值. 如 solid 代表实线
  401. */
  402. setBorder: function (border) {
  403. if (border != undefined) {
  404. this.default_border = border;
  405. }
  406. if (this.div != null) {
  407. this.div.style.border = this.default_border;
  408. }
  409. },
  410. /**
  411. * APIMethod: setContentHTML
  412. * 用于用户设置弹窗的内容
  413. *
  414. * Parameters:
  415. * contentHTML - {String} 弹窗内容
  416. */
  417. setContentHTML: function (titleArray,content) {
  418. if(content) {
  419. if(typeof(content) === "object") {
  420. //传入的对象属性
  421. var contentHtml = '<table class="content-table">';
  422. if(titleArray && titleArray.length>0) {
  423. for (var key=0; key<titleArray.length; key++) {
  424. var title = titleArray[key];
  425. if(key % 2 === 0) {
  426. contentHtml = contentHtml + '<tr><td class="col-xs-5"><span title="'+ title +'">' + title + '</span></td><td class="col-xs-5">' + content[title] + '</td></tr>';
  427. } else {
  428. contentHtml = contentHtml + '<tr class="gray-line"><td class="col-xs-5 title-td"><span title="'+ title +'">' + title + '</span></td><td class="col-xs-5">' + content[title] + '</td></tr>';
  429. }
  430. }
  431. } else {
  432. key = 0;
  433. this.content = SuperMap.Util.extend({}, content.attributes);
  434. for(var propTitle in this.content) {
  435. if(key % 2 === 0) {
  436. contentHtml = contentHtml + '<tr><td class="col-xs-5"><span title="'+ propTitle +'">' + propTitle + '</span></td><td class="col-xs-5">' + this.content[propTitle] + '</td></tr>';
  437. } else {
  438. contentHtml = contentHtml + '<tr class="gray-line"><td class="col-xs-5"><span title="'+ propTitle +'">' + propTitle + '</span></td><td class="col-xs-5">' + this.content[propTitle] + '</td></tr>';
  439. }
  440. key++;
  441. }
  442. }
  443. contentHtml = contentHtml + '</table>';
  444. this.contentHTML = contentHtml;
  445. } else {
  446. //传入html
  447. this.contentHTML = content;
  448. }
  449. }
  450. if ((this.contentDiv != null) &&
  451. (this.contentHTML != null) &&
  452. (this.contentHTML != this.groupDiv.innerHTML)) {
  453. this.groupDiv.innerHTML = this.contentHTML;
  454. }
  455. },
  456. /**
  457. * Method: addCloseBox
  458. *
  459. * Parameters:
  460. * callback - {Function} 当点击关闭按钮时,执行回调函数
  461. */
  462. addCloseBox: function (callback) {
  463. this.closeDiv = SuperMap.Util.createDiv(
  464. this.id + "_hide", null, new SuperMap.Size(17, 17)
  465. );
  466. if (this.titleBox == true) {
  467. this.closeDiv.className = "supermapol-icons-clear pop-hide";
  468. }
  469. else {
  470. this.closeDiv.className = "supermapol-icons-clear2 pop-hide";
  471. }
  472. this.header.appendChild(this.closeDiv);
  473. var closeInfoWindow = callback || function (e) {
  474. this.hide();
  475. SuperMap.Event.stop(e);
  476. };
  477. SuperMap.Event.observe(this.closeDiv, "click",
  478. SuperMap.Function.bindAsEventListener(closeInfoWindow, this));
  479. },
  480. /**
  481. * Method: registerEvents
  482. * 在弹出窗口上注册事件。
  483. *
  484. * 在一个单独的函数中这样做,以便子类可以选择重写它,
  485. * 如果他们希望对鼠标事件进行不同的处理
  486. *
  487. * 在以下的处理函数,需要特别注意正确处理鼠标和弹出音符。
  488. *
  489. * 因为用户可能会选择缩放矩形选项,然后拖动到一个弹出,
  490. * 我们需要一个安全的方式来允许MouseMove和mouseup事件通过弹出时,
  491. * 他们开始从外面。同样的程序是touchmove和touchend事件需要。
  492. *
  493. * 否则,我们想基本上杀死所有其他事件的事件传播,
  494. * 虽然我们必须这样做,小心,而不禁用基本的HTML功能,如点击超链接或拖动选择文本。
  495. */
  496. registerEvents: function () {
  497. this.events = new SuperMap.Events(this, this.div, null, true);
  498. function onTouchstart(evt) {
  499. SuperMap.Event.stop(evt, true);
  500. }
  501. this.events.on({
  502. "mousedown": this.onmousedown,
  503. "mousemove": this.onmousemove,
  504. "mouseup": this.onmouseup,
  505. "click": this.onclick,
  506. "mouseout": this.onmouseout,
  507. "dblclick": this.ondblclick,
  508. "touchstart": onTouchstart,
  509. scope: this
  510. });
  511. },
  512. /**
  513. * Method: onmousedown
  514. * 当鼠标落在弹出,请注意它的局部,
  515. * 然后不传播MouseDown(但这样做安全,用户可以选择文本内)
  516. *
  517. * Parameters:
  518. * evt - {Event}
  519. */
  520. onmousedown: function (evt) {
  521. this.mousedown = true;
  522. SuperMap.Event.stop(evt, true);
  523. },
  524. /**
  525. * Method: onmousemove
  526. * 如果拖在弹出的开始,然后不传播MouseMove
  527. * (但这样做安全,用户可以选择文本内)
  528. *
  529. * Parameters:
  530. * evt - {Event}
  531. */
  532. onmousemove: function (evt) {
  533. if (this.mousedown) {
  534. SuperMap.Event.stop(evt, true);
  535. }
  536. },
  537. /**
  538. * Method: onmouseup
  539. * 当鼠标出现在弹出窗口中,在它向下后,重置标志,
  540. * 然后(再次)不传播事件,但这样做安全,使用户可以选择文本内
  541. *
  542. * Parameters:
  543. * evt - {Event}
  544. */
  545. onmouseup: function (evt) {
  546. if (this.mousedown) {
  547. this.mousedown = false;
  548. SuperMap.Event.stop(evt, true);
  549. }
  550. },
  551. /**
  552. * Method: onclick
  553. * 忽略点击,但允许默认浏览器处理
  554. *
  555. * Parameters:
  556. * evt - {Event}
  557. */
  558. onclick: function (evt) {
  559. SuperMap.Event.stop(evt, true);
  560. },
  561. /**
  562. * Method: onmouseout
  563. *当鼠标跳出弹出窗口时,该标志将被设置为false,
  564. * 这样如果它们放了又拖回来,我们就不会被混淆了。
  565. *
  566. * Parameters:
  567. * evt - {Event}
  568. */
  569. onmouseout: function (evt) {
  570. this.mousedown = false;
  571. },
  572. /**
  573. * Method: ondblclick
  574. * 忽略双击,但允许默认浏览器处理
  575. *
  576. * Parameters:
  577. * evt - {Event}
  578. */
  579. ondblclick: function (evt) {
  580. SuperMap.Event.stop(evt, true);
  581. },
  582. CLASS_NAME: "SuperMap.InfoWindow"
  583. });