diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..316385da --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +js/autologin.js +js/autologin.js +.idea/scopes/scope_settings.xml +.idea/workspace.xml +js/autologin.js \ No newline at end of file diff --git a/DotNet/README.md b/DotNet/README.md new file mode 100644 index 00000000..4abe5a50 --- /dev/null +++ b/DotNet/README.md @@ -0,0 +1,69 @@ +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. For example using the following steps: + * Open IIS Manager + * If you put the DotNet folder within wwwroot, right-click it and select "Convert to Application". + * Make sure the "Application pool" is at least 4.0. +* Test that the proxy is installed and available: +``` +http://[yourmachine]/DotNet/proxy.ashx?ping +``` +* 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 +``` +* Troubleshooting: If you get an error message 404.3, it's possible that ASP.NET have not been set up. On Windows 8, go to "Turn Windows features on or off" -> "Internet Information Services" -> "World Wide Web Services" -> "Application Development Features" -> "ASP.NET 4.5". +* Edit the proxy.config file in a text editor to set up your [proxy configuration settings](../README.md#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. + +## Folders and Files + +The proxy consists of the following files: +* proxy.config: This file contains the [configuration settings for the proxy](../README.md#proxy-configuration-settings). 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. +* proxy.xsd: a schema file for easier editing of proxy.config in Visual Studio. +* Web.config: An XML file that stores ASP.NET configuration data. +NOTE: as of v1.1.0, log levels and log file locations are specified in proxy config. 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/DotNet/Web.config b/DotNet/Web.config new file mode 100644 index 00000000..8d1a4012 --- /dev/null +++ b/DotNet/Web.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/DotNet/proxy.ashx b/DotNet/proxy.ashx new file mode 100644 index 00000000..e7d71094 --- /dev/null +++ b/DotNet/proxy.ashx @@ -0,0 +1,1122 @@ +<%@ WebHandler Language="C#" Class="proxy" %> + +/* + * DotNet proxy client. + * + * Version 1.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; +using System.Text.RegularExpressions; + +public class proxy : IHttpHandler { + + private static String version = "1.1.1-beta"; + + 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 System.Net.IWebProxy SYSTEM_PROXY = System.Net.HttpWebRequest.DefaultWebProxy; // Use the default system proxy + private static LogTraceListener logTraceListener = null; + private static Object _rateMapLock = new Object(); + + public void ProcessRequest(HttpContext context) { + + + if (logTraceListener == null) + { + logTraceListener = new LogTraceListener(); + Trace.Listeners.Add(logTraceListener); + } + + + HttpResponse response = context.Response; + if (context.Request.Url.Query.Length < 1) + { + string errorMsg = "This proxy does not support empty parameters."; + log(TraceLevel.Error, errorMsg); + sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.BadRequest); + return; + } + + string uri = context.Request.Url.Query.Substring(1); + + //if uri is ping + if (uri.Equals("ping", StringComparison.InvariantCultureIgnoreCase)) + { + ProxyConfig proxyConfig = ProxyConfig.GetCurrentConfig(); + + String checkConfig = (proxyConfig == null) ? "Not Readable" : "OK"; + String checkLog = ""; + if (checkConfig != "OK") + { + checkLog = "Can not verify"; + } + else + { + String filename = proxyConfig.logFile; + checkLog = (filename != null && filename != "") ? "OK" : "Not Exist/Readable"; + + if (checkLog == "OK") + log(TraceLevel.Info, "Log from ping"); + + } + + sendPingResponse(response, version, checkConfig, checkLog); + return; + } + + //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; + try { + serverUrl = getConfig().GetConfigServerUrl(uri); + + if (serverUrl == null) { + //if no serverUrl found, send error message and get out. + string errorMsg = "The request URL does not match with the ServerUrl in proxy.config! Please check the proxy.config!"; + log(TraceLevel.Error, errorMsg); + sendErrorResponse(context.Response, null, errorMsg, System.Net.HttpStatusCode.BadRequest); + return; + } + } + //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 && context.Request.Headers["referer"] != null) + { + PROXY_REFERER = context.Request.Headers["referer"]; + string requestReferer = context.Request.Headers["referer"]; + try + { + String checkValidUri = new UriBuilder(requestReferer.StartsWith("//") ? requestReferer.Substring(requestReferer.IndexOf("//") + 2) : requestReferer).Host; + + } + catch (Exception e) + { + log(TraceLevel.Warning, "Proxy is being used from an invalid referer: " + context.Request.Headers["referer"]); + sendErrorResponse(context.Response, "Error verifying referer. ", "403 - Forbidden: Access is denied.", System.Net.HttpStatusCode.Forbidden); + return; + } + + if (!checkReferer(allowedReferersArray, requestReferer)) + { + log(TraceLevel.Warning, "Proxy is being used from an unknown referer: " + context.Request.Headers["referer"]); + sendErrorResponse(context.Response, "Unsupported referer. ", "403 - Forbidden: Access is denied.", System.Net.HttpStatusCode.Forbidden); + } + + + } + + //Check to see if allowed referer list is specified and reject if referer is null + if (context.Request.Headers["referer"] == null && allowedReferersArray != null && !allowedReferersArray[0].Equals("*")) + { + log(TraceLevel.Warning, "Proxy is being called by a null referer. Access denied."); + sendErrorResponse(response, "Current proxy configuration settings do not allow requests which do not include a referer header.", "403 - Forbidden: Access is denied.", System.Net.HttpStatusCode.Forbidden); + return; + } + + //Throttling: checking the rate limit coming from particular client IP + if (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)429); + 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 ((serverUrl.HostRedirect != null) && (serverUrl.HostRedirect != string.Empty)) + { + requestUri = serverUrl.HostRedirect + new Uri(requestUri).PathAndQuery; + } + + if (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 = requestUri.Contains("?token=") || requestUri.Contains("&token=") || post.Contains("?token=") || post.Contains("&token="); + + if (!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, requestUri); + } + + 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(requestUri, 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 (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, requestUri); + serverResponse = forwardToServer(context, addTokenToUri(requestUri, 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": + case "accept-ranges": // Prevent requests for partial content + 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) { + 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.Contains("error") + && (strResponse.Contains("\"code\": 498") || strResponse.Contains("\"code\": 499") || strResponse.Contains("\"code\":498") || strResponse.Contains("\"code\":499")) + ) + return true; + + //Copy the header info and the content to the reponse to client + copyHeaders(serverResponse, clientResponse); + clientResponse.Write(strResponse); + } + } else { + // Binary response (image, lyr file, other binary file) + + //Copy the header info to the reponse to client + copyHeaders(serverResponse, clientResponse); + // 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(new char[] { '?' }, 2); + uri = uriArray[0]; + 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; + + // Use the default system proxy + req.Proxy = SYSTEM_PROXY; + + 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)) { + string owningSystemUrl = getJsonValue(infoResponse, "owningSystemUrl"); + if (!string.IsNullOrEmpty(owningSystemUrl)) { + tokenServiceUri = owningSystemUrl + "/sharing/generateToken"; + } + } + 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 bool checkWildcardSubdomain(String allowedReferer, String requestedReferer) + { + String[] allowedRefererParts = Regex.Split(allowedReferer, "(\\.)"); + String[] refererParts = Regex.Split(requestedReferer, "(\\.)"); + + if (allowedRefererParts.Length != refererParts.Length) + { + return false; + } + + int index = allowedRefererParts.Length - 1; + while (index >= 0) + { + if (allowedRefererParts[index].Equals(refererParts[index], StringComparison.OrdinalIgnoreCase)) + { + index = index - 1; + } + else + { + if (allowedRefererParts[index].Equals("*")) + { + index = index - 1; + continue; //next + } + return false; + } + } + return true; + } + + private bool pathMatched(String allowedRefererPath, String refererPath) + { + //If equal, return true + if (refererPath.Equals(allowedRefererPath)) + { + return true; + } + + //If the allowedRefererPath contain a ending star and match the begining part of referer, it is proper start with. + if (allowedRefererPath.EndsWith("*")) + { + String allowedRefererPathShort = allowedRefererPath.Substring(0, allowedRefererPath.Length - 1); + if (refererPath.ToLower().StartsWith(allowedRefererPathShort.ToLower())) + { + return true; + } + } + return false; + } + + private bool domainMatched(String allowedRefererDomain, String refererDomain) + { + if (allowedRefererDomain.Equals(refererDomain)){ + return true; + } + + //try if the allowed referer contains wildcard for subdomain + if (allowedRefererDomain.Contains("*")){ + if (checkWildcardSubdomain(allowedRefererDomain, refererDomain)){ + return true;//return true if match wildcard subdomain + } + } + + return false; + } + + private bool protocolMatch(String allowedRefererProtocol, String refererProtocol) + { + return allowedRefererProtocol.Equals(refererProtocol); + } + + private String getDomainfromURL(String url, String protocol) + { + String domain = url.Substring(protocol.Length + 3); + + domain = domain.IndexOf('/') >= 0 ? domain.Substring(0, domain.IndexOf('/')) : domain; + + return domain; + } + + private bool checkReferer(String[] allowedReferers, String referer) + { + if (allowedReferers != null && allowedReferers.Length > 0) + { + if (allowedReferers.Length == 1 && allowedReferers[0].Equals("*")) return true; //speed-up + + foreach (String allowedReferer in allowedReferers) + { + + //Parse the protocol, domain and path of the referer + String refererProtocol = referer.StartsWith("https://") ? "https" : "http"; + String refererDomain = getDomainfromURL(referer, refererProtocol); + String refererPath = referer.Substring(refererProtocol.Length + 3 + refererDomain.Length); + + + String allowedRefererCannonical = null; + + //since the allowedReferer can be a malformed URL, we first construct a valid one to be compared with referer + //if allowedReferer starts with https:// or http://, then exact match is required + if (allowedReferer.StartsWith("https://") || allowedReferer.StartsWith("http://")) + { + allowedRefererCannonical = allowedReferer; + + } + else + { + + String protocol = refererProtocol; + //if allowedReferer starts with "//" or no protocol, we use the one from refererURL to prefix to allowedReferer. + if (allowedReferer.StartsWith("//")) + { + allowedRefererCannonical = protocol + ":" + allowedReferer; + } + else + { + //if the allowedReferer looks like "example.esri.com" + allowedRefererCannonical = protocol + "://" + allowedReferer; + } + } + + //parse the protocol, domain and the path of the allowedReferer + String allowedRefererProtocol = allowedRefererCannonical.StartsWith("https://") ? "https" : "http"; + String allowedRefererDomain = getDomainfromURL(allowedRefererCannonical, allowedRefererProtocol); + String allowedRefererPath = allowedRefererCannonical.Substring(allowedRefererProtocol.Length + 3 + allowedRefererDomain.Length); + + //Check if both domain and path match + if (protocolMatch(allowedRefererProtocol, refererProtocol) && + domainMatched(allowedRefererDomain, refererDomain) && + pathMatched(allowedRefererPath, refererPath)) + { + return true; + } + } + return false;//no-match + } + return true;//when allowedReferer is null, then allow everything + } + + 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 sendPingResponse(HttpResponse response, String version, String config, String log) + { + response.AddHeader("Content-Type", "application/json"); + response.AddHeader("Accept-Encoding", "gzip"); + String message = "{ " + + "\"Proxy Version\": \"" + version + "\"" + + ", \"Configuration File\": \"" + config + "\"" + + ", \"Log File\": \"" + log + "\"" + + "}"; + response.StatusCode = 200; + response.Write(message); + response.Flush(); + } + + 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; + //custom status description for when the rate limit has been exceeded + if (response.StatusCode == 429) { + response.StatusDescription = "Too Many Requests"; + } + //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); + + ProxyConfig config = ProxyConfig.GetCurrentConfig(); + TraceSwitch ts = null; + + if (config.logLevel != null) + { + ts = new TraceSwitch("TraceLevelSwitch2", "TraceSwitch in the proxy.config file", config.logLevel); + } + else + { + ts = new TraceSwitch("TraceLevelSwitch2", "TraceSwitch in the proxy.config file", "Error"); + config.logLevel = "Error"; + } + + Trace.WriteLineIf(logLevel <= ts.Level, logMessage); + } + + private static object _lockobject = new object(); + +} + +class LogTraceListener : TraceListener +{ + private static object _lockobject = new object(); + public override void Write(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.Write(message); + } + } + } + } + + + public override void WriteLine(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; + public String logLevel; + 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; } + } + + //logLevel + [XmlAttribute("logLevel")] + public String LogLevel + { + get { return logLevel; } + set + { logLevel = value; } + } + + + //referer + [XmlAttribute("allowedReferers")] + public string AllowedReferers + { + get { return allowedReferers; } + set + { + allowedReferers = Regex.Replace(value, @"\s", ""); + } + } + + 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) + { + return new ServerUrl(uri); + } + else + { + throw new ArgumentException("Proxy has not been set up for this URL. Make sure there is a serverUrl in the configuration file that matches: " + uri); + } + } +} + +public class ServerUrl { + string url; + string hostRedirect; + bool matchAll; + string oauth2Endpoint; + string domain; + string username; + string password; + string clientId; + string clientSecret; + string accessToken; + string tokenParamName; + string rateLimit; + string rateLimitPeriod; + + private ServerUrl() + { + } + + public ServerUrl(String url) + { + this.url = url; + } + + [XmlAttribute("url")] + public string Url { + get { return url; } + set { url = value; } + } + [XmlAttribute("hostRedirect")] + public string HostRedirect + { + get { return hostRedirect; } + set { hostRedirect = 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/DotNet/proxy.config b/DotNet/proxy.config new file mode 100644 index 00000000..4b30ac35 --- /dev/null +++ b/DotNet/proxy.config @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/DotNet/proxy.xsd b/DotNet/proxy.xsd new file mode 100644 index 00000000..399dd3ec --- /dev/null +++ b/DotNet/proxy.xsd @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Layers.html b/Layers.html new file mode 100644 index 00000000..ea122675 --- /dev/null +++ b/Layers.html @@ -0,0 +1,61 @@ + + + + + + + Vector tiles - 4.0beta3 + + + + + + + + +
+ + \ No newline at end of file diff --git a/README.md b/README.md index 1e7e6d21..f6634f54 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,12 @@ -basic-viewer-template +WCAG basic-viewer-template ===================== -*Basic Viewer* is a configurable application template used to display a web map with a specified set of commonly used tools and options. +*WCAG Basic Viewer* is a configurable application template used to display a web map with a specified set of commonly used tools and options. +This template includes many WCAG 2.0 A, AA and AAA capabilities, which serves as a starting point for developers building fully complaint apps or is an example of WCAG apps. -![Screen Shot](https://dl.dropboxusercontent.com/u/24627279/screenshots/Viewer_screenshot.png) +![Screen Shot](https://github.com/EsriCanada/WCAG-BasicViewer/blob/master/WCAGViewer.png) -[View it live] (http://www.arcgis.com/apps/Viewer/index.html?webmap=f5b13dbed07c46cdb783cf361833aa6b) +[View it live] (http://apps.esri.ca/templates/WCAGViewer/index.html?appid=94caadf44ba447a89998b1f3828f006f) -#November 2015 Release Updates -- **Splash Screen:** Add a configurable splash screen and specify custom title and contents. -- **LayerList:** The layer list widget has been updated to add support for displaying legend info and setting layer opacity. -#July 2015 Release Updates -- **Optional Subtitle:** Added support for specifying a subtitle. To specify subtitle text update config/defaults.js and specify the subtitle text using the subtitle property. -- **Custom URL Parameters:** The template now supports the ability to define a custom url parameter along with the search layer and field. This can be used to build applications that display a particular feature at application startup. For example if your app displays parcel features you could define a custom url parameter called parcel and then users can navigate there directly by appending ?parcel=1245243242343 and the map will zoom to that feature on startup. Define these values using by setting the customUrlParam and customUrlLayer properties in config/defaults.js. -- **Support for toggling sub layers:** The layers feature now allows you to toggle the visilibity of sub layers. -- **Define custom print layout title:** The print feature now provides an option for users to define a custom title for the print layout. -- **Reduce white space on panels:** Reduced white space on panels. Now the panels (Legend, layer, desc etc) size to fit the content. #Features The template can be configured using the following options: diff --git a/WCAGViewer.png b/WCAGViewer.png new file mode 100644 index 00000000..63774e59 Binary files /dev/null and b/WCAGViewer.png differ diff --git a/WCAGthumbnail.jpg b/WCAGthumbnail.jpg new file mode 100644 index 00000000..390eb589 Binary files /dev/null and b/WCAGthumbnail.jpg differ diff --git a/config/defaults.js b/config/defaults.js index 0aeb4132..53ee7793 100755 --- a/config/defaults.js +++ b/config/defaults.js @@ -1,4 +1,4 @@ -/*global define,location */ +/*global define,location */ /*jslint sloppy:true */ /* | Copyright 2014 Esri @@ -18,9 +18,15 @@ define({ //Default configuration settings for the application. This is where you'll define things like a bing maps key, //default web map, default app color theme and more. These values can be overwritten by template configuration settings and url parameters. - "appid": "",//"73909e939be34d2b931f0765ba3bf4a6", - "webmap": "f5b13dbed07c46cdb783cf361833aa6b", - "oauthappid": null, //"AFTKRmv16wj14N3z", + "appid": null, // + //"440b2da51b2b42878e8cb946c2de0326", //Farmers Markets + //"142adb5e5e1d4bddb578aefc6f6e6deb", // TH Demo + "webmap": "116178b11f0f41bb9b1c7c96aa09fd5a", + //"fe35efe129f845ef8de5296c15325118", // MAtt English + //"326320c9eab3489d8d17bc389ce1e023", + + "oauthappid": "", //"i0sXrSseaL5Ifovc", + "portalUrl": "https://www.arcgis.com", //Group templates must support a group url parameter. This will contain the id of the group. //group: "", //Enter the url to the proxy if needed by the application. See the 'Using the proxy page' help topic for details @@ -28,78 +34,71 @@ define({ "proxyurl": "", "bingKey": "", //Enter the url to your organizations bing maps key if you want to use bing basemaps //Defaults to arcgis.com. Set this value to your portal or organization host name. - "sharinghost": location.protocol + "//" + "www.arcgis.com", - // Set splashModal to display a splash screen when the app loads - // Define the splash content using splashTitle and splashContent. - "splashModal": false, - "splashTitle": null, - "splashContent": null, - "find": null, + "sharinghost": "https://www.arcgis.com", + //If you need localization set the localize value to true to get the localized strings + //from the javascript/nls/resource files. + //Note that we've included a placeholder nls folder and a resource file with one error string + //to show how to setup the strings file. "localize": true, "units": null, - // This is an option added so that developers working with the - // hosted version of the application can apply custom styles - // not used in the download version. - "customstyle": null,//'#mapDiv { background-color: #cfdfec; } .arcgisSearch .searchGroup .searchInput { border: 0px solid #BDBDBD; background-color: #fff!important; height: 27px; font-size: 16px; color: #333; } .esriIconZoom:before { color: white; } .arcgisSearch .searchBtn { border: 0px solid #57585A; rgba(0, 0, 0, 0.41); } #mapDiv_graphics_layer path { stroke: rgba(221, 0, 32, 1); stroke-width: 4px; opacity: 0.01; } .icon-menu:before { opacity: 0.01; } input#search_input:focus { outline: none; } .arcgisSearch .searchMenu { border: 0px solid #57585A; } .esriIconClose:before { color: white; } #panelLogo img { max-width: 80px; max-height: 68px; } #panelLogo { width: 81px; padding-top: 3px; } .titleButton.maximize:before { visibility: hidden!important; } .pageHeaderImg { display: none; } .pageTitle { display: none; } .arcgisSearch .hasMultipleSources .searchToggle { display: none!important; } #search_input::-webkit-input-placeholder { color: #3B3C3D; } #search_input::-moz-placeholder { color: #3B3C3D; } #search_input:-ms-input-placeholder { color: #3B3C3D; } #panelTop { height: 79px!important; } #search > div > div.searchBtn.searchSubmit { height: 27px; } .arcgisSearch .searchIcon { line-height: 29px; } #panelSearch { margin: 10px 10px 10px 20px!important; } .esriIconClose:before { color: rgb(134, 134, 134); padding-right: 7px; } #panelTitle { border-bottom: none; } .no-search #panelLogo { width: 87px; padding-right: 19px; } .no-search #panelLogo img { max-width: 86px !important; } #panelText { max-width: 500px; }', //Theme defines the background color of the title area and tool dialog //Color defines the text color for the title and details. Note that //both these values must be specified as hex colors. - "theme": "#80ab00", - "color": "#fff", + "theme": "#037EAF", + "color": "#ffffff", + "hoverColor": "#00A9E6", + "focusColor": "#00FFC5", + "activeColor": "#00C5FF", + "animated_marker":true, + "marker":"images/ripple-dot1.gif", + "marker_size":"35", + "alt_keys":true, + //Specify the tool icon color for the tools on the toolbar and the menu icon. // Valid values are white and black. "icons": "white", - "logo": null, + "new_icons": true, + "logo": "images/logo.png", //Set of tools that will be added to the toolbar "tools": [ + {"name": "instructions", "enabled": true}, + {"name": "details", "enabled": false}, + {"name": "overview", "enabled": true}, {"name": "legend", "enabled": true}, - {"name": "bookmarks", "enabled": true}, - {"name": "layers", "enabled": true, "sublayers": true, "legend": true, "opacityslider": true}, + {"name": "layers", "enabled": true}, {"name": "basemap", "enabled": true}, - {"name": "overview", "enabled": true}, + {"name": "features", "enabled": true}, + {"name": "filter", "enabled": true}, {"name": "measure", "enabled": true}, - {"name": "edit", "enabled": true, "toolbar": false}, - {"name": "print", "enabled": true, "legend": false, "layouts":false, "format":"pdf"}, - {"name": "details", "enabled": true}, - {"name": "share", "enabled": true} + {"name": "edit", "enabled": true, "toolbar": true}, + {"name": "share", "enabled": true}, + {"name": "bookmarks", "enabled": true}, + {"name": "navigation", "enabled": true}, + {"name": "print", "enabled": true, "legend": false, "layouts":false, "format":"pdf"} ], //Set the active tool on the toolbar. Note home and locate can't be the active tool. //Set to "" to display no tools at startup - "activeTool": "legend", + "activeTool": "instructions", //Add the geocoding tool next to the title bar. "search": true, "locationSearch": true, - "popupPanel": false, //When searchExtent is true the locator will prioritize results within the current map extent. - "searchExtent": false, + "searchExtent": true, "searchLayers":[{ - "id": "", - "fields": [] + "id": "Incidents", + "fields": ["Incident Type", "Province", "Company", "Status", "Substance"] }], - //Setup the app to support a custom url parameter. Use this if you want users - //to be able to search for a string field in a layer. For example if the web map - //has parcel data and you'd like to be able to zoom to a feature using its parcel id - //you could add a custom url param named parcel then users could enter - //a value for that param in the url. index.html?parcel=3203 - "customUrlLayer":{ - "id": null,//id of the search layer as defined in the web map - "fields": []//Name of the string field to search - }, - "customUrlParam": null,//Name of url param. For example parcels //Add the home extent button to the toolbar "home": true, //Add the geolocation button on the toolbar. Only displayed if browser supports geolocation "locate": true, - "locate_track": false, //When true display a scalebar on the map "scalebar": false, //Specify a title for the application. If not provided the web map title is used. "title": "", - //Optionally specify some sub title text. - "subtitle":null, "level": null, "center": null, //Replace these with your own bitly key - "bitlyLogin": "arcgis", - "bitlyKey": "R_b8a169f3a8b978b9697f64613bf1db6d" + "bitlyLogin": "", + "bitlyKey": "" }); diff --git a/config/demoMap.js b/config/demoMap.js old mode 100755 new mode 100644 diff --git a/config/templateConfig.js b/config/templateConfig.js index 66ec26b0..ee706148 100755 --- a/config/templateConfig.js +++ b/config/templateConfig.js @@ -22,10 +22,6 @@ define({ "queryForGroupInfo": false, // When true, the template will query arcgis.com for the items contained within the group "queryForGroupItems": false, - // Use a local hosted webmap instead of a webmap on ArcGIS or portal. - "useLocalWebmap": false, - // Webmap file to use for the local webmap - "localWebmapFile": "config/demoMap", //When true the template will query arcgis.com for default settings for helper services, units etc. If you //want to use custom settings for units or any of the helper services set queryForOrg to false then enter //default values for any items you need using the helper services and units properties. @@ -47,8 +43,8 @@ define({ //if you want users to be able to specify lat/lon coordinates that define the map's center or //specify an alternate basemap via a url parameter. "urlItems": [ - "color","extent", "center", "level", "feature", "find" // example param. ?theme= + "color", "extent", "center", "level" // example param. ?theme= ], // For esri hosted environments only. Will automatically create a sharingurl and proxyurl for the application. esriEnvironment: false -}); \ No newline at end of file +}); diff --git a/css/AComboBoxWidget.css b/css/AComboBoxWidget.css new file mode 100644 index 00000000..b8d98104 --- /dev/null +++ b/css/AComboBoxWidget.css @@ -0,0 +1,81 @@ +.aComboBox { + vertical-align: middle; + box-sizing: border-box; + background-color: #fff; + border: 1px solid rgb(169, 169, 169); + border-radius: 4px; + position: relative; + background-clip: border-box; + cursor: pointer; +} + +.aComboBox_btn { + border: 1px solid transparent; + position: absolute; + right: 0; + cursor: pointer; + display: inline-block; + margin: 1px; + /**/ + -webkit-appearance: button; + height: calc(100% - 4px); + pointer-events: none; +} + +.aComboBox img { + width: 10px; + height: 10px; + padding: 3px; + /* pointer-events: none; */ +} + +.aComboBox input[type="text"] { + width: calc(100% - 8px); + border: none; + outline: 0; + padding: 1px 4px; + background-color: #fff; + font: inherit; + cursor: default; +} + +.aComboBox .popup_container { + max-height: 202px; + min-height: 20px; + position: absolute; + top: 100%; + margin-top: 1px; + z-index: 1005; + left: 0; + right: 0; + box-sizing: border-box; + box-shadow: 0 5px 6px rgba(0,0,0,.2); + overflow: auto; + border: 1px solid rgb(169, 169, 169); + background-color: white; + border-bottom-left-radius: 5px; +} + +.aComboBox ul { + padding: 0; + width: 100%; +} + +.aComboBox ul li { + padding: 0; + margin: 0 2px; + width: calc(100% - 8px); + border-radius: 3px; + cursor: pointer; + border-color: transparent; + border-width: 2px; +} + +.aComboBox ul li:hover { + border-color: #ccf; +} + +.aComboBox .selected { + background-color: #ccf; +} + diff --git a/css/filters.css b/css/filters.css new file mode 100644 index 00000000..1c853e41 --- /dev/null +++ b/css/filters.css @@ -0,0 +1,113 @@ +.filterButtons{ + /* position: absolute; */ + /* bottom: 0px; */ + padding-top: 10px; + padding-bottom: 10px; +} + +.filterContent { + +} + +.filterContent label { + background: white; + padding: 0; + border-style: none; + margin: 0; +} + +.filterAdd {/* border-color: aquamarine; *//* border-style: solid; */position: inherit;/* padding-top: 4px; */padding-bottom: 10px;/* bottom: 30px; */position: relative;width: 100%;} + +.filterAdd select { + font-size: 16px; + width: calc(100% - 130px); +} + +.tabContent ul {list-style-type: none;-webkit-margin-before: 0;-webkit-margin-after: 0;-webkit-margin-start: 0px;-webkit-margin-end: 0px;-webkit-padding-start: 0;/* border-color: gray; *//* border-width: 1px; *//* border-style: solid; */padding: 5px;/* max-height: calc(100% - 66px); */width: calc(100% - 10px);overflow-y: auto;position: relative;/* top: 35px; */} +.tabContent ul li { + border-color: gray; + border-width: 1px; + border-style: solid; + padding: 10px; + margin-bottom: 10px; + width: calc(100% - 22px); + } + +._filter { + display:inline-block; + padding-top: 10px; + width: 100%; +} + +._filter ._fieldExamples { + max-height: 80px; + overflow: auto; + width: calc(100% - 30px); + border-width: 1px; + border-style: solid; + margin: 0; + margin-top: 4px; +} + +.filterTabs{ + display: -webkit-inline-box; + overflow-x: auto; + position: absolute; + width: calc(100% - 14px); + height: calc(100% - 20px); + margin: 10px; + margin-right: 0; +} + +.selectOpenParan { + position: relative; + top: -20px; + left: calc(100% - 120px); +} + +.selectCloseParan { + position: absolute; + margin=top: 10px; + left: calc(100% - 75px); +} + +.selectAndOr {position: absolute;right: 25px;} + +.filterTabsZone { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + height: 50px; + overflow-x: auto; + overflow-y: hidden; + margin-top: -6px; + position: absolute; + top: calc(100% - 50px); + width: 100%; +} + +.filterTabsContent { + width: calc(100% - 22px); + /* left: 1px; */ + /* top: 0px; */ + height: calc(100% - 73px); + border: 1px solid black; + border-top-right-radius: 8px; + padding: 10px; + position: absolute; + overflow-y: auto; +} + +.dijitCalendarYearContainer { + display: initial !important; +} +.dijitCalendarYearLabel { + position: absolute; + bottom: 0; +} + +.dijitTooltipContainer { + background-color: red !important; + color:white !important; + margin-left: 10px; +} \ No newline at end of file diff --git a/css/styles.css b/css/styles.css index 11d6f7f6..5c789d96 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1,1467 +1,1942 @@ -/*Phones (520px) Small Tablets (720px)*/ -html, body { - padding: 0; - margin: 0; - height: 100%; - width: 100%; - font-family: Arial, Helvetica, sans-serif; } - -.noscroll{ - overflow: hidden; -} - -.modal-scrollbar { - margin-right: 17px; } - -/* Fonts */ -@font-face { - font-family: "HelveticaNeue"; - src: url('../fonts/helveticaneue.eot'); - src: url('../fonts/helveticaneue.eot?#iefix') format('embedded-opentype'), url('../fonts/helveticaneue.woff') format('woff'), url('../fonts/helveticaneue.ttf') format('truetype'), url('../fonts/helveticaneue.svg#HelveticaNeue') format('svg'); - font-style: normal; - font-weight: normal; } - -@font-face { - font-family: "HelveticaNeueUltraLight "; - src: url('../fonts/helveticaneueultralight.eot'); - src: url('../fonts/helveticaneueultralight.eot?#iefix') format('embedded-opentype'), url('../fonts/helveticaneueultralight.woff') format('woff'), url('../fonts/helveticaneueultralight.ttf') format('truetype'), url('../fonts/helveticaneueultralight.svg#HelveticaNeueUltraLight') format('svg'); - font-style: normal; - font-weight: normal; } - -#mapDiv { - position: fixed; - padding: 0; - margin: 0; - width: 100%; - height: 100%; } - -.loading-indicator { - /* Loading status */ - display: none; - height: 100%; - width: 100%; - z-index: 500; - position: absolute; - top: 0; - left: 0; - overflow: hidden; - background: #fff; } - -.app-loading .loading-indicator, .app-error .loading-indicator { - /* Show this class when loading or error */ - display: block; } - -.app-loading .loading-indicator { - /* Loading image */ - background-position: center center; - background-image: url(../images/ajax-loader.gif); - background-repeat: no-repeat; } - -.app-error .loading-message { - /* Error status */ - background-position: top center; - background-image: url(../images/error.png); - background-repeat: no-repeat; - padding-top: 60px; - width: 100%; - position: absolute; - z-index: 2; - top: 40%; - left: 0; - text-align: center; -} - -.esriLayer{ - background-color: #fff; -} -.esriLayerList .esriTitle { - background-color: #fff; - border-bottom:none; -} -.esriLayerList .esriList ul{ - background-color: #fff; -} -.esriLayerList .esriContainer{ - border-bottom:1px solid #ccc; - border-top: none !important; -} -.esriLayerList .esriTitleContainer{ - border-color:#eee; -} -.esriLayerList .esriList{ - border-top:none; - background-color:#fff; -} - -#pageBody_layers{ - background-color: #fff; -} - -.esriOverviewMap .ovwContainer{ - width:300px !important; -} -#pageBody_overview{ - height:350px !important; -} -@media only screen and (min-width: 720px) { - #pageBody_overview { - max-height: 80%; } } - -/*Styles for the print dialog checkbox*/ -#pageBody_print .checkbox { - display: inline-block; - cursor: pointer; - line-height: 18px; - height: 18px; -} -/*Editor*/ -.calcite .dijitTextBoxDisabled , .esriAttributeInspector .dijitTextBoxDisabled input{ - background-color: #d3d3d3; - padding:0 !important; -} -/*Layer list dialog*/ -.layer-menu { - width: 100%; } - -.layer-menu .dijitMenuItem:hover { - background: #d3d3d3; } - -.layer-menu .dijitMenuItemSelected { - background: #d3d3d3; } - -/* Esri Javascript API*/ -.templatePicker { - height: 100%; - padding-top: 30px; - border: none; } - -.esriEditor { - height: 100%; } - -.esriEditor .esriDrawingToolbar { - position: absolute; - right: 0; - left: 0; - top: 10px; - border-top: none; - border-right: none; - border-left: none; } - -@media only screen and (min-width: 720px) { - .esriEditor .esriDrawingToolbar { - top: 10px; - right: 0; - left: 0; } } - -.esriPopup .pointer.bottom, .esriPopup .pointer.bottomLeft, .esriPopup .pointer.bottomRight { - background: #f7f7f7 !important; } - -.esriPopupMaximized { - top: 100px !important; } - -.esriPopup.light .actionsPane .action.zoomTo { - background-position: 0 -224px; } - -.esriPopup .actionsPane .action { - margin-right: 10px; } - -.esriPopup.light .actionsPane .action { - position: absolute; - cursor: pointer; - background-image: url("../images/popup-sprite.png"); - width: 12px; - height: 12px; - text-indent: -9999px; -} -.printTitle{ - width: 265px; - font-size: 14px; - border-width: 2px; - border-color: #CCC; - border-style: solid; - padding: 5px; - margin-bottom: 12px; - display:block; -} -.esriPrintout{ - font-size:20px; -} -.esriPrintout a { - color: #5daddd; -} -.esriPrint { - position: absolute; - bottom:5px; - right: 20px; - padding:5px; - color:#fff; -} - -.loadPrint{ - padding:0 0.3em; - text-align: center; - vertical-align: middle; - line-height:32px; - -webkit-animation-name: rotate; - -webkit-animation-duration: 1s; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - -moz-animation-name: rotate; - -moz-animation-duration: 1s; - -moz-animation-iteration-count: infinite; - -moz-animation-timing-function: linear; - animation-name: rotate; - animation-duration: 1s; - animation-iteration-count: infinite; - animation-timing-function: linear; -} -@-webkit-keyframes rotate { - from {-webkit-transform: rotate(0deg);} - to {-webkit-transform: rotate(360deg);} -} - -@-moz-keyframes rotate { - from {-moz-transform: rotate(0deg);} - to {-moz-transform: rotate(360deg);} -} - -@keyframes rotate { - from {transform: rotate(0deg);} - to {transform: rotate(360deg);} -} - -.esriMeasurement .esriMeasurementResultTable{ - padding:6px; -} -.esriMeasurement .esriMeasurementButtonPane { - padding-left: 6px; } - -.esriMeasurement .esriMeasurementResultLabel { - padding-left: 4px; } - -.esriMeasurement .areaIcon { - background-image: url(../images/area_measure.png); - width: 28px; - height: 28px; } - -.esriMeasurement .distanceIcon { - background-image: url(../images/dist_measure.png); - width: 28px; - height: 28px; } - -.esriMeasurement .locationIcon { - background-image: url(../images/dist_point.png); - width: 28px; - height: 28px; } - -.esriMeasurement .esriButtonHover .dijitButtonNode { - background: #a9a9a9; } - -.esriMeasurement .esriButtonChecked .dijitButtonNode { - background: #a9a9a9; } - -.no-touch .esriMeasurement .esriButton .dijitButtonNode:hover { - background: #a9a9a9; } - -.locationIcon { - width: 32px; - height: 32px; } - -.esriSimpleSlider { - text-align: center; - color: #ffffff; - background-color: transparent; - background-color: rgba(255, 255, 255, 0.8); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#CCFFFFFF,endColorstr=#CCFFFFFF); - zoom: 1; - -webkit-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); - box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); - cursor: pointer; - display: none; } - -@media only screen and (min-width: 760px) { - .esriSimpleSlider { - display: block; } } - -.esriSimpleSlider div { - width: 30px; - height: 40px; - font-size: 24px; - font-family: verdana, helvetica; - font-weight: bold; - line-height: 40px; - -webkit-user-select: none; } - -.arcgisSearch .searchGroup .searchInput { - width: 200px; - padding: 6px 17px 6px 18px; - background-color: transparent; - background-color: rgba(0, 0, 0, 0.3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); - zoom: 1; - color: #fff; } - -#search_input.dark::-webkit-input-placeholder{ - color: #000; -} - -#search_input.dark:-moz-placeholder{ - color: #000; -} - -#search_input.dark::-moz-placeholder{ - color: #000; -} - -#search_input.dark:-ms-input-placeholder{ - color: #000; -} -@media only screen and (max-width: 400px){ - .arcgisSearch .searchGroup .searchInput{ - width:130px; - } -} -@media only screen and (min-width: 800px) { - .arcgisSearch .searchGroup .searchInput { - display: block; } } - -.arcgisSearch .searchClear { - background: none; - background-color: inherit; - color: #a8a8a8; } - -.arcgisSearch .searchBtn { - background-color: transparent; - background-color: rgba(0, 0, 0, 0.3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); - color: #a8a8a8; } - -.notools div { - width: 30px; - height: 30px; - line-height: 25px; - font-size: 24px; } - -.esriSimpleSliderTL { - top: 10px; - left: 10px; - border: none; } - -.esriSimpleSliderDecrementButton:hover, .esriSimpleSliderIncrementButton:hover { - background-color: transparent; - background-color: rgba(0, 0, 0, 0.3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); - zoom: 1; } - -.esriSimpleSliderVertical .esriSimpleSliderIncrementButton { - border: none !important; } - -.simpleGeocoder .esriGeocoder { - display: block; - width: 100%; - margin: 0; - border: transparent; - background-color: transparent; - background-color: rgba(0, 0, 0, 0.3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); - zoom: 1; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -ms-border-radius: 5px; - border-radius: 5px; } - -.simpleGeocoder .esriGeocoderMenu { - color: #333; } - -.simpleGeocoder .esriGeocoderResults { - border: transparent; - background-color: transparent; - background-color: rgba(0, 0, 0, 0.6); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000,endColorstr=#99000000); - zoom: 1; } - -.simpleGeocoder .esriGeocoder input { - color: #FFF !important; } - -.simpleGeocoder .esriGeocoderResult:hover, .simpleGeocoder .esriGeocoderResultEven:focus, .simpleGeocoder .esriGeocoderResultOdd:focus { - background-color: transparent; - background-color: rgba(0, 0, 0, 0.3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); - zoom: 1; } - -.simpleGeocoder .esriGeocoder .esriGeocoderMenuArrow { - background: none; } - -.simpleGeocoder .esriGeocoder .esriGeocoderMenuArrow:before { - content: "\e809"; - width: 16px; - height: 16px; - font-size: 16px; - color: #a8a8a8; - font-family: "tool-icons"; } - -.esriMeasurement .esriMeasurementResultLabel { - text-align: left; - font-weight: 500; } - -.esriControlsBR { - position: absolute; - left: 0px; - bottom: 0px; } - -.logo-med { - position: absolute; - left: 0px; - bottom: 0px; } - -.logo-sm { - position: absolute; - left: 0px; - bottom: 0px; } - -.esriAttribution { - position: absolute; - left: 65px; - bottom: 5px; } - -.esriBookmarks { - border: none; } - -.esriBookmarkItem { - width: 300px; - background: #f8f8f8; - color: #555; - border-bottom: 1px solid #e0e0e0; - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - padding: 6px; } - -.esriBookmarks .dojoDndItem{ - border-bottom: none; -} -.esriBookmarkLabel { - font-size: 16px; } - -.esriBookmarkHighlight { - background-color: transparent; - background-color: rgba(0, 0, 0, 0.2); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#33000000,endColorstr=#33000000); - zoom: 1; } - -.esriBasemapGallery { - padding: 20px; } - -.scalebar_bottom-left { - left: 25px; - bottom: 40px; } - -.HomeButton .home { - padding: 0 !important; - width: 24px; - height: 24px; - line-height: 24px; - font-size: 20px; - background-color: transparent; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - cursor: pointer; - -webkit-border-radius: 13px; - -moz-border-radius: 13px; - -ms-border-radius: 13px; - border-radius: 13px; - background-size: 14px 14px; - background-image: none; } - -.no-touch .HomeButton .home:hover, .LocateButton .zoomLocateButton:hover { - background-color: none !important; } - -.scalebar_bottom-left { - left: 25px; - bottom: 40px; } - -.LocateButton .zoomLocateButton:before ,.HomeButton .home:before { - font-size: 18px; - width: 24px; - height: 24px; - line-height: 24px; - position: absolute; - font-family: "tool-icons"; - text-align: center; - speak: none; -} -.HomeButton .home:before { - content: "\e800"; -} - -.LocateButton .zoomLocateButton:before { - content: "\e630"; - } - -.LocateButton .zoomLocateButton { - padding: 0; - width: 24px; - height: 24px; - font-size: 20px; - line-height: 24px; - background-color: transparent; - background-image: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - cursor: pointer; - -webkit-border-radius: 13px; - -moz-border-radius: 13px; - -ms-border-radius: 13px; - border-radius: 13px; } - -img { - border: 0; -} -.LocateButton .tracking { - background-image: none !important; - color:rgba(0,0,0,0.3); - font-size:20px; -} - -/*custom tooltips for toolbar tools. Test on early IE*/ -[data-title]:hover:after { - content: attr(data-title); - padding: 4px 8px; - color: #fff; - font-size: 12px; - font-weight: bold; - white-space: nowrap; - z-index: 99; - -webkit-box-shadow: 0px 0px 4px #222; - -moz-box-shadow: 0px 0px 4px #222; - box-shadow: 0px 0px 4px #222; - background-color: transparent; - background-color: rgba(0, 0, 0, 0.5); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#80000000,endColorstr=#80000000); - zoom: 1; } - -/* Template */ -.bg { - display: block; } - -.fc { - font-weight: bold; } - -.rounded { - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -ms-border-radius: 5px; - border-radius: 5px; } - -.roundedTop { - -moz-border-radius: 5px 5px 0 0; - -webkit-border-radius: 5px 5px 0 0; - border-radius: 5px 5px 0 0; } - -.shadow { - -webkit-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); - box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); } - -#panelContent { - font-family: Helvetica, Arial, sans-serif; - position: absolute; - right: 15px; - top: 0; - height: 100%; - width: 0px; - display: block; - background-color: transparent; - background-color: rgba(0, 0, 0, 0.5); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#80000000,endColorstr=#80000000); - zoom: 1; } - -#panelPages { - width: 100%; - height: 100%; - background-color: transparent; - background-color: rgba(255, 0, 0, 0.5); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#80FF0000,endColorstr=#80FF0000); - zoom: 1; } - -.page { - overflow: visible; - position: relative; - width: 100%; - height: 100%; - display: block; - background-color: transparent; - background-color: rgba(0, 0, 0, 0.5); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#80000000,endColorstr=#80000000); - zoom: 1; } - -.pageblank { - width: 0px; - height: 100%; } - -.pageContent { - position: absolute; - left: 10px; - right: 10px; - margin-left: -310px; - width: 300px; - display: block; - overflow: hidden; - top: 100px; - background-color: transparent; - background-color: white; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#FFFFFFFF,endColorstr=#FFFFFFFF); - zoom: 1; } - -@media only screen and (min-width: 600px) { - .pageContent { - max-height: 80%; - } } - -@media only screen and (min-width: 1070px) { - .pageContent { - top: 10px; - } } -/*Rules for popup when displayed in panel*/ -.panelPopup{ - display: none; -} -.hidden{ - display:none; -} -.popupHeader{ - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; - font-size:12px; - font-weight: bold; -} -#popupContainer{ - font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; - font-size:12px; - width:100%; - height: 100%; -} -#popupFooter > .action:before{ - content:"\e800"; - font-family: "popup"; -} -#popupFooter > .action:hover:before{ - color:#6b6b6b; -} -#popupFooter > .action{ - padding: 0 10px 10px 10px; - text-decoration: none; - color: #000; -} -.popupTitle{ - width:100%; - padding:10px; - font-size: 12px; - line-height: 16px; -} -.popupButton{ - position: absolute; - text-align: center; - width:16px; - height: 16px; - background:none; - font-size: 12px; - line-height: 16px; - top:10px; - cursor:pointer; -} -.popupButton:hover{ - /*match out of the box hover color for home and locate*/ - background-color: transparent; - background-color: rgba(102, 102, 102, 0.6); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#E6666666,endColorstr=#E6666666); - zoom: 1; - border-radius: 4px; -} -.popupButton.next:before{ - font-family: "popup"; - content:"\e808"; -} -.popupButton.prev{ - right:73px; -} -.popupButton.next{ - right:55px; -} -.popupButton.close{ - right:35px; -} -.popupButton.prev:before{ - font-family: "popup"; - content:"\e809"; -} -.popupButton.close:before{ - font-family: "popup"; - content:"\e806"; -} -#pageBody_popup{ - overflow-x:auto !important; - height:auto; -} -#popupFooter{ - padding:4px; -} -.popupTitle{ - position: absolute; - left:5px; -} -.popupHeader{ - position: relative; - display: block; - width:100%; - height: 40px; - line-height: 40px; -} -.pageHeader { - position: relative; - width: 100%; - height: 90px; - text-align: center; - display: block; } - -.pageTitle { - position: absolute; - left: 80px; - top: 30px; - width: auto; - height: 20px; - font-size: 14px; - color: #ffffff; - line-height: 20px; - overflow: hidden; -} - -.pageHeaderImg { - position: absolute; - left: 5px; - margin-left: 0px; - top: 5px; - width: 60px; - height: 60px; - line-height: 60px; - border: 5px solid rgba(0, 0, 0, 0.1); - -webkit-border-radius: 35px; - -moz-border-radius: 35px; - -ms-border-radius: 35px; - border-radius: 35px; } - -.pageIcon { - position: absolute; - left: 11px; - top: 11px; - width: 40px; - height: 40px; } - -.pageNav{ - color:#ccc; - cursor: pointer; - position: absolute; - right:5px; - display: block; -} -.pageClose { - top: 9px; - } - -.pageUp { - top: 35px; - } - -.pageDown:before, .pageUp:before, .pageClose:before{ - font-family: "tool-icons"; - font-size:12px; - line-height: 14px; - background-color: rgba(0, 0, 0, 0.3); - border-radius: 20px; - padding:4px; -} -.pageDown { - top: 60px; -} -.pageClose:before{ - content:"\e600"; -} -.pageUp:before{ - content: "\e682"; -} -.pageDown:before{ - content: "\e681"; -} -.icons-dark{ - color:#333; -} -.pageBody { - position: relative; - top: 0; - /*display: -moz-box; - display: -webkit-box; - display: box;*/ - display:block; - max-height: 400px; - width:100%; - overflow-x:hidden; - overflow-y:auto; - color: #000000; -} - -#pageBody_print { - padding: 10px; - height:120px; -} - -#pageBody_legend { - padding-top: 20px; -} -@media only screen and (max-width: 480px) { - #pageBody_legend { - max-height:250px; - } - .pageBody{ - max-height: 250px; - } -} -.legend{ - margin:auto; - max-width: 90%; -} - - -#pageBody_share { - padding: 5px 20px; - overflow-y: auto; -} - -.desc { - padding: 10px; } - -#pageBody_details { - padding: 5px; - max-width: 288px; -} - -#panelTop { - position: fixed; - left: 0px; - top: 0px; - width: 100%; - color: #ffffff; - padding: 0px; - display: block; - overflow: visible; - height: auto; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; -} - -@media only screen and (min-width: 760px) { - #panelTop { - width: auto; - left: 44px; - top: 10px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -ms-border-radius: 5px; - border-radius: 5px; - } - - #panelLogo { - margin-left: 5px; - display:block !important; - overflow: hidden; - float: left; - line-height: 50px; - width:50px; - height: 50px; - } - - #panelLogo img { - vertical-align: middle; - line-height: inherit; - max-width: 45px; - max-height: 45px; - } - - } -#panelTitle { - height: 50px; - width: 100%; - float: left; - color: #ffffff; - display: block; - border-bottom: 1px solid rgba(255, 255, 255, 0.5); - font-family: "Helvetica Neue", "Arial", sans-serif; - font-weight: normal; -} - -.no-search #panelText { - display: block; -} -.no-search #panelLogo { - display: block !important; - width: 50px; - height: 50px; - overflow: hidden; - float: left; - line-height: 50px; - margin-left: 5px; -} -.no-search #panelLogo img{ - max-width:45px !important; - max-height:45px !important; -} -#panelLogo{ - display:none; -} -#panelText { - z-index: 40; - display: none; - float: left; - max-width: 196px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - margin-left: 5px; - height:50px; - line-height: 50px; -} -h1, h2{ - margin:0; -} - -#title{ - overflow: hidden; - text-overflow:ellipsis; - height: 30px; - line-height: 30px; - font-size: 1.3em; -} -#subtitle{ - overflow: hidden; - text-overflow:ellipsis; - height: 30px; - line-height: 20px; - font-size: 0.9em; -} -#title.nosubtitle{ - overflow: hidden; - text-overflow:ellipsis; - height: 50px; - line-height: 50px; -} - -@media only screen and (min-width: 720px) { - #panelText { - display: block; - max-width: 300px; - } -} - -@media only screen and (min-width: 800px) { - #panelText { - max-width: 400px; - } -} - -#panelText.nosearch { - display: block; -} -#panelSearch { - float: left; - margin: 10px 0 10px 20px; - height: 30px; -} - -@media only screen and (min-width: 720px) { - #panelSearch { - margin: 10px 4px 10px 20px; - float: right; } } - -#panelTools { - position: relative; - left: 0px; - top: 0px; - min-height: 30px; - width: 100%; - display: block; - clear: both; } - -@media only screen and (min-width: 720px) { - #panelTools { - display: block !important; } } - -#panelMenu { - position: absolute; - top: 10px; - right: 2px; - font-size: 24px; - cursor: pointer; - color: #fff; - display: block; } - -@media only screen and (min-width: 720px) { - #panelMenu { - display: none; } } - -.panelTool { - float: left; - width: 26px; - height: 26px; - margin-top: 2px; - text-align: center; - line-height: 26px; - -webkit-border-radius: 13px; - -moz-border-radius: 13px; - -ms-border-radius: 13px; - border-radius: 13px; } - -/*Added hover to match the out-of-the-box hover for the home and locate buttons*/ -.no-touch .panelTool:hover { - background-color: transparent; - background-color: rgba(102, 102, 102, 0.9); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#E6666666,endColorstr=#E6666666); - zoom: 1; } - -.panelToolActive { - background-color: transparent; - background-color: rgba(0, 0, 0, 0.3); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); - zoom: 1; } - -.tool { - margin: 1px; - width: 24px; - height: 24px; - cursor: pointer; } - -#panelHome { - float: left; - width: 24px; - height: 24px; - margin-left: 1px; - margin-top: 3px; } - -#panelLocate { - float: left; - width: 24px; - height: 24px; - line-height: 24px; - margin-left: 0; - margin-top: 3px; } - -/*Calcite updates keep list and submit issues*/ -.calcite .esriPopup .title { - font-weight: bold; } - -.dijitMenuItemIconChar.dijitCheckedMenuItemIconChar { - display: inline-block; - visibility: hidden; } - -.calcite .dijitMenu { - border: none; } - -.dijitMenuItemLabel { - padding: 0; } - -.dijitChecked .dijitMenuItemIconChar.dijitCheckedMenuItemIconChar { - display: inline-block; - visibility: visible; } - -.no-touch .dijitMenuItem:hover { - background-color: transparent; - background-color: rgba(93, 173, 221, 0.4); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#665DADDD,endColorstr=#665DADDD); - zoom: 1; } - -.calcite .dijitMenuItemLabel { - max-width: 250px; } - -.dijitButtonNode { - margin: 0; - line-height: normal; - vertical-align: middle; - text-align: center; - white-space: nowrap; } - -.esriAttributeInspector .atiButton { - background-image: none; } - -.esriAttributeInspector .atiButton .dijitButtonNode { - padding: 1em !important; - line-height: 16px; - background: #d3d3d3; - /*#5daddd; calcite blue */ - color: #fff; - padding: 0; - border: none; - display: inline-block; - white-space: nowrap; - cursor: pointer; - position: relative; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - border-radius: 4px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); - box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-transition: all 0.5s linear; - -moz-transition: all 0.5s linear; - -ms-transition: all 0.5s linear; - -o-transition: all 0.5s linear; - transition: all 0.5s linear; } - -.esriPrintButton { - padding: 0.5em 1em 0.5em !important; - line-height: 32px; - background: #d3d3d3; - /*#5daddd; calcite blue */ - color: #fff; - padding: 0; - border: none; - display: inline-block; - white-space: nowrap; - cursor: pointer; - position: relative; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - border-radius: 4px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); - box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-transition: all 0.5s linear; - -moz-transition: all 0.5s linear; - -ms-transition: all 0.5s linear; - -o-transition: all 0.5s linear; - transition: all 0.5s linear; } - -.esriPrintButton .esriPrintButton { - background: #d3d3d3; - /*#5daddd; calcite blue */ } - -.esriPrintButton:hover { - background: #a9a9a9; - /*#4997d2; calcite hover */ } - -.calcite .dijitArrowButtonInner { - width: 16px; - height: 16px; } - -.calcite .dijitPopup { - border: solid 1px #d3d3d3; } - -.esriPopup .titlePane { - font-size: 12px; - line-height: 16px; - height: 16px; - padding: 10px; - border: 0; - border-radius: 3px 3px 0 0; - -webkit-border-radius: 3px 3px 0 0; } - -.esriPopup .titlePane .title { - line-height: 16px; } - -.esriPopup .titleButton { - top: 10px; - background: none; } - -.esriPopup .titleButton.prev { - right: 73px; } - -.dj_rtl .esriPopup .titleButton.prev { - left: 73px; - right: auto; } - -.esriPopup .titleButton.next { - right: 55px; } - -.dj_rtl .esriPopup .titleButton.next { - left: 55px; - right: auto; } - -.esriPopup .titleButton.maximize { - right: 31px; } - -.dj_rtl .esriPopup .titleButton.maximize { - left: 31px; - right: auto; } - -.esriPopup .titleButton.restore { - right: 29px; } - -.dj_rtl .esriPopup .titleButton.restore { - left: 29px; - right: auto; } - -.esriPopup .titleButton.close { - right: 10px; } - -.dj_rtl .esriPopup .titleButton.close { - left: 10px; - right: auto; } - -/*Share Dialog updates*/ -.calcite .icon-container { - margin: 10px 0 10px; - display: none; } - -@media only screen and (min-width: 720px) { - .calcite .icon-container { - display: block; } } - -.calcite .map-size-label { - float: left; - font-size: 15px; - margin: 0 5px 0 0; } - -.esriRTL .calcite .map-size-label { - float: right; - margin: 0 0 0 5px; } - -.calcite .share-dialog-icon { - font-size: 30px; - line-height: 30px; - float: left; - color: rgba(0, 0, 0, 0.3); - cursor: pointer; - margin: 0 10px 0 0; - text-decoration: none; - display: block; } - -.calcite .share-dialog-icon:visited { - color: rgba(0, 0, 0, 0.5); } - -.esriRTL .calcite .share-dialog-icon { - float: right; - margin: 0 0 0 10px; } - -.calcite .share-dialog-icon:hover { - color: rgba(0, 0, 0, 0.8); } - -.calcite .share-map-url { - width: 50%; - min-width: 80%; - max-width: 90%; - height: 25px; - font-size: 14px; - color: #777; - border: 1px solid #dcdcdc; - margin: 0 0 10px; } - -.calcite .map-size-container { - margin: 0; - padding: 0 0 20px; } - -.calcite .embed-map-size-clear, .calcite .icon-clear { - clear: both; } - -.calcite .embed-map-size-dropdown { - float: left; } - -.esriRTL .calcite .embed-map-size-dropdown { - float: right; } - -.calcite .share-dialog-subheader { - font-size: 17px; - display: none; } - -@media only screen and (min-width: 720px) { - .calcite .share-dialog-subheader { - display: block; } } - -.calcite .share-dialog-textarea { - width: auto; - max-width: 90%; - width: 80%; - height: 50px; - font-size: 14px; - color: #777; - border: 1px solid #dcdcdc; - margin: 0 0 10px; } - -.share-dialog-extent{ - height:30px; - line-height: 30px; -} -input[type=checkbox]{ - font-size:1em; -} - -/*Icon Fonts*/ -@font-face { - font-family: 'tool-icons'; - src: url('../fonts/tool-icons.eot?3smunh'); - src: url('../fonts/tool-icons.eot?3smunh#iefix') format('embedded-opentype'), url('../fonts/tool-icons.woff?3smunh') format('woff'), url('../fonts/tool-icons.ttf?3smunh') format('truetype'), url('../fonts/tool-icons.svg?3smunh#tool-icons') format('svg'); - font-weight: normal; - font-style: normal; } - -/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ -/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ -/* -@media screen and (-webkit-min-device-pixel-ratio:0) { - @font-face { - font-family: 'tool-icons'; - src: url('../fonts/tool-icons.svg?23344103#tool-icons') format('svg'); } -} -*/ -[class^="icon-"]:before, [class*=" icon-"]:before { - font-family: "tool-icons"; - font-style: normal; - font-weight: normal; - speak: none; - display: inline-block; - text-decoration: inherit; - width: 1em; - margin-right: 0.2em; - text-align: center; - /* opacity: .8; */ - /* For safety - reset parent styles, that can break glyph codes*/ - font-variant: normal; - text-transform: none; - /* fix buttons height, for twitter bootstrap */ - line-height: 1em; - /* Animation center compensation - margins should be symmetric */ - /* remove if not needed */ - margin-left: 0.2em; - /* you can be more comfortable with increased icons size */ - /* font-size: 120%; */ - /* Uncomment for 3D effect */ - /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } - -.icon-home-1:before { - content: '\e800'; } - -/* '' */ -.icon-twitter:before { - content: '\e802'; } - -/* '' */ -.icon-facebook-squared:before { - content: '\e803'; } - -/* '' */ -.icon-gplus:before { - content: '\e804'; } - -/* '' */ -.icon-mail-alt:before { - content: '\e805'; } - -/* '' */ -.icon-link:before { - content: '\e806'; } - -/* '' */ -.icon-buffer:before { - content: '\e80b'; } - -/* ' ' */ -.icon-target:before { - content: "\e630"; -} -/* ' ' */ -.icon-down:before { - content: '\681'; } - -.icon-up:before { - content: "\e682"; -} -.icon-close:before { - content: "\e600"; -} -/*Styles for the modal dialog (splash screen)*/ - -#modal { - z-index:1000; - position: absolute; - top: 50%; - left: 50%; - margin: -100px 0 0 -20%; - opacity: 1; - visibility: visible; - width: 40%; - box-shadow: 0 3px 7px rgba(0,0,0,.25); - box-sizing: border-box; - transition: all .4s ease-in-out; - -moz-transition: all .4s ease-in-out; - -webkit-transition: all .4s ease-in-out; - overflow:visible; -} -#modal.hide-modal{ - top: -50%; - visibility: hidden; - opacity: 0; -} -#modal .modal-header{ - border-bottom: 1px solid #ccc; - border-radius: 5px 5px 0 0 -} -#modal h2 { - margin: 0; -} -#modal .copy{ - color: #333; - background-color:#fff; -} -#modal .copy, #modal .modal-header { - padding: 10px; -} -.modal-content { - position: relative; - z-index: 20; -} -#modal .overlay { - position: fixed; - left: 0; - top: 0; - height: 100%; - width: 100%; - z-index: 10 -} - -#closeOverlay{ - display: inline-block; - position: absolute; - right:2px; - top:2px; - height:36px; - width:36px; - font-size:24px; - line-height: 24px; -} -.tool-btn{ - text-align: center; - margin:0; - background: transparent; - border: none !important; - cursor:pointer; - border-radius:4px; -} \ No newline at end of file +#loadingTitle { + display:none; +} + +blockquote { + display: inherit; + -webkit-margin-before: 0; + -webkit-margin-after: 0; + -webkit-margin-start: 0; + -webkit-margin-end: 0; + font-style: italic; +} + +.arcgisSearch .showNoResults .noResultsMenu { + width: calc(100% - 6px); + margin: 0 2px; +} + +.TableOfContents { + width: auto; +} + +.TableOfContents .toc-title { + border-bottom: 1px solid gray; + font-size: 16px; + line-height: 16px; + -webkit-user-select: none; + -moz-user-select: none; + margin: 1px 2px; +} + +.TableOfContents .toc-title-container { + background: none; + color: #555; + position: relative; +} + +.esriRTL label.checkbox { + float: right; +} + +.esriRTL .checkbox { + float: right; +} + +.toc-settings { + cursor: pointer; + float: right; + margin-top: 5px; + opacity: 0.15; + position: absolute; + margin-left: 264px; +} + +.toc-settings img{ + width:16px; + height:16px; +} + +.toc-settings:hover { + opacity: 1; +} + +.toc-settings:active { + color: #97cbec; } + +.esriRTL .toc-settings { + margin-right: 0; + float: left; + margin-left: 15px; } + +.toc-account { + margin-left: 4px; + cursor: pointer; + text-decoration: none; } + +.esriRTL .toc-account { + margin-left: 0; + margin-right: 4px; } + +.clear { + clear: both; + /*width: 22px; + height: 22px; + background-color: silver; + display: inline-block; + position: absolute; + right: 10px; + top: 8px;*/ +} + +.modal-scrollbar { + margin-right: 17px; } + +/* Fonts */ +@font-face { + font-family: "HelveticaNeue"; + src: url('../fonts/helveticaneue.eot'); + src: url('../fonts/helveticaneue.eot?#iefix') format('embedded-opentype'), url('../fonts/helveticaneue.woff') format('woff'), url('../fonts/helveticaneue.ttf') format('truetype'), url('../fonts/helveticaneue.svg#HelveticaNeue') format('svg'); + font-style: normal; + font-weight: normal; +} + +@font-face { + font-family: "HelveticaNeueUltraLight"; + src: url('../fonts/helveticaneueultralight.eot'); + src: url('../fonts/helveticaneueultralight.eot?#iefix') format('embedded-opentype'), url('../fonts/helveticaneueultralight.woff') format('woff'), url('../fonts/helveticaneueultralight.ttf') format('truetype'), url('../fonts/helveticaneueultralight.svg#HelveticaNeueUltraLight') format('svg'); + font-style: normal; + font-weight: normal; +} + +#mapDiv { + width: calc(100% - 0px) !important; + height: calc(100% - 0px) !important; + /*padding: 1px;*/ +} + +.map .container { + /*padding:1px; + background-color: transparent !important;*/ +} + +.loading-indicator { + /* Loading status */ + display: none; + height: 100%; + width: 100%; + z-index: 500; + position: absolute; + top: 0; + left: 0; + overflow: hidden; + background: #fff; +} + +.app-loading .loading-indicator, .app-error .loading-indicator { + /* Show this class when loading or error */ + display: block; +} + +.app-loading .loading-indicator { + /* Loading image */ + background-position: center center; + background-image: url(../images/ajax-loader.gif); + background-repeat: no-repeat; +} + +.app-error .loading-message { + /* Error status */ + background-position: top center; + background-image: url(../images/error.png); + background-repeat: no-repeat; + padding-top: 60px; + width: 100%; + position: absolute; + z-index: 2; + top: 75%; + left: 0; + text-align: center; +} + +/*Layer list dialog*/ +.layer-menu { + width: 100%; +} + +.layer-menu .dijitMenuItem:hover { + background: #d3d3d3; +} + +.layer-menu .dijitMenuItemSelected { + background: #d3d3d3; +} + +/* Esri Javascript API*/ +.templatePicker { +} + +.esriEditor { + height: 100%; + width: 100%; + /* position: absolute; */ +} + +.esriEditor .esriDrawingToolbar { + right: 0; + left: 0; + top: 10px; +} + +.esriPopup .pointer.bottom, .esriPopup .pointer.bottomLeft, .esriPopup .pointer.bottomRight { + background: #f7f7f7 !important; +} + +.esriPopupMaximized { + top: 100px !important; +} + +.esriPopup.light .actionsPane .action.zoomTo { + background-position: 0 -224px; +} + +.esriPopup .actionsPane .action { + margin-right: 10px; +} + +.esriPopup.light .actionsPane .action { + position: absolute; + cursor: pointer; + background-image: url("http://js.arcgis.com/3.12/esri/css/calcite/images/popup-sprite.png"); + width: 12px; + height: 12px; + text-indent: -9999px; +} + +.esriPrintout a { + color: #5daddd; +} + +.esriMeasurement .esriMeasurementButtonPane { + padding-left: 6px; +} + +.esriMeasurement .esriMeasurementResultLabel { + padding-left: 4px; +} + +.esriMeasurement .areaIcon { + background-image: url(../images/area_measure.png); + width: 28px; + height: 28px; +} + +.esriMeasurement .distanceIcon { + background-image: url(../images/dist_measure.png); + width: 28px; + height: 28px; +} + +.esriMeasurement .locationIcon { + background-image: url(../images/dist_point.png); + width: 28px; + height: 28px; +} + +.esriMeasurement .esriButtonHover .dijitButtonNode { + background: #a9a9a9; +} + +.esriMeasurement .esriButtonChecked .dijitButtonNode { + background: #a9a9a9; +} + +.no-touch .esriMeasurement .esriButton .dijitButtonNode:hover { + background: #a9a9a9; +} + +.locationIcon { + width: 32px; + height: 32px; +} + +/*.esriSimpleSlider { + text-align: center; + color: #ffffff; + background-color: transparent; + background-color: rgba(255, 255, 255, 0.8); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#CCFFFFFF,endColorstr=#CCFFFFFF); + zoom: 1; + -webkit-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); + box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); + cursor: pointer; + display: none; +} + +@media only screen and (min-width: 760px) {*/ + .esriSimpleSlider { + display: block; + } +/*}*/ + +.notools div { + width: 30px; + height: 30px; + line-height: 25px; + font-size: 24px; +} + +/*.esriSimpleSliderTL { + top: 10px; + left: 10px; + border: none; +} + +.esriSimpleSliderDecrementButton:hover, .esriSimpleSliderIncrementButton:hover { + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); + zoom: 1; +} + + +.esriSimpleSliderVertical .esriSimpleSliderIncrementButton { + border-bottom: 1px solid rgba(255, 255, 255, 0.5); +} + +.esriSimpleSliderVertical .esriSimpleSliderIncrementButton input[type='image'], +.esriSimpleSliderVertical .esriSimpleSliderHomeButton input[type='image'], +.esriSimpleSliderVertical .esriSimpleSliderDecrementButton input[type='image'] { + border-bottom: 0; + width: 20px; + height: 20px; +}*/ + +.simpleGeocoder .esriGeocoder { + display: block; + width: 100%; + margin: 0; + border: transparent; + background-color: transparent; + background-color: rgba(0, 0, 0, 0.3); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); + zoom: 1; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -ms-border-radius: 5px; + border-radius: 5px; +} + +.simpleGeocoder .esriGeocoderMenu { + color: #333; +} + +.simpleGeocoder .esriGeocoderResults { + border: transparent; + background-color: transparent; + background-color: rgba(0, 0, 0, 0.6); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000,endColorstr=#99000000); + zoom: 1; +} + +.simpleGeocoder .esriGeocoder input { + color: #FFF !important; +} + +.simpleGeocoder .esriGeocoderResult:hover, .simpleGeocoder .esriGeocoderResultEven:focus, .simpleGeocoder .esriGeocoderResultOdd:focus { + --background-color: rgba(0, 0, 0, 0.3); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); + zoom: 1; +} + +.simpleGeocoder .esriGeocoder .esriGeocoderMenuArrow { + background: none; +} + +.simpleGeocoder .esriGeocoder .esriGeocoderMenuArrow:before { + content: "\e809"; + width: 16px; + height: 16px; + font-size: 16px; + color: #a8a8a8; + font-family: "tool-icons"; +} + +.esriMeasurement .esriMeasurementResultLabel { + text-align: left; + font-weight: 500; +} + +.esriControlsBR { + position: absolute; + left: 0px; + bottom: 0px; +} + +.logo-med { + position: absolute; + left: 0px; + bottom: 0px; +} + +.logo-sm { + position: absolute; + left: 0px; + bottom: 0px; +} + +.esriAttribution { + position: absolute; + left: 65px; + bottom: 5px; +} + +.esriBookmarks { + border: none; + width: 100%; +} + +.esriBookmarkItem { + width: calc(100% - 16px); + background: #f8f8f8; + color: #555; + border-bottom: 1px solid #e0e0e0; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + padding: 5px 3px 5px 12px; +} + +.esriBookmarkLabel { + font-size: 16px; + text-overflow: ellipsis; + color: inherit; + margin-left:0; + padding-left:0; + width: 100%; +} + +.esriBookmarkHighlight { + background-color: none !important; + border-radius: 5px; + -webkit-border-radius: 5px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#33000000,endColorstr=#33000000); +} + +.esriBasemapGallery { + padding: 10px 0 10px 10px; +} + +.scalebar_bottom-left { + left: 25px; + bottom: 40px; +} + +.scalebar_bottom-left { + left: 25px; + bottom: 40px; +} + +img { + border: 0; +} + +/*custom tooltips for toolbar tools. Test on early IE*/ +[data-title]:hover:after { + content: attr(data-title); + padding: 4px 8px; + color: #fff; + font-size: 12px; + font-weight: bold; + white-space: nowrap; + z-index: 99; + -webkit-box-shadow: 0px 0px 4px #222; + -moz-box-shadow: 0px 0px 4px #222; + box-shadow: 0px 0px 4px #222; + background-color: transparent; + background-color: rgba(0, 0, 0, 0.5); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#80000000,endColorstr=#80000000); +} + +/*.esriSimpleSliderIncrementButton[data-title]:hover:after { + line-height: 11; +} + +.esriSimpleSliderDecrementButton[data-title]:hover:after { + line-height: 0; +}*/ + +/* Template +.bg { +} */ + +.rounded { + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -ms-border-radius: 5px; + border-radius: 5px; +} + +.roundedTop { + -moz-border-radius: 5px 5px 0 0; + /* -webkit-border-radius: 5px 5px 0 0; */ + /* border-radius: 5px 5px 0 0; */ +} + +.shadow { + -webkit-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); + box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); +} + +#panelContent { + font-family: Helvetica, Arial, sans-serif; + /* background-color: rgba(0, 0, 0, 0.5); */ + /* position: relative; */ +} + +#panelPages { + font-family: Helvetica, Arial, sans-serif; + width: calc(100% - 2px); + height: calc(100% - 155px); + background-color: #F9F9F9; + overflow: auto; + padding: 1px; + background: #aaa; +} + +.page { + overflow: hidden; + background-color: transparent; + /* background-color: rgba(0, 0, 0, 0.5); */ + height: calc(100% - 22px); + height: 100%; + position: relative; + top: 0; +} + +.pageContent { + background-color: white; + margin: 0; + height: 100%; + width: 100%; + top: 0; +} + +.pageHeader { + position: relative; + width: calc(100% + 2px); + /*height: 25px;*/ +} + +.pageTitle { + display: inline-block; + font-size: 12px; + font-weight: bolder; + overflow: hidden; + padding: 0 4px; + margin: 0; + -webkit-margin-before: 0px; + -webkit-margin-after: 0px; + vertical-align: baseline; +} + +@media only screen and (min-width: 720px) { + .pageTitle { + font-size: 18px; + font-weight: bold; + } +} + +.pageHeaderImg { + position: absolute; + left: 5px; + margin-left: 0px; + top: 11px; + width: 60px; + height: 60px; + line-height: 60px; + border: 5px solid rgba(0, 0, 0, 0.1); + -webkit-border-radius: 35px; + -moz-border-radius: 35px; + -ms-border-radius: 35px; + border-radius: 35px; +} + +.pageIcon { + position: absolute; + left: 11px; + top: 11px; + width: 40px; + height: 40px; +} + +.pageBody { + width: 100%; + height: calc(100% - 34px); + color: #000000; + overflow: hidden; + overflow-y: auto; + background-color: transparent; + display: inline-block; + position: relative; +} + +#pageBody_details { + display: flex; + flex-direction: column; + justify-content: space-around; +} + +@media only screen and (min-width: 720px) { + .pageBody { + width: 100%; + height: calc(100% - 52px); + } +} + +.esriPrint { + padding: 0px; + padding-bottom: 16px; +} + +#panelTop { + width: 100%; +} + +.no-search #panelText { + display: block; +} + +#panelText { + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + overflow: hidden; + display: block; +} + +@media only screen and (min-width: 720px) { + #panelText { + font-size: 28px; + } +} + +#panelText:focus { + white-space: inherit; + /*font-size: 14px;*/ +} + +#panelTitle h1 { + padding: 6px; + margin: 0; + -webkit-margin-before: 0; + -webkit-margin-after: 0; + -webkit-margin-start: 0px; + -webkit-margin-end: 0px; +} + +#panelText.nosearch { + display: block; +} + +#panelTools { + left: 0px; + top: 0px; + min-height: 16px; + width: calc(100% - 2px); + display: block; + clear: both; +} + +@media only screen and (min-width: 720px) { + #panelTools { + min-height: 26px; + } +} + +#panelMenu { + position: absolute; + top: 10px; + right: 2px; + font-size: 24px; + cursor: pointer; + color: #fff; + display: none; +} + +.panelTool { + float: left; + padding: 1px; + border-color: transparent; + background-color: transparent; + position: relative; + cursor: pointer; + background: transparent; +} + +.panelTool input[type="image"] { + width: 14px; + height: 14px; + float: left; +} + +@media only screen and (min-width: 720px) { + .panelTool input[type="image"] { + width: 24px; + height: 24px; + float: left; + } +} + +/*Added hover to match the out-of-the-box hover for the home and locate buttons*/ +.no-touch .panelTool:hover { + --background-color: rgba(0, 0, 0, 0.3); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#E6666666,endColorstr=#E6666666); +} + +.tool { + /* margin: 1px; */ + /* width: 24px; */ + /* height: 24px; */ + cursor: pointer; + } + +#panelLocate { + float: left; + margin-left: 0px; +} + +/*Calcite updates keep list and submit issues*/ +.calcite .esriPopup .title { + font-weight: bold; } + +.dijitMenuItemIconChar.dijitCheckedMenuItemIconChar { + display: inline-block; + visibility: hidden; } + +.calcite .dijitMenu { + border: none; } + +.dijitMenuItemLabel { + padding: 0; } + +.dijitChecked .dijitMenuItemIconChar.dijitCheckedMenuItemIconChar { + display: inline-block; + visibility: visible; } + +.no-touch .dijitMenuItem:hover { + --background-color: rgba(0, 0, 0, 0.8); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#665DADDD,endColorstr=#665DADDD); + zoom: 1; } + +.calcite .dijitMenuItemLabel { + max-width: 250px; } + +.dijitButtonNode { + margin: 0; + line-height: normal; + vertical-align: middle; + text-align: center; + white-space: nowrap; } + +.esriAttributeInspector .atiButton { + background-image: none; } + +.esriAttributeInspector .atiButton .dijitButtonNode { + padding: 1em !important; + line-height: 16px; + background: #d3d3d3; + /*#5daddd; calcite blue */ + color: #fff; + padding: 0; + border: none; + display: inline-block; + white-space: nowrap; + cursor: pointer; + position: relative; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + border-radius: 4px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); + box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transition: all 0.5s linear; + -moz-transition: all 0.5s linear; + -ms-transition: all 0.5s linear; + -o-transition: all 0.5s linear; + transition: all 0.5s linear; } + +.esriPrintButton { + padding: 0.5em 1em 0.5em !important; + line-height: 32px; + background: #d3d3d3; + /*#5daddd; calcite blue */ + color: #fff; + padding: 0; + border: none; + display: inline-block; + white-space: nowrap; + cursor: pointer; + position: relative; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + border-radius: 4px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); + box-shadow: 1px 2px 1px rgba(0, 0, 0, 0.3); + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transition: all 0.5s linear; + -moz-transition: all 0.5s linear; + -ms-transition: all 0.5s linear; + -o-transition: all 0.5s linear; + transition: all 0.5s linear; } + +.esriPrintButton .esriPrintButton { + background: #d3d3d3; + /*#5daddd; calcite blue */ } + +.esriPrintButton:hover { + --background: #a9a9a9; + /*#4997d2; calcite hover */ +} + +.calcite .dijitArrowButtonInner { + width: 16px; + height: 16px; + } + +.calcite .dijitPopup { + border: solid 1px #d3d3d3; } + +.esriPopup .titlePane { + font-size: 12px; + line-height: 16px; + height: 16px; + padding: 10px; + border: 0; + border-radius: 3px 3px 0 0; + -webkit-border-radius: 3px 3px 0 0; } + +.esriPopup .titlePane .title { + line-height: 16px; } + +.esriPopup .titleButton { + top: 10px; + background: none; } + +.esriPopup .titleButton.prev { + right: 73px; } + +.dj_rtl .esriPopup .titleButton.prev { + left: 73px; + right: auto; } + +.esriPopup .titleButton.next { + right: 55px; } + +.dj_rtl .esriPopup .titleButton.next { + left: 55px; + right: auto; } + +.esriPopup .titleButton.maximize { + right: 31px; } + +.dj_rtl .esriPopup .titleButton.maximize { + left: 31px; + right: auto; } + +.esriPopup .titleButton.restore { + right: 29px; } + +.dj_rtl .esriPopup .titleButton.restore { + left: 29px; + right: auto; } + +.esriPopup .titleButton.close { + right: 10px; } + +.dj_rtl .esriPopup .titleButton.close { + left: 10px; + right: auto; } + +/*Share Dialog updates*/ +.calcite .icon-container { + margin: 10px 0 10px; + display: none; } + +/*@media only screen and (min-width: 720px) {*/ + .calcite .icon-container { + display: block; } +/*}*/ + +.calcite .map-size-label { + float: left; + font-size: 15px; + margin: 0 5px 0 0; } + +.esriRTL .calcite .map-size-label { + float: right; + margin: 0 0 0 5px; } + +.calcite .share-dialog-icon { + --font-size: 30px; + --line-height: 30px; + float: left; + --color: rgba(0, 0, 0, 0.3); + cursor: pointer; + margin: 0 10px 0 0; + text-decoration: none; + display: block; + height: 32px; + /* opacity: 0.5; */ +} + +.calcite .share-dialog-icon:hover, .calcite .share-dialog-icon:focus { + opacity: 1; +} + +.calcite .share-dialog-icon:visited { + color: rgba(0, 0, 0, 0.5); } + +.esriRTL .calcite .share-dialog-icon { + float: right; + margin: 0 0 0 10px; } + +.calcite .share-dialog-icon:hover { + color: rgba(0, 0, 0, 0.8); +} + +.calcite .share-map-url { + width: calc(100% - 0px) !important; + height: 24px; + font-size: 14px; + color: #777; + /* border: 1px solid #dcdcdc; */ + margin: 0 0 10px 0; + padding-left:1px; + padding-right:1px; +} + +.calcite .share-dialog-subheader { + margin-bottom: 0; +} + +.calcite .map-size-container { + margin: 0; + padding: 0 0 20px; } + +.calcite .embed-map-size-clear, .calcite .icon-clear { + clear: both; } + +.calcite .embed-map-size-dropdown { + float: left; } + +.esriRTL .calcite .embed-map-size-dropdown { + float: right; } + +.calcite .share-dialog-subheader { + font-size: 17px; + display: none; } + +/*@media only screen and (min-width: 720px) {*/ + .calcite .share-dialog-subheader { + display: block; } +/*}*/ + +.calcite .share-dialog-textarea { + max-width: calc(100% - 0px) !important; + margin: 0 !important; + width: calc(100% - 0px) !important; + height: 100px; + font-size: 14px; + color: #777; + /* border: 1px solid #dcdcdc; */ + margin: 0 0 10px; +} + +.share-dialog-checkbox { + display: inline-block; + cursor: pointer; + padding: 8px; +} + +input[type=checkbox].share-dialog-checkbox { + display: none; +} + +.share-dialog-checkbox:before { + content: ""; + display: inline-block; + width: 18px; + height: 18px; + vertical-align: middle; + background-color: #d3d3d3; + color: #f3f3f3; + text-align: center; + border-radius: 3px; } + +input[type=checkbox]:checked.share-dialog-checkbox + .share-dialog-checkbox:before { + content: "\2713"; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2); + font-size: 15px; } + +.esriBasemapGallery{ + padding:10px 0 10px 10px; +} + +.esriBasemapGalleryNode +{ + margin: 1px 8px !important; + padding: 5px 8px 0px 5px; +} + +.HomeButton:focus, .LocateButton:focus, +.share-dialog-icon +{ + border-radius: 5px; + -webkit-border-radius: 5px; +} + +.esriSimpleSliderDisabledButton +{ + background-color: #CEF6CE; +} + +.HomeButton .home:before{ + /* width:26px; */ + /* height:26px; */ + margin-top: 2px; + content:""; + text-align:center; +} +.LocateButton .zoomLocateButton:before{ + content:""; + font-size:20px; + width:26px; + height:26px !important; + margin-top: 2px; + text-align:center; +} + +.esriBookmarkItem { + /* width: 281px; */ + margin-left: 1px; +} + +.searchBtn .searchIcon, .searchClear .searchIcon { + opacity: 0.5; +} + +.searchBtn:focus .searchIcon, .searchBtn:hover .searchIcon, +.searchClear:focus .searchIcon, .searchClear:hover .searchIcon { + opacity: 1; +} + +.esriMeasurement { + width: 93%; + height: auto; + position: relative; + padding: 10px; +} + +.esriMeasurement .esriButtonChecked .dijitButtonNode, +.esriBasemapGallerySelectedNode +{ + background: transparent; + border-radius: 5px; + -webkit-border-radius: 5px; +} + +.pageClose, .pageUp, .pageDown { + opacity: 0.2; +} + +.pageClose:hover, .pageUp:hover, .pageDown:hover, +.pageClose:focus, .pageUp:focus, .pageDown:focus { + opacity: 1; + outline-style: none; +} + +.homeContainer, .locateContainer { + background-color: transparent; +} + +.arcgisSearch .searchBtn:hover, .arcgisSearch .searchBtn:focus { + opacity: 1; + background-color: rgba(0, 0, 0, 0.3); +} + +.arcgisSearch .searchBtn { + opacity: 0.5; + background-color: rgba(0, 0, 0, 0.3); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); + color: #a8a8a8; +} + +.arcgisSearch .searchBtn:focus, .arcgisSearch .searchClear:focus, +input[type='checkbox']:focus { + /* outline-style: none; */ +} + +#dijit_form_ComboButton_0 { + margin: 0; +} + +.esriBookmarkList { + margin-top: 1px; + margin-bottom: 2px; +} + +.esriBasemapGallerySelectedNode a { + color:white; +} + +.esriMeasurement .esriToggleButton .dijitButtonNode{ + height: 32px; + width: auto; +} + +.esriEditor .esriDrawingToolbar { + top:0px; +} + +#esri_dijit_editing_TemplatePicker_1, +.templatePicker +{ + /* padding: 2px; */ + position: relative; + top: 0; + height: calc(100% - 40px); +} + +.attrName { + text-align: left; + font-weight: normal; + padding: 2px; + white-space: normal; +} + +.rounded { + -moz-border-radius: 5px 5px 5px 5px; + /* -webkit-border-radius: 5px 5px 5px 5px; */ + /* border-radius: 5px 5px 5px 5px; */ +} + +.roundedTop { + -moz-border-radius: 5px 5px 0 0; + /* -webkit-border-radius: 5px 5px 0 0; */ + /* border-radius: 5px 5px 0 0; */ +} + +.roundedBottom { + -moz-border-radius: 0 0 5px 5px; + -webkit-border-radius: 0 0 5px 5px; + border-radius: 0 0 5px 5px; +} + +h3 { + padding:3px; +} + +.esriOverviewMap .ovwContainer { + display: block; + position: absolute; +} + +#panelTool_instructions, +#panelTool_details { + float: right; +} + +.ovwHighlight { + background-color: rgba(0, 0, 0, 0.5); +} + +.dojoxGrid .dojoxGridCell { + width:80px !important; + padding: 3px !important; + font-size: 12px !important; +} + +.dojoxGridContent { + width:auto !important; + height:501px; +} + +.esriDrawingToolbar { + background-color: rgb(3, 126, 175) !important; +} + +.esriViewPopup .editSummary { + color: #883988 !important; +} + +.share-dialog-textarea, .share-map-url { + color: #a12177 !important; +} + +.esriLegendMsg { + margin-bottom: 18px; +} + +html, body { + width: 100%; + height: 100%; + margin: 0; + overflow:hidden; + padding: 0; + font-family: Arial, Helvetica, sans-serif; +} + +#borderContainer { + width: 100%; + height: 100%; +} + +.dijitSplitterV, .dijitGutterV { + background: none; + border: 0; + width: 5px; + margin: 0; +} +.dijitSplitterV .dijitSplitterThumb { + background: gray none; + height: 10%; + left: 0.5px; + width: calc(100% - 1.5px); + margin: 0; + overflow: hidden; + position: absolute; + top: 45%; + border-radius: 2px; +} + +.dj_a11y .dijitSplitterV { + border-left: 1px solid #d3d3d3 !important; + border-right: 1px solid #d3d3d3 !important; +} +.dijitSplitterV { + width: 6px; + margin: 1px; + cursor: col-resize; + -webkit-tap-highlight-color: transparent; +} + +.dijitSplitter { + position: absolute; + /* overflow: hidden; */ + z-index: 10; + background-color: #fff; + border-color: gray; + border-style: solid; + border-width: 0; +} + +.showAttr { + /*display: grid;*/ +} + +.hideAttr { + display:none; + height: 0; +} + +.splitterContent { + overflow-y: hidden !important; +} + +.borderBottom { + border-bottom: 1px solid rgba(255, 255, 255, 0.5); +} + +.borderTop { + border-top: 1px solid rgba(255, 255, 255, 0.5); +} + +#leftPanel{ + min-width: 300px; + height: 100%; + width: 100%; + background-color: white; +} + +@media only screen and (min-width: 720px) { + #leftPanel{ + min-width: 425px; + } +} + +#panelBottom { + position:absolute; + bottom: 0px; + /*color: white;*/ + white-space: nowrap; + width: calc(100% - 4px); + padding: 2px; + font-size: 10px; +} + +@media only screen and (min-width: 720px) { + #panelBottom { + width: calc(100% - 8px); + padding: 4px; + font-size: 16px; + } +} + +#fixContent { + width: calc(100% - 2px); + height: calc(100% - 27px); + /* height: 100%; */ + osition: relative; + /* overflow: hidden; */ +} + +#instructionsDiv { + /*position: absolute;*/ + /*bottom: 0;*/ + padding: 0 5px; + /*max-height: 38%;*/ + overflow-y: auto; + width: calc(100% - 10px); +} + +@media only screen and (min-width: 720px) { + #instructionsDiv { + padding: 0 10px; + width: calc(100% - 20px); + } +} + +.goThereHint { + position: absolute; + padding: 0px 2px !important; + border-radius: 2px; + border-width: 2px; + font-size: 11px !important; + box-shadow: 2px 2px 6px rgba(0, 0, 0, 1); + z-index: 100; + background-color: white; + border-color: black; + border-style: solid; + color: black; + font-family: verdana, helvetica; + /*height: 18px !important;*/ + /*line-height: 18px !important;*/ + overflow: hidden; + display: none; +} + +@media only screen and (min-width: 720px) { + .goThereHint { + padding: 1px 4px !important; + border-radius: 4px; + border-width: 2px; + font-size: 14px !important; + box-shadow: 3px 3px 10px rgba(0, 0, 0, 1); + } +} + +.margin { + /* border: transparent; */ + /* border-width: 10px; */ + /* border-style: solid; */ + /* height: calc(100% - 28px); */ + /* width: calc(100% - 22px); */ +} + +.app-loading .loading-indicator { + /* Loading image */ + background-position: center center; + background-image: url(../images/reload3.gif); + background-repeat: no-repeat; +} + +.small-loading { + display: inline-block; + vertical-align: baseline; +} + +.small-loading img { + width: 14px; + height: 14px; +} + +@media only screen and (min-width: 720px) { + .small-loading img { + width: 20px; + height: 20px; + } +} + +#loadingTitle { + width: 100%; + position: absolute; + z-index: 2; + top: 45%; + left: 0; + text-align: center; +} + +.skip a { + color: black; + background-color: white; + + position: absolute; + top: 2px; + left: 2px; + overflow: hidden; + margin: 0; + width: auto; + height: auto; + padding: 5px 20px; + clip: rect(1px, 1px, 1px, 1px); + box-shadow: 2px 2px 2px 2px rgba(0,0,0,0.5) !important; + z-index: 1000; + font-size: 14px; + font-weight: bold; +} + +@media only screen and (min-width: 720px) { + .skip a { + font-size: 18px; + padding: 10px 40px; + } +} + +.skip a:focus { + /*width: auto; + height: auto; + padding: .5rem 1rem;*/ + clip: auto; +} + +.skip { + /*position: absolute;*/ + white-space: nowrap; + font-size: 18px; +} + +.pageBtn { + background-color: rgb(3, 126, 175); + color: white; + background-blend-mode: darken; + padding: 3px 10px 0px 10px; + border-radius: 4px; +} + +.dataInputBox { + width:120px; +} +._filter._date .dijitArrowButtonInner { + padding-bottom: 6px !important; +} + +._filter._date .dijitInputField { + padding: 2px; + width: 80px; +} + +._filter._number .dijitInputField { + padding: 0px; + width: 100px; +} + +.esriBookmarkTable { + width:100%; +} + +.esriOverviewMap { + margin: 4px; + width: calc(100% - 8px); + height: calc(100% - 8px); +} + +.ovwContainer { + width: 400px !important; + height: 400px !important; + background-color: transparent !important; + left: calc(50% - 200px); + top: calc(50% - 200px); +} + +.legend, .ShareDialog, .PrintDialog { + padding: 10px; +} + +.arcgisSearch .searchInput::-moz-placeholder { + color: #c6c6c1; !important + opacity: 1; +} + +.arcgisSearch .searchInput:-ms-input-placeholder { + color: #c6c6c1; !important +} + +.arcgisSearch .searchInput::-webkit-input-placeholder { + color: #c6c6c1; !important +} + +#detailDiv { + background: white; + overflow-y: auto; + /*position: absolute;*/ + color: black; + width: calc(100% - 20px); + padding: 10px 10px 0 10px; +} + +.detailFull { + height: calc(100% - 10px); +} + +.detailHalf { + /*height: calc(100% - 274px);*/ + /*max-height:60%;*/ + height:auto; +} + +.leftWrapper { + display: flex; + flex-direction: column; +} + +header { + height: auto; +} + +footer { + height: auto; +} + +main { + flex: 1; +} + +.hideLoading { + display:none; +} + +.setIndicator { + width: 15px; + height: 15px; + top: -3px; + right: -6px; + position: absolute; + /*pointer-events: none;*/ + z-index: 1000; +} + + +.navToolBar { + height: auto !important; + border-radius: 5px; +} + +.navToolBar div { + display:block; +} + +.HomeButton .home:hover, +.LocateButton .zoomLocateButton:hover { + background-color: transparent !important; +} + +#extenderNav { + height: 12px; + font-size: 0; + line-height: 0; + padding: 1px 0; +} + +#extenderNav input[type="checkbox"] { + display:none; +} + +#extenderNav input[type="checkbox"] + label .extenderNavCollapse { + display:none !important; +} + +#extenderNav input[type="checkbox"] + label .extenderNavExtend { + display:inline-block !important; +} + +#extenderNav input[type="checkbox"]:checked + label .extenderNavCollapse { + display:inline-block !important; +} + +#extenderNav input[type="checkbox"]:checked + label .extenderNavExtend { + display:none !important; +} + +.disabledBtn { + background-color: rgba(255, 255, 255, 0.5); + position: absolute; +} + +.disabledBtn:focus { + outline-style: none; +} + +#instructionsList { + margin: 0; + -webkit-padding-start: 16px; + font-size: 12px; +} + +@media only screen and (min-width: 720px) { + #instructionsList { + -webkit-padding-start: 20px; + font-size: 14px; + } +} + +.instructionsTitle { + padding: 0; + margin: 2px; + font-size: 14px; +} + +@media only screen and (min-width: 720px) { + .instructionsTitle { + margin: 4px; + font-size: 18px; + } +} + +#panelSearch { + margin: 0; + width:100%; + position: relative; +} + +#panelSearch .searchLabel { + padding: 4px 5px; + float: left; + font-size: 12px; + margin-bottom: 0 !important; +} + +.arcgisSearch .searchIcon { + font-size: 16px; + width: auto; + height: auto; + line-height: 10px; + display: inline-block; +} + +.arcgisSearch .searchIcon img { + width: 14px; + height: 14px; +} + +.arcgisSearch .searchBtn { + padding: 3px; + font-size: 10px; + line-height: 14px; + height: 14px; + -webkit-border-radius: 0 2px 2px 0; + border-radius: 0 2px 2px 0; +} + +.arcgisSearch .searchGroup .searchInput { + height:14px; + width: 155px; + padding: 3px 20px 3px 6px ; + font-size: 12px; + line-height: 16px; + -webkit-border-radius: 2px 0 0 2px; + border-radius: 2px 0 0 2px; +} + +.arcgisSearch .searchGroup .searchInput { + /* width: 298px; */ + /*padding: 6px 17px 6px 18px;*/ + background-color: rgba(0, 0, 0, 0.3); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000); + zoom: 1; + color: #fff; +} + +/*@media only screen and (min-width: 800px) {*/ + .arcgisSearch .searchGroup .searchInput { + /* max-width: 300px; */ + display: block; + } +/*}*/ + +.arcgisSearch .searchClose, .arcgisSearch .searchSpinner { + vertical-align: baseline; + margin-top: 3.5px; + position: absolute; + right: 2px; +} + +.arcgisSearch .searchSpinner { + display: none; +} + +.arcgisSearch .hasMultipleSources .searchToggle { + display: inline-block; + float: left; +} + +.arcgisSearch .hasValue .searchClear { + background-color: inherit; + display: inline-block; + width: 18px; + height: 100%; + top: 0; +} + +@media only screen and (min-width: 720px) { + #panelSearch { + margin: 0; + width:100%; + position: relative; + } + + #panelSearch .searchLabel { + padding: 8px 4px; + float: left; + font-size: 14px; + } + + .arcgisSearch .searchIcon { + width: auto; + height: auto; + } + + .arcgisSearch .searchIcon img { + width: 20px; + height: 20px; + } + + .arcgisSearch .searchBtn { + padding: 6px; + font-size: 16px; + line-height: 20px; + height: 20px; + -webkit-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; + } + + .arcgisSearch .searchGroup .searchInput { + height: 20px; + width: 200px; + padding: 6px 28px 6px 12px; + font-size: 14px; + line-height: 20px; + -webkit-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; + } + + .arcgisSearch .searchClose, .arcgisSearch .searchSpinner { + line-height: 28px; + vertical-align: baseline; + position: absolute; + opacity:0.5; + height: 29px; + } + + .searchClose:focus, .searchClose:hover { + background-color: rgba(0, 0, 0, 0.701961); + opacity:1; + } + + .arcgisSearch .hasValue .searchClear { + width: 28px; + } + +} + +.pageBody h2, .groupLabel { + font-size:14px; + -webkit-margin-before: 0; + -webkit-margin-after: 0; +} + +.esriLegendServiceLabel { + -webkit-margin-before: 0; + -webkit-margin-after: 0; + margin-top: 0; + margin-bottom: 0; + padding: 2px; + margin: 0; +} + +.legend table { + width:100%; + font-size: 14px; +} + +@media only screen and (min-width: 720px) { + .pageBody h2, .groupLabel { + font-size:16px; + } + .legend table { + font-size: 16px; + } + +} + +label.checkbox { + padding: 1.2px 0px; + font-size: 14px; + margin: 0 !important; + border: 2px solid transparent; + vertical-align: text-top; + font-weight: bold; + display: inline-block; +} + +input[type=checkbox].checkbox { + height: 16px; + width: 16px; + border: 1px solid #bababa; + vertical-align: top; + background-color: #fff; + color: #000; + cursor: pointer; +} + +.toc-title-container { + padding: 2px; +} + +@media only screen and (min-width: 720px) { + input[type=checkbox].checkbox { + height: 20px; + width: 20px; + } + + label.checkbox { + padding: 3px 0px; + font-size: 16px; + } + + .toc-title-container { + padding: 6px; + } +} + +.esriSimpleSlider { + border-style: none; +} + +.esriSimpleSliderTL { + top: 10px; + left: 10px; +} + +.esriSimpleSlider div { + width: 20px; + height: 28px; +} + +.navToolBar input[type="image"] { + width: 15px; + height: 15px; +} + +#extenderNav { + height: 8px; + font-size: 0; + line-height: 0; + padding: 1px 0; +} + +#extenderNavLabel img { + width: 18px; + height: 8px; +} + +.HomeButton .home, .LocateButton .zoomLocateButton { + background-color: transparent; + width: inherit; + height: inherit; + padding: 0; + cursor: pointer; + background-image: none; +} + +.homeContainer input, .locateContainer input +{ + height: 22px; + width: 22px; + padding: 1px; +} + +@media only screen and (min-width: 720px) { + .esriSimpleSliderTL { + top: 20px; + left: 20px; + } + + .esriSimpleSlider div { + width: 30px; + height: 40px; + line-height: 40px; + } + + #extenderNav { + height: 12px; + } + + .navToolBar input[type="image"] { + width: 20px; + height: 20px; + } + + #extenderNavLabel img { + width: 24px; + height: 12px; + } +} + +#panelLogo { + overflow: hidden; + line-height: 0; + margin:1px; +} + +#panelLogo img { + max-height: 24px; + padding: 2px; +} + +#panelTitle { + height:28px; +} + +@media only screen and (min-width: 720px) { + #panelLogo img { + max-height: 32px; + padding: 3px; + } + + #panelTitle { + height:45px; + } +} + + diff --git a/css/styles.scss b/css/styles.scss index b04a500e..cf7c3fb6 100644 --- a/css/styles.scss +++ b/css/styles.scss @@ -361,16 +361,12 @@ html, body { border: none; } -.esriSimpleSliderDecrementButton:hover, .esriSimpleSliderIncrementButton:hover { - @include transparent($black-color, 0.3); -} - -.esriSimpleSliderVertical .esriSimpleSliderIncrementButton{ +.esriSimpleSliderVertical .esriSimpleSliderIncrementButton, .esriSimpleSliderVertical .esriSimpleSliderHomeButton{ border:none !important; } -.esriSimpleSliderIncrementButton { - /*border-bottom: 1px solid rgba(255,255,255,0.5) !important;*/ +.esriSimpleSliderDecrementButton:hover, esriSimpleSliderHomeButton:hover, .esriSimpleSliderIncrementButton:hover { + @include transparent($black-color, 0.3); } .simpleGeocoder .esriGeocoder { @@ -483,9 +479,11 @@ html, body { background-size: 14px 14px; background-image:none; } + .no-touch .HomeButton .home:hover, .LocateButton .zoomLocateButton:hover{ background-color:none !important; } + .scalebar_bottom-left { left: 25px; bottom: 40px; @@ -1234,3 +1232,6 @@ img{ .icon-target:before { content: '\e80c'; } /* '' */ .icon-angle-down:before { content: '\e809'; } /* '' */ .icon-check-1:before {content: '\2713';} + + + diff --git a/css/styles1.css b/css/styles1.css new file mode 100644 index 00000000..af0a1b80 --- /dev/null +++ b/css/styles1.css @@ -0,0 +1,86 @@ +li[role='menuitem']:hover, +div[role='button']:not(.searchBtn):not(.searchClear):not(.home):not(.zoomLocateButton):hover, +.no-touch .esriMeasurement .esriButton .dijitButtonNode:hover, +#dijit_form_ComboButton_0_arrow:hover, +button:hover, .navBtn:hover, +.panelToolActive:hover, +.esriMeasurement .esriToggleButton .dijitButtonNode:hover, +.panelTool:hover, .esriBookmarkItem:hover, +.esriSimpleSliderDecrementButton:hover, .esriSimpleSliderHomeButton:hover, .esriSimpleSliderIncrementButton:hover +{ + background-color: rgba(255, 255, 255, 0.5) !important; +} + +/*.HomeButton .home:hover, +.LocateButton .zoomLocateButton:hover { + background-color: rgba(255, 255, 255, 0.5) !important; +} +*/ +/* +INTERNET EXPLORER and FIREFOX : not supported or buggy: +http://reference.sitepoint.com/css/outline-color +*/ +:focus { + /*--outline: -webkit-focus-ring-color auto 5px !important;*/ + outline-width: 2px; + outline-offset: -1px; + outline-style: solid; + outline-color: invert; + /*box-shadow: 0 0 0 4px red inset;*/ + } +} + +.esriSimpleSliderIncrementButton:focus, +.esriSimpleSliderHomeButton:hover, +.esriSimpleSliderDecrementButton:focus{ + outline-offset: 2px; +} + +.homeContainer input:focus, .locateContainer input:focus +{ +} + +.panelTool input[type="image" i]:focus +{ + /*--outline: -webkit-focus-ring-color auto 5px !important;*/ + outline-width: 2px; +} + +.ovwHighlight:focus{ + /*--outline: -webkit-focus-ring-color auto 5px !important;*/ + outline-offset: 2px !important; +} + +#embedMapSizeDropDown:focus { + /*--outline: -webkit-focus-ring-color auto 5px !important; + outline-width: 2px;*/ + outline-style: solid; + /*outline-color: rgba(0, 0, 0, 0.9);*/ +} + +.arcgisSearch .searchMenu li:hover, .arcgisSearch .searchMenu li:focus { + background-color: rgba(0, 0, 0, 0.3); +} + +.activeMarker, +.panelToolActive, +.esriBasemapGallerySelectedNode, +.dijitSplitterThumb { + background-color: rgba(0, 0, 0, 0.7); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#4D000000,endColorstr=#4D000000) !important; +} + +.esriBasemapGallerySelectedNode:hover span { + color: black; + text-align: center; +} + +.goThereHint { +} + +.searchBtn:focus, .searchClear:focus, .searchInput:focus { + background-color: rgba(0, 0, 0, 0.7) !important; +} + + + diff --git a/css/tabs.css b/css/tabs.css new file mode 100644 index 00000000..70e7c9e4 --- /dev/null +++ b/css/tabs.css @@ -0,0 +1,68 @@ +.tabs { + position: relative; + clear: both; + height: calc(100% - 50px); + display: -webkit-box; +} +.tab { +} + +.tab label { + background: #81D6FF; + padding: 5px; + padding-bottom: 4px; + border: 1px solid black; + margin-left: 5px; + position: relative; + border-bottom-left-radius: 8px; + top: 10px; + z-index: 1; +} +.tab [type=radio] {display: none;position: relative;top: calc(100% - 10px);} + +.tab [type=radio] ~ label ~ .tabContent { + display: none; +} + +.tabContent { + position: absolute; + background: transparent; + height: calc(100% - 20px); + width: calc(100% - 22px); +} + +.contentInner { +} + +.tab [type=radio]:checked ~ label { + background: white; + border-top: 1px solid white; +} +.tab [type=radio] ~ .tabContent { + display: none; +} + +.tab [type=radio]:checked ~ .tabContent{ + z-index: 1; + display: inherit; + top: 0; +} + +.tabHide { + display: none; +} + +.tabShow { + display: inherit; +} + +.tab span { + padding:2px; +} + +.Flipped, .Flipped .Content +{ + transform:rotateX(180deg); */ + -ms-transform:rotateX(180deg); /* IE 9 */ + -webkit-transform:rotateX(180deg); */ /* Safari and Chrome */ +} diff --git a/fonts/tool-icons.eot b/fonts/tool-icons.eot old mode 100755 new mode 100644 index ad73b2a6..fd06b416 Binary files a/fonts/tool-icons.eot and b/fonts/tool-icons.eot differ diff --git a/fonts/tool-icons.svg b/fonts/tool-icons.svg old mode 100755 new mode 100644 index 1ee09ae6..2b2cb738 --- a/fonts/tool-icons.svg +++ b/fonts/tool-icons.svg @@ -1,21 +1,23 @@ - + -Generated by IcoMoon +Copyright (C) 2014 by original authors @ fontello.com - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/tool-icons.ttf b/fonts/tool-icons.ttf old mode 100755 new mode 100644 index b8feda05..ab5caa06 Binary files a/fonts/tool-icons.ttf and b/fonts/tool-icons.ttf differ diff --git a/fonts/tool-icons.woff b/fonts/tool-icons.woff old mode 100755 new mode 100644 index b4329834..87716ab7 Binary files a/fonts/tool-icons.woff and b/fonts/tool-icons.woff differ diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100755 index b1fa442b..00000000 --- a/gulpfile.js +++ /dev/null @@ -1,80 +0,0 @@ -var gulp = require("gulp"); -var jshint = require("gulp-jshint"); -var beautify = require("gulp-beautify"); -var sass = require("gulp-sass"); -var compass = require("gulp-compass"); -var cssbeautify = require("gulp-cssbeautify"); -var uncss = require("gulp-uncss"); -var minifycss = require("gulp-minify-css"); -var stylus = require("gulp-stylus"); -var nib = require("nib"); - - -//Define a task. Takes two arguments the name of the task and a function -//which will be run when you call the task -//In this example we specify that we want to run jshint on each .js file -//in the javascript folder and report the results using the jshint reporter. -gulp.task("lint", function(){ - gulp.src("./js/*.js") - .pipe(jshint()) - .pipe(jshint.reporter("default")); -}); - -//Run the beautify task and specify options in this example -//I added a base to overwrite existing files. You could -//specify that results are written to another location by specifying -//a different destination instead. -gulp.task("beautify", function(){ - gulp.src("./js/*.js",{base:"./"}) - .pipe(beautify({indentSize:4})) - .pipe(gulp.dest("./")); -}); - -//Process sass -gulp.task('sass', function () { - gulp.src("./css/*.scss") - .pipe(sass()) - .pipe(sass({errLogToConsole: true})) - .pipe(gulp.dest("./css")); -}); - - -//use nib - -gulp.task('nib', function(){ - gulp.src('./css/*.styl') - .pipe(stylus({ use: nib(), compress: false })) - .pipe(gulp.dest('./css')); -}); - -//Task to remove unused css. I write these updates out to a new folder -//called public so I don't lose any css I may need. -gulp.task("uncss", function(){ - gulp.src("./css/*.css") - .pipe(uncss({ - html: ["index.html"] - })) - .pipe(gulp.dest("./public")); -}); - -//Beautify css -gulp.task("css",function(){ - gulp.src("./css/*.css", {base:"./"}) - .pipe(cssbeautify()) - .pipe(gulp.dest("./public")) -}); - - -//Minify css -gulp.task("minify-css", function(){ - gulp.src("./css/*.css",{base:"./"}) - .pipe(minifycss()) - .pipe(gulp.dest("./")) -}); - - - -//define a default task that will run if you type gulp at the command line - -gulp.task('default', ["lint","beautify","css"]); - diff --git a/images/Disability.50x50.png b/images/Disability.50x50.png new file mode 100644 index 00000000..562521c9 Binary files /dev/null and b/images/Disability.50x50.png differ diff --git a/images/Disability.png b/images/Disability.png new file mode 100644 index 00000000..49c454ec Binary files /dev/null and b/images/Disability.png differ diff --git a/images/Filter0.png b/images/Filter0.png new file mode 100644 index 00000000..e50834b4 Binary files /dev/null and b/images/Filter0.png differ diff --git a/images/Filter1.png b/images/Filter1.png new file mode 100644 index 00000000..2b9a8def Binary files /dev/null and b/images/Filter1.png differ diff --git a/images/ZoomIn.cur b/images/ZoomIn.cur new file mode 100644 index 00000000..590c14f3 Binary files /dev/null and b/images/ZoomIn.cur differ diff --git a/images/ZoomOut.cur b/images/ZoomOut.cur new file mode 100644 index 00000000..1a33a2c0 Binary files /dev/null and b/images/ZoomOut.cur differ diff --git a/images/ajax-loader.gif b/images/ajax-loader.gif old mode 100644 new mode 100755 index 0f49d6a9..e0e6e976 Binary files a/images/ajax-loader.gif and b/images/ajax-loader.gif differ diff --git a/images/animated-ripple-dot-red.gif b/images/animated-ripple-dot-red.gif new file mode 100644 index 00000000..3c603f15 Binary files /dev/null and b/images/animated-ripple-dot-red.gif differ diff --git a/images/caret-down.png b/images/caret-down.png new file mode 100644 index 00000000..fa7a2a4e Binary files /dev/null and b/images/caret-down.png differ diff --git a/images/close.png b/images/close.png index 026ae80b..4cdfeff9 100644 Binary files a/images/close.png and b/images/close.png differ diff --git a/images/down.png b/images/down.png index c41b48fa..37245f88 100644 Binary files a/images/down.png and b/images/down.png differ diff --git a/images/downArrow.png b/images/downArrow.png new file mode 100644 index 00000000..7ea745da Binary files /dev/null and b/images/downArrow.png differ diff --git a/images/favicon.png b/images/favicon.png new file mode 100644 index 00000000..fd90cc50 Binary files /dev/null and b/images/favicon.png differ diff --git a/images/featureSelected.png b/images/featureSelected.png new file mode 100644 index 00000000..62655b26 Binary files /dev/null and b/images/featureSelected.png differ diff --git a/images/flag.CA.22.png b/images/flag.CA.22.png new file mode 100644 index 00000000..9f4ef833 Binary files /dev/null and b/images/flag.CA.22.png differ diff --git a/images/flag.CH.22.png b/images/flag.CH.22.png new file mode 100644 index 00000000..1c51d584 Binary files /dev/null and b/images/flag.CH.22.png differ diff --git a/images/flag.EU.22.png b/images/flag.EU.22.png new file mode 100644 index 00000000..50b976dd Binary files /dev/null and b/images/flag.EU.22.png differ diff --git a/images/flag.FR.22.png b/images/flag.FR.22.png new file mode 100644 index 00000000..8074c5b9 Binary files /dev/null and b/images/flag.FR.22.png differ diff --git a/images/flag.QC.22.png b/images/flag.QC.22.png new file mode 100644 index 00000000..371fe33b Binary files /dev/null and b/images/flag.QC.22.png differ diff --git a/images/flag.UK.22.png b/images/flag.UK.22.png new file mode 100644 index 00000000..f39abc5b Binary files /dev/null and b/images/flag.UK.22.png differ diff --git a/images/flag.US.22.png b/images/flag.US.22.png new file mode 100644 index 00000000..23cc7330 Binary files /dev/null and b/images/flag.US.22.png differ diff --git a/images/flag.on.22.png b/images/flag.on.22.png new file mode 100644 index 00000000..328ad89a Binary files /dev/null and b/images/flag.on.22.png differ diff --git a/images/icon-cog.png b/images/icon-cog.png new file mode 100644 index 00000000..c2d88daa Binary files /dev/null and b/images/icon-cog.png differ diff --git a/images/icons_black/Redo.png b/images/icons_black/Redo.png new file mode 100644 index 00000000..08a9e8b8 Binary files /dev/null and b/images/icons_black/Redo.png differ diff --git a/images/icons_black/Undo.png b/images/icons_black/Undo.png new file mode 100644 index 00000000..a8ecb0a3 Binary files /dev/null and b/images/icons_black/Undo.png differ diff --git a/images/icons_black/ZoomMinus.png b/images/icons_black/ZoomMinus.png new file mode 100644 index 00000000..a1ea2d50 Binary files /dev/null and b/images/icons_black/ZoomMinus.png differ diff --git a/images/icons_black/ZoomPlus.png b/images/icons_black/ZoomPlus.png new file mode 100644 index 00000000..f0e75187 Binary files /dev/null and b/images/icons_black/ZoomPlus.png differ diff --git a/images/icons_black/basemap.png b/images/icons_black/basemap.png index cdb58188..fa65bc0a 100644 Binary files a/images/icons_black/basemap.png and b/images/icons_black/basemap.png differ diff --git a/images/icons_black/bookmarks.png b/images/icons_black/bookmarks.png index 76339c65..225bc5bf 100644 Binary files a/images/icons_black/bookmarks.png and b/images/icons_black/bookmarks.png differ diff --git a/images/icons_black/details.png b/images/icons_black/details.png index a7221ce5..74c4857f 100644 Binary files a/images/icons_black/details.png and b/images/icons_black/details.png differ diff --git a/images/icons_black/down.png b/images/icons_black/down.png new file mode 100644 index 00000000..a0c884aa Binary files /dev/null and b/images/icons_black/down.png differ diff --git a/images/icons_black/downArrow.png b/images/icons_black/downArrow.png new file mode 100644 index 00000000..e78549ce Binary files /dev/null and b/images/icons_black/downArrow.png differ diff --git a/images/icons_black/edit.png b/images/icons_black/edit.png index 6d5a2b1c..ca253e78 100755 Binary files a/images/icons_black/edit.png and b/images/icons_black/edit.png differ diff --git a/images/icons_black/features.png b/images/icons_black/features.png new file mode 100644 index 00000000..b3b54a5d Binary files /dev/null and b/images/icons_black/features.png differ diff --git a/images/icons_black/filter!.png b/images/icons_black/filter!.png new file mode 100644 index 00000000..4ecb9d8d Binary files /dev/null and b/images/icons_black/filter!.png differ diff --git a/images/icons_black/filter.png b/images/icons_black/filter.png new file mode 100644 index 00000000..60268015 Binary files /dev/null and b/images/icons_black/filter.png differ diff --git a/images/icons_black/filterFeatures.png b/images/icons_black/filterFeatures.png new file mode 100644 index 00000000..e51b7de2 Binary files /dev/null and b/images/icons_black/filterFeatures.png differ diff --git a/images/icons_black/home.png b/images/icons_black/home.png new file mode 100644 index 00000000..2cbc40bb Binary files /dev/null and b/images/icons_black/home.png differ diff --git a/images/icons_black/info.png b/images/icons_black/info.png new file mode 100644 index 00000000..32338433 Binary files /dev/null and b/images/icons_black/info.png differ diff --git a/images/icons_black/instructions.png b/images/icons_black/instructions.png new file mode 100644 index 00000000..b6d4dd39 Binary files /dev/null and b/images/icons_black/instructions.png differ diff --git a/images/icons_black/layers.png b/images/icons_black/layers.png index 97b5d58a..b9620acf 100644 Binary files a/images/icons_black/layers.png and b/images/icons_black/layers.png differ diff --git a/images/icons_black/legend.png b/images/icons_black/legend.png index 075e9781..c81f21f4 100644 Binary files a/images/icons_black/legend.png and b/images/icons_black/legend.png differ diff --git a/images/icons_black/locate.png b/images/icons_black/locate.png new file mode 100644 index 00000000..3d72a4b6 Binary files /dev/null and b/images/icons_black/locate.png differ diff --git a/images/icons_black/measure.png b/images/icons_black/measure.png index 4ab1fc8c..14deb41b 100644 Binary files a/images/icons_black/measure.png and b/images/icons_black/measure.png differ diff --git a/images/icons_black/minus.new.png b/images/icons_black/minus.new.png new file mode 100644 index 00000000..5bee0217 Binary files /dev/null and b/images/icons_black/minus.new.png differ diff --git a/images/icons_black/minus.old.png b/images/icons_black/minus.old.png new file mode 100644 index 00000000..80b460c8 Binary files /dev/null and b/images/icons_black/minus.old.png differ diff --git a/images/icons_black/minus.png b/images/icons_black/minus.png new file mode 100644 index 00000000..2d5a921d Binary files /dev/null and b/images/icons_black/minus.png differ diff --git a/images/icons_black/next.png b/images/icons_black/next.png new file mode 100644 index 00000000..050d4d43 Binary files /dev/null and b/images/icons_black/next.png differ diff --git a/images/icons_black/overview.png b/images/icons_black/overview.png index 0aae7360..19a7445e 100644 Binary files a/images/icons_black/overview.png and b/images/icons_black/overview.png differ diff --git a/images/icons_black/plus.new.png b/images/icons_black/plus.new.png new file mode 100644 index 00000000..9fd30316 Binary files /dev/null and b/images/icons_black/plus.new.png differ diff --git a/images/icons_black/plus.old.png b/images/icons_black/plus.old.png new file mode 100644 index 00000000..bcb6cc73 Binary files /dev/null and b/images/icons_black/plus.old.png differ diff --git a/images/icons_black/plus.png b/images/icons_black/plus.png new file mode 100644 index 00000000..041f758e Binary files /dev/null and b/images/icons_black/plus.png differ diff --git a/images/icons_black/prev.png b/images/icons_black/prev.png new file mode 100644 index 00000000..fe26b66f Binary files /dev/null and b/images/icons_black/prev.png differ diff --git a/images/icons_black/print.png b/images/icons_black/print.png index 2ecfe3bb..6c2af3c8 100644 Binary files a/images/icons_black/print.png and b/images/icons_black/print.png differ diff --git a/images/icons_black/searchClear.png b/images/icons_black/searchClear.png new file mode 100644 index 00000000..b71b60b6 Binary files /dev/null and b/images/icons_black/searchClear.png differ diff --git a/images/icons_black/searchZoom.png b/images/icons_black/searchZoom.png new file mode 100644 index 00000000..3db31c9b Binary files /dev/null and b/images/icons_black/searchZoom.png differ diff --git a/images/icons_black/share.png b/images/icons_black/share.png index fd1195ef..0818720f 100644 Binary files a/images/icons_black/share.png and b/images/icons_black/share.png differ diff --git a/images/icons_black/up.png b/images/icons_black/up.png new file mode 100644 index 00000000..fcb11f21 Binary files /dev/null and b/images/icons_black/up.png differ diff --git a/images/icons_white/Redo.png b/images/icons_white/Redo.png new file mode 100644 index 00000000..867be938 Binary files /dev/null and b/images/icons_white/Redo.png differ diff --git a/images/icons_white/Undo.png b/images/icons_white/Undo.png new file mode 100644 index 00000000..f4d992b5 Binary files /dev/null and b/images/icons_white/Undo.png differ diff --git a/images/icons_white/ZoomMinus.png b/images/icons_white/ZoomMinus.png new file mode 100644 index 00000000..4bb7d2c1 Binary files /dev/null and b/images/icons_white/ZoomMinus.png differ diff --git a/images/icons_white/ZoomPlus.png b/images/icons_white/ZoomPlus.png new file mode 100644 index 00000000..b1dce50a Binary files /dev/null and b/images/icons_white/ZoomPlus.png differ diff --git a/images/icons_white/basemap.png b/images/icons_white/basemap.png index 09cdd7ce..915c00cf 100644 Binary files a/images/icons_white/basemap.png and b/images/icons_white/basemap.png differ diff --git a/images/icons_white/bookmarks.png b/images/icons_white/bookmarks.png index f9ba1792..0e58bd2f 100644 Binary files a/images/icons_white/bookmarks.png and b/images/icons_white/bookmarks.png differ diff --git a/images/icons_white/details.png b/images/icons_white/details.png index 2c2e6e46..62ec1321 100644 Binary files a/images/icons_white/details.png and b/images/icons_white/details.png differ diff --git a/images/icons_white/down.png b/images/icons_white/down.png new file mode 100644 index 00000000..f641a300 Binary files /dev/null and b/images/icons_white/down.png differ diff --git a/images/icons_white/downArrow.png b/images/icons_white/downArrow.png new file mode 100644 index 00000000..8e47196a Binary files /dev/null and b/images/icons_white/downArrow.png differ diff --git a/images/icons_white/edit.png b/images/icons_white/edit.png index 847bc50f..92b276e3 100755 Binary files a/images/icons_white/edit.png and b/images/icons_white/edit.png differ diff --git a/images/icons_white/features.png b/images/icons_white/features.png new file mode 100644 index 00000000..33055fc0 Binary files /dev/null and b/images/icons_white/features.png differ diff --git a/images/icons_white/filter!.png b/images/icons_white/filter!.png new file mode 100644 index 00000000..f56998bf Binary files /dev/null and b/images/icons_white/filter!.png differ diff --git a/images/icons_white/filter.png b/images/icons_white/filter.png new file mode 100644 index 00000000..fffc0761 Binary files /dev/null and b/images/icons_white/filter.png differ diff --git a/images/icons_white/filterFeatures.png b/images/icons_white/filterFeatures.png new file mode 100644 index 00000000..43683138 Binary files /dev/null and b/images/icons_white/filterFeatures.png differ diff --git a/images/icons_white/home.png b/images/icons_white/home.png new file mode 100644 index 00000000..cbf749f8 Binary files /dev/null and b/images/icons_white/home.png differ diff --git a/images/icons_white/info.png b/images/icons_white/info.png new file mode 100644 index 00000000..dba86078 Binary files /dev/null and b/images/icons_white/info.png differ diff --git a/images/icons_white/instructions.png b/images/icons_white/instructions.png new file mode 100644 index 00000000..eb08ced1 Binary files /dev/null and b/images/icons_white/instructions.png differ diff --git a/images/icons_white/layers.png b/images/icons_white/layers.png index 88eecacf..97b8717e 100644 Binary files a/images/icons_white/layers.png and b/images/icons_white/layers.png differ diff --git a/images/icons_white/legend.png b/images/icons_white/legend.png index d45eaa1a..c96a4f39 100644 Binary files a/images/icons_white/legend.png and b/images/icons_white/legend.png differ diff --git a/images/icons_white/locate.png b/images/icons_white/locate.png new file mode 100644 index 00000000..29b76728 Binary files /dev/null and b/images/icons_white/locate.png differ diff --git a/images/icons_white/measure.png b/images/icons_white/measure.png index d61027e5..d90e7ad2 100644 Binary files a/images/icons_white/measure.png and b/images/icons_white/measure.png differ diff --git a/images/icons_white/minus.new.png b/images/icons_white/minus.new.png new file mode 100644 index 00000000..3b8482d9 Binary files /dev/null and b/images/icons_white/minus.new.png differ diff --git a/images/icons_white/minus.old.png b/images/icons_white/minus.old.png new file mode 100644 index 00000000..f67f2a3c Binary files /dev/null and b/images/icons_white/minus.old.png differ diff --git a/images/icons_white/minus.png b/images/icons_white/minus.png new file mode 100644 index 00000000..d2dd8089 Binary files /dev/null and b/images/icons_white/minus.png differ diff --git a/images/icons_white/next.png b/images/icons_white/next.png new file mode 100644 index 00000000..97c5a143 Binary files /dev/null and b/images/icons_white/next.png differ diff --git a/images/icons_white/overview.png b/images/icons_white/overview.png index 2fc993e3..c5adf9e9 100644 Binary files a/images/icons_white/overview.png and b/images/icons_white/overview.png differ diff --git a/images/icons_white/plus.new.png b/images/icons_white/plus.new.png new file mode 100644 index 00000000..f136bebe Binary files /dev/null and b/images/icons_white/plus.new.png differ diff --git a/images/icons_white/plus.old.png b/images/icons_white/plus.old.png new file mode 100644 index 00000000..39060062 Binary files /dev/null and b/images/icons_white/plus.old.png differ diff --git a/images/icons_white/plus.png b/images/icons_white/plus.png new file mode 100644 index 00000000..4682d9bd Binary files /dev/null and b/images/icons_white/plus.png differ diff --git a/images/icons_white/prev.png b/images/icons_white/prev.png new file mode 100644 index 00000000..2207fdfa Binary files /dev/null and b/images/icons_white/prev.png differ diff --git a/images/icons_white/print.png b/images/icons_white/print.png index 5ff6edd8..3d074f1f 100644 Binary files a/images/icons_white/print.png and b/images/icons_white/print.png differ diff --git a/images/icons_white/searchClear.png b/images/icons_white/searchClear.png new file mode 100644 index 00000000..f749f533 Binary files /dev/null and b/images/icons_white/searchClear.png differ diff --git a/images/icons_white/searchZoom.png b/images/icons_white/searchZoom.png new file mode 100644 index 00000000..3c08db49 Binary files /dev/null and b/images/icons_white/searchZoom.png differ diff --git a/images/icons_white/share.png b/images/icons_white/share.png index 2c9752ac..cf80d7b3 100644 Binary files a/images/icons_white/share.png and b/images/icons_white/share.png differ diff --git a/images/icons_white/up.png b/images/icons_white/up.png new file mode 100644 index 00000000..3525ee89 Binary files /dev/null and b/images/icons_white/up.png differ diff --git a/images/logo.png b/images/logo.png index a367efab..ff8e306d 100644 Binary files a/images/logo.png and b/images/logo.png differ diff --git a/images/reload.gif b/images/reload.gif new file mode 100644 index 00000000..0ff44674 Binary files /dev/null and b/images/reload.gif differ diff --git a/images/reload1.gif b/images/reload1.gif new file mode 100644 index 00000000..7c6f2572 Binary files /dev/null and b/images/reload1.gif differ diff --git a/images/reload2.gif b/images/reload2.gif new file mode 100644 index 00000000..3ce56db5 Binary files /dev/null and b/images/reload2.gif differ diff --git a/images/reload3.gif b/images/reload3.gif new file mode 100644 index 00000000..37a14274 Binary files /dev/null and b/images/reload3.gif differ diff --git a/images/ripple-dot.gif b/images/ripple-dot.gif new file mode 100644 index 00000000..f7344c62 Binary files /dev/null and b/images/ripple-dot.gif differ diff --git a/images/ripple-dot1.gif b/images/ripple-dot1.gif new file mode 100644 index 00000000..53c58245 Binary files /dev/null and b/images/ripple-dot1.gif differ diff --git a/images/searchClear.png b/images/searchClear.png new file mode 100644 index 00000000..dc427c2a Binary files /dev/null and b/images/searchClear.png differ diff --git a/images/searchZoom.png b/images/searchZoom.png new file mode 100644 index 00000000..497ef5cd Binary files /dev/null and b/images/searchZoom.png differ diff --git a/images/share-email.png b/images/share-email.png new file mode 100644 index 00000000..94da4e8d Binary files /dev/null and b/images/share-email.png differ diff --git a/images/share-facebook.png b/images/share-facebook.png new file mode 100644 index 00000000..5b231f67 Binary files /dev/null and b/images/share-facebook.png differ diff --git a/images/share-gplus.png b/images/share-gplus.png new file mode 100644 index 00000000..e93e787d Binary files /dev/null and b/images/share-gplus.png differ diff --git a/images/share-link.png b/images/share-link.png new file mode 100644 index 00000000..049c45c0 Binary files /dev/null and b/images/share-link.png differ diff --git a/images/share-twitter.png b/images/share-twitter.png new file mode 100644 index 00000000..793b9900 Binary files /dev/null and b/images/share-twitter.png differ diff --git a/images/someFilters.png b/images/someFilters.png new file mode 100644 index 00000000..63ede6a6 Binary files /dev/null and b/images/someFilters.png differ diff --git a/images/up.png b/images/up.png index 3694ab24..9859e5c8 100644 Binary files a/images/up.png and b/images/up.png differ diff --git a/index.html b/index.html index 42980f74..0d4ac715 100644 --- a/index.html +++ b/index.html @@ -1,94 +1,108 @@ - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-
- - -
+ + + Accessible Viewer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + -
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- -
- -
-
-

-

-
-
-
-
-
-
+ - -
- -
+
+
+ + + +
+
+
+
+
+
+ +
+
+ Press ALT + 0 to 7 keys for fast navigation + +
+
-
-
+
- + + + - - - + require([ + "dojo/parser", + "config/templateConfig", + "application/template", + "application/main", + "dijit/layout/BorderContainer", + "dijit/layout/ContentPane", + "dijit/layout/StackContainer" + ], function( + parser, + templateConfig, + Template, + Main + ){ + // create the template. This will take care of all the logic required for template applications + var myTemplate = new Template(templateConfig); + // create my main application. Start placing your logic in the main.js file. + var myApp = new Main(); + + + // start template + myTemplate.startup().then(function (config) { + // The config object contains the following properties: helper services, (optionally) + // i18n, appid, webmap and any custom values defined by the application. + // In this example we have one called theme. + myApp.startup(config); + }, function (error) { + // something went wrong. Let's report it. + myApp.reportError(error); + }); + }); + + + diff --git a/js/AComboBoxWidget/AComboBoxWidget.js b/js/AComboBoxWidget/AComboBoxWidget.js new file mode 100644 index 00000000..834b625d --- /dev/null +++ b/js/AComboBoxWidget/AComboBoxWidget.js @@ -0,0 +1,199 @@ +define(["dojo/_base/declare", "dojo/_base/lang", "dojo/has", "dojo/dom","esri/kernel", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/on", "dijit/form/DateTextBox", + "dojo/Deferred", "dojo/promise/all", + "dojo/query", "dojo/_base/fx", "dojo/dom-style", "dojo/mouse", + "dojo/text!application/AComboBoxWidget/templates/AComboBoxWidget.html", + "dojo/dom-class", "dojo/dom-attr", "dojo/dom-style", "dojo/dom-construct", "dojo/_base/event", + "dojo/NodeList-dom", "dojo/NodeList-traverse" + + ], function ( + declare, lang, has, dom, esriNS, + _WidgetBase, _TemplatedMixin, on, DateTextBox, + Deferred, all, + query, fx, style, mouse, + AComboBox, + domClass, domAttr, domStyle, domConstruct, event + ) { + var Widget = declare("esri.dijit.AComboBox", [_WidgetBase, _TemplatedMixin], { + // defaults + templateString: AComboBox, + + options: { + visible: true, + selectedIndex: 0, + expanded: false, + }, + + constructor: function (options, srcRefNode, labelRefNode) { + var defaults = lang.mixin({}, this.options, options); + this.set("ComboItems", defaults.items); + this.set("SelectedIndex", defaults.selectedIndex); + if(srcRefNode) + { + this.domNode = srcRefNode; + } + if(labelRefNode) + { + this.labelRefNode = labelRefNode; + } + + this.set('expanded', defaults.expanded); + }, + + startup: function () { + this._init(); + }, + + _init: function () { + this.inputControl.onblur = lang.hitch(this, function() { + this._expandCombo(false); + }); + + if(this.labelRefNode){ + domAttr.set(this.labelRefNode,'for',this.inputControl.id); + } + + this.ListItems.innerHTML= ''; + for(var i=0; i= w-h) { + var display = domStyle.get(this.popup_container,'display') === 'none'; + this._expandCombo(display); + } + }, + + mousePointer: function(ev) { + var w = ev.currentTarget.clientWidth; + var h = ev.currentTarget.clientHeight; + var x = ev.offsetX; + var y = ev.offsetY; + if(x >= w-h) { + } + }, + + _expandCombo : function(expand) { + domAttr.set(this.inputControl, 'aria-expanded', expand+''); + if(!expand) { + fx.fadeOut({ + node: this.popup_container, + duration: 250, + onBegin: lang.hitch(this, function(){ + style.set(this.popup_container, "opacity", "1"); + }), + onEnd: lang.hitch(this, function(){ + domStyle.set(this.popup_container,'display', 'none'); + this.expanded = false; + }), + }).play(); + } else { + fx.fadeIn({ + node: this.popup_container, + duration: 250, + onBegin: lang.hitch(this, function(){ + style.set(this.popup_container, "opacity", "0"); + domStyle.set(this.popup_container,'display', ''); + }), + onEnd: lang.hitch(this, function(){ + this.expanded = true; + }), + }).play(); + } + }, + + navigateCombo : function(ev) { + switch(ev.keyIdentifier) { +// case "Alt" : //? +// ev.preventDefault = true; +// break; + case "Enter" : + lang.hitch(this, this._expandCombo(!this.expanded)); + ev.preventDefault = true; + break; + case "Down" : + if(this.SelectedIndex < this.ComboItems.length-1) { + this._setSelectedIndex(++this.SelectedIndex); + } + ev.preventDefault = true; + break; + case "Up" : + if(this.SelectedIndex > 0) { + this._setSelectedIndex(--this.SelectedIndex); + } + ev.preventDefault = true; + break; + case "Home" : + this._setSelectedIndex(0); + ev.preventDefault = true; + break; + case "End" : + this._setSelectedIndex(this.ComboItems.length-1); + ev.preventDefault = true; + break; + } + }, + + }); + if (has("extend-esri")) { + lang.setObject("dijit.AComboBox", Widget, esriNS); + } + return Widget; +}); diff --git a/js/AComboBoxWidget/Templates/AComboBoxWidget.html b/js/AComboBoxWidget/Templates/AComboBoxWidget.html new file mode 100644 index 00000000..6c729fe7 --- /dev/null +++ b/js/AComboBoxWidget/Templates/AComboBoxWidget.html @@ -0,0 +1,44 @@ +
+
+ Expand +
+ + + +
diff --git a/js/FeatureList/FeatureList.js b/js/FeatureList/FeatureList.js new file mode 100644 index 00000000..d1d7dfea --- /dev/null +++ b/js/FeatureList/FeatureList.js @@ -0,0 +1,491 @@ +define(["dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/has", "esri/kernel", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/on", + "dojo/Deferred", "dojo/promise/all", "dojo/query", + "esri/tasks/query", "esri/tasks/QueryTask", + "dojo/text!application/FeatureList/templates/FeatureList.html", + "dojo/dom", "dojo/dom-class", "dojo/dom-attr", "dojo/dom-style", "dojo/dom-construct", "dojo/_base/event", + "dojo/string", + "dojo/text!application/FeatureList/templates/FeatureListTemplate.html", + "dojo/i18n!application/nls/FeatureList", + "dojo/i18n!application/nls/resources", + "esri/symbols/SimpleMarkerSymbol", "esri/symbols/PictureMarkerSymbol", + "esri/symbols/CartographicLineSymbol", + "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleLineSymbol", + "esri/graphic", "esri/Color", + "dojo/NodeList-dom", "dojo/NodeList-traverse" + + ], function ( + Evented, declare, lang, has, esriNS, + _WidgetBase, _TemplatedMixin, on, + Deferred, all, query, + Query, QueryTask, + FeatureList, + dom, domClass, domAttr, domStyle, domConstruct, event, + string, + listTemplate, i18n, Ri18n, + SimpleMarkerSymbol, PictureMarkerSymbol, + CartographicLineSymbol, + SimpleFillSymbol, SimpleLineSymbol, + Graphic, Color + ) { + var Widget = declare("esri.dijit.FeatureList", [_WidgetBase, _TemplatedMixin, Evented], { + // defaults + templateString: FeatureList, + + options: { + map: null, + layers: null, + visible: true + }, + + constructor: function (options, srcRefNode) { + var defaults = lang.mixin({}, this.options, options); + this.domNode = srcRefNode; + + dojo.create("link", { + href : "js/FeatureList/Templates/FeatureList.css", + type : "text/css", + rel : "stylesheet", + }, document.head); + + // properties + this.set("map", defaults.map); + var Layers = this._getLayers(defaults.layers); + this.set("Layers", Layers); + + window._this = this; + + if(options.animatedMarker) { + window.markerSymbol = new esri.symbol.PictureMarkerSymbol({ + "angle": 0, + "xoffset": 0, + "yoffset": 0, + "type": "esriPMS", + "url": require.toUrl("./"+options.markerImage), + "contentType": "image/gif", + "width": options.markerSize, + "height": options.markerSize + }); + } else { + window.markerSymbol = new SimpleMarkerSymbol({ + "color": [3,126,175,20], + "size": options.markerSize, + "xoffset": 0, + "yoffset": 0, + "type": "esriSMS", + "style": "esriSMSCircle", + "outline": { + "color": [3,26,255,220], + "width": 2, + "type": "esriSLS", + "style": "esriSLSSolid" + } + }); + } + this.css = { + }; + }, + + _getLayers : function(layers) { + var l1 = layers.filter(function (l) { return l.hasOwnProperty("url");}); //l => l.hasOwnProperty("url")); + var l2 = layers.filter(function (l) { return !l.hasOwnProperty("url");}); //l => !l.hasOwnProperty("url")); + if(l2.length>0) { + console.info("Feature List - These Layers are not services: ", l2); + } + return l1; + }, + + startup: function () { + if (!this.map) { + this.destroy(); + console.log("FeaturesList::map required"); + } + if (this.map.loaded) { + this._init(); + } else { + on.once(this.map, "load", lang.hitch(this, function () { + this._init(); + })); + } + }, + + _init: function () { + this._createList(); + this.set("loaded", true); + this.emit("load", {}); + + on(this.toolbar, 'updateTool_features', lang.hitch(this, function(name) { + this._reloadList(this.map); + dom.byId('pageBody_features').focus(); + })); + }, + + FocusDetails: function() { + if(!this._isVisible()) return; + + var details = this.domNode.querySelector('.showAttr'); + if(details) { + var page = query(details).closest('.borderLi')[0]; + page.querySelector('.checkbox').focus(); + } + }, + + _isVisible : function() { + var page = query(this.domNode).closest('.page')[0]; + return dojo.hasClass(page, "showAttr"); + }, + + __reloadList : function(ext) { + var deferred = new Deferred(); + + var list = query("#featuresList")[0]; + this.map.graphics.clear(); + window.tasks.filter(function(t) { + return t.layer.visible && t.layer.visibleAtMapScale; + }).forEach(lang.hitch(this.map, function(t) { + t.query.geometry = ext.extent; + var exp=t.layer.getDefinitionExpression(); + t.query.where = exp; + t.result = t.task.execute(t.query); + })); + promises = all(window.tasks.map(function(t) {return t.result;})); + promises.then( + function(results) { + list.innerHTML = ""; + var preselected = null; + if(results) for(var i = 0; i\n'; + content+=' \n'; + content+=' '+pField.label+'\n'; + content+=' :\n'; + content+=' '+fieldValue+'\n'; + content+='\n'; + } + } + for(var j = 0; j)((?:http:\/\/www\.|https:\/\/www\.|ftp:\/\/www.|www\.)[a-z0-9]+(?:[\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(?:\/.*)?)(?:<))|(FORMAT_(DATE|TIME|NUM)\((-?\d*\.?\d*),\"(.+)\"\))/gm; + do { + var matches = re.exec(result); + if(!matches) break; + if(matches[6] && (!matches[7] || matches[7] === '')) { + result = result.replace(matches[5], ''); + } + if(matches[2]===">") { + result = result.replace(matches[1], ">"+i18n.widgets.featureList.followLink+"<"); + } + else if(matches[6]==="DATE") { + var dateNum = matches[7]; + if(!isNaN(parseFloat(dateNum)) && isFinite(dateNum)) { + var date = new Date(Number(dateNum)); + result = result.replace(matches[5], date.toLocaleDateString( + document.documentElement.lang, + { + year: "numeric", month: "long", day: "numeric" + } + )); + } else + result = result.replace(matches[5],''); + } + else if(matches[6]==="TIME") { + var timeNum = matches[7]; + if(!isNaN(parseFloat(timeNum)) && isFinite(timeNum)) { + var time = new Date(Number(timeNum)); + result = result.replace(matches[5], time.toLocaleDateString( + document.documentElement.lang, + { + year: "numeric", month: "numeric", day: "numeric", + hour: "2-digit", minute: "2-digit" + } + )); + } else + result = result.replace(matches[5],''); + } + else if(matches[6]==="NUM") { + var num = matches[7]; + if(!isNaN(parseFloat(num)) && isFinite(num)) { + num = Number(num); + var d89=matches[8].split('|'); + var dec = Number(d89[0]); + var useSeparator = d89[1] === "true"; + num = num.toLocaleString(document.documentElement.lang, + { + minimumFractionDigits: dec, + maximumFractionDigits: dec, + useGrouping: useSeparator + } + ); + + result = result.replace(matches[5], num); + } else + result = result.replace(matches[5],''); + } + + } while (true); + + return result; + } catch (e) { + console.log("Error on feature ("+featureId+")\n\t "+layer.infoTemplate.title(f)+"\n\t",e); + return null; + } + }; + }, + }); + if (has("extend-esri")) { + lang.setObject("dijit.FeaturesList", Widget, esriNS); + } + return Widget; +}); + diff --git a/js/FeatureList/Templates/FeatureList.css b/js/FeatureList/Templates/FeatureList.css new file mode 100644 index 00000000..2af3ccd9 --- /dev/null +++ b/js/FeatureList/Templates/FeatureList.css @@ -0,0 +1,36 @@ +#featuresList { + list-style: none; + margin: 0px; + padding-left: 0; + -webkit-padding-start: 0; + background-color: white; + overflow-y: auto; + overflow-x: hidden; +} +#featuresList li { + margin: 3px; +} + +.borderLi { + border: solid 1px gray; +} + +.featureItem h2 { + font-size: 16px; + text-align: left; +} + +/*.featureItem tr .filterBtn { + width:18px; + height:18px; + opacity: 0.1; +} + +.featureItem tr:focus .filterBtn, +.featureItem tr:hover .filterBtn +{ + opacity: 1; +}*/ + + + diff --git a/js/FeatureList/Templates/FeatureList.html b/js/FeatureList/Templates/FeatureList.html new file mode 100644 index 00000000..81c651d1 --- /dev/null +++ b/js/FeatureList/Templates/FeatureList.html @@ -0,0 +1,3 @@ + diff --git a/js/FeatureList/Templates/FeatureListTemplate.html b/js/FeatureList/Templates/FeatureListTemplate.html new file mode 100644 index 00000000..84b67f6b --- /dev/null +++ b/js/FeatureList/Templates/FeatureListTemplate.html @@ -0,0 +1,27 @@ +
+ + + + + + + + + + + + + ${_content} +
+ + + +
+ + + + +
+
Alt + 7 ${hint}
+
+
diff --git a/js/Filters/FilterDate.js b/js/Filters/FilterDate.js new file mode 100644 index 00000000..5ecaddba --- /dev/null +++ b/js/Filters/FilterDate.js @@ -0,0 +1,95 @@ +define([ + "dojo/_base/declare", "dojo/dom-construct", "dojo/parser", "dojo/ready", + "dijit/form/DateTextBox", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", + "dojo/_base/lang", "dojo/has", "esri/kernel", + "dojo/dom-style", "esri/tasks/query", "esri/tasks/QueryTask", + "dojo/text!./templates/FilterDate.html", + "dojo/i18n!application/nls/FilterDialog", +], function( + declare, domConstruct, parser, ready, + DateTextBox, + _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, + lang, has, esriNS, + domStyle, Query, QueryTask, + FilterItemTemplate, i18n){ + var Widget = declare("FilterDate", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { + templateString: FilterItemTemplate, + + options: { + }, + + + constructor: function(options, srcRefNode){ + var defaults = lang.mixin({}, this.options, options); + this._i18n = i18n; + this.domNode = srcRefNode; + this.set("map", defaults.map); + this.set("layer", defaults.layer); + this.set("field", defaults.field); + }, + + startup: function () { + if (!this.map) { + this.destroy(); + console.log("Filter::map required"); + } + if (this.map.loaded) { + this._init(); + } else { + on.once(this.map, "load", lang.hitch(this, function () { + this._init(); + })); + } + }, + + _init: function () { + this.domNode = domConstruct.create("div", {innerHTML: this.field.fieldName}); + }, + + getBetweenMode : function() { + return this.criteria.value === ' BETWEEN ' || this.criteria.value === ' NOT BETWEEN '; + }, + + criteriaChanged: function(ev) { + switch(this.getBetweenMode()) { + case false: + domStyle.set(this.divMaxValue,'display', 'none'); + break; + case true: + domStyle.set(this.divMaxValue,'display', 'inline'); + break; + } + }, + + getFilterExpresion: function() { + if(this.getBetweenMode()) { + var minDate = this.minValue.value.getSQLDate(); + var maxDate = this.maxValue.value.getSQLDate(); + if(minDate && maxDate) { + var where = this.field.fieldName+this.criteria.value+"'"+minDate+"' AND '"+maxDate+"'"; +// console.log(where); + return where; + } + else { + return null; + } + } else { + var date = this.minValue.value.getSQLDate(); + if(date) { + var where1 = this.field.fieldName+this.criteria.value+"'"+date+"'"; + //console.log(where); + return where1; + } + else { + return null; + } + } + } + }); + + if (has("extend-esri")) { + lang.setObject("dijit.FilterDate", Widget, esriNS); + } + return Widget; +}); \ No newline at end of file diff --git a/js/Filters/FilterItem.js b/js/Filters/FilterItem.js new file mode 100644 index 00000000..92fb77ca --- /dev/null +++ b/js/Filters/FilterItem.js @@ -0,0 +1,85 @@ +define([ + "dojo/Evented", "dojo/_base/declare", "dojo/dom-construct", "dojo/parser", "dojo/ready", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/_base/lang", "dojo/has", "esri/kernel", + "dojo/text!application/Filters/templates/FilterItem.html", + "dojo/i18n!application/nls/FilterDialog", + "application/Filters/FilterString", + "application/Filters/FilterDate", + "application/Filters/FilterNumber", +], function( + Evented, declare, domConstruct, parser, ready, + _WidgetBase, _TemplatedMixin, lang, has, esriNS, + FilterItemTemplate, i18n, + FilterString, FilterDate, FilterNumber){ + var Widget = declare("FilterItem", [_WidgetBase, _TemplatedMixin, Evented], { + templateString: FilterItemTemplate, + + options: { + }, + + constructor: function(options, srcRefNode){ + var defaults = lang.mixin({}, this.options, options); + this._i18n = i18n; + this.domNode = srcRefNode; + this.set("map", defaults.map); + this.set("layer", defaults.layer); + this.set("field", defaults.field); + + this.set("field_label", this.field.label); + this.set('field_Type', this.layer.layerObject.fields.find(lang.hitch(this, function(f){return f.name == this.field.fieldName;})).type); + this.set('filterField', null); + }, + + startup: function () { + if (!this.map) { + this.destroy(); + console.log("Filter::map required"); + } + if (this.map.loaded) { + this._init(); + } else { + on.once(this.map, "load", lang.hitch(this, function () { + this._init(); + })); + } + }, + + _init: function () { + // esriFieldTypeString, esriFieldTypeDate, esriFieldTypeDouble, + // esriFieldTypeInteger, esriFieldTypeOID, + + switch(this.field_Type) { + case "esriFieldTypeString" : + this.filterField = new FilterString({map:this.map, layer:this.layer, field:this.field}, this.content); + this.filterField.startup(); + break; + case "esriFieldTypeDate" : + this.filterField = new FilterDate({map:this.map, layer:this.layer, field:this.field}, this.content); + this.filterField.startup(); + break; + case "esriFieldTypeInteger" : + this.filterField = new FilterNumber({map:this.map, layer:this.layer, field:this.field, format:"\\\\d+"}, this.content); + this.filterField.startup(); + break; + case "esriFieldTypeDouble" : + this.filterField = new FilterNumber({map:this.map, layer:this.layer, field:this.field, format:"\\\\d+\\\\.?\\\\d*"}, this.content); + this.filterField.startup(); + break; + default : + this.content.innerHTML = "Unknown Field Type: '"+this.field_Type+"'"; + break; + } + }, + + filterRemove: function(btn) { + var id = this.domNode.id; + this.emit("removeFilterItem", {id:id}); + this.domNode.remove(); + }, + }); + + if (has("extend-esri")) { + lang.setObject("dijit.FilterItem", Widget, esriNS); + } + return Widget; +}); \ No newline at end of file diff --git a/js/Filters/FilterNumber.js b/js/Filters/FilterNumber.js new file mode 100644 index 00000000..021393c1 --- /dev/null +++ b/js/Filters/FilterNumber.js @@ -0,0 +1,96 @@ +define([ + "dojo/_base/declare", "dojo/dom-construct", "dojo/parser", "dojo/ready", + "dijit/form/ValidationTextBox", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", + "dojo/_base/lang", "dojo/has", "esri/kernel", + "dojo/dom-style", "esri/tasks/query", "esri/tasks/QueryTask", + "dojo/text!./templates/FilterNumber.html", + "dojo/i18n!application/nls/FilterDialog", +], function( + declare, domConstruct, parser, ready, + ValidationTextBox, + _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, + lang, has, esriNS, + domStyle, Query, QueryTask, + FilterItemTemplate, i18n){ + var Widget = declare("FilterNumber", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], { + templateString: FilterItemTemplate, + + options: { + }, + + + constructor: function(options, srcRefNode){ + var defaults = lang.mixin({format:"integer"}, this.options, options); + this._i18n = i18n; + this.domNode = srcRefNode; + this.set("map", defaults.map); + this.set("layer", defaults.layer); + this.set("field", defaults.field); + this.set("format", defaults.NumberFormat); + }, + + startup: function () { + if (!this.map) { + this.destroy(); + console.log("Filter::map required"); + } + if (this.map.loaded) { + this._init(); + } else { + on.once(this.map, "load", lang.hitch(this, function () { + this._init(); + })); + } + }, + + _init: function () { + this.domNode = domConstruct.create("div", {innerHTML: this.field.fieldName}); + }, + + getBetweenMode : function() { + return this.criteria.value === ' BETWEEN ' || this.criteria.value === ' NOT BETWEEN '; + }, + + criteriaChanged: function(ev) { + switch(this.getBetweenMode()) { + case false: + domStyle.set(this.divMaxValue,'display', 'none'); + break; + case true: + domStyle.set(this.divMaxValue,'display', 'inline'); + break; + } + }, + + getFilterExpresion: function() { + if(this.getBetweenMode()) { + var minNumb = this.minValue.value; + var maxNumb = this.maxValue.value; + if(minNumb && maxNumb) { + var where = this.field.fieldName+this.criteria.value+"'"+minNumb+"' AND '"+maxNumb+"'"; + //console.log(where); + return where; + } + else { + return null; + } + } else { + var numb = this.minValue.value; + if(numb) { + var where1 = this.field.fieldName+this.criteria.value+"'"+numb+"'"; + //console.log(where1); + return where1; + } + else { + return null; + } + } + } + }); + + if (has("extend-esri")) { + lang.setObject("dijit.FilterNumber", Widget, esriNS); + } + return Widget; +}); \ No newline at end of file diff --git a/js/Filters/FilterString.js b/js/Filters/FilterString.js new file mode 100644 index 00000000..c111e039 --- /dev/null +++ b/js/Filters/FilterString.js @@ -0,0 +1,133 @@ +define([ + "dojo/_base/declare", "dojo/dom-construct", "dojo/parser", "dojo/ready", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/_base/lang", "dojo/has", "esri/kernel", + "dojo/dom-style", "esri/tasks/query", "esri/tasks/QueryTask", + "dojo/text!./templates/FilterString.html", + "dojo/i18n!application/nls/FilterDialog", +], function( + declare, domConstruct, parser, ready, + _WidgetBase, _TemplatedMixin, lang, has, esriNS, + domStyle, Query, QueryTask, + FilterItemTemplate, i18n){ + var Widget = declare("FilterString", [_WidgetBase, _TemplatedMixin], { + templateString: FilterItemTemplate, + + options: { + }, + + constructor: function(options, srcRefNode){ + var defaults = lang.mixin({}, this.options, options); + this._i18n = i18n; + this.domNode = srcRefNode; + this.set("map", defaults.map); + this.set("layer", defaults.layer); + this.set("field", defaults.field); + }, + + startup: function () { + if (!this.map) { + this.destroy(); + console.log("Filter::map required"); + } + if (this.map.loaded) { + this._init(); + } else { + on.once(this.map, "load", lang.hitch(this, function () { + this._init(); + })); + } + }, + + _init: function () { + this.domNode = domConstruct.create("div", {innerHTML: this.field.fieldName}); + }, + + getListMode : function() { + return this.criteria.value === ' IN ' || this.criteria.value === ' NOT IN '; + }, + + criteriaChanged: function(ev) { +// var listMode = ev.target.value === 'In' || ev.target.value === 'NotIn'; + switch(this.getListMode()) { + case false: + domStyle.set(this.textInput,'display', ''); + domStyle.set(this.listInput,'display', 'none'); + break; + case true: + domStyle.set(this.textInput,'display', 'none'); + domStyle.set(this.listInput,'display', ''); + + if(this.listInput.innerHTML === '') { + var _query = new Query(); + _query.outFields = [this.field.fieldName]; + _query.returnGeometry = false; + _query.where = "1=1"; + _query.spatialRelationship = "esriSpatialRelIntersects"; + _query.returnDistinctValues = true; + _query.orderByFields = [this.field.fieldName]; + var task = new QueryTask(this.layer.layerObject.url); + task.execute(_query).then(lang.hitch(this, function(results) { +// console.log(results); + results.features.map(lang.hitch(this, function(f) { + return f.attributes[this.field.fieldName];})).forEach(lang.hitch(this, function(v) { + if(v) { + var id = this.id+'_'+v; + this.listInput.innerHTML += ''; + this.listInput.innerHTML += ''; + this.listInput.innerHTML += '
'; + } + })); + })); + } + break; + } + }, + + getFilterExpresion: function() { + if(this.getListMode()) { + var list = Array.prototype.slice.call(this.listInput.children).filter(function(c) { + return c.nodeName=="INPUT" && c.checked; + }).map(function(c) { return c.value; }); + if(!list || list.length === 0) + { + return null; + } + else if(list.length == 1) { + var op = " = "; + if(this.criteria.value.indexOf('NOT')>=0) { + op = " != "; + } + return this.field.fieldName+op+"'"+list[0]+"'"; + } else { + var comma =''; + var inList=list.reduce(function(previousValue, currentValue) { + if(previousValue && previousValue!=='') + comma = ', '; + return previousValue+"'"+comma+"'"+currentValue; + }); + return this.field.fieldName+this.criteria.value + "('"+inList+"')"; + } + } else { + if(this.textInput.value !== '') { + var text = this.textInput.value; + if(this.criteria.value.indexOf('LIKE')>=0){ + var re = /(.*%.*)|(.*_.*)|(\[.*\])/gm; + var matches = re.exec(text); + if(!matches || matches.length === 0) { + text += '%'; + } + } + return this.field.fieldName+this.criteria.value+"'"+text+"'"; + } + else { + return null; + } + } + } + }); + + if (has("extend-esri")) { + lang.setObject("dijit.FilterString", Widget, esriNS); + } + return Widget; +}); \ No newline at end of file diff --git a/js/Filters/FilterTab.js b/js/Filters/FilterTab.js new file mode 100644 index 00000000..e7dda92b --- /dev/null +++ b/js/Filters/FilterTab.js @@ -0,0 +1,189 @@ +define([ + "dojo/Evented", "dojo/_base/declare", "dojo/dom-construct", "dojo/dom-class", "dojo/parser", "dojo/ready", + "dojo/on", "esri/tasks/query", "esri/tasks/QueryTask", "esri/graphicsUtils", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/_base/lang", "dojo/has", "esri/kernel", + "dojo/dom", "dojo/query", "dojo/dom-attr", "dojo/dom-style", + "dojo/text!application/Filters/templates/FilterTab.html", + "dojo/i18n!application/nls/FilterDialog" +], function( + Evented, declare, domConstruct, domClass, parser, ready, + on, Query, QueryTask, graphicsUtils, + _WidgetBase, _TemplatedMixin, lang, has, esriNS, + dom, query, domAttr, domStyle, + FilterTabTemplate, + i18n + ){ + var Widget = declare("FilterTab", [_WidgetBase, _TemplatedMixin, Evented], { + templateString: FilterTabTemplate, + + options: { + }, + + constructor: function(options, srcRefTabsZone, srcRefTabsContent){ + var defaults = lang.mixin({}, this.options, options); + this._i18n = i18n; + + //this.domNode = srcRefNode; + this.set("filter", defaults.filter); + + this.set("filter_name", this.filter.layer.resourceInfo.name); + // this.set("checked", defaults.checked); + this.set("FilterItems", []); + //this.set("filtersOn", []); + + + if(window.filtersOn === undefined) { + window.filtersOn = []; + } + }, + + FilterItems: [], + + startup: function () { + this._init(); + }, + + fieldSelect:null, + + _init: function () { + var items = []; + this.filter.fields.forEach(lang.hitch(this, function(fl){ + this.fieldsCombo.innerHTML += ''; + })); + }, + + filterKeyPress: function(btn) { + // console.log(btn, btn.currentTarget.parentElement); + if(btn.keyCode == 13 || btn.keyCode == 32) { + btn.currentTarget.parentElement.click(); + } + }, + + filterChange: function(ev) { + var pageId = ev.target.value; + var pages = document.querySelectorAll('.tabContent'); + for(var i = 0; i< pages.length; i++) { + var page = pages[i]; + if(page.id === pageId) { + domClass.add(page, 'tabShow'); + domClass.remove(page, 'tabHide'); + } else { + domClass.add(page, 'tabHide'); + domClass.remove(page, 'tabShow'); + } + } + }, + + check: function() { + this.btn.checked=true; + }, + + _filterAdd: function(fieldId) { + var field = this.filter.fields.find(function(f) {return f.fieldName === fieldId;}); + var layer = this.filter.layer; + + var filterItem = new FilterItem({map:layer.layerObject._map, layer:layer, field:field});//, myItem); + this.filterList.appendChild(filterItem.domNode); + filterItem.startup(); + this.FilterItems.push(filterItem); + filterItem.on("removeFilterItem", lang.hitch(this, function (id) { + this.FilterItems.splice(this.FilterItems.indexOf(filterItem), 1); + })); + filterItem.domNode.focus(); + }, + + filterAdd: function(ev) { + var fieldId = this.fieldsCombo.value; + this._filterAdd(fieldId); + }, + + filterApply: function(btn) { + var layer = this.filter.layer; + var exps = []; + this.FilterItems.filter(function(f) { return f.Active.checked;}).forEach(function(f) { + try { + var exp = f.filterField.getFilterExpresion(); + if(exp) { + exps.push(exp); + } + } + catch (er) { + } + }); + if(exps.length === 1) { + this.showBadge(true); + this.getDefinitionExtensionExtent(layer,exps[0]); + } else if (exps.length >= 1) { + var op =''; + var inList=exps.reduce(function(previousValue, currentValue) { + if(previousValue && previousValue!=='') + op = ' AND '; + return previousValue+")"+op+"("+currentValue; + }); + this.showBadge(true); + this.getDefinitionExtensionExtent(layer,"("+inList+")"); + } else { + this.showBadge(false); + this.getDefinitionExtensionExtent(layer,''); + } + }, + + getDefinitionExtensionExtent: function(layer, expression) { + layer.layerObject.setDefinitionExpression(expression); + var task = new QueryTask(layer.url); + var q = new Query(); + q.where = expression ? expression : '1=1'; + q.outFields = []; + q.returnGeometry = true; + task.execute(q).then(function(ev) { + var myExtent = graphicsUtils.graphicsExtent(ev.features); + if(myExtent.xmin===myExtent.xmax && myExtent.ymin===myExtent.ymax) { + filter.map.centerAndZoom(myExtent.getCenter(), 13); + } + else { + var ext = myExtent.expand(1.5); + filter.map.setExtent(ext); + } + }); + }, + + filterIgnore: function(btn) { + var layer = this.filter.layer; + this.getDefinitionExtensionExtent(layer, null); + this.showBadge(false); + }, + + showBadge: function(show) { + var tabIndex = window.filtersOn.indexOf(this.id); + var tabIndicator = query('#'+this.id+"_img")[0]; + if(show) { + domStyle.set(tabIndicator,'display',''); + if(tabIndex<0) + { + window.filtersOn.push(this.id); + } + } else { + domStyle.set(tabIndicator,'display','none'); + if(tabIndex>=0) + { + window.filtersOn.splice(tabIndex, 1); + } + } + + var badgeindicator = query('#badge_somefilters')[0]; + if (window.filtersOn.length>0) { + domStyle.set(badgeindicator,'display',''); + domAttr.set(badgeindicator, "title", i18n.widgets.FilterTab.someFilters); + domAttr.set(badgeindicator, "alt", i18n.widgets.FilterTab.someFilters); + } else { + domStyle.set(badgeindicator,'display','none'); + } + + }, + }); + + if (has("extend-esri")) { + lang.setObject("dijit.FilterTab", Widget, esriNS); + } + return Widget; +}); \ No newline at end of file diff --git a/js/Filters/Filters.js b/js/Filters/Filters.js new file mode 100644 index 00000000..1bed536c --- /dev/null +++ b/js/Filters/Filters.js @@ -0,0 +1,109 @@ +define(["dojo/_base/declare", "dojo/_base/lang", "dojo/has", "dojo/dom","esri/kernel", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/on", "dijit/form/DateTextBox", + "dojo/Deferred", "dojo/promise/all", + "dojo/query", + // "dijit/layout/BorderContainer", "dijit/layout/TabContainer", "dijit/layout/ContentPane", + "dojo/text!application/Filters/templates/Filters.html", + "dojo/dom-class", "dojo/dom-attr", "dojo/dom-style", "dojo/dom-construct", "dojo/_base/event", + "application/Filters/FilterTab","application/Filters/FilterItem", + "dojo/NodeList-dom", "dojo/NodeList-traverse" + + ], function ( + declare, lang, has, dom, esriNS, + _WidgetBase, _TemplatedMixin, on, DateTextBox, + Deferred, all, + query, + // BorderContainer, TabContainer, ContentPane, + Filters, + domClass, domAttr, domStyle, domConstruct, event, + FilterTab, FilterItem + ) { + var Widget = declare("esri.dijit.Filters", [_WidgetBase, _TemplatedMixin], { + // defaults + templateString: Filters, + + options: { + map: null, + layers: null, + visible: true + }, + + constructor: function (options, srcRefNode) { + var defaults = lang.mixin({}, this.options, options); + + this.domNode = srcRefNode; + // properties + this.set("map", defaults.map); + var Layers = this._getLayers(defaults.layers); + var VisibleLayers = Layers.filter(function(l) { return l.visibility; });//l => l.visibility); + this.set("layers", VisibleLayers); + window.filters = []; + VisibleLayers.forEach(lang.hitch(this,function(layer){ + if(layer.popupInfo) { + window.filters.push({ + id: layer.id, + layer: layer, + fields:layer.popupInfo.fieldInfos.filter(function(l){return l.visible;}) + }); + } + })); + }, + + _getLayers : function(layers) { + var l1 = layers.filter(function (l) { return l.hasOwnProperty("url");}); //l => l.hasOwnProperty("url")); + var l2 = layers.filter(function (l) { return !l.hasOwnProperty("url");}); //l => !l.hasOwnProperty("url")); + // var l1 = layers.filter(l => l.hasOwnProperty("url")); + // var l2 = layers.filter(l => !l.hasOwnProperty("url")); + if(l2.length>0) { + console.info("Filters - These Layers are not services: ", l2); + } + return l1; + }, + + startup: function () { + if (!this.map) { + this.destroy(); + console.log("Filter::map required"); + } + if (this.map.loaded) { + this._init(); + } else { + on.once(this.map, "load", lang.hitch(this, function () { + this._init(); + })); + } + }, + + _init: function () { + var ck='checked'; + window.filters.forEach(lang.hitch(this, function(filter){ + var filterTab = new FilterTab({filter:filter, checked:ck}); + dojo.place(filterTab.domNode, this.filterTabs); + filterTab.startup(); + + var tab = document.querySelector('#'+filterTab.domNode.id+' .tab'); + var content = document.querySelector('#'+filterTab.domNode.id+' .tabContent'); + + dojo.place(tab, this.filterTabsZone); + dojo.place(content, this.filterTabsContent); + + this.filterTabs.innerHTML = ''; + + if(ck!=='') { + domClass.add(content, 'tabShow'); + domClass.remove(content, 'tabHide'); + ck=''; + } + })); + + on(this.toolbar, 'updateTool_filter', lang.hitch(this, function(name) { + dom.byId('pageBody_filter').focus(); + })); + + }, + }); + if (has("extend-esri")) { + lang.setObject("dijit.Filters", Widget, esriNS); + } + return Widget; +}); diff --git a/js/Filters/Templates/FilterDate.html b/js/Filters/Templates/FilterDate.html new file mode 100644 index 00000000..54cab737 --- /dev/null +++ b/js/Filters/Templates/FilterDate.html @@ -0,0 +1,17 @@ +
+ + +
+ +
+
\ No newline at end of file diff --git a/js/Filters/Templates/FilterItem.html b/js/Filters/Templates/FilterItem.html new file mode 100644 index 00000000..627dac1a --- /dev/null +++ b/js/Filters/Templates/FilterItem.html @@ -0,0 +1,8 @@ +
  • +
    + + + +
    +
    +
  • \ No newline at end of file diff --git a/js/Filters/Templates/FilterNumber.html b/js/Filters/Templates/FilterNumber.html new file mode 100644 index 00000000..a9d72632 --- /dev/null +++ b/js/Filters/Templates/FilterNumber.html @@ -0,0 +1,21 @@ +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/js/Filters/Templates/FilterString.html b/js/Filters/Templates/FilterString.html new file mode 100644 index 00000000..f7da4eaf --- /dev/null +++ b/js/Filters/Templates/FilterString.html @@ -0,0 +1,18 @@ +
    + + +
    + + +
    +
    \ No newline at end of file diff --git a/js/Filters/Templates/FilterTab.html b/js/Filters/Templates/FilterTab.html new file mode 100644 index 00000000..97b1ca1e --- /dev/null +++ b/js/Filters/Templates/FilterTab.html @@ -0,0 +1,43 @@ +
    +
    + + +
    + +
    +
    + + + + + + +
    + +
      + +
      + + +
      +
      +
      \ No newline at end of file diff --git a/js/Filters/Templates/Filters.html b/js/Filters/Templates/Filters.html new file mode 100644 index 00000000..e7800689 --- /dev/null +++ b/js/Filters/Templates/Filters.html @@ -0,0 +1,5 @@ + diff --git a/js/LanguageSelect/LanguageSelect.js b/js/LanguageSelect/LanguageSelect.js new file mode 100644 index 00000000..6ae4f8b2 --- /dev/null +++ b/js/LanguageSelect/LanguageSelect.js @@ -0,0 +1,174 @@ +define([ + "dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/has", "dojo/dom","esri/kernel", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/on", + "dojo/query", "dijit/registry", + "dojo/text!application/LanguageSelect/templates/LanguageSelect.html", + "dojo/i18n!application/nls/LanguageSelect", + "dijit/form/DropDownButton", "dijit/DropDownMenu", "dijit/MenuItem", + "dojo/dom-class", "dojo/dom-attr", "dojo/dom-style", + "dojo/dom-construct", "dojo/_base/event", "esri/lang", + "dojo/NodeList-dom", "dojo/NodeList-traverse" + + ], function ( + Evented, declare, _lang, has, dom, esriNS, + _WidgetBase, _TemplatedMixin, on, + query, registry, + LanguageSelectTemplate, i18n, + DropDownButton, DropDownMenu, MenuItem, + domClass, domAttr, domStyle, + domConstruct, event, esriLang + ) { + var Widget = declare("esri.dijit.LanguageSelect", [_WidgetBase, _TemplatedMixin, Evented], { + templateString: LanguageSelectTemplate, + + options: { + locale: 'en-us', + languages:{}, + textColor:null, + showLabel:true + }, + + constructor: function (options, srcRefNode) { + this.defaults = _lang.mixin({}, this.options, options); + //this._i18n = i18n; + this.domNode = srcRefNode; + + var link = document.createElement("link"); + link.href = "js/LanguageSelect/Templates/LanguageSelect.css"; + link.type = "text/css"; + link.rel = "stylesheet"; + query('html')[0].appendChild(link); + }, + + Click: function(e) { + //console.log(e.srcElement.parentElement); + var menuItemDataSet = query(e.srcElement).closest('.dijitMenuItem')[0].dataset; + var docLocale = query('html')[0].lang; + var locale = menuItemDataSet.code; + if(!locale || locale==='' || locale === "undefined" || locale === undefined) + { + locale = navigator.language; + } + locale = locale.toLowerCase(); + + if(docLocale.toLowerCase() === locale) return; + + var appId = menuItemDataSet.appid; + if(!appId || appId==='' || appId === "undefined" || appId === undefined) { + appId = /(?:[?|&]appid=)([a-z0-9]*)/gi.exec(window.location.search); + if(appId && appId.length===2) { + appId = appId[1]; + } + } + window.location.search=('?appid='+appId+'&locale='+locale); + }, + + startup: function () { + if(this.button) return; + + var menu = new DropDownMenu({ + style: "display: none;", + //id: 'languageMenu', + }); + var currentLocale = this.defaults.locale.substring(0,2).toUpperCase(); + var currentIcon = null; + var currentLanguage = null; + + for(var i = 0; i'; + } + if(this.defaults.textColor) { + btnLbl = ''+btnLbl+''; + } + + this.button = new DropDownButton({ + label: btnLbl, + title: currentHint, + dropDown: menu, + id: 'languageButton', + role: 'application', + }); + this.button.startup(); + + if(currentIcon) { + dojo.removeClass(this.button.iconNode, "dijitNoIcon"); + dojo.place(currentIcon, query(this.button._buttonNode).query(".dijitReset").query(".dijitArrowButtonChar")[0], "after"); + dojo.attr(this.button.iconNode,'aria-label', currentHint); + dojo.attr(this.button.iconNode,'title', esriLang.stripTags(currentHint)); + } + + // if(this.defaults.showLabel) { + // domConstruct.create("label", { + // for: 'languageButton', + // innerHTML: i18n.widgets.languageSelect.language, + // title: i18n.widgets.languageSelect.changeHere, + // 'aria-label': i18n.widgets.languageSelect.changeHere, + // tabindex: -1 + // }, this.domNode); + // } + + this.domNode.appendChild(this.button.domNode); + + query('.dijitMenuTable').forEach(function(table){ + dojo.attr(table, "role", "presentation"); + }); + + query('.dijitPopup').forEach(function(table){ + dojo.attr(table, "role", "menu"); + }); + } + }); + + if (has("extend-esri")) { + _lang.setObject("dijit.LanguageSelect", Widget, esriNS); + } + return Widget; +}); diff --git a/js/LanguageSelect/Templates/LanguageSelect.css b/js/LanguageSelect/Templates/LanguageSelect.css new file mode 100644 index 00000000..bf0a68c4 --- /dev/null +++ b/js/LanguageSelect/Templates/LanguageSelect.css @@ -0,0 +1,52 @@ +#languageSelectNode { + padding: 5px; +} + +#languageSelectNode label { + font-size: 14px; + margin-right: 2px; + margin-bottom: 0; +} + +@media only screen and (min-width: 720px) { + #languageSelectNode { + padding: 12px; + } + + #languageSelectNode label { + font-size: 18px; + margin-right: 4px; + } +} + +#languageSelectNode .dijitDropDownButton { + margin:0; + padding:0; + width:100% + height:100%; +} + +#languageSelectNode .dijitArrowButtonInner { + display:none; +} + +#languageSelectNode .dijitButtonText{ + vertical-align: middle; + padding: 2px; +} + +.langMenuItemIcon { + height:20px; + vertical-align: middle; +} + +.langIcon { + height:20px; + vertical-align: middle; +} + +@media only screen and (min-width: 720px) { + .langIcon { + height:22px; + } +} diff --git a/js/LanguageSelect/Templates/LanguageSelect.html b/js/LanguageSelect/Templates/LanguageSelect.html new file mode 100644 index 00000000..c4e81556 --- /dev/null +++ b/js/LanguageSelect/Templates/LanguageSelect.html @@ -0,0 +1 @@ + diff --git a/js/NavToolBar/NavToolBar.js b/js/NavToolBar/NavToolBar.js new file mode 100644 index 00000000..3e8b1594 --- /dev/null +++ b/js/NavToolBar/NavToolBar.js @@ -0,0 +1,221 @@ +define([ + "dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/has", "dojo/dom","esri/kernel", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/on", + "dojo/query", "esri/toolbars/navigation", "dijit/registry", + "esri/dijit/HomeButton", "esri/dijit/LocateButton", + "esri/symbols/SimpleLineSymbol", "esri/Color", + "dojo/text!application/NavToolBar/templates/NavToolBar.html", + "dojo/i18n!application/nls/NavToolBar", + "dojo/dom-class", "dojo/dom-attr", "dojo/dom-style", + "dojo/dom-construct", "dojo/_base/event", + "dojo/NodeList-dom", "dojo/NodeList-traverse" + + ], function ( + Evented, declare, lang, has, dom, esriNS, + _WidgetBase, _TemplatedMixin, on, + query, Navigation, registry, + HomeButton, LocateButton, + SimpleLineSymbol, Color, + NavToolBarTemplate, i18n, + domClass, domAttr, domStyle, + domConstruct, event + ) { + var Widget = declare("esri.dijit.NavToolBar", [_WidgetBase, _TemplatedMixin, Evented], { + templateString: NavToolBarTemplate, + + options: { + map: null, + navToolBar:null, + iconColor:"white", + newIcons:'', + zoomColor:'red', + }, + + constructor: function (options, srcRefNode) { + var defaults = lang.mixin({}, this.options, options); + this._i18n = i18n; + this.domNode = srcRefNode; + + this.set("map", defaults.map); + this.set("navToolBar", defaults.navToolBar); + this.set("nav", new Navigation(this.map)); + this.set("iconColor", defaults.iconColor); + this.set("newIcons", defaults.newIcons); + this.set("zoomColor", defaults.zoomColor); + }, + + startup: function () { + if (this.map.loaded) { + this._init(); + } else { + on.once(this.map, "load", lang.hitch(this, function () { + this._init(); + })); + } + }, + + _init: function () { + //if(!dom.byId("navZoomIn")) return; + this.nav.setZoomSymbol(new SimpleLineSymbol("SOLID", new Color(this.zoomColor), 4)); + + dojo.empty(this.navToolBar); + + domConstruct.place(this.domNode, this.navToolBar); + + on(dom.byId("navZoomIn"), "click", lang.hitch(this, function(e) { + this.map.setLevel(this.map.getLevel()+1); + })); + + on(dom.byId("navZoomOut"), "click", lang.hitch(this, function(e) { + this.map.setLevel(this.map.getLevel()-1); + })); + + if(has("home")) { + var home = new HomeButton({ + map: this.map + }, domConstruct.create("div",{},dom.byId("navHome"))); + home.startup(); + + var homeButton = dojo.query(".homeContainer")[0]; + var homen = dojo.query(".homeContainer .home")[0]; + dojo.removeAttr(homen, 'role'); + var homeNode = dojo.query(".home")[0]; + dojo.empty(homeNode); + var homeHint = i18n.widgets.navToolBar.home; + + var btnHome = domConstruct.create("input", { + type: 'image', + src: 'images/icons_'+this.iconColor+'/home.png', + alt: homeHint, + title: homeHint, + }, homeNode); + } else { + dojo.destroy("navHome"); + } + + var isLocationEnabled = !(!!window.chrome && !!window.chrome.webstore) || + (window.location.protocol === "https:") || + (window.location.hostname === "localhost"); + if (has("locate") && isLocationEnabled) { + var geoLocate = new LocateButton({ + map: this.map + }, domConstruct.create("div",{},dom.byId("navLocate"))); + geoLocate.startup(); + + var locateButton = dojo.query(".locateContainer")[0]; + var zoomLocateButton = dojo.query(".zoomLocateButton")[0]; + //dojo.removeAttr(zoomLocateButton, 'title'); + var locateHint = dojo.attr(zoomLocateButton, 'title'); + dojo.removeAttr(zoomLocateButton, 'role'); + + dojo.empty(zoomLocateButton); + + domConstruct.create("input", { + type: 'image', + src: 'images/icons_white/locate.png', + alt: locateHint, + title: locateHint, + }, zoomLocateButton); + } else { + dojo.destroy("navLocate"); + } + + if(has("navigation")) { + on(dom.byId("navPrev"), "click", lang.hitch(this, function(e) { + this.nav.zoomToPrevExtent(); + })); + + on(dom.byId("navNext"), "click", lang.hitch(this, function(e) { + this.nav.zoomToNextExtent(); + })); + + on(dom.byId("navZoomInTool"), "click", lang.hitch(this, function(e) { + this.map.setMapCursor("url(images/ZoomIn.cur),auto"); + this.nav.activate("zoomin"); + })); + + on(dom.byId("navZoomOutTool"), "click", lang.hitch(this, function(e) { + this.map.setMapCursor("url(images/ZoomOut.cur),auto"); + this.nav.activate("zoomout"); + })); + + on(dom.byId("extenderNavLabel"), "keypress", lang.hitch(this, function(e) { + if(e.key === " " || e.char === " ") { + e.target.click(); + } + })); + + on(dom.byId("extenderNavCheckbox"), "change", lang.hitch(this, function(e) { + var ck = e.target.checked; + + dojo.setStyle(dom.byId("extendedTools"), "display", ck?"inherit":"none"); + this.nav.deactivate(); + this.map.setMapCursor("default"); + })); + + } else { + dojo.destroy("navPrevNext"); + dojo.destroy("ZoomTools"); + dojo.destroy("extenderNav"); + dojo.setStyle(dom.byId("extendedTools"), "display", "inherit"); + } + + this.nav.on("extent-history-change", lang.hitch(this, function () { + var zoom = this.map.getZoom(); + this.tryDisableBtn("navZoomIn", zoom == this.map.getMaxZoom()); + this.tryDisableBtn("navZoomOut", zoom == this.map.getMinZoom()); + this.tryDisableBtn("navHome",window.initExt === this.map.extent); + if(has("navigation")) { + this.tryDisableBtn("navPrev",this.nav.isFirstExtent()); + this.tryDisableBtn("navNext",this.nav.isLastExtent()); + this.nav.deactivate(); + this.map.setMapCursor("default"); + } + })); + + // on(dom.byId("testBtn"), "click", lang.hitch(this, function(e) { + // this.map._createLabelLayer(); + // })); + + + }, + + //disTabs : 1, + + tryDisableBtn:function(id, disable) { + var div = query("#"+id)[0]; + var btn = query("input", div)[0]; + var dis = query(".disabledBtn", div)[0]; + var crs = disable ? "not-allowed": "pointer"; + dojo.setStyle(btn, "cursor", crs); + dojo.setStyle(div, "cursor", crs); + dojo.setStyle(dis, "cursor", crs); + dojo.setAttr(btn, "tabIndex", disable?-1:0); + // if(this.disTabs>=0 && disable) { + // this.disTabs-=1; + // } else { + // dojo.setAttr(dis, "tabIndex", disable?-1:0); + // } + //dojo.setStyle(btn, "pointer-events", disable?"none":"all"); + // if(disable && dojo.getStyle(dis, "display") !== "none" ) + // this.blurAll();//dojo.getAttr(dis, 'aria-label')); + dojo.setStyle(dis, "display", disable?"inherit":"none"); + return disable; + }, + + blurAll: function(text) { + if(text===undefined) + text=''; + var tmp = domConstruct.create("div", {tabindex:0, 'aria-label':text}, document.body); + //document.body.appendChild(tmp); + tmp.focus(); + document.body.removeChild(tmp); + } + + }); + + if (has("extend-esri")) { + lang.setObject("dijit.NavToolBar", Widget, esriNS); + } + return Widget; +}); diff --git a/js/NavToolBar/Templates/NavToolBar.html b/js/NavToolBar/Templates/NavToolBar.html new file mode 100644 index 00000000..57bc8516 --- /dev/null +++ b/js/NavToolBar/Templates/NavToolBar.html @@ -0,0 +1,117 @@ +
      + + + +
      + +
      +
      diff --git a/js/PopupPanel.js b/js/PopupPanel.js index e8597f55..6347257e 100644 --- a/js/PopupPanel.js +++ b/js/PopupPanel.js @@ -52,7 +52,7 @@ function( on(this.popup, "set-features", lang.hitch(this, this._displayPopupContent)); //setup title text for popup buttons - console.log(esriStrings.widgets.popup); + //console.log(esriStrings.widgets.popup); dom.byId("popupButtonClose").title = esriStrings.widgets.popup.NLS_close; dom.byId("popupButtonNext").title = esriStrings.widgets.popup.NLS_nextFeature; dom.byId("popupButtonPrev").title = esriStrings.widgets.popup.NLS_prevFeature; diff --git a/js/SearchSources.js b/js/SearchSources.js index c0b463d1..6cd7f51a 100644 --- a/js/SearchSources.js +++ b/js/SearchSources.js @@ -92,7 +92,10 @@ declare, lang, array, dojoJson, domConstruct, esriLang, Locator, FeatureLayer, S }, _createWebMapItemSources: function () { - if (this.itemData && this.itemData.applicationProperties && this.itemData.applicationProperties.viewing && this.itemData.applicationProperties.viewing.search) { + if (this.itemData && + this.itemData.applicationProperties && + this.itemData.applicationProperties.viewing && + this.itemData.applicationProperties.viewing.search) { //search is configured on the web map item var searchOptions = this.itemData.applicationProperties.viewing.search; array.forEach(searchOptions.layers, lang.hitch(this, function (searchLayer) { diff --git a/js/ShareDialog.js b/js/ShareDialog.js index e3dfb8cf..aff9da2f 100644 --- a/js/ShareDialog.js +++ b/js/ShareDialog.js @@ -1,7 +1,14 @@ -define(["dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/has", "esri/kernel", "dijit/_WidgetBase", "dijit/a11yclick", "dijit/_TemplatedMixin", "dojo/on", +define(["dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/has", + "esri/kernel", "dijit/_WidgetBase", "dijit/a11yclick", "dijit/_TemplatedMixin", "dojo/on", // load template -"dojo/text!application/dijit/templates/ShareDialog.html", "dojo/i18n!application/nls/ShareDialog", "dojo/dom-class", "dojo/dom-style", "dojo/dom-attr", "dojo/dom-construct", "esri/request", "esri/urlUtils", "dijit/Dialog", "dojo/number", "dojo/_base/event"], function ( -Evented, declare, lang, has, esriNS, _WidgetBase, a11yclick, _TemplatedMixin, on, dijitTemplate, i18n, domClass, domStyle, domAttr, domConstruct, esriRequest, urlUtils, Dialog, number, event) { +"dojo/text!application/dijit/templates/ShareDialog.html", +"dojo/i18n!application/nls/ShareDialog", +"dojo/dom-class", "dojo/dom-style", "dojo/dom-attr", "dojo/dom-construct", "esri/request", "esri/urlUtils", "dijit/Dialog", "dojo/number", "dojo/_base/event"], function ( +Evented, declare, lang, has, +esriNS, _WidgetBase, a11yclick, _TemplatedMixin, on, +dijitTemplate, +i18n, +domClass, domStyle, domAttr, domConstruct, esriRequest, urlUtils, Dialog, number, event) { var Widget = declare("esri.dijit.ShareDialog", [_WidgetBase, _TemplatedMixin, Evented], { templateString: dijitTemplate, options: { @@ -19,8 +26,8 @@ Evented, declare, lang, has, esriNS, _WidgetBase, a11yclick, _TemplatedMixin, on twitterURL: "https://twitter.com/intent/tweet?url={url}&text={title}&hashtags={hashtags}", googlePlusURL: "https://plus.google.com/share?url={url}", bitlyAPI: location.protocol === "https:" ? "https://api-ssl.bitly.com/v3/shorten" : "http://api.bit.ly/v3/shorten", - bitlyLogin: "arcgis", - bitlyKey: "R_b8a169f3a8b978b9697f64613bf1db6d", + bitlyLogin: "", + bitlyKey: "", embedSizes: [{ "width": "100%", "height": "640px" @@ -101,24 +108,28 @@ Evented, declare, lang, has, esriNS, _WidgetBase, a11yclick, _TemplatedMixin, on shareDialogSubHeader: "share-dialog-subheader", shareDialogTextarea: "share-dialog-textarea", shareDialogExtent: "share-dialog-extent", - shareDialogExtentChk: "share-dialog-checkbox", + shareDialogExtentChk: "checkbox", mapSizeContainer: "map-size-container", embedMapSizeClear: "embed-map-size-clear", iconClear: "icon-clear" }; }, + // bind listener for button to action postCreate: function () { this.inherited(arguments); this._setExtentChecked(); this._shareLink(); this.own(on(this._extentInput, a11yclick, lang.hitch(this, this._useExtentUpdate))); + + domAttr.remove(dojo.query(".share-dialog-textarea")[0], "style"); }, + // start widget. called by user startup: function () { this._init(); - }, + // connections/subscriptions will be cleaned up during the destroy() lifecycle phase destroy: function () { this.inherited(arguments); @@ -177,15 +188,12 @@ Evented, declare, lang, has, esriNS, _WidgetBase, a11yclick, _TemplatedMixin, on // get url params var urlObject = urlUtils.urlToObject(window.location.href); urlObject.query = urlObject.query || {}; - if(urlObject.query.locale){ - delete urlObject.query.locale; - } // include extent in url if (this.get("useExtent") && map) { // get map extent in geographic var gExtent = map.geographicExtent; // set extent string - urlObject.query.extent = gExtent.xmin.toFixed(4) + "," + gExtent.ymin.toFixed(4) + "," + gExtent.xmax.toFixed(4) + "," + gExtent.ymax.toFixed(4); + urlObject.query.extent = gExtent.xmin.toFixed(4) + ',' + gExtent.ymin.toFixed(4) + ',' + gExtent.xmax.toFixed(4) + ',' + gExtent.ymax.toFixed(4); } else { urlObject.query.extent = null; @@ -202,7 +210,6 @@ Evented, declare, lang, has, esriNS, _WidgetBase, a11yclick, _TemplatedMixin, on url += "?"; useSeparator = true; } - url += i + "=" + urlObject.query[i]; } } diff --git a/js/TableOfContents.js b/js/TableOfContents.js index 4f7c1603..e9c3d535 100755 --- a/js/TableOfContents.js +++ b/js/TableOfContents.js @@ -1,7 +1,17 @@ -define(["dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/has", "esri/kernel", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/on", -// load template -"dojo/text!application/dijit/templates/TableOfContents.html", "dojo/dom-class", "dojo/dom-style", "dojo/dom-construct", "dojo/_base/event", "dojo/_base/array"], function ( -Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemplate, domClass, domStyle, domConstruct, event, array) { +define(["dojo/Evented", "dojo/_base/declare", "dojo/_base/lang", "dojo/has", "esri/kernel", + "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/on", + "dojo/text!application/dijit/templates/TableOfContents.html", + "dojo/dom-class", "dojo/dom-attr", "dojo/dom-style", "dojo/dom-construct", "dojo/_base/event", + "dojo/_base/array", + "esri/symbols/TextSymbol", "esri/renderers/SimpleRenderer", "esri/layers/LabelLayer" + ], function ( + Evented, declare, lang, has, esriNS, + _WidgetBase, _TemplatedMixin, on, + dijitTemplate, + domClass, domAttr, domStyle, domConstruct, event, + array, + TextSymbol, SimpleRenderer, LabelLayer + ) { var Widget = declare("esri.dijit.TableOfContents", [_WidgetBase, _TemplatedMixin, Evented], { templateString: dijitTemplate, // defaults @@ -11,6 +21,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp layers: null, visible: true }, + // lifecycle: 1 constructor: function (options, srcRefNode) { // mix in settings and defaults @@ -35,9 +46,9 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp title: "toc-title", titleContainer: "toc-title-container", content: "toc-content", - titleCheckbox: "toc-checkbox", + titleCheckbox: "checkbox", checkboxCheck: "icon-check-1", - titleText: "toc-text", + titleText: "checkbox", accountText: "toc-account", visible: "toc-visible", settingsIcon: "icon-cog", @@ -47,6 +58,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp clear: "clear" }; }, + // start widget. called by user startup: function () { // map not defined @@ -63,6 +75,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp })); } }, + // connections/subscriptions will be cleaned up during the destroy() lifecycle phase destroy: function () { this._removeEvents(); @@ -75,21 +88,27 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp // toggle // expand // collapse + /* ---------------- */ /* Public Functions */ /* ---------------- */ + show: function () { this.set("visible", true); }, + hide: function () { this.set("visible", false); }, + refresh: function () { this._createList(); }, - /* ---------------- */ + + /* ----------------- */ /* Private Functions */ - /* ---------------- */ + /* ----------------- */ + _createList: function () { var layers = this.get("layers"); this._nodes = []; @@ -97,10 +116,13 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp this._removeEvents(); // clear node this._layersNode.innerHTML = ""; + domAttr.set(this._layersNode, "role", "list"); // if we got layers if (layers && layers.length) { + for (var i = 0; i < layers.length; i++) { var layer = layers[i]; + // ceckbox class var titleCheckBoxClass = this.css.titleCheckbox; // layer class @@ -116,61 +138,76 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp titleCheckBoxClass += " "; titleCheckBoxClass += this.css.checkboxCheck; } + // layer node var layerDiv = domConstruct.create("div", { - className: layerClass + className: layerClass, + role: "listitem", }); domConstruct.place(layerDiv, this._layersNode, "first"); + // title of layer var titleDiv = domConstruct.create("div", { - className: this.css.title - }); - domConstruct.place(titleDiv, layerDiv, "last"); + className: this.css.title, + }, layerDiv); + // title container var titleContainerDiv = domConstruct.create("div", { - className: this.css.titleContainer - }); - domConstruct.place(titleContainerDiv, titleDiv, "last"); - // Title checkbox - var titleCheckbox = domConstruct.create("div", { - className: titleCheckBoxClass - }); - domConstruct.place(titleCheckbox, titleContainerDiv, "last"); - // Title text - var titleText = domConstruct.create("div", { - className: this.css.titleText, - title: layer.title, - innerHTML: layer.title - }); - domConstruct.place(titleText, titleContainerDiv, "last"); - // Account text - var accountText; + className: this.css.titleContainer, + tabindex: -1, + }, titleDiv); + + titleCheckbox = domConstruct.create("input", + { + id: "layer_ck_"+i, + className: titleCheckBoxClass, //this.css.titleCheckbox, + type: "checkbox", + tabindex: 0, + checked: layer.visibility, + }, titleContainerDiv); + + var titleText = domConstruct.create("label", { + "for": "layer_ck_"+i, + "className": this.css.titleText, + "innerHTML": layer.title, + role: "presentation", + //"title" : layer.title + }, titleContainerDiv); + + this._atachSpaceKey(titleContainerDiv, titleCheckbox); + + var accountText = ''; if (layer.account) { accountText = domConstruct.create("a", { className: this.css.accountText, id: layer.account - }); - domConstruct.place(accountText, titleText, "last"); + }, titleText); } + // settings var settingsDiv, settingsIcon; - if (layer.settings) { + if (layer.layerObject && + dojo.exists("settings", layer) && + layer.layerObject.isEditable()) + { settingsDiv = domConstruct.create("div", { className: this.css.settings, id: layer.settings - }); - domConstruct.place(settingsDiv, titleContainerDiv, "last"); - // settings icon - settingsIcon = domConstruct.create("div", { - className: this.css.settingsIcon - }); - domConstruct.place(settingsIcon, settingsDiv, "last"); + }, titleContainerDiv); + + settingsIcon = domConstruct.create("img", { + 'src' : 'images/icon-cog.png', + alt:'Configuration', + role: "button,", + tabindex:0, + }, settingsDiv); } + // clear css var clearCSS = domConstruct.create("div", { className: this.css.clear - }); - domConstruct.place(clearCSS, titleContainerDiv, "last"); + }, titleContainerDiv); + // lets save all the nodes for events var nodesObj = { checkbox: titleCheckbox, @@ -189,9 +226,18 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp this._setLayerEvents(); } }, + + _atachSpaceKey: function(onButton, clickButton) { + on(onButton, 'keyup', lang.hitch(clickButton, function(event){ + if(event.keyCode=='32') + this.click(); + })); + }, + _refreshLayers: function () { this.refresh(); }, + _removeEvents: function () { var i; // checkbox click events @@ -209,15 +255,25 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp this._checkEvents = []; this._layerEvents = []; }, + _toggleVisible: function (index, visible) { // update checkbox and layer visibility classes domClass.toggle(this._nodes[index].layer, this.css.visible, visible); domClass.toggle(this._nodes[index].checkbox, this.css.checkboxCheck, visible); + this.emit("toggle", { index: index, visible: visible }); + + if(visible) { + domAttr.set(this._nodes[index].checkbox, "checked", "checked"); + } + else { + domAttr.set(this._nodes[index].checkbox, "checked", ""); + } }, + _layerEvent: function (layer, index) { // layer visibility changes var visChange = on(layer, "visibility-change", lang.hitch(this, function (evt) { @@ -226,6 +282,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp })); this._layerEvents.push(visChange); }, + _featureCollectionVisible: function (layer, index, visible) { // all layers either visible or not var equal; @@ -253,6 +310,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp this._toggleVisible(index, visible); } }, + _createFeatureLayerEvent: function (layer, index, i) { var layers = layer.featureCollection.layers; // layer visibility changes @@ -262,6 +320,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp })); this._layerEvents.push(visChange); }, + _featureLayerEvent: function (layer, index) { // feature collection layers var layers = layer.featureCollection.layers; @@ -272,6 +331,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp } } }, + _setLayerEvents: function () { // this function sets up all the events for layers var layers = this.get("layers"); @@ -291,6 +351,7 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp } } }, + _toggleLayer: function (layerIndex) { // all layers if (this.layers && this.layers.length) { @@ -331,34 +392,32 @@ Evented, declare, lang, has, esriNS, _WidgetBase, _TemplatedMixin, on, dijitTemp } } }, + _checkboxEvent: function (index) { // when checkbox is clicked - var checkEvent = on(this._nodes[index].checkbox, "click", lang.hitch(this, function (evt) { + var checkEvent = on(this._nodes[index].checkbox, "click", lang.hitch(this, + function (evt) { // toggle layer visibility this._toggleLayer(index); - event.stop(evt); + //event.stop(evt); })); this._checkEvents.push(checkEvent); - // when title is clicked - var titleEvent = on(this._nodes[index].titleText, "click", lang.hitch(this, function (evt) { - // toggle layer visibility - this._toggleLayer(index); - event.stop(evt); - })); - this._checkEvents.push(titleEvent); }, + _init: function () { this._visible(); this._createList(); this.set("loaded", true); this.emit("load", {}); }, + _updateThemeWatch: function () { var oldVal = arguments[1]; var newVal = arguments[2]; domClass.remove(this.domNode, oldVal); domClass.add(this.domNode, newVal); }, + _visible: function () { if (this.get("visible")) { domStyle.set(this.domNode, "display", "block"); diff --git a/js/dijit/templates/ShareDialog.html b/js/dijit/templates/ShareDialog.html index 27ab1891..e48fc596 100644 --- a/js/dijit/templates/ShareDialog.html +++ b/js/dijit/templates/ShareDialog.html @@ -4,26 +4,44 @@
      ${_i18n.widgets.ShareDialog.heading}
      -
      -
      -
      -
      - -
      + + + + + + + +
      -
      - +
      + +
      -
      ${_i18n.widgets.ShareDialog.url}
      - -
      ${_i18n.widgets.ShareDialog.embed}
      + +
      - + +
      -
      ${_i18n.widgets.ShareDialog.size}
      - + +
      diff --git a/js/dijit/templates/instructions.french.html b/js/dijit/templates/instructions.french.html new file mode 100644 index 00000000..a8722bf5 --- /dev/null +++ b/js/dijit/templates/instructions.french.html @@ -0,0 +1,12 @@ +
      +

      Outre la souris, vous pouvez utiliser les raccourcis suivants:

      +
        +
      • La touche Tab pour naviguer d’un élément à l’autre.
      • +
      • MAJ Tab pour naviguer vers l’arrière.
      • +
      • La barre d’ESPACEMENT pour activer une sélection plus précise.
      • +
      • La touche ENTRÉE pour activer l’élément choisi.
      • +
      • Appuyer sur Alt + flèche vers le bas pour ouvrir une liste déroulante.
      • +
      • La touche Alt avec un numéro pour naviguer rapidement (l’indice s’affiche).
      • +
      • La touche ÉCHAPPEMENT pour aller au point de départ de l’élément choisi.
      • +
      +
      \ No newline at end of file diff --git a/js/dijit/templates/instructions.html b/js/dijit/templates/instructions.html new file mode 100644 index 00000000..6b90d019 --- /dev/null +++ b/js/dijit/templates/instructions.html @@ -0,0 +1,12 @@ +
      +

      In addition to the mouse you may:

      +
        +
      • Use Tab key to navigate from item to item.
      • +
      • Use SHIFT Tab to navigate backwards.
      • +
      • Hit SPACE bar to toggle a focused check box.
      • +
      • Press ENTER to activate the focused item.
      • +
      • Press Alt + Arrow-Down to expand a combo box.
      • +
      • Use Alt with a number to fast navigate (hint will display.)
      • +
      • Use ESCAPE to go to the parent of the focus element.
      • +
      +
      \ No newline at end of file diff --git a/js/has-config.js b/js/has-config.js index f4390842..6547176b 100644 --- a/js/has-config.js +++ b/js/has-config.js @@ -4,6 +4,7 @@ https://dojotoolkit.org/documentation/tutorials/1.8/device_optimized_builds/ http://dante.dojotoolkit.org/hasjs/tests/runTests.html*/ define(["dojo/has"], function (has) { + var getTool = function (name, config) { var tool = false; for (var i = 0; i < config.tools.length; i++) { @@ -16,6 +17,14 @@ define(["dojo/has"], function (has) { }; /*App capabilities*/ + has.add("navigation", function (g) { + var navigation = getTool("navigation", g.config); + //overwrite the default with app settings + if (g.config.hasOwnProperty("navigation")) { + navigation = g.config.navigation; + } + return navigation; + }); has.add("search", function (g) { var search = g.config.search || false; //overwrite the default with app settings @@ -87,57 +96,37 @@ define(["dojo/has"], function (has) { } return home; }); - has.add("layers", function (g) { - var layers = getTool("layers", g.config); + has.add("features", function (g) { + var features = getTool("features", g.config); //overwrite the default with app settings - if (g.config.hasOwnProperty("tool_layers")) { - layers = g.config.tool_layers; + if (g.config.hasOwnProperty("tool_features")) { + features = g.config.tool_features; } - return layers; + return features; }); - - has.add("layers-sublayers", function (g) { - var subLayers = false; - for (var i = 0; i < g.config.tools.length; i++) { - if (g.config.tools[i].name === "layers") { - subLayers = g.config.tools[i].sublayers; - break; - } - } + has.add("filter", function (g) { + var filter = getTool("filter", g.config); //overwrite the default with app settings - if (g.config.hasOwnProperty("tool_sublayers")) { - subLayers = g.config.tool_sublayers; + if (g.config.hasOwnProperty("tool_filter")) { + filter = g.config.tool_filter; } - return subLayers; + return filter; }); - has.add("layers-legend", function (g) { - var layerLegend = false; - for (var i = 0; i < g.config.tools.length; i++) { - if (g.config.tools[i].name === "layers") { - layerLegend = g.config.tools[i].legend; - break; - } - } + has.add("layers", function (g) { + var layers = getTool("layers", g.config); //overwrite the default with app settings - if (g.config.hasOwnProperty("tool_layerlegend")) { - layerLegend = g.config.tool_layerlegend; + if (g.config.hasOwnProperty("tool_layers")) { + layers = g.config.tool_layers; } - return layerLegend; + return layers; }); - - has.add("layers-opacity", function (g) { - var opacity = false; - for (var i = 0; i < g.config.tools.length; i++) { - if (g.config.tools[i].name === "layers") { - opacity = g.config.tools[i].opacityslider; - break; - } - } + has.add("instructions", function (g) { + var instructions = getTool("instructions", g.config); //overwrite the default with app settings - if (g.config.hasOwnProperty("tool_opacity")) { - opacity = g.config.tool_opacity; + if (g.config.hasOwnProperty("tool_instructions")) { + instructions = g.config.tool_instructions; } - return opacity; + return instructions; }); has.add("legend", function (g) { @@ -224,6 +213,8 @@ define(["dojo/has"], function (has) { return printLayouts; }); + + has.add("share", function (g) { var share = getTool("share", g.config); //overwrite the default with app settings diff --git a/js/main.js b/js/main.js index 5811a910..e3692c35 100755 --- a/js/main.js +++ b/js/main.js @@ -13,144 +13,136 @@ | See the License for the specific language governing permissions and | limitations under the License. */ -define([ - "dojo/ready", - "dojo/json", - "dojo/i18n!esri/nls/jsapi", +define(["dojo/ready", + "dojo/aspect", "dijit/registry", + "dojo/json", "dojo/_base/array", "dojo/_base/Color", "dojo/_base/declare", + "dojo/_base/lang", "dojo/dom", "dojo/dom-geometry", "dojo/dom-attr", "dojo/dom-class", + "dojo/dom-construct", "dojo/dom-style", "dojo/on", "dojo/Deferred", "dojo/promise/all", + "dojo/query", "dijit/Menu", "dijit/CheckedMenuItem", "application/toolbar", + "application/has-config", "esri/arcgis/utils", "esri/lang", + "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dijit/focus", + "esri/tasks/query", + "esri/dijit/HomeButton", "esri/dijit/LocateButton", + "esri/dijit/Legend", "esri/dijit/BasemapGallery", + "dojo/i18n!application/nls/resources", + "dojo/i18n!application/nls/BaseMapLabels", + "esri/dijit/Measurement", "esri/dijit/OverviewMap", "esri/geometry/Extent", + "esri/layers/FeatureLayer", "application/NavToolBar/NavToolBar", + "application/FeatureList/FeatureList", "application/Filters/Filters", "application/TableOfContents", + "application/LanguageSelect/LanguageSelect", + "application/ShareDialog", //"application/SearchSources", + "esri/symbols/SimpleMarkerSymbol", "esri/symbols/PictureMarkerSymbol", "esri/graphic", + "esri/dijit/InfoWindow", + "dojo/NodeList-dom", "dojo/NodeList-traverse"], + function ( + ready, + aspect, registry, + JSON, array, Color, declare, + lang, dom, domGeometry, domAttr, domClass, + domConstruct, domStyle, on, Deferred, all, + query, Menu, CheckedMenuItem, Toolbar, + has, arcgisUtils, esriLang, + BorderContainer, ContentPane, focusUtil, + Query, + HomeButton, LocateButton, + Legend, BasemapGallery, + i18n, i18n_BaseMapLabels, + Measurement, OverviewMap, Extent, + FeatureLayer, NavToolBar, + FeatureList, Filters, TableOfContents, LanguageSelect, + ShareDialog, //SearchSources, + SimpleMarkerSymbol, PictureMarkerSymbol, Graphic, + InfoWindow) { - "dojo/_base/array", - "dojo/_base/Color", - "dojo/_base/declare", - "dojo/_base/lang", - - "dojo/dom", - "dojo/dom-geometry", - "dojo/dom-attr", - "dojo/dom-class", - "dojo/dom-construct", - "dojo/dom-style", - - "dojo/on", - "dojo/Deferred", - "dojo/promise/all", - "dojo/query", - - "dijit/registry", - "dijit/Menu", - "dijit/CheckedMenuItem", - - "application/toolbar", - "application/has-config", - "application/ShareDialog", - "application/SearchSources", - - "esri/arcgis/utils", - "esri/lang", - "esri/urlUtils", - - "esri/dijit/HomeButton", - "esri/dijit/LocateButton", - "esri/dijit/Legend", - "esri/dijit/BasemapGallery", - "esri/dijit/Measurement", - "esri/dijit/OverviewMap", - "esri/dijit/LayerList", - - "esri/geometry/Extent", - "esri/layers/FeatureLayer" - ], function ( - ready,JSON, - - esriBundle, - - array, Color, declare, lang, - - dom, domGeometry, domAttr, domClass, - domConstruct, domStyle, - - on, Deferred, all, query, - - registry, Menu, CheckedMenuItem, - - Toolbar, has, - ShareDialog, SearchSources, - - arcgisUtils, esriLang, urlUtils, - - HomeButton, LocateButton, Legend, - BasemapGallery, Measurement, - OverviewMap, LayerList, - - Extent, FeatureLayer - - ) { return declare(null, { config: {}, color: null, theme: null, map: null, + initExt: null, mapExt: null, editorDiv: null, editor: null, editableLayers: null, timeFormats: ["shortDateShortTime", "shortDateLEShortTime", "shortDateShortTime24", "shortDateLEShortTime24", "shortDateLongTime", "shortDateLELongTime", "shortDateLongTime24", "shortDateLELongTime24"], + startup: function (config) { // config will contain application and user defined info for the template such as i18n strings, the web map id // and application id and any url parameters and any application specific configuration information. if (config) { this.config = config; - this.color = this._setColor(this.config.color); - this.theme = this._setColor(this.config.theme); - - // Create and add custom style sheet - if(this.config.customstyle){ - var style = document.createElement("style"); - style.appendChild(document.createTextNode(this.config.customstyle)); - document.head.appendChild(style); - } - - + this.color = this.setColor(this.config.color); + this.hoverColor = typeof(this.config.hoverColor)=='undefined' ? this.setColor('#000000', 0.4) : this.setColor(this.config.hoverColor, 0.9); + this.focusColor = typeof(this.config.focusColor)=='undefined' ? this.setColor('#1f1f1f', 0.4) : this.setColor(this.config.focusColor, 0.9); + this.activeColor = typeof(this.config.activeColor)=='undefined' ? this.focusColor : this.setColor(this.config.activeColor, 0.9); + this.theme = this.setColor(this.config.theme); // document ready ready(lang.hitch(this, function () { //supply either the webmap id or, if available, the item info var itemInfo = this.config.itemInfo || this.config.webmap; - - // Setup the modal overlay if enabled - if(this.config.splashModal){ - domClass.remove("modal", "hide-modal"); - var title = this.config.splashTitle || ""; - var content = this.config.splashContent || ""; - dom.byId("modalTitle").innerHTML = title; - dom.byId("modalContent").innerHTML = content; - // Close button handler for the overlay - on(dom.byId("closeOverlay"), "click", lang.hitch(this, function(){ - this._createWebMap(itemInfo); - domClass.add("modal", "hide-modal"); - })); - }else{ - this._createWebMap(itemInfo); + //If a custom extent is set as a url parameter handle that before creating the map + if (this.config.extent) { + var extArray = decodeURIComponent(this.config.extent).split(","); + + if (extArray.length === 4) { + itemInfo.item.extent = [ + [parseFloat(extArray[0]), parseFloat(extArray[1])], + [parseFloat(extArray[2]), parseFloat(extArray[3])] + ]; + } else if (extArray.length === 5) { + this.initExt = new Extent(JSON.parse(this.config.extent)); + } } - - - + this._createWebMap(itemInfo); })); } else { var error = new Error("Main:: Config is not defined"); this.reportError(error); } + + var languages = [ + { + code:this.config.lang1code, + img:this.config.lang1imageSrc, + shortName:this.config.lang1shortName, + name:this.config.lang1name, + appId:this.config.lang1appId + }, + { + code:this.config.lang2code, + img:this.config.lang2imageSrc, + shortName:this.config.lang2shortName, + name:this.config.lang2name, + appId:this.config.lang2appId + }, + { + code:this.config.lang3code, + img:this.config.lang3imageSrc, + shortName:this.config.lang3shortName, + name:this.config.lang3name, + appId:this.config.lang3appId + } + ]; + new LanguageSelect({ + locale: document.documentElement.lang, + //location: window.location, + languages:languages, + textColor:this.titleColor, + showLabel:this.config.languageLabel + }, dojo.byId('languageSelectNode')).startup(); }, reportError: function (error) { // remove loading class from body - domClass.remove(document.body, "app-loading"); - domClass.add(document.body, "app-error"); + domClass.replace(document.body, "app-error","app-loading"); // an error occurred - notify the user. In this example we pull the string from the // resource.js file located in the nls folder because we've set the application up // for localization. If you don't need to support multiple languages you can hardcode the // strings here and comment out the call in index.html to get the localization strings. // set message var node = dom.byId("loading_message"); + if (node) { if (this.config && this.config.i18n) { node.innerHTML = this.config.i18n.map.error + ": " + error.message; @@ -159,20 +151,146 @@ define([ } } }, - // Map is ready + + setColor: function (color, tr) { + var rgb = Color.fromHex(color).toRgb(); + var outputColor = null; + if (has("ie") < 9) { + outputColor = color; + } else { + //rgba supported so add + rgb.push(tr); + outputColor = Color.fromArray(rgb); + } + return outputColor; + }, + _mapLoaded: function () { + // this.map.resize(); + // this.map.reposition(); + query(".esriSimpleSlider").style("backgroundColor", this.theme.toString()); // remove loading class from body + domClass.remove(document.body, "app-loading"); - if(!this.config.popupPanel){ - //on(this.map.infoWindow, "selection-change", lang.hitch(this, this._movePopup)); - on(window, "orientationchange", lang.hitch(this, this._adjustPopupSize)); - this._adjustPopupSize(); + on(window, "orientationchange", lang.hitch(this, this._adjustPopupSize)); + this._adjustPopupSize(); + + var _map = this.map; + + on(this.map.infoWindow, "show", lang.hitch(this, function() { + this._initPopup(this.map.infoWindow.domNode); + })); + + on(this.map.infoWindow, "selection-change", lang.hitch(this, function() { + this._initPopup(this.map.infoWindow.domNode); + })); + }, + + _initPopup : function (node) { + images = node.querySelectorAll('img'); + for (var i = 0; i= 1) { + upper[0].focus(); + } else { + skipToMap(); + } + break; + case '0' : + if (event.altKey) { + skipSkip(); + } + break; + default: + break; + } + + }); + + on(document.body, 'keyup', function(event) { + if(!event.altKey) { + query('.goThereHint').forEach(function(h) { + domStyle.set(h, 'display',''); + }); + } + }); + + if(this.config.alt_keys) { + domConstruct.create("div", { + class:'goThereHint', + innerHTML: 'Alt + 1 '+this.config.i18n.skip.tools, + style:'left:20%; top:10px;' + }, dom.byId('panelTools')); + + domConstruct.create("div", { + class:'goThereHint', + innerHTML: 'Alt + 2 '+this.config.i18n.skip.search, + style:'left:20%; top:50%;' + }, dom.byId('panelSearch')); + + domConstruct.create("div", { + class:'goThereHint', + innerHTML: 'Alt + 3 '+this.config.i18n.skip.content, + style:'left:20%; top:50%;' + }, dom.byId('panelPages')); + + domConstruct.create("div", { + class:'goThereHint', + innerHTML: 'Alt + 4 '+this.config.i18n.skip.splitter, + style:'left:-30px; top:40%;' + }, dom.byId('dijit_layout_ContentPane_1_splitter')); + + domConstruct.create("div", { + class:'goThereHint', + innerHTML: 'Alt + 5 '+this.config.i18n.skip.map, + style:'left:10%; top:30%' + }, dom.byId('mapDiv')); + + domConstruct.create("div", { + class:'goThereHint', + innerHTML: 'Alt + 6 '+this.config.i18n.skip.help, + style:'left:20%; top:-75%;' + }, dom.byId('panelBottom')); + } + + var skipTools = query('.skip #skip-tools')[0]; + var skipSearch = query('.skip #skip-search')[0]; + var skipContent = query('.skip #skip-content')[0]; + var skipSplitter = query('.skip #skip-splitter')[0]; + var skipMap = query('.skip #skip-map')[0]; + var skipInstructions = query('.skip #skip-instructions')[0]; + var skipFeature = query('.skip #skip-feature')[0]; + + dojo.html.set(skipTools, "1. "+this.config.i18n.skip.tools); + dojo.html.set(skipSearch, "2. "+this.config.i18n.skip.search); + dojo.html.set(skipContent, "3. "+this.config.i18n.skip.content); + dojo.html.set(skipSplitter, "4. "+this.config.i18n.skip.splitter); + dojo.html.set(skipMap, "5. "+this.config.i18n.skip.map); + dojo.html.set(skipInstructions, "6. "+this.config.i18n.skip.help); + dojo.html.set(skipFeature, "7. "+this.config.i18n.skip.featureDetaills); + + skipTools.addEventListener('click', function (e) { skipToTools(); }); + skipSearch.addEventListener('click', function (e) { skipToSearch(); }); + skipContent.addEventListener('click', function (e) { skipToContent(); }); + skipSplitter.addEventListener('click', function (e) { skipToSplitter(); }); + skipMap.addEventListener('click', function (e) { skipToMap(); }); + skipInstructions.addEventListener('click', function (e) { skipToInstructions(); }); + skipFeature.addEventListener('click', function (e) { skipToFeature(); }); + + query('.skip').forEach(function(h) { + h.addEventListener('keydown', function (e) { + if(e.key === "Enter" || e.key === " " || e.char === " ") + { + e.target.click(); + e.preventDefault(); + } + }); + }); + + query('.skip a').forEach(function(a) { + a.onfocus = lang.hitch(a, function () { + console.log(this); + domAttr.set(this, "aria-hidden", "false"); + console.log(this); + }); + a.onblur = lang.hitch(a, function () { + console.log(this); + domAttr.set(this, "aria-hidden", "true"); + console.log(this); + }); + }); + + dojo.html.set(dom.byId('panelBottomSpan'), this.config.i18n.pressAlt); + dojo.html.set(dom.byId('searchLabel'), this.config.i18n.search); + + skipSkip = function() { + dom.byId('skip-tools').focus(); + }; + + skipToTools = function() { + query('#panelTools .panelToolActive input[type="image"]')[0].focus(); + //dom.byId('panelTools').focus(); + }; + + skipToSearch = function() { + dom.byId('search_input').focus(); + }; + + skipToContent = function() { + //dom.byId('panelPages').focus(); + dojo.query(".page.showAttr .pageBody")[0].focus(); + }; + + skipToSplitter = function() { + dom.byId('dijit_layout_ContentPane_1_splitter').focus(); + }; + + skipToMap = function() { + //document.querySelector('.esriSimpleSliderIncrementButton input').focus(); + document.querySelector('#mapDiv').focus(); + }; + + skipToInstructions = function() { + var activeTool = query('.panelToolActive'); + if(activeTool && activeTool.length>0) { + activeTool = activeTool[0].childNodes[0]; + activeTool.click(); + } + dom.byId('instructionsDiv').focus(); + }; + + skipToFeature = function() { + if(featureList) { + featureList.FocusDetails(); + } + }; + }, + + featureList : null, + + _addFeatures: function (tool, toolbar) { + //Add the legend tool to the toolbar. Only activated if the web map has operational layers. + var deferred = new Deferred(); + if (has("features")) { + var featuresDiv = toolbar.createTool(tool, "", "reload1.gif", "featureSelected"); + + var layers = this.config.response.itemInfo.itemData.operationalLayers; + + featureList = new FeatureList({ + map: this.map, + layers: layers, + toolbar: toolbar, + animatedMarker: this.config.animated_marker, + markerImage: this.config.marker, + markerSize: this.config.marker_size + }, featuresDiv); + featureList.startup(); + + // on(toolbar, 'updateTool_features', lang.hitch(this, function(name) { + // dom.byId('pageBody_features').focus(); + // })); + + deferred.resolve(true); + } + else { + // window._prevSelected = null; + deferred.resolve(false); + } + + return deferred.promise; + }, + + navDeferred : null, + + _addNavigation: function (tool, oldNaviagationToolBar, deferred) { + var navToolBar = domConstruct.create("div", { + id: "newNaviagationToolBar", + }); + + nav = new NavToolBar({ + map: this.map, + navToolBar: oldNaviagationToolBar, + iconColor: this.config.icons, + newIcons: this.config.new_icons?'.new':'', + zoomColor: this.focusColor, + }, navToolBar); + nav.startup(); + deferred.resolve(true); + return deferred.promise; + }, + + _addFilter: function (tool, toolbar) { + //Add the legend tool to the toolbar. Only activated if the web map has operational layers. + var deferred = new Deferred(); + if (has("filter")) { + var filterDiv = toolbar.createTool(tool, "", "", "somefilters"); + + var layers = this.config.response.itemInfo.itemData.operationalLayers; + + filter = new Filters({ + map: this.map, + layers: layers, + toolbar: toolbar, + }, filterDiv); + filter.startup(); + + // on(toolbar, 'updateTool_filter', lang.hitch(this, function(name) { + // dom.byId('pageBody_filter').focus(); + // })); + + deferred.resolve(true); + } + else { + // window._prevSelected = null; + deferred.resolve(false); + } + + return deferred.promise; }, - _addBasemapGallery: function (tool, toolbar, panelClass) { - //Add the basemap gallery to the toolbar. + + _addBasemapGallery: function (tool, toolbar) { var deferred = new Deferred(); if (has("basemap")) { - var basemapDiv = toolbar.createTool(tool, panelClass); + var basemapDiv = toolbar.createTool(tool); var basemap = new BasemapGallery({ id: "basemapGallery", map: this.map, @@ -312,7 +661,85 @@ define([ portalUrl: this.config.sharinghost, basemapsGroup: this._getBasemapGroup() }, domConstruct.create("div", {}, basemapDiv)); + basemap.startup(); + + on(basemap, "load", lang.hitch(basemap, function () { + var list = this.domNode.querySelector("div"); + domAttr.set(list, "role", "list"); + + var galleryNodeObserver = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + //console.log(mutation); + var node = mutation.target; + var aSpan = node.querySelector("a span"); + var l = aSpan.innerText; + if(dojo.hasClass(node, "esriBasemapGallerySelectedNode")) + { + l += ' '+this.config.i18n.tools.basemapGallery.selected; + } + l += '.'; + //node.querySelector('a').focus(); + domAttr.set(aSpan, 'aria-label', l); + //aSpan.focus(); + }); + }); + + var observerCfg = { attributes: true, childList: false, characterData: false }; + + var nodes = this.domNode.querySelectorAll(".esriBasemapGalleryNode"); + array.forEach(nodes, function(node){ + domAttr.set(node, "role", "listitem"); + //domAttr.set(node, "aria-hidden", "true"); + + galleryNodeObserver.observe(node, observerCfg); + + var img = node.querySelector("img"); + img.alt=''; + domAttr.set(img, "aria-hidden", true); + domAttr.remove(img, "title"); + domAttr.remove(img, "tabindex"); + + var aNode = node.querySelector("a"); + domAttr.set(aNode, "tabindex", -1); + var labelNode = node.querySelector(".esriBasemapGalleryLabelContainer"); + domAttr.remove(labelNode.firstChild, "alt"); + domAttr.remove(labelNode.firstChild, "title"); + dojo.place(labelNode, aNode, "last"); + + var aSpan = node.querySelector("a span"); + var aSpanLabel = aSpan.innerHTML.toLowerCase().replace(/\s/g, '_'); + try { + var localizedLabel = i18n_BaseMapLabels.baseMapLabels[aSpanLabel]; + if(localizedLabel && localizedLabel !== undefined) + aSpan.innerText = localizedLabel; + var l = aSpan.innerText; + if(dojo.hasClass(node, "esriBasemapGallerySelectedNode")) + { + l += ' '+this.config.i18n.tools.basemapGallery.selected; + } + l += '.'; + domAttr.set(aSpan, 'aria-label', l); + //img.alt=aSpan.innerText; + } catch(e) {} + + domAttr.set(labelNode, "tabindex", 0); + on(img, "click", function() { node.focus();}); + on(node,"keydown", function(ev) { + if(ev.key === "Enter" || ev.key === " " || ev.char === " ") { + aNode.click(); + } else if(ev.key === "Tab" && !ev.shiftKey) { + if(node.nextElementSibling.nodeName != "BR") { + node.nextElementSibling.focus(); + } else { + document.querySelector('#dijit_layout_ContentPane_0_splitter').focus(); + } + } else if(ev.key === "Tab" && ev.shiftKey) { + node.focus(); + } + }); + }); + })); deferred.resolve(true); } else { deferred.resolve(false); @@ -321,67 +748,139 @@ define([ return deferred.promise; }, - _addBookmarks: function (tool, toolbar, panelClass) { - //Add the bookmarks tool to the toolbar. Only activated if the webmap contains bookmarks. + _addBookmarks: function (tool, toolbar) { var deferred = new Deferred(); if (this.config.response.itemInfo.itemData.bookmarks) { - //Conditionally load this module since most apps won't have bookmarks + require(["application/has-config!bookmarks?esri/dijit/Bookmarks"], lang.hitch(this, function (Bookmarks) { if (!Bookmarks) { deferred.resolve(false); return; } - var bookmarkDiv = toolbar.createTool(tool, panelClass); + var bookmarkDiv = toolbar.createTool(tool); + // var bookmarkDiv = domConstruct.create("div",{ class: "margin"}, bDiv); var bookmarks = new Bookmarks({ map: this.map, bookmarks: this.config.response.itemInfo.itemData.bookmarks }, domConstruct.create("div", {}, bookmarkDiv)); + items = bookmarks.bookmarkDomNode.querySelectorAll('.esriBookmarkItem'); + if(items && items.length>0) + { + itemsTable = items[0].parentNode.parentNode.parentNode.parentNode; + var header = document.createElement("tr"); + header.innerHTML = "Bookmarks"; + itemsTable.insertBefore(header, items[0].parentNode.parentNode.parentNode); + domAttr.set(itemsTable,"role","list"); + + for(i=0; i"; + var detailDiv = toolbar.createTool(tool); + domConstruct.create('div',{ + tabindex:0 + }); + detailDiv.innerHTML = "
      "+description+"
      "; + detailDiv = dojo.query("#detailDiv")[0]; + if(!has("instructions")) + domClass.add(detailDiv, "detailFull"); + else + domClass.add(detailDiv, "detailHalf"); + + var detailBtn = dojo.query("#toolButton_details")[0]; + domClass.add(detailBtn, "panelToolDefault"); } deferred.resolve(true); } else { - deferred.resolve(false); + deferred.resolve(true); } return deferred.promise; + }, + + _addInstructions: function (tool, toolbar, deferedDetails) { + var deferred = new Deferred(); + if (!has("instructions")) { + deferred.resolve(false); + } + else + { + if(!has("details")) + { + require(["dojo/text!application/dijit/templates/"+this.config.i18n.instructions+".html"], + function(instructionsText){ + var instructionsDiv = toolbar.createTool(tool); + domConstruct.create('div',{ + id:"instructionsDiv", + innerHTML: instructionsText, + tabindex: 0 + }, domConstruct.create("div", {}, instructionsDiv)); + }); + + var instructionsBtn = dojo.query("#toolButton_instructions")[0]; + domClass.add(instructionsBtn, "panelToolDefault"); + } + else { + deferedDetails.then(lang.hitch(this, function(r) { + require(["dojo/text!application/dijit/templates/"+this.config.i18n.instructions+".html"], + function(instructionsText){ + var instructionsDiv = domConstruct.create('div',{ + id:"instructionsDiv", + innerHTML: instructionsText, + tabindex: 0 + }, dom.byId("pageBody_details")); + }); + + on(toolbar, 'updateTool_details', this._adjustDetails); + on(this.map, 'resize', this._adjustDetails); + document.body.onresize = this._adjustDetails; + })); + } + deferred.resolve(true); + } + return deferred.promise; + }, + _adjustDetails :function() { + try { + var pageBody = dojo.byId('pageBody_details'); + var detailDiv = dojo.byId('detailDiv'); + detailDiv.style.maxHeight=(pageBody.clientHeight-instructionsDiv.clientHeight - 30) + 'px'; + } catch (e) { + /* ignore instructionDiv not defined error: will come defined next time! */ + } }, - _addEditor: function (tool, toolbar, panelClass) { + _addEditor: function (tool, toolbar) { //Add the editor widget to the toolbar if the web map contains editable layers var deferred = new Deferred(); this.editableLayers = this._getEditableLayers(this.config.response.itemInfo.itemData.operationalLayers); if (has("edit") && this.editableLayers.length > 0) { if (this.editableLayers.length > 0) { - this.editorDiv = toolbar.createTool(tool, panelClass); + this.editorDiv = toolbar.createTool(tool); return this._createEditor(); } else { console.log("No Editable Layers"); @@ -393,6 +892,7 @@ define([ return deferred.promise; }, + _createEditor: function () { var deferred = new Deferred(); //Dynamically load since many apps won't have editable layers @@ -417,15 +917,17 @@ define([ time: true }; } - //Add all editable fields even if not visible. - //if (field.visible) { - fieldInfos.push(field); - //} + + if (field.visible) { + fieldInfos.push(field); + } + })); layer.fieldInfos = fieldInfos; } })); + var settings = { map: this.map, layerInfos: this.editableLayers, @@ -435,22 +937,37 @@ define([ settings: settings }, domConstruct.create("div", {}, this.editorDiv)); - this.editor.startup(); deferred.resolve(true); - })); + return deferred.promise; + }, + + _getEditableLayers: function (layers) { + var layerInfos = []; + array.forEach(layers, lang.hitch(this, function (layer) { + if (layer && layer.layerObject) { + var eLayer = layer.layerObject; + if (eLayer instanceof FeatureLayer && eLayer.isEditable()) { + layerInfos.push({ + "featureLayer": eLayer + }); + } + } + })); + return layerInfos; }, + _destroyEditor: function () { if (this.editor) { this.editor.destroy(); this.editor = null; } - }, - _addLayers: function (tool, toolbar, panelClass) { + + _addLayers: function (tool, toolbar) { //Toggle layer visibility if web map has operational layers var deferred = new Deferred(); @@ -460,27 +977,20 @@ define([ deferred.resolve(false); } else { if (has("layers")) { + panelClass = ""; - //Use small panel class if layer layer is less than 5 - if (layers.length < 5) { - panelClass = "small"; - } else if (layers.length < 15) { - panelClass = "medium"; - } else { - panelClass = "large"; - } - var layersDiv = toolbar.createTool(tool, panelClass); + var layersDivDesc = toolbar.createTool(tool); + // var layersDivDesc = domConstruct.create("div", {class:'margin'}, layersDiv); - var toc = new LayerList({ + var toc = new TableOfContents({ map: this.map, - showSubLayers: has("layers-sublayers"), - subLayers: has("layers-sublayers"), - showLegend: has("layers-legend"), - showOpacitySlider: has("layers-opacity"), - layers: arcgisUtils.getLayerList(this.config.response) - }, domConstruct.create("div", {}, layersDiv)); + layers: layers + }, domConstruct.create("div", {}, layersDivDesc)); toc.startup(); + // on(toolbar, 'updateTool_layers', lang.hitch(this, function(name) { + // dom.byId('pageBody_layers').focus(); + // })); deferred.resolve(true); } else { @@ -489,60 +999,119 @@ define([ } return deferred.promise; }, - _addLegend: function (tool, toolbar, panelClass) { + + _addLegend: function (tool, toolbar) { //Add the legend tool to the toolbar. Only activated if the web map has operational layers. var deferred = new Deferred(); var layers = arcgisUtils.getLegendLayers(this.config.response); - if (layers.length === 0) { deferred.resolve(false); } else { if (has("legend")) { - var legendLength = 0; - array.forEach(layers, lang.hitch(this, function (layer) { - if (layer.infos && layer.infos.length) { - legendLength += layer.infos.length; - } - })); - - if (legendLength.length < 5) { - panelClass = "small"; - } else if (legendLength.length < 15) { - panelClass = "medium"; - } else { - panelClass = "large"; - } - - var legendDiv = toolbar.createTool(tool, panelClass); + var legendDiv = toolbar.createTool(tool, ""); var legend = new Legend({ map: this.map, layerInfos: layers - }, domConstruct.create("div", {}, legendDiv)); + }, domConstruct.create("div", {role:'application'}, legendDiv));//Desc)); domClass.add(legend.domNode, "legend"); legend.startup(); - if (this.config.activeTool !== "") { - toolbar.activateTool(this.config.activeTool || "legend"); - } else { - toolbar.closePage(); - } + + on(toolbar, 'updateTool_legend', lang.hitch(this, function(name) { + fixLegend(); + // dom.byId('pageBody_legend').focus(); + })); + + var fixLegend = function() { + var tables = legend.domNode.querySelectorAll("table"); + if(tables && tables.length>0) + { + for(var t=0; t0) + { + for(var s=0; s0) + { + for(var i=0; i -this.div.clientHeight/2) { + dojo.style(this.div, 'top', --top + 'px'); + } + break; + case 40 : // down + if(top < this.div.parentElement.offsetHeight - this.div.clientHeight/2) { + dojo.style(this.div, 'top', ++top + 'px'); + } + break; + case 37 : // left + if(left > -this.div.clientWidth/2) { + dojo.style(this.div, 'left', --left + 'px'); + } + break; + case 39 : // right + if(left < this.div.parentElement.offsetWidth - this.div.clientWidth/2) { + dojo.style(this.div, 'left', ++left + 'px'); + } + break; + } + switch (event.keyCode) { + case 9: // tab + case 33: // PgUp + case 34: // PgDn + case 27: // Esc + break; + default: + event.stopPropagation(); + event.preventDefault(); + break; + } + })); + + on(onButton, 'keyup', lang.hitch(map, function(event) { + switch (event.keyCode) { + case 38 : // up + case 40 : // down + case 37 : // left + case 39 : // right + var a; + this._moveStopHandler(a); + break; + } + switch (event.keyCode) { + case 9: // tab + case 33: // PgUp + case 34: // PgDn + case 27: // Esc + break; + default: + event.stopPropagation(); + event.preventDefault(); + break; + } + })); + }, + + _addPrint: function (tool, toolbar) { + //Add the print widget to the toolbar. TODO: test custom layouts. var deferred = new Deferred(), - print = null; - require(["application/has-config!print?application/PrintConfig", "application/has-config!print?esri/dijit/Print"], lang.hitch(this, function (PrintConfig, Print) { - if (!PrintConfig || !Print) { + legendNode = null, + print = null; + + require(["application/has-config!print?esri/dijit/Print"], lang.hitch(this, function (Print) { + if (!Print) { deferred.resolve(false); return; } - var printDiv = toolbar.createTool(tool, panelClass); - var format = null; - array.forEach(this.config.tools, function (tool) { - if (tool.name === "print") { - format = tool.format; - } - }); - if (this.config.hasOwnProperty("tool_print_format")) { - format = this.config.tool_print_format; - } + var layoutOptions = { "titleText": this.config.title, "scalebarUnit": this.config.units, "legendLayers": [] }; - var printOptions = { - legendLayers: this.config.response, - layouts: has("print-layouts"), - format: format.toLowerCase() || null, - printTaskUrl: this.config.helperServices.printTask.url, - printi18n: this.config.i18n.tools.print, - layoutOptions: layoutOptions - }; - if(this.config.helperServices.printTask && this.config.helperServices.printTask.templates){ - printOptions.templates = this.config.helperServices.printTask.templates; + + var printDiv = domConstruct.create("div", { + class:"PrintDialog" + }, toolbar.createTool(tool)); + //get format + this.format = "PDF"; //default if nothing is specified + for (var i = 0; i < this.config.tools.length; i++) { + if (this.config.tools[i].name === "print") { + var f = this.config.tools[i].format; + this.format = f.toLowerCase(); + break; + } } - var printConfig = new PrintConfig(printOptions); - printConfig.createPrintOptions().then(lang.hitch(this, function (results) { - var templates = results.templates; - var legendLayers = results.legendLayers; - - //add a text box so users can enter a custom title - var titleNode = domConstruct.create("input", { - id: "print_title", - className: "printTitle", - tabindex: "0", - placeholder: this.config.i18n.tools.print.titlePrompt - }, domConstruct.create("div")); - domConstruct.place(titleNode, printDiv); + if (this.config.hasOwnProperty("tool_print_format")) { + this.format = this.config.tool_print_format.toLowerCase(); + } - if (has("print-legend")) { - var legendNode = domConstruct.create("input", { - id: "legend_ck", - className: "checkbox", - type: "checkbox", - checked: false - }, domConstruct.create("div", { - "class": "checkbox" - })); + if (has("print-legend")) { + legendNode = domConstruct.create("input", { + id: "legend_ck", + className: "checkbox", + type: "checkbox", + checked: false + }, domConstruct.create("div", { + "class": "checkbox" + })); + var labelNode = domConstruct.create("label", { + "for": "legend_ck", + "className": "checkbox", + "innerHTML": this.config.i18n.tools.print.legend + }, domConstruct.create("div")); + domConstruct.place(legendNode, printDiv); + domConstruct.place(labelNode, printDiv); - var labelNode = domConstruct.create("label", { - "for": "legend_ck", - "className": "checkbox", - "innerHTML": " " + this.config.i18n.tools.print.legend - }, domConstruct.create("div")); - domConstruct.place(legendNode, printDiv); - domConstruct.place(labelNode, printDiv); + on(legendNode, "change", lang.hitch(this, function (arg) { - on(legendNode, "change", lang.hitch(this, function (arg) { - if (legendNode.checked && legendLayers.length > 0) { + if (legendNode.checked) { + var layers = arcgisUtils.getLegendLayers(this.config.response); + var legendLayers = array.map(layers, function (layer) { + return { + "layerId": layer.layer.id + }; + }); + if (legendLayers.length > 0) { layoutOptions.legendLayers = legendLayers; - } else { - layoutOptions.legendLayers = []; } - array.forEach(this.print.templates, lang.hitch(this, function (template) { + array.forEach(print.templates, function (template) { template.layoutOptions = layoutOptions; - })); - })); + }); - } else { - domStyle.set("pageBody_print", "height", "90px"); - } - var printOptions = { - map: this.map, - id: "printButton", - url: this.config.helperServices.printTask.url - }; - if(templates){ - printOptions.templates = templates; + } else { + array.forEach(print.templates, function (template) { + if (template.layoutOptions && template.layoutOptions.legendLayers) { + template.layoutOptions.legendLayers = []; + } + }); + } + })); + } + + require(["application/has-config!print-layouts?esri/request", "application/has-config!print-layouts?esri/tasks/PrintTemplate"], lang.hitch(this, function (esriRequest, PrintTemplate) { + if (!esriRequest && !PrintTemplate) { + //Use the default print templates + var templates = [{ + layout: "Letter ANSI A Landscape", + layoutOptions: layoutOptions, + label: this.config.i18n.tools.print.layouts.label1 + " ( " + this.format + " )", + format: this.format + }, + { + layout: "Letter ANSI A Portrait", + layoutOptions: layoutOptions, + label: this.config.i18n.tools.print.layouts.label2 + " ( " + this.format + " )", + format: this.format + }, + { + layout: "Letter ANSI A Landscape", + layoutOptions: layoutOptions, + label: this.config.i18n.tools.print.layouts.label3 + " ( image )", + format: "PNG32" + }, + { + layout: "Letter ANSI A Portrait", + layoutOptions: layoutOptions, + label: this.config.i18n.tools.print.layouts.label4 + " ( image )", + format: "PNG32" + }]; + + print = new Print({ + map: this.map, + id: "printButton", + templates: templates, + url: this.config.helperServices.printTask.url + }, domConstruct.create("div")); + domConstruct.place(print.printDomNode, printDiv, "first"); + + print.startup(); + + deferred.resolve(true); + return; } - // Add a loading indicator to the Printing label - esriBundle.widgets.print.NLS_printing = esriBundle.widgets.print.NLS_printing + " "; - this.print = new Print(printOptions, domConstruct.create("div")); - domConstruct.place(this.print.printDomNode, printDiv, "first"); + esriRequest({ + url: this.config.helperServices.printTask.url, + content: { + "f": "json" + }, + "callbackParamName": "callback" + }).then(lang.hitch(this, function (response) { + var layoutTemplate, templateNames, mapOnlyIndex, templates; + + layoutTemplate = array.filter(response.parameters, function (param, idx) { + return param.name === "Layout_Template"; + }); - this.print.on("print-start", lang.hitch(this, function () { - var printBox = dom.byId("print_title"); - if (printBox.value) { - array.forEach(this.print.templates, lang.hitch(this, function (template) { - template.layoutOptions.titleText = printBox.value; - })); + if (layoutTemplate.length === 0) { + console.log("print service parameters name for templates must be \"Layout_Template\""); + return; } - })); + templateNames = layoutTemplate[0].choiceList; - this.print.startup(); + // remove the MAP_ONLY template then add it to the end of the list of templates + mapOnlyIndex = array.indexOf(templateNames, "MAP_ONLY"); + if (mapOnlyIndex > -1) { + var mapOnly = templateNames.splice(mapOnlyIndex, mapOnlyIndex + 1)[0]; + templateNames.push(mapOnly); + } - })); - deferred.resolve(true); - return; + // create a print template for each choice + templates = array.map(templateNames, lang.hitch(this, function (name) { + var plate = new PrintTemplate(); + plate.layout = plate.label = name; + plate.format = this.format; + plate.layoutOptions = layoutOptions; + return plate; + })); + print = new Print({ + map: this.map, + templates: templates, + url: this.config.helperServices.printTask.url + }, domConstruct.create("div")); + domConstruct.place(print.printDomNode, printDiv, "first"); + print.startup(); + deferred.resolve(true); + })); + })); })); + return deferred.promise; }, - _addShare: function (tool, toolbar, panelClass) { + + _addShare: function (tool, toolbar) { //Add share links for facebook, twitter and direct linking. //Add the measure widget to the toolbar. var deferred = new Deferred(); if (has("share")) { - var shareDiv = toolbar.createTool(tool, panelClass); - + var shareDiv = domConstruct.create('div', {class:'pageBody'},toolbar.createTool(tool));//); + var shareDialog = new ShareDialog({ bitlyLogin: this.config.bitlyLogin, bitlyKey: this.config.bitlyKey, @@ -747,34 +1474,17 @@ define([ title: this.config.title, summary: this.config.response.itemInfo.item.snippet || "" }, shareDiv); - domClass.add(shareDialog.domNode, "pageBody"); + // domClass.add(shareDialog.domNode, "margin"); shareDialog.startup(); + //domClass.add(dom.byId('_dialogNode'),'margin') deferred.resolve(true); } else { deferred.resolve(false); } - return deferred.promise; - }, - _getEditableLayers: function (layers) { - var layerInfos = []; - array.forEach(layers, lang.hitch(this, function (layer) { - - if (layer && layer.layerObject) { - var eLayer = layer.layerObject; - if (eLayer instanceof FeatureLayer && eLayer.isEditable()) { - layerInfos.push({ - "featureLayer": eLayer - }); - } - } - })); - return layerInfos; - }, - _getBasemapGroup: function () { //Get the id or owner and title for an organizations custom basemap group. @@ -793,27 +1503,9 @@ define([ }, _createMapUI: function () { - // Add map specific widgets like the Home and locate buttons. Also add the geocoder. - if (has("home")) { - domConstruct.create("div", { - id: "panelHome", - className: "icon-color tool", - innerHTML: "
      " - }, dom.byId("panelTools"), 0); - var home = new HomeButton({ - map: this.map - }, dom.byId("btnHome")); - - if (!has("touch")) { - //add a tooltip - domAttr.set("btnHome", "data-title", this.config.i18n.tooltips.home); - } else { - //remove no-touch class from body - domClass.remove(document.body, "no-touch"); - - } - - home.startup(); + if (!has("touch")) { + //remove no-touch class from body + domClass.remove(document.body, "no-touch"); } require(["application/has-config!scalebar?esri/dijit/Scalebar"], lang.hitch(this, function (Scalebar) { @@ -824,170 +1516,233 @@ define([ map: this.map, scalebarUnit: this.config.units }); - })); - - if (has("locate")) { - domConstruct.create("div", { - id: "panelLocate", - className: "icon-color tool", - innerHTML: "
      " - }, dom.byId("panelTools"), 1); - var geoLocate = new LocateButton({ - map: this.map, - useTracking: this.config.locate_track - }, dom.byId("btnLocate")); - if (!has("touch")) { - //add a tooltip - domAttr.set("btnLocate", "data-title", this.config.i18n.tooltips.locate); - } - - geoLocate.startup(); - - } - //Add the location search widget - require(["application/has-config!search?esri/dijit/Search", "application/has-config!search?esri/tasks/locator"], lang.hitch(this, function (Search, Locator) { + require(["application/has-config!search?esri/dijit/Search", + "application/has-config!search?esri/tasks/locator"], + lang.hitch(this, function (Search, Locator) { if (!Search && !Locator) { //add class so we know we don't have to hide title since search isn't visible domClass.add("panelTop", "no-search"); return; } - var searchOptions = { + var options = { map: this.map, - useMapExtent: this.config.searchExtent, - itemData: this.config.response.itemInfo.itemData + addLayersFromMap: false, + enableSearchingAll: true, + activeSourceIndex: "All" }; - if (this.config.searchConfig) { - searchOptions.applicationConfiguredSources = this.config.searchConfig.sources || []; - } else { - var configuredSearchLayers = (this.config.searchLayers instanceof Array) ? this.config.searchLayers : JSON.parse(this.config.searchLayers); - searchOptions.configuredSearchLayers = configuredSearchLayers; - searchOptions.geocoders = this.config.locationSearch ? this.config.helperServices.geocode : []; - } - var searchSources = new SearchSources(searchOptions); - var createdOptions = searchSources.createOptions(); - - if (this.config.searchConfig && this.config.searchConfig.activeSourceIndex) { - createdOptions.activeSourceIndex = this.config.searchConfig.activeSourceIndex; - } - - var search = new Search(createdOptions, domConstruct.create("div", { + var searchLayers = false; + var search = new Search(options, domConstruct.create("div", { id: "search" }, "mapDiv")); + var defaultSources = []; - search.on("select-result", lang.hitch(this, function () { - //if edit tool is enabled we'll have to delete/create - //so info window behaves correctly. - on.once(this.map.infoWindow, "hide", lang.hitch(this, function () { - search.clearGraphics(); - if (this.editor) { - this._destroyEditor(); - this._createEditor(); + //setup geocoders defined in common config + if (this.config.helperServices.geocode && this.config.locationSearch) { + var geocoders = lang.clone(this.config.helperServices.geocode); + array.forEach(geocoders, lang.hitch(this, function (geocoder) { + if (geocoder.url.indexOf(".arcgis.com/arcgis/rest/services/World/GeocodeServer") > -1) { + + geocoder.hasEsri = true; + geocoder.locator = new Locator(geocoder.url); + + geocoder.singleLineFieldName = "SingleLine"; + + geocoder.name = geocoder.name || "Esri World Geocoder"; + + if (this.config.searchExtent) { + geocoder.searchExtent = this.map.extent; + geocoder.localSearchOptions = { + minScale: 300000, + distance: 50000 + }; + } + defaultSources.push(geocoder); + } else if (esriLang.isDefined(geocoder.singleLineFieldName)) { + + //Add geocoders with a singleLineFieldName defined + geocoder.locator = new Locator(geocoder.url); + + defaultSources.push(geocoder); } })); + } + //add configured search layers to the search widget + var configuredSearchLayers = (this.config.searchLayers instanceof Array) ? this.config.searchLayers : JSON.parse(this.config.searchLayers); + + array.forEach(configuredSearchLayers, lang.hitch(this, function (layer) { + + var mapLayer = this.map.getLayer(layer.id); + if (mapLayer) { + var source = {}; + source.featureLayer = mapLayer; + + if (layer.fields && layer.fields.length && layer.fields.length > 0) { + source.searchFields = layer.fields; + source.displayField = layer.fields[0]; + source.outFields = ["*"]; + searchLayers = true; + defaultSources.push(source); + if (mapLayer.infoTemplate) { + source.infoTemplate = mapLayer.infoTemplate; + } + } + } })); + + //Add search layers defined on the web map item + if (this.config.response.itemInfo.itemData && + this.config.response.itemInfo.itemData.applicationProperties && + this.config.response.itemInfo.itemData.applicationProperties.viewing && + this.config.response.itemInfo.itemData.applicationProperties.viewing.search) { + + var searchOptions = this.config.response.itemInfo.itemData.applicationProperties.viewing.search; + + array.forEach(searchOptions.layers, lang.hitch(this, function (searchLayer) { + //we do this so we can get the title specified in the item + var operationalLayers = this.config.itemInfo.itemData.operationalLayers; + var layer = null; + array.some(operationalLayers, function (opLayer) { + if (opLayer.id === searchLayer.id) { + layer = opLayer; + return true; + } + }); + + if (layer && layer.hasOwnProperty("url")) { + var source = {}; + var url = layer.url; + var name = layer.title || layer.name; + + if (esriLang.isDefined(searchLayer.subLayer)) { + url = url + "/" + searchLayer.subLayer; + array.some(layer.layerObject.layerInfos, function (info) { + if (info.id == searchLayer.subLayer) { + name += " - " + layer.layerObject.layerInfos[searchLayer.subLayer].name; + return true; + } + }); + } + + source.featureLayer = new FeatureLayer(url); + + source.name = name; + + source.exactMatch = searchLayer.field.exactMatch; + source.displayField = searchLayer.field.name; + source.searchFields = [searchLayer.field.name]; + source.placeholder = searchOptions.hintText; + defaultSources.push(source); + searchLayers = true; + } + + })); + } + + defaultSources.forEach(function(source) { + if(!source.placeholder || source.placeholder === undefined || source.placeholder ==="") { + if(source.featureLayer && source.featureLayer.name) { + source.placeholder = i18n.searchEnterCriteria+" "+source.featureLayer.name; + } + else { + source.placeholder = i18n.searchPlaceholder; + } + } + }); + search.set("sources", defaultSources); + search.startup(); if (search && search.domNode) { domConstruct.place(search.domNode, "panelGeocoder"); - } - // update the search placeholder text color and dropdown - // to match the icon text - if(this.config.icons === "black"){ - query(".arcgisSearch .searchIcon").style("color", "#000"); - domClass.add(dom.byId("search_input"),"dark"); - } + + var esriIconDownArrowNode = dojo.query(".searchIcon.esri-icon-down-arrow")[0]; + if(esriIconDownArrowNode) + { + domClass.remove(esriIconDownArrowNode, "searchIcon esri-icon-down-arrow"); - })); + esriIconDownArrowNode.innerHTML = + 'Search in'; + } + var searchInput = dojo.query(".searchInput")[0]; + dojo.setAttr(searchInput, 'role', 'search'); - //Feature Search or find (if no search widget) - if ((this.config.find || (this.config.customUrlLayer.id !== null && this.config.customUrlLayer.fields.length > 0 && this.config.customUrlParam !== null))) { - require(["esri/dijit/Search"], lang.hitch(this, function (Search) { - var source = null, - value = null, - searchLayer = null; - - var urlObject = urlUtils.urlToObject(document.location.href); - urlObject.query = urlObject.query || {}; - urlObject.query = esriLang.stripTags(urlObject.query); - var customUrl = null; - for(var prop in urlObject.query){ - if(urlObject.query.hasOwnProperty(prop)){ - if(prop.toUpperCase() === this.config.customUrlParam.toUpperCase()){ - customUrl = prop; - } - } + var esriIconZoomNode = dojo.query(".searchIcon.esri-icon-search")[0]; + if(esriIconZoomNode) + { + domClass.remove(esriIconZoomNode, "searchIcon esri-icon-search"); + esriIconZoomNode.innerHTML = + 'Search'; } - //Support find or custom url param - if (this.config.find) { - value = decodeURIComponent(this.config.find); - } else if (customUrl){ - - value = urlObject.query[customUrl]; - searchLayer = this.map.getLayer(this.config.customUrlLayer.id); - if (searchLayer) { - - var searchFields = this.config.customUrlLayer.fields[0].fields; - source = { - exactMatch: true, - outFields: ["*"], - featureLayer: searchLayer, - displayField: searchFields[0], - searchFields: searchFields - }; - } + var esriIconCloseNode = dojo.query(".searchIcon.esri-icon-close.searchClose")[0]; + if(esriIconCloseNode) { + domClass.remove(esriIconCloseNode, "searchIcon esri-icon-close"); + esriIconCloseNode.innerHTML = + 'Clear search'; } - var urlSearch = new Search({ - map: this.map - }); + } - if (source) { - urlSearch.set("sources", [source]); + var emptySearchItems = query('.searchInputGroup > input[type="text"] '); + emptySearchItems.forEach(function(s) { + if(domAttr.get(s, "placeholder") ==='') + { + domAttr.set(s, "placeholder", i18n.searchPlaceholder); + domAttr.set(s, "title", i18n.searchPlaceholder); } - urlSearch.on("load", lang.hitch(this, function () { - urlSearch.search(value).then(lang.hitch(this, function () { - on.once(this.map.infoWindow, "hide", lang.hitch(this, function () { - //urlSearch.clear(); - urlSearch.destroy(); - if (this.editor) { - this._destroyEditor(); - this._createEditor(); + }); + + + var containerNode = dojo.query('#search [data-dojo-attach-point=containerNode]'); + if(containerNode && containerNode.length > 0) { + var containerNodeObserver = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + //console.log(mutation.target); + var box = dojo.query('#search .searchInputGroup')[0]; + if(dojo.hasClass(mutation.target, 'showNoResults')) + { + var nrText = ''; + var h = query('#search .noResultsText'); + if(h && h.length>0) { + nrText = query('#search .noResultsHeader')[0].innerHTML+': '+h[0].innerHTML; + } else { + h = query('#search .noValueText'); + if(h && h.length > 0) { + nrText = h[0].innerHTML; + } } - })); - })); - })); - urlSearch.startup(); + //console.log(nrText); + //console.log(mutation.target); + dojo.attr(box, 'aria-label', nrText); + dojo.attr(box, 'tabindex', 0); + box.focus(); + } + else { + dojo.removeAttr(box, 'tabindex'); + dojo.removeAttr(box, 'aria-label'); + } + }); + }); - })); - } + var observerCfg = { attributes: true, childList: false, characterData: false }; - //create the tools - this._createUI(); + containerNodeObserver.observe(containerNode[0], observerCfg); + } - }, - _setColor: function (color) { - //Convert the string color from the config file to rgba if supported. - var rgb = Color.fromHex(color).toRgb(); - var outputColor = null; - if (has("ie") < 9) { - outputColor = color; - } else { - //rgba supported so add - rgb.push(0.9); - outputColor = Color.fromArray(rgb); + })); - } - return outputColor; + //create the tools + this._createUI(); }, + _updateTheme: function () { - //Update the app to use the configured color scheme. + //Set the background color using the configured theme value query(".bg").style("backgroundColor", this.theme.toString()); query(".esriPopup .pointer").style("backgroundColor", this.theme.toString()); @@ -1008,20 +1763,76 @@ define([ query(".icon-color").style("color", "#000"); } + var styleSheetList = document.styleSheets; + var styleCss = null; + for(i=0; i0) { + styleCss = css; + break; + } + } + + if(styleCss) { + for(i=0; i= 0) { + rule.style.backgroundColor = this._rgbaColor(this.hoverColor); + } + // if(rule.selectorText.indexOf('.goThereHint') >= 0) { + // rule.style.backgroundColor = this._rgbaColor(this.hoverColor); + // } + //focus + if(rule.selectorText.indexOf(':focus') >= 0) { + // rule.style.outlineStyle = 'none'; + // rule.style.outlineColor = 'transparent'; + // rule.style.boxShadow = '0 0 0 2px '+this.focusColor+' inset'; + rule.style.outlineColor = this._rgbaColor(this.focusColor); + } + if(rule.selectorText.indexOf('.goThereHint') >= 0) { + rule.style.borderColor = this._rgbaColor(this.focusColor); + //rule.style.boxShadow = "3px 3px 10px "+this._rgbaColor(this.focusColor); + } + //active + if(rule.selectorText.indexOf('.activeMarker') >= 0 || + //rule.selectorText.indexOf('.goThereHint') >= 0 || + rule.selectorText.indexOf('dijitSplitterThumb') >= 0) { + rule.style.backgroundColor = this._rgbaColor(this.activeColor); + rule.style.outlineStyle = 'none'; + rule.style.outlineColor = 'transparent'; + rule.style.boxShadow = '0 0 15px 15px '+this.activeColor+' inset'; + } + } + } + } + //debugger; }, - _adjustPopupSize: function () { - //Set the popup size to be half the widget and .35% of the map height + _rgbaColor: function(color) { + return 'rgb('+color.r+', '+color.g+', '+color.b+')'; + }, + + _checkExtent: function () { + var pt = this.map.extent.getCenter(); + if (this.mapExt && !this.initExt.contains(pt)) { + this.map.setExtent(this.mapExt); + } else { + this.mapExt = this.map.extent; + } + }, + + _adjustPopupSize: function () { if (!this.map) { return; } - var box = domGeometry.getContentBox(this.map.container); var width = 270, - height = 300, - newWidth = Math.round(box.w * 0.50), - newHeight = Math.round(box.h * 0.35); + height = 300, + newWidth = Math.round(box.w * 0.50), + newHeight = Math.round(box.h * 0.35); if (newWidth < width) { width = newWidth; } @@ -1029,69 +1840,186 @@ define([ height = newHeight; } this.map.infoWindow.resize(width, height); - on(this.map.infoWindow, "show", lang.hitch(this, function () { - domClass.add(document.body, "noscroll"); - })); - on(this.map.infoWindow, "hide", lang.hitch(this, function () { - domClass.remove(document.body, "noscroll"); - })); - }, + _createWebMap: function (itemInfo) { window.config = this.config; - itemInfo = this._setExtent(itemInfo); - var mapOptions = {}; - mapOptions = this._setLevel(mapOptions); - mapOptions = this._setCenter(mapOptions); + var options = {}; + //specify center and zoom if provided as url params + if (this.config.level) { + options.zoom = this.config.level; + } + if (this.config.center) { + var points = this.config.center.split(","); + if (points && points.length === 2) { + options.center = [parseFloat(points[0]), parseFloat(points[1])]; + } + + } // create a map based on the input web map id arcgisUtils.createMap(itemInfo, "mapDiv", { - mapOptions: mapOptions, + mapOptions: options, editable: has("edit"), //is the app editable usePopupManager: true, - layerMixins: this.config.layerMixins, bingMapsKey: this.config.bingKey }).then(lang.hitch(this, function (response) { + var mapDiv = document.querySelector('#mapDiv'); + on(mapDiv, 'keydown', lang.hitch(this, function(evn){ + if(!document.querySelector(':focus') || document.querySelector(':focus').id !== "mapDiv") return; + switch(evn.keyCode) { + case 40 : //down + this.map._fixedPan(0, this.map.height * 0.0135); + evn.preventDefault(); + evn.stopPropagation(); + break; + case 38 : //up + this.map._fixedPan(0, this.map.height * -0.0135); + evn.preventDefault(); + evn.stopPropagation(); + break; + case 37 : //left + this.map._fixedPan(this.map.width * -0.0135, 0); + evn.preventDefault(); + evn.stopPropagation(); + break; + case 39 : //right + this.map._fixedPan(this.map.width * 0.0135, 0); + evn.preventDefault(); + evn.stopPropagation(); + break; + case 33 : //pgup + this.map._fixedPan(this.map.width * 0.0135, this.map.height * -0.0135); + evn.preventDefault(); + evn.stopPropagation(); + break; + case 34 : //pgdn + this.map._fixedPan(this.map.width * 0.0135, this.map.height * 0.0135); + evn.preventDefault(); + evn.stopPropagation(); + break; + case 35 : //end + this.map._fixedPan(this.map.width * -0.0135, this.map.height * 0.0135); + evn.preventDefault(); + evn.stopPropagation(); + break; + case 36 : //home + this.map._fixedPan(this.map.width * -0.0135, this.map.height * -0.0135); + evn.preventDefault(); + evn.stopPropagation(); + break; + } + })); + on(mapDiv, 'keypress', lang.hitch(this, function(evn){ + if(!document.querySelector(':focus') || document.querySelector(':focus').id !== "mapDiv") return; + evn.preventDefault(); + evn.stopPropagation(); + if((evn.keyCode === 43) && !evn.ctrlKey && !evn.altKey) // Shift-'+' + { + this.map.setLevel(this.map.getLevel() + 1); + } + if((evn.keyCode === 45) && !evn.ctrlKey && !evn.altKey) // Shift-'-' + { + this.map.setLevel(this.map.getLevel() - 1); + } + })); + this.map = response.map; - domClass.add(this.map.infoWindow.domNode, "light"); - this._updateTheme(); + var title; + if (this.config.title === null || this.config.title === "") { + title = response.itemInfo.item.title + " - " + this.config.i18n.wcagViewer; + } else { + title = this.config.title+': '+response.itemInfo.item.title + " - " + this.config.i18n.wcagViewer; + } + + if(this.config.altMapText !== undefined) { + var altMapText = esriLang.stripTags(this.config.altMapText); + domAttr.set(this.map.container, "aria-label", altMapText); + } + //Add a logo if provided if (this.config.logo) { - domConstruct.create("div", { + var altText = this.config.logoAltText; + if(!altText || altText === '') + altText = title; + var panelLogo = domConstruct.create("div", { id: "panelLogo", - innerHTML: "" - }, dom.byId("panelTitle"), "first"); - domClass.add("panelTop", "largerTitle"); + TabIndex:0, + innerHTML: "" + }, dom.byId("panelTitle"));//, "first"); + //domClass.add("panelTop", "largerTitle"); + dojo.place(panelLogo, dojo.byId('panelText'), 'before'); } //Set the application title - this.map = response.map; + //this.map = response.map; //Set the title - use the config value if provided. - var title; + //var title = (this.config.title === null) ? response.itemInfo.item.title : this.config.title; + + //if title is short make title area smaller + if (title && title.length && title.length === 0) { + domClass.add("panelTop", "smallerTitle"); + } else if (title && title.length && title.length <= 20 && !this.config.logo) { + domClass.add("panelTop", "smallerTitle"); + } + + document.title = title; if (this.config.title === null || this.config.title === "") { - title = response.itemInfo.item.title; - } else { - title = this.config.title; + dom.byId("panelText").innerHTML = response.itemInfo.item.title; + } + else { + dom.byId("panelText").innerHTML = this.config.title; } + domAttr.set(dom.byId("panelText"),"TabIndex",0); this.config.title = title; - document.title = esriLang.stripTags(title); - dom.byId("title").innerHTML = title; + if(!response.itemInfo.itemData) + response.itemInfo.itemData = {}; + if(!response.itemInfo.itemData.applicationProperties) + response.itemInfo.itemData.applicationProperties = {}; + if(!response.itemInfo.itemData.applicationProperties.viewing) + response.itemInfo.itemData.applicationProperties.viewing = {}; + if(!response.itemInfo.itemData.applicationProperties.viewing.search) + response.itemInfo.itemData.applicationProperties.viewing.search = { hintText: i18n.searchPlaceholder }; + this.config.response = response; + window.config = this.config; - //Set subtitle if provided - if (this.config.subtitle) { - dom.byId("subtitle").innerHTML = this.config.subtitle; - } else { - domClass.add("title", "nosubtitle"); + if (this.initExt !== null) { + this.map.setExtent(this.initExt); } - this.config.response = response; + window.initExt = this.initExt = this.map.extent; + + on.once(this.map, "extent-change", lang.hitch(this, function() { + navDeferred.then(lang.hitch(this, function (results) { + this._checkExtent(); + var homeButton = document.querySelector(".HomeButton input[type='image']"); + if(homeButton) + homeButton.click(); + })); + + // legend heades missing + var dojoxGridRowTables = query('.dojoxGridRowTable'); + if(dojoxGridRowTables) + { + dojoxGridRowTables.forEach(function(table) { + dojo.removeAttr(table,"role"); + }); + } + + })); + + on(this.map, "extent-change", function() { + var imgs = this.container.querySelectorAll("img"); + for(i=0; i 0 && this.config.itemInfo && this.config.itemInfo.item && this.config.itemInfo.item.extent) { - this.config.itemInfo.item.extent = [ - [ - parseFloat(this.config.application_extent[0][0]), parseFloat(this.config.application_extent[0][1]) - ], - [ - parseFloat(this.config.application_extent[1][0]), parseFloat(this.config.application_extent[1][1]) - ] - ]; - } - // Set the geometry helper service to be the app default. - if (this.config.helperServices && this.config.helperServices.geometry && this.config.helperServices.geometry.url) { - esriConfig.defaults.geometryService = new GeometryService(this.config.helperServices.geometry.url); - } - }, - _mixinAll: function() { - /* + // We have all we need, let's set up a few things + this._completeApplication(); + deferred.resolve(this.config); + }), deferred.reject); + }), deferred.reject); + })); + // return promise + return deferred.promise; + }, + _completeApplication: function () { + // ArcGIS.com allows you to set an application extent on the application item. Overwrite the + // existing web map extent with the application item extent when set. + if (this.config.appid && this.config.application_extent && this.config.application_extent.length > 0 && this.config.itemInfo && this.config.itemInfo.item && this.config.itemInfo.item.extent) { + this.config.itemInfo.item.extent = [ + [ + parseFloat(this.config.application_extent[0][0]), parseFloat(this.config.application_extent[0][1]) + ], + [ + parseFloat(this.config.application_extent[1][0]), parseFloat(this.config.application_extent[1][1]) + ] + ]; + } + // Set the geometry helper service to be the app default. + if (this.config.helperServices && this.config.helperServices.geometry && this.config.helperServices.geometry.url) { + esriConfig.defaults.geometryService = new GeometryService(this.config.helperServices.geometry.url); + } + }, + _mixinAll: function () { + /* mix in all the settings we got! {} <- i18n <- organization <- application <- group info <- group items <- webmap <- custom url params <- standard url params. */ - lang.mixin(this.config, this.i18nConfig, this.orgConfig, this.appConfig, this.groupInfoConfig, this.groupItemConfig, this.itemConfig, this.customUrlConfig, this.urlConfig); - }, - _createPortal: function() { - var deferred = new Deferred(); - if (this.templateConfig.queryForGroupInfo || this.templateConfig.queryForGroupItems) { - this.portal = new esriPortal.Portal(this.config.sharinghost); - this.portal.on("load", function() { - deferred.resolve(); - }); - } else { - deferred.resolve(); - } - return deferred.promise; - }, - _getUrlParamValues: function(items) { - // retrieves only the items specified from the URL object. - var urlObject = this.urlObject; - var obj = {}; - if (urlObject && urlObject.query && items && items.length) { - for (var i = 0; i < items.length; i++) { - var item = urlObject.query[items[i]]; - if (item) { - if (typeof item === "string") { - switch (item.toLowerCase()) { - case "true": - obj[items[i]] = true; - break; - case "false": - obj[items[i]] = false; - break; - default: - obj[items[i]] = item; - } - } else { - obj[items[i]] = item; - } - } - } - } - return obj; - }, - _createUrlParamsObject: function() { - var urlObject, - url; - // retrieve url parameters. Templates all use url parameters to determine which arcgis.com - // resource to work with. - // Map templates use the webmap param to define the webmap to display - // Group templates use the group param to provide the id of the group to display. - // appid is the id of the application based on the template. We use this - // id to retrieve application specific configuration information. The configuration - // information will contain the values the user selected on the template configuration - // panel. - url = document.location.href; - urlObject = urlUtils.urlToObject(url); - urlObject.query = urlObject.query || {}; - // remove any HTML tags from query item - urlObject.query = esriLang.stripTags(urlObject.query); - return urlObject; - }, - _initializeApplication: function() { - // If this app is hosted on an Esri environment. - if (this.templateConfig.esriEnvironment) { - var appLocation, instance; - // Check to see if the app is hosted or a portal. If the app is hosted or a portal set the - // sharing url and the proxy. Otherwise use the sharing url set it to arcgis.com. - // We know app is hosted (or portal) if it has /apps/ or /home/ in the url. - appLocation = location.pathname.indexOf("/apps/"); - if (appLocation === -1) { - appLocation = location.pathname.indexOf("/home/"); - } - // app is hosted and no sharing url is defined so let's figure it out. - if (appLocation !== -1) { - // hosted or portal - instance = location.pathname.substr(0, appLocation); //get the portal instance name - this.config.sharinghost = location.protocol + "//" + location.host + instance; - this.config.proxyurl = location.protocol + "//" + location.host + instance + "/sharing/proxy"; - } - } - arcgisUtils.arcgisUrl = this.config.sharinghost + "/sharing/rest/content/items"; - // Define the proxy url for the app - if (this.config.proxyurl) { - esriConfig.defaults.io.proxyUrl = this.config.proxyurl; - esriConfig.defaults.io.alwaysUseProxy = false; - } - }, - _checkSignIn: function() { - var deferred, signedIn, oAuthInfo; - deferred = new Deferred(); - //If there's an oauth appid specified register it - if (this.config.oauthappid) { - oAuthInfo = new ArcGISOAuthInfo({ - appId: this.config.oauthappid, - portalUrl: this.config.sharinghost, - popup: true - }); - IdentityManager.registerOAuthInfos([oAuthInfo]); + lang.mixin(this.config, this.i18nConfig, this.orgConfig, this.appConfig, this.groupInfoConfig, this.groupItemConfig, this.displayItemConfig, this.customUrlConfig, this.urlConfig); + }, + _createPortal: function () { + var deferred = new Deferred(); + if (this.templateConfig.queryForGroupInfo || this.templateConfig.queryForGroupItems) { + this.portal = new esriPortal.Portal(this.config.sharinghost); + this.portal.on("load", function () { + deferred.resolve(); + }); + } else { + deferred.resolve(); + } + return deferred.promise; + }, + _getUrlParamValues: function (items) { + // retreives only the items specified from the URL object. + var urlObject = this.urlObject, + obj = {}, + i; + if (urlObject && urlObject.query && items && items.length) { + for (i = 0; i < items.length; i++) { + if (urlObject.query[items[i]]) { + var item = urlObject.query[items[i]]; + obj[items[i]] = item === "true" || (item == "false" ? false : item); + } + } + } + return obj; + }, + _createUrlParamsObject: function () { + var urlObject, + url; + // retrieve url parameters. Templates all use url parameters to determine which arcgis.com + // resource to work with. + // Map templates use the webmap param to define the webmap to display + // Group templates use the group param to provide the id of the group to display. + // appid is the id of the application based on the template. We use this + // id to retrieve application specific configuration information. The configuration + // information will contain the values the user selected on the template configuration + // panel. + url = document.location.href; + urlObject = urlUtils.urlToObject(url); + urlObject.query = urlObject.query || {}; + // remove any HTML tags from query item + urlObject.query = esriLang.stripTags(urlObject.query); + return urlObject; + }, + _initializeApplication: function () { + // If this app is hosted on an Esri environment. + if (this.templateConfig.esriEnvironment) { + var appLocation, instance; + // Check to see if the app is hosted or a portal. If the app is hosted or a portal set the + // sharing url and the proxy. Otherwise use the sharing url set it to arcgis.com. + // We know app is hosted (or portal) if it has /apps/ or /home/ in the url. + appLocation = location.pathname.indexOf("/apps/"); + if (appLocation === -1) { + appLocation = location.pathname.indexOf("/home/"); + } + // app is hosted and no sharing url is defined so let's figure it out. + if (appLocation !== -1) { + // hosted or portal + instance = location.pathname.substr(0, appLocation); //get the portal instance name + this.config.sharinghost = location.protocol + "//" + location.host + instance; + this.config.proxyurl = location.protocol + "//" + location.host + instance + "/sharing/proxy"; + } + } + arcgisUtils.arcgisUrl = this.config.sharinghost + "/sharing/rest/content/items"; + // Define the proxy url for the app + if (this.config.proxyurl) { + esriConfig.defaults.io.proxyUrl = this.config.proxyurl; + esriConfig.defaults.io.alwaysUseProxy = false; + } + }, + _checkSignIn: function () { + var deferred, signedIn, oAuthInfo; + deferred = new Deferred(); + //If there's an oauth appid specified register it + if (this.config.oauthappid) { + oAuthInfo = new ArcGISOAuthInfo({ + appId: this.config.oauthappid, + portalUrl: this.config.sharinghost, + popup: true + }); + IdentityManager.registerOAuthInfos([oAuthInfo]); + } + // check sign-in status + signedIn = IdentityManager.checkSignInStatus(this.config.sharinghost + "/sharing"); + // resolve regardless of signed in or not. + signedIn.promise.always(function () { + deferred.resolve(); + }); + return deferred.promise; + }, + + _getLocalization: function () { + var deferred, dirNode, classes, rtlClasses; + deferred = new Deferred(); + if (this.templateConfig.queryForLocale) { + require(["dojo/i18n!application/nls/resources"], lang.hitch(this, function (appBundle) { + // Get the localization strings for the template and store in an i18n variable. Also determine if the + // application is in a right-to-left language like Arabic or Hebrew. + this.i18nConfig.i18n = appBundle || {}; + // Bi-directional language support added to support right-to-left languages like Arabic and Hebrew + // Note: The map must stay ltr + this.i18nConfig.i18n.direction = "ltr"; + array.some(["ar", "he"], lang.hitch(this, function (l) { + if (kernel.locale.indexOf(l) !== -1) { + this.i18nConfig.i18n.direction = "rtl"; + return true; } - // check sign-in status - signedIn = IdentityManager.checkSignInStatus(this.config.sharinghost + "/sharing"); - // resolve regardless of signed in or not. - signedIn.promise.always(function() { - deferred.resolve(); + return false; + })); + // add a dir attribute to the html tag. Then you can add special css classes for rtl languages + dirNode = document.getElementsByTagName("html")[0]; + classes = dirNode.className; + if (this.i18nConfig.i18n.direction === "rtl") { + // need to add support for dj_rtl. + // if the dir node is set when the app loads dojo will handle. + dirNode.setAttribute("dir", "rtl"); + rtlClasses = " esriRTL dj_rtl dijitRtl " + classes.replace(/ /g, "-rtl "); + dirNode.className = lang.trim(classes + rtlClasses); + } else { + dirNode.setAttribute("dir", "ltr"); + domClass.add(dirNode, "esriLTR"); + } + deferred.resolve(appBundle); + })); + } else { + deferred.resolve(); + } + return deferred.promise; + }, + queryGroupItems: function (options) { + var deferred = new Deferred(), + error, defaultParams, params; + // If we want to get the group info + if (this.templateConfig.queryForGroupItems) { + if (this.config.group) { + // group params + defaultParams = { + q: "group:\"${groupid}\" AND -type:\"Code Attachment\"", + sortField: "modified", + sortOrder: "desc", + num: 9, + start: 0, + f: "json" + }; + // mixin params + params = lang.mixin(defaultParams, this.templateConfig.groupParams, options); + // place group ID + if (params.q) { + params.q = string.substitute(params.q, { + groupid: this.config.group }); - return deferred.promise; - }, - _queryLocalization: function() { - var deferred, dirNode, classes, rtlClasses; - deferred = new Deferred(); - if (this.templateConfig.queryForLocale) { - require(["dojo/i18n!application/nls/resources"], lang.hitch(this, function(appBundle) { - var cfg = {}; - // Get the localization strings for the template and store in an i18n variable. Also determine if the - // application is in a right-to-left language like Arabic or Hebrew. - cfg.i18n = appBundle || {}; - // Bi-directional language support added to support right-to-left languages like Arabic and Hebrew - // Note: The map must stay ltr - cfg.i18n.direction = "ltr"; - array.some(["ar", "he"], lang.hitch(this, function(l) { - if (kernel.locale.indexOf(l) !== -1) { - cfg.i18n.direction = "rtl"; - return true; - } - return false; - })); - // add a dir attribute to the html tag. Then you can add special css classes for rtl languages - dirNode = document.getElementsByTagName("html")[0]; - classes = dirNode.className + " "; - if (cfg.i18n.direction === "rtl") { - // need to add support for dj_rtl. - // if the dir node is set when the app loads dojo will handle. - dirNode.setAttribute("dir", "rtl"); - rtlClasses = " esriRTL dj_rtl dijitRtl " + classes.replace(/ /g, "-rtl "); - dirNode.className = lang.trim(classes + rtlClasses); - } else { - dirNode.setAttribute("dir", "ltr"); - domClass.add(dirNode, "esriLTR"); - } - this.i18nConfig = cfg; - deferred.resolve(cfg); - })); - } else { - deferred.resolve(); - } - return deferred.promise; - }, - queryGroupItems: function(options) { - var deferred = new Deferred(), - error, defaultParams, params; - // If we want to get the group info - if (this.templateConfig.queryForGroupItems) { - if (this.config.group) { - // group params - defaultParams = { - q: "group:\"${groupid}\" AND -type:\"Code Attachment\"", - sortField: "modified", - sortOrder: "desc", - num: 9, - start: 0, - f: "json" - }; - // mixin params - params = lang.mixin(defaultParams, this.templateConfig.groupParams, options); - // place group ID - if (params.q) { - params.q = string.substitute(params.q, { - groupid: this.config.group - }); - } - // get items from the group - this.portal.queryItems(params).then(lang.hitch(this, function(response) { - var cfg = {}; - cfg.groupItems = response; - this.groupItemConfig = cfg; - deferred.resolve(cfg); - }), function(error) { - deferred.reject(error); - }); - } else { - error = new Error("Group undefined."); - deferred.reject(error); - } - } else { - // just resolve - deferred.resolve(); - } - return deferred.promise; - }, - queryGroupInfo: function() { - var deferred = new Deferred(), - error, params; - // If we want to get the group info - if (this.templateConfig.queryForGroupInfo) { - if (this.config.group) { - // group params - params = { - q: "id:\"" + this.config.group + "\"", - f: "json" - }; - this.portal.queryGroups(params).then(lang.hitch(this, function(response) { - var cfg = {}; - cfg.groupInfo = response; - this.groupInfoConfig = cfg; - deferred.resolve(cfg); - }), function(error) { - deferred.reject(error); - }); - } else { - error = new Error("Group undefined."); - deferred.reject(error); - } - } else { - // just resolve - deferred.resolve(); - } - return deferred.promise; - }, - queryItem: function() { - var deferred, cfg = {}; - // Get details about the specified web map. If the web map is not shared publicly users will - // be prompted to log-in by the Identity Manager. - deferred = new Deferred(); - // If we want to get the webmap - if (this.templateConfig.queryForWebmap) { - // Use local webmap instead of portal webmap - if (this.templateConfig.useLocalWebmap) { - // get webmap js file - require([this.templateConfig.localWebmapFile], lang.hitch(this, function(webmap) { - // return webmap json - cfg.itemInfo = webmap; - this.itemConfig = cfg; - deferred.resolve(cfg); - })); - } - // no webmap is set and we have organization's info - else if (!this.config.webmap && this.config.orgInfo) { - var defaultWebmap = { - "item": { - "title": "Default Webmap", - "type": "Web Map", - "description": "A webmap with the default basemap and extent.", - "snippet": "A webmap with the default basemap and extent.", - "extent": this.config.orgInfo.defaultExtent - }, - "itemData": { - "operationalLayers": [], - "baseMap": this.config.orgInfo.defaultBasemap - } - }; - cfg.itemInfo = defaultWebmap; - this.itemConfig = cfg; - deferred.resolve(cfg); - } - // use webmap from id - else { - arcgisUtils.getItem(this.config.webmap).then(lang.hitch(this, function(itemInfo) { - // Set the itemInfo config option. This can be used when calling createMap instead of the webmap id - cfg.itemInfo = itemInfo; - this.itemConfig = cfg; - deferred.resolve(cfg); - }), function(error) { - if (!error) { - error = new Error("Error retrieving display item."); - } - deferred.reject(error); - }); - } - } else { - // we're done. we dont need to get the webmap - deferred.resolve(); - } - return deferred.promise; - }, - queryApplication: function() { - // Get the application configuration details using the application id. When the response contains - // itemData.values then we know the app contains configuration information. We'll use these values - // to overwrite the application defaults. - var deferred = new Deferred(); - if (this.config.appid) { - arcgisUtils.getItem(this.config.appid).then(lang.hitch(this, function(response) { - var cfg = {}; - if (response.item && response.itemData && response.itemData.values) { - // get app config values - we'll merge them with config later. - cfg = response.itemData.values; - // save response - cfg.appResponse = response; - } - // get the extent for the application item. This can be used to override the default web map extent - if (response.item && response.item.extent) { - cfg.application_extent = response.item.extent; - } - // get any app proxies defined on the application item - if (response.item && response.item.appProxies) { - var layerMixins = array.map(response.item.appProxies, function(p) { - return { - "url": p.sourceUrl, - "mixin": { - "url": p.proxyUrl - } - }; - }); - cfg.layerMixins = layerMixins; - } - this.appConfig = cfg; - deferred.resolve(cfg); - }), function(error) { - if (!error) { - error = new Error("Error retrieving application configuration."); - } - deferred.reject(error); - }); - } else { - deferred.resolve(); - } - return deferred.promise; - }, - queryOrganization: function() { - var deferred = new Deferred(); - if (this.templateConfig.queryForOrg) { - // Query the ArcGIS.com organization. This is defined by the sharinghost that is specified. For example if you - // are a member of an org you'll want to set the sharinghost to be http://.arcgis.com. We query - // the organization by making a self request to the org url which returns details specific to that organization. - // Examples of the type of information returned are custom roles, units settings, helper services and more. - // If this fails, the application will continue to function - esriRequest({ - url: this.config.sharinghost + "/sharing/rest/portals/self", - content: { - "f": "json" - }, - callbackParamName: "callback" - }).then(lang.hitch(this, function(response) { - var cfg = {}; - // save organization information - cfg.orgInfo = response; - // get units defined by the org or the org user - cfg.units = "metric"; - if (response.user && response.user.units) { //user defined units - cfg.units = response.user.units; - } else if (response.units) { //org level units - cfg.units = response.units; - } else if ((response.user && response.user.region && response.user.region === "US") || (response.user && !response.user.region && response.region === "US") || (response.user && !response.user.region && !response.region) || (!response.user && response.ipCntryCode === "US") || (!response.user && !response.ipCntryCode && kernel.locale === "en-us")) { - // use feet/miles only for the US and if nothing is set for a user - cfg.units = "english"; - } - //Get the basemap group for the organization - var q = this._parseQuery(response.basemapGalleryGroupQuery); - this.orgConfig.basemapgroup = { - id: null, - title: null, - owner: null - }; - if (q.id) { - this.orgConfig.basemapgroup.id = q.id; - } else if (q.title && q.owner) { - this.orgConfig.basemapgroup.title = q.title; - this.orgConfig.basemapgroup.owner = q.owner; - } - // Get the helper services (routing, print, locator etc) - cfg.helperServices = response.helperServices; - // are any custom roles defined in the organization? - if (response.user && esriLang.isDefined(response.user.roleId)) { - if (response.user.privileges) { - cfg.userPrivileges = response.user.privileges; - } - } - this.orgConfig = cfg; - deferred.resolve(cfg); - }), function(error) { - if (!error) { - error = new Error("Error retrieving organization information."); - } - deferred.reject(error); - }); - } else { - deferred.resolve(); + } + // get items from the group + this.portal.queryItems(params).then(lang.hitch(this, function (response) { + this.groupItemConfig.groupItems = response; + deferred.resolve(response); + }), function (error) { + deferred.reject(error); + }); + } else { + error = new Error("Group undefined."); + deferred.reject(error); + } + } else { + // just resolve + deferred.resolve(); + } + return deferred.promise; + }, + _queryGroupInfo: function () { + var deferred = new Deferred(), + error, params; + // If we want to get the group info + if (this.templateConfig.queryForGroupInfo) { + if (this.config.group) { + // group params + params = { + q: "id:\"" + this.config.group + "\"", + f: "json" + }; + this.portal.queryGroups(params).then(lang.hitch(this, function (response) { + this.groupInfoConfig.groupInfo = response; + deferred.resolve(response); + }), function (error) { + deferred.reject(error); + }); + } else { + error = new Error("Group undefined."); + deferred.reject(error); + } + } else { + // just resolve + deferred.resolve(); + } + return deferred.promise; + }, + _queryDisplayItem: function () { + var deferred; + // Get details about the specified web map. If the web map is not shared publicly users will + // be prompted to log-in by the Identity Manager. + deferred = new Deferred(); + // If we want to get the webmap + if (this.templateConfig.queryForWebmap) { + // if webmap does not exist + if (!this.config.webmap) { + // use default webmap for boilerplate + this.config.webmap = "24e01ef45d40423f95300ad2abc5038a"; + } + arcgisUtils.getItem(this.config.webmap).then(lang.hitch(this, function (itemInfo) { + // Set the itemInfo config option. This can be used when calling createMap instead of the webmap id + this.displayItemConfig.itemInfo = itemInfo; + deferred.resolve(itemInfo); + }), function (error) { + if (!error) { + error = new Error("Error retrieving display item."); + } + deferred.reject(error); + }); + } else { + // we're done. we dont need to get the webmap + deferred.resolve(); + } + return deferred.promise; + }, + _queryApplicationConfiguration: function () { + // Get the application configuration details using the application id. When the response contains + // itemData.values then we know the app contains configuration information. We'll use these values + // to overwrite the application defaults. + var deferred = new Deferred(); + if (this.config.appid) { + arcgisUtils.getItem(this.config.appid).then(lang.hitch(this, function (response) { + if (response.item && response.itemData && response.itemData.values) { + // get app config values - we'll merge them with config later. + this.appConfig = response.itemData.values; + // save response + this.appResponse = response; + } + // get the extent for the application item. This can be used to override the default web map extent + if (response.item && response.item.extent) { + this.appConfig.application_extent = response.item.extent; + } + deferred.resolve(response); + }), function (error) { + if (!error) { + error = new Error("Error retrieving application configuration."); + } + deferred.reject(error); + }); + } else { + deferred.resolve(); + } + return deferred.promise; + }, + _queryOrganizationInformation: function () { + var deferred = new Deferred(); + if (this.templateConfig.queryForOrg) { + // Query the ArcGIS.com organization. This is defined by the sharinghost that is specified. For example if you + // are a member of an org you'll want to set the sharinghost to be http://.arcgis.com. We query + // the organization by making a self request to the org url which returns details specific to that organization. + // Examples of the type of information returned are custom roles, units settings, helper services and more. + // If this fails, the application will continue to function + esriRequest({ + url: this.config.sharinghost + "/sharing/rest/portals/self", + content: { + "f": "json" + }, + callbackParamName: "callback" + }).then(lang.hitch(this, function (response) { + // save organization information + this.orgConfig.orgInfo = response; + // get units defined by the org or the org user + this.orgConfig.units = "metric"; + if (response.user && response.user.units) { //user defined units + this.orgConfig.units = response.user.units; + } else if (response.units) { //org level units + this.orgConfig.units = response.units; + } else if ((response.user && response.user.region && response.user.region === "US") || (response.user && !response.user.region && response.region === "US") || (response.user && !response.user.region && !response.region) || (!response.user && response.ipCntryCode === "US") || (!response.user && !response.ipCntryCode && kernel.locale === "en-us")) { + // use feet/miles only for the US and if nothing is set for a user + this.orgConfig.units = "english"; + } + + //Get the basemap group for the organization + var q = this._parseQuery(response.basemapGalleryGroupQuery); + this.orgConfig.basemapgroup = { + id: null, + title: null, + owner: null + }; + if (q.id) { + this.orgConfig.basemapgroup.id = q.id; + } else if (q.title && q.owner) { + this.orgConfig.basemapgroup.title = q.title; + this.orgConfig.basemapgroup.owner = q.owner; + } + + + // Get the helper services (routing, print, locator etc) + this.orgConfig.helperServices = response.helperServices; + // are any custom roles defined in the organization? + if (response.user && esriLang.isDefined(response.user.roleId)) { + if (response.user.privileges) { + this.orgConfig.userPrivileges = response.user.privileges; } - return deferred.promise; - }, - _parseQuery: function (queryString) { + } + deferred.resolve(response); + }), function (error) { + if (!error) { + error = new Error("Error retrieving organization information."); + } + deferred.reject(error); + }); + } else { + deferred.resolve(); + } + return deferred.promise; + }, + _parseQuery: function (queryString) { - var regex = /(AND|OR)?\W*([a-z]+):/ig, - fields = {}, - fieldName, fieldIndex, result = regex.exec(queryString); - while (result) { - fieldName = result && result[2]; - fieldIndex = result ? (result.index + result[0].length) : -1; + var regex = /(AND|OR)?\W*([a-z]+):/ig, + fields = {}, + fieldName, fieldIndex, result = regex.exec(queryString); + while (result) { + fieldName = result && result[2]; + fieldIndex = result ? (result.index + result[0].length) : -1; - result = regex.exec(queryString); + result = regex.exec(queryString); - fields[fieldName] = queryString.substring(fieldIndex, result ? result.index : queryString.length).replace(/^\s+|\s+$/g, "").replace(/\"/g, ""); //remove extra quotes in title - } - return fields; + fields[fieldName] = queryString.substring(fieldIndex, result ? result.index : queryString.length).replace(/^\s+|\s+$/g, "").replace(/\"/g, ""); //remove extra quotes in title } - }); + return fields; + } + }); }); diff --git a/js/toolbar.js b/js/toolbar.js index b129a322..c5d92d47 100644 --- a/js/toolbar.js +++ b/js/toolbar.js @@ -1,16 +1,19 @@ -define(["dojo/Evented", "dojo/_base/declare", "dojo/_base/window", "dojo/_base/fx", "dojo/_base/html", "dojo/_base/lang", "dojo/has", "dojo/dom", "dojo/dom-class", "dojo/dom-style", "dojo/dom-attr", "dojo/dom-construct", "dojo/dom-geometry", "dojo/on", "dojo/mouse", "dojo/query", "dojo/Deferred"], function ( -Evented, declare, win, fx, html, lang, has, dom, domClass, domStyle, domAttr, domConstruct, domGeometry, on, mouse, query, Deferred) { +define([ + "dojo/Evented", "dojo/_base/declare", "dojo/_base/window", "dojo/_base/fx", + "dojo/_base/html", "dojo/_base/lang", "dojo/has", "dojo/dom", + "dojo/dom-class", "dojo/dom-style", "dojo/dom-attr", "dojo/dom-construct", "dojo/dom-geometry", + "dojo/on", "dojo/mouse", "dojo/query", "dojo/Deferred"], function ( +Evented, declare, win, fx, html, lang, has, dom, +domClass, domStyle, domAttr, domConstruct, domGeometry, +on, mouse, query, Deferred) { return declare([Evented], { - map: null, tools: [], toollist: [], - snap: true, - curTool: 0, + curTool: -1, scrollTimer: null, config: {}, pTools: null, - pMenu: null, pPages: null, constructor: function (config) { @@ -19,12 +22,14 @@ Evented, declare, win, fx, html, lang, has, dom, domClass, domStyle, domAttr, do startup: function () { var deferred = this._init(); - deferred.then(lang.hitch(this, function (config) { - // optional ready event to listen to - this.emit("ready", config); - }), lang.hitch(this, function (error) { - // optional error event to listen to - this.emit("error", error); + deferred.then( + lang.hitch(this, function (config) { + // optional ready event to listen to + this.emit("ready", config); + }), + lang.hitch(this, function (error) { + // optional error event to listen to + this.emit("error", error); })); return deferred; }, @@ -37,14 +42,13 @@ Evented, declare, win, fx, html, lang, has, dom, domClass, domStyle, domAttr, do on(window, "scroll", lang.hitch(this, this._windowScrolled)); on(window, "resize", lang.hitch(this, this._windowScrolled)); this.pTools = dom.byId("panelTools"); - this.pMenu = dom.byId("panelMenu"); - on(this.pMenu, "click", lang.hitch(this, this._menuClick)); + this.pPages = dom.byId("panelPages"); //Prevent body scroll when scrolling to the end of the panel content on(this.pPages, mouse.enter, lang.hitch(this, function () { if (this._hasScrollbar()) { - var p = dom.byId("panelContent"); + var p = dom.byId("panelPages"); if (p) { domClass.add(p, "modal-scrollbar"); } @@ -54,7 +58,7 @@ Evented, declare, win, fx, html, lang, has, dom, domClass, domStyle, domAttr, do })); on(this.pPages, mouse.leave, lang.hitch(this, function () { if (this._hasScrollbar === false) { - var p = dom.byId("panelContent"); + var p = dom.byId("panelPages"); if (p) { domClass.remove(p, "modal-scrollbar"); } @@ -63,22 +67,14 @@ Evented, declare, win, fx, html, lang, has, dom, domClass, domStyle, domAttr, do })); - domConstruct.empty(this.pPages); - // add blank page - domConstruct.create("div", { - className: "pageblank", - id: "page_blank" - }, this.pPages); - deferred.resolve(); return deferred.promise; }, + _hasScrollbar: function () { // The Modern solution - if (typeof window.innerWidth === "number") { - return window.innerWidth > document.documentElement.clientWidth; - } + if (typeof window.innerWidth === 'number') return window.innerWidth > document.documentElement.clientWidth; // rootElem for quirksmode var rootElem = document.documentElement || document.body; @@ -86,288 +82,175 @@ Evented, declare, win, fx, html, lang, has, dom, domClass, domStyle, domAttr, do // Check overflow style property on body for fauxscrollbars var overflowStyle; - if (typeof rootElem.currentStyle !== "undefined") { - overflowStyle = rootElem.currentStyle.overflow; - } + if (typeof rootElem.currentStyle !== 'undefined') overflowStyle = rootElem.currentStyle.overflow; - overflowStyle = overflowStyle || window.getComputedStyle(rootElem, "").overflow; + overflowStyle = overflowStyle || window.getComputedStyle(rootElem, '').overflow; // Also need to check the Y axis overflow var overflowYStyle; - if (typeof rootElem.currentStyle !== "undefined") { - overflowYStyle = rootElem.currentStyle.overflowY; - } + if (typeof rootElem.currentStyle !== 'undefined') overflowYStyle = rootElem.currentStyle.overflowY; - overflowYStyle = overflowYStyle || window.getComputedStyle(rootElem, "").overflowY; + overflowYStyle = overflowYStyle || window.getComputedStyle(rootElem, '').overflowY; var contentOverflows = rootElem.scrollHeight > rootElem.clientHeight; var overflowShown = /^(visible|auto)$/.test(overflowStyle) || /^(visible|auto)$/.test(overflowYStyle); - var alwaysShowScroll = overflowStyle === "scroll" || overflowYStyle === "scroll"; + var alwaysShowScroll = overflowStyle === 'scroll' || overflowYStyle === 'scroll'; return (contentOverflows && overflowShown) || (alwaysShowScroll); }, + //Create a tool and return the div where you can place content - createTool: function (tool, panelClass) { + createTool: function (tool, panelClass, loaderImg, badgeEvName) { var name = tool.name; // add tool - var pTool = domConstruct.create("div", { + var refNode = this.pTools; + var tip = this.config.i18n.tooltips[name] || name; + var panelTool = domConstruct.create("div", { className: "panelTool", - role: "button", - tabindex: "0", - id: "panelTool_" + name - }, this.pTools); - - if (!has("touch")) { - //add a tooltip - var tip = this.config.i18n.tooltips[name] || name; - domAttr.set(pTool, "data-title", tip); + tabindex: -1, + id: "toolButton_" + name, + autofocus: true, + // "aria-label": tip, + }, refNode); + var pTool = domConstruct.create("input", { + type: "image", + src: "images/icons_" + this.config.icons + "/" + name + ".png", + title: tip, + alt: tip + }, panelTool); + + if (!has("touch")) + { domAttr.set(pTool, "title", tip); - on(pTool, mouse.enter, function () { - domAttr.set(pTool, "title", ""); - }); - on(pTool, mouse.leave, function () { - domAttr.set(pTool, "title", tip); - }); + } + + if(badgeEvName && badgeEvName !== '') { + var setIndicator = domConstruct.create("img", { + src:"images/"+badgeEvName+".png", + class:"setIndicator", + style:"display:none;", + tabindex:0, + id: 'badge_'+badgeEvName, + alt:"" + }); + domConstruct.place(setIndicator, panelTool); } - domConstruct.create("img", { - className: "tool", - src: "images/icons_" + this.config.icons + "/" + name + ".png" - }, pTool); on(pTool, "click", lang.hitch(this, this._toolClick, name)); this.tools.push(name); - - // add page var page = domConstruct.create("div", { - className: "page", - id: "page_" + name + className: "page hideAttr", + id: "page_" + name, + // tabindex: 0 }, this.pPages); var pageContent = domConstruct.create("div", { - className: "pageContent rounded shadow", - id: "pageContent_" + name + className: "pageContent", + id: "pageContent_" + name, + role: "dialog", + "aria-labelledby": "pagetitle_" + name, }, page); - domClass.add(pageContent, panelClass); - var pageHeader = domConstruct.create("div", { id: "pageHeader_" + name, - className: "pageHeader bg roundedTop" - }, pageContent); - + className: "pageHeader fr bg", + //tabindex: 0, + }, + pageContent); - domConstruct.create("div", { + domConstruct.create("h2", { className: "pageTitle fc", - innerHTML: this.config.i18n.tooltips[name] || name + innerHTML: this.config.i18n.tooltips[name] || name, + //style: 'display:inline', + id: "pagetitle_" + name }, pageHeader); - domConstruct.create("div", { - className: "pageHeaderImg", - innerHTML: "" - }, pageHeader); + if(loaderImg && loaderImg !=="") { + domConstruct.create('img',{ + src: 'images/reload1.gif', + alt: 'Reloading', + title: 'Reloading' + }, domConstruct.create("div", { + id: "loading_" + name, + class: 'hideLoading small-loading' + }, pageHeader)); + } + // domConstruct.create("div", { + // className: "pageHeaderImg", + // innerHTML: "" + // }, pageHeader); var pageBody = domConstruct.create("div", { className: "pageBody", - id: "pageBody_" + name - }, pageContent); - return pageBody; + tabindex: 0, + id: "pageBody_" + name, + }, + pageContent); + domClass.add(pageBody, panelClass); + + on(this, "updateTool_" + name, lang.hitch(this, function(name) { + pageBody.focus(); + })); + return pageBody; }, - updatePageNavigation: function () { - //Adds the up/down and close tools to the page header. - for (var i = 0; i < this.tools.length; i++) { - var name = this.tools[i]; - var pageClose = domConstruct.create("div", { - className: "pageNav pageClose", - tabindex: "0", - title: this.config.i18n.nav.close - }, "pageHeader_" + name); - if(this.config.icons === "black"){ - domClass.add(pageClose, "icons-dark"); - } - on(pageClose, "click, keypress", lang.hitch(this, this.closePage)); - - var pageUp = domConstruct.create("div", { - className: "pageNav pageUp", - tabindex: "0", - title: this.config.i18n.nav.previous - }, "pageHeader_" + name); - if(this.config.icons === "black"){ - domClass.add(pageUp, "icons-dark"); - } - on(pageUp, "click, keypress", lang.hitch(this, this._showPreviousPage, name)); - - if (name != this.tools[this.tools.length - 1]) { - var pageDown = domConstruct.create("div", { - className: "pageNav pageDown", - tabindex: "0", - title: this.config.i18n.nav.next - }, "pageHeader_" + name); - if(this.config.icons === "black"){ - domClass.add(pageDown, "icons-dark"); - } - on(pageDown, "click, keypress", lang.hitch(this, this._showNextPage, name)); - } - + _toolClick: function (name) { + + var defaultBtns = dojo.query(".panelToolDefault"); + var defaultBtn; + if(defaultBtns !== undefined && defaultBtns.length > 0) { + defaultBtn = defaultBtns[0].id.split("_")[1]; } - - }, - setContent: function (name, content) { - domConstruct.place(content, "pageBody_" + name, "last"); - }, - - activateTool: function (name) { - //Instead of scrolling to the tool just go there. - var num = this._getPageNum(name) + 1; - var box = html.getContentBox(dom.byId("panelContent")); - - var endPos = num * box.h; - - document.body.scrollTop = endPos; - document.documentElement.scrollTop = endPos; - - this._updateMap(); - - - this.curTool = num; - this._updateTool(num); - }, - - _toolClick: function (name) { - this._showPage(name); - }, - - _getPageNum: function (name) { - for (var i = 0; i < this.tools.length; i++) { - if (this.tools[i] == name) { - return i; + this._updateMap(); // ! out of place + var active = false; + var page = dom.byId("page_"+name); + var hidden = page.classList.contains("hideAttr"); + var pages = query(".page"); + pages.forEach(function(p){ + if(hidden && p === page) { + active = true; } - } - return 0; - }, - _showPage: function (name) { - var num = this._getPageNum(name) + 1; - - if (num != this.curTool) { - this._scrollToPage(num); - } else { - this._scrollToPage(0); - } - }, - _showPreviousPage: function (name) { - var num = this._getPageNum(name); - this._scrollToPage(num); - - }, - - _showNextPage: function (name) { - var num = this._getPageNum(name) + 2; - this._scrollToPage(num); - }, - - closePage: function (e) { - this._scrollToPage(-1); - }, - _scrollToPage: function (num) { - - var box = html.getContentBox(dom.byId("panelContent")); - - var startPos = this.curTool * box.h; - var endPos = num * box.h; - var diff = Math.abs(num - this.curTool); - this.snap = false; - if (diff == 1) { - this._animateScroll(startPos, endPos); - } else { - document.body.scrollTop = endPos; - document.documentElement.scrollTop = endPos; - this._updateMap(); - this.snap = true; - } - this.curTool = num; - this._updateTool(num); - - }, - - // window scrolled - _windowScrolled: function (evt) { - - if (this.scrollTimer) { - clearTimeout(this.scrollTimer); - } - if (this.snap === true) { - this.scrollTimer = setTimeout(lang.hitch(this, this._snapScroll), 300); - } - }, - _snapScroll: function () { - - var startPos = domGeometry.docScroll().y; - var box = html.getContentBox(dom.byId("panelContent")); - var numActual = startPos / box.h; - var num = Math.round(numActual); + }); - if (numActual > this.curTool) { - if (numActual - this.curTool > 0.2) { - num = Math.ceil(startPos / box.h); + pages.forEach(function(p){ + if(hidden && p === page) { + domClass.replace(p, "showAttr","hideAttr"); + } else { + domClass.replace(p,"hideAttr","showAttr"); } - if (num > this.tools.length - 1) { - num = this.tools.length - 1; - } - } else if (numActual < this.curPage) { - if (this.curTool - numActual > 0.2) { - num = Math.floor(startPos / box.h); - } - if (num < 0) { - num = 0; + }); + var tool = dom.byId("toolButton_"+name); + var tools = query(".panelTool"); + this.emit("updateTool", name); + tools.forEach(lang.hitch(this, function(t){ + if(active && t === tool) { + domClass.add(t, "panelToolActive"); + this.emit("updateTool_"+name); + } else { + domClass.remove(t,"panelToolActive"); } - } - var endPos = num * box.h; + })); - this.curTool = num; - this._updateTool(num); - - if (num != numActual) { - this._animateScroll(startPos, endPos); + if(!active && defaultBtns !== undefined) { + this._activateDefautTool(); } - }, - _animateScroll: function (start, end) { - //var me = this; - var anim = new fx.Animation({ - duration: 500, - curve: [start, end] - }); - on(anim, "Animate", function (v) { - document.body.scrollTop = v; - document.documentElement.scrollTop = v; - }); - - on(anim, "End", lang.hitch(this, function () { - setTimeout(lang.hitch(this, this._resetSnap), 500); - this._updateMap(); + _atachEnterKey: function(onButton, clickButton) { + on(onButton, 'keydown', lang.hitch(clickButton, function(event){ + if(event.keyCode=='13') + this.click(); })); - - anim.play(); }, - // highlight the active tool on the toolbar - _updateTool: function (num) { - query(".panelTool").removeClass("panelToolActive"); - var name = this.tools[num - 1]; - if (name) { - domClass.add("panelTool_" + name, "panelToolActive"); - } - this.emit("updateTool", name); - }, _updateMap: function () { if (this.map) { this.map.resize(); @@ -375,21 +258,32 @@ Evented, declare, win, fx, html, lang, has, dom, domClass, domStyle, domAttr, do } }, - _resetSnap: function () { - this.snap = true; - - }, - - // menu click - _menuClick: function () { - if (query("#panelTools").style("display") == "block") { - query("#panelTools").style("display", "none"); - this.closePage(); - - } else { - query("#panelTools").style("display", "block"); + _activateDefautTool: function() { + var defaultBtns = dojo.query(".panelToolDefault"); + var defaultBtn; + if(defaultBtns !== undefined && defaultBtns.length>0) { + defaultBtn = defaultBtns[0].id.split("_")[1]; } - this._updateMap(); - } + if(defaultBtn !== undefined) { + this._toolClick(defaultBtn); + } + else if (this.config.activeTool !== "" && has(this.config.activeTool)) { + toolbar.activateTool(this.config.activeTool); + } + // else { + // toolbar._closePage(); + // } + }, + + // // menu click + // _menuClick: function () { + // if (query("#panelTools").style("display") == "block") { + // query("#panelTools").style("display", "none"); + // this._closePage(); + // } else { + // query("#panelTools").style("display", "block"); + // } + // this._updateMap(); + // } }); -}); \ No newline at end of file +}); diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 00000000..b4432769 --- /dev/null +++ b/js/utils.js @@ -0,0 +1,139 @@ +"use strict" + +Number.prototype.padLeft = function (n,str){ + return new Array(n-String(this).length+1).join(str||'0')+this; +}; + +Date.prototype.getSQLDate = function() { + if(this.toDateString() === "Invalid Date") { + return null; + } + return this.getFullYear().padLeft(4)+(this.getMonth()+1).padLeft(2)+this.getDate().padLeft(2); +}; + + +// Add ECMA262-5 method binding if not supported natively +// +if (!('bind' in Function.prototype)) { + Function.prototype.bind= function(owner) { + var that= this; + if (arguments.length<=1) { + return function() { + return that.apply(owner, arguments); + }; + } else { + var args= Array.prototype.slice.call(arguments, 1); + return function() { + return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments))); + }; + } + }; +} + +// Add ECMA262-5 string trim if not supported natively +// +if (!('trim' in String.prototype)) { + String.prototype.trim= function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }; +} + +// if (!Array.prototype.remove) { +// Array.prototype.remove = function() { +// var what, a = arguments, L = a.length, ax; +// while (L && this.length) { +// what = a[--L]; +// while ((ax = this.indexOf(what)) !== -1) { +// this.splice(ax, 1); +// } +// } +// return this; +// }; +// } +// Add ECMA262-5 Array methods if not supported natively +// +if (!Array.prototype.find) { + Array.prototype.find = function(predicate) { + if (this === null) { + throw new TypeError('Array.prototype.find called on null or undefined'); + } + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + var list = Object(this); + var length = list.length >>> 0; + var thisArg = arguments[1]; + var value; + + for (var i = 0; i < length; i++) { + value = list[i]; + if (predicate.call(thisArg, value, i, list)) { + return value; + } + } + return undefined; + }; +} +if (!('indexOf' in Array.prototype)) { + Array.prototype.indexOf= function(find, i /*opt*/) { + if (i===undefined) i= 0; + if (i<0) i+= this.length; + if (i<0) i= 0; + for (var n= this.length; ithis.length-1) i= this.length-1; + for (i++; i-->0;) /* i++ because from-argument is sadly inclusive */ + if (i in this && this[i]===find) + return i; + return -1; + }; +} +if (!('forEach' in Array.prototype)) { + Array.prototype.forEach= function(action, that /*opt*/) { + for (var i= 0, n= this.length; iApp Settings", + "category":"Configure template", "fields":[ - { - "type": "appproxies" + { "type":"webmap"}, + + { "type":"string", + "stringFieldOption": "richtext", + "label": "Alternate Map Text", + "tooltip": "Define text that will be read by screen reader", + "fieldName":"altMapText" }, - { - "type":"webmap" + { "type": "paragraph", + "value": "The primary purpose of alternative map text is to be read by screen readers to allow the content and function of the map to be accessible to users with visual or certain cognitive disabilities." }, - { + + { "label":"Description", + "type":"string", + "fieldName":"description", + "placeHolder":"Defaults to web map description.", + "stringFieldOption":"richtext"}, + { "type": "paragraph", + "value": "When present, this markup will replace the map description in the Details panel." + }, + { "label":"Title:", "placeHolder":"Defaults to web map title", - "label":"Title:", "fieldName":"title", "type":"string", - "tooltip":"Defaults to web map title" - }, - { - "placeHolder":"Optional subtitle text", - "label":"SubTitle:", - "fieldName":"subtitle", - "type":"string", - "tooltip":"Optional subtitle text" - }, - { - "label":"Title logo:", + "tooltip":"Defaults to web map title" }, + { "label":"Logo Image:", "fieldName":"logo", "type":"string", - "tooltip":"Defaults to sample logo" - }, - { + "tooltip":"Defaults to sample logo"}, + { "label":"Logo Alternate Text:", + "fieldName":"logoAltText", "type":"string", - "fieldName":"description", - "label":"Details", - "placeHolder":"Defaults to web map description.", - "tooltip":"Enter content for the details panel", - "stringFieldOption":"richtext" + "tooltip":"Type here the text from the Logo Image"}, + { "label":"Animated Marker", + "type":"boolean", + "fieldName":"animated_marker"}, + { "label":"Animated Marker File:", + "fieldName":"marker", + "type":"string", + "tooltip":"Defaults to blue-animated"}, + { "label":"Marker size:", + "fieldName":"marker_size", + "type":"int", + "tooltip":"Size of the Marker"}, + + { "label":"Access Keys", + "fieldName":"alt_keys", + "type":"boolean", + "tooltip":"Show hints for alternate keys when pressing ALT key."}, + { "type": "paragraph", + "value": "The Access Key is a shortcut to activate or focus a screen control.
      The way of accessing the shortcut key is varying in different browsers.
      Most browsers use the [Alt]+# or [Alt][Shift]+#.
      However the shortcut can be set to another combination of keys." }, - { + + + { "label":"New Icons", + "type":"boolean", + "fieldName":"new_icons"} + ] + }, + { + "category":"Colors", + "fields":[ + { "label":"Theme Color:", "type":"color", "fieldName":"theme", - "tooltip":"Color theme to use", - "label":"Color Theme:" - }, - { + "tooltip":"Title bar color"}, + { "label":"Title Color:", "type":"color", "fieldName":"color", - "tooltip":"Title bar text color", - "label":"Title Color:" - }, - { + "tooltip":"Title bar text color"}, + { "label":"Hover Color:", + "type":"color", + "fieldName":"hoverColor", + "tooltip":"Hover over color"}, + { "label":"Focus Color:", + "type":"color", + "fieldName":"focusColor", + "tooltip":"Focus border color"}, + { "label":"Active Color:", + "type":"color", + "fieldName":"activeColor", + "tooltip":"Selection color"}, + { "label":"Icon color:", "type":"string", "fieldName":"icons", "tooltip":"Icon color", - "label":"Icon color:", "options":[ { "label":"White", @@ -63,49 +98,16 @@ "label":"Black", "value":"black" } - ] - },{ - "type":"paragraph", - "value": "Use the Custom css option to paste css that overwrites rules in the app." - },{ - "type":"string", - "fieldName": "customstyle", - "tooltip":"Custom css", - "label": "Custom css" - },{ - "type": "boolean", - "fieldName": "popupPanel", - "label": "Display popup content in floating panel" - } + ]} ] - },{ - "category":"Splash Screen", - "fields":[{ - "type": "boolean", - "fieldName": "splashModal", - "tooltip": "Enable Splash Screen", - "label": "Splash Screen" - },{ - "type": "string", - "fieldName": "splashTitle", - "label": "Splash screen title", - "tooltip": "Define splash screen title" - },{ - "type":"string", - "fieldName":"splashContent", - "label":"Splash screen content text", - "tooltip":"Define splash screen content", - "stringFieldOption":"richtext" - } ] - }, + }, { "category":"Tools", "fields":[ - { + { "label":"Active Tool:", "type":"string", "fieldName":"activeTool", "tooltip":"Active Tool", - "label":"Active Tool:", "options":[ { "label":"None", @@ -123,6 +125,10 @@ "label":"Details", "value":"details" }, + { + "label":"Instructions", + "value":"instructions" + }, { "label":"Edit", "value":"edit" @@ -131,6 +137,14 @@ "label":"Layers", "value":"layers" }, + { + "label":"Features", + "value":"features" + }, + { + "label":"Filters", + "value":"filter" + }, { "label":"Legend", "value":"legend" @@ -151,95 +165,103 @@ "label":"Share", "value":"share" } - ] - }, - { + ]}, + + { "label":"Details", "type":"boolean", - "fieldName":"tool_basemap", - "label":"Basemap Gallery" - }, - { + "fieldName":"tool_details"}, + { "label":"Instructions", "type":"boolean", - "fieldName":"tool_bookmarks", - "label":"Bookmarks" - }, - { + "fieldName":"tool_instructions"}, + { "label":"Overview Map", "type":"boolean", - "fieldName":"tool_locate", - "label":"Find Location" - },{ - "type": "boolean", - "fieldName": "locate_track", - "label": "Enable tracking" - }, - { + "fieldName":"tool_overview"}, + { "label":"Basemap Gallery", "type":"boolean", - "fieldName":"tool_home", - "label":"Home Extent Button" - }, - { + "fieldName":"tool_basemap"}, + { "label":"Bookmarks", "type":"boolean", - "fieldName":"scalebar", - "label":"Scalebar" - }, - { + "fieldName":"tool_bookmarks"}, + { "label":"Find Location", "type":"boolean", - "fieldName":"tool_layers", - "label":"Layer List" - },{ + "fieldName":"tool_locate"}, + { "label":"Home Button", "type":"boolean", - "fieldName": "tool_sublayers", - "label": "Include sublayers in Layer List" - },{ - "type": "boolean", - "fieldName": "tool_layerlegend", - "label": "Include legend in Layer List" - },{ - "type": "boolean", - "fieldName": "tool_opacity", - "label": "Include opacity slider in Layer List" - }, - { + "tooltip":"(Default Extent)", + "fieldName":"tool_home"}, + { "label":"Legend", "type":"boolean", - "fieldName":"tool_legend", - "label":"Legend" - }, - { + "fieldName":"tool_legend"}, + { "label":"Layers", "type":"boolean", - "fieldName":"tool_details", - "label":"Map Details" - }, - { + "fieldName":"tool_layers"}, + { "label":"Feature List", "type":"boolean", - "fieldName":"tool_measure", - "label":"Measure Tool" - }, - { + "fieldName":"tool_features"}, + { "label":"Filters", "type":"boolean", - "fieldName":"tool_overview", - "label":"Overview Map" - }, - { + "fieldName":"tool_filter"}, + { "label":"Measure Tool", "type":"boolean", - "fieldName":"tool_share", - "label":"Share Tools" - } + "fieldName":"tool_measure"}, + { "label":"Share Tools", + "type":"boolean", + "fieldName":"tool_share"}, + { "label":"Print Button", + "type":"boolean", + "fieldName":"tool_print"}, + { "label":"Scalebar", + "type":"boolean", + "fieldName":"scalebar"}, + { "label":"Extended Navigation Tool Bar", + "type":"boolean", + "fieldName":"navigation"} ] - },{ + }, + { "category": "Search Settings", "fields": [ { - "type":"paragraph", - "value": "Enable search to allow users to find a location or data in the map. Configure the search settings to refine the experience in your app by setting the default search resource, placeholder text, etc." + "type": "paragraph", + "value": "Enable/disable the search tool and optionally select layers (and fields) to add to the search tool." }, { + "label":"Select search layers and fields", + "fieldName":"searchLayers", + "type":"multilayerandfieldselector", + "tooltip":"Select layer and fields to search", + "layerOptions":{ + "supportedTypes":[ + "FeatureLayer" + ], + "geometryTypes":[ + "esriGeometryPoint", + "esriGeometryLine", + "esriGeometryPolyline", + "esriGeometryPolygon" + ] + }, + "fieldOptions":{ + "supportedTypes":[ + "esriFieldTypeString" + ] + } + },{ "type":"boolean", "fieldName":"tool_search", - "label":"Enable search tool" + "label":"Address Finder" + }, + { + "type":"boolean", + "fieldName":"searchExtent", + "label":"Prioritize search results in current extent." + },{ + "type":"paragraph", + "value": "When Location Search is true the search widget will allow users to search for addresses and locations using one or more locators and also search the layers and fields specified in the Search Layers configuration option. Unchecking the Location Search option will remove the locator search and only configured search layers will be displayed." },{ - "type":"search", - "fieldName": "searchConfig", - "label": "Configure Search" + "type": "boolean", + "fieldName": "locationSearch", + "label": "Location Search" } ] }, @@ -296,75 +318,170 @@ "label":"Add Legend to Output" } ] - },{ - "category": "Custom URL Parameter", - "fields": [ - { - "type": "paragraph", - "value": "Setup the app to support a custom url parameter. For example if your map contains a feature layer with parcel information and you'd like to be able to find parcels using a url parameter you can use this section to do so. Select a layer and search field then define the name of a custom param. Once you've defined these values you can append the custom search to your application url using the custom parameter name you define. For example, if I set the custom param value to parcels a custom url would look like this index.html?parcel=3045" - },{ - "placeHolder":"i.e. parcels", - "label":"URL param name:", - "fieldName":"customUrlParam", + }, + { + "category":"Languages", + "fields":[ + { "label":"Control with label", + "type":"boolean", + "fieldName":"languageLabel", + "tooltip": "Place a localized label in front of the Language control."}, + { + "type":"paragraph", + "value":"Language 1" + }, + + { "label": "Language code", + "fieldName":"lang1code", + "type":"string" + }, + { "type":"paragraph", + "value":"Enter the locale of the language. Example: 'en-us'" + }, + { "label": "Language Short Name", + "fieldName":"lang1shortName", + "type":"string" + }, + { "type": "paragraph", + "value":"The name that will appear on the Language widget as active language.
      Leave it blank to display first two letters of language code when no image.
      You may use national charactes here to change the default display." + }, + { "label": "Flag Image", + "fieldName":"lang1imageSrc", + "type":"string" + }, + { "type": "paragraph", + "value":"The location of the image representing the flag of the country.
      Note: Leave it blank to display no image and show the Language Short Name instead." + }, + { "label": "Language name", + "fieldName":"lang1name", + "type":"string" + }, + { "type":"paragraph", + "value":"The name that will appear in the combo-box pull-down." + }, + { "label": "Application Id", + "fieldName":"lang1appId", + "type":"string" + }, + { "type":"paragraph", + "value":"ID of application that will restart for this language.
      Note: you may want another application to customize layers, data and other custom attributes.
      Leave blank to use same application." + }, + + { "type":"paragraph", + "value":"Language 2" + + }, + { "label": "Language code", + "fieldName":"lang2code", "type":"string", - "tooltip":"Custom URL param name" - },{ - "type":"layerAndFieldSelector", - "fieldName":"customUrlLayer", - "label":"Layer to search for custom url param value", - "tooltip":"Url param search layer", - "fields":[ - { - "multipleSelection":false, - "fieldName":"urlField", - "label":"URL param search field", - "tooltip":"URL param search field" - } - ], - "layerOptions":{ - "supportedTypes":[ - "FeatureLayer" - ], - "geometryTypes":[ - "esriGeometryPoint", - "esriGeometryLine", - "esriGeometryPolygon" - ] - } - } + "tooltip": "The locale of language 2." + }, + { "label": "Language Short Name", + "fieldName":"lang2shortName", + "type":"string", + "tooltip": "The two-letter name that will appear on the Language widget." + }, + { "label": "Flag Image", + "fieldName":"lang2imageSrc", + "type":"string", + "tooltip": "The location of a 22x22 image." + }, + { "label": "Language name", + "fieldName":"lang2name", + "type":"string", + "tooltip": "The name that will appear in the combo-box pull-down." + }, + { "label": "Application Id", + "fieldName":"lang2appId", + "type":"string", + "tooltip": "Application ID for language 2." + }, - ] + { "type":"paragraph", + "value":"Language 3" + }, + { "label": "Language code", + "fieldName":"lang3code", + "type":"string", + "tooltip": "The locale of language 3." + }, + { "label": "Language Short Name", + "fieldName":"lang3shortName", + "type":"string", + "tooltip": "The name that will appear on the Language widget." + }, + { "label": "Flag Image", + "fieldName":"lang3imageSrc", + "type":"string", + "tooltip": "The location of a 22x22 image." + }, + { "label": "Language name", + "fieldName":"lang3name", + "type":"string", + "tooltip": "The name that will appear in the combo-box pull-down." + }, + { "label": "Application Id", + "fieldName":"lang3appId", + "type":"string", + "tooltip": "Application ID for language 3." + } + ] } ], "values":{ "icons":"white", + "new_icons":false, + "animated_marker":true, + "marker":"images/ripple-dot1.gif", + "marker_size":"35", + "alt_keys":true, "logo":"images/logo.png", - "color":"#fff", - "theme":"80ab00", - "activeTool":"legend", + "logoAltText":"", + + "color":"#ffffff", + "hoverColor":"#00A9E6", + "focusColor":"#FF7700", + "activeColor":"#00b9f6", + "theme":"#005ce6", + + "activeTool":"details", "scalebar":false, - "splashModal": false, - "tool_print":true, + "navigation":false, "tool_print_layouts":false, "tool_print_legend":false, "tool_share":true, "tool_overview":true, "tool_measure":true, "tool_details":true, + "tool_instructions":true, + "tool_filter":true, "tool_legend":true, "tool_layers":true, - "tool_sublayers": true, - "tool_opacity": true, - "tool_layerlegend": true, "tool_home":true, "tool_locate":true, - "locate_track": false, "tool_edit":true, "tool_edit_toolbar":false, "tool_bookmarks":true, "tool_basemap":true, "tool_search":true, + "tool_print":true, "locationSearch": true, - "popupPanel": false + "searchExtent":false, + + "languageLabel":true, + + "lang1shortName": "EN", + "lang1name": "English", + "lang1code": "EN-US", + "lang1imageSrc": "images/flag.uk.22.png", + + "lang2shortName": "FR", + "lang2name": "French", + "lang2code": "FR-CA", + "lang2imageSrc": "images/flag.fr.22.png", + + "lang3shortName": "", + "lang3name": "", + "lang3code": "" } }