diff --git a/build.xml b/build.xml index bc3864f3f..bde01edef 100644 --- a/build.xml +++ b/build.xml @@ -48,7 +48,20 @@ - + + + + + + + + + + + + + + @@ -60,7 +73,7 @@ - + @@ -117,6 +130,14 @@ + + + + + + + + @@ -132,6 +153,7 @@ + @@ -206,6 +228,10 @@ + + + + @@ -214,24 +240,54 @@ - + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mapcomposer/app/applications/example/buildjs.cfg b/mapcomposer/app/applications/example/buildjs.cfg new file mode 100644 index 000000000..67acb213f --- /dev/null +++ b/mapcomposer/app/applications/example/buildjs.cfg @@ -0,0 +1,5 @@ +[example.js] +root = static/script +include = + plugins/hello.js + diff --git a/mapcomposer/app/applications/example/config.js b/mapcomposer/app/applications/example/config.js new file mode 100644 index 000000000..60592dc6d --- /dev/null +++ b/mapcomposer/app/applications/example/config.js @@ -0,0 +1,4 @@ +exports.config = function(urls, middleware) { + urls[0] = [(/^\/(index(.html)?)?/), require("./index").app]; + middleware.push(require("ringo/middleware/static").middleware({base: module.resolve("static")})); +}; \ No newline at end of file diff --git a/mapcomposer/app/applications/example/index.js b/mapcomposer/app/applications/example/index.js new file mode 100644 index 000000000..1c7111325 --- /dev/null +++ b/mapcomposer/app/applications/example/index.js @@ -0,0 +1,18 @@ +var Response = require("ringo/webapp/response").Response; +var Request = require("ringo/webapp/request").Request; +var auth = require("../../auth"); + +exports.app = function(req) { + var request = new Request(req); + var details = auth.getDetails(request); + + if(request.isPost){ + var content = JSON.stringify(request.postParams); + print("Post Content is : " + content); + var response = Response.skin(module.resolve("templates/example.html"), {status: details.status || 404, content: content}); + }else{ + var response = Response.skin(module.resolve("templates/example.html"), {status: details.status || 404, content: "{}"}); + } + + return response; +}; diff --git a/mapcomposer/app/applications/example/static/config/exampleConfig.js b/mapcomposer/app/applications/example/static/config/exampleConfig.js new file mode 100644 index 000000000..848604d37 --- /dev/null +++ b/mapcomposer/app/applications/example/static/config/exampleConfig.js @@ -0,0 +1,64 @@ +{ + "portalConfig":{ + "header":false + }, + "gsSources": { + "google": { + "ptype": "gxp_googlesource" + } + }, + "map":{ + "projection": "EPSG:900913", + "units": "m", + "maxExtent": [ + -20037508.34, -20037508.34, + 20037508.34, 20037508.34 + ], + "layers": [{ + "source": "google", + "title": "Google Hybrid", + "name": "HYBRID", + "group": "background" + }], + "center": [1250000.000000, 5370000.000000], + "zoom": 5 + }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, + "customTools":[ + { + "ptype": "gxp_mouseposition", + "displayProjectionCode":"EPSG:4326", + "customCss": "font-weight: bold; text-shadow: 1px 0px 0px #FAFAFA, 1px 1px 0px #FAFAFA, 0px 1px 0px #FAFAFA,-1px 1px 0px #FAFAFA, -1px 0px 0px #FAFAFA, -1px -1px 0px #FAFAFA, 0px -1px 0px #FAFAFA, 1px -1px 0px #FAFAFA, 1px 4px 5px #aeaeae;color:#050505 " + }, { + "ptype": "gxp_geolocationmenu", + "actionTarget": {"target": "paneltbar", "index": 23}, + "toggleGroup": "toolGroup" + }, { + "ptype": "gxp_wmsgetfeatureinfo", + "useTabPanel": true, + "toggleGroup": "toolGroup", + "actionTarget": {"target": "paneltbar", "index": 24} + }, { + "ptype": "gxp_addlayer", + "showCapabilitiesGrid": true, + "useEvents": false, + "showReport": "never", + "directAddLayer": false, + "id": "addlayer" + }, { + "ptype": "gxp_about", + "poweredbyURL": "http://www.geo-solutions.it/about/contacts/", + "actionTarget": {"target": "panelbbar", "index": 1} + }, { + "ptype": "gxp_languageselector", + "actionTarget": {"target": "panelbbar", "index": 3} + }, { + "ptype": "gxp_hello", + "actionTarget": {"target": "paneltbar", "index": 25} + } + ] +} diff --git a/mapcomposer/app/applications/example/static/css/example.css b/mapcomposer/app/applications/example/static/css/example.css new file mode 100644 index 000000000..43f194204 --- /dev/null +++ b/mapcomposer/app/applications/example/static/css/example.css @@ -0,0 +1,3 @@ +.gx-hello{ + background:url(../images/hello.png) no-repeat; +} \ No newline at end of file diff --git a/mapcomposer/app/applications/example/static/images/hello.png b/mapcomposer/app/applications/example/static/images/hello.png new file mode 100644 index 000000000..dea5bddab Binary files /dev/null and b/mapcomposer/app/applications/example/static/images/hello.png differ diff --git a/mapcomposer/app/applications/example/static/script/plugins/hello.js b/mapcomposer/app/applications/example/static/script/plugins/hello.js new file mode 100644 index 000000000..2dd72cea1 --- /dev/null +++ b/mapcomposer/app/applications/example/static/script/plugins/hello.js @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2007 - 2012 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** api: (define) + * module = gxp.plugins + * class = HelloWorld + */ + +/** api: (extends) + * plugins/Tool.js + */ +Ext.namespace("gxp.plugins"); + +/** api: constructor + * .. class:: HelloWorld(config) + * + * Sample Plugin + * + */ +gxp.plugins.HelloWorld = Ext.extend(gxp.plugins.Tool, { + ptype:'gxp_hello', + + /** i18n */ + menuText:'hello', + text:'Hello', + title:'Hello Window', + iconCls:'gx-hello', + tooltip:'Open the Hello Window', + + + /** end of i18n */ + /** api: config[description] + * ``String`` Html to show in the window + */ + description: '

Hello window

I just wanted to say hello

', + /** api: config[showOnStartup] + * ``Boolean`` Show the window on startup if true + */ + showOnStartup:false, + /** api: config[windowOptions] + * ``Object`` Options for override the window configuration + */ + windowOptions:{ + height:200, + width:500 + }, + + /** api: method[addActions] + */ + addActions: function() { + var actions = [{ + menuText: this.menuText, + text:this.text, + enableToggle: false, + iconCls: this.iconCls, + tooltip: this.tooltip, + handler: function(button, state) { + this.showHello(); + }, + scope: this + }]; + if(this.showOnStartup){ + this.target.on('ready', this.showHello,this); + + } + return gxp.plugins.HelloWorld.superclass.addActions.apply(this, [actions]); + }, + + showHello:function(){ + + new Ext.Window(Ext.apply({ + layout:'fit', + title: this.title, + border:false, + autoScroll:false, + items:{html: this.description, autoScroll:true, bodyStyle:'padding:10px'}, + modal:true, + height:200 + },this.windowOptions)).show(); + } +}); + +Ext.preg(gxp.plugins.HelloWorld.prototype.ptype, gxp.plugins.HelloWorld); \ No newline at end of file diff --git a/mapcomposer/app/applications/example/templates/example.html b/mapcomposer/app/applications/example/templates/example.html new file mode 100644 index 000000000..a7b3e7e93 --- /dev/null +++ b/mapcomposer/app/applications/example/templates/example.html @@ -0,0 +1,423 @@ + + + + + Map Viewer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <% render content %> +
+ + diff --git a/mapcomposer/app/autoloader.js b/mapcomposer/app/autoloader.js index 6e3a362bb..32f37bd2a 100644 --- a/mapcomposer/app/autoloader.js +++ b/mapcomposer/app/autoloader.js @@ -69,22 +69,24 @@ var notFound = function() { throw {notfound: true}; }; -var App = function(config) { - - var sections = CONFIG.parse(config); - var group, root, order, urls = {}; - for (var section in sections) { - group = sections[section]; - // make root relative to config - root = FS.join(FS.directory(config), group.root[0]); - group.root = [root]; - order = MERGE.order(group); - // create lib loader - urls["/" + section] = libLoader(section, order); - // create static loader for all scripts in lib - var app = STATIC(root)(notFound); - urls["/@" + section] = app; - } +var App = function(configs) { + var group, root, order, urls = {}; + for(var i = 0, l = configs.length; i < l; i++) { + var config = configs[i]; + var sections = CONFIG.parse(config); + for (var section in sections) { + group = sections[section]; + // make root relative to config + root = FS.join(FS.directory(config), group.root[0]); + group.root = [root]; + order = MERGE.order(group); + // create lib loader + urls["/" + section] = libLoader(section, order); + // create static loader for all scripts in lib + var app = STATIC(root)(notFound); + urls["/@" + section] = app; + } + } return URLMap(urls); }; diff --git a/mapcomposer/app/config.js b/mapcomposer/app/config.js index 65a8e08b7..1e82dfb39 100644 --- a/mapcomposer/app/config.js +++ b/mapcomposer/app/config.js @@ -5,6 +5,7 @@ var urls = [ [(/^\/(index(.html)?)?/), require("./root/index").app], //[(/^\/(login)/), require("./root/login").app], [(/^\/(loginpage)/), require("./root/loginpage").app], + [(/^\/(session)/), require("./root/session").app], //[(/^\/(maps(\/\d+)?)/), require("./root/maps").app], //[(/^\/(geonetwork)/), require("./root/geonetwork").app], // Enable this only for the GeoNetwork integration [(/^\/(composer)/), require("./root/composer").app], @@ -21,13 +22,19 @@ if (debug_proxy) { urls.push([(/^\/(proxy)/), require("./root/proxy").app]); } +var FS = require("fs"); // debug mode loads unminified scripts if (java.lang.System.getProperty("app.debug")) { - var FS = require("fs"); + var config = FS.normal(FS.join(module.directory, "..", "buildjs.cfg")); + var configs = [config]; + if(environment.applicationPath) { + configs.push(FS.normal(FS.join(module.directory, environment.applicationPath.toString(), "buildjs.cfg"))); + } urls.push( - [(/^\/script(\/.*)/), require("./autoloader").App(config)] + [(/^\/script(\/.*)/), require("./autoloader").App(configs)] ); + // proxy a remote geoserver on /geoserver by setting proxy.geoserver to remote URL // only recommended for debug mode @@ -105,3 +112,36 @@ exports.app = require("ringo/webapp").handleRequest; exports.charset = "UTF-8"; exports.contentType = "text/html"; + +// handle application configuration +if(environment.applicationPath) { + // for debug + var applicationConfig = require(environment.applicationPath.toString() + '/config'); + applicationConfig.config(urls, exports.middleware); +} else if(!java.lang.System.getProperty("app.debug")) { + // for deploy + var applicationsFolder = getRepository(module.resolve('./applications')); + + if(applicationsFolder && applicationsFolder.exists()) { + var application = null; + var files = applicationsFolder.getResources(true); + + for(var i = 0, l = files.length; i < l; i++) { + var file = files[i].path; + // Looks for the first folder containing a config.js file + if(file.lastIndexOf("config.js") > -1 ){ + + file = file.substring(applicationsFolder.path.length); + application = file.split('/config.js')[0]; + + break; + } + } + + if(application) { + var applicationConfig = require('applications/' + application + '/config'); + applicationConfig.config(urls, exports.middleware); + } + } +} + diff --git a/mapcomposer/app/main.js b/mapcomposer/app/main.js index e0d7613a5..06794ebc3 100644 --- a/mapcomposer/app/main.js +++ b/mapcomposer/app/main.js @@ -3,7 +3,26 @@ // main script to start application if (require.main == module) { - // TODO: accept args + var {Parser} = require('ringo/args'); + var parser = new Parser(); + parser.addOption("p", "port", '8080', "Optional port (defaults to 8081)") + .addOption("a", "application", '[APPLICATION]', "Optional application"); + var args = []; + for(var i = 1; i< system.args.length; i++) { + args.push(system.args[i]); + } + system.args = ['mapcomposer/app/main.js']; + var options = parser.parse(args); + if(options.port) { + system.args = ['mapcomposer/app/main.js','-p',options.port]; + } java.lang.System.setProperty("app.debug", 1); - require("ringo/webapp").main(module.directory); + var {writeln} = require('ringo/term'); + if(options.application) { + environment.applicationPath = 'applications/' + options.application; + writeln('Loading application from '+environment.applicationPath); + require("ringo/webapp").main(module.directory); + } else { + require("ringo/webapp").main(module.directory); + } } diff --git a/mapcomposer/app/root/composer.js b/mapcomposer/app/root/composer.js index 3fe2f95d1..a1ecf3c47 100644 --- a/mapcomposer/app/root/composer.js +++ b/mapcomposer/app/root/composer.js @@ -8,7 +8,7 @@ exports.app = function(req) { if(request.isPost){ var content = JSON.stringify(request.postParams); - print("Post Content is : " + content); + //print("Post Content is : " + content); var response = Response.skin(module.resolve("../templates/composer.html"), {status: details.status || 404, content: content}); }else{ var response = Response.skin(module.resolve("../templates/composer.html"), {status: details.status || 404, content: "{}"}); diff --git a/mapcomposer/app/root/proxy.js b/mapcomposer/app/root/proxy.js index e8bec45d9..150e06817 100644 --- a/mapcomposer/app/root/proxy.js +++ b/mapcomposer/app/root/proxy.js @@ -119,17 +119,22 @@ function proxyPass(config) { async: false }); } - exchange.wait(); - var headers = new Headers(objects.clone(exchange.headers)); - if (!config.allowAuth) { - // strip out authorization and cookie headers - headers.unset("WWW-Authenticate"); - headers.unset("Set-Cookie"); + if (!exchange) { + response = responseForStatus(404); + return response; + } else { + exchange.wait(); + var headers = new Headers(objects.clone(exchange.headers)); + if (!config.allowAuth) { + // strip out authorization and cookie headers + headers.unset("WWW-Authenticate"); + headers.unset("Set-Cookie"); + } + headers.unset("Content-Length"); + return { + status: exchange.status, + headers: headers, + body: new MemoryStream(exchange.contentBytes) + }; } - headers.unset("Content-Length"); - return { - status: exchange.status, - headers: headers, - body: new MemoryStream(exchange.contentBytes) - }; } diff --git a/mapcomposer/app/root/session.js b/mapcomposer/app/root/session.js new file mode 100644 index 000000000..b71cf08f0 --- /dev/null +++ b/mapcomposer/app/root/session.js @@ -0,0 +1,10 @@ +var Response = require("ringo/webapp/response").Response; +var Request = require("ringo/webapp/request").Request; +var auth = require("../auth"); + +exports.app = function(req) { + var request = new Request(req); + var details = auth.getDetails(request); + var response = Response.skin(module.resolve("../templates/sessionlogin.html"), {status: details.status || 404}); + return response; +}; diff --git a/mapcomposer/app/static/config/common/localConfig.js b/mapcomposer/app/static/config/common/localConfig.js index ed0f5346f..0ff6d2731 100644 --- a/mapcomposer/app/static/config/common/localConfig.js +++ b/mapcomposer/app/static/config/common/localConfig.js @@ -1,5 +1,7 @@ -/** This file contains the common configuration options - * that can be overridden by the serverConfig objects in templates */ +/** + * This file contains the common configuration options that can + * be overridden by the serverConfig objects in templates. + */ var localConfig = { geoStoreBase: "", proxy:"/http_proxy/proxy/?url=", diff --git a/mapcomposer/app/static/config/comunege.js b/mapcomposer/app/static/config/comunege.js deleted file mode 100644 index 47205ab0a..000000000 --- a/mapcomposer/app/static/config/comunege.js +++ /dev/null @@ -1,324 +0,0 @@ -{ - "geoStoreBase":"", - "gnBaseUrl": "http://vm-gistest1/geonetwork/", - "defaultLanguage": "it", - "advancedScaleOverlay": true, - "tab": true, - "gsSources":{ - "mapquest": { - "ptype": "gxp_mapquestsource" - }, - "osm": { - "ptype": "gxp_osmsource" - }, - "google": { - "ptype": "gxp_googlesource" - }, - "bing": { - "ptype": "gxp_bingsource" - }, - "ol": { - "ptype": "gxp_olsource" - }, - "local": { - "ptype": "gxp_wmssource", - "title": "Local GeoServer", - "version":"1.1.1", - "url": "http://localhost:8080/geoserver/ows" - } - }, - "map": { - "projection": "EPSG:900913", - "units": "m", - "center": [1250000.000000, 5370000.000000], - "zoom":5, - "maxExtent": [ - -20037508.34, -20037508.34, - 20037508.34, 20037508.34 - ], - "layers": [ - { - "source": "local", - "title": "Circoscrizioni", - "name": "topp:circoscrizioni" - }, - { - "source": "bing", - "title": "Bing Aerial", - "name": "Aerial", - "group": "background" - }, { - "source": "osm", - "title": "Open Street Map", - "name": "mapnik", - "group": "background" - },{ - "source": "mapquest", - "title": "MapQuest OpenStreetMap", - "name": "osm", - "group": "background" - },{ - "source": "google", - "title": "Google Roadmap", - "name": "ROADMAP", - "group": "background" - },{ - "source": "google", - "title": "Google Terrain", - "name": "TERRAIN", - "group": "background" - },{ - "source": "google", - "title": "Google Hybrid", - "name": "HYBRID", - "group": "background" - } - ] - }, - "customPanels":[ - { - "xtype": "tabpanel", - "title": "Data Viewer", - "border": false, - "id": "south", - "region": "south", - "split":true, - "height": 330, - "collapsed": true, - "collapsible": true, - "activeItem": 0, - "header": true, - "items": [ - { - "xtype": "container", - "title": "Feature Grid", - "border": false, - "layout": "fit", - "id": "featuregrid" - },{ - "xtype": "container", - "title": "Metadata Explorer", - "iconCls": "csw-viewer", - "border": false, - "layout": "fit", - "id": "metadata" - } - ] - }, - { - "xtype": "panel", - "title": "Query Panel", - "border": false, - "id": "east", - "width": 400, - "height": 500, - "region": "east", - "layout": "fit", - "collapsed": false, - "collapsible": true, - "header": true - } - - ], - "removeTools": ["googleearth_plugin", "googleearth_separator"], - "customTools":[ - { - "ptype":"gxp_print", - "customParams":{ - "outputFilename":"mapstore-print", - "geodetic": true - }, - "ignoreLayers": "Google Hybrid,Bing Aerial,Nessuno sfondo,Google Terrain,Google Roadmap,Marker,GeoRefMarker", - "printService":"http://vm-gistest1/geoserver/pdf/", - "legendPanelId":"legendPanel", - "actionTarget":{ - "target":"paneltbar", - "index":4 - } - }, - { - "ptype": "gxp_featuremanager", - "id": "featuremanager", - "paging": false, - "page": {}, - "layer": { - "source": "local", - "name": "topp:circoscrizioni" - }, - "autoLoadFeatures": true - }, - { - "ptype": "gxp_featureeditor", - "featureManager": "featuremanager", - "autoLoadFeatures": true - }, - { - "ptype": "gxp_featuregrid", - "featureManager": "featuremanager", - "outputConfig": { - "loadMask": true - }, - "showExportCSV": true, - "outputTarget": "featuregrid" - }, - { - "ptype": "gxp_mouseposition", - "displayProjectionCode":"EPSG:4326", - "customCss": "font-weight: bold; text-shadow: 1px 0px 0px #FAFAFA, 1px 1px 0px #FAFAFA, 0px 1px 0px #FAFAFA,-1px 1px 0px #FAFAFA, -1px 0px 0px #FAFAFA, -1px -1px 0px #FAFAFA, 0px -1px 0px #FAFAFA, 1px -1px 0px #FAFAFA, 1px 4px 5px #aeaeae;color:#050505 " - },{ - "ptype": "gxp_metadataexplorer", - "id": "metadataexplorer", - "outputTarget": "metadata", - "saveState": true, - "cswconfig": { - "catalogs": [ - {"name": "CSI Piemonte", "url": "http://www.ruparpiemonte.it/geocatalogorp/geonetworkrp/srv/it/csw", "description": "GeoPortale della Regione Piemonte"}, - {"name": "Comune di Firenze", "url": "http://datigis.comune.fi.it/geonetwork/srv/it/csw", "description": "GeoPortale del Comune di Firenze"}, - {"name": "PTA", "url": "http://pta.partout.it/geoportalPTA/csw", "description": "Piattaforma Tecnologica alpina", "metaDataOptions":{"base":"http://pta.partout.it/geoportalPTA/catalog/search/resource/details.page","idParam":"uuid","idIndex":0}}, - {"name": "Treviso", "url": "http://ows.provinciatreviso.it/geonetwork/srv/it/csw", "description": "Treviso Geonetwork"}, - {"name": "kscNet", "url": "http://geoportal.kscnet.ru/geonetwork/srv/ru/csw", "description": "kscNet"}, - {"name": "CSI-CGIAR", "url": "http://geonetwork.csi.cgiar.org/geonetwork/srv/en/csw", "description" : "CSI-CGIAR"}, - {"name": "EauFrance", "url": "http://sandre.eaufrance.fr/geonetwork/srv/fr/csw", "description" : "EauFrance"}, - {"name": "SOPAC", "url": "http://geonetwork.sopac.org/geonetwork/srv/en/csw", "description" : "SOPAC"}, - {"name": "SADC", "url": "http://www.sadc.int/geonetwork/srv/en/csw", "description" : "SADC"}, - {"name": "MAPAS", "url": "http://mapas.mma.gov.br/geonetwork/srv/en/csw", "description" : "MAPAS"} - ], - "dcProperty": "title", - "initialBBox": { - "minx": 11.145, - "miny": 43.718, - "maxx": 11.348, - "maxy": 43.84 - }, - "cswVersion": "2.0.2", - "filterVersion": "1.1.0", - "start": 1, - "limit": 10, - "timeout": 60000 - } - }, { - "actions": ["-"], - "actionTarget": "paneltbar" - }, { - "ptype": "gxp_geolocationmenu", - "actionTarget": {"target": "paneltbar", "index": 23}, - "toggleGroup": "toolGroup" - }, { - "ptype": "gxp_addlayer", - "showCapabilitiesGrid": true, - "id": "addlayer" - }, { - "ptype": "gxp_spatialqueryform", - "featureManager": "featuremanager", - "featureGridContainer": "south", - "outputTarget": "east", - "showSelectionSummary": true, - "actions": null, - "id": "bboxquery", - "outputConfig":{ - "outputSRS": "EPSG:900913", - "selectStyle":{ - "strokeColor": "#ee9900", - "fillColor": "#ee9900", - "fillOpacity": 0.4, - "strokeWidth": 1 - }, - "spatialFilterOptions": { - "lonMax": 20037508.34, - "lonMin": -20037508.34, - "latMax": 20037508.34, - "latMin": -20037508.34 - }, - "bufferOptions": { - "minValue": 1, - "maxValue": 1000, - "decimalPrecision": 2, - "distanceUnits": "m" - } - }, - "spatialSelectorsConfig":{ - "bbox":{ - "ptype": "gxp_spatial_bbox_selector" - }, - "buffer":{ - "ptype": "gxp_spatial_buffer_selector" - }, - "circle":{ - "ptype": "gxp_spatial_circle_selector", - "zoomToCurrentExtent": true - }, - "polygon":{ - "ptype": "gxp_spatial_polygon_selector" - }, - "municipi":{ - "ptype": "gxp_spatial_geocoding_selector", - "multipleSelection": false, - "name": "Municipi", - "label": "Municipi", - "searchComboOutputFormat": "json", - "wfsBaseURL": "http://geoserver.comune.genova.it/geoserver/wfs", - "geocoderTypeName": "TOPONOMASTICA:MUNICIPI", - "geocoderTypeRecordModel":[ - { - "name":"id", - "mapping":"id" - }, - { - "name":"name", - "mapping":"properties.NOME_MUNIC" - }, - { - "name":"custom", - "mapping":"properties.COD_MUNIC" - }, - { - "name":"geometry", - "mapping":"geometry" - } - ], - "geocoderTypeSortBy":null, - "geocoderTypeQueriableAttributes":[ - "NOME_MUNIC", "COD_MUNIC", "ID1" - ], - "spatialOutputCRS": "EPSG:4326", - "geocoderTypePageSize": 10, - "zoomToCurrentExtent": true - }, - "unita":{ - "ptype": "gxp_spatial_geocoding_selector", - "multipleSelection": false, - "name": "Unita Urbanistiche", - "label": "Unita Urbanistiche", - "searchComboOutputFormat": "json", - "wfsBaseURL": "http://geoserver.comune.genova.it/geoserver/wfs", - "geocoderTypeName": "TOPONOMASTICA:UNITA_URBANISTICHE", - "geocoderTypeRecordModel":[ - { - "name":"id", - "mapping":"id" - }, - { - "name":"name", - "mapping":"properties.NOME_UU" - }, - { - "name":"custom", - "mapping":"properties.COD_UU" - }, - { - "name":"geometry", - "mapping":"geometry" - } - ], - "geocoderTypeSortBy":null, - "geocoderTypeQueriableAttributes":[ - "NOME_UU", "COD_UU", "ID1" - ], - "spatialOutputCRS": "EPSG:3003", - "geocoderTypePageSize": 10, - "zoomToCurrentExtent": true - } - } - } - ] -} diff --git a/mapcomposer/app/static/config/customPanelSample.js b/mapcomposer/app/static/config/customPanelSample.js index d1c0cf3b6..de527dfd0 100644 --- a/mapcomposer/app/static/config/customPanelSample.js +++ b/mapcomposer/app/static/config/customPanelSample.js @@ -55,6 +55,11 @@ "title": "MapStore WebSite" } ], + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map":{ "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/examples/onbeforeunload.js b/mapcomposer/app/static/config/examples/onbeforeunload.js new file mode 100644 index 000000000..1dfa9c035 --- /dev/null +++ b/mapcomposer/app/static/config/examples/onbeforeunload.js @@ -0,0 +1,179 @@ +{ + "scaleOverlayMode": "basic", + "gsSources":{ + "mapquest": { + "ptype": "gxp_mapquestsource" + }, + "osm": { + "ptype": "gxp_osmsource" + }, + "google": { + "ptype": "gxp_googlesource" + }, + "bing": { + "ptype": "gxp_bingsource" + }, + "ol": { + "ptype": "gxp_olsource" + } + }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, + "map": { + "projection": "EPSG:900913", + "units": "m", + "center": [1250000.000000, 5370000.000000], + "zoom":5, + "maxExtent": [ + -20037508.34, -20037508.34, + 20037508.34, 20037508.34 + ], + "layers": [ + { + "source": "google", + "title": "Google Roadmap", + "name": "ROADMAP", + "group": "background" + },{ + "source": "google", + "title": "Google Terrain", + "name": "TERRAIN", + "group": "background" + },{ + "source": "google", + "title": "Google Hybrid", + "name": "HYBRID", + "group": "background" + },{ + "source": "mapquest", + "title": "MapQuest OpenStreetMap", + "name": "osm", + "group": "background" + },{ + "source": "osm", + "title": "Open Street Map", + "name": "mapnik", + "group": "background" + },{ + "source": "bing", + "title": "Bing Aerial", + "name": "Aerial", + "group": "background" + },{ + "source": "bing", + "title": "Bing Aerial With Labels", + "name": "AerialWithLabels", + "group": "background" + },{ + "source": "ol", + "group": "background", + "fixed": true, + "type": "OpenLayers.Layer", + "visibility": false, + "args": [ + "None", {"visibility": false} + ] + } + ] + }, + "customPanels":[ + { + "xtype": "panel", + "title": "Metadata Explorer", + "iconCls": "csw-viewer", + "border": false, + "id": "south", + "region": "south", + "layout": "fit", + "split":true, + "height": 330, + "collapsed": true, + "collapsible": true, + "ctCls": "south-panel", + "header": true + } + ], + "scaleOverlayUnits":{ + "bottomOutUnits":"nmi", + "bottomInUnits":"nmi", + "topInUnits":"m", + "topOutUnits":"km" + }, + "customTools":[ + { + "ptype": "gxp_embedmapdialog", + "actionTarget": {"target": "paneltbar", "index": 2}, + "embeddedTemplateName": "viewer", + "showDirectURL": true + }, { + "ptype": "gxp_categoryinitializer", + "silentErrors": true + }, { + "ptype": "gxp_mouseposition", + "displayProjectionCode":"EPSG:4326", + "customCss": "font-weight: bold; text-shadow: 1px 0px 0px #FAFAFA, 1px 1px 0px #FAFAFA, 0px 1px 0px #FAFAFA,-1px 1px 0px #FAFAFA, -1px 0px 0px #FAFAFA, -1px -1px 0px #FAFAFA, 0px -1px 0px #FAFAFA, 1px -1px 0px #FAFAFA, 1px 4px 5px #aeaeae;color:#050505 " + }, { + "ptype": "gxp_metadataexplorer", + "id": "metadataexplorer", + "outputTarget": "south", + "saveState":true, + "cswconfig": { + "catalogs": [ + {"name": "CSI Piemonte", "url": "http://www.ruparpiemonte.it/geocatalogorp/geonetworkrp/srv/it/csw", "description": "GeoPortale della Regione Piemonte"}, + {"name": "Comune di Firenze", "url": "http://datigis.comune.fi.it/geonetwork/srv/it/csw", "description": "GeoPortale del Comune di Firenze"}, + {"name": "PTA", "url": "http://pta.partout.it/geoportalPTA/csw", "description": "Piattaforma Tecnologica alpina", "metaDataOptions":{"base":"http://pta.partout.it/geoportalPTA/catalog/search/resource/details.page","idParam":"uuid","idIndex":0}}, + {"name": "Treviso", "url": "http://ows.provinciatreviso.it/geonetwork/srv/it/csw", "description": "Treviso Geonetwork"}, + {"name": "kscNet", "url": "http://geoportal.kscnet.ru/geonetwork/srv/ru/csw", "description": "kscNet"}, + {"name": "CSI-CGIAR", "url": "http://geonetwork.csi.cgiar.org/geonetwork/srv/en/csw", "description" : "CSI-CGIAR"}, + {"name": "EauFrance", "url": "http://sandre.eaufrance.fr/geonetwork/srv/fr/csw", "description" : "EauFrance"}, + {"name": "SOPAC", "url": "http://geonetwork.sopac.org/geonetwork/srv/en/csw", "description" : "SOPAC"}, + {"name": "SADC", "url": "http://www.sadc.int/geonetwork/srv/en/csw", "description" : "SADC"}, + {"name": "MAPAS", "url": "http://mapas.mma.gov.br/geonetwork/srv/en/csw", "description" : "MAPAS"} + ], + "dcProperty": "title", + "cswVersion": "2.0.2", + "filterVersion": "1.1.0", + "start": 1, + "limit": 10, + "timeout": 60000 + } + }, { + "ptype": "gxp_addlayer", + "showCapabilitiesGrid": true, + "useEvents": false, + "showReport": "never", + "directAddLayer": false, + "id": "addlayer" + }, { + "actions": ["-"], + "actionTarget": "paneltbar" + }, { + "ptype": "gxp_geolocationmenu", + "actionTarget": {"target": "paneltbar", "index": 23}, + "toggleGroup": "toolGroup" + }, { + "actions": ["->"], + "actionTarget": "paneltbar" + }, { + "ptype": "gxp_help", + "actionTarget": "paneltbar", + "text": "Help", + "tooltip":"MapStore Guide", + "index": 24, + "showOnStartup": false, + "fileDocURL": "MapStore-Help.pdf" + }, { + "ptype": "gxp_about", + "poweredbyURL": "http://www.geo-solutions.it/about/contacts/", + "actionTarget": {"target": "panelbbar", "index": 1} + }, { + "ptype": "gxp_languageselector", + "actionTarget": {"target": "panelbbar", "index": 3} + }, { + "ptype":"gxp_onpageunloadalert" + } + ] +} diff --git a/mapcomposer/app/static/config/geonetwork.js b/mapcomposer/app/static/config/geonetwork.js index 0b46a9969..2da8f5bbc 100644 --- a/mapcomposer/app/static/config/geonetwork.js +++ b/mapcomposer/app/static/config/geonetwork.js @@ -21,6 +21,11 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map": { "projection": "EPSG:900913", "units": "m", @@ -112,7 +117,7 @@ {"name": "SOPAC", "url": "http://geonetwork.sopac.org/geonetwork/srv/en/csw", "description" : "SOPAC"}, {"name": "SADC", "url": "http://www.sadc.int/geonetwork/srv/en/csw", "description" : "SADC"}, {"name": "MAPAS", "url": "http://mapas.mma.gov.br/geonetwork/srv/en/csw", "description" : "MAPAS"} - ], + ], "dcProperty": "title", "initialBBox": { "minx": 11.145, @@ -137,6 +142,16 @@ "ptype": "gxp_addlayer", "showCapabilitiesGrid": true, "id": "addlayer" + }, { + "ptype": "gxp_about", + "poweredbyURL": "null", + "actionTarget": {"target": "panelbbar", "index": 1} + }, { + "ptype": "gxp_wmsgetfeatureinfo_menu", + "regex": "[\\s\\S]*[\\w]+[\\s\\S]*", + "useTabPanel": true, + "toggleGroup": "toolGroup", + "actionTarget": {"target": "paneltbar", "index": 20} } ] } diff --git a/mapcomposer/app/static/config/manager/managerConfig-admin.js b/mapcomposer/app/static/config/manager/managerConfig-admin.js new file mode 100644 index 000000000..998a46ac5 --- /dev/null +++ b/mapcomposer/app/static/config/manager/managerConfig-admin.js @@ -0,0 +1,201 @@ +{ + "composerUrl":"", + "socialUrl":"", + "start":0, + "limit":20, + "msmTimeout":30000, + "header":{}, + + "twitter":{ + "via":"geosolutions_it", + "hashtags":"" + }, + "mediaContent":"./externals/mapmanager/theme/media", + "ASSET":{ + "delete_icon": "./externals/mapmanager/theme/img/user_delete.png", + "edit_icon": "./externals/mapmanager/theme/img/user_edit.png" + }, + "locales":[ + [ + "en", + "English" + ], + [ + "it", + "Italiano" + ], + [ + "fr", + "Français" + ], + [ + "de", + "Deutsch" + ], + [ + "es", + "Español" + ] + ], + "tools":[{ + "ptype": "mxp_login", + "pluginId": "loginTool", + "forceLogin":true, + "actionTarget":{ + "target": "north.tbar", + "index": 3 + } + }], + "adminTools":[{ + "ptype": "mxp_entity_manger", + "buttonText": "OpenSDI Info", + "iconCls":"information_ic", + "notDuplicateOutputs":true, + "actionTarget":{ + "target": "north.tbar", + "index": 0 + }, + "entities": [ + { "api":{}, + + "autoExpandColumn": "patterns", + "autoload": true, + "basePath": "mvc/OpenSDIInfo/endpointdoc/", + "canCreate": false, + "canDelete": false, + "canEdit": false, + "iconCls": "information_ic", + "id": "Mappings", + "idProperty": "id", + "name": "Mappings", + "pluralName": "Mappings", + "restful": false, + "root": "data", + "columns": [ + { + "header": "Bean", + "mapping": "method.bean", + "name": "bean" + }, + { + "header": "Type", + "mapping": "method.type", + "name": "type" + },{ + "header": "Class Method", + "xtype":"templatecolumn", + "tpl":"{name}" + },{ + "header": "Patterns", + "xtype":"templatecolumn", + "tpl":"{.} ", + "name": "patterns" + } + ], + "displayField": "label", + "editHeight": 270, + "fields": [ + { + "mapping": "method.bean", + "name": "bean" + },{ + "mapping": "method.beanType", + "name": "type" + },{ + "mapping":"method.method", + "name":"method", + }, + { + "mapping": "info.patternsCondition.patterns", + "name": "patterns" + } + ], + + + }, { "api":{}, + + "autoExpandColumn": "path", + "autoload": true, + "basePath": "mvc/OpenSDIInfo/facade/", + "canCreate": false, + "canDelete": false, + "canEdit": false, + "iconCls": "icon-export", + "id": "Facade", + "idProperty": "path", + "name": "Facade", + "pluralName": "Facades", + "restful": false, + "root": "data", + "columns": [ + { + "header": "Path", + "mapping": "path", + "name": "path" + },{ + "header": "Url", + "name": "urlWrapped", + "mapping":"urlWrapped" + + } + ], + "displayField": "label", + "editHeight": 270, + "fields": [ + { + "mapping": "path", + "name": "path" + },{ + "mapping":"urlWrapped", + "name":"urlWrapped", + "type":"string" + },{ + "mapping": "proxy", + "name": "proxy" + } + ] + } + + ] + }, { + "ptype": "mxp_login", + "pluginId": "loginTool", + "actionTarget":{ + "target": "north.tbar", + "index": 10 + } + }], + "loggedTools":[{ + "ptype": "mxp_mapmanager", + "loginManager": "loginTool", + "actionTarget": null + },{ + "ptype": "mxp_myaccount", + "loginManager": "loginTool", + "actionTarget":{ + "target": "north.tbar", + "index": 1 + } + },{ + "ptype": "mxp_login", + "pluginId": "loginTool", + "actionTarget":{ + "target": "north.tbar", + "index": 4 + } + },{ + "ptype": "mxp_languageselector", + "actionTarget":{ + "target": "north.tbar", + "index": 8 + } + }], + "embedLink": { + "embeddedTemplateName": "viewer", + "showDirectURL": true, + "showQRCode":true, + "qrCodeSize":128, + "appDownloadUrl":"http://demo.geo-solutions.it/share/mapstoremobile/MapStoreMobile.apk" + + } +} diff --git a/mapcomposer/app/static/config/managerConfig.js b/mapcomposer/app/static/config/managerConfig.js index a2bc066dd..0215d119d 100644 --- a/mapcomposer/app/static/config/managerConfig.js +++ b/mapcomposer/app/static/config/managerConfig.js @@ -8,6 +8,7 @@ "via":"geosolutions_it", "hashtags":"" }, + "loginDataStorage" : "sessionStorage", "mediaContent":"./externals/mapmanager/theme/media", "ASSET":{ "delete_icon": "./externals/mapmanager/theme/img/user_delete.png", @@ -35,6 +36,7 @@ "Español" ] ], + "cookieConsent":true, "tools":[{ "ptype": "mxp_mapmanager", "loginManager": "loginTool", diff --git a/mapcomposer/app/static/config/mapStoreConfig.js b/mapcomposer/app/static/config/mapStoreConfig.js index df0e86ad3..935ab9962 100644 --- a/mapcomposer/app/static/config/mapStoreConfig.js +++ b/mapcomposer/app/static/config/mapStoreConfig.js @@ -1,7 +1,6 @@ { - "scaleOverlayMode": "basic", - "gsSources":{ + "gsSources":{ "mapquest": { "ptype": "gxp_mapquestsource" }, @@ -18,6 +17,12 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, + "cookieConsent":true, "map": { "projection": "EPSG:900913", "units": "m", @@ -63,6 +68,15 @@ "title": "Bing Aerial With Labels", "name": "AerialWithLabels", "group": "background" + },{ + "source": "ol", + "group": "background", + "fixed": true, + "type": "OpenLayers.Layer", + "visibility": false, + "args": [ + "None", {"visibility": false} + ] } ] }, @@ -96,7 +110,8 @@ "embeddedTemplateName": "viewer", "showDirectURL": true }, { - "ptype": "gxp_categoryinitializer" + "ptype": "gxp_categoryinitializer", + "silentErrors": true }, { "ptype": "gxp_mouseposition", "displayProjectionCode":"EPSG:4326", @@ -117,8 +132,8 @@ {"name": "EauFrance", "url": "http://sandre.eaufrance.fr/geonetwork/srv/fr/csw", "description" : "EauFrance"}, {"name": "SOPAC", "url": "http://geonetwork.sopac.org/geonetwork/srv/en/csw", "description" : "SOPAC"}, {"name": "SADC", "url": "http://www.sadc.int/geonetwork/srv/en/csw", "description" : "SADC"}, - {"name": "MAPAS", "url": "http://mapas.mma.gov.br/geonetwork/srv/en/csw", "description" : "MAPAS"} - ], + {"name": "MAPAS", "url": "http://mapas.mma.gov.br/geonetwork/srv/en/csw", "description" : "MAPAS"} + ], "dcProperty": "title", "cswVersion": "2.0.2", "filterVersion": "1.1.0", @@ -130,7 +145,7 @@ "ptype": "gxp_addlayer", "showCapabilitiesGrid": true, "useEvents": false, - "showReport": false, + "showReport": "never", "directAddLayer": false, "id": "addlayer" }, { @@ -158,6 +173,12 @@ }, { "ptype": "gxp_languageselector", "actionTarget": {"target": "panelbbar", "index": 3} + }, { + "ptype": "gxp_wmsgetfeatureinfo_menu", + "regex": "[\\s\\S]*[\\w]+[\\s\\S]*", + "useTabPanel": true, + "toggleGroup": "toolGroup", + "actionTarget": {"target": "paneltbar", "index": 20} } ] } diff --git a/mapcomposer/app/static/config/markerEditor.js b/mapcomposer/app/static/config/markerEditor.js index e9c241185..c4c34b22e 100644 --- a/mapcomposer/app/static/config/markerEditor.js +++ b/mapcomposer/app/static/config/markerEditor.js @@ -23,7 +23,11 @@ "layout":"fit" } ], - + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map":{ "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/minimal.js b/mapcomposer/app/static/config/minimal.js index e54a5f939..d87c9aa3f 100644 --- a/mapcomposer/app/static/config/minimal.js +++ b/mapcomposer/app/static/config/minimal.js @@ -7,6 +7,11 @@ "ptype": "gxp_googlesource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map":{ "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/preview.js b/mapcomposer/app/static/config/preview.js index 7080fe1f4..9ed11bb8f 100644 --- a/mapcomposer/app/static/config/preview.js +++ b/mapcomposer/app/static/config/preview.js @@ -6,8 +6,13 @@ "gsSources": { "bing": { "ptype": "gxp_bingsource" - } + } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map":{ "projection": "EPSG:900913", "units": "m", @@ -36,7 +41,7 @@ "ptype": "gxp_addlayer", "showCapabilitiesGrid": true, "useEvents": false, - "showReport": false, + "showReport": "always", "directAddLayer": false, "id": "addlayer" }, { diff --git a/mapcomposer/app/static/config/queryConfig.js b/mapcomposer/app/static/config/queryConfig.js index 940feeab7..5aacb8007 100644 --- a/mapcomposer/app/static/config/queryConfig.js +++ b/mapcomposer/app/static/config/queryConfig.js @@ -31,6 +31,11 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map": { "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/queryConfigWithValidator.js b/mapcomposer/app/static/config/queryConfigWithValidator.js index c27c9b95e..89c1e6a4d 100644 --- a/mapcomposer/app/static/config/queryConfigWithValidator.js +++ b/mapcomposer/app/static/config/queryConfigWithValidator.js @@ -31,6 +31,11 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map": { "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/spatialSelectorQueryConfig.js b/mapcomposer/app/static/config/spatialSelectorQueryConfig.js index ee69b3c69..4beb1e52c 100644 --- a/mapcomposer/app/static/config/spatialSelectorQueryConfig.js +++ b/mapcomposer/app/static/config/spatialSelectorQueryConfig.js @@ -24,6 +24,11 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map": { "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/spatialSelectorWithAutoCompleteConfig.js b/mapcomposer/app/static/config/spatialSelectorWithAutoCompleteConfig.js index 7ebff7eb8..4fa17a32c 100644 --- a/mapcomposer/app/static/config/spatialSelectorWithAutoCompleteConfig.js +++ b/mapcomposer/app/static/config/spatialSelectorWithAutoCompleteConfig.js @@ -24,6 +24,11 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map": { "projection": "EPSG:900913", "units": "m", @@ -173,6 +178,7 @@ "showSelectionSummary": true, "actions": null, "id": "bboxquery", + "filterLayer": true, "autoComplete": { "sources": ["default"], "url": "http://localhost:8080/geoserver/wps", diff --git a/mapcomposer/app/static/config/spatialSelectorWithoutWPSQueryConfig.js b/mapcomposer/app/static/config/spatialSelectorWithoutWPSQueryConfig.js index 6ecb17e03..caa53d5b0 100644 --- a/mapcomposer/app/static/config/spatialSelectorWithoutWPSQueryConfig.js +++ b/mapcomposer/app/static/config/spatialSelectorWithoutWPSQueryConfig.js @@ -24,6 +24,11 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map": { "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/trainingConfig.js b/mapcomposer/app/static/config/trainingConfig.js index e9218beeb..dfdab06c2 100644 --- a/mapcomposer/app/static/config/trainingConfig.js +++ b/mapcomposer/app/static/config/trainingConfig.js @@ -23,6 +23,11 @@ "ptype": "gxp_olsource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map": { "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/viewerConfig-alternative.js b/mapcomposer/app/static/config/viewerConfig-alternative.js index 6a99b39af..d81897072 100644 --- a/mapcomposer/app/static/config/viewerConfig-alternative.js +++ b/mapcomposer/app/static/config/viewerConfig-alternative.js @@ -7,6 +7,11 @@ "ptype": "gxp_googlesource" } }, + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "map":{ "projection": "EPSG:900913", "units": "m", diff --git a/mapcomposer/app/static/config/viewerConfig.js b/mapcomposer/app/static/config/viewerConfig.js index ba2189c3c..aec6826e5 100644 --- a/mapcomposer/app/static/config/viewerConfig.js +++ b/mapcomposer/app/static/config/viewerConfig.js @@ -3,9 +3,9 @@ "header":false }, "gsSources": { - "google": { - "ptype": "gxp_googlesource" - } + "bing": { + "ptype": "gxp_bingsource" + } }, "map":{ "projection": "EPSG:900913", @@ -15,15 +15,21 @@ 20037508.34, 20037508.34 ], "layers": [{ - "source": "google", - "title": "Google Hybrid", - "name": "HYBRID", - "group": "background" - }], + "source": "bing", + "title": "Bing Aerial", + "name": "AerialWithLabels", + "group": "background" + }], "center": [1250000.000000, 5370000.000000], "zoom": 5 }, - + "cookieConsent":true, + + "loadingPanel": { + "width": 100, + "height": 100, + "center": true + }, "customTools":[ { "ptype": "gxp_mouseposition", @@ -42,7 +48,7 @@ "ptype": "gxp_addlayer", "showCapabilitiesGrid": true, "useEvents": false, - "showReport": false, + "showReport": "never", "directAddLayer": false, "id": "addlayer" }, { diff --git a/mapcomposer/app/static/cookies-policy-en.html b/mapcomposer/app/static/cookies-policy-en.html new file mode 100644 index 000000000..7e9295f74 --- /dev/null +++ b/mapcomposer/app/static/cookies-policy-en.html @@ -0,0 +1,133 @@ + + + + Privacy e Cookies Policy + + + + + +
+
+ +
+

Privacy and Cookies Policy

+
+

+ In accordance with Legislative Decree No. 196/2003, which replaced the Law No. 675/1996 regarding + Personal Data Protection, we inform you that continuing navigation on this site you consent + the processing of your personal data (sensitive information will not be treated in any way). + Your data will only be used for operations at the site and will not be + disclosed to third party companies without your consent. In any case, it is possible + exercise the rights granted to you under the Legislative Decree 196/2003 (access to data, + updating, cancellation). + If you click the "Agree" button, you acknowledge that you have read this text and consent to treatment + of personal data. At any time you can ask the staff of the site to delete you from + service. +

+ +

What are Cookies

+ +

+ A cookie is a small piece of text sent to your browser by a website you visit. + It helps the website to remember information about your visit, like your preferred language and other settings. + That can make your next visit easier and the site more useful to you. + The browser saves the information and forward it to the site server when you visit again + that website. +

+ +

+ Our cookies help us to: +

+ +
    +
  • Make our website work as you'd expect
  • +
  • Remember your settings during and between visits
  • +
  • Improve the performance/security of our website
  • +
  • Allow you to share pages with social networks like Facebook
  • +
+ +

+ We do not use cookies to: +

+ +
    +
  • Collect any personally identifiable information (without your express permission)
  • +
  • Collect any sensitive information (without your express permission)
  • +
  • Pass data to advertising networks
  • +
+

+ You can learn more about all the cookies we use below. +

+ +

+ If the settings on your software that you are using to view this website (your browser) are adjusted to accept cookies we take this, and your continued use of our website, to mean that you are fine with this. Should you wish to remove or not use cookies from our site you can learn how to do this below, however doing so will likely mean that our site will not work as you would expect. +

+ +

Cookies set by our own Website

+ +

+ We use cookies to make our site work, including: +

+ +
    +
  • Determining if you are logged in or not
  • +
  • Authentication to services WMS, WFS, WPS through HTTP requests
  • +
+ +

Social Website Cookies

+

+ To facilitate easy sharing or liking of content on social network platforms such Facebook and Twitter, we have included sharing buttons on our site. Cookies are enabled for: +

+ +
    +
  • Twitter
  • +
  • Facebook
  • +
+

+ The privacy implications on this will vary from social network to social network and will be dependent on the privacy settings you have chosen on these networks. +

+

Controlling Cookies

+ +

+ All browsers allow you to manage which cookies you accept, reject and delete via controls found under the ‘Preferences' or ‘Tools' menu. Further information on deleting or controlling cookies is available + here). + Please note however that if you disable cookies you may not be able to access certain services or facilities of this as of many of the Web sites around the world (cookies are a standard component of most modern websites). +

+ +


+ +
+
+
+ + \ No newline at end of file diff --git a/mapcomposer/app/static/cookies-policy-it.html b/mapcomposer/app/static/cookies-policy-it.html new file mode 100644 index 000000000..691eb6928 --- /dev/null +++ b/mapcomposer/app/static/cookies-policy-it.html @@ -0,0 +1,138 @@ + + + + Privacy e Cookies Policy + + + + + +
+
+ +
+

Privacy e Cookies Policy

+
+

+ Ai sensi del Decreto Legislativo n ° 196/2003, che ha sostituito la legge n ° 675/1996 in materia di + protezione dei dati personali, Vi informiamo che proseguendo la navigazione su questo sito voi acconsentite + al trattamento dei vostri dati personali (i dati sensibili non saranno trattati in alcun modo). + I suoi dati saranno utilizzati esclusivamente per le operazioni all'interno del sito e non potranno essere + comunicati a società di terze parti senza il vostro consenso. In ogni caso, è possibile + esercitare i diritti concessi all'utente ai sensi del Decreto Legislativo n ° 196/2003 (accesso ai dati, + aggiornamento, integrazione, cancellazione). + Facendo clic sul pulsante "Accetto", si dichiara di aver letto questo testo e di acconsentire al trattamento + dei dati personali. In qualsiasi momento è possibile chiedere alla redazione del sito di cancellarti dal + servizio. +

+ +

Cosa sono i Cookies

+ +

+ Un cookie è un file di testo che un sito web salva sul browser del computer dell’utente. Solitamente i cookie + consentono di memorizzare le preferenze espresse dall’utente per non dover essere reinserite successivamente. + Il browser salva l’informazione e la ritrasmette al server del sito nel momento in cui l’utente visita nuovamente + quel sito web. +

+ +

+ I nostri cookie ci aiutano a: +

+ +
    +
  • Permettere al sito web di lavorare come atteso
  • +
  • Ricordare le impostazioni durante e tra le visite al sito
  • +
  • Migliorare la velocità / sicurezza del sito
  • +
  • Permette di condividere le pagine con i social network come Facebook
  • +
+ +

+ Non viene fatto uso di cookies per: +

+ +
    +
  • Raccogliere tutte le informazioni di identificazione personale (senza il vostro consenso)
  • +
  • Raccogliere tutte le informazioni sensibili (senza il tuo esplicito consenso)
  • +
  • Passare i dati alle reti pubblicitarie
  • +
+

+ Potete saperne di più su tutti i cookies che utilizziamo qui sotto. +

+ +

+ Se le impostazioni del software che si sta utilizzando per la navigazione del sito (il browser) sono regolati per accettare + i cookies prendiamo questo, e l'uso continuato del nostro sito web, a significare che si acconsente all'utilizzo di cookies. + Se si desidera rimuovere o non usare i cookies dal nostro sito lo si può fare seguendo le indicazioni riportate di seguito, + così facendo tuttavia il nostro sito potrebbe non funzionare come ci si aspetterebbe. +

+ +

I nostri Cookies

+ +

+ Utilizziamo i cookie per garantire determinate funzionalità del sito web tra cui: +

+ +
    +
  • Operazioni di Login e successive autenticazioni
  • +
  • Autenticazione a servizi WMS, WFS, WPS attraverso richieste HTTP
  • +
+ +

Social Website Cookies

+

+ Tu puoi facilmente esprimere il tuo gradimento o condividere alcuni contenuti su Facebook e Twitter (abbiamo incluso + i pulsanti di condivisione sul nostro sito). I cookies sono stati abilitati per: +

+ +
    +
  • Twitter
  • +
  • Facebook
  • +
+

+ Le implicazioni di privacy su questo variano da social network a social network e dipenderanno dalle impostazioni di privacy + che avete scelto su queste reti. +

+

Disabilitare i Cookies

+ +

+ Di solito è possibile disattivare i cookies modificando le impostazioni del browser per impedire l'uso degli stessi (vedi + qui). + In questo modo però probabilmente si limiteranno le funzionalità del nostro come di molti dei siti web di tutto il mondo (i cookie + sono una componente standard della maggior parte dei siti web moderni). +

+ +


+ +
+
+
+ + \ No newline at end of file diff --git a/mapcomposer/app/static/externals/GeoExt/lib/GeoExt/data/WMSCapabilitiesReader.js b/mapcomposer/app/static/externals/GeoExt/lib/GeoExt/data/WMSCapabilitiesReader.js index 1c97a7d86..d2aeee277 100644 --- a/mapcomposer/app/static/externals/GeoExt/lib/GeoExt/data/WMSCapabilitiesReader.js +++ b/mapcomposer/app/static/externals/GeoExt/lib/GeoExt/data/WMSCapabilitiesReader.js @@ -61,6 +61,7 @@ GeoExt.data.WMSCapabilitiesReader = function(meta, recordType) { {name: "maxScale", type: "float"}, {name: "prefix", type: "string"}, {name: "formats"}, // array + {name: "infoFormats"},//array {name: "styles"}, // array {name: "srs"}, // object {name: "dimensions"}, // object diff --git a/mapcomposer/app/static/externals/canvg-1.2/html2canvas.js b/mapcomposer/app/static/externals/canvg-1.2/html2canvas.js new file mode 100644 index 000000000..b5e3fc7a5 --- /dev/null +++ b/mapcomposer/app/static/externals/canvg-1.2/html2canvas.js @@ -0,0 +1,2877 @@ +/* + html2canvas 0.4.1 + Copyright (c) 2013 Niklas von Hertzen + + Released under MIT License + Modified for MapStore Usage skipping scripttag proxy (jsonp) and using the MapStore proxy as a bridge. +*/ + +(function(window, document, undefined){ + +"use strict"; + +var _html2canvas = {}, +previousElement, +computedCSS, +html2canvas; + +_html2canvas.Util = {}; + +_html2canvas.Util.log = function(a) { + if (_html2canvas.logging && window.console && window.console.log) { + window.console.log(a); + } +}; + +_html2canvas.Util.trimText = (function(isNative){ + return function(input) { + return isNative ? isNative.apply(input) : ((input || '') + '').replace( /^\s+|\s+$/g , '' ); + }; +})(String.prototype.trim); + +_html2canvas.Util.asFloat = function(v) { + return parseFloat(v); +}; + +(function() { + // TODO: support all possible length values + var TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g; + var TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g; + _html2canvas.Util.parseTextShadows = function (value) { + if (!value || value === 'none') { + return []; + } + + // find multiple shadow declarations + var shadows = value.match(TEXT_SHADOW_PROPERTY), + results = []; + for (var i = 0; shadows && (i < shadows.length); i++) { + var s = shadows[i].match(TEXT_SHADOW_VALUES); + results.push({ + color: s[0], + offsetX: s[1] ? s[1].replace('px', '') : 0, + offsetY: s[2] ? s[2].replace('px', '') : 0, + blur: s[3] ? s[3].replace('px', '') : 0 + }); + } + return results; + }; +})(); + + +_html2canvas.Util.parseBackgroundImage = function (value) { + var whitespace = ' \r\n\t', + method, definition, prefix, prefix_i, block, results = [], + c, mode = 0, numParen = 0, quote, args; + + var appendResult = function(){ + if(method) { + if(definition.substr( 0, 1 ) === '"') { + definition = definition.substr( 1, definition.length - 2 ); + } + if(definition) { + args.push(definition); + } + if(method.substr( 0, 1 ) === '-' && + (prefix_i = method.indexOf( '-', 1 ) + 1) > 0) { + prefix = method.substr( 0, prefix_i); + method = method.substr( prefix_i ); + } + results.push({ + prefix: prefix, + method: method.toLowerCase(), + value: block, + args: args + }); + } + args = []; //for some odd reason, setting .length = 0 didn't work in safari + method = + prefix = + definition = + block = ''; + }; + + appendResult(); + for(var i = 0, ii = value.length; i -1){ + continue; + } + switch(c) { + case '"': + if(!quote) { + quote = c; + } + else if(quote === c) { + quote = null; + } + break; + + case '(': + if(quote) { break; } + else if(mode === 0) { + mode = 1; + block += c; + continue; + } else { + numParen++; + } + break; + + case ')': + if(quote) { break; } + else if(mode === 1) { + if(numParen === 0) { + mode = 0; + block += c; + appendResult(); + continue; + } else { + numParen--; + } + } + break; + + case ',': + if(quote) { break; } + else if(mode === 0) { + appendResult(); + continue; + } + else if (mode === 1) { + if(numParen === 0 && !method.match(/^url$/i)) { + args.push(definition); + definition = ''; + block += c; + continue; + } + } + break; + } + + block += c; + if(mode === 0) { method += c; } + else { definition += c; } + } + appendResult(); + + return results; +}; + +_html2canvas.Util.Bounds = function (element) { + var clientRect, bounds = {}; + + if (element.getBoundingClientRect){ + clientRect = element.getBoundingClientRect(); + + // TODO add scroll position to bounds, so no scrolling of window necessary + bounds.top = clientRect.top; + bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); + bounds.left = clientRect.left; + + bounds.width = element.offsetWidth; + bounds.height = element.offsetHeight; + } + + return bounds; +}; + +// TODO ideally, we'd want everything to go through this function instead of Util.Bounds, +// but would require further work to calculate the correct positions for elements with offsetParents +_html2canvas.Util.OffsetBounds = function (element) { + var parent = element.offsetParent ? _html2canvas.Util.OffsetBounds(element.offsetParent) : {top: 0, left: 0}; + + return { + top: element.offsetTop + parent.top, + bottom: element.offsetTop + element.offsetHeight + parent.top, + left: element.offsetLeft + parent.left, + width: element.offsetWidth, + height: element.offsetHeight + }; +}; + +function toPX(element, attribute, value ) { + var rsLeft = element.runtimeStyle && element.runtimeStyle[attribute], + left, + style = element.style; + + // Check if we are not dealing with pixels, (Opera has issues with this) + // Ported from jQuery css.js + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + + if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( value ) && /^-?\d/.test(value) ) { + // Remember the original values + left = style.left; + + // Put in the new values to get a computed value out + if (rsLeft) { + element.runtimeStyle.left = element.currentStyle.left; + } + style.left = attribute === "fontSize" ? "1em" : (value || 0); + value = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if (rsLeft) { + element.runtimeStyle.left = rsLeft; + } + } + + if (!/^(thin|medium|thick)$/i.test(value)) { + return Math.round(parseFloat(value)) + "px"; + } + + return value; +} + +function asInt(val) { + return parseInt(val, 10); +} + +function parseBackgroundSizePosition(value, element, attribute, index) { + value = (value || '').split(','); + value = value[index || 0] || value[0] || 'auto'; + value = _html2canvas.Util.trimText(value).split(' '); + + if(attribute === 'backgroundSize' && (!value[0] || value[0].match(/cover|contain|auto/))) { + //these values will be handled in the parent function + } else { + value[0] = (value[0].indexOf( "%" ) === -1) ? toPX(element, attribute + "X", value[0]) : value[0]; + if(value[1] === undefined) { + if(attribute === 'backgroundSize') { + value[1] = 'auto'; + return value; + } else { + // IE 9 doesn't return double digit always + value[1] = value[0]; + } + } + value[1] = (value[1].indexOf("%") === -1) ? toPX(element, attribute + "Y", value[1]) : value[1]; + } + return value; +} + +_html2canvas.Util.getCSS = function (element, attribute, index) { + if (previousElement !== element) { + computedCSS = document.defaultView.getComputedStyle(element, null); + } + + var value = computedCSS[attribute]; + + if (/^background(Size|Position)$/.test(attribute)) { + return parseBackgroundSizePosition(value, element, attribute, index); + } else if (/border(Top|Bottom)(Left|Right)Radius/.test(attribute)) { + var arr = value.split(" "); + if (arr.length <= 1) { + arr[1] = arr[0]; + } + return arr.map(asInt); + } + + return value; +}; + +_html2canvas.Util.resizeBounds = function( current_width, current_height, target_width, target_height, stretch_mode ){ + var target_ratio = target_width / target_height, + current_ratio = current_width / current_height, + output_width, output_height; + + if(!stretch_mode || stretch_mode === 'auto') { + output_width = target_width; + output_height = target_height; + } else if(target_ratio < current_ratio ^ stretch_mode === 'contain') { + output_height = target_height; + output_width = target_height * current_ratio; + } else { + output_width = target_width; + output_height = target_width / current_ratio; + } + + return { + width: output_width, + height: output_height + }; +}; + +function backgroundBoundsFactory( prop, el, bounds, image, imageIndex, backgroundSize ) { + var bgposition = _html2canvas.Util.getCSS( el, prop, imageIndex ) , + topPos, + left, + percentage, + val; + + if (bgposition.length === 1){ + val = bgposition[0]; + + bgposition = []; + + bgposition[0] = val; + bgposition[1] = val; + } + + if (bgposition[0].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[0])/100); + left = bounds.width * percentage; + if(prop !== 'backgroundSize') { + left -= (backgroundSize || image).width*percentage; + } + } else { + if(prop === 'backgroundSize') { + if(bgposition[0] === 'auto') { + left = image.width; + } else { + if (/contain|cover/.test(bgposition[0])) { + var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, bgposition[0]); + left = resized.width; + topPos = resized.height; + } else { + left = parseInt(bgposition[0], 10); + } + } + } else { + left = parseInt( bgposition[0], 10); + } + } + + + if(bgposition[1] === 'auto') { + topPos = left / image.width * image.height; + } else if (bgposition[1].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[1])/100); + topPos = bounds.height * percentage; + if(prop !== 'backgroundSize') { + topPos -= (backgroundSize || image).height * percentage; + } + + } else { + topPos = parseInt(bgposition[1],10); + } + + return [left, topPos]; +} + +_html2canvas.Util.BackgroundPosition = function( el, bounds, image, imageIndex, backgroundSize ) { + var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize ); + return { left: result[0], top: result[1] }; +}; + +_html2canvas.Util.BackgroundSize = function( el, bounds, image, imageIndex ) { + var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex ); + return { width: result[0], height: result[1] }; +}; + +_html2canvas.Util.Extend = function (options, defaults) { + for (var key in options) { + if (options.hasOwnProperty(key)) { + defaults[key] = options[key]; + } + } + return defaults; +}; + + +/* + * Derived from jQuery.contents() + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ +_html2canvas.Util.Children = function( elem ) { + var children; + try { + children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? elem.contentDocument || elem.contentWindow.document : (function(array) { + var ret = []; + if (array !== null) { + (function(first, second ) { + var i = first.length, + j = 0; + + if (typeof second.length === "number") { + for (var l = second.length; j < l; j++) { + first[i++] = second[j]; + } + } else { + while (second[j] !== undefined) { + first[i++] = second[j++]; + } + } + + first.length = i; + + return first; + })(ret, array); + } + return ret; + })(elem.childNodes); + + } catch (ex) { + _html2canvas.Util.log("html2canvas.Util.Children failed with exception: " + ex.message); + children = []; + } + return children; +}; + +_html2canvas.Util.isTransparent = function(backgroundColor) { + return (backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)"); +}; +_html2canvas.Util.Font = (function () { + + var fontData = {}; + + return function(font, fontSize, doc) { + if (fontData[font + "-" + fontSize] !== undefined) { + return fontData[font + "-" + fontSize]; + } + + var container = doc.createElement('div'), + img = doc.createElement('img'), + span = doc.createElement('span'), + sampleText = 'Hidden Text', + baseline, + middle, + metricsObj; + + container.style.visibility = "hidden"; + container.style.fontFamily = font; + container.style.fontSize = fontSize; + container.style.margin = 0; + container.style.padding = 0; + + doc.body.appendChild(container); + + // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) + img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; + img.width = 1; + img.height = 1; + + img.style.margin = 0; + img.style.padding = 0; + img.style.verticalAlign = "baseline"; + + span.style.fontFamily = font; + span.style.fontSize = fontSize; + span.style.margin = 0; + span.style.padding = 0; + + span.appendChild(doc.createTextNode(sampleText)); + container.appendChild(span); + container.appendChild(img); + baseline = (img.offsetTop - span.offsetTop) + 1; + + container.removeChild(span); + container.appendChild(doc.createTextNode(sampleText)); + + container.style.lineHeight = "normal"; + img.style.verticalAlign = "super"; + + middle = (img.offsetTop-container.offsetTop) + 1; + metricsObj = { + baseline: baseline, + lineWidth: 1, + middle: middle + }; + + fontData[font + "-" + fontSize] = metricsObj; + + doc.body.removeChild(container); + + return metricsObj; + }; +})(); + +(function(){ + var Util = _html2canvas.Util, + Generate = {}; + + _html2canvas.Generate = Generate; + + var reGradients = [ + /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)\-]+)\)$/, + /^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/, + /^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z\-]*)([\w\d\.\s,%\(\)]+)\)$/, + /^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/ + ]; + + /* + * TODO: Add IE10 vendor prefix (-ms) support + * TODO: Add W3C gradient (linear-gradient) support + * TODO: Add old Webkit -webkit-gradient(radial, ...) support + * TODO: Maybe some RegExp optimizations are possible ;o) + */ + Generate.parseGradient = function(css, bounds) { + var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl; + + for(i = 0; i < len; i+=1){ + m1 = css.match(reGradients[i]); + if(m1) { + break; + } + } + + if(m1) { + switch(m1[1]) { + case '-webkit-linear-gradient': + case '-o-linear-gradient': + + gradient = { + type: 'linear', + x0: null, + y0: null, + x1: null, + y1: null, + colorStops: [] + }; + + // get coordinates + m2 = m1[2].match(/\w+/g); + if(m2){ + m2Len = m2.length; + for(i = 0; i < m2Len; i+=1){ + switch(m2[i]) { + case 'top': + gradient.y0 = 0; + gradient.y1 = bounds.height; + break; + + case 'right': + gradient.x0 = bounds.width; + gradient.x1 = 0; + break; + + case 'bottom': + gradient.y0 = bounds.height; + gradient.y1 = 0; + break; + + case 'left': + gradient.x0 = 0; + gradient.x1 = bounds.width; + break; + } + } + } + if(gradient.x0 === null && gradient.x1 === null){ // center + gradient.x0 = gradient.x1 = bounds.width / 2; + } + if(gradient.y0 === null && gradient.y1 === null){ // center + gradient.y0 = gradient.y1 = bounds.height / 2; + } + + // get colors and stops + m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3] === '%'){ + stop /= 100; + } else { // px - stupid opera + stop /= bounds.width; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + + case '-webkit-gradient': + + gradient = { + type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions + x0: 0, + y0: 0, + x1: 0, + y1: 0, + colorStops: [] + }; + + // get coordinates + m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/); + if(m2){ + gradient.x0 = (m2[1] * bounds.width) / 100; + gradient.y0 = (m2[2] * bounds.height) / 100; + gradient.x1 = (m2[3] * bounds.width) / 100; + gradient.y1 = (m2[4] * bounds.height) / 100; + } + + // get colors and stops + m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g); + if(m2){ + m2Len = m2.length; + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/); + stop = parseFloat(m3[2]); + if(m3[1] === 'from') { + stop = 0.0; + } + if(m3[1] === 'to') { + stop = 1.0; + } + gradient.colorStops.push({ + color: m3[3], + stop: stop + }); + } + } + break; + + case '-moz-linear-gradient': + + gradient = { + type: 'linear', + x0: 0, + y0: 0, + x1: 0, + y1: 0, + colorStops: [] + }; + + // get coordinates + m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); + + // m2[1] == 0% -> left + // m2[1] == 50% -> center + // m2[1] == 100% -> right + + // m2[2] == 0% -> top + // m2[2] == 50% -> center + // m2[2] == 100% -> bottom + + if(m2){ + gradient.x0 = (m2[1] * bounds.width) / 100; + gradient.y0 = (m2[2] * bounds.height) / 100; + gradient.x1 = bounds.width - gradient.x0; + gradient.y1 = bounds.height - gradient.y0; + } + + // get colors and stops + m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3]){ // percentage + stop /= 100; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + + case '-webkit-radial-gradient': + case '-moz-radial-gradient': + case '-o-radial-gradient': + + gradient = { + type: 'circle', + x0: 0, + y0: 0, + x1: bounds.width, + y1: bounds.height, + cx: 0, + cy: 0, + rx: 0, + ry: 0, + colorStops: [] + }; + + // center + m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); + if(m2){ + gradient.cx = (m2[1] * bounds.width) / 100; + gradient.cy = (m2[2] * bounds.height) / 100; + } + + // size + m2 = m1[3].match(/\w+/); + m3 = m1[4].match(/[a-z\-]*/); + if(m2 && m3){ + switch(m3[0]){ + case 'farthest-corner': + case 'cover': // is equivalent to farthest-corner + case '': // mozilla removes "cover" from definition :( + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.max(tl, tr, br, bl); + break; + case 'closest-corner': + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.min(tl, tr, br, bl); + break; + case 'farthest-side': + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.max( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // ellipse + + gradient.type = m2[0]; + + gradient.rx = Math.max( + gradient.cx, + gradient.x1 - gradient.cx + ); + gradient.ry = Math.max( + gradient.cy, + gradient.y1 - gradient.cy + ); + } + break; + case 'closest-side': + case 'contain': // is equivalent to closest-side + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.min( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // ellipse + + gradient.type = m2[0]; + + gradient.rx = Math.min( + gradient.cx, + gradient.x1 - gradient.cx + ); + gradient.ry = Math.min( + gradient.cy, + gradient.y1 - gradient.cy + ); + } + break; + + // TODO: add support for "30px 40px" sizes (webkit only) + } + } + + // color stops + m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3] === '%'){ + stop /= 100; + } else { // px - stupid opera + stop /= bounds.width; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + } + } + + return gradient; + }; + + function addScrollStops(grad) { + return function(colorStop) { + try { + grad.addColorStop(colorStop.stop, colorStop.color); + } + catch(e) { + Util.log(['failed to add color stop: ', e, '; tried to add: ', colorStop]); + } + }; + } + + Generate.Gradient = function(src, bounds) { + if(bounds.width === 0 || bounds.height === 0) { + return; + } + + var canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'), + gradient, grad; + + canvas.width = bounds.width; + canvas.height = bounds.height; + + // TODO: add support for multi defined background gradients + gradient = _html2canvas.Generate.parseGradient(src, bounds); + + if(gradient) { + switch(gradient.type) { + case 'linear': + grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); + gradient.colorStops.forEach(addScrollStops(grad)); + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + break; + + case 'circle': + grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); + gradient.colorStops.forEach(addScrollStops(grad)); + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + break; + + case 'ellipse': + var canvasRadial = document.createElement('canvas'), + ctxRadial = canvasRadial.getContext('2d'), + ri = Math.max(gradient.rx, gradient.ry), + di = ri * 2; + + canvasRadial.width = canvasRadial.height = di; + + grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); + gradient.colorStops.forEach(addScrollStops(grad)); + + ctxRadial.fillStyle = grad; + ctxRadial.fillRect(0, 0, di, di); + + ctx.fillStyle = gradient.colorStops[gradient.colorStops.length - 1].color; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); + break; + } + } + + return canvas; + }; + + Generate.ListAlpha = function(number) { + var tmp = "", + modulus; + + do { + modulus = number % 26; + tmp = String.fromCharCode((modulus) + 64) + tmp; + number = number / 26; + }while((number*26) > 26); + + return tmp; + }; + + Generate.ListRoman = function(number) { + var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"], + decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], + roman = "", + v, + len = romanArray.length; + + if (number <= 0 || number >= 4000) { + return number; + } + + for (v=0; v < len; v+=1) { + while (number >= decimal[v]) { + number -= decimal[v]; + roman += romanArray[v]; + } + } + + return roman; + }; +})(); +function h2cRenderContext(width, height) { + var storage = []; + return { + storage: storage, + width: width, + height: height, + clip: function() { + storage.push({ + type: "function", + name: "clip", + 'arguments': arguments + }); + }, + translate: function() { + storage.push({ + type: "function", + name: "translate", + 'arguments': arguments + }); + }, + fill: function() { + storage.push({ + type: "function", + name: "fill", + 'arguments': arguments + }); + }, + save: function() { + storage.push({ + type: "function", + name: "save", + 'arguments': arguments + }); + }, + restore: function() { + storage.push({ + type: "function", + name: "restore", + 'arguments': arguments + }); + }, + fillRect: function () { + storage.push({ + type: "function", + name: "fillRect", + 'arguments': arguments + }); + }, + createPattern: function() { + storage.push({ + type: "function", + name: "createPattern", + 'arguments': arguments + }); + }, + drawShape: function() { + + var shape = []; + + storage.push({ + type: "function", + name: "drawShape", + 'arguments': shape + }); + + return { + moveTo: function() { + shape.push({ + name: "moveTo", + 'arguments': arguments + }); + }, + lineTo: function() { + shape.push({ + name: "lineTo", + 'arguments': arguments + }); + }, + arcTo: function() { + shape.push({ + name: "arcTo", + 'arguments': arguments + }); + }, + bezierCurveTo: function() { + shape.push({ + name: "bezierCurveTo", + 'arguments': arguments + }); + }, + quadraticCurveTo: function() { + shape.push({ + name: "quadraticCurveTo", + 'arguments': arguments + }); + } + }; + + }, + drawImage: function () { + storage.push({ + type: "function", + name: "drawImage", + 'arguments': arguments + }); + }, + fillText: function () { + storage.push({ + type: "function", + name: "fillText", + 'arguments': arguments + }); + }, + setVariable: function (variable, value) { + storage.push({ + type: "variable", + name: variable, + 'arguments': value + }); + return value; + } + }; +} +_html2canvas.Parse = function (images, options) { + window.scroll(0,0); + + var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default + numDraws = 0, + doc = element.ownerDocument, + Util = _html2canvas.Util, + support = Util.Support(options, doc), + ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), + body = doc.body, + getCSS = Util.getCSS, + pseudoHide = "___html2canvas___pseudoelement", + hidePseudoElements = doc.createElement('style'); + + hidePseudoElements.innerHTML = '.' + pseudoHide + '-before:before { content: "" !important; display: none !important; }' + + '.' + pseudoHide + '-after:after { content: "" !important; display: none !important; }'; + + body.appendChild(hidePseudoElements); + + images = images || {}; + + function documentWidth () { + return Math.max( + Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), + Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), + Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) + ); + } + + function documentHeight () { + return Math.max( + Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), + Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), + Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) + ); + } + + function getCSSInt(element, attribute) { + var val = parseInt(getCSS(element, attribute), 10); + return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html + } + + function renderRect (ctx, x, y, w, h, bgcolor) { + if (bgcolor !== "transparent"){ + ctx.setVariable("fillStyle", bgcolor); + ctx.fillRect(x, y, w, h); + numDraws+=1; + } + } + + function capitalize(m, p1, p2) { + if (m.length > 0) { + return p1 + p2.toUpperCase(); + } + } + + function textTransform (text, transform) { + switch(transform){ + case "lowercase": + return text.toLowerCase(); + case "capitalize": + return text.replace( /(^|\s|:|-|\(|\))([a-z])/g, capitalize); + case "uppercase": + return text.toUpperCase(); + default: + return text; + } + } + + function noLetterSpacing(letter_spacing) { + return (/^(normal|none|0px)$/.test(letter_spacing)); + } + + function drawText(currentText, x, y, ctx){ + if (currentText !== null && Util.trimText(currentText).length > 0) { + ctx.fillText(currentText, x, y); + numDraws+=1; + } + } + + function setTextVariables(ctx, el, text_decoration, color) { + var align = false, + bold = getCSS(el, "fontWeight"), + family = getCSS(el, "fontFamily"), + size = getCSS(el, "fontSize"), + shadows = Util.parseTextShadows(getCSS(el, "textShadow")); + + switch(parseInt(bold, 10)){ + case 401: + bold = "bold"; + break; + case 400: + bold = "normal"; + break; + } + + ctx.setVariable("fillStyle", color); + ctx.setVariable("font", [getCSS(el, "fontStyle"), getCSS(el, "fontVariant"), bold, size, family].join(" ")); + ctx.setVariable("textAlign", (align) ? "right" : "left"); + + if (shadows.length) { + // TODO: support multiple text shadows + // apply the first text shadow + ctx.setVariable("shadowColor", shadows[0].color); + ctx.setVariable("shadowOffsetX", shadows[0].offsetX); + ctx.setVariable("shadowOffsetY", shadows[0].offsetY); + ctx.setVariable("shadowBlur", shadows[0].blur); + } + + if (text_decoration !== "none"){ + return Util.Font(family, size, doc); + } + } + + function renderTextDecoration(ctx, text_decoration, bounds, metrics, color) { + switch(text_decoration) { + case "underline": + // Draws a line at the baseline of the font + // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size + renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); + break; + case "overline": + renderRect(ctx, bounds.left, Math.round(bounds.top), bounds.width, 1, color); + break; + case "line-through": + // TODO try and find exact position for line-through + renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); + break; + } + } + + function getTextBounds(state, text, textDecoration, isLast, transform) { + var bounds; + if (support.rangeBounds && !transform) { + if (textDecoration !== "none" || Util.trimText(text).length !== 0) { + bounds = textRangeBounds(text, state.node, state.textOffset); + } + state.textOffset += text.length; + } else if (state.node && typeof state.node.nodeValue === "string" ){ + var newTextNode = (isLast) ? state.node.splitText(text.length) : null; + bounds = textWrapperBounds(state.node, transform); + state.node = newTextNode; + } + return bounds; + } + + function textRangeBounds(text, textNode, textOffset) { + var range = doc.createRange(); + range.setStart(textNode, textOffset); + range.setEnd(textNode, textOffset + text.length); + return range.getBoundingClientRect(); + } + + function textWrapperBounds(oldTextNode, transform) { + var parent = oldTextNode.parentNode, + wrapElement = doc.createElement('wrapper'), + backupText = oldTextNode.cloneNode(true); + + wrapElement.appendChild(oldTextNode.cloneNode(true)); + parent.replaceChild(wrapElement, oldTextNode); + + var bounds = transform ? Util.OffsetBounds(wrapElement) : Util.Bounds(wrapElement); + parent.replaceChild(backupText, wrapElement); + return bounds; + } + + function renderText(el, textNode, stack) { + var ctx = stack.ctx, + color = getCSS(el, "color"), + textDecoration = getCSS(el, "textDecoration"), + textAlign = getCSS(el, "textAlign"), + metrics, + textList, + state = { + node: textNode, + textOffset: 0 + }; + + if (Util.trimText(textNode.nodeValue).length > 0) { + textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); + textAlign = textAlign.replace(["-webkit-auto"],["auto"]); + + textList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(textAlign) && noLetterSpacing(getCSS(el, "letterSpacing"))) ? + textNode.nodeValue.split(/(\b| )/) + : textNode.nodeValue.split(""); + + metrics = setTextVariables(ctx, el, textDecoration, color); + + if (options.chinese) { + textList.forEach(function(word, index) { + if (/.*[\u4E00-\u9FA5].*$/.test(word)) { + word = word.split(""); + word.unshift(index, 1); + textList.splice.apply(textList, word); + } + }); + } + + textList.forEach(function(text, index) { + var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix); + if (bounds) { + drawText(text, bounds.left, bounds.bottom, ctx); + renderTextDecoration(ctx, textDecoration, bounds, metrics, color); + } + }); + } + } + + function listPosition (element, val) { + var boundElement = doc.createElement( "boundelement" ), + originalType, + bounds; + + boundElement.style.display = "inline"; + + originalType = element.style.listStyleType; + element.style.listStyleType = "none"; + + boundElement.appendChild(doc.createTextNode(val)); + + element.insertBefore(boundElement, element.firstChild); + + bounds = Util.Bounds(boundElement); + element.removeChild(boundElement); + element.style.listStyleType = originalType; + return bounds; + } + + function elementIndex(el) { + var i = -1, + count = 1, + childs = el.parentNode.childNodes; + + if (el.parentNode) { + while(childs[++i] !== el) { + if (childs[i].nodeType === 1) { + count++; + } + } + return count; + } else { + return -1; + } + } + + function listItemText(element, type) { + var currentIndex = elementIndex(element), text; + switch(type){ + case "decimal": + text = currentIndex; + break; + case "decimal-leading-zero": + text = (currentIndex.toString().length === 1) ? currentIndex = "0" + currentIndex.toString() : currentIndex.toString(); + break; + case "upper-roman": + text = _html2canvas.Generate.ListRoman( currentIndex ); + break; + case "lower-roman": + text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase(); + break; + case "lower-alpha": + text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase(); + break; + case "upper-alpha": + text = _html2canvas.Generate.ListAlpha( currentIndex ); + break; + } + + return text + ". "; + } + + function renderListItem(element, stack, elBounds) { + var x, + text, + ctx = stack.ctx, + type = getCSS(element, "listStyleType"), + listBounds; + + if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { + text = listItemText(element, type); + listBounds = listPosition(element, text); + setTextVariables(ctx, element, "none", getCSS(element, "color")); + + if (getCSS(element, "listStylePosition") === "inside") { + ctx.setVariable("textAlign", "left"); + x = elBounds.left; + } else { + return; + } + + drawText(text, x, listBounds.bottom, ctx); + } + } + + function loadImage (src){ + var img = images[src]; + return (img && img.succeeded === true) ? img.img : false; + } + + function clipBounds(src, dst){ + var x = Math.max(src.left, dst.left), + y = Math.max(src.top, dst.top), + x2 = Math.min((src.left + src.width), (dst.left + dst.width)), + y2 = Math.min((src.top + src.height), (dst.top + dst.height)); + + return { + left:x, + top:y, + width:x2-x, + height:y2-y + }; + } + + function setZ(element, stack, parentStack){ + var newContext, + isPositioned = stack.cssPosition !== 'static', + zIndex = isPositioned ? getCSS(element, 'zIndex') : 'auto', + opacity = getCSS(element, 'opacity'), + isFloated = getCSS(element, 'cssFloat') !== 'none'; + + // https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context + // When a new stacking context should be created: + // the root element (HTML), + // positioned (absolutely or relatively) with a z-index value other than "auto", + // elements with an opacity value less than 1. (See the specification for opacity), + // on mobile WebKit and Chrome 22+, position: fixed always creates a new stacking context, even when z-index is "auto" (See this post) + + stack.zIndex = newContext = h2czContext(zIndex); + newContext.isPositioned = isPositioned; + newContext.isFloated = isFloated; + newContext.opacity = opacity; + newContext.ownStacking = (zIndex !== 'auto' || opacity < 1); + + if (parentStack) { + parentStack.zIndex.children.push(stack); + } + } + + function renderImage(ctx, element, image, bounds, borders) { + + var paddingLeft = getCSSInt(element, 'paddingLeft'), + paddingTop = getCSSInt(element, 'paddingTop'), + paddingRight = getCSSInt(element, 'paddingRight'), + paddingBottom = getCSSInt(element, 'paddingBottom'); + + drawImage( + ctx, + image, + 0, //sx + 0, //sy + image.width, //sw + image.height, //sh + bounds.left + paddingLeft + borders[3].width, //dx + bounds.top + paddingTop + borders[0].width, // dy + bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw + bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh + ); + } + + function getBorderData(element) { + return ["Top", "Right", "Bottom", "Left"].map(function(side) { + return { + width: getCSSInt(element, 'border' + side + 'Width'), + color: getCSS(element, 'border' + side + 'Color') + }; + }); + } + + function getBorderRadiusData(element) { + return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) { + return getCSS(element, 'border' + side + 'Radius'); + }); + } + + var getCurvePoints = (function(kappa) { + + return function(x, y, r1, r2) { + var ox = (r1) * kappa, // control point offset horizontal + oy = (r2) * kappa, // control point offset vertical + xm = x + r1, // x-middle + ym = y + r2; // y-middle + return { + topLeft: bezierCurve({ + x:x, + y:ym + }, { + x:x, + y:ym - oy + }, { + x:xm - ox, + y:y + }, { + x:xm, + y:y + }), + topRight: bezierCurve({ + x:x, + y:y + }, { + x:x + ox, + y:y + }, { + x:xm, + y:ym - oy + }, { + x:xm, + y:ym + }), + bottomRight: bezierCurve({ + x:xm, + y:y + }, { + x:xm, + y:y + oy + }, { + x:x + ox, + y:ym + }, { + x:x, + y:ym + }), + bottomLeft: bezierCurve({ + x:xm, + y:ym + }, { + x:xm - ox, + y:ym + }, { + x:x, + y:y + oy + }, { + x:x, + y:y + }) + }; + }; + })(4 * ((Math.sqrt(2) - 1) / 3)); + + function bezierCurve(start, startControl, endControl, end) { + + var lerp = function (a, b, t) { + return { + x:a.x + (b.x - a.x) * t, + y:a.y + (b.y - a.y) * t + }; + }; + + return { + start: start, + startControl: startControl, + endControl: endControl, + end: end, + subdivide: function(t) { + var ab = lerp(start, startControl, t), + bc = lerp(startControl, endControl, t), + cd = lerp(endControl, end, t), + abbc = lerp(ab, bc, t), + bccd = lerp(bc, cd, t), + dest = lerp(abbc, bccd, t); + return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)]; + }, + curveTo: function(borderArgs) { + borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]); + }, + curveToReversed: function(borderArgs) { + borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]); + } + }; + } + + function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) { + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]); + corner1[0].curveTo(borderArgs); + corner1[1].curveTo(borderArgs); + } else { + borderArgs.push(["line", x, y]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]); + } + } + + function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) { + var borderArgs = []; + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]); + outer1[1].curveTo(borderArgs); + } else { + borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]); + outer2[0].curveTo(borderArgs); + borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]); + inner2[0].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c2[0], borderData.c2[1]]); + borderArgs.push([ "line", borderData.c3[0], borderData.c3[1]]); + } + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]); + inner1[1].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c4[0], borderData.c4[1]]); + } + + return borderArgs; + } + + function calculateCurvePoints(bounds, borderRadius, borders) { + + var x = bounds.left, + y = bounds.top, + width = bounds.width, + height = bounds.height, + + tlh = borderRadius[0][0], + tlv = borderRadius[0][1], + trh = borderRadius[1][0], + trv = borderRadius[1][1], + brh = borderRadius[2][0], + brv = borderRadius[2][1], + blh = borderRadius[3][0], + blv = borderRadius[3][1], + + topWidth = width - trh, + rightHeight = height - brv, + bottomWidth = width - brh, + leftHeight = height - blv; + + return { + topLeftOuter: getCurvePoints( + x, + y, + tlh, + tlv + ).topLeft.subdivide(0.5), + + topLeftInner: getCurvePoints( + x + borders[3].width, + y + borders[0].width, + Math.max(0, tlh - borders[3].width), + Math.max(0, tlv - borders[0].width) + ).topLeft.subdivide(0.5), + + topRightOuter: getCurvePoints( + x + topWidth, + y, + trh, + trv + ).topRight.subdivide(0.5), + + topRightInner: getCurvePoints( + x + Math.min(topWidth, width + borders[3].width), + y + borders[0].width, + (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, + trv - borders[0].width + ).topRight.subdivide(0.5), + + bottomRightOuter: getCurvePoints( + x + bottomWidth, + y + rightHeight, + brh, + brv + ).bottomRight.subdivide(0.5), + + bottomRightInner: getCurvePoints( + x + Math.min(bottomWidth, width + borders[3].width), + y + Math.min(rightHeight, height + borders[0].width), + Math.max(0, brh - borders[1].width), + Math.max(0, brv - borders[2].width) + ).bottomRight.subdivide(0.5), + + bottomLeftOuter: getCurvePoints( + x, + y + leftHeight, + blh, + blv + ).bottomLeft.subdivide(0.5), + + bottomLeftInner: getCurvePoints( + x + borders[3].width, + y + leftHeight, + Math.max(0, blh - borders[3].width), + Math.max(0, blv - borders[2].width) + ).bottomLeft.subdivide(0.5) + }; + } + + function getBorderClip(element, borderPoints, borders, radius, bounds) { + var backgroundClip = getCSS(element, 'backgroundClip'), + borderArgs = []; + + switch(backgroundClip) { + case "content-box": + case "padding-box": + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width); + break; + + default: + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height); + break; + } + + return borderArgs; + } + + function parseBorders(element, bounds, borders){ + var x = bounds.left, + y = bounds.top, + width = bounds.width, + height = bounds.height, + borderSide, + bx, + by, + bw, + bh, + borderArgs, + // http://www.w3.org/TR/css3-background/#the-border-radius + borderRadius = getBorderRadiusData(element), + borderPoints = calculateCurvePoints(bounds, borderRadius, borders), + borderData = { + clip: getBorderClip(element, borderPoints, borders, borderRadius, bounds), + borders: [] + }; + + for (borderSide = 0; borderSide < 4; borderSide++) { + + if (borders[borderSide].width > 0) { + bx = x; + by = y; + bw = width; + bh = height - (borders[2].width); + + switch(borderSide) { + case 0: + // top border + bh = borders[0].width; + + borderArgs = drawSide({ + c1: [bx, by], + c2: [bx + bw, by], + c3: [bx + bw - borders[1].width, by + bh], + c4: [bx + borders[3].width, by + bh] + }, borderRadius[0], borderRadius[1], + borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner); + break; + case 1: + // right border + bx = x + width - (borders[1].width); + bw = borders[1].width; + + borderArgs = drawSide({ + c1: [bx + bw, by], + c2: [bx + bw, by + bh + borders[2].width], + c3: [bx, by + bh], + c4: [bx, by + borders[0].width] + }, borderRadius[1], borderRadius[2], + borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner); + break; + case 2: + // bottom border + by = (by + height) - (borders[2].width); + bh = borders[2].width; + + borderArgs = drawSide({ + c1: [bx + bw, by + bh], + c2: [bx, by + bh], + c3: [bx + borders[3].width, by], + c4: [bx + bw - borders[3].width, by] + }, borderRadius[2], borderRadius[3], + borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner); + break; + case 3: + // left border + bw = borders[3].width; + + borderArgs = drawSide({ + c1: [bx, by + bh + borders[2].width], + c2: [bx, by], + c3: [bx + bw, by + borders[0].width], + c4: [bx + bw, by + bh] + }, borderRadius[3], borderRadius[0], + borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner); + break; + } + + borderData.borders.push({ + args: borderArgs, + color: borders[borderSide].color + }); + + } + } + + return borderData; + } + + function createShape(ctx, args) { + var shape = ctx.drawShape(); + args.forEach(function(border, index) { + shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1)); + }); + return shape; + } + + function renderBorders(ctx, borderArgs, color) { + if (color !== "transparent") { + ctx.setVariable( "fillStyle", color); + createShape(ctx, borderArgs); + ctx.fill(); + numDraws+=1; + } + } + + function renderFormValue (el, bounds, stack){ + + var valueWrap = doc.createElement('valuewrap'), + cssPropertyArray = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], + textValue, + textNode; + + cssPropertyArray.forEach(function(property) { + try { + valueWrap.style[property] = getCSS(el, property); + } catch(e) { + // Older IE has issues with "border" + Util.log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); + } + }); + + valueWrap.style.borderColor = "black"; + valueWrap.style.borderStyle = "solid"; + valueWrap.style.display = "block"; + valueWrap.style.position = "absolute"; + + if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){ + valueWrap.style.lineHeight = getCSS(el, "height"); + } + + valueWrap.style.top = bounds.top + "px"; + valueWrap.style.left = bounds.left + "px"; + + textValue = (el.nodeName === "SELECT") ? (el.options[el.selectedIndex] || 0).text : el.value; + if(!textValue) { + textValue = el.placeholder; + } + + textNode = doc.createTextNode(textValue); + + valueWrap.appendChild(textNode); + body.appendChild(valueWrap); + + renderText(el, textNode, stack); + body.removeChild(valueWrap); + } + + function drawImage (ctx) { + ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1)); + numDraws+=1; + } + + function getPseudoElement(el, which) { + var elStyle = window.getComputedStyle(el, which); + if(!elStyle || !elStyle.content || elStyle.content === "none" || elStyle.content === "-moz-alt-content" || elStyle.display === "none") { + return; + } + var content = elStyle.content + '', + first = content.substr( 0, 1 ); + //strips quotes + if(first === content.substr( content.length - 1 ) && first.match(/'|"/)) { + content = content.substr( 1, content.length - 2 ); + } + + var isImage = content.substr( 0, 3 ) === 'url', + elps = document.createElement( isImage ? 'img' : 'span' ); + + elps.className = pseudoHide + "-before " + pseudoHide + "-after"; + + Object.keys(elStyle).filter(indexedProperty).forEach(function(prop) { + // Prevent assigning of read only CSS Rules, ex. length, parentRule + try { + elps.style[prop] = elStyle[prop]; + } catch (e) { + Util.log(['Tried to assign readonly property ', prop, 'Error:', e]); + } + }); + + if(isImage) { + elps.src = Util.parseBackgroundImage(content)[0].args[0]; + } else { + elps.innerHTML = content; + } + return elps; + } + + function indexedProperty(property) { + return (isNaN(window.parseInt(property, 10))); + } + + function injectPseudoElements(el, stack) { + var before = getPseudoElement(el, ':before'), + after = getPseudoElement(el, ':after'); + if(!before && !after) { + return; + } + + if(before) { + el.className += " " + pseudoHide + "-before"; + el.parentNode.insertBefore(before, el); + parseElement(before, stack, true); + el.parentNode.removeChild(before); + el.className = el.className.replace(pseudoHide + "-before", "").trim(); + } + + if (after) { + el.className += " " + pseudoHide + "-after"; + el.appendChild(after); + parseElement(after, stack, true); + el.removeChild(after); + el.className = el.className.replace(pseudoHide + "-after", "").trim(); + } + + } + + function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) { + var offsetX = Math.round(bounds.left + backgroundPosition.left), + offsetY = Math.round(bounds.top + backgroundPosition.top); + + ctx.createPattern(image); + ctx.translate(offsetX, offsetY); + ctx.fill(); + ctx.translate(-offsetX, -offsetY); + } + + function backgroundRepeatShape(ctx, image, backgroundPosition, bounds, left, top, width, height) { + var args = []; + args.push(["line", Math.round(left), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(height + top)]); + args.push(["line", Math.round(left), Math.round(height + top)]); + createShape(ctx, args); + ctx.save(); + ctx.clip(); + renderBackgroundRepeat(ctx, image, backgroundPosition, bounds); + ctx.restore(); + } + + function renderBackgroundColor(ctx, backgroundBounds, bgcolor) { + renderRect( + ctx, + backgroundBounds.left, + backgroundBounds.top, + backgroundBounds.width, + backgroundBounds.height, + bgcolor + ); + } + + function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) { + var backgroundSize = Util.BackgroundSize(el, bounds, image, imageIndex), + backgroundPosition = Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize), + backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(Util.trimText); + + image = resizeImage(image, backgroundSize); + + backgroundRepeat = backgroundRepeat[imageIndex] || backgroundRepeat[0]; + + switch (backgroundRepeat) { + case "repeat-x": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left, bounds.top + backgroundPosition.top, 99999, image.height); + break; + + case "repeat-y": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top, image.width, 99999); + break; + + case "no-repeat": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height); + break; + + default: + renderBackgroundRepeat(ctx, image, backgroundPosition, { + top: bounds.top, + left: bounds.left, + width: image.width, + height: image.height + }); + break; + } + } + + function renderBackgroundImage(element, bounds, ctx) { + var backgroundImage = getCSS(element, "backgroundImage"), + backgroundImages = Util.parseBackgroundImage(backgroundImage), + image, + imageIndex = backgroundImages.length; + + while(imageIndex--) { + backgroundImage = backgroundImages[imageIndex]; + + if (!backgroundImage.args || backgroundImage.args.length === 0) { + continue; + } + + var key = backgroundImage.method === 'url' ? + backgroundImage.args[0] : + backgroundImage.value; + + image = loadImage(key); + + // TODO add support for background-origin + if (image) { + renderBackgroundRepeating(element, bounds, ctx, image, imageIndex); + } else { + Util.log("html2canvas: Error loading background:", backgroundImage); + } + } + } + + function resizeImage(image, bounds) { + if(image.width === bounds.width && image.height === bounds.height) { + return image; + } + + var ctx, canvas = doc.createElement('canvas'); + canvas.width = bounds.width; + canvas.height = bounds.height; + ctx = canvas.getContext("2d"); + drawImage(ctx, image, 0, 0, image.width, image.height, 0, 0, bounds.width, bounds.height ); + return canvas; + } + + function setOpacity(ctx, element, parentStack) { + return ctx.setVariable("globalAlpha", getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1)); + } + + function removePx(str) { + return str.replace("px", ""); + } + + var transformRegExp = /(matrix)\((.+)\)/; + + function getTransform(element, parentStack) { + var transform = getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform"); + var transformOrigin = getCSS(element, "transform-origin") || getCSS(element, "-webkit-transform-origin") || getCSS(element, "-moz-transform-origin") || getCSS(element, "-ms-transform-origin") || getCSS(element, "-o-transform-origin") || "0px 0px"; + + transformOrigin = transformOrigin.split(" ").map(removePx).map(Util.asFloat); + + var matrix; + if (transform && transform !== "none") { + var match = transform.match(transformRegExp); + if (match) { + switch(match[1]) { + case "matrix": + matrix = match[2].split(",").map(Util.trimText).map(Util.asFloat); + break; + } + } + } + + return { + origin: transformOrigin, + matrix: matrix + }; + } + + function createStack(element, parentStack, bounds, transform) { + var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), + stack = { + ctx: ctx, + opacity: setOpacity(ctx, element, parentStack), + cssPosition: getCSS(element, "position"), + borders: getBorderData(element), + transform: transform, + clip: (parentStack && parentStack.clip) ? Util.Extend( {}, parentStack.clip ) : null + }; + + setZ(element, stack, parentStack); + + // TODO correct overflow for absolute content residing under a static position + if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(element, "overflow")) === true && /(BODY)/i.test(element.nodeName) === false){ + stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds; + } + + return stack; + } + + function getBackgroundBounds(borders, bounds, clip) { + var backgroundBounds = { + left: bounds.left + borders[3].width, + top: bounds.top + borders[0].width, + width: bounds.width - (borders[1].width + borders[3].width), + height: bounds.height - (borders[0].width + borders[2].width) + }; + + if (clip) { + backgroundBounds = clipBounds(backgroundBounds, clip); + } + + return backgroundBounds; + } + + function getBounds(element, transform) { + var bounds = (transform.matrix) ? Util.OffsetBounds(element) : Util.Bounds(element); + transform.origin[0] += bounds.left; + transform.origin[1] += bounds.top; + return bounds; + } + + function renderElement(element, parentStack, pseudoElement, ignoreBackground) { + var transform = getTransform(element, parentStack), + bounds = getBounds(element, transform), + image, + stack = createStack(element, parentStack, bounds, transform), + borders = stack.borders, + ctx = stack.ctx, + backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip), + borderData = parseBorders(element, bounds, borders), + backgroundColor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"); + + + createShape(ctx, borderData.clip); + + ctx.save(); + ctx.clip(); + + if (backgroundBounds.height > 0 && backgroundBounds.width > 0 && !ignoreBackground) { + renderBackgroundColor(ctx, bounds, backgroundColor); + renderBackgroundImage(element, backgroundBounds, ctx); + } else if (ignoreBackground) { + stack.backgroundColor = backgroundColor; + } + + ctx.restore(); + + borderData.borders.forEach(function(border) { + renderBorders(ctx, border.args, border.color); + }); + + if (!pseudoElement) { + injectPseudoElements(element, stack); + } + + switch(element.nodeName){ + case "IMG": + if ((image = loadImage(element.getAttribute('src')))) { + renderImage(ctx, element, image, bounds, borders); + } else { + Util.log("html2canvas: Error loading :" + element.getAttribute('src')); + } + break; + case "INPUT": + // TODO add all relevant type's, i.e. HTML5 new stuff + // todo add support for placeholder attribute for browsers which support it + if (/^(text|url|email|submit|button|reset)$/.test(element.type) && (element.value || element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "TEXTAREA": + if ((element.value || element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "SELECT": + if ((element.options||element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "LI": + renderListItem(element, stack, backgroundBounds); + break; + case "CANVAS": + renderImage(ctx, element, element, bounds, borders); + break; + } + + return stack; + } + + function isElementVisible(element) { + return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore")); + } + + function parseElement (element, stack, pseudoElement) { + if (isElementVisible(element)) { + stack = renderElement(element, stack, pseudoElement, false) || stack; + if (!ignoreElementsRegExp.test(element.nodeName)) { + parseChildren(element, stack, pseudoElement); + } + } + } + + function parseChildren(element, stack, pseudoElement) { + Util.Children(element).forEach(function(node) { + if (node.nodeType === node.ELEMENT_NODE) { + parseElement(node, stack, pseudoElement); + } else if (node.nodeType === node.TEXT_NODE) { + renderText(element, node, stack); + } + }); + } + + function init() { + var background = getCSS(document.documentElement, "backgroundColor"), + transparentBackground = (Util.isTransparent(background) && element === document.body), + stack = renderElement(element, null, false, transparentBackground); + parseChildren(element, stack); + + if (transparentBackground) { + background = stack.backgroundColor; + } + + body.removeChild(hidePseudoElements); + return { + backgroundColor: background, + stack: stack + }; + } + + return init(); +}; + +function h2czContext(zindex) { + return { + zindex: zindex, + children: [] + }; +} + +_html2canvas.Preload = function( options ) { + + var images = { + numLoaded: 0, // also failed are counted here + numFailed: 0, + numTotal: 0, + cleanupDone: false + }, + pageOrigin, + Util = _html2canvas.Util, + methods, + i, + count = 0, + element = options.elements[0] || document.body, + doc = element.ownerDocument, + domImages = element.getElementsByTagName('img'), // Fetch images of the present element only + imgLen = domImages.length, + link = doc.createElement("a"), + supportCORS = (function( img ){ + return (img.crossOrigin !== undefined); + })(new Image()), + timeoutTimer; + + link.href = window.location.href; + pageOrigin = link.protocol + link.host; + + function isSameOrigin(url){ + link.href = url; + link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ + var origin = link.protocol + link.host; + return (origin === pageOrigin); + } + + function start(){ + Util.log("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); + if (!images.firstRun && images.numLoaded >= images.numTotal){ + Util.log("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); + + if (typeof options.complete === "function"){ + options.complete(images); + } + + } + } + + // TODO modify proxy to serve images with CORS enabled, where available + function proxyGetImage(url, img, imageObj){ + var callback_name, + scriptUrl = options.proxy, + script; + + link.href = url; + url = link.href; // work around for pages with base href="" set - WARNING: this may change the url + + callback_name = 'html2canvas_' + (count++); + imageObj.callbackname = callback_name; + + if (scriptUrl.indexOf("?") > -1) { + scriptUrl += "&"; + } else { + scriptUrl += "?"; + } + scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; + script = doc.createElement("script"); + + window[callback_name] = function(a){ + if (a.substring(0,6) === "error:"){ + imageObj.succeeded = false; + images.numLoaded++; + images.numFailed++; + start(); + } else { + setImageLoadHandlers(img, imageObj); + img.src = a; + } + window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[callback_name]; // for all browser that support this + } catch(ex) {} + script.parentNode.removeChild(script); + script = null; + delete imageObj.script; + delete imageObj.callbackname; + }; + + script.setAttribute("type", "text/javascript"); + script.setAttribute("src", scriptUrl); + imageObj.script = script; + window.document.body.appendChild(script); + + } + //uses an img tag to load the img as proxy + function proxyGetImageWithImgTag(url, img, imageObj){ + img.src = options.proxy + encodeURIComponent(url); + return img.src; + + } + + function loadPseudoElement(element, type) { + var style = window.getComputedStyle(element, type), + content = style.content; + if (content.substr(0, 3) === 'url') { + methods.loadImage(_html2canvas.Util.parseBackgroundImage(content)[0].args[0]); + } + loadBackgroundImages(style.backgroundImage, element); + } + + function loadPseudoElementImages(element) { + loadPseudoElement(element, ":before"); + loadPseudoElement(element, ":after"); + } + + function loadGradientImage(backgroundImage, bounds) { + var img = _html2canvas.Generate.Gradient(backgroundImage, bounds); + + if (img !== undefined){ + images[backgroundImage] = { + img: img, + succeeded: true + }; + images.numTotal++; + images.numLoaded++; + start(); + } + } + + function invalidBackgrounds(background_image) { + return (background_image && background_image.method && background_image.args && background_image.args.length > 0 ); + } + + function loadBackgroundImages(background_image, el) { + var bounds; + + _html2canvas.Util.parseBackgroundImage(background_image).filter(invalidBackgrounds).forEach(function(background_image) { + if (background_image.method === 'url') { + methods.loadImage(background_image.args[0]); + } else if(background_image.method.match(/\-?gradient$/)) { + if(bounds === undefined) { + bounds = _html2canvas.Util.Bounds(el); + } + loadGradientImage(background_image.value, bounds); + } + }); + } + + function getImages (el) { + var elNodeType = false; + + // Firefox fails with permission denied on pages with iframes + try { + Util.Children(el).forEach(getImages); + } + catch( e ) {} + + try { + elNodeType = el.nodeType; + } catch (ex) { + elNodeType = false; + Util.log("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); + } + + if (elNodeType === 1 || elNodeType === undefined) { + loadPseudoElementImages(el); + try { + loadBackgroundImages(Util.getCSS(el, 'backgroundImage'), el); + } catch(e) { + Util.log("html2canvas: failed to get background-image - Exception: " + e.message); + } + loadBackgroundImages(el); + } + } + + function setImageLoadHandlers(img, imageObj) { + img.onload = function() { + if ( imageObj.timer !== undefined ) { + // CORS succeeded + window.clearTimeout( imageObj.timer ); + } + + images.numLoaded++; + imageObj.succeeded = true; + img.onerror = img.onload = null; + start(); + }; + img.onerror = function() { + if (img.crossOrigin === "anonymous") { + // CORS failed + window.clearTimeout( imageObj.timer ); + + // let's try with proxy instead + if ( options.proxy ) { + proxyGetImageWithImgTag( img.src, img, imageObj ); + var src = img.src; + img = new Image(); + imageObj.img = img; + img.src = src; + return; + } + } + + images.numLoaded++; + images.numFailed++; + imageObj.succeeded = false; + img.onerror = img.onload = null; + start(); + }; + } + + methods = { + loadImage: function( src ) { + var img, imageObj; + if ( src && images[src] === undefined ) { + img = new Image(); + if ( src.match(/data:image\/.*;base64,/i) ) { + img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + } else if ( isSameOrigin( src ) || options.allowTaint === true ) { + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + } else if ( supportCORS && !options.allowTaint && options.useCORS ) { + // attempt to load with CORS + + img.crossOrigin = "anonymous"; + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + } else if ( options.proxy ) { + + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = options.proxy + encodeURIComponent(src); + + } + } + + }, + cleanupDOM: function(cause) { + var img, src; + if (!images.cleanupDone) { + if (cause && typeof cause === "string") { + Util.log("html2canvas: Cleanup because: " + cause); + } else { + Util.log("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); + } + + for (src in images) { + if (images.hasOwnProperty(src)) { + img = images[src]; + if (typeof img === "object" && img.callbackname && img.succeeded === undefined) { + // cancel proxy image request + window[img.callbackname] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[img.callbackname]; // for all browser that support this + } catch(ex) {} + if (img.script && img.script.parentNode) { + img.script.setAttribute("src", "about:blank"); // try to cancel running request + img.script.parentNode.removeChild(img.script); + } + images.numLoaded++; + images.numFailed++; + Util.log("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); + } + } + } + + // cancel any pending requests + if(window.stop !== undefined) { + window.stop(); + } else if(document.execCommand !== undefined) { + document.execCommand("Stop", false); + } + if (document.close !== undefined) { + document.close(); + } + images.cleanupDone = true; + if (!(cause && typeof cause === "string")) { + start(); + } + } + }, + + renderingDone: function() { + if (timeoutTimer) { + window.clearTimeout(timeoutTimer); + } + } + }; + + if (options.timeout > 0) { + timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); + } + + Util.log('html2canvas: Preload starts: finding background-images'); + images.firstRun = true; + + getImages(element); + + Util.log('html2canvas: Preload: Finding images'); + // load images + for (i = 0; i < imgLen; i+=1){ + methods.loadImage( domImages[i].getAttribute( "src" ) ); + } + + images.firstRun = false; + Util.log('html2canvas: Preload: Done.'); + if (images.numTotal === images.numLoaded) { + start(); + } + + return methods; +}; + +_html2canvas.Renderer = function(parseQueue, options){ + + // http://www.w3.org/TR/CSS21/zindex.html + function createRenderQueue(parseQueue) { + var queue = [], + rootContext; + + rootContext = (function buildStackingContext(rootNode) { + var rootContext = {}; + function insert(context, node, specialParent) { + var zi = (node.zIndex.zindex === 'auto') ? 0 : Number(node.zIndex.zindex), + contextForChildren = context, // the stacking context for children + isPositioned = node.zIndex.isPositioned, + isFloated = node.zIndex.isFloated, + stub = {node: node}, + childrenDest = specialParent; // where children without z-index should be pushed into + + if (node.zIndex.ownStacking) { + // '!' comes before numbers in sorted array + contextForChildren = stub.context = { '!': [{node:node, children: []}]}; + childrenDest = undefined; + } else if (isPositioned || isFloated) { + childrenDest = stub.children = []; + } + + if (zi === 0 && specialParent) { + specialParent.push(stub); + } else { + if (!context[zi]) { context[zi] = []; } + context[zi].push(stub); + } + + node.zIndex.children.forEach(function(childNode) { + insert(contextForChildren, childNode, childrenDest); + }); + } + insert(rootContext, rootNode); + return rootContext; + })(parseQueue); + + function sortZ(context) { + Object.keys(context).sort().forEach(function(zi) { + var nonPositioned = [], + floated = [], + positioned = [], + list = []; + + // positioned after static + context[zi].forEach(function(v) { + if (v.node.zIndex.isPositioned || v.node.zIndex.opacity < 1) { + // http://www.w3.org/TR/css3-color/#transparency + // non-positioned element with opactiy < 1 should be stacked as if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’. + positioned.push(v); + } else if (v.node.zIndex.isFloated) { + floated.push(v); + } else { + nonPositioned.push(v); + } + }); + + (function walk(arr) { + arr.forEach(function(v) { + list.push(v); + if (v.children) { walk(v.children); } + }); + })(nonPositioned.concat(floated, positioned)); + + list.forEach(function(v) { + if (v.context) { + sortZ(v.context); + } else { + queue.push(v.node); + } + }); + }); + } + + sortZ(rootContext); + + return queue; + } + + function getRenderer(rendererName) { + var renderer; + + if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) { + renderer = _html2canvas.Renderer[rendererName](options); + } else if (typeof rendererName === "function") { + renderer = rendererName(options); + } else { + throw new Error("Unknown renderer"); + } + + if ( typeof renderer !== "function" ) { + throw new Error("Invalid renderer defined"); + } + return renderer; + } + + return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue.stack), _html2canvas); +}; + +_html2canvas.Util.Support = function (options, doc) { + + function supportSVGRendering() { + var img = new Image(), + canvas = doc.createElement("canvas"), + ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); + if (ctx === false) { + return false; + } + canvas.width = canvas.height = 10; + img.src = [ + "data:image/svg+xml,", + "", + "", + "
", + "sup", + "
", + "
", + "
" + ].join(""); + try { + ctx.drawImage(img, 0, 0); + canvas.toDataURL(); + } catch(e) { + return false; + } + _html2canvas.Util.log('html2canvas: Parse: SVG powered rendering available'); + return true; + } + + // Test whether we can use ranges to measure bounding boxes + // Opera doesn't provide valid bounds.height/bottom even though it supports the method. + + function supportRangeBounds() { + var r, testElement, rangeBounds, rangeHeight, support = false; + + if (doc.createRange) { + r = doc.createRange(); + if (r.getBoundingClientRect) { + testElement = doc.createElement('boundtest'); + testElement.style.height = "123px"; + testElement.style.display = "block"; + doc.body.appendChild(testElement); + + r.selectNode(testElement); + rangeBounds = r.getBoundingClientRect(); + rangeHeight = rangeBounds.height; + + if (rangeHeight === 123) { + support = true; + } + doc.body.removeChild(testElement); + } + } + + return support; + } + + return { + rangeBounds: supportRangeBounds(), + svgRendering: options.svgRendering && supportSVGRendering() + }; +}; +window.html2canvas = function(elements, opts) { + elements = (elements.length) ? elements : [elements]; + var queue, + canvas, + options = { + // general + logging: false, + elements: elements, + background: "#fff", + + // preload options + proxy: null, + timeout: 0, // no timeout + useCORS: false, // try to load images as CORS (where available), before falling back to proxy + allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true + + // parse options + svgRendering: false, // use svg powered rendering where available (FF11+) + ignoreElements: "IFRAME|OBJECT|PARAM", + useOverflow: true, + letterRendering: false, + chinese: false, + + // render options + + width: null, + height: null, + taintTest: true, // do a taint test with all images before applying to canvas + renderer: "Canvas" + }; + + options = _html2canvas.Util.Extend(opts, options); + + _html2canvas.logging = options.logging; + options.complete = function( images ) { + + if (typeof options.onpreloaded === "function") { + if ( options.onpreloaded( images ) === false ) { + return; + } + } + queue = _html2canvas.Parse( images, options ); + + if (typeof options.onparsed === "function") { + if ( options.onparsed( queue ) === false ) { + return; + } + } + + canvas = _html2canvas.Renderer( queue, options ); + + if (typeof options.onrendered === "function") { + options.onrendered( canvas ); + } + + + }; + + // for pages without images, we still want this to be async, i.e. return methods before executing + window.setTimeout( function(){ + _html2canvas.Preload( options ); + }, 0 ); + + return { + render: function( queue, opts ) { + return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) ); + }, + parse: function( images, opts ) { + return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) ); + }, + preload: function( opts ) { + return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) ); + }, + log: _html2canvas.Util.log + }; +}; + +window.html2canvas.log = _html2canvas.Util.log; // for renderers +window.html2canvas.Renderer = { + Canvas: undefined // We are assuming this will be used +}; +_html2canvas.Renderer.Canvas = function(options) { + options = options || {}; + + var doc = document, + safeImages = [], + testCanvas = document.createElement("canvas"), + testctx = testCanvas.getContext("2d"), + Util = _html2canvas.Util, + canvas = options.canvas || doc.createElement('canvas'); + + function createShape(ctx, args) { + ctx.beginPath(); + args.forEach(function(arg) { + ctx[arg.name].apply(ctx, arg['arguments']); + }); + ctx.closePath(); + } + + function safeImage(item) { + if (safeImages.indexOf(item['arguments'][0].src ) === -1) { + testctx.drawImage(item['arguments'][0], 0, 0); + try { + testctx.getImageData(0, 0, 1, 1); + } catch(e) { + testCanvas = doc.createElement("canvas"); + testctx = testCanvas.getContext("2d"); + return false; + } + safeImages.push(item['arguments'][0].src); + } + return true; + } + + function renderItem(ctx, item) { + switch(item.type){ + case "variable": + ctx[item.name] = item['arguments']; + break; + case "function": + switch(item.name) { + case "createPattern": + if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) { + try { + ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat"); + } + catch(e) { + Util.log("html2canvas: Renderer: Error creating pattern", e.message); + } + } + break; + case "drawShape": + createShape(ctx, item['arguments']); + break; + case "drawImage": + if (item['arguments'][8] > 0 && item['arguments'][7] > 0) { + if (!options.taintTest || (options.taintTest && safeImage(item))) { + ctx.drawImage.apply( ctx, item['arguments'] ); + } + } + break; + default: + ctx[item.name].apply(ctx, item['arguments']); + } + break; + } + } + + return function(parsedData, options, document, queue, _html2canvas) { + var ctx = canvas.getContext("2d"), + newCanvas, + bounds, + fstyle, + zStack = parsedData.stack; + + canvas.width = canvas.style.width = options.width || zStack.ctx.width; + canvas.height = canvas.style.height = options.height || zStack.ctx.height; + + fstyle = ctx.fillStyle; + ctx.fillStyle = (Util.isTransparent(zStack.backgroundColor) && options.background !== undefined) ? options.background : parsedData.backgroundColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = fstyle; + + queue.forEach(function(storageContext) { + // set common settings for canvas + ctx.textBaseline = "bottom"; + ctx.save(); + + if (storageContext.transform.matrix) { + ctx.translate(storageContext.transform.origin[0], storageContext.transform.origin[1]); + ctx.transform.apply(ctx, storageContext.transform.matrix); + ctx.translate(-storageContext.transform.origin[0], -storageContext.transform.origin[1]); + } + + if (storageContext.clip){ + ctx.beginPath(); + ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); + ctx.clip(); + } + + if (storageContext.ctx.storage) { + storageContext.ctx.storage.forEach(function(item) { + renderItem(ctx, item); + }); + } + + ctx.restore(); + }); + + Util.log("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); + + if (options.elements.length === 1) { + if (typeof options.elements[0] === "object" && options.elements[0].nodeName !== "BODY") { + // crop image to the bounds of selected (single) element + bounds = _html2canvas.Util.Bounds(options.elements[0]); + newCanvas = document.createElement('canvas'); + newCanvas.width = Math.ceil(bounds.width); + newCanvas.height = Math.ceil(bounds.height); + ctx = newCanvas.getContext("2d"); + + ctx.drawImage(canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height); + canvas = null; + return newCanvas; + } + } + + return canvas; + }; +}; +})(window,document); diff --git a/mapcomposer/app/static/externals/cookiechoices/LICENSE b/mapcomposer/app/static/externals/cookiechoices/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/mapcomposer/app/static/externals/cookiechoices/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/mapcomposer/app/static/externals/cookiechoices/cookiechoices.css b/mapcomposer/app/static/externals/cookiechoices/cookiechoices.css new file mode 100644 index 000000000..63ad94eb9 --- /dev/null +++ b/mapcomposer/app/static/externals/cookiechoices/cookiechoices.css @@ -0,0 +1,51 @@ +#cookie-statement { + background-color: #5a5a5a; + + + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + color: #fff; + display: none; + font-weight: bold; + margin: 0 -15px; + padding-left: 0; + padding-right: 0; + position: absolute; + width: 100%; + z-index: 999 +} + +#cookieChoiceInfo.display { + display: block +} + +#cookieChoiceInfo .text { + padding: 0 15px; + display: block +} + +#cookieChoiceInfo a { + background-color: #303030; + border: 1px solid rgba(0, 0, 0, .1); + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + color: #fff; + cursor: pointer; + line-height: 19px; + padding: 4px 8px; + text-decoration: none; + white-space: nowrap +} +#cookieChoiceInfo, #cookieChoiceInfo div { + background-color: #5a5a5a; + opacity: .8; + color: #fff; + font-weight: bold; + min-height: 40px; + text-align: center; + padding-top: 15px; + +} + diff --git a/mapcomposer/app/static/externals/cookiechoices/cookiechoices.js b/mapcomposer/app/static/externals/cookiechoices/cookiechoices.js new file mode 100644 index 000000000..f7158d505 --- /dev/null +++ b/mapcomposer/app/static/externals/cookiechoices/cookiechoices.js @@ -0,0 +1,171 @@ +/* + Copyright 2014 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +(function(window) { + + if (!!window.cookieChoices) { + return window.cookieChoices; + } + + var document = window.document; + // IE8 does not support textContent, so we should fallback to innerText. + var supportsTextContent = 'textContent' in document.body; + + var cookieChoices = (function() { + + var cookieName = 'displayCookieConsent'; + var cookieConsentId = 'cookieChoiceInfo'; + var dismissLinkId = 'cookieChoiceDismiss'; + + function _createHeaderElement(cookieText, dismissText, linkText, linkHref) { + var butterBarStyles = 'position:fixed;width:100%;' + + 'margin:0; left:0; top:0;z-index:1000;text-align:center;'; + + var cookieConsentElement = document.createElement('div'); + cookieConsentElement.id = cookieConsentId; + cookieConsentElement.style.cssText = butterBarStyles; + cookieConsentElement.appendChild(_createConsentText(cookieText)); + + if (!!linkText && !!linkHref) { + cookieConsentElement.appendChild(_createInformationLink(linkText, linkHref)); + } + cookieConsentElement.appendChild(_createDismissLink(dismissText)); + return cookieConsentElement; + } + + function _createDialogElement(cookieText, dismissText, linkText, linkHref) { + var glassStyle = 'position:fixed;width:100%;height:100%;z-index:999;' + + 'top:0;left:0;opacity:0.5;filter:alpha(opacity=50);' + + 'background-color:#ccc;'; + var dialogStyle = 'z-index:1000;position:fixed;left:50%;top:50%'; + var contentStyle = 'position:relative;left:-50%;margin-top:-25%;' + + 'background-color:#fff;padding:20px;box-shadow:4px 4px 25px #888;'; + + var cookieConsentElement = document.createElement('div'); + cookieConsentElement.id = cookieConsentId; + + var glassPanel = document.createElement('div'); + glassPanel.style.cssText = glassStyle; + + var content = document.createElement('div'); + content.style.cssText = contentStyle; + + var dialog = document.createElement('div'); + dialog.style.cssText = dialogStyle; + + var dismissLink = _createDismissLink(dismissText); + dismissLink.style.display = 'block'; + dismissLink.style.textAlign = 'right'; + dismissLink.style.marginTop = '8px'; + + content.appendChild(_createConsentText(cookieText)); + if (!!linkText && !!linkHref) { + content.appendChild(_createInformationLink(linkText, linkHref)); + } + content.appendChild(dismissLink); + dialog.appendChild(content); + cookieConsentElement.appendChild(glassPanel); + cookieConsentElement.appendChild(dialog); + return cookieConsentElement; + } + + function _setElementText(element, text) { + if (supportsTextContent) { + element.textContent = text; + } else { + element.innerText = text; + } + } + + function _createConsentText(cookieText) { + var consentText = document.createElement('span'); + _setElementText(consentText, cookieText); + return consentText; + } + + function _createDismissLink(dismissText) { + var dismissLink = document.createElement('a'); + _setElementText(dismissLink, dismissText); + dismissLink.id = dismissLinkId; + dismissLink.href = '#'; + dismissLink.style.marginLeft = '24px'; + return dismissLink; + } + + function _createInformationLink(linkText, linkHref) { + var infoLink = document.createElement('a'); + _setElementText(infoLink, linkText); + infoLink.href = linkHref; + infoLink.target = '_blank'; + infoLink.style.marginLeft = '8px'; + return infoLink; + } + + function _dismissLinkClick() { + _saveUserPreference(); + _removeCookieConsent(); + return false; + } + + function _showCookieConsent(cookieText, dismissText, linkText, linkHref, isDialog) { + if (_shouldDisplayConsent()) { + _removeCookieConsent(); + var consentElement = (isDialog) ? + _createDialogElement(cookieText, dismissText, linkText, linkHref) : + _createHeaderElement(cookieText, dismissText, linkText, linkHref); + var fragment = document.createDocumentFragment(); + fragment.appendChild(consentElement); + document.body.appendChild(fragment.cloneNode(true)); + document.getElementById(dismissLinkId).onclick = _dismissLinkClick; + } + } + + function showCookieConsentBar(cookieText, dismissText, linkText, linkHref) { + _showCookieConsent(cookieText, dismissText, linkText, linkHref, false); + } + + function showCookieConsentDialog(cookieText, dismissText, linkText, linkHref) { + _showCookieConsent(cookieText, dismissText, linkText, linkHref, true); + } + + function _removeCookieConsent() { + var cookieChoiceElement = document.getElementById(cookieConsentId); + if (cookieChoiceElement != null) { + cookieChoiceElement.parentNode.removeChild(cookieChoiceElement); + } + } + + function _saveUserPreference() { + // Set the cookie expiry to one year after today. + var expiryDate = new Date(); + expiryDate.setFullYear(expiryDate.getFullYear() + 1); + document.cookie = cookieName + '=y; expires=' + expiryDate.toGMTString(); + } + + function _shouldDisplayConsent() { + // Display the header only if the cookie has not been set. + return !document.cookie.match(new RegExp(cookieName + '=([^;]+)')); + } + + var exports = {}; + exports.showCookieConsentBar = showCookieConsentBar; + exports.showCookieConsentDialog = showCookieConsentDialog; + return exports; + })(); + + window.cookieChoices = cookieChoices; + return cookieChoices; +})(this); diff --git a/mapcomposer/app/static/externals/csw/src/CSWGrid.js b/mapcomposer/app/static/externals/csw/src/CSWGrid.js index cdca024fe..37b4cf8c8 100644 --- a/mapcomposer/app/static/externals/csw/src/CSWGrid.js +++ b/mapcomposer/app/static/externals/csw/src/CSWGrid.js @@ -351,7 +351,7 @@ CSWGrid = Ext.extend(Ext.grid.GridPanel, { layers:mapInfo, bbox: values.bounds, uuid: values.identifier, - gnURL: values.absolutePath.substring(0,values.absolutePath.length-10) + gnURL: values.absolutePath.substring(0,values.absolutePath.length-11) }; diff --git a/mapcomposer/app/static/externals/csw/src/CSWHttpProxy.js b/mapcomposer/app/static/externals/csw/src/CSWHttpProxy.js index fd500328a..aaeceb6e2 100644 --- a/mapcomposer/app/static/externals/csw/src/CSWHttpProxy.js +++ b/mapcomposer/app/static/externals/csw/src/CSWHttpProxy.js @@ -139,12 +139,12 @@ } // Sort results by a given property - optionsCsw.Query.SortBy = { - SortProperty: { - PropertyName: {value: this.sortProperty}, - SortOrder: {value: this.sortOrder} + optionsCsw.Query.SortBy = [ + { + property: this.sortProperty, + order: this.sortOrder } - }; + ]; var format = new OpenLayers.Format.CSWGetRecords(); return format.write(optionsCsw); diff --git a/mapcomposer/app/static/externals/csw/src/CSWSearchTool.js b/mapcomposer/app/static/externals/csw/src/CSWSearchTool.js index 8e3566a55..8efab9f4c 100644 --- a/mapcomposer/app/static/externals/csw/src/CSWSearchTool.js +++ b/mapcomposer/app/static/externals/csw/src/CSWSearchTool.js @@ -221,13 +221,16 @@ CSWSearchTool = Ext.extend(Ext.form.FormPanel, { // If no filter has been set, builds the query as to select every record, // otherwise the conditions are applied as filters - if (filters.length != 0) { + if (filters.length > 1){ options.filter = new OpenLayers.Filter.Logical({ type : OpenLayers.Filter.Logical.AND, filters : filters }); options.emptySearch = false; - } else { + } else if (filters.length == 1){ + options.filter=filters.pop(); + options.emptySearch = false; + }else { options.emptySearch = true; } @@ -640,6 +643,10 @@ CSWSearchTool = Ext.extend(Ext.form.FormPanel, { this.searchButton.disable(); this.resetButton.disable(); + this.on('afterlayout',function(panel, layout){ + panel.catalogSelectionPan.doLayout(); + }); + //event associations this.catalogChooser.on('select', function() { this.searchButton.disable(); diff --git a/mapcomposer/app/static/externals/gxp/src/script/GeoExplorerLoader.js b/mapcomposer/app/static/externals/gxp/src/script/GeoExplorerLoader.js index b085b8a52..cf12793e7 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/GeoExplorerLoader.js +++ b/mapcomposer/app/static/externals/gxp/src/script/GeoExplorerLoader.js @@ -39,7 +39,8 @@ var GeoExplorerLoader = Ext.extend(Ext.util.Observable, { this.config = config || {}; this.mapId = mapId; - this.auth = auth ? decodeURI(auth): null; + + this.authHeader= this.getAuth(); this.fScreen = fScreen; this.templateId = templateId; this.geoStoreBaseURL = config != null && config.geoStoreBaseURL ? config.geoStoreBaseURL : ('http://' + window.location.host + '/geostore/rest/'); @@ -123,9 +124,16 @@ var GeoExplorerLoader = Ext.extend(Ext.util.Observable, { var config = this.config; var me = this; var url = this.geoStoreBaseURL + 'extjs/search/category/MAPSTORECONFIG'; - if(url.indexOf("http") == 0){ + /*if(url.indexOf("http") == 0){ url = this.proxy + url; + }*/ + var h = { + 'Accept': 'application/json' + }; + if(this.authHeader){ + h['Authorization'] = this.authHeader; } + url = OpenLayers.Request.makeSameOrigin(url, this.proxy); this.adminConfigStore = new Ext.data.JsonStore({ root: 'results', autoLoad: false, @@ -141,10 +149,7 @@ var GeoExplorerLoader = Ext.extend(Ext.util.Observable, { restful: true, method : 'GET', disableCaching: true, - headers:{ - 'Accept': 'application/json', - 'Authorization' : this.auth - }, + headers: h, failure: function (response) { // no custom configs available me.fireEvent("configfinished", config); @@ -197,17 +202,30 @@ var GeoExplorerLoader = Ext.extend(Ext.util.Observable, { * Load a configuration from an url */ loadData: function (url, dataId, eventListener){ - var url = (url ? url: this.geoStoreBaseURL + "data/" + dataId); - if(url.indexOf("http") == 0){ - url = this.proxy + url; + var url = (url ? url : this.geoStoreBaseURL + "data/" + dataId); + url = OpenLayers.Request.makeSameOrigin(url, this.proxy); + var h = { + 'Accept': 'application/json' + }; + if(this.authHeader){ + h['Authorization'] = this.authHeader; } Ext.Ajax.request({ method: 'GET', + headers:h, scope: this, url: url, success: function(response, opts){ try{ var loadedConfig = Ext.util.JSON.decode(response.responseText); + // //////////////////////////////////////////////////////////////////////////// + // TODO: Fix this in GeoStore also for maps configuration. At the first time + // GeoStore incapsulates the JSON in a "data" object. This is a temporary fix + // useful for template configurations. + // //////////////////////////////////////////////////////////////////////////// + if(loadedConfig.data){ + loadedConfig = Ext.util.JSON.decode(loadedConfig.data); + } this.fireEvent(eventListener, loadedConfig); }catch(e){ console.error("Error getting config"); @@ -268,6 +286,28 @@ var GeoExplorerLoader = Ext.extend(Ext.util.Observable, { if(pendingConfig == 0){ this.fireEvent("configfinished", config); } + }, + /** + * Retrieves auth from (in this order) + * * the session storage (if enabled userDetails, see ManagerViewPort.js class of mapmanager) + * * the parent window (For usage in manager) + * We should imagine to get the auth from other contexts. + */ + getAuth: function(){ + var auth; + + //get from the session storage + var existingUserDetails = sessionStorage["userDetails"]; + if(existingUserDetails){ + this.userDetails = Ext.util.JSON.decode(sessionStorage["userDetails"]); + auth = this.userDetails.authHeader; + } else if(window.parent && window.parent.window && window.parent.window.manager && window.parent.window.manager.auth){ + //get from the parent + auth = window.parent.window.manager.auth; + return auth; + } + + return auth; } }); diff --git a/mapcomposer/app/static/externals/gxp/src/script/data/WFSFeatureStore.js b/mapcomposer/app/static/externals/gxp/src/script/data/WFSFeatureStore.js index c39890b71..e19e491af 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/data/WFSFeatureStore.js +++ b/mapcomposer/app/static/externals/gxp/src/script/data/WFSFeatureStore.js @@ -55,7 +55,8 @@ gxp.data.WFSFeatureStore = Ext.extend(GeoExt.data.FeatureStore, { schema: config.schema, filter: config.ogcFilter, maxFeatures: config.maxFeatures, - viewparams: config.viewparams + viewparams: config.viewparams, + sortBy: config.sortBy }, config.proxy)); } if(!config.writer) { diff --git a/mapcomposer/app/static/externals/gxp/src/script/data/WFSProtocolProxy.js b/mapcomposer/app/static/externals/gxp/src/script/data/WFSProtocolProxy.js index 9ca2e8bc7..342205ecb 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/data/WFSProtocolProxy.js +++ b/mapcomposer/app/static/externals/gxp/src/script/data/WFSProtocolProxy.js @@ -70,7 +70,8 @@ gxp.data.WFSProtocolProxy = Ext.extend(GeoExt.data.ProtocolProxy, { schema: config.schema, filter: config.filter, maxFeatures: config.maxFeatures, - viewparams: config.viewparams + viewparams: config.viewparams, + sortBy: config.sortBy }, config.protocol)); } @@ -99,6 +100,16 @@ gxp.data.WFSProtocolProxy = Ext.extend(GeoExt.data.ProtocolProxy, { // remove the xaction param tagged on because we're using a single url // for all actions delete params.xaction; + var sort = params.sort; + var dir = params.dir; + params.sort = undefined; + params.dir = undefined; + if(sort){ + arg.sortBy = { + property: sort, + order: dir + } + } if (action === Ext.data.Api.actions.read) { this.load(params, reader, callback, scope, arg); diff --git a/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesProxy.js b/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesProxy.js index c6073abe6..edcddb94c 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesProxy.js +++ b/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesProxy.js @@ -76,13 +76,22 @@ gxp.data.WPSUniqueValuesProxy = Ext.extend(Ext.data.DataProxy, { if (!params.inputs) { return; } + var me = this; var execOptions = { inputs: {}, outputs: params.output, type: "raw", success: function(outputs) { - var records = reader.read({responseText: outputs}); - cb.call(scope, records, arg); + if (outputs.trim().charAt(0) === "<") { + // response contains XML data: exception occurred! + var wpsExecuteFormat = new OpenLayers.Format.WPSExecute(); + var errorResponse = wpsExecuteFormat.read(outputs).executeResponse.status.exception; + + me.fireEvent("exception", me, 'remote', action, params, errorResponse, arg); + } else { + var records = reader.read({responseText: outputs}); + cb.call(scope, records, arg); + } } }; diff --git a/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesReader.js b/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesReader.js index c22aa6c5f..382dc5a55 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesReader.js +++ b/mapcomposer/app/static/externals/gxp/src/script/data/WPSUniqueValuesReader.js @@ -51,7 +51,58 @@ gxp.data.WPSUniqueValuesReader = Ext.extend(Ext.data.JsonReader, { */ readResponse : function(action, response) { this.read(response); - } + }, + + /* + * Overridden version of private method extractData in Ext.data.DataReader. + * This version skips null values in the data root (no row will be + * created for these objects). + */ + /** + * returns extracted, type-cast rows of data. Iterates to call #extractValues for each row + * @param {Object[]/Object} data-root from server response + * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record + * @private + */ + extractData : function(root, returnRecords) { + // A bit ugly this, too bad the Record's raw data couldn't be saved in a common property named "raw" or something. + var rawName = (this instanceof Ext.data.JsonReader) ? 'json' : 'node'; + + var rs = []; + // Had to add Check for XmlReader, #isData returns true if root is an Xml-object. Want to check in order to re-factor + // #extractData into DataReader base, since the implementations are almost identical for JsonReader, XmlReader + if (this.isData(root) && !(this instanceof Ext.data.XmlReader)) { + root = [root]; + } + var f = this.recordType.prototype.fields, + fi = f.items, + fl = f.length, + rs = []; + if (returnRecords === true) { + var Record = this.recordType; + for (var i = 0; i < root.length; i++) { + var n = root[i]; + // skip null values + if (n !== null) { + var record = new Record(this.extractValues(n, fi, fl), this.getId(n)); + record[rawName] = n; // <-- There's implementation of ugly bit, setting the raw record-data. + rs.push(record); + } + } + } + else { + for (var i = 0; i < root.length; i++) { + // skip null values + if (root[i] !== null) { + var data = this.extractValues(root[i], fi, fl); + data[this.meta.idProperty] = this.getId(root[i]); + rs.push(data); + } + } + } + return rs; + } + }); Ext.reg('gxp_wpsuniquereader', gxp.data.WPSUniqueValuesReader); \ No newline at end of file diff --git a/mapcomposer/app/static/externals/gxp/src/script/loader.js b/mapcomposer/app/static/externals/gxp/src/script/loader.js index 623ae59f1..5967881b2 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/loader.js +++ b/mapcomposer/app/static/externals/gxp/src/script/loader.js @@ -13,7 +13,9 @@ "widgets/FilterBuilder.js", "widgets/QueryPanel.js", "widgets/StylePropertiesDialog.js", + "widgets/LayerPanel.js", "widgets/WMSLayerPanel.js", + "widgets/WMTSLayerPanel.js", "widgets/WMSStylesDialog.js", "widgets/NewSourceWindow.js", "widgets/FillSymbolizer.js", @@ -47,6 +49,7 @@ "plugins/GoogleSource.js", "plugins/OLSource.js", "plugins/MapQuestSource.js", + "plugins/WMTSSource.js", "plugins/StyleWriter.js", "plugins/GeoServerStyleWriter.js", "plugins/Tool.js", diff --git a/mapcomposer/app/static/externals/gxp/src/script/locale/ca.js b/mapcomposer/app/static/externals/gxp/src/script/locale/ca.js index eb3667a52..7265abd02 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/locale/ca.js +++ b/mapcomposer/app/static/externals/gxp/src/script/locale/ca.js @@ -33,7 +33,7 @@ GeoExt.Lang.add("ca", { }, "gxp.plugins.FeatureGrid.prototype": { - displayFeatureText: "Mostra al mapa", + displayFeatureText: "Mostra", firstPageTip: "Primera pàgina", previousPageTip: "Pàgina anterior", zoomPageExtentTip: "Ajusta vista a l'extensió de la pàgina", diff --git a/mapcomposer/app/static/externals/gxp/src/script/locale/en.js b/mapcomposer/app/static/externals/gxp/src/script/locale/en.js index e3936d2cb..d83add74c 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/locale/en.js +++ b/mapcomposer/app/static/externals/gxp/src/script/locale/en.js @@ -33,7 +33,7 @@ GeoExt.Lang.add("en", { }, "gxp.plugins.FeatureGrid.prototype": { - displayFeatureText: "Display on map", + displayFeatureText: "Display", firstPageTip: "First page", previousPageTip: "Previous page", zoomPageExtentTip: "Zoom to page extent", diff --git a/mapcomposer/app/static/externals/gxp/src/script/locale/es.js b/mapcomposer/app/static/externals/gxp/src/script/locale/es.js index f34e98814..7bdeca891 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/locale/es.js +++ b/mapcomposer/app/static/externals/gxp/src/script/locale/es.js @@ -33,7 +33,7 @@ GeoExt.Lang.add("es", { }, "gxp.plugins.FeatureGrid.prototype": { - displayFeatureText: "Mostrar en el mapa", + displayFeatureText: "Mostrar", firstPageTip: "Primera página", previousPageTip: "Página anterior", zoomPageExtentTip: "Zoom a la extensión de la página", diff --git a/mapcomposer/app/static/externals/gxp/src/script/locale/fr.js b/mapcomposer/app/static/externals/gxp/src/script/locale/fr.js index 7bfff78c4..16cc53a8e 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/locale/fr.js +++ b/mapcomposer/app/static/externals/gxp/src/script/locale/fr.js @@ -25,7 +25,7 @@ GeoExt.Lang.add("fr", { }, "gxp.plugins.FeatureGrid.prototype": { - displayFeatureText: "Afficher sur la carte", + displayFeatureText: "Afficher", firstPageTip: "Première page", previousPageTip: "Page précédente", zoomPageExtentTip: "Zoom sur la page", diff --git a/mapcomposer/app/static/externals/gxp/src/script/locale/nl.js b/mapcomposer/app/static/externals/gxp/src/script/locale/nl.js index f35e8827a..cb1e132db 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/locale/nl.js +++ b/mapcomposer/app/static/externals/gxp/src/script/locale/nl.js @@ -24,7 +24,7 @@ GeoExt.Lang.add("nl", { }, "gxp.plugins.FeatureGrid.prototype": { - displayFeatureText: "Toon op kaart", + displayFeatureText: "Toon", firstPageTip: "Eerste pagina", previousPageTip: "Vorige pagina", zoomPageExtentTip: "Zoom naar de uitsnede van de pagina", diff --git a/mapcomposer/app/static/externals/gxp/src/script/plugins/AddGroup.js b/mapcomposer/app/static/externals/gxp/src/script/plugins/AddGroup.js index 1f4213bd2..bd088ed07 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/plugins/AddGroup.js +++ b/mapcomposer/app/static/externals/gxp/src/script/plugins/AddGroup.js @@ -20,6 +20,7 @@ /** * @requires plugins/Tool.js + * @requires widgets/tree/LayerNodeUI.js */ /** api: (define) @@ -139,7 +140,7 @@ gxp.plugins.AddGroup = Ext.extend(gxp.plugins.Tool, { if(textField.isDirty() && textField.isValid()){ var group = textField.getValue(); - var LayerNodeUI = Ext.extend(GeoExt.tree.LayerNodeUI, + var LayerNodeUI = Ext.extend(gxp.tree.LayerNodeUI, new GeoExt.tree.TreeNodeUIEventMixin()); var locIndex= tree.localIndexs[GeoExt.Lang.locale]; diff --git a/mapcomposer/app/static/externals/gxp/src/script/plugins/AddLayer.js b/mapcomposer/app/static/externals/gxp/src/script/plugins/AddLayer.js index 518bba9fa..ee910ffa1 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/plugins/AddLayer.js +++ b/mapcomposer/app/static/externals/gxp/src/script/plugins/AddLayer.js @@ -146,74 +146,86 @@ gxp.plugins.AddLayer = Ext.extend(gxp.plugins.Tool, { this.on({ scope: this, - 'ready' : function(record, report){ - if(this.showReport === true && this.directAddLayer === false - && this.disableAllNotifications === false){ + 'ready' : function(records){ + if((this.showReport == "always" || this.showReport == "errors") && + this.directAddLayer === false && this.disableAllNotifications === false){ - if(report){ - exceptionReport.push(report); - } - - if(reportWin){ - reportWin.hide(); - reportWin.destroy(); - } - - var html = "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - //"" + - "" + - "" + - ""; - - var urls = []; - for(var i=0; i" + exceptionReport[i].name + "" + - "" + - "" + - //"" + - "" - - html += row; + + if(reportWin){ + reportWin.hide(); + reportWin.destroy(); } - } - html += "" + - "
Resource NameResource TypeNoteURL
" + (exceptionReport[i].type == "layer" ? "WMS Layer" : "WMS Service" + (exceptionReport[i].title ? " :" + exceptionReport[i].title : "")) + "" + (!exceptionReport[i].msg ? "" : "Error in remote service. Show more") + "" + exceptionReport[i].url + "
" + - "" + - ""; - - reportWin = new Ext.Window({ - title: "List of requested resources", - html: html, - frame: true, - modal: true, - autoHeigth: true, - autoWidth: true, - maxHeigth: 500, - maxWidth: 600 - }); + var html = "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + //"" + + "" + + "" + + ""; + + var urls = []; + for(var i=0; i" + exceptionReport[i].name + "" + + "" + + "" + + //"" + + "" + + html += row; + } + } - reportWin.show(); + html += "" + + "
Resource NameResource TypeNoteURL
" + (exceptionReport[i].type == "layer" ? "WMS Layer" : "WMS Service" + (exceptionReport[i].title ? " :" + exceptionReport[i].title : "")) + "" + (!exceptionReport[i].msg ? "" : "Error in remote service. Show more") + "" + exceptionReport[i].url + "
" + + "" + + ""; + + if(exceptionReport.length > 0){ + reportWin = new Ext.Window({ + title: "List of requested resources", + html: html, + frame: true, + modal: true, + autoHeigth: true, + autoWidth: true, + maxHeigth: 500, + maxWidth: 600 + }); + + reportWin.show(); + } + } } } }); @@ -241,11 +253,14 @@ gxp.plugins.AddLayer = Ext.extend(gxp.plugins.Tool, { var msLayerUUID = options.msLayerUUID; var gnLangStr = options.gnLangStr; var customParams = options.customParams; + var msGroupName = options.msGroupName; var props = { name: msLayerName, title: msLayerTitle, source: source.id + // Currently the group setting is not supported for dynamic layer additions + /*,group: msGroupName*/ }; if(customParams){ @@ -263,7 +278,7 @@ gxp.plugins.AddLayer = Ext.extend(gxp.plugins.Tool, { var record = source.createLayerRecord(props); - var report; + //var report; if (record) { var layerStore = this.target.mapPanel.layers; layerStore.add([record]); @@ -303,40 +318,46 @@ gxp.plugins.AddLayer = Ext.extend(gxp.plugins.Tool, { map.zoomToExtent(extent, true); - report = { + var report = { name: msLayerName, title: msLayerTitle, + group: options.msGroupName, type: "layer", url: source.url, id: source.id }; + this.records.push({record: record, report: report}); }else{ - report = { + var report = { name: msLayerName, title: source.title, + group: options.msGroupName, type: "service", url: source.url, id: source.id }; - + + this.records.push({record: undefined, report: report}); + // // Show the capabilities grid if any layers was not found // this.showCapGrid(source.id); } - if(this.useEvents) - this.fireEvent('ready', record, report); + if(this.useEvents && this.records.length == this.resourcesSize){ + this.fireEvent('ready', this.records); + } }, showCapGrid: function(sourceId){ - var addLayerAction = this.target.tools["addlayers"]; - // // Show the capabilities grid // if(this.showCapabilitiesGrid === true && this.disableAllNotifications === false){ + var addLayerAction = this.target.tools["addlayers"]; + addLayerAction.showCapabilitiesGrid(); // @@ -388,54 +409,81 @@ gxp.plugins.AddLayer = Ext.extend(gxp.plugins.Tool, { /** * api: method[addLayer] */ - addLayer: function(options){ + addLayer: function(resources){ + // + // We check the type of the argument for retrocompatibility + // + this.records = []; + if(resources instanceof Array){ + this.resourcesSize = resources.length; + + for(var i=0; i 0){ + this.sourceComboBox.onSelect(sources.getAt(0), 0); + }else{ + this.sourceComboBox.clearValue(); + var capGridStore = capGridPanel.getStore(); + capGridStore.removeAll(); + } + + this.target.modified = true; + }else{ + Ext.Msg.show({ + title: this.deleteSourceDialogTitle, + msg: this.deleteSourceText, + buttons: Ext.Msg.OK, + icon: Ext.MessageBox.WARNING, + scope: this + }); + } + } + } + + Ext.Msg.show({ + title: this.deleteSourceDialogTitle, + msg: this.deleteSourceDialogMsg, + buttons: Ext.Msg.OKCANCEL, + fn: removeFunction, + icon: Ext.MessageBox.QUESTION, + scope: this + }); } })); } + for(var count = 0, l = this.additionalSources.length; count < l; count++) { + this.availableSources.push(this.additionalSources[count]); + } + var newSourceWindow = new gxp.NewSourceWindow({ modal: true, + defaultVendor: this.defaultVendor, + availableSources: this.availableSources, + availableVendorOptions: this.availableVendorOptions, + availableFormats: this.availableFormats, listeners: { - "server-added": function(url) { + scope: this, + "server-added": function(url, type, sourceCfg) { newSourceWindow.setLoading(); + var sourceCfg; + for(var count = 0, l = this.availableSources.length; count < l; count++) { + var currentSource = this.availableSources[count]; + if(currentSource.name === type) { + //sourceCfg = Ext.apply({url: url}, currentSource.config); + sourceCfg = Ext.apply(currentSource.config, sourceCfg); + } + } + this.target.addLayerSource({ - config: {url: url}, // assumes default of gx_wmssource + config: sourceCfg, callback: function(id) { - // add to combo and select - var record = new sources.recordType({ - id: id, - title: this.target.layerSources[id].title || this.untitledText - }); - sources.insert(0, [record]); - this.sourceComboBox.onSelect(record, 0); + // //////////////////////////////////////////// + // Add to combo and select: check before if + // record already exists (the edit case) + // //////////////////////////////////////////// + var idx = sources.find("id", id); + if(idx < 0){ + var record = new sources.recordType({ + id: id, + title: this.target.layerSources[id].title || this.untitledText + }); + sources.insert(0, [record]); + this.sourceComboBox.onSelect(record, 0); + } + + this.target.modified = true; newSourceWindow.hide(); }, fallback: function(source, msg) { newSourceWindow.setError( - new Ext.Template(this.addLayerSourceErrorText).apply({msg: msg}) + new Ext.Template(this.addLayerSourceErrorText).apply({type: type, msg: msg}) ); }, scope: this }); }, - scope: this + "server-modified": function(url, type, sourceCfg, sourceId) { + newSourceWindow.setLoading(); + + var sourceRecordIdx = sources.find("id", sourceId); + if(sourceRecordIdx >= 0){ + // The record of the combo box store + var sourceRecord = sources.getAt(sourceRecordIdx); + var title = sourceRecord.get("title"); + if(sourceCfg.title != title){ + sourceRecord.set("title", sourceCfg.title); + //sourceRecord.commit(); + } + + // The record of the real used sources store + var source = this.target.layerSources[sourceId]; + + Ext.apply(source.initialConfig, sourceCfg); + Ext.apply(source, sourceCfg); + + if(source.store.baseParams.VERSION != sourceCfg.version){ + source.store.baseParams.VERSION = sourceCfg.version; + + // ////////////////////////////// + // Check also the authparam + // TODO: More checks on this + // ////////////////////////////// + if(source.authParam != sourceCfg.authParam){ + if(source.authParam){ + delete source.store.baseParams[source.authParam]; + } + + source.store.baseParams[sourceCfg.authParam] = source.getAuthParam(); + } + } + + source.on("ready", function(){ + newSourceWindow.hide(); + }); + source.on("failure", function(){ + newSourceWindow.hide(); + }); + + source.store.reload(); + this.target.modified = true; + } + } } }); @@ -394,6 +669,7 @@ gxp.plugins.AddLayers = Ext.extend(gxp.plugins.Tool, { layout: "fit", items: [capGridPanel] }; + if (this.instructionsText) { items.items.push({ xtype: "box", @@ -470,7 +746,7 @@ gxp.plugins.AddLayers = Ext.extend(gxp.plugins.Tool, { closeAction: "hide", layout: "fit", height: 300, - width: 570, + width: 580, modal: true, items: items, tbar: capGridToolbar, @@ -480,7 +756,9 @@ gxp.plugins.AddLayers = Ext.extend(gxp.plugins.Tool, { capGridPanel.getSelectionModel().clearSelections(); }, show: function(win) { - this.setSelectedSource(this.target.layerSources[data[idx][0]]); + if(!this.selectedSource){ + this.setSelectedSource(this.target.layerSources[data[idx][0]]); + } }, scope: this } diff --git a/mapcomposer/app/static/externals/gxp/src/script/plugins/AdvancedSnappingAgent.js b/mapcomposer/app/static/externals/gxp/src/script/plugins/AdvancedSnappingAgent.js new file mode 100644 index 000000000..4246989b6 --- /dev/null +++ b/mapcomposer/app/static/externals/gxp/src/script/plugins/AdvancedSnappingAgent.js @@ -0,0 +1,231 @@ +/** + * Copyright (C) 2007 - 2012 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * GPLv3 + Classpath exception + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @requires plugins/Tool.js + */ + +/** api: (define) + * module = gxp.plugins + * class = AdvancedSnappingAgent + */ + +/** api: (extends) + * plugins/Tool.js + */ +Ext.namespace("gxp.plugins"); + +/** api: constructor + * .. class:: AdvancedSnappingAgent(config) + * + * Plugin for managing snapping while editing. + * + * Author: Tobia Di Pisa at tobia.dipisa@geo-solutions.it + */ +gxp.plugins.AdvancedSnappingAgent = Ext.extend(gxp.plugins.Tool, { + + /** api: ptype = gxp_advancedsnappingagent */ + ptype: "gxp_advancedsnappingagent", + + iconCls: "snapping-button", + + tooltipText: "Enable/Disable Snapping", + + /** api: config[controlOptions] + * ``Object`` Options for the ``OpenLayers.Control.Snapping`` used with + * this tool. + */ + + /** private: method[init] + */ + init: function(target) { + gxp.plugins.AdvancedSnappingAgent.superclass.init.apply(this, arguments); + this.snappingTargets = []; + this.controls = []; + this.setSnappingTargets(this.targets); + }, + + /** private: method[setSnappingTargets] + */ + setSnappingTargets: function(targets) { + // clear any previous targets + this.snappingTargets.length = 0; + // configure any given targets + if (targets) { + for (var i=0, ii=targets.length; i"].concat(!this.alwaysDisplayOnMap ? [{ + }, { + xtype: 'tbspacer', + width: 10 + }, + this.displayItem + ); + + var bbar = (featureManager.paging ? [toolbarElements] : []).concat(["->"].concat(!this.alwaysDisplayOnMap ? [{ text: this.displayFeatureText, id: "showButton", iconCls: "gxp-icon-addtomap", @@ -400,6 +522,7 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { autoScroll: true, title: this.title, bbar: bbar, + featureMaxZoomLevel:(this.featureMaxZoomLevel)?this.featureMaxZoomLevel:null, listeners: { "added": function(cmp, ownerCt) { var onClear = OpenLayers.Function.bind(function() { @@ -488,6 +611,10 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { featureManager.showLayer(this.id, this.displayMode); } + // ///////////////////////////////////// + // FeatureManager events's listeners + // ///////////////////////////////////// + var me = this; featureManager.paging && featureManager.on("setpage", function(mgr, condition, callback, scope, pageIndex, numPages) { var paging = (mgr.page && (mgr.page.numFeatures > 0)) || numPages > 1; featureGrid.zoomToPageButton.setDisabled(!paging); @@ -497,17 +624,49 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { var next = (paging && (pageIndex !== numPages-1)); featureGrid.lastPageButton.setDisabled(!next); featureGrid.nextPageButton.setDisabled(!next); + + if(featureManager.pagingType == 1){ + featureGrid.currentPage.enable(); + featureGrid.currentPage.setValue(featureManager.pageIndex + 1); + featureGrid.numberOfPagesLabel.setText(featureManager.numPages); + + if(me.showNumberOfRecords === true){ + featureGrid.totalRecords.setText(featureManager.numberOfFeatures + "}"); + } + } }, this); - featureManager.on("layerchange", function(mgr, rec, schema) { + featureManager.on("layerchange", function(mgr, rec, schema) { + if(featureManager.pagingType == 1){ + featureGrid.currentPage.disable(); + featureGrid.currentPage.setValue("0"); + featureGrid.numberOfPagesLabel.setText("0"); + + if(me.showNumberOfRecords === true){ + featureGrid.totalRecords.setText("0}"); + } + } + //TODO use schema instead of store to configure the fields - var ignoreFields = ["feature", "state", "fid"]; + var ignoreFields = this.ignoreFields; schema && schema.each(function(r) { r.get("type").indexOf("gml:") == 0 && ignoreFields.push(r.get("name")); }); featureGrid.ignoreFields = ignoreFields; featureGrid.setStore(featureManager.featureStore, schema); }, this); + + featureManager.on("clearfeatures", function(mgr, rec, schema) { + if(featureManager.pagingType == 1){ + featureGrid.currentPage.disable(); + featureGrid.currentPage.setValue("0"); + featureGrid.numberOfPagesLabel.setText("0"); + + if(me.showNumberOfRecords === true){ + featureGrid.totalRecords.setText("0}"); + } + } + }, this); return featureGrid; }, @@ -592,7 +751,7 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { handler: function() { if(this.exportWindow.form.getForm().isValid()){ var format = this.exportWindow.form.getForm().getValues().format; - this.doExport(false, format); + this.doExport(true, format); }else{ Ext.Msg.show({ title: this.noFormatTitleText, @@ -686,9 +845,22 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { var protocol = grid.getStore().proxy.protocol; var allPage = {}; + + + allPage.extent = featureManager.getPagingExtent("getMaxExtent"); - var filter = featureManager.setPageFilter(single ? featureManager.page : allPage); + //IF WFS_PAGING create filter with fid id + if (featureManager.pagingType === gxp.plugins.FeatureManager.WFS_PAGING) { + var fidsA=[]; + featureManager.featureStore.each(function(r){ + fidsA.push(r.get("fid")); + }); + var filter= new OpenLayers.Filter.FeatureId({fids: fidsA}); + } + else{ + var filter = featureManager.setPageFilter(single ? featureManager.page : allPage); + } var node = new OpenLayers.Format.Filter({ version: protocol.version, @@ -698,50 +870,61 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { this.xml = new OpenLayers.Format.XML().write(node); var colModel = grid.getColumnModel(); - - var numColumns = colModel.getColumnCount(true); + //get all columns and see if they are visible + var numColumns = colModel.getColumnCount(false); var propertyName = []; - for (var i=0; i
" + failedExport + "

" + "Error: " + serverError.exceptions[0].text, @@ -752,7 +935,7 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { catch(err) { // submit filter in a standard form (before check) - this.doDownloadPost(this.url, this.xml); + this.doDownloadPost(this.url, this.xml,outputFormat); } }else{ @@ -768,13 +951,82 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { }); }else{ // submit filter in a standard form to skip double check - this.doDownloadPost(this.url, this.xml); + this.doDownloadPost(this.url, this.xml,outputFormat); } }, /** api: method[doDownloadPost] + * create a dummy iframe and a form. Submit the form */ + + doDownloadPost: function(url, data,outputFormat){ + // + //delete other iframes appended + // + if(document.getElementById(this.downloadFormId)) { + document.body.removeChild(document.getElementById(this.downloadFormId)); + } + if(document.getElementById(this.downloadIframeId)) { + document.body.removeChild(document.getElementById(this.downloadIframeId)); + } + // create iframe + var iframe = document.createElement("iframe"); + iframe.setAttribute("style","visiblity:hidden;width:0px;height:0px;"); + this.downloadIframeId = Ext.id(); + iframe.setAttribute("id",this.downloadIframeId); + iframe.setAttribute("name",this.downloadIframeId); + document.body.appendChild(iframe); + iframe.onload = function(){ + if(!iframe.contentWindow) return; + + var error =""; + var body = iframe.contentWindow.document.getElementsByTagName('body')[0]; + var content =""; + if (body.textContent){ + content = body.textContent; + }else{ + content = body.innerText; + } + try{ + var serverError = Ext.util.JSON.decode(content); + error = serverError.exceptions[0].text + }catch(err){ + error = body.innerHTML || content; + } + Ext.Msg.show({ + title: me.invalidParameterValueErrorText, + msg: "outputFormat: " + outputFormat + "

" + + "

" + + "Error: " + error, + buttons: Ext.Msg.OK, + icon: Ext.MessageBox.ERROR + }); + } + var me = this; + + // submit form with enctype = application/xml + var form = document.createElement("form"); + this.downloadFormId = Ext.id(); + form.setAttribute("id", this.downloadFormId); + form.setAttribute("method", "POST"); + //this is to skip cross domain exception notifying the response body + var urlregex =/^https?:\/\//i; + //if absoulte url and do not contain the local host + var iframeURL = (!urlregex.test(url) || url.indexOf(location.host)>0) ? url : proxy + encodeURIComponent(url); + form.setAttribute("action", iframeURL ); + form.setAttribute("target",this.downloadIframeId); + + var hiddenField = document.createElement("input"); + hiddenField.setAttribute("name", "filter"); + hiddenField.value= data; + form.appendChild(hiddenField); + document.body.appendChild(form); + form.submit(); + } + /** api: method[doDownloadPost] + */ +/* doDownloadPost: function(url, data){ // //delete other iframes appended @@ -794,7 +1046,7 @@ gxp.plugins.FeatureGrid = Ext.extend(gxp.plugins.ClickableFeatures, { document.body.appendChild(form); form.submit(); } - + */ }); Ext.preg(gxp.plugins.FeatureGrid.prototype.ptype, gxp.plugins.FeatureGrid); diff --git a/mapcomposer/app/static/externals/gxp/src/script/plugins/FeatureManager.js b/mapcomposer/app/static/externals/gxp/src/script/plugins/FeatureManager.js index e3a3f54f4..0e88960e6 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/plugins/FeatureManager.js +++ b/mapcomposer/app/static/externals/gxp/src/script/plugins/FeatureManager.js @@ -95,6 +95,17 @@ gxp.plugins.FeatureManager = Ext.extend(gxp.plugins.Tool, { * each with a valid symbolizer object for OpenLayers. Will be used to * render features. */ + /** api: config[selectedSymbolizer] + * ``Object`` An object with "Point", "Line" and "Polygon" properties, + * each with a valid symbolizer object for OpenLayers. Will be used to + * render selected features. + */ + /** api: config[vecLayerOptions] + * ``Object`` Optional object with non-default properties to set on the + * OpenLayers.Layer.Vector used to render features + * i.e. to have zindex use "vecLayerOptions": {"rendererOptions":{"zIndexing": true}} + * in composer configuration + */ /** api: config[format] * ``String`` Optional response format to use for WFS GetFeature requests. @@ -346,20 +357,20 @@ gxp.plugins.FeatureManager = Ext.extend(gxp.plugins.Tool, { } })] }), - "selected": new OpenLayers.Style(null, { - rules: [new OpenLayers.Rule({symbolizer: {display: "none"}})] + "selected": new OpenLayers.Style(null, { + rules: [new OpenLayers.Rule({symbolizer: this.initialConfig.selectedSymbolizer || {display: "none"}})] }) }; - this.featureLayer = new OpenLayers.Layer.Vector(this.id, { + this.featureLayer = new OpenLayers.Layer.Vector(this.id, Ext.apply({ displayInLayerSwitcher: false, visibility: false, styleMap: new OpenLayers.StyleMap({ - "select": OpenLayers.Util.extend({display: ""}, + "select": this.initialConfig.selectedSymbolizer ? this.style["selected"] : OpenLayers.Util.extend({display: ""}, OpenLayers.Feature.Vector.style["select"]), "vertex": this.style["all"] }, {extendDefault: false}) - }); + },this.vecLayerOptions||{})); this.target.on({ ready: function() { @@ -705,6 +716,8 @@ gxp.plugins.FeatureManager = Ext.extend(gxp.plugins.Tool, { maxFeatures: this.maxFeatures, layer: this.featureLayer, ogcFilter: filter, + sortBy: this.sortBy, + remoteSort:this.remoteSort, autoLoad: autoLoad, autoSave: false, listeners: { diff --git a/mapcomposer/app/static/externals/gxp/src/script/plugins/GeoStoreAccount.js b/mapcomposer/app/static/externals/gxp/src/script/plugins/GeoStoreAccount.js index a02776a79..6d4311b43 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/plugins/GeoStoreAccount.js +++ b/mapcomposer/app/static/externals/gxp/src/script/plugins/GeoStoreAccount.js @@ -42,7 +42,7 @@ Ext.namespace("gxp.plugins"); */ gxp.plugins.GeoStoreAccount = Ext.extend(gxp.plugins.Tool, { ptype: "gxp_geostore_account", - displayAttributes:['Name','Surname','email'], + //i18n usernameLabel:'User Name', changePassword:'Change Password', textPassword: 'Password', @@ -51,10 +51,34 @@ gxp.plugins.GeoStoreAccount = Ext.extend(gxp.plugins.Tool, { textCancel: 'Cancel', textPasswordChangeSuccess: 'Password Changed', textErrorPasswordChange: 'Error Changing Password', - scrollable:true, + //config geoStoreBase: null, - auth: null, - notAllowedAttributes:['UUID'], + authHeader: null, + scrollable:true, + /** api: config[displayPanels] + * ``boolean`` use panels to see attributes instead of template + */ + displayPanels:true, + + /** api: config[additionalControls] + * ``Array``buttons to place in the right panel + */ + additionalControls:null, + + /** api: config[controlsWidth] + * ``integer``width of the `dditionalControls` + */ + controlsWidth:200, + + /** api: config[hideAttributes] + * ``Array`` user attributes to hide in the my account tab + */ + hideAttributes:['UUID'], + + /** api: config[userTemplate] + * ``Array`` XTemplate to display user page. + * user is the root element. groups.group and attribute are arrays to iterate + */ userTemplate: [ '