index.js 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. (function (root, factory) {
  2. if (typeof exports === 'object') {
  3. module.exports = factory();
  4. } else if (typeof define === 'function' && define.amd) {
  5. define(factory);
  6. } else {
  7. root.BMapLib = root.BMapLib || {};
  8. root.BMapLib.TextIconOverlay = root.BMapLib.TextIconOverlay || factory();
  9. }
  10. })(this, function() {
  11. var T,
  12. baidu = T = baidu || {version: "1.3.8"};
  13. var context = {}
  14. //提出guid,防止在与老版本Tangram混用时
  15. //在下一行错误的修改context[undefined]
  16. baidu.guid = "$BAIDU$";
  17. //Tangram可能被放在闭包中
  18. //一些页面级别唯一的属性,需要挂载在context[baidu.guid]上
  19. context[baidu.guid] = context[baidu.guid] || {};
  20. /**
  21. * @ignore
  22. * @namespace baidu.dom 操作dom的方法。
  23. */
  24. baidu.dom = baidu.dom || {};
  25. /**
  26. * 从文档中获取指定的DOM元素
  27. * @name baidu.dom.g
  28. * @function
  29. * @grammar baidu.dom.g(id)
  30. * @param {string|HTMLElement} id 元素的id或DOM元素
  31. * @shortcut g,T.G
  32. * @meta standard
  33. * @see baidu.dom.q
  34. *
  35. * @returns {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数
  36. */
  37. baidu.dom.g = function (id) {
  38. if ('string' == typeof id || id instanceof String) {
  39. return document.getElementById(id);
  40. } else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
  41. return id;
  42. }
  43. return null;
  44. };
  45. // 声明快捷方法
  46. baidu.g = baidu.G = baidu.dom.g;
  47. /**
  48. * 获取目标元素所属的document对象
  49. * @name baidu.dom.getDocument
  50. * @function
  51. * @grammar baidu.dom.getDocument(element)
  52. * @param {HTMLElement|string} element 目标元素或目标元素的id
  53. * @meta standard
  54. * @see baidu.dom.getWindow
  55. *
  56. * @returns {HTMLDocument} 目标元素所属的document对象
  57. */
  58. baidu.dom.getDocument = function (element) {
  59. element = baidu.dom.g(element);
  60. return element.nodeType == 9 ? element : element.ownerDocument || element.document;
  61. };
  62. /**
  63. * @ignore
  64. * @namespace baidu.lang 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
  65. */
  66. baidu.lang = baidu.lang || {};
  67. /**
  68. * 判断目标参数是否string类型或String对象
  69. * @name baidu.lang.isString
  70. * @function
  71. * @grammar baidu.lang.isString(source)
  72. * @param {Any} source 目标参数
  73. * @shortcut isString
  74. * @meta standard
  75. * @see baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
  76. *
  77. * @returns {boolean} 类型判断结果
  78. */
  79. baidu.lang.isString = function (source) {
  80. return '[object String]' == Object.prototype.toString.call(source);
  81. };
  82. // 声明快捷方法
  83. baidu.isString = baidu.lang.isString;
  84. /**
  85. * 从文档中获取指定的DOM元素
  86. * **内部方法**
  87. *
  88. * @param {string|HTMLElement} id 元素的id或DOM元素
  89. * @meta standard
  90. * @return {HTMLElement} DOM元素,如果不存在,返回null,如果参数不合法,直接返回参数
  91. */
  92. baidu.dom._g = function (id) {
  93. if (baidu.lang.isString(id)) {
  94. return document.getElementById(id);
  95. }
  96. return id;
  97. };
  98. // 声明快捷方法
  99. baidu._g = baidu.dom._g;
  100. /**
  101. * @ignore
  102. * @namespace baidu.browser 判断浏览器类型和特性的属性。
  103. */
  104. baidu.browser = baidu.browser || {};
  105. if (/msie (\d+\.\d)/i.test(navigator.userAgent)) {
  106. //IE 8下,以documentMode为准
  107. //在百度模板中,可能会有$,防止冲突,将$1 写成 \x241
  108. /**
  109. * 判断是否为ie浏览器
  110. * @property ie ie版本号
  111. * @grammar baidu.browser.ie
  112. * @meta standard
  113. * @shortcut ie
  114. * @see baidu.browser.firefox,baidu.browser.safari,baidu.browser.opera,baidu.browser.chrome,baidu.browser.maxthon
  115. */
  116. baidu.browser.ie = baidu.ie = document.documentMode || + RegExp['\x241'];
  117. }
  118. /**
  119. * 获取目标元素的computed style值。如果元素的样式值不能被浏览器计算,则会返回空字符串(IE)
  120. *
  121. * @author berg
  122. * @name baidu.dom.getComputedStyle
  123. * @function
  124. * @grammar baidu.dom.getComputedStyle(element, key)
  125. * @param {HTMLElement|string} element 目标元素或目标元素的id
  126. * @param {string} key 要获取的样式名
  127. *
  128. * @see baidu.dom.getStyle
  129. *
  130. * @returns {string} 目标元素的computed style值
  131. */
  132. baidu.dom.getComputedStyle = function(element, key){
  133. element = baidu.dom._g(element);
  134. var doc = baidu.dom.getDocument(element),
  135. styles;
  136. if (doc.defaultView && doc.defaultView.getComputedStyle) {
  137. styles = doc.defaultView.getComputedStyle(element, null);
  138. if (styles) {
  139. return styles[key] || styles.getPropertyValue(key);
  140. }
  141. }
  142. return '';
  143. };
  144. /**
  145. * 提供给setStyle与getStyle使用
  146. */
  147. baidu.dom._styleFixer = baidu.dom._styleFixer || {};
  148. /**
  149. * 提供给setStyle与getStyle使用
  150. */
  151. baidu.dom._styleFilter = baidu.dom._styleFilter || [];
  152. /**
  153. * 为获取和设置样式的过滤器
  154. * @private
  155. * @meta standard
  156. */
  157. baidu.dom._styleFilter.filter = function (key, value, method) {
  158. for (var i = 0, filters = baidu.dom._styleFilter, filter; filter = filters[i]; i++) {
  159. if (filter = filter[method]) {
  160. value = filter(key, value);
  161. }
  162. }
  163. return value;
  164. };
  165. /**
  166. * @ignore
  167. * @namespace baidu.string 操作字符串的方法。
  168. */
  169. baidu.string = baidu.string || {};
  170. /**
  171. * 将目标字符串进行驼峰化处理
  172. * @name baidu.string.toCamelCase
  173. * @function
  174. * @grammar baidu.string.toCamelCase(source)
  175. * @param {string} source 目标字符串
  176. * @remark
  177. * 支持单词以“-_”分隔
  178. * @meta standard
  179. *
  180. * @returns {string} 驼峰化处理后的字符串
  181. */
  182. baidu.string.toCamelCase = function (source) {
  183. //提前判断,提高getStyle等的效率 thanks xianwei
  184. if (source.indexOf('-') < 0 && source.indexOf('_') < 0) {
  185. return source;
  186. }
  187. return source.replace(/[-_][^-_]/g, function (match) {
  188. return match.charAt(1).toUpperCase();
  189. });
  190. };
  191. /**
  192. * 获取目标元素的样式值
  193. * @name baidu.dom.getStyle
  194. * @function
  195. * @grammar baidu.dom.getStyle(element, key)
  196. * @param {HTMLElement|string} element 目标元素或目标元素的id
  197. * @param {string} key 要获取的样式名
  198. * @remark
  199. *
  200. * 为了精简代码,本模块默认不对任何浏览器返回值进行归一化处理(如使用getStyle时,不同浏览器下可能返回rgb颜色或hex颜色),也不会修复浏览器的bug和差异性(如设置IE的float属性叫styleFloat,firefox则是cssFloat)。<br />
  201. * baidu.dom._styleFixer和baidu.dom._styleFilter可以为本模块提供支持。<br />
  202. * 其中_styleFilter能对颜色和px进行归一化处理,_styleFixer能对display,float,opacity,textOverflow的浏览器兼容性bug进行处理。
  203. * @shortcut getStyle
  204. * @meta standard
  205. * @see baidu.dom.setStyle,baidu.dom.setStyles, baidu.dom.getComputedStyle
  206. *
  207. * @returns {string} 目标元素的样式值
  208. */
  209. baidu.dom.getStyle = function (element, key) {
  210. var dom = baidu.dom;
  211. element = dom.g(element);
  212. key = baidu.string.toCamelCase(key);
  213. //computed style, then cascaded style, then explicitly set style.
  214. var value = element.style[key] ||
  215. (element.currentStyle ? element.currentStyle[key] : "") ||
  216. dom.getComputedStyle(element, key);
  217. // 在取不到值的时候,用fixer进行修正
  218. if (!value) {
  219. var fixer = dom._styleFixer[key];
  220. if(fixer){
  221. value = fixer.get ? fixer.get(element) : baidu.dom.getStyle(element, fixer);
  222. }
  223. }
  224. /* 检查结果过滤器 */
  225. if (fixer = dom._styleFilter) {
  226. value = fixer.filter(key, value, 'get');
  227. }
  228. return value;
  229. };
  230. // 声明快捷方法
  231. baidu.getStyle = baidu.dom.getStyle;
  232. if (/opera\/(\d+\.\d)/i.test(navigator.userAgent)) {
  233. /**
  234. * 判断是否为opera浏览器
  235. * @property opera opera版本号
  236. * @grammar baidu.browser.opera
  237. * @meta standard
  238. * @see baidu.browser.ie,baidu.browser.firefox,baidu.browser.safari,baidu.browser.chrome
  239. */
  240. baidu.browser.opera = + RegExp['\x241'];
  241. }
  242. /**
  243. * 判断是否为webkit内核
  244. * @property isWebkit
  245. * @grammar baidu.browser.isWebkit
  246. * @meta standard
  247. * @see baidu.browser.isGecko
  248. */
  249. baidu.browser.isWebkit = /webkit/i.test(navigator.userAgent);
  250. /**
  251. * 判断是否为gecko内核
  252. * @property isGecko
  253. * @grammar baidu.browser.isGecko
  254. * @meta standard
  255. * @see baidu.browser.isWebkit
  256. */
  257. baidu.browser.isGecko = /gecko/i.test(navigator.userAgent) && !/like gecko/i.test(navigator.userAgent);
  258. /**
  259. * 判断是否严格标准的渲染模式
  260. * @property isStrict
  261. * @grammar baidu.browser.isStrict
  262. * @meta standard
  263. */
  264. baidu.browser.isStrict = document.compatMode == "CSS1Compat";
  265. /**
  266. * 获取目标元素相对于整个文档左上角的位置
  267. * @name baidu.dom.getPosition
  268. * @function
  269. * @grammar baidu.dom.getPosition(element)
  270. * @param {HTMLElement|string} element 目标元素或目标元素的id
  271. * @meta standard
  272. *
  273. * @returns {Object} 目标元素的位置,键值为top和left的Object。
  274. */
  275. baidu.dom.getPosition = function (element) {
  276. element = baidu.dom.g(element);
  277. var doc = baidu.dom.getDocument(element),
  278. browser = baidu.browser,
  279. getStyle = baidu.dom.getStyle,
  280. // Gecko 1.9版本以下用getBoxObjectFor计算位置
  281. // 但是某些情况下是有bug的
  282. // 对于这些有bug的情况
  283. // 使用递归查找的方式
  284. BUGGY_GECKO_BOX_OBJECT = browser.isGecko > 0 &&
  285. doc.getBoxObjectFor &&
  286. getStyle(element, 'position') == 'absolute' &&
  287. (element.style.top === '' || element.style.left === ''),
  288. pos = {"left":0,"top":0},
  289. viewport = (browser.ie && !browser.isStrict) ? doc.body : doc.documentElement,
  290. parent,
  291. box;
  292. if(element == viewport){
  293. return pos;
  294. }
  295. if(element.getBoundingClientRect){ // IE and Gecko 1.9+
  296. //当HTML或者BODY有border width时, 原生的getBoundingClientRect返回值是不符合预期的
  297. //考虑到通常情况下 HTML和BODY的border只会设成0px,所以忽略该问题.
  298. box = element.getBoundingClientRect();
  299. pos.left = Math.floor(box.left) + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
  300. pos.top = Math.floor(box.top) + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
  301. // IE会给HTML元素添加一个border,默认是medium(2px)
  302. // 但是在IE 6 7 的怪异模式下,可以被html { border: 0; } 这条css规则覆盖
  303. // 在IE7的标准模式下,border永远是2px,这个值通过clientLeft 和 clientTop取得
  304. // 但是。。。在IE 6 7的怪异模式,如果用户使用css覆盖了默认的medium
  305. // clientTop和clientLeft不会更新
  306. pos.left -= doc.documentElement.clientLeft;
  307. pos.top -= doc.documentElement.clientTop;
  308. var htmlDom = doc.body,
  309. // 在这里,不使用element.style.borderLeftWidth,只有computedStyle是可信的
  310. htmlBorderLeftWidth = parseInt(getStyle(htmlDom, 'borderLeftWidth')),
  311. htmlBorderTopWidth = parseInt(getStyle(htmlDom, 'borderTopWidth'));
  312. if(browser.ie && !browser.isStrict){
  313. pos.left -= isNaN(htmlBorderLeftWidth) ? 2 : htmlBorderLeftWidth;
  314. pos.top -= isNaN(htmlBorderTopWidth) ? 2 : htmlBorderTopWidth;
  315. }
  316. } else {
  317. // safari/opera/firefox
  318. parent = element;
  319. do {
  320. pos.left += parent.offsetLeft;
  321. pos.top += parent.offsetTop;
  322. // safari里面,如果遍历到了一个fixed的元素,后面的offset都不准了
  323. if (browser.isWebkit > 0 && getStyle(parent, 'position') == 'fixed') {
  324. pos.left += doc.body.scrollLeft;
  325. pos.top += doc.body.scrollTop;
  326. break;
  327. }
  328. parent = parent.offsetParent;
  329. } while (parent && parent != element);
  330. // 对body offsetTop的修正
  331. if(browser.opera > 0 || (browser.isWebkit > 0 && getStyle(element, 'position') == 'absolute')){
  332. pos.top -= doc.body.offsetTop;
  333. }
  334. // 计算除了body的scroll
  335. parent = element.offsetParent;
  336. while (parent && parent != doc.body) {
  337. pos.left -= parent.scrollLeft;
  338. // see https://bugs.opera.com/show_bug.cgi?id=249965
  339. if (!browser.opera || parent.tagName != 'TR') {
  340. pos.top -= parent.scrollTop;
  341. }
  342. parent = parent.offsetParent;
  343. }
  344. }
  345. return pos;
  346. };
  347. /**
  348. * @ignore
  349. * @namespace baidu.event 屏蔽浏览器差异性的事件封装。
  350. * @property target 事件的触发元素
  351. * @property pageX 鼠标事件的鼠标x坐标
  352. * @property pageY 鼠标事件的鼠标y坐标
  353. * @property keyCode 键盘事件的键值
  354. */
  355. baidu.event = baidu.event || {};
  356. /**
  357. * 事件监听器的存储表
  358. * @private
  359. * @meta standard
  360. */
  361. baidu.event._listeners = baidu.event._listeners || [];
  362. /**
  363. * 为目标元素添加事件监听器
  364. * @name baidu.event.on
  365. * @function
  366. * @grammar baidu.event.on(element, type, listener)
  367. * @param {HTMLElement|string|window} element 目标元素或目标元素id
  368. * @param {string} type 事件类型
  369. * @param {Function} listener 需要添加的监听器
  370. * @remark
  371. *
  372. 1. 不支持跨浏览器的鼠标滚轮事件监听器添加<br>
  373. 2. 改方法不为监听器灌入事件对象,以防止跨iframe事件挂载的事件对象获取失败
  374. * @shortcut on
  375. * @meta standard
  376. * @see baidu.event.un
  377. *
  378. * @returns {HTMLElement|window} 目标元素
  379. */
  380. baidu.event.on = function (element, type, listener) {
  381. type = type.replace(/^on/i, '');
  382. element = baidu.dom._g(element);
  383. var realListener = function (ev) {
  384. // 1. 这里不支持EventArgument, 原因是跨frame的事件挂载
  385. // 2. element是为了修正this
  386. listener.call(element, ev);
  387. },
  388. lis = baidu.event._listeners,
  389. filter = baidu.event._eventFilter,
  390. afterFilter,
  391. realType = type;
  392. type = type.toLowerCase();
  393. // filter过滤
  394. if(filter && filter[type]){
  395. afterFilter = filter[type](element, type, realListener);
  396. realType = afterFilter.type;
  397. realListener = afterFilter.listener;
  398. }
  399. // 事件监听器挂载
  400. if (element.addEventListener) {
  401. element.addEventListener(realType, realListener, false);
  402. } else if (element.attachEvent) {
  403. element.attachEvent('on' + realType, realListener);
  404. }
  405. // 将监听器存储到数组中
  406. lis[lis.length] = [element, type, listener, realListener, realType];
  407. return element;
  408. };
  409. // 声明快捷方法
  410. baidu.on = baidu.event.on;
  411. /**
  412. * 返回一个当前页面的唯一标识字符串。
  413. * @name baidu.lang.guid
  414. * @function
  415. * @grammar baidu.lang.guid()
  416. * @version 1.1.1
  417. * @meta standard
  418. *
  419. * @returns {String} 当前页面的唯一标识字符串
  420. */
  421. (function(){
  422. //不直接使用window,可以提高3倍左右性能
  423. var guid = context[baidu.guid];
  424. baidu.lang.guid = function() {
  425. return "TANGRAM__" + (guid._counter ++).toString(36);
  426. };
  427. guid._counter = guid._counter || 1;
  428. })();
  429. /**
  430. * 所有类的实例的容器
  431. * key为每个实例的guid
  432. * @meta standard
  433. */
  434. context[baidu.guid]._instances = context[baidu.guid]._instances || {};
  435. /**
  436. * 判断目标参数是否为function或Function实例
  437. * @name baidu.lang.isFunction
  438. * @function
  439. * @grammar baidu.lang.isFunction(source)
  440. * @param {Any} source 目标参数
  441. * @version 1.2
  442. * @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
  443. * @meta standard
  444. * @returns {boolean} 类型判断结果
  445. */
  446. baidu.lang.isFunction = function (source) {
  447. // chrome下,'function' == typeof /a/ 为true.
  448. return '[object Function]' == Object.prototype.toString.call(source);
  449. };
  450. /**
  451. *
  452. * @ignore
  453. * @class Tangram继承机制提供的一个基类,用户可以通过继承baidu.lang.Class来获取它的属性及方法。
  454. * @name baidu.lang.Class
  455. * @grammar baidu.lang.Class(guid)
  456. * @param {string} guid 对象的唯一标识
  457. * @meta standard
  458. * @remark baidu.lang.Class和它的子类的实例均包含一个全局唯一的标识guid。guid是在构造函数中生成的,因此,继承自baidu.lang.Class的类应该直接或者间接调用它的构造函数。<br>baidu.lang.Class的构造函数中产生guid的方式可以保证guid的唯一性,及每个实例都有一个全局唯一的guid。
  459. * @meta standard
  460. * @see baidu.lang.inherits,baidu.lang.Event
  461. */
  462. baidu.lang.Class = function(guid) {
  463. this.guid = guid || baidu.lang.guid();
  464. context[baidu.guid]._instances[this.guid] = this;
  465. };
  466. context[baidu.guid]._instances = context[baidu.guid]._instances || {};
  467. /**
  468. * 释放对象所持有的资源,主要是自定义事件。
  469. * @name dispose
  470. * @grammar obj.dispose()
  471. */
  472. baidu.lang.Class.prototype.dispose = function(){
  473. delete context[baidu.guid]._instances[this.guid];
  474. for(var property in this){
  475. if (!baidu.lang.isFunction(this[property])) {
  476. delete this[property];
  477. }
  478. }
  479. this.disposed = true;
  480. };
  481. /**
  482. * 重载了默认的toString方法,使得返回信息更加准确一些。
  483. * @return {string} 对象的String表示形式
  484. */
  485. baidu.lang.Class.prototype.toString = function(){
  486. return "[object " + (this._className || "Object" ) + "]";
  487. };
  488. /**
  489. * @ignore
  490. * @class 自定义的事件对象。
  491. * @name baidu.lang.Event
  492. * @grammar baidu.lang.Event(type[, target])
  493. * @param {string} type 事件类型名称。为了方便区分事件和一个普通的方法,事件类型名称必须以"on"(小写)开头。
  494. * @param {Object} [target]触发事件的对象
  495. * @meta standard
  496. * @remark 引入该模块,会自动为Class引入3个事件扩展方法:addEventListener、removeEventListener和dispatchEvent。
  497. * @meta standard
  498. * @see baidu.lang.Class
  499. */
  500. baidu.lang.Event = function (type, target) {
  501. this.type = type;
  502. this.returnValue = true;
  503. this.target = target || null;
  504. this.currentTarget = null;
  505. };
  506. /**
  507. * 注册对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  508. * @grammar obj.addEventListener(type, handler[, key])
  509. * @param {string} type 自定义事件的名称
  510. * @param {Function} handler 自定义事件被触发时应该调用的回调函数
  511. * @param {string} [key] 为事件监听函数指定的名称,可在移除时使用。如果不提供,方法会默认为它生成一个全局唯一的key。
  512. * @remark 事件类型区分大小写。如果自定义事件名称不是以小写"on"开头,该方法会给它加上"on"再进行判断,即"click"和"onclick"会被认为是同一种事件。
  513. */
  514. baidu.lang.Class.prototype.addEventListener = function (type, handler, key) {
  515. if (!baidu.lang.isFunction(handler)) {
  516. return;
  517. }
  518. !this.__listeners && (this.__listeners = {});
  519. var t = this.__listeners, id;
  520. if (typeof key == "string" && key) {
  521. if (/[^\w\-]/.test(key)) {
  522. throw("nonstandard key:" + key);
  523. } else {
  524. handler.hashCode = key;
  525. id = key;
  526. }
  527. }
  528. type.indexOf("on") != 0 && (type = "on" + type);
  529. typeof t[type] != "object" && (t[type] = {});
  530. id = id || baidu.lang.guid();
  531. handler.hashCode = id;
  532. t[type][id] = handler;
  533. };
  534. /**
  535. * 移除对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  536. * @grammar obj.removeEventListener(type, handler)
  537. * @param {string} type 事件类型
  538. * @param {Function|string} handler 要移除的事件监听函数或者监听函数的key
  539. * @remark 如果第二个参数handler没有被绑定到对应的自定义事件中,什么也不做。
  540. */
  541. baidu.lang.Class.prototype.removeEventListener = function (type, handler) {
  542. if (typeof handler != "undefined") {
  543. if ( (baidu.lang.isFunction(handler) && ! (handler = handler.hashCode))
  544. || (! baidu.lang.isString(handler))
  545. ){
  546. return;
  547. }
  548. }
  549. !this.__listeners && (this.__listeners = {});
  550. type.indexOf("on") != 0 && (type = "on" + type);
  551. var t = this.__listeners;
  552. if (!t[type]) {
  553. return;
  554. }
  555. if (typeof handler != "undefined") {
  556. t[type][handler] && delete t[type][handler];
  557. } else {
  558. for(var guid in t[type]){
  559. delete t[type][guid];
  560. }
  561. }
  562. };
  563. /**
  564. * 派发自定义事件,使得绑定到自定义事件上面的函数都会被执行。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  565. * @grammar obj.dispatchEvent(event, options)
  566. * @param {baidu.lang.Event|String} event Event对象,或事件名称(1.1.1起支持)
  567. * @param {Object} options 扩展参数,所含属性键值会扩展到Event对象上(1.2起支持)
  568. * @remark 处理会调用通过addEventListenr绑定的自定义事件回调函数之外,还会调用直接绑定到对象上面的自定义事件。例如:<br>
  569. myobj.onMyEvent = function(){}<br>
  570. myobj.addEventListener("onMyEvent", function(){});
  571. */
  572. baidu.lang.Class.prototype.dispatchEvent = function (event, options) {
  573. if (baidu.lang.isString(event)) {
  574. event = new baidu.lang.Event(event);
  575. }
  576. !this.__listeners && (this.__listeners = {});
  577. // 20100603 添加本方法的第二个参数,将 options extend到event中去传递
  578. options = options || {};
  579. for (var i in options) {
  580. event[i] = options[i];
  581. }
  582. var i, t = this.__listeners, p = event.type;
  583. event.target = event.target || this;
  584. event.currentTarget = this;
  585. p.indexOf("on") != 0 && (p = "on" + p);
  586. baidu.lang.isFunction(this[p]) && this[p].apply(this, arguments);
  587. if (typeof t[p] == "object") {
  588. for (i in t[p]) {
  589. t[p][i].apply(this, arguments);
  590. }
  591. }
  592. return event.returnValue;
  593. };
  594. baidu.lang.inherits = function (subClass, superClass, className) {
  595. var key, proto,
  596. selfProps = subClass.prototype,
  597. clazz = new Function();
  598. clazz.prototype = superClass.prototype;
  599. proto = subClass.prototype = new clazz();
  600. for (key in selfProps) {
  601. proto[key] = selfProps[key];
  602. }
  603. subClass.prototype.constructor = subClass;
  604. subClass.superClass = superClass.prototype;
  605. // 类名标识,兼容Class的toString,基本没用
  606. if ("string" == typeof className) {
  607. proto._className = className;
  608. }
  609. };
  610. // 声明快捷方法
  611. baidu.inherits = baidu.lang.inherits;
  612. /**
  613. * 图片的路径
  614. * @private
  615. * @type {String}
  616. */
  617. var _IMAGE_PATH = 'http://api.map.baidu.com/library/TextIconOverlay/1.2/src/images/m';
  618. /**
  619. * 图片的后缀名
  620. * @private
  621. * @type {String}
  622. */
  623. var _IMAGE_EXTENSION = 'png';
  624. /**
  625. *@exports TextIconOverlay as BMapLib.TextIconOverlay
  626. */
  627. /**
  628. * TextIconOverlay
  629. * @class 此类表示地图上的一个覆盖物,该覆盖物由文字和图标组成,从Overlay继承。文字通常是数字(0-9)或字母(A-Z ),而文字与图标之间有一定的映射关系。
  630. *该覆盖物适用于以下类似的场景:需要在地图上添加一系列覆盖物,这些覆盖物之间用不同的图标和文字来区分,文字可能表示了该覆盖物的某一属性值,根据该文字和一定的映射关系,自动匹配相应颜色和大小的图标。
  631. *
  632. *@constructor
  633. *@param {Point} position 表示一个经纬度坐标位置。
  634. *@param {String} text 表示该覆盖物显示的文字信息。
  635. *@param {Json Object} options 可选参数,可选项包括:<br />
  636. *"<b>styles</b>":{Array<IconStyle>} 一组图标风格。单个图表风格包括以下几个属性:<br />
  637. * url {String} 图片的url地址。(必选)<br />
  638. * size {Size} 图片的大小。(必选)<br />
  639. * anchor {Size} 图标定位在地图上的位置相对于图标左上角的偏移值,默认偏移值为图标的中心位置。(可选)<br />
  640. * offset {Size} 图片相对于可视区域的偏移值,此功能的作用等同于CSS中的background-position属性。(可选)<br />
  641. * textSize {Number} 文字的大小。(可选,默认10)<br />
  642. * textColor {String} 文字的颜色。(可选,默认black)<br />
  643. */
  644. var TextIconOverlay = function(position, text, options){
  645. try {
  646. BMap;
  647. } catch (e) {
  648. throw Error('Baidu Map JS API is not ready yet!');
  649. }
  650. T.lang.inherits(TextIconOverlay, BMap.Overlay, "TextIconOverlay");
  651. this._position = position;
  652. this._text = text;
  653. this._options = options || {};
  654. this._styles = this._options['styles'] || [];
  655. (!this._styles.length) && this._setupDefaultStyles();
  656. };
  657. TextIconOverlay.prototype._setupDefaultStyles = function(){
  658. var sizes = [53, 56, 66, 78, 90];
  659. for(var i = 0, size; size = sizes[i]; i++){
  660. this._styles.push({
  661. url:_IMAGE_PATH + i + '.' + _IMAGE_EXTENSION,
  662. size: new BMap.Size(size, size)
  663. });
  664. }//for循环的简洁写法
  665. };
  666. /**
  667. *继承Overlay的intialize方法,自定义覆盖物时必须。
  668. *@param {Map} map BMap.Map的实例化对象。
  669. *@return {HTMLElement} 返回覆盖物对应的HTML元素。
  670. */
  671. TextIconOverlay.prototype.initialize = function(map){
  672. this._map = map;
  673. this._domElement = document.createElement('div');
  674. this._updateCss();
  675. this._updateText();
  676. this._updatePosition();
  677. this._bind();
  678. this._map.getPanes().markerMouseTarget.appendChild(this._domElement);
  679. return this._domElement;
  680. };
  681. /**
  682. *继承Overlay的draw方法,自定义覆盖物时必须。
  683. *@return 无返回值。
  684. */
  685. TextIconOverlay.prototype.draw = function(){
  686. this._map && this._updatePosition();
  687. };
  688. /**
  689. *获取该覆盖物上的文字。
  690. *@return {String} 该覆盖物上的文字。
  691. */
  692. TextIconOverlay.prototype.getText = function(){
  693. return this._text;
  694. };
  695. /**
  696. *设置该覆盖物上的文字。
  697. *@param {String} text 要设置的文字,通常是字母A-Z或数字0-9。
  698. *@return 无返回值。
  699. */
  700. TextIconOverlay.prototype.setText = function(text){
  701. if(text && (!this._text || (this._text.toString() != text.toString()))){
  702. this._text = text;
  703. this._updateText();
  704. this._updateCss();
  705. this._updatePosition();
  706. }
  707. };
  708. /**
  709. *获取该覆盖物的位置。
  710. *@return {Point} 该覆盖物的经纬度坐标。
  711. */
  712. TextIconOverlay.prototype.getPosition = function () {
  713. return this._position;
  714. };
  715. /**
  716. *设置该覆盖物的位置。
  717. *@param {Point} position 要设置的经纬度坐标。
  718. *@return 无返回值。
  719. */
  720. TextIconOverlay.prototype.setPosition = function (position) {
  721. if(position && (!this._position || !this._position.equals(position))){
  722. this._position = position;
  723. this._updatePosition();
  724. }
  725. };
  726. /**
  727. *由文字信息获取风格数组的对应索引值。
  728. *内部默认的对应函数为文字转换为数字除以10的结果,比如文字8返回索引0,文字25返回索引2.
  729. *如果需要自定义映射关系,请覆盖该函数。
  730. *@param {String} text 文字。
  731. *@param {Array<IconStyle>} styles 一组图标风格。
  732. *@return {Number} 对应的索引值。
  733. */
  734. TextIconOverlay.prototype.getStyleByText = function(text, styles){
  735. var count = parseInt(text);
  736. var index = parseInt(count / 10);
  737. index = Math.max(0, index);
  738. index = Math.min(index, styles.length - 1);
  739. return styles[index];
  740. }
  741. /**
  742. *更新相应的CSS。
  743. *@return 无返回值。
  744. */
  745. TextIconOverlay.prototype._updateCss = function(){
  746. if (!this._domElement) {
  747. return
  748. }
  749. var style = this.getStyleByText(this._text, this._styles);
  750. this._domElement.style.cssText = this._buildCssText(style);
  751. };
  752. /**
  753. *更新覆盖物的显示文字。
  754. *@return 无返回值。
  755. */
  756. TextIconOverlay.prototype._updateText = function(){
  757. if (this._domElement) {
  758. this._domElement.innerHTML = this._text;
  759. }
  760. };
  761. /**
  762. *调整覆盖物在地图上的位置更新覆盖物的显示文字。
  763. *@return 无返回值。
  764. */
  765. TextIconOverlay.prototype._updatePosition = function(){
  766. if (this._domElement && this._position) {
  767. var style = this._domElement.style;
  768. var pixelPosition= this._map.pointToOverlayPixel(this._position);
  769. pixelPosition.x -= Math.ceil(parseInt(style.width) / 2);
  770. pixelPosition.y -= Math.ceil(parseInt(style.height) / 2);
  771. style.left = pixelPosition.x + "px";
  772. style.top = pixelPosition.y + "px";
  773. }
  774. };
  775. /**
  776. * 为该覆盖物的HTML元素构建CSS
  777. * @param {IconStyle} 一个图标的风格。
  778. * @return {String} 构建完成的CSSTEXT。
  779. */
  780. TextIconOverlay.prototype._buildCssText = function(style) {
  781. //根据style来确定一些默认值
  782. var url = style['url'];
  783. var size = style['size'];
  784. var anchor = style['anchor'];
  785. var offset = style['offset'];
  786. var textColor = style['textColor'] || 'black';
  787. var textSize = style['textSize'] || 10;
  788. var csstext = [];
  789. if (T.browser["ie"] < 7) {
  790. csstext.push('filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
  791. 'sizingMethod=scale,src="' + url + '");');
  792. } else {
  793. csstext.push('background-image:url(' + url + ');');
  794. var backgroundPosition = '0 0';
  795. (offset instanceof BMap.Size) && (backgroundPosition = offset.width + 'px' + ' ' + offset.height + 'px');
  796. csstext.push('background-position:' + backgroundPosition + ';');
  797. }
  798. if (size instanceof BMap.Size){
  799. if (anchor instanceof BMap.Size) {
  800. if (anchor.height > 0 && anchor.height < size.height) {
  801. csstext.push('height:' + (size.height - anchor.height) + 'px; padding-top:' + anchor.height + 'px;');
  802. }
  803. if(anchor.width > 0 && anchor.width < size.width){
  804. csstext.push('width:' + (size.width - anchor.width) + 'px; padding-left:' + anchor.width + 'px;');
  805. }
  806. } else {
  807. csstext.push('height:' + size.height + 'px; line-height:' + size.height + 'px;');
  808. csstext.push('width:' + size.width + 'px; text-align:center;');
  809. }
  810. }
  811. csstext.push('cursor:pointer; color:' + textColor + '; position:absolute; font-size:' +
  812. textSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
  813. return csstext.join('');
  814. };
  815. /**
  816. * 当鼠标点击该覆盖物时会触发该事件
  817. * @name TextIconOverlay#click
  818. * @event
  819. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  820. * <br />"<b>type</b> : {String} 事件类型
  821. * <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
  822. *
  823. */
  824. /**
  825. * 当鼠标进入该覆盖物区域时会触发该事件
  826. * @name TextIconOverlay#mouseover
  827. * @event
  828. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  829. * <br />"<b>type</b> : {String} 事件类型
  830. * <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
  831. * <br />"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象
  832. * <br />"<b>pixel</b>:{BMap.pixel} 最新添加上的节点BMap.Pixel对象
  833. *
  834. * @example <b>参考示例:</b><br />
  835. * myTextIconOverlay.addEventListener("mouseover", function(e) { alert(e.point); });
  836. */
  837. /**
  838. * 当鼠标离开该覆盖物区域时会触发该事件
  839. * @name TextIconOverlay#mouseout
  840. * @event
  841. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  842. * <br />"<b>type</b> : {String} 事件类型
  843. * <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
  844. * <br />"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象
  845. * <br />"<b>pixel</b>:{BMap.pixel} 最新添加上的节点BMap.Pixel对象
  846. *
  847. * @example <b>参考示例:</b><br />
  848. * myTextIconOverlay.addEventListener("mouseout", function(e) { alert(e.point); });
  849. */
  850. /**
  851. * 为该覆盖物绑定一系列事件
  852. * 当前支持click mouseover mouseout
  853. * @return 无返回值。
  854. */
  855. TextIconOverlay.prototype._bind = function(){
  856. if (!this._domElement){
  857. return;
  858. }
  859. var me = this;
  860. var map = this._map;
  861. var BaseEvent = T.lang.Event;
  862. function eventExtend(e, be){
  863. var elem = e.srcElement || e.target;
  864. var x = e.clientX || e.pageX;
  865. var y = e.clientY || e.pageY;
  866. if (e && be && x && y && elem){
  867. var offset = T.dom.getPosition(map.getContainer());
  868. be.pixel = new BMap.Pixel(x - offset.left, y - offset.top);
  869. be.point = map.pixelToPoint(be.pixel);
  870. }
  871. return be;
  872. }//给事件参数增加pixel和point两个值
  873. T.event.on(this._domElement,"mouseover", function(e){
  874. me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseover")));
  875. });
  876. T.event.on(this._domElement,"mouseout", function(e){
  877. me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseout")));
  878. });
  879. T.event.on(this._domElement,"click", function(e){
  880. me.dispatchEvent(eventExtend(e, new BaseEvent("onclick")));
  881. });
  882. };
  883. return TextIconOverlay;
  884. });