├── .gitignore ├── .vscode ├── extensions.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── app ├── _test.spec.ts ├── app.component.html ├── app.component.ts ├── app.module.ts ├── coordinate │ ├── coordinate.component.css │ ├── coordinate.component.html │ ├── coordinate.component.spec.ts │ └── coordinate.component.ts ├── main.ts ├── map.component.ts ├── map.service.ts ├── polyfills.ts └── vendor.ts ├── index.html ├── karma-test-main.js ├── karma-test-shim.js ├── karma.conf.js ├── package-lock.json ├── package.json ├── styles ├── esri-fonts.css └── main.css ├── tsconfig.json ├── tslint.json └── webpack ├── webpack.config.js └── webpack.karma.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # project 2 | dist/ 3 | 4 | node_modules/ 5 | bower_components/ 6 | _test-output/ 7 | app/**/*.js 8 | app/**/*.js.map 9 | typings/ 10 | npm-debug*.log 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 6 | "christian-kohler.path-intellisense", 7 | "eg2.tslint", 8 | "johnpapa.Angular2" 9 | ] 10 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/.git": true, 5 | "**/.DS_Store": true, 6 | "app/**/*.js": { 7 | "when": "$(basename).ts" 8 | }, 9 | "app/**/*.js.map": true 10 | }, 11 | // Control whether tslint is enabled for TypeScript files or not. 12 | "tslint.enable": true, 13 | // The number of spaces a tab is equal to. This setting is overriden based on the file contents when `editor.detectIndentation` is on. 14 | "editor.tabSize": 2, 15 | // Insert spaces when pressing Tab. This setting is overriden based on the file contents when `editor.detectIndentation` is on. 16 | "editor.insertSpaces": true, 17 | "editor.formatOnSave": true, 18 | "editor.formatOnType": true, 19 | "vsicons.presets.angular": true, 20 | "typescript.tsdk": "./node_modules/typescript/lib", 21 | "search.exclude": { 22 | "**/node_modules": true, 23 | "**/bower_components": true, 24 | "**/build": true, 25 | "**/dist": true 26 | } 27 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "tsc", 6 | "isShellCommand": true, 7 | "args": ["-p", "."], 8 | "showOutput": "silent", 9 | "problemMatcher": "$tsc" 10 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Josh Werts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular2-esri-play 2 | 3 | Angular 2 app w/ Typescript, Webpack and ESRI JSAPI 4.x. Includes custom coordinate component. 4 | 5 | [Demo](http://joshwerts.com/angular2-esri-play) 6 | 7 | ``` 8 | npm install 9 | npm start 10 | 11 | # Run unit tests 12 | npm test 13 | 14 | # Builds minimized version for production 15 | npm run build 16 | ``` -------------------------------------------------------------------------------- /app/_test.spec.ts: -------------------------------------------------------------------------------- 1 | let a = 0; 2 | 3 | describe('test spec', () => { 4 | it('should pass', () => { 5 | expect(true).toBeTruthy(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'my-app', 5 | template: require('./app.component.html') 6 | }) 7 | export class AppComponent implements OnInit { 8 | constructor() { 9 | 10 | } 11 | ngOnInit() { 12 | 13 | } 14 | 15 | onViewCreated() { 16 | console.log('view created'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { HttpModule } from '@angular/http'; 4 | 5 | import { AppComponent } from './app.component'; 6 | import { MapComponent } from './map.component'; 7 | import { MapService } from './map.service'; 8 | 9 | import { CoordinateComponent } from './coordinate/coordinate.component'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | BrowserModule, 14 | HttpModule 15 | ], 16 | declarations: [ 17 | AppComponent, 18 | MapComponent, 19 | CoordinateComponent 20 | ], 21 | bootstrap: [ 22 | AppComponent 23 | ], 24 | providers: [ 25 | MapService 26 | ] 27 | }) 28 | export class AppModule { 29 | constructor() { 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/coordinate/coordinate.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | position: absolute; 3 | bottom: 10px; 4 | left: 6px; 5 | z-index: 1; 6 | font-size: 0.90em; 7 | } 8 | #cordContainer > p { 9 | margin: 0; 10 | } 11 | #coordContainer > p:first-child { 12 | margin-top: 0; 13 | margin-bottom: 6px; 14 | } 15 | #scaleLine { 16 | color: white; 17 | text-shadow: black 0 0 5px, black 0 0 5px, black 0 0 5px; 18 | } 19 | #coordinatesLine { 20 | color: white; 21 | text-shadow: black 0 0 5px, black 0 0 5px, black 0 0 5px; 22 | } -------------------------------------------------------------------------------- /app/coordinate/coordinate.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | Level: {{mapView.zoom | number:'.0-0'}}      Scale: {{mapView.scale | number:'.0-0'}} 4 |

5 |

6 | Lat: {{longitude | number:'.6-6'}}      Lon: {{latitude | number:'.6-6'}} 7 |

8 |
-------------------------------------------------------------------------------- /app/coordinate/coordinate.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, ComponentFixture } from '@angular/core/testing'; 2 | import { DebugElement } from '@angular/core'; 3 | 4 | import { CoordinateComponent } from './coordinate.component'; 5 | 6 | let fixture: ComponentFixture; 7 | let component: CoordinateComponent; 8 | let de: DebugElement; 9 | let el: HTMLElement; 10 | let $mapDiv; 11 | let mapDivEl: Element; 12 | beforeEach(() => { 13 | // refine the test module by declaring the test component 14 | TestBed.configureTestingModule({ 15 | declarations: [CoordinateComponent] 16 | }); 17 | 18 | // create component and test fixture 19 | fixture = TestBed.createComponent(CoordinateComponent); 20 | el = fixture.nativeElement; 21 | 22 | // get test component from the fixture 23 | component = fixture.componentInstance; 24 | 25 | // add a div to the body 26 | $mapDiv = $('
', { id: 'mapDiv' }).appendTo('body'); 27 | mapDivEl = $mapDiv[0]; 28 | 29 | // mock required MapView props/functions 30 | component.mapView = { 31 | container: mapDivEl, 32 | toMap: (point) => { 33 | return { 34 | longitude: 5, 35 | latitude: 10 36 | }; 37 | }, 38 | zoom: 7, 39 | scale: 500 40 | }; 41 | 42 | // runs ngOnInit 43 | fixture.detectChanges(); 44 | }); 45 | 46 | describe('Coordinate', () => { 47 | it('should update lat/long on mousemove', () => { 48 | mapDivEl.dispatchEvent(new Event('mousemove', { 49 | bubbles: true, 50 | cancelable: true 51 | })); 52 | // pageX, pageY 53 | fixture.detectChanges(); 54 | expect(el.querySelector('#longitude').innerHTML).toBe('5.000000'); 55 | expect(el.querySelector('#latitude').innerHTML).toBe('10.000000'); 56 | expect(el.querySelector('#zoom').innerHTML).toBe('7'); 57 | expect(el.querySelector('#scale').innerHTML).toBe('500'); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /app/coordinate/coordinate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy, Input } from '@angular/core'; 2 | 3 | import MapView = require('esri/views/MapView'); 4 | import ScreenPoint = require('esri/geometry/ScreenPoint'); 5 | import Point = require('esri/geometry/Point'); 6 | 7 | @Component({ 8 | selector: 'coordinate', 9 | template: require('./coordinate.component.html'), 10 | styles: [require('./coordinate.component.css')] 11 | }) 12 | export class CoordinateComponent implements OnInit, OnDestroy { 13 | @Input('map-view') 14 | mapView: MapView; 15 | private mapViewElement: HTMLDivElement; 16 | 17 | private _longitude = 0; 18 | get longitude(): number { return this._longitude; } 19 | 20 | private _latitude = 0; 21 | get latitude(): number { return this._latitude; } 22 | 23 | constructor() { 24 | 25 | } 26 | 27 | ngOnInit() { 28 | this.mapViewElement = this.mapView.container; 29 | this.mapViewElement.addEventListener('mousemove', this); 30 | } 31 | 32 | ngOnDestroy() { 33 | if (this.mapViewElement) { 34 | this.mapViewElement.removeEventListener('mousemove', this); 35 | } 36 | } 37 | 38 | handleEvent(event: Event) { 39 | switch (event.type) { 40 | case 'mousemove': 41 | this.onMouseMove(event); 42 | break; 43 | } 44 | } 45 | 46 | private onMouseMove(e: MouseEvent) { 47 | let rect = this.mapViewElement.getBoundingClientRect(); 48 | let point = new ScreenPoint({ 49 | x: e.clientX - rect.left, 50 | y: e.clientY - rect.top 51 | }); 52 | let mapPoint: Point = this.mapView.toMap(point); 53 | if (mapPoint !== null) { 54 | this._longitude = mapPoint.longitude; 55 | this._latitude = mapPoint.latitude; 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /app/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { AppModule } from './app.module'; 3 | 4 | platformBrowserDynamic().bootstrapModule(AppModule); 5 | -------------------------------------------------------------------------------- /app/map.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, Output, EventEmitter } from '@angular/core'; 2 | import { MapService } from './map.service'; 3 | 4 | import MapView = require('esri/views/MapView'); 5 | import Point = require('esri/geometry/Point'); 6 | import SpatialReference = require('esri/geometry/SpatialReference'); 7 | 8 | @Component({ 9 | selector: 'esri-map', 10 | template: '
' 11 | }) 12 | export class MapComponent { 13 | 14 | @Output() 15 | viewCreated = new EventEmitter(); 16 | 17 | mapView: MapView; 18 | 19 | constructor(private mapService: MapService, 20 | private elementRef: ElementRef) { } 21 | 22 | ngOnInit() { 23 | this.mapView = new MapView({ 24 | container: this.elementRef.nativeElement.firstChild, 25 | map: this.mapService.map, 26 | center: new Point({ 27 | x: -82.44, 28 | y: 35.61, 29 | spatialReference: new SpatialReference({ wkid: 4326 }) 30 | }), 31 | zoom: 14 32 | }); 33 | this.viewCreated.next(this.mapView); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/map.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import Map = require('esri/Map'); 4 | 5 | @Injectable() 6 | export class MapService { 7 | map: Map; 8 | constructor() { 9 | this.map = new Map({ 10 | basemap: 'topo-vector' 11 | }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/es6'; 2 | import 'core-js/es7/reflect'; 3 | require('zone.js/dist/zone'); 4 | -------------------------------------------------------------------------------- /app/vendor.ts: -------------------------------------------------------------------------------- 1 | // Angular 2 2 | import '@angular/platform-browser'; 3 | import '@angular/platform-browser-dynamic'; 4 | import '@angular/core'; 5 | import '@angular/common'; 6 | import '@angular/http'; 7 | import '@angular/router'; 8 | 9 | import 'rxjs'; 10 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | joshwerts::angular2-esri-play 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | Loading... 15 |
16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /karma-test-main.js: -------------------------------------------------------------------------------- 1 | var tests = []; 2 | for (var file in window.__karma__.files) { 3 | if (window.__karma__.files.hasOwnProperty(file)) { 4 | //if (/\.spec\.ts/.test(file)) { 5 | if (/karma-test-shim\.js$/.test(file)) { 6 | tests.push(file); 7 | } 8 | } 9 | } 10 | 11 | window.dojoConfig = { 12 | packages: [{ 13 | name: "local", 14 | location: "/base" 15 | }, { 16 | name: "esri", 17 | location: "http://js.arcgis.com/4.5/esri" 18 | }, { 19 | name: "dojo", 20 | location: "http://js.arcgis.com/4.5/dojo" 21 | }, { 22 | name: "dojox", 23 | location: "http://js.arcgis.com/4.5/dojox" 24 | }, { 25 | name: "dijit", 26 | location: "http://js.arcgis.com/4.5/dijit" 27 | }, { 28 | name: 'moment', 29 | location: 'http://js.arcgis.com/4.5/moment', 30 | main: 'moment' 31 | }], 32 | async: false 33 | }; 34 | 35 | 36 | /** 37 | * This function must be defined and is called back by the dojo adapter 38 | * @returns {string} a list of dojo spec/test modules to register with your testing framework 39 | */ 40 | window.__karma__.dojoStart = function () { 41 | return tests; 42 | }; -------------------------------------------------------------------------------- /karma-test-shim.js: -------------------------------------------------------------------------------- 1 | Error.stackTraceLimit = Infinity; 2 | 3 | require('core-js/client/shim'); 4 | require('reflect-metadata'); 5 | 6 | require('ts-helpers'); 7 | 8 | require('zone.js/dist/zone'); 9 | require('zone.js/dist/long-stack-trace-zone'); 10 | require('zone.js/dist/proxy'); 11 | require('zone.js/dist/sync-test'); 12 | require('zone.js/dist/jasmine-patch'); 13 | require('zone.js/dist/async-test'); 14 | require('zone.js/dist/fake-async-test'); 15 | 16 | /* 17 | Ok, this is kinda crazy. We can use the the context method on 18 | require that webpack created in order to tell webpack 19 | what files we actually want to require or import. 20 | Below, context will be a function/object with file names as keys. 21 | using that regex we are saying look in client/app and find 22 | any file that ends with '.spec.ts' and get its path. By passing in true 23 | we say do this recursively 24 | */ 25 | var appContext = require.context('./', true, /\.spec\.ts/); 26 | 27 | // get all the files, for each file, call the context function 28 | // that will require the file and load it up here. Context will 29 | // loop and require those spec files here 30 | appContext.keys().forEach(appContext); 31 | 32 | // Select BrowserDomAdapter. 33 | // see https://github.com/AngularClass/angular2-webpack-starter/issues/124 34 | // Somewhere in the test setup 35 | var testing = require('@angular/core/testing'); 36 | var browser = require('@angular/platform-browser-dynamic/testing'); 37 | 38 | testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting()); -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var webpackConfig = require('./webpack/webpack.karma.config'); 2 | webpackConfig.devtool = 'inline-source-map'; 3 | 4 | module.exports = function (config) { 5 | var _config = { 6 | basePath: '', 7 | 8 | frameworks: ['jasmine', 'dojo'], 9 | 10 | files: [ 11 | // asset (HTML & CSS) paths loaded via Angular's component compiler 12 | // (these paths need to be rewritten, see proxies section) 13 | { 14 | pattern: './app/**/*.html', 15 | included: false, 16 | watched: false 17 | }, { 18 | pattern: './app/**/*.css', 19 | included: false, 20 | watched: false 21 | }, 22 | 23 | // paths for debugging with source maps in dev tools 24 | { 25 | pattern: './app/**/*.ts', 26 | included: false, 27 | watched: false 28 | }, 29 | 30 | { 31 | pattern: 'karma-test-main.js', 32 | watched: false, 33 | included: true 34 | }, 35 | { 36 | pattern: 'karma-test-shim.js', 37 | included: false, 38 | watched: false 39 | } 40 | ], 41 | 42 | preprocessors: { 43 | './karma-test-shim.js': ['webpack', 'sourcemap'] 44 | }, 45 | 46 | webpack: webpackConfig, 47 | 48 | webpackMiddleware: { 49 | // stats: 'errors-only' 50 | stats: { 51 | colors: true 52 | }, 53 | // display no info to console (only warnings and errors) 54 | noInfo: false, 55 | }, 56 | 57 | webpackServer: { 58 | noInfo: true 59 | }, 60 | 61 | // level of logging 62 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 63 | //logLevel: config.LOG_DEBUG, 64 | logLevel: config.LOG_INFO, 65 | 66 | reporters: ['progress'], 67 | port: 9876, 68 | colors: true, 69 | autoWatch: true, 70 | browsers: ['Chrome'], 71 | singleRun: false, 72 | 73 | browserNoActivityTimeout: 100000 74 | }; 75 | 76 | config.set(_config); 77 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-esri-play", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "npm run build & webpack-dev-server --config ./webpack/webpack.config.js --inline --progress --port 8080", 7 | "build": "webpack --config ./webpack/webpack.config.js --progress --colors", 8 | "dist": "webpack --config ./webpack/webpack.config.js --progress --colors --env.minimize", 9 | "watch": "webpack --config ./webpack/webpack.config.js --watch", 10 | "webpack": "webpack --config ./webpack/webpack.config.js", 11 | "test": "karma start" 12 | }, 13 | "keywords": [], 14 | "author": "joshwerts@gmail.com", 15 | "license": "MIT", 16 | "dependencies": { 17 | "@angular/common": "4.4.4", 18 | "@angular/compiler": "4.4.4", 19 | "@angular/core": "4.4.4", 20 | "@angular/forms": "4.4.4", 21 | "@angular/http": "4.4.4", 22 | "@angular/platform-browser": "4.4.4", 23 | "@angular/platform-browser-dynamic": "4.4.4", 24 | "@angular/router": "4.4.4", 25 | "bootstrap": "3.3.7", 26 | "core-js": "2.5.1", 27 | "jquery": "3.2.1", 28 | "reflect-metadata": "0.1.10", 29 | "rxjs": "5.4.2", 30 | "zone.js": "0.8.17" 31 | }, 32 | "devDependencies": { 33 | "@types/arcgis-js-api": "4.4.0", 34 | "@types/core-js": "0.9.35", 35 | "@types/dojo": "^1.9.38", 36 | "@types/jasmine": "2.5.38", 37 | "@types/jquery": "3.2.12", 38 | "@types/node": "7.0.5", 39 | "canonical-path": "0.0.2", 40 | "css-loader": "0.28.7", 41 | "html-loader": "0.5.1", 42 | "jasmine-core": "2.8.0", 43 | "json-loader": "0.5.7", 44 | "karma": "1.7.1", 45 | "karma-chrome-launcher": "2.2.0", 46 | "karma-cli": "1.0.1", 47 | "karma-dojo": "0.0.1", 48 | "karma-jasmine": "1.1.0", 49 | "karma-sourcemap-loader": "0.3.7", 50 | "karma-spec-reporter": "0.0.31", 51 | "karma-webpack": "2.0.4", 52 | "lodash": "4.17.4", 53 | "rimraf": "2.6.2", 54 | "style-loader": "^0.18.2", 55 | "to-string-loader": "1.1.5", 56 | "ts-helpers": "1.1.2", 57 | "ts-loader": "2.3.7", 58 | "tslint": "5.7.0", 59 | "typescript": "2.5.3", 60 | "webpack": "3.6.0", 61 | "webpack-dev-server": "2.9.1" 62 | }, 63 | "repository": {} 64 | } 65 | -------------------------------------------------------------------------------- /styles/esri-fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'CalciteWebCoreIcons'; 3 | src: 4 | url('https://raw.githubusercontent.com/Esri/arcgis-js-api/master/themes/calcite/icons/fonts/CalciteWebCoreIcons.ttf?jpfyfr') format('truetype'), 5 | url('https://raw.githubusercontent.com/Esri/arcgis-js-api/master/themes/calcite/icons/fonts/CalciteWebCoreIcons.woff?jpfyfr') format('woff'), 6 | url('https://raw.githubusercontent.com/Esri/arcgis-js-api/master/themes/calcite/icons/fonts/CalciteWebCoreIcons.svg?jpfyfr#CalciteWebCoreIcons') format('svg'); 7 | font-weight: normal; 8 | font-style: normal; 9 | } 10 | 11 | [class^="esri-icon-"], [class*=" esri-icon-"] { 12 | /* use !important to prevent issues with browser extensions that change fonts */ 13 | font-family: 'CalciteWebCoreIcons' !important; 14 | speak: none; 15 | font-style: normal; 16 | font-weight: normal; 17 | font-variant: normal; 18 | text-transform: none; 19 | line-height: 1; 20 | 21 | /* Better Font Rendering =========== */ 22 | -webkit-font-smoothing: antialiased; 23 | -moz-osx-font-smoothing: grayscale; 24 | } 25 | 26 | .esri-icon-close:before { 27 | content: "\e600"; 28 | } 29 | .esri-icon-drag-horizontal:before { 30 | content: "\e601"; 31 | } 32 | .esri-icon-drag-vertical:before { 33 | content: "\e602"; 34 | } 35 | .esri-icon-handle-horizontal:before { 36 | content: "\e603"; 37 | } 38 | .esri-icon-handle-vertical:before { 39 | content: "\e604"; 40 | } 41 | .esri-icon-check-mark:before { 42 | content: "\e605"; 43 | } 44 | .esri-icon-left-triangle-arrow:before { 45 | content: "\e606"; 46 | } 47 | .esri-icon-right-triangle-arrow:before { 48 | content: "\e607"; 49 | } 50 | .esri-icon-down-arrow:before { 51 | content: "\e608"; 52 | } 53 | .esri-icon-up-arrow:before { 54 | content: "\e609"; 55 | } 56 | .esri-icon-overview-arrow-bottom-left:before { 57 | content: "\e60a"; 58 | } 59 | .esri-icon-overview-arrow-bottom-right:before { 60 | content: "\e60b"; 61 | } 62 | .esri-icon-overview-arrow-top-left:before { 63 | content: "\e60c"; 64 | } 65 | .esri-icon-overview-arrow-top-right:before { 66 | content: "\e60d"; 67 | } 68 | .esri-icon-maximize:before { 69 | content: "\e60e"; 70 | } 71 | .esri-icon-minimize:before { 72 | content: "\e60f"; 73 | } 74 | .esri-icon-checkbox-unchecked:before { 75 | content: "\e610"; 76 | } 77 | .esri-icon-checkbox-checked:before { 78 | content: "\e611"; 79 | } 80 | .esri-icon-radio-unchecked:before { 81 | content: "\e612"; 82 | } 83 | .esri-icon-radio-checked:before { 84 | content: "\e613"; 85 | } 86 | .esri-icon-up-arrow-circled:before { 87 | content: "\e614"; 88 | } 89 | .esri-icon-down-arrow-circled:before { 90 | content: "\e615"; 91 | } 92 | .esri-icon-left-arrow-circled:before { 93 | content: "\e616"; 94 | } 95 | .esri-icon-right-arrow-circled:before { 96 | content: "\e617"; 97 | } 98 | .esri-icon-zoom-out-fixed:before { 99 | content: "\e618"; 100 | } 101 | .esri-icon-zoom-in-fixed:before { 102 | content: "\e619"; 103 | } 104 | .esri-icon-refresh:before { 105 | content: "\e61a"; 106 | } 107 | .esri-icon-edit:before { 108 | content: "\e61b"; 109 | } 110 | .esri-icon-authorize:before { 111 | content: "\e61c"; 112 | } 113 | .esri-icon-map-pin:before { 114 | content: "\e61d"; 115 | } 116 | .esri-icon-blank-map-pin:before { 117 | content: "\e61e"; 118 | } 119 | .esri-icon-table:before { 120 | content: "\e61f"; 121 | } 122 | .esri-icon-plus:before { 123 | content: "\e620"; 124 | } 125 | .esri-icon-minus:before { 126 | content: "\e621"; 127 | } 128 | .esri-icon-beginning:before { 129 | content: "\e622"; 130 | } 131 | .esri-icon-reverse:before { 132 | content: "\e623"; 133 | } 134 | .esri-icon-pause:before { 135 | content: "\e624"; 136 | } 137 | .esri-icon-play:before { 138 | content: "\e625"; 139 | } 140 | .esri-icon-forward:before { 141 | content: "\e626"; 142 | } 143 | .esri-icon-end:before { 144 | content: "\e627"; 145 | } 146 | .esri-icon-erase:before { 147 | content: "\e628"; 148 | } 149 | .esri-icon-up-down-arrows:before { 150 | content: "\e629"; 151 | } 152 | .esri-icon-left:before { 153 | content: "\e62a"; 154 | } 155 | .esri-icon-right:before { 156 | content: "\e62b"; 157 | } 158 | .esri-icon-announcement:before { 159 | content: "\e62c"; 160 | } 161 | .esri-icon-notice-round:before { 162 | content: "\e62d"; 163 | } 164 | .esri-icon-notice-triangle:before { 165 | content: "\e62e"; 166 | } 167 | .esri-icon-home:before { 168 | content: "\e62f"; 169 | } 170 | .esri-icon-locate:before { 171 | content: "\e630"; 172 | } 173 | .esri-icon-expand:before { 174 | content: "\e631"; 175 | } 176 | .esri-icon-collapse:before { 177 | content: "\e632"; 178 | } 179 | .esri-icon-layer-list:before { 180 | content: "\e633"; 181 | } 182 | .esri-icon-basemap:before { 183 | content: "\e634"; 184 | } 185 | .esri-icon-globe:before { 186 | content: "\e635"; 187 | } 188 | .esri-icon-applications:before { 189 | content: "\e636"; 190 | } 191 | .esri-icon-arrow-up-circled:before { 192 | content: "\e637"; 193 | } 194 | .esri-icon-arrow-down-circled:before { 195 | content: "\e638"; 196 | } 197 | .esri-icon-arrow-left-circled:before { 198 | content: "\e639"; 199 | } 200 | .esri-icon-arrow-right-circled:before { 201 | content: "\e63a"; 202 | } 203 | .esri-icon-minus-circled:before { 204 | content: "\e63b"; 205 | } 206 | .esri-icon-plus-circled:before { 207 | content: "\e63c"; 208 | } 209 | .esri-icon-add-attachment:before { 210 | content: "\e63d"; 211 | } 212 | .esri-icon-attachment:before { 213 | content: "\e63e"; 214 | } 215 | .esri-icon-calendar:before { 216 | content: "\e63f"; 217 | } 218 | .esri-icon-close-circled:before { 219 | content: "\e640"; 220 | } 221 | .esri-icon-browser:before { 222 | content: "\e641"; 223 | } 224 | .esri-icon-collection:before { 225 | content: "\e642"; 226 | } 227 | .esri-icon-comment:before { 228 | content: "\e643"; 229 | } 230 | .esri-icon-configure-popup:before { 231 | content: "\e644"; 232 | } 233 | .esri-icon-contact:before { 234 | content: "\e645"; 235 | } 236 | .esri-icon-dashboard:before { 237 | content: "\e646"; 238 | } 239 | .esri-icon-deny:before { 240 | content: "\e647"; 241 | } 242 | .esri-icon-description:before { 243 | content: "\e648"; 244 | } 245 | .esri-icon-directions:before { 246 | content: "\e649"; 247 | } 248 | .esri-icon-directions2:before { 249 | content: "\e64a"; 250 | } 251 | .esri-icon-documentation:before { 252 | content: "\e64b"; 253 | } 254 | .esri-icon-duplicate:before { 255 | content: "\e64c"; 256 | } 257 | .esri-icon-review:before { 258 | content: "\e64d"; 259 | } 260 | .esri-icon-environment-settings:before { 261 | content: "\e64e"; 262 | } 263 | .esri-icon-error:before { 264 | content: "\e64f"; 265 | } 266 | .esri-icon-error2:before { 267 | content: "\e650"; 268 | } 269 | .esri-icon-experimental:before { 270 | content: "\e651"; 271 | } 272 | .esri-icon-feature-layer:before { 273 | content: "\e652"; 274 | } 275 | .esri-icon-filter:before { 276 | content: "\e653"; 277 | } 278 | .esri-icon-grant:before { 279 | content: "\e654"; 280 | } 281 | .esri-icon-group:before { 282 | content: "\e655"; 283 | } 284 | .esri-icon-key:before { 285 | content: "\e656"; 286 | } 287 | .esri-icon-labels:before { 288 | content: "\e657"; 289 | } 290 | .esri-icon-tag:before { 291 | content: "\e658"; 292 | } 293 | .esri-icon-layers:before { 294 | content: "\e659"; 295 | } 296 | .esri-icon-left-arrow:before { 297 | content: "\e65a"; 298 | } 299 | .esri-icon-right-arrow:before { 300 | content: "\e65b"; 301 | } 302 | .esri-icon-link-external:before { 303 | content: "\e65c"; 304 | } 305 | .esri-icon-link:before { 306 | content: "\e65d"; 307 | } 308 | .esri-icon-loading-indicator:before { 309 | content: "\e65e"; 310 | } 311 | .esri-icon-maps:before { 312 | content: "\e65f"; 313 | } 314 | .esri-icon-marketplace:before { 315 | content: "\e660"; 316 | } 317 | .esri-icon-media:before { 318 | content: "\e661"; 319 | } 320 | .esri-icon-media2:before { 321 | content: "\e662"; 322 | } 323 | .esri-icon-menu:before { 324 | content: "\e663"; 325 | } 326 | .esri-icon-mobile:before { 327 | content: "\e664"; 328 | } 329 | .esri-icon-phone:before { 330 | content: "\e665"; 331 | } 332 | .esri-icon-navigation:before { 333 | content: "\e666"; 334 | } 335 | .esri-icon-pan:before { 336 | content: "\e667"; 337 | } 338 | .esri-icon-printer:before { 339 | content: "\e668"; 340 | } 341 | .esri-icon-pie-chart:before { 342 | content: "\e669"; 343 | } 344 | .esri-icon-chart:before { 345 | content: "\e66a"; 346 | } 347 | .esri-icon-line-chart:before { 348 | content: "\e66b"; 349 | } 350 | .esri-icon-question:before { 351 | content: "\e66c"; 352 | } 353 | .esri-icon-resend-invitation:before { 354 | content: "\e66d"; 355 | } 356 | .esri-icon-rotate:before { 357 | content: "\e66e"; 358 | } 359 | .esri-icon-save:before { 360 | content: "\e66f"; 361 | } 362 | .esri-icon-settings:before { 363 | content: "\e670"; 364 | } 365 | .esri-icon-settings2:before { 366 | content: "\e671"; 367 | } 368 | .esri-icon-share:before { 369 | content: "\e672"; 370 | } 371 | .esri-icon-sign-out:before { 372 | content: "\e673"; 373 | } 374 | .esri-icon-support:before { 375 | content: "\e674"; 376 | } 377 | .esri-icon-user:before { 378 | content: "\e675"; 379 | } 380 | .esri-icon-time-clock:before { 381 | content: "\e676"; 382 | } 383 | .esri-icon-trash:before { 384 | content: "\e677"; 385 | } 386 | .esri-icon-upload:before { 387 | content: "\e678"; 388 | } 389 | .esri-icon-download:before { 390 | content: "\e679"; 391 | } 392 | .esri-icon-zoom-in-magnifying-glass:before { 393 | content: "\e67a"; 394 | } 395 | .esri-icon-search:before { 396 | content: "\e67b"; 397 | } 398 | .esri-icon-zoom-out-magnifying-glass:before { 399 | content: "\e67c"; 400 | } 401 | .esri-icon-locked:before { 402 | content: "\e67d"; 403 | } 404 | .esri-icon-unlocked:before { 405 | content: "\e67e"; 406 | } 407 | .esri-icon-favorites:before { 408 | content: "\e67f"; 409 | } 410 | .esri-icon-compass:before { 411 | content: "\e680"; 412 | } 413 | .esri-icon-down:before { 414 | content: "\e681"; 415 | } 416 | .esri-icon-up:before { 417 | content: "\e682"; 418 | } 419 | .esri-icon-chat:before { 420 | content: "\e683"; 421 | } 422 | .esri-icon-dock-bottom:before { 423 | content: "\e684"; 424 | } 425 | .esri-icon-dock-left:before { 426 | content: "\e685"; 427 | } 428 | .esri-icon-dock-right:before { 429 | content: "\e686"; 430 | } 431 | .esri-icon-organization:before { 432 | content: "\e687"; 433 | } 434 | .esri-icon-north-navigation:before { 435 | content: "\e900"; 436 | } 437 | .esri-icon-locate-circled:before { 438 | content: "\e901"; 439 | } 440 | .esri-icon-dial:before { 441 | content: "\e902"; 442 | } -------------------------------------------------------------------------------- /styles/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | height: 100%; 5 | width: 100%; 6 | } 7 | 8 | .container-fluid { 9 | padding: 0; 10 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "amd", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "removeComments": false, 10 | "noImplicitAny": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | "typings/main", 16 | "typings/main.d.ts" 17 | ] 18 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "max-line-length": [ 17 | true, 18 | 140 19 | ], 20 | "member-access": false, 21 | "member-ordering": [ 22 | true, 23 | "static-before-instance", 24 | "variables-before-functions" 25 | ], 26 | "no-arg": true, 27 | "no-bitwise": true, 28 | "no-console": [ 29 | true, 30 | "debug", 31 | "info", 32 | "time", 33 | "timeEnd", 34 | "trace" 35 | ], 36 | "no-construct": true, 37 | "no-debugger": true, 38 | "no-duplicate-variable": true, 39 | "no-empty": false, 40 | "no-eval": true, 41 | "no-inferrable-types": true, 42 | "no-shadowed-variable": true, 43 | "no-string-literal": false, 44 | "no-switch-case-fall-through": true, 45 | "no-trailing-whitespace": true, 46 | "no-unused-expression": true, 47 | "no-unused-variable": true, 48 | "no-use-before-declare": true, 49 | "no-var-keyword": true, 50 | "object-literal-sort-keys": false, 51 | "one-line": [ 52 | true, 53 | "check-open-brace", 54 | "check-catch", 55 | "check-else", 56 | "check-whitespace" 57 | ], 58 | "quotemark": [ 59 | true, 60 | "single" 61 | ], 62 | "radix": true, 63 | "semicolon": [ 64 | "always" 65 | ], 66 | "triple-equals": [ 67 | true, 68 | "allow-null-check" 69 | ], 70 | "typedef-whitespace": [ 71 | true, 72 | { 73 | "call-signature": "nospace", 74 | "index-signature": "nospace", 75 | "parameter": "nospace", 76 | "property-declaration": "nospace", 77 | "variable-declaration": "nospace" 78 | } 79 | ], 80 | "variable-name": false, 81 | "whitespace": [ 82 | true, 83 | "check-branch", 84 | "check-decl", 85 | "check-operator", 86 | "check-separator", 87 | "check-type" 88 | ] 89 | } 90 | } -------------------------------------------------------------------------------- /webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require("webpack"); 3 | 4 | // Helper functions 5 | function root(args) { 6 | args = Array.prototype.slice.call(arguments, 0); 7 | return path.join.apply(path, [__dirname].concat(args)); 8 | } 9 | 10 | module.exports = function (env) { 11 | var minimize = false; 12 | var htmlLoaderOptions = {}; 13 | if (env) { 14 | minimize = env.minimize; 15 | htmlLoaderOptions = { 16 | minimize: false 17 | }; 18 | } 19 | var config = { 20 | entry: { 21 | polyfills: './app/polyfills.ts', 22 | main: './app/main.ts', 23 | vendor: './app/vendor.ts' 24 | }, 25 | output: { 26 | filename: './dist/[name].bundle.js', 27 | publicPath: './', 28 | libraryTarget: "amd" 29 | }, 30 | resolve: { 31 | extensions: ['.ts', '.tsx', '.js', 'json'] 32 | }, 33 | module: { 34 | rules: [ 35 | // typescript 36 | { 37 | test: /\.tsx?$/, 38 | use: 'ts-loader' 39 | }, 40 | // css 41 | { 42 | test: /\.css$/, 43 | use: ["to-string-loader", "css-loader"] 44 | }, 45 | // html 46 | { 47 | test: /\.html$/, 48 | use: [ 49 | { 50 | loader: 'html-loader', 51 | options: htmlLoaderOptions 52 | } 53 | ] 54 | }, 55 | // images 56 | { 57 | test: /\.(jpe?g|gif|png)$/, 58 | use: 'file-loader?emitFile=false&name=[path][name].[ext]' 59 | } 60 | ] 61 | }, 62 | plugins: [ 63 | // https://github.com/angular/angular/issues/11580 64 | new webpack.ContextReplacementPlugin( 65 | // The (\\|\/) piece accounts for path separators in *nix and Windows 66 | /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, 67 | root('./src') // location of your src 68 | ), 69 | 70 | new webpack.ProvidePlugin({ 71 | jQuery: 'jquery', 72 | $: 'jquery', 73 | jquery: 'jquery' 74 | }), 75 | 76 | new webpack.optimize.CommonsChunkPlugin({ 77 | name: ['app', 'vendor', 'polyfills'], 78 | minChunks: Infinity 79 | }) 80 | ], 81 | externals: [ 82 | function (context, request, callback) { 83 | if (/^dojo/.test(request) || 84 | /^dojox/.test(request) || 85 | /^dijit/.test(request) || 86 | /^esri/.test(request) 87 | ) { 88 | return callback(null, "amd " + request); 89 | } 90 | callback(); 91 | } 92 | ], 93 | devtool: 'source-map' 94 | }; 95 | 96 | if (minimize) { 97 | config.plugins.push(new webpack.optimize.UglifyJsPlugin({ 98 | beautify: false, //prod 99 | sourceMap: true, 100 | output: { 101 | comments: false 102 | }, //prod 103 | mangle: { 104 | except: ['$', 'require'], 105 | screw_ie8: true, 106 | keep_fnames: true 107 | }, //prod 108 | compress: { 109 | screw_ie8: true, 110 | warnings: false, 111 | conditionals: true, 112 | unused: true, 113 | comparisons: true, 114 | sequences: true, 115 | dead_code: true, 116 | evaluate: true, 117 | if_return: true, 118 | join_vars: true, 119 | negate_iife: false // we need this for lazy v8 120 | } 121 | })); 122 | } 123 | 124 | return config; 125 | } -------------------------------------------------------------------------------- /webpack/webpack.karma.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require("webpack"); 3 | 4 | // Helper functions 5 | function root(args) { 6 | args = Array.prototype.slice.call(arguments, 0); 7 | return path.join.apply(path, [__dirname].concat(args)); 8 | } 9 | 10 | module.exports = { 11 | devtool: 'source-map', 12 | output: { 13 | path: "/", 14 | libraryTarget: "amd" 15 | }, 16 | resolve: { 17 | extensions: ['.ts', '.tsx', '.js', 'json'] 18 | }, 19 | module: { 20 | rules: [ 21 | // typescript 22 | { 23 | test: /\.tsx?$/, 24 | use: 'ts-loader' 25 | }, 26 | // css 27 | { 28 | test: /\.css$/, 29 | use: ["to-string-loader", "css-loader"] 30 | }, 31 | // html 32 | { 33 | test: /\.html$/, 34 | use: 'html-loader' 35 | }, 36 | // images 37 | { 38 | test: /\.(jpe?g|gif|png)$/, 39 | use: 'file-loader?emitFile=false&name=[path][name].[ext]' 40 | } 41 | ] 42 | }, 43 | plugins: [ 44 | // https://github.com/angular/angular/issues/11580 45 | new webpack.ContextReplacementPlugin( 46 | // The (\\|\/) piece accounts for path separators in *nix and Windows 47 | /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, 48 | root('./app') // location of your src 49 | ), 50 | 51 | new webpack.ProvidePlugin({ 52 | jQuery: 'jquery', 53 | $: 'jquery', 54 | jquery: 'jquery' 55 | }), 56 | ], 57 | externals: [ 58 | function (context, request, callback) { 59 | if (/^dojo/.test(request) || 60 | /^dojox/.test(request) || 61 | /^dijit/.test(request) || 62 | /^esri/.test(request) 63 | ) { 64 | console.log(context + ": " + request); 65 | return callback(null, "amd " + request); 66 | } 67 | callback(); 68 | } 69 | ] 70 | }; --------------------------------------------------------------------------------