├── .editorconfig ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── config.xml ├── hooks ├── README.md └── after_prepare │ └── 010_add_platform_class.js ├── ionic.config.json ├── ionic.project ├── package.json ├── resources ├── android │ ├── icon │ │ ├── drawable-hdpi-icon.png │ │ ├── drawable-ldpi-icon.png │ │ ├── drawable-mdpi-icon.png │ │ ├── drawable-xhdpi-icon.png │ │ ├── drawable-xxhdpi-icon.png │ │ └── drawable-xxxhdpi-icon.png │ └── splash │ │ ├── drawable-land-hdpi-screen.png │ │ ├── drawable-land-ldpi-screen.png │ │ ├── drawable-land-mdpi-screen.png │ │ ├── drawable-land-xhdpi-screen.png │ │ ├── drawable-land-xxhdpi-screen.png │ │ ├── drawable-land-xxxhdpi-screen.png │ │ ├── drawable-port-hdpi-screen.png │ │ ├── drawable-port-ldpi-screen.png │ │ ├── drawable-port-mdpi-screen.png │ │ ├── drawable-port-xhdpi-screen.png │ │ ├── drawable-port-xxhdpi-screen.png │ │ └── drawable-port-xxxhdpi-screen.png ├── icon-text.svg ├── icon.png ├── icon.svg ├── ios │ ├── icon │ │ ├── icon-40.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-50.png │ │ ├── icon-50@2x.png │ │ ├── icon-60.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-72.png │ │ ├── icon-72@2x.png │ │ ├── icon-76.png │ │ ├── icon-76@2x.png │ │ ├── icon-83.5@2x.png │ │ ├── icon-small.png │ │ ├── icon-small@2x.png │ │ ├── icon-small@3x.png │ │ ├── icon.png │ │ └── icon@2x.png │ └── splash │ │ ├── Default-568h@2x~iphone.png │ │ ├── Default-667h.png │ │ ├── Default-736h.png │ │ ├── Default-Landscape-736h.png │ │ ├── Default-Landscape@2x~ipad.png │ │ ├── Default-Landscape~ipad.png │ │ ├── Default-Portrait@2x~ipad.png │ │ ├── Default-Portrait~ipad.png │ │ ├── Default@2x~iphone.png │ │ └── Default~iphone.png └── splash.png ├── shrinkwrap-check.js ├── src ├── CNAME ├── app │ ├── app.component.ts │ ├── app.html │ ├── app.module.ts │ ├── app.scss │ └── main.ts ├── assets │ ├── icon │ │ └── favicon.ico │ └── leaflet │ │ ├── images │ │ ├── layers-2x.png │ │ ├── layers.png │ │ ├── marker-icon-2x.png │ │ ├── marker-icon.png │ │ └── marker-shadow.png │ │ └── leaflet.css ├── components │ └── geofence-list-item │ │ ├── geofence-list-item.html │ │ └── geofence-list-item.ts ├── declarations.d.ts ├── index.html ├── manifest.json ├── models │ └── geofence.ts ├── pages │ ├── geofence-details │ │ ├── geofence-details.html │ │ ├── geofence-details.scss │ │ └── geofence-details.ts │ └── geofence-list │ │ ├── geofence-list.html │ │ ├── geofence-list.scss │ │ └── geofence-list.ts ├── service-worker.js ├── services │ ├── geofence-plugin-mock.ts │ └── geofence-service.ts ├── theme │ └── variables.scss └── utils │ └── uuid.ts ├── tsconfig.json ├── tslint.json ├── typings.json └── typings ├── globals └── cordova-plugin-geofence │ ├── index.d.ts │ └── typings.json └── index.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.ts] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.{css,scss}] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [*.html] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [*.{diff,md}] 33 | trim_trailing_whitespace = false 34 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb/base", 3 | 4 | "env": { 5 | "browser": true, 6 | "es6": true, 7 | "node": true 8 | }, 9 | 10 | "rules": { 11 | "quotes": [2, "double", "avoid-escape"], 12 | "indent": [2, 2], 13 | "func-names": 0, 14 | "no-use-before-define": [2, "nofunc"], 15 | "prefer-arrow-callback": 0 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.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 | Copyright (c) 2016 Tomasz Subik 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ionic2 geofence sample project 2 | 3 | Sample project using [cordova geofence plugin](https://github.com/cowbell/cordova-plugin-geofence) built with Ionic2 framework 4 | 5 | ![ionic2-screen1](https://cloud.githubusercontent.com/assets/1286444/23096546/4d522902-f61f-11e6-8ed4-b7ddf5cbc62a.png) 6 | ![ionic2-screen2](https://cloud.githubusercontent.com/assets/1286444/23096550/5a38b406-f61f-11e6-878e-e4ca09dd60a2.png) 7 | 8 | ## Working Example 9 | 10 | Check out [the working example](https://ionic2-geofence.surge.sh/) directly in your browser. 11 | 12 | ## Installation 13 | 14 | Use local npm packages 15 | 16 | ``` 17 | npm install 18 | ``` 19 | 20 | If you're using `ionic-cli` installed globally not locally you should install the latest beta version in order to run this project. 21 | ``` 22 | npm install -g ionic@beta 23 | ``` 24 | 25 | For testing in the browser 26 | 27 | ``` 28 | ionic serve 29 | ``` 30 | 31 | For android 32 | 33 | ``` 34 | ionic platform add android 35 | ionic run android 36 | ``` 37 | 38 | For iOS 39 | 40 | ``` 41 | ionic platform add ios 42 | ionic run ios 43 | ``` 44 | 45 | ## Platforms 46 | 47 | - Android 48 | - iOS 49 | 50 | ## License 51 | 52 | This software is released under the [MIT License](https://raw.githubusercontent.com/tsubik/ionic2-geofence/master/LICENSE). 53 | 54 | © 2016 Tomasz Subik. All rights reserved 55 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ionic2 Geofence 4 | Sample Cordova Geofence Application Built With Ionic 2 5 | Tomasz Subik 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 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /hooks/README.md: -------------------------------------------------------------------------------- 1 | 21 | # Cordova Hooks 22 | 23 | Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. Hook scripts could be defined by adding them to the special predefined folder (`/hooks`) or via configuration files (`config.xml` and `plugin.xml`) and run serially in the following order: 24 | * Application hooks from `/hooks`; 25 | * Application hooks from `config.xml`; 26 | * Plugin hooks from `plugins/.../plugin.xml`. 27 | 28 | __Remember__: Make your scripts executable. 29 | 30 | __Note__: `.cordova/hooks` directory is also supported for backward compatibility, but we don't recommend using it as it is deprecated. 31 | 32 | ## Supported hook types 33 | The following hook types are supported: 34 | 35 | after_build/ 36 | after_compile/ 37 | after_docs/ 38 | after_emulate/ 39 | after_platform_add/ 40 | after_platform_rm/ 41 | after_platform_ls/ 42 | after_plugin_add/ 43 | after_plugin_ls/ 44 | after_plugin_rm/ 45 | after_plugin_search/ 46 | after_plugin_install/ <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being installed 47 | after_prepare/ 48 | after_run/ 49 | after_serve/ 50 | before_build/ 51 | before_compile/ 52 | before_docs/ 53 | before_emulate/ 54 | before_platform_add/ 55 | before_platform_rm/ 56 | before_platform_ls/ 57 | before_plugin_add/ 58 | before_plugin_ls/ 59 | before_plugin_rm/ 60 | before_plugin_search/ 61 | before_plugin_install/ <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being installed 62 | before_plugin_uninstall/ <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being uninstalled 63 | before_prepare/ 64 | before_run/ 65 | before_serve/ 66 | pre_package/ <-- Windows 8 and Windows Phone only. 67 | 68 | ## Ways to define hooks 69 | ### Via '/hooks' directory 70 | To execute custom action when corresponding hook type is fired, use hook type as a name for a subfolder inside 'hooks' directory and place you script file here, for example: 71 | 72 | # script file will be automatically executed after each build 73 | hooks/after_build/after_build_custom_action.js 74 | 75 | 76 | ### Config.xml 77 | 78 | Hooks can be defined in project's `config.xml` using `` elements, for example: 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | ... 89 | 90 | 91 | 92 | 93 | 94 | 95 | ... 96 | 97 | 98 | ### Plugin hooks (plugin.xml) 99 | 100 | As a plugin developer you can define hook scripts using `` elements in a `plugin.xml` like that: 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | ... 109 | 110 | 111 | `before_plugin_install`, `after_plugin_install`, `before_plugin_uninstall` plugin hooks will be fired exclusively for the plugin being installed/uninstalled. 112 | 113 | ## Script Interface 114 | 115 | ### Javascript 116 | 117 | If you are writing hooks in Javascript you should use the following module definition: 118 | ```javascript 119 | module.exports = function(context) { 120 | ... 121 | } 122 | ``` 123 | 124 | You can make your scipts async using Q: 125 | ```javascript 126 | module.exports = function(context) { 127 | var Q = context.requireCordovaModule('q'); 128 | var deferral = new Q.defer(); 129 | 130 | setTimeout(function(){ 131 | console.log('hook.js>> end'); 132 | deferral.resolve(); 133 | }, 1000); 134 | 135 | return deferral.promise; 136 | } 137 | ``` 138 | 139 | `context` object contains hook type, executed script full path, hook options, command-line arguments passed to Cordova and top-level "cordova" object: 140 | ```json 141 | { 142 | "hook": "before_plugin_install", 143 | "scriptLocation": "c:\\script\\full\\path\\appBeforePluginInstall.js", 144 | "cmdLine": "The\\exact\\command\\cordova\\run\\with arguments", 145 | "opts": { 146 | "projectRoot":"C:\\path\\to\\the\\project", 147 | "cordova": { 148 | "platforms": ["wp8"], 149 | "plugins": ["com.plugin.withhooks"], 150 | "version": "0.21.7-dev" 151 | }, 152 | "plugin": { 153 | "id": "com.plugin.withhooks", 154 | "pluginInfo": { 155 | ... 156 | }, 157 | "platform": "wp8", 158 | "dir": "C:\\path\\to\\the\\project\\plugins\\com.plugin.withhooks" 159 | } 160 | }, 161 | "cordova": {...} 162 | } 163 | 164 | ``` 165 | `context.opts.plugin` object will only be passed to plugin hooks scripts. 166 | 167 | You can also require additional Cordova modules in your script using `context.requireCordovaModule` in the following way: 168 | ```javascript 169 | var Q = context.requireCordovaModule('q'); 170 | ``` 171 | 172 | __Note__: new module loader script interface is used for the `.js` files defined via `config.xml` or `plugin.xml` only. 173 | For compatibility reasons hook files specified via `/hooks` folders are run via Node child_process spawn, see 'Non-javascript' section below. 174 | 175 | ### Non-javascript 176 | 177 | Non-javascript scripts are run via Node child_process spawn from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables: 178 | 179 | * CORDOVA_VERSION - The version of the Cordova-CLI. 180 | * CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios). 181 | * CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer) 182 | * CORDOVA_HOOK - Path to the hook that is being executed. 183 | * CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate) 184 | 185 | If a script returns a non-zero exit code, then the parent cordova command will be aborted. 186 | 187 | ## Writing hooks 188 | 189 | We highly recommend writing your hooks using Node.js so that they are 190 | cross-platform. Some good examples are shown here: 191 | 192 | [http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/) 193 | 194 | Also, note that even if you are working on Windows, and in case your hook scripts aren't bat files (which is recommended, if you want your scripts to work in non-Windows operating systems) Cordova CLI will expect a shebang line as the first line for it to know the interpreter it needs to use to launch the script. The shebang line should match the following example: 195 | 196 | #!/usr/bin/env [name_of_interpreter_executable] 197 | -------------------------------------------------------------------------------- /hooks/after_prepare/010_add_platform_class.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Add Platform Class 4 | // v1.0 5 | // Automatically adds the platform class to the body tag 6 | // after the `prepare` command. By placing the platform CSS classes 7 | // directly in the HTML built for the platform, it speeds up 8 | // rendering the correct layout/style for the specific platform 9 | // instead of waiting for the JS to figure out the correct classes. 10 | 11 | var fs = require('fs'); 12 | var path = require('path'); 13 | 14 | var rootdir = process.argv[2]; 15 | 16 | function addPlatformBodyTag(indexPath, platform) { 17 | // add the platform class to the body tag 18 | try { 19 | var platformClass = 'platform-' + platform; 20 | var cordovaClass = 'platform-cordova platform-webview'; 21 | 22 | var html = fs.readFileSync(indexPath, 'utf8'); 23 | 24 | var bodyTag = findBodyTag(html); 25 | if(!bodyTag) return; // no opening body tag, something's wrong 26 | 27 | if(bodyTag.indexOf(platformClass) > -1) return; // already added 28 | 29 | var newBodyTag = bodyTag; 30 | 31 | var classAttr = findClassAttr(bodyTag); 32 | if(classAttr) { 33 | // body tag has existing class attribute, add the classname 34 | var endingQuote = classAttr.substring(classAttr.length-1); 35 | var newClassAttr = classAttr.substring(0, classAttr.length-1); 36 | newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; 37 | newBodyTag = bodyTag.replace(classAttr, newClassAttr); 38 | 39 | } else { 40 | // add class attribute to the body tag 41 | newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); 42 | } 43 | 44 | html = html.replace(bodyTag, newBodyTag); 45 | 46 | fs.writeFileSync(indexPath, html, 'utf8'); 47 | 48 | process.stdout.write('add to body class: ' + platformClass + '\n'); 49 | } catch(e) { 50 | process.stdout.write(e); 51 | } 52 | } 53 | 54 | function findBodyTag(html) { 55 | // get the body tag 56 | try{ 57 | return html.match(/])(.*?)>/gi)[0]; 58 | }catch(e){} 59 | } 60 | 61 | function findClassAttr(bodyTag) { 62 | // get the body tag's class attribute 63 | try{ 64 | return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; 65 | }catch(e){} 66 | } 67 | 68 | if (rootdir) { 69 | 70 | // go through each of the platform directories that have been prepared 71 | var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); 72 | 73 | for(var x=0; x 2 | 3 | 4 | 22 | 24 | 25 | 27 | image/svg+xml 28 | 30 | 31 | 32 | 33 | 35 | 55 | 61 | 66 | 71 | 82 | 2 94 | ionic2Geofence 111 | 122 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/icon.png -------------------------------------------------------------------------------- /resources/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 25 | 27 | image/svg+xml 28 | 30 | 31 | 32 | 33 | 35 | 55 | 61 | 66 | 71 | 82 | 2 94 | 95 | -------------------------------------------------------------------------------- /resources/ios/icon/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-40.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-40@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-40@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-50.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-50@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-60.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-60@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-60@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-72.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-72@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-76.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-76@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-83.5@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-small.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-small@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon-small@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon.png -------------------------------------------------------------------------------- /resources/ios/icon/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/icon/icon@2x.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-568h@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-568h@2x~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-667h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-667h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-Landscape-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-Landscape@2x~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-Landscape~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-Portrait@2x~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default-Portrait~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default@2x~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/ios/splash/Default~iphone.png -------------------------------------------------------------------------------- /resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/resources/splash.png -------------------------------------------------------------------------------- /shrinkwrap-check.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* eslint-env node */ 4 | /* eslint no-console: 0 */ 5 | 6 | const fs = require('fs'); 7 | const path = require('path'); 8 | const _ = require('lodash'); 9 | const shrinkwrapPath = 'npm-shrinkwrap.json'; 10 | 11 | module.exports = function shrinkwrapCheck() { 12 | try { 13 | fs.accessSync(shrinkwrapPath); 14 | } catch (error) { 15 | console.error('npm-shrinkwrap.json file not found. Please run "npm shrinkwrap --dev" first.'); 16 | process.exit(1); 17 | } 18 | 19 | const shrinkwrap = readFile(shrinkwrapPath); 20 | const dependencies = _(shrinkwrap.dependencies) 21 | .toPairs() 22 | .map((dependency) => ( 23 | { 24 | name: dependency[0], 25 | version: dependency[1].version, 26 | } 27 | )); 28 | 29 | const hasPassed = dependencies 30 | .map(verifyDependency) 31 | .every((verification) => verification); 32 | 33 | if (!hasPassed) { 34 | console.error('\nSome installed dependencies are incorrect. Run "npm install" to fix it.'); 35 | process.exit(1); 36 | } 37 | 38 | function readFile(filepath) { 39 | const file = fs.readFileSync(filepath); 40 | return JSON.parse(file); 41 | } 42 | 43 | function verifyDependency(dependency) { 44 | const packagePath = path.join('node_modules', dependency.name, 'package.json'); 45 | const prefix = `${dependency.name}@${dependency.version}`; 46 | 47 | try { 48 | fs.accessSync(packagePath); 49 | } catch (error) { 50 | console.error(`${prefix}: missing.`); 51 | return false; 52 | } 53 | 54 | const installedVersion = readFile(packagePath).version; 55 | const requiredVersion = dependency.version; 56 | 57 | if (installedVersion !== requiredVersion) { 58 | console.error(`${prefix}: incorrect version installed ${installedVersion}.`); 59 | return false; 60 | } 61 | 62 | return true; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/CNAME: -------------------------------------------------------------------------------- 1 | https://ionic2-geofence.surge.sh 2 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from "@angular/core"; 2 | import { Nav, Platform, AlertController, MenuController } from "ionic-angular"; 3 | import * as Leaflet from "leaflet"; 4 | 5 | import { GeofenceListPage } from "../pages/geofence-list/geofence-list"; 6 | import { GeofenceService } from "../services/geofence-service"; 7 | import { GeofencePluginMock, TransitionType } from "../services/geofence-plugin-mock"; 8 | import { FIXTURES } from "../models/geofence"; 9 | 10 | @Component({ 11 | templateUrl: "app.html" 12 | }) 13 | export class MyApp { 14 | @ViewChild(Nav) nav: Nav; 15 | 16 | rootPage: any = GeofenceListPage; 17 | 18 | constructor( 19 | platform: Platform, 20 | private alertCtrl: AlertController, 21 | private geofenceService: GeofenceService, 22 | private menuCtrl: MenuController 23 | ) { 24 | platform.ready().then(() => { 25 | Leaflet.Icon.Default.imagePath = "assets/leaflet/images/"; 26 | 27 | if (window.geofence === undefined) { 28 | console.warn("Geofence Plugin not found. Using mock instead."); 29 | window.geofence = GeofencePluginMock; 30 | window.TransitionType = TransitionType; 31 | } 32 | 33 | window.geofence.initialize().then((initStatus) => { 34 | console.log("Geofence Plugin has been initialized", initStatus); 35 | window.geofence.onTransitionReceived = function (geofences) { 36 | geofences.forEach(function (geo) { 37 | console.log("Geofence transition detected", geo); 38 | }); 39 | }; 40 | 41 | window.geofence.onNotificationClicked = function (notificationData) { 42 | console.log("App opened from Geo Notification!", notificationData); 43 | }; 44 | }).catch((error) => { 45 | console.error(error); 46 | }) 47 | }); 48 | } 49 | 50 | addFixtures() { 51 | FIXTURES.forEach((fixture) => this.geofenceService.addOrUpdate(fixture)); 52 | this.menuCtrl.close(); 53 | } 54 | 55 | removeAll() { 56 | const confirm = this.alertCtrl.create({ 57 | title: "Are you sure?", 58 | message: "Are you sure you want to remove all geofences?", 59 | buttons: [ 60 | { text: "No" }, 61 | { 62 | text: "Yes", 63 | handler: () => { 64 | this.geofenceService.removeAll(); 65 | }, 66 | }, 67 | ], 68 | }); 69 | this.menuCtrl.close(); 70 | confirm.present(); 71 | } 72 | 73 | testApp() { 74 | const confirm = this.alertCtrl.create({ 75 | title: "Are you sure?", 76 | message: "Running tests will remove all your geofences. Do you want to continue?", 77 | buttons: [ 78 | { text: "No" }, 79 | { 80 | text: "Yes", 81 | handler: () => { 82 | window.location.href = "cdvtests/index.html"; 83 | }, 84 | }, 85 | ], 86 | }); 87 | 88 | this.menuCtrl.close(); 89 | confirm.present(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Ionic2 Geofence 5 | 6 | 7 | 8 | 9 | 13 | 17 | 21 | 22 | 23 | Source 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ErrorHandler } from "@angular/core"; 2 | import { IonicApp, IonicModule, IonicErrorHandler } from "ionic-angular"; 3 | 4 | import { GeofenceDetailsPage } from "../pages/geofence-details/geofence-details"; 5 | import { GeofenceListItem } from "../components/geofence-list-item/geofence-list-item"; 6 | import { GeofenceListPage } from "../pages/geofence-list/geofence-list"; 7 | import { GeofenceService } from "../services/geofence-service"; 8 | import { MyApp } from "./app.component"; 9 | 10 | const components = [ 11 | MyApp, 12 | GeofenceDetailsPage, 13 | GeofenceListPage, 14 | GeofenceListItem 15 | ] 16 | 17 | @NgModule({ 18 | declarations: components, 19 | imports: [ 20 | IonicModule.forRoot(MyApp) 21 | ], 22 | bootstrap: [IonicApp], 23 | entryComponents: components, 24 | providers: [ 25 | {provide: ErrorHandler, useClass: IonicErrorHandler}, 26 | GeofenceService 27 | ] 28 | }) 29 | export class AppModule {} 30 | -------------------------------------------------------------------------------- /src/app/app.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/src/app/app.scss -------------------------------------------------------------------------------- /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/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/src/assets/icon/favicon.ico -------------------------------------------------------------------------------- /src/assets/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/src/assets/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /src/assets/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/src/assets/leaflet/images/layers.png -------------------------------------------------------------------------------- /src/assets/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/src/assets/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /src/assets/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/src/assets/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /src/assets/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsubik/ionic2-geofence/30e4b17b73764896c5d880c0658f064f3b9c624c/src/assets/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /src/assets/leaflet/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles */ 2 | 3 | .leaflet-pane, 4 | .leaflet-tile, 5 | .leaflet-marker-icon, 6 | .leaflet-marker-shadow, 7 | .leaflet-tile-container, 8 | .leaflet-pane > svg, 9 | .leaflet-pane > canvas, 10 | .leaflet-zoom-box, 11 | .leaflet-image-layer, 12 | .leaflet-layer { 13 | position: absolute; 14 | left: 0; 15 | top: 0; 16 | } 17 | .leaflet-container { 18 | overflow: hidden; 19 | } 20 | .leaflet-tile, 21 | .leaflet-marker-icon, 22 | .leaflet-marker-shadow { 23 | -webkit-user-select: none; 24 | -moz-user-select: none; 25 | user-select: none; 26 | -webkit-user-drag: none; 27 | } 28 | /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ 29 | .leaflet-safari .leaflet-tile { 30 | image-rendering: -webkit-optimize-contrast; 31 | } 32 | /* hack that prevents hw layers "stretching" when loading new tiles */ 33 | .leaflet-safari .leaflet-tile-container { 34 | width: 1600px; 35 | height: 1600px; 36 | -webkit-transform-origin: 0 0; 37 | } 38 | .leaflet-marker-icon, 39 | .leaflet-marker-shadow { 40 | display: block; 41 | } 42 | /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ 43 | /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ 44 | .leaflet-container .leaflet-overlay-pane svg, 45 | .leaflet-container .leaflet-marker-pane img, 46 | .leaflet-container .leaflet-shadow-pane img, 47 | .leaflet-container .leaflet-tile-pane img, 48 | .leaflet-container img.leaflet-image-layer { 49 | max-width: none !important; 50 | } 51 | 52 | .leaflet-container.leaflet-touch-zoom { 53 | -ms-touch-action: pan-x pan-y; 54 | touch-action: pan-x pan-y; 55 | } 56 | .leaflet-container.leaflet-touch-drag { 57 | -ms-touch-action: pinch-zoom; 58 | } 59 | .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { 60 | -ms-touch-action: none; 61 | touch-action: none; 62 | } 63 | .leaflet-tile { 64 | filter: inherit; 65 | visibility: hidden; 66 | } 67 | .leaflet-tile-loaded { 68 | visibility: inherit; 69 | } 70 | .leaflet-zoom-box { 71 | width: 0; 72 | height: 0; 73 | -moz-box-sizing: border-box; 74 | box-sizing: border-box; 75 | z-index: 800; 76 | } 77 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 78 | .leaflet-overlay-pane svg { 79 | -moz-user-select: none; 80 | } 81 | 82 | .leaflet-pane { z-index: 400; } 83 | 84 | .leaflet-tile-pane { z-index: 200; } 85 | .leaflet-overlay-pane { z-index: 400; } 86 | .leaflet-shadow-pane { z-index: 500; } 87 | .leaflet-marker-pane { z-index: 600; } 88 | .leaflet-tooltip-pane { z-index: 650; } 89 | .leaflet-popup-pane { z-index: 700; } 90 | 91 | .leaflet-map-pane canvas { z-index: 100; } 92 | .leaflet-map-pane svg { z-index: 200; } 93 | 94 | .leaflet-vml-shape { 95 | width: 1px; 96 | height: 1px; 97 | } 98 | .lvml { 99 | behavior: url(#default#VML); 100 | display: inline-block; 101 | position: absolute; 102 | } 103 | 104 | 105 | /* control positioning */ 106 | 107 | .leaflet-control { 108 | position: relative; 109 | z-index: 800; 110 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 111 | pointer-events: auto; 112 | } 113 | .leaflet-top, 114 | .leaflet-bottom { 115 | position: absolute; 116 | z-index: 1000; 117 | pointer-events: none; 118 | } 119 | .leaflet-top { 120 | top: 0; 121 | } 122 | .leaflet-right { 123 | right: 0; 124 | } 125 | .leaflet-bottom { 126 | bottom: 0; 127 | } 128 | .leaflet-left { 129 | left: 0; 130 | } 131 | .leaflet-control { 132 | float: left; 133 | clear: both; 134 | } 135 | .leaflet-right .leaflet-control { 136 | float: right; 137 | } 138 | .leaflet-top .leaflet-control { 139 | margin-top: 10px; 140 | } 141 | .leaflet-bottom .leaflet-control { 142 | margin-bottom: 10px; 143 | } 144 | .leaflet-left .leaflet-control { 145 | margin-left: 10px; 146 | } 147 | .leaflet-right .leaflet-control { 148 | margin-right: 10px; 149 | } 150 | 151 | 152 | /* zoom and fade animations */ 153 | 154 | .leaflet-fade-anim .leaflet-tile { 155 | will-change: opacity; 156 | } 157 | .leaflet-fade-anim .leaflet-popup { 158 | opacity: 0; 159 | -webkit-transition: opacity 0.2s linear; 160 | -moz-transition: opacity 0.2s linear; 161 | -o-transition: opacity 0.2s linear; 162 | transition: opacity 0.2s linear; 163 | } 164 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 165 | opacity: 1; 166 | } 167 | .leaflet-zoom-animated { 168 | -webkit-transform-origin: 0 0; 169 | -ms-transform-origin: 0 0; 170 | transform-origin: 0 0; 171 | } 172 | .leaflet-zoom-anim .leaflet-zoom-animated { 173 | will-change: transform; 174 | } 175 | .leaflet-zoom-anim .leaflet-zoom-animated { 176 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 177 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 178 | -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); 179 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 180 | } 181 | .leaflet-zoom-anim .leaflet-tile, 182 | .leaflet-pan-anim .leaflet-tile { 183 | -webkit-transition: none; 184 | -moz-transition: none; 185 | -o-transition: none; 186 | transition: none; 187 | } 188 | 189 | .leaflet-zoom-anim .leaflet-zoom-hide { 190 | visibility: hidden; 191 | } 192 | 193 | 194 | /* cursors */ 195 | 196 | .leaflet-interactive { 197 | cursor: pointer; 198 | } 199 | .leaflet-grab { 200 | cursor: -webkit-grab; 201 | cursor: -moz-grab; 202 | } 203 | .leaflet-crosshair, 204 | .leaflet-crosshair .leaflet-interactive { 205 | cursor: crosshair; 206 | } 207 | .leaflet-popup-pane, 208 | .leaflet-control { 209 | cursor: auto; 210 | } 211 | .leaflet-dragging .leaflet-grab, 212 | .leaflet-dragging .leaflet-grab .leaflet-interactive, 213 | .leaflet-dragging .leaflet-marker-draggable { 214 | cursor: move; 215 | cursor: -webkit-grabbing; 216 | cursor: -moz-grabbing; 217 | } 218 | 219 | /* marker & overlays interactivity */ 220 | .leaflet-marker-icon, 221 | .leaflet-marker-shadow, 222 | .leaflet-image-layer, 223 | .leaflet-pane > svg path, 224 | .leaflet-tile-container { 225 | pointer-events: none; 226 | } 227 | 228 | .leaflet-marker-icon.leaflet-interactive, 229 | .leaflet-image-layer.leaflet-interactive, 230 | .leaflet-pane > svg path.leaflet-interactive { 231 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 232 | pointer-events: auto; 233 | } 234 | 235 | /* visual tweaks */ 236 | 237 | .leaflet-container { 238 | background: #ddd; 239 | outline: 0; 240 | } 241 | .leaflet-container a { 242 | color: #0078A8; 243 | } 244 | .leaflet-container a.leaflet-active { 245 | outline: 2px solid orange; 246 | } 247 | .leaflet-zoom-box { 248 | border: 2px dotted #38f; 249 | background: rgba(255,255,255,0.5); 250 | } 251 | 252 | 253 | /* general typography */ 254 | .leaflet-container { 255 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 256 | } 257 | 258 | 259 | /* general toolbar styles */ 260 | 261 | .leaflet-bar { 262 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 263 | border-radius: 4px; 264 | } 265 | .leaflet-bar a, 266 | .leaflet-bar a:hover { 267 | background-color: #fff; 268 | border-bottom: 1px solid #ccc; 269 | width: 26px; 270 | height: 26px; 271 | line-height: 26px; 272 | display: block; 273 | text-align: center; 274 | text-decoration: none; 275 | color: black; 276 | } 277 | .leaflet-bar a, 278 | .leaflet-control-layers-toggle { 279 | background-position: 50% 50%; 280 | background-repeat: no-repeat; 281 | display: block; 282 | } 283 | .leaflet-bar a:hover { 284 | background-color: #f4f4f4; 285 | } 286 | .leaflet-bar a:first-child { 287 | border-top-left-radius: 4px; 288 | border-top-right-radius: 4px; 289 | } 290 | .leaflet-bar a:last-child { 291 | border-bottom-left-radius: 4px; 292 | border-bottom-right-radius: 4px; 293 | border-bottom: none; 294 | } 295 | .leaflet-bar a.leaflet-disabled { 296 | cursor: default; 297 | background-color: #f4f4f4; 298 | color: #bbb; 299 | } 300 | 301 | .leaflet-touch .leaflet-bar a { 302 | width: 30px; 303 | height: 30px; 304 | line-height: 30px; 305 | } 306 | 307 | 308 | /* zoom control */ 309 | 310 | .leaflet-control-zoom-in, 311 | .leaflet-control-zoom-out { 312 | font: bold 18px 'Lucida Console', Monaco, monospace; 313 | text-indent: 1px; 314 | } 315 | .leaflet-control-zoom-out { 316 | font-size: 20px; 317 | } 318 | 319 | .leaflet-touch .leaflet-control-zoom-in { 320 | font-size: 22px; 321 | } 322 | .leaflet-touch .leaflet-control-zoom-out { 323 | font-size: 24px; 324 | } 325 | 326 | 327 | /* layers control */ 328 | 329 | .leaflet-control-layers { 330 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 331 | background: #fff; 332 | border-radius: 5px; 333 | } 334 | .leaflet-control-layers-toggle { 335 | background-image: url(images/layers.png); 336 | width: 36px; 337 | height: 36px; 338 | } 339 | .leaflet-retina .leaflet-control-layers-toggle { 340 | background-image: url(images/layers-2x.png); 341 | background-size: 26px 26px; 342 | } 343 | .leaflet-touch .leaflet-control-layers-toggle { 344 | width: 44px; 345 | height: 44px; 346 | } 347 | .leaflet-control-layers .leaflet-control-layers-list, 348 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 349 | display: none; 350 | } 351 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 352 | display: block; 353 | position: relative; 354 | } 355 | .leaflet-control-layers-expanded { 356 | padding: 6px 10px 6px 6px; 357 | color: #333; 358 | background: #fff; 359 | } 360 | .leaflet-control-layers-scrollbar { 361 | overflow-y: scroll; 362 | padding-right: 5px; 363 | } 364 | .leaflet-control-layers-selector { 365 | margin-top: 2px; 366 | position: relative; 367 | top: 1px; 368 | } 369 | .leaflet-control-layers label { 370 | display: block; 371 | } 372 | .leaflet-control-layers-separator { 373 | height: 0; 374 | border-top: 1px solid #ddd; 375 | margin: 5px -10px 5px -6px; 376 | } 377 | 378 | /* Default icon URLs */ 379 | .leaflet-default-icon-path { 380 | background-image: url(images/marker-icon.png); 381 | } 382 | 383 | 384 | /* attribution and scale controls */ 385 | 386 | .leaflet-container .leaflet-control-attribution { 387 | background: #fff; 388 | background: rgba(255, 255, 255, 0.7); 389 | margin: 0; 390 | } 391 | .leaflet-control-attribution, 392 | .leaflet-control-scale-line { 393 | padding: 0 5px; 394 | color: #333; 395 | } 396 | .leaflet-control-attribution a { 397 | text-decoration: none; 398 | } 399 | .leaflet-control-attribution a:hover { 400 | text-decoration: underline; 401 | } 402 | .leaflet-container .leaflet-control-attribution, 403 | .leaflet-container .leaflet-control-scale { 404 | font-size: 11px; 405 | } 406 | .leaflet-left .leaflet-control-scale { 407 | margin-left: 5px; 408 | } 409 | .leaflet-bottom .leaflet-control-scale { 410 | margin-bottom: 5px; 411 | } 412 | .leaflet-control-scale-line { 413 | border: 2px solid #777; 414 | border-top: none; 415 | line-height: 1.1; 416 | padding: 2px 5px 1px; 417 | font-size: 11px; 418 | white-space: nowrap; 419 | overflow: hidden; 420 | -moz-box-sizing: border-box; 421 | box-sizing: border-box; 422 | 423 | background: #fff; 424 | background: rgba(255, 255, 255, 0.5); 425 | } 426 | .leaflet-control-scale-line:not(:first-child) { 427 | border-top: 2px solid #777; 428 | border-bottom: none; 429 | margin-top: -2px; 430 | } 431 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 432 | border-bottom: 2px solid #777; 433 | } 434 | 435 | .leaflet-touch .leaflet-control-attribution, 436 | .leaflet-touch .leaflet-control-layers, 437 | .leaflet-touch .leaflet-bar { 438 | box-shadow: none; 439 | } 440 | .leaflet-touch .leaflet-control-layers, 441 | .leaflet-touch .leaflet-bar { 442 | border: 2px solid rgba(0,0,0,0.2); 443 | background-clip: padding-box; 444 | } 445 | 446 | 447 | /* popup */ 448 | 449 | .leaflet-popup { 450 | position: absolute; 451 | text-align: center; 452 | margin-bottom: 20px; 453 | } 454 | .leaflet-popup-content-wrapper { 455 | padding: 1px; 456 | text-align: left; 457 | border-radius: 12px; 458 | } 459 | .leaflet-popup-content { 460 | margin: 13px 19px; 461 | line-height: 1.4; 462 | } 463 | .leaflet-popup-content p { 464 | margin: 18px 0; 465 | } 466 | .leaflet-popup-tip-container { 467 | width: 40px; 468 | height: 20px; 469 | position: absolute; 470 | left: 50%; 471 | margin-left: -20px; 472 | overflow: hidden; 473 | pointer-events: none; 474 | } 475 | .leaflet-popup-tip { 476 | width: 17px; 477 | height: 17px; 478 | padding: 1px; 479 | 480 | margin: -10px auto 0; 481 | 482 | -webkit-transform: rotate(45deg); 483 | -moz-transform: rotate(45deg); 484 | -ms-transform: rotate(45deg); 485 | -o-transform: rotate(45deg); 486 | transform: rotate(45deg); 487 | } 488 | .leaflet-popup-content-wrapper, 489 | .leaflet-popup-tip { 490 | background: white; 491 | color: #333; 492 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 493 | } 494 | .leaflet-container a.leaflet-popup-close-button { 495 | position: absolute; 496 | top: 0; 497 | right: 0; 498 | padding: 4px 4px 0 0; 499 | border: none; 500 | text-align: center; 501 | width: 18px; 502 | height: 14px; 503 | font: 16px/14px Tahoma, Verdana, sans-serif; 504 | color: #c3c3c3; 505 | text-decoration: none; 506 | font-weight: bold; 507 | background: transparent; 508 | } 509 | .leaflet-container a.leaflet-popup-close-button:hover { 510 | color: #999; 511 | } 512 | .leaflet-popup-scrolled { 513 | overflow: auto; 514 | border-bottom: 1px solid #ddd; 515 | border-top: 1px solid #ddd; 516 | } 517 | 518 | .leaflet-oldie .leaflet-popup-content-wrapper { 519 | zoom: 1; 520 | } 521 | .leaflet-oldie .leaflet-popup-tip { 522 | width: 24px; 523 | margin: 0 auto; 524 | 525 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 526 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 527 | } 528 | .leaflet-oldie .leaflet-popup-tip-container { 529 | margin-top: -1px; 530 | } 531 | 532 | .leaflet-oldie .leaflet-control-zoom, 533 | .leaflet-oldie .leaflet-control-layers, 534 | .leaflet-oldie .leaflet-popup-content-wrapper, 535 | .leaflet-oldie .leaflet-popup-tip { 536 | border: 1px solid #999; 537 | } 538 | 539 | 540 | /* div icon */ 541 | 542 | .leaflet-div-icon { 543 | background: #fff; 544 | border: 1px solid #666; 545 | } 546 | 547 | 548 | /* Tooltip */ 549 | /* Base styles for the element that has a tooltip */ 550 | .leaflet-tooltip { 551 | position: absolute; 552 | padding: 6px; 553 | background-color: #fff; 554 | border: 1px solid #fff; 555 | border-radius: 3px; 556 | color: #222; 557 | white-space: nowrap; 558 | -webkit-user-select: none; 559 | -moz-user-select: none; 560 | -ms-user-select: none; 561 | user-select: none; 562 | pointer-events: none; 563 | box-shadow: 0 1px 3px rgba(0,0,0,0.4); 564 | } 565 | .leaflet-tooltip.leaflet-clickable { 566 | cursor: pointer; 567 | pointer-events: auto; 568 | } 569 | .leaflet-tooltip-top:before, 570 | .leaflet-tooltip-bottom:before, 571 | .leaflet-tooltip-left:before, 572 | .leaflet-tooltip-right:before { 573 | position: absolute; 574 | pointer-events: none; 575 | border: 6px solid transparent; 576 | background: transparent; 577 | content: ""; 578 | } 579 | 580 | /* Directions */ 581 | 582 | .leaflet-tooltip-bottom { 583 | margin-top: 6px; 584 | } 585 | .leaflet-tooltip-top { 586 | margin-top: -6px; 587 | } 588 | .leaflet-tooltip-bottom:before, 589 | .leaflet-tooltip-top:before { 590 | left: 50%; 591 | margin-left: -6px; 592 | } 593 | .leaflet-tooltip-top:before { 594 | bottom: 0; 595 | margin-bottom: -12px; 596 | border-top-color: #fff; 597 | } 598 | .leaflet-tooltip-bottom:before { 599 | top: 0; 600 | margin-top: -12px; 601 | margin-left: -6px; 602 | border-bottom-color: #fff; 603 | } 604 | .leaflet-tooltip-left { 605 | margin-left: -6px; 606 | } 607 | .leaflet-tooltip-right { 608 | margin-left: 6px; 609 | } 610 | .leaflet-tooltip-left:before, 611 | .leaflet-tooltip-right:before { 612 | top: 50%; 613 | margin-top: -6px; 614 | } 615 | .leaflet-tooltip-left:before { 616 | right: 0; 617 | margin-right: -12px; 618 | border-left-color: #fff; 619 | } 620 | .leaflet-tooltip-right:before { 621 | left: 0; 622 | margin-left: -12px; 623 | border-right-color: #fff; 624 | } 625 | -------------------------------------------------------------------------------- /src/components/geofence-list-item/geofence-list-item.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{{header}}

4 |

{{details}}

5 |
6 | 7 | 8 | 9 |
10 | -------------------------------------------------------------------------------- /src/components/geofence-list-item/geofence-list-item.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from "@angular/core"; 2 | import { GeofenceService } from "../../services/geofence-service"; 3 | 4 | /* 5 | Generated class for the GeofenceListItem component. 6 | 7 | See https://angular.io/docs/ts/latest/api/core/ComponentMetadata-class.html 8 | for more info on Angular 2 Components. 9 | */ 10 | @Component({ 11 | selector: "geofence-list-item", 12 | templateUrl: "geofence-list-item.html" 13 | }) 14 | export class GeofenceListItem { 15 | @Input() geofence: Geofence; 16 | @Output() onItemTapped: EventEmitter = new EventEmitter(); 17 | 18 | constructor(private geofenceService: GeofenceService) { 19 | 20 | } 21 | 22 | get header() { 23 | return this.geofence.notification.text; 24 | } 25 | 26 | get details() { 27 | return `When ${this.transitionTypeText} within ${this.geofence.radius}m`; 28 | } 29 | 30 | get transitionTypeText() { 31 | switch(this.geofence.transitionType) { 32 | case 1: return "entering region"; 33 | case 2: return "exiting region"; 34 | case 3: return "entering or exiting region"; 35 | } 36 | } 37 | 38 | itemTapped() { 39 | this.onItemTapped.emit(null); 40 | } 41 | 42 | remove() { 43 | this.geofenceService.remove(this.geofence); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /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 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 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ionic Geofence 2", 3 | "short_name": "Ionic Geofence 2", 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 | } 14 | -------------------------------------------------------------------------------- /src/models/geofence.ts: -------------------------------------------------------------------------------- 1 | export const FIXTURES = [ 2 | { id: "1", latitude: 50.3021079, longitude: 18.6771861, radius: 3000, transitionType: 1, notification: { text: "Gliwice Train Station"} }, 3 | { id: "2", latitude: 50.4728049, longitude: 19.0736874, radius: 3000, transitionType: 1, notification: { text: "Pyrzowice Airport"} }, 4 | { id: "3", latitude: 50.0671974, longitude: 19.945232, radius: 3000, transitionType: 1, notification: { text: "Cracow Main Station"} }, 5 | { id: "4", latitude: 52.2287803, longitude: 21.001124, radius: 3000, transitionType: 1, notification: { text: "Warsaw Main Station"} }, 6 | { id: "5", latitude: 40.7257093, longitude: -74.0032786, radius: 4000, transitionType: 3, notification: { text: "New York - SOHO"} }, 7 | { id: "6", latitude: 34.0033907, longitude: -118.5069657, radius: 3000, transitionType: 2, notification: { text: "LA - Santa Monica State Beach"} }, 8 | { id: "7", latitude: 25.8938595, longitude: -80.1330216, radius: 500, transitionType: 1, notification: { text: "Dexter's Apartment - Miami Bay Harbour" } }, 9 | ]; 10 | -------------------------------------------------------------------------------- /src/pages/geofence-details/geofence-details.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Edit Geofence 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | When you? 19 | 20 | Enter Region 21 | Exit Region 22 | Enter or Exit Region 23 | 24 | 25 | 26 | 27 | on 28 | {{radius}}m 29 | 30 | 31 | 32 |
33 |
34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /src/pages/geofence-details/geofence-details.scss: -------------------------------------------------------------------------------- 1 | .geofence-details-page { 2 | .geofence-input-list { 3 | margin-bottom: 15px; 4 | } 5 | 6 | .geofence-radius-container { 7 | display: flex; 8 | justify-content: space-between; 9 | padding: 5px; 10 | 11 | .geofence-radius-input-container { 12 | width: 100%; 13 | padding-left: 10px; 14 | padding-right: 10px; 15 | 16 | input { 17 | width: 100%; 18 | } 19 | } 20 | } 21 | 22 | .map-container { 23 | width: 100%; 24 | position: absolute; 25 | top: 150px; 26 | bottom: 0px; 27 | } 28 | 29 | #map { 30 | width: 100%; 31 | height: 100%; 32 | } 33 | 34 | .scroll-content { 35 | overflow: hidden; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/pages/geofence-details/geofence-details.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NavController, NavParams, MenuController } from "ionic-angular"; 3 | import * as Leaflet from "leaflet"; 4 | import { GeofenceService } from "../../services/geofence-service"; 5 | 6 | @Component({ 7 | templateUrl: "geofence-details.html" 8 | }) 9 | export class GeofenceDetailsPage { 10 | private geofence: Geofence; 11 | private _radius: number; 12 | private _latLng: any; 13 | private notificationText: string; 14 | private transitionType: string; 15 | private circle: any; 16 | private marker: any; 17 | private map: any; 18 | 19 | constructor( 20 | private nav: NavController, 21 | navParams: NavParams, 22 | private geofenceService: GeofenceService, 23 | private menu: MenuController 24 | ) { 25 | this.geofenceService = geofenceService; 26 | this.geofence = navParams.get("geofence"); 27 | this.transitionType = this.geofence.transitionType.toString(); 28 | this.notificationText = this.geofence.notification.text; 29 | this._radius = this.geofence.radius; 30 | this._latLng = Leaflet.latLng(this.geofence.latitude, this.geofence.longitude); 31 | } 32 | 33 | get radius() { 34 | return this._radius; 35 | } 36 | 37 | set radius(value) { 38 | this._radius = value; 39 | this.circle.setRadius(value); 40 | } 41 | 42 | set latLng(value) { 43 | this._latLng = value; 44 | this.circle.setLatLng(value); 45 | this.marker.setLatLng(value); 46 | } 47 | 48 | get latLng() { 49 | return this._latLng; 50 | } 51 | 52 | ionViewDidLoad() { 53 | this.menu.enable(false); 54 | // workaround map is not correctly displayed 55 | // maybe this should be done in some other event 56 | setTimeout(this.loadMap.bind(this), 100); 57 | } 58 | 59 | loadMap() { 60 | this.map = Leaflet 61 | .map("map") 62 | .setView(this.latLng, 13) 63 | .on("click", this.onMapClicked.bind(this)) 64 | 65 | Leaflet.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") 66 | .addTo(this.map); 67 | 68 | this.marker = Leaflet 69 | .marker(this.latLng, { draggable: true }) 70 | .on("dragend", this.onMarkerPositionChanged.bind(this)) 71 | .addTo(this.map); 72 | 73 | this.circle = Leaflet.circle(this.latLng, this.radius).addTo(this.map); 74 | } 75 | 76 | onMapClicked(e) { 77 | this.latLng = e.latlng; 78 | } 79 | 80 | onMarkerPositionChanged(e) { 81 | const latlng = e.target.getLatLng(); 82 | 83 | this.latLng = latlng; 84 | } 85 | 86 | saveChanges() { 87 | const geofence = this.geofence; 88 | 89 | geofence.notification.text = this.notificationText; 90 | geofence.radius = this.radius; 91 | geofence.latitude = this.latLng.lat; 92 | geofence.longitude = this.latLng.lng; 93 | geofence.transitionType = parseInt(this.transitionType, 10); 94 | 95 | this.geofenceService.addOrUpdate(geofence).then(() => { 96 | this.nav.pop(); 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/pages/geofence-list/geofence-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | Ionic2 Geofence Example 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | There are no geofences yet. 19 | 20 | 21 | 22 | 23 | Loading geofences... 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /src/pages/geofence-list/geofence-list.scss: -------------------------------------------------------------------------------- 1 | .geofence-list-page { 2 | ion-list { 3 | margin-bottom: 80px; /*space for fab button*/ 4 | } 5 | } 6 | 7 | .add-geofence-button { 8 | z-index: 100; 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/geofence-list/geofence-list.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NavController, Platform, MenuController } from "ionic-angular"; 3 | import { GeofenceDetailsPage } from "../geofence-details/geofence-details"; 4 | import { GeofenceService } from "../../services/geofence-service"; 5 | import { Splashscreen } from "ionic-native"; 6 | 7 | @Component({ 8 | templateUrl: "geofence-list.html" 9 | }) 10 | export class GeofenceListPage { 11 | isLoading: boolean = false; 12 | geofences: [Geofence]; 13 | 14 | constructor( 15 | private nav: NavController, 16 | private geofenceService: GeofenceService, 17 | private platform: Platform, 18 | private menu: MenuController 19 | ) { 20 | this.isLoading = true; 21 | this.platform.ready().then(() => { 22 | this.geofenceService.findAll() 23 | .then(geofences => { 24 | this.geofences = geofences; 25 | this.isLoading = false; 26 | }) 27 | .catch(() => this.isLoading = false); 28 | }); 29 | } 30 | 31 | ionViewDidEnter() { 32 | this.menu.enable(true); 33 | } 34 | 35 | ionViewDidLoad() { 36 | this.platform.ready().then(() => { 37 | Splashscreen.hide(); 38 | }); 39 | } 40 | 41 | new() { 42 | navigator.geolocation.getCurrentPosition( 43 | (position) => { 44 | const geofence = this.geofenceService.create({ 45 | longitude: position.coords.longitude, 46 | latitude: position.coords.latitude, 47 | }); 48 | 49 | this.transitionToDetailsPage(geofence); 50 | }, 51 | (error) => { 52 | 53 | }, 54 | { timeout: 5000 } 55 | ); 56 | } 57 | 58 | geofenceItemTapped(geofence) { 59 | this.transitionToDetailsPage(geofence); 60 | } 61 | 62 | transitionToDetailsPage(geofence) { 63 | this.nav.push(GeofenceDetailsPage, { 64 | geofence 65 | }) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /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/services/geofence-plugin-mock.ts: -------------------------------------------------------------------------------- 1 | import { FIXTURES } from "../models/geofence"; 2 | 3 | export const GeofencePluginMock = { 4 | addOrUpdate(fences) { 5 | console.log("Mocked geofence plugin addOrUpdate", fences); 6 | 7 | return Promise.resolve(); 8 | }, 9 | getWatched() { 10 | return Promise.resolve(JSON.stringify(FIXTURES)); 11 | }, 12 | remove(ids) { 13 | console.log("Mocked geofence plugin remove", ids); 14 | 15 | return Promise.resolve(); 16 | }, 17 | removeAll() { 18 | console.log("Mocked geofence plugin removeAll"); 19 | 20 | return Promise.resolve(); 21 | }, 22 | initialize() { 23 | console.log("Mocked geofence plugin initialize"); 24 | 25 | return Promise.resolve(); 26 | }, 27 | onTransitionReceived() { 28 | console.log("Mocked geofence plugin onTransitionReceived"); 29 | }, 30 | onNotificationClicked() { 31 | console.log("Mocked geofence plugin onNotificationclicked"); 32 | } 33 | }; 34 | 35 | export const TransitionType = { 36 | ENTER: 1, 37 | EXIT: 2, 38 | BOTH: 3, 39 | } 40 | -------------------------------------------------------------------------------- /src/services/geofence-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import generateUUID from "../utils/uuid"; 3 | 4 | @Injectable() 5 | export class GeofenceService { 6 | private geofences: Geofence[]; 7 | 8 | create(attributes) { 9 | const defaultGeofence = { 10 | id: generateUUID(), 11 | latitude: 50, 12 | longitude: 50, 13 | radius: 1000, 14 | transitionType: window.TransitionType.ENTER, 15 | notification: { 16 | id: this.getNextNotificationId(), 17 | title: "Ionic geofence example", 18 | text: "", 19 | icon: "res://ic_menu_mylocation", 20 | openAppOnClick: true, 21 | }, 22 | }; 23 | 24 | return Object.assign(defaultGeofence, attributes); 25 | } 26 | 27 | clone(geofence: Geofence) { 28 | return JSON.parse(JSON.stringify(geofence)); 29 | } 30 | 31 | addOrUpdate(geofence: Geofence) { 32 | return window.geofence.addOrUpdate(geofence) 33 | .then(() => this.findById(geofence.id)) 34 | .then((found) => { 35 | if (!found) { 36 | this.geofences.push(geofence); 37 | } else { 38 | const index = this.geofences.indexOf(found); 39 | 40 | this.geofences[index] = geofence; 41 | } 42 | }); 43 | } 44 | 45 | findAll() { 46 | return window.geofence.getWatched() 47 | .then((geofencesJson) => { 48 | const geofences = JSON.parse(geofencesJson); 49 | 50 | this.geofences = geofences; 51 | return geofences; 52 | }); 53 | } 54 | 55 | findById(id) { 56 | const found = this.geofences.filter(g => g.id === id); 57 | 58 | if (found.length > 0) { 59 | return found[0]; 60 | } 61 | 62 | return undefined; 63 | } 64 | 65 | removeAll() { 66 | return window.geofence.removeAll().then(() => { 67 | this.geofences.length = 0; 68 | }); 69 | } 70 | 71 | remove(geofence) { 72 | return window.geofence.remove(geofence.id).then(() => { 73 | this.geofences.splice(this.geofences.indexOf(geofence), 1); 74 | }); 75 | } 76 | 77 | private getNextNotificationId() { 78 | var max = 0; 79 | 80 | this.geofences.forEach(function (gf) { 81 | if (gf.notification && gf.notification.id) { 82 | if (gf.notification.id > max) { 83 | max = gf.notification.id; 84 | } 85 | } 86 | }); 87 | 88 | return max + 1; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /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: #387ef5, 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 | -------------------------------------------------------------------------------- /src/utils/uuid.ts: -------------------------------------------------------------------------------- 1 | export default function() { 2 | var d = new Date().getTime(); 3 | let uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 4 | let r = (d + Math.random()*16)%16 | 0; 5 | d = Math.floor(d/16); 6 | 7 | return (c == "x" ? r : (r&0x3|0x8)).toString(16); 8 | }); 9 | return uuid; 10 | } 11 | -------------------------------------------------------------------------------- /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 | "typings/**/*.ts" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "typings/main", 23 | "typings/main.d.ts" 24 | ], 25 | "compileOnSave": false, 26 | "atom": { 27 | "rewriteTsconfig": false 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": {}, 3 | "devDependencies": {}, 4 | "ambientDependencies": { 5 | "cordova-plugin-geofence": "file:../cordova/cordova-plugin-geofence/typings/cordova-plugin-geofence.d.ts", 6 | "leaflet": "registry:dt/leaflet#0.7.3+20160330160843" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/globals/cordova-plugin-geofence/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/cowbell/cordova-plugin-geofence/master/typings/cordova-plugin-geofence.d.ts 3 | interface TransitionType { 4 | ENTER: number; 5 | EXIT: number; 6 | BOTH: number; 7 | } 8 | 9 | interface Window { 10 | geofence: GeofencePlugin; 11 | TransitionType: TransitionType; 12 | } 13 | 14 | interface GeofencePlugin { 15 | initialize( 16 | successCallback?: (result: any) => void, 17 | errorCallback?: (error: string) => void 18 | ): Promise; 19 | 20 | addOrUpdate( 21 | geofence: Geofence | Geofence[], 22 | successCallback?: (result: any) => void, 23 | errorCallback?: (error: string) => void 24 | ): Promise; 25 | 26 | remove( 27 | id: number | number[], 28 | successCallback?: (result: any) => void, 29 | errorCallback?: (error: string) => void 30 | ): Promise; 31 | 32 | removeAll( 33 | successCallback?: (result: any) => void, 34 | errorCallback?: (error: string) => void 35 | ): Promise; 36 | 37 | getWatched( 38 | successCallback?: (result: any) => void, 39 | errorCallback?: (error: string) => void 40 | ): Promise; 41 | 42 | onTransitionReceived: (geofences: Geofence[]) => void; 43 | onNotificationClicked: (notificationData: Object) => void; 44 | } 45 | 46 | interface Geofence { 47 | id: string; 48 | latitude: number; 49 | longitude: number; 50 | radius: number; 51 | transitionType: number; 52 | notification?: Notification; 53 | } 54 | 55 | interface Notification { 56 | id?: number; 57 | title?: string; 58 | text: string; 59 | smallIcon?: string; 60 | icon?: string; 61 | openAppOnClick?: boolean; 62 | vibration?: number[]; 63 | data?: Object; 64 | } 65 | -------------------------------------------------------------------------------- /typings/globals/cordova-plugin-geofence/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/cowbell/cordova-plugin-geofence/master/typings/cordova-plugin-geofence.d.ts", 5 | "raw": "github:cowbell/cordova-plugin-geofence/typings/cordova-plugin-geofence.d.ts", 6 | "typings": "https://raw.githubusercontent.com/cowbell/cordova-plugin-geofence/master/typings/cordova-plugin-geofence.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | --------------------------------------------------------------------------------