diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..413eaa59d --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,49 @@ + + +__How often can you reproduce it?__ + +- [ ] Always +- [ ] Sometimes +- [ ] Rarely +- [ ] Unable +- [ ] I didn’t try + + + +__Description:__ + + + + + +__Steps to reproduce:__ + +1. Include a JS Bin (or equivalent) link if possible. [You can use this as a starting point](http://jsbin.com/guresequba/edit?js,output) +2. Detail the exact steps taken to produce the problem +3. Include a gif if possible; you can use LICEcap to make a gif: http://www.cockos.com/licecap/ +4. Check the browser console for errors (Use F12 to access the console) + + + +__Expected results:__ + + + + + +__Actual results:__ + + + + + +__Environment:__ + +| Software | Version +| ------------------ | ------- +| CMV Version | +| Browser | +| Operating system | diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..3fd7ca439 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ + + +# Description + + +# Use case + +```javascript +// how the code can be used +``` + +# Checklist + + + - [ ] `grunt lint` produces no error messages diff --git a/Gruntfile.js b/Gruntfile.js index 55c15596a..7dd557a52 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,14 +4,15 @@ module.exports = function (grunt) { // middleware for grunt.connect var middleware = function (connect, options, middlewares) { // inject a custom middleware into the array of default middlewares for proxy page + var bodyParser = require('body-parser'); var proxypage = require('proxypage'); var proxyRe = /\/proxy\/proxy.ashx/i; var enableCORS = function (req, res, next) { - res.setHeader('Access-Control-Allow-Origin', req.headers.origin); + res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*'); res.setHeader('Access-Control-Allow-Credentials', true); res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,PUT,PATCH,POST,DELETE'); - res.setHeader('Access-Control-Allow-Headers', req.headers['access-control-request-headers']); + res.setHeader('Access-Control-Allow-Headers', req.headers['access-control-request-headers'] || 'Origin, X-Requested-With, Content-Type, Accept'); return next(); }; @@ -24,8 +25,8 @@ module.exports = function (grunt) { middlewares.unshift(proxyMiddleware); middlewares.unshift(enableCORS); - middlewares.unshift(connect.json()); //body parser, see https://github.com/senchalabs/connect/wiki/Connect-3.0 - middlewares.unshift(connect.urlencoded()); //body parser + middlewares.unshift(bodyParser.json()); //body parser, see https://github.com/senchalabs/connect/wiki/Connect-3.0 + middlewares.unshift(bodyParser.urlencoded({extended: true})); //body parser return middlewares; }; @@ -124,6 +125,8 @@ module.exports = function (grunt) { port: 3000, base: 'viewer', hostname: '*', + protocol: 'https', + keepalive: true, middleware: middleware } }, @@ -132,16 +135,18 @@ module.exports = function (grunt) { port: 3001, base: 'dist/viewer', hostname: '*', + protocol: 'https', + keepalive: true, middleware: middleware } } }, open: { 'dev_browser': { - path: 'http://localhost:3000/index.html' + path: 'https://localhost:3000/index.html' }, 'build_browser': { - path: 'http://localhost:3001/index.html' + path: 'https://localhost:3001/index.html' } }, compress: { diff --git a/README.md b/README.md index efb59a017..7404eb130 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # CMV - The Configurable Map Viewer -[![Read The Docs](https://img.shields.io/badge/docs-1.3.4-brightgreen.svg?style=flat)](http://docs.cmv.io/) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/cmv/cmv-app?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](http://travis-ci.org/cmv/cmv-app.svg?branch=master)](http://travis-ci.org/cmv/cmv-app) +[![Greenkeeper badge](https://badges.greenkeeper.io/cmv/cmv-app.svg)](https://greenkeeper.io/) + +[![Read The Docs](https://img.shields.io/badge/docs-1.3.4-brightgreen.svg?style=flat)](http://docs.cmv.io/) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/cmv/cmv-app?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](http://travis-ci.org/cmv/cmv-app.svg?branch=master)](http://travis-ci.org/cmv/cmv-app) ## Introduction -[CMV](http://cmv.io/) is a community-supported open source mapping framework. CMV works with the [Esri JavaScript API](http://docs.cmv.io/en/latest/developers.arcgis.com/javascript/jsapi/), [ArcGIS Server](http://www.esri.com/software/arcgis/arcgisserver), [ArcGIS Online](https://arcgis.com/) and more. +[CMV](http://cmv.io/) is a community-supported open source mapping framework. CMV works with the [Esri JavaScript API](http://developers.arcgis.com/javascript/jsapi/3/), [ArcGIS Server](https://www.esri.com/software/arcgis/arcgisserver), [ArcGIS Online](https://arcgis.com/) and more. ## Make It Your Own! This JavaScript web app can be easily configured or used as a boilerplate/starting point for basic viewers. It also demonstrates best practices for modular design and OOP via classes in JS using dojo's great [declare](http://dojotoolkit.org/reference-guide/1.9/dojo/_base/declare.html) system. @@ -14,7 +16,8 @@ This JavaScript web app can be easily configured or used as a boilerplate/starti ![screen shot 2014-08-20 at 9 59 48 pm](https://cloud.githubusercontent.com/assets/661156/3991302/5aa2e0f2-28df-11e4-94d0-9c813937d933.png) ## Widgets Included: -- Base Maps +- Basemaps +- Basemaps Gallery - Bookmarks - Directions - Draw @@ -27,6 +30,7 @@ This JavaScript web app can be easily configured or used as a boilerplate/starti - Identify - Layer Control (Table of Contents) - Legend +- Locale (Change the Country + Language) - Locate Button (Geolocation) - MapInfo - Measure @@ -39,6 +43,9 @@ This JavaScript web app can be easily configured or used as a boilerplate/starti Read more about the [core widgets](http://docs.cmv.io/en/latest/widgets/). In addition, there is a growing number of [widgets contributed by the CMV developer community](https://github.com/cmv/cmv-contrib-widgets). +## Resource Proxy: +A [resource proxy](https://github.com/Esri/resource-proxy) may be required to access some MapServices and other content that reside on a different domain. A proxy is not available for the Github demo. + ## Documentation: Use the [documentation](http://docs.cmv.io/) for getting started and guidance on configuring your application. The initial documentation is sparse. Please help make it better by contributing over at the [cmv documentation repo](https://github.com/cmv/cmv-docs). diff --git a/package.js b/package.js new file mode 100644 index 000000000..ca310ab6a --- /dev/null +++ b/package.js @@ -0,0 +1,75 @@ +/** + * This file is referenced by the `dojoBuild` key in `package.json` and provides extra hinting specific to the Dojo + * build system about how certain files in the package need to be handled at build time. Build profiles for the + * application itself are stored in the `profiles` directory. + */ + +var profile = (function () { + //only copy files matching these expressions + //this prevents them from being evaluated as amd modules + var reCopyOnly = [ + /Gruntfile/, + /package/, + /app\.js/ + ]; + //exclude from builds completely + var reMiniExclude = [ + /Gruntfile/, + /package/ + ]; + //non-amd modules + var reNonAmd = [ + /plugins\/Google/ + ]; + return { + // Resource tags are functions that provide hints to the build system about the way files should be processed. + // Each of these functions is called once for every file in the package directory. The first argument passed to + // the function is the filename of the file, and the second argument is the computed AMD module ID of the file. + resourceTags: { + // Files that contain test code and should be excluded when the `copyTests` build flag exists and is `false`. + // It is strongly recommended that the `mini` build flag be used instead of `copyTests`. Therefore, no files + // are marked with the `test` tag here. + test: function (filename, mid) { + return false; + }, + + // Files that should be copied as-is without being modified by the build system. + // All files in the `app/resources` directory that are not CSS files are marked as copy-only, since these files + // are typically binaries (images, etc.) and may be corrupted by the build system if it attempts to process + // them and naively assumes they are scripts. + copyOnly: function (filename, mid) { + for (var i = 0; i < reCopyOnly.length; i++) { + if (reCopyOnly[i].test(filename)) { + return true; + } + } + return (/\/(images)\//.test(mid) && !/\.css$/.test(filename)) || + /\/node_modules\//.test(mid); + }, + + // Files that are AMD modules. + // All JavaScript in this package should be AMD modules if you are starting a new project. If you are copying + // any legacy scripts from an existing project, those legacy scripts should not be given the `amd` tag. + amd: function (filename, mid) { + for (var i = 0; i < reNonAmd.length; i++) { + if (reNonAmd[i].test(filename)) { + return false; + } + } + return !this.copyOnly(filename, mid) && /\.js$/.test(filename); + }, + + // Files that should not be copied when the `mini` build flag is set to true. + // In this case, we are excluding this package configuration file which is not necessary in a built copy of + // the application. + miniExclude: function (filename, mid) { + for (var i = 0; i < reMiniExclude.length; i++) { + if (reMiniExclude[i].test(filename)) { + return true; + } + } + return false; + } + } + }; +})(); diff --git a/package.json b/package.json index 58ad7b674..a83a5b61f 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,31 @@ { "name": "ConfigurableMapViewerCMV", - "version": "1.4.0", + "version": "2.0.0-beta.1", "author": "cmv.io - https://github.com/cmv/", "license": "MIT", - "year": "2016", - "homepage": "http://cmv.io/", + "year": "2017", + "homepage": "https://cmv.io/", "repository": "https://github.com/cmv/cmv-app/", "dependencies": { - "babel-eslint": "6.0.x", - "csslint": "0.10.x", - "eslint": "2.5.x", - "grunt": "0.4.x", - "grunt-postcss": "0.8.x", + "babel-eslint": "~7.1.1", + "csslint": "1.0.x", + "eslint": "~3.17.0", + "grunt": "1.0.x", "grunt-contrib-clean": "1.0.x", + "grunt-contrib-compress": "~1.4.1", "grunt-contrib-connect": "1.0.x", "grunt-contrib-copy": "1.0.x", - "grunt-contrib-csslint": "1.0.x", - "grunt-contrib-cssmin": "1.0.x", - "grunt-eslint": "18.0.x", - "grunt-contrib-uglify": "1.0.x", + "grunt-contrib-csslint": "~2.0.x", + "grunt-contrib-cssmin": "~2.0.0", + "grunt-contrib-uglify": "~2.2.0", "grunt-contrib-watch": "1.0.x", - "grunt-newer": "1.1.x", + "grunt-eslint": "19.0.x", + "grunt-newer": "1.2.x", "grunt-open": "0.2.x", - "grunt-contrib-compress": "1.2.x", + "grunt-postcss": "0.8.x", + "body-parser": "~1.17.0", "proxypage": "*" }, - "engine": "node >= 4" -} \ No newline at end of file + "engine": "node >= 4", + "dojoBuild": "package.js" +} diff --git a/resource-proxy.zip b/resource-proxy.zip deleted file mode 100644 index 4e1279876..000000000 Binary files a/resource-proxy.zip and /dev/null differ diff --git a/viewer/css/cmv-theme-overrides.css b/viewer/css/cmv-theme-overrides.css new file mode 100644 index 000000000..55219bc4a --- /dev/null +++ b/viewer/css/cmv-theme-overrides.css @@ -0,0 +1,286 @@ +.cmv .dijitToolbar .dijitButtonContents { + padding: 1px !important; +} + +.cmv .dijitTitlePane { + margin-bottom: 2px; + background-color: #FFF; +} + +.cmv .dijitTitlePaneTitle { + color: #666; + background-color: #F5F5F5; +} + +.cmv .dijitTitlePaneTitleOpen { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.cmv label { + font-weight: bold; +} + +.cmv :focus { + outline: none !important; +} + +/* icons for the sidebar */ +.cmv .dijitTitlePane .dijitTitlePaneTitle .titlePaneIcon { + margin: 0 0 0 8px; + padding-top: 2px; +} +/* end custom icons */ + +/* esri popup window overrides */ + +.cmv .esriPopup .sizer { + width: 325px; +} + +.cmv .esriPopup .esriPopupWrapper { + background-color: #FFFFFF; +} +.cmv .esriPopup .attachmentsSection div { + font-weight: bold; +} +.cmv .esriPopup .contentPane table.attrTable { + width: 100%; + border-collapse: collapse; +} +.cmv .esriPopup .contentPane table.attrTable td { + padding: 2px; +} +.cmv .esriPopup .contentPane table.attrTable td.attrName { + text-align: right; + font-weight: bold; + color: #333333; + width: 40%; + padding-right: 5px; +} +.cmv .esriPopup .contentPane table.attrTable td.attrValue { + width: 60%; +} +.cmv .esriPopup .contentPane table.attrTable tr { + vertical-align: top; + border-bottom: 1px solid rgb(221, 221, 221); +} +.cmv .esriPopup .contentPane table.attrTable tr:nth-child(odd) { + background-color: none; +} +.cmv .esriPopup .contentPane table.attrTable tr:nth-child(even) { + background-color: rgb(238, 238, 238); +} + +/* end esri popup window overrides */ + +/* esri mobile popup overrides */ + +.cmv .esriPopupMobile { + z-index: 999; +} +.cmv .esriMobileNavigationBar { + background-color: #666666; + background: url("../images/linen.jpg") repeat-x scroll left top transparent; + color: #FFFFFF; +} +.cmv .esriPopupMobile .titlePane { + background-color: #666666; + background: url("../images/linen.jpg") repeat-x scroll left top transparent; + color: #FFFFFF; +} +.cmv .esriPopupMobile .pointer.bottom{ + background:url("../images/pointertop.png"); + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -o-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.cmv .esriPopupMobile .pointer.top { + background:url("../images/pointertop.png"); +} + +/* end esri mobile popup overrides */ + +/* esri directions widget overrides */ +.cmv .simpleDirections .esriStopsGetDirections, +.cmv .simpleDirections .esriResultsPrint, +.simpleDirections .esriActivateButton { + background-color: #E6E6E6; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(1, #e6e6e6)); + background-image: -webkit-linear-gradient(top, #fff 0%, #e6e6e6 100%); + background-image: -moz-linear-gradient(top, #fff 0%, #e6e6e6 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e6e6e6 100%); + background-image: -ms-linear-gradient(top, #fff 0%, #e6e6e6 100%); + background-image: linear-gradient(top, #fff 0%, #e6e6e6 100%); + background-repeat: repeat-x; + border: 1px solid #BBB; + border-bottom: 1px solid #A8A8A8; + padding: 0px 12px; + color: #000; + letter-spacing: 0; + text-transform: none; +} +.cmv .simpleDirections .esriStopsGetDirections:before { + content: '\f277'; + font-family: 'FontAwesome'; + margin-right: 8px; +} + +.cmv .simpleDirections .esriResultsPrint { + padding: 8px; +} + +.cmv .simpleDirections .esriResultsPrint:before { + content: '\f02f'; + font-family: 'FontAwesome'; + font-size: 14px; +} + +.cmv .simpleDirections .esriActivateButton { + padding: 8px; +} + +.cmv .simpleDirections .esriActivateButton:before { + content: '\f041'; + font-family: 'FontAwesome'; + font-size: 14px; +} + +.cmv .esriDirectionsPressedButton { + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(1, #e6e6e6)); + background-image: -webkit-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: -moz-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: -o-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: -ms-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-repeat: repeat-x; + box-shadow: inset 0 2px 4px rgba(0,0,0,0.4), 0 1px 1px rgba(0,0,0,0.2); + border-top-color: #444; + border-color: #666; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,0.4), 0 1px 1px rgba(0,0,0,0.2); +} + +.cmv .simpleDirections .esriLinkButton { + color: #000; + letter-spacing: 0; + text-transform: none; +} +/* end esri directions widget overrides */ + +/* flat theme */ +/* makes the flat theme more like dbootstrap */ +.flat { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.flat a, +.flat a:hover { + text-decoration: none; +} + +.flat .dijitTitlePane { + border-color: #E0E0E0; +} + +.flat .dijitTabContainerTop-tabs .dijitTabChecked:before { + height: 3px; + background-color: #666; + top: -1px; + left: -1px; + right: -1px; +} + +.flat .dijitTitlePaneTitle { + border: 1px solid #DDD; + -webkit-border-radius: 4px; + border-radius: 4px; + padding: 8px 15px; +} + +.flat .dijitDialogTitleBar { + background-color: #F5F5F5; + color: #666; +} + +.flat .dijitTitlePaneContentOuter { + border: 1px solid #DDD; + border-top: none; +} + +.flat .dijitButtonHover .dijitButtonNode, +.flat .dijitDropDownButtonHover .dijitButtonNode, +.flat .dijitComboButton .dijitButtonNodeHover, +.flat .dijitComboButton .dijitDownArrowButtonHover, +.flat .dijitToggleButtonHover .dijitButtonNode, +.flat .dijitDropDownButtonActive .dijitButtonNode { + background-position: 0 -15px; + text-decoration: none; + transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -ms-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + -webkit-transition: background-position 0.1s linear; +} +.flat .dijitButtonActive .dijitButtonNode, +.flat .dijitComboButton .dijitButtonNodeActive, +.flat .dijitToggleButtonActive .dijitButtonNode, +.flat .dijitToggleButtonChecked .dijitButtonNode, +.flat .dijitComboButton .dijitArrowButton.dijitHasDropDownOpen { + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(1, #e6e6e6)); + background-image: -webkit-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: -moz-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: -o-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: -ms-linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-image: linear-gradient(bottom, #fff 0%, #e6e6e6 100%); + background-repeat: repeat-x; + box-shadow: inset 0 2px 4px rgba(0,0,0,0.4), 0 1px 1px rgba(0,0,0,0.2); + border-top-color: #444; + border-color: #666; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,0.4), 0 1px 1px rgba(0,0,0,0.2); +} +.flat .dijitButtonDisabled .dijitButtonNode, +.flat .dijitDropDownButtonDisabled .dijitButtonNode, +.flat .dijitComboButtonDisabled .dijitButtonNode, +.flat .dijitToggleButtonDisabled .dijitButtonNode, +.flat .dijitComboBoxDisabled .dijitButtonNode, +.flat .dijitSpinnerDisabled .dijitButtonNode, +.flat .dijitSelectDisabled .dijitButtonNode { + background-image: none; + box-shadow: none; + cursor: not-allowed; + filter: alpha(opacity=65); + opacity: 0.65; + -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=65); + -webkit-box-shadow: none; +} + +.flat .success .dijitButtonNode { + background: #409843; + color: #FFF; + border-color: #39883c; +} +.flat .danger .dijitButtonNode { + background: #e32d29; + color: #FFF; + border-color: #d4201b; +} + +.flat .dijitTextBoxHover, +.flat .dijitTextBoxFocused, +.flat .dijitCheckBoxChecked, +.flat .dijitCheckBoxHover, +.flat .dijitCheckBoxCheckedHover, +.flat .dijitRadio, +.flat .dijitRadioIcon, +.flat .dijitRadioHover { + border-color: #666; +} +.flat .dijitCheckBoxChecked, +.flat .dijitRadio:after, +.flat .dijitRadioIcon:after { + background-color: #999; +} + +/* end flat theme */ diff --git a/viewer/css/main.css b/viewer/css/main.css index a88b52722..abf36af1f 100644 --- a/viewer/css/main.css +++ b/viewer/css/main.css @@ -4,7 +4,6 @@ body, html { margin: 0; padding: 0; overflow: hidden; - /*font-family: Tahoma;*/ font-size: 14px; } .appHeader { @@ -59,7 +58,7 @@ body, html { right: 0; border: none; padding: 0; - background-color: #FFFFFF + background-color: #FFFFFF; } #sidebarLeft { width: 334px; @@ -170,6 +169,9 @@ body, html { border-radius: 0 0 5px 5px; border-bottom: 1px solid #B5BCC7; } +.sidebarCollapseButton .open:before, .sidebarCollapseButton .close:before { + font-family: FontAwesome; +} .sidebarCollapseButtonHorz .button { padding-top: 4px; padding-left: 2px; @@ -208,111 +210,20 @@ body, html { #help_parent_underlay { display: block; } - -/* dbootstrap overrides*/ - -.dbootstrap .dijitToolbar .dijitButtonContents { - padding: 1px !important; -} -.dbootstrap .dijitTitlePane { - border: 1px solid #DDDDDD; - margin-bottom: 2px; - -webkit-border-radius: 4px; - border-radius: 4px; - background-color: #FFFFFF; -} -.dbootstrap .dijitTitlePaneTitle { - color: #666666 !important; - border-bottom: none; - padding: 8px 15px; - background-color: #F5F5F5; -} -.dijitSliderBarContainerH { - z-index: 0 !important; -} -.dijitBorderContainerNoGutterPane { - z-index: auto; -} -.dbootstrap :focus { - outline: none !important; -} - -/* end dbootstrap overrides*/ - -/* esri popup window overrides */ - -.esriPopup .sizer { - width: 325px; -} - -.esriPopup .esriPopupWrapper { - background-color: #FFFFFF; -} -.esriPopup .attachmentsSection div { - font-weight: bold; -} -.esriPopup .contentPane table.attrTable { - width: 100%; - border-collapse: collapse; -} -.esriPopup .contentPane table.attrTable td { - padding: 2px; -} -.esriPopup .contentPane table.attrTable td.attrName { - text-align: right; - font-weight: bold; - color: #333333; - width: 40%; - padding-right: 5px; -} -.esriPopup .contentPane table.attrTable td.attrValue { - width: 60%; -} -.esriPopup .contentPane table.attrTable tr { - vertical-align: top; - border-bottom: 1px solid rgb(221, 221, 221); -} -.esriPopup .contentPane table.attrTable tr:nth-child(odd) { - background-color: none; -} -.esriPopup .contentPane table.attrTable tr:nth-child(even) { - background-color: rgb(238, 238, 238); -} - -/* end esri popup window overrides */ - -/* esri mobile popup overrides */ - -.esriPopupMobile { - z-index: 999; -} -.esriMobileNavigationBar { - background-color: #666666; - background: url("../images/linen.jpg") repeat-x scroll left top transparent; - color: #FFFFFF; -} -.esriPopupMobile .titlePane { - background-color: #666666; - background: url("../images/linen.jpg") repeat-x scroll left top transparent; - color: #FFFFFF; -} -.esriPopupMobile .pointer.bottom{ - background:url("../images/pointertop.png"); - -webkit-transform: rotate(180deg); - -moz-transform: rotate(180deg); - -o-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} -.esriPopupMobile .pointer.top { - background:url("../images/pointertop.png"); +.widgetLoader { + color: #333; + display: block; + font-size: 18px; + left: 0; + margin: 4px; + text-align: center; + top: 0; + width: 100%; } -/* end esri mobile popup overrides */ - /* media queries bootstrap style - http://getbootstrap.com/css/#grid-media-queries + https://getbootstrap.com/css/#grid-media-queries */ @media screen and (max-width: 991px) { diff --git a/viewer/css/theme/flat/flat.css b/viewer/css/theme/flat/flat.css new file mode 100644 index 000000000..99e67f488 --- /dev/null +++ b/viewer/css/theme/flat/flat.css @@ -0,0 +1 @@ +.dj_ie .dijitButtonNode,.dj_ie .dijitSliderRtl .dijitRuleLabelH,.dj_ie6 .dijitTabContainerLeft-tabs .dijitTabRtl,.dj_ie6 .dijitTabContainerRight-tabs .dijitTabRtl,.dj_ie6 .dijitTabRtl .tabLabel,.dj_ie6 .dijitTitlePane .dijitTitlePaneTitle,.dj_ie6 .dijitTitlePaneContentOuter,.dj_ie7 .dijitTabContainerLeft-tabs .dijitTabRtl,.dj_ie7 .dijitTabContainerRight-tabs .dijitTabRtl{zoom:1}.dijitBorderContainer>.dijitTextArea,.dijitExpandingTextArea{resize:none}.dijitReset{margin:0;border:0;padding:0;font:inherit;color:inherit}.dj_a11y .dijitReset{-moz-appearance:none}.dijitInline{display:inline-block;#zoom:1;#display:inline;border:0;padding:0;vertical-align:middle;#vertical-align:auto}table.dijitInline{display:inline-table;box-sizing:content-box;-moz-box-sizing:content-box}.dijitHidden{display:none!important}.dijitVisible{display:block!important;position:relative}.dijitInputContainer,.dj_ie6 .dijitComboBox .dijitInputContainer{#zoom:1;overflow:hidden;float:none!important;position:relative}.dj_ie7 .dijitInputContainer{float:left!important;clear:left;display:inline-block!important}.dj_ie .dijitSelect input,.dj_ie .dijitTextBox input,.dj_ie input.dijitTextBox{font-size:100%}.dijitSelect .dijitButtonText{float:left;vertical-align:top}TABLE.dijitSelect{padding:0!important}.dijitTextBox .dijitArrowButtonContainer,.dijitTextBox .dijitSpinnerButtonContainer,.dijitValidationTextBox .dijitValidationContainer{float:right;text-align:center}.dijitSelect input.dijitInputField,.dijitTextBox input.dijitInputField{padding-left:0!important;padding-right:0!important}.dijitValidationTextBox .dijitValidationContainer{display:none}.dijitTeeny{font-size:1px;line-height:1px}.dijitOffScreen{position:absolute!important;left:50%!important;top:-10000px!important}.dijitPopup{position:absolute;background-color:transparent;margin:0;border:0;padding:0}.dijitPositionOnly{padding:0!important;border:0!important;background-color:transparent!important;background-image:none!important;height:auto!important;width:auto!important}.dijitNonPositionOnly{float:none!important;position:static!important;margin:0!important;vertical-align:middle!important}.dijitButtonNode,.dijitButtonNode *,.dijitButtonNode img,.dijitTextBox,.dj_ie .dijitToolbar .dijitComboBox{vertical-align:middle}.dijitBackgroundIframe{position:absolute;left:0;top:0;width:100%;height:100%;z-index:-1;border:0;padding:0;margin:0}.dijitDisplayNone{display:none!important}.dijitContainer{overflow:hidden}.dj_a11y .dijitCalendarIncrementControl,.dj_a11y .dijitIcon,.dj_a11y .dijitTreeExpando,.dj_a11y div.dijitArrowButtonInner,.dj_a11y img.dijitArrowButtonInner,.dj_a11y span.dijitArrowButtonInner{display:none}.dijitSpinner div.dijitArrowButtonInner{display:block}.dj_a11y .dijitA11ySideArrow{display:inline!important;cursor:pointer}.dj_a11y .dijitCalendarDateLabel{padding:1px;border:0!important}.dj_a11y .dijitCalendarSelectedDate .dijitCalendarDateLabel{border-style:solid!important;border-width:1px!important;padding:0}.dj_a11y .dijitCalendarDateTemplate{padding-bottom:.1em!important;border:0!important}.dj_a11y .dijitButtonNode{border:outset #000!important;padding:0!important}.dj_a11y .dijitArrowButton{padding:0!important}.dj_a11y .dijitButtonContents{margin:.15em}.dj_a11y .dijitTextBoxReadOnly .dijitButtonNode,.dj_a11y .dijitTextBoxReadOnly .dijitInputField{border-style:outset!important;border-width:medium!important;border-color:#999!important;color:#999!important}.dijitButtonNode,.dijitSelect{border:1px solid gray}.dijitButtonNode .dijitArrowButtonInner,.dijitSelect .dijitArrowButtonInner{background:center no-repeat;direction:ltr}.dijitLeft{background-position:left top;background-repeat:no-repeat}.dijitStretch{white-space:nowrap;background-repeat:repeat-x}.dijitRight{#display:inline;background-position:right top;background-repeat:no-repeat}.dj_gecko .dj_a11y .dijitButtonDisabled .dijitButtonNode{opacity:.5}.dijitButton,.dijitComboButton,.dijitDropDownButton,.dijitToggleButton{margin:.2em;vertical-align:middle}.dijitButtonContents{display:block}td.dijitButtonContents{display:table-cell}.dijitToolbar .dijitComboButton{border-collapse:separate}.dijitToolbar .dijitButton,.dijitToolbar .dijitComboButton,.dijitToolbar .dijitDropDownButton,.dijitToolbar .dijitToggleButton{margin:0}.dijitToolbar .dijitButtonContents{padding:1px 2px}.dj_gecko .dijitToolbar .dijitButtonNode::-moz-focus-inner{padding:0}.dijitButtonNode{margin:0;line-height:20px;#vertical-align:auto;text-align:center;white-space:nowrap}.dj_webkit .dijitSpinner .dijitSpinnerButtonContainer{line-height:inherit}.dijitTextBox .dijitButtonNode{border-width:0}.dijitButtonNode,.dijitButtonNode *,.dijitSelect,.dijitSelect *{cursor:pointer}.dj_ie .dijitButtonNode button{overflow:visible}div.dijitArrowButton{float:right}.dijitTextBox{border:1px solid #000;#overflow:hidden;width:15em}.dijitTextBoxDisabled,.dijitTextBoxReadOnly{color:gray}.dj_safari .dijitTextBoxDisabled input{color:#B0B0B0}.dj_safari textarea.dijitTextAreaDisabled{color:#333}.dj_gecko .dijitTextBoxDisabled input,.dj_gecko .dijitTextBoxReadOnly input.dijitInputField{-moz-user-input:none}.dijitPlaceHolder{color:#999;position:absolute;top:0;left:0;#filter:""}.dijitTimeTextBox{width:8em}.dijitTextBox input:focus{outline:0}.dijitTextBoxFocused{outline:-webkit-focus-ring-color 5px}.dijitSelect input,.dijitTextBox input{float:left}.dj_ie6 .dijitTextBox input,.dj_ie6 input.dijitTextBox{float:none}.dijitInputInner{border:0!important;background-color:transparent!important;width:100%!important;box-shadow:none!important;padding-left:0!important;padding-right:0!important;margin-left:0!important;margin-right:0!important}.dj_a11y .dijitTextBox input{margin:0!important}.dijitSelect input,.dijitTextBox input.dijitArrowButtonInner,.dijitValidationTextBoxError input.dijitValidationInner{text-indent:-2em!important;direction:ltr!important;text-align:left!important;#text-indent:0!important;#letter-spacing:-5em!important;#text-align:right!important}.dj_ie .dijitSelect input,.dj_ie .dijitTextBox input,.dj_ie input.dijitTextBox{overflow-y:visible;line-height:20px;height:20px}.dijitSelect .dijitSelectLabel span{line-height:100%}.dj_ie .dijitSelect .dijitSelectLabel{line-height:normal}.dijitSelect td,.dj_ie6 .dijitSelect .dijitSelectLabel,.dj_ie6 .dijitSelect .dijitValidationContainer,.dj_ie6 .dijitSelect input,.dj_ie6 .dijitTextBox input,.dj_ie6 input.dijitTextBox,.dj_ie7 .dijitSelect .dijitSelectLabel,.dj_ie8 .dijitSelect .dijitSelectLabel,.dj_iequirks .dijitSelect .dijitSelectLabel,.dj_iequirks .dijitSelect input,.dj_iequirks .dijitTextBox input.dijitArrowButtonInner,.dj_iequirks .dijitTextBox input.dijitInputInner,.dj_iequirks .dijitTextBox input.dijitSpinnerButtonInner,.dj_iequirks .dijitTextBox input.dijitValidationInner,.dj_iequirks input.dijitTextBox{line-height:100%}.dj_a11y input.dijitArrowButtonInner,.dj_a11y input.dijitValidationInner{text-indent:0!important;width:1em!important;text-align:left!important;color:#000!important}.dijitValidationTextBoxError .dijitValidationContainer{display:inline;cursor:default}.bootstrap .dijitSelect .dijitArrowButton,.dijitComboBox .dijitArrowButtonContainer,.dijitSpinner .dijitSpinnerButtonContainer{border-width:0 0 0 1px!important}.dijitToolbar .dijitComboBox .dijitArrowButtonContainer,.dj_a11y .dijitSelect .dijitArrowButtonContainer{border-width:0!important}.dijitComboBox .dijitButtonNode,.dijitSpinner .dijitSpinnerButtonContainer .dijitButtonNode,.dijitSpinnerButtonContainer .dijitButtonNode{border-width:0}.dijitComboBoxMenu{list-style-type:none}.dj_ie .dj_a11y .dijitSpinner .dijitSpinnerButtonContainer .dijitButtonNode{clear:both}.dijitTextBox .dijitSpinnerButtonContainer{width:1em;position:relative!important;overflow:hidden}.dijitSpinner .dijitSpinnerButtonInner{width:1em;visibility:hidden!important;overflow-x:hidden}.dj_a11y .dijitSpinnerButtonContainer .dijitButtonNode{border-width:0!important;border-style:solid!important}.dj_a11y .dijitSpinner .dijitArrowButtonInner,.dj_a11y .dijitSpinnerButtonContainer input,.dj_a11y .dijitTextBox .dijitSpinnerButtonContainer{width:1em!important}.dj_a11y .dijitSpinner .dijitArrowButtonInner{margin:0 auto!important}.dj_ie .dj_a11y .dijitSpinner .dijitArrowButtonInner .dijitInputField{padding-left:.3em!important;padding-right:.3em!important;margin-left:.3em!important;margin-right:.3em!important;width:1.4em!important}.dj_ie7 .dj_a11y .dijitSpinner .dijitArrowButtonInner .dijitInputField{padding-left:0!important;padding-right:0!important;width:1em!important}.dj_ie6 .dj_a11y .dijitSpinner .dijitArrowButtonInner .dijitInputField{margin-left:.1em!important;margin-right:.1em!important;width:1em!important}.dj_iequirks .dj_a11y .dijitSpinner .dijitArrowButtonInner .dijitInputField{margin-left:0!important;margin-right:0!important;width:2em!important}.dijitSpinner .dijitSpinnerButtonContainer .dijitArrowButton{padding:0;position:absolute!important;float:none;height:50%;width:100%;bottom:auto;left:0;right:auto}.dj_iequirks .dijitSpinner .dijitSpinnerButtonContainer .dijitArrowButton{width:auto}.dj_a11y .dijitSpinnerButtonContainer .dijitArrowButton{overflow:visible!important}.dijitSpinner .dijitSpinnerButtonContainer .dijitDownArrowButton{top:50%;border-top-width:1px!important}.dijitSpinner .dijitSpinnerButtonContainer .dijitUpArrowButton{#bottom:50%;top:0}.dijitSpinner .dijitArrowButtonInner{margin:auto;overflow-x:hidden}.dj_iequirks .dijitSpinner .dijitArrowButtonInner{height:auto!important}.dijitSpinner .dijitArrowButtonInner .dijitInputField{-moz-transform:scale(.5);-moz-transform-origin:center top;-webkit-transform:scale(.5);-webkit-transform-origin:center top;-o-transform:scale(.5);-o-transform-origin:center top;transform:scale(.5);transform-origin:left top;padding-top:0;padding-bottom:0;padding-left:0!important;padding-right:0!important;width:100%;visibility:hidden}.dj_ie .dijitSpinner .dijitArrowButtonInner .dijitInputField{display:none}.dijitSpinner .dijitSpinnerButtonContainer .dijitArrowButtonInner{overflow:hidden}.dj_a11y .dijitSpinner .dijitSpinnerButtonContainer .dijitArrowButton{width:100%}.dj_a11y .dijitSpinnerButtonContainer,.dj_iequirks .dj_a11y .dijitSpinner .dijitSpinnerButtonContainer .dijitArrowButton{width:1em}.dj_a11y .dijitSpinner .dijitArrowButtonInner .dijitInputField{vertical-align:top;visibility:visible}.dijitCalendarIncrementControl,.dijitCheckedMenuItemIconChar,.dijitColorPalette .dijitPaletteCell,.dijitMenuItemLabel,.dijitSliderMoveable,.dijitTab *,.dijitTitlePaneTitle *,.dijitTreeRow img{vertical-align:middle}.dijitCheckBox,.dijitCheckBoxInput,.dijitRadio{padding:0;border:0;width:20px;height:20px;background-position:center center;background-repeat:no-repeat;overflow:hidden;cursor:pointer}.dijitCheckBox input,.dijitRadio input{margin:0;padding:0;display:block}.dijitCheckBoxInput{opacity:.01}.dj_ie .dijitCheckBoxInput{filter:alpha(opacity=0)}.dj_a11y .dijitCheckBox,.dj_a11y .dijitRadio{width:auto!important;height:auto!important}.dj_a11y .dijitCheckBoxInput{opacity:1;filter:none;width:auto;height:auto}.dj_a11y .dijitFocusedLabel{border:1px dotted;outline:0!important}.dijitProgressBar{z-index:0}.dijitProgressBarEmpty{position:relative;overflow:hidden;border:1px solid #000;z-index:0}.dijitProgressBarFull,.dijitProgressBarTile{position:absolute;overflow:hidden;top:0;width:100%}.dijitProgressBarFull{z-index:-1}.dj_ie6 .dijitProgressBarFull{height:1.6em}.dijitProgressBarTile{left:0;bottom:0;right:0;margin:0;padding:0;height:auto;background-color:#aaa}.dj_a11y .dijitProgressBarTile{border-width:2px;border-style:solid;background-color:transparent!important}.dj_ie6 .dijitProgressBarTile{position:static;height:1.6em}.dijitProgressBarIndeterminateHighContrastImage{display:none}.dj_a11y .dijitProgressBarIndeterminate .dijitProgressBarIndeterminateHighContrastImage{display:block;position:absolute;top:0;bottom:0;margin:0;padding:0;width:100%;height:auto}.dijitProgressBarLabel{display:block;position:static;width:100%;text-align:center;background-color:transparent!important}.dijitTooltip,.dijitTooltipConnector{position:absolute}.dijitTooltip{z-index:2000;display:block;left:0;top:-10000px;overflow:visible}.dijitTooltipContainer{border:2px solid #000;background:#b8b5b5;color:#000;font-size:small}.dijitTooltipFocusNode{padding:2px}.dijitTooltipData,.dj_a11y .dijitTooltipConnector{display:none}.dijitLayoutContainer{position:relative;display:block;overflow:hidden}.dijitAlignBottom,.dijitAlignLeft,.dijitAlignRight,.dijitAlignTop{position:absolute;overflow:hidden}body .dijitAlignClient{position:absolute}.dijitBorderContainer,.dijitBorderContainerNoGutter{position:relative;overflow:hidden;z-index:0}.dijitBorderContainerNoGutterPane,.dijitBorderContainerPane{position:absolute!important;z-index:2}.dijitGutter{position:absolute;font-size:1px}.dijitSplitter{position:absolute;overflow:hidden;z-index:10;background-color:#fff;border-color:gray;border-style:solid;border-width:0}.dj_ie .dijitSplitter{z-index:1}.dijitSplitterActive{z-index:11!important}.dijitSplitterCover{position:absolute;z-index:-1;top:0;left:0;width:100%;height:100%}.dijitSplitterCoverActive{z-index:3!important}.dj_ie .dijitSplitterCover{background:#fff;filter:alpha(opacity=0)}.dijitSplitterH{height:7px;border-top:1px;border-bottom:1px;cursor:row-resize}.dijitSplitContainerSizerH,.dijitSplitContainerVirtualSizerH,.dijitSplitterV{cursor:col-resize}.dijitSplitterV{width:7px;border-left:1px;border-right:1px}.dijitSplitContainer{position:relative;overflow:hidden;display:block}.dijitSplitPane{position:absolute}.dijitSplitContainerSizerH,.dijitSplitContainerSizerV{position:absolute;font-size:1px;background-color:ThreeDFace;border:1px solid;border-color:ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;margin:0}.dijitSplitContainerSizerH .thumb,.dijitSplitterV .dijitSplitterThumb{overflow:hidden;position:absolute;top:49%}.dijitSplitContainerSizerV .thumb,.dijitSplitterH .dijitSplitterThumb{position:absolute;left:49%}.dijitSplitContainerVirtualSizerH,.dijitSplitContainerVirtualSizerV,.dijitSplitterShadow{font-size:1px;background-color:ThreeDShadow;-moz-opacity:.5;opacity:.5;filter:Alpha(Opacity=50);margin:0}.dijitSplitContainerSizerV,.dijitSplitContainerVirtualSizerV{cursor:row-resize}.dj_a11y .dijitSplitterH{border-top:1px solid #d3d3d3!important;border-bottom:1px solid #d3d3d3!important}.dj_a11y .dijitSplitterV{border-left:1px solid #d3d3d3!important;border-right:1px solid #d3d3d3!important}.dijitContentPane{display:block;overflow:auto}.dijitAccordionChildWrapper,.dijitContentPaneSingleChild,.dijitSpacer,.dijitTitlePane{overflow:hidden}.dijitContentPaneError .dijitIconError,.dijitContentPaneLoading .dijitIconLoading{margin-right:9px}.dijitTitlePane{display:block}.dijitFixedClosed .dijitArrowNode,.dijitFixedClosed .dijitArrowNodeInner,.dijitFixedOpen .dijitArrowNode,.dijitFixedOpen .dijitArrowNodeInner,.dijitTitlePane .dijitArrowNodeInner{display:none}.dijitTitlePaneTitle{cursor:pointer}.dijitFixedClosed,.dijitFixedOpen{cursor:default}.dj_a11y .dijitTitlePane .dijitArrowNodeInner{display:inline!important;font-family:monospace}.dijitAccordionTitle .arrowTextDown,.dijitAccordionTitle .arrowTextUp,.dj_a11y .dijitTitlePane .dijitArrowNode{display:none}.dijitColorPalette{border:1px solid #999;background:#fff;position:relative}.dijitColorPalette .dijitPaletteTable{padding:2px 3px 3px;position:relative;overflow:hidden;outline:0;border-collapse:separate}.dj_ie6 .dijitColorPalette .dijitPaletteTable,.dj_ie7 .dijitColorPalette .dijitPaletteTable,.dj_iequirks .dijitColorPalette .dijitPaletteTable{padding:0;margin:2px 3px 3px}.dijitColorPalette .dijitPaletteCell{font-size:1px;text-align:center;background:0 0}.dijitColorPalette .dijitPaletteImg{padding:1px;border:1px solid #999;margin:2px 1px;cursor:default;font-size:1px}.dj_gecko .dijitColorPalette .dijitPaletteImg{padding-bottom:0}.dijitColorPalette .dijitColorPaletteSwatch{width:14px;height:12px}.dijitPaletteTable td{padding:0}.dijitColorPalette .dijitPaletteCell:hover .dijitPaletteImg{border:1px solid #000}.dijitColorPalette .dijitPaletteCell:active .dijitPaletteImg,.dijitColorPalette .dijitPaletteTable .dijitPaletteCellSelected .dijitPaletteImg{border:2px solid #000;margin:1px 0}.dj_a11y .dijitColorPalette .dijitPaletteTable,.dj_a11y .dijitColorPalette .dijitPaletteTable *{background-color:transparent!important}.dijitAccordionContainer{border:1px solid #b7b7b7;border-top:0!important}.dijitAccordionTitle{cursor:pointer}.dijitAccordionTitleSelected{cursor:default}.dj_a11y .dijitAccordionTitle .arrowTextUp,.dj_a11y .dijitAccordionTitleSelected .arrowTextDown{display:inline}.dijitMenuExpandA11y,.dj_a11y .dijitAccordionTitleSelected .arrowTextUp{display:none}.dijitCalendarContainer{width:auto}.dijitCalendarContainer td,.dijitCalendarContainer th{padding:1px 2px 2px;vertical-align:middle}.dijitCalendarYearLabel{white-space:nowrap}.dijitCalendarNextYear{margin:0 0 0 .55em}.dijitCalendarPreviousYear{margin:0 .55em 0 0}.dijitCalendarDateTemplate,.dijitCalendarIncrementControl,.dijitCalendarMonthLabel,.dijitCalendarNextYear,.dijitCalendarPreviousYear{cursor:pointer}.dijitCalendarDisabledDate{color:gray;text-decoration:line-through;cursor:default}.dijitSpacer{position:relative;height:1px;visibility:hidden}.dijitCalendarMonthMenu .dijitCalendarMonthLabel{text-align:center}.dijitMenu{border:1px solid #000;background-color:#fff}.dijitMenuTable{border-collapse:collapse;border-width:0;background-color:#fff}.dj_webkit .dijitMenuTable td[colspan="2"]{border-right:hidden}.dijitMenuItem{text-align:left;white-space:nowrap;padding:.1em .2em;cursor:pointer}.dijitMenuItemDisabled *,.dijitStackController .dijitToggleButtonChecked *{cursor:default}.dijitMenuItem:focus{outline:0}.dijitMenuItemSelected,.dijitMenuPassive .dijitMenuItemHover{background-color:#000;color:#fff}.dijitMenuExpand,.dijitMenuItemIcon{background-repeat:no-repeat}.dj_ie .dijitMenuItemDisabled *,.dj_ie .dj_a11y .dijitMenuItemDisabled,.dj_ie .dj_a11y .dijitMenuItemDisabled *{color:gray;filter:alpha(opacity=35)}.dijitMenuItemLabel{position:relative}.dj_a11y .dijitMenuItemSelected{border:1px dotted #000!important}.dj_a11y .dijitMenuItemSelected .dijitMenuItemLabel{border-width:1px;border-style:solid}.dj_ie8 .dj_a11y .dijitMenuItemLabel{position:static}.dj_a11y .dijitMenuExpandA11y{display:inline}.dijitMenuSeparator td{border:0;padding:0}.dijitMenuSeparatorTop{height:50%;margin:3px 0 0;font-size:1px}.dijitMenuSeparatorBottom{height:50%;margin:0 0 3px;font-size:1px}.dijitCheckedMenuItemIconChar{visibility:hidden}.dijitCheckedMenuItemChecked .dijitCheckedMenuItemIconChar{visibility:visible}.dj_a11y .dijitCheckedMenuItemIconChar{display:inline!important}.dj_a11y .dijitCheckedMenuItemIcon{display:none}.dj_ie .dj_a11y .dijitMenuBar .dijitMenuItem{margin:0}.dijitTabContainer{z-index:0;overflow:visible}.dj_ie6 .dijitTabContainer{overflow:hidden}.dijitTabContainerNoLayout{width:100%}.dijitTabContainerBottom-tabs,.dijitTabContainerLeft-tabs,.dijitTabContainerRight-tabs,.dijitTabContainerTop-tabs{z-index:1;overflow:visible!important}.dijitTabController{z-index:1}.dijitTabContainerBottom-container,.dijitTabContainerLeft-container,.dijitTabContainerRight-container,.dijitTabContainerTop-container{z-index:0;overflow:hidden;border:1px solid #000}.nowrapTabStrip{width:50000px;display:block;position:relative;text-align:left;z-index:1}.dijitTabListWrapper{overflow:hidden;z-index:1}.dj_a11y .tabStripButton img{display:none}.dijitTabContainerTop-tabs{border-bottom:1px solid #000}.dijitTabContainerTop-container{border-top:0}.dijitTabContainerLeft-tabs{border-right:1px solid #000;float:left}.dijitTabContainerLeft-container{border-left:0}.dijitTabContainerBottom-tabs{border-top:1px solid #000}.dijitTabContainerBottom-container{border-bottom:0}.dijitTabContainerRight-tabs{border-left:1px solid #000;float:left}.dijitTabContainerRight-container{border-right:0}.dj_ie div.dijitTabDisabled,div.dijitTabDisabled{cursor:auto}.dijitTab{position:relative;cursor:pointer;white-space:nowrap;z-index:3}.dijitTabChecked{cursor:default}.dijitTabContainerTop-tabs .dijitTab{top:1px}.dijitTabContainerBottom-tabs .dijitTab{top:-1px}.dijitTabContainerLeft-tabs .dijitTab{left:1px}.dijitTabContainerRight-tabs .dijitTab{left:-1px}.dijitTabContainerBottom-tabs .dijitTab,.dijitTabContainerTop-tabs .dijitTab{display:inline-block;#zoom:1;#display:inline}.dijitTabButtonDisabled .tabStripButton,.dijitTabCloseText{display:none}.tabStripButton{z-index:12}.dijitTabCloseButton{margin-left:1em}.dijitTab .tabLabel{display:inline-block}.dijitNoIcon{display:none}.dj_ie6 .dijitTab .dijitNoIcon{display:inline;height:15px;width:1px}.dj_a11y .dijitTabCloseButton{background-image:none!important;width:auto!important;height:auto!important}.dj_a11y .dijitTabCloseText{display:inline}.dijitAccordionContainer-child,.dijitStackContainer-child,.dijitTabPane{border:none!important}.dijitInlineEditBoxDisplayMode{border:1px solid transparent;cursor:text}.dijitInlineEditBoxDisplayModeDisabled,.dijitTreeContent{cursor:default}.dj_a11y .dijitInlineEditBoxDisplayMode,.dj_ie6 .dijitInlineEditBoxDisplayMode{border:none}.dijitInlineEditBoxDisplayModeHover,.dj_a11y .dijitInlineEditBoxDisplayModeHover,.dj_ie6 .dijitInlineEditBoxDisplayModeHover{background-color:#e2ebf2;border:1px solid #000}.dijitTree{overflow:auto}.dijitTreeContainer{float:left}.dijitTreeIndent{width:19px}.dijitTreeContent,.dijitTreeRow{white-space:nowrap}.dj_ie .dijitTreeLabel:focus{outline:#000 dotted 1px}.dijitExpandoText{display:none}.dj_a11y .dijitExpandoText{display:inline;padding-left:10px;padding-right:10px;font-family:monospace;border-style:solid;border-width:thin;cursor:pointer}.dijitSlider .dijitButtonNode,.dijitSliderButton{padding:0;display:block}.dijitTreeLabel{margin:0 4px}.dijitDialog{position:absolute;z-index:999;overflow:hidden}.dijitDialogTitleBar{cursor:move}.dijitDialogFixed .dijitDialogTitleBar{cursor:default}.dijitDialogCloseIcon,.dijitSliderBar,.dijitSliderButtonContainer *,.dijitTimePickerItemHover{cursor:pointer}.dijitDialogPaneContent{-webkit-overflow-scrolling:touch}.dijitDialogUnderlayWrapper{position:absolute;left:0;top:0;z-index:998;display:none;background:0 0!important}.dijitDialogUnderlay{background:#eee;opacity:.5}.dj_ie .dijitDialogUnderlay{filter:alpha(opacity=50)}.dj_a11y .dijitDialog,.dj_a11y .dijitSpinnerButtonContainer{opacity:1!important;background-color:#fff!important}.dijitDialog .closeText{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;display:block;color:#000;text-shadow:0 1px 0 #FFF;position:absolute}.dj_a11y .dijitDialog .closeText{display:inline}.dijitSliderMoveable{z-index:99;position:absolute!important;display:block}.dijitSliderMoveableH{right:0}.dijitSliderMoveableV{right:50%}.dijitSliderImageHandle,.dj_a11y div.dijitSliderImageHandle{margin:0;padding:0;position:relative!important;border:8px solid gray;width:0;height:0;cursor:pointer}.dijitSliderBarContainerH,.dijitSliderBarContainerV{position:relative;z-index:1}.dj_iequirks .dj_a11y .dijitSliderImageHandle{font-size:0}.dj_ie7 .dijitSliderImageHandle{overflow:hidden}.dj_ie7 .dj_a11y .dijitSliderImageHandle{overflow:visible}.dj_a11y .dijitSliderFocused .dijitSliderImageHandle{border:4px solid #000;height:8px;width:8px}.dijitSliderImageHandleV{top:-8px;right:-50%}.dijitSliderImageHandleH{left:50%;top:-5px;vertical-align:top}.dijitSliderBar{border-style:solid;border-color:#000}.dijitSliderBarContainerV{height:100%}.dijitSliderBarH{height:4px;border-width:1px 0}.dijitSliderBarV{width:4px;border-width:0 1px}.dijitSliderProgressBar{background-color:red;z-index:1}.dijitSliderProgressBarV{position:static!important;height:0;vertical-align:top;text-align:left}.dijitSliderProgressBarH{position:absolute!important;width:0;vertical-align:middle;overflow:visible}.dijitSliderBumper,.dijitSliderRemainingBar{overflow:hidden;z-index:1}.dijitSliderRemainingBar{background-color:transparent}.dijitSliderRemainingBarV{height:100%;text-align:left}.dijitSliderRemainingBarH{width:100%!important}.dijitSliderBumperV{width:4px;height:8px;border-width:0 1px}.dijitSliderBumperH{width:8px;height:4px;border-width:1px 0}.dijitSliderBottomBumper,.dijitSliderLeftBumper{background-color:red}.dijitSliderRightBumper,.dijitSliderTopBumper{background-color:transparent}.dijitSliderDecoration{text-align:center}.dijitSliderDecorationC,.dijitSliderDecorationV{position:relative}.dijitSliderDecorationH{width:100%}.dijitSliderDecorationV{height:100%;white-space:nowrap}.dijitSliderButton{font-family:monospace;margin:0}.dj_a11y .dijitSliderButtonInner{visibility:visible!important}.dijitSliderButtonContainer{text-align:center;height:0}.dijitRuleContainer{position:relative;overflow:visible}.dijitRuleLabelContainer,.dijitRuleMark{position:absolute}.dijitRuleContainerV{height:100%;line-height:0;float:left;text-align:left}.dj_opera .dijitRuleContainerV{line-height:2%}.dj_ie .dijitRuleContainerV{line-height:normal}.dj_gecko .dijitRuleContainerV{margin:0 0 1px}.dijitRuleMark{border:1px solid #000;line-height:0;height:100%}.dijitRuleMarkH{width:0;border-top-width:0!important;border-bottom-width:0!important}.dijitRuleLabelContainerH{text-align:center;display:inline-block}.dijitRuleLabelH{position:relative;left:-50%}.dijitRuleLabelV{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.dijitRuleMarkV{height:0;border-right-width:0!important;border-left-width:0!important;width:100%;left:0}.dj_ie .dijitRuleLabelContainerV{margin-top:-.55em}.dj_a11y .dijitSliderDisabled,.dj_a11y .dijitSliderReadOnly{opacity:.6}.dj_ie .dj_a11y .dijitSliderDisabled .dijitSliderBar,.dj_ie .dj_a11y .dijitSliderReadOnly .dijitSliderBar{filter:alpha(opacity=40)}.dj_a11y .dijitSlider .dijitSliderButtonContainer div{font-family:monospace;font-size:1em;line-height:1em;height:auto;width:auto;margin:0 4px}.dj_a11y .dijitButtonContents .dijitButtonText,.dj_a11y .dijitTab .tabLabel{display:inline!important}.dj_a11y .dijitSelect .dijitButtonText{display:inline-block!important}.dijitSelectError .dijitButtonContents .dijitButtonText{display:none!important}.dijitTextArea{width:100%;overflow-y:auto}.dijitTextArea[cols],.dj_ie .dijitTextAreaCols{width:auto}.dijitToolbarSeparator{height:18px;width:5px;padding:0 1px;margin:0}.dijitIEFixedToolbar{position:absolute;top:expression(eval((document.documentElement||document.body).scrollTop))}.dijitEditor{display:block}.dijitEditorDisabled,.dijitEditorReadOnly{color:gray}.dijitTimePickerItemInner{text-align:center;border:0;padding:2px 8px}.dijitTimePicker .dijitDownArrowButton{border-top:none!important}.dijitTimePickerMarker{color:#000}.dijitTimePickerItemSelected{font-weight:700;color:#333;background-color:#b7cdee}.dijitTimePickerItemHover{background-color:gray;color:#fff}.dijitTimePickerItemDisabled{color:gray;text-decoration:line-through}.dj_a11y .dijitTimePickerItemSelected .dijitTimePickerItemInner{border:4px solid #000}.dj_a11y .dijitTimePickerItemHover .dijitTimePickerItemInner{border:4px dashed #000}.dijitToggleButtonIconChar{display:none!important}.dj_a11y .dijitToggleButton .dijitToggleButtonIconChar{display:inline!important;visibility:hidden}.dj_ie6 .dijitToggleButtonIconChar,.dj_ie6 .tabStripButton .dijitButtonText{font-family:"Arial Unicode MS"}.dojoDndAvatarHeader:before,.flat .dijitCalendarIncrementControl{font-family:flat-icon;speak:none;font-style:normal;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dj_a11y .dijitToggleButtonChecked .dijitToggleButtonIconChar{display:inline!important;visibility:visible!important}.dijitArrowButtonChar{display:none!important}.dj_a11y .dijitArrowButtonChar{display:inline!important}.dj_a11y .dijitComboButton .dijitArrowButtonInner,.dj_a11y .dijitDropDownButton .dijitArrowButtonInner{display:none!important}.dijitSelectMenu .dijitMenuItemIcon,.flat .dijitA11ySideArrow{display:none}.dj_a11y .dijitSelect{border-collapse:separate!important;border-width:1px;border-style:solid}.flat .dijitCalendar,.flat .dijitMenuTable,.flat table.dijitComboButton{border-collapse:separate}.dj_ie .dijitSelect{vertical-align:middle}.dj_ie6 .dijitSelect .dijitValidationContainer,.dj_ie8 .dijitSelect .dijitButtonText{vertical-align:top}.dijitSelect .dijitSelectLabel,.dijitSelectLabel *,.dj_ie6 .dijitSpinner .dijitSpinnerButtonInner,.dj_ie6 .dijitTextBox .dijitArrowButtonInner,.dj_ie6 .dijitTextBox .dijitInputContainer,.dj_iequirks .dijitTextBox .dijitInputContainer{vertical-align:baseline}.dijitNumberTextBox{text-align:left;direction:ltr}.dijitNumberTextBox .dijitInputInner{text-align:inherit}.dijitToolbar .dijitSelect{margin:0}.dj_webkit .dijitToolbar .dijitSelect{padding-left:.3em}.dijitSelect .dijitButtonContents{padding:0;white-space:nowrap;text-align:left;border-style:none solid none none;border-width:0}.dijitSelectFixedWidth .dijitButtonContents{width:100%}.dj_ie6 .dijitSelectMenu .dijitMenuItemLabel,.dj_ie7 .dijitSelectMenu .dijitMenuItemLabel{position:static}.flat .dijitCalendarYearContainer,.flat .dijitCalendarYearLabel span{vertical-align:middle}.dijitSelectSelectedOption *{font-weight:400}.dijitSelectMenu{border-width:1px}.dijitSelectMenu .dijitMenuTable{margin:0;background-color:transparent}.dijitForceStatic{position:static!important}.dijitDisabled,.dijitDisabled *,.dijitReadOnly,.dijitReadOnly *{cursor:default}.dojoDndItem{padding:2px;-webkit-touch-callout:none;-webkit-user-select:none}.dojoDndHorizontal .dojoDndItem{#display:inline;display:inline-block}.dojoDndItemAfter,.dojoDndItemBefore{border:0 solid #369}.dojoDndItemBefore{border-width:2px 0 0;padding:0 2px 2px}.dojoDndItemAfter{border-width:0 0 2px;padding:2px 2px 0}.dojoDndHorizontal .dojoDndItemBefore{border-width:0 0 0 2px;padding:2px 2px 2px 0}.dojoDndHorizontal .dojoDndItemAfter{border-width:0 2px 0 0;padding:2px 0 2px 2px}.dj_gecko .dijitArrowButtonInner INPUT,.dj_gecko INPUT.dijitArrowButtonInner{-moz-user-focus:ignore}.dijitFocused .dijitMenuItemShortcutKey{text-decoration:underline}.flat .dijitCalendar{background-color:#fff;text-align:center;padding:4px;border:1px solid #9e9e9e;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2);box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2)}.flat .dijitCalendarMonthContainer th{text-align:center;line-height:20px;vertical-align:middle;margin:4px 0}.flat .dijitCalendarIncrementControl{font-weight:400;line-height:1;font-size:24px;border:1px solid transparent;padding:4px}.flat .dijitCalendarDecrease:before{content:"\f000"}.flat .dijitCalendarIncrease:before{content:"\f001"}.flat .dijitCalendarArrow:hover .dijitCalendarIncrementControl,.flat .dijitCalendarArrowHover .dijitCalendarIncrementControl,.flat .dijitCalendarNextYear:hover,.flat .dijitCalendarNextYearHover,.flat .dijitCalendarPreviousYear:hover,.flat .dijitCalendarPreviousYearHover{border-style:solid;border-width:1px;border-color:#9e9e9e;-webkit-border-radius:3px;border-radius:3px;line-height:20px;cursor:pointer;-webkit-transition:all 50ms linear;-moz-transition:all 50ms linear;-o-transition:all 50ms linear;-ms-transition:all 50ms linear;transition:all 50ms linear;background:#fff;padding:4px}.flat .dijitCalendarArrow:active .dijitCalendarIncrementControl,.flat .dijitCalendarArrowActive .dijitCalendarIncrementControl,.flat .dijitCalendarNextYear:active,.flat .dijitCalendarNextYearActive,.flat .dijitCalendarPreviousYear:active,.flat .dijitCalendarPreviousYearActive{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border-color:#b3b3b3}.flat .dijitCalendarContainer td,.flat .dijitCalendarContainer th{padding:4px}.flat .dijitCalendarDayLabelTemplate{text-align:center;border-bottom:#9e9e9e}.flat .dijitCalendarDayLabel{font-weight:700;text-align:center}.flat .dijitCalendarDateTemplate{font-size:.9em;letter-spacing:.05em;text-align:center}.flat .dijitCalendarDateTemplate .dijitCalendarDateLabel{text-decoration:none;display:block;padding:2px 4px;border:0;-webkit-border-radius:50%;border-radius:50%}.flat .dijitCalendarNextMonth .dijitCalendarDateLabel,.flat .dijitCalendarPreviousMonth .dijitCalendarDateLabel{color:#c2c2c2}.flat .dijitCalendarCurrentDate .dijitCalendarDateLabel{border-color:#2196f3}.flat .dijitCalendarEnabledDate:hover .dijitCalendarDateLabel,.flat .dijitCalendarHoveredDate .dijitCalendarDateLabel{background-color:#f2f2f2}.flat .dijitCalendarActiveDate .dijitCalendarDateLabel,.flat .dijitCalendarEnabledDate:active .dijitCalendarDateLabel{background-color:#e6e6e6}.flat .dijitCalendarSelectedDate .dijitCalendarDateLabel,.flat .dijitCalendarSelectedDate.dijitCalendarHoveredDate .dijitCalendarDateLabel{color:#fff;background-color:#2196f3}.flat .dijitCalendarDisabledDate .dijitCalendarDateLabel{opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitCalendarYearLabel{padding:4px 0 0;margin:0;font-size:1.15em}.flat .dijitCalendarNextYear,.flat .dijitCalendarPreviousYear,.flat .dijitCalendarSelectedYear{padding:4px}.flat .dijitCalendarSelectedYear{color:#2196f3;padding:0 4px}.flat .dijitCalendarNextYear,.flat .dijitCalendarPreviousYear{color:#2196f3;font-size:.9em;line-height:20px;border:1px solid transparent}.flat .dijitCalendar .dijitDropDownButton{margin:0}.flat .dijitCalendarMonthMenu{padding:8px 0}.flat .dijitCalendarMonthMenu .dijitCalendarMonthLabel,.flat .dijitColorPalette .dijitPaletteTable{padding:4px}.flat .dijitCalendarMonthMenu .dijitCalendarMonthLabelHover{color:#fff;background-color:#2196f3}.flat .dijitColorPalette{border:1px solid #9e9e9e;background-color:#fff;-webkit-border-radius:3px;border-radius:3px}.flat .dijitColorPalette .dijitColorPaletteSwatch{height:15px;width:15px;-webkit-border-radius:2px;border-radius:2px}.flat .dijitColorPalette .dijitPaletteImg{border:1px solid transparent;line-height:normal}.flat .dijitColorPalette .dijitPaletteCell:hover .dijitPaletteImg{border-color:#9e9e9e;-webkit-box-shadow:none;box-shadow:none;-webkit-border-radius:2px;border-radius:2px;-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-o-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2)}.flat .dijitColorPalette .dijitPaletteCell:active .dijitPaletteImg,.flat .dijitColorPalette .dijitPaletteTable .dijitPaletteCellSelected .dijitPaletteImg{border:1px solid #2196f3;-webkit-box-shadow:0 1px .5px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.2);box-shadow:0 1px .5px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.2);-webkit-border-radius:2px;border-radius:2px;-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-o-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2)}.dojoDndAvatarItem td>*,.flat .dijitDialog{-webkit-box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2)}.dijitPopup{-webkit-border-radius:3px;border-radius:3px}.dojoDndItem{border:1px solid transparent;cursor:pointer;-webkit-transition-duration:.25s;-moz-transition-duration:.25s;-o-transition-duration:.25s;-ms-transition-duration:.25s;transition-duration:.25s;-webkit-transition-property:background-color,border-color,opacity;-moz-transition-property:background-color,border-color,opacity;-o-transition-property:background-color,border-color,opacity;-ms-transition-property:background-color,border-color,opacity;transition-property:background-color,border-color,opacity}.dojoDndItemOver{cursor:pointer;background-color:#f5f5f5;-webkit-border-radius:3px;border-radius:3px}.dojoDndItemAnchor{background-color:transparent;border:1px dashed #2196f3;-webkit-border-radius:3px;border-radius:3px}.dojoDndItemBefore{background:0 0;padding-top:2px;border-top:1px solid #2196f3}.dojoDndItemAfter{background:0 0;padding-bottom:2px;border-bottom:1px solid #2196f3}table.dojoDndAvatar{display:block}.dojoDndAvatarHeader td{display:none}.dojoDndAvatarHeader:before{font-weight:400;line-height:1;font-size:16px;display:table-cell}.flat .dijitCheckedMenuItemIconChar,.flat .dijitDialogCloseIcon .closeText{display:none}.dojoDndCopy .dojoDndAvatarHeader:before,.dojoDndMove .dojoDndAvatarHeader:before{color:#dd2c00;content:"\f01c"}.dojoDndCopy .dojoDndAvatarCanDrop .dojoDndAvatarHeader:before,.dojoDndMove .dojoDndAvatarCanDrop .dojoDndAvatarHeader:before{color:#43a047;content:"\f008"}.dojoDndAvatarItem{-webkit-border-radius:3px;border-radius:3px}.dojoDndAvatarItem td>*{padding:4px 8px;list-style-type:none;background-color:#fff;box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2)}.flat .dijitDialog{background-color:#fff;border:1px solid #9e9e9e;-webkit-border-radius:3px;border-radius:3px;box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2)}.flat .dijitMenu,.flat .dijitTooltipDialog .dijitTooltipContainer{-webkit-box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2)}.flat .dijitDialogPaneContent{background-color:#fff;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;padding:8px;position:relative}.flat .dijitDialogPaneActionBar{padding-top:8px;text-align:right;position:relative}.flat .dijitDialogPaneActionBar .dijitButton{float:none}.flat .dijitTooltipDialog .dijitDialogPaneActionBar{-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;margin:8px 0 0}.flat .dijitDialogTitleBar{line-height:20px;border-bottom:1px solid #e0e0e0;padding:8px 12px;-webkit-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.flat .dijitDialogTitle{font-size:1.1em;font-weight:700}.flat .dijitDialogCloseIcon,.flat .dijitMenuExpand{font-family:flat-icon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.flat .dijitDialogCloseIcon{width:20px;height:20px;text-align:center;position:absolute;top:8px;right:12px;line-height:1;font-size:16px;opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitDialogCloseIcon:before{content:"\f00e";font-size:20px}.flat .dijitDialogCloseIconActive,.flat .dijitDialogCloseIconHover{opacity:1;-ms-filter:none;filter:none}.flat .dijitDialogUnderlay{background:#000;opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitTooltip,.flat .dijitTooltipDialog{background:0 0}.flat .dijitTooltipContainer{background-color:#424242;opacity:1;-ms-filter:none;filter:none;padding:4px 8px;-webkit-border-radius:3px;border-radius:3px}.flat .dijitTooltip .dijitTooltipContainer{color:#fff;border:0}.flat .dijitTooltipConnector{z-index:2;width:auto;height:auto;opacity:1;-ms-filter:none;filter:none}.flat .dijitTooltipABRight .dijitTooltipConnector{left:auto!important;right:8px}.flat .dijitTooltipBelow{padding-top:4px}.flat .dijitTooltipBelow .dijitTooltipConnector{top:0;left:8px;border-bottom:4px solid #424242;border-left:4px solid transparent;border-right:4px solid transparent;border-top:0}.flat .dijitTooltipAbove{padding-bottom:4px}.flat .dijitTooltipAbove .dijitTooltipConnector{bottom:0;left:8px;border-top:4px solid #424242;border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:0}.flat .dijitTooltipLeft{padding-right:4px}.flat .dijitTooltipLeft .dijitTooltipConnector{right:0;border-left:4px solid #424242;border-bottom:4px solid transparent;border-top:4px solid transparent;border-right:0}.flat .dijitTooltipRight{padding-left:4px}.flat .dijitTooltipRight .dijitTooltipConnector{left:0;border-bottom:4px solid transparent;border-top:4px solid transparent;border-right:4px solid #424242}.flat .dijitTooltipDialog .dijitTooltipContainer{background:#fff;border:1px solid #9e9e9e;-webkit-border-radius:3px;border-radius:3px;box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2);opacity:1;-ms-filter:none;filter:none}.flat .dijitTooltipDialog.dijitTooltipBelow{padding-top:6px}.flat .dijitTooltipDialog.dijitTooltipAbove{padding-bottom:6px}.flat .dijitTooltipDialog.dijitTooltipLeft{padding-right:6px}.flat .dijitTooltipDialog.dijitTooltipRight{padding-left:6px}.flat .dijitTooltipDialog .dijitTooltipConnector{height:0;width:0;position:absolute;z-index:2;opacity:1;-ms-filter:none;filter:none}.flat .dijitTooltipDialog .dijitTooltipConnector:after{content:"";height:0;width:0;position:absolute}.flat .dijitTooltipDialog.dijitTooltipAbove .dijitTooltipConnector{border-color:#9e9e9e transparent transparent;border-width:7px 7px 0;border-style:solid}.flat .dijitTooltipDialog.dijitTooltipAbove .dijitTooltipConnector:after{border-color:#fff transparent transparent;border-width:6px 6px 0;border-style:solid;left:-6px;top:-7px}.flat .dijitTooltipDialog.dijitTooltipBelow .dijitTooltipConnector{border-color:transparent transparent #9e9e9e;border-width:0 7px 7px;border-style:solid}.flat .dijitTooltipDialog.dijitTooltipBelow .dijitTooltipConnector:after{border-color:transparent transparent #fff;border-width:0 6px 6px;border-style:solid;left:-6px;bottom:-7px}.flat .dijitTooltipDialog.dijitTooltipLeft .dijitTooltipConnector{border-color:transparent transparent transparent #9e9e9e;border-width:7px 0 7px 7px;border-style:solid}.flat .dijitTooltipDialog.dijitTooltipLeft .dijitTooltipConnector:after{border-color:transparent transparent transparent #fff;border-width:6px 0 6px 6px;border-style:solid;top:-6px;left:-7px}.flat .dijitTooltipDialog.dijitTooltipRight .dijitTooltipConnector{border-color:transparent #9e9e9e transparent transparent;border-width:7px 7px 7px 0;border-style:solid}.flat .dijitTooltipDialog.dijitTooltipRight .dijitTooltipConnector:after{border-color:transparent #fff transparent transparent;border-width:6px 6px 6px 0;border-style:solid;top:-6px;right:-7px}.flat .dijitEditor{background-color:#fff;border:1px solid #9e9e9e;-webkit-border-radius:3px;border-radius:3px}.flat .dijitEditor .dijitEditorIFrameContainer{border:1px solid transparent;border-top:1px solid #9e9e9e;padding:4px 8px;-webkit-transition:border .2s linear 0s;-moz-transition:border .2s linear 0s;-o-transition:border .2s linear 0s;-ms-transition:border .2s linear 0s;transition:border .2s linear 0s}.flat .dijitAccordionTitle,.flat .dijitTitlePaneTitle{-webkit-transition:all 50ms linear;-moz-transition:all 50ms linear;-ms-transition:all 50ms linear;cursor:pointer}.flat .dijitEditorFocused .dijitEditorIFrameContainer,.flat .dijitEditorFocused .dijitEditorIFrameContainer .dijitEditorIFrame,.flat .dijitEditorHover .dijitEditorIFrameContainer,.flat .dijitEditorHover .dijitEditorIFrameContainer .dijitEditorIFrame{border:1px solid #2196f3}.flat .dijitEditorDisabled{border:1px solid #9e9e9e;opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitEditorDisabled .dijitEditorIFrame,.flat .dijitEditorDisabled .dijitEditorIFrameContainer,.flat .dijitEditorDisabled .dijitEditorIFrameContainer .dijitEditorIFrame{background-color:#f5f5f5;border:1px solid transparent}.flat .dijitInlineEditBoxDisplayMode{border:1px dashed transparent;padding:4px 6px}.flat .dijitInlineEditBoxDisplayModeHover{background-color:transparent;border:1px dashed #2196f3}.flat .dijitInlineEditBoxDisplayModeDisabled{opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitMenu{background:#fff;border:1px solid #9e9e9e;-webkit-border-radius:3px;border-radius:3px;margin:0;box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2)}.flat .dijitComboBoxMenu,.flat .dijitMenuTable{padding:8px 0}.flat .dijitComboBoxMenu{margin-left:0;background-image:none}.flat .dijitMenuTable{border-spacing:0 0}.flat .dijitMenuItem,.flat .dijitMenuItem td{line-height:20px;padding:8px;white-space:nowrap}.flat .dijitMenuItemActive,.flat .dijitMenuItemActive td,.flat .dijitMenuItemHover,.flat .dijitMenuItemHover td,.flat .dijitMenuItemSelected,.flat .dijitMenuItemSelected td{color:#fff;background-color:#2196f3}.flat .dijitMenuItemDisabled{color:#9e9e9e}.flat .dijitMenuItemDisabled.dijitMenuItemSelected,.flat .dijitMenuItemDisabled.dijitMenuItemSelected td{color:#f2f2f2;background:#6fbbf7}.flat .dijitMenuSeparatorTop{height:auto;margin-top:1px;border-bottom:1px solid #9e9e9e}.flat .dijitMenuSeparatorBottom{height:auto;margin-bottom:1px;border-top:1px solid transparent}.flat td.dijitMenuItemIconCell{padding:4px;margin:0 0 0 4px;text-align:center}.flat .dijitMenuExpand{line-height:1;font-size:16px}.flat .dijitMenuExpand:before{content:"\f001"}.flat .dijitMenuNextButton,.flat .dijitMenuPreviousButton{font-style:italic}.flat .dijitAccordionTitle .arrowTextDown,.flat .dijitAccordionTitle .arrowTextUp,.flat .dijitTabCloseButton,.flat .dijitTitlePane .dijitArrowNode,.flat .dijitTreeExpando{font-family:flat-icon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.flat .dijitMenuBar{margin:0;padding:0;background-color:#f5f5f5}.flat .dijitMenuBar .dijitMenuItem{padding:8px 12px;margin:0}.flat .dijitMenuBar .dijitMenuItemActive,.flat .dijitMenuBar .dijitMenuItemActive.dijitMenuItemSelected,.flat .dijitMenuBar .dijitMenuItemHover,.flat .dijitMenuBar .dijitMenuItemHover.dijitMenuItemSelected,.flat .dijitMenuBar .dijitMenuItemSelected{color:#fff;background-color:#2196f3}.flat .dijitMenuBar .dijitMenuItemDisabled.dijitMenuItemSelected{color:#f2f2f2;background:#6fbbf7}.flat .dijitMenuPopup,.flat .dijitMenuPopup .dijitMenu{border-top-left-radius:0;border-top-right-radius:0}.flat .dijitMenuPopup .dijitMenuItem,.flat .dijitMenuPopup .dijitMenuItem td{padding:8px}.flat .dijitProgressBar{background-color:#e0e0e0;border:0;-webkit-border-radius:3px;border-radius:3px}.flat .dijitProgressBarTile{background:url(images/progressBarStrips.png) top repeat-x;-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.flat .dijitProgressBarFull{background-color:#2196f3;-webkit-transition-property:width;-moz-transition-property:width;-o-transition-property:width;-ms-transition-property:width;transition-property:width;-webkit-transition-duration:.25s;-moz-transition-duration:.25s;-o-transition-duration:.25s;-ms-transition-duration:.25s;transition-duration:.25s;height:100%}.flat .dijitProgressBar.alt-primary .dijitProgressBarFull{background-color:#1e88e5}.flat .dijitProgressBar.alt-success .dijitProgressBarFull{background-color:#43a047}.flat .dijitProgressBar.alt-info .dijitProgressBarFull{background-color:#03a9f4}.flat .dijitProgressBar.alt-warning .dijitProgressBarFull{background-color:#fb8c00}.flat .dijitProgressBar.alt-danger .dijitProgressBarFull{background-color:#e53935}.flat .dijitProgressBar.alt-inverse .dijitProgressBarFull{background-color:#616161}.flat .dijitProgressBarLabel{margin-top:.2em;margin-bottom:.2em;color:#fff;font-size:1em;text-shadow:.1em .1em 1px #424242}@-moz-keyframes progress-bar-stripes{from{background-position:75px 0}to{background-position:0 0}}@-webkit-keyframes progress-bar-stripes{from{background-position:75px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:75px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:75px 0}to{background-position:0 0}}.dijitTimePickerPopup{-webkit-box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2);box-shadow:0 7px 3px -4px rgba(0,0,0,.3),0 8px 8px rgba(0,0,0,.2);height:200px}.dijitTimePicker{background-color:#fff;padding:4px 0;border:1px solid #9e9e9e;-webkit-border-radius:3px;border-radius:3px}.dijitTimePickerItem{margin:0}.dijitTimePickerTick{color:#9e9e9e;border:0}.dijitTimePickerMarker{background-color:transparent;white-space:nowrap;border:0}.dijitTimePickerMarkerHover,.dijitTimePickerMarkerSelected,.dijitTimePickerTickHover,.dijitTimePickerTickSelected{background:#f2f2f2;color:#424242}.dijitTimePickerMarker .dijitTimePickerItemInner,.dijitTimePickerTick .dijitTimePickerItemInner{padding:8px;margin:0}.flat .dijitTitlePaneTitle{border-style:solid;border-width:1px;border-color:#9e9e9e;padding:4px;line-height:20px;-o-transition:all 50ms linear;transition:all 50ms linear;background:#fff;-webkit-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.flat .dijitTitlePaneTitleHover{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border-color:#d9d9d9}.flat .dijitTitlePaneTitleActive{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border-color:#b3b3b3}.flat .dijitTitlePane .dijitArrowNode{line-height:1;font-size:18px;text-align:center}.flat .dijitTitlePane .dijitArrowNode:before{content:"\f007"}.flat .dijitTitlePane .dijitClosed{-webkit-border-radius:3px;border-radius:3px}.flat .dijitTitlePane .dijitClosed .dijitArrowNode:before{content:"\f006"}.flat .dijitTitlePaneContentOuter{background-color:#fff;border:1px solid #9e9e9e;border-top:none;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.flat .dijitTitlePaneContentInner{padding:8px}.flat .dijitTitlePaneTextNode{margin-left:8px;margin-right:8px;vertical-align:text-top}.flat .dijitToolbar{background-color:#f5f5f5;padding:4px;zoom:1}.flat .dijitToolbar label{padding:8px}.flat .dijitToolbar .dijitButton,.flat .dijitToolbar .dijitComboButton,.flat .dijitToolbar .dijitDropDownButton,.flat .dijitToolbar .dijitToggleButton{margin-right:4px}.flat .dijitToolbar .dijitButton .dijitButtonNode,.flat .dijitToolbar .dijitComboBox .dijitButtonNode,.flat .dijitToolbar .dijitComboButton .dijitButtonNode,.flat .dijitToolbar .dijitDropDownButton .dijitButtonNode,.flat .dijitToolbar .dijitToggleButton .dijitButtonNode{border-color:transparent;padding:4px;background-color:transparent;-webkit-border-radius:3px;border-radius:3px;-webkit-transition-property:background-color;-moz-transition-property:background-color;-o-transition-property:background-color;-ms-transition-property:background-color;transition-property:background-color;-webkit-transition-duration:.3s;-moz-transition-duration:.3s;-o-transition-duration:.3s;-ms-transition-duration:.3s;transition-duration:.3s}.flat .dijitToolbar .dijitComboButton .dijitStretch{-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.flat .dijitToolbar .dijitComboButton .dijitArrowButton{-webkit-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.flat .dijitToolbar .dijitComboBox .dijitButtonNode{padding:0 8px}.flat .dijitToolbar .dijitComboBox .dijitInputInner{padding:0}.flat .dijitToolbar .dijitDropDownButton .dijitArrowButtonInner{margin-left:4px}.flat .dijitToolbar .dijitButtonHover .dijitButtonNode,.flat .dijitToolbar .dijitComboButtonHover .dijitButtonNode,.flat .dijitToolbar .dijitDropDownButtonHover .dijitButtonNode,.flat .dijitToolbar .dijitToggleButtonHover .dijitButtonNode{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border:1px solid #9e9e9e}.flat .dijitToolbar .dijitButtonActive .dijitButtonNode,.flat .dijitToolbar .dijitDropDownButtonActive .dijitButtonNode,.flat .dijitToolbar .dijitToggleButtonActive .dijitButtonNode,.flat .dijitToolbar .dijitToggleButtonChecked .dijitButtonNode{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border:1px solid #9e9e9e}.flat .dijitToolbarSeparator{width:1px;height:20px;background-color:#9e9e9e;padding:0;margin:0 4px}.flat .dijitDisabled .dijitToolbar{background-color:#f5f5f5;border-bottom:1px solid #9e9e9e}.flat .dijitTreeIsRoot{background-color:transparent}.flat .dijitTreeRowActive,.flat .dijitTreeRowHover{background-color:#f2f2f2;border-color:transparent}.flat .dijitTreeNode .dojoDndItemAfter,.flat .dijitTreeNode .dojoDndItemBefore,.flat .dijitTreeRow{padding:8px 0;border:0 transparent;line-height:20px;-webkit-transition-property:background-color;-moz-transition-property:background-color;-o-transition-property:background-color;-ms-transition-property:background-color;transition-property:background-color;-webkit-transition-duration:.15s;-moz-transition-duration:.15s;-o-transition-duration:.15s;-ms-transition-duration:.15s;transition-duration:.15s;-webkit-transition-timing-function:ease-out;-moz-transition-timing-function:ease-out;-o-transition-timing-function:ease-out;-ms-transition-timing-function:ease-out;transition-timing-function:ease-out}.flat .dijitTreeRowHover{-webkit-transition-duration:.15s;-moz-transition-duration:.15s;-o-transition-duration:.15s;-ms-transition-duration:.15s;transition-duration:.15s}.flat .dijitTreeRowActive.dijitTreeRowSelected,.flat .dijitTreeRowHover.dijitTreeRowSelected,.flat .dijitTreeRowSelected{color:#fff;background-color:#2196f3;border-color:transparent}.flat .dijitTreeRowActive.dijitTreeRowSelected .dijitTreeExpando,.flat .dijitTreeRowHover.dijitTreeRowSelected .dijitTreeExpando,.flat .dijitTreeRowSelected .dijitTreeExpando{color:#fff}.flat .dijitTreeExpando{font-size:16px;width:16px;height:16px;line-height:16px;text-align:center;margin-left:4px;margin-right:4px;color:#2196f3;vertical-align:middle}.flat .dijitTreeExpandoOpened:before{content:"\e60d";cursor:pointer}.flat .dijitTreeExpandoClosed:before{content:"\e60a";cursor:pointer}.flat .dijitTreeExpandoLoading:before{content:"\e60e";-webkit-animation:spinning 2s linear infinite;-moz-animation:spinning 2s linear infinite;-o-animation:spinning 2s linear infinite;-ms-animation:spinning 2s linear infinite;animation:spinning 2s linear infinite}.dj_ie8 .dijitTreeExpandoLoading:before,.dj_ie9 .dijitTreeExpandoLoading:before,.flat .dijitTab:before{content:""}.dj_ie8 .dijitTreeExpandoLoading,.dj_ie9 .dijitTreeExpandoLoading{background:url(images/loadingAnimation.gif) no-repeat}@-moz-keyframes spinning{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes spinning{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes spinning{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinning{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}.flat .dijitAccordionContainer{border:0;-webkit-border-radius:3px;border-radius:3px}.flat .dijitAccordionInnerContainer{background-color:#fff;border:1px solid #9e9e9e;-webkit-transition-property:background-color,border;-moz-transition-property:background-color,border;-o-transition-property:background-color,border;-ms-transition-property:background-color,border;transition-property:background-color,border;-webkit-transition-duration:.3s;-moz-transition-duration:.3s;-o-transition-duration:.3s;-ms-transition-duration:.3s;transition-duration:.3s;-webkit-transition-timing-function:linear;-moz-transition-timing-function:linear;-o-transition-timing-function:linear;-ms-transition-timing-function:linear;transition-timing-function:linear}.flat .dijitAccordionTitle{padding:4px;line-height:20px;-o-transition:all 50ms linear;transition:all 50ms linear;background:#fff;border:0;-webkit-border-radius:3px;border-radius:3px}.flat .dijitAccordionTitle .arrowTextDown,.flat .dijitAccordionTitle .arrowTextUp{display:none;line-height:1;text-align:center;font-size:0}.flat .dijitAccordionTitle .arrowTextDown:before,.flat .dijitAccordionTitle .arrowTextUp:before{content:"\f007";font-size:18px}.flat .dijitAccordionTitle .arrowTextUp{display:block}.flat .dijitAccordionTitle .arrowTextUp:before{content:"\f006"}.flat .dijitAccordionInnerContainerHover .dijitAccordionTitle{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border-color:#d9d9d9}.flat .dijitAccordionInnerContainerActive .dijitAccordionTitle{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border-color:#b3b3b3}.flat .dijitAccordionInnerContainerSelected{border:0}.flat .dijitAccordionInnerContainerSelected .dijitAccordionTitle{color:#fff;background-color:#2196f3;-webkit-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.flat .dijitAccordionInnerContainerSelected .dijitAccordionTitle .arrowTextUp{display:none}.flat .dijitAccordionInnerContainerSelected .dijitAccordionTitle .arrowTextDown{display:block}.flat .dijitAccordionContainer .dijitAccordionChildWrapper{background-color:#fff;border:1px solid #9e9e9e;border-top:0 none;position:relative;z-index:1;clear:both;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.flat .dijitAccordionInnerContainer,.flat .dijitAccordionInnerContainer .dijitAccordionTitle,.flat .dijitAccordionInnerContainer:not(:last-child) .dijitAccordionChildWrapper{-webkit-border-radius:0;border-radius:0}.flat .dijitAccordionInnerContainer+.dijitAccordionInnerContainer{margin-top:0;position:relative;border-top:0 none}.flat .dijitAccordionInnerContainer+.dijitAccordionInnerContainerSelected:last-child .dijitAccordionTitle{-webkit-border-radius:0;border-radius:0}.flat .dijitAccordionInnerContainer:first-child,.flat .dijitAccordionInnerContainer:first-child .dijitAccordionTitle{-webkit-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.flat .dijitAccordionInnerContainer:last-child,.flat .dijitAccordionInnerContainer:last-child .dijitAccordionTitle{-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.flat .dijitBorderContainer{padding:5px}.flat .dijitBorderContainer-child,.flat .dijitSplitContainer-child{border:1px solid #9e9e9e}.flat .dijitBorderContainer-dijitAccordionContainer,.flat .dijitBorderContainer-dijitTabContainerBottom,.flat .dijitBorderContainer-dijitTabContainerLeft,.flat .dijitBorderContainer-dijitTabContainerRight,.flat .dijitBorderContainer-dijitTabContainerTop{border:none}.flat .dijitBorderContainer-dijitBorderContainer{border:0;padding:0}.flat .dijitGutterH,.flat .dijitSplitterH{background:0 0;border:0;height:5px}.flat .dijitSplitterH .dijitSplitterThumb{background:#9e9e9e;height:1px;top:2px;width:19px}.flat .dijitGutterV,.flat .dijitSplitterV{background:0 0;border:0;width:5px;margin:0}.flat .dijitSplitterV .dijitSplitterThumb{background:#9e9e9e;height:19px;left:2px;width:1px;margin:0}.flat .dijitSplitterHHover,.flat .dijitSplitterVHover{font-size:1px;background:#f2f2f2}.flat .dijitSplitterHHover .dijitSplitterThumb,.flat .dijitSplitterVHover .dijitSplitterThumb{background:#767676}.flat .dijitSplitterHActive,.flat .dijitSplitterVActive{font-size:1px;background:#f2f2f2}.flat .dijitSplitterHActive .dijitSplitterThumb,.flat .dijitSplitterVActive .dijitSplitterThumb{background:#767676}.flat .dijitContentPane{background-color:#fff;padding:8px}.flat .dijitAccordionContainer-dijitContentPane,.flat .dijitTabContainerBottom-dijitContentPane,.flat .dijitTabContainerLeft-dijitContentPane,.flat .dijitTabContainerRight-dijitContentPane,.flat .dijitTabContainerTop-dijitContentPane{background-color:#fff;padding:8px;left:0!important;top:0!important}.flat .dijitTabContainer{-webkit-border-radius:3px;border-radius:3px}.flat .dijitTabPaneWrapper{background:#fff;border:1px solid #9e9e9e;margin:0;padding:0;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}.flat .dijitTabContainerBottom-tabs,.flat .dijitTabContainerLeft-tabs,.flat .dijitTabContainerRight-tabs,.flat .dijitTabContainerTop-tabs{border:none}.flat .dijitTabSpacer{display:none}.flat .dijitTab{border:1px solid transparent;background-color:#fff;text-align:center;-webkit-transition-property:background,padding,margin;-moz-transition-property:background,padding,margin;-o-transition-property:background,padding,margin;-ms-transition-property:background,padding,margin;transition-property:background,padding,margin;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;-o-transition-duration:.2s;-ms-transition-duration:.2s;transition-duration:.2s;-webkit-transition-timing-function:ease;-moz-transition-timing-function:ease;-o-transition-timing-function:ease;-ms-transition-timing-function:ease;transition-timing-function:ease;position:relative;z-index:0}.flat .dijitTab:before{display:block;position:absolute}.flat .dijitTabContainerTabListNested .dijitTabChecked.dijitTabActive:before,.flat .dijitTabContainerTabListNested .dijitTabChecked.dijitTabHover:before,.flat .dijitTabContainerTabListNested .dijitTabChecked:before,.flat .dijitToggleButton .dijitCheckBoxIcon{display:none}.flat .dijitTabHover{background-color:#f2f2f2}.flat .dijitTabActive{background-color:#e6e6e6}.flat .dijitTabChecked{border:1px solid #9e9e9e;z-index:1}.flat .dijitTabChecked.dijitTabActive,.flat .dijitTabChecked.dijitTabHover{border:1px solid #9e9e9e;background-color:#fff;color:#424242}.flat .dijitTabDisabled{opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitTabCloseButton{line-height:1;font-size:1em;vertical-align:middle;margin-left:4px;opacity:.35;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=35)";filter:alpha(opacity=35)}.flat .dijitArrowButton,.flat .dijitDropDownButton .dijitArrowButtonInner,.flat .dijitTabStripIcon{font-family:flat-icon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.flat .dijitTabCloseButton:before{content:"\f00e"}.flat .dijitTabCloseButtonHover{opacity:.75;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=75)";filter:alpha(opacity=75)}.flat .dijitTabCloseButtonActive{opacity:1;-ms-filter:none;filter:none}.flat .dijitTabContainerTop-tabs .dijitTab{margin-right:0;padding:6px 16px;border-bottom-color:#9e9e9e;border-left:none;border-right:none}.flat .dijitTabContainerTop-tabs .dijitTabChecked{border-bottom:1px solid #fff;border-left:1px solid #9e9e9e;border-right:1px solid #9e9e9e}.flat .dijitTabContainerTop-tabs .dijitTabChecked:before{height:3px;background:#2196f3;top:-1px;left:-1px;right:-1px}.flat .dijitTabListContainer-bottom .dijitTab,.flat .dijitTabListContainer-top .dijitTab{top:0}.flat .dijitTabListContainer-top{margin-top:1px}.flat .dijitTabPaneWrapper.dijitTabContainerBottom-container{-webkit-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.flat .dijitTabContainerBottom-tabs .dijitTab{margin-right:0;padding:6px 16px;border-top-color:#9e9e9e;border-left:none;border-right:none}.flat .dijitTabContainerBottom-tabs .dijitTabChecked{border-top:1px solid #fff;border-left:1px solid #9e9e9e;border-right:1px solid #9e9e9e}.flat .dijitTabContainerBottom-tabs .dijitTabChecked:before{height:3px;background:#2196f3;bottom:-1px;left:-1px;right:-1px}.flat .dijitTabListContainer-bottom{margin-top:-1px}.flat .dijitTabPaneWrapper.dijitTabContainerLeft-container{-webkit-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.flat .dijitTabContainerLeft-tabs .dijitTab{margin-bottom:0;padding:8px 12px;border-right-color:#9e9e9e;border-top:none;border-bottom:none}.flat .dijitTabContainerLeft-tabs .dijitTabChecked{border-right:1px solid #fff;border-top:1px solid #9e9e9e;border-bottom:1px solid #9e9e9e}.flat .dijitTabContainerLeft-tabs .dijitTabChecked:before{width:3px;background:#2196f3;bottom:-1px;left:-1px;top:-1px}.flat .dijitTabPaneWrapper.dijitTabContainerRight-container{-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.flat .dijitTabContainerRight-tabs .dijitTab{margin-bottom:0;padding:8px 12px;border-left-color:#9e9e9e;border-top:none;border-bottom:none}.flat .dijitTabContainerRight-tabs .dijitTabChecked{border-left:1px solid #fff;border-top:1px solid #9e9e9e;border-bottom:1px solid #9e9e9e}.flat .dijitTabContainerRight-tabs .dijitTabChecked:before{width:3px;background:#2196f3;bottom:-1px;right:-1px;top:-1px}.flat .tabStripButton{background-color:#fff;border:1px solid transparent;-webkit-transition-property:background-color;-moz-transition-property:background-color;-o-transition-property:background-color;-ms-transition-property:background-color;transition-property:background-color}.flat .dijitTabListContainer-bottom .tabStripButton,.flat .dijitTabListContainer-top .tabStripButton{padding:4px 8px;margin-left:0;margin-right:0}.flat .dijitTabListContainer-top .tabStripButton{margin-bottom:1px}.flat .dijitTabListContainer-bottom .tabStripButton{margin-top:1px}.flat .tabStripButtonHover{background-color:#f2f2f2}.flat .tabStripButtonActive{background-color:#e6e6e6}.flat .dijitTabStripIcon{line-height:1;color:#2196f3;vertical-align:middle}.flat .dijitTabStripIcon:before{content:"\f004"}.flat .dijitTabStripSlideRightIcon:before{content:"\f005"}.flat .dijitTabStripMenuIcon:before{content:"\f006"}.flat .dijitTabListContainer-bottom .tabStripButtonDisabled,.flat .dijitTabListContainer-top .tabStripButtonDisabled{opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitTabContainerNested .dijitTabListWrapper{height:auto}.flat .dijitTabContainerTabListNested .dijitTab{color:#2196f3;margin:4px;padding:4px 8px;border:1px solid transparent;-webkit-border-radius:3px;border-radius:3px;-webkit-transition-property:background-color,border-color;-moz-transition-property:background-color,border-color;-o-transition-property:background-color,border-color;-ms-transition-property:background-color,border-color;transition-property:background-color,border-color;-webkit-transition-duration:.3s;-moz-transition-duration:.3s;-o-transition-duration:.3s;-ms-transition-duration:.3s;transition-duration:.3s}.flat .dijitTabContainerTabListNested .dijitTabHover{background-color:#f2f2f2}.flat .dijitTabContainerTabListNested .dijitTabActive{color:#2196f3;background-color:#e6e6e6}.flat .dijitTabContainerTabListNested .dijitTabChecked,.flat .dijitTabContainerTabListNested .dijitTabChecked.dijitTabActive,.flat .dijitTabContainerTabListNested .dijitTabChecked.dijitTabHover{color:#fff;background-color:#2196f3}.flat .dijitTabContainerTabListNested.dijitTabContainerBottom-tabs .dijitTab,.flat .dijitTabContainerTabListNested.dijitTabContainerTop-tabs .dijitTab{margin-right:4px}.flat .dijitTabContainerTabListNested.dijitTabContainerLeft-tabs .dijitTab,.flat .dijitTabContainerTabListNested.dijitTabContainerRight-tabs .dijitTab{margin-bottom:4px}.flat .dijitTabPaneWrapperNested{border:none;-webkit-box-shadow:none;box-shadow:none}.flat .dijitButtonText{padding:0 4px;text-align:center}.flat .dijitButton .dijitButtonNode,.flat .dijitComboButton .dijitButtonNode,.flat .dijitDropDownButton .dijitButtonNode,.flat .dijitToggleButton .dijitButtonNode{border-style:solid;border-width:1px;border-color:#9e9e9e;padding:4px;-webkit-border-radius:3px;border-radius:3px;line-height:20px;cursor:pointer;-webkit-transition:all 50ms linear;-moz-transition:all 50ms linear;-o-transition:all 50ms linear;-ms-transition:all 50ms linear;transition:all 50ms linear;background:#fff}.flat .dijitButton.alt-primary .dijitButtonNode,.flat .dijitComboBox.alt-primary .dijitButtonNode,.flat .dijitComboButton.alt-primary .dijitButtonNode,.flat .dijitDropDownButton.alt-primary .dijitButtonNode,.flat .dijitSelect.alt-primary .dijitButtonContents,.flat .dijitSelect.alt-primary .dijitButtonNode,.flat .dijitSpinner.alt-primary .dijitArrowButton,.flat .dijitToggleButton.alt-primary .dijitButtonNode{background:#1e88e5;color:#fff;border-color:#166fbd}.flat .dijitComboButton.alt-primary .dijitStretch{border-right-color:#166fbd}.flat .dijitComboButtonRtl.alt-primary .dijitStretch{border-left-color:#166fbd}.flat .dijitButton.alt-success .dijitButtonNode,.flat .dijitComboBox.alt-success .dijitButtonNode,.flat .dijitComboButton.alt-success .dijitButtonNode,.flat .dijitDropDownButton.alt-success .dijitButtonNode,.flat .dijitSelect.alt-success .dijitButtonContents,.flat .dijitSelect.alt-success .dijitButtonNode,.flat .dijitSpinner.alt-success .dijitArrowButton,.flat .dijitToggleButton.alt-success .dijitButtonNode{background:#43a047;color:#fff;border-color:#37823a}.flat .dijitComboButton.alt-success .dijitStretch{border-right-color:#37823a}.flat .dijitComboButtonRtl.alt-success .dijitStretch{border-left-color:#37823a}.flat .dijitButton.alt-info .dijitButtonNode,.flat .dijitComboBox.alt-info .dijitButtonNode,.flat .dijitComboButton.alt-info .dijitButtonNode,.flat .dijitDropDownButton.alt-info .dijitButtonNode,.flat .dijitSelect.alt-info .dijitButtonContents,.flat .dijitSelect.alt-info .dijitButtonNode,.flat .dijitSpinner.alt-info .dijitArrowButton,.flat .dijitToggleButton.alt-info .dijitButtonNode{background:#03a9f4;color:#fff;border-color:#028ac7}.flat .dijitComboButton.alt-info .dijitStretch{border-right-color:#028ac7}.flat .dijitComboButtonRtl.alt-info .dijitStretch{border-left-color:#028ac7}.flat .dijitButton.alt-warning .dijitButtonNode,.flat .dijitComboBox.alt-warning .dijitButtonNode,.flat .dijitComboButton.alt-warning .dijitButtonNode,.flat .dijitDropDownButton.alt-warning .dijitButtonNode,.flat .dijitSelect.alt-warning .dijitButtonContents,.flat .dijitSelect.alt-warning .dijitButtonNode,.flat .dijitSpinner.alt-warning .dijitArrowButton,.flat .dijitToggleButton.alt-warning .dijitButtonNode{background:#fb8c00;color:#fff;border-color:#cd7200}.flat .dijitComboButton.alt-warning .dijitStretch{border-right-color:#cd7200}.flat .dijitComboButtonRtl.alt-warning .dijitStretch{border-left-color:#cd7200}.flat .dijitButton.alt-danger .dijitButtonNode,.flat .dijitComboBox.alt-danger .dijitButtonNode,.flat .dijitComboButton.alt-danger .dijitButtonNode,.flat .dijitDropDownButton.alt-danger .dijitButtonNode,.flat .dijitSelect.alt-danger .dijitButtonContents,.flat .dijitSelect.alt-danger .dijitButtonNode,.flat .dijitSpinner.alt-danger .dijitArrowButton,.flat .dijitToggleButton.alt-danger .dijitButtonNode{background:#e53935;color:#fff;border-color:#cc1e1a}.flat .dijitComboButton.alt-danger .dijitStretch{border-right-color:#cc1e1a}.flat .dijitComboButtonRtl.alt-danger .dijitStretch{border-left-color:#cc1e1a}.flat .dijitButton.alt-inverse .dijitButtonNode,.flat .dijitComboBox.alt-inverse .dijitButtonNode,.flat .dijitComboButton.alt-inverse .dijitButtonNode,.flat .dijitDropDownButton.alt-inverse .dijitButtonNode,.flat .dijitSelect.alt-inverse .dijitButtonContents,.flat .dijitSelect.alt-inverse .dijitButtonNode,.flat .dijitSpinner.alt-inverse .dijitArrowButton,.flat .dijitToggleButton.alt-inverse .dijitButtonNode{background:#616161;color:#fff;border-color:#4f4f4f}.flat .dijitComboButton.alt-inverse .dijitStretch{border-right-color:#4f4f4f}.flat .dijitComboBoxRtlDisabled.alt-primary .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-primary .dijitStretch,.flat .dijitDateTextBoxRtlDisabled.alt-primary .dijitButtonNode,.flat .dijitTimeTextBoxRtlDisabled.alt-primary .dijitButtonNode{border-right-color:#50a2eb}.flat .dijitComboButtonRtl.alt-inverse .dijitStretch{border-left-color:#4f4f4f}.flat .dijitComboBoxDisabled.alt-primary .dijitButtonNode,.flat .dijitComboButtonRtlDisabled.alt-primary .dijitStretch,.flat .dijitDateTextBoxDisabled.alt-primary .dijitButtonNode,.flat .dijitTimeTextBoxDisabled.alt-primary .dijitButtonNode{border-left-color:#50a2eb}.flat .dijitButtonHover .dijitButtonNode,.flat .dijitComboButton .dijitButtonNodeHover,.flat .dijitComboButton .dijitDownArrowButtonHover,.flat .dijitDropDownButtonHover .dijitButtonNode,.flat .dijitToggleButtonHover .dijitButtonNode{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border-color:#d9d9d9}.flat .dijitButtonHover.alt-primary .dijitButtonNode,.flat .dijitComboBoxHover.alt-primary .dijitButtonNode,.flat .dijitComboButton.alt-primary .dijitButtonNodeHover,.flat .dijitComboButton.alt-primary .dijitDownArrowButtonHover,.flat .dijitDropDownButtonHover.alt-primary .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-primary .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-primary .dijitButtonContents,.flat .dijitSelectHover.alt-primary .dijitButtonContents,.flat .dijitSelectHover.alt-primary .dijitButtonNode,.flat .dijitSpinner.alt-primary .dijitDownArrowButtonHover,.flat .dijitSpinner.alt-primary .dijitUpArrowButtonHover,.flat .dijitToggleButtonHover.alt-primary .dijitButtonNode{background:#1981dd;border-color:#1774c5}.flat .dijitButtonHover.alt-success .dijitButtonNode,.flat .dijitComboBoxHover.alt-success .dijitButtonNode,.flat .dijitComboButton.alt-success .dijitButtonNodeHover,.flat .dijitComboButton.alt-success .dijitDownArrowButtonHover,.flat .dijitDropDownButtonHover.alt-success .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-success .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-success .dijitButtonContents,.flat .dijitSelectHover.alt-success .dijitButtonContents,.flat .dijitSelectHover.alt-success .dijitButtonNode,.flat .dijitSpinner.alt-success .dijitDownArrowButtonHover,.flat .dijitSpinner.alt-success .dijitUpArrowButtonHover,.flat .dijitToggleButtonHover.alt-success .dijitButtonNode{background:#409843;border-color:#39883c}.flat .dijitButtonHover.alt-info .dijitButtonNode,.flat .dijitComboBoxHover.alt-info .dijitButtonNode,.flat .dijitComboButton.alt-info .dijitButtonNodeHover,.flat .dijitComboButton.alt-info .dijitDownArrowButtonHover,.flat .dijitDropDownButtonHover.alt-info .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-info .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-info .dijitButtonContents,.flat .dijitSelectHover.alt-info .dijitButtonContents,.flat .dijitSelectHover.alt-info .dijitButtonNode,.flat .dijitSpinner.alt-info .dijitDownArrowButtonHover,.flat .dijitSpinner.alt-info .dijitUpArrowButtonHover,.flat .dijitToggleButtonHover.alt-info .dijitButtonNode{background:#03a1e8;border-color:#0390cf}.flat .dijitButtonHover.alt-warning .dijitButtonNode,.flat .dijitComboBoxHover.alt-warning .dijitButtonNode,.flat .dijitComboButton.alt-warning .dijitButtonNodeHover,.flat .dijitComboButton.alt-warning .dijitDownArrowButtonHover,.flat .dijitDropDownButtonHover.alt-warning .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-warning .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-warning .dijitButtonContents,.flat .dijitSelectHover.alt-warning .dijitButtonContents,.flat .dijitSelectHover.alt-warning .dijitButtonNode,.flat .dijitSpinner.alt-warning .dijitDownArrowButtonHover,.flat .dijitSpinner.alt-warning .dijitUpArrowButtonHover,.flat .dijitToggleButtonHover.alt-warning .dijitButtonNode{background:#ee8500;border-color:#d57700}.flat .dijitButtonHover.alt-danger .dijitButtonNode,.flat .dijitComboBoxHover.alt-danger .dijitButtonNode,.flat .dijitComboButton.alt-danger .dijitButtonNodeHover,.flat .dijitComboButton.alt-danger .dijitDownArrowButtonHover,.flat .dijitDropDownButtonHover.alt-danger .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-danger .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-danger .dijitButtonContents,.flat .dijitSelectHover.alt-danger .dijitButtonContents,.flat .dijitSelectHover.alt-danger .dijitButtonNode,.flat .dijitSpinner.alt-danger .dijitDownArrowButtonHover,.flat .dijitSpinner.alt-danger .dijitUpArrowButtonHover,.flat .dijitToggleButtonHover.alt-danger .dijitButtonNode{background:#e32d29;border-color:#d4201b}.flat .dijitButtonHover.alt-inverse .dijitButtonNode,.flat .dijitComboBoxHover.alt-inverse .dijitButtonNode,.flat .dijitComboButton.alt-inverse .dijitButtonNodeHover,.flat .dijitComboButton.alt-inverse .dijitDownArrowButtonHover,.flat .dijitDropDownButtonHover.alt-inverse .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-inverse .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-inverse .dijitButtonContents,.flat .dijitSelectHover.alt-inverse .dijitButtonContents,.flat .dijitSelectHover.alt-inverse .dijitButtonNode,.flat .dijitSpinner.alt-inverse .dijitDownArrowButtonHover,.flat .dijitSpinner.alt-inverse .dijitUpArrowButtonHover,.flat .dijitToggleButtonHover.alt-inverse .dijitButtonNode{background:#5c5c5c;border-color:#525252}.flat .dijitButtonActive .dijitButtonNode,.flat .dijitComboButton .dijitButtonNodeActive,.flat .dijitComboButton .dijitDownArrowButtonActive,.flat .dijitDropDownButtonActive .dijitButtonNode,.flat .dijitToggleButtonActive .dijitButtonNode,.flat .dijitToggleButtonChecked .dijitButtonNode{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border-color:#b3b3b3}.flat .dijitButtonActive.alt-primary .dijitButtonNode,.flat .dijitComboBox.alt-primary .dijitButtonNode.dijitHasDropDownOpen,.flat .dijitComboBoxActive.alt-primary .dijitButtonNode,.flat .dijitComboButton.alt-primary .dijitButtonNodeActive,.flat .dijitDropDownButtonActive.alt-primary .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-primary .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-primary .dijitButtonContents,.flat .dijitSelectActive.alt-primary .dijitArrowButton,.flat .dijitSelectActive.alt-primary .dijitButtonContents,.flat .dijitSpinner.alt-primary .dijitDownArrowButtonActive,.flat .dijitSpinner.alt-primary .dijitUpArrowButtonActive,.flat .dijitToggleButtonActive.alt-primary .dijitButtonNode{background:#1878cc;border-color:#135fa3}.flat .dijitButtonActive.alt-success .dijitButtonNode,.flat .dijitComboBox.alt-success .dijitButtonNode.dijitHasDropDownOpen,.flat .dijitComboBoxActive.alt-success .dijitButtonNode,.flat .dijitComboButton.alt-success .dijitButtonNodeActive,.flat .dijitDropDownButtonActive.alt-success .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-success .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-success .dijitButtonContents,.flat .dijitSelectActive.alt-success .dijitArrowButton,.flat .dijitSelectActive.alt-success .dijitButtonContents,.flat .dijitSpinner.alt-success .dijitDownArrowButtonActive,.flat .dijitSpinner.alt-success .dijitUpArrowButtonActive,.flat .dijitToggleButtonActive.alt-success .dijitButtonNode{background:#3b8d3e;border-color:#2f7032}.flat .dijitButtonActive.alt-info .dijitButtonNode,.flat .dijitComboBox.alt-info .dijitButtonNode.dijitHasDropDownOpen,.flat .dijitComboBoxActive.alt-info .dijitButtonNode,.flat .dijitComboButton.alt-info .dijitButtonNodeActive,.flat .dijitDropDownButtonActive.alt-info .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-info .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-info .dijitButtonContents,.flat .dijitSelectActive.alt-info .dijitArrowButton,.flat .dijitSelectActive.alt-info .dijitButtonContents,.flat .dijitSpinner.alt-info .dijitDownArrowButtonActive,.flat .dijitSpinner.alt-info .dijitUpArrowButtonActive,.flat .dijitToggleButtonActive.alt-info .dijitButtonNode{background:#0395d7;border-color:#0276ab}.flat .dijitButtonActive.alt-warning .dijitButtonNode,.flat .dijitComboBox.alt-warning .dijitButtonNode.dijitHasDropDownOpen,.flat .dijitComboBoxActive.alt-warning .dijitButtonNode,.flat .dijitComboButton.alt-warning .dijitButtonNodeActive,.flat .dijitDropDownButtonActive.alt-warning .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-warning .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-warning .dijitButtonContents,.flat .dijitSelectActive.alt-warning .dijitArrowButton,.flat .dijitSelectActive.alt-warning .dijitButtonContents,.flat .dijitSpinner.alt-warning .dijitDownArrowButtonActive,.flat .dijitSpinner.alt-warning .dijitUpArrowButtonActive,.flat .dijitToggleButtonActive.alt-warning .dijitButtonNode{background:#dd7b00;border-color:#b06200}.flat .dijitButtonActive.alt-danger .dijitButtonNode,.flat .dijitComboBox.alt-danger .dijitButtonNode.dijitHasDropDownOpen,.flat .dijitComboBoxActive.alt-danger .dijitButtonNode,.flat .dijitComboButton.alt-danger .dijitButtonNodeActive,.flat .dijitDropDownButtonActive.alt-danger .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-danger .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-danger .dijitButtonContents,.flat .dijitSelectActive.alt-danger .dijitArrowButton,.flat .dijitSelectActive.alt-danger .dijitButtonContents,.flat .dijitSpinner.alt-danger .dijitDownArrowButtonActive,.flat .dijitSpinner.alt-danger .dijitUpArrowButtonActive,.flat .dijitToggleButtonActive.alt-danger .dijitButtonNode{background:#dc211c;border-color:#af1a17}.flat .dijitButtonActive.alt-inverse .dijitButtonNode,.flat .dijitComboBox.alt-inverse .dijitButtonNode.dijitHasDropDownOpen,.flat .dijitComboBoxActive.alt-inverse .dijitButtonNode,.flat .dijitComboButton.alt-inverse .dijitButtonNodeActive,.flat .dijitDropDownButtonActive.alt-inverse .dijitButtonNode,.flat .dijitSelect.dijitSelectOpened.alt-inverse .dijitArrowButton,.flat .dijitSelect.dijitSelectOpened.alt-inverse .dijitButtonContents,.flat .dijitSelectActive.alt-inverse .dijitArrowButton,.flat .dijitSelectActive.alt-inverse .dijitButtonContents,.flat .dijitSpinner.alt-inverse .dijitDownArrowButtonActive,.flat .dijitSpinner.alt-inverse .dijitUpArrowButtonActive,.flat .dijitToggleButtonActive.alt-inverse .dijitButtonNode{background:#555;border-color:#444}.flat .dijitButtonDisabled,.flat .dijitComboButtonDisabled,.flat .dijitDropDownButtonDisabled,.flat .dijitToggleButtonDisabled{outline:0}.flat .dijitButtonDisabled .dijitButtonNode,.flat .dijitComboButtonDisabled .dijitButtonNode,.flat .dijitDropDownButtonDisabled .dijitButtonNode,.flat .dijitToggleButtonDisabled .dijitButtonNode{cursor:default;color:#9e9e9e;background-color:#f5f5f5;border-color:#e3e3e3}.flat .dijitButtonDisabled.alt-primary .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-primary .dijitButtonNode,.flat .dijitDropDownButtonDisabled.alt-primary .dijitButtonNode,.flat .dijitToggleButtonDisabled.alt-primary .dijitButtonNode{background:#6db2ee;border-color:#50a2eb;color:#f2f2f2}.flat .dijitComboBoxDisabled.alt-success .dijitButtonNode,.flat .dijitComboButtonRtlDisabled.alt-success .dijitStretch,.flat .dijitDateTextBoxDisabled.alt-success .dijitButtonNode,.flat .dijitTimeTextBoxDisabled.alt-success .dijitButtonNode{border-left-color:#63be67}.flat .dijitComboBoxRtlDisabled.alt-success .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-success .dijitStretch,.flat .dijitDateTextBoxRtlDisabled.alt-success .dijitButtonNode,.flat .dijitTimeTextBoxRtlDisabled.alt-success .dijitButtonNode{border-right-color:#63be67}.flat .dijitButtonDisabled.alt-success .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-success .dijitButtonNode,.flat .dijitDropDownButtonDisabled.alt-success .dijitButtonNode,.flat .dijitToggleButtonDisabled.alt-success .dijitButtonNode{background:#7dc981;border-color:#63be67;color:#f2f2f2}.flat .dijitComboBoxDisabled.alt-info .dijitButtonNode,.flat .dijitComboButtonRtlDisabled.alt-info .dijitStretch,.flat .dijitDateTextBoxDisabled.alt-info .dijitButtonNode,.flat .dijitTimeTextBoxDisabled.alt-info .dijitButtonNode{border-left-color:#34befd}.flat .dijitComboBoxRtlDisabled.alt-info .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-info .dijitStretch,.flat .dijitDateTextBoxRtlDisabled.alt-info .dijitButtonNode,.flat .dijitTimeTextBoxRtlDisabled.alt-info .dijitButtonNode{border-right-color:#34befd}.flat .dijitButtonDisabled.alt-info .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-info .dijitButtonNode,.flat .dijitDropDownButtonDisabled.alt-info .dijitButtonNode,.flat .dijitToggleButtonDisabled.alt-info .dijitButtonNode{background:#56c9fd;border-color:#34befd;color:#f2f2f2}.flat .dijitComboBoxDisabled.alt-warning .dijitButtonNode,.flat .dijitComboButtonRtlDisabled.alt-warning .dijitStretch,.flat .dijitDateTextBoxDisabled.alt-warning .dijitButtonNode,.flat .dijitTimeTextBoxDisabled.alt-warning .dijitButtonNode{border-left-color:#ffa635}.flat .dijitComboBoxRtlDisabled.alt-warning .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-warning .dijitStretch,.flat .dijitDateTextBoxRtlDisabled.alt-warning .dijitButtonNode,.flat .dijitTimeTextBoxRtlDisabled.alt-warning .dijitButtonNode{border-right-color:#ffa635}.flat .dijitButtonDisabled.alt-warning .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-warning .dijitButtonNode,.flat .dijitDropDownButtonDisabled.alt-warning .dijitButtonNode,.flat .dijitToggleButtonDisabled.alt-warning .dijitButtonNode{background:#ffb557;border-color:#ffa635;color:#f2f2f2}.flat .dijitComboBoxDisabled.alt-danger .dijitButtonNode,.flat .dijitComboButtonRtlDisabled.alt-danger .dijitStretch,.flat .dijitDateTextBoxDisabled.alt-danger .dijitButtonNode,.flat .dijitTimeTextBoxDisabled.alt-danger .dijitButtonNode{border-left-color:#eb6561}.flat .dijitComboBoxRtlDisabled.alt-danger .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-danger .dijitStretch,.flat .dijitDateTextBoxRtlDisabled.alt-danger .dijitButtonNode,.flat .dijitTimeTextBoxRtlDisabled.alt-danger .dijitButtonNode{border-right-color:#eb6561}.flat .dijitButtonDisabled.alt-danger .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-danger .dijitButtonNode,.flat .dijitDropDownButtonDisabled.alt-danger .dijitButtonNode,.flat .dijitToggleButtonDisabled.alt-danger .dijitButtonNode{background:#ee7e7c;border-color:#eb6561;color:#f2f2f2}.flat .dijitComboBoxDisabled.alt-inverse .dijitButtonNode,.flat .dijitComboButtonRtlDisabled.alt-inverse .dijitStretch,.flat .dijitDateTextBoxDisabled.alt-inverse .dijitButtonNode,.flat .dijitTimeTextBoxDisabled.alt-inverse .dijitButtonNode{border-left-color:#848484}.flat .dijitComboBoxRtlDisabled.alt-inverse .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-inverse .dijitStretch,.flat .dijitDateTextBoxRtlDisabled.alt-inverse .dijitButtonNode,.flat .dijitTimeTextBoxRtlDisabled.alt-inverse .dijitButtonNode{border-right-color:#848484}.flat .dijitButtonDisabled.alt-inverse .dijitButtonNode,.flat .dijitComboButtonDisabled.alt-inverse .dijitButtonNode,.flat .dijitDropDownButtonDisabled.alt-inverse .dijitButtonNode,.flat .dijitToggleButtonDisabled.alt-inverse .dijitButtonNode{background:#989898;border-color:#848484;color:#f2f2f2}.flat .dijitComboButtonDisabled .dijitArrowButton{border-left-width:0}.flat .dijitDropDownButton .dijitButtonNode{padding-right:8px}.flat table.dijitComboButton .dijitStretch{-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.flat table.dijitComboButton .dijitArrowButton{padding:4px;width:20px;-webkit-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;border-left-width:0}.flat .dijitCheckBox,.flat .dijitCheckedMenuItem .dijitCheckedMenuItemIcon{border:1px solid #9e9e9e;width:16px;text-align:center;overflow:visible;height:16px}.flat .dijitToggleButtonChecked .dijitIcon{display:inline-block}.flat .dijitDropDownButton .dijitArrowButtonInner{margin-left:3px}.flat .dijitArrowButton,.flat .dijitDropDownButton .dijitArrowButtonInner{line-height:1}.flat .dijitArrowButton:before,.flat .dijitDropDownButton .dijitArrowButtonInner:before{content:"\f002"}.flat .dijitLeftArrowButton:before{content:"\f000"}.flat .dijitRightArrowButton:before{content:"\f001"}.flat .dijitUpArrowButton:before{content:"\f003"}.flat .dijitCheckBoxChecked:before,.flat .dijitCheckBoxCheckedDisabled:before,.flat .dijitCheckBoxIcon:before,.flat .dijitCheckedMenuItemChecked .dijitCheckedMenuItemIcon:before{line-height:1;font-size:16px;content:"\f00c";speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:flat-icon}.flat .dijitCheckBox{background-color:#fff;line-height:1;padding:0;-webkit-border-radius:2px;border-radius:2px;position:relative;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;-o-transition:all .1s linear;-ms-transition:all .1s linear;transition:all .1s linear}.flat .dijitCheckBox input{position:absolute;top:0}.flat .dijitCheckBoxChecked:before,.flat .dijitCheckBoxCheckedDisabled:before,.flat .dijitCheckBoxIcon:before{color:#fff}.flat .dijitCheckBoxIcon{padding:0}.flat .dijitCheckBoxIcon:before{color:#2196f3}.flat .alt-danger .dijitCheckBoxIcon:before,.flat .alt-info .dijitCheckBoxIcon:before,.flat .alt-inverse .dijitCheckBoxIcon:before,.flat .alt-primary .dijitCheckBoxIcon:before,.flat .alt-success .dijitCheckBoxIcon:before,.flat .alt-warning .dijitCheckBoxIcon:before{color:#fff}.flat .dijitCheckBoxChecked{background-color:#2196f3;border-color:#2196f3}.flat .dijitCheckBoxHover{background-color:#fff;border:1px solid #2196f3}.flat .dijitCheckBoxCheckedHover{background-color:#42a6f5;border:1px solid #2196f3}.flat .dijitCheckBoxDisabled{color:#9e9e9e;background-color:#f5f5f5;border-color:#e3e3e3}.flat .dijitCheckBoxCheckedDisabled{color:#a6a6a6;background-color:#6fbbf7;border-color:#6fbbf7}.flat .dijitCheckedMenuItem .dijitCheckedMenuItemIcon{background-color:#fff;line-height:1;padding:0;-webkit-border-radius:2px;border-radius:2px;position:relative;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;-o-transition:all .1s linear;-ms-transition:all .1s linear;transition:all .1s linear}.dijitMenuItemRtl,.dj_ie .dijitSliderRtl .dijitRuleContainerV,div.dijitNumberTextBoxRtl{text-align:right}.flat .dijitCheckedMenuItemChecked .dijitCheckedMenuItemIcon:before{color:#2196f3}.flat .dijitSpinner .dijitSpinnerButtonContainer{overflow:hidden;position:relative;width:auto;padding:0;border:1px solid #9e9e9e}.flat .dijitSpinner .dijitSpinnerButtonInner{width:30px;padding:4px 0!important;margin:0}.flat .dijitSpinner .dijitArrowButton{line-height:20px;cursor:pointer;-webkit-transition:all 50ms linear;-moz-transition:all 50ms linear;-o-transition:all 50ms linear;-ms-transition:all 50ms linear;transition:all 50ms linear;background:#fff;-webkit-border-radius:0;border-radius:0;border:0;width:auto;overflow:hidden;left:0;right:0;padding:0}.flat .dijitSpinner .dijitArrowButton:before{content:none}.flat .dijitSpinner .dijitUpArrowButton{border-top-right-radius:2px}.flat .dijitSpinner .dijitDownArrowButton{border-bottom-right-radius:2px}.flat .dijitSpinner .dijitDownArrowButtonHover,.flat .dijitSpinner .dijitUpArrowButtonHover{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border-color:#d9d9d9}.flat .dijitSpinner .dijitDownArrowButtonActive,.flat .dijitSpinner .dijitUpArrowButtonActive{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border-color:#b3b3b3}.flat .dijitSpinner .dijitArrowButtonInner{line-height:16px;display:block}.flat .dijitSpinner .dijitArrowButtonInner .dijitInputField{padding:0}.flat .dijitSpinner .dijitArrowButtonInner:before{content:"\f003"}.flat .dijitSpinner .dijitDownArrowButton .dijitArrowButtonInner:before{content:"\f002"}.flat .dijitSpinnerDisabled .dijitDownArrowButton,.flat .dijitSpinnerDisabled .dijitUpArrowButton{cursor:default;color:#9e9e9e;background-color:#f5f5f5;border-color:#e3e3e3}.flat .alt-primary .dijitSpinnerButtonContainer{border-color:#1e88e5}.flat .alt-success .dijitSpinnerButtonContainer{border-color:#43a047}.flat .alt-info .dijitSpinnerButtonContainer{border-color:#03a9f4}.flat .alt-warning .dijitSpinnerButtonContainer{border-color:#fb8c00}.flat .alt-danger .dijitSpinnerButtonContainer{border-color:#e53935}.flat .alt-inverse .dijitSpinnerButtonContainer{border-color:#616161}.flat .dijitRadio,.flat .dijitRadioIcon{width:16px;height:16px;background:#fff;border:1px solid #2196f3;-webkit-border-radius:50%;border-radius:50%;position:relative;overflow:visible;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.flat .dijitRadio:after,.flat .dijitRadioIcon:after{content:" ";display:block;width:0;height:0;background-color:#2196f3;-webkit-border-radius:50%;border-radius:50%;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:alpha(opacity=0);margin:8px;position:absolute;top:0;left:0;-webkit-transition:all .15s ease-in-out;-moz-transition:all .15s ease-in-out;-o-transition:all .15s ease-in-out;-ms-transition:all .15s ease-in-out;transition:all .15s ease-in-out}.flat .alt-danger .dijitRadioIcon:after,.flat .alt-info .dijitRadioIcon:after,.flat .alt-inverse .dijitRadioIcon:after,.flat .alt-primary .dijitRadioIcon:after,.flat .alt-success .dijitRadioIcon:after,.flat .alt-warning .dijitRadioIcon:after{background-color:#fff}.flat .dijitRadioHover{border-color:#59b0f6}.flat .alt-danger .dijitRadioIcon,.flat .alt-info .dijitRadioIcon,.flat .alt-inverse .dijitRadioIcon,.flat .alt-primary .dijitRadioIcon,.flat .alt-success .dijitRadioIcon,.flat .alt-warning .dijitRadioIcon{border-color:#fff}.flat .dijitChecked .dijitRadioIcon:after,.flat .dijitRadioChecked:after{width:8px;height:8px;margin:3px;opacity:1;-ms-filter:none;filter:none}.flat .dijitRadioDisabled{background-color:#f5f5f5;border-color:#e3e3e3}.flat .dijitRadioCheckedDisabled{background-color:#f5f5f5;border-color:#6fbbf7}.flat .dijitRadioCheckedDisabled:after{background-color:#6fbbf7}.flat .dijitRadioMenuItem .dijitCheckedMenuItemIcon{width:16px;height:16px;background:#fff;border:1px solid #9e9e9e;-webkit-border-radius:50%;border-radius:50%;position:relative;overflow:visible;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.flat .dijitRadioMenuItemChecked .dijitCheckedMenuItemIcon,.flat .dijitSelect{border-color:#9e9e9e}.flat .dijitRadioMenuItem .dijitCheckedMenuItemIcon:after{content:" ";display:block;width:0;height:0;background-color:#2196f3;-webkit-border-radius:50%;border-radius:50%;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:alpha(opacity=0);margin:8px;position:absolute;top:0;left:0;-webkit-transition:all .15s ease-in-out;-moz-transition:all .15s ease-in-out;-o-transition:all .15s ease-in-out;-ms-transition:all .15s ease-in-out;transition:all .15s ease-in-out}.flat .dijitRadioMenuItemChecked .dijitCheckedMenuItemIcon:after{width:8px;height:8px;margin:3px;opacity:1;-ms-filter:none;filter:none}.flat .dijitComboBox .dijitArrowButtonInner,.flat .dijitSelect .dijitArrowButtonInner{margin:0;width:0;height:0}.flat .dijitComboBox .dijitArrowButton,.flat .dijitSelect .dijitArrowButton{width:20px;padding:4px}.flat .dijitSelect{border-style:solid;border-width:1px;padding:4px;-webkit-border-radius:3px;border-radius:3px;line-height:20px;cursor:pointer;-webkit-transition:all 50ms linear;-moz-transition:all 50ms linear;-o-transition:all 50ms linear;-ms-transition:all 50ms linear;transition:all 50ms linear;background:#fff;table-layout:fixed}.flat .dijitSelect .dijitArrowButton,.flat .dijitSelect .dijitButtonContents{line-height:20px;padding:4px 12px;border:0;-webkit-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0}.flat .dijitSelect .dijitButtonContents{padding:0;overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis;-webkit-border-radius:2px 0 0 2px;border-radius:2px 0 0 2px}.flat .dijitSelect .dijitInputField{padding:0 0 0 12px}.flat .dijitSelectHover{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border-color:#d9d9d9}.flat .dijitSelectActive{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border-color:#b3b3b3}.flat .dijitSelectFocused{border:1px solid #9e9e9e}.flat .dijitSelectDisabled{cursor:default;color:#9e9e9e;background-color:#f5f5f5;border-color:#e3e3e3}.flat .dijitComboBox .dijitButtonNode{border-style:solid;border-width:1px;border-color:#9e9e9e;padding:4px;line-height:20px;cursor:pointer;-webkit-transition:all 50ms linear;-moz-transition:all 50ms linear;-o-transition:all 50ms linear;-ms-transition:all 50ms linear;transition:all 50ms linear;background:#fff;-webkit-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0}.flat .dijitComboBox .dijitDownArrowButtonHover,.flat .dijitComboBoxOpenHover .dijitButtonNode{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border-color:#d9d9d9;-webkit-box-shadow:none;box-shadow:none}.flat .dijitComboBoxDisabled .dijitButtonNode{cursor:default;color:#9e9e9e;background-color:#f5f5f5;border-color:#e3e3e3}.flat .dijitToolbar .dijitComboBox .dijitArrowButtonInner{border:none}.flat .dijitDateTextBox .dijitArrowButton:before{content:"\f01e"}.flat .dijitTimeTextBox .dijitArrowButton:before{content:"\f01f"}.flat select{padding:4px 0;border:1px solid #9e9e9e;-webkit-box-shadow:0 1px .5px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.2);box-shadow:0 1px .5px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.2)}.flat select option{padding:4px 8px}.flat .dijitRuleLabelsContainerH,.flat .dijitRuleLabelsContainerV{padding:0}.flat .dijitSelectMenu td.dijitMenuArrowCell,.flat .dijitSelectMenu td.dijitMenuItemIconCell{display:none}.flat .dijitSliderBar{border-style:solid;outline:1px}.flat .dijitRuleLabelsContainer{color:#424242;font-size:smaller}.flat .dijitSliderDisabled{opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitSliderBarH,.flat .dijitSliderBumperH{height:3px}.flat .dijitSlider .dijitSliderLeftBumper{-webkit-border-radius:1.5px 0 0 1.5px;border-radius:1.5px 0 0 1.5px;margin-left:4px}.flat .dijitSlider .dijitSliderRightBumper{-webkit-border-radius:0 1.5px 1.5px 0;border-radius:0 1.5px 1.5px 0;margin-left:-2px;margin-right:4px}.flat .dijitSlider .dijitSliderLeftBumper,.flat .dijitSlider .dijitSliderProgressBarH{border:0;background-color:#2196f3;background-image:none}.flat .dijitSlider .dijitSliderRemainingBarH,.flat .dijitSlider .dijitSliderRightBumper{border:0;background-color:#9e9e9e}.flat .dijitSliderHover .dijitSliderLeftBumper,.flat .dijitSliderHover .dijitSliderProgressBarH{background-color:#0d8cf1;background-image:none}.flat .dijitSliderFocused .dijitSliderLeftBumper,.flat .dijitSliderFocused .dijitSliderProgressBarH,.flat .dijitSliderFocused .dijitSliderRemainingBarH,.flat .dijitSliderFocused .dijitSliderRightBumper{-webkit-box-shadow:none;box-shadow:none}.flat .dijitSliderBarV,.flat .dijitSliderBumperV{width:3px}.flat .dijitSlider .dijitSliderTopBumper{-webkit-border-radius:1.5px 1.5px 0 0;border-radius:1.5px 1.5px 0 0;margin-top:4px;margin-bottom:-2px}.flat .dijitSlider .dijitSliderBottomBumper{-webkit-border-radius:0 0 1.5px 1.5px;border-radius:0 0 1.5px 1.5px;margin-bottom:4px}.flat .dijitSlider .dijitSliderBottomBumper,.flat .dijitSlider .dijitSliderProgressBarV{border:0;background-color:#2196f3;background-image:none}.flat .dijitSlider .dijitSliderRemainingBarV,.flat .dijitSlider .dijitSliderTopBumper{border:0;background-color:#9e9e9e}.flat .dijitSliderHover .dijitSliderBottomBumper,.flat .dijitSliderHover .dijitSliderProgressBarV{background-color:#0d8cf1;background-image:none}.flat .dijitSliderFocused .dijitSliderBottomBumper,.flat .dijitSliderFocused .dijitSliderProgressBarV,.flat .dijitSliderFocused .dijitSliderRemainingBarV,.flat .dijitSliderFocused .dijitSliderTopBumper{-webkit-box-shadow:none;box-shadow:none}.flat .dijitSliderImageHandle{background:#fff;-webkit-box-shadow:0 1px .5px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.2);box-shadow:0 1px .5px rgba(0,0,0,.3),0 2px 2px rgba(0,0,0,.2);-webkit-border-radius:50%;border-radius:50%;border:1px solid #2196f3;width:16px;height:16px;margin-top:-2px;position:absolute;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.flat .dijitSliderImageHandle:after{content:"";display:block;background:#2196f3;-webkit-border-radius:50%;border-radius:50%;height:10px;width:10px;left:2px;top:2px;position:absolute}.flat .dijitSliderDecrementIconH .dijitSliderButtonInner,.flat .dijitSliderDecrementIconV .dijitSliderButtonInner,.flat .dijitSliderDisabled.dijitSliderFocused .dijitSliderImageHandle:after,.flat .dijitSliderIncrementIconH .dijitSliderButtonInner,.flat .dijitSliderIncrementIconV .dijitSliderButtonInner,.flat .dijitValidationTextBoxError .dijitValidationIcon{display:none}.flat .dijitSliderImageHandleV{margin-top:0}.flat .dijitSliderFocused .dijitSliderImageHandle,.flat .dijitSliderHover .dijitSliderImageHandle{-webkit-box-shadow:0 4px 2px -2px rgba(0,0,0,.3),0 4px 6px rgba(0,0,0,.2);box-shadow:0 4px 2px -2px rgba(0,0,0,.3),0 4px 6px rgba(0,0,0,.2)}.flat .dijitSliderDecrementIconH,.flat .dijitSliderDecrementIconV,.flat .dijitSliderIncrementIconH,.flat .dijitSliderIncrementIconV{border-style:solid;border-width:1px;border-color:#9e9e9e;-webkit-border-radius:3px;border-radius:3px;-webkit-transition:all 50ms linear;-moz-transition:all 50ms linear;-o-transition:all 50ms linear;-ms-transition:all 50ms linear;transition:all 50ms linear;background:#fff;height:20px;width:20px;cursor:pointer;color:#2196f3;padding:0;font-family:flat-icon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.flat .dijitSliderDecrementIconH:hover,.flat .dijitSliderDecrementIconV:hover,.flat .dijitSliderIncrementIconH:hover,.flat .dijitSliderIncrementIconV:hover{-webkit-transition:all .1s;-moz-transition:all .1s;-o-transition:all .1s;-ms-transition:all .1s;transition:all .1s;background:#f2f2f2;border-color:#d9d9d9}.flat .dijitSliderDecrementIconH:active,.flat .dijitSliderDecrementIconV:active,.flat .dijitSliderIncrementIconH:active,.flat .dijitSliderIncrementIconV:active{-webkit-transition:none;-moz-transition:none;-o-transition:none;-ms-transition:none;transition:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.05);box-shadow:inset 0 3px 5px rgba(0,0,0,.05);background:#e0e0e0;border-color:#b3b3b3}.flat .dijitSliderDisabled .dijitSliderDecrementIconH,.flat .dijitSliderDisabled .dijitSliderDecrementIconV,.flat .dijitSliderDisabled .dijitSliderIncrementIconH,.flat .dijitSliderDisabled .dijitSliderIncrementIconV,.flat .dijitSliderReadOnly .dijitSliderDecrementIconH,.flat .dijitSliderReadOnly .dijitSliderDecrementIconV,.flat .dijitSliderReadOnly .dijitSliderIncrementIconH,.flat .dijitSliderReadOnly .dijitSliderIncrementIconV{opacity:.65;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=65)";filter:alpha(opacity=65)}.flat .dijitSliderDecrementIconH:before,.flat .dijitSliderDecrementIconV:before,.flat .dijitSliderIncrementIconH:before,.flat .dijitSliderIncrementIconV:before{content:"\f011";top:0;line-height:20px}.flat .dijitSliderDecrementIconH:before,.flat .dijitSliderDecrementIconV:before{content:"\f012"}.flat .dijitRuleMark{border:0}.flat .dijitRuleMarkH{border-right:1px solid #e0e0e0}.flat .dijitRuleMarkV{border-bottom:1px solid #e0e0e0}.flat .dijitRuleLabelContainerH{margin-top:2px;margin-bottom:2px}.flat .dijitRuleLabelContainerV{margin-left:2px;margin-right:2px}.flat .dijitInputInner,.flat .dijitTextBox{line-height:20px}.dijitEditorIcon,.dijitIcon,.flat .dijitValidationTextBoxError .dijitValidationContainer,[class*=" flat-"],[class^=flat-]{speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.flat .dijitTextBox{background:#fff;border:1px solid #9e9e9e;-webkit-border-radius:3px;border-radius:3px;-webkit-transition:border .2s linear 0s,box-shadow .2s linear 0s;-moz-transition:border .2s linear 0s,box-shadow .2s linear 0s;-o-transition:border .2s linear 0s,box-shadow .2s linear 0s;-ms-transition:border .2s linear 0s,box-shadow .2s linear 0s;transition:border .2s linear 0s,box-shadow .2s linear 0s}.flat .dijitTextArea{padding:4px 6px}.flat .dijitTextBox .dijitInputField{padding:0 4px;margin:0 2px}.flat .dijitComboBox.alt-primary,.flat .dijitSelect.alt-primary,.flat .dijitSpinner.alt-primary{border-color:#1e88e5}.flat .dijitComboBox.alt-success,.flat .dijitSelect.alt-success,.flat .dijitSpinner.alt-success{border-color:#43a047}.flat .dijitComboBox.alt-info,.flat .dijitSelect.alt-info,.flat .dijitSpinner.alt-info{border-color:#03a9f4}.flat .dijitComboBox.alt-warning,.flat .dijitSelect.alt-warning,.flat .dijitSpinner.alt-warning{border-color:#fb8c00}.flat .dijitComboBox.alt-danger,.flat .dijitSelect.alt-danger,.flat .dijitSpinner.alt-danger{border-color:#e53935}.flat .dijitComboBox.alt-inverse,.flat .dijitSelect.alt-inverse,.flat .dijitSpinner.alt-inverse{border-color:#616161}.flat .dijitTextBox .dijitInputField .dijitPlaceHolder,.flat .dijitTextBox .dijitInputInner,.flat .dijitValidationTextBox .dijitValidationContainer{padding:4px}.flat .dijitTextBoxHover{border-color:#2196f3;-webkit-transition-duration:.25s;-moz-transition-duration:.25s;-o-transition-duration:.25s;-ms-transition-duration:.25s;transition-duration:.25s}.flat .dijitTextBoxFocused{border-color:#2196f3;-webkit-transition-duration:.1s;-moz-transition-duration:.1s;-o-transition-duration:.1s;-ms-transition-duration:.1s;transition-duration:.1s}.flat .dijitTextBoxDisabled{color:#9e9e9e;background-color:#f5f5f5;border-color:#e3e3e3}.flat .dijitComboBoxDisabled.alt-primary,.flat .dijitSpinnerDisabled.alt-primary{background:#f5f5f5;color:#9e9e9e;border:1px solid #6db2ee}.flat .dijitComboBoxDisabled.alt-primary .dijitButtonNode,.flat .dijitSelectDisabled.alt-primary .dijitButtonNode,.flat .dijitSelectDisabled.alt-primary .dijitStretch,.flat .dijitSpinnerDisabled.alt-primary .dijitButtonNode{background:#6db2ee;color:#f2f2f2}.flat .dijitSpinnerDisabled.alt-primary .dijitSpinnerButtonContainer{border-left-color:#6db2ee}.flat .dijitSpinnerRtlDisabled.alt-primary .dijitSpinnerButtonContainer{border-right-color:#6db2ee}.flat .dijitSelectDisabled.alt-primary{border-color:#6db2ee}.flat .dijitComboBoxDisabled.alt-success,.flat .dijitSpinnerDisabled.alt-success{background:#f5f5f5;color:#9e9e9e;border:1px solid #7dc981}.flat .dijitComboBoxDisabled.alt-success .dijitButtonNode,.flat .dijitSelectDisabled.alt-success .dijitButtonNode,.flat .dijitSelectDisabled.alt-success .dijitStretch,.flat .dijitSpinnerDisabled.alt-success .dijitButtonNode{background:#7dc981;color:#f2f2f2}.flat .dijitSpinnerDisabled.alt-success .dijitSpinnerButtonContainer{border-left-color:#7dc981}.flat .dijitSpinnerRtlDisabled.alt-success .dijitSpinnerButtonContainer{border-right-color:#7dc981}.flat .dijitSelectDisabled.alt-success{border-color:#7dc981}.flat .dijitComboBoxDisabled.alt-info,.flat .dijitSpinnerDisabled.alt-info{background:#f5f5f5;color:#9e9e9e;border:1px solid #56c9fd}.flat .dijitComboBoxDisabled.alt-info .dijitButtonNode,.flat .dijitSelectDisabled.alt-info .dijitButtonNode,.flat .dijitSelectDisabled.alt-info .dijitStretch,.flat .dijitSpinnerDisabled.alt-info .dijitButtonNode{background:#56c9fd;color:#f2f2f2}.flat .dijitSpinnerDisabled.alt-info .dijitSpinnerButtonContainer{border-left-color:#56c9fd}.flat .dijitSpinnerRtlDisabled.alt-info .dijitSpinnerButtonContainer{border-right-color:#56c9fd}.flat .dijitSelectDisabled.alt-info{border-color:#56c9fd}.flat .dijitComboBoxDisabled.alt-warning,.flat .dijitSpinnerDisabled.alt-warning{background:#f5f5f5;color:#9e9e9e;border:1px solid #ffb557}.flat .dijitComboBoxDisabled.alt-warning .dijitButtonNode,.flat .dijitSelectDisabled.alt-warning .dijitButtonNode,.flat .dijitSelectDisabled.alt-warning .dijitStretch,.flat .dijitSpinnerDisabled.alt-warning .dijitButtonNode{background:#ffb557;color:#f2f2f2}.flat .dijitSpinnerDisabled.alt-warning .dijitSpinnerButtonContainer{border-left-color:#ffb557}.flat .dijitSpinnerRtlDisabled.alt-warning .dijitSpinnerButtonContainer{border-right-color:#ffb557}.flat .dijitSelectDisabled.alt-warning{border-color:#ffb557}.flat .dijitComboBoxDisabled.alt-danger,.flat .dijitSpinnerDisabled.alt-danger{background:#f5f5f5;color:#9e9e9e;border:1px solid #ee7e7c}.flat .dijitComboBoxDisabled.alt-danger .dijitButtonNode,.flat .dijitSelectDisabled.alt-danger .dijitButtonNode,.flat .dijitSelectDisabled.alt-danger .dijitStretch,.flat .dijitSpinnerDisabled.alt-danger .dijitButtonNode{background:#ee7e7c;color:#f2f2f2}.flat .dijitSpinnerDisabled.alt-danger .dijitSpinnerButtonContainer{border-left-color:#ee7e7c}.flat .dijitSpinnerRtlDisabled.alt-danger .dijitSpinnerButtonContainer{border-right-color:#ee7e7c}.flat .dijitSelectDisabled.alt-danger{border-color:#ee7e7c}.flat .dijitComboBoxDisabled.alt-inverse,.flat .dijitSpinnerDisabled.alt-inverse{background:#f5f5f5;color:#9e9e9e;border:1px solid #989898}.flat .dijitComboBoxDisabled.alt-inverse .dijitButtonNode,.flat .dijitSelectDisabled.alt-inverse .dijitButtonNode,.flat .dijitSelectDisabled.alt-inverse .dijitStretch,.flat .dijitSpinnerDisabled.alt-inverse .dijitButtonNode{background:#989898;color:#f2f2f2}.flat .dijitSpinnerDisabled.alt-inverse .dijitSpinnerButtonContainer{border-left-color:#989898}.flat .dijitSpinnerRtlDisabled.alt-inverse .dijitSpinnerButtonContainer{border-right-color:#989898}.flat .dijitSelectDisabled.alt-inverse{border-color:#989898}.flat .dijitTextBoxError,.flat .dijitTextBoxError .dijitButtonNode{border-color:#dd2c00}.flat .dijitTextBoxErrorFocused,.flat .dijitTextBoxErrorFocused .dijitButtonNode{border:1px solid #bc2500}.flat .dijitValidationTextBoxError .dijitValidationContainer{color:#dd2c00;width:18px;font-family:flat-icon;font-size:18px}.flat .dijitValidationTextBoxError .dijitValidationContainer:before{content:"\f017"}@font-face{font-family:flat-icon;src:url(fonts/flat-icon.eot?90nq1s);src:url(fonts/flat-icon.eot?#iefix90nq1s) format('embedded-opentype'),url(fonts/flat-icon.ttf?90nq1s) format('truetype'),url(fonts/flat-icon.woff?90nq1s) format('woff'),url(fonts/flat-icon.svg?90nq1s#flat-icon) format('svg');font-weight:400;font-style:normal}[class*=" flat-"],[class^=flat-]{font-family:flat-icon}.dijitEditorIcon,.dijitIcon{font-family:flat-icon;font-size:16px;width:16px;height:16px}.flat-drop-left:before{content:"\f000"}.flat-drop-right:before{content:"\f001"}.flat-drop-down:before{content:"\f002"}.flat-drop-up:before{content:"\f003"}.flat-chevron-left:before{content:"\f004"}.flat-chevron-right:before{content:"\f005"}.flat-chevron-down:before{content:"\f006"}.flat-chevron-up:before{content:"\f007"}.flat-arrow-left:before{content:"\f008"}.flat-arrow-right:before{content:"\f009"}.flat-arrow-down:before{content:"\f00a"}.flat-arrow-up:before{content:"\f00b"}.flat-check:before{content:"\f00c"}.flat-check-circle:before{content:"\f00d"}.flat-close:before{content:"\f00e"}.dijitIconClear:before,.flat-close-circle:before{content:"\f00f"}.dijitEditorIconCancel:before,.flat-close-circle-o:before{content:"\f010"}.flat-add:before{content:"\f011"}.flat-remove:before{content:"\f012"}.flat-add-circle:before{content:"\f013"}.flat-remove-circle:before{content:"\f014"}.flat-add-circle-o:before{content:"\f015"}.flat-remove-circle-o:before{content:"\f016"}.dijitIconError:before,.flat-error:before{content:"\f017"}.flat-error-o:before{content:"\f018"}.flat-warning:before{content:"\f019"}.flat-report:before{content:"\f01a"}.flat-help:before{content:"\f01b"}.flat-no-symbol:before{content:"\f01c"}.flat-update:before{content:"\f01d"}.flat-calendar:before{content:"\f01e"}.flat-clock:before{content:"\f01f"}.dijitFolderClosed:before,.dijitIconFolderClosed:before,.flat-folder:before{content:"\f020"}.dijitFolderOpened:before,.dijitIconFolderOpen:before,.flat-folder-open:before{content:"\f021"}.dijitIconEdit:before,.flat-edit:before{content:"\f022"}.dijitEditorIconSave:before,.dijitIconSave:before,.flat-save:before{content:"\f023"}.dijitEditorIconPrint:before,.dijitIconPrint:before,.flat-print:before{content:"\f024"}.dijitEditorIconDelete:before,.dijitIconDelete:before,.flat-delete:before{content:"\f025"}.dijitLeaf:before,.flat-page:before{content:"\f026"}.flat-page-o:before{content:"\f027"}.flat-page-add:before{content:"\f028"}.flat-page-remove:before{content:"\f029"}.flat-page-add-o:before{content:"\f02a"}.flat-page-remove-o:before{content:"\f02b"}.dijitIconFile:before,.flat-file:before{content:"\f02c"}.dijitIconMail:before,.flat-mail:before{content:"\f02d"}.dijitIconDatabase:before,.flat-storage:before{content:"\f02e"}.dijitIconConfigure:before,.flat-settings:before{content:"\f02f"}.dijitIconSearch:before,.flat-search:before{content:"\f030"}.dijitIconBookmark:before,.flat-bookmark:before{content:"\f031"}.flat-menu:before{content:"\f032"}.dijitIconApplication:before,.flat-application:before{content:"\f033"}.dijitIconKey:before,.flat-key:before{content:"\f034"}.dijitEditorIconInsertTable:before,.dijitIconTable:before,.flat-table:before{content:"\f035"}.flat-grid:before{content:"\f036"}.dijitIconChart:before,.flat-chart:before{content:"\f037"}.dijitIconFilter:before,.flat-filter:before{content:"\f038"}.dijitIconFunction:before,.flat-function:before{content:"\f039"}.flat-user:before{content:"\f03a"}.dijitIconUsers:before,.flat-users:before{content:"\f03b"}.dijitIconConnector:before,.flat-connector:before{content:"\f03c"}.dijitIconDocuments:before,.flat-documents:before{content:"\f03d"}.dijitIconEditProperty:before,.flat-edit-property:before{content:"\f03e"}.dijitIconTask:before,.flat-task:before{content:"\f03f"}.dijitIconNewTask:before,.flat-task-new:before{content:"\f040"}.dijitIconEditTask:before,.flat-task-edit:before{content:"\f041"}.dijitIconSample:before,.flat-sample:before{content:"\f042"}.dijitIconPackage:before,.flat-package:before{content:"\f043"}.dijitEditorIconUndo:before,.flat-undo:before{content:"\f044"}.dijitEditorIconRedo:before,.flat-redo:before{content:"\f045"}.dijitEditorIconCopy:before,.dijitIconCopy:before,.flat-copy:before{content:"\f046"}.dijitEditorIconCut:before,.dijitIconCut:before,.flat-cut:before{content:"\f047"}.dijitEditorIconPaste:before,.flat-paste:before{content:"\f048"}.dijitEditorIconBold:before,.flat-bold:before{content:"\f049"}.dijitEditorIconItalic:before,.flat-italic:before{content:"\f04a"}.dijitEditorIconUnderline:before,.flat-underline:before{content:"\f04b"}.dijitEditorIconStrikethrough:before,.flat-strikethrough:before{content:"\f04c"}.dijitEditorIconRemoveFormat:before,.flat-clear-format:before{content:"\f04d"}.flat-quote:before{content:"\f04e"}.dijitEditorIconSuperscript:before,.flat-superscript:before{content:"\f04f"}.dijitEditorIconSubscript:before,.flat-subscript:before{content:"\f050"}.dijitEditorIconForeColor:before,.flat-color-text:before{content:"\f051"}.dijitEditorIconBackColor:before,.flat-color-fill:before{content:"\f052"}.dijitEditorIconHiliteColor:before,.flat-color-highlight:before{content:"\f053"}.flat-font-size:before{content:"\f054"}.dijitEditorIconJustifyCenter:before,.flat-align-center:before{content:"\f055"}.dijitEditorIconJustifyFull:before,.flat-align-justify:before{content:"\f056"}.dijitEditorIconJustifyLeft:before,.flat-align-left:before{content:"\f057"}.dijitEditorIconJustifyRight:before,.flat-align-right:before{content:"\f058"}.dijitEditorIconIndent:before,.flat-indent:before{content:"\f059"}.dijitEditorIconOutdent:before,.flat-outdent:before{content:"\f05a"}.flat-sort:before{content:"\f05b"}.dijitEditorIconSpace:before,.flat-keyboard-space:before{content:"\f05c"}.dijitEditorIconTabIndent:before,.flat-keyboard-tab:before{content:"\f05d"}.dijitEditorIconInsertUnorderedList:before,.flat-list-bullet:before{content:"\f05e"}.dijitEditorIconInsertOrderedList:before,.flat-list-number:before{content:"\f05f"}.dijitEditorIconListBulletIndent:before,.flat-list-bullet-indent:before{content:"\f060"}.dijitEditorIconListBulletOutdent:before,.flat-list-bullet-outdent:before{content:"\f061"}.dijitEditorIconListNumIndent:before,.flat-list-number-indent:before{content:"\f062"}.dijitEditorIconListNumOutdent:before,.flat-list-number-outdent:before{content:"\f063"}.dijitEditorIconViewSource:before,.flat-code:before{content:"\f064"}.dijitEditorIconCreateLink:before,.flat-link:before{content:"\f065"}.dijitEditorIconUnlink:before,.flat-unlink:before{content:"\f066"}.dijitEditorIconFullScreen:before,.flat-fullscreen:before{content:"\f067"}.flat-fullscreen-exit:before{content:"\f068"}.dijitEditorIconInsertImage:before,.flat-image:before{content:"\f069"}.dijitEditorIconNewPage:before,.flat-page-new:before{content:"\f06a"}.dijitEditorIconToggleDir:before,.flat-toggle-dir:before{content:"\f06b"}.dijitEditorIconLeftToRight:before,.flat-left-to-right:before{content:"\f06c"}.dijitEditorIconRightToLeft:before,.flat-right-to-left:before{content:"\f06d"}.dijitEditorIconSelectAll:before,.flat-select-all:before{content:"\f06e"}.dijitEditorIconWikiword:before,.flat-wikiword:before{content:"\f06f"}.icon-spin{-webkit-animation:spin-right 2s infinite linear;-moz-animation:spin-right 2s infinite linear;-o-animation:spin-right 2s infinite linear;-ms-animation:spin-right 2s infinite linear;animation:spin-right 2s infinite linear}.dijitIconLoading{font-size:24px}.dijitIconLoading:before{content:"\f01d";-webkit-animation:spin-left 2s linear infinite;-moz-animation:spin-left 2s linear infinite;-o-animation:spin-left 2s linear infinite;-ms-animation:spin-left 2s linear infinite;animation:spin-left 2s linear infinite}.dj_ie8 .dijitIconLoading,.dj_ie9 .dijitIconLoading{background:url(images/loadingAnimation.gif) no-repeat;height:20px;width:20px}.dj_ie8 .dijitIconLoading:before,.dj_ie9 .dijitIconLoading:before{content:""}.dijitRtl .dijitEditorIconRedo:before,.dijitRtl .dijitEditorIconUndo:before{content:"\f044"}.dijitRtl .dijitEditorIconTabIndent:before{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-o-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.dijitRtl .dijitEditorIconInsertOrderedList,.dijitRtl .dijitEditorIconInsertUnorderedList{-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-o-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}:root .dijitRtl .dijitEditorIconInsertOrderedList,:root .dijitRtl .dijitEditorIconInsertUnorderedList{filter:none}@-moz-keyframes spin-right{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes spin-right{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes spin-right{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spin-right{from{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes spin-left{from{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}to{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}}@-webkit-keyframes spin-left{from{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}to{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}}@-o-keyframes spin-left{from{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}to{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}}@keyframes spin-left{from{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}to{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}}.dijitRtl .dijitOffScreen{left:auto!important;right:-10000px!important}.dijitRtl .dijitPlaceHolder,.dijitSpinnerRtl .dijitSpinnerButtonContainer .dijitArrowButton{right:0;left:auto}.dj_iequirks .dijitComboButtonRtl button{float:left}.dj_ie .dijitTextBoxRtl .dijitInputContainer{clear:right}.dijitComboBoxRtl .dijitArrowButtonContainer,.dijitTextBoxRtl .dijitSpinnerButtonContainer,.dijitTextBoxRtl .dijitValidationContainer{border-right-width:1px!important;border-left-width:0!important}.dijitSelectRtl .dijitButtonText{float:right}.dijitTextBoxRtl .dijitArrowButtonContainer,.dijitTextBoxRtl .dijitSpinnerButtonContainer,.dijitValidationTextBoxRtl .dijitValidationContainer{float:left}.dijitSliderRtl .dijitRuleContainerV,.dijitTreeRtl .dijitTreeContainer,.flat .dijitTreeRtl .dijitTreeContainer{float:right}.dijitCalendarRtl .dijitCalendarNextYear{margin:0 .55em 0 0}.dijitCalendarRtl .dijitCalendarPreviousYear{margin:0 0 0 .55em}.dijitSliderRtl .dijitSliderImageHandleV{left:auto}.dijitSliderRtl .dijitSliderImageHandleH{left:-50%}.dijitSliderRtl .dijitSliderMoveableH{right:auto;left:0}.dj_ie .dijitSliderRtl .dijitRuleLabelV{text-align:left}.dijitSliderRtl .dijitSliderProgressBarH{float:right;right:0;left:auto}.dijitRtl .dijitContentPaneError .dijitIconError,.dijitRtl .dijitContentPaneLoading .dijitIconLoading{margin-right:0;margin-left:9px}.dijitTabControllerRtl .nowrapTabStrip{text-align:right}.dijitTabRtl .dijitTabCloseButton{margin-left:0;margin-right:1em}.dj_ie6 .dijitTabContainerRight-tabs .dijitTabRtl,.dj_ie7 .dijitTabContainerRight-tabs .dijitTabRtl{left:0}.dijitColorPaletteRtl .dijitColorPaletteUnder,.flat .dijitColorPaletteRtl .dijitColorPaletteUnder{left:auto;right:0}.dj_ie6 .dijitTabContainerLeftRtl .dijitTabContainerLeft-tabs,.dj_ie6 .dijitTabContainerRightRtl .dijitTabContainerRight-tabs{width:1%}.dj_ie .dijitTimePickerRtl .dijitTimePickerItem{width:100%}.dijitSelectRtl .dijitButtonContents{border-style:none none none solid;text-align:right}.dijitRtl .dojoDndHorizontal .dojoDndItemBefore{border-width:0 2px 0 0;padding:2px 0 2px 2px}.dijitRtl .dojoDndHorizontal .dojoDndItemAfter{border-width:0 0 0 2px;padding:2px 2px 2px 0}.flat .dijitCalendarRtl .dijitCalendarDecrease:before{content:"\f001"}.flat .dijitCalendarRtl .dijitCalendarIncrease:before,.flat .dijitMenuItemRtl .dijitMenuExpand:before{content:"\f000"}.flat .dijitDialogRtl .dijitDialogCloseIcon{right:auto;left:12px}.flat .dijitDialogRtl .dijitDialogPaneActionBar,.flat .dijitTooltipDialogRtl .dijitDialogPaneActionBar{text-align:left}.flat .dijitMenuBarRtl,.flat .dijitMenuItemRtl,.flat .dijitTabControllerRtl,.flat .dijitTabControllerRtl .nowrapTabStrip,.flat .dijitTitlePaneRtl .dijitTitlePaneTitle,.flat .dijitTreeRtl,.flat div.dijitNumberTextBoxRtl{text-align:right}.flat .dijitTitlePaneRtl .dijitClosed .dijitArrowNode:before{content:"\f006"}.flat .dijitToolbar .dijitButtonRtl,.flat .dijitToolbar .dijitComboButtonRtl,.flat .dijitToolbar .dijitDropDownButtonRtl,.flat .dijitToolbar .dijitToggleButtonRtl{margin-left:4px;margin-right:auto}.flat .dijitToolbar .dijitDropDownButtonRtl .dijitArrowButtonInner{margin-left:auto;margin-right:4px}.flat .dijitTreeRtl .dijitTreeExpandoClosed:before{content:"\e60b"}.flat .dijitAccordionTitle .arrowTextDown,.flat .dijitAccordionTitle .arrowTextUp{float:left}.flat .dijitTabContainerBottom-tabs .dijitTabRtl,.flat .dijitTabContainerTop-tabs .dijitTabRtl{margin-right:0;margin-left:-1px}.flat .dijitTabRtl .dijitTabCloseButton{margin-left:0;margin-right:4px}.flat table.dijitComboButtonRtl .dijitStretch{-webkit-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.flat table.dijitComboButtonRtl .dijitArrowButton{-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;border-left-width:1px;border-right-width:0}.flat .dijitDropDownButtonRtl .dijitButtonNode{padding-left:8px}.flat .dijitDropDownButtonRtl .dijitArrowButtonInner{margin-left:0;margin-right:12px}.flat .dijitSpinnerRtl .dijitSpinnerButtonContainer .dijitArrowButton{right:0;left:auto}.flat .dijitSelectRtl .dijitButtonText{float:right;padding:0 12px 0 0}.flat .dijitSelectRtl .dijitButtonContents{border-style:none none none solid;text-align:right}.flat .dijitComboBoxRtl .dijitButtonNode.dijitArrowButtonContainer{-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.flat .dijitComboBoxRtl .dijitArrowButtonContainer{border-right-width:1px!important;border-left-width:0!important}.flat .dijitSliderRtl .dijitSliderProgressBarH{float:right;right:0;left:auto}.flat .dijitSliderRtl .dijitSliderLeftBumper{border-left-width:0;border-right-width:1px;margin-left:0;margin-right:4px;-webkit-border-radius:0 1.5px 1.5px 0;border-radius:0 1.5px 1.5px 0}.flat .dijitSliderRtl .dijitSliderRightBumper{border-left-width:1px;border-right-width:0;margin-left:4px;margin-right:-2px;-webkit-border-radius:1.5px 0 0 1.5px;border-radius:1.5px 0 0 1.5px}.flat .dijitSliderRtl .dijitSliderMoveableH{right:auto;left:0}.flat .dijitSliderRtl .dijitSliderImageHandleV{left:auto}.flat .dijitSliderRtl .dijitSliderImageHandleH{left:-50%}.flat .dijitSliderRtl .dijitRuleContainerV{float:right}.flat .dijitTextBoxRtl .dijitSpinnerButtonContainer,.flat .dijitTextBoxRtl .dijitValidationContainer{border-right-width:1px!important;border-left-width:0!important}.flat .dijitTextBoxRtlError .dijitValidationContainer{border-left-width:0;border-right-width:1px}.flat .dijitRtl .dijitPlaceHolder{left:auto;right:0}.flat .dijitTextBoxRtl .dijitArrowButtonContainer,.flat .dijitTextBoxRtl .dijitSpinnerButtonContainer,.flat .dijitValidationTextBoxRtl .dijitValidationContainer{float:left} \ No newline at end of file diff --git a/viewer/css/theme/flat/fonts/flat-icon.eot b/viewer/css/theme/flat/fonts/flat-icon.eot new file mode 100644 index 000000000..803a04220 Binary files /dev/null and b/viewer/css/theme/flat/fonts/flat-icon.eot differ diff --git a/viewer/css/theme/flat/fonts/flat-icon.svg b/viewer/css/theme/flat/fonts/flat-icon.svg new file mode 100644 index 000000000..60db68b50 --- /dev/null +++ b/viewer/css/theme/flat/fonts/flat-icon.svg @@ -0,0 +1,122 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/viewer/css/theme/flat/fonts/flat-icon.ttf b/viewer/css/theme/flat/fonts/flat-icon.ttf new file mode 100644 index 000000000..96d0c500a Binary files /dev/null and b/viewer/css/theme/flat/fonts/flat-icon.ttf differ diff --git a/viewer/css/theme/flat/fonts/flat-icon.woff b/viewer/css/theme/flat/fonts/flat-icon.woff new file mode 100644 index 000000000..a62cb0b77 Binary files /dev/null and b/viewer/css/theme/flat/fonts/flat-icon.woff differ diff --git a/viewer/css/theme/flat/images/loadingAnimation.gif b/viewer/css/theme/flat/images/loadingAnimation.gif new file mode 100644 index 000000000..694e2cb3f Binary files /dev/null and b/viewer/css/theme/flat/images/loadingAnimation.gif differ diff --git a/viewer/css/theme/flat/images/progressBarStrips.png b/viewer/css/theme/flat/images/progressBarStrips.png new file mode 100644 index 000000000..50644f98c Binary files /dev/null and b/viewer/css/theme/flat/images/progressBarStrips.png differ diff --git a/viewer/index.html b/viewer/index.html index ca69ab38d..6dbebc88c 100644 --- a/viewer/index.html +++ b/viewer/index.html @@ -11,12 +11,13 @@ Configurable Map Viewer - - - + + + + - +
+ - + diff --git a/viewer/js/config/app.js b/viewer/js/config/app.js index f2f2f0139..f83155e22 100644 --- a/viewer/js/config/app.js +++ b/viewer/js/config/app.js @@ -12,6 +12,12 @@ }, { name: 'config', location: path + 'js/config' + }, { + name: 'proj4js', + location: '//cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.15' + }, { + name: 'flag-icon-css', + location: '//cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.8.0' } ] }; @@ -30,6 +36,7 @@ 'viewer/_MapMixin', // build and manage the Map 'viewer/_WidgetsMixin' // build and manage the Widgets + // 'viewer/_WebMapMixin' // for WebMaps //'config/_customMixin' ], function ( @@ -41,16 +48,32 @@ _MapMixin, _WidgetsMixin + // _WebMapMixin //_MyCustomMixin ) { - var controller = new (declare([ - _ControllerBase, - _ConfigMixin, + var App = declare([ + + // add custom mixins here...note order may be important and + // overriding certain methods incorrectly may break the app + // First on the list are last called last, for instance the startup + // method on _ControllerBase is called FIRST, and _LayoutMixin is called LAST + // for the most part they are interchangeable, except _ConfigMixin + // and _ControllerBase + // _LayoutMixin, + _WidgetsMixin, + // _WebMapMixin, _MapMixin, - _WidgetsMixin - ]))(); - controller.startup(); + + // configMixin should be right before _ControllerBase so it is + // called first to initialize the config object + _ConfigMixin, + + // controller base needs to be last + _ControllerBase + ]); + var app = new App(); + app.startup(); }); -})(); \ No newline at end of file +})(); diff --git a/viewer/js/config/basemaps.js b/viewer/js/config/basemaps.js index 2949e10a0..d6d7aa074 100644 --- a/viewer/js/config/basemaps.js +++ b/viewer/js/config/basemaps.js @@ -1,12 +1,12 @@ define([ - //'esri/dijit/Basemap', - //'esri/dijit/BasemapLayer' -], function (/* Basemap, BasemapLayer */) { + 'esri/dijit/Basemap', + 'esri/dijit/BasemapLayer', + 'dojo/i18n!./nls/main' +], function (Basemap, BasemapLayer, i18n) { return { map: true, // needs a reference to the map - mode: 'agol', // mut be either 'agol' or 'custom' - title: 'Basemaps', // title for widget + //mode: 'agol', // mut be either 'agol' or 'custom' /* optional starting basemap / otherwise uses the basemap from the map @@ -22,47 +22,21 @@ define([ // define all valid basemaps here. basemaps: { - streets: { - title: 'Streets' - }, - 'streets-night-vector': { // requires v3.16 or higher - title: 'Streets (Night)' - }, - 'streets-navigation-vector': { // requires v3.16 or higher - title: 'Streets (Navigation)' - }, - 'streets-relief-vector': { // requires v3.16 or higher - title: 'Street (Relief)' - }, - satellite: { - title: 'Satellite' - }, - hybrid: { - title: 'Hybrid' - }, - topo: { - title: 'Topo' - }, - 'terrain': { - title: 'Terrain' - }, - 'gray-vector': { // requires v3.16 or higher - title: 'Gray' - }, - 'dark-gray-vector': { // requires v3.16 or higher - title: 'Dark Gray' - }, - oceans: { - title: 'Oceans' - }, - 'national-geographic': { - title: 'Nat Geo' - }, - osm: { - title: 'Open Street Map' - }, - LandsatShaded: { - title: 'Landsat Shaded', + streets: {}, + 'streets-night-vector': {}, // requires v3.16 or higher + 'streets-navigation-vector': {}, // requires v3.16 or higher + 'streets-relief-vector': {}, // requires v3.16 or higher + satellite: {}, + hybrid: {}, + topo: {}, + terrain: {}, + 'gray-vector': {}, // requires v3.16 or higher + 'dark-gray-vector': {}, // requires v3.16 or higher + oceans: {}, + 'national-geographic': {}, + osm: {}, + landsatShaded: { + title: i18n.basemaps.landsatShaded, basemap: { baseMapLayers: [ { @@ -71,8 +45,8 @@ define([ ] } }, - EarthAtNight: { - title: 'Earth at Night', + earthAtNight: { + title: i18n.basemaps.earthAtNight, basemap: { baseMapLayers: [ { @@ -81,15 +55,28 @@ define([ ] } }, - DavidRumseyMap1812: { - title: 'David Rumsey 1812', + davidRumseyMap1812: { + title: i18n.basemaps.davidRumseyMap1812, basemap: { baseMapLayers: [ { - url: 'http://tiles.arcgis.com/tiles/IEuSomXfi6iB7a25/arcgis/rest/services/World_Globe_1812/MapServer' + url: 'https://tiles.arcgis.com/tiles/IEuSomXfi6iB7a25/arcgis/rest/services/World_Globe_1812/MapServer' } ] } + }, + mapboxPirates: { + title: 'Pirates (mapbox.com)', + basemap: new Basemap({ + id: 'mapboxPirates', + layers: [new BasemapLayer({ + url: 'https://${subDomain}.tiles.mapbox.com/v3/aj.Sketchy2/${level}/${col}/${row}.png', + copyright: 'mapbox, 2016', + id: 'mapboxPirates', + subDomains: ['a', 'b', 'c', 'd'], + type: 'WebTiledLayer' + })] + }) } // additional examples of vector tile basemaps (requires v3.16 or higher) @@ -141,7 +128,7 @@ define([ ] } }, - darkGrayVector: { + 'dark-gray-vector': { title: 'Dark Gray Canvas', basemap: { baseMapLayers: [ @@ -163,7 +150,7 @@ define([ ] } }, - streetsNightVector: { + 'streets-night-vector': { title: 'Streets Night', basemap: { baseMapLayers: [ @@ -252,7 +239,7 @@ define([ basemap: new Basemap({ id: 'stamenToner', layers: [new BasemapLayer({ - url: 'http://tile.stamen.com/toner/${level}/${col}/${row}.png', + url: 'https://tile.stamen.com/toner/${level}/${col}/${row}.png', copyright: 'stamen, 2016', id: 'stamenToner', type: 'WebTiledLayer' @@ -264,7 +251,7 @@ define([ basemap: new Basemap({ id: 'stamenTerrain', layers: [new BasemapLayer({ - url: 'http://tile.stamen.com/terrain/${level}/${col}/${row}.png', + url: 'https://tile.stamen.com/terrain/${level}/${col}/${row}.png', copyright: 'stamen, 2016', id: 'stamenTerrain', type: 'WebTiledLayer' @@ -276,26 +263,13 @@ define([ basemap: new Basemap({ id: 'stamenWatercolor', layers: [new BasemapLayer({ - url: 'http://tile.stamen.com/watercolor/${level}/${col}/${row}.png', + url: 'https://tile.stamen.com/watercolor/${level}/${col}/${row}.png', copyright: 'stamen, 2016', id: 'stamenWatercolor', type: 'WebTiledLayer' })] }) }, - mapboxPirates: { - title: 'Pirates (mapbox.com)', - basemap: new Basemap({ - id: 'mapboxPirates', - layers: [new BasemapLayer({ - url: 'https://${subDomain}.tiles.mapbox.com/v3/aj.Sketchy2/${level}/${col}/${row}.png', - copyright: 'mapbox, 2016', - id: 'mapboxPirates', - subDomains: ['a', 'b', 'c', 'd'], - type: 'WebTiledLayer' - })] - }) - } */ } }; diff --git a/viewer/js/config/bookmarks.js b/viewer/js/config/bookmarks.js index cf89998e3..90beb5de4 100644 --- a/viewer/js/config/bookmarks.js +++ b/viewer/js/config/bookmarks.js @@ -1,18 +1,35 @@ -define({ - map: true, - editable: true, - bookmarks: [ - { - extent: { - xmin: -15489130.48708616, - ymin: 398794.4860580916, - xmax: -5891085.7193757, - ymax: 8509680.431452557, - spatialReference: { - wkid: 102100 - } +define([ + 'dojo/i18n!./nls/main' +], function (i18n) { + + return { + map: true, + editable: true, + bookmarks: [ + { + extent: { + xmin: -15489130.48708616, + ymin: 398794.4860580916, + xmax: -5891085.7193757, + ymax: 8509680.431452557, + spatialReference: { + wkid: 102100 + } + }, + name: i18n.bookmarks.usa }, - name: 'USA' - } - ] + { + extent: { + xmin: 0, + ymin: 0, + xmax: 0, + ymax: 0, + spatialReference: { + wkid: 102100 + } + }, + name: i18n.bookmarks.nullIsland + } + ] + }; }); \ No newline at end of file diff --git a/viewer/js/config/find.js b/viewer/js/config/find.js index 5e9cb84c7..ad6bfb877 100644 --- a/viewer/js/config/find.js +++ b/viewer/js/config/find.js @@ -1,112 +1,117 @@ /*eslint no-console: 0, no-alert: 0*/ -define({ - map: true, - zoomExtentFactor: 2, - queries: [ - { - description: 'Find A Public Safety Location By Name', - url: 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer', - layerIds: [1, 2, 3, 4, 5, 6, 7], - searchFields: ['FDNAME, PDNAME', 'NAME', 'RESNAME'], - minChars: 2, - gridColumns: [ - { - field: 'Name', - label: 'Name' - }, - { - field: 'layerName', - label: 'Layer', - width: 100, - sortable: false, - resizable: false - } - ], - sort: [ - { - attribute: 'Name', - descending: false - } - ], - prompt: 'fdname, pdname, name or resname', - selectionMode: 'single' - }, - { - description: 'Find Incident By Code/Description', - url: 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer', - layerIds: [15, 17, 18], - searchFields: ['FCODE', 'DESCRIPTION'], - minChars: 4, - gridColumns: [ - { - field: 'layerName', - label: 'Layer', - width: 100, - sortable: false, - resizable: false - }, - { - field: 'Fcode', - label: 'Fcode', - width: 100 - }, - { - field: 'Description', - label: 'Descr' - }, - { - field: 'SORT_VALUE', - visible: false, - get: function (findResult) { - return findResult.layerName + ' ' + findResult.feature.attributes.Fcode; //seems better to use attributes[ 'Fcode' ] but fails build. Attribute names will be aliases and may contain spaces and mixed cases. +define([ + 'dojo/i18n!./nls/main' +], function (i18n) { + + return { + map: true, + zoomExtentFactor: 2, + queries: [ + { + description: i18n.find.louisvillePubSafety, + url: 'https://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer', + layerIds: [1, 2, 3, 4, 5, 6, 7], + searchFields: ['FDNAME, PDNAME', 'NAME', 'RESNAME'], + minChars: 2, + gridColumns: [ + { + field: 'Name', + label: 'Name' + }, + { + field: 'layerName', + label: 'Layer', + width: 100, + sortable: false, + resizable: false } - } - ], - sort: [ - { - attribute: 'SORT_VALUE', - descending: false - } - ], - prompt: 'fdname, pdname, name or resname', - customGridEventHandlers: [ - { - event: '.dgrid-row:click', - handler: function (event) { - alert('You clicked a row!'); - console.log(event); + ], + sort: [ + { + attribute: 'Name', + descending: false + } + ], + prompt: 'fdname, pdname, name or resname', + selectionMode: 'single' + }, + { + description: i18n.find.sf311Incidents, + url: 'https://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer', + layerIds: [15, 17, 18], + searchFields: ['FCODE', 'DESCRIPTION'], + minChars: 4, + gridColumns: [ + { + field: 'layerName', + label: 'Layer', + width: 100, + sortable: false, + resizable: false + }, + { + field: 'Fcode', + label: 'Fcode', + width: 100 + }, + { + field: 'Description', + label: 'Descr' + }, + { + field: 'SORT_VALUE', + visible: false, + get: function (findResult) { + return findResult.layerName + ' ' + findResult.feature.attributes.Fcode; //seems better to use attributes[ 'Fcode' ] but fails build. Attribute names will be aliases and may contain spaces and mixed cases. + } } + ], + sort: [ + { + attribute: 'SORT_VALUE', + descending: false + } + ], + prompt: 'fdname, pdname, name or resname', + customGridEventHandlers: [ + { + event: '.dgrid-row:click', + handler: function (event) { + alert('You clicked a row!'); + console.log(event); + } + } + ] + } + ], + selectionSymbols: { + polygon: { + type: 'esriSFS', + style: 'esriSFSSolid', + color: [255, 0, 0, 62], + outline: { + type: 'esriSLS', + style: 'esriSLSSolid', + color: [255, 0, 0, 255], + width: 3 + } + }, + point: { + type: 'esriSMS', + style: 'esriSMSCircle', + size: 25, + color: [255, 0, 0, 62], + angle: 0, + xoffset: 0, + yoffset: 0, + outline: { + type: 'esriSLS', + style: 'esriSLSSolid', + color: [255, 0, 0, 255], + width: 2 } - ] - } - ], - selectionSymbols: { - polygon: { - type: 'esriSFS', - style: 'esriSFSSolid', - color: [255, 0, 0, 62], - outline: { - type: 'esriSLS', - style: 'esriSLSSolid', - color: [255, 0, 0, 255], - width: 3 } }, - point: { - type: 'esriSMS', - style: 'esriSMSCircle', - size: 25, - color: [255, 0, 0, 62], - angle: 0, - xoffset: 0, - yoffset: 0, - outline: { - type: 'esriSLS', - style: 'esriSLSSolid', - color: [255, 0, 0, 255], - width: 2 - } - } - }, - selectionMode: 'extended' + selectionMode: 'extended' + }; }); \ No newline at end of file diff --git a/viewer/js/config/identify.js b/viewer/js/config/identify.js index ba781edda..a85fb35b1 100644 --- a/viewer/js/config/identify.js +++ b/viewer/js/config/identify.js @@ -1,68 +1,80 @@ -define({ - map: true, - mapClickMode: true, - mapRightClickMenu: true, - identifyLayerInfos: true, - identifyTolerance: 5, +define([ + 'dojo/i18n!./nls/main', + 'dojo/_base/lang' +], function (i18n, lang) { - // config object definition: - // {:{ - // :{ - // - // } - // }, - // :{ - // :{ - // - // } - // } - // } + var linkTemplate = '{text}'; + function directionsFormatter (noValue, attributes) { + return lang.replace(linkTemplate, { + url: 'https://www.google.com/maps/dir/' + attributes.Address + ' Louisville, KY', + text: 'Get Directions' + }); + } + return { + map: true, + mapClickMode: true, + mapRightClickMenu: true, + identifyLayerInfos: true, + identifyTolerance: 5, + draggable: false, - // for details on pop-up definition see: https://developers.arcgis.com/javascript/jshelp/intro_popuptemplate.html + // config object definition: + // {:{ + // :{ + // + // } + // }, + // :{ + // :{ + // + // } + // } + // } - identifies: { - meetupHometowns: { - 0: { - title: 'Hometowns', - fieldInfos: [{ - fieldName: 'Location', - visible: true - }] - } - }, - louisvillePubSafety: { - 2: { - title: 'Police Station', - fieldInfos: [{ - fieldName: 'Name', - visible: true - }, { - fieldName: 'Address', - visible: true - }, { - fieldName: 'Type', - visible: true - }, { - fieldName: 'Police Function', - visible: true - }, { - fieldName: 'Last Update Date', - visible: true - }] - }, - 8: { - title: 'Traffic Camera', - description: '{Description} lasted updated: {Last Update Date}', - mediaInfos: [{ - title: '', - caption: '', - type: 'image', - value: { - sourceURL: '{Location URL}', - linkURL: '{Location URL}' - } - }] + // for details on pop-up definition see: https://developers.arcgis.com/javascript/jshelp/intro_popuptemplate.html + + identifies: { + louisvillePubSafety: { + 2: { + title: i18n.identify.louisvillePubSafety.policeStation, + fieldInfos: [{ + // example of adding a 'calculated' or formatted field + // click on a louisville kentucky police station to see + // the result + fieldName: 'Directions', + visible: true, + formatter: directionsFormatter + }, { + fieldName: 'Name', + visible: true + }, { + fieldName: 'Address', + visible: true + }, { + fieldName: 'Type', + visible: true + }, { + fieldName: 'Police Function', + visible: true + }, { + fieldName: 'Last Update Date', + visible: true + }] + }, + 8: { + title: i18n.identify.louisvillePubSafety.trafficCamera, + description: '{Description} lasted updated: {Last Update Date}', + mediaInfos: [{ + title: '', + caption: '', + type: 'image', + value: { + sourceURL: '{Location URL}', + linkURL: '{Location URL}' + } + }] + } } } - } -}); \ No newline at end of file + }; +}); diff --git a/viewer/js/config/nls/es/main.js b/viewer/js/config/nls/es/main.js new file mode 100644 index 000000000..47acf6117 --- /dev/null +++ b/viewer/js/config/nls/es/main.js @@ -0,0 +1,50 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + basemaps: { + davidRumseyMap1812: 'David Rumsey 1812', + earthAtNight: 'Tierra en la noche', + landsatShaded: 'Landsat sombreada' + }, + bookmarks: { + nullIsland: 'Isla nula', + usa: 'EE.UU.' + }, + find: { + louisvillePubSafety: 'Encontrar un local de Seguridad Pública por su nombre', + sf311Incidents: 'Encuentra incidente por código/descripción' + }, + identify: { + louisvillePubSafety: { + policeStation: 'Comisaría de Policía', + trafficCamera: 'Cámara de Tráfico' + } + }, + viewer: { + operationalLayers: { + damageAssessment: 'Valoración de daño', + louisvillePubSafety: 'Seguridad Pública de Louisville', + restaurants: 'Restaurantes', + sf311Incidents: '311 Incidentes de San Francisco' + }, + titles: { + header: 'Configurable Map Viewer', + pageTitle: 'Configurable Map Viewer — Un visor de mapas configurables', // One configurable map viewer + subHeader: 'personalizarlo a su gusto' // customize it at your will (the literal translation doesn’t sound good) + }, + widgets: { + bookmarks: 'Marcadores', + directions: 'Direcciones', + draw: 'Dibujar', + editor: 'Editor', + find: 'Encontrar', + help: 'Ayuda', + identify: 'Identificar', + measure: 'Medición', + layerControl: 'Capas', + legend: 'Leyenda', + locale: 'Lugar', + print: 'Imprimir', + streetview: 'Google Street View' + } + } +}); \ No newline at end of file diff --git a/viewer/js/config/nls/fr/main.js b/viewer/js/config/nls/fr/main.js new file mode 100644 index 000000000..dbf7b877d --- /dev/null +++ b/viewer/js/config/nls/fr/main.js @@ -0,0 +1,50 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + basemaps: { + davidRumseyMap1812: 'David Rumsey 1812', + earthAtNight: 'Terre la nuit', + landsatShaded: 'Landsat et relief ombragé' + }, + bookmarks: { + nullIsland: 'Île Null', + usa: 'États-Unis' + }, + find: { + louisvillePubSafety: 'Trouvez un bâtiment de sécurité publique par nom', + sf311Incidents: 'Trouver un incident par code/description' + }, + identify: { + louisvillePubSafety: { + policeStation: 'Poste de police', + trafficCamera: 'Caméra de circulation' + } + }, + viewer: { + operationalLayers: { + damageAssessment: 'Évaluation des dommages', + louisvillePubSafety: 'Sécurité publique de Louisville', + restaurants: 'Restaurants', + sf311Incidents: 'Incidents 311 de San Francisco' + }, + titles: { + header: 'Configurable Map Viewer', + pageTitle: 'Configurable Map Viewer - Un visualiseur de cartes configurables', + subHeader: 'Personnalisez-le' + }, + widgets: { + bookmarks: 'Géosignets', + directions: 'Directions', + draw: 'Annotations', + editor: 'Éditeur', + find: 'Rechercher', + help: 'Aide', + identify: 'Identifier', + measure: 'Mesures', + layerControl: 'Couches d\'information', + legend: 'Légende', + locale: 'Lieu', + print: 'Impression', + streetview: 'Google StreetView' + } + } +}); diff --git a/viewer/js/config/nls/main.js b/viewer/js/config/nls/main.js new file mode 100644 index 000000000..d4e18302b --- /dev/null +++ b/viewer/js/config/nls/main.js @@ -0,0 +1,56 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + root: { + basemaps: { + davidRumseyMap1812: 'David Rumsey 1812', + earthAtNight: 'Earth at Night', + landsatShaded: 'Landsat Shaded' + }, + bookmarks: { + nullIsland: 'Null Island', + usa: 'USA' + }, + find: { + louisvillePubSafety: 'Find A Public Safety Location By Name', + sf311Incidents: 'Find Incident By Code/Description' + }, + identify: { + louisvillePubSafety: { + policeStation: 'Police Station', + trafficCamera: 'Traffic Camera' + } + }, + viewer: { + operationalLayers: { + damageAssessment: 'Damage Assessment', + louisvillePubSafety: 'Louisville Public Safety', + restaurants: 'Restaurants', + sf311Incidents: 'San Francisco 311 Incidents' + }, + titles: { + header: 'Configurable Map Viewer', + pageTitle: 'Configurable Map Viewer', + subHeader: 'make it your own' + }, + widgets: { + bookmarks: 'Bookmarks', + directions: 'Directions', + draw: 'Draw', + editor: 'Editor', + find: 'Find', + help: 'Help', + identify: 'Identify', + measure: 'Measurement', + layerControl: 'Layers', + legend: 'Legend', + locale: 'Locale', + print: 'Print', + streetview: 'Google Street View' + } + } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true +}); diff --git a/viewer/js/config/nls/pt-br/main.js b/viewer/js/config/nls/pt-br/main.js new file mode 100644 index 000000000..3bb82f665 --- /dev/null +++ b/viewer/js/config/nls/pt-br/main.js @@ -0,0 +1,50 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + basemaps: { + davidRumseyMap1812: 'David Rumsey 1812', + earthAtNight: 'Terra à noite', + landsatShaded: 'Landsat sombreado' + }, + bookmarks: { + nullIsland: 'Ilha Nula', + usa: 'EUA' + }, + find: { + louisvillePubSafety: 'Encontrar um local de Segurança Pública pelo nome', + sf311Incidents: 'Encontrar incidente por código/descrição' + }, + identify: { + louisvillePubSafety: { + policeStation: 'Esquadra da Polícia', + trafficCamera: 'Câmara de trânsito' + } + }, + viewer: { + operationalLayers: { + damageAssessment: 'Avaliação de dano', + louisvillePubSafety: 'Segurança Pública de Louisville', + restaurants: 'Restaurantes', + sf311Incidents: 'Incidentes do 311 de São Francisco' + }, + titles: { + header: 'Configurable Map Viewer', + pageTitle: 'Configurable Map Viewer — Um visualizador de mapas configurável', // One configurable map viewer + subHeader: 'personaliza-o ao teu gosto' // customize it at your will (the literal translation doesn’t sound good) + }, + widgets: { + bookmarks: 'Marcadores', + directions: 'Direcções', + draw: 'Desenhar', + editor: 'Editor', + find: 'Procurar', + help: 'Ajuda', + identify: 'Identificar', + measure: 'Medir', + layerControl: 'Camadas', + legend: 'Legendas', + locale: 'Localidade', + print: 'Imprimir', + streetview: 'Google Street View' + } + } +}); diff --git a/viewer/js/config/nls/pt-pt/main.js b/viewer/js/config/nls/pt-pt/main.js new file mode 100644 index 000000000..3bb82f665 --- /dev/null +++ b/viewer/js/config/nls/pt-pt/main.js @@ -0,0 +1,50 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + basemaps: { + davidRumseyMap1812: 'David Rumsey 1812', + earthAtNight: 'Terra à noite', + landsatShaded: 'Landsat sombreado' + }, + bookmarks: { + nullIsland: 'Ilha Nula', + usa: 'EUA' + }, + find: { + louisvillePubSafety: 'Encontrar um local de Segurança Pública pelo nome', + sf311Incidents: 'Encontrar incidente por código/descrição' + }, + identify: { + louisvillePubSafety: { + policeStation: 'Esquadra da Polícia', + trafficCamera: 'Câmara de trânsito' + } + }, + viewer: { + operationalLayers: { + damageAssessment: 'Avaliação de dano', + louisvillePubSafety: 'Segurança Pública de Louisville', + restaurants: 'Restaurantes', + sf311Incidents: 'Incidentes do 311 de São Francisco' + }, + titles: { + header: 'Configurable Map Viewer', + pageTitle: 'Configurable Map Viewer — Um visualizador de mapas configurável', // One configurable map viewer + subHeader: 'personaliza-o ao teu gosto' // customize it at your will (the literal translation doesn’t sound good) + }, + widgets: { + bookmarks: 'Marcadores', + directions: 'Direcções', + draw: 'Desenhar', + editor: 'Editor', + find: 'Procurar', + help: 'Ajuda', + identify: 'Identificar', + measure: 'Medir', + layerControl: 'Camadas', + legend: 'Legendas', + locale: 'Localidade', + print: 'Imprimir', + streetview: 'Google Street View' + } + } +}); diff --git a/viewer/js/config/viewer.js b/viewer/js/config/viewer.js index 9569a7007..d11dc7490 100644 --- a/viewer/js/config/viewer.js +++ b/viewer/js/config/viewer.js @@ -4,8 +4,11 @@ define([ 'esri/config', /*'esri/urlUtils',*/ 'esri/tasks/GeometryService', - 'esri/layers/ImageParameters' -], function (units, Extent, esriConfig, /*urlUtils,*/ GeometryService, ImageParameters) { + 'esri/layers/ImageParameters', + 'gis/plugins/Google', + 'dojo/i18n!./nls/main', + 'dojo/topic' +], function (units, Extent, esriConfig, /*urlUtils,*/ GeometryService, ImageParameters, GoogleMapsLoader, i18n, topic) { // url to your proxy page, must be on same machine hosting you app. See proxy folder for readme. esriConfig.defaults.io.proxyUrl = 'proxy/proxy.ashx'; @@ -19,7 +22,11 @@ define([ });*/ // url to your geometry server. - esriConfig.defaults.geometryService = new GeometryService('http://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer'); + esriConfig.defaults.geometryService = new GeometryService('https://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer'); + + // Use your own Google Maps API Key. + // https://developers.google.com/maps/documentation/javascript/get-api-key + GoogleMapsLoader.KEY = 'NOT-A-REAL-API-KEY'; // helper function returning ImageParameters for dynamic layers // example: @@ -40,6 +47,27 @@ define([ return ip; } + //some example topics for listening to menu item clicks + //these topics publish a simple message to the growler + //in a real world example, these topics would be used + //in their own widget to listen for layer menu click events + topic.subscribe('layerControl/hello', function (event) { + topic.publish('growler/growl', { + title: 'Hello!', + message: event.layer._titleForLegend + ' ' + + (event.subLayer ? event.subLayer.name : '') + + ' says hello' + }); + }); + topic.subscribe('layerControl/goodbye', function (event) { + topic.publish('growler/growl', { + title: 'Goodbye!', + message: event.layer._titleForLegend + ' ' + + (event.subLayer ? event.subLayer.name : '') + + ' says goodbye' + }); + }); + return { // used for debugging your app isDebug: true, @@ -53,6 +81,10 @@ define([ zoom: 5, sliderStyle: 'small' }, + + //webMapId: 'ef9c7fbda731474d98647bebb4b33c20', // High Cost Mortgage + // webMapOptions: {}, + // panes: { // left: { // splitter: true @@ -81,13 +113,34 @@ define([ // }, // collapseButtonsPane: 'center', //center or outer + // custom titles + titles: { + header: i18n.viewer.titles.header, + subHeader: i18n.viewer.titles.subHeader, + pageTitle: i18n.viewer.titles.pageTitle + }, + + // user-defined layer types + /* + layerTypes: { + myCustomLayer: 'widgets/MyCustomLayer' + }, + */ + + // user-defined widget types + /* + widgetTypes: [ + 'myWidgetType' + ], + */ + // operationalLayers: Array of Layers to load on top of the basemap: valid 'type' options: 'dynamic', 'tiled', 'feature'. // The 'options' object is passed as the layers options for constructor. Title will be used in the legend only. id's must be unique and have no spaces. // 3 'mode' options: MODE_SNAPSHOT = 0, MODE_ONDEMAND = 1, MODE_SELECTION = 2 operationalLayers: [{ type: 'feature', - url: 'http://services1.arcgis.com/6bXbLtkf4y11TosO/arcgis/rest/services/Restaurants/FeatureServer/0', - title: 'Restaurants', + url: 'https://services1.arcgis.com/6bXbLtkf4y11TosO/arcgis/rest/services/Restaurants/FeatureServer/0', + title: i18n.viewer.operationalLayers.restaurants, options: { id: 'restaurants', opacity: 1.0, @@ -101,24 +154,31 @@ define([ legendLayerInfos: { exclude: false, layerInfo: { - title: 'Restaurants' + title: i18n.viewer.operationalLayers.restaurants } } }, { type: 'feature', - url: 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0', - title: 'San Francisco 311 Incidents', + url: 'https://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0', + title: i18n.viewer.operationalLayers.sf311Incidents, options: { id: 'sf311Incidents', opacity: 1.0, visible: true, outFields: ['req_type', 'req_date', 'req_time', 'address', 'district'], mode: 0 + }, + layerControlLayerInfos: { + menu: [{ + topic: 'hello', + label: 'Say Hello Custom', + iconClass: 'fa fa-smile-o' + }] } }, { type: 'dynamic', - url: 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer', - title: 'Louisville Public Safety', + url: 'https://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer', + title: i18n.viewer.operationalLayers.louisvillePubSafety, options: { id: 'louisvillePubSafety', opacity: 1.0, @@ -141,10 +201,10 @@ define([ } }, { type: 'dynamic', - url: 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/MapServer', - title: 'Damage Assessment', + url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/MapServer', + title: i18n.viewer.operationalLayers.damageAssessment, options: { - id: 'DamageAssessment', + id: 'damageAssessment', opacity: 1.0, visible: true, imageParameters: buildImageParameters() @@ -155,14 +215,21 @@ define([ layerControlLayerInfos: { swipe: true, metadataUrl: true, - expanded: true + expanded: true, + + //override the menu on this particular layer + subLayerMenu: [{ + topic: 'hello', + label: 'Say Hello', + iconClass: 'fa fa-smile-o' + }] } /* //examples of vector tile layers (beta in v3.15) }, { type: 'vectortile', title: 'Light Gray Canvas Vector', - url: '//www.arcgis.com/sharing/rest/content/items/bdf1eec3fa79456c8c7c2bb62f86dade/resources/styles/root.json', + url: 'https//www.arcgis.com/sharing/rest/content/items/bdf1eec3fa79456c8c7c2bb62f86dade/resources/styles/root.json', options: { id: 'vectortile1', opacity: 0.8, @@ -176,12 +243,12 @@ define([ id: 'vectortile2', opacity: 1.0, visible: true, - 'glyphs': '//www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/fonts/{fontstack}/{range}.pbf', - 'sprite': '//www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/sprites/sprite', + 'glyphs': 'https://www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/fonts/{fontstack}/{range}.pbf', + 'sprite': 'https://www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/sprites/sprite', 'version': 8, 'sources': { 'esri': { - 'url': '//basemapsdev.arcgis.com/arcgis/rest/services/World_Basemap/VectorTileServer', + 'url': 'https://basemapsdev.arcgis.com/arcgis/rest/services/World_Basemap/VectorTileServer', 'type': 'vector' } }, @@ -225,7 +292,7 @@ define([ srcNodeRef: 'growlerDijit', options: {} }, - geocoder: { + search: { include: true, type: 'domNode', path: 'esri/dijit/Search', @@ -238,24 +305,25 @@ define([ expanded: false } }, + basemaps: { + include: true, + id: 'basemaps', + type: 'domNode', + path: 'gis/dijit/Basemaps', + srcNodeRef: 'basemapsDijit', + options: 'config/basemaps' + }, identify: { include: true, id: 'identify', type: 'titlePane', path: 'gis/dijit/Identify', - title: 'Identify', + title: i18n.viewer.widgets.identify, + iconClass: 'fa-info-circle', open: false, position: 3, options: 'config/identify' }, - basemaps: { - include: true, - id: 'basemaps', - type: 'domNode', - path: 'gis/dijit/Basemaps', - srcNodeRef: 'basemapsDijit', - options: 'config/basemaps' - }, mapInfo: { include: false, id: 'mapInfo', @@ -341,10 +409,11 @@ define([ include: true, id: 'legend', type: 'titlePane', - path: 'esri/dijit/Legend', - title: 'Legend', + path: 'gis/dijit/Legend', + title: i18n.viewer.widgets.legend, + iconClass: 'fa-picture-o', open: false, - position: 0, + position: 1, options: { map: true, legendLayerInfos: true @@ -355,7 +424,8 @@ define([ id: 'layerControl', type: 'titlePane', path: 'gis/dijit/LayerControl', - title: 'Layers', + title: i18n.viewer.widgets.layerControl, + iconClass: 'fa-th-list', open: false, position: 0, options: { @@ -363,7 +433,25 @@ define([ layerControlLayerInfos: true, separated: true, vectorReorder: true, - overlayReorder: true + overlayReorder: true, + // create a custom menu entry in all of these feature types + // the custom menu item will publish a topic when clicked + menu: { + feature: [{ + topic: 'hello', + iconClass: 'fa fa-smile-o', + label: 'Say Hello' + }] + }, + //create a example sub layer menu that will + //apply to all layers of type 'dynamic' + subLayerMenu: { + dynamic: [{ + topic: 'goodbye', + iconClass: 'fa fa-frown-o', + label: 'Say goodbye' + }] + } } }, bookmarks: { @@ -371,7 +459,8 @@ define([ id: 'bookmarks', type: 'titlePane', path: 'gis/dijit/Bookmarks', - title: 'Bookmarks', + title: i18n.viewer.widgets.bookmarks, + iconClass: 'fa-bookmark', open: false, position: 2, options: 'config/bookmarks' @@ -382,7 +471,8 @@ define([ type: 'titlePane', canFloat: true, path: 'gis/dijit/Find', - title: 'Find', + title: i18n.viewer.widgets.find, + iconClass: 'fa-search', open: false, position: 3, options: 'config/find' @@ -393,7 +483,8 @@ define([ type: 'titlePane', canFloat: true, path: 'gis/dijit/Draw', - title: 'Draw', + title: i18n.viewer.widgets.draw, + iconClass: 'fa-paint-brush', open: false, position: 4, options: { @@ -407,7 +498,8 @@ define([ type: 'titlePane', canFloat: true, path: 'gis/dijit/Measurement', - title: 'Measurement', + title: i18n.viewer.widgets.measure, + iconClass: 'fa-expand', open: false, position: 5, options: { @@ -423,7 +515,8 @@ define([ type: 'titlePane', canFloat: true, path: 'gis/dijit/Print', - title: 'Print', + title: i18n.viewer.widgets.print, + iconClass: 'fa-print', open: false, position: 6, options: { @@ -441,14 +534,15 @@ define([ id: 'directions', type: 'titlePane', path: 'gis/dijit/Directions', - title: 'Directions', + title: i18n.viewer.widgets.directions, + iconClass: 'fa-map-signs', open: false, position: 7, options: { map: true, mapRightClickMenu: true, options: { - routeTaskUrl: 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer/Route', + routeTaskUrl: 'https://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer/Route', routeParams: { directionsLanguage: 'en-US', directionsLengthUnits: units.MILES @@ -462,7 +556,8 @@ define([ id: 'editor', type: 'titlePane', path: 'gis/dijit/Editor', - title: 'Editor', + title: i18n.viewer.widgets.editor, + iconClass: 'fa-pencil', open: false, position: 8, options: { @@ -491,7 +586,8 @@ define([ canFloat: true, position: 9, path: 'gis/dijit/StreetView', - title: 'Google Street View', + title: i18n.viewer.widgets.streetview, + iconClass: 'fa-street-view', paneOptions: { resizable: true, resizeOptions: { @@ -507,15 +603,29 @@ define([ mapRightClickMenu: true } }, + locale: { + include: true, + id: 'locale', + //type: 'titlePane', + //position: 0, + //open: true, + type: 'domNode', + srcNodeRef: 'geocodeDijit', + path: 'gis/dijit/Locale', + title: i18n.viewer.widgets.locale, + options: { + style: 'margin-left: 30px;' + } + }, help: { include: true, id: 'help', type: 'floating', path: 'gis/dijit/Help', - title: 'Help', + title: i18n.viewer.widgets.help, options: {} } } }; -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/BasemapGallery.js b/viewer/js/gis/dijit/BasemapGallery.js new file mode 100644 index 000000000..9b0c5b31a --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery.js @@ -0,0 +1,65 @@ +define([ + 'dojo/_base/declare', + 'dijit/_WidgetBase', + 'dijit/_TemplatedMixin', + 'dijit/_WidgetsInTemplateMixin', + + 'dojo/_base/lang', + 'dojo/topic', + + 'esri/dijit/BasemapGallery', + + 'dojo/text!./BasemapGallery/templates/BasemapGallery.html', + 'dojo/i18n!./BasemapGallery/nls/resource', + + 'dijit/layout/ContentPane', + 'dijit/TitlePane', + + 'xstyle/css!./BasemapGallery/css/BasemapGallery.css' + +], function ( + declare, + _WidgetBase, + _TemplatedMixin, + _WidgetsInTemplateMixin, + + lang, + topic, + + BasemapGallery, + + template, + i18n +) { + + return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { + widgetsInTemplate: true, + templateString: template, + i18n: i18n, + baseClass: 'cmvBasemapGalleryWidget', + + galleryOptions: { + showArcGISBasemaps: true + }, + + postCreate: function () { + this.inherited(arguments); + + var opts = lang.mixin({ + map: this.map + }, this.galleryOptions || {}); + this.basemapGallery = new BasemapGallery(opts, 'basemapGallery'); + this.basemapGallery.startup(); + + this.basemapGallery.on('selection-change', lang.hitch(this, 'basemapSelected')); + + this.basemapGallery.on('error', function (msg) { + topic.publish('viewer/handleError', 'basemap gallery error: ' + msg); + }); + }, + + basemapSelected: function (/* basemap */) { + this.basemapGalleryTitlePane.set('open', false); + } + }); +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/BasemapGallery/css/BasemapGallery.css b/viewer/js/gis/dijit/BasemapGallery/css/BasemapGallery.css new file mode 100644 index 000000000..4bb99ef2b --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery/css/BasemapGallery.css @@ -0,0 +1,41 @@ +.cmvBasemapGalleryWidget .dijitTitlePane { + border-radius: 4px; +} + +.cmvBasemapGalleryWidget .dijitTitlePaneTitle { + border-color: #BBB; + padding: 4px; +} + +.cmvBasemapGalleryWidget .dijitTitlePane .dijitArrowNode { + float: right; + padding-top: 3px; +} + +.cmvBasemapGalleryWidget .dijitTitlePaneTextNode:before { + content: '\f009'; + font-family: FontAwesome; +} + +.cmvBasemapGalleryWidget .dijitTitlePaneContentOuter { + border-color: #BBB; +} + +.cmvBasemapGalleryWidget .dijitTitlePaneContentInner { + padding: 0; +} + +.cmvBasemapGalleryWidget .basemapGalleryContent { + height: 280px; + overflow:auto; + width: 410px; +} +.cmvBasemapGalleryWidget .esriBasemapGalleryNode { + margin: 5px 15px; +} + +@media screen and (max-width: 767px) { + .cmvBasemapGalleryWidget .basemapGalleryContent { + width: 140px; + } +} \ No newline at end of file diff --git a/viewer/js/gis/dijit/BasemapGallery/nls/es/resource.js b/viewer/js/gis/dijit/BasemapGallery/nls/es/resource.js new file mode 100644 index 000000000..48f8546d9 --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery/nls/es/resource.js @@ -0,0 +1,3 @@ +define ({ + title: 'Mapas base' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/BasemapGallery/nls/fr/resource.js b/viewer/js/gis/dijit/BasemapGallery/nls/fr/resource.js new file mode 100644 index 000000000..1717b565f --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery/nls/fr/resource.js @@ -0,0 +1,3 @@ +define ({ + title: 'Fond de carte' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/BasemapGallery/nls/pt-br/resource.js b/viewer/js/gis/dijit/BasemapGallery/nls/pt-br/resource.js new file mode 100644 index 000000000..48f8546d9 --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery/nls/pt-br/resource.js @@ -0,0 +1,3 @@ +define ({ + title: 'Mapas base' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/BasemapGallery/nls/pt-pt/resource.js b/viewer/js/gis/dijit/BasemapGallery/nls/pt-pt/resource.js new file mode 100644 index 000000000..d67f99c97 --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery/nls/pt-pt/resource.js @@ -0,0 +1,3 @@ +define({ + title: 'Mapas base' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/BasemapGallery/nls/resource.js b/viewer/js/gis/dijit/BasemapGallery/nls/resource.js new file mode 100644 index 000000000..99c7c0fae --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery/nls/resource.js @@ -0,0 +1,9 @@ +define ({ + root: { + title: 'Basemaps' + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/BasemapGallery/templates/BasemapGallery.html b/viewer/js/gis/dijit/BasemapGallery/templates/BasemapGallery.html new file mode 100644 index 000000000..26ea418e5 --- /dev/null +++ b/viewer/js/gis/dijit/BasemapGallery/templates/BasemapGallery.html @@ -0,0 +1,7 @@ +
+
+
+
+
+
+
\ No newline at end of file diff --git a/viewer/js/gis/dijit/Basemaps.js b/viewer/js/gis/dijit/Basemaps.js index a0deaee34..72a9dbbce 100644 --- a/viewer/js/gis/dijit/Basemaps.js +++ b/viewer/js/gis/dijit/Basemaps.js @@ -7,7 +7,6 @@ define([ 'dojo/_base/lang', 'dojo/_base/array', 'dojo/topic', - 'dojox/lang/functional', 'dijit/DropDownMenu', 'dijit/MenuItem', @@ -29,7 +28,6 @@ define([ lang, array, topic, - functional, DropDownMenu, MenuItem, @@ -46,62 +44,93 @@ define([ widgetsInTemplate: true, i18n: i18n, title: i18n.title, - mode: 'agol', basemaps: {}, currentBasemap: null, mapStartBasemap: null, basemapsToShow: null, + galleryOptions: { + basemapIds: null, + basemaps: null, + basemapsGroup: null, + bingMapsKey: null, + portalUrl: null, + referenceIds: null, + showArcGISBasemaps: false + }, postCreate: function () { this.inherited(arguments); + this.initializeBasemaps(); + this.createBasemapGallery(); + topic.subscribe('basemaps/updateBasemap', lang.hitch(this, 'updateBasemap')); + }, - // if the basemaps to show is not explicitly set, - // get them from the basemap object - if (!this.basemapsToShow) { - this.basemapsToShow = Object.keys(this.basemaps); - } + initializeBasemaps: function () { + if (this.galleryOptions.basemaps) { + // if the basemaps to show is not explicitly set, get them from the gallery's basemap array + this.basemapsToShow = this.basemapsToShow || array.map(this.galleryOptions.basemaps, function (basemap) { + return basemap.id; + }); + } else { + // no basemaps? use the Esri basemaps + if (!this.basemaps || Object.keys(this.basemaps).length < 1) { + this.basemaps = lang.clone(esriBasemaps); + this.galleryOptions.showArcGISBasemaps = false; + } - // if the starting basemap is not explicitly set, - // get it from the map - if (!this.mapStartBasemap) { - this.mapStartBasemap = this.map.getBasemap(); + // if the basemaps to show is not explicitly set, get them from the basemap object + this.basemapsToShow = this.basemapsToShow || Object.keys(this.basemaps); + + var basemaps = []; + array.forEach(this.basemapsToShow, lang.hitch(this, function (key) { + var map = this.basemaps[key]; + // determine if it is a custom basemap or an esri basemap + if (map.basemap && map.basemap.declaredClass === 'esri.dijit.Basemap') { + var basemap = map.basemap; + basemap.title = map.title || basemap.title; + basemap.id = key; + basemaps.push(basemap); + } else { + if (!esriBasemaps[key]) { + map.basemap.title = map.title || map.basemap.title; + esriBasemaps[key] = map.basemap; + } + map.title = map.title || esriBasemaps[key].title; + this.galleryOptions.showArcGISBasemaps = false; + } + })); + this.galleryOptions.basemaps = basemaps; } - // check to make sure the starting basemap - // is found in the basemaps object - if (!this.basemaps.hasOwnProperty(this.mapStartBasemap)) { + // if the starting basemap is not explicitly set, get it from the map + this.mapStartBasemap = this.mapStartBasemap || this.map.getBasemap(); + + // check to make sure the starting basemap is found in the basemaps object + if (array.indexOf(this.basemapsToShow, this.mapStartBasemap) < 0) { this.mapStartBasemap = this.basemapsToShow[0]; } + }, - if (this.mode === 'custom') { - this.gallery = new BasemapGallery({ - map: this.map, - showArcGISBasemaps: false, - basemaps: functional.map(this.basemaps, function (map) { - return map.basemap; - }) - }); - this.gallery.startup(); + createBasemapGallery: function () { + var opts = lang.mixin({ + map: this.map + }, this.galleryOptions); + this.gallery = new BasemapGallery(opts); + this.gallery.startup(); + if (this.galleryOptions.showArcGISBasemaps) { + this.gallery.on('load', lang.hitch(this, 'buildMenu')); + } else { + this.buildMenu(); } + }, + + buildMenu: function () { this.menu = new DropDownMenu({ style: 'display: none;' }); - array.forEach(this.basemapsToShow, function (basemap) { if (this.basemaps.hasOwnProperty(basemap)) { - if (this.mode !== 'custom') { - // add any custom to the esri basemaps - var basemapObj = this.basemaps[basemap]; - if (basemapObj.basemap) { - if (!esriBasemaps[basemap]) { - if (!basemapObj.basemap.title) { - basemapObj.basemap.title = basemapObj.title || basemap; - } - esriBasemaps[basemap] = basemapObj.basemap; - } - } - } var menuItem = new MenuItem({ id: basemap, label: this.basemaps[basemap].title, @@ -111,22 +140,29 @@ define([ this.menu.addChild(menuItem); } }, this); - topic.subscribe('basemaps/updateBasemap', lang.hitch(this, 'updateBasemap')); this.dropDownButton.set('dropDown', this.menu); + this.setStartingBasemap(); + }, + + setStartingBasemap: function () { + if (this.mapStartBasemap && (this.gallery.get(this.mapStartBasemap) || esriBasemaps[this.mapStartBasemap])) { + this.updateBasemap(this.mapStartBasemap); + } }, updateBasemap: function (basemap) { if (basemap !== this.currentBasemap && (array.indexOf(this.basemapsToShow, basemap) !== -1)) { - if (!this.basemaps.hasOwnProperty(basemap)) { - return; - } - this.currentBasemap = basemap; - if (this.mode === 'custom') { + if (this.gallery.get(basemap)) { this.gallery.select(basemap); - } else { + } else if (esriBasemaps[basemap]) { + this.gallery._removeBasemapLayers(); + this.gallery._removeReferenceLayer(); this.map.setBasemap(basemap); + } else { + topic.publish('viewer/error', 'Invalid basemap selected.'); + return; } - + this.currentBasemap = basemap; var ch = this.menu.getChildren(); array.forEach(ch, function (c) { if (c.id === basemap) { @@ -136,15 +172,6 @@ define([ } }); } - }, - - startup: function () { - this.inherited(arguments); - if (this.mapStartBasemap) { - if (this.map.getBasemap() !== this.mapStartBasemap) { - this.updateBasemap(this.mapStartBasemap); - } - } } }); }); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Basemaps/nls/es/resource.js b/viewer/js/gis/dijit/Basemaps/nls/es/resource.js new file mode 100644 index 000000000..48f8546d9 --- /dev/null +++ b/viewer/js/gis/dijit/Basemaps/nls/es/resource.js @@ -0,0 +1,3 @@ +define ({ + title: 'Mapas base' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Basemaps/nls/fr/resource.js b/viewer/js/gis/dijit/Basemaps/nls/fr/resource.js new file mode 100644 index 000000000..1717b565f --- /dev/null +++ b/viewer/js/gis/dijit/Basemaps/nls/fr/resource.js @@ -0,0 +1,3 @@ +define ({ + title: 'Fond de carte' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Basemaps/nls/pt-br/resource.js b/viewer/js/gis/dijit/Basemaps/nls/pt-br/resource.js new file mode 100644 index 000000000..48f8546d9 --- /dev/null +++ b/viewer/js/gis/dijit/Basemaps/nls/pt-br/resource.js @@ -0,0 +1,3 @@ +define ({ + title: 'Mapas base' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Basemaps/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Basemaps/nls/pt-pt/resource.js new file mode 100644 index 000000000..d67f99c97 --- /dev/null +++ b/viewer/js/gis/dijit/Basemaps/nls/pt-pt/resource.js @@ -0,0 +1,3 @@ +define({ + title: 'Mapas base' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Basemaps/nls/resource.js b/viewer/js/gis/dijit/Basemaps/nls/resource.js index b7dace410..99c7c0fae 100644 --- a/viewer/js/gis/dijit/Basemaps/nls/resource.js +++ b/viewer/js/gis/dijit/Basemaps/nls/resource.js @@ -1,5 +1,9 @@ define ({ root: { title: 'Basemaps' - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Directions.js b/viewer/js/gis/dijit/Directions.js index 524c9cf37..88fcbac5d 100644 --- a/viewer/js/gis/dijit/Directions.js +++ b/viewer/js/gis/dijit/Directions.js @@ -12,8 +12,9 @@ define([ 'esri/geometry/Point', 'esri/SpatialReference', 'dojo/topic', - 'dojo/i18n!./Directions/nls/resource' -], function (declare, _WidgetBase, _TemplatedMixin, Directions, template, lang, Menu, MenuItem, PopupMenuItem, MenuSeparator, Point, SpatialReference, topic, i18n) { + 'dojo/i18n!./Directions/nls/resource', + 'dojo/dom-style' +], function (declare, _WidgetBase, _TemplatedMixin, Directions, template, lang, Menu, MenuItem, PopupMenuItem, MenuSeparator, Point, SpatialReference, topic, i18n, domStyle) { return declare([_WidgetBase, _TemplatedMixin], { templateString: template, @@ -27,12 +28,14 @@ define([ //temp fix for 3.12 and 3.13 map click button. if (this.directions._activateButton) { - this.directions._activateButton.style.display = 'none'; + domStyle.set(this.directions._activateButton, 'display', 'none'); } else if (this.directions._activateButtonNode) { - this.directions._activateButtonNode.style.display = 'none'; - this.directions._addDestinationNode.style.float = 'inherit'; - this.directions._optionsButtonNode.style.float = 'inherit'; - this.directions._optionsButtonNode.style.marginRight = '5px'; + domStyle.set(this.directions._activateButtonNode, 'display', 'none'); + domStyle.set(this.directions._addDestinationNode, 'float', 'inherit'); + domStyle.set(this.directions._optionsButtonNode, { + 'float': 'inherit', + marginRight: '5px' + }); } if (this.mapRightClickMenu) { @@ -130,4 +133,4 @@ define([ }); } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/Directions/nls/es/resource.js b/viewer/js/gis/dijit/Directions/nls/es/resource.js new file mode 100644 index 000000000..0f1f0d9e0 --- /dev/null +++ b/viewer/js/gis/dijit/Directions/nls/es/resource.js @@ -0,0 +1,23 @@ +define ({ + labels: { + startAtMyLocation: 'comenzará a mi ubicación', + endAtMyLocation: 'terminar en mi ubicación', + clearStops: 'paradas claras', + addStop: 'Añadir parada', + directionsToHere: 'Direcciones a aquí', + directionsFromHere: 'Direcciones de aquí', + useMyLocationAsStart: 'Usar mi ubicación como punto de inicio', + useMyLocationAsEnd: 'Usar mi ubicación como punto final', + directions: 'Direcciones' + }, + errors: { + geoLocation: { + title: 'Error', + message: 'No geolocalización apoyado por su hojeanr.' + }, + location: { + title: 'Error', + message: 'Hubo un problema con su ubicación: ' + } + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Directions/nls/fr/resource.js b/viewer/js/gis/dijit/Directions/nls/fr/resource.js new file mode 100644 index 000000000..2cc3dd12c --- /dev/null +++ b/viewer/js/gis/dijit/Directions/nls/fr/resource.js @@ -0,0 +1,23 @@ +define ({ + labels: { + startAtMyLocation: 'Commencer à mon endroit', + endAtMyLocation: 'Terminer à mon endroit', + clearStops: 'Supprimer les arrêts', + addStop: 'Ajouter un arrêt', + directionsToHere: 'Directions vers ici', + directionsFromHere: 'Directions à partir d\'ici', + useMyLocationAsStart: 'Utiliser ma position comme point de départ', + useMyLocationAsEnd: 'Utiliser ma position comme point final', + directions: 'Directions' + }, + errors: { + geoLocation: { + title: 'Erreur', + message: 'Géolocalisation non supporté par votre navigateur.' + }, + location: { + title: 'Erreur', + message: 'Il y a un problème pour obtenir votre position: ' + } + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Directions/nls/pt-br/resource.js b/viewer/js/gis/dijit/Directions/nls/pt-br/resource.js new file mode 100644 index 000000000..51bf9d23f --- /dev/null +++ b/viewer/js/gis/dijit/Directions/nls/pt-br/resource.js @@ -0,0 +1,23 @@ +define({ + labels: { + startAtMyLocation: 'Inicie na minha Localização', + endAtMyLocation: 'Termine na minha Localização', + clearStops: 'Limpar paradas', + addStop: 'Adicionar parada', + directionsToHere: 'Direções para aqui', + directionsFromHere: 'Direções daqui', + useMyLocationAsStart: 'Use minha Localização como ponto de início', + useMyLocationAsEnd: 'Use minha Localização como ponto final', + directions: 'Direções' + }, + errors: { + geoLocation: { + title: 'Erro', + message: 'GeoLocalização não suportada no seu navegador.' + }, + location: { + title: 'Erro', + message: 'Houve um problema ao buscar sua Localização: ' + } + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Directions/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Directions/nls/pt-pt/resource.js new file mode 100644 index 000000000..e1ccff40e --- /dev/null +++ b/viewer/js/gis/dijit/Directions/nls/pt-pt/resource.js @@ -0,0 +1,23 @@ +define({ + labels: { + startAtMyLocation: 'iniciar na minha localização', + endAtMyLocation: 'terminar na minha localização', + clearStops: 'limpar paragens', + addStop: 'Adicionar paragem', + directionsToHere: 'Direcções para aqui', + directionsFromHere: 'Direcções a partir daqui', + useMyLocationAsStart: 'Usar a minha localização como ponto inicial', + useMyLocationAsEnd: 'Usar a minha localização como ponto final', + directions: 'Direcções' + }, + errors: { + geoLocation: { + title: 'Erro', + message: 'GeoLocalização não suportada no seu navegador.' + }, + location: { + title: 'Erro', + message: 'Ocorreu um problema ao obter a sua localização: ' + } + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Directions/nls/resource.js b/viewer/js/gis/dijit/Directions/nls/resource.js index 125b2e678..b1db86f01 100644 --- a/viewer/js/gis/dijit/Directions/nls/resource.js +++ b/viewer/js/gis/dijit/Directions/nls/resource.js @@ -21,5 +21,9 @@ define ({ message: 'There was a problem getting your location: ' } } - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Draw/nls/es/resource.js b/viewer/js/gis/dijit/Draw/nls/es/resource.js new file mode 100644 index 000000000..32e5d4098 --- /dev/null +++ b/viewer/js/gis/dijit/Draw/nls/es/resource.js @@ -0,0 +1,14 @@ +define ({ + labels: { + point: 'Punto', + circle: 'Circulo', + polyline: 'Polilínea', + freehandPolyline: 'Polilínea a mano alzada', + polygon: 'Polígono', + freehandPolygon: 'Polígono a mano alzada', + stopDrawing: 'Comienzo dibujo', + clearDrawing: 'Despejar dibujo', + currentDrawMode: 'El modo de dibujo:', + currentDrawModeNone: 'Nada' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Draw/nls/fr/resource.js b/viewer/js/gis/dijit/Draw/nls/fr/resource.js new file mode 100644 index 000000000..93c4c0146 --- /dev/null +++ b/viewer/js/gis/dijit/Draw/nls/fr/resource.js @@ -0,0 +1,14 @@ +define ({ + labels: { + point: 'Point', + circle: 'Cercle', + polyline: 'Polyline', + freehandPolyline: 'Polyligne à main levée', + polygon: 'Polygone', + freehandPolygon: 'Polygone à main levée', + stopDrawing: 'Terminer le dessin', + clearDrawing: 'Effacer le dessin', + currentDrawMode: 'Mode de dessin:', + currentDrawModeNone: 'Aucun' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Draw/nls/pt-br/resource.js b/viewer/js/gis/dijit/Draw/nls/pt-br/resource.js new file mode 100644 index 000000000..4192728c6 --- /dev/null +++ b/viewer/js/gis/dijit/Draw/nls/pt-br/resource.js @@ -0,0 +1,14 @@ +define({ + labels: { + point: 'Ponto', + circle: 'Círculo', + polyline: 'Polilinha', + freehandPolyline: 'Polilinha a Mão Livre', + polygon: 'Polígono', + freehandPolygon: 'Polígono a Mão Livre', + stopDrawing: 'Parar de Desenhar', + clearDrawing: 'Limpar Desenhos', + currentDrawMode: 'Modo de desenho atual:', + currentDrawModeNone: 'Nenhum' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Draw/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Draw/nls/pt-pt/resource.js new file mode 100644 index 000000000..9dc6144bb --- /dev/null +++ b/viewer/js/gis/dijit/Draw/nls/pt-pt/resource.js @@ -0,0 +1,14 @@ +define({ + labels: { + point: 'Ponto', + circle: 'Círculo', + polyline: 'Polilinha', + freehandPolyline: 'Polilinha à mão livre', + polygon: 'Polígono', + freehandPolygon: 'Polígono à mão livre', + stopDrawing: 'Parar de desenhar', + clearDrawing: 'Limpar desenhos', + currentDrawMode: 'Modo de desenho actual:', + currentDrawModeNone: 'Nenhum' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Draw/nls/resource.js b/viewer/js/gis/dijit/Draw/nls/resource.js index 29c6ab534..b55a4fd3c 100644 --- a/viewer/js/gis/dijit/Draw/nls/resource.js +++ b/viewer/js/gis/dijit/Draw/nls/resource.js @@ -12,5 +12,9 @@ define ({ currentDrawMode: 'Current draw mode:', currentDrawModeNone: 'None' } - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Editor/nls/es/resource.js b/viewer/js/gis/dijit/Editor/nls/es/resource.js new file mode 100644 index 000000000..445721fa7 --- /dev/null +++ b/viewer/js/gis/dijit/Editor/nls/es/resource.js @@ -0,0 +1,6 @@ +define ({ + labels: { + startEditing: 'Comenzar a editar', + stopEditing: 'Detener la edición' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Editor/nls/fr/resource.js b/viewer/js/gis/dijit/Editor/nls/fr/resource.js new file mode 100644 index 000000000..237f02020 --- /dev/null +++ b/viewer/js/gis/dijit/Editor/nls/fr/resource.js @@ -0,0 +1,6 @@ +define ({ + labels: { + startEditing: 'Commencez à éditer', + stopEditing: 'Arrêter l\'édition' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Editor/nls/pt-br/resource.js b/viewer/js/gis/dijit/Editor/nls/pt-br/resource.js new file mode 100644 index 000000000..79974dfec --- /dev/null +++ b/viewer/js/gis/dijit/Editor/nls/pt-br/resource.js @@ -0,0 +1,6 @@ +define({ + labels: { + startEditing: 'Iniciar Edição', + stopEditing: 'Parar Edição' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Editor/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Editor/nls/pt-pt/resource.js new file mode 100644 index 000000000..5c499062e --- /dev/null +++ b/viewer/js/gis/dijit/Editor/nls/pt-pt/resource.js @@ -0,0 +1,6 @@ +define({ + labels: { + startEditing: 'Iniciar edição', + stopEditing: 'Parar edição' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Editor/nls/resource.js b/viewer/js/gis/dijit/Editor/nls/resource.js index e24f161fb..5320f6518 100644 --- a/viewer/js/gis/dijit/Editor/nls/resource.js +++ b/viewer/js/gis/dijit/Editor/nls/resource.js @@ -4,5 +4,9 @@ define ({ startEditing: 'Start editing', stopEditing: 'Stop editing' } - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Find.js b/viewer/js/gis/dijit/Find.js index 71d7d5242..e8bea99f7 100644 --- a/viewer/js/gis/dijit/Find.js +++ b/viewer/js/gis/dijit/Find.js @@ -29,6 +29,7 @@ define([ 'dijit/form/FilteringSelect', 'dijit/form/ValidationTextBox', 'dijit/form/CheckBox', + 'dijit/form/Button', 'xstyle/css!./Find/css/Find.css' ], function ( declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, domConstruct, lang, array, on, keys, domStyle, Memory, @@ -594,4 +595,4 @@ define([ } } ); -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/Find/nls/es/resource.js b/viewer/js/gis/dijit/Find/nls/es/resource.js new file mode 100644 index 000000000..0efc73504 --- /dev/null +++ b/viewer/js/gis/dijit/Find/nls/es/resource.js @@ -0,0 +1,25 @@ +define ({ + selectQuery: 'Consulta de selección', + searchText: { + label: 'Buscar', + placeholder: 'Introduzca el texto que desea buscar.' + }, + exactMatches: 'Buscar sólo coincidencias exactas', + searchButton: { + label: 'Buscar', + busyLabel: 'buscando' + }, + clearButton: { + label: 'Despejar' + }, + searching: 'Buscando...', + resultsLabel: { + multipleResultsSuffix: 's', + labelPrefix: 'Resultado', + labelSuffix: 'encontró' + }, + noResultsLabel: 'No se han encontrado resultados.', + optionsLabel: 'Opciones', + zoomOnSelect: 'Ampliar en seleccione', + zoomOnDeselect: 'Ampliar en no seleccione' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Find/nls/fr/resource.js b/viewer/js/gis/dijit/Find/nls/fr/resource.js new file mode 100644 index 000000000..63dfd8680 --- /dev/null +++ b/viewer/js/gis/dijit/Find/nls/fr/resource.js @@ -0,0 +1,25 @@ +define ({ + selectQuery: 'Sélection', + searchText: { + label: 'Rechercher', + placeholder: 'Entrez le texte que vous souhaitez rechercher.' + }, + exactMatches: 'Correspondances exactes seulement', + searchButton: { + label: 'Rechercher', + busyLabel: 'recherche' + }, + clearButton: { + label: 'Effacer' + }, + searching: 'Recherche...', + resultsLabel: { + multipleResultsSuffix: 's', + labelPrefix: 'Résultat', + labelSuffix: 'Trouvé' + }, + noResultsLabel: 'Aucun résultat', + optionsLabel: 'Options', + zoomOnSelect: 'Zoom sur la sélection', + zoomOnDeselect: 'Zoom en desélectionnant' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Find/nls/pt-br/resource.js b/viewer/js/gis/dijit/Find/nls/pt-br/resource.js new file mode 100644 index 000000000..fa350b994 --- /dev/null +++ b/viewer/js/gis/dijit/Find/nls/pt-br/resource.js @@ -0,0 +1,26 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + selectQuery: 'Selecionar Procura', + searchText: { + label: 'Procurar por', + placeholder: 'Digite o texto que você quer procurar.' + }, + exactMatches: 'Apenas buscas exatas', + searchButton: { + label: 'Procurar', + busyLabel: 'procurando' + }, + clearButton: { + label: 'Limpar' + }, + searching: 'Procurando...', + resultsLabel: { + multipleResultsSuffix: 's', + labelPrefix: 'Resultado', + labelSuffix: 'encontrado' + }, + noResultsLabel: 'Nenhum Resultado Encontrado.', + optionsLabel: 'Opções', + zoomOnSelect: 'Zoom para selecionar', + zoomOnDeselect: 'Zoom para deselecionar' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Find/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Find/nls/pt-pt/resource.js new file mode 100644 index 000000000..e38f69d21 --- /dev/null +++ b/viewer/js/gis/dijit/Find/nls/pt-pt/resource.js @@ -0,0 +1,26 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + selectQuery: 'Seleccionar consulta', + searchText: { + label: 'Procurar por', + placeholder: 'Introduza o texto que pretende procurar.' + }, + exactMatches: 'Apenas correspondências exactas', + searchButton: { + label: 'Procurar', + busyLabel: 'a procurar' + }, + clearButton: { + label: 'Limpar' + }, + searching: 'A procurar...', + resultsLabel: { + multipleResultsSuffix: 's', + labelPrefix: 'Resultado', + labelSuffix: 'encontrado' + }, + noResultsLabel: 'Nenhum resultado encontrado.', + optionsLabel: 'Opções', + zoomOnSelect: 'Aproximar ao seleccionar', + zoomOnDeselect: 'Aproximar ao desseleccionar' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Find/nls/resource.js b/viewer/js/gis/dijit/Find/nls/resource.js index 0d5c82f74..4e0a71f8d 100644 --- a/viewer/js/gis/dijit/Find/nls/resource.js +++ b/viewer/js/gis/dijit/Find/nls/resource.js @@ -1,4 +1,4 @@ -// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +// https://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html define({ root: { selectQuery: 'Select query', @@ -24,6 +24,10 @@ define({ optionsLabel: 'Options', zoomOnSelect: 'Zoom on select', zoomOnDeselect: 'Zoom on deselect' - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); diff --git a/viewer/js/gis/dijit/FloatingTitlePane.js b/viewer/js/gis/dijit/FloatingTitlePane.js index 81a6c4820..6c9e05c16 100644 --- a/viewer/js/gis/dijit/FloatingTitlePane.js +++ b/viewer/js/gis/dijit/FloatingTitlePane.js @@ -6,6 +6,7 @@ define([ 'dojo/dnd/Moveable', 'dojo/aspect', 'dojo/topic', + 'dojo/sniff', 'dojo/_base/window', 'dojo/window', 'dojo/dom-geometry', @@ -16,7 +17,7 @@ define([ 'dojox/layout/ResizeHandle', 'xstyle/css!dojox/layout/resources/ResizeHandle.css', 'xstyle/css!./FloatingTitlePane/css/FloatingTitlePane.css' -], function (declare, TitlePane, on, lang, Moveable, aspect, topic, win, winUtils, domGeom, domStyle, domConstruct, domAttr, domClass, ResizeHandle) { +], function (declare, TitlePane, on, lang, Moveable, aspect, topic, has, win, winUtils, domGeom, domStyle, domConstruct, domAttr, domClass, ResizeHandle) { return declare([TitlePane], { sidebarPosition: null, @@ -35,6 +36,11 @@ define([ this.createDomNodes(); this.own(on(window, 'resize', lang.hitch(this, '_endDrag'))); } + if (this.iconClass) { + this.iconNode = domConstruct.create('span', { + 'class': 'titlePaneIcon fa fa-fw ' + this.iconClass + }, this.titleNode, 'before'); + } this.own(topic.subscribe('titlePane/event', lang.hitch(this, '_updateWidgetSidebarPosition'))); this.own(aspect.after(this, 'toggle', lang.hitch(this, '_afterToggle'))); this.inherited(arguments); @@ -42,8 +48,16 @@ define([ startup: function () { if (this.titleBarNode && this.canFloat) { this._moveable = new Moveable(this.domNode, { - handle: this.titleBarNode + handle: this.titleBarNode, + delay: this.dragDelay }); + if (this.dragDelay > 0) { + this._moveable.mover.prototype.onMouseUp = function (e) { + this.destroy(); + e.preventDefault(); + e.stopPropagation(); + }; + } this._titleBarHeight = domStyle.get(this.titleBarNode, 'height'); aspect.after(this._moveable, 'onMove', lang.hitch(this, '_dragging'), true); aspect.after(this._moveable, 'onMoveStop', lang.hitch(this, '_endDrag'), true); @@ -100,12 +114,7 @@ define([ }, /* Methods for Dragging */ - _dragging: function (mover) { - // add our own delay since the movable delay - // property breaks in all versions of Internet Explorer - if (Math.abs(mover.marginBox.l - this._moverBox.l) <= this.dragDelay || Math.abs(mover.marginBox.t - this._moverBox.t) <= this.dragDelay) { - return; - } + _dragging: function () { this.isDragging = true; if (!this.titleCursor) { this.titleCursor = domStyle.get(this.titleBarNode, 'cursor'); diff --git a/viewer/js/gis/dijit/Geocoder/nls/es/resource.js b/viewer/js/gis/dijit/Geocoder/nls/es/resource.js new file mode 100644 index 000000000..edd0ec207 --- /dev/null +++ b/viewer/js/gis/dijit/Geocoder/nls/es/resource.js @@ -0,0 +1,14 @@ +define ({ + title: 'Alternar barra de búsqueda', + labels: { + address: 'Dirección', + neighborhood: 'Barrio', + city: 'Ciudad', + subregion: 'Subregión', + region: 'Región', + postalCode: 'Código postal', + countryCode: 'Código de país', + locatorName: 'Nombre del localizador', + getAddressHere: 'Obtener dirección aquí' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Geocoder/nls/fr/resource.js b/viewer/js/gis/dijit/Geocoder/nls/fr/resource.js new file mode 100644 index 000000000..8d4e233f8 --- /dev/null +++ b/viewer/js/gis/dijit/Geocoder/nls/fr/resource.js @@ -0,0 +1,14 @@ +define ({ + title: 'Activer/désactiver la barre de recherche', + labels: { + address: 'Adresse', + neighborhood: 'Quartier', + city: 'Ville', + subregion: 'Sous-région', + region: 'Région', + postalCode: 'Code postal', + countryCode: 'Code de pays', + locatorName: 'Nom du localisateur', + getAddressHere: 'Obtenir l\'adresse ici' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Geocoder/nls/pt-br/resource.js b/viewer/js/gis/dijit/Geocoder/nls/pt-br/resource.js new file mode 100644 index 000000000..8f79694a2 --- /dev/null +++ b/viewer/js/gis/dijit/Geocoder/nls/pt-br/resource.js @@ -0,0 +1,14 @@ +define ({ + title: 'Alternar barra de Pesquisa', + labels: { + address: 'Endereço', + neighborhood: 'Bairro', + city: 'Cidade', + subregion: 'Subregião', + region: 'Região', + postalCode: 'Código Postal (CEP)', + countryCode: 'Código do País', + locatorName: 'Nome do Localizador', + getAddressHere: 'Obtenha o Endereço daqui' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Geocoder/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Geocoder/nls/pt-pt/resource.js new file mode 100644 index 000000000..7667ddf08 --- /dev/null +++ b/viewer/js/gis/dijit/Geocoder/nls/pt-pt/resource.js @@ -0,0 +1,14 @@ +define({ + title: 'Alternar barra de pesquisa', + labels: { + address: 'Endereço', + neighborhood: 'Bairro', + city: 'Cidade', + subregion: 'Subregião', + region: 'Região', + postalCode: 'Código postal', + countryCode: 'Código do país', + locatorName: 'Nome do localizador', + getAddressHere: 'Obter o endereço daqui' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Geocoder/nls/resource.js b/viewer/js/gis/dijit/Geocoder/nls/resource.js index 5f02f504a..a93722458 100644 --- a/viewer/js/gis/dijit/Geocoder/nls/resource.js +++ b/viewer/js/gis/dijit/Geocoder/nls/resource.js @@ -12,5 +12,9 @@ define ({ locatorName: 'Locator name', getAddressHere: 'Get address here' } - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Help.js b/viewer/js/gis/dijit/Help.js index 2594baf0c..461a374d1 100644 --- a/viewer/js/gis/dijit/Help.js +++ b/viewer/js/gis/dijit/Help.js @@ -9,17 +9,18 @@ define([ 'dojo/_base/lang', 'dojo/aspect', 'dojo/text!./Help/templates/HelpDialog.html', + 'dojo/i18n!./Help/nls/resource', 'dijit/form/Button', 'dijit/layout/TabContainer', 'dijit/layout/ContentPane', 'xstyle/css!./Help/css/Help.css' -], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _FloatingWidgetMixin, domConstruct, on, lang, aspect, template) { +], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _FloatingWidgetMixin, domConstruct, on, lang, aspect, template, i18n) { return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _FloatingWidgetMixin], { widgetsInTemplate: true, templateString: template, - title: 'Help', - html: 'Help', + i18n: i18n, + html: 'link'.replace('link', i18n.link), domTarget: 'helpDijit', draggable: false, baseClass: 'helpDijit', diff --git a/viewer/js/gis/dijit/Help/nls/es/resource.js b/viewer/js/gis/dijit/Help/nls/es/resource.js new file mode 100644 index 000000000..6c4cea412 --- /dev/null +++ b/viewer/js/gis/dijit/Help/nls/es/resource.js @@ -0,0 +1,36 @@ +define ({ + link: 'Ayuda', + navigation: { + title: 'Navegación', + description: 'Mapa de navegación a través del ratón y el teclado:', + pan1: 'Arrastre a la sartén', + recenter: 'SHIFT + Clic para volver a centrar', + zoomIn1: 'SHIFT + Arrastre para ampliar la imagen', + zoomOut1: 'SHIFT + CTRL + arrastrar para alejarla', + zoomIn2: 'De desplazamiento del ratón hacia adelante para hacer un zoom', + zoomOut2: 'De desplazamiento del ratón hacia atrás para reducir', + pan2: 'Utilice las teclas de flecha para desplazarse', + zoomInLevel: '+ clave para hacer un zoom un nivel', + zoomOutLevel: '- clave para alejar un nivel', + zoomCenter: 'Doble click para Centro y hacer zoom in' + }, + search: { + title: 'Buscar', + description: 'Una variedad de búsquedas se pueden realizar en el buscador:', + address: 'Buscar por Dirección', + place: 'Búsqueda Territorial', + etc: 'Buscar por código postal, condado, etc.' + }, + tools: { + title: 'Herramientas', + description: 'Además de las capacidades de búsqueda, se proporcionan las siguientes herramientas:', + measure: { + title: 'Medición', + description: 'La herramienta de medida proporciona la capacidad para dibujar un punto, una línea o un polígono en el mapa y especificar la unidad de medida.' + }, + print: { + title: 'Impresión', + description: 'Este mapa se puede exportar a diversos formatos y diseños.' + } + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Help/nls/fr/resource.js b/viewer/js/gis/dijit/Help/nls/fr/resource.js new file mode 100644 index 000000000..6bda3e07f --- /dev/null +++ b/viewer/js/gis/dijit/Help/nls/fr/resource.js @@ -0,0 +1,36 @@ +define ({ + link: 'Aide', + navigation: { + title: 'Navigation', + description: 'Navigation dans la carte en utilisant la souris et le clavier:', + pan1: 'Clic + Glisser pour déplacer la carte', + recenter: 'SHIFT + Clic pour recentrer', + zoomIn1: 'SHIFT + Sélection pour agrandir', + zoomOut1: 'SHIFT + CTRL + glisser pour effectuer un zoom arrière', + zoomIn2: 'Roulette de la souris vers l\'avant pour agrandir', + zoomOut2: 'Roulette de la souris vers l\'avant pour rétrécir', + pan2: 'Utilisez les flèches pour vous déplacer', + zoomInLevel: '+ pour zoomer un niveau', + zoomOutLevel: '- pour rétrécir un niveau', + zoomCenter: 'Double cliquez pour centrer et agrandir' + }, + search: { + title: 'Recherche', + description: 'Une variété de recherches peuvent être effectuées dans la zone de recherche:', + address: 'Recherche par adresse', + place: 'Recherche par nom de lieu', + etc: 'Recherche par code postal, région, etc.' + }, + tools: { + title: 'Outils', + description: 'En plus des capacités de recherche, les outils suivants sont fournis:', + measure: { + title: 'Mesurer', + description: 'L\'outil de mesure fournit les capacités pour dessiner un point, une ligne ou un polygone sur la carte et indiquer l\'unité de mesure.' + }, + print: { + title: 'Impression', + description: 'Cette carte peut être exportée vers différents formats et mises en page.' + } + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Help/nls/pt-br/resource.js b/viewer/js/gis/dijit/Help/nls/pt-br/resource.js new file mode 100644 index 000000000..2793d85a8 --- /dev/null +++ b/viewer/js/gis/dijit/Help/nls/pt-br/resource.js @@ -0,0 +1,36 @@ +define({ + link: 'Ajuda', + navigation: { + title: 'Navegação', + description: 'Navegação no mapa usando o rato e o teclado', + pan1: 'Arrastar para mover', + recenter: 'SHIFT + Clique para centrar', + zoomIn1: 'SHIFT + Arrastar para aproximar à área', + zoomOut1: 'SHIFT + CTRL + Arrastar para afastar', + zoomIn2: 'Mover roda de deslocação do rato para a frente para aproximar', + zoomOut2: 'Mover roda de deslocação do rato para trás para afastar', + pan2: 'Teclas de seta para mover', + zoomInLevel: 'Tecla + para aproximar um nível', + zoomOutLevel: 'Tecla - para afastar um nível', + zoomCenter: 'Duplo clique para centrar e aproximar' + }, + search: { + title: 'Pesquisa', + description: 'A caixa de pesquisa suporta vários tipos de pesquisa:', + address: 'Pesquisar por endereço', + place: 'Pesquisar por nome do local', + etc: 'Pesquisar por código postal, região, etc.' + }, + tools: { + title: 'Ferramentas', + description: 'Além das funcionalidades de pesquisa, são incluídas as seguintes ferramentas:', + measure: { + title: 'Medir', + description: 'A ferramenta de medição permite desenhar um ponto, linha, ou polígono, no mapa e especificar a unidade de medida.' + }, + print: { + title: 'Imprimir', + description: 'Este mapa pode ser exportado para diferentes formatos e modelos.' + } + } +}); diff --git a/viewer/js/gis/dijit/Help/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Help/nls/pt-pt/resource.js new file mode 100644 index 000000000..2793d85a8 --- /dev/null +++ b/viewer/js/gis/dijit/Help/nls/pt-pt/resource.js @@ -0,0 +1,36 @@ +define({ + link: 'Ajuda', + navigation: { + title: 'Navegação', + description: 'Navegação no mapa usando o rato e o teclado', + pan1: 'Arrastar para mover', + recenter: 'SHIFT + Clique para centrar', + zoomIn1: 'SHIFT + Arrastar para aproximar à área', + zoomOut1: 'SHIFT + CTRL + Arrastar para afastar', + zoomIn2: 'Mover roda de deslocação do rato para a frente para aproximar', + zoomOut2: 'Mover roda de deslocação do rato para trás para afastar', + pan2: 'Teclas de seta para mover', + zoomInLevel: 'Tecla + para aproximar um nível', + zoomOutLevel: 'Tecla - para afastar um nível', + zoomCenter: 'Duplo clique para centrar e aproximar' + }, + search: { + title: 'Pesquisa', + description: 'A caixa de pesquisa suporta vários tipos de pesquisa:', + address: 'Pesquisar por endereço', + place: 'Pesquisar por nome do local', + etc: 'Pesquisar por código postal, região, etc.' + }, + tools: { + title: 'Ferramentas', + description: 'Além das funcionalidades de pesquisa, são incluídas as seguintes ferramentas:', + measure: { + title: 'Medir', + description: 'A ferramenta de medição permite desenhar um ponto, linha, ou polígono, no mapa e especificar a unidade de medida.' + }, + print: { + title: 'Imprimir', + description: 'Este mapa pode ser exportado para diferentes formatos e modelos.' + } + } +}); diff --git a/viewer/js/gis/dijit/Help/nls/resource.js b/viewer/js/gis/dijit/Help/nls/resource.js new file mode 100644 index 000000000..137c491ff --- /dev/null +++ b/viewer/js/gis/dijit/Help/nls/resource.js @@ -0,0 +1,42 @@ +define({ + root: { + link: 'Help', + navigation: { + title: 'Navigation', + description: 'Map navigation using mouse and keyboard:', + pan1: 'Drag to pan', + recenter: 'SHIFT + Click to recenter', + zoomIn1: 'SHIFT + Drag to zoom in', + zoomOut1: 'SHIFT + CTRL + Drag to zoom out', + zoomIn2: 'Mouse Scroll Forward to zoom in', + zoomOut2: 'Mouse Scroll Backward to zoom out', + pan2: 'Use Arrow keys to pan', + zoomInLevel: '+ key to zoom in a level', + zoomOutLevel: '- key to zoom out a level', + zoomCenter: 'Double Click to Center and Zoom in' + }, + search: { + title: 'Search', + description: 'A variety of searches can be performed in the search box:', + address: 'Search by Address', + place: 'Search by Place Name', + etc: 'Search By Zip Code, County, etc.' + }, + tools: { + title: 'Tools', + description: 'In addition to Search capabilities, the following tools are provided:', + measure: { + title: 'Measure', + description: 'The measure tool provides the capabilities to draw a point, line, or polygon on the map and specify the unit of measurement.' + }, + print: { + title: 'Print', + description: 'This map can be exported to various formats and layouts.' + } + } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true +}); diff --git a/viewer/js/gis/dijit/Help/templates/HelpDialog.html b/viewer/js/gis/dijit/Help/templates/HelpDialog.html index 82a31e5b9..da701f42f 100644 --- a/viewer/js/gis/dijit/Help/templates/HelpDialog.html +++ b/viewer/js/gis/dijit/Help/templates/HelpDialog.html @@ -2,42 +2,42 @@
-
+
@@ -45,35 +45,34 @@
- Map navigation using mouse and keyboard: + ${i18n.navigation.description}
  • - Drag to pan + ${i18n.navigation.pan1}
  • - SHIFT + Click to recenter + ${i18n.navigation.recenter}
  • - SHIFT + Drag to zoom in + ${i18n.navigation.zoomIn1}
  • - SHIFT + CTRL + Drag to zoom out + ${i18n.navigation.zoomOut1}
  • - Mouse Scroll Forward to zoom in + ${i18n.navigation.zoomIn2}
  • - Mouse Scroll Backward to zoom out + ${i18n.navigation.zoomOut2}
  • - Use Arrow keys to pan + ${i18n.navigation.pan2}
  • - + key to zoom in a level + ${i18n.navigation.zoomInLevel}
  • - - key to zoom out a level + ${i18n.navigation.zoomOutLevel}
  • - Double Click to Center and Zoom in + ${i18n.navigation.zoomCenter}
-
- A variety of searches can be performed in the search box: +
+ ${i18n.search.description}
  • - Search by Address + ${i18n.search.address}
  • - Search by Place Name + ${i18n.search.place}
  • - Search By Zip Code, County, etc. + ${i18n.search.etc}
-
- In addition to Search capabilities, the following tools are provided: +
+ ${i18n.tools.description}
  • - Measure + ${i18n.tools.measure.title}
  • - The measure tool provides the capabilities to draw a point, line, or polygon - on the map and specify the unit of measurement. + ${i18n.tools.measure.description}
  • - Print + ${i18n.tools.print.title}
  • - This map can be exported to varouis formats and layouts + ${i18n.tools.print.description}
diff --git a/viewer/js/gis/dijit/Identify.js b/viewer/js/gis/dijit/Identify.js index dc91afac5..ccae23bc5 100644 --- a/viewer/js/gis/dijit/Identify.js +++ b/viewer/js/gis/dijit/Identify.js @@ -16,13 +16,17 @@ define([ 'esri/tasks/IdentifyTask', 'esri/tasks/IdentifyParameters', 'esri/dijit/PopupTemplate', + 'esri/layers/FeatureLayer', + 'esri/TimeExtent', + 'dojo/Deferred', 'dojo/text!./Identify/templates/Identify.html', 'dojo/i18n!./Identify/nls/resource', + './Identify/Formatters', 'dijit/form/Form', 'dijit/form/FilteringSelect', 'xstyle/css!./Identify/css/Identify.css' -], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, MenuItem, lang, array, all, topic, query, domStyle, domClass, Moveable, Memory, IdentifyTask, IdentifyParameters, PopupTemplate, IdentifyTemplate, i18n) { +], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, MenuItem, lang, array, all, topic, query, domStyle, domClass, Moveable, Memory, IdentifyTask, IdentifyParameters, PopupTemplate, FeatureLayer, TimeExtent, Deferred, IdentifyTemplate, i18n, Formatters) { return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { widgetsInTemplate: true, @@ -33,6 +37,7 @@ define([ mapClickMode: null, identifies: {}, infoTemplates: {}, + featureLayers: {}, ignoreOtherGraphics: true, createDefaultInfoTemplates: true, draggable: false, @@ -44,6 +49,19 @@ define([ 'shape_len', 'shape.stlength()', 'shape.area', 'shape_area', 'shape.starea()' ], + /** + * field type mappings to their default formatter functions + * overriding this object will globally replace the default + * formatter function for the field type + * @type {Object} + */ + defaultFormatters: { + 'esriFieldTypeSmallInteger': Formatters.formatInt, + 'esriFieldTypeInteger': Formatters.formatInt, + 'esriFieldTypeSingle': Formatters.formatFloat, + 'esriFieldTypeDouble': Formatters.formatFloat, + 'esriFieldTypeDate': Formatters.formatDate + }, postCreate: function () { this.inherited(arguments); @@ -51,56 +69,10 @@ define([ this.identifies = {}; } this.layers = []; - array.forEach(this.layerInfos, function (layerInfo) { - var lyrId = layerInfo.layer.id; - var layer = this.map.getLayer(lyrId); - if (layer) { - var url = layer.url; - - // handle feature layers - if (layer.declaredClass === 'esri.layers.FeatureLayer') { - - // If is a feature layer that does not support - // Identify (Feature Service), create an - // infoTemplate for the graphic features. Create - // it only if one does not already exist. - if (layer.capabilities && array.indexOf(layer.capabilities.toLowerCase(), 'data') < 0) { - if (!layer.infoTemplate) { - var infoTemplate = this.getInfoTemplate(layer, layer.layerId); - if (infoTemplate) { - layer.setInfoTemplate(infoTemplate); - return; - } - } - } - - // If it is a feature Layer, we get the base url - // for the map service by removing the layerId. - var lastSL = url.lastIndexOf('/' + layer.layerId); - if (lastSL > 0) { - url = url.substring(0, lastSL); - } - } - - this.layers.push({ - ref: layer, - layerInfo: layerInfo, - identifyTask: new IdentifyTask(url) - }); - - // rebuild the layer selection list when any layer is hidden - // but only if we have a UI - if (this.parentWidget) { - layer.on('visibility-change', lang.hitch(this, function (evt) { - if (evt.visible === false) { - this.createIdentifyLayerList(); - } - })); - } - } - }, this); + this.addLayerInfos(this.layerInfos); this.own(topic.subscribe('mapClickMode/currentSet', lang.hitch(this, 'setMapClickMode'))); + this.own(topic.subscribe('identify/addLayerInfos', lang.hitch(this, 'addLayerInfos'))); this.map.on('click', lang.hitch(this, function (evt) { if (this.mapClickMode === 'identify') { @@ -124,6 +96,80 @@ define([ this.setupDraggable(); } }, + /** + * handles an array of layerInfos to call addLayerInfo for each layerInfo + * @param {Array} layerInfos The array of layer infos + * @returns {undefined} + */ + addLayerInfos: function (layerInfos) { + array.forEach(layerInfos, lang.hitch(this, 'addLayerInfo')); + }, + /** + * Initializes an infoTemplate on a layerInfo.layer object if it doesn't + * exist already. + * @param {object} layerInfo A cmv layerInfo object that contains a layer property + * @return {undefined} + */ + addLayerInfo: function (layerInfo) { + var lyrId = layerInfo.layer.id, layer = this.map.getLayer(lyrId), infoTemplate; + if (layer) { + var url = layer.url; + + // handle feature layers + if (layer.declaredClass === 'esri.layers.FeatureLayer') { + + // If is a feature layer that does not support + // Identify (Feature Service), create an + // infoTemplate for the graphic features. Create + // it only if one does not already exist. + if (layer.capabilities && array.indexOf(layer.capabilities.toLowerCase(), 'data') < 0) { + if (!layer.infoTemplate) { + infoTemplate = this.getInfoTemplate(layer, layer.layerId); + if (infoTemplate) { + layer.setInfoTemplate(infoTemplate); + var fieldInfos = infoTemplate.info.fieldInfos; + var formatters = array.filter(fieldInfos, function (info) { + return (info.formatter); + }); + if (formatters.length > 0) { + layer.on('graphic-draw', lang.hitch(this, 'getFormattedFeature', layer.infoTemplate)); + } + } + } + } + + // If it is a feature Layer, we get the base url + // for the map service by removing the layerId. + var lastSL = url.lastIndexOf('/' + layer.layerId); + if (lastSL > 0) { + url = url.substring(0, lastSL); + } + } else if (layer.layerInfos) { + array.forEach(layer.layerInfos, lang.hitch(this, function (subLayerInfo) { + var subLayerId = subLayerInfo.id; + if ((layerInfo.layerIds === null) || (array.indexOf(layerInfo.layerIds, subLayerId) >= 0)) { + this.getFeatureLayerForDynamicSublayer(layer, subLayerId); + } + })); + } + + this.layers.push({ + ref: layer, + layerInfo: layerInfo, + identifyTask: new IdentifyTask(url) + }); + + // rebuild the layer selection list when any layer is hidden + // but only if we have a UI + if (this.parentWidget) { + layer.on('visibility-change', lang.hitch(this, function (evt) { + if (evt.visible === false) { + this.createIdentifyLayerList(); + } + })); + } + } + }, addRightClickMenu: function () { this.map.on('MouseDown', lang.hitch(this, function (evt) { this.mapRightClick = evt; @@ -157,8 +203,27 @@ define([ } }, executeIdentifyTask: function (evt) { + + + var mapPoint = evt.mapPoint; + var identifyParams = this.createIdentifyParams(mapPoint); + var identifies = []; + var identifiedlayers = []; + var selectedLayer = this.getSelectedLayer(); + + if (!this.checkForGraphicInfoTemplate(evt)) { - return; + // return; + var layer = array.filter(this.layers, function (l) { + return l.ref.id === evt.graphic._layer.id; + })[0]; + if (!layer) { + return; + } + identifiedlayers.push(layer); + var d = new Deferred(); + identifies.push(d.promise); + d.resolve([{feature: evt.graphic}]); } this.map.infoWindow.hide(); @@ -169,20 +234,18 @@ define([ return; } - var mapPoint = evt.mapPoint; - var identifyParams = this.createIdentifyParams(mapPoint); - var identifies = []; - var identifiedlayers = []; - var selectedLayer = this.getSelectedLayer(); - array.forEach(this.layers, lang.hitch(this, function (layer) { - var layerIds = this.getLayerIds(layer, selectedLayer); + array.forEach(this.layers, lang.hitch(this, function (lyr) { + var layerIds = this.getLayerIds(lyr, selectedLayer); if (layerIds.length > 0) { var params = lang.clone(identifyParams); - params.layerDefinitions = layer.ref.layerDefinitions; + params.layerDefinitions = lyr.ref.layerDefinitions; params.layerIds = layerIds; - identifies.push(layer.identifyTask.execute(params)); - identifiedlayers.push(layer); + if (lyr.ref.timeInfo && lyr.ref.timeInfo.timeExtent && this.map.timeExtent) { + params.timeExtent = new TimeExtent(this.map.timeExtent.startTime, this.map.timeExtent.endTime); + } + identifies.push(lyr.identifyTask.execute(params)); + identifiedlayers.push(lyr); } })); @@ -293,16 +356,33 @@ define([ if (result.feature.infoTemplate === undefined) { var infoTemplate = this.getInfoTemplate(ref, null, result); if (infoTemplate) { + if (result.layerId && ref.layerInfos && infoTemplate.info.showAttachments) { + result.feature._layer = this.getFeatureLayerForDynamicSublayer(ref, result.layerId); + } result.feature.setInfoTemplate(infoTemplate); } else { return; } } - fSet.push(result.feature); + var feature = this.getFormattedFeature(result.feature.infoTemplate, result.feature); + fSet.push(feature); }, this); }, this); this.map.infoWindow.setFeatures(fSet); }, + getFormattedFeature: function (infoTemplate, feature) { + if (feature.graphic) { + feature = feature.graphic; + } + if (feature && infoTemplate && infoTemplate.info) { + array.forEach(infoTemplate.info.fieldInfos, function (info) { + if (typeof info.formatter === 'function') { + feature.attributes[info.fieldName] = info.formatter(feature.attributes[info.fieldName], feature.attributes, lang.clone(feature.geometry)); + } + }); + } + return feature; + }, identifyError: function (err) { this.map.infoWindow.hide(); topic.publish('viewer/handleError', { @@ -315,43 +395,41 @@ define([ }, getInfoTemplate: function (layer, layerId, result) { - var popup = null, - content = null; + var popup, config; if (result) { - layerId = result.layerId; + layerId = result.layerId || layer.layerId; } else if (layerId === null) { layerId = layer.layerId; } - // see if we have a Popup config defined for this layer - if (this.identifies.hasOwnProperty(layer.id)) { - if (this.identifies[layer.id].hasOwnProperty(layerId)) { - popup = this.identifies[layer.id][layerId]; - if (popup) { - if (typeof (popup.declaredClass) !== 'string') { // has it been created already? - if (popup.content) { - content = popup.content; - } - popup = new PopupTemplate(popup); - if (content) { - popup.setContent(content); - } - this.identifies[layer.id][layerId] = popup; - } + var ids = this.identifies; + if (ids.hasOwnProperty(layer.id)) { + if (ids[layer.id].hasOwnProperty(layerId)) { + popup = ids[layer.id][layerId]; + if (popup instanceof PopupTemplate) { + return popup; } } + } else { + ids[layer.id] = {}; } - // if no Popup config found, create one with all attributes or layer fields - if (!popup) { - popup = this.createDefaultInfoTemplate(layer, layerId, result); + // by mixin in the users config with the default props we can + // generate a config object that provides the basics automatically + // while letting the user override only the parts they want...like mediaInfos + config = lang.mixin(this.createDefaultInfoTemplate(layer, layerId, result), ids[layer.id][layerId] || {}); + + popup = ids[layer.id][layerId] = new PopupTemplate(config); + if (config.content) { + popup.setContent(config.content); } - return popup; + return ids[layer.id][layerId]; }, createDefaultInfoTemplate: function (layer, layerId, result) { - var popup = null, fieldInfos = []; + var popup = null, + fieldInfos = []; var layerName = this.getLayerName(layer); if (result) { @@ -366,14 +444,14 @@ define([ if (attributes.hasOwnProperty(prop)) { this.addDefaultFieldInfo(fieldInfos, { fieldName: prop, - label: prop.replace(/_/g, ' '), + label: this.makeSentenceCase(prop), visible: true }); } } } - // from the outFields of the layer + // from the outFields of the layer } else if (layer._outFields && (layer._outFields.length) && (layer._outFields[0] !== '*')) { var fields = layer.fields; @@ -390,32 +468,44 @@ define([ } })); - // from the fields layer + // from the fields layer } else if (layer.fields) { array.forEach(layer.fields, lang.hitch(this, function (field) { this.addDefaultFieldInfo(fieldInfos, { fieldName: field.name, - label: field.alias, + label: field.alias === field.name ? this.makeSentenceCase(field.name) : field.alias, visible: true }); })); } if (fieldInfos.length > 0) { - popup = new PopupTemplate({ + popup = { title: layerName, fieldInfos: fieldInfos, showAttachments: (layer.hasAttachments) - }); - if (!this.identifies[layer.id]) { - this.identifies[layer.id] = {}; - } - this.identifies[layer.id][layerId] = popup; + }; } return popup; }, + /** + * converts a string to a nice sentence case format + * @url http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript + * @param {string} str The string to convert + * @return {string} The converted string + */ + makeSentenceCase: function (str) { + if (!str.length) { + return ''; + } + str = str.toLowerCase().replace(/_/g, ' ').split(' '); + for (var i = 0; i < str.length; i++) { + str[i] = str[i].charAt(0).toUpperCase() + (str[i].substr(1).length ? str[i].substr(1) : ''); + } + return (str.length ? str.join(' ') : str); + }, addDefaultFieldInfo: function (fieldInfos, field) { var nameLC = field.fieldName.toLowerCase(); @@ -489,8 +579,10 @@ define([ if (layerInfo.subLayerIds !== null) { return false; } - // only include sublayers that are currently visible - if (array.indexOf(ref.visibleLayers, layerInfo.id) < 0) { + + if (this.isDefaultLayerVisibility(ref) && !this.checkVisibilityRecursive(ref, layerInfo.id)) { + return false; + } else if (array.indexOf(ref.visibleLayers, layerInfo.id) < 0) { return false; } // only include sublayers that are within the current map scale @@ -518,6 +610,44 @@ define([ return true; }, + /** + * recursively check all a layer's parent(s) layers for visibility + * this only needs to be done if the layers visibleLayers array is + * set to the default visibleLayers. After setVisibleLayers + * is called the first time group layers are NOT included. + * @param {esri/layers/DynamicMapServiceLayer} layer The layer reference + * @param {Integer} id The sublayer id to check for visibility + * @return {Boolean} Whether or not the sublayer is visible based on its parent(s) visibility + */ + checkVisibilityRecursive: function (layer, id) { + var layerInfos = array.filter(layer.layerInfos, function (layerInfo) { + return (layerInfo.id === id); + }); + if (layerInfos.length > 0) { + var info = layerInfos[0]; + if (layer.visibleLayers.indexOf(id) !== -1 && + (info.parentLayerId === -1 || this.checkVisibilityRecursive(layer, info.parentLayerId))) { + return true; + } + } + return false; + }, + /** + * check each defaultVisibility and if its not in the visibleLayers + * array, then the layer has non-default layer visibility + * @param {esri/layers/DynamicMapServiceLayer} layer The layer reference + * @return {Boolean} Whether or not we're operating with the default visibleLayers array or not + */ + isDefaultLayerVisibility: function (layer) { + for (var i = 0; i < layer.layerInfos.length; i++) { + var item = layer.layerInfos[i]; + if (item.defaultVisibility && layer.visibleLayers.indexOf(item.id) === -1) { + return false; + } + } + return true; + }, + getLayerName: function (layer) { var name = null; if (layer.layerInfo) { @@ -540,6 +670,17 @@ define([ return name; }, + getFeatureLayerForDynamicSublayer: function (layer, layerId) { + if (!layer.layerInfos) { + return false; + } + var key = layer.url + '/' + layerId; + if (!this.featureLayers.hasOwnProperty(key)) { + this.featureLayers[key] = new FeatureLayer(key); + } + return this.featureLayers[key]; + }, + layerVisibleAtCurrentScale: function (layer) { var mapScale = this.map.getScale(); return !(((layer.maxScale !== 0 && mapScale < layer.maxScale) || (layer.minScale !== 0 && mapScale > layer.minScale))); @@ -567,4 +708,4 @@ define([ }, this); } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/Identify/Formatters.js b/viewer/js/gis/dijit/Identify/Formatters.js new file mode 100644 index 000000000..b7c259e9d --- /dev/null +++ b/viewer/js/gis/dijit/Identify/Formatters.js @@ -0,0 +1,21 @@ +define([ + 'dojo/number', + 'dojo/date/locale' +], function (number, locale) { + return { + formatInt: function (value) { + return number.format(value); + }, + formatFloat: function (value) { + return number.format(value, { + places: 3 + }); + }, + formatDate: function (value) { + var date = new Date(value); + return locale.format(date, { + formatLength: 'short' + }); + } + }; +}); diff --git a/viewer/js/gis/dijit/Identify/nls/es/resource.js b/viewer/js/gis/dijit/Identify/nls/es/resource.js new file mode 100644 index 000000000..fda941704 --- /dev/null +++ b/viewer/js/gis/dijit/Identify/nls/es/resource.js @@ -0,0 +1,12 @@ +define ({ + labels: { + selectLayer: 'Seleccione la opción "Todas las capas visibles" o una sola capa para identificar:', + allVisibleLayers: '*** Todas las capas visibles ***' + }, + rightClickMenuItem: { + label: 'Identificar aquí' + }, + mapInfoWindow: { + identifyingTitle: 'Identificando...' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Identify/nls/fr/resource.js b/viewer/js/gis/dijit/Identify/nls/fr/resource.js new file mode 100644 index 000000000..013c39962 --- /dev/null +++ b/viewer/js/gis/dijit/Identify/nls/fr/resource.js @@ -0,0 +1,12 @@ +define ({ + labels: { + selectLayer: 'Choisissez "Tous les calques visibles" ou une seule couche pour identifier:', + allVisibleLayers: '*** Tous les calques visibles ***' + }, + rightClickMenuItem: { + label: 'Identifier ici' + }, + mapInfoWindow: { + identifyingTitle: 'Identification...' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Identify/nls/pt-br/resource.js b/viewer/js/gis/dijit/Identify/nls/pt-br/resource.js new file mode 100644 index 000000000..1a4d3327b --- /dev/null +++ b/viewer/js/gis/dijit/Identify/nls/pt-br/resource.js @@ -0,0 +1,12 @@ +define({ + labels: { + selectLayer: 'Escolha "Todas Camadas Visíveis" ou uma camada única para Identificar:', + allVisibleLayers: '*** Todas Camadas Visíveis ***' + }, + rightClickMenuItem: { + label: 'Identifique aqui' + }, + mapInfoWindow: { + identifyingTitle: 'Identificando...' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Identify/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Identify/nls/pt-pt/resource.js new file mode 100644 index 000000000..ab2998ff7 --- /dev/null +++ b/viewer/js/gis/dijit/Identify/nls/pt-pt/resource.js @@ -0,0 +1,12 @@ +define({ + labels: { + selectLayer: 'Escolher "Todas as camadas visíveis" ou uma única camada para identificar:', + allVisibleLayers: '*** Todas as camadas visíveis ***' + }, + rightClickMenuItem: { + label: 'Identificar aqui' + }, + mapInfoWindow: { + identifyingTitle: 'A identificar...' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Identify/nls/resource.js b/viewer/js/gis/dijit/Identify/nls/resource.js index 54093328c..f46111906 100644 --- a/viewer/js/gis/dijit/Identify/nls/resource.js +++ b/viewer/js/gis/dijit/Identify/nls/resource.js @@ -10,5 +10,9 @@ define ({ mapInfoWindow: { identifyingTitle: 'Identifying...' } - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); \ No newline at end of file diff --git a/viewer/js/gis/dijit/LayerControl.js b/viewer/js/gis/dijit/LayerControl.js index a47cb8c5b..4a8f14098 100644 --- a/viewer/js/gis/dijit/LayerControl.js +++ b/viewer/js/gis/dijit/LayerControl.js @@ -33,8 +33,8 @@ define([ map: null, layerInfos: [], icons: { - expand: 'fa-plus-square-o', - collapse: 'fa-minus-square-o', + expand: 'fa-caret-right', + collapse: 'fa-caret-down', checked: 'fa-check-square-o', unchecked: 'fa-square-o', update: 'fa-refresh', @@ -51,6 +51,7 @@ define([ noLegend: null, noZoom: null, noTransparency: null, + menu: {}, subLayerMenu: {}, swipe: null, swiperButtonStyle: 'position:absolute;top:20px;left:120px;z-index:50;', @@ -85,6 +86,8 @@ define([ }); return; } + // add any user-defined controls - possibly for user-defined layers + this._controls = lang.mixin(this._controls, options.controls || {}); }, postCreate: function () { this.inherited(arguments); @@ -204,7 +207,7 @@ define([ if (layer.loaded) { this._applyLayerControlOptions(layerInfo.controlOptions, layer); } else { - layer.on('load', lang.hitch(this, '_applyLayerControlOptions', layer.controlOptions)); + layer.on('load', lang.hitch(this, '_applyLayerControlOptions', layerInfo.controlOptions)); } } var layerControl = new Control({ @@ -218,7 +221,8 @@ define([ swipe: null, expanded: false, sublayers: true, - menu: this.subLayerMenu[layerInfo.type] + menu: this.menu[layerInfo.type], + subLayerMenu: this.subLayerMenu[layerInfo.type] }, layerInfo.controlOptions) }); layerControl.startup(); @@ -344,29 +348,37 @@ define([ if (this.separated) { if (this.vectorReorder) { array.forEach(this._vectorContainer.getChildren(), function (child) { - if (!child.getPreviousSibling()) { - child._reorderUp.set('disabled', true); - } else { - child._reorderUp.set('disabled', false); + if (child._reorderUp) { + if (!child.getPreviousSibling()) { + child._reorderUp.set('disabled', true); + } else { + child._reorderUp.set('disabled', false); + } } - if (!child.getNextSibling()) { - child._reorderDown.set('disabled', true); - } else { - child._reorderDown.set('disabled', false); + if (child._reorderDown) { + if (!child.getNextSibling()) { + child._reorderDown.set('disabled', true); + } else { + child._reorderDown.set('disabled', false); + } } }, this); } if (this.overlayReorder) { array.forEach(this._overlayContainer.getChildren(), function (child) { - if (!child.getPreviousSibling()) { - child._reorderUp.set('disabled', true); - } else { - child._reorderUp.set('disabled', false); + if (child._reorderUp) { + if (!child.getPreviousSibling()) { + child._reorderUp.set('disabled', true); + } else { + child._reorderUp.set('disabled', false); + } } - if (!child.getNextSibling()) { - child._reorderDown.set('disabled', true); - } else { - child._reorderDown.set('disabled', false); + if (child._reorderDown) { + if (!child.getNextSibling()) { + child._reorderDown.set('disabled', true); + } else { + child._reorderDown.set('disabled', false); + } } }, this); } @@ -477,4 +489,4 @@ define([ } }); return LayerControl; -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/LayerControl/controls/Dynamic.js b/viewer/js/gis/dijit/LayerControl/controls/Dynamic.js index bc6e8c453..c4ce2a847 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/Dynamic.js +++ b/viewer/js/gis/dijit/LayerControl/controls/Dynamic.js @@ -88,6 +88,13 @@ define([ menu.addChild(new MenuSeparator()); } }, + _initCustomMenu: function () { + // add custom sublayer menu items if we only have one sublayer + if (!this._hasSublayers) { + array.forEach(this.controlOptions.subLayerMenu, lang.hitch(this, '_addCustomMenuItem', this.layerMenu)); + this.layerMenu.addChild(new MenuSeparator()); + } + }, // toggle all sublayers on/off _toggleAllSublayers: function (state) { array.forEach(this._sublayerControls, function (control) { @@ -222,4 +229,4 @@ define([ } }); return DynamicControl; -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/LayerControl/controls/Feature.js b/viewer/js/gis/dijit/LayerControl/controls/Feature.js index 4ac2061a5..8706f5e29 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/Feature.js +++ b/viewer/js/gis/dijit/LayerControl/controls/Feature.js @@ -28,4 +28,4 @@ define([ } }); return FeatureControl; -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/LayerControl/controls/_Control.js b/viewer/js/gis/dijit/LayerControl/controls/_Control.js index e9086017a..58e7f6179 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/_Control.js +++ b/viewer/js/gis/dijit/LayerControl/controls/_Control.js @@ -10,6 +10,7 @@ define([ 'dojo/dom-attr', 'dojo/fx', 'dojo/html', + 'dijit/MenuItem', './../plugins/LayerMenu', 'dojo/text!./templates/Control.html' ], function ( @@ -24,6 +25,7 @@ define([ domAttr, fx, html, + MenuItem, LayerMenu, template ) { @@ -91,6 +93,7 @@ define([ leftClickToOpen: true }); this.layerMenu.startup(); + this._initCustomMenu(); } else { domClass.remove(this.menuNode, 'fa, layerControlMenuIcon, ' + this.icons.menu); domStyle.set(this.menuClickNode, 'cursor', 'default'); @@ -117,6 +120,21 @@ define([ layer.on('visibility-change', lang.hitch(this, '_visibilityChange')) ); }, + _initCustomMenu: function () { + array.forEach(this.controlOptions.menu, lang.hitch(this, '_addCustomMenuItem', this.layerMenu)); + }, + _addCustomMenuItem: function (menu, menuItem) { + //create the menu item + var item = new MenuItem(menuItem); + item.set('onClick', lang.hitch(this, function () { + topic.publish('layerControl/' + menuItem.topic, { + layer: this.layer, + iconNode: this.iconNode, + menuItem: item + }); + })); + menu.addChild(item); + }, // add on event to expandClickNode _expandClick: function () { this._expandClickHandler = on(this.expandClickNode, 'click', lang.hitch(this, '_expandClicked')); @@ -147,7 +165,13 @@ define([ domConst.destroy(this.expandNode); }, // set layer visibility and update icon - _setLayerVisibility: function (layer, checkNode) { + _setLayerVisibility: function (layer, checkNode, event) { + + // prevent click event from bubbling + if (event.stopPropagation) { + event.stopPropagation(); + } + if (layer.visible) { this._setLayerCheckbox(layer, checkNode); layer.hide(); @@ -253,4 +277,4 @@ define([ } }); return _Control; -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/LayerControl/controls/_DynamicFolder.js b/viewer/js/gis/dijit/LayerControl/controls/_DynamicFolder.js index 584a3c9a4..15a601720 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/_DynamicFolder.js +++ b/viewer/js/gis/dijit/LayerControl/controls/_DynamicFolder.js @@ -56,7 +56,13 @@ define([ } else { this._setSublayerCheckbox(false, checkNode); } - this._handlers.push(on(checkNode, 'click', lang.hitch(this, function () { + this._handlers.push(on(checkNode, 'click', lang.hitch(this, function (event) { + + // prevent click event from bubbling + if (event.stopPropagation) { + event.stopPropagation(); + } + if (domAttr.get(checkNode, 'data-checked') === 'checked') { this._setSublayerCheckbox(false, checkNode); } else { @@ -124,4 +130,4 @@ define([ } }); return _DynamicFolder; -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/LayerControl/controls/_DynamicSublayer.js b/viewer/js/gis/dijit/LayerControl/controls/_DynamicSublayer.js index 3cce73151..51c9dd271 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/_DynamicSublayer.js +++ b/viewer/js/gis/dijit/LayerControl/controls/_DynamicSublayer.js @@ -66,7 +66,13 @@ define([ } else { this._setSublayerCheckbox(false, checkNode); } - this._handlers.push(on(checkNode, 'click', lang.hitch(this, function () { + this._handlers.push(on(checkNode, 'click', lang.hitch(this, function (event) { + + // prevent click event from bubbling + if (event.stopPropagation) { + event.stopPropagation(); + } + if (domAttr.get(checkNode, 'data-checked') === 'checked') { this._setSublayerCheckbox(false, checkNode); } else { @@ -82,17 +88,17 @@ define([ this._handlers.push(this.control.layer.getMap().on('zoom-end', lang.hitch(this, '_checkboxScaleRange'))); } //set up menu - if (this.control.controlOptions.menu && - this.control.controlOptions.menu.length) { - domClass.add(this.labelNode, 'menuLink'); - domClass.add(this.iconNode, 'menuLink'); + if (this.control.controlOptions.subLayerMenu && + this.control.controlOptions.subLayerMenu.length) { this.menu = new Menu({ contextMenuForWindow: false, - targetNodeIds: [this.labelNode], + targetNodeIds: [this.menuClickNode], leftClickToOpen: true }); - array.forEach(this.control.controlOptions.menu, lang.hitch(this, '_addMenuItem')); + array.forEach(this.control.controlOptions.subLayerMenu, lang.hitch(this, '_addMenuItem')); this.menu.startup(); + } else { + domClass.add(this.menuClickNode, 'hidden'); } }, _addMenuItem: function (menuItem) { diff --git a/viewer/js/gis/dijit/LayerControl/controls/templates/Control.html b/viewer/js/gis/dijit/LayerControl/controls/templates/Control.html index 5836fe4b0..e7e224030 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/templates/Control.html +++ b/viewer/js/gis/dijit/LayerControl/controls/templates/Control.html @@ -1,7 +1,7 @@
- - +
+
diff --git a/viewer/js/gis/dijit/LayerControl/controls/templates/Folder.html b/viewer/js/gis/dijit/LayerControl/controls/templates/Folder.html index 628438952..c62a97f34 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/templates/Folder.html +++ b/viewer/js/gis/dijit/LayerControl/controls/templates/Folder.html @@ -1,7 +1,7 @@ -
- - -
+
+ + +
diff --git a/viewer/js/gis/dijit/LayerControl/controls/templates/Sublayer.html b/viewer/js/gis/dijit/LayerControl/controls/templates/Sublayer.html index 8e5b82ea6..6d2a2b464 100644 --- a/viewer/js/gis/dijit/LayerControl/controls/templates/Sublayer.html +++ b/viewer/js/gis/dijit/LayerControl/controls/templates/Sublayer.html @@ -1,7 +1,7 @@ -
- - -
+
+ + + +
@@ -9,9 +9,14 @@ - + + + + +
- +
diff --git a/viewer/js/gis/dijit/LayerControl/css/LayerControl.css b/viewer/js/gis/dijit/LayerControl/css/LayerControl.css index 2a09ad53e..c3fa09b4e 100644 --- a/viewer/js/gis/dijit/LayerControl/css/LayerControl.css +++ b/viewer/js/gis/dijit/LayerControl/css/LayerControl.css @@ -45,8 +45,7 @@ line-height: 15px; } -.layerControlDijit .layerControlTableCheck, -.layerControlDijit .layerControlTableMenu { +.layerControlDijit .layerControlTableCheck, .layerControlDijit .layerControlTableMenu { cursor: pointer; width: 19px; height: 16px; @@ -54,10 +53,14 @@ } .layerControlDijit .layerControlTableLabel { + cursor: pointer; font-size: 15px; height: 16px; line-height: 16px; - cursor: default; +} + +.layerControlDijit .layerControlTableMenu { + cursor: pointer; } .layerControlDijit .layerControlTableUpdate { @@ -70,6 +73,7 @@ .layerControlDijit .layerControlHidden { display: none; } + .layerControlDijit .layerControlVisible { display: block; } @@ -99,7 +103,9 @@ color: #BBB; } + /* not in use - retain for links */ + .layerControlDijit .layerControlClick { cursor: pointer; color: #1f78af; @@ -118,7 +124,7 @@ vertical-align: middle; } -.layerControlDijit .layerControlLegendImage > img { +.layerControlDijit .layerControlLegendImage>img { border: none; padding: 0; } @@ -127,7 +133,9 @@ padding: 0 0 0 4px; } + /* temp esri/Legend overrides */ + .layerControlDijit .esriLegendService td { padding: 0; } @@ -140,13 +148,12 @@ display: none; } -.layerControlDijit .menuLink { - color: #369; - text-decoration: none; +/* sublayer menu */ + +.layerControlDijit .layerControlSublayer .layerControlTable td.layerControlTableMenu { + padding-right: 20px; } -.layerControlDijit .menuLink:hover { - color: #5196DB; - text-decoration: underline; - cursor: pointer; +.layerControlDijit .menuClickNode.hidden { + display: none; } diff --git a/viewer/js/gis/dijit/LayerControl/nls/es/resource.js b/viewer/js/gis/dijit/LayerControl/nls/es/resource.js new file mode 100644 index 000000000..f877b51f5 --- /dev/null +++ b/viewer/js/gis/dijit/LayerControl/nls/es/resource.js @@ -0,0 +1,14 @@ +define ({ + noLegend: 'Sin Leyenda', + moveUp: 'Ascender', + moveDown: 'Mover hacia abajo', + zoomTo: 'Amplía a Capa', + transparency: 'Transparencia', + metadata: 'Metadatos', + layerSwipe: 'Flagelo capa', + layerSwipeVertical: 'Vertical', + layerSwipeHorizontal: 'Horizontal', + layerSwipeScope: 'Alcance', + dynamicSublayersOn: 'Abra todas las subcapas', + dynamicSublayersOff: 'Apagar todas las subcapas' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/LayerControl/nls/fr/resource.js b/viewer/js/gis/dijit/LayerControl/nls/fr/resource.js new file mode 100644 index 000000000..789762185 --- /dev/null +++ b/viewer/js/gis/dijit/LayerControl/nls/fr/resource.js @@ -0,0 +1,14 @@ +define ({ + noLegend: 'Aucune légende', + moveUp: 'Déplacer vers le haut', + moveDown: 'Descendre', + zoomTo: 'Zoom sur la couche', + transparency: 'Transparence', + metadata: 'Métadonnées', + layerSwipe: 'Couche swipe', + layerSwipeVertical: 'Vertical', + layerSwipeHorizontal: 'Horizontal', + layerSwipeScope: 'Étendue', + dynamicSublayersOn: 'Activer toutes les sous-couches', + dynamicSublayersOff: 'Éteignez toutes les sous-couches' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/LayerControl/nls/pt-br/resource.js b/viewer/js/gis/dijit/LayerControl/nls/pt-br/resource.js new file mode 100644 index 000000000..612db0653 --- /dev/null +++ b/viewer/js/gis/dijit/LayerControl/nls/pt-br/resource.js @@ -0,0 +1,21 @@ +// internationalization for LayerControl +// +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +// +// if you would like to add a locale please create an issue at +// https://github.com/cmv/cmv-app/issues and someone will assist +// if need be or checkout the link above and submit a PR +define({ + noLegend: 'Sem Legenda', + moveUp: 'Mover para Cima', + moveDown: 'Mover para Baixo', + zoomTo: 'Zoom para a Camada', + transparency: 'Transparência', + metadata: 'Metadados', + layerSwipe: 'Cortina de Camada', + layerSwipeVertical: 'Vertical', + layerSwipeHorizontal: 'Horizontal', + layerSwipeScope: 'Escopo', + dynamicSublayersOn: 'Liga todas Subcamadas', + dynamicSublayersOff: 'Desliga todas Subcamadas' +}); diff --git a/viewer/js/gis/dijit/LayerControl/nls/pt-pt/resource.js b/viewer/js/gis/dijit/LayerControl/nls/pt-pt/resource.js new file mode 100644 index 000000000..e66ea4143 --- /dev/null +++ b/viewer/js/gis/dijit/LayerControl/nls/pt-pt/resource.js @@ -0,0 +1,21 @@ +// internationalization for LayerControl +// +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +// +// if you would like to add a locale please create an issue at +// https://github.com/cmv/cmv-app/issues and someone will assist +// if need be or checkout the link above and submit a PR +define({ + noLegend: 'Sem legenda', + moveUp: 'Mover para cima', + moveDown: 'Mover para baixo', + zoomTo: 'Aproximar à Camada', + transparency: 'Transparência', + metadata: 'Metadados', + layerSwipe: 'Deslizar camada', + layerSwipeVertical: 'Vertical', + layerSwipeHorizontal: 'Horizontal', + layerSwipeScope: 'Janela', + dynamicSublayersOn: 'Activar todas as subcamadas', + dynamicSublayersOff: 'Desligar todas as subcamadas' +}); diff --git a/viewer/js/gis/dijit/LayerControl/nls/resource.js b/viewer/js/gis/dijit/LayerControl/nls/resource.js index 3910c23cb..c371d3507 100644 --- a/viewer/js/gis/dijit/LayerControl/nls/resource.js +++ b/viewer/js/gis/dijit/LayerControl/nls/resource.js @@ -1,6 +1,6 @@ // internationalization for LayerControl // -// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +// https://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html // // if you would like to add a locale please create an issue at // https://github.com/cmv/cmv-app/issues and someone will assist @@ -19,5 +19,9 @@ define({ layerSwipeScope: 'Scope', dynamicSublayersOn: 'Turn On All Sublayers', dynamicSublayersOff: 'Turn Off All Sublayers' - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); diff --git a/viewer/js/gis/dijit/LayerControl/plugins/LayerMenu.js b/viewer/js/gis/dijit/LayerControl/plugins/LayerMenu.js index 32869d74d..1908610bb 100644 --- a/viewer/js/gis/dijit/LayerControl/plugins/LayerMenu.js +++ b/viewer/js/gis/dijit/LayerControl/plugins/LayerMenu.js @@ -120,4 +120,4 @@ define([ } } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/LayerControl/plugins/legendUtil.js b/viewer/js/gis/dijit/LayerControl/plugins/legendUtil.js index eeef8b35e..830a71792 100644 --- a/viewer/js/gis/dijit/LayerControl/plugins/legendUtil.js +++ b/viewer/js/gis/dijit/LayerControl/plugins/legendUtil.js @@ -82,7 +82,7 @@ define([ _arcgisLegendRequest: function (layer, expandNode, callback, errback) { var index = layer.url.toLowerCase().indexOf('/rest/'); var soap = layer.url.substring(0, index) + layer.url.substring(index + 5, layer.url.length); - var url = 'http://utility.arcgis.com/sharing/tools/legend?soapUrl=' + window.escape(soap); + var url = 'https://utility.arcgis.com/sharing/tools/legend?soapUrl=' + window.escape(soap); if (!has('ie') || has('ie') > 8) { url += '&returnbytes=true'; } diff --git a/viewer/js/gis/dijit/Legend.js b/viewer/js/gis/dijit/Legend.js new file mode 100644 index 000000000..1cd5b92ba --- /dev/null +++ b/viewer/js/gis/dijit/Legend.js @@ -0,0 +1,31 @@ +define([ + 'dojo/_base/declare', + 'dijit/_WidgetBase', + 'dojo/_base/lang', + 'esri/dijit/Legend' +], function ( + declare, + _WidgetBase, + lang, + Legend +) { + return declare([_WidgetBase], { + startup: function () { + this.inherited(arguments); + + this.legend = new Legend({ + arrangement: this.arrangement || Legend.ALIGN_LEFT, + autoUpdate: this.autoUpdate || true, + id: this.id + '_legend', + layerInfos: this.layerInfos, + map: this.map, + respectCurrentMapScale: this.respectCurrentMapScale || true + }, this.domNode); + this.legend.startup(); + + this.map.on('update-end', lang.hitch(this, function () { + this.legend.refresh(); + })); + } + }); +}); diff --git a/viewer/js/gis/dijit/Locale.js b/viewer/js/gis/dijit/Locale.js new file mode 100644 index 000000000..32322fc58 --- /dev/null +++ b/viewer/js/gis/dijit/Locale.js @@ -0,0 +1,187 @@ +define([ + 'dojo/_base/declare', + 'dijit/_WidgetBase', + 'dijit/_TemplatedMixin', + + 'dojo/_base/lang', + 'dojo/on', + 'dojo/dom-style', + 'dojo/_base/array', + 'dojo/_base/kernel', + 'dojo/io-query', + + 'dijit/form/DropDownButton', + 'dijit/DropDownMenu', + 'dijit/MenuItem', + + './Locale/countries', + + 'dojo/text!./Locale/templates/Locale.html', + 'dojo/i18n!./Locale/nls/resource', + + 'xstyle/css!flag-icon-css/css/flag-icon.min.css', + 'xstyle/css!./Locale/css/Locale.css' +], function ( + declare, + _WidgetBase, + _TemplatedMixin, + + lang, + on, + domStyle, + array, + kernel, + ioQuery, + + DropDownButton, + DropDownMenu, + MenuItem, + + countries, + + template, + i18n +) { + + return declare([_WidgetBase, _TemplatedMixin], { + templateString: template, + i18n: i18n, + baseClass: 'cmvLocaleDijit', + + currentLocale: null, + + includeFlag: true, + includeCountry: true, + includeLanguage: true, + + languages: { + 'en': 'English', + 'es': 'Español', + 'fr': 'Français', + 'pt': 'Português' + }, + + locales: [ + 'es-ar', + 'es-bo', + 'pt-br', + 'en-ca', + 'fr-ca', + 'es-cl', + 'es-co', + 'es-cr', + 'es-do', + 'es-ec', + 'es-sv', + 'fr-FR', + 'es-gt', + 'fr-ht', + 'es-hn', + 'en-in', + 'es-mx', + 'es-pa', + 'es-pe', + 'es-pr', + 'pt-pt', + 'es-py', + 'es-es', + 'en-gb', + 'en-us', + 'es-us', + 'es-uy', + 'es-ve' + ], + + postCreate: function () { + this.inherited(arguments); + + this.currentLocale = kernel.locale; + + if (this.parentWidget) { + if (this.parentWidget.toggleable) { + domStyle.set(this.localeLabelContainer, 'display', 'block'); + } + } + + var menu = new DropDownMenu({ + baseClass: 'localeMenu' + }); + + array.forEach(this.locales, lang.hitch(this, function (locale) { + var vals = locale.split('-'); + + // only include supported languages + var language = this.languages[vals[0]]; + if (language) { + var label = '', country = null, icon = null; + if (vals[1]) { + if (this.includeFlag) { + icon = 'flag-icon flag-icon-' + vals[1].toLowerCase(); + } + country = countries[vals[1].toUpperCase()]; + if (country && this.includeCountry) { + label = country; + } + } + if (this.includeLanguage) { + if (label.length > 0) { + label += ' - '; + } + label += language; + } + var menuItem = new MenuItem({ + id: locale, + label: label, + iconClass: icon, + onClick: lang.hitch(this, 'switchLocale', locale) + }); + menu.addChild(menuItem); + } + })); + menu.startup(); + + var vals = this.currentLocale.split('-'); + var language = this.languages[vals[0]]; + var label = '', country = null; + if (vals[1]) { + if (this.includeFlag) { + label = '
'; + } + country = countries[vals[1].toUpperCase()]; + if (country && this.includeCountry) { + label += country; + } + if (this.includeLanguage) { + if (country && this.includeCountry) { + label += ' - '; + } + label += language; + } + } + + var button = new DropDownButton({ + label: label, + dropDown: menu + }); + + this.localeDropDownContainer.appendChild(button.domNode); + }, + + switchLocale: function (newLocale) { + if (newLocale !== this.currentLocale) { + var uri = window.location.href, qsObj = {}; + if (uri.indexOf('?') > -1) { + var qs = uri.substring(uri.indexOf('?') + 1, uri.length); + qsObj = ioQuery.queryToObject(qs); + } + + // set the new locale + qsObj.locale = newLocale; + + // reload the page + window.location = window.location.pathname + '?' + ioQuery.objectToQuery(qsObj); + + } + } + }); +}); diff --git a/viewer/js/gis/dijit/Locale/countries.js b/viewer/js/gis/dijit/Locale/countries.js new file mode 100644 index 000000000..2313b1ee2 --- /dev/null +++ b/viewer/js/gis/dijit/Locale/countries.js @@ -0,0 +1,247 @@ +define ({ + 'AF': 'Afghanistan', + 'AX': 'Åland Islands', + 'AL': 'Albania', + 'DZ': 'Algeria', + 'AS': 'American Samoa', + 'AD': 'Andorra', + 'AO': 'Angola', + 'AI': 'Anguilla', + 'AQ': 'Antarctica', + 'AG': 'Antigua and Barbuda', + 'AR': 'Argentina', + 'AM': 'Armenia', + 'AW': 'Aruba', + 'AU': 'Australia', + 'AT': 'Austria', + 'AZ': 'Azerbaijan', + 'BS': 'Bahamas', + 'BH': 'Bahrain', + 'BD': 'Bangladesh', + 'BB': 'Barbados', + 'BY': 'Belarus', + 'BE': 'Belgium', + 'BZ': 'Belize', + 'BJ': 'Benin', + 'BM': 'Bermuda', + 'BT': 'Bhutan', + 'BO': 'Bolivia', + 'BA': 'Bosnia and Herzegovina', + 'BW': 'Botswana', + 'BV': 'Bouvet Island', + 'BR': 'Brazil', + 'IO': 'British Indian Ocean Territory', + 'BN': 'Brunei Darussalam', + 'BG': 'Bulgaria', + 'BF': 'Burkina Faso', + 'BI': 'Burundi', + 'KH': 'Cambodia', + 'CM': 'Cameroon', + 'CA': 'Canada', + 'CV': 'Cape Verde', + 'KY': 'Cayman Islands', + 'CF': 'Central African Republic', + 'TD': 'Chad', + 'CL': 'Chile', + 'CN': 'China', + 'CX': 'Christmas Island', + 'CC': 'Cocos (Keeling) Islands', + 'CO': 'Colombia', + 'KM': 'Comoros', + 'CG': 'Congo', + 'CD': 'Congo, The Democratic Republic of the', + 'CK': 'Cook Islands', + 'CR': 'Costa Rica', + 'CI': 'Cote D\'Ivoire', + 'HR': 'Croatia', + 'CU': 'Cuba', + 'CY': 'Cyprus', + 'CZ': 'Czech Republic', + 'DK': 'Denmark', + 'DJ': 'Djibouti', + 'DM': 'Dominica', + 'DO': 'Dominican Republic', + 'EC': 'Ecuador', + 'EG': 'Egypt', + 'SV': 'El Salvador', + 'GQ': 'Equatorial Guinea', + 'ER': 'Eritrea', + 'EE': 'Estonia', + 'ET': 'Ethiopia', + 'FK': 'Falkland Islands (Malvinas)', + 'FO': 'Faroe Islands', + 'FJ': 'Fiji', + 'FI': 'Finland', + 'FR': 'France', + 'GF': 'French Guiana', + 'PF': 'French Polynesia', + 'TF': 'French Southern Territories', + 'GA': 'Gabon', + 'GM': 'Gambia', + 'GE': 'Georgia', + 'DE': 'Germany', + 'GH': 'Ghana', + 'GI': 'Gibraltar', + 'GR': 'Greece', + 'GL': 'Greenland', + 'GD': 'Grenada', + 'GP': 'Guadeloupe', + 'GU': 'Guam', + 'GT': 'Guatemala', + 'GG': 'Guernsey', + 'GN': 'Guinea', + 'GW': 'Guinea-Bissau', + 'GY': 'Guyana', + 'HT': 'Haiti', + 'HM': 'Heard Island and Mcdonald Islands', + 'VA': 'Holy See (Vatican City State)', + 'HN': 'Honduras', + 'HK': 'Hong Kong', + 'HU': 'Hungary', + 'IS': 'Iceland', + 'IN': 'India', + 'ID': 'Indonesia', + 'IR': 'Iran, Islamic Republic Of', + 'IQ': 'Iraq', + 'IE': 'Ireland', + 'IM': 'Isle of Man', + 'IL': 'Israel', + 'IT': 'Italy', + 'JM': 'Jamaica', + 'JP': 'Japan', + 'JE': 'Jersey', + 'JO': 'Jordan', + 'KZ': 'Kazakhstan', + 'KE': 'Kenya', + 'KI': 'Kiribati', + 'KP': 'Democratic People\'s Republic of Korea', + 'KR': 'Korea, Republic of', + 'XK': 'Kosovo', + 'KW': 'Kuwait', + 'KG': 'Kyrgyzstan', + 'LA': 'Lao People\'s Democratic Republic', + 'LV': 'Latvia', + 'LB': 'Lebanon', + 'LS': 'Lesotho', + 'LR': 'Liberia', + 'LY': 'Libyan Arab Jamahiriya', + 'LI': 'Liechtenstein', + 'LT': 'Lithuania', + 'LU': 'Luxembourg', + 'MO': 'Macao', + 'MK': 'Macedonia, The Former Yugoslav Republic of', + 'MG': 'Madagascar', + 'MW': 'Malawi', + 'MY': 'Malaysia', + 'MV': 'Maldives', + 'ML': 'Mali', + 'MT': 'Malta', + 'MH': 'Marshall Islands', + 'MQ': 'Martinique', + 'MR': 'Mauritania', + 'MU': 'Mauritius', + 'YT': 'Mayotte', + 'MX': 'Mexico', + 'FM': 'Micronesia, Federated States of', + 'MD': 'Moldova, Republic of', + 'MC': 'Monaco', + 'MN': 'Mongolia', + 'ME': 'Montenegro', + 'MS': 'Montserrat', + 'MA': 'Morocco', + 'MZ': 'Mozambique', + 'MM': 'Myanmar', + 'NA': 'Namibia', + 'NR': 'Nauru', + 'NP': 'Nepal', + 'NL': 'Netherlands', + 'AN': 'Netherlands Antilles', + 'NC': 'New Caledonia', + 'NZ': 'New Zealand', + 'NI': 'Nicaragua', + 'NE': 'Niger', + 'NG': 'Nigeria', + 'NU': 'Niue', + 'NF': 'Norfolk Island', + 'MP': 'Northern Mariana Islands', + 'NO': 'Norway', + 'OM': 'Oman', + 'PK': 'Pakistan', + 'PW': 'Palau', + 'PS': 'Palestinian Territory, Occupied', + 'PA': 'Panama', + 'PG': 'Papua New Guinea', + 'PY': 'Paraguay', + 'PE': 'Peru', + 'PH': 'Philippines', + 'PN': 'Pitcairn', + 'PL': 'Poland', + 'PT': 'Portugal', + 'PR': 'Puerto Rico', + 'QA': 'Qatar', + 'RE': 'Reunion', + 'RO': 'Romania', + 'RU': 'Russian Federation', + 'RW': 'Rwanda', + 'SH': 'Saint Helena', + 'KN': 'Saint Kitts and Nevis', + 'LC': 'Saint Lucia', + 'PM': 'Saint Pierre and Miquelon', + 'VC': 'Saint Vincent and the Grenadines', + 'WS': 'Samoa', + 'SM': 'San Marino', + 'ST': 'Sao Tome and Principe', + 'SA': 'Saudi Arabia', + 'SN': 'Senegal', + 'RS': 'Serbia', + 'SC': 'Seychelles', + 'SL': 'Sierra Leone', + 'SG': 'Singapore', + 'SK': 'Slovakia', + 'SI': 'Slovenia', + 'SB': 'Solomon Islands', + 'SO': 'Somalia', + 'ZA': 'South Africa', + 'GS': 'South Georgia and the South Sandwich Islands', + 'ES': 'Spain', + 'LK': 'Sri Lanka', + 'SD': 'Sudan', + 'SR': 'Suriname', + 'SJ': 'Svalbard and Jan Mayen', + 'SZ': 'Swaziland', + 'SE': 'Sweden', + 'CH': 'Switzerland', + 'SY': 'Syrian Arab Republic', + 'TW': 'Taiwan', + 'TJ': 'Tajikistan', + 'TZ': 'Tanzania, United Republic of', + 'TH': 'Thailand', + 'TL': 'Timor-Leste', + 'TG': 'Togo', + 'TK': 'Tokelau', + 'TO': 'Tonga', + 'TT': 'Trinidad and Tobago', + 'TN': 'Tunisia', + 'TR': 'Turkey', + 'TM': 'Turkmenistan', + 'TC': 'Turks and Caicos Islands', + 'TV': 'Tuvalu', + 'UG': 'Uganda', + 'UA': 'Ukraine', + 'AE': 'United Arab Emirates', + 'GB': 'United Kingdom', + 'US': 'United States', + 'UM': 'United States Minor Outlying Islands', + 'UY': 'Uruguay', + 'UZ': 'Uzbekistan', + 'VU': 'Vanuatu', + 'VE': 'Venezuela', + 'VN': 'Viet Nam', + 'VG': 'Virgin Islands, British', + 'VI': 'Virgin Islands, U.S.', + 'WF': 'Wallis and Futuna', + 'EH': 'Western Sahara', + 'YE': 'Yemen', + 'ZM': 'Zambia', + 'ZW': 'Zimbabwe' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Locale/css/Locale.css b/viewer/js/gis/dijit/Locale/css/Locale.css new file mode 100644 index 000000000..295d669fc --- /dev/null +++ b/viewer/js/gis/dijit/Locale/css/Locale.css @@ -0,0 +1,14 @@ +.cmvLocaleDijit label { + font-weight: bold; +} +.flag-icon { + width: 20px; + height: 15px; + margin-right: 5px; + margin-top: -1px; + border: 1px solid #DDD; +} + +.localeMenuPopup { + max-height: 300px; +} \ No newline at end of file diff --git a/viewer/js/gis/dijit/Locale/nls/es/resource.js b/viewer/js/gis/dijit/Locale/nls/es/resource.js new file mode 100644 index 000000000..ca3c987cb --- /dev/null +++ b/viewer/js/gis/dijit/Locale/nls/es/resource.js @@ -0,0 +1,3 @@ +define ({ + selectLocale: 'Seleccione su Lugar:' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Locale/nls/fr/resource.js b/viewer/js/gis/dijit/Locale/nls/fr/resource.js new file mode 100644 index 000000000..0c23e350e --- /dev/null +++ b/viewer/js/gis/dijit/Locale/nls/fr/resource.js @@ -0,0 +1,3 @@ +define ({ + selectLocale: 'Sélectionnez votre lieu :' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Locale/nls/pt-br/resource.js b/viewer/js/gis/dijit/Locale/nls/pt-br/resource.js new file mode 100644 index 000000000..eac84670c --- /dev/null +++ b/viewer/js/gis/dijit/Locale/nls/pt-br/resource.js @@ -0,0 +1,3 @@ +define ({ + selectLocale: 'Escolha a sua localidade:' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Locale/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Locale/nls/pt-pt/resource.js new file mode 100644 index 000000000..eac84670c --- /dev/null +++ b/viewer/js/gis/dijit/Locale/nls/pt-pt/resource.js @@ -0,0 +1,3 @@ +define ({ + selectLocale: 'Escolha a sua localidade:' +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Locale/nls/resource.js b/viewer/js/gis/dijit/Locale/nls/resource.js new file mode 100644 index 000000000..239076ecf --- /dev/null +++ b/viewer/js/gis/dijit/Locale/nls/resource.js @@ -0,0 +1,9 @@ +define ({ + root: { + selectLocale: 'Select Your Locale:' + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Locale/templates/Locale.html b/viewer/js/gis/dijit/Locale/templates/Locale.html new file mode 100644 index 000000000..e07a68851 --- /dev/null +++ b/viewer/js/gis/dijit/Locale/templates/Locale.html @@ -0,0 +1,6 @@ +
+
+ +
+
+
\ No newline at end of file diff --git a/viewer/js/gis/dijit/MapInfo.js b/viewer/js/gis/dijit/MapInfo.js index 07c238bea..a6d414ba2 100644 --- a/viewer/js/gis/dijit/MapInfo.js +++ b/viewer/js/gis/dijit/MapInfo.js @@ -10,7 +10,7 @@ define([ 'dojo/dom-style', 'dojo/number', 'dojo/topic', - '//cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.12/proj4.js', + 'proj4js/proj4', 'xstyle/css!./MapInfo/css/MapInfo.css' ], function ( declare, @@ -36,6 +36,7 @@ define([ scaleLabel: '1:', zoomLabel: 'Z:', minWidth: 0, + proj4BaseURL: 'https://epsg.io/', proj4Catalog: null, proj4Wkid: null, proj4CustomURL: null, @@ -99,27 +100,26 @@ define([ this._mode = 3; } else { this._mode = 4; - // spatialreference.org uses the old - // Proj4js style so we need an alias - // https://github.com/proj4js/proj4js/issues/23 - window.Proj4js = proj4; - //load custom projection file or default to spatialreference.org - if (!this.proj4Catalog && !this.proj4Wkid && !this.proj4CustomURL) { + if (!window.proj4) { + window.proj4 = proj4; + } + if (this.proj4Wkid) { + wkid = this.proj4Wkid; + } + //load custom projection file or default to epsg.io + if (!this.proj4Catalog && !wkid && !this.proj4CustomURL) { topic.publish('viewer/handleError', { source: 'MapInfo', error: 'MapInfo error::a proj4Catalog/proj4Wkid or custom URL must be defined' }); return; } - if (this.proj4CustomURL) { - require([this.proj4CustomURL], lang.hitch(this, function () { - this._projectionLoaded = true; - this._projection = this.proj4Catalog + ':' + this.proj4Wkid; - })); - } else { - require(['http://spatialreference.org/ref/' + this.proj4Catalog.toLowerCase() + '/' + this.proj4Wkid + '/proj4js/'], lang.hitch(this, function () { + var key = this.proj4Catalog + ':' + String(wkid); + this._projection = key; + if (!proj4.defs[key]) { + var url = this.proj4CustomURL || this.proj4BaseURL + String(wkid) + '.js'; + require([url], lang.hitch(this, function () { this._projectionLoaded = true; - this._projection = this.proj4Catalog + ':' + this.proj4Wkid; })); } } @@ -217,4 +217,4 @@ define([ return deg + '°' + minIntTxt + '\'' + secTxt + '" ' + dir; } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/Print.js b/viewer/js/gis/dijit/Print.js index 5c7383255..7fa56891f 100644 --- a/viewer/js/gis/dijit/Print.js +++ b/viewer/js/gis/dijit/Print.js @@ -12,11 +12,13 @@ define([ 'dojo/dom-style', 'dojo/dom-construct', 'dojo/dom-class', + 'dojo/date/locale', 'dojo/text!./Print/templates/Print.html', 'dojo/text!./Print/templates/PrintResult.html', 'esri/tasks/PrintTemplate', 'esri/tasks/PrintParameters', 'esri/request', + 'esri/urlUtils', 'dojo/i18n!./Print/nls/resource', 'dijit/form/Form', @@ -30,7 +32,7 @@ define([ 'dijit/TooltipDialog', 'dijit/form/RadioButton', 'xstyle/css!./Print/css/Print.css' -], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, PrintTask, Memory, lang, array, topic, Style, domConstruct, domClass, printTemplate, printResultTemplate, PrintTemplate, PrintParameters, esriRequest, i18n) { +], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, PrintTask, Memory, lang, array, topic, Style, domConstruct, domClass, locale, printTemplate, printResultTemplate, PrintTemplate, PrintParameters, esriRequest, urlUtils, i18n) { // Print result dijit var PrintResultDijit = declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { @@ -39,13 +41,20 @@ define([ i18n: i18n, url: null, fileHandle: null, + resultOrder: 'last', // first or last + postCreate: function () { this.inherited(arguments); this.fileHandle.then(lang.hitch(this, '_onPrintComplete'), lang.hitch(this, '_onPrintError')); }, _onPrintComplete: function (data) { if (data.url) { - this.url = data.url; + var proxyRule = urlUtils.getProxyRule(data.url); + if (proxyRule && proxyRule.proxyUrl) { + this.url = proxyRule.proxyUrl + '?' + data.url; + } else { + this.url = data.url; + } this.nameNode.innerHTML = '' + this.docName + ''; domClass.add(this.resultNode, 'printResultHover'); } else { @@ -223,9 +232,9 @@ define([ count: this.count.toString(), icon: (form.format === 'PDF') ? this.pdfIcon : this.imageIcon, docName: form.title, - title: form.format + ', ' + form.layout, + title: form.format + ', ' + form.layout + ', ' + locale.format(new Date(), {formatLength: 'short'}), fileHandle: fileHandle - }).placeAt(this.printResultsNode, 'last'); + }).placeAt(this.printResultsNode, this.resultOrder); if (this.printTask.async) { result.own(this.printTask.printGp.on('status-update', lang.hitch(result, '_handleStatusUpdate'))); diff --git a/viewer/js/gis/dijit/Print/nls/es/resource.js b/viewer/js/gis/dijit/Print/nls/es/resource.js new file mode 100644 index 000000000..a11fa0e2b --- /dev/null +++ b/viewer/js/gis/dijit/Print/nls/es/resource.js @@ -0,0 +1,39 @@ +define ({ + title: 'Título', + format: 'Formato', + layout: 'Diseño', + settings: 'Ajustes', + mapScaleExtent: 'Escala/Extensión de mapa', + preserve: 'Preservar', + mapScale: 'escala del mapa', + mapExtent: 'extensión de mapa', + fullLayoutOptions: 'Opciones de diseño completos', + scaleBarUnits: 'Unidades barra de escala', + miles: 'Millas', + kilometers: 'Kilómetros', + meters: 'Metros', + feet: 'Pies', + includeLegend: 'Incluir la leyenda', + printQualityOptions: 'Opciones de calidad de impresión', + dpiInput: { + label: 'DPI', + invalidMessage: 'Por favor, introduzca un valor numérico.', + rangeMessage: 'Por favor, introduzca un valor entre 100 y 300.' + }, + mapOnlyOptions: 'Opciones MAP_ONLY', + width: 'Anchura', + height: 'Altura', + printButton: { + busyLabel: 'imprenta', + label: 'Impresión' + }, + clearHistoryButton: { + label: 'Despejar el historial de impresión' + }, + printResults: { + progressBar: { + label: 'Creación de impresión' + }, + errorMessage: 'Error, inténtalo de nuevo' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Print/nls/fr/resource.js b/viewer/js/gis/dijit/Print/nls/fr/resource.js new file mode 100644 index 000000000..a232434bb --- /dev/null +++ b/viewer/js/gis/dijit/Print/nls/fr/resource.js @@ -0,0 +1,39 @@ +define ({ + title: 'Titre', + format: 'Format', + layout: 'Disposition', + settings: 'Paramètres', + mapScaleExtent: 'Échelle/étendue de la carte', + preserve: 'Préserver', + mapScale: 'échelle de la carte', + mapExtent: 'étendue de la carte', + fullLayoutOptions: 'Options complètes de mise en page', + scaleBarUnits: 'Unités de la barre d\'échelle', + miles: 'Miles', + kilometers: 'Kilomètres', + meters: 'Mètres', + feet: 'Pieds', + includeLegend: 'Inclure la légende', + printQualityOptions: 'Qualité d`impression', + dpiInput: { + label: 'DPI', + invalidMessage: 'S\'il vous plaît entrer une valeur numérique.', + rangeMessage: 'S\'il vous plaît entrer une valeur entre 100 et 300.' + }, + mapOnlyOptions: 'Options MAP_ONLY', + width: 'Largeur', + height: 'Hauteur', + printButton: { + busyLabel: 'Impression', + label: 'Imprimer' + }, + clearHistoryButton: { + label: 'Effacer l\'historique d\'impression' + }, + printResults: { + progressBar: { + label: 'Création de l\'impression' + }, + errorMessage: 'Erreur, réessayez' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Print/nls/pt-br/resource.js b/viewer/js/gis/dijit/Print/nls/pt-br/resource.js new file mode 100644 index 000000000..dbb30c663 --- /dev/null +++ b/viewer/js/gis/dijit/Print/nls/pt-br/resource.js @@ -0,0 +1,40 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + title: 'Título', + format: 'Formato', + layout: 'Modelo', + settings: 'Configurações', + mapScaleExtent: 'Escala/Extensão do Mapa', + preserve: 'Preservar', + mapScale: 'Escala do mapa', + mapExtent: 'Extensão do mapa', + fullLayoutOptions: 'Opções de Modelo Completa', + scaleBarUnits: 'Unidades da barra de escala', + miles: 'Milhas', + kilometers: 'Quilômetros', + meters: 'Metros', + feet: 'Pés', + includeLegend: 'Incluir Legenda', + printQualityOptions: 'Opções da qualidade de Impressão', + dpiInput: { + label: 'DPI', + invalidMessage: 'Por favor entre um valor numérico.', + rangeMessage: 'Por favor entre um valor entre 100 e 300.' + }, + mapOnlyOptions: 'opções MAP_ONLY', + width: 'Largura', + height: 'Altura', + printButton: { + busyLabel: 'imprimindo', + label: 'Imprimir' + }, + clearHistoryButton: { + label: 'Limpar histórico de impressão' + }, + printResults: { + progressBar: { + label: 'Criando impressão' + }, + errorMessage: 'Erro, tente novamente' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Print/nls/pt-pt/resource.js b/viewer/js/gis/dijit/Print/nls/pt-pt/resource.js new file mode 100644 index 000000000..2f9ac62fa --- /dev/null +++ b/viewer/js/gis/dijit/Print/nls/pt-pt/resource.js @@ -0,0 +1,40 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + title: 'Título', + format: 'Formato', + layout: 'Modelo', + settings: 'Configurações', + mapScaleExtent: 'Escala/Extensão do mapa', + preserve: 'Preservar', + mapScale: 'Escala do mapa', + mapExtent: 'Extensão do mapa', + fullLayoutOptions: 'Todas as opções do modelo', + scaleBarUnits: 'Unidades da escala gráfica', + miles: 'Milhas', + kilometers: 'Quilómetros', + meters: 'Metros', + feet: 'Pés', + includeLegend: 'Incluir legenda', + printQualityOptions: 'Opções da qualidade de impressão', + dpiInput: { + label: 'DPI', + invalidMessage: 'Por favor introduza um valor numérico.', + rangeMessage: 'Por favor introduza um valor entre 100 e 300.' + }, + mapOnlyOptions: 'Opções do mapa', + width: 'Largura', + height: 'Altura', + printButton: { + busyLabel: 'a imprimir', + label: 'Imprimir' + }, + clearHistoryButton: { + label: 'Limpar o histórico de impressão' + }, + printResults: { + progressBar: { + label: 'A criar a impressão' + }, + errorMessage: 'Erro, tente novamente' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/Print/nls/resource.js b/viewer/js/gis/dijit/Print/nls/resource.js index 3c250c54d..3af6b5a74 100644 --- a/viewer/js/gis/dijit/Print/nls/resource.js +++ b/viewer/js/gis/dijit/Print/nls/resource.js @@ -1,4 +1,4 @@ -// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +// https://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html define({ root: { title: 'Title', @@ -38,6 +38,10 @@ define({ }, errorMessage: 'Error, try again' } - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); diff --git a/viewer/js/gis/dijit/Print/templates/Print.html b/viewer/js/gis/dijit/Print/templates/Print.html index 28d754273..c8ed5cdab 100644 --- a/viewer/js/gis/dijit/Print/templates/Print.html +++ b/viewer/js/gis/dijit/Print/templates/Print.html @@ -42,12 +42,12 @@
- diff --git a/viewer/js/gis/dijit/StreetView.js b/viewer/js/gis/dijit/StreetView.js index 057332415..a9dc90a0f 100644 --- a/viewer/js/gis/dijit/StreetView.js +++ b/viewer/js/gis/dijit/StreetView.js @@ -1,4 +1,3 @@ -/*global google */ define([ 'dojo/_base/declare', 'dijit/_WidgetBase', @@ -17,69 +16,76 @@ define([ 'esri/geometry/Point', 'esri/SpatialReference', 'dijit/MenuItem', - '//cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.12/proj4.js', + 'proj4js/proj4', 'dojo/i18n!./StreetView/nls/resource', - + 'gis/plugins/Google', 'dijit/form/ToggleButton', - 'xstyle/css!./StreetView/css/StreetView.css', - 'gis/plugins/async!//maps.google.com/maps/api/js?v=3' -], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, lang, aspect, topic, GraphicsLayer, Graphic, SimpleRenderer, template, PictureMarkerSymbol, domStyle, domGeom, Point, SpatialReference, MenuItem, proj4, i18n) { - + 'xstyle/css!./StreetView/css/StreetView.css' +], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, lang, aspect, topic, GraphicsLayer, Graphic, SimpleRenderer, template, PictureMarkerSymbol, domStyle, domGeom, Point, SpatialReference, MenuItem, proj4, i18n, Google) { + //cache google so + var google; return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { widgetsInTemplate: true, templateString: template, i18n: i18n, mapClickMode: null, - panoOptions: { - addressControlOptions: { - position: google.maps.ControlPosition.TOP_RIGHT - }, - linksControl: false, - panControl: false, - zoomControlOptions: { - style: google.maps.ZoomControlStyle.SMALL - }, - enableCloseButton: false - }, + panoOptions: null, // in case this changes some day - proj4BaseURL: 'http://spatialreference.org/', + proj4BaseURL: 'https://epsg.io/', // options are ESRI, EPSG and SR-ORG - // See http://spatialreference.org/ for more information + // See http://sepsg.io/ for more information proj4Catalog: 'EPSG', // if desired, you can load a projection file from your server - // instead of using one from spatialreference.org + // instead of using one from epsg.io // i.e., http://server/projections/102642.js - projCustomURL: null, + proj4CustomURL: null, postCreate: function () { this.inherited(arguments); - this.createGraphicsLayer(); - this.map.on('click', lang.hitch(this, 'getStreetView')); + //load the google api asynchronously + Google.load(lang.hitch(this, function (g) { + //store a reference to google + google = g; - this.own(topic.subscribe('mapClickMode/currentSet', lang.hitch(this, 'setMapClickMode'))); + //init our panoOptions since they depend on google + this.panoOptions = { + addressControlOptions: { + position: google.maps.ControlPosition.TOP_RIGHT + }, + linksControl: false, + panControl: false, + zoomControlOptions: { + style: google.maps.ZoomControlStyle.SMALL + }, + enableCloseButton: false + }; + this.createGraphicsLayer(); + this.map.on('click', lang.hitch(this, 'getStreetView')); - if (this.parentWidget) { - if (this.parentWidget.toggleable) { - this.own(aspect.after(this.parentWidget, 'toggle', lang.hitch(this, function () { - this.onLayoutChange(this.parentWidget.open); - }))); + this.own(topic.subscribe('mapClickMode/currentSet', lang.hitch(this, 'setMapClickMode'))); + + if (this.parentWidget) { + if (this.parentWidget.toggleable) { + this.own(aspect.after(this.parentWidget, 'toggle', lang.hitch(this, function () { + this.onLayoutChange(this.parentWidget.open); + }))); + } + this.own(aspect.after(this.parentWidget, 'resize', lang.hitch(this, 'resize'))); + this.own(topic.subscribe(this.parentWidget.id + '/resize/resize', lang.hitch(this, 'resize'))); } - this.own(aspect.after(this.parentWidget, 'resize', lang.hitch(this, 'resize'))); - this.own(topic.subscribe(this.parentWidget.id + '/resize/resize', lang.hitch(this, 'resize'))); - } - // spatialreference.org uses the old - // Proj4js style so we need an alias - // https://github.com/proj4js/proj4js/issues/23 - window.Proj4js = proj4; + if (!window.proj4) { + window.proj4 = proj4; + } - if (this.mapRightClickMenu) { - this.addRightClickMenu(); - } + if (this.mapRightClickMenu) { + this.addRightClickMenu(); + } + })); }, createGraphicsLayer: function () { this.pointSymbol = new PictureMarkerSymbol(require.toUrl('gis/dijit/StreetView/images/blueArrow.png'), 30, 30); @@ -129,7 +135,7 @@ define([ } else { this.connectMapClick(); } - //get map click, set up listener in post create + //get map click, set up listener in post create }, disconnectMapClick: function () { this.streetViewButtonDijit.set('checked', true); @@ -168,9 +174,9 @@ define([ if (wkid === 102100) { wkid = 3857; } - var key = this.proj4Catalog + ':' + wkid; + var key = this.proj4Catalog + ':' + String(wkid); if (!proj4.defs[key]) { - var url = this.proj4CustomURL || this.proj4BaseURL + 'ref/' + this.proj4Catalog.toLowerCase() + '/' + wkid + '/proj4js/'; + var url = this.proj4CustomURL || this.proj4BaseURL + String(wkid) + '.js'; require([url], lang.hitch(this, 'getStreetView', evt, true)); return; } @@ -254,9 +260,9 @@ define([ if (wkid === 102100) { wkid = 3857; } - var key = this.proj4Catalog + ':' + wkid; + var key = this.proj4Catalog + ':' + String(wkid); if (!proj4.defs[key]) { - var url = this.proj4CustomURL || this.proj4BaseURL + 'ref/' + this.proj4Catalog.toLowerCase() + '/' + wkid + '/proj4js/'; + var url = this.proj4CustomURL || this.proj4BaseURL + String(wkid) + '.js'; require([url], lang.hitch(this, 'setPlaceMarkerPosition')); return; } @@ -302,4 +308,4 @@ define([ this.mapClickMode = mode; } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/gis/dijit/StreetView/css/StreetView.css b/viewer/js/gis/dijit/StreetView/css/StreetView.css index d0ba18386..d13f67d68 100644 --- a/viewer/js/gis/dijit/StreetView/css/StreetView.css +++ b/viewer/js/gis/dijit/StreetView/css/StreetView.css @@ -2,6 +2,7 @@ width: 100%; height: 250px; padding-bottom: 5px; + position: relative; } .gis_StreetView .streetViewButton { diff --git a/viewer/js/gis/dijit/StreetView/nls/es/resource.js b/viewer/js/gis/dijit/StreetView/nls/es/resource.js new file mode 100644 index 000000000..2318abc5e --- /dev/null +++ b/viewer/js/gis/dijit/StreetView/nls/es/resource.js @@ -0,0 +1,9 @@ +define ({ + messages: { + instructions: 'Haga clic en el botón de StreetView a continuación, haga clic en el mapa en su posición deseada.', + notAvailable: 'Desafortunadamente, Google StreetView todavía no está disponible en ese lugar.' + }, + rightClickMenuItem: { + label: 'Google StreetView aquí' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/StreetView/nls/fr/resource.js b/viewer/js/gis/dijit/StreetView/nls/fr/resource.js new file mode 100644 index 000000000..f3c961b39 --- /dev/null +++ b/viewer/js/gis/dijit/StreetView/nls/fr/resource.js @@ -0,0 +1,9 @@ +define ({ + messages: { + instructions: 'Cliquez sur le bouton StreetView puis cliquez sur la carte à l\'endroit désiré.', + notAvailable: 'Malheureusement, les images de Google StreetView ne sont pas encore disponible à cet endroit.' + }, + rightClickMenuItem: { + label: 'Ouvrir Google StreetView à cet endroit' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/StreetView/nls/pt-br/resource.js b/viewer/js/gis/dijit/StreetView/nls/pt-br/resource.js new file mode 100644 index 000000000..6e9f7e049 --- /dev/null +++ b/viewer/js/gis/dijit/StreetView/nls/pt-br/resource.js @@ -0,0 +1,10 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + messages: { + instructions: 'Clique no botão do StreetView e depois clique na localização desejada no mapa.', + notAvailable: 'Infelizmente, o Google Street View não está disponível nesta localização.' + }, + rightClickMenuItem: { + label: 'Street View aqui' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/StreetView/nls/pt-pt/resource.js b/viewer/js/gis/dijit/StreetView/nls/pt-pt/resource.js new file mode 100644 index 000000000..6e9f7e049 --- /dev/null +++ b/viewer/js/gis/dijit/StreetView/nls/pt-pt/resource.js @@ -0,0 +1,10 @@ +// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +define({ + messages: { + instructions: 'Clique no botão do StreetView e depois clique na localização desejada no mapa.', + notAvailable: 'Infelizmente, o Google Street View não está disponível nesta localização.' + }, + rightClickMenuItem: { + label: 'Street View aqui' + } +}); \ No newline at end of file diff --git a/viewer/js/gis/dijit/StreetView/nls/resource.js b/viewer/js/gis/dijit/StreetView/nls/resource.js index d417ab380..6e80605ca 100644 --- a/viewer/js/gis/dijit/StreetView/nls/resource.js +++ b/viewer/js/gis/dijit/StreetView/nls/resource.js @@ -1,4 +1,4 @@ -// http://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html +// https://dojotoolkit.org/reference-guide/1.10/dojo/i18n.html define({ root: { messages: { @@ -8,6 +8,10 @@ define({ rightClickMenuItem: { label: 'Google StreetView here' } - } + }, + 'es': true, + 'fr': true, + 'pt-br': true, + 'pt-pt': true }); diff --git a/viewer/js/gis/plugins/Google.js b/viewer/js/gis/plugins/Google.js new file mode 100644 index 000000000..796c6926c --- /dev/null +++ b/viewer/js/gis/plugins/Google.js @@ -0,0 +1,223 @@ +//https://www.npmjs.com/package/google-maps +/* eslint-disable */ +(function(root, factory) { + + if (root === null) { + //throw new Error('Google-maps package can be used only in browser'); + } + + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.GoogleMapsLoader = factory(); + } + +})(typeof window !== 'undefined' ? window : null, function() { + + + 'use strict'; + + + var script = null; + + var google = null; + + var loading = false; + + var callbacks = []; + + var onLoadEvents = []; + + var originalCreateLoaderMethod = null; + + + var GoogleMapsLoader = {}; + + + GoogleMapsLoader.URL = 'https://maps.googleapis.com/maps/api/js'; + + GoogleMapsLoader.KEY = null; + + GoogleMapsLoader.LIBRARIES = []; + + GoogleMapsLoader.CLIENT = null; + + GoogleMapsLoader.CHANNEL = null; + + GoogleMapsLoader.LANGUAGE = null; + + GoogleMapsLoader.REGION = null; + + GoogleMapsLoader.VERSION = '3'; + + GoogleMapsLoader.WINDOW_CALLBACK_NAME = '__google_maps_api_provider_initializator__'; + + + GoogleMapsLoader._googleMockApiObject = {}; + + + GoogleMapsLoader.load = function(fn) { + if (google === null) { + if (loading === true) { + if (fn) { + callbacks.push(fn); + } + } else { + loading = true; + + window[GoogleMapsLoader.WINDOW_CALLBACK_NAME] = function() { + ready(fn); + }; + + GoogleMapsLoader.createLoader(); + } + } else if (fn) { + fn(google); + } + }; + + + GoogleMapsLoader.createLoader = function() { + script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = GoogleMapsLoader.createUrl(); + + document.body.appendChild(script); + }; + + + GoogleMapsLoader.isLoaded = function() { + return google !== null; + }; + + + GoogleMapsLoader.createUrl = function() { + var url = GoogleMapsLoader.URL; + + url += '?callback=' + GoogleMapsLoader.WINDOW_CALLBACK_NAME; + + if (GoogleMapsLoader.KEY) { + url += '&key=' + GoogleMapsLoader.KEY; + } + + if (GoogleMapsLoader.LIBRARIES.length > 0) { + url += '&libraries=' + GoogleMapsLoader.LIBRARIES.join(','); + } + + if (GoogleMapsLoader.VERSION) { + url += '&v=' + GoogleMapsLoader.VERSION; + } + + if (GoogleMapsLoader.CLIENT) { + url += '&client=' + GoogleMapsLoader.CLIENT; + } + + if (GoogleMapsLoader.CHANNEL) { + url += '&channel=' + GoogleMapsLoader.CHANNEL; + } + + if (GoogleMapsLoader.LANGUAGE) { + url += '&language=' + GoogleMapsLoader.LANGUAGE; + } + + if (GoogleMapsLoader.REGION) { + url += '®ion=' + GoogleMapsLoader.REGION; + } + + return url; + }; + + + GoogleMapsLoader.release = function(fn) { + var release = function() { + GoogleMapsLoader.KEY = null; + GoogleMapsLoader.LIBRARIES = []; + GoogleMapsLoader.CLIENT = null; + GoogleMapsLoader.CHANNEL = null; + GoogleMapsLoader.LANGUAGE = null; + GoogleMapsLoader.REGION = null; + GoogleMapsLoader.VERSION = googleVersion; + + google = null; + loading = false; + callbacks = []; + onLoadEvents = []; + + if (typeof window.google !== 'undefined') { + delete window.google; + } + + if (typeof window[GoogleMapsLoader.WINDOW_CALLBACK_NAME] !== 'undefined') { + delete window[GoogleMapsLoader.WINDOW_CALLBACK_NAME]; + } + + if (originalCreateLoaderMethod !== null) { + GoogleMapsLoader.createLoader = originalCreateLoaderMethod; + originalCreateLoaderMethod = null; + } + + if (script !== null) { + script.parentElement.removeChild(script); + script = null; + } + + if (fn) { + fn(); + } + }; + + if (loading) { + GoogleMapsLoader.load(function() { + release(); + }); + } else { + release(); + } + }; + + + GoogleMapsLoader.onLoad = function(fn) { + onLoadEvents.push(fn); + }; + + + GoogleMapsLoader.makeMock = function() { + originalCreateLoaderMethod = GoogleMapsLoader.createLoader; + + GoogleMapsLoader.createLoader = function() { + window.google = GoogleMapsLoader._googleMockApiObject; + window[GoogleMapsLoader.WINDOW_CALLBACK_NAME](); + }; + }; + + + var ready = function(fn) { + var i; + + loading = false; + + if (google === null) { + google = window.google; + } + + for (i = 0; i < onLoadEvents.length; i++) { + onLoadEvents[i](google); + } + + if (fn) { + fn(google); + } + + for (i = 0; i < callbacks.length; i++) { + callbacks[i](google); + } + + callbacks = []; + }; + + + return GoogleMapsLoader; + +}); diff --git a/viewer/js/viewer/_ConfigMixin.js b/viewer/js/viewer/_ConfigMixin.js index 0113fc381..999f8ab14 100644 --- a/viewer/js/viewer/_ConfigMixin.js +++ b/viewer/js/viewer/_ConfigMixin.js @@ -10,10 +10,50 @@ define([ return declare(null, { + // the default name of the config file to load if ?config=configName + // is not specified + defaultConfig: 'viewer', + loadConfig: function (wait) { + + // this will be used to make any inherited methods 'wait' + var waitDeferred; + + if (wait) { + waitDeferred = new Deferred(); + + // if we need to wait for a previous deferred + // wait for it, + wait.then(lang.hitch(this, function () { + + // load the config + this.initConfigAsync().then(lang.hitch(this, function () { + + // do some stuff + this.initConfigSuccess(arguments); + + // resolve + waitDeferred.resolve(); + }), + lang.hitch(this, 'initConfigError') + ); + + })); + } else { + + waitDeferred = this.initConfigAsync(); + waitDeferred.then( + lang.hitch(this, 'initConfigSuccess'), + lang.hitch(this, 'initConfigError') + ); + } + // call any inherited methods or return a deferred + return this.inherited(arguments, [waitDeferred]) || waitDeferred; + }, + initConfigAsync: function () { var returnDeferred = new Deferred(); // get the config file from the url if present - var file = 'config/viewer', + var file = 'config/' + this.defaultConfig, s = window.location.search, q = s.match(/config=([^&]*)/i); if (q && q.length > 0) { @@ -30,7 +70,6 @@ define([ initConfigSuccess: function (config) { this.config = config; - if (config.isDebug) { window.app = this; //dev only } @@ -40,15 +79,6 @@ define([ current: config.defaultMapClickMode, defaultMode: config.defaultMapClickMode }; - - // in _LayoutMixin - this.initLayout(); - - // in _MapMixin - this.initMapAsync().then( - lang.hitch(this, 'initMapComplete'), - lang.hitch(this, 'initMapError') - ); }, initConfigError: function (err) { @@ -58,4 +88,4 @@ define([ }); } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/viewer/_ControllerBase.js b/viewer/js/viewer/_ControllerBase.js index 08ae67ed6..4ae03c5f7 100644 --- a/viewer/js/viewer/_ControllerBase.js +++ b/viewer/js/viewer/_ControllerBase.js @@ -1,27 +1,95 @@ /*eslint no-console: 0*/ define([ 'dojo/_base/declare', - 'dojo/_base/lang' + 'dojo/_base/lang', + 'dojo/Deferred' ], function ( - declare, - lang + declare, lang, Deferred ) { return declare(null, { + /** + * Mixes in this apps properties with the passed arguments + * @param {Object} args The properties to mixin + * @return {undefined} + */ + constructor: function (args) { + lang.mixin(this, args); + }, + + /** + * A method run before anything else, can be inherited by mixins to + * load and process the config sync or async + * @return {undefined | Deferred} If the operation is async it should return + * a deferred, otherwise it should return the value of `this.inherited(arguments)` + */ + loadConfig: function () { + return this.inherited(arguments); + }, + /** + * A method run after the config is loaded but before startup is called + * on mixins + * @return {undefined | Deferred} If the operation is async it should return + * a deferred, otherwise it should return the value of `this.inherited(arguments)` + */ + postConfig: function () { + return this.inherited(arguments); + }, + /** + * Start the application mixin chain, once the + * startupDeferred is resolved + * @return {undefined} + */ startup: function () { - this.inherited(arguments); - // in _ConfigMixin - this.initConfigAsync().then( - lang.hitch(this, 'initConfigSuccess'), - lang.hitch(this, 'initConfigError') - ); + // cache the inherited + var inherited = this.getInherited(arguments); + + // load config and process it + this.startupDeferred = this.executeSync([ + this.loadConfig, + this.postConfig + ]); + + // wait for any loading to complete + this.startupDeferred.then(lang.hitch(this, function () { + + // start up the mixin chain + inherited.apply(this); + })); + }, + /** + * executes an array of asynchronous methods synchronously + * @param {Array} methods The array of functions to execute + * @param {Deferred} deferred A deferred created inside the method and resolved once all methods are complete + * @return {Deferred} A deferred resolved once all methods are executed + */ + executeSync: function (methods, deferred) { + deferred = deferred || new Deferred(); + + // if our list is empty, resolve the deferred and quit + if (!methods || !methods.length) { + deferred.resolve(); + return deferred; + } + + // execute and remove the method from the list + var result = lang.hitch(this, methods.splice(0, 1)[0])(); + + // execute our next function once this one completes + if (result) { + result.then(lang.hitch(this, 'executeSync', methods, deferred)); + } else { + this.executeSync(methods, deferred); + } + return deferred; + }, //centralized error handler handleError: function (options) { if (this.config.isDebug) { - if (typeof (console) === 'object') { + if (typeof(console) === 'object') { for (var option in options) { if (options.hasOwnProperty(option)) { console.log(option, options[option]); @@ -32,6 +100,26 @@ define([ // add growler here? return; } + }, + + mixinDeep: function (dest, source) { + //Recursively mix the properties of two objects + var empty = {}; + for (var name in source) { + if (!(name in dest) || (dest[name] !== source[name] && (!(name in empty) || empty[name] !== source[name]))) { + try { + if (source[name].constructor === Object) { + dest[name] = this.mixinDeep(dest[name], source[name]); + } else { + dest[name] = source[name]; + } + } catch (e) { + // Property in destination object not set. Create it and set its value. + dest[name] = source[name]; + } + } + } + return dest; } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/viewer/_LayoutMixin.js b/viewer/js/viewer/_LayoutMixin.js index 8438591e6..4940098d1 100644 --- a/viewer/js/viewer/_LayoutMixin.js +++ b/viewer/js/viewer/_LayoutMixin.js @@ -11,6 +11,7 @@ define([ 'dojo/dom-class', 'dojo/dom-geometry', 'dojo/sniff', + 'dojo/Deferred', 'put-selector', @@ -33,6 +34,7 @@ define([ domClass, domGeom, has, + Deferred, put, @@ -61,21 +63,31 @@ define([ } }, collapseButtons: {}, + postConfig: function () { + this.layoutDeferred = new Deferred(); + return this.inherited(arguments); + }, - initLayout: function () { + startup: function () { this.config.layout = this.config.layout || {}; this.addTopics(); this.addTitles(); this.detectTouchDevices(); this.initPanes(); + + this.mapDeferred.then(lang.hitch(this, 'createPanes')); + + // resolve the layout deferred + this.layoutDeferred.resolve(); + this.inherited(arguments); }, // add topics for subscribing and publishing addTopics: function () { // toggle a sidebar pane topic.subscribe('viewer/togglePane', lang.hitch(this, function (args) { - this.togglePane(args.pane, args.show); + this.togglePane(args.pane, args.show, args.suppressEvent); })); // load a widget @@ -127,9 +139,10 @@ define([ initPanes: function () { var key, panes = this.config.panes || {}; + this.defaultPanes = lang.clone(this.panes); for (key in this.panes) { - if (this.panes.hasOwnProperty(key)) { - panes[key] = lang.mixin(this.panes[key], panes[key]); + if (this.defaultPanes.hasOwnProperty(key)) { + panes[key] = lang.mixin(this.defaultPanes[key], panes[key]); } } @@ -166,11 +179,11 @@ define([ var key, panes = this.config.panes || {}; for (key in this.panes) { - if (this.panes.hasOwnProperty(key)) { - panes[key] = lang.mixin(this.panes[key], panes[key]); + if (this.defaultPanes.hasOwnProperty(key)) { + panes[key] = lang.mixin(this.defaultPanes[key], panes[key]); } } - // where to place the buttons + // where to place the buttons // either the center map pane or the outer pane? this.collapseButtonsPane = this.config.collapseButtonsPane || 'outer'; @@ -178,7 +191,7 @@ define([ if (panes.hasOwnProperty(key)) { if (panes[key].collapsible) { this.collapseButtons[key] = put(this.panes[this.collapseButtonsPane].domNode, 'div.sidebarCollapseButton.sidebar' + key + 'CollapseButton.sidebarCollapseButton' + ((key === 'bottom' || key === 'top') ? 'Vert' : 'Horz') + ' div.dijitIcon.button.close').parentNode; - on(this.collapseButtons[key], 'click', lang.hitch(this, 'togglePane', key)); + on(this.collapseButtons[key], 'click', lang.hitch(this, 'togglePane', key, null, false)); this.positionSideBarToggle(key); if (this.collapseButtonsPane === 'outer') { var splitter = this.panes[key]._splitterWidget; @@ -187,9 +200,9 @@ define([ aspect.after(splitter, '_stopDrag', lang.hitch(this, '_splitterStopDrag', key)); } } - if (panes[key].open !== undefined) { - this.togglePane(key, panes[key].open); - } + } + if (panes[key].open !== undefined) { + this.togglePane(key, panes[key].open, true); } if (key !== 'center' && this.panes[key]._splitterWidget) { domClass.add(this.map.root.parentNode, 'pane' + key); @@ -212,20 +225,49 @@ define([ this.resizeLayout(); }, - togglePane: function (id, show) { + togglePane: function (id, show, suppressEvent) { if (!this.panes[id]) { return; } var domNode = this.panes[id].domNode; if (domNode) { - var disp = (show && typeof (show) === 'string') ? show : (domStyle.get(domNode, 'display') === 'none') ? 'block' : 'none'; - domStyle.set(domNode, 'display', disp); - if (this.panes[id]._splitterWidget) { // show/hide the splitter, if found - domStyle.set(this.panes[id]._splitterWidget.domNode, 'display', disp); + var oldDisp = domStyle.get(domNode, 'display'); + var newDisp; + + if (typeof(show) === 'string' && (show === 'none' || show === 'block')) { + // Set (CSS Display Property) + newDisp = show; + } else if (typeof(show) === 'boolean') { + // Set (boolean) + newDisp = (show) ? 'block' : 'none'; + } else if (show === undefined || show === null) { + // Toggle + newDisp = (oldDisp === 'none') ? 'block' : 'none'; + } else { + this.handleError({ + source: '_LayoutMixin', + error: 'Invalid type passed as "show" property of "togglePane" function : ' + typeof(show) + }); + return; } - this.positionSideBarToggle(id); - if (this.panes.outer) { - this.panes.outer.resize(); + show = (newDisp === 'block'); + + if (newDisp !== oldDisp) { + domStyle.set(domNode, 'display', newDisp); + if (this.panes[id]._splitterWidget) { // show/hide the splitter, if found + domStyle.set(this.panes[id]._splitterWidget.domNode, 'display', newDisp); + } + this.positionSideBarToggle(id); + if (this.panes.outer) { + this.panes.outer.resize(); + } + + if (!suppressEvent) { + topic.publish('viewer/onTogglePane', { + pane: id, + show: show + }); + } } } }, @@ -297,4 +339,4 @@ define([ } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/viewer/_MapMixin.js b/viewer/js/viewer/_MapMixin.js index 57cd22244..71e658d0a 100644 --- a/viewer/js/viewer/_MapMixin.js +++ b/viewer/js/viewer/_MapMixin.js @@ -23,31 +23,75 @@ define([ return declare(null, { + postConfig: function () { + this.mapDeferred = new Deferred(); + return this.inherited(arguments); + }, + + startup: function () { + this.inherited(arguments); + this.layoutDeferred.then(lang.hitch(this, 'initMapAsync')); + }, + initMapAsync: function () { var returnDeferred = new Deferred(); var returnWarnings = []; - this._createMap(); + this.createMap(returnWarnings).then( + lang.hitch(this, '_createMapResult', returnDeferred, returnWarnings) + ); + returnDeferred.then(lang.hitch(this, 'initMapComplete')); + return returnDeferred; + }, - if (this.config.mapOptions.basemap) { - this.map.on('load', lang.hitch(this, '_initLayers', returnWarnings)); + createMap: function (returnWarnings) { + + // mixins override the default createMap method and return a deferred + var result = this.inherited(arguments); + if (result) { + return result; + } + + // otherwise we can create the map + var mapDeferred = new Deferred(), + container = dom.byId(this.config.layout.map) || 'mapCenter'; + + this.map = new Map(container, this.config.mapOptions); + + // let some other mixins modify or add map items async + var wait = this.inherited(arguments); + if (wait) { + wait.then(function (warnings) { + if (warnings) { + returnWarnings = returnWarnings.concat(warnings); + } + mapDeferred.resolve(returnWarnings); + }); } else { - this._initLayers(returnWarnings); + mapDeferred.resolve(returnWarnings); } + return mapDeferred; + }, - if (this.config.operationalLayers && this.config.operationalLayers.length > 0) { - on.once(this.map, 'layers-add-result', lang.hitch(this, '_onLayersAddResult', returnDeferred, returnWarnings)); + _createMapResult: function (returnDeferred, returnWarnings) { + if (this.map) { + if (!this.config.webMapId && this.config.mapOptions && this.config.mapOptions.basemap) { + this.map.on('load', lang.hitch(this, '_initLayers', returnWarnings)); + } else { + this._initLayers(returnWarnings); + } + + if (this.config.operationalLayers && this.config.operationalLayers.length > 0) { + on.once(this.map, 'layers-add-result', lang.hitch(this, '_onLayersAddResult', returnDeferred, returnWarnings)); + } else { + returnDeferred.resolve(returnWarnings); + } } else { returnDeferred.resolve(returnWarnings); } return returnDeferred; }, - _createMap: function () { - var container = dom.byId(this.config.layout.map) || 'mapCenter'; - this.map = new Map(container, this.config.mapOptions); - }, - _onLayersAddResult: function (returnDeferred, returnWarnings, lyrsResult) { array.forEach(lyrsResult.layers, function (addedLayer) { if (addedLayer.success !== true) { @@ -60,34 +104,36 @@ define([ _initLayers: function (returnWarnings) { this.layers = []; var layerTypes = { - csv: 'CSV', - dataadapter: 'DataAdapterFeature', //untested - dynamic: 'ArcGISDynamicMapService', - feature: 'Feature', - georss: 'GeoRSS', - image: 'ArcGISImageService', - imagevector: 'ArcGISImageServiceVector', - kml: 'KML', - label: 'Label', //untested - mapimage: 'MapImage', //untested - osm: 'OpenStreetMap', - raster: 'Raster', - stream: 'Stream', - tiled: 'ArcGISTiledMapService', - vectortile: 'VectorTile', - webtiled: 'WebTiled', - wfs: 'WFS', - wms: 'WMS', - wmts: 'WMTS' //untested + csv: 'esri/layers/CSVLayer', + dataadapter: 'esri/layers/DataAdapterFeatureLayer', //untested + dynamic: 'esri/layers/ArcGISDynamicMapServiceLayer', + feature: 'esri/layers/FeatureLayer', + georss: 'esri/layers/GeoRSSLayer', + image: 'esri/layers/ArcGISImageServiceLayer', + imagevector: 'esri/layers/ArcGISImageServiceVectorLayer', + kml: 'esri/layers/KMLLayer', + label: 'esri/layers/LabelLayer', //untested + mapimage: 'esri/layers/MapImageLayer', //untested + osm: 'esri/layers/OpenStreetMapLayer', + raster: 'esri/layers/RasterLayer', + stream: 'esri/layers/StreamLayer', + tiled: 'esri/layers/ArcGISTiledMapServiceLayer', + vectortile: 'esri/layers/VectorTileLayer', + webtiled: 'esri/layers/WebTiledLayer', + wfs: 'esri/layers/WFSLayer', + wms: 'esri/layers/WMSLayer', + wmts: 'esri/layers/WMTSLayer' //untested }; + // add any user-defined layer types such as https://github.com/Esri/geojson-layer-js + layerTypes = lang.mixin(layerTypes, this.config.layerTypes || {}); // loading all the required modules first ensures the layer order is maintained var modules = []; array.forEach(this.config.operationalLayers, function (layer) { var type = layerTypes[layer.type]; if (type) { - modules.push('esri/layers/' + type + 'Layer'); + modules.push(type); } else { - returnWarnings.push('Layer type "' + layer.type + '"" isnot supported: '); + returnWarnings.push('Layer type "' + layer.type + '" is not supported: '); } }, this); @@ -95,7 +141,7 @@ define([ array.forEach(this.config.operationalLayers, function (layer) { var type = layerTypes[layer.type]; if (type) { - require(['esri/layers/' + type + 'Layer'], lang.hitch(this, '_initLayer', layer)); + require([type], lang.hitch(this, '_initLayer', layer)); } }, this); this.map.addLayers(this.layers); @@ -169,18 +215,19 @@ define([ }); } - this.map.on('resize', function (evt) { - var pnt = evt.target.extent.getCenter(); - setTimeout(function () { - evt.target.centerAt(pnt); - }, 100); - }); + if (this.map) { - // in _LayoutsMixin - this.createPanes(); + this.map.on('resize', function (evt) { + var pnt = evt.target.extent.getCenter(); + setTimeout(function () { + evt.target.centerAt(pnt); + }, 100); + }); + + // resolve the map deferred + this.mapDeferred.resolve(this.map); + } - // in _WidgetsMixin - this.initWidgets(); }, initMapError: function (err) { @@ -204,4 +251,4 @@ define([ } } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/viewer/_WebMapMixin.js b/viewer/js/viewer/_WebMapMixin.js new file mode 100644 index 000000000..106a302b2 --- /dev/null +++ b/viewer/js/viewer/_WebMapMixin.js @@ -0,0 +1,233 @@ +define([ + 'dojo/_base/declare', + 'dojo/_base/lang', + 'dojo/_base/array', + 'dojo/dom', + + 'esri/arcgis/utils', + 'esri/units' + +], function ( + declare, + lang, + array, + dom, + + arcgisUtils, + units +) { + return declare(null, { + startup: function () { + this.inherited(arguments); + // this.mapDeferred.then(lang.hitch(this, '_initWebMap')); + }, + + createMap: function () { + var webMapOptions = this.config.webMapOptions || {}; + if (!webMapOptions.mapOptions && this.config.mapOptions) { + webMapOptions.mapOptions = this.config.mapOptions; + } + var container = dom.byId(this.config.layout.map) || 'mapCenter'; + + var mapDeferred = arcgisUtils.createMap(this.config.webMapId, container, webMapOptions); + mapDeferred.then(lang.hitch(this, function (response) { + this.webMap = { + clickEventHandle: response.clickEventHandle, + clickEventListener: response.clickEventListener, + itemInfo: response.itemInfo + }; + this.map = response.map; + + // get the layerInfos from the webmap + this._initWebMapLayerInfos(response); + + // add any widgets included in the webmap + this._initWebMapWidgets(response); + })); + return mapDeferred; + + }, + + _initWebMapLayerInfos: function (response) { + if (this.config.layerControlLayerInfos) { + // get the layerInfos for the layerControl widget from the config + this.layerControlLayerInfos = this.config.layerControlLayerInfos; + + } else if (response.itemInfo && response.itemInfo.itemData) { + // get the layerInfos for the layerControl widget from the webmap + + // https://developers.arcgis.com/web-map-specification/objects/operationalLayers/ + var layerTypes = { + 'CSV': 'csv', + 'ArcGISMapServiceLayer': 'dynamic', + 'ArcGISFeatureLayer': 'feature', + 'GeoRSSLayer': 'georss', + 'ArcGISImageServiceLayer': 'image', + 'esri/layers/ArcGISImageServiceVectorLayer': 'imagevector', + 'KML': 'kml', + 'ArcGISStreamLayer': 'stream', + 'ArcGISTiledMapServiceLayer': 'tiled', + 'VectorTileLayer': 'vectortile', + 'WebTiledLayer': 'webtiled', + 'WMS': 'wms' + + /* + Are these supported in Webmaps? + + 'dataadapter': 'esri/layer/DataAdapterFeatureLayer', //untested + label: 'esri/layers/LabelLayer', //untested + mapimage: 'esri/layers/MapImageLayer', //untested + osm: 'esri/layers/OpenStreetMapLayer', + raster: 'esri/layers/RasterLayer', + 'wfs': 'esri/layers/WFSLayer', + 'esri/layers/WMTS': 'wmts' + */ + }; + + var operationalLayers = response.itemInfo.itemData.operationalLayers; + array.forEach(operationalLayers, lang.hitch(this, function (layer) { + var layerType = layerTypes[layer.layerType]; + if (layerType) { + this.layerControlLayerInfos.push({ + layer: layer.layerObject, + type: layerType, + title: layer.title + }); + } + })); + } + + if (this.config.legendLayerInfos) { + // get the layerInfos for the legend widget from the config + this.legendLayerInfos = this.config.legendLayerInfos; + } else { + // get the layerInfos for the legend widget from the webmap + this.legendLayerInfos = arcgisUtils.getLegendLayers(response); + } + }, + + _initWebMapWidgets: function (response) { + if (!response.itemInfo || !response.itemInfo.itemData) { + return; + } + + // existing widgets if any + var widgets = this.config.widgets; + + var bookmarks = response.itemInfo.itemData.bookmarks; + if (bookmarks && bookmarks.length > 0) { + widgets.bookmarks = this.mixinDeep({ + include: true, + id: 'bookmarks', + type: 'titlePane', + path: 'gis/dijit/Bookmarks', + title: 'Bookmarks', + open: false, + position: 999, + options: { + map: true, + editable: false, + bookmarks: bookmarks + } + }, widgets.bookmarks || {}); + } + + if (response.itemInfo.itemData.applicationProperties) { + // https://developers.arcgis.com/web-map-specification/objects/viewing/ + var viewing = response.itemInfo.itemData.applicationProperties.viewing; + // possible widgets: basemapGallery, measure, routing, search + + if (viewing.basemapGallery && viewing.basemapGallery.enabled) { + if (!widgets.basemaps || !widgets.basemaps.include) { // basemap gallery widget and basemaps widget cannot co-exist + widgets.basemapGallery = this.mixinDeep({ + include: true, + id: 'basemapGallery', + type: 'domNode', + path: 'gis/dijit/BasemapGallery', + srcNodeRef: 'basemapsDijit', + options: { + map: true + } + }, widgets.basemapGallery || {}); + } + } + + if (viewing.routing && viewing.routing.enabled) { + widgets.directions = this.mixinDeep({ + include: true, + id: 'directions', + type: 'titlePane', + path: 'gis/dijit/Directions', + title: 'Directions', + open: false, + position: 999, + options: { + map: true, + mapRightClickMenu: true, + options: { + routeTaskUrl: 'https://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer/Route', + routeParams: { + directionsLanguage: 'en-US', + directionsLengthUnits: units.MILES + }, + active: false //for 3.12, starts active by default, which we dont want as it interfears with mapClickMode + } + } + }, widgets.directions || {}); + } + + if (viewing.measure && viewing.measure.enabled) { + widgets.measure = this.mixinDeep({ + include: true, + id: 'measurement', + type: 'titlePane', + path: 'gis/dijit/Measurement', + title: 'Measurement', + open: false, + position: 999, + options: { + map: true, + mapClickMode: true, + defaultAreaUnit: units.SQUARE_MILES, + defaultLengthUnit: units.MILES + } + }, widgets.measure || {}); + } + + if (viewing.search && viewing.search.enabled) { + widgets.search = this.mixinDeep({ + include: true, + type: 'domNode', + path: 'esri/dijit/Search', + srcNodeRef: 'geocoderButton', + options: { + map: true, + visible: true, + enableButtonMode: true, + expanded: true, + disablePlaceFinder: viewing.search.disablePlaceFinder, + hintText: viewing.search.hintText, + layers: viewing.search.layers + } + }, widgets.search || {}); + } + } + + // https://developers.arcgis.com/web-map-specification/objects/widgets/ + if (response.itemInfo.itemData.widgets) { + var timeSlider = response.itemInfo.itemData.widgets.timeSlider; + if (timeSlider) { + widgets.timeSlider = this.mixinDeep({ + include: true, + type: 'domNode', + path: 'esri/dijit/TimeSlider', + srcNodeRef: 'geocoderButton', + options: lang.mixin({ + map: true + }, timeSlider) + }, widgets.slider || {}); + } + } + } + }); +}); diff --git a/viewer/js/viewer/_WidgetsMixin.js b/viewer/js/viewer/_WidgetsMixin.js index 78dc52f41..8ab99d897 100644 --- a/viewer/js/viewer/_WidgetsMixin.js +++ b/viewer/js/viewer/_WidgetsMixin.js @@ -2,6 +2,8 @@ define([ 'dojo/_base/declare', 'dojo/_base/array', 'dojo/_base/lang', + 'dojo/promise/all', + 'dojo/Deferred', 'put-selector', @@ -15,6 +17,8 @@ define([ declare, array, lang, + promiseAll, + Deferred, put, @@ -32,16 +36,54 @@ define([ identifyLayerInfos: [], layerControlLayerInfos: [], - initWidgets: function () { + widgets: {}, + widgetTypes: ['titlePane', 'contentPane', 'floating', 'domNode', 'invisible', 'map', 'layer', 'layout', 'loading'], + postConfig: function (wait) { + + var waitDeferred; + if (wait) { + waitDeferred = new Deferred(); + + wait.then(lang.hitch(this, function () { + // load loading widgets + promiseAll(this.createWidgets(['loading'])).then(waitDeferred.resolve); + })); + } else { + var deferreds = this.createWidgets(['loading']); + if (deferreds && deferreds.length) { + waitDeferred = promiseAll(deferreds); + } + } + + return this.inherited(arguments) || waitDeferred; + }, + startup: function () { + this.inherited(arguments); + if (this.mapDeferred) { + this.mapDeferred.then(lang.hitch(this, 'createWidgets', ['map', 'layer'])); + } + if (this.layoutDeferred) { + promiseAll([this.mapDeferred, this.layoutDeferred]) + .then(lang.hitch(this, 'createWidgets', null)); + } + }, + + createWidgets: function (widgetTypes) { var widgets = [], paneWidgets; + widgetTypes = widgetTypes || this.widgetTypes; for (var key in this.config.widgets) { if (this.config.widgets.hasOwnProperty(key)) { var widget = lang.clone(this.config.widgets[key]); - if (widget.include) { - widget.position = (typeof (widget.position) !== 'undefined') ? widget.position : 10000; + widget.widgetKey = widget.widgetKey || widget.id || key; + if (widget.include && (!this.widgets[widget.widgetKey]) && (array.indexOf(widgetTypes, widget.type) >= 0)) { + widget.position = (typeof(widget.position) !== 'undefined') ? widget.position : 10000; + if ((widget.type === 'titlePane' || widget.type === 'contentPane') && !widget.placeAt) { + widget.placeAt = 'left'; + } widgets.push(widget); + this.widgets[key] = true; // will be replaced by actual widget once created } } } @@ -54,11 +96,14 @@ define([ } for (var pane in this.panes) { - if (this.panes.hasOwnProperty(pane) && (pane !== 'outer' || pane !== 'center')) { + if (this.panes.hasOwnProperty(pane) && pane !== 'outer' && pane !== 'center') { paneWidgets = getPaneWidgets(pane); paneWidgets.sort(function (a, b) { return a.position - b.position; }); + if (paneWidgets.length > 0 && paneWidgets[0].position !== 0) { + paneWidgets[0].position = 0; + } array.forEach(paneWidgets, function (paneWidget, i) { this.widgetLoader(paneWidget, i); }, this); @@ -70,23 +115,29 @@ define([ paneWidgets.sort(function (a, b) { return a.position - b.position; }); - + var deferreds = []; array.forEach(paneWidgets, function (paneWidget, i) { - this.widgetLoader(paneWidget, i); + var def = this.widgetLoader(paneWidget, i); + if (def) { + deferreds.push(def); + } }, this); + return deferreds; }, widgetLoader: function (widgetConfig, position) { var parentId, pnl; + var widgetTypes = this.widgetTypes; + // add any user-defined widget types + widgetTypes = widgetTypes.concat(this.config.widgetTypes || []); // only proceed for valid widget types - var widgetTypes = ['titlePane', 'contentPane', 'floating', 'domNode', 'invisible', 'map']; if (array.indexOf(widgetTypes, widgetConfig.type) < 0) { this.handleError({ source: 'Controller', error: 'Widget type "' + widgetConfig.type + '" (' + widgetConfig.title + ') at position ' + position + ' is not supported.' }); - return; + return null; } if (position) { @@ -94,8 +145,8 @@ define([ } // build a titlePane or floating widget as the parent - if ((widgetConfig.type === 'titlePane' || widgetConfig.type === 'contentPane' || widgetConfig.type === 'floating') && (widgetConfig.id && widgetConfig.id.length > 0)) { - parentId = widgetConfig.id + '_parent'; + if ((widgetConfig.type === 'titlePane' || widgetConfig.type === 'contentPane' || widgetConfig.type === 'floating')) { + parentId = widgetConfig.widgetKey + '_parent'; if (widgetConfig.type === 'titlePane') { pnl = this._createTitlePaneWidget(parentId, widgetConfig); } else if (widgetConfig.type === 'contentPane') { @@ -104,18 +155,52 @@ define([ pnl = this._createFloatingWidget(parentId, widgetConfig); } widgetConfig.parentWidget = pnl; + this._showWidgetLoader(pnl); } // 2 ways to use require to accommodate widgets that may have an optional separate configuration file - if (typeof (widgetConfig.options) === 'string') { - require([widgetConfig.options, widgetConfig.path], lang.hitch(this, 'createWidget', widgetConfig)); + var deferred = new Deferred(); + if (typeof(widgetConfig.options) === 'string') { + require([widgetConfig.options, widgetConfig.path], lang.hitch(this, function (options, WidgetClass) { + deferred.resolve(); + this.createWidget(widgetConfig, options, WidgetClass); + })); } else { - require([widgetConfig.path], lang.hitch(this, 'createWidget', widgetConfig, widgetConfig.options)); + require([widgetConfig.path], lang.hitch(this, function (WidgetClass) { + deferred.resolve(); + this.createWidget(widgetConfig, widgetConfig.options, WidgetClass); + })); } + return deferred; }, createWidget: function (widgetConfig, options, WidgetClass) { + var key = widgetConfig.widgetKey; + if (!key) { + return; + } + // set any additional options + options = this._setWidgetOptions(widgetConfig, options); + + // create the widget + var pnl = options.parentWidget; + var widgets = this.widgets; + if ((widgetConfig.type === 'titlePane' || widgetConfig.type === 'contentPane' || widgetConfig.type === 'floating')) { + widgets[key] = new WidgetClass(options, put('div')).placeAt(pnl.containerNode); + } else if (widgetConfig.type === 'domNode') { + widgets[key] = new WidgetClass(options, widgetConfig.srcNodeRef); + } else { + widgets[key] = new WidgetClass(options); + } + // start up the widget + if (widgets[key] && widgets[key].startup && !widgets[key]._started) { + widgets[key].startup(); + } + this._hideWidgetLoader(pnl); + }, + + _setWidgetOptions: function (widgetConfig, options) { if (widgetConfig.id) { options.id = widgetConfig.id + '_widget'; } @@ -151,20 +236,20 @@ define([ if (options.identifyLayerInfos) { options.layerInfos = this.identifyLayerInfos; } + return options; + }, - // create the widget - var pnl = options.parentWidget; - if ((widgetConfig.type === 'titlePane' || widgetConfig.type === 'contentPane' || widgetConfig.type === 'floating')) { - this[widgetConfig.id] = new WidgetClass(options, put('div')).placeAt(pnl.containerNode); - } else if (widgetConfig.type === 'domNode') { - this[widgetConfig.id] = new WidgetClass(options, widgetConfig.srcNodeRef); - } else { - this[widgetConfig.id] = new WidgetClass(options); + _showWidgetLoader: function (pnl) { + if (pnl && pnl.containerNode) { + pnl.loadingNode = put(pnl.containerNode, 'div.widgetLoader i.fa.fa-spinner.fa-pulse.fa-fw').parentNode; } + }, - // start up the widget - if (this[widgetConfig.id] && this[widgetConfig.id].startup && !this[widgetConfig.id]._started) { - this[widgetConfig.id].startup(); + _hideWidgetLoader: function (pnl) { + if (pnl && pnl.loadingNode) { + require(['dojo/domReady!'], function () { + put(pnl.loadingNode, '!'); + }); } }, @@ -172,6 +257,7 @@ define([ var tp, options = lang.mixin({ title: widgetConfig.title || 'Widget', + iconClass: widgetConfig.iconClass, open: widgetConfig.open || false, canFloat: widgetConfig.canFloat || false, resizable: widgetConfig.resizable || false @@ -180,7 +266,7 @@ define([ options.id = parentId; } var placeAt = widgetConfig.placeAt; - if (typeof (placeAt) === 'string') { + if (typeof(placeAt) === 'string') { placeAt = this.panes[placeAt]; } if (!placeAt) { @@ -220,8 +306,8 @@ define([ } var placeAt = widgetConfig.placeAt; if (!placeAt) { - placeAt = this.panes.sidebar; - } else if (typeof (placeAt) === 'string') { + placeAt = this.panes.left; + } else if (typeof(placeAt) === 'string') { placeAt = this.panes[placeAt]; } if (placeAt) { @@ -232,4 +318,4 @@ define([ } }); -}); \ No newline at end of file +}); diff --git a/viewer/js/viewer/templates/leftContent.html b/viewer/js/viewer/templates/leftContent.html deleted file mode 100644 index e69de29bb..000000000 diff --git a/viewer/proxy/PROXY_README.md b/viewer/proxy/PROXY_README.md deleted file mode 100755 index 371a189b3..000000000 --- a/viewer/proxy/PROXY_README.md +++ /dev/null @@ -1,96 +0,0 @@ -DotNet Proxy File -================= - -A .NET proxy that handles support for -* Accessing cross domain resources -* Requests that exceed 2048 characters -* Accessing resources secured with token based authentication. -* [OAuth 2.0 app logins](https://developers.arcgis.com/en/authentication). -* Enabling logging -* Both resource and referer based rate limiting - -##Instructions - -* Download and unzip the .zip file or clone the repository. You can download [a released version](https://github.com/Esri/resource-proxy/releases) (recommended) or the [most recent daily build](https://github.com/Esri/resource-proxy/archive/master.zip). -* Install the contents of the DotNet folder as a .NET Web Application, specifying a .NET 4.0 application pool or later -* Test that the proxy is able to forward requests directly in the browser using: -``` -http://[yourmachine]/DotNet/proxy.ashx?http://services.arcgisonline.com/ArcGIS/rest/services/?f=pjson -``` -* Edit the proxy.config file in a text editor to set up your proxy configuration settings. -* Update your application to use the proxy for the specified services. In this JavaScript example requests to route.arcgis.com will utilize the proxy. - -``` - urlUtils.addProxyRule({ - urlPrefix: "route.arcgis.com", - proxyUrl: "http://[yourmachine]/proxy/proxy.ashx" - }); -``` -* Security tip: By default, the proxy.config allows any referrer. To lock this down, replace the ```*``` in the ```allowedReferers``` property with your own application URLs. - -##Proxy Configuration Settings - -* Use the ProxyConfig tag to specify the following proxy level settings. - * **mustMatch="true"** : When true only the sites listed using serverUrl will be proxied. Set to false to proxy any site, which can be useful in testing. However, we recommend setting it to "true" for production sites. - * **allowedReferers="http://server.com/app1,http://server.com/app2"** : A comma-separated list of referer URLs. Only requests coming from referers in the list will be proxied. -* Add a new \ entry for each service that will use the proxy. The proxy.config allows you to use the serverUrl tag to specify one or more ArcGIS Server services that the proxy will forward requests to. The serverUrl tag has the following attributes: - * **url**: Location of the ArcGIS Server service (or other URL) to proxy. Specify either the specific URL or the root (in which case you should set matchAll="false"). - * **matchAll="true"**: When true all requests that begin with the specified URL are forwarded. Otherwise, the URL requested must match exactly. - * **username**: Username to use when requesting a token - if needed for ArcGIS Server token based authentication. - * **password**: Password to use when requesting a token - if needed for ArcGIS Server token based authentication. - * **clientId**. Used with clientSecret for OAuth authentication to obtain a token - if needed for OAuth 2.0 authentication. **NOTE**: If used to access hosted services, the service(s) must be owned by the user accessing it, (with the exception of credit-based esri services, e.g. routing, geoenrichment, etc.) - * **clientSecret**: Used with clientId for OAuth authentication to obtain a token - if needed for OAuth 2.0 authentication. - * **oauth2Endpoint**: When using OAuth 2.0 authentication specify the portal specific OAuth 2.0 authentication endpoint. The default value is https://www.arcgis.com/sharing/oauth2/. - * **accessToken**: OAuth2 access token to use instead of on-demand access-token generation using clientId & clientSecret. - * **rateLimit**: The maximum number of requests with a particular referer over the specified **rateLimitPeriod**. - * **rateLimitPeriod**: The time period (in minutes) within which the specified number of requests (rate_limit) sent with a particular referer will be tracked. The default value is 60 (one hour). - -Note: Refresh the proxy application after updates to the proxy.config have been made. - -Example of proxy using application credentials and limiting requests to 10/minute -``` - - -``` -Example of a tag for a resource which does not require authentication -``` - - -``` -Note: You may have to refresh the proxy application after updates to the proxy.config have been made. - -##Folders and Files - -The proxy consists of the following files: -* proxy.config: This file contains the configuration settings for the proxy. This is where you will define all the resources that will use the proxy. After updating this file you might need to refresh the proxy application using IIS tools in order for the changes to take effect. -* **Important note:** In order to keep your credentials safe, ensure that your web server will not display the text inside your proxy.config in the browser (ie: http://[yourmachine]/proxy/proxy.config). -* proxy.ashx: The actual proxy application. In most cases you will not need to modify this file. -* web.config: An XML file that stores ASP.NET configuration data. Use this file to configure logging for the proxy. By default the proxy will write log messages to a file named auth_proxy.log located in 'C:\Temp\Shared\proxy_logs'. Note that the folder location needs to exist in order for the log file to be successfully created. -##Requirements - -* ASP.NET 4.0 or greater (4.5 is required on Windows 8/Server 2012, see [this article] (http://www.iis.net/learn/get-started/whats-new-in-iis-8/iis-80-using-aspnet-35-and-aspnet-45) for more information) - -##Issues - -Found a bug or want to request a new feature? Let us know by submitting an issue. - -##Contributing - -All contributions are welcome. - -##Licensing - -Copyright 2014 Esri - -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 specific language governing permissions and limitations under the license. diff --git a/viewer/proxy/Web.config b/viewer/proxy/Web.config deleted file mode 100755 index 8d1a40128..000000000 --- a/viewer/proxy/Web.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/viewer/proxy/proxy.ashx b/viewer/proxy/proxy.ashx deleted file mode 100755 index 6446e630d..000000000 --- a/viewer/proxy/proxy.ashx +++ /dev/null @@ -1,834 +0,0 @@ -<%@ WebHandler Language="C#" Class="proxy" %> - -/* - * DotNet proxy client. - * - * Version 1.1 beta - * See https://github.com/Esri/resource-proxy for more information. - * - */ - -#define TRACE -using System; -using System.IO; -using System.Web; -using System.Xml.Serialization; -using System.Web.Caching; -using System.Collections.Concurrent; -using System.Diagnostics; - -public class proxy : IHttpHandler { - - class RateMeter { - double _rate; //internal rate is stored in requests per second - int _countCap; - double _count = 0; - DateTime _lastUpdate = DateTime.Now; - - public RateMeter(int rate_limit, int rate_limit_period) { - _rate = (double) rate_limit / rate_limit_period / 60; - _countCap = rate_limit; - } - - //called when rate-limited endpoint is invoked - public bool click() { - TimeSpan ts = DateTime.Now - _lastUpdate; - _lastUpdate = DateTime.Now; - //assuming uniform distribution of requests over time, - //reducing the counter according to # of seconds passed - //since last invocation - _count = Math.Max(0, _count - ts.TotalSeconds * _rate); - if (_count <= _countCap) { - //good to proceed - _count++; - return true; - } - return false; - } - - public bool canBeCleaned() { - TimeSpan ts = DateTime.Now - _lastUpdate; - return _count - ts.TotalSeconds * _rate <= 0; - } - } - - private static string PROXY_REFERER = "http://localhost/proxy/proxy.ashx"; - private static string DEFAULT_OAUTH = "https://www.arcgis.com/sharing/oauth2/"; - private static int CLEAN_RATEMAP_AFTER = 10000; //clean the rateMap every xxxx requests - - private static Object _rateMapLock = new Object(); - - public void ProcessRequest(HttpContext context) { - HttpResponse response = context.Response; - if (context.Request.Url.Query.Length < 1) - { - string errorMsg = "No URL specified"; - log(TraceLevel.Error, errorMsg); - sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.BadRequest); - return; - } - - string uri = context.Request.Url.Query.Substring(1); - - //if url is encoded, decode it. - if (uri.StartsWith("http%3a%2f%2f", StringComparison.InvariantCultureIgnoreCase) || uri.StartsWith("https%3a%2f%2f", StringComparison.InvariantCultureIgnoreCase)) - uri = HttpUtility.UrlDecode(uri); - - log(TraceLevel.Info, uri); - ServerUrl serverUrl; - bool passThrough = false; - try { - serverUrl = getConfig().GetConfigServerUrl(uri); - passThrough = serverUrl == null; - } - //if XML couldn't be parsed - catch (InvalidOperationException ex) { - - string errorMsg = ex.InnerException.Message + " " + uri; - log(TraceLevel.Error, errorMsg); - sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.InternalServerError); - return; - } - //if mustMatch was set to true and URL wasn't in the list - catch (ArgumentException ex) { - string errorMsg = ex.Message + " " + uri; - log(TraceLevel.Error, errorMsg); - sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.Forbidden); - return; - } - //use actual request header instead of a placeholder, if present - if (context.Request.Headers["referer"] != null) - PROXY_REFERER = context.Request.Headers["referer"]; - - //referer - //check against the list of referers if they have been specified in the proxy.config - String[] allowedReferersArray = ProxyConfig.GetAllowedReferersArray(); - if (allowedReferersArray != null && allowedReferersArray.Length > 0) - { - bool allowed = false; - string requestReferer = context.Request.Headers["referer"]; - - foreach (var referer in allowedReferersArray) - { - if ((allowedReferersArray.Length == 1) && referer == String.Empty) - break; - - if (requestReferer != null && requestReferer != String.Empty && (ProxyConfig.isUrlPrefixMatch(referer, requestReferer)) || referer == "*") - { - allowed = true; - break; - } - } - - if (!allowed) - { - string errorMsg = "Proxy is being used from an unsupported referer: " + context.Request.Headers["referer"]; - log(TraceLevel.Error, errorMsg); - sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.Forbidden); - return; - } - } - - //Throttling: checking the rate limit coming from particular client IP - if (!passThrough && serverUrl.RateLimit > -1) { - lock (_rateMapLock) - { - ConcurrentDictionary ratemap = (ConcurrentDictionary)context.Application["rateMap"]; - if (ratemap == null) - { - ratemap = new ConcurrentDictionary(); - context.Application["rateMap"] = ratemap; - context.Application["rateMap_cleanup_counter"] = 0; - } - string key = "[" + serverUrl.Url + "]x[" + context.Request.UserHostAddress + "]"; - RateMeter rate; - if (!ratemap.TryGetValue(key, out rate)) - { - rate = new RateMeter(serverUrl.RateLimit, serverUrl.RateLimitPeriod); - ratemap.TryAdd(key, rate); - } - if (!rate.click()) - { - log(TraceLevel.Warning, " Pair " + key + " is throttled to " + serverUrl.RateLimit + " requests per " + serverUrl.RateLimitPeriod + " minute(s). Come back later."); - sendErrorResponse(context.Response, "This is a metered resource, number of requests have exceeded the rate limit interval.", "Unable to proxy request for requested resource", System.Net.HttpStatusCode.PaymentRequired); - return; - } - - //making sure the rateMap gets periodically cleaned up so it does not grow uncontrollably - int cnt = (int)context.Application["rateMap_cleanup_counter"]; - cnt++; - if (cnt >= CLEAN_RATEMAP_AFTER) - { - cnt = 0; - cleanUpRatemap(ratemap); - } - context.Application["rateMap_cleanup_counter"] = cnt; - } - } - - //readying body (if any) of POST request - byte[] postBody = readRequestPostBody(context); - string post = System.Text.Encoding.UTF8.GetString(postBody); - - System.Net.NetworkCredential credentials = null; - string requestUri = uri; - bool hasClientToken = false; - string token = string.Empty; - string tokenParamName = null; - - if (!passThrough && serverUrl.Domain != null) - { - credentials = new System.Net.NetworkCredential(serverUrl.Username, serverUrl.Password, serverUrl.Domain); - } - else - { - //if token comes with client request, it takes precedence over token or credentials stored in configuration - hasClientToken = uri.Contains("?token=") || uri.Contains("&token=") || post.Contains("?token=") || post.Contains("&token="); - - if (!passThrough && !hasClientToken) - { - // Get new token and append to the request. - // But first, look up in the application scope, maybe it's already there: - token = (String)context.Application["token_for_" + serverUrl.Url]; - bool tokenIsInApplicationScope = !String.IsNullOrEmpty(token); - - //if still no token, let's see if there is an access token or if are credentials stored in configuration which we can use to obtain new token - if (!tokenIsInApplicationScope) - { - token = serverUrl.AccessToken; - if (String.IsNullOrEmpty(token)) - token = getNewTokenIfCredentialsAreSpecified(serverUrl, uri); - } - - if (!String.IsNullOrEmpty(token) && !tokenIsInApplicationScope) - { - //storing the token in Application scope, to do not waste time on requesting new one untill it expires or the app is restarted. - context.Application.Lock(); - context.Application["token_for_" + serverUrl.Url] = token; - context.Application.UnLock(); - } - } - - //name by which token parameter is passed (if url actually came from the list) - tokenParamName = serverUrl != null ? serverUrl.TokenParamName : null; - - if (String.IsNullOrEmpty(tokenParamName)) - tokenParamName = "token"; - - requestUri = addTokenToUri(uri, token, tokenParamName); - } - - - - //forwarding original request - System.Net.WebResponse serverResponse = null; - try { - serverResponse = forwardToServer(context, requestUri, postBody, credentials); - } catch (System.Net.WebException webExc) { - - string errorMsg = webExc.Message + " " + uri; - log(TraceLevel.Error, errorMsg); - - if (webExc.Response != null) - { - copyHeaders(webExc.Response as System.Net.HttpWebResponse, context.Response); - - using (Stream responseStream = webExc.Response.GetResponseStream()) - { - byte[] bytes = new byte[32768]; - int bytesRead = 0; - - while ((bytesRead = responseStream.Read(bytes, 0, bytes.Length)) > 0) - { - responseStream.Write(bytes, 0, bytesRead); - } - - context.Response.StatusCode = (int)(webExc.Response as System.Net.HttpWebResponse).StatusCode; - context.Response.OutputStream.Write(bytes, 0, bytes.Length); - } - } - else - { - System.Net.HttpStatusCode statusCode = System.Net.HttpStatusCode.InternalServerError; - sendErrorResponse(context.Response, null, errorMsg, statusCode); - } - return; - } - - if (passThrough || string.IsNullOrEmpty(token) || hasClientToken) - //if token is not required or provided by the client, just fetch the response as is: - fetchAndPassBackToClient(serverResponse, response, true); - else { - //credentials for secured service have come from configuration file: - //it means that the proxy is responsible for making sure they were properly applied: - - //first attempt to send the request: - bool tokenRequired = fetchAndPassBackToClient(serverResponse, response, false); - - - //checking if previously used token has expired and needs to be renewed - if (tokenRequired) { - log(TraceLevel.Info, "Renewing token and trying again."); - //server returned error - potential cause: token has expired. - //we'll do second attempt to call the server with renewed token: - token = getNewTokenIfCredentialsAreSpecified(serverUrl, uri); - serverResponse = forwardToServer(context, addTokenToUri(uri, token, tokenParamName), postBody); - - //storing the token in Application scope, to do not waste time on requesting new one untill it expires or the app is restarted. - context.Application.Lock(); - context.Application["token_for_" + serverUrl.Url] = token; - context.Application.UnLock(); - - fetchAndPassBackToClient(serverResponse, response, true); - } - } - response.End(); - } - - public bool IsReusable { - get { return true; } - } - -/** -* Private -*/ - private byte[] readRequestPostBody(HttpContext context) { - if (context.Request.InputStream.Length > 0) { - byte[] bytes = new byte[context.Request.InputStream.Length]; - context.Request.InputStream.Read(bytes, 0, (int)context.Request.InputStream.Length); - return bytes; - } - return new byte[0]; - } - - private System.Net.WebResponse forwardToServer(HttpContext context, string uri, byte[] postBody, System.Net.NetworkCredential credentials = null) - { - return - postBody.Length > 0? - doHTTPRequest(uri, postBody, "POST", context.Request.Headers["referer"], context.Request.ContentType, credentials): - doHTTPRequest(uri, context.Request.HttpMethod, credentials); - } - - /// - /// Attempts to copy all headers from the fromResponse to the the toResponse. - /// - /// The response that we are copying the headers from - /// The response that we are copying the headers to - private void copyHeaders(System.Net.WebResponse fromResponse, HttpResponse toResponse) - { - foreach (var headerKey in fromResponse.Headers.AllKeys) - { - switch (headerKey.ToLower()) - { - case "content-type": - case "transfer-encoding": - continue; - default: - toResponse.AddHeader(headerKey, fromResponse.Headers[headerKey]); - break; - } - } - toResponse.ContentType = fromResponse.ContentType; - } - - private bool fetchAndPassBackToClient(System.Net.WebResponse serverResponse, HttpResponse clientResponse, bool ignoreAuthenticationErrors) { - if (serverResponse != null) { - copyHeaders(serverResponse, clientResponse); - using (Stream byteStream = serverResponse.GetResponseStream()) { - // Text response - if (serverResponse.ContentType.Contains("text") || - serverResponse.ContentType.Contains("json") || - serverResponse.ContentType.Contains("xml")) { - using (StreamReader sr = new StreamReader(byteStream)) { - string strResponse = sr.ReadToEnd(); - if ( - !ignoreAuthenticationErrors - && strResponse.IndexOf("{\"error\":{") > -1 - && (strResponse.IndexOf("\"code\":498") > -1 || strResponse.IndexOf("\"code\":499") > -1) - ) - return true; - clientResponse.Write(strResponse); - } - } else { - // Binary response (image, lyr file, other binary file) - - // Tell client not to cache the image since it's dynamic - clientResponse.CacheControl = "no-cache"; - byte[] buffer = new byte[32768]; - int read; - while ((read = byteStream.Read(buffer, 0, buffer.Length)) > 0) - { - clientResponse.OutputStream.Write(buffer, 0, read); - } - clientResponse.OutputStream.Close(); - } - serverResponse.Close(); - } - } - return false; - } - - private System.Net.WebResponse doHTTPRequest(string uri, string method, System.Net.NetworkCredential credentials = null) - { - byte[] bytes = null; - String contentType = null; - log(TraceLevel.Info, "Sending request!"); - - if (method.Equals("POST")) - { - String[] uriArray = uri.Split('?'); - - if (uriArray.Length > 1) - { - contentType = "application/x-www-form-urlencoded"; - String queryString = uriArray[1]; - - bytes = System.Text.Encoding.UTF8.GetBytes(queryString); - } - } - - return doHTTPRequest(uri, bytes, method, PROXY_REFERER, contentType, credentials); - } - - private System.Net.WebResponse doHTTPRequest(string uri, byte[] bytes, string method, string referer, string contentType, System.Net.NetworkCredential credentials = null) - { - System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(uri); - req.ServicePoint.Expect100Continue = false; - req.Referer = referer; - req.Method = method; - - if (credentials != null) - req.Credentials = credentials; - - if (bytes != null && bytes.Length > 0 || method == "POST") { - req.Method = "POST"; - req.ContentType = string.IsNullOrEmpty(contentType) ? "application/x-www-form-urlencoded" : contentType; - if (bytes != null && bytes.Length > 0) - req.ContentLength = bytes.Length; - using (Stream outputStream = req.GetRequestStream()) { - outputStream.Write(bytes, 0, bytes.Length); - } - } - return req.GetResponse(); - } - - private string webResponseToString(System.Net.WebResponse serverResponse) { - using (Stream byteStream = serverResponse.GetResponseStream()) { - using (StreamReader sr = new StreamReader(byteStream)) { - string strResponse = sr.ReadToEnd(); - return strResponse; - } - } - } - - private string getNewTokenIfCredentialsAreSpecified(ServerUrl su, string reqUrl) { - string token = ""; - string infoUrl = ""; - - bool isUserLogin = !String.IsNullOrEmpty(su.Username) && !String.IsNullOrEmpty(su.Password); - bool isAppLogin = !String.IsNullOrEmpty(su.ClientId) && !String.IsNullOrEmpty(su.ClientSecret); - if (isUserLogin || isAppLogin) { - log(TraceLevel.Info, "Matching credentials found in configuration file. OAuth 2.0 mode: " + isAppLogin); - if (isAppLogin) { - //OAuth 2.0 mode authentication - //"App Login" - authenticating using client_id and client_secret stored in config - su.OAuth2Endpoint = string.IsNullOrEmpty(su.OAuth2Endpoint) ? DEFAULT_OAUTH : su.OAuth2Endpoint; - if (su.OAuth2Endpoint[su.OAuth2Endpoint.Length - 1] != '/') - su.OAuth2Endpoint += "/"; - log(TraceLevel.Info, "Service is secured by " + su.OAuth2Endpoint + ": getting new token..."); - string uri = su.OAuth2Endpoint + "token?client_id=" + su.ClientId + "&client_secret=" + su.ClientSecret + "&grant_type=client_credentials&f=json"; - string tokenResponse = webResponseToString(doHTTPRequest(uri, "POST")); - token = extractToken(tokenResponse, "token"); - if (!string.IsNullOrEmpty(token)) - token = exchangePortalTokenForServerToken(token, su); - } else { - //standalone ArcGIS Server/ArcGIS Online token-based authentication - - //if a request is already being made to generate a token, just let it go - if (reqUrl.ToLower().Contains("/generatetoken")) { - string tokenResponse = webResponseToString(doHTTPRequest(reqUrl, "POST")); - token = extractToken(tokenResponse, "token"); - return token; - } - - //lets look for '/rest/' in the requested URL (could be 'rest/services', 'rest/community'...) - if (reqUrl.ToLower().Contains("/rest/")) - infoUrl = reqUrl.Substring(0, reqUrl.IndexOf("/rest/", StringComparison.OrdinalIgnoreCase)); - - //if we don't find 'rest', lets look for the portal specific 'sharing' instead - else if (reqUrl.ToLower().Contains("/sharing/")) { - infoUrl = reqUrl.Substring(0, reqUrl.IndexOf("/sharing/", StringComparison.OrdinalIgnoreCase)); - infoUrl = infoUrl + "/sharing"; - } - else - throw new ApplicationException("Unable to determine the correct URL to request a token to access private resources"); - - if (infoUrl != "") { - log(TraceLevel.Info," Querying security endpoint..."); - infoUrl += "/rest/info?f=json"; - //lets send a request to try and determine the URL of a token generator - string infoResponse = webResponseToString(doHTTPRequest(infoUrl, "GET")); - String tokenServiceUri = getJsonValue(infoResponse, "tokenServicesUrl"); - if (string.IsNullOrEmpty(tokenServiceUri)) - tokenServiceUri = getJsonValue(infoResponse, "tokenServiceUrl"); - if (tokenServiceUri != "") { - log(TraceLevel.Info," Service is secured by " + tokenServiceUri + ": getting new token..."); - string uri = tokenServiceUri + "?f=json&request=getToken&referer=" + PROXY_REFERER + "&expiration=60&username=" + su.Username + "&password=" + su.Password; - string tokenResponse = webResponseToString(doHTTPRequest(uri, "POST")); - token = extractToken(tokenResponse, "token"); - } - } - - - } - } - return token; - } - - private string exchangePortalTokenForServerToken(string portalToken, ServerUrl su) { - //ideally, we should POST the token request - log(TraceLevel.Info," Exchanging Portal token for Server-specific token for " + su.Url + "..."); - string uri = su.OAuth2Endpoint.Substring(0, su.OAuth2Endpoint.IndexOf("/oauth2/", StringComparison.OrdinalIgnoreCase)) + - "/generateToken?token=" + portalToken + "&serverURL=" + su.Url + "&f=json"; - string tokenResponse = webResponseToString(doHTTPRequest(uri, "GET")); - return extractToken(tokenResponse, "token"); - } - - private static void sendErrorResponse(HttpResponse response, String errorDetails, String errorMessage, System.Net.HttpStatusCode errorCode) - { - String message = string.Format("{{error: {{code: {0},message:\"{1}\"", (int)errorCode, errorMessage); - if (!string.IsNullOrEmpty(errorDetails)) - message += string.Format(",details:[message:\"{0}\"]", errorDetails); - message += "}}"; - response.StatusCode = (int)errorCode; - //this displays our customized error messages instead of IIS's custom errors - response.TrySkipIisCustomErrors = true; - response.Write(message); - response.Flush(); - } - - private static string getClientIp(HttpRequest request) - { - if (request == null) - return null; - string remoteAddr = request.ServerVariables["HTTP_X_FORWARDED_FOR"]; - if (string.IsNullOrWhiteSpace(remoteAddr)) - { - remoteAddr = request.ServerVariables["REMOTE_ADDR"]; - } - else - { - // the HTTP_X_FORWARDED_FOR may contain an array of IP, this can happen if you connect through a proxy. - string[] ipRange = remoteAddr.Split(','); - remoteAddr = ipRange[ipRange.Length - 1]; - } - return remoteAddr; - } - - private string addTokenToUri(string uri, string token, string tokenParamName) { - if (!String.IsNullOrEmpty(token)) - uri += uri.Contains("?")? "&" + tokenParamName + "=" + token : "?" + tokenParamName + "=" + token; - return uri; - } - - private string extractToken(string tokenResponse, string key) { - string token = getJsonValue(tokenResponse, key); - if (string.IsNullOrEmpty(token)) - log(TraceLevel.Error," Token cannot be obtained: " + tokenResponse); - else - log(TraceLevel.Info," Token obtained: " + token); - return token; - } - - private string getJsonValue(string text, string key) { - int i = text.IndexOf(key); - String value = ""; - if (i > -1) { - value = text.Substring(text.IndexOf(':', i) + 1).Trim(); - value = value.Length > 0 && value[0] == '"' ? - value.Substring(1, value.IndexOf('"', 1) - 1): - value = value.Substring(0, Math.Max(0, Math.Min(Math.Min(value.IndexOf(","), value.IndexOf("]")), value.IndexOf("}")))); - } - return value; - } - - private void cleanUpRatemap(ConcurrentDictionary ratemap) { - foreach (string key in ratemap.Keys){ - RateMeter rate = ratemap[key]; - if (rate.canBeCleaned()) - ratemap.TryRemove(key, out rate); - } - } - -/** -* Static -*/ - private static ProxyConfig getConfig() { - ProxyConfig config = ProxyConfig.GetCurrentConfig(); - if (config != null) - return config; - else - throw new ApplicationException("The proxy configuration file cannot be found, or is not readable."); - } - - //writing Log file - private static void log(TraceLevel logLevel, string msg) { - string logMessage = string.Format("{0} {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msg); - if (TraceLevel.Error == logLevel) - { - Trace.TraceError(logMessage); - logMessageToFile(logMessage); - } - else if (TraceLevel.Warning == logLevel) - { - Trace.TraceWarning(logMessage); - logMessageToFile(logMessage); - } - else - { - Trace.TraceInformation(logMessage); - logMessageToFile(logMessage); - } - } - - private static object _lockobject = new object(); - - private static void logMessageToFile(String message) - { - //Only log messages to disk if logFile has value in configuration, otherwise log nothing. - ProxyConfig config = ProxyConfig.GetCurrentConfig(); - if (config.LogFile != null) - { - string log = config.LogFile; - if (!log.Contains("\\") || log.Contains(".\\")) - { - if (log.Contains(".\\")) //If this type of relative pathing .\log.txt - { - log = log.Replace(".\\", ""); - } - string configDirectory = HttpContext.Current.Server.MapPath("proxy.config"); //Cannot use System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath b/ config may be in a child directory - string path = configDirectory.Replace("proxy.config",""); - log = path + log; - } - - lock(_lockobject) { - using (StreamWriter sw = File.AppendText(log)) - { - sw.WriteLine(message); - } - } - } - } -} - - -[XmlRoot("ProxyConfig")] -public class ProxyConfig -{ - private static object _lockobject = new object(); - public static ProxyConfig LoadProxyConfig(string fileName) { - ProxyConfig config = null; - lock (_lockobject) { - if (System.IO.File.Exists(fileName)) { - XmlSerializer reader = new XmlSerializer(typeof(ProxyConfig)); - using (System.IO.StreamReader file = new System.IO.StreamReader(fileName)) { - try { - config = (ProxyConfig)reader.Deserialize(file); - } - catch (Exception ex) { - throw ex; - } - } - } - } - return config; - } - - public static ProxyConfig GetCurrentConfig() { - ProxyConfig config = HttpRuntime.Cache["proxyConfig"] as ProxyConfig; - if (config == null) { - string fileName = HttpContext.Current.Server.MapPath("proxy.config"); - config = LoadProxyConfig(fileName); - if (config != null) { - CacheDependency dep = new CacheDependency(fileName); - HttpRuntime.Cache.Insert("proxyConfig", config, dep); - } - } - return config; - } - - //referer - //create an array with valid referers using the allowedReferers String that is defined in the proxy.config - public static String[] GetAllowedReferersArray() - { - if (allowedReferers == null) - return null; - - return allowedReferers.Split(','); - } - - //referer - //check if URL starts with prefix... - public static bool isUrlPrefixMatch(String prefix, String uri) - { - - return uri.ToLower().StartsWith(prefix.ToLower()) || - uri.ToLower().Replace("https://", "http://").StartsWith(prefix.ToLower()) || - uri.ToLower().Substring(uri.IndexOf("//")).StartsWith(prefix.ToLower()); - } - - ServerUrl[] serverUrls; - public String logFile; - bool mustMatch; - //referer - static String allowedReferers; - - [XmlArray("serverUrls")] - [XmlArrayItem("serverUrl")] - public ServerUrl[] ServerUrls { - get { return this.serverUrls; } - set - { - this.serverUrls = value; - } - } - [XmlAttribute("mustMatch")] - public bool MustMatch { - get { return mustMatch; } - set - { mustMatch = value; } - } - - //logFile - [XmlAttribute("logFile")] - public String LogFile - { - get { return logFile; } - set - { logFile = value; } - } - - - //referer - [XmlAttribute("allowedReferers")] - public string AllowedReferers - { - get { return allowedReferers; } - set - { - allowedReferers = value; - } - } - - public ServerUrl GetConfigServerUrl(string uri) { - //split both request and proxy.config urls and compare them - string[] uriParts = uri.Split(new char[] {'/','?'}, StringSplitOptions.RemoveEmptyEntries); - string[] configUriParts = new string[] {}; - - foreach (ServerUrl su in serverUrls) { - //if a relative path is specified in the proxy.config, append what's in the request itself - if (!su.Url.StartsWith("http")) - su.Url = su.Url.Insert(0, uriParts[0]); - - configUriParts = su.Url.Split(new char[] { '/','?' }, StringSplitOptions.RemoveEmptyEntries); - - //if the request has less parts than the config, don't allow - if (configUriParts.Length > uriParts.Length) continue; - - int i = 0; - for (i = 0; i < configUriParts.Length; i++) { - - if (!configUriParts[i].ToLower().Equals(uriParts[i].ToLower())) break; - } - if (i == configUriParts.Length) { - //if the urls don't match exactly, and the individual matchAll tag is 'false', don't allow - if (configUriParts.Length == uriParts.Length || su.MatchAll) - return su; - } - } - - if (mustMatch) - throw new ArgumentException("Proxy is being used for an unsupported service:"); - - return null; - } - - -} - -public class ServerUrl { - string url; - bool matchAll; - string oauth2Endpoint; - string domain; - string username; - string password; - string clientId; - string clientSecret; - string accessToken; - string tokenParamName; - string rateLimit; - string rateLimitPeriod; - - [XmlAttribute("url")] - public string Url { - get { return url; } - set { url = value; } - } - [XmlAttribute("matchAll")] - public bool MatchAll { - get { return matchAll; } - set { matchAll = value; } - } - [XmlAttribute("oauth2Endpoint")] - public string OAuth2Endpoint { - get { return oauth2Endpoint; } - set { oauth2Endpoint = value; } - } - [XmlAttribute("domain")] - public string Domain - { - get { return domain; } - set { domain = value; } - } - [XmlAttribute("username")] - public string Username { - get { return username; } - set { username = value; } - } - [XmlAttribute("password")] - public string Password { - get { return password; } - set { password = value; } - } - [XmlAttribute("clientId")] - public string ClientId { - get { return clientId; } - set { clientId = value; } - } - [XmlAttribute("clientSecret")] - public string ClientSecret { - get { return clientSecret; } - set { clientSecret = value; } - } - [XmlAttribute("accessToken")] - public string AccessToken { - get { return accessToken; } - set { accessToken = value; } - } - [XmlAttribute("tokenParamName")] - public string TokenParamName { - get { return tokenParamName; } - set { tokenParamName = value; } - } - [XmlAttribute("rateLimit")] - public int RateLimit { - get { return string.IsNullOrEmpty(rateLimit)? -1 : int.Parse(rateLimit); } - set { rateLimit = value.ToString(); } - } - [XmlAttribute("rateLimitPeriod")] - public int RateLimitPeriod { - get { return string.IsNullOrEmpty(rateLimitPeriod)? 60 : int.Parse(rateLimitPeriod); } - set { rateLimitPeriod = value.ToString(); } - } -} diff --git a/viewer/proxy/proxy.config b/viewer/proxy/proxy.config deleted file mode 100755 index ef689f898..000000000 --- a/viewer/proxy/proxy.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/viewer/proxy/proxy.xsd b/viewer/proxy/proxy.xsd deleted file mode 100755 index d6c057fa8..000000000 --- a/viewer/proxy/proxy.xsd +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/viewer/proxy/readme.md b/viewer/proxy/readme.md deleted file mode 100644 index 630a8993d..000000000 --- a/viewer/proxy/readme.md +++ /dev/null @@ -1,7 +0,0 @@ -# AGS Proxy page help - -For full info please read here: -[https://developers.arcgis.com/javascript/jshelp/ags_proxy.html](https://developers.arcgis.com/javascript/jshelp/ags_proxy.html) - -You will more than likely need a proxy page for printing and other large cross domain requests. -There are diffrent flavors (.net, jsp, php) of the proxy page depending on your server side technology. See the above link for full details. \ No newline at end of file
+ ${i18n.preserve}: - ${i18n.mapScale}   + ${i18n.mapScale}
${i18n.mapExtent}