Source: api/SpwViewer.js

Retour à la documentation
define([
        "dojo/_base/declare","dijit/_WidgetBase", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", "dojo/text!./templates/SpwViewer.html",
        "spw/api/ConfigLoader", "esri/geometry/Extent", "esri/SpatialReference", "dojo/topic", "dojo/_base/lang", "dojo/_base/array",
        "dojo/dom-construct", "spw/api/Utils", "spw/api/SpwMap", "spw/api/SpwWidgetsManager", "spw/api/SpwBaseToolBar", "dijit/layout/ContentPane",
        "dijit/layout/BorderContainer", "dijit/layout/TabContainer", "dojo/dom-class", "esri/config", "esri/tasks/GeometryService", "spw/api/MessageManager",
        "dojo/on", "dojo/query", "dojo/Evented", "dojo/aspect", "esri/symbols/Font", "dojox/layout/Dock", "dojo/_base/window", "dijit/Dialog",
        "spw/api/ProjectionManager", "dojo/dom-style", "dojo/touch", "dojo/mouse", "dijit/TitlePane", "dojo/sniff",
        "spw/api/SpwMobile", "esri/geometry/screenUtils", "esri/geometry/ScreenPoint", "esri/geometry/Point", "spw/api/SpwBorderContainer",
        "dojo/request/xhr", "dojo/_base/config", "dojo/dom-geometry"
    ],
    function (declare, _WidgetBase, _Templated, _WidgetsInTemplateMixin, template, ConfigLoader, Extent, SpatialReference, topic, lang,
              array, domConstruct, Utils, SpwMap, SpwWidgetsManager, SpwBaseToolBar, ContentPane, BorderContainer, TabContainer, domClass,
              esriConfig, GeometryService, MessageManager, on, query, Evented, aspect, Font, Dock, win, Dialog, ProjectionManager, domStyle, touch, mouse,
              TitlePane, has, SpwMobile, screenUtils, ScreenPoint, Point, SpwBorderContainer, request, dojoConfig, domGeometry) {

        var builtInSort = Array.prototype.sort;

        Array.prototype.sort = function(func) {
            var fixNumber = function(a, b) {
                if (typeof a === 'number' && typeof b === 'number') {
                    if( !isFinite(a) && !isFinite(b) ) {
                        return 0;
                    }
                    if( !isFinite(a) ) {
                        return 1;
                    }
                    if( !isFinite(b) ) {
                        return -1;
                    }
                }

                return func(a, b);
            };

            if (func == null) {
                return builtInSort.call(this);
            }

            return builtInSort.call(this, fixNumber);
        };
        if(!Array.prototype.find) {
            Array.prototype.find = function(func) {
                var results = [];
                for (var i = 0; i < this.length; i++){
                    if(func(this[i])) {
                        results.push(this[i]);
                    }
                }
                return results[0];
            };
        }

        var SpwViewer = null;
        SpwViewer = declare("spw.api.SpwViewer", [_WidgetBase, _Templated, _WidgetsInTemplateMixin, Evented], /** @lends spw.api.SpwViewer.prototype */{

            templateString: template,
            widgetsInTemplate: true,

            /* START Config file : viewer.json */
            /**
			 * Extent initial de la carte au démarrage du viewer
			 */
            initialExtent : null,

            /**
			 * Extent maximum de la carte
			 */
            fullExtent : null,

            /**
			 * Nom donné au fullExtent
			 */
            fullExtentLabel : null,

            /**
			 * Référence spatiale de la carte
			 */
            spatialReference : null,
            initialSpatialReference: null, // pour garder une trace de celle de
											// la config

            /**
			 * Url du service de géométrie
			 */
            geometryServiceUrl: null,

            /**
			 * Position pour l'affichage des messages de notification
			 */
            notificationPosition:null,

            /**
			 * Echelle minimum du viewer
			 */
            minScale : null,

            /**
			 * Echelle maximum du viewer
			 */
            maxScale : null,

            /**
			 * Texte de copyright général du viewer
			 */
            copyRightText:null,

            /**
			 * Indique si l'url doit se synchroniser dans le cadre du share
			 */
            shareable: false,

            /**
			 * Permet de forcer les lods disponibles dans le Viewer
			 */
            lods: null,

            /**
			 * Définit le contexte utilisée dans la deuxième fenêtre (par
			 * défaut, le contexte courant sera utilisé)
			 */
            sideScreenContext: null,

            /**
			 * Permet de définir le type de layout du viewer : 'sidebar' ou
			 * 'headline'. Voir dijit/layout/BorderContainer de Dojo pour le
			 * détail.
			 */
            layoutDesign: 'headline',

            /**
			 * Description de la région gauche du viewer
			 * 
			 * @property {string} width largeur de la zone.
			 * @property {Boolean} resizable indique si la zone est
			 *           redimensionnable par l'utilisateur.
			 * @property {Boolean} minimizable indique si la zone peut être
			 *           réduite.
			 * @property {Boolean} minimized indique si la zone est initialement
			 *           réduite.
			 */
            leftRegion: {
                width:"200px",
                resizable: true,
                minimizeable: true,
                minimized: false
            },
            /**
			 * Description de la région droite du viewer
			 * 
			 * @property {string} width largeur de la zone.
			 * @property {Boolean} resizable indique si la zone est
			 *           redimensionnable par l'utilisateur.
			 * @property {Boolean} minimizable indique si la zone peut être
			 *           réduite.
			 * @property {Boolean} minimized indique si la zone est initialement
			 *           réduite.
			 */
            rightRegion: {
                width:"200px",
                resizable: false,
                minimizeable: false,
                minimized: false
            },
            /**
			 * Description de la région haut du viewer
			 * 
			 * @property {string} height hauteur de la zone.
			 * @property {Boolean} resizable indique si la zone est
			 *           redimensionnable par l'utilisateur.
			 * @property {Boolean} minimizable indique si la zone peut être
			 *           réduite.
			 * @property {Boolean} minimized indique si la zone est initialement
			 *           réduite.
			 */
            topRegion: {
                width:"130px",
                resizable: false,
                minimizeable: false,
                minimized: false
            },
            /**
			 * Description de la région bas du viewer
			 * 
			 * @property {string} height hauteur de la zone.
			 * @property {Boolean} resizable indique si la zone est
			 *           redimensionnable par l'utilisateur.
			 * @property {Boolean} minimizable indique si la zone peut être
			 *           réduite.
			 * @property {Boolean} minimized indique si la zone est initialement
			 *           réduite.
			 */
            bottomRegion: {
                width:"130px",
                resizable: false,
                minimizeable: false,
                minimized: false
            },
            /**
			 * Définit la position de la toolbar sur la carte
			 * 
			 * @property {string} top distance par rapport au sommet.
			 * @property {string} bottom distance par rapport au bas.
			 * @property {string} left distance par rapport à la gauche.
			 * @property {string} right distance par rapport à la droite.
			 */
            toolbarPosition: {
                top:"3px",
                left:"65px"
            },
            /**
			 * Définit si la toolbar est réactive : opacité partielle et opacité
			 * complète lorsque le curseur passe au dessus.
			 */
            reactiveToolbar: false,
            /**
			 * Indique le niveau de verbosité du chargement : 0, 1, 2
			 */
            loadingVerbosity: 0,
            /**
			 * Url de la page proxy à utiliser par le viewer
			 */
            proxyPageUrl: null,
            /**
			 * Position du load sur la carte.
			 * 
			 * @property {string} top distance par rapport au sommet.
			 * @property {string} bottom distance par rapport au bas.
			 * @property {string} left distance par rapport à la gauche.
			 * @property {string} right distance par rapport à la droite.
			 */
            mapLoaderPosition: null,
            /* END Config file : viewer.json */

            /**
			 * Référence vers la carte
			 * 
			 * @type spw.api.SpwMap
			 */
            spwMap: null,
            /**
			 * Référence vers le SpwWidgetManager.
			 * 
			 * @type spw.api.SpwWidgetManager
			 */
            spwWidgetsManager: null,

            grayscaleProxy: dojoConfig.geoviewerApiUrl + '/grayscale.jsp',
            /**
			 * Référence vers la région principale "gauche" du viewer
			 * 
			 * @type dijit/layout/BorderContainer
			 */
            mainLeft: null,
            /**
			 * Référence vers la région principale "droite" du viewer
			 * 
			 * @type dijit/layout/BorderContainer
			 */
            mainRight: null,
            /**
			 * Référence vers la région principale "bas" du viewer
			 * 
			 * @type dijit/layout/BorderContainer
			 */
            mainBottom: null,
            /**
			 * Référence vers la région principale "dessus" du viewer
			 * 
			 * @type dijit/layout/BorderContainer
			 */
            mainTop: null,

            /**
			 * Référence vers la toolbar.
			 * 
			 * @type spw.api.SpwBaseToolBar
			 */
            toolbar: null,

            /**
			 * Classe CSS appliquée à l'élement body de la page html instanciant
			 * le viewer
			 * 
			 * @type string
			 * @default claro
			 */
            bodyClass: null,

            /**
			 * Référence vers le service de géométrie Esri.
			 * 
			 * @type esri.tasks.GeometryService
			 */
            geometryService: null,

            /**
			 * Id de l'élément dom où placer les différents composants
			 * périphériques du viewer (panel, messages, ...).
			 * 
			 * @type string
			 */
            placementNode: null,

            /**
			 * Le dock dans lequel les widgets peuvent être minimisés sans pour
			 * autant être désactivés.
			 */
            dock:null,

            /**
			 * La position du dock
			 * 
			 * @property {string} top distance par rapport au sommet.
			 * @property {string} bottom distance par rapport au bas.
			 * @property {string} left distance par rapport à la gauche.
			 * @property {string} right distance par rapport à la droite.
			 * @property {number} opacity l'opacité du dock entre 0 et 1.
			 */
            dockPosition:null,

            /**
			 * L'url du serveur de sécurisation des services Esri.
			 * 
			 * @type string
			 */
            secureServerUrl: null,

            /**
			 * Indique si le viewer est chargé.
			 * 
			 * @type Boolean
			 */
            viewerLoaded: null,

            /**
             * URL de la ShareServlet à utiliser dans l'API.
             *
             * @type String
             */
            shareServlet: null,

            spwMobile: null,
            mobile: false,

            corsEnabledServers: ["geoservices.test.wallonie.be", "geoservices.valid.wallonie.be", "geoservices.wallonie.be"],
            corsDetection: null,

            corsCheckUrl: dojoConfig.geoviewerApiUrl + '/checkCors.jsp',
            checkCors: true,
            corsErrorMessage: "Votre config réseau ne permet pas d'utiliser pleinement le site (problème reséau lié à CORS). Le site ne fonctionnera pas de manière optimale.",

            mapConfig: {
                widgetId: 'SpwMap',
                position: 'center',
                width: '100%',
                height: '100%',
                order: -Infinity,
                resizable: false,
                closable: true,
                activated: true
            },
            
            mapParams: {},

            /**
			 * Le constructeur ne doit pas être appelé directement. SpwViewer
			 * est une classe statique et doit être accédée via la méthode
			 * {@link spw.api.SpwViewer.getInstance getInstance}.
			 * 
			 * @constructs
			 * @extends {dijit._WidgetBase}
			 * @classdesc Le Viewer. Il s'agit de la classe de base de l'API.
			 *            Cette classe permet d'obtenir un singleton, une
			 *            instance unique du viewer. Il s'agit en quelque sorte
			 *            du "point d'entrée" de l'application.
			 */
            constructor: function(config){
                this.checkCorsStatus(config);

                // this.inherited(arguments);

                this.viewerLoaded = false;

                this.spwMobile = new SpwMobile({
                    spwViewer: this,
                    regions: lang.mixin({
                        "left": {
                            "percentage": 70,
                            "percentageLandscape": 50
                        },
                        "right": {
                            "percentage": 70,
                            "percentageLandscape": 50
                        },
                        "bottom": {
                            "percentage": 30,
                            "percentageLandscape": 50
                        }
                    }, this.mobile)
                });

                MessageManager.getInstance({
                    placementNode: config.placementNode ? config.placementNode : document.body
                });

                if(config.loadingVerbosity && config.loadingVerbosity > 0){
                    config.loadingVerbosity > 1 ? MessageManager.getInstance().displayLoading("Création du viewer") : MessageManager.getInstance().displayLoading("");
                }
            },

            /**
			 * Vérifie la configuration CORS du viewer
			 * 
			 * @param cfg
			 *            la configuration CORS du viewer
			 */
            checkCorsStatus: function(cfg) {
                var checkCors = typeof cfg.checkCors === 'undefined' ? this.checkCors : cfg.checkCors;
                var checkCorsUrl = typeof cfg.corsCheckUrl === 'undefined' ? this.corsCheckUrl : cfg.corsCheckUrl;

                if (checkCors) {
                    request(checkCorsUrl, {
                        handleAs: 'json',
                        preventCache: true,
                        sync: true,
                        timeout: 3000
                    }).then(lang.hitch(this, function(response) {
                        if (response !== 'ok') {
                            this.disableCors(cfg);
                        }
                    }), lang.hitch(this, function(err) {
                        this.disableCors(cfg);
                    }));
                }
            },

            /**
			 * Désactive le CORS
			 * 
			 * @param cfg
			 *            la configuration CORS du viewer
			 */
            disableCors: function(cfg) {
                array.forEach(ConfigLoader.getInstance()._dictionnary['widgets'], lang.hitch(this, function(wdgt) {
                    if (wdgt && wdgt.config) {
                        wdgt.config.useCors = false;
                    }
                }));

                cfg.corsEnabledServers = 'clear';
                cfg.corsDetection = false;

                MessageManager.getInstance().notifyWarning(this.corsErrorMessage, 'Erreur réseau', 10000);
            },

            /**
			 * Lorsque le propriétés ont été "mixées" au Viewer, surcharge
			 * l'extent avec celui définit dans l'url, initialise le
			 * GeometryService et la page proxy si renseignée.
			 */
            postMixInProperties: function() {
                this.inherited(arguments);

                var coor = Utils.gua("COOR");
                var adr = Utils.gua("ADR");
                if (coor || adr) {
                    Utils.rua('BBOX');
                }
                var bbox = Utils.gua("BBOX");
                if(coor != null && coor != ""){
                    var params = {};
                    var stringParams = coor.split("|");
                    array.forEach(stringParams, lang.hitch(this, function(p, i) {
                        var splittedP = p.split('=');
                        if (i == 0) {
                            params['point'] = splittedP[0];
                        } else {
                            params[splittedP[0]] = splittedP[1];
                        }
                    }))
                    if (params.point) {
                        var coords = params.point.split(',');
                        if (coords.length == 2) {
                            var point = new Point(coords[0], coords[1], {"wkid" : 31370});
                            this.initialExtent = [parseFloat(point.x)-50, parseFloat(point.x)+50, parseFloat(point.y)-50, parseFloat(point.y)+50];
                            var pointText = "<div>X:"+point.x +", Y:"+point.y+ "</div>";
                            var options = Utils.buildLocalisableUrlParams(params);
                            options.text = pointText + options.text;
                            this._addMarkerParameters = [point, options];
                        }
                    }
                } else if(bbox != null && bbox != ""){
                    var extentSplit = bbox.split(",");
                    if(extentSplit != null && extentSplit.length == 4){
                        this.initialExtent = array.map(extentSplit, function(item){
                            return parseFloat(item);
                        });
                    }
                }

                this.initialSpatialReference = this.get('spatialReference');
                this.initialExtent = new Extent(this.initialExtent[0],this.initialExtent[2],this.initialExtent[1],this.initialExtent[3], this.get('spatialReference'));
                this.fullExtent = new Extent(this.fullExtent[0],this.fullExtent[2],this.fullExtent[1],this.fullExtent[3], this.get('spatialReference'));

                this.geometryService = new GeometryService(this.geometryServiceUrl);
                esriConfig.defaults.geometryService = this.geometryService;
                if(this.proxyPageUrl){
                    esriConfig.defaults.io.proxyUrl = this.proxyPageUrl;
                }

				esriConfig.defaults.io.corsEnabledServers.push("geoservices.test.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoservices.valid.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoservices.wallonie.be");
				
				esriConfig.defaults.io.corsEnabledServers.push("geoservices2.test.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoservices2.valid.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoservices2.wallonie.be");
				
				esriConfig.defaults.io.corsEnabledServers.push("geoservices3.test.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoservices3.valid.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoservices3.wallonie.be");
				
				esriConfig.defaults.io.corsEnabledServers.push("geoapps.test.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoapps.valid.wallonie.be");
				esriConfig.defaults.io.corsEnabledServers.push("geoapps.wallonie.be");
				
				esriConfig.defaults.io.corsEnabledServers.push("www.dov.vlaanderen.be");
				esriConfig.defaults.io.corsEnabledServers.push("ccff02.minfin.fgov.be");
				
                if(this.corsDetection === true || this.corsDetection === false) {
                    esriConfig.defaults.io.corsDetection = this.corsDetection;
                }

                if (this.corsEnabledServers === 'clear') {
                    esriConfig.defaults.io.corsEnabledServers = [];
                }
                else if(this.corsEnabledServers && this.corsEnabledServers.length > 0){
                    esriConfig.defaults.io.corsEnabledServers = esriConfig.defaults.io.corsEnabledServers.concat(this.corsEnabledServers);
                }

                MessageManager.getInstance().setNotificationPosition(this.notificationPosition);
                if(!this.shareServlet) {
                    this.shareServlet = dojo.config.geoviewerApiUrl + '/ShareServlet';
                }
            },

            /**
			 * Au rendering, prépare le layout général du viewer.
			 */
            buildRendering: function() {
                this.inherited(arguments);

                this._createDock();

                this._prepareLayout();
            },

            _createDock: function(){
                var dockPos = this.dockPosition ? this.dockPosition : {bottom: "5px", left: "5px", opacity: 0.80};
                var dockStyle = "";
                for(var k in dockPos){
                    dockStyle += (k +":"+ dockPos[k]+";");
                }
                var dockTmpl = "<div style='position: absolute;background: none;padding: 5px;z-index:5000;"+dockStyle+"'><div data-dojo-attach-point='containerNode'></div></div>";
                this.dock = new Dock({
                    templateString:dockTmpl
                }, domConstruct.create("div", {}, this.get('placementNode'), "last"));
                this.dock.startup();
            },

            /**
			 * Parcourt la configuration des widgets et pré-initialise les
			 * régions possibles dans le viewer.
			 */
            _prepareLayout: function() {
                if (this.mobile) {
                    return;
                }

                var positions = [];
                array.forEach(ConfigLoader.getInstance().get("widgets"), lang.hitch(this, function(widgetConfig){
                    if(widgetConfig.config && array.indexOf(positions, widgetConfig.config.position) == -1 && widgetConfig.config.activated && widgetConfig.config.showOnActivate !== false){
                        positions.push(widgetConfig.config.position);

                        var regionAP = this.getRegion(widgetConfig.config.position);

                        if(regionAP && (regionAP.getParent() != this.spwViewerBorderContainer
                                || regionAP.getParent() != this.spwViewerInnerBorderContainer)){
                            if(regionAP.get('id') != 'mainBottom'){
                                this.spwViewerBorderContainer.addChild(regionAP);
                            } else {
                                this.spwViewerInnerBorderContainer.addChild(regionAP);
                                if (regionAP._resizerWidget == null) {
                                    this.addResizer(regionAP);
                                }
                            }
                        }
                    }
                }));
            },

            /**
			 * Démarre le viewer. Lorsque la structure du viewer est en place,
			 * initialise la carte (chargement des services de cartes) et le
			 * widgetManager (instanciation des widgets).
			 */
            startup: function() {
                this.inherited(arguments);

                if(this.bodyClass && !domClass.contains(win.body(), this.bodyClass)){
                    if(this.domNode && this.domNode.parentNode && !domClass.contains(this.domNode.parentNode, this.bodyClass)){
                        domClass.add(this.domNode.parentNode, this.bodyClass);
                    } else {
                        domClass.add(win.body(), this.bodyClass);
                    }
                }


                if(this.loadingVerbosity && this.loadingVerbosity > 0){
                    this.loadingVerbosity > 1 ? MessageManager.getInstance().displayLoading("Initialisation de la carte") : null;
                }

                if(this.get('loadingVerbosity') && this.get('loadingVerbosity') > 0){
                    this.get('loadingVerbosity') > 1 ? MessageManager.getInstance().displayLoading("Récupération et placement des widgets") : null;
                }

                this.spwWidgetsManager = new SpwWidgetsManager({spwViewer: this});
                this.spwWidgetsManager.on(SpwWidgetsManager.events.widgetsCreated,lang.hitch(this, this.onWidgetsCreated));

                if(this.mapConfigMainScreen) {
                    this.mapConfig.configMainScreen = this.mapConfigMainScreen;
                }
                if(this.mapConfigSecondScreen) {
                    this.mapConfig.configSecondScreen = this.mapConfigSecondScreen;
                }
                
                this.mapConfig.mapParams = this.mapParams;

                this.spwWidgetsManager.loadWidgetClasses({
                    className: 'spw/api/SpwMap',
                    order: -Infinity,
                    config: this.mapConfig
                });
            },

            /**
			 * Méthode appellée lorsque tous les widgets de l'application ont
			 * été instanciés
			 */
            onWidgetsCreated: function() {
                if(this.mainBottom && !this.mainBottom.hasChildren()) {
                    this.hideWidget(this.mainBottom);
                }

                // pour garder la référence vers la première map
				// (retrocompatibilité)
                this.spwMap = this.get('spwMap');
                this.spwMap.startup();
                if (this._addMarkerParameters) {
                    var point = this._addMarkerParameters[0];
                    if (point.spatialReference.wkid != this.get('spwMap').getSpatialReferenceSRID()) {
                        point = this.get('spwMap').getEsriPointFromXYWkid(point.x, point.y, point.spatialReference.wkid);
                    }
                    this.initialExtent = [parseFloat(point.x)-50, parseFloat(point.x)+50, parseFloat(point.y)-50, parseFloat(point.y)+50];
                    this.spwMap.addMarkerOnPoint(point, this._addMarkerParameters[1]);
                }

                this.viewerLoaded = true;
                this.emit(SpwViewer.events.viewerLoaded);

                if(this.get('loadingVerbosity') && this.get('loadingVerbosity') > 0){
                    MessageManager.getInstance().removeLoading();
                    require(["spw/api/ContextManager"], function(ContextManager){
                        ContextManager.getInstance();
                    });
                }

                // FIX: loading infini IE après share...
                // IE ou Edge
                if (has('ie') || (window.navigator.appName === 'Netscape' && window.navigator.userAgent.indexOf('Trident') > -1)) {
                    this.get('spwMap').esriMap.onUpdateEnd();
                }

                if(this.mainLeft && !this.mainLeft.hasChildren()) {
                    this.hideWidget(this.mainLeft);
                }
                if(this.mainRight && !this.mainRight.hasChildren()) {
                    this.hideWidget(this.mainRight);
                }
                if(this.mainTop && !this.mainTop.hasChildren()) {
                    this.hideWidget(this.mainTop);
                }

                if(this.leftRegion && this.leftRegion.minimized){
                    this.regionCollapserClicked(this.leftRegion, this.mainLeft, 'left', 'right');
                }
                if(this.rightRegion && this.rightRegion.minimized){
                    this.regionCollapserClicked(this.rightRegion, this.mainRight, 'right', 'left');
                }
                if(this.topRegion && this.topRegion.minimized){
                    this.regionCollapserClicked(this.topRegion, this.mainTop, 'top', 'bottom');
                }
                if(this.bottomRegion && this.bottomRegion.minimized){
                    this.regionCollapserClicked(this.bottomRegion, this.mainBottom, 'bottom', 'top');
                }

                // //Reset previous region collapsed state
                if (this.topRegionMustBeInverted) {
                    this.regionCollapserClicked(this.topRegion, this.mainTop, 'top', 'bottom');
                    this.topRegionMustBeInverted = false;
                }
                if (this.leftRegionMustBeInverted) {
                    this.regionCollapserClicked(this.leftRegion, this.mainLeft, 'left', 'right');
                    this.leftRegionMustBeInverted = false;
                }
                if (this.rightRegionMustBeInverted) {
                    this.regionCollapserClicked(this.rightRegion, this.mainRight, 'right', 'left');
                    this.rightRegionMustBeInverted = false;
                }

            },

            _getLocalSpwMapAttr: function() {
                if (this.spwWidgetsManager == null) {
                    return null;
                }

                return this.spwWidgetsManager.getWidget(this.mapConfig.widgetId);
            },

            _getSpwMapAttr: function() {
                if (this.spwWidgetsManager == null) {
                    return null;
                }

                var map;
                if (this.sideWindow && this.sideWindow.viewer &&
                    this.sideWindow.require("spw/api/SpwViewer").getInstance().get('localSpwMap') &&
                    this.sideWindow.require("spw/api/SpwViewer").getInstance().get('localSpwMap').configSecondScreen.activated) {
                    map = this.sideWindow.require("spw/api/SpwViewer").getInstance().get('localSpwMap');
                } else if(this.isSideWindow()) {
                    map = window.opener.require("spw/api/SpwViewer").getInstance().get('localSpwMap');
                } else {
                    map = this.spwWidgetsManager.getWidget(this.mapConfig.widgetId);
                }

                return map;
            },

            _getPlacementNodeAttr: function() {
                return this.placementNode ? this.placementNode : document.body;
            },

            getPlacementNode: function() {
              var node = this.get('placementNode');
              return node.domNode ? node : document.getElementById(node);
            },

            _getInitialExtentAttr: function(){
                return this.initialExtent;
            },

            _setInitialExtentAttr: function(extent) {
                this.initialExtent = extent;
            },

            _getFullExtentAttr: function() {
                return this.fullExtent;
            },

            _setFullExtentAttr: function(extent) {
                this.fullExtent = extent;
            },

            _getSpatialReferenceAttr: function() {
                return new SpatialReference({
                    wkid: this.spatialReference
                });
            },

            _setSpatialReferenceAttr: function(wkid) {
                if (wkid !== this.spatialReference) {
                    this.set('initialExtent', ProjectionManager.getInstance().transform(this.spatialReference, wkid, this.initialExtent));
                    this.set('fullExtent', ProjectionManager.getInstance().transform(this.spatialReference, wkid, this.fullExtent));

                    this.spatialReference = wkid;
                }
            },

            /**
			 * Ajoute un widget dans la toolbar.
			 * 
			 * @param widget
			 */
            addWidgetInToolbar: function(widget) {
                if(this.toolbar == null) {
                    this.toolbar = new SpwBaseToolBar({mapPosition: this.toolbarPosition, reactive: this.reactiveToolbar}, domConstruct.create("div"));
                    this.get('spwMap').placeIn(this.toolbar);
                }
                this.toolbar.addWidget(widget);
            },

            /**
			 * Retire un widget de la toolbar.
			 * 
			 * @param widget
			 */
            removeWidgetFromToolbar: function(widget){
                if(((widget.get('inToolbar') && widget.get('toolBarButton')) || widget.get('position') == 'toolbar') && this.toolbar != null){
                    this.toolbar.removeWidget(widget);
                }
            },

            /**
			 * Ajoute un widget sur la carte.
			 * 
			 * @param widget
			 */
            addWidgetOnMap: function(widget){
                this.get('spwMap').placeIn(widget);
            },

            /**
			 * Récupère la référence vers une région principale du viewer sur
			 * base de son code (left, right, top, bottom).
			 * 
			 * @param region
			 *            code région
			 * @returns {dijit.layout.BorderContainer}
			 */
            getRegion: function(region) {
                switch(region){
                    case 'left':
                        if(!this.mainLeft){
                            this.mainLeft = new SpwBorderContainer({id: "mainLeft", region:'leading', gutters: false, splitter:this.leftRegion.resizable, style: "width:" + this.leftRegion.width + ";"});
                            if(this.leftRegion.minimizeable){
                                this.createMinimizeRegionButton(this.leftRegion, this.mainLeft, 'left', 'right');
                            }
                        }
                        return this.mainLeft;
                    case 'right':
                        if(!this.mainRight){
                            this.mainRight = new SpwBorderContainer({id: "mainRight", region:'trailing', gutters: false, splitter:this.rightRegion.resizable, style: "width:" + this.rightRegion.width + ";"});
                            if(this.rightRegion.minimizeable){
                                this.createMinimizeRegionButton(this.rightRegion, this.mainRight, 'right', 'left');
                            }
                        }
                        return this.mainRight;
                    case 'top':
                        if(!this.mainTop){
                            this.mainTop = new SpwBorderContainer({id: "mainTop", region:'top', gutters: false, splitter:this.topRegion.resizable, style: "height:" + this.topRegion.height + ";padding:0;"});
                            if(this.topRegion.minimizeable){
                                this.createMinimizeRegionButton(this.topRegion, this.mainTop, 'top', 'bottom');
                            }
                        }
                        return this.mainTop;
                    case 'bottom':
                        if(!this.mainBottom){
                            this.mainBottom = new SpwBorderContainer({id: "mainBottom", region:'bottom', gutters: false, splitter:this.bottomRegion.resizable, style: "height:" + this.bottomRegion.height + ";"});
                            if(this.bottomRegion.minimizeable){
                                this.createMinimizeRegionButton(this.bottomRegion, this.mainBottom, 'bottom', 'top');
                            }
                        }
                        return this.mainBottom;

                    case 'center':
                        return this.spwViewerInnerBorderContainer;
                }
            },

            /**
			 * Crée les boutons permettant à l'utilisateur de minimiser les
			 * régions principales du viewer.
			 * 
			 * @param {Object}
			 *            region l'objet configuration correspondant à une
			 *            région.
			 * @param {dijit.layout.borderContainer}
			 *            container le widget content la région.
			 * @param {String}
			 *            direction direction soit : left, right, bottom ou top.
			 * @param {String}
			 *            inverseDirection la direction inverse.
			 */
            createMinimizeRegionButton: function(region, container, direction, inverseDirection) {
                var img = null;
                if(region.collapser) {
                    img = domConstruct.create("img", {
                        src: region.collapser.imageUrl,
                        "class": region.collapser.className + " " + direction + "RegionCollapser"
                    }, container.domNode);

                    if (region.collapser.style) {
                        domStyle.set(img, region.collapser.style);
                    }
                } else {
                    var baseImagesUrl = require.toUrl("spw/api") + "/images/";

                    img = domConstruct.create("img", {
                        src: baseImagesUrl+"collapser.png",
                        "class": direction + "RegionCollapser "+ direction + "CurrentDirection"
                    }, container.domNode);
                }

                lang.mixin(container, {
                    _imgCollapser: img
                });

                on(img, "click", lang.hitch(this, function(){this.regionCollapserClicked(region, container, direction, inverseDirection);}));
            },

            /**
			 * Méthode appelée lorsque le collapser d'une région est cliqué
			 * (petit flèche permettant de déployer / retracter la région)
			 * 
			 * @param region
			 *            la configuration de la région
			 * @param container
			 *            le container html de la région
			 * @param {string}
			 *            direction la direction de la région
			 * @param {stirng}
			 *            inverseDirection la direction inverse de la direction
			 *            de la région
			 */
            regionCollapserClicked: function(region, container, direction, inverseDirection){
                switch(container._minimizeState){
                    case 'minimized':
                        lang.mixin(container, {
                            _minimizeState: 'maximized'
                        });
                        container.resize(container._sizeBeforeMinimize);
                        container._imgCollapser.src = container._imgCollapser.src.replace(inverseDirection, direction);
                        container._imgCollapser.className = container._imgCollapser.className.replace(inverseDirection+ "CurrentDirection", direction+ "CurrentDirection");
                        array.forEach(container.getChildren(), function(child){child.domNode.style.display = '';});
                        query("#" + container.get('id') + '_splitter').forEach(function(item){ item.style.display = region.resizable ? "block" : "none";});
                        domClass.remove(container.domNode, "minimized");
                        break;
                    case 'maximized':
                    default:
                        lang.mixin(container, {
                            _minimizeState: 'minimized',
                            _sizeBeforeMinimize: (direction == "left" || direction == "right" ? {w:container.w} : {h:container.h})
                        });
                        container.resize((direction == "left" || direction == "right" ? {w:1} : {h:1}));
                        container._imgCollapser.src = container._imgCollapser.src.replace(direction, inverseDirection);
                        container._imgCollapser.className = container._imgCollapser.className.replace(direction+ "CurrentDirection", inverseDirection+ "CurrentDirection");
                        array.forEach(container.getChildren(), function(child){child.domNode.style.display = 'none';});
                        query("#" + container.get('id') + '_splitter').forEach(function(item){ item.style.display = "none";});
                        domClass.add(container.domNode, "minimized");
                }
                this.spwViewerBorderContainer.layout();
            },

            /**
			 * Méthode appelée pour afficher un widget en mobile
			 * 
			 * @param widget
			 *            le widget à afficher
			 */
            showMobileWidget: function(widget) {
                this.spwMobile.showWidget(widget);
            },

            /**
			 * Méthode appelée pour masquer un widget en mobile
			 * 
			 * @param widget
			 *            le widget à masquer
			 */
            removeMobileWidget: function(widget) {
                this.spwMobile.removeWidget(widget);
            },

            /**
			 * Affiche un widget dans un panel latéral (version mobile)
			 */
            addToSidePanel: function(widget) {
                this.spwMobile.addToMenu(widget);
            },

            /**
			 * Affiche un widget dans le viewer
			 * 
			 * @param {spw.api.SpwBaseWidget}
			 *            widget le widget à afficher
			 * @param {String}
			 *            region le code région dans laquelle placer le widget
			 * @param {String}
			 *            place la "sous-région", soit top, bottom, left ou
			 *            right.
			 * @param {Boolean}
			 *            forceSelect indique si le widget doit être sélectionné
			 *            si plusieurs widgets sont dans la région.
			 */
            showWidget: function(widget, region, place, forceSelect) {
                var regionAP = this.getRegion(region);
                this.makeSureRegionIsCollapsed(region);

                if(regionAP.getParent() != this.spwViewerBorderContainer && regionAP.getParent() != this.spwViewerInnerBorderContainer){
                    if(regionAP.get('id') != 'mainBottom'){
                        this.spwViewerBorderContainer.addChild(regionAP);
                    } else {
                        this.spwViewerInnerBorderContainer.addChild(regionAP);
                    }
                }

                // Récupérer tabcontainer dans region.
                var tabContainer = this.getTabContainerInRegion(regionAP);

                if (region === 'bottom' && regionAP._resizerWidget == null) {
                    this.addResizer(regionAP);
                }

                if (region === 'bottom' && regionAP.setTitle) {
                    regionAP.setTitle('');
                }

                var nbWidgetForTab = /bottom/.test(region) ? 1 : 0;

                if(tabContainer){
                    // Si trouvé
                    // Ajoute un tab avec le widget à afficher
                    if(tabContainer.region == place) {
                        this.addTabToTabContainer(tabContainer, widget, forceSelect);
                    }else{
                        this.placeWidgetInRegion(widget, regionAP, place);
                    }
                } else if (regionAP.getChildren().length > nbWidgetForTab){
                    // Sinon si widget(s) présent(s)
                    // Récupérer widget == tabWidget
                    var tabWidget = this.getTabWidgetInRegion(regionAP, place);
                    if(tabWidget){
                        // Si trouvé
                        // Create tabContainer et placer les 2 widget dedans
                        this.createTabContainer(regionAP, widget, tabWidget, forceSelect, place);
                    } else {
                        // Sinon
                        // placer le widget dans la région
                        this.placeWidgetInRegion(widget, regionAP, place);
                    }
                } else {
                    // Sinon
                    // placer le widget dans la région
                    if (region === 'bottom' && regionAP.setTitle) {
                        regionAP.setTitle(widget.widgetTitle ? widget.widgetTitle : "");
                    }

                    this.placeWidgetInRegion(widget, regionAP, place);
                }
            },

            /**
			 * S'assure que la région dans laquelle un widget va être affiché
			 * est bien visible (afin d'éviter des bugs d'affichage).
			 * 
			 * @param {string}
			 *            region le nom de la région à vérifier
			 */
            makeSureRegionIsCollapsed: function(region) {
                switch (region) {
                    case 'top':
                        if (this.topRegion && this.mainTop._minimizeState == 'minimized') {
                            this.regionCollapserClicked(this.topRegion, this.mainTop, 'top', 'bottom');
                            this.topRegionMustBeInverted = true;
                        }
                        break;
                    case 'left':
                        if (this.leftRegion && this.mainLeft._minimizeState == 'minimized') {
                            this.regionCollapserClicked(this.leftRegion, this.mainLeft, 'left', 'right');
                            this.leftRegionMustBeInverted = true;
                        }
                        break;
                    case 'right':
                        if (this.rightRegion && this.mainRight._minimizeState == 'minimized') {
                            this.regionCollapserClicked(this.rightRegion, this.mainRight, 'right', 'left');
                            this.rightRegionMustBeInverted = true;
                        }
                }
            },

            /**
			 * Ajoute un widget dans une région contenant déjà plusieurs
			 * widgets.
			 * 
			 * @param {dijit.layout.TabContainer}
			 *            tabContainer le tabContainer de la région
			 * @param {spw.api.SpwBaseWidget}
			 *            widget le widget à ajouter
			 * @param {Boolean}
			 *            forceSelect indique si l'onglet du widget doit être
			 *            sélectionné.
			 */
            addTabToTabContainer: function(tabContainer, widget, forceSelect) {
                var newWidgetContainer = new ContentPane({
                    content: widget,
                    title: widget.widgetTitle ? widget.widgetTitle : "",
                    style: 'padding: 0px;'
                });

                tabContainer.startup();

                var index = widget.get('orderInRegion') != null ? widget.get('orderInRegion') : 0;
                var insIndex = -1;
                array.every(tabContainer.getChildren(), function(tabPanel, idx){
                    var currIndex = tabPanel.getChildren()[0].get('orderInRegion');
                    if(!currIndex){
                        currIndex = 0;
                    }
                    if(index < currIndex){
                        insIndex = idx;
                        return false;
                    }
                    return true;
                });
                if(insIndex > -1){
                    tabContainer.addChild(newWidgetContainer, insIndex);
                } else {
                    tabContainer.addChild(newWidgetContainer);
                }
                if(forceSelect){
                    tabContainer.selectChild(newWidgetContainer, true);
                }
            },

            /**
			 * Crée un TabContainer dans une région qui contiendra plus d'un
			 * widget.
			 * 
			 * @param {dijit.layout.BorderContainer}
			 *            regionAP Référence vers la région principale du
			 *            viewer.
			 * @param {spw.api.SpwBaseWidget}
			 *            widget le widget à placer
			 * @param {spw.api.SpwBaseWidget}
			 *            child le widget déjà existant dans la région.
			 * @param {Boolean}
			 *            forceSelect
			 * @param {String}
			 *            place la "sous-région", soit top, bottom, left ou
			 *            right.
			 */
            createTabContainer: function(regionAP, widget, child, forceSelect, place) {
                var index = widget.get('orderInRegion') != null ? widget.get('orderInRegion') : 0;

                regionAP.removeChild(child);
                if(regionAP.titleDiv){
                    domConstruct.destroy(regionAP.titleDiv);
                    domClass.remove(child.domNode, "titled");
                }

                var tabContainer = new TabContainer({region:place});

                tabContainer.startup();

                tabContainer.addChild(child);

                var existingChildIndex = child.getChildren()[0].get('orderInRegion') != null ? child.getChildren()[0].get('orderInRegion') : 0;
                var newWidgetContainer = new ContentPane({
                    content: widget,
                    title: widget.widgetTitle ? widget.widgetTitle : "",
                    style: 'padding: 0px;'
                });
                tabContainer.addChild(newWidgetContainer, existingChildIndex < index ? 1 : 0);

                regionAP.addChild(tabContainer);

                if(forceSelect){
                    tabContainer.selectChild(newWidgetContainer, true);
                } else {
                    tabContainer.selectChild(child, true);
                }
            },

            /**
			 * Récupère la référence vers le widget d'une région qui va contenir
			 * d'autres widgets.
			 * 
			 * @param {dijit.layout.TabContainer}
			 *            region la région principale du viewer.
			 * @param {String}
			 *            place la "sous-région", soit top, bottom, left ou
			 *            right.
			 * @returns {spw.api.SpwBaseWidget} widget
			 */
            getTabWidgetInRegion: function(region, place) {
                var tabWidget = null;

                if(region && region.getChildren().length > 0){
                    array.every(region.getChildren(), function(child){
                        var widget = child.getChildren()[0];
                        if(child.get('region') == place && widget/*
																	 * &&
																	 * widget.get &&
																	 * widget.get('isTabWidget')
																	 */){
                            tabWidget = child;
                            return false;
                        }
                        return true;
                    });
                }

                return tabWidget;
            },

            /**
			 * Récupère le TabContainer dans une région du viewer.
			 * 
			 * @param region
			 *            la région principale du viewer.
			 * @returns {dijit.layout.TabContainer} TabContainer
			 */
            getTabContainerInRegion: function(region) {
                var tabContainer = null;

                if(region && region.getChildren().length > 0){
                    array.every(region.getChildren(), function(child){
                        if(child && child.isInstanceOf && child.isInstanceOf(TabContainer)){
                            tabContainer = child;
                            return false;
                        }
                        return true;
                    });
                }

                return tabContainer;
            },

            /**
			 * Adapte le cadrage de la map pour le mobile
			 * 
			 * @param extent
			 *            l'extent sur lequel centrer la map
			 */
            centerExtentForVisibleZone: function(extent) {
                if (!this.mobile) {
                    return extent;
                }

                var _map = this.get('spwMap').esriMap;
                var zone = this.spwMobile.getScreenVisibleZone();

                var xRatio = 1 / (zone.w / this.get('spwMap').esriMap.width);
                var yRatio = 1 / (zone.h / this.get('spwMap').esriMap.height);

                var tmp = _map._fixExtent(new esri.geometry.Extent(extent.toJson()), true); // tmp
																							// {
																							// extent,
																							// lod,
																							// tile
																							// }
                var newExtent = tmp.extent;

                if (xRatio > yRatio) {
                    var width = extent.getWidth();

                    var widthOnScreen = screenUtils.toScreenPoint(newExtent, _map.width, _map.height, new Point(extent.xmax, extent.ymax)).x
                        - screenUtils.toScreenPoint(newExtent, _map.width, _map.height, new Point(extent.xmin, extent.ymin)).x;

                    if (widthOnScreen === 0) {
                        return extent;
                    }

                    var tmpRatio = zone.w / widthOnScreen;
                    var newXmax = (width * tmpRatio) + extent.xmin;
                    var newXmin = extent.xmax - (width * tmpRatio);

                    newXmax = (newXmax - newXmin) * xRatio + newXmin;
                    return extent.update(newXmin, extent.ymin, newXmax, extent.ymax, extent.spatialReference);
                }
                else {
                    var height = extent.getHeight();

                    var heightOnScreen = screenUtils.toScreenPoint(newExtent, _map.width, _map.height, new Point(extent.xmin, extent.ymin)).y
                        - screenUtils.toScreenPoint(newExtent, _map.width, _map.height, new Point(extent.xmax, extent.ymax)).y;

                    if (heightOnScreen === 0) {
                        return extent;
                    }

                    var tmpRatio = (zone.h / heightOnScreen) * 2;
                    var newYmax = (height * tmpRatio) + extent.ymin;
                    var newYmin = extent.ymax - (height * tmpRatio);

                    newYmin = newYmax - ((newYmax - newYmin) * yRatio);
                    return extent.update(extent.xmin, newYmin, extent.xmax, newYmax, extent.spatialReference);
                }
            },

            /**
			 * Crée un point sur base d'un x et d'un y en l'adaptant à l'écran
			 * du téléphone
			 * 
			 * @param pt
			 *            la coordonnée X du point
			 * @param pt
			 *            la coordonnée Y du point
			 * @return le point adapté
			 */
            centerPointForVisibleZone: function(pt, y) {
                if (y) {
                    pt = new Point(pt, y);
                    pt.setSpatialReference(new SpatialReference(this.get('spwMap').getSpatialReferenceSRID()));
                }

                if (!this.mobile) {
                    return {
                        center: pt
                    };
                }

                var zone = this.spwMobile.getScreenVisibleZone();

                var zoneCenter = new ScreenPoint(zone.w / 2, zone.h / 2);

                var _map = this.get('spwMap').esriMap;

                var mp = screenUtils.toMapPoint(_map.extent, _map.width, _map.height, zoneCenter);

                var center = _map.extent.getCenter();

                var dx = pt.x - mp.x;
                var dy = pt.y - mp.y;

                center.x = center.x + dx;
                center.y = center.y + dy;

                return {
                    center: center,
                    dx: dx,
                    dy: dy
                };
            },

            /**
			 * Place un point au centre de la zone visible de la carte et
			 * recentre la carte
			 */
            centerInVisibleZone: function(x, y) {
                var res = this.centerPointForVisibleZone(x, y);
                this.get('spwMap').esriMap.centerAt(res.center);
            },

            /**
			 * Ajout un resizer (bouton permettant de changer à la volée la
			 * taille de la région) à la région
			 * 
			 * @param regionAP
			 *            la région à laquelle il faut ajouter le resizer
			 */
            addResizer: function(regionAP) {
                var div = domConstruct.create('div', {
                    'class': 'SpwBottomRegionTitleDiv',
                    style: 'width: 100%; height: 100%;'
                });

                var span = domConstruct.create('span', {
                    innerHTML: ''
                }, div, 'first');

                regionAP.setTitle = lang.hitch(regionAP, function(title) {
                    span.innerHTML = title ? title : '';
                });

                var closeButton = domConstruct.create('div', {
                    'class': 'SpwBottomRegionCloseButton',
                    innerHTML: 'X'
                }, div, 'last');

                var resizer = new ContentPane({
                    'class': 'SpwBottomRegionTitle',
                    content: div,
                    region: 'top'
                });
                var splitter = regionAP._splitterWidget;
                if(splitter){
                    resizer.own(
                        on(resizer, touch.press, lang.hitch(splitter, splitter._startDrag)),
                        // on(resizer, mouse.enter, lang.hitch(splitter,
						// splitter._onMouse)),
                        // on(resizer, mouse.leave, lang.hitch(splitter,
						// splitter._onMouse)),
                        on(closeButton, touch.press, lang.hitch(this, function() {
                            var tabContainer = this.getTabContainerInRegion(regionAP);
                            var w = null;

                            if (tabContainer) {
                                w = tabContainer.selectedChildWidget.getChildren()[0];
                            }
                            else {
                                // premier = resizer
                                w = regionAP.getChildren()[1].getChildren()[0];
                            }

                            w.closable && w.onDeactivate && w.onDeactivate();
                        }))
                    );

                    regionAP.addChild(resizer);
                    resizer.startup();
                    regionAP._resizerWidget = resizer;
                }
            },

            /**
			 * Place un widget dans une région
			 * 
			 * @param {spw.api.SpwBaseWidget}
			 *            widget le widget à placer
			 * @param {dijit.layout.TabContainer}
			 *            regionAP
			 * @param {String}
			 *            place la "sous-région", soit top, bottom, left ou
			 *            right.
			 */
            placeWidgetInRegion: function(widget, regionAP, place) {
                var panel = new ContentPane({
                    content: widget,
                    title: widget.widgetTitle ? widget.widgetTitle : "",
                    style:"padding:0;",
                    region:place
                });

                regionAP.addChild(panel);
                panel.startup();

                if(regionAP.getChildren().length > 1 && widget.widgetTitle){
                    var titleDiv = domConstruct.create("div", {style:"height:18px; width:100%;", "class":"customContentPaneTitle", innerHTML: widget.widgetTitle ? widget.widgetTitle : ""}, regionAP.domNode, "first");
                    lang.mixin(regionAP, {titleDiv: titleDiv});
                    panel.watch('title', function(prop, oldVal, newVal) {
                        if(panel.titleDiv){
                            panel.titleDiv.innerHTML = newVal;
                        }
                    });
                    domClass.add(panel.domNode, "titled");
                }
                
				if(regionAP.getChildren().length == 1 && widget.widgetTitle && widget.showTitleInRegion){
					domClass.add(regionAP.domNode, "singleWidget"); 
					regionAP.titleDiv = domConstruct.create("div", {style:"height:18px; width:100%;", "class":"customContentPaneTitle", innerHTML: widget.widgetTitle ? widget.widgetTitle : ""}, regionAP.domNode, "first");
				} else {
					domClass.remove(regionAP.domNode, "singleWidget");
				}
            },

            /**
			 * Retire un widget de sa région (sans le détruire).
			 * 
			 * @param widget
			 *            le widget à retirer.
			 */
            hideWidget: function(widget) {
                if(widget.domNode && widget.getParent && widget.getParent() && widget.getParent().removeChild){
                    var parentWidget = widget.getParent();
                    parentWidget.removeChild(widget);

                    if(parentWidget.titleDiv){
                        domConstruct.destroy(parentWidget.titleDiv);
                        domClass.remove(parentWidget.domNode, "titled");
                    }

                    if(!parentWidget.hasChildren()) {
                        this.hideWidget(parentWidget);
                    }
                    else if (parentWidget.getChildren().length === 1 && parentWidget.getChildren()[0] === parentWidget._resizerWidget) {
                        parentWidget.removeChild(parentWidget.getChildren()[0]);
                        parentWidget._resizerWidget = null;
                        this.hideWidget(parentWidget);
                    }
                    else if(parentWidget && parentWidget.isInstanceOf && parentWidget.isInstanceOf(TabContainer) && parentWidget.getChildren().length == 1) {
                        var upperParent = parentWidget.getParent();
                        var tabContent = parentWidget.getChildren()[0];
                        upperParent.removeChild(parentWidget);
                        if(upperParent.titleDiv){
                            domConstruct.destroy(upperParent.titleDiv);
                            domClass.remove(upperParent.domNode, "titled");
                        }
                        // add title div
                        if(tabContent.hasChildren() && tabContent.getChildren()[0] && tabContent.getChildren()[0].widgetTitle){
                            var titleDiv = domConstruct.create("div", {style:"height:18px; width:100%;", "class":"customContentPaneTitle", innerHTML: tabContent.getChildren()[0].widgetTitle ? tabContent.getChildren()[0].widgetTitle : ""}, upperParent.domNode, "first");
                            lang.mixin(upperParent, {titleDiv: titleDiv});
                            tabContent.watch('title', function(prop, oldVal, newVal) {
                                if(tabContent.titleDiv){
                                    tabContent.titleDiv.innerHTML = newVal;
                                }
                            });
                            domClass.add(tabContent.domNode, "titled");
                        }

                        if(tabContent && !tabContent.get('region')){
                            tabContent.set('region', 'center');
                        }
                        parentWidget.removeChild(tabContent);
                        upperParent.addChild(tabContent);
                    }
                }
            },

            /**
			 * Active le mode double écran
			 */
            doubleScreen: function() {
                if (this.mobile) {
                    MessageManager.getInstance().notifyWarning('Mode double écran non disponible en version mobile');
                    return;
                }

                if (this.sideWindow) {
                    this.sideWindow.focus();
                    return this.sideWindow;
                }

                var params = [
                    'height=' + screen.height,
                    'width=' + screen.width,
                    'menubar=no',
                    'fullscreen=yes' // ne fonctionne qu'avec IE
                ].join(',');

                var ctx = this.sideScreenContext ? this.sideScreenContext : ConfigLoader.getInstance().currentContext;

                this.sideWindow = window.open(location.href + '#CTX=' + ctx, '_blank', params);
                this.sideWindow.moveTo(0, 0);

                on.once(this.sideWindow, 'load', lang.hitch(this, function() {
                    var v = this.sideWindow.require('spw/api/SpwViewer').getInstance();

                    if (!v.viewerLoaded) {
                        on.once(v, SpwViewer.events.viewerLoaded, lang.hitch(this, this.sideViewerLoaded, v));
                    }
                    else {
                        this.sideViewerLoaded(v);
                    }

                    on.once(this.sideWindow, 'beforeunload', lang.hitch(this, this.onSideWindowClose));

                    this.repositionWidgets();
                }));

                return this.sideWindow;
            },

            /**
			 * Détermine si le viewer possède une seconde fenêtre (si le mode
			 * double écran est actif)
			 */
            hasSideWindow: function(){
                return this.sideWindow != null;
            },

            /**
			 * Détermine si la fenêtre actuelle est la seconde fenêtre du viewer
			 * ou la fenêtre principale
			 */
            isSideWindow: function(){
                try {
                    if(window.opener && window.opener != window && window.opener.require){
                    	try {
                            return window.opener.require("spw/api/SpwViewer").getInstance() != null && window.opener.require("spw/api/SpwViewer").getInstance().sideWindow == window;
                    	} catch (err) {
                    		return false;
                    	}
                    } else {
                        return false;
                    }
                } catch(error) {
                    console.info("Error while detecting isSideWindow", error);
                    return false;
                }

            },

            /**
			 * Repositionne les widgets une fois le mode double écran activé /
			 * désactivé
			 * 
			 * @param {string}
			 *            reset détermine s'il faut positionner les widgets sur
			 *            1 écran ou sur les 2
			 */
            repositionWidgets: function(reset) {
                reset = (reset === 'reset');

                var map = this.get('localSpwMap');
                var propsToCopy = ['position', 'top', 'bottom', 'right', 'left'];
                var toReactivate = false;
                var oldPos = null;

                array.forEach(this.spwWidgetsManager.getWidgets(), lang.hitch(this, function(widget) {
                    if (widget === map) {
                        if(!this.mapConfigMainScreen && !this.configMapSecondScreen) {
                            if (reset && map.closable) {
                                map.onActivate();
                            }
                            else {
                                map.onDeactivate();
                            }
                        }
                        return;
                    }

                    if (!widget.configWhenSideOpened && !widget.configMainScreen && !widget.configSecondScreen) {
                        // if (widget.position === 'map') {
                        // if (!reset) {
                        // widget.onDeactivate();
                        // }
                        // else {
                        // widget.onActivate();
                        // }
                        // }
                        return;
                    }

                    toReactivate = false;
                    oldPos = {};

                    if (widget.activated) {
                        widget.onDeactivate();
                        toReactivate = true;
                    }

                    if(widget.configMainScreen && widget.configMainScreen.activated !== true){
                        toReactivate = false;
                    }

                    if (widget.configWhenSideOpened === 'map') {
                        propsToCopy.forEach(function(prop) {
                            if (reset) {
                                if (widget.oldPosition) {
                                    widget.set(prop, widget.oldPosition[prop]);
                                }
                            }
                            else {
                                oldPos[prop] = (prop === 'activated' ? toReactivate : widget[prop]); // onDeactivate
																										// étant
																										// appelé
																										// juste
																										// avant...
                                widget.set(prop, map[prop]);
                            }
                        });
                    }
                    else if (widget.configWhenSideOpened) {
                        for (var key in widget.configWhenSideOpened) {
                            if (widget.configWhenSideOpened.hasOwnProperty(key)) {
                                if (reset) {
                                    if (widget.oldPosition) {
                                        widget.set(key, widget.oldPosition[key]);
                                    }
                                }
                                else {
                                    oldPos[key] = (key === 'activated' ? toReactivate : widget[key]); // onDeactivate
																										// étant
																										// appelé
																										// juste
																										// avant...
                                    widget.set(key, widget.configWhenSideOpened[key]);
                                }

                                if (key === 'activated') {
                                    toReactivate = widget[key];
                                    widget.set(key, false);
                                }
                            }
                        }
                    }

                    if (!reset) {
                        widget.oldPosition = oldPos;
                    }
                    else {
                        widget.oldPosition = null;
                    }

                    if (toReactivate) {
                        widget.onActivate();
                    }
                }));
            },

            /**
			 * Appelé lorsque le deuxième écran du mode double écran est fermé
			 */
            onSideWindowClose: function() {
                array.forEach(this.sideHandlers, function(h) {
                    h.remove();
                });

                this.set('spatialReference', this.get('localSpwMap').getSpatialReferenceSRID());

                if(this.sideWindow && this.sideWindow.viewer &&
                    this.sideWindow.require("spw/api/SpwViewer").getInstance().get('localSpwMap') &&
                    this.sideWindow.require("spw/api/SpwViewer").getInstance().get('localSpwMap').configSecondScreen.activated){
                    this.get('localSpwMap').copyServices(this.sideViewer.get('spwMap'));
                    this.get('localSpwMap').zoomToExtent(this.sideViewer.get('spwMap').getCurrentExtent());
                }

                this.repositionWidgets('reset');

                this.sideViewer = null;
                this.sideWindow = null;
                this.sideHandlers = null;

                this.emit(SpwViewer.events.SpwMapChanged, this.get('spwMap'));
            },

            /**
			 * Appelé lorsque le viewer de la seconde fenêtre est chargé
			 * 
			 * @param viewer
			 *            l'instance de SpwViewer
			 */
            sideViewerLoaded: function(viewer) {
                this.sideViewer = viewer;

                // TODO: cacher la carte + repositionner widgets
                this.sideHandlers = [].concat(viewer.get('spwMap').mapEvents(this.get('localSpwMap')));
                this.sideHandlers = this.sideHandlers.concat(this.get('localSpwMap').mapEvents(viewer.get('spwMap')));

                this.set('spatialReference', viewer.get('spwMap').getSpatialReferenceSRID());

                if(this.sideWindow && this.sideWindow.viewer &&
                    this.sideWindow.require("spw/api/SpwViewer").getInstance().get('localSpwMap') &&
                    this.sideWindow.require("spw/api/SpwViewer").getInstance().get('localSpwMap').configSecondScreen.activated){
                    viewer.get('spwMap').copyServices(this.get('localSpwMap'));
                }

                this.emit(SpwViewer.events.SpwMapChanged, viewer.get('spwMap'));
            },

            /**
			 * Redimensionne la carte
			 */
            resize: function() {
                this.inherited(arguments);

                this.spwViewerBorderContainer.resize();

                if (this.get('spwMap')) {
                    this.get('spwMap').resize();
                }
                // var floatingWidgets = this.spwWidgetsManager.getWidgets({position: 'panel-light'});
                // var activated = array.filter(floatingWidgets, lang.hitch(this, function(widget) {
                //     return widget.activated;
                // }));
                //
                // if (activated) {
                //     array.forEach(activated, lang.hitch(this, function(widget) {
                //         var constraint = widget._panel.moveable.constraints();
                //         var widgetPosition = domGeometry.position(widget._panel.domNode);
                //         var horizontal = widgetPosition.x + widgetPosition.w;
                //         var vertical = widgetPosition.y + widgetPosition.h;
                //         if (horizontal > constraint.w) {
                //             // domStyle.set(widget._panel.domNode, 'left', '15px');
                //             // domStyle.set(widget._panel.domNode, 'right', '');
                //         } else if (vertical > constraint.h) {
                //             console.log('widget must be verticaly repositioned');
                //         }
                //     }))
                // }


            },

            /**
			 * Permet d'enregistrer un évènement dans Google Analytics.
			 * 
			 * @param {String}
			 *            category catégorie de l'évènement
			 * @param {String}
			 *            action axction d'évènement
			 * @param {String}
			 *            label libellé pour l'action
			 * @param {String}
			 *            value valeur de l'action
			 */
            trackEvent : function(category,action,label,value) {
                if(typeof(_gaq) != "undefined") {
                    _gaq.push(['_trackEvent', category, action, label, value]);
                } else if(typeof(ga) == 'function') {
                    ga('send', 'event', category, action, label);
                }
            },

            /**
			 * Permet de projeter un point dans la projection actuelle de la
			 * carte.
			 * 
			 * @param from
			 *            référence spatiale du point
			 * @param x
			 *            coordonnée X du point
			 * @param y
			 *            coordonnée Y du point
			 */
            projectToMap: function(from, x, y) {
                if (x.x) {
                    y = x.y;
                    x = x.x;
                }

                return ProjectionManager.getInstance().projectPoint(from, this.get('spatialReference').wkid, x, y);
            },

            /**
			 * Permet de projeter un point de la même référence spatiale que la
			 * carte vers une autre référence spatiale.
			 * 
			 * @param dest
			 *            référence spatiale dans laquelle projeter
			 * @param x
			 *            coordonnée X du point
			 * @param y
			 *            coordonnée Y du point
			 */
            projectFromMap: function(dest, x, y) {
                if (x.x) {
                    y = x.y;
                    x = x.x;
                }

                return ProjectionManager.getInstance().projectPoint(this.get('spatialReference').wkid, dest, x, y);
            },

            /**
			 * Appelé à la destruction du SpwViewer
			 */
            destroy: function(){
                this.inherited(arguments);
                if(this.get('spwMap'))
                    this.get('spwMap').destroy();
                if(this.spwWidgetsManager)
                    this.spwWidgetsManager.destroy();
                _INSTANCE = null;
            }

        });

        SpwViewer.events = {
            /**
			 * Le Viewer est complètement chargé et prêt à être affiché.
			 * 
			 * @event spw.api.SpwViewer#viewerLoaded
			 */
            "viewerLoaded": "viewerLoaded",
            "SpwMapChanged": "SpwMapChanged"
        };

        /**
		 * Patch IE < 8 for VML TextSymbol
		 */
        if(!Font.prototype.decoration){
            Font.prototype.decoration = "none";
        }
        /**
		 * END patch
		 */

        /**
		 * Patch zIndex for Dijit Dialog
		 */
        if(Dialog && Dialog._DialogLevelManager) { Dialog._DialogLevelManager._beginZIndex = 5000; }
        /**
		 * END patch
		 */

        var _INSTANCE = null;
        /**
		 * Permet de récupérer l'instance du SpwViewer
		 * 
		 * @method getInstance
		 * @memberof spw.api.SpwViewer
		 * @returns spw.api.SpwViewer
		 */
        SpwViewer.getInstance = function(config){
            if(_INSTANCE == null){
                var context = Utils.gua("CTX") || Utils.gup("configContext") || Utils.gup("internalContext") || "default";
                var c = config && config.placementNode ? {placementNode: config.placementNode} : {};
                ConfigLoader.getInstance(c).load(context);
                var viewerConfig = lang.mixin(config, ConfigLoader.getInstance().get("viewer"));
                _INSTANCE = new SpwViewer(viewerConfig, domConstruct.create("div"));
            }
            return _INSTANCE;
        };

        return SpwViewer;
    });