├── .gitignore
├── LICENSE
├── README.md
├── config.xml
├── ionic.config.json
├── package.json
├── src
├── app
│ ├── app.component.ts
│ ├── app.html
│ ├── app.module.ts
│ ├── app.scss
│ └── main.ts
├── components
│ └── multi-image-upload
│ │ ├── multi-image-upload.html
│ │ ├── multi-image-upload.module.ts
│ │ ├── multi-image-upload.scss
│ │ └── multi-image-upload.ts
├── declarations.d.ts
├── index.html
├── manifest.json
├── service-worker.js
└── theme
│ └── variables.scss
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Specifies intentionally untracked files to ignore when using Git
2 | # http://git-scm.com/docs/gitignore
3 |
4 | *~
5 | *.sw[mnpcod]
6 | *.log
7 | *.tmp
8 | *.tmp.*
9 | log.txt
10 | *.sublime-project
11 | *.sublime-workspace
12 | .vscode/
13 | npm-debug.log*
14 |
15 | .idea/
16 | .sass-cache/
17 | .tmp/
18 | .versions/
19 | coverage/
20 | dist/
21 | node_modules/
22 | tmp/
23 | temp/
24 | hooks/
25 | platforms/
26 | plugins/
27 | plugins/android.json
28 | plugins/ios.json
29 | www/
30 | $RECYCLE.BIN/
31 |
32 | .DS_Store
33 | Thumbs.db
34 | UserInterfaceState.xcuserstate
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Kevin Wang
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ionic2-multi-image-upload
2 | ===========================
3 |
4 | * Supports browser / Android / iOS (from camera or photo library).
5 | * Preview images before uploading, add or delete photos with ease.
6 | * Spontaneous uploading of multiple images, notifies server reply of all requests when all are finished.
7 | * Shows a progress bar and a spin while uploading, shows a checkmark when finished.
8 | * Lets the user decide whether to retry or abort upon failure.
9 |
10 | # Demo
11 | [Check out the demo in your browser!](http://rawgit.com/KevinWang15/ionic2-multi-image-upload/demo/)
12 |
13 | # Dependencies
14 |
15 | * https://ionicframework.com/docs/native/transfer/
16 | * https://ionicframework.com/docs/native/camera/
17 | * https://ionicframework.com/docs/native/file/
18 | * https://ionicframework.com/docs/native/file-path/
19 |
20 | # Installation
21 |
22 | At its current stage, it is recommended that you copy ```src/components/multi-image-upload``` to your own project and modify it as you need to.
23 |
24 | Configuration API and npm package coming soon.
25 |
--------------------------------------------------------------------------------
/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ionic2-multi-image-upload
4 | An awesome Ionic/Cordova app.
5 | Ionic Framework Team
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/ionic.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ionic2-multi-image-upload",
3 | "app_id": "",
4 | "v2": true,
5 | "typescript": true
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ionic2-multi-image-upload",
3 | "version": "0.0.0",
4 | "author": "Ionic Framework",
5 | "homepage": "http://ionicframework.com/",
6 | "private": true,
7 | "scripts": {
8 | "clean": "ionic-app-scripts clean",
9 | "build": "ionic-app-scripts build",
10 | "ionic:build": "ionic-app-scripts build",
11 | "ionic:serve": "ionic-app-scripts serve"
12 | },
13 | "dependencies": {
14 | "@angular/common": "4.0.0",
15 | "@angular/compiler": "4.0.0",
16 | "@angular/compiler-cli": "4.0.0",
17 | "@angular/core": "4.0.0",
18 | "@angular/forms": "4.0.0",
19 | "@angular/http": "4.0.0",
20 | "@angular/platform-browser": "4.0.0",
21 | "@angular/platform-browser-dynamic": "4.0.0",
22 | "@ionic-native/camera": "^3.5.0",
23 | "@ionic-native/core": "3.4.2",
24 | "@ionic-native/file": "^3.5.0",
25 | "@ionic-native/file-path": "^3.5.0",
26 | "@ionic-native/splash-screen": "3.4.2",
27 | "@ionic-native/status-bar": "3.4.2",
28 | "@ionic-native/transfer": "^3.5.0",
29 | "@ionic/storage": "2.0.1",
30 | "ionic-angular": "3.0.1",
31 | "ionicons": "3.0.0",
32 | "rxjs": "5.1.1",
33 | "sw-toolbox": "3.4.0",
34 | "zone.js": "^0.8.4"
35 | },
36 | "devDependencies": {
37 | "@ionic/app-scripts": "1.3.0",
38 | "typescript": "~2.2.1"
39 | },
40 | "cordovaPlugins": [
41 | "cordova-plugin-whitelist",
42 | "cordova-plugin-console",
43 | "cordova-plugin-statusbar",
44 | "cordova-plugin-device",
45 | "ionic-plugin-keyboard",
46 | "cordova-plugin-splashscreen"
47 | ],
48 | "cordovaPlatforms": [],
49 | "description": "ionic2-multi-image-upload: An Ionic project"
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ViewChild} from '@angular/core';
2 | import {MultiImageUpload} from "../components/multi-image-upload/multi-image-upload";
3 | import {AlertController, ToastController} from "ionic-angular";
4 |
5 | @Component({
6 | templateUrl: 'app.html'
7 | })
8 | export class Ionic2MultiImageUploadDemo {
9 | @ViewChild(MultiImageUpload) multiImageUpload: MultiImageUpload;
10 | protected uploadFinished = false;
11 |
12 |
13 | constructor(private alertCtrl: AlertController, private toastCtrl: ToastController) {
14 |
15 | }
16 |
17 | protected submit() {
18 | if (this.multiImageUpload.images.length == 0) {
19 | this.showToast("Please select at least 1 photo");
20 | return;
21 | }
22 |
23 | this.multiImageUpload.uploadImages().then((images) => {
24 | this.showToast("Upload successful, view console for details");
25 | this.uploadFinished = true;
26 | console.dir(images);
27 | }).catch(() => {
28 | });
29 | }
30 |
31 | protected cancel() {
32 | this.confirm('Are you sure to cancel?').then(value => {
33 | if (value) {
34 | this.multiImageUpload.abort();
35 | }
36 | })
37 | }
38 |
39 | private showToast(text: string) {
40 | this.toastCtrl.create({
41 | message: text,
42 | duration: 5000,
43 | position: 'bottom'
44 | }).present();
45 | }
46 |
47 | private confirm(text: string) {
48 | return new Promise(
49 | (resolve) => {
50 | this.alertCtrl.create({
51 | message: text,
52 | buttons: [
53 | {
54 | text: "No",
55 | role: 'cancel',
56 | handler: () => {
57 | resolve(false);
58 | }
59 | },
60 | {
61 | text: "Yes",
62 | handler: () => {
63 | resolve(true);
64 | }
65 | }
66 | ]
67 | }).present();
68 | }
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/app/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | multi-image-upload demo
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, ErrorHandler} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {IonicApp, IonicModule, IonicErrorHandler} from 'ionic-angular';
4 | import {Ionic2MultiImageUploadDemo} from './app.component';
5 |
6 | import {StatusBar} from '@ionic-native/status-bar';
7 | import {SplashScreen} from '@ionic-native/splash-screen';
8 | import {MultiImageUpload} from "../components/multi-image-upload/multi-image-upload";
9 |
10 | @NgModule({
11 | declarations: [
12 | Ionic2MultiImageUploadDemo,
13 | MultiImageUpload
14 | ],
15 |
16 | imports: [
17 | BrowserModule,
18 | IonicModule.forRoot(Ionic2MultiImageUploadDemo)
19 | ],
20 | bootstrap: [IonicApp],
21 | entryComponents: [
22 | Ionic2MultiImageUploadDemo
23 | ],
24 | providers: [
25 | StatusBar,
26 | SplashScreen,
27 | {provide: ErrorHandler, useClass: IonicErrorHandler},
28 | MultiImageUpload
29 | ]
30 | })
31 | export class AppModule {
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/app.scss:
--------------------------------------------------------------------------------
1 | button {
2 | ion-icon {
3 | margin-right: 10px;
4 | }
5 | }
--------------------------------------------------------------------------------
/src/app/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 |
3 | import { AppModule } from './app.module';
4 |
5 | platformBrowserDynamic().bootstrapModule(AppModule);
6 |
--------------------------------------------------------------------------------
/src/components/multi-image-upload/multi-image-upload.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |

8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |

20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/multi-image-upload/multi-image-upload.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { IonicPageModule } from 'ionic-angular';
3 | import { MultiImageUpload } from './multi-image-upload';
4 |
5 | @NgModule({
6 | declarations: [
7 | MultiImageUpload,
8 | ],
9 | imports: [
10 | IonicPageModule.forChild(MultiImageUpload),
11 | ],
12 | exports: [
13 | MultiImageUpload
14 | ]
15 | })
16 | export class MultiImageUploadModule {}
17 |
--------------------------------------------------------------------------------
/src/components/multi-image-upload/multi-image-upload.scss:
--------------------------------------------------------------------------------
1 | multi-image-upload {
2 | .images-wrapper {
3 | font-size: 0;
4 | .image-wrapper {
5 | display: inline-block;
6 | margin: 0;
7 | width: 33.3333%;
8 | box-sizing: border-box;
9 | padding: 1px;
10 | position: relative;
11 | .remove-image {
12 | z-index: 100;
13 | position: absolute;
14 | right: 1px;
15 | top: 1px;
16 | padding: 5px 6px;
17 | font-size: 20px;
18 | text-shadow: white 0 0 1px;
19 | }
20 | ion-spinner {
21 | position: absolute;
22 | top: 50%;
23 | left: 50%;
24 | transform: translate(-50%, -50%);
25 | * {
26 | stroke: white;
27 | }
28 | }
29 | .image {
30 | position: relative;
31 | background-size: cover;
32 | background-position: center center;
33 | img {
34 | width: 100%;
35 | &.uploading {
36 | background: rgba(0, 0, 0, 0.20);
37 | }
38 | }
39 | ion-icon {
40 | font-size: 50px;
41 | position: absolute;
42 | transform: translate(-50%, -50%);
43 | top: 50%;
44 | left: 50%;
45 | color: white;
46 | }
47 | .progress-bar {
48 | height: 6px;
49 | width: 100%;
50 | position: absolute;
51 | bottom: 0px;
52 | overflow: hidden;
53 | .progress {
54 | background: map_get($colors, 'primary');
55 | height: 100%;
56 | }
57 | }
58 | }
59 |
60 | &.add-image-btn .image {
61 | background: #DDD;
62 | }
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/components/multi-image-upload/multi-image-upload.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {DomSanitizer} from "@angular/platform-browser";
3 | import {ActionSheetController, AlertController, Platform, ToastController} from "ionic-angular";
4 | import {Camera, CameraOptions} from "@ionic-native/camera";
5 | import {File} from "@ionic-native/file";
6 | import {FilePath} from "@ionic-native/file-path";
7 | import {TransferObject} from "@ionic-native/transfer";
8 |
9 | @Component({
10 | selector: 'multi-image-upload',
11 | templateUrl: 'multi-image-upload.html',
12 | providers: [Camera, File, FilePath, Platform]
13 | })
14 |
15 | export class MultiImageUpload {
16 | public serverUrl = "http://jquery-file-upload.appspot.com/";
17 |
18 | public isUploading = false;
19 | public uploadingProgress = {};
20 | public uploadingHandler = {};
21 | public images: any = [];
22 | protected imagesValue: Array;
23 |
24 | constructor(private sanitization: DomSanitizer, private actionSheetCtrl: ActionSheetController, private camera: Camera, private file: File, private alertCtrl: AlertController, private toastCtrl: ToastController) {
25 | }
26 |
27 | public uploadImages(): Promise> {
28 | return new Promise((resolve, reject) => {
29 | this.isUploading = true;
30 | Promise.all(this.images.map(image => {
31 | return this.uploadImage(image);
32 | }))
33 | .then(resolve)
34 | .catch(reason => {
35 | this.isUploading = false;
36 | this.uploadingProgress = {};
37 | this.uploadingHandler = {};
38 | reject(reason);
39 | });
40 |
41 | });
42 | }
43 |
44 | public abort() {
45 | if (!this.isUploading)
46 | return;
47 | this.isUploading = false;
48 | for (let key in this.uploadingHandler) {
49 | this.uploadingHandler[key].abort();
50 | }
51 | }
52 |
53 | // ======================================================================
54 |
55 | protected removeImage(image) {
56 | if (this.isUploading)
57 | return;
58 | this.util.confirm("Are you sure to remove it?").then(value => {
59 | if (value) {
60 | this.util.removeFromArray(this.imagesValue, image);
61 | this.util.removeFromArray(this.images, image.url);
62 | }
63 | });
64 | }
65 |
66 | protected showAddImage() {
67 | if (!window['cordova']) {
68 | let input = document.createElement('input');
69 | input.type = 'file';
70 | input.accept = "image/x-png,image/gif,image/jpeg";
71 | input.click();
72 | input.onchange = () => {
73 | let blob = window.URL.createObjectURL(input.files[0]);
74 | this.images.push(blob);
75 | this.util.trustImages();
76 | }
77 | } else {
78 | new Promise((resolve, reject) => {
79 | let actionSheet = this.actionSheetCtrl.create({
80 | title: 'Add a photo',
81 | buttons: [
82 | {
83 | text: 'From photo library',
84 | handler: () => {
85 | resolve(this.camera.PictureSourceType.PHOTOLIBRARY);
86 | }
87 | },
88 | {
89 | text: 'From camera',
90 | handler: () => {
91 | resolve(this.camera.PictureSourceType.CAMERA);
92 | }
93 | },
94 | {
95 | text: 'Cancel',
96 | role: 'cancel',
97 | handler: () => {
98 | reject();
99 | }
100 | }
101 | ]
102 | });
103 | actionSheet.present();
104 | }).then(sourceType => {
105 | if (!window['cordova'])
106 | return;
107 | let options: CameraOptions = {
108 | quality: 100,
109 | sourceType: sourceType as number,
110 | saveToPhotoAlbum: false,
111 | correctOrientation: true
112 | };
113 | this.camera.getPicture(options).then((imagePath) => {
114 | this.images.push(imagePath);
115 | this.util.trustImages();
116 | });
117 | }).catch(() => {
118 | });
119 | }
120 | }
121 |
122 |
123 | private uploadImage(targetPath) {
124 | return new Promise((resolve, reject) => {
125 | this.uploadingProgress[targetPath] = 0;
126 |
127 | if (window['cordova']) {
128 | let options = {
129 | fileKey: "files[]",
130 | fileName: targetPath,
131 | chunkedMode: false,
132 | mimeType: "multipart/form-data",
133 | };
134 |
135 | const fileTransfer = new TransferObject();
136 | this.uploadingHandler[targetPath] = fileTransfer;
137 |
138 | fileTransfer.upload(targetPath, this.serverUrl, options).then(data => {
139 | resolve(JSON.parse(data.response));
140 | }).catch(() => {
141 | askRetry();
142 | });
143 |
144 | fileTransfer.onProgress(event2 => {
145 | this.uploadingProgress[targetPath] = event2.loaded * 100 / event2.total;
146 | });
147 | } else {
148 | const xhr = new XMLHttpRequest();
149 | xhr.open('GET', targetPath, true);
150 | xhr.responseType = 'blob';
151 | xhr.onload = (e) => {
152 | if (xhr['status'] != 200) {
153 | this.util.showToast("Your browser doesn't support blob API");
154 | console.error(e, xhr);
155 | askRetry();
156 | } else {
157 | const blob = xhr['response'];
158 | let formData: FormData = new FormData(),
159 | xhr2: XMLHttpRequest = new XMLHttpRequest();
160 | formData.append('files[]', blob);
161 | this.uploadingHandler[targetPath] = xhr2;
162 |
163 | xhr2.onreadystatechange = () => {
164 | if (xhr2.readyState === 4) {
165 | if (xhr2.status === 200)
166 | resolve(JSON.parse(xhr2.response));
167 | else
168 | askRetry();
169 | }
170 | };
171 |
172 | xhr2.upload.onprogress = (event) => {
173 | this.uploadingProgress[targetPath] = event.loaded * 100 / event.total;
174 | };
175 |
176 | xhr2.open('POST', this.serverUrl, true);
177 | xhr2.send(formData);
178 | }
179 | };
180 | xhr.send();
181 | }
182 |
183 | let askRetry = () => {
184 | // might have been aborted
185 | if (!this.isUploading) return reject(null);
186 | this.util.confirm('Do you wish to retry?', 'Upload failed').then(res => {
187 | if (!res) {
188 | this.isUploading = false;
189 | for (let key in this.uploadingHandler) {
190 | this.uploadingHandler[key].abort();
191 | }
192 | return reject(null);
193 | }
194 | else {
195 | if (!this.isUploading) return reject(null);
196 | this.uploadImage(targetPath).then(resolve, reject);
197 | }
198 | });
199 | };
200 | });
201 | }
202 |
203 | private util = ((_this: any) => {
204 | return {
205 | removeFromArray(array: Array, item: T) {
206 | let index: number = array.indexOf(item);
207 | if (index !== -1) {
208 | array.splice(index, 1);
209 | }
210 | },
211 | confirm(text, title = '', yes = "Yes", no = "No") {
212 | return new Promise(
213 | (resolve) => {
214 | _this.alertCtrl.create({
215 | title: title,
216 | message: text,
217 | buttons: [
218 | {
219 | text: no,
220 | role: 'cancel',
221 | handler: () => {
222 | resolve(false);
223 | }
224 | },
225 | {
226 | text: yes,
227 | handler: () => {
228 | resolve(true);
229 | }
230 | }
231 | ]
232 | }).present();
233 | }
234 | );
235 | },
236 | trustImages() {
237 | _this.imagesValue = _this.images.map(
238 | val => {
239 | return {
240 | url: val,
241 | sanitized: _this.sanitization.bypassSecurityTrustStyle("url(" + val + ")")
242 | }
243 | }
244 | );
245 | },
246 | showToast(text: string) {
247 | _this.toastCtrl.create({
248 | message: text,
249 | duration: 5000,
250 | position: 'bottom'
251 | }).present();
252 | }
253 | }
254 | })(this);
255 | }
256 |
--------------------------------------------------------------------------------
/src/declarations.d.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Declaration files are how the Typescript compiler knows about the type information(or shape) of an object.
3 | They're what make intellisense work and make Typescript know all about your code.
4 |
5 | A wildcard module is declared below to allow third party libraries to be used in an app even if they don't
6 | provide their own type declarations.
7 |
8 | To learn more about using third party libraries in an Ionic app, check out the docs here:
9 | http://ionicframework.com/docs/v2/resources/third-party-libs/
10 |
11 | For more info on type definition files, check out the Typescript docs here:
12 | https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html
13 | */
14 | declare module '*';
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ionic App
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ionic",
3 | "short_name": "Ionic",
4 | "start_url": "index.html",
5 | "display": "standalone",
6 | "icons": [{
7 | "src": "assets/imgs/logo.png",
8 | "sizes": "512x512",
9 | "type": "image/png"
10 | }],
11 | "background_color": "#4e8ef7",
12 | "theme_color": "#4e8ef7"
13 | }
--------------------------------------------------------------------------------
/src/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Check out https://googlechrome.github.io/sw-toolbox/docs/master/index.html for
3 | * more info on how to use sw-toolbox to custom configure your service worker.
4 | */
5 |
6 |
7 | 'use strict';
8 | importScripts('./build/sw-toolbox.js');
9 |
10 | self.toolbox.options.cache = {
11 | name: 'ionic-cache'
12 | };
13 |
14 | // pre-cache our key assets
15 | self.toolbox.precache(
16 | [
17 | './build/main.js',
18 | './build/main.css',
19 | './build/polyfills.js',
20 | 'index.html',
21 | 'manifest.json'
22 | ]
23 | );
24 |
25 | // dynamically cache any other local assets
26 | self.toolbox.router.any('/*', self.toolbox.cacheFirst);
27 |
28 | // for any other requests go to the network, cache,
29 | // and then only use that cached resource if your user goes offline
30 | self.toolbox.router.default = self.toolbox.networkFirst;
--------------------------------------------------------------------------------
/src/theme/variables.scss:
--------------------------------------------------------------------------------
1 | // Ionic Variables and Theming. For more info, please see:
2 | // http://ionicframework.com/docs/v2/theming/
3 | $font-path: "../assets/fonts";
4 |
5 | @import "ionic.globals";
6 |
7 |
8 | // Shared Variables
9 | // --------------------------------------------------
10 | // To customize the look and feel of this app, you can override
11 | // the Sass variables found in Ionic's source scss files.
12 | // To view all the possible Ionic variables, see:
13 | // http://ionicframework.com/docs/v2/theming/overriding-ionic-variables/
14 |
15 |
16 |
17 |
18 | // Named Color Variables
19 | // --------------------------------------------------
20 | // Named colors makes it easy to reuse colors on various components.
21 | // It's highly recommended to change the default colors
22 | // to match your app's branding. Ionic uses a Sass map of
23 | // colors so you can add, rename and remove colors as needed.
24 | // The "primary" color is the only required color in the map.
25 |
26 | $colors: (
27 | primary: #488aff,
28 | secondary: #32db64,
29 | danger: #f53d3d,
30 | light: #f4f4f4,
31 | dark: #222
32 | );
33 |
34 |
35 | // App iOS Variables
36 | // --------------------------------------------------
37 | // iOS only Sass variables can go here
38 |
39 |
40 |
41 |
42 | // App Material Design Variables
43 | // --------------------------------------------------
44 | // Material Design only Sass variables can go here
45 |
46 |
47 |
48 |
49 | // App Windows Variables
50 | // --------------------------------------------------
51 | // Windows only Sass variables can go here
52 |
53 |
54 |
55 |
56 | // App Theme
57 | // --------------------------------------------------
58 | // Ionic apps can have different themes applied, which can
59 | // then be future customized. This import comes last
60 | // so that the above variables are used and Ionic's
61 | // default are overridden.
62 |
63 | @import "ionic.theme.default";
64 |
65 |
66 | // Ionicons
67 | // --------------------------------------------------
68 | // The premium icon font for Ionic. For more info, please see:
69 | // http://ionicframework.com/docs/v2/ionicons/
70 |
71 | @import "ionic.ionicons";
72 |
73 |
74 | // Fonts
75 | // --------------------------------------------------
76 |
77 | @import "roboto";
78 | @import "noto-sans";
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "declaration": false,
5 | "emitDecoratorMetadata": true,
6 | "experimentalDecorators": true,
7 | "lib": [
8 | "dom",
9 | "es2015"
10 | ],
11 | "module": "es2015",
12 | "moduleResolution": "node",
13 | "sourceMap": true,
14 | "target": "es5"
15 | },
16 | "include": [
17 | "src/**/*.ts"
18 | ],
19 | "exclude": [
20 | "node_modules"
21 | ],
22 | "compileOnSave": false,
23 | "atom": {
24 | "rewriteTsconfig": false
25 | }
26 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-duplicate-variable": true,
4 | "no-unused-variable": [
5 | true
6 | ]
7 | },
8 | "rulesDirectory": [
9 | "node_modules/tslint-eslint-rules/dist/rules"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------