/bin/html/map/mapapi.renderer.js

https://bitbucket.org/VirtualReality/software-testing · JavaScript · 489 lines · 429 code · 34 blank · 26 comment · 105 complexity · 09177ee1dddd3f91d9ca5c59aa332a52 MD5 · raw file

  1. /**
  2. * License and Terms of Use
  3. *
  4. * Copyright (c) 2011 SignpostMarv
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. (function(window, undefined){
  25. window['mapapi'] = window['mapapi'] || {};
  26. var
  27. document = window['document'],
  28. EventTarget = window['EventTarget'],
  29. mapapi = window['mapapi'],
  30. gridPoint = mapapi['gridPoint'],
  31. bounds = mapapi['bounds'],
  32. size = mapapi['size'],
  33. empty = mapapi['utils']['empty'],
  34. shape = mapapi['shape']
  35. ;
  36. if(EventTarget == undefined){
  37. throw 'EventTarget not loaded';
  38. }
  39. function each(array, cb){
  40. for(var i=0;i<array.length;++i){
  41. cb(array[i],i);
  42. }
  43. }
  44. function dblclick_handler(e){
  45. var
  46. obj = this,
  47. point = e['pos'],
  48. zoom = obj['zoom']() - 1
  49. ;
  50. if(this['smoothZoom']()){
  51. this['animate']({
  52. 'zoom' : zoom,
  53. 'focus' : point
  54. }, .5);
  55. }else{
  56. obj['zoom'](zoom);
  57. obj['focus'](point);
  58. }
  59. }
  60. function dragpan(e){
  61. var
  62. obj = this,
  63. pos = e['to']
  64. ;
  65. obj['focus'](pos);
  66. }
  67. /**
  68. * @constructor
  69. */
  70. function renderer(){
  71. var
  72. obj = this
  73. ;
  74. EventTarget['call'](this);
  75. obj['opts'] = {'shapes':new mapapi['shapeManager']};
  76. obj['_focus'] = new gridPoint(0,0);
  77. }
  78. renderer.prototype = new EventTarget();
  79. renderer.prototype['constructor'] = renderer;
  80. renderer.prototype['browserSupported'] = false;
  81. renderer.prototype['options'] = function(options){
  82. var
  83. obj = this,
  84. options = options || {},
  85. opts = obj['opts'],
  86. container = options['container'],
  87. hasFunc = ['minZoom', 'maxZoom', 'scrollWheelZoom', 'smoothZoom', 'draggable', 'dblclickZoom', 'zoom', 'focus', 'panUnitLR', 'panUnitUD'],
  88. checkFunc
  89. ;
  90. options['minZoom'] = options['minZoom'] || 0;
  91. options['maxZoom'] = options['maxZoom'] || 0;
  92. options['panUnitLR'] = options['panUnitLR'] || 0;
  93. options['panUnitUD'] = options['panUnitUD'] || 0;
  94. for(var i=0;i<hasFunc.length;++i){
  95. var
  96. checkFunc = hasFunc[i]
  97. ;
  98. if(options[checkFunc] != undefined){
  99. obj[checkFunc](options[checkFunc]);
  100. }
  101. }
  102. obj['addListener']('drag', dragpan);
  103. obj['addListener']('click', function(e){
  104. opts['shapes']['click'](e['pos']);
  105. });
  106. if(container){
  107. if(!container['appendChild']){
  108. throw 'Container is invalid';
  109. }else{
  110. opts['container'] = container;
  111. if(obj['contentNode']){
  112. obj['contentNode']['style']['width'] = '100%';
  113. obj['contentNode']['style']['height'] = '100%';
  114. empty(container)['appendChild'](obj['contentNode']);
  115. }
  116. }
  117. }
  118. }
  119. renderer.prototype['minZoom'] = function(value){
  120. var
  121. opts = this['opts']
  122. ;
  123. if(value != undefined){
  124. opts['minZoom'] = Math.max(0, value);
  125. }
  126. return opts['minZoom'];
  127. };
  128. renderer.prototype['maxZoom'] = function(value){
  129. var
  130. opts = this['opts']
  131. ;
  132. if(value != undefined){
  133. opts['maxZoom'] = Math.max(this.minZoom() + 1, value);
  134. }
  135. return opts['maxZoom'];
  136. }
  137. renderer.prototype['zoom'] = function(value){
  138. var
  139. obj = this,
  140. opts = obj['opts']
  141. ;
  142. if(value != undefined){
  143. opts['zoom'] = Math.min(Math.max(value, obj['minZoom']()), obj['maxZoom']());
  144. }
  145. return opts['zoom'];
  146. }
  147. renderer.prototype['panUnitUD'] = function(value){
  148. var
  149. opts = this['opts']
  150. ;
  151. if(value){
  152. opts['panUnitUD'] = Math.max(value, 1);
  153. }
  154. return opts['panUnitUD'] * Math.pow(2, this['zoom']());
  155. }
  156. renderer.prototype['panUnitLR'] = function(value){
  157. var
  158. opts = this['opts']
  159. ;
  160. if(value){
  161. opts['panUnitLR'] = Math.max(value, 1);
  162. }
  163. return opts['panUnitLR'] * Math.pow(2, this['zoom']());
  164. }
  165. renderer.prototype['panTo'] = function(pos, a){
  166. if(typeof pos == 'number' && typeof a == 'number'){
  167. pos = new gridPoint(pos, a);
  168. }else if(typeof pos == 'object' && typeof pos['x'] == 'number' && typeof pos['y'] == 'number'){
  169. pos = new gridPoint(pos['x'], pos['y']);
  170. }
  171. if(pos instanceof gridPoint){
  172. if(this['bounds']()['isWithin'](pos)){
  173. this['animate']({
  174. 'focus' : pos
  175. }, .5);
  176. }else{
  177. this['focus'](pos);
  178. }
  179. }
  180. }
  181. renderer.prototype['panUp'] = function(){
  182. var pos = this.focus();
  183. this.panTo(pos['x'], pos['y'] + this.panUnitUD());
  184. }
  185. renderer.prototype['panDown'] = function(){
  186. var pos = this.focus();
  187. this.panTo(pos['x'], pos['y'] - this.panUnitUD());
  188. }
  189. renderer.prototype['panLeft'] = function(){
  190. var pos = this.focus();
  191. this.panTo(pos['x'] - this.panUnitLR(), pos['y']);
  192. }
  193. renderer.prototype['panRight'] = function(){
  194. var pos = this.focus();
  195. this.panTo(pos['x'] + this.panUnitLR(), pos['y']);
  196. }
  197. renderer.prototype['scrollWheelZoom'] = function(flag){
  198. var
  199. opts = this['opts']
  200. ;
  201. if(flag != undefined){
  202. opts['scrollWheelZoom'] = !!flag;
  203. }
  204. return opts['scrollWheelZoom'];
  205. }
  206. renderer.prototype['smoothZoom'] = function(flag){
  207. var
  208. obj = this,
  209. opts = obj['opts']
  210. ;
  211. if(flag != undefined){
  212. opts['smoothZoom'] = !!flag;
  213. }
  214. return opts['smoothZoom'];
  215. }
  216. renderer.prototype['draggable'] = function(flag){
  217. if(flag != undefined){
  218. if(flag){ // do stuff to make the map renderer draggable
  219. return true;
  220. }else{ // do stuff to make it non-draggable
  221. return false;
  222. }
  223. }
  224. return flag; // should return from other property
  225. }
  226. renderer.prototype['focus'] = function(pos, zoom, a){ // should return an instance of mapapi.gridPoint
  227. if(typeof pos == 'number'){
  228. pos = new mapapi['gridPoint'](pos, zoom);
  229. zoom = this['zoom']();
  230. }
  231. if(zoom != undefined){
  232. this['zoom'](zoom);
  233. }
  234. var
  235. opts = this['opts']
  236. ;
  237. if(pos instanceof mapapi['gridPoint']){ // implementations should do something to update the renderer to the focal point
  238. opts['focus'] = pos;
  239. this['fire']('focus_changed', {'pos':pos, 'withinBounds' : this['bounds']()['isWithin'](pos)});
  240. }
  241. return opts['focus'];
  242. }
  243. renderer.prototype['px2point'] = function(x, y){
  244. var
  245. obj = this,
  246. content = obj['contentNode'],
  247. cWidth = content['width'],
  248. cw2 = cWidth / 2.0,
  249. cHeight = content['height'],
  250. ch2 = cHeight / 2.0,
  251. size = obj['tileSize'](),
  252. distX = (x - cw2) / size['width'],
  253. distY = ((cHeight - y) - ch2) / size['height'],
  254. focus = obj['focus']()//,
  255. mapX = focus['x'] + distX,
  256. mapY = focus['y'] + distY
  257. ;
  258. return new gridPoint(mapX, mapY);
  259. }
  260. renderer.prototype['point2px'] = function(x, y){
  261. if(x instanceof gridPoint){
  262. y = x['y'];
  263. x = x['x'];
  264. }
  265. var
  266. content = this['contentNode'],
  267. cWidth = content['clientWidth'],
  268. cw2 = cWidth / 2.0,
  269. cHeight = content['clientHeight'],
  270. ch2 = cHeight / 2.0,
  271. size = this['tileSize'](),
  272. focus = this['focus'](),
  273. fx = focus['x'],
  274. fy = focus['y'],
  275. diffX = (x - fx) * size['width'],
  276. diffY = (y - fy) * size['height']
  277. ;
  278. return {'x':cw2 + diffX, 'y':ch2 - diffY};
  279. }
  280. renderer.prototype['dblclickZoom'] = function(flag){
  281. var
  282. obj = this,
  283. opts = obj['opts']
  284. ;
  285. if(flag != undefined){
  286. opts['dblclickZoom'] = !!flag;
  287. if(obj['contentNode']){
  288. if(!!flag){
  289. obj['addListener']('dblclick', dblclick_handler);
  290. }else{
  291. obj['removeListener']('dblclick', dblclick_handler);
  292. }
  293. }
  294. }
  295. return opts['dblclickZoom']; // should return from other property
  296. }
  297. renderer.prototype['animate'] = function(opts, time){
  298. if(opts == undefined || (opts != undefined && typeof time != 'number')){
  299. return;
  300. }
  301. var
  302. obj = this,
  303. time = (!time || time < 0) ? 1 : time,
  304. czoom = obj['zoom'](),
  305. mnzm = obj['minZoom'](),
  306. mxzm = obj['maxZoom'](),
  307. cpos = obj['focus'](),
  308. gridPoint = mapapi['gridPoint'],
  309. zoom,
  310. pos,
  311. animateOrder
  312. ;
  313. if(typeof opts == 'number'){
  314. opts = {'zoom':opts};
  315. }else if(opts instanceof gridPoint || (typeof opts == 'object' && typeof opts['x'] == 'number' && typeof opts['y'] == 'number')){
  316. opts = {'focus':(opts instanceof gridPoint) ? opts : new gridPoint(opts['x'],opts['y'])};
  317. }
  318. pos = animateOrder = !1;
  319. if(opts['zoom'] != undefined){
  320. zoom = (typeof opts['zoom'] == 'number') ? opts['zoom'] : (opts['zoom'] * 1);
  321. zoom = (zoom != czoom && zoom >= mnzm && zoom <= mxzm) ? zoom : undefined;
  322. }
  323. if(opts['focus'] instanceof gridPoint){
  324. pos = (opts['focus']['x'] != cpos['x'] || opts['focus']['y'] != cpos['y']) ? opts['focus'] : !1;
  325. }
  326. var
  327. a = (zoom != undefined),
  328. b = !!pos
  329. ;
  330. if(a || b){
  331. animateOrder = {};
  332. if(zoom != undefined){
  333. animateOrder['zoom'] = zoom;
  334. animateOrder['fromZoom'] = czoom;
  335. }
  336. if(b){
  337. animateOrder['focus'] = pos;
  338. animateOrder['fromFocus'] = cpos;
  339. }
  340. animateOrder['start'] = (new Date().getTime()) / 1000;
  341. animateOrder['end'] = animateOrder['start'] + time;
  342. obj['animateOrder'] = animateOrder;
  343. }
  344. }
  345. renderer.prototype['doAnimation'] = function(){
  346. var
  347. obj = this,
  348. ao = obj['animateOrder']
  349. ;
  350. if(!ao){
  351. return false;
  352. }
  353. var
  354. a = ao['zoom'],
  355. b = ao['fromZoom'],
  356. c = ao['focus'],
  357. d = ao['fromFocus'],
  358. e = ao['start'],
  359. f = ao['end'],
  360. now,diff,g,h,i
  361. ;
  362. if(!!ao){
  363. now = (new Date().getTime()) / 1000;
  364. diff = (now - e) / (f - e);
  365. if(now >= ao['end']){
  366. if(!!c){
  367. obj['focus'](c, !!a ? a : undefined);
  368. }else if(!!a){
  369. obj['zoom'](a);
  370. }
  371. obj['animateOrder'] = !1;
  372. return true;
  373. }else if(now > ao['start']){
  374. i = (a != undefined) ? (b + ((a - b) * diff)) : undefined;
  375. if(!!c){
  376. g = !!c ? (d['x'] + ((c['x'] - d['x']) * diff)) : 0;
  377. h = !!c ? (d['y'] + ((c['y'] - d['y']) * diff)) : 0;
  378. obj['focus'](g, h, i);
  379. }else if(i != undefined){
  380. obj['zoom'](i);
  381. }
  382. return true;
  383. }
  384. }
  385. return false;
  386. }
  387. renderer.prototype['bounds'] = function(){
  388. var
  389. obj = this,
  390. content = obj['contentNode'],
  391. zoom = obj['zoom'](),
  392. zoom_a = .5 + (.5 * (1 - (zoom % 1))),
  393. zoom_b = 1 << Math.floor(zoom),
  394. focus = obj['focus'](),
  395. cWidth = content['width'],
  396. cHeight = content['height'],
  397. tWidth = (obj.tileSource['size']['width'] * zoom_a) / zoom_b,
  398. tHeight = (obj.tileSource['size']['height'] * zoom_a) / zoom_b,
  399. wView = Math.ceil(cWidth / tWidth),
  400. hView = Math.ceil(cHeight / tHeight),
  401. wVhalf = Math.ceil(wView / 2.0),
  402. hVhalf = Math.ceil(hView / 2.0)
  403. ;
  404. return new bounds({'x': focus['x'] - wVhalf, 'y': focus['y'] - hVhalf},{'x': focus['x'] + wVhalf, 'y': focus['y'] + hVhalf});
  405. }
  406. renderer.prototype['tileSize'] = function(){
  407. var
  408. obj = this,
  409. zoom = obj['zoom'](),
  410. zoom_a = .5 + (.5 * (1 - (zoom % 1))),
  411. zoom_b = 1 << Math.floor(zoom),
  412. tWidth = (obj['tileSource']['size']['width'] * zoom_a) / zoom_b,
  413. tHeight = (obj['tileSource']['size']['height'] * zoom_a) / zoom_b
  414. ;
  415. return new size(tWidth, tHeight);
  416. }
  417. renderer.prototype['addShape'] = function(){ // bool return indicates whether shape was added
  418. var
  419. ret = false
  420. ;
  421. if(shape != undefined){
  422. for(var i=0;i<arguments.length;++i){
  423. if(arguments[i] instanceof shape && this['opts']['shapes']['indexOf'](arguments[i]) < 0){
  424. ret = true;
  425. this['opts']['shapes']['push'](arguments[i]);
  426. }
  427. }
  428. }
  429. return ret;
  430. }
  431. renderer.prototype['removeShape'] = function(){
  432. var
  433. ret = false
  434. ;
  435. if(shape != undefined){
  436. for(var i=0;i<arguments.length;++i){
  437. if(arguments[i] instanceof shape){
  438. var
  439. pos = this['opts']['shapes']['indexOf'](arguments[i])
  440. ;
  441. if(pos >= 0){
  442. ret = true;
  443. this['opts']['shapes']['splice'](pos,1);
  444. }
  445. }
  446. }
  447. }
  448. return ret;
  449. }
  450. renderer.prototype['shapes'] = function(){
  451. return this['opts']['shapes'];
  452. }
  453. mapapi['renderer'] = renderer;
  454. mapapi['renderers'] = {};
  455. })(window);