﻿//Visor Web
function WVViewerNode()
{
    var oThis           = this;
    var urlBase         = "";
    var tileWidth       = 256;  //tamaño de tiles
    var tileHeight      = 256;
    var container       = null; //contenedor (DIV) principal del visor
    var containerWidth  = 0;    //tamaño de contenedor
    var containerHeight = 0;
    
    var isInitialized   = false;          //indica si el visor se ha inicializado OK
    var isUpdating      = false;          //indica si está actualizando el estado interno del visor (capas, comportamientos, etc.)
    var vOffset         = new Vector2D(); //vector de desplazamiento para la vista (scrolling, etc.)
    var lastOffset      = {x:0,y:0};      //último desplazamiento aplicado al visor
    
    var baseID          = "";   //identificador primario sobre el que se construyen los demás controles    
    var visorGUID       = ""; 
    var currentScale    = 0;    //escala actual del visor
    var isPower2Scale   = true; //define si la escala será siempre potencia de 2
    var centerIniUtmX   = 0;    //utm central del visor
    var centerIniUtmY   = 0;
    var currentUtmX     = 0;    //UTMs de la posición del puntero del ratón sobre el visor
    var currentUtmY     = 0;
    var bounds          = 0;    //bounding de la cartografía
    var allowFit        = true; //indica si se puede hacer FitView
    var allowPanning    = true; //indica si se puede hacer Panning
    var allowZooming    = true; //indica si se puede hacer Zooming
    var scaleMin        = 1;    //escala mínima a la que se puede visualizar
    var scaleMax        = 1;    //escala máxima a la que se puede visualizar
    var superBounds     = 0;    //bounding box para ajustarse a la caché de disco en el raster
    var confString      = "WebViewerConf"; //cadena para leer/escribir configuración en cookie
    var hasOlderVisualization = false //indica si se debe registrar la última localización
    var rasterOpacity   = false;
    var vectorOpacity   = false;
    
    var systemLayers            = new Array();  //lista de capas de sistema
    var customLayers            = new Array();  //lista de capas del usuario
    var systemBehaviors         = new Array();  //lista de comportamientos de sistema
    var systemControllers       = new Array();  //lista de controladores de sistema
    var customBehaviors         = new Array();  //lista de comportamientos del usuario
    var activeBehavior          = null;         //comportamiento activo
    var defaultBehavior         = null;         //comportamiento por defecto
    var defaultBehaviorType     = null;         //tipo del comportamiento por defecto
    var defaultBehaviorLInit    = null;         //lista de acciones a realizar cuando inicie el comportamiento por defecto
    var defaultBehaviorLFinalize= null;         //lista de acciones a realizar cuando finalice el comportamiento por defecto
    var browser                 = null          //navegador; cross-browsing compatible
    
    var lstEvtChangeBehavior    = new Array();  //lista con callbacks para notificar eventos de cambio de comportamiento activo
    var lstEvtChangeScale       = new Array();  //lista con callbacks para notificar eventos de cambio de escala en el visor
    var lstEvtMouseMove         = new Array();  //lista con callbacks para notificar evento mousemove
    var lstEvtLeftClick         = new Array();  //lista con callbacks para notificar evento leftclick
    var lstEvtRightClick        = new Array();  //lista con callbacks para notificar evento rightclick
    var lstEvtDoubleClick       = new Array();  //lista con callbacks para notificar evento doubleclick
    
    var cbOnLoad; //callback para notificar inicialización correcta del visor
    var cbOnError;//callback para notificar error en la inicialización del visor
    
    var imgLoading      = null;     //imagen de carga de capas
    var defaultCursor   = "default";//cursor por defecto
    var zIndexBase      = 0;        //índice Z del contenedor del visor
    var pingSeedCache   = 0;        //semilla para no cachear URLs de ping
    
    var isWindowResizing = false;   //indica si se está redimensionando la ventana
    var originalWidth = 0; //tamaños originales del contenedor del visor
    var originalHeight= 0;
    var idTimerResize = null; //Timer para notificar Resize de ventana
    
    //opt. lookup
    var containerWidth_Div2;
    var containerHeight_Div2;
    
    var domains=new Array(); //lista de nombres de dominio
    
    this.init = function (parentContainerID, GUID, center, widthView, heightView, bounding, canFitView, url, domainServers)
    {
        if (domainServers!=null && domainServers!="")
        {
            domainServers = domainServers.split("@");
            for(var i=0;i<domainServers.length;i++)
            {
                var domain = domainServers[i];
                if (domain!="" && domain!=null) domains.push(domain);
            }
        }
        
        baseID = parentContainerID == "" ? parentContainerID : parentContainerID + "_";
        container = document.getElementById(baseID + GUID + "_divFrame");
        if (container==null) //iteramos desde root hasta encontrar div del visor
        {
            var parentCtrls = parentContainerID.split('_');
            baseID = "";
            for(var i=0;i<parentCtrls.length;i++)
            {
                container = document.getElementById(baseID + GUID + "_divFrame");
                if (container!=null) break;
                baseID+=parentCtrls[i]+"_";
            }
        }
        container.style.zIndex  = zIndexBase;
        urlBase         = url;
        visorGUID       = GUID;
        allowFit        = canFitView;
        bounds          = bounding;
        
        containerWidth  = container.offsetWidth <=0 ? container.parentNode.offsetWidth : container.offsetWidth;
        containerHeight = container.offsetHeight <=0 ? container.parentNode.offsetHeight : container.offsetHeight;
        containerWidth_Div2     = containerWidth >> 1;
        containerHeight_Div2    = containerHeight >> 1;
        
        originalWidth  = container.style.width;
        originalHeight = container.style.height;
        
        container.style.width   = containerWidth;
        container.style.height  = containerHeight;
        
        browser = new WVBrowser(); //Navegador cross-browsing compatible
        browser.init(this);
        
        //escalas Min/Max
        var scalingData = document.getElementById(baseID + GUID + "_scaletypeParams").value.split('@');
        isPower2Scale = parseInt(scalingData[1]) > 0 ? false : true;
        scaleMin = calcBestScale(parseInt(scalingData[0])); 
        var sW = Math.max(bounds.width/(containerWidth-5));
        var sH = Math.max(bounds.height/(containerHeight-5));
        scaleMax = calcBestScale(Math.max(sW, sH));
        
        if (isPower2Scale)
        {
            superBounds = calcSuperBounds(bounds);
        }
        
        //localización inicial
        hasOlderVisualization = document.getElementById(baseID + GUID + "_olderVisualization").value;
        hasOlderVisualization = parseInt(hasOlderVisualization) > 0 ? true : false;
        if (!hasOlderVisualization)
        {
            browser.deleteCookie(confString, null, null);
            oThis.setCenterUtm(center); 
            var sW = Math.max(widthView/(containerWidth-5));
            var sH = Math.max(heightView/(containerHeight-5));
            oThis.setScale(Math.max(sW, sH));
        }
        else
        {
            var cookie = browser.getCookie(confString);
            if (cookie==null)
            {
                oThis.setCenterUtm(center);
                var sW = Math.max(widthView/containerWidth);
                var sH = Math.max(heightView/containerHeight);
                oThis.setScale(Math.max(sW, sH));
            }
            else
            {
                var data = cookie.split('@');
                oThis.setCenterUtm({x:parseInt(data[1]),y:parseInt(data[2])});
                oThis.setScale(parseInt(data[0]));
            }
        }      
        
        //ajuste de capas y elementos en profundidad
        var divFence = document.getElementById( baseID + GUID + "_divFence" );
        var divUI = document.getElementById( baseID + GUID + "_divUI" );
        if (divFence!=null) divFence.style.zIndex = zIndexBase + 40;
        if (divUI!=null)    divUI.style.zIndex = zIndexBase + 50;
        
        imgLoading = document.getElementById(baseID + GUID + "_imgWait"); //imagen de carga de capas
        
        oThis.resetCursor(); 
        //setupViewer();
        setupViewerJSON();
        //document.body.onload = function() {setupViewer();};
        //document.body.attachEvent("onload",setupViewer);
    }
    
    //Asigna/retorna si hay animación de opacidad para la capa raster
    this.setRasterOpacity=function(opacity)
    {
        rasterOpacity = opacity;
    }
    this.getRasterOpacity=function()
    {
        return rasterOpacity;
    }
    
    //Asigna/retorna si hay animación de opacidad para la capa vectorial
    this.setVectorOpacity=function(opacity)
    {
        vectorOpacity = opacity;
    }
    this.getVectorOpacity=function()
    {
        return vectorOpacity;
    }
    
    //Retorna la lista de dominios sobre los que se realizarán las peticiones
    this.getDomains=function()
    {
        return domains;
    }
    
    //Retorna el SuperBounding utilizado para ajustarse a la caché de disco en el Raster
    this.getSuperBounds=function()
    {
        return superBounds;
    }
    
    //Retorna el índice Z del contenedor del visor
    this.getZIndexBase=function()
    {
        return zIndexBase;
    }
    
    //Retorna identificador base
    this.getBaseID=function()
    {
        return baseID;
    }
    
    //Retorna si el visor se ha cargado correctamente
    this.isLoaded = function()
    {
        return isInitialized;
    }
    
    //Retorna la dirección Http base para hacer peticiones
    this.getUrlBase=function()
    {
        return urlBase;
    }
    
    //Retorna navegador compatible
    this.getBrowser=function()
    {
        return browser;
    }
    
    //Retorna identificador del visor registrado en el componente del servidor (GUID)
    this.getGUID=function()
    {
        return visorGUID;
    }
    
    //Retorna ancho del visor
    this.getWidth =function()
    {
        return containerWidth;
    }
    
    //Retorna alto del visor
    this.getHeight =function()
    {
        return containerHeight;
    }
    
    //Retorna ancho del Tile
    this.getTileWidth=function()
    {
        return tileWidth;
    }
    
    //Retorna alto del Tile
    this.getTileHeight=function()
    {
        return tileHeight;
    }
    
    //Retorna los bounds de cartografía en UTM (minY,minY,w,h)
    this.getBounds=function()
    {
        return bounds;
    }
    
    //Retorna la escala mínima a la que se puede visualizar
    this.getScaleMin=function()
    {
        return scaleMin;
    }
    
    //Retorna la escala máxima a la que se puede visualizar
    this.getScaleMax=function()
    {
        return scaleMax;
    }
    
    //Retorna contenedor del visor
    this.getContainer = function()
    {
        return container;
    }
    
    //Propiedades para determinar si el visor puede hacer Panning
    this.getAllowPanning=function()
    {
        return allowPanning;
    }
    this.setAllowPanning=function(allow)
    {
        allowPanning = allow;
    }
    
    //Propiedades para determinar si el visor puede hacer Zooming
    this.getAllowZooming=function()
    {
        return allowZooming;
    }
    this.setAllowZooming=function(allow)
    {
        allowZooming = allow;
    }
    
    //Propiedades para determinar si el visor puede hacer FitView
    this.getAllowFit=function()
    {
        return allowFit;
    }
    this.setAllowFit=function(allow)
    {
        allowFit = allow;
    }
    
    //Retorna si el visor utiliza escalas en potencia de dos
    this.usePower2Scale = function()
    {
        return isPower2Scale;
    }
    
    //Retorna lista de capas creadas en el visor
    this.getSystemLayers=function()
    {
        return systemLayers;
    }
    
    //Retorna una capa en base a su LayerType
    this.getSystemLayer=function(type)
    {
        var result=null;
        var n=systemLayers.length;
        for(var i=0;i<n;i++)
            if (systemLayers[i].getLayerType().toString()==type.toString())
            {
                result=systemLayers[i];
                break;
            }
        return result;
    }
    
    //Crea y agrega una nueva capa de sistema
    this.createSystemLayer = function (type)
    {
        var layer = null;
        
        switch(type.toString())
        {
            case WVLayerType.RASTER.toString():
                layer = new WVRasterLayer();
                break;
            case WVLayerType.VECTOR.toString():
                layer = new WVVectorLayer();
                break;
            case WVLayerType.ICON.toString():
                layer = new WVIconLayer();
                break;
        }
        
        if (layer!=null)
        {
            systemLayers.push(layer);
            layer.init(oThis);
            container.appendChild(layer.getContainer());
        }
        return layer;
    }
    
    //Elimina una capa de sistema
    this.removeSystemLayer=function(type)
    {
        var n=systemLayers.length;
        if (n>0)
        {
            try
            {
                for(var i=0;i<n;i++)
                    if (systemLayers[i].getLayerType().toString()==type.toString())
                    {
                        container.removeChild(systemLayers[i].getContainer());
                        systemLayers[i].dispose();
                        systemLayers.splice(i,1);
                        return;
                    }
            }catch(e){}
        }
    }
    
    //Crea y retorna una nueva capa personalizada
    this.createCustomLayer = function (idLayer)
    {
        var layer = new WVCustomLayer();
        customLayers.push(layer);
        layer.init(oThis, idLayer);
        container.appendChild(layer.getContainer());
        
        return layer;
    }
    
    //Elimina una capa personalizada
    this.removeCustomLayer=function(layer)
    {
        var n=customLayers.length;
        if (n>0)
        {
            try
            {
                for(var i=0;i<n;i++)
                    if (customLayers[i]==layer)
                    {
                        container.removeChild(customLayers[i].getContainer());
                        customLayers[i].dispose();
                        customLayers.splice(i,1);
                        i=n;
                    }
            }catch(e){}
        }
    }
    
    //Retorna todas las capas personalizadas
    this.getCustomLayers=function()
    {
        return customLayers;
    }
    
    //Retorna una capa personalizada en base a su ID
    this.getCustomLayer=function(id)
    {
        var result=null;
        var n=customLayers.length;
        for(var i=0;i<n;i++)
            if (customLayers[i].getID()==id)
            {
                result=customLayers[i];
                break;
            }
        return result;
    }
    
    //Notifica a las capas que se recoloquen (operación de layout)
    this.redockLayers=function()
    {
        var n=systemLayers.length;
        for(var i=0;i<n;i++)
        {
            systemLayers[i].onRedock();
        }
        
        n=customLayers.length;
        for(var i=0;i<n;i++)
        {
            customLayers[i].onRedock();
        }
    }
    
    //Retorna la escala actual para la relación pixel:UTM
    this.getScale=function()
    {
        return currentScale;
    }
    
    //Asigna una nueva escala al Visor Web
    this.setScale=function(scale)
    {
        if (scale == currentScale) return;  
        scale = scale < scaleMin ? scaleMin : scale;   
        scale = scale > scaleMax ? scaleMax : scale; 
        currentScale=calcBestScale(scale);  
        notifyChangeScale();
    }
    
    //Agrega un comportamiento del usuario
    this.addCustomBehavior=function(b)
    {
        customBehaviors.push(b);
    }
    
    //Elimina un comportamiento del usuario
    this.removeCustomBehavior=function(b)
    {
        var n=customBehaviors.length;
        if (n>0)
        {
            try
            {
                for(var i=0;i<n;i++)
                    if (customBehaviors[i]==b)
                    {            
                        customBehaviors[i].setEnabled(false);    
                        customBehaviors[i].dispose();
                        customBehaviors.splice(i,1);
                        i=n;
                    }
            }catch(e){}
        }
    }
    
    //Registra un comportamiento de sistema
    this.addSystemBehavior = function(b)
	{
	    systemBehaviors.push(b);
	}
	
	//Retorna un comportamiento en base a su tipo
    this.getSystemBehavior = function (type)
    {
        var result = null;
        var n=systemBehaviors.length;
        if (n>0)
        {
            for(var i=0;i<n;i++)
                if (systemBehaviors[i].getBehaviorType().toString()==type.toString())
                {
                    result = systemBehaviors[i];
                    break;
                }
        }
        return result;
    }
    
    //Elimina un comportamiento del sistema
    this.removeSystemBehavior=function(type)
    {
        var n=systemBehaviors.length;
        if (n>0)
        {
            try
            {
                for(var i=0;i<n;i++)
                    if (systemBehaviors[i].getBehaviorType().toString()==type.toString())
                    {
                        systemBehaviors[i].setEnabled(false);
                        systemBehaviors.splice(i,1);
                        i=n;
                    }
            }catch(e){}
        }
    }
	
	//Registra un controlador de sistema
	this.addSystemController = function(c)
	{
	    systemControllers.push(c);
	}
	
    //Establece un comportamiento por defecto
    this.setDefaultBehavior = function(btype, listInit, listFinalize)
    {
        defaultBehavior             = this.getSystemBehavior(btype); 
        defaultBehaviorType         = btype;
        defaultBehaviorLInit        = listInit;
        defaultBehaviorLFinalize    = listFinalize;
        if(defaultBehavior != null) this.releaseBehavior();
    }
    
    //Establece un comportamiento como activo
    this.setActiveBehavior = function(b)
    {
        if(b==null || (activeBehavior == b || b.alwaysKeepEnabled()) )return;
        
        if( activeBehavior != null ) 
        {
            activeBehavior.setEnabled(false);
            activeBehavior.finalize();
            var n = lstEvtChangeBehavior.length; //notifica finalización de comportamiento anterior
            if (n>0)
                for( var i = 0; i < n; i++ )
                    lstEvtChangeBehavior[i]();
        }
        oThis.resetCursor();
        activeBehavior  = b;
        b.init( oThis );
        b.setEnabled(true);
    }
    
    //Libera el comportamiento actual y pone el definido por defecto
    this.releaseBehavior = function()
    {
        if( defaultBehaviorLInit != null )
        {
            var n = defaultBehaviorLInit.length;
            if (n>0)
            {
                for ( var i = 0; i < n; i++ )
                    defaultBehavior.registerCustomInit(defaultBehaviorLInit[i]);
            }
        }
        if( defaultBehaviorLFinalize != null )
        {
            var n = defaultBehaviorLFinalize.length;
            if (n>0)
            {
                for ( var i = 0; i < n; i++ )
                    defaultBehavior.registerCustomFinalize(defaultBehaviorLFinalize[i]);
            }
        }
        oThis.setActiveBehavior( defaultBehavior )
    }
    
    //Retorna un pintor de líneas, polígonos, etc. sobre una customLayer
    this.createLayerPainter=function(layer)
    {
        var layerDIV    = layer.getContainer();
        layerDIV.id     = layer.getID();
        var painter     = new jsGraphics(layerDIV.id);
        return painter;
    }
    
    //Suma un vector de desplazamiento a la vista actual
    this.addOffset=function(vector)
    {
        if (allowPanning)
        {
            vOffset.x +=vector.x;
            vOffset.y +=vector.y;
        }
    }
    
    //Retorna el último desplazamiento aplicado a la vista actual
    this.getLastOffset=function()
    {
        return lastOffset;
    }
    
    //retorna la posición actual en UTM del puntero del ratón sobre el visor
    this.getCurrentUtm=function()
    {
        return {x:currentUtmX,y:currentUtmY}; 
    }
    
    //Retorna la coordenada central UTM para el eje X
    this.getCenterUtmX=function()
    {
        return centerIniUtmX;
    }
    
    //Retorna la coordenada central UTM para el eje Y
    this.getCenterUtmY = function()
    {
        return centerIniUtmY;
    }
    
    //Retorna la coordenada central UTM del visor
    this.getCenterUtm = function()
    {
        return {x:centerIniUtmX , y:centerIniUtmY};
    }
    
    //Establece la posición central del visor en UTM (el parámetro es del tipo Vector2D)
    this.setCenterUtm = function( value )
    {
        centerIniUtmX   = value.x;
        centerIniUtmY   = value.y;
    }
    
    //Establece un nuevo cursor
    this.setCursor=function(cursor)
    {
        container.style.cursor=cursor;
    }
    
    //Establece el cursor por defecto
    this.resetCursor=function()
    {
        container.style.cursor=defaultCursor;
    }
    
    //Refresca una capa de cartografía en base a su tipo
    this.refreshSystemLayer=function (layerType)
    {
        var layer = this.getSystemLayer(layerType);
        if (layer!=null)
        {
            layer.renoveCache();
            layer.refresh();
        }
    }
    
    //Refresca la vista actual de las capas de cartografía registradas
    this.refresh = function(renoveCache)
    {
        renoveCache = (renoveCache==null || renoveCache==false) ? false : true;
        
        var layers = oThis.getSystemLayers();
        if (layers!=null)
        {
            var n=layers.length;
            //for(var i=0;i<n;i++) //refresca capas del sistema
            for(var i=n-1;i>=0;i--) //refresca capas del sistema
            {
                if (renoveCache) layers[i].renoveCache();
                if (layers[i].getVisible()) layers[i].refresh();
            }
        }
        
        layers = oThis.getCustomLayers();
        if (layers!=null)
        {
            var n=layers.length;
            for(var i=0;i<n;i++) //refresca capas del sistema
            {
                if (layers[i].getVisible())
                {
                    layers[i].refresh();
                }
            }
        }
    }
    
    //Actualización del nodo. Llamado por SceneManager a cada tick del Timer (deltaTime en milisegundos)
    this.onUpdate = function (deltaTime)
    {
        if (!isUpdating)
        {
            isUpdating=true;
            
            if (isInitialized && !isWindowResizing)
            {
                var offset = {x:vOffset.x,y:vOffset.y};
                lastOffset = {x:vOffset.x,y:vOffset.y};
                vOffset = {x:0,y:0};
                
                
                if (offset.x!=0) centerIniUtmX -= offset.x * currentScale;
                if (offset.y!=0) centerIniUtmY += offset.y * currentScale;
                
                imgLoading.style.display="none";
                
                //actualización de capas
                var n=systemLayers.length; //capas de sistema
                //for(var i=0;i<n;i++)
                for(var i=n-1;i>=0;i--)
                {
                    if (systemLayers[i]!=null)
                    {
                        systemLayers[i].onUpdate(offset, deltaTime);
                        
                        if (!systemLayers[i].isLoaded()) //visualización de estado de carga de capas
                        {
                            imgLoading.style.display="block";
                        }
                    }
                }
                
                n=customLayers.length; //capas de usuario
                for(var i=0;i<n;i++)
                {
                    if (customLayers[i]!=null)
                    {
                        customLayers[i].onUpdate(offset);
                    }
                }

                //actualización de comportamientos del sistema
                n=systemBehaviors.length;
                for(var i=0;i<n;i++)
                    if (systemBehaviors[i]!=null && (systemBehaviors[i].getEnabled() || systemBehaviors[i].alwaysKeepEnabled()) )
                        systemBehaviors[i].onUpdate(deltaTime);
                        
                //actualización de comportamientos de usuario
                n=customBehaviors.length;
                for(var i=0;i<n;i++)
                    if (customBehaviors[i]!=null && customBehaviors[i].getEnabled())
                        customBehaviors[i].onUpdate(deltaTime);
            }
            
            isUpdating = false;
        }
    }
    
    //Suscribe callback para notificación de inicialización correcta del visor
    this.notifyLoad = function(callback)
    {
        cbOnLoad = callback;
    }
    
    //Suscribe callback para notificación de error en la inicialización del visor
    this.notifyError = function(callback)
    {
        cbOnError = callback;
    }
    
    //Transforma coordenadas pantalla a coordenadas mundo (px a utm)
    this.transformLocalToWorld  = function(xLocal, yLocal)
    {
        var v   = new Vector2D();
        v.x     = centerIniUtmX + ((xLocal - containerWidth_Div2) * currentScale);
        v.y     = centerIniUtmY + ((containerHeight - yLocal - containerHeight_Div2) * currentScale);
        return v;
    }   
    
    //Transforma coordenadas mundo a coordenadas pantalla (utm a px)
    this.transformWorldToLocal  = function(utmX, utmY)
    {
        var v   = new Vector2D();
        v.x     = ((utmX - centerIniUtmX) / currentScale) + containerWidth_Div2;
        v.y     = containerHeight - (((utmY - centerIniUtmY) / currentScale ) + containerHeight_Div2);
        return v;
    }
    
    //Suscripción a un evento del visor
    this.subscribeEvent=function(evtType, callback)
    {
        switch(evtType.toString())
        {
            case WVEventType.DOUBLE_CLICK.toString():
                lstEvtDoubleClick.push(callback);
                break;
            case WVEventType.LEFT_CLICK.toString():
                lstEvtLeftClick.push(callback);
                break;
            case WVEventType.RIGHT_CLICK.toString():
                lstEvtRightClick.push(callback);
                break;
            case WVEventType.MOUSE_MOVE.toString():
                lstEvtMouseMove.push(callback);
                break;
            case WVEventType.CHANGE_SCALE.toString():
                lstEvtChangeScale.push(callback);
                break;
            case WVEventType.CHANGE_BEHAVIOR.toString():
                lstEvtChangeBehavior.push(callback);
                break;
            case WVEventType.ICON_CLICK.toString():
                var layer = oThis.getSystemLayer(WVLayerType.ICON);
	            if (layer!=null)
	            {
	                layer.registerEventIconClick(callback);
	            }
                break;
            case WVEventType.ICON_OVER.toString():
                var layer = oThis.getSystemLayer(WVLayerType.ICON);
	            if (layer!=null)
	            {
	                layer.registerEventIconOver(callback);
	            }
                break;
            case WVEventType.ICON_OUT.toString():
                var layer = oThis.getSystemLayer(WVLayerType.ICON);
	            if (layer!=null)
	            {
	                layer.registerEventIconOut(callback);
	            }
                break;
        }
    }
    
    //Elimina suscripción a evento del visor
    this.unsubscribeEvent=function(evtType, callback)
    {
        switch(evtType.toString())
        {
            case WVEventType.DOUBLE_CLICK.toString():
                removeEventSubscription(lstEvtDoubleClick, callback);
                break;
            case WVEventType.LEFT_CLICK.toString():
                removeEventSubscription(lstEvtLeftClick, callback);
                break;
            case WVEventType.RIGHT_CLICK.toString():
                removeEventSubscription(lstEvtRightClick, callback);
                break;
            case WVEventType.MOUSE_MOVE.toString():
                removeEventSubscription(lstEvtMouseMove, callback);
                break;
            case WVEventType.CHANGE_SCALE.toString():
                removeEventSubscription(lstEvtChangeScale, callback);
                break;
            case WVEventType.CHANGE_BEHAVIOR.toString():
                removeEventSubscription(lstEvtChangeBehavior, callback);
                break;
            case WVEventType.ICON_CLICK.toString():
                break;
            case WVEventType.ICON_OVER.toString():
                break;
            case WVEventType.ICON_OUT.toString():
                break;
        }
    }
    
    //Elimina suscripción de una lista
    function removeEventSubscription(lstEvt, callback)
    {
        for (var i=0;i<lstEvt.length;i++)
            if (lstEvt[i]==callback)
            {
                lstEvt.splice(i,1);
                break;
            }
    }
    
    //Desactiva controladores de sistema
    this.lockControllers=function()
    {
        for (var i=0;i<systemControllers.length;i++)
        {
            var ctrller = systemControllers[i];
            if (ctrller!=null && ctrller.getEnabled())
            {
                ctrller.setEnabled(false);
            }
        }
    }
    
    //Activa controladores de sistema
    this.unlockControllers=function()
    {
        for (var i=0;i<systemControllers.length;i++)
        {
            var ctrller = systemControllers[i];
            if (ctrller!=null && !ctrller.getEnabled())
            {
                ctrller.setEnabled(true);
            }
        }
    }
    
    //Centra la vista en una posición
    this.locate = function (utmX, utmY)
    {
        oThis.setCenterUtm({x:utmX,y:utmY});
        oThis.refresh();
    }
    
    //Desplaza la vista la cantidad especificada de pixels
    this.move=function(offsetX, offsetY)
    {
        oThis.addOffset({x:parseInt(offsetX),y:parseInt(offsetY)});
    }
    
    //Visualiza el contenido cartográfico total
    this.fitView=function()
    {
        if (allowFit)
	    {
            var b = oThis.getSystemBehavior(WVBehaviorType.FIT);
            oThis.setActiveBehavior(b);
            b.fitView();
        }
    }
    
    this.zoomWin=function(utmX,utmY,utmWidth,utmHeight)
	{
        var fenceWidthPX  = parseInt(utmWidth / oThis.getScale());
        var fenceHeightPX = parseInt(utmHeight / oThis.getScale());
                
        oThis.setCenterUtm({x:utmX + (utmWidth >> 1),y:utmY + (utmHeight >> 1)});
        oThis.setScale( parseInt(oThis.getScale() / Math.min(oThis.getWidth()/fenceWidthPX, oThis.getHeight()/fenceHeightPX)));
        oThis.refresh();
	}
    
    //Retorna si la vista está actualizada por completo para todos los tiles e iconos
    this.isViewUpdated=function()
    {
        var allLoaded=true;
        
        var layers = oThis.getSystemLayers();
        if (layers!=null)
            for(var i=0;i<layers.length;i++) 
                if (!layers[i].isLoaded())
                {
                    allLoaded=false;
                    break;
                }
                
        return allLoaded;
    }
    
    //Muestra u oculta la interfaz de usuario del visor (botoneras)
    this.showUI=function(ver)
    {
        var divUI = document.getElementById(oThis.getBaseID() + oThis.getGUID() + "_divUI");
        var imgNavigator=document.getElementById(oThis.getBaseID() + oThis.getGUID() + "_imgNavigator");
        if (divUI!=null)
        {
            if (ver)
            {
                divUI.style.display="block";
                imgNavigator.style.visibility="visible";
            }
            else
            {
                divUI.style.display="none";
                imgNavigator.style.visibility="hidden";
            }
        }
    }    
    
    //Mantiene viva la sesión interna del visor
    this.ping=function()
    {
        var img = new Image();
        img.src = urlBase + "op=12&VisorID=" + visorGUID + "&nocache=" + (pingSeedCache++);
    }
    
    //Calcula tamaño de superbounds para trabajar con caché de disco
    function calcSuperBounds(b)
    {
        var minx,miny,maxx,maxy;
        
        var w = 256;
        var h = 256;
        minx = b.x - (b.x % w);
        miny = b.y - (b.y % h);
        maxx = b.x + b.width  + w - ((b.x + b.width) % w);
        maxy = b.y + b.height + h - ((b.y + b.height)% h);
        
        var wBound = maxx-minx;
        var hBound = maxy-miny;
        
        return {x:minx,y:miny,width:wBound,height:hBound};
    }
    
    //Retorna la escala adecuada en base a si se requiere o no escala en potencia de 2
    function calcBestScale(scale)
    {
        if (scale<=0) return 1;
        
        scale = Math.ceil(scale);
        
        if (isPower2Scale)
        {
            if (scale<256) 
            {
                var s = 1;
                do
                {
                    s<<=1;
                }
                while (s<scale);
                
                //toma la escala más próxima
                var currScaleDist = Math.abs(s - scale); 
                var prevScaleDist = Math.abs((s>>1) - scale);
                s = currScaleDist > prevScaleDist ? s>>1 : s;
                
                return s; 
            }
            
            var s = 0;
            do
            {
                s+=256;
            }
            while (s<scale);
            
            //toma la escala más próxima
            var currScaleDist = Math.abs(s - scale); 
            var prevScaleDist = Math.abs((s-256) - scale);
            s = currScaleDist > prevScaleDist ? s-256 : s;
            
            return s;
        }
        else
        {
            return scale;
        }
    }
    
    /*
	//Configuración del visor
	function setupViewer()
	{
	    if (!isInitialized)
	    {
	        var cnt = new HttpConnector();
	        cnt.init(30000);
	        cnt.connect(urlBase + "op=3&VisorID=" + visorGUID, oThis.onViewerSetupOK, onViewerSetupError);
	    }
	}
	*/
	
	//Configuración del visor
	function setupViewerJSON()
	{
	    if (!isInitialized)
	    {
	        jsonConnector=new JSONscriptRequest(urlBase + "op=3&VisorID=" + visorGUID + "&callback=onViewerSetupJSON");      
	        jsonConnector.tag = oThis;
            jsonConnector.buildScriptTag();
            jsonConnector.addScriptTag();
	    }
	}
	
	//Configura el visor (capas, comportamientos, etc.) al recibir respuesta del Server
    this.onViewerSetupOK = function(resp)
	{
        var code = null;
        try
        {
            code = parseInt(resp);
        }
        catch(e){code = null;}
        
        if (code == null || isNaN(code)) 
        {
            //setTimeout(setupViewer, 3000);
            onViewerSetupError();
            return;
        }
        
        //Setup de Layers
        if (code==0) //no hay capas definidas
        {
            onViewerSetupError();
            return;
        }
        if (code & 1) oThis.createSystemLayer(WVLayerType.RASTER); //Raster Layer
        if (code & 2) oThis.createSystemLayer(WVLayerType.VECTOR); //Vectorial Layer 
        
        //iconos
        var iconData = document.getElementById(oThis.getBaseID() + oThis.getGUID()+"_iconsParams").value.split('@');
        var showIcons= parseInt(iconData[2]); 
        var allScaleIcons=parseInt(iconData[0]);
        var maxScaleIcons=parseInt(iconData[1]);
        if (showIcons)
        {
            var layerIcons = oThis.createSystemLayer(WVLayerType.ICON); //Icon Layer
            layerIcons.setAllScaleVisible(allScaleIcons);
            layerIcons.setScaleMaxVisible(maxScaleIcons);
        }
        //tacks
        var tackData = document.getElementById(oThis.getBaseID() + oThis.getGUID() +"_tacksParams").value.split('@');
        var showTacks=parseInt(tackData[2]);
        var allScaleTacks=parseInt(tackData[0]);
        var maxScaleTacks=parseInt(tackData[1]);
        if (showTacks)
        {
            var layerTacks = oThis.createCustomLayer( "TackLayer" );   //Tack Layer
            layerTacks.setAllScaleVisible( allScaleTacks );
            layerTacks.setScaleMaxVisible( maxScaleTacks );
            LoadTacks();
        }
        
        //Setup de behaviors y controllers
        var pan = new WVPanning();
        oThis.addSystemBehavior( pan );
        oThis.addSystemBehavior(new WVEmpty());
        
        oThis.addSystemBehavior(new WVFence());
        oThis.addSystemBehavior(new WVFit());
        oThis.addSystemBehavior(new WVZoomPowerTwo());
        oThis.addSystemBehavior(new WVArea());
        oThis.addSystemBehavior(new WVDistance());
        oThis.addSystemBehavior(new WVNavigator());
        oThis.addSystemBehavior(new WVHistoryBrowser());
        oThis.addSystemBehavior(new WVUTMCoords());
        oThis.addSystemBehavior(new WVCartInf());
        oThis.addSystemBehavior(new WVAlphInf());
        oThis.addSystemBehavior(new WVPrint());
        oThis.addSystemBehavior(new WVRefresh());
        
        if(defaultBehaviorType == null)
        {
            oThis.setDefaultBehavior(pan.getBehaviorType());
        }
        else
        {
            oThis.setDefaultBehavior(defaultBehaviorType, defaultBehaviorLInit, defaultBehaviorLFinalize);
        }
        
        oThis.addSystemController(new WVMouseWheelCtrl());
        oThis.addSystemController(new WVMouseFenceCtrl());
        oThis.addSystemController(new WVKeyboardNavCtrl());
        oThis.addSystemController(new WVMousePanCtrl());
        oThis.addSystemController(new WVMouseUTMCtrl());
        oThis.addSystemController(new WVMouseInformationCtrl());
        
        for( var i=0; i<systemBehaviors.length; i++ )
        {
            if (systemBehaviors[i].alwaysKeepEnabled()) //solo se inicializan los que necesitan estar activos siempre (p.e. WVNavigator y WVUTMCoords)
            {
                systemBehaviors[i].init(oThis);
            }
        }
        
        for( var i=0; i<systemControllers.length; i++ )
        {
            systemControllers[i].init(oThis);
        }
        
        //registra eventos sobre el visor
        oThis.getBrowser().registerEventMouseMove(oThis.getContainer(), notifyMouseMove);
        oThis.getBrowser().registerEventMouseDown(oThis.getContainer(), notifyMouseDown);
        oThis.getBrowser().registerEventMouseUp(oThis.getContainer(), notifyMouseUp);
        window.onunload=saveConfig;
        if (!window.addEventListener && !window.opera) window.onresize=onResizeWindow;        
        
        //notifica inicialización completada
        isInitialized = true;
        if (cbOnLoad) cbOnLoad();
	}
	
	function onViewerSetupError()
	{
	    if (cbOnError) cbOnError();//notifica error en la carga
	}
	
	//guarda valores de configuración del visor
	function saveConfig()
	{
	    if (hasOlderVisualization)
	    {
	        var exdate=new Date();
            exdate.setDate(exdate.getDate()+90);
            oThis.getBrowser().registerCookie(confString, oThis.getScale() + "@" + oThis.getCenterUtmX() + "@" + oThis.getCenterUtmY(), exdate ,null,null,null);
        }
	}
	
	function onResizeWindow()
	{
	    if (!isWindowResizing)
	    {
	        isWindowResizing = true;
	        
	        container.style.width  = originalWidth;
            container.style.height = originalHeight;
            
            //fuerza apagado de capas de sistema
            var layers = oThis.getSystemLayers();
            if (layers.length>0)
            {
                for(var i=layers.length-1;i>=0;i--) //quita capas personalizadas
                {
                      layers[i].onRedock();
                }
            }
            
            //fuerza apagado de capas personalizadas
            var layers = oThis.getCustomLayers();
            if (layers.length>0)
            {
                for(var i=layers.length-1;i>=0;i--) //quita capas personalizadas
                {
                      layers[i].onRedock();
                }
            }
        }
	    
        if (idTimerResize!=null) clearTimeout(idTimerResize);
        idTimerResize = setTimeout(onEndResizeWindow, 250);
	}
	
	function onEndResizeWindow()
	{
	    //recalcula tamaños del contenedor del visor
	    containerWidth  = container.offsetWidth <=0 ? container.parentNode.offsetWidth : container.offsetWidth;
        containerHeight = container.offsetHeight <=0 ? container.parentNode.offsetHeight : container.offsetHeight;
        containerWidth_Div2     = containerWidth >> 1;
        containerHeight_Div2    = containerHeight >> 1;
        
        container.style.width   = containerWidth;
        container.style.height  = containerHeight;
        
        //recalcula escala máxima para FitView
        var sW = Math.max(bounds.width/(containerWidth-5));
        var sH = Math.max(bounds.height/(containerHeight-5));
        scaleMax = calcBestScale(Math.max(sW, sH));
        
        //reconstruye capas de sistema
        var lTypes = new Array(); //guarda tipos de capas de sistema actualmente registradas
        var layers = oThis.getSystemLayers();
        var allScaleVisibleIcons=null;
        var maxScaleVisibleIcons=null;
        for(var i=layers.length-1;i>=0;i--) // elimina capas de sistema
        {
            var type =layers[i].getLayerType(); 
            lTypes.unshift(type);
            if (type==WVLayerType.ICON)
            {   
                allScaleVisibleIcons = layers[i].getAllScaleVisible();
                maxScaleVisibleIcons = layers[i].getScaleMaxVisible();
            }
            oThis.removeSystemLayer(type);
        }
        
        var tmpCustomLayers=new Array();
        layers = oThis.getCustomLayers();
        if (layers.length>0)
        {
            for(var i=layers.length-1;i>=0;i--) //quita capas personalizadas
            {
                  container.removeChild(layers[i].getContainer());
                  tmpCustomLayers.unshift(layers[i]);
                  customLayers.splice(i,1);
            }
        }
        
        for(var i=0;i<lTypes.length;i++) //pone nuevas capas de sistema
        {
            var l = oThis.createSystemLayer(lTypes[i]);
            if (lTypes[i]==WVLayerType.ICON)
            {
                l.setAllScaleVisible(allScaleVisibleIcons);
                l.setScaleMaxVisible(maxScaleVisibleIcons);
            }
        }
        
        if (tmpCustomLayers.length>0)
        {
            for(var i=0;i<tmpCustomLayers.length;i++)//restaura capas personalizadas
            {
                customLayers.push(tmpCustomLayers[i]);
                tmpCustomLayers[i].onResize();
                container.appendChild(tmpCustomLayers[i].getContainer());
            }
        }
        notifyChangeScale(); //emula cambio de escala pues hay capas que se recalculan al cambiar de escala el visor
	    isWindowResizing = false;
	}
	
	//Carga de Tacks agregados al visor mediante la colección Tacks
	function LoadTacks()
    {
        var bboxParams= bounds.x + ":" + bounds.y + ":" + (bounds.x + bounds.width) + ":" + (bounds.y + bounds.height);
        
        var host  = oThis.getUrlBase()+"op=9&";
        var visor = "VisorID=" + oThis.getGUID() + "&";
        var data  = "IdFromCoords=" + bboxParams;
        
        var url= host + visor + data;
        var cnx = new HttpConnector();
        cnx.init(20000);
        cnx.connect(url, tackQueryResult, tackQueryError);
    }
    
    //carga tacks en la capa
    function tackQueryResult(value, connector)
    {
        if (value!=null || value!="") 
        {
            var lstTacks = value.split('@');
            var i=lstTacks.length-1;
            do
            {
                if (lstTacks[i]!="")
                {
                    var chunk = lstTacks[i].split(',');
                    
                    var utmX=chunk[0];
                    var utmY=chunk[1];
                    var msg=chunk[2];
                    oThis.PutTack(utmX, utmY,msg);                    
                }
            }
            while(i--);
        }
    }
    function tackQueryError(){}
	
	//********** notificaciones de eventos
	function notifyChangeScale()
	{
        var n=lstEvtChangeScale.length;
        if (n>0)
	    {
            for(var i=0;i<n;i++)
                lstEvtChangeScale[i](oThis);
        }
	}
    
	function notifyMouseMove(e)
	{
	    if (!e) e = window.event; 
	    
	    browser.stopEvent(e);
	    var paux = browser.getOffSetMouseClient(e);
	    
	    if (!isNaN(paux.x) && !isNaN(paux.y))
	    {
	        var p = oThis.transformLocalToWorld(paux.x,paux.y);//browser.mouseclientX(e),browser.mouseclientY(e));
            currentUtmX = p.x;
            currentUtmY = p.y;
    	     
	        var n=lstEvtMouseMove.length;
            for(var i=0;i<n;i++)
            {
                lstEvtMouseMove[i](oThis, currentUtmX, currentUtmY);
            }
        }
	}
		
	function notifyMouseDown(e)
	{
	    if (!e) e = window.event; 
	    
	    if (!document.addEventListener) browser.stopEvent(e);
	    
	    var p = oThis.getCurrentUtm();
	    var numButton = browser.getButtonClicked(e);
	    
	    if (numButton==1)
	    {
	        var n=lstEvtLeftClick.length;
            for(var i=0;i<n;i++)
            {
                lstEvtLeftClick[i](oThis, p.x, p.y);
            }
        }
	    else
	    {
	        var n=lstEvtRightClick.length;
            for(var i=0;i<n;i++)
            {
                lstEvtRightClick[i](oThis, p.x, p.y);
            }
        }
	}
	
	var lastTimeClick = 0;
	var lastPointClick = {x:0,y:0};
	function notifyMouseUp(e)
	{
	    if (!e) e = window.event; 
	    var numButton = browser.getButtonClicked(e);
	    browser.stopEvent(e);
	    
	    if (numButton==1)
	    {
	        if (lastTimeClick==0)
	        {
	            lastTimeClick = new Date().getTime();
	            lastPointClick= oThis.getCurrentUtm();
	            lastPointClick= oThis.transformWorldToLocal(lastPointClick.x, lastPointClick.y);
	        }
	        else
	        {
	            var nowTime = new Date().getTime();
                var deltaTime = nowTime - lastTimeClick;
                if (deltaTime<500)
                {
                    var p = oThis.getCurrentUtm();
                    var px = oThis.transformWorldToLocal(p.x, p.y);
                    var tolerancia = 4;
                    if (Math.abs(px.x - lastPointClick.x) <= tolerancia && Math.abs(px.y - lastPointClick.y)<= tolerancia)
                    {
                        var n=lstEvtDoubleClick.length;
                        for(var i=0;i<n;i++)
                        {
                            lstEvtDoubleClick[i](oThis, p.x, p.y);
                        }
                    }
                }
                lastTimeClick = 0;
            }
        }
        else
        {
            lastTimeClick = 0;
        }
	}

    //*******  API antigua ******//
    this.CenterUtmX=function()
    {
        return oThis.getCenterUtmX();
    }
    
    this.CenterUtmY=function()
    {
        return oThis.getCenterUtmY();
    }
    
    this.CreatePanel=function(id)
    {
        return oThis.createCustomLayer(id);
    }
    
    this.RemovePanel=function(obj)
    {
        oThis.removeCustomLayer(obj);
    }
    
    this.ReleaseService=function()
    {
        oThis.releaseBehavior();
    }
    
    this.RefreshView=function()
    {
        oThis.refresh(true);
    }
    
    this.RefreshIcons=function()
    {
        oThis.refreshSystemLayer(WVLayerType.ICON);
    }
    
    this.ClearCache=function()
    {
        //do nothing
    }
    
    this.IsViewUpdated=function()
    {
        return oThis.isViewUpdated();
    }
    
    this.UIVisible=function(ver)
    {
        oThis.showUI(ver);
    }
    
    this.SetCursor=function(obj)
    {
        oThis.setCursor(obj);
    }
    
    this.GetScale=function()
    {
        return oThis.getScale();
    }
    
    this.Locate = function (utmX, utmY)
    {
        oThis.locate(utmX, utmY);
    }
    
    this.FitView = function() 
	{
	    oThis.fitView();
	}
	
	this.ZoomWin=function(utmX,utmY,utmWidth,utmHeight)
	{
        oThis.zoomWin(utmX, utmY, utmWidth, utmHeight);
	}
    
    this.ZoomIn=function()
    {
        oThis.setScale(oThis.getScale() >> 1);
        oThis.refresh();
    }
    
    this.ZoomOut=function()
    {
        oThis.setScale(oThis.getScale() << 1);
        oThis.refresh();
    }
    
    this.Move=function(offsetX, offsetY)
    {
        oThis.move(offsetX,offsetY);
    }
    
    this.PanEnabled=function(enabled)
    {
        oThis.setAllowPanning(enabled);
    }
    
    this.ZoomWinEnabled=function(enabled)
    {
        oThis.setAllowZooming(enabled);
    }
    
    //coordenada local a coordenada mundo
	this.localToWorldCoordX = function (pLocal) 
	{
		var p = oThis.transformLocalToWorld(pLocal,0)
		return p.x;
	}
	this.localToWorldCoordY = function (pLocal) 
	{
		var p = oThis.transformLocalToWorld(0,pLocal)
		return p.y;
	}
	//coordenada mundo a coordenada local
	this.worldToLocalCoordX = function (pWorld)
	{
		var p = oThis.transformWorldToLocal(pWorld,0)
		return p.x;
	}
	this.worldToLocalCoordY = function (pWorld)
	{
		var p = oThis.transformWorldToLocal(0, pWorld)
		return p.y;
	}
	
	this.GetUtmX=function()
	{
	    return oThis.getCurrentUtm().x;
	}
	
	this.GetUtmY=function()
	{
	    return oThis.getCurrentUtm().y;
	}
	
	this.PutTack=function(tackX,tackY, msg)
	{
	    var layer = oThis.getCustomLayer("TackLayer");
	    if (layer!=null)
	    {
	        var img = document.createElement('IMG');
            img.style.width="30";
            img.style.height="25";
            img.src=document.getElementById(oThis.getBaseID() + oThis.getGUID() + "_tackSrc").value;
            img.setAttribute("title",msg);
            img.title=msg;
            img.alt=msg;
	        layer.addItem(img, tackX, tackY);
	    }
	}
	
	this.RemoveAllTacks = function()
	{
	    var layer = oThis.getCustomLayer("TackLayer");
	    if (layer!=null)
	    {
	        layer.clear();
	    }
	}
	
	//URL
	this.getActualMap   = function()
	{
	    var scaleP  = oThis.getScale();
	    var width   = containerWidth*scaleP;
	    var height  = containerHeight*scaleP;
	    return getURL(centerIniUtmX-(width>>1),centerIniUtmY-(height>>1),width,height,scaleP ,2);
	}
	this.getMapRaster   = function ( minX, minY, maxX, maxY )
    {
        return getURL(minX,minY,maxX-minX,maxY-minY, oThis.getScale(),0);
    }
    this.getMapVector   = function ( minX, minY, maxX, maxY )
    {
        return getURL(minX,minY,maxX-minX,maxY-minY, oThis.getScale(),1);
    }
    this.getMapMixed    = function ( minX, minY, maxX, maxY )
    {
        return getURL(minX,minY,maxX-minX,maxY-minY, oThis.getScale(),2);
    }
    this.getMapScale    = function ( minX, minY, maxX, maxY, mmXpixel )
    {
        return getURL(minX,minY,maxX-minX,maxY-minY, mmXpixel,2);
    }
	function getURL(x,y,width,height, scaleP,op)
	{
	    return oThis.getUrlBase()+"op="+op+"&VisorID=" + oThis.getGUID() + "&Args="+x+","+y+","+width+","+height+","+parseInt(width/scaleP)+","+parseInt(height/scaleP)+"&scale="+scaleP;
	}
	
	//Registro de Eventos
	this.AttachToolFinished=function(callback)
    {
        oThis.subscribeEvent(WVEventType.CHANGE_BEHAVIOR, callback);
    }
	this.AttachLeftClick=function(callback)
	{
	    oThis.subscribeEvent(WVEventType.LEFT_CLICK, callback);
	}
	this.AttachRightClick=function(callback)
	{
	    oThis.subscribeEvent(WVEventType.RIGHT_CLICK, callback);
	}
	this.AttachMouseMove=function(callback)
	{
	    oThis.subscribeEvent(WVEventType.MOUSE_MOVE, callback);
	}
	this.AttachChangeScale=function(callback)
	{
	    oThis.subscribeEvent(WVEventType.CHANGE_SCALE, callback);
	}
	this.AttachIconEvent=function(callback)
	{
        oThis.subscribeEvent(WVEventType.ICON_CLICK, callback);
	}
	this.AttachIconOverEvent=function(callback)
	{
        oThis.subscribeEvent(WVEventType.ICON_OVER, callback);
	}
	this.AttachIconOutEvent=function(callback)
	{
        oThis.subscribeEvent(WVEventType.ICON_OUT, callback);
	}
	
	this.DetachToolFinished=function(callback)
	{
	    oThis.unsubscribeEvent(WVEventType.CHANGE_BEHAVIOR, callback);
	}
	this.DetachLeftClick=function(callback)
	{
	    oThis.unsubscribeEvent(WVEventType.LEFT_CLICK, callback);
	}
	this.DetachRightClick=function(callback)
	{
	    oThis.unsubscribeEvent(WVEventType.RIGHT_CLICK, callback);
	}
	this.DetachChangeScale=function(callback)
	{
	    oThis.unsubscribeEvent(WVEventType.CHANGE_SCALE, callback);
	}
	this.DetachMouseMove=function(callback)
	{
	    oThis.unsubscribeEvent(WVEventType.MOUSE_MOVE, callback);
	}
}

//Helpers JSON
var jsonConnector = null;
function onViewerSetupJSON(data)
{
    jsonConnector.removeScriptTag();
    jsonConnector.tag.onViewerSetupOK(data.layers);
}