/**
* javascript library for TiledImage
* $Id: TiledImage.js 16 2013-04-13 09:02:12Z nazotoko $
* @author Copyright 2013 Shun N. Watanabe <nazotoko (a+) users.sourceforge.net>
* @module TiledImage
* @main
*/
/**
* TiledImage top level Object. Before calling the contsructor, create div element
* in HTML part. This constructor needs the id name of the div element.
* @example
<div id="id_name" style="height:123px;width:456px;"></div>
<script>
new TiledImage("id_name")
.addLayer(new TilesLayer("tile","test/z${z}/${x}_${y}.jpg",4000,3000))
.addControl(new ButtonControl());
</script>
* @class TiledImage
* @constructor
* @parm id {String} Id of the div for top level
**/
function TiledImage(id){
/**
* self: private scope of this
* @property self
* @type object
* @private
* @default this
*/
var self=this,
/**
* property of current zoom
* @property z_
* @private
* @type Number
* @default 0
*/
z_=0,
/**
* maxSize
* @property maxSize
* @private
* @type Array[Number,Number]
* @default [0,0]
*/
maxSize=[0,0],
/**
* wrap flag
* @property wrap
* @private
* @type Array[boolean,boolean]
* @default [false,false]
*/
wrap=[false,false],
/**
* position of top-left
* @property position
* @private
* @type Array[Number,Number]
* @default [0,0]
*/
position=[0,0],
/**
* windowSize
* @property windowSize
* @private
* @type Array[Number,Number]
* @default [0,0]
*/
windowSize=[0,0],
/**
* max Zoom level
* @property maxZ
* @private
* @type Number
* @default 0
*/
maxZ=0,
/**
* min Zoom level
* @property minZ
* @private
* @type Number
* @default 10
*/
minZ=10,
/**
* array of Controls
* @property aCont
* @private
* @type Array[Object]
* @default []
*/
aCont=[],
/**
* array of Layer object
* @property aLayers
* @private
* @type Array[Object]
* @default []
*/
aLayers=[],
/**
* array of Layer Listener
* @property aChgL
* @private
* @type Array[Object]
* @default []
*/
aChgL=[],
/**
* array of move Listener
* @property aChgM
* @private
* @type Array[Object]
* @default []
*/
aChgM=[],
/**
* array of zoom Listener
* @property aChgZ
* @private
* @type Array[Object]
* @default []
*/
aChgZ=[],
/**
* array of resize Listener
* @property aChgS
* @private
* @type Array[Object]
* @default []
*/
aChgS=[];
var
/**
* remove all the elements in each Layers
* @private
* @method removeAll
*/
removeAll=function(){
for(var i in aLayers){
aLayers[i].l.removeAll();
}
},
/**
* append elements in each Layers within the view
* @private
* @method append
*/
append=function(){
var p=position,w=windowSize,i;
for(i in aLayers){
if(aLayers[i].show){
aLayers[i].l.append(p[0],p[1],p[0]+w[0],p[1]+w[1]);
}
}
for(i in aChgM){
aChgM[i](self.getPosition());
}
},
/**
* wrapPosition
* @private
* @method wrapPosition
*/
wrapPosition=function(){
var t,i=2;
while(--i>=0){
t= maxSize[i]>>(maxZ-z_);
while(position[i]<-t){
position[i]+=t;
}
position[i]%=t;
}
};
d.mod(this,{
/**
* set zoom level
* @public
* @method setZoom
* @param {Number} z zoom level
*/
setZoom:function(z){
if(z>maxZ)
z=maxZ;
else if(z<minZ)
z=minZ;
if(z==z_)return;
removeAll();
wrapPosition();
var p=position,w=windowSize,i=2,dz=z-z_;
while(--i>=0){
p[i]+=w[i]/2;
if(dz>0){ // zooming
p[i]<<=dz;
}else {
p[i]>>=-dz;
}
}
z_=z;
for(i in aLayers){
aLayers[i].l.setZoom(z);
}
for(i in aChgZ){
aChgZ[i](this.getZooms());
}
this.move(-w[0]/2,-w[1]/2);
},
/**
* set position (absolute jump)
* @public
* @method setPosition
* @param {Number} x position of originl image in x
* @param {Number} y position of originl image in y
*/
setPosition:function(x,y){
var z=maxZ-z_;
position[0]=(x>>z)-windowSize[0]/2;
position[1]=(y>>z)-windowSize[1]/2;
d.go(this.map,[-position[0],-position[1]]);
append();
},
/**
* get absolute position (jump)
* @public
* @method getPosition
* @return {Array} [x,y] position of originl image in x,y
*/
getPosition:function(){
var z=maxZ-z_,
w=windowSize,
x=[],i=2;
while(--i>=0){
x[i]=(position[i]+w[i]/2)<<z;
}
return x;
},
/**
* move pixels in the zoom level
* @public
* @method move
* @param {Number} x pixels in the view in x
* @param {Number} y pixels in the view in y
*/
move:function(x,y){
var z=maxZ-z_,
p=position,
i=2,t;
p[0]+=x;p[1]+=y;
while(--i>=0){
if(!wrap[i]){
t=(maxSize[i]>>z)-windowSize[i];
if(t<0){
p[i]=t/2;
} else if(p[i]<0){
p[i]=0;
}else if(p[i]>t){
p[i]=t;
}
}
}
d.go(this.map,[-p[0],-p[1]]);
append();
},
/**
* get curent zoom level
* @public
* @method getZoom
* @return {Number} current zoom level
*/
getZoom:function(){
return z_;
},
/**
* get min, curent, max zoom levels
* @public
* @method getZooms
* @return {Array[Number,Number,Number]} [min, curent, max] zoom levels
*/
getZooms:function(){
return [minZ,z_,maxZ];
},
/**
* get current windows size
* @public
* @method getWindowSize
* @return {Array[Number,Number]} curent window size
*/
getWindowSize:function(){
return [windowSize[0],windowSize[1]];
},
/**
* add Control objects under this TiledImage object
* @public
* @method addControl
* @chainable
* @return this
*/
addControl:function(cont){
var elist=cont.setParent(this),
i=0,o;
for(;i<elist.length;i++){
o=elist[i];
if(o.m=='changelayer'){
/**
* Fire if layers status changed.
* @event changelayer
* @param aLayers {Array[Object]} array of layers status object.
* <ul><li>object{<ul>
* <li>l: layer object</li>
* <li>show: boolean for the showing status</li>
* </ul></li>
* </ul>
*/
aChgL[aChgL.length]=o.f;
o.f(aLayers);
} else if(o.m=='changezoom'){
/**
* Fire if zooms status changed.
* @event changezoom
* @param zooms {Array[Object]} array of [minZ, curZ, maxZ].
*/
aChgZ[aChgZ.length]=o.f;
o.f(this.getZooms());
} else if(o.m=='resize'){
/**
* Fire if the view window size changed.
* @event resize
* @param windowSize {Array[Number,Number]} array of [width, height].
*/
aChgS[aChgS.length]=o.f;
o.f(this.getWindowSize());
} else if(o.m=='move'){
/**
* Fire if the view is moved.
* @event move
* @param position {Array[Number,Number]} array of absolute Position.
*/
aChgM[aChgM.length]=o.f;
o.f(this.getPosition());
} else {
d.addEv(o);
}
}
aCont[aCont.length]=cont;
return this
},
/**
* Add Layers objects under this TiledImage object
* @public
* @method addLayer
* @chainable
* @return this
*/
addLayer:function(layer,over){
aLayers[aLayers.length]={l:layer,show:true,over:over};
layer.setParent(this);
var i,z,max=layer.getWrap();
for(i=0;i<2;i++){
wrap[i]|=max[i];
}
max=layer.getMaxZ();
if (max>maxZ){
maxZ=max;
}
max=layer.getMaxSize();
z=0;
for(i=0;i<2;i++){
if(maxSize[i]<max[i]){
maxSize[i]=max[i];
while(max[i]>>z >windowSize[i]){
z++;
}
}
}
if(minZ>maxZ-z){
minZ=maxZ-z;
}
if(z_<minZ || z_>maxZ){
this.setZoom(z_);
}else {
layer.setZoom(z_);
append();
}
for(i in aChgL){
aChgL[i](aLayers);
}
return this
},
/**
* modulate Layer attribute
* @public
* @method modLayer
* @param {String} name nameof the layer
* @param {boolean} v visibility
*/
modLayer:function(name,v){
var i,ls=aLayers;
for(i in aLayers){
if(ls[i].l.getName()==name){
ls[i].show=v;
if(v){
append();
} else {
ls[i].l.removeAll();
}
}
}
for(i in aChgL){
aChgL[i](aLayers);
}
},
/**
* removeLayer attribute
* @public
* @method removeLayer
* @param {String} name of the layer
*/
removeLayer:function(name){
var i,mz=0,ms=[0,0],z;
for(i=0;i<aLayers.length;i++){
if(aLayers[i].l.getName()==name){
aLayers[i].l.detach();
z=i;
while(z<aLayers.length-1){
aLayers[z]=aLayers[z+1];
z++;
}
aLayers.length--;
i--;
} else {
z=aLayers[i].l.getMaxZ();
if(mz<z){mz=z;}
z=aLayers[i].l.getMaxSize();
if(ms[0]<z[0]){ms[0]=z[0];}
if(ms[1]<z[1]){ms[1]=z[1];}
}
}
maxZ=mz;
maxSize=ms;
for(z=i=0;i<2;i++){
while(ms[i]>>z >windowSize[i]){
z++;
}
}
minZ=mz-z;
this.setZoom(z_);
for(i in aChgL){
aChgL[i](aLayers);
}
},
/**
* get Max Size
* @public
* @method getMaxSize
* @return {Array} maxSize
*/
getMaxSize:function(){
var r=[maxSize[0],maxSize[1]];
return r;
},
/**
* resize windows
* @public
* @method resize
* @param {Number} w width of the window
* @param {Number} h height of the window
*/
resize:function(w,h){
windowSize=[w,h];
d.mod(this.target,{style:{width:w+'px',height:h+'px'}});
append();
for(var i in aChgS){
aChgS[i](this.getWindowSize());
}
}
});
// initialize
var e;
/**
* The target DOM Element of TiledImage
* @property target
* @public
* @type DOMElement
*/
(e=this.target=d.get(id)).className='tiledImage'; // top layer
windowSize=[
e.style.width.replace('px','')-0,
e.style.height.replace('px','')-0
];
/**
* DOM Element of the moving layer
* @property map
* @public
* @type DOMElement
*/
d.go(this.map=d.app(e,'div', // map layer
{className:'map',onselectstart:'return false;'}),[0,0]);
};