├── .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 | };
--------------------------------------------------------------------------------