├── .bowerrc ├── .gitattributes ├── .gitignore ├── .jshintrc ├── README.md ├── bower.json ├── dojo-bootstrap-map-js.png ├── gruntfile.js ├── license.txt ├── package.json ├── profiles └── app.profile.js └── src ├── app ├── config.js ├── layout │ ├── AboutModal.js │ ├── NavBar.js │ ├── nls │ │ ├── de │ │ │ └── strings.js │ │ ├── es │ │ │ └── strings.js │ │ ├── fr │ │ │ └── strings.js │ │ ├── it │ │ │ └── strings.js │ │ └── strings.js │ └── templates │ │ ├── AboutModal.html │ │ └── NavBar.html ├── main.js ├── mapping │ ├── MapControls.js │ └── templates │ │ └── Map.html └── resources │ ├── app.css │ └── main.css └── index.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src" 3 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | src/dijit 4 | src/dojo 5 | src/dojox 6 | src/dstore 7 | src/esri 8 | src/util 9 | src/dgrid 10 | src/put-selector 11 | src/xstyle 12 | src/bootstrap-map-js 13 | src/dojo-bootstrap 14 | src/spinjs 15 | dist 16 | .grunt -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Configuration, esri jsapi 3 | // Modified from [jshint web defaults][1]. 4 | // Differences between the default and our file are noted 5 | // Options that are commented out completely are uneccesary or problematic for our codebase 6 | // [1] : https://github.com/jshint/jshint/blob/2.x/examples/.jshintrc 7 | // See http://jshint.com/docs/ for more details 8 | 9 | "maxerr" : 5000, // {int} Maximum error before stopping ** Get ALL the errors ** 10 | 11 | // Enforcing - true = enforce this rule, false = don't enforce this rule 12 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 13 | "camelcase" : false, // true: Identifiers must be in camelCase 14 | "curly" : true, // true: Require {} for every new block or scope 15 | "eqeqeq" : false, // true: Require triple equals (===) for comparison ** Just use triples with undefined, null, false, 0 and 1 ** 16 | "es3" : true, // true: Adhere to ECMAScript 3 specification ** Still needed until IE8 support is dropped ** 17 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() ** Still needed until IE8 support is dropped ** 18 | "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` ** Avoids confusion and minification errors ** 19 | "latedef" : false, // true: Require variables/functions to be defined before being used 20 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` ** Coding style enforcement ** 21 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 22 | "noempty" : true, // true: Prohibit use of empty blocks 23 | "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) ** Coding style enforcement ** 24 | "plusplus" : false, // true: Prohibit use of `++` & `--` 25 | "quotmark" : true, // Quotation mark consistency: ** Use the same style. Doubles should be used in most cases ** 26 | // false : do nothing (default) 27 | // true : ensure whatever is used is consistent 28 | // "single" : require single quotes 29 | // "double" : require double quotes 30 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 31 | "unused" : "vars", // true: Require all defined variables be used 32 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode ** Dojo style and existing codebase conflicts ** 33 | "trailing" : false, // true: Prohibit trailing whitespaces 34 | //"indent" : 4, // {int} Number of spaces to use for indentation 35 | //"maxparams" : false, // {int} Max number of formal params allowed per function 36 | //"maxdepth" : false, // {int} Max depth of nested blocks (within functions) 37 | //"maxstatements" : false, // {int} Max number statements per function 38 | //"maxcomplexity" : false, // {int} Max cyclomatic complexity per function 39 | //"maxlen" : false, // {int} Max number of characters per line 40 | 41 | // Relaxing - false = continue to enforce this rule, true = don't enforce this rule (relax it) 42 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 43 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 44 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 45 | "eqnull" : true, // true: Tolerate use of `== null` 46 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 47 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 48 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 49 | // (ex: `for each`, multiple try/catch, function expression…) 50 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 51 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 52 | "funcscope" : true, // true: Tolerate defining variables inside control statements ** Other variable checks keep use from abusing this ** 53 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 54 | "iterator" : false, // true: Tolerate using the `__iterator__` property 55 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 56 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 57 | "laxcomma" : false, // true: Tolerate comma-first style coding 58 | "loopfunc" : true, // true: Tolerate functions being defined in loops ** Almost required in some callback & promise style code ** 59 | "multistr" : false, // true: Tolerate multi-line strings 60 | "proto" : false, // true: Tolerate using the `__proto__` property 61 | "scripturl" : true, // true: Tolerate script-targeted URLs ** If this is being used, there is probably a good reason ** 62 | "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment 63 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 64 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 65 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 66 | "validthis" : true, // true: Tolerate using this in a non-constructor function ** We don't run in `strict mode` & coding style conflicts ** 67 | 68 | // Environments 69 | "browser" : true, // Web Browser (window, document, etc) 70 | "devel" : true, // Development/debugging (alert, confirm, etc) 71 | "couch" : false, // CouchDB 72 | "dojo" : false, // Dojo Toolkit ** Don't use global dojo objects. Use AMD style coding ** 73 | "jquery" : false, // jQuery 74 | "mootools" : false, // MooTools 75 | "node" : false, // Node.js 76 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 77 | "prototypejs" : false, // Prototype and Scriptaculous 78 | "rhino" : false, // Rhino 79 | "worker" : false, // Web Workers ** Make a jshint comment when this is `true` ** 80 | "wsh" : false, // Windows Scripting Host 81 | "yui" : false, // Yahoo User Interface 82 | 83 | // Legacy ** According to jshint docs, these options are NOT to be used or relied on. Removing them. 84 | //"nomen" : false, // true: Prohibit dangling `_` in variables 85 | //"onevar" : false, // true: Allow only one `var` statement per function 86 | //"passfail" : false, // true: Stop on first error 87 | //"white" : false, // true: Check against strict whitespace and indentation rules 88 | 89 | // Custom Globals - additional predefined global variables 90 | // Using both `predef` and `globals` to support tools with older jshint parsers 91 | "predef" : [ 92 | "define", 93 | "require" 94 | ], 95 | "globals" : { // ** `false` = don't allow variable to be redefined locally 96 | "define": false, 97 | "require": false 98 | } 99 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dojo-bootstrap-map-js 2 | 3 | An application boilerplate demonstrating how to use the Esri [ArcGIS API for JavaScript](//js.arcgis.com) with [Bootstrap Map](//github.com/Esri/bootstrap-map-js) and [Dojo Bootstrap](//github.com/xsokev/Dojo-Bootstrap) to create a responsive mapping application using [Bootstrap](//getbootstrap.com) components. 4 | 5 | [View it live](http://esri.github.io/dojo-bootstrap-map-js/) 6 | 7 | ![App Screenshot](https://raw.githubusercontent.com/Esri/dojo-bootstrap-map-js/master/dojo-bootstrap-map-js.png) 8 | 9 | This boilerplate is ideal for mobile applications because it provides the responsive UI of Bootstrap without the overhead of loading all of jQuery. Furthermore, you can run a Dojo build to optimize all of the JavaScript and CSS source code and dependencies to get a roughly 95% reduction in number of requests for scripts and 25% reduction in the size of those scripts. 10 | 11 | This application boilerplate demonstrates how to build a mapping application that utilizes the best parts of Dojo (AMD modules, classes and widgets, promises, i18n, custom builds, etc) along with the responsive UI of Bootstrap. For simpler examples of how to get started with [Bootstrap Map](//github.com/Esri/bootstrap-map-js) and [Dojo Bootstrap](//github.com/xsokev/Dojo-Bootstrap), see the [Boostrap Map demo pages](http://esri.github.io/bootstrap-map-js/demo/dojo/getstarted.html). 12 | 13 | ## Instructions 14 | 15 | By default, this boilerplate assumes you will want to work from local copies of all dependencies so that you can create a custom build. However, you can also work off of remotely hosted (CDN) dependencies by using `nobuild.html`. 16 | 17 | ### Quick Start 18 | 19 | 1. [Download](https://github.com/Esri/dojo-bootstrap-map-js/archive/master.zip) or [Fork and clone the repo](https://help.github.com/articles/fork-a-repo) 20 | 2. Make sure the `dojo-bootstrap-map-js` folder is served via your local web server 21 | 3. Load `src/index.html` in your browser 22 | 23 | ### Downloading Dependencies and Building 24 | 25 | You will need to install [Node](http://nodejs.org/), [Bower](http://bower.io/), and [Grunt](http://gruntjs.com/) in order to download dependencies and build the app via the command line. 26 | 27 | 1. [Fork and clone the repo](https://help.github.com/articles/fork-a-repo) 28 | 2. `cd` into the `dojo-bootstrap-map-js` folder 29 | 3. Run `npm install` to install the Grunt tasks 30 | 4. Run `bower install` to download Dojo and other dependencies 31 | 5. Run `grunt serve` to load the unbuilt app into a web browser 32 | 6. Modify the code as needed 33 | 7. Run `grunt build` to build the app under the `dist` folder 34 | 9. Run `grunt serve:build` to load the built app into a web browser 35 | 36 | You may want to run `grunt clean:deploy` to remove all uncompressed javascript and source maps before you deploy the contents of that folder to your web server. 37 | 38 | ## Requirements 39 | 40 | * Notepad or your favorite text editor 41 | * Web browser with access to the Internet 42 | * [Node](http://nodejs.org/), [Bower](http://bower.io/), and [Grunt](http://gruntjs.com/) to run the command line development tools 43 | * Java, in order to build Dojo. 44 | 45 | ## Example Applications 46 | 47 | Below are a few example applications that have been built using this boilerplate: 48 | 49 | * Demo for DevSummit 2015, [responsive-citizens](http://tomwayson.github.io/responsive-citizens/), a responsive take on the Attribute editing - mobile sample. [Github repo](https://github.com/tomwayson/responsive-citizens). 50 | * San Juan County GIS, [traffic-collisions-app](http://sjcgis.github.io/traffic-collisions-app/), A web application showing traffic collisions in San Juan County. Filterable by island and severity. [Github repo](https://github.com/SJCGIS/traffic-collisions-app) 51 | * City of Menlo Park, [General Plan Review](http://arcgis.github.io/menlo-park-general-plan-review/src/), a wep application that allows users to dynamically modify the proposed general plan and view the impact on a map and chart. 52 | 53 | ## Resources 54 | 55 | * [Bootstrap Map](//github.com/Esri/bootstrap-map-js) 56 | * [Dojo Bootstrap](//github.com/xsokev/Dojo-Bootstrap) 57 | * [ArcGIS API for JavaScript](//js.arcgis.com) 58 | * [Bootstrap](//getbootstrap.com) 59 | * [ArcGIS Blog](http://blogs.esri.com/esri/arcgis) 60 | * [twitter@esri](http://twitter.com/esri) 61 | 62 | ## Issues 63 | 64 | Find a bug or want to request a new feature? Please let us know by submitting an issue. 65 | 66 | ## Contributing 67 | 68 | Anyone and everyone is welcome to contribute. 69 | 70 | ## Licensing 71 | Copyright 2012 Esri 72 | 73 | Licensed under the Apache License, Version 2.0 (the "License"); 74 | you may not use this file except in compliance with the License. 75 | You may obtain a copy of the License at 76 | 77 | http://www.apache.org/licenses/LICENSE-2.0 78 | 79 | Unless required by applicable law or agreed to in writing, software 80 | distributed under the License is distributed on an "AS IS" BASIS, 81 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 82 | See the License for the specific language governing permissions and 83 | limitations under the License. 84 | 85 | A copy of the license is available in the repository's [license.txt](https://raw.github.com/Esri/dojo-bootstrap-map-js/master/license.txt) file. 86 | 87 | [](Esri Tags: ArcGIS Web Mapping Bootstrap) 88 | [](Esri Language: JavaScript)​ 89 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dojo-bootstrap-map-js", 3 | "version": "3.2.3", 4 | "homepage": "https://github.com/Esri/dojo-bootstrap-map-js", 5 | "authors": [ 6 | "Tom Wayson " 7 | ], 8 | "main": "src/index.html", 9 | "keywords": [ 10 | "Esi", 11 | "Dojo", 12 | "Bootstrap" 13 | ], 14 | "license": "Apache", 15 | "private": true, 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests" 22 | ], 23 | "dependencies": { 24 | "bootstrap-map-js": "~0.0.2", 25 | "esri": "arcgis-js-api#3.15", 26 | "dojo-bootstrap": "~3.0.2", 27 | "dijit": "1.10.4", 28 | "dojo": "1.10.4", 29 | "dojox": "1.10.4", 30 | "util": "dojo-util#1.10.4", 31 | "dgrid": "0.3.17", 32 | "put-selector": "0.3.6", 33 | "xstyle": "0.1.3", 34 | "spinjs": "2.0.1" 35 | }, 36 | "resolutions": { 37 | "xstyle": "0.1.3/esri-3.14.0", 38 | "dojo": "v1.10.4/esri-3.14.0", 39 | "dgrid": "0.3.17/esri-3.14.0", 40 | "dijit": "v1.10.4/esri-3.14.0", 41 | "dojox": "v1.10.4/esri-3.14.0", 42 | "util": "v1.10.4/esri-3.14.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /dojo-bootstrap-map-js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/dojo-bootstrap-map-js/a667b0a5c959aa3598d46aa0a73b58ab0062ffa9/dojo-bootstrap-map-js.png -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | var LIVERELOAD_PORT = 35729; 3 | var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT}); 4 | var mountFolder = function (connect, dir) { 5 | return connect['static'](require('path').resolve(dir)); 6 | }; 7 | module.exports = function(grunt) { 8 | 9 | // Project configuration. 10 | grunt.initConfig({ 11 | 12 | jshint: { 13 | options: { 14 | jshintrc: true 15 | }, 16 | all: ['src/app/**/*.js'] 17 | }, 18 | 19 | connect: { 20 | options: { 21 | port:9000, 22 | // change this to '0.0.0.0' to access the server from outside 23 | hostname: 'localhost' 24 | }, 25 | // load cdn code w/ livereload 26 | cdn: { 27 | options: { 28 | base: 'src' 29 | } 30 | }, 31 | // load built app 32 | build: { 33 | options: { 34 | base: 'dist' 35 | } 36 | } 37 | }, 38 | 39 | //Open default browser at the app 40 | open: { 41 | cdn: { 42 | path: 'http://localhost:<%= connect.options.port %>/' 43 | }, 44 | build: { 45 | path: 'http://localhost:<%= connect.options.port %>/' 46 | } 47 | }, 48 | //setup watch tasks 49 | watch: { 50 | options: { 51 | nospan: true, 52 | livereload: LIVERELOAD_PORT 53 | }, 54 | 55 | source: { 56 | files: ['./src/js/**/*.js'], 57 | tasks: ['jshint'] 58 | }, 59 | livereload:{ 60 | options: { 61 | livereload:LIVERELOAD_PORT 62 | }, 63 | files:[ 64 | './src/js/**/*.js', 65 | './src/**/*.html', 66 | './src/css/**/*.css' 67 | ] 68 | } 69 | }, 70 | // clean the output directory before each build 71 | clean: { 72 | build: ['dist'], 73 | deploy: ['dist/**/*.consoleStripped.js','dist/**/*.uncompressed.js','dist/**/*.js.map'], 74 | bower: ['src/bootstrap-map-js', 'src/dgrid', 'src/dijit', 'src/dojo', 'src/dojo-bootstrap', 'src/dojox', 'src/dstore', 'src/esri', 'src/put-selector', 'src/spinjs', 'src/util', 'src/xstyle'] 75 | }, 76 | // dojo build configuration, mainly taken from dojo boilerplate 77 | dojo: { 78 | dist: { 79 | options: { 80 | profile: 'profiles/app.profile.js' // Profile for build 81 | } 82 | }, 83 | options: { 84 | dojo: 'src/dojo/dojo.js', // Path to dojo.js file in dojo source 85 | load: 'build', // Optional: Utility to bootstrap (Default: 'build') 86 | // profiles: [], // Optional: Array of Profiles for build 87 | // appConfigFile: '', // Optional: Config file for dojox/app 88 | // package: '', // Optional: Location to search package.json (Default: nothing) 89 | // packages: [], // Optional: Array of locations of package.json (Default: nothing) 90 | // require: '', // Optional: Module to require for the build (Default: nothing) 91 | // requires: [], // Optional: Array of modules to require for the build (Default: nothing) 92 | releaseDir: '../dist', // Optional: release dir rel to basePath (Default: 'release') 93 | cwd: './', // Directory to execute build within 94 | // dojoConfig: '', // Optional: Location of dojoConfig (Default: null), 95 | // Optional: Base Path to pass at the command line 96 | // Takes precedence over other basePaths 97 | // Default: null 98 | basePath: './src' 99 | } 100 | }, 101 | processhtml: { 102 | build: { 103 | files: { 104 | 'dist/index.html': ['src/index.html'] 105 | } 106 | } 107 | }, 108 | 'gh-pages': { 109 | options: { 110 | base: 'src' 111 | }, 112 | src: ['app/**', 'index.html'] 113 | } 114 | }); 115 | grunt.loadNpmTasks('grunt-contrib-jshint'); 116 | grunt.loadNpmTasks('grunt-contrib-watch'); 117 | grunt.loadNpmTasks('grunt-contrib-connect'); 118 | grunt.loadNpmTasks('grunt-open'); 119 | grunt.loadNpmTasks('grunt-gh-pages'); 120 | grunt.loadNpmTasks('grunt-contrib-clean'); 121 | grunt.loadNpmTasks('grunt-dojo'); 122 | grunt.loadNpmTasks('grunt-processhtml'); 123 | 124 | grunt.registerTask('default', ['serve']); 125 | 126 | grunt.registerTask('serve', function (target) { 127 | var trgt = target || 'cdn'; 128 | grunt.task.run([ 129 | 'jshint', 130 | 'connect:' + trgt, 131 | 'open:' + trgt, 132 | 'watch' 133 | ]); 134 | }); 135 | 136 | grunt.registerTask('hint', ['jshint']); 137 | 138 | grunt.registerTask('build', ['jshint', 'clean:build', 'dojo', 'processhtml']); 139 | 140 | grunt.registerTask('deploy', ['gh-pages']); 141 | }; 142 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Apache License - 2.0 2 | 3 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 4 | 5 | 1. Definitions. 6 | 7 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 8 | 9 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 10 | 11 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control 12 | with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management 13 | of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial 14 | ownership of such entity. 15 | 16 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 17 | 18 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, 19 | and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to 22 | compiled object code, generated documentation, and conversions to other media types. 23 | 24 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice 25 | that is included in or attached to the work (an example is provided in the Appendix below). 26 | 27 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the 28 | editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes 29 | of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, 30 | the Work and Derivative Works thereof. 31 | 32 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work 33 | or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual 34 | or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of 35 | electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on 36 | electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for 37 | the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing 38 | by the copyright owner as "Not a Contribution." 39 | 40 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and 41 | subsequently incorporated within the Work. 42 | 43 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, 44 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, 45 | publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 46 | 47 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, 48 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, 49 | sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are 50 | necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was 51 | submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work 52 | or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You 53 | under this License for that Work shall terminate as of the date such litigation is filed. 54 | 55 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, 56 | and in Source or Object form, provided that You meet the following conditions: 57 | 58 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 59 | 60 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 61 | 62 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices 63 | from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 64 | 65 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a 66 | readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the 67 | Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the 68 | Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever 69 | such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. 70 | You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, 71 | provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to 72 | Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your 73 | modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with 74 | the conditions stated in this License. 75 | 76 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You 77 | to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, 78 | nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 79 | 80 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except 81 | as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 82 | 83 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides 84 | its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, 85 | any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for 86 | determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under 87 | this License. 88 | 89 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required 90 | by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, 91 | including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the 92 | use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or 93 | any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 94 | 95 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a 96 | fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting 97 | such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree 98 | to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your 99 | accepting any such warranty or additional liability. 100 | 101 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dojo-bootstrap-map-js", 3 | "version": "3.2.3", 4 | "description": "A boilerplate application demonstrating how to create a responsive mapping application using the Esri ArcGIS API for JavaScript with Bootstrap Map and Dojo Bootstrap", 5 | "main": "gruntfile.js", 6 | "directories": { 7 | "src": "src" 8 | }, 9 | "scripts": { 10 | "test": "grunt test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/Esri/dojo-bootstrap-map-js.git" 15 | }, 16 | "keywords": [ 17 | "esri", 18 | "maps", 19 | "dojo", 20 | "bootstrap" 21 | ], 22 | "author": { 23 | "name": "Tom Wayson", 24 | "homepage": "https://github.com/tomwayson" 25 | }, 26 | "contributors": [ 27 | { 28 | "name": "Allan Laframboise", 29 | "homepage": "https://github.com/alaframboise" 30 | }, 31 | { 32 | "name": "Ben Stoltz", 33 | "homepage": "https://github.com/benstoltz" 34 | }, 35 | { 36 | "name": "Nick Peihl", 37 | "homepage": "https://github.com/npeihl" 38 | }, 39 | { 40 | "name": "Arnaud Ferrand", 41 | "homepage": "https://github.com/tsamaya" 42 | } 43 | ], 44 | "license": "Apache", 45 | "bugs": { 46 | "url": "https://github.com/Esri/dojo-bootstrap-map-js/issues" 47 | }, 48 | "homepage": "https://github.com/Esri/dojo-bootstrap-map-js", 49 | "devDependencies": { 50 | "connect-livereload": "~0.3.2", 51 | "grunt": "~0.4.2", 52 | "grunt-build-gh-pages": "^1.0.3", 53 | "grunt-contrib-clean": "^0.6.0", 54 | "grunt-contrib-connect": "~0.7.1", 55 | "grunt-contrib-jshint": "^0.10.0", 56 | "grunt-contrib-watch": "~0.5.3", 57 | "grunt-dojo": "^1.0.1", 58 | "grunt-gh-pages": "^0.9.1", 59 | "grunt-open": "~0.2.3", 60 | "grunt-processhtml": "^0.3.7" 61 | }, 62 | "engines": { 63 | "node": ">=0.8.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /profiles/app.profile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look to `util/build/buildControlDefault.js` for more information on available options and their default values. 3 | */ 4 | 5 | var profile = { 6 | // `basePath` is relative to the directory containing this profile file; in this case, it is being set to the 7 | // src/ directory, which is the same place as the `baseUrl` directory in the loader configuration. (If you change 8 | // this, you will also need to update run.js.) 9 | basePath: '../src/', 10 | 11 | // This is the directory within the release directory where built packages will be placed. The release directory 12 | // itself is defined by `build.sh`. You should probably not use this; it is a legacy option dating back to Dojo 13 | // 0.4. 14 | // If you do use this, you will need to update build.sh, too. 15 | // releaseName: '', 16 | 17 | // Builds a new release. 18 | action: 'release', 19 | 20 | // Strips all comments and whitespace from CSS files and inlines @imports where possible. 21 | cssOptimize: 'comments', 22 | 23 | // Excludes tests, demos, and original template files from being included in the built version. 24 | mini: true, 25 | 26 | // Uses Closure Compiler as the JavaScript minifier. This can also be set to "shrinksafe" to use ShrinkSafe, 27 | // though ShrinkSafe is deprecated and not recommended. 28 | // This option defaults to "" (no compression) if not provided. 29 | optimize: 'closure', 30 | 31 | // We're building layers, so we need to set the minifier to use for those, too. 32 | // This defaults to "shrinksafe" if not provided. 33 | layerOptimize: 'closure', 34 | 35 | // A list of packages that will be built. The same packages defined in the loader should be defined here in the 36 | // build profile. 37 | packages: [ 38 | // Using a string as a package is shorthand for `{ name: 'app', location: 'app' }` 39 | 'app', 40 | 'dgrid', 41 | 'dijit', 42 | 'dojo', 43 | 'dojox', 44 | 'esri', 45 | 'put-selector', 46 | 'xstyle', 47 | 'dojo-bootstrap', 48 | { 49 | name: 'spin-js', 50 | location: 'spinjs', 51 | resourceTags: { 52 | copyOnly: function(filename) { 53 | return (/.*\jquery.spin.js.?/).test(filename); 54 | } 55 | } 56 | }, { 57 | name: 'bootstrap-map-js', 58 | location: 'bootstrap-map-js/src' 59 | } 60 | ], 61 | 62 | // Strips all calls to console functions within the code. You can also set this to "warn" to strip everything 63 | // but console.error, and any other truthy value to strip everything but console.warn and console.error. 64 | // This defaults to "normal" (strip all but warn and error) if not provided. 65 | stripConsole: 'all', 66 | 67 | // The default selector engine is not included by default in a dojo.js build in order to make mobile builds 68 | // smaller. We add it back here to avoid that extra HTTP request. There is also an "acme" selector available; if 69 | // you use that, you will need to set the `selectorEngine` property in index.html, too. 70 | selectorEngine: 'lite', 71 | 72 | // Any module in an application can be converted into a "layer" module, which consists of the original module + 73 | // additional dependencies built into the same file. Using layers allows applications to reduce the number of HTTP 74 | // requests by combining all JavaScript into a single file. 75 | layers: { 76 | // This is the main loader module. It is a little special because it is treated like an AMD module even though 77 | // it is actually just plain JavaScript. There is some extra magic in the build system specifically for this 78 | // module ID. 79 | 'dojo/dojo': { 80 | // By default, the build system will try to include `dojo/main` in the built `dojo/dojo` layer, which adds 81 | // a bunch of stuff we do not want or need. We want the initial script load to be as small and quick to 82 | // load as possible, so we configure it as a custom, bootable base. 83 | boot: true, 84 | customBase: true, 85 | include: [ 86 | // include the app 87 | 'app/main', 88 | // dpendencies of esri/map that will be requested if not included 89 | 'dojox/gfx/path', 90 | 'dojox/gfx/svg', 91 | 'dojox/gfx/shape', 92 | 'esri/dijit/Attribution', 93 | 94 | // be sure to include the layer types used in your web map 95 | // otherwise they will be requested asyncronously 96 | 'esri/layers/FeatureLayer' 97 | ], 98 | includeLocales: ['en-us'] 99 | }//, 100 | 101 | // In this demo application, we load `app/main` on the client-side, so here we could build a separate layer containing 102 | // that code. (Practically speaking, you probably just want to roll everything into the `dojo/dojo` layer, 103 | // but this helps provide a basic illustration of how multi-layer builds work.) Note that when you create a new 104 | // layer, the module referenced by the layer is always included in the layer (in this case, `app/main`), so it 105 | // does not need to be explicitly defined in the `include` array. 106 | // 'app/main': {} 107 | }, 108 | 109 | // Providing hints to the build system allows code to be conditionally removed on a more granular level than simple 110 | // module dependencies can allow. This is especially useful for creating tiny mobile builds. Keep in mind that dead 111 | // code removal only happens in minifiers that support it! Currently, only Closure Compiler to the Dojo build system 112 | // with dead code removal. A documented list of has-flags in use within the toolkit can be found at 113 | // . 114 | staticHasFeatures: { 115 | // The trace & log APIs are used for debugging the loader, so we do not need them in the build. 116 | 'dojo-trace-api': false, 117 | 'dojo-log-api': false, 118 | 119 | // This causes normally private loader data to be exposed for debugging. In a release build, we do not need 120 | // that either. 121 | 'dojo-publish-privates': false, 122 | 123 | // This application is pure AMD, so get rid of the legacy loader. 124 | 'dojo-sync-loader': false, 125 | 126 | // `dojo-xhr-factory` relies on `dojo-sync-loader`, which we have removed. 127 | 'dojo-xhr-factory': false, 128 | 129 | // We are not loading tests in production, so we can get rid of some test sniffing code. 130 | 'dojo-test-sniff': false 131 | } 132 | }; -------------------------------------------------------------------------------- /src/app/config.js: -------------------------------------------------------------------------------- 1 | define(['esri/InfoTemplate'], function(InfoTemplate) { 2 | return { 3 | 4 | portalUrl: 'http://www.arcgis.com', 5 | 6 | mapControls: { 7 | // ********************************************** 8 | // Example configuration when using a webmap 9 | // ********************************************** 10 | 11 | // example web maps: 12 | // Portland Bike Map example from Boostrap Map demo pages, see 13 | // http://esri.github.io/bootstrap-map-js/demo/dojo/webmap.html 14 | itemId: '8e42e164d4174da09f61fe0d3f206641', 15 | 16 | // SoCal running trails 17 | // GPX tracks embeded in web map as feature collections 18 | // itemId: 'cbb968b3854e4e4fac3f95c30ca41b38', 19 | 20 | // Los Angeles Bike Paths - KML layer of bike paths 21 | // itemId: '78ca84d1f2534d3496e63fa80240d4f3', 22 | 23 | // web maps from ArcGIS JSAPI sample pages 24 | // NOTE: both require a dijit theme (i.e. claro) 25 | // to support the dojox/charting dijits in the popups 26 | 27 | // Tapastry Segments - dynamic map servce w/ dojox/chart in popup 28 | // itemId: '4778fee6371d4e83a22786029f30c7e1', 29 | 30 | // mobile web map example, see: 31 | // https://developers.arcgis.com/javascript/jssamples/mobile_arcgis.html 32 | // itemId: '1e79439598494713b553f990a4040886', 33 | 34 | // NOTE: this is the options sent to arcgisUtils.createMap() 35 | // see: https://developers.arcgis.com/javascript/jsapi/esri.arcgis.utils-amd.html#createmap 36 | options: { 37 | mapOptions: { 38 | basemap: 'topo', 39 | sliderPosition: 'bottom-right' 40 | } 41 | }, 42 | 43 | // ********************************************** 44 | // Example configuration when NOT using a webmap 45 | // and loading layers from the settings in this config 46 | // ********************************************** 47 | 48 | // NOTE: this is the options sent to new Map() 49 | // see: https://developers.arcgis.com/javascript/jsapi/map-amd.html#map1 50 | // options: { 51 | // basemap: 'gray', 52 | // center: [-122.4167, 37.7833], 53 | // zoom: 14, 54 | // sliderPosition: 'bottom-right' 55 | // }, 56 | 57 | // operationalLayers: [{ 58 | // type: 'feature', 59 | // url: 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Earthquakes/Since_1970/MapServer/0', 60 | // title: 'Earthquakes around the world', 61 | // options: { 62 | // id: 'earthquake', 63 | // opacity: 1.0, 64 | // visible: true, 65 | // outFields: ['*'], 66 | // infoTemplate: new InfoTemplate('Earthquake', '${*}'), 67 | // mode: 0 68 | // } 69 | // }, { 70 | // type: 'dynamic', 71 | // url: 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/SF311/MapServer', 72 | // title: 'SF 311', 73 | // options: { 74 | // id: 'sf311', 75 | // opacity: 0.5, 76 | // visible: true 77 | // } 78 | // }], 79 | 80 | // TODO: add basemaps 81 | // basemaps: {}, 82 | 83 | // set the id of a node to place the legend 84 | // comment this out if you don't want to show legend 85 | legendNodeId: 'mapLegend', 86 | 87 | // Add config parameters for each map widget you want to include 88 | // The map reference will get appended to the options 89 | // To accept default options just pass empty object {} 90 | // NOTE: to change the position of these widgets, make changes in map.css 91 | widgets: { 92 | scalebar: { 93 | // see https://developers.arcgis.com/javascript/jsapi/scalebar-amd.html#scalebar1 94 | }, 95 | homeButton: { 96 | // see: https://developers.arcgis.com/javascript/jsapi/homebutton-amd.html#homebutton1 97 | }, 98 | locateButton: { 99 | // see: https://developers.arcgis.com/javascript/jsapi/locatebutton-amd.html#locatebutton1 100 | }, 101 | geocoder: { 102 | // see https://developers.arcgis.com/javascript/jsapi/geocoder-amd.html#geocoder1 103 | autoComplete: true, 104 | arcgisGeocoder: { 105 | placeholder: 'Address or Location' 106 | }, 107 | 'class': 'geocoder' 108 | } 109 | } 110 | }, 111 | 112 | // about modal 113 | aboutModal: { 114 | moreInfoUrl: 'https://github.com/Esri/dojo-bootstrap-map-js' 115 | } 116 | }; 117 | }); 118 | -------------------------------------------------------------------------------- /src/app/layout/AboutModal.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo/_base/declare', 3 | 4 | 'dijit/_WidgetBase', 5 | 'dijit/_TemplatedMixin', 6 | 7 | 'dojo/text!./templates/AboutModal.html', 8 | 'dojo/i18n!./nls/strings' 9 | ], function( 10 | declare, 11 | _WidgetBase, _TemplatedMixin, 12 | template, strings 13 | ) { 14 | 15 | return declare([_WidgetBase, _TemplatedMixin], { 16 | templateString: template, 17 | strings: strings, 18 | 19 | _setMoreInfoUrlAttr: { 20 | node: 'moreInfoNode', 21 | type: 'attribute', 22 | attribute: 'href' 23 | }, 24 | 25 | _setTitleAttr: { 26 | node: 'titleNode', 27 | type: 'innerHTML' 28 | }, 29 | 30 | _setContentAttr: { 31 | node: 'contentNode', 32 | type: 'innerHTML' 33 | }, 34 | 35 | // get default title/content from i18n strings 36 | postCreate: function() { 37 | this.set('title', strings.modalAboutTitle); 38 | this.set('content', '

' + strings.modalAboutContent + '

'); 39 | } 40 | 41 | }); 42 | }); -------------------------------------------------------------------------------- /src/app/layout/NavBar.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo/_base/declare', 3 | 'dojo/query', 4 | 'dojo/topic', 5 | 'dojo/on', 6 | 7 | 'dijit/_WidgetBase', 8 | 'dijit/_TemplatedMixin', 9 | 10 | 'dojo/text!./templates/NavBar.html', 11 | 'dojo/i18n!./nls/strings', 12 | 13 | 'dojo-bootstrap/Collapse', 14 | 'dojo-bootstrap/Dropdown' 15 | ], function( 16 | declare, query, topic, on, 17 | _WidgetBase, _TemplatedMixin, 18 | template, strings 19 | ) { 20 | 21 | return declare([_WidgetBase, _TemplatedMixin], { 22 | templateString: template, 23 | strings: strings, 24 | 25 | _setTitleAttr: function(newTitle) { 26 | this.title = newTitle; 27 | this.titleNode.innerHTML = this.title; 28 | window.document.title = this.title; 29 | }, 30 | 31 | postCreate: function() { 32 | this.inherited(arguments); 33 | this.set('title', strings.appTitle); 34 | this._attachEventHandlers(); 35 | }, 36 | 37 | _attachEventHandlers: function() { 38 | var self = this; 39 | // toggle sidebar 40 | this.own(on(this.sidebarToggleButton, 'click', function(e) { 41 | topic.publish('sidebar/toggle'); 42 | self._hideDropdownNav(e); 43 | })); 44 | // change basemap 45 | query('.basemap-list li', this.domNode).on('click', function(e) { 46 | e.preventDefault(); 47 | topic.publish('basemap/set', { 48 | basemap: e.target.text 49 | }); 50 | self._hideDropdownNav(e); 51 | }); 52 | // show about modal 53 | query('a[href="#about"]', this.domNode).on('click', function(e) { 54 | e.preventDefault(); 55 | topic.publish('about/show'); 56 | self._hideDropdownNav(e); 57 | }); 58 | }, 59 | 60 | _hideDropdownNav: function(e) { 61 | // hide nav dropdown on mobile 62 | if (query('.navbar-collapse.in', this.domNode).length > 0) { 63 | e.stopPropagation(); 64 | this.collapseMenuToggleButton.click(); 65 | } 66 | } 67 | }); 68 | }); -------------------------------------------------------------------------------- /src/app/layout/nls/de/strings.js: -------------------------------------------------------------------------------- 1 | define({ 2 | appTitle: 'Dojo Bootstrap Karte', 3 | navBasemaps: 'Basiskarten', 4 | navAbout: 'Info', 5 | modalAboutTitle: 'Info', 6 | modalAboutContent: 'Ziel dieser Applikationsvorlage ist es, zu zeigen, wie man eine Mapping-Anwendung, welche die besten Funktionen von Dojo (AMD Module, Klassen und Widgets, Promises, i18n, Routing etc.) mit dem responsive Userinterface von Bootstrap verbindet.', 7 | modalAboutMoreInfo: 'Mehr...' 8 | }); -------------------------------------------------------------------------------- /src/app/layout/nls/es/strings.js: -------------------------------------------------------------------------------- 1 | define({ 2 | appTitle: 'Mapa Dojo Bootstrap', 3 | navBasemaps: 'Plan de fondo', 4 | navAbout: 'Información', 5 | modalAboutTitle: 'Información', 6 | modalAboutContent: 'El objetivo de este aplicación es demostrar cómo construir una aplicación de mapas que utiliza las mejores partes de Dojo (módulos AMD, clases y widgets, promesas, i18n, enrutamiento, etc) junto con la interfaz de usuario sensible de Bootstrap.', 7 | modalAboutMoreInfo: 'Más...' 8 | }); -------------------------------------------------------------------------------- /src/app/layout/nls/fr/strings.js: -------------------------------------------------------------------------------- 1 | define({ 2 | appTitle: 'Carte Dojo Bootstrap', 3 | navBasemaps: 'Fonds de plan', 4 | navAbout: 'A propos', 5 | modalAboutTitle: 'A propos', 6 | modalAboutContent: 'Le but de cette application est de montrer comment construire une application cartographique avec le meilleur de Dojo (modules AMD, classes et widgets, Promises, i18n, itinéraires, etc) tout cela avec une IHM réactive de Bootstrap.', 7 | modalAboutMoreInfo: 'Suite...' 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/layout/nls/it/strings.js: -------------------------------------------------------------------------------- 1 | define({ 2 | appTitle: 'Carta Dojo Bootstrap', 3 | navBasemaps: 'Mappe di base', 4 | navAbout: 'Circa', 5 | modalAboutTitle: 'Circa', 6 | modalAboutContent: 'Lo scopo di questa applicazione è quello di dimostrare come creare un\'applicazione di carta con il migliori del Dojo (moduli AMD, classi e widget, promesse, i18n, rotte, ecc) tutti con un\'interfaccia utente reattiva Bootstrap.', 7 | modalAboutMoreInfo: 'Seguito...' 8 | }); -------------------------------------------------------------------------------- /src/app/layout/nls/strings.js: -------------------------------------------------------------------------------- 1 | define({ 2 | root: ({ 3 | appTitle: 'Dojo Bootstrap Map', 4 | navBasemaps: 'Basemaps', 5 | navAbout: 'About', 6 | modalAboutTitle: 'About', 7 | modalAboutContent: 'The goal of this application boilerplate is to demonstrate how to build a mapping application that utilizes the best parts of Dojo (AMD modules, classes and widgets, promises, i18n, routing, etc) along with the responsive UI of Bootstrap.', 8 | modalAboutMoreInfo: 'More...' 9 | }), 10 | fr: true, 11 | es: true, 12 | it: true, 13 | de: true 14 | // TODO: define other locales as needed 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/layout/templates/AboutModal.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/app/layout/templates/NavBar.html: -------------------------------------------------------------------------------- 1 |
2 | 13 | 32 |
-------------------------------------------------------------------------------- /src/app/main.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo/query', 3 | 'dojo/dom', 4 | 'dojo/dom-class', 5 | 'dojo/dom-style', 6 | 'dojo/topic', 7 | 8 | './config', 9 | './mapping/MapControls', 10 | './layout/NavBar', 11 | './layout/AboutModal', 12 | 13 | 'dojo-bootstrap/Modal', 14 | 15 | 'dojo/domReady!'], 16 | function( 17 | query, dom, domClass, domStyle, topic, 18 | config, MapControls, NavBar, AboutModal 19 | ) { 20 | 'use strict'; 21 | var app = {}; 22 | 23 | // start map 24 | app.mapControls = new MapControls(config.mapControls, 'mapControls'); 25 | app.mapControls.startup(); 26 | 27 | // start nav 28 | app.navBar = new NavBar({}, 'navBar'); 29 | app.navBar.startup(); 30 | 31 | // start about modal 32 | app.aboutModal = new AboutModal(config.aboutModal, 'aboutModal'); 33 | app.aboutModal.startup(); 34 | 35 | // responsive sidebar 36 | app.sidebar = dom.byId('sidebar'); 37 | 38 | // app topics 39 | // set app title and about modal based on web map 40 | topic.subscribe('webmap/load', function(args) { 41 | var item; 42 | if (!args.itemInfo || !args.itemInfo.item) { 43 | return; 44 | } 45 | item = args.itemInfo.item; 46 | app.navBar.set('title', item.title); 47 | app.aboutModal.set('title', item.title); 48 | app.aboutModal.set('content', item.snippet || item.description); 49 | if (config.portalUrl) { 50 | app.aboutModal.set('moreInfoUrl', config.portalUrl + '/home/item.html?id=' + item.id); 51 | } 52 | }); 53 | 54 | // set the basemap 55 | topic.subscribe('basemap/set', function(args) { 56 | app.mapControls.setBasemap(args.basemap); 57 | }); 58 | 59 | // toggle the sidebar 60 | topic.subscribe('sidebar/toggle', function() { 61 | if (!app.sidebar) { 62 | return; 63 | } 64 | // make sure sidebar is same height as the map 65 | domStyle.set(app.sidebar, 'height', app.mapControls.getMapHeight() + 'px'); 66 | domClass.toggle(window.document.body, 'sidebar-open'); 67 | }); 68 | 69 | // show the about modal 70 | topic.subscribe('about/show', function() { 71 | query('.about-modal').modal('show'); 72 | }); 73 | 74 | return app; 75 | }); 76 | -------------------------------------------------------------------------------- /src/app/mapping/MapControls.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'dojo/_base/declare', 3 | 'dojo/_base/array', 4 | 'dojo/_base/lang', 5 | 'dojo/dom-class', 6 | 'dojo/topic', 7 | 8 | 'dijit/_WidgetBase', 9 | 'dijit/_TemplatedMixin', 10 | 11 | 'esri/map', 12 | 'esri/dijit/Scalebar', 13 | 'esri/layers/WebTiledLayer', 14 | 'esri/dijit/HomeButton', 15 | 'esri/dijit/LocateButton', 16 | 'esri/dijit/Geocoder', 17 | 'esri/arcgis/utils', 18 | 'esri/dijit/Legend', 19 | 20 | 'bootstrap-map-js/js/bootstrapmap', 21 | 22 | 'spin-js/spin', 23 | 24 | 'dojo/text!./templates/Map.html' 25 | ], function( 26 | declare, array, lang, domClass, topic, 27 | _WidgetBase, _TemplatedMixin, 28 | Map, Scalebar, WebTiledLayer, HomeButton, LocateButton, Geocoder, arcgisUtils, Legend, 29 | BootstrapMap, Spinner, 30 | template) { 31 | return declare([_WidgetBase, _TemplatedMixin], { 32 | templateString: template, 33 | 34 | postCreate: function() { 35 | this.inherited(arguments); 36 | // NOTE: BootstrapMap needs to work off an id 37 | this.mapNode.id = this.id + 'Map'; 38 | this._initMap(); 39 | }, 40 | 41 | _wireEvents: function() { 42 | // summary: 43 | // wire events 44 | // 45 | this.map.on('update-start', lang.hitch(this, 'showSpinner')); 46 | this.map.on('update-end', lang.hitch(this, 'hideSpinner')); 47 | }, 48 | 49 | showSpinner: function() { 50 | // summary: 51 | // sets up and shows the spinner 52 | // 53 | var opts = { 54 | lines: 9, // The number of lines to draw 55 | length: 4, // The length of each line 56 | width: 3, // The line thickness 57 | radius: 4, // The radius of the inner circle 58 | corners: 1, // Corner roundness (0..1) 59 | rotate: 0, // The rotation offset 60 | direction: 1, // 1: clockwise, -1: counterclockwise 61 | color: '#000', // #rgb or #rrggbb or array of colors 62 | speed: 1, // Rounds per second 63 | trail: 60, // Afterglow percentage 64 | shadow: false, // Whether to render a shadow 65 | hwaccel: true, // Whether to use hardware acceleration 66 | className: 'spinner', // The CSS class to assign to the spinner 67 | zIndex: 100, // The z-index (defaults to 2000000000) 68 | top: '50%', // Top position relative to parent in px 69 | left: '50%' // Left position relative to parent in px 70 | }; 71 | 72 | if(!this.spinner) { 73 | this.spinner = new Spinner(opts).spin(this.spinnerNode); 74 | } else { 75 | if(!this.spinner.el) { 76 | // only start spinner if not already started 77 | this.spinner.spin(this.spinnerNode); 78 | } 79 | } 80 | }, 81 | 82 | hideSpinner: function() { 83 | // summary: 84 | // hides the spinner 85 | // 86 | if( this.spinner ) { 87 | this.spinner.stop(); 88 | } 89 | }, 90 | 91 | // initalize map from configuration parameters 92 | _initMap: function() { 93 | if (!this.options) { 94 | this.options = {}; 95 | } 96 | 97 | this.showSpinner(); 98 | 99 | if (this.itemId) { 100 | BootstrapMap.createWebMap(this.itemId, this.mapNode.id, this.options).then(lang.hitch(this, '_onWebMapLoad')); 101 | } else { 102 | this.map = BootstrapMap.create(this.mapNode.id, this.options); 103 | this._wireEvents(); 104 | this._initLayers(); 105 | this._initWidgets(); 106 | } 107 | }, 108 | 109 | // set reference to the map 110 | // then show legend and map widgets 111 | _onWebMapLoad: function (response) { 112 | var self = this; 113 | var layerInfos; 114 | this.map = response.map; 115 | this._wireEvents(); 116 | if (this.legendNodeId) { 117 | layerInfos = arcgisUtils.getLegendLayers(response); 118 | if (this.map.loaded) { 119 | this._initLegend(layerInfos); 120 | } else { 121 | this.map.on('load',function(){ 122 | self._initLegend(layerInfos); 123 | }); 124 | } 125 | } 126 | this._initWidgets(); 127 | topic.publish('webmap/load', response); 128 | }, 129 | 130 | // init map layers from options instead of a web map 131 | _initLayers: function() { 132 | if (!this.operationalLayers) { 133 | return; 134 | } 135 | var layers = []; 136 | var layerTypes = { 137 | dynamic: 'ArcGISDynamicMapService', 138 | feature: 'Feature', 139 | tiled: 'ArcGISTiledMapService' 140 | }; 141 | // loading all the required modules first ensures the layer order is maintained 142 | var modules = []; 143 | array.forEach(this.operationalLayers, function(operationalLayer) { 144 | var type = layerTypes[operationalLayer.type]; 145 | if (type) { 146 | modules.push('esri/layers/' + type + 'Layer'); 147 | } else { 148 | console.log('Layer type not supported: ', operationalLayer.type); 149 | } 150 | }, this); 151 | require(modules, lang.hitch(this, function() { 152 | var layerInfos = []; 153 | array.forEach(this.operationalLayers, function(operationalLayer) { 154 | var type = layerTypes[operationalLayer.type]; 155 | if (type) { 156 | require(['esri/layers/' + type + 'Layer'], lang.hitch(this, '_initLayer', operationalLayer, layers, layerInfos)); 157 | } 158 | }, this); 159 | this.map.addLayers(layers); 160 | this._initLegend(layerInfos); 161 | })); 162 | }, 163 | 164 | _initLayer: function(operationalLayer, layers, layerInfos, LayerClass) { 165 | var l = new LayerClass(operationalLayer.url, operationalLayer.options); 166 | // unshift instead of push to keep layer ordering on map intact 167 | layers.unshift(l); 168 | layerInfos.unshift({ 169 | layer: l, 170 | title: operationalLayer.title || l.name 171 | }); 172 | }, 173 | 174 | _initLegend: function(layerInfos) { 175 | if (!this.legendNodeId) { 176 | return; 177 | } 178 | this.legend = new Legend({ 179 | map: this.map, 180 | layerInfos: layerInfos 181 | }, this.legendNodeId); 182 | this.legend.startup(); 183 | }, 184 | 185 | // init map widgets if they are in config 186 | _initWidgets: function() { 187 | var self = this; 188 | if (!this.widgets) { 189 | return; 190 | } 191 | 192 | // scalebar 193 | if (this.widgets.scalebar) { 194 | this.scalebar = new Scalebar(lang.mixin({ 195 | map: this.map, 196 | scalebarUnit: 'dual' 197 | }, this.widgets.scalebar)); 198 | } 199 | 200 | // home button 201 | if (this.widgets.homeButton) { 202 | this.homeButton = new HomeButton(lang.mixin({ 203 | map: this.map 204 | }, this.widgets.homeButton), this.homeNode); 205 | this.homeButton.startup(); 206 | } 207 | 208 | // locate button 209 | if (this.widgets.locateButton) { 210 | this.locateButton = new LocateButton(lang.mixin({ 211 | map: this.map, 212 | 'class': 'locate-button' 213 | }, this.widgets.locateButton), this.locateNode); 214 | this.locateButton.startup(); 215 | } 216 | 217 | // geocoder 218 | if (this.widgets.geocoder) { 219 | this.geocoder = new Geocoder(lang.mixin({ 220 | map: this.map, 221 | 'class': 'geocoder' 222 | }, this.widgets.geocoder), this.searchNode); 223 | this.geocoder.startup(); 224 | this.own(this.geocoder.on('select', function(e) { 225 | domClass.remove(self.geocoder.domNode, 'shown'); 226 | })); 227 | } 228 | }, 229 | 230 | getMapHeight: function() { 231 | if(this.map) { 232 | return this.map.height; 233 | } else { 234 | return 0; 235 | } 236 | }, 237 | 238 | clearBaseMap: function() { 239 | var map = this.map; 240 | if (map.basemapLayerIds && map.basemapLayerIds.length > 0) { 241 | array.forEach(map.basemapLayerIds, function(lid) { 242 | map.removeLayer(map.getLayer(lid)); 243 | }); 244 | map.basemapLayerIds = []; 245 | } else { 246 | map.removeLayer(map.getLayer(map.layerIds[0])); 247 | } 248 | }, 249 | 250 | setBasemap: function(basemapText) { 251 | var map = this.map; 252 | this.clearBaseMap(); 253 | switch (basemapText) { 254 | case 'Streets': 255 | map.setBasemap('streets'); 256 | break; 257 | case 'Imagery': 258 | map.setBasemap('hybrid'); 259 | break; 260 | case 'National Geographic': 261 | map.setBasemap('national-geographic'); 262 | break; 263 | case 'Topographic': 264 | map.setBasemap('topo'); 265 | break; 266 | case 'Gray': 267 | map.setBasemap('gray'); 268 | break; 269 | case 'Dark Gray': 270 | map.setBasemap('dark-gray'); 271 | break; 272 | case 'Open Street Map': 273 | map.setBasemap('osm'); 274 | break; 275 | } 276 | } 277 | }); 278 | }); -------------------------------------------------------------------------------- /src/app/mapping/templates/Map.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /src/app/resources/app.css: -------------------------------------------------------------------------------- 1 | /* top nav */ 2 | body { 3 | padding-top: 51px; 4 | } 5 | 6 | .navbar-toggle-icon { 7 | padding: 6px 10px; 8 | } 9 | 10 | .navbar-toggle-left { 11 | float: left; 12 | margin-left: 15px; 13 | margin-right: 5px; 14 | } 15 | 16 | .navbar-toggle .glyphicon { 17 | color: #fff; 18 | font-size: 14px; 19 | } 20 | 21 | /* no margins or padding around map */ 22 | .container-map, .container-map .col-map { 23 | padding-left: 0; 24 | padding-right: 0; 25 | } 26 | 27 | .container-map .row-map { 28 | margin-left: 0; 29 | margin-right: 0; 30 | } 31 | 32 | /* map widgets */ 33 | .HomeButton { 34 | position: absolute; 35 | top: 15px; 36 | left: 15px; 37 | z-index: 30; 38 | margin: 2px 0; /*needed to line up w/ geocoder */ 39 | } 40 | 41 | .locate-button { 42 | position: absolute; 43 | top: 15px; 44 | left: 55px; 45 | z-index: 30; 46 | margin: 2px 0; /*needed to line up w/ geocoder */ 47 | } 48 | 49 | .geocoder { 50 | display: block; 51 | position: absolute; 52 | z-index: 30; 53 | top: 15px; 54 | right: 15px; 55 | } 56 | 57 | .esriSimpleSliderBR { 58 | right: 15px; 59 | } 60 | 61 | /* override defaults set in bootstrapmap.css */ 62 | .simpleGeocoder .esriGeocoderContainer { 63 | width: 210px; 64 | } 65 | .esriSimpleSlider { 66 | top: auto; 67 | left: auto; 68 | } 69 | 70 | /* responsive styles for mobile */ 71 | @media (max-width:767px) { 72 | 73 | /* off canvas sidebar */ 74 | #sidebar { 75 | position: fixed; 76 | top: 51px; 77 | left: -260px; 78 | z-index: 100; 79 | font-size: small; 80 | width: 260px; 81 | /* 82 | default to iPhone 5 portrait height, but 83 | this will be overwritten by app 84 | */ 85 | height: 320px; 86 | overflow: auto; 87 | 88 | /* mimic col styles */ 89 | padding-left: 15px; 90 | padding-right: 15px; 91 | background-color: #efefef; 92 | 93 | -webkit-transition: left .5s ease-in-out; 94 | -moz-transition: left .5s ease-in-out; 95 | -o-transition: left .5s ease-in-out; 96 | transition: left .5s ease-in-out; 97 | } 98 | 99 | /* overlay sidebar */ 100 | .sidebar-open #sidebar { 101 | left: 0; 102 | } 103 | 104 | /* don't wrap title */ 105 | a.navbar-brand { 106 | white-space: nowrap; 107 | overflow-x: hidden; 108 | } 109 | 110 | /* hide scalebar */ 111 | .esriScalebar { 112 | display: none; 113 | } 114 | } 115 | 116 | @media (max-width:600px) { 117 | /* smaller title */ 118 | a.navbar-brand { 119 | padding-left: 5px; 120 | padding-right: 5px; 121 | max-width: 200px; 122 | } 123 | 124 | /* smaller attribution */ 125 | .esriSimpleSliderBR { 126 | bottom: 30px; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/app/resources/main.css: -------------------------------------------------------------------------------- 1 | @import url("../../esri/css/esri.css"); 2 | @import url("../../bootstrap-map-js/src/css/bootstrapmap.css"); 3 | @import url("app.css"); 4 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Dojo Bootstrap Map 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 37 |
38 |
39 | 40 |
41 |
42 |
43 | 44 |
45 | 49 |
50 |
51 |
52 | 53 | 56 | 57 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | --------------------------------------------------------------------------------