PageRenderTime 39ms CodeModel.GetById 20ms app.highlight 16ms RepoModel.GetById 0ms app.codeStats 0ms

/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
 40	function each(array, cb){
 41		for(var i=0;i<array.length;++i){
 42			cb(array[i],i);
 43		}
 44	}
 45
 46	function dblclick_handler(e){
 47		var
 48			obj   = this,
 49			point = e['pos'],
 50			zoom  = obj['zoom']() - 1
 51		;
 52		if(this['smoothZoom']()){
 53			this['animate']({
 54				'zoom'  : zoom,
 55				'focus' : point
 56			}, .5);
 57		}else{
 58			obj['zoom'](zoom);
 59			obj['focus'](point);
 60		}
 61	}
 62	function dragpan(e){
 63		var
 64			obj = this,
 65			pos = e['to']
 66		;
 67		obj['focus'](pos);
 68	}
 69
 70/**
 71*	@constructor
 72*/
 73	function renderer(){
 74		var
 75			obj        = this
 76		;
 77		EventTarget['call'](this);
 78
 79		obj['opts'] = {'shapes':new mapapi['shapeManager']};
 80		obj['_focus'] = new gridPoint(0,0);
 81	}
 82
 83	renderer.prototype = new EventTarget();
 84	renderer.prototype['constructor'] = renderer;
 85	renderer.prototype['browserSupported'] = false;
 86
 87	renderer.prototype['options'] = function(options){
 88		var
 89			obj       = this,
 90			options   = options || {},
 91			opts      = obj['opts'],
 92			container = options['container'],
 93			hasFunc   = ['minZoom', 'maxZoom', 'scrollWheelZoom', 'smoothZoom', 'draggable', 'dblclickZoom', 'zoom', 'focus', 'panUnitLR', 'panUnitUD'],
 94			checkFunc
 95		;
 96
 97		options['minZoom']   = options['minZoom']   || 0;
 98		options['maxZoom']   = options['maxZoom']   || 0;
 99		options['panUnitLR'] = options['panUnitLR'] || 0;
100		options['panUnitUD'] = options['panUnitUD'] || 0;
101
102		for(var i=0;i<hasFunc.length;++i){
103			var
104				checkFunc = hasFunc[i]
105			;
106			if(options[checkFunc] != undefined){
107				obj[checkFunc](options[checkFunc]);
108			}
109		}
110
111		obj['addListener']('drag', dragpan);
112		obj['addListener']('click', function(e){
113			opts['shapes']['click'](e['pos']);
114		});
115
116		if(container){
117			if(!container['appendChild']){
118				throw 'Container is invalid';
119			}else{
120				opts['container'] = container;
121				if(obj['contentNode']){
122					obj['contentNode']['style']['width']  = '100%';
123					obj['contentNode']['style']['height'] = '100%';
124					empty(container)['appendChild'](obj['contentNode']);
125				}
126			}
127		}
128	}
129
130	renderer.prototype['minZoom'] = function(value){
131		var
132			opts = this['opts']
133		;
134		if(value != undefined){
135			opts['minZoom'] = Math.max(0, value);
136		}
137		return opts['minZoom'];
138	};
139
140	renderer.prototype['maxZoom'] = function(value){
141		var
142			opts = this['opts']
143		;
144		if(value != undefined){
145			opts['maxZoom'] = Math.max(this.minZoom() + 1, value);
146		}
147		return opts['maxZoom'];
148	}
149
150	renderer.prototype['zoom'] = function(value){
151		var
152			obj  = this,
153			opts = obj['opts']
154		;
155		if(value != undefined){
156			opts['zoom'] = Math.min(Math.max(value, obj['minZoom']()), obj['maxZoom']());
157		}
158		return opts['zoom'];
159	}
160
161	renderer.prototype['panUnitUD'] = function(value){
162		var
163			opts = this['opts']
164		;
165		if(value){
166			opts['panUnitUD'] = Math.max(value, 1);
167		}
168		return opts['panUnitUD'] * Math.pow(2, this['zoom']());
169	}
170
171	renderer.prototype['panUnitLR'] = function(value){
172		var
173			opts = this['opts']
174		;
175		if(value){
176			opts['panUnitLR'] = Math.max(value, 1);
177		}
178		return opts['panUnitLR'] * Math.pow(2, this['zoom']());
179	}
180
181	renderer.prototype['panTo'] = function(pos, a){
182		if(typeof pos == 'number' && typeof a == 'number'){
183			pos = new gridPoint(pos, a);
184		}else if(typeof pos == 'object' && typeof pos['x'] == 'number' && typeof pos['y'] == 'number'){
185			pos = new gridPoint(pos['x'], pos['y']);
186		}
187		if(pos instanceof gridPoint){
188			if(this['bounds']()['isWithin'](pos)){
189				this['animate']({
190					'focus' : pos
191				}, .5);
192			}else{
193				this['focus'](pos);
194			}
195		}
196	}
197
198	renderer.prototype['panUp'] = function(){
199		var pos = this.focus();
200		this.panTo(pos['x'], pos['y'] + this.panUnitUD());
201	}
202
203	renderer.prototype['panDown'] = function(){
204		var pos = this.focus();
205		this.panTo(pos['x'], pos['y'] - this.panUnitUD());
206	}
207
208	renderer.prototype['panLeft'] = function(){
209		var pos = this.focus();
210		this.panTo(pos['x'] - this.panUnitLR(), pos['y']);
211	}
212
213	renderer.prototype['panRight'] = function(){
214		var pos = this.focus();
215		this.panTo(pos['x'] + this.panUnitLR(), pos['y']);
216	}
217
218	renderer.prototype['scrollWheelZoom'] = function(flag){
219		var
220			opts = this['opts']
221		;
222		if(flag != undefined){
223			opts['scrollWheelZoom'] = !!flag;
224		}
225		return opts['scrollWheelZoom'];
226	}
227
228	renderer.prototype['smoothZoom'] = function(flag){
229		var
230			obj  = this,
231			opts = obj['opts']
232		;
233		if(flag != undefined){
234			opts['smoothZoom'] = !!flag;
235		}
236		return opts['smoothZoom'];
237	}
238
239	renderer.prototype['draggable'] = function(flag){
240		if(flag != undefined){
241			if(flag){ // do stuff to make the map renderer draggable
242				return true;
243			}else{ // do stuff to make it non-draggable
244				return false;
245			}
246		}
247		return flag; // should return from other property
248	}
249
250	renderer.prototype['focus'] = function(pos, zoom, a){ // should return an instance of mapapi.gridPoint
251		if(typeof pos == 'number'){
252			pos  = new mapapi['gridPoint'](pos, zoom);
253			zoom = this['zoom']();
254		}
255		if(zoom != undefined){
256			this['zoom'](zoom);
257		}
258		var
259			opts = this['opts']
260		;
261		if(pos instanceof mapapi['gridPoint']){ // implementations should do something to update the renderer to the focal point
262			opts['focus'] = pos;
263			this['fire']('focus_changed', {'pos':pos, 'withinBounds' : this['bounds']()['isWithin'](pos)});
264		}
265		return opts['focus'];
266	}
267
268	renderer.prototype['px2point'] = function(x, y){
269		var
270			obj     = this,
271			content = obj['contentNode'],
272			cWidth  = content['width'],
273			cw2     = cWidth / 2.0,
274			cHeight = content['height'],
275			ch2     = cHeight / 2.0,
276			size    = obj['tileSize'](),
277			distX   = (x - cw2) / size['width'],
278			distY   = ((cHeight - y) - ch2) / size['height'],
279			focus   = obj['focus']()//,
280			mapX    = focus['x'] + distX,
281			mapY    = focus['y'] + distY
282		;
283		return new gridPoint(mapX, mapY);
284	}
285
286	renderer.prototype['point2px'] = function(x, y){
287		if(x instanceof gridPoint){
288			y = x['y'];
289			x = x['x'];
290		}
291		var
292			content = this['contentNode'],
293			cWidth  = content['clientWidth'],
294			cw2     = cWidth / 2.0,
295			cHeight = content['clientHeight'],
296			ch2     = cHeight / 2.0,
297			size    = this['tileSize'](),
298			focus   = this['focus'](),
299			fx      = focus['x'],
300			fy      = focus['y'],
301			diffX   = (x - fx) * size['width'],
302			diffY   = (y - fy) * size['height']
303		;
304		return {'x':cw2 + diffX, 'y':ch2 - diffY};
305	}
306
307	renderer.prototype['dblclickZoom'] = function(flag){
308		var
309			obj  = this,
310			opts = obj['opts']
311		;
312		if(flag != undefined){
313			opts['dblclickZoom'] = !!flag;
314			if(obj['contentNode']){
315				if(!!flag){
316					obj['addListener']('dblclick', dblclick_handler);
317				}else{
318					obj['removeListener']('dblclick', dblclick_handler);
319				}
320			}
321		}
322		return opts['dblclickZoom']; // should return from other property
323	}
324
325	renderer.prototype['animate'] = function(opts, time){
326		if(opts == undefined || (opts != undefined && typeof time != 'number')){
327			return;
328		}
329		var
330			obj       = this,
331			time      = (!time || time < 0) ? 1 : time,
332			czoom     = obj['zoom'](),
333			mnzm      = obj['minZoom'](),
334			mxzm      = obj['maxZoom'](),
335			cpos      = obj['focus'](),
336			gridPoint = mapapi['gridPoint'],
337			zoom,
338			pos,
339			animateOrder
340		;
341		if(typeof opts == 'number'){
342			opts = {'zoom':opts};
343		}else if(opts instanceof gridPoint || (typeof opts == 'object' && typeof opts['x'] == 'number' && typeof opts['y'] == 'number')){
344			opts = {'focus':(opts instanceof gridPoint) ? opts : new gridPoint(opts['x'],opts['y'])};
345		}
346		pos = animateOrder = !1;
347		if(opts['zoom'] != undefined){
348			zoom = (typeof opts['zoom'] == 'number') ? opts['zoom'] : (opts['zoom'] * 1);
349			zoom = (zoom != czoom && zoom >= mnzm && zoom <= mxzm) ? zoom : undefined;
350		}
351		if(opts['focus'] instanceof gridPoint){
352			pos  = (opts['focus']['x'] != cpos['x'] || opts['focus']['y'] != cpos['y']) ? opts['focus'] : !1;
353		}
354		var
355			a = (zoom != undefined),
356			b = !!pos
357		;
358		if(a || b){
359			animateOrder = {};
360			if(zoom != undefined){
361				animateOrder['zoom']      = zoom;
362				animateOrder['fromZoom']  = czoom;
363			}
364			if(b){
365				animateOrder['focus']     = pos;
366				animateOrder['fromFocus'] = cpos;
367			}
368			animateOrder['start'] = (new Date().getTime()) / 1000;
369			animateOrder['end']   = animateOrder['start'] + time;
370			obj['animateOrder']   = animateOrder;
371		}
372	}
373	renderer.prototype['doAnimation'] = function(){
374		var
375			obj = this,
376			ao  = obj['animateOrder']
377		;
378		if(!ao){
379			return false;
380		}
381		var
382			a   = ao['zoom'],
383			b   = ao['fromZoom'],
384			c   = ao['focus'],
385			d   = ao['fromFocus'],
386			e   = ao['start'],
387			f   = ao['end'],
388			now,diff,g,h,i
389		;
390		if(!!ao){
391			now  = (new Date().getTime()) / 1000;
392			diff = (now - e) / (f - e);
393			if(now >= ao['end']){
394				if(!!c){
395					obj['focus'](c, !!a ? a : undefined);
396				}else if(!!a){
397					obj['zoom'](a);					
398				}
399				obj['animateOrder'] = !1;
400				return true;
401			}else if(now > ao['start']){
402				i = (a != undefined) ? (b + ((a - b) * diff)) : undefined;
403				if(!!c){
404					g = !!c ? (d['x'] + ((c['x'] - d['x']) * diff)) : 0;
405					h = !!c ? (d['y'] + ((c['y'] - d['y']) * diff)) : 0;
406					obj['focus'](g, h, i);
407				}else if(i != undefined){
408					obj['zoom'](i);
409				}
410				return true;
411			}
412		}
413		return false;
414	}
415
416	renderer.prototype['bounds'] = function(){
417		var
418			obj     = this,
419			content = obj['contentNode'],
420			zoom    = obj['zoom'](),
421			zoom_a  = .5 + (.5 * (1 - (zoom % 1))),
422			zoom_b  = 1 << Math.floor(zoom),
423			focus   = obj['focus'](),
424			cWidth  = content['width'],
425			cHeight = content['height'],
426			tWidth  = (obj.tileSource['size']['width'] * zoom_a) / zoom_b,
427			tHeight = (obj.tileSource['size']['height'] * zoom_a) / zoom_b,
428			wView   = Math.ceil(cWidth / tWidth),
429			hView   = Math.ceil(cHeight / tHeight),
430			wVhalf  = Math.ceil(wView / 2.0),
431			hVhalf  = Math.ceil(hView / 2.0)
432		;
433		return new bounds({'x': focus['x'] - wVhalf, 'y': focus['y'] - hVhalf},{'x': focus['x'] + wVhalf,  'y': focus['y'] + hVhalf});
434	}
435
436	renderer.prototype['tileSize'] = function(){
437		var
438			obj = this,
439			zoom    = obj['zoom'](),
440			zoom_a  = .5 + (.5 * (1 - (zoom % 1))),
441			zoom_b  = 1 << Math.floor(zoom),
442			tWidth  = (obj['tileSource']['size']['width'] * zoom_a) / zoom_b,
443			tHeight = (obj['tileSource']['size']['height'] * zoom_a) / zoom_b
444		;
445		return new size(tWidth, tHeight);
446	}
447
448	renderer.prototype['addShape'] = function(){ // bool return indicates whether shape was added
449		var
450			ret = false
451		;
452		if(shape != undefined){
453			for(var i=0;i<arguments.length;++i){
454				if(arguments[i] instanceof shape && this['opts']['shapes']['indexOf'](arguments[i]) < 0){
455					ret = true;
456					this['opts']['shapes']['push'](arguments[i]);
457				}
458			}
459		}
460		return ret;
461	}
462
463	renderer.prototype['removeShape'] = function(){
464		var
465			ret = false
466		;
467		if(shape != undefined){
468			for(var i=0;i<arguments.length;++i){
469				if(arguments[i] instanceof shape){
470					var
471						pos = this['opts']['shapes']['indexOf'](arguments[i])
472					;
473					if(pos >= 0){
474						ret = true;
475						this['opts']['shapes']['splice'](pos,1);
476					}
477				}
478			}
479		}
480		return ret;
481	}
482
483	renderer.prototype['shapes'] = function(){
484		return this['opts']['shapes'];
485	}
486
487	mapapi['renderer']  = renderer;
488	mapapi['renderers'] = {};
489})(window);