├── .bowerrc
├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .yo-rc.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bower.json
├── dist
├── ionic-settings.css
├── ionic-settings.js
├── ionic-settings.min.css
└── ionic-settings.min.js
├── gulpfile.js
├── karma-dist-concatenated.conf.js
├── karma-dist-minified.conf.js
├── karma-src.conf.js
├── lib
└── ionic
│ └── ionic.bundle.js
├── nbproject
├── project.properties
└── project.xml
├── package.json
├── src
└── ionic-settings
│ ├── ionic-settings.scss
│ └── ionicSettings.module.js
└── test
└── unit
└── ionic-settings
└── ionicSettingsSpec.js
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower"
3 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ._*
2 | .~lock.*
3 | .buildpath
4 | .DS_Store
5 | .idea
6 | .project
7 | .settings
8 |
9 | # Ignore node stuff
10 | node_modules/
11 | npm-debug.log
12 | libpeerconnection.log
13 |
14 | # OS-specific
15 | .DS_Store
16 |
17 | # Bower components
18 | bower
19 | /nbproject/private/
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise": true,
3 | "camelcase": true,
4 | "curly": true,
5 | "eqeqeq": true,
6 | "es3": false,
7 | "forin": true,
8 | "freeze": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": "nofunc",
12 | "newcap": true,
13 | "noarg": true,
14 | "noempty": true,
15 | "nonbsp": true,
16 | "nonew": true,
17 | "plusplus": false,
18 | "quotmark": "single",
19 | "undef": true,
20 | "unused": false,
21 | "strict": false,
22 | "maxparams": 10,
23 | "maxdepth": 5,
24 | "maxstatements": 40,
25 | "maxcomplexity": 8,
26 | "maxlen": 120,
27 |
28 | "asi": false,
29 | "boss": false,
30 | "debug": false,
31 | "eqnull": true,
32 | "esnext": false,
33 | "evil": false,
34 | "expr": false,
35 | "funcscope": false,
36 | "globalstrict": false,
37 | "iterator": false,
38 | "lastsemic": false,
39 | "laxbreak": false,
40 | "laxcomma": false,
41 | "loopfunc": true,
42 | "maxerr": false,
43 | "moz": false,
44 | "multistr": false,
45 | "notypeof": false,
46 | "proto": false,
47 | "scripturl": false,
48 | "shadow": false,
49 | "sub": true,
50 | "supernew": false,
51 | "validthis": false,
52 | "noyield": false,
53 |
54 | "browser": true,
55 | "node": true,
56 |
57 | "globals": {
58 | "angular": false,
59 | "$": false
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: ["0.10"]
3 | before_script:
4 | - npm install -g bower
5 | - bower install
6 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-angularjs-library": {
3 | "props": {
4 | "author": {
5 | "name": "Ivan Weber",
6 | "email": "ivan.weber@gmx.de"
7 | },
8 | "libraryName": {
9 | "original": "ionic-settings",
10 | "camelized": "ionicSettings",
11 | "dasherized": "ionic-settings",
12 | "slugified": "ionic-settings",
13 | "parts": [
14 | "ionic",
15 | "settings"
16 | ]
17 | },
18 | "includeModuleDirectives": false,
19 | "includeModuleFilters": false,
20 | "includeModuleServices": false,
21 | "includeAngularModuleResource": false,
22 | "includeAngularModuleCookies": false,
23 | "includeAngularModuleSanitize": false,
24 | "librarySrcDirectory": "src/ionic-settings",
25 | "libraryUnitTestDirectory": "test/unit/ionic-settings",
26 | "libraryUnitE2eDirectory": "test/e2e/ionic-settings"
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 1.0.2 (2016-05-15)
2 |
3 | #### Bug fixes
4 |
5 | * **selection** fixed issue with multiple selection components
6 |
7 | ### 1.0.1 (2016-04-29)
8 |
9 | #### Changes
10 |
11 | * **initializing:** simplified the initializing process
12 | * **pin:** improved pin view appearance and usability
13 | * **setting types:** added range and input setting types
14 |
15 | ### 1.0.0 (2016-04-25)
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Ivan Weber
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
13 | all 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
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #
ionic-settings
2 |
3 | * [Introduction](#1-introduction)
4 | * [Usage](#2-usage)
5 | * [Configuration provider](#3-configuration-provider)
6 | * [Services](#4-services)
7 | * [Directives](#5-directives)
8 |
9 | ##1. Introduction
10 | This plugin provides settings and lock screen for your ionic app. The keys and values are stored as app preferences on condition that you have installed
11 | [the app preferences plugin](https://github.com/apla/me.apla.cordova.app-preferences), otherwise they are stored in `localStorage`.
12 | You can test the plugin via the [ionic view app](http://view.ionic.io/) with the ID **141b234c**.
13 |
14 | ##1.1 Features
15 | * 5 setting types: `selection`, `toggle`, `input`, `range` and `pin`
16 | * 2 read-only types: `button` and `text`
17 | * grouping of settings
18 | * including settings into a view or modal view
19 | * customizing of settings appearance (color, icons and button positions)
20 | * storage as app preferences using the above mentioned plugin, otherwise in `localStorage`
21 | * touch id support if the [touch id plugin](https://github.com/leecrossley/cordova-plugin-touchid) is installed
22 |
23 | ##1.2 Screenshots
24 |
25 |
26 | ##1.3 Demo
27 | ##### Note: To test the lock screen feature respectively settings persistence please reload the demo after value changing.
28 | #### [Ionic Playground](http://play.ionic.io/app/28ea71301b60)
29 | #### 
30 |
31 | ##1.4 License
32 |
33 | [MIT](https://github.com/ivandroid/ionic-settings/blob/master/LICENSE)
34 |
35 | ##1.5 Versions
36 |
37 | [CHANGELOG](https://github.com/ivandroid/ionic-settings/blob/master/CHANGELOG.md)
38 |
39 | ###1.6 Author
40 | * E-Mail: ivan.weber@gmx.de
41 | * Twitter: https://twitter.com/hybrid_app
42 | * Github: https://github.com/ivandroid
43 | * Ionic Market: https://market.ionic.io/user/6540
44 | * Your support: If you find this project useful and want support its development you can press the button below or star the project. Thanks in advance!
45 |
46 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=ivan%2eweber%40gmx%2ede&lc=DE&item_name=GithubRepositories&no_note=0¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHostedGuest)
47 |
48 | ##2. Usage
49 | 1. Get the files from here or install from bower:
50 |
51 | ```
52 | bower install ionic-settings
53 | ```
54 |
55 | 2. Include the javascript and css files or the minified versions into your `index.html` file.
56 |
57 | ```html
58 |
59 |
60 | ```
61 |
62 | 3. Add the module `ionicSettings` to your application dependencies:
63 |
64 | ```javascript
65 | angular.module('starter', ['ionic', 'ionicSettings'])
66 | ```
67 |
68 | 4. Settings are defined according to the following pattern:
69 |
70 | ```javascript
71 | var settings = {
72 | label1: 'Group 1', // OPTIONAL GROUP LABEL
73 | mySelection: { // KEY
74 | type: 'selection', // TYPE
75 | values: ['value 1', 'value 2', 'value 3', 'value 4', 'value 5'], // IN THIS CASE: SELECTION ARRAY
76 | label: 'Selection', // LABEL
77 | value: 'value 1', // VALUE
78 | icon: 'ion-checkmark-round' // OPTIONAL ICON
79 | },
80 | myToggle: {
81 | type: 'toggle',
82 | label: 'Toggle',
83 | value: true,
84 | icon: 'ion-toggle'
85 | },
86 | myInput: {
87 | type: 'input',
88 | label: 'Input',
89 | inputType: 'text',
90 | value: 'Hello World!',
91 | icon: 'ion-edit'
92 | },
93 | myRange: {
94 | type: 'range',
95 | label: 'Range',
96 | iconLeft: 'ion-minus-circled',
97 | iconRight: 'ion-plus-circled',
98 | min: 0,
99 | max: 100,
100 | value: 50
101 | },
102 | label2: 'Group 2',
103 | myButton: {
104 | type: 'button',
105 | label: 'Button',
106 | icon: 'ion-disc',
107 | onClick: function() {
108 | alert('Hello world!');
109 | }
110 | },
111 | myText: {
112 | type: 'text',
113 | label: 'Text',
114 | icon: 'ion-document-text',
115 | value: '
Hello World!
'
116 | },
117 | myPin: {
118 | type: 'pin',
119 | label: 'PIN & Touch ID',
120 | value: '',
121 | icon: 'ion-locked',
122 | onValid: function() { // OPTIONAL ACTION ON VALID PIN
123 | alert('Success!');
124 | },
125 | onInvalid: function() { // OPTIONAL ACTION ON INVALID PIN
126 | alert('Fail!');
127 | }
128 | }
129 | };
130 | ```
131 |
132 | 5. To initialize the settings invoke the `init()` method of the `$ionicSettings` service (returns promise) passing your settings model object. If you'd like to protect your app with a pin / touch id, make sure to initialize your settings before the main state of your app is loaded like shown below.
133 |
134 | ```javascript
135 | // INITIALIZATION IN CONFIG PHASE (USING PIN)
136 | angular.module('starter', ['ionic', 'ionicSettings'])
137 | .config(function($stateProvider, $urlRouterProvider) {
138 | $stateProvider
139 | .state('main', {
140 | url: '/main',
141 | abstract: true,
142 | templateUrl: 'templates/main.html',
143 | resolve: {
144 | settings: function($ionicSettings, $ionicPopup) {
145 | var settings = {
146 | toggle1: {
147 | type: 'toggle',
148 | label: 'Toggle 1',
149 | value: true
150 | },
151 | toggle2: {
152 | type: 'toggle',
153 | label: 'Toggle 2',
154 | value: false
155 | },
156 | pin: {
157 | type: 'pin',
158 | label: 'PIN',
159 | value: '',
160 | onValid: function() {
161 | $ionicPopup.alert({
162 | title: 'Success',
163 | template: 'Welcome!'
164 | });
165 | },
166 | onInvalid: function($event, wrongPinValue) {
167 | $ionicPopup.alert({
168 | title: 'Fail',
169 | template: 'Wrong pin: ' + wrongPinValue + '! Try again.'
170 | });
171 | }
172 | }
173 | };
174 | return $ionicSettings.init(settings);
175 | }
176 | }
177 | })
178 | });
179 | // INITIALIZATION IN CONTROLLER (WITHOUT PIN)
180 | angular.module('starter.controllers', [])
181 | .controller('YourCtrl', function($scope, $ionicSettings) {
182 | $ionicSettings.init({
183 | awesomeSelection: {
184 | type: 'selection',
185 | values: ['one', 'two', 'three'],
186 | label: 'Awesome Selection',
187 | value: 'two'
188 | },
189 | coolToggle: {
190 | type: 'toggle',
191 | label: 'Cool toggle',
192 | value: true
193 | }
194 | });
195 | });
196 | ```
197 |
198 | 6. To include your settings into a view simply use the `ion-settings` directive within the `ion-content` element. To show the settings as modal add the directive `ion-settings-button` to the navigation bar. Pressing this button opens the modal.
199 |
200 | ```html
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | ```
214 |
215 | ##3. Configuration provider
216 |
217 | Via the `$ionicSettingsConfigProvider` the following options can be set in the configuration phase:
218 |
219 | option|description|type|accepted values|default value
220 | ---|---|---|---|---
221 | color|color of setting elements|string|ionic color names|*positive*
222 | icon|settings icon|string|ion-icons|*ion-android-settings* for Android and *ion-ios-gear* for iOS
223 | iconClose|close button icon|string|ion-icons|*ion-android-close* for Android and *ion-ios-close-empty* for iOS
224 | iconClosePosition|close button icon position|string|*right, left*|*right*
225 | modalAnimation|modal animation|string|ionic modal animation identifiers|custom animation for Android (ionic-settings.scss) and *slide-in-up* for iOS
226 | title|settings title|string|text|*Settings*
227 | touchID|touch id support|boolean|*true, false*|*true*
228 |
229 | #### Example
230 | ##### Code
231 |
232 | ```javascript
233 | angular.module('starter', ['ionic', 'ionicSettings'])
234 | .config(function($ionicSettingsConfigProvider) {
235 | $ionicSettingsConfigProvider.setColor('assertive');
236 | $ionicSettingsConfigProvider.setIcon('ion-wrench');
237 | $ionicSettingsConfigProvider.setIconClose('ion-close-circled');
238 | $ionicSettingsConfigProvider.setIconClosePosition('left');
239 | $ionicSettingsConfigProvider.setModalAnimation('slide-in-up');
240 | $ionicSettingsConfigProvider.setTitle('My awesome settings');
241 | $ionicSettingsConfigProvider.setTouchID(false);
242 | });
243 | ```
244 |
245 | ##### Result
246 |
247 |
248 | ##4. Services
249 | ### Service `$ionicSettings`
250 |
251 | Using this service you have access to the following events and methods:
252 |
253 | event|description|return-value
254 | ---|---|---
255 | `changed`|Setting changed event|object containing key and value of a changed setting
256 |
257 | method|description|return-value
258 | ---|---|---
259 | `get(key)`|Getting a value by key|value of a given key
260 | `getData()`|Getting all settings keys and values|object containing all key value pairs
261 | `init(modelObject)`|Initializing of settings passing your settings model object|initialized settings model object as promise
262 | `set(key, value)`|Setting a value by key|none
263 |
264 | #### Example
265 |
266 | ```javascript
267 | angular.module('starter.controllers', [])
268 | .controller('YourCtrl', function($rootScope, $ionicSettings) {
269 | $rootScope.$on($ionicSettings.changed, function($event, changedSetting) {
270 | alert(changedSetting.key + ' -> ' + changedSetting.value);
271 | });
272 | });
273 | ```
274 |
275 | ```javascript
276 | angular.module('starter.controllers', [])
277 | .controller('YourCtrl', function($scope, $ionicSettings) {
278 | $scope.set = function(key, value) {
279 | $ionicSettings.set(key, value);
280 | };
281 | $scope.get = function(key) {
282 | alert($ionicSettings.get(key));
283 | };
284 | });
285 | ```
286 |
287 | ##5. Directives
288 |
289 | ### Directive `ion-settings`
290 |
291 | Use this directive to include your settings into a view.
292 |
293 | #### Example
294 |
295 | ```html
296 |
297 |
298 |
299 |
300 |
301 | ```
302 |
303 | ### Directive `ion-settings-button`
304 |
305 | Use this directive to include the settings button to the navigation bar and use settings as modal.
306 |
307 | #### Example
308 |
309 | ```html
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 | ```
318 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ionic-settings",
3 | "homepage": "https://github.com/ivandroid/ionic-settings",
4 | "version": "1.0.2",
5 | "authors": [
6 | {
7 | "name": "Ivan Weber",
8 | "email": "ivan.weber@gmx.de"
9 | }
10 | ],
11 | "license": "MIT",
12 | "main": [
13 | "dist/ionic-settings.min.js",
14 | "dist/ionic-settings.min.css"
15 | ],
16 | "ignore": [
17 | "src",
18 | "test",
19 | "gulpfile.js",
20 | "karma-*.conf.js",
21 | "**/.*"
22 | ],
23 | "dependencies": {},
24 | "devDependencies": {
25 | "angular-mocks": ">=1.2.0",
26 | "angular-scenario": ">=1.2.0",
27 | "angular": ">=1.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/dist/ionic-settings.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2016 Ivan Weber
3 | *
4 | * ionic-settings, v1.0.2
5 | *
6 | * Licensed under the MIT license. Please see LICENSE for more information.
7 | *
8 | */
9 | .ionic-settings-numbers > div {
10 | margin-top: 20px !important; }
11 |
12 | .ionic-settings-numbers-button {
13 | border-radius: 50% !important;
14 | height: 70px !important;
15 | width: 72px !important;
16 | font-size: 20px !important;
17 | margin: 10px !important; }
18 |
19 | .ionic-settings-numbers-dot {
20 | font-size: 27px;
21 | margin: 10px; }
22 |
23 | .ionic-settings-numbers-row {
24 | display: flex;
25 | flex-direction: row;
26 | justify-content: center;
27 | width: 100%;
28 | height: 60px;
29 | margin-bottom: 20px; }
30 |
31 | .ionic-settings-numbers-value {
32 | font-size: 38px;
33 | margin: 10px; }
34 |
35 | .ionic-settings-icon-smaller:before {
36 | font-size: 27px; }
37 |
38 | .ionic-settings-icon-larger:before {
39 | font-size: 30px !important;
40 | margin-bottom: 27px !important; }
41 |
42 | .ionic-settings-left {
43 | float: left; }
44 |
45 | .ionic-settings-right {
46 | float: right; }
47 |
48 | .ionic-settings-android {
49 | font-size: 14px;
50 | color: #696969;
51 | margin-top: 25px; }
52 |
53 | .ionic-settings-ios {
54 | color: gray; }
55 |
56 | .animate-ng-show.ng-hide-add {
57 | animation: 0.5s zoomOut ease;
58 | -webkit-animation: 0.5s zoomOut ease; }
59 |
60 | .animate-ng-show.ng-hide-remove {
61 | animation: 0.5s zoomIn ease;
62 | -webkit-animation: 0.5s zoomIn ease; }
63 |
64 | .ionic-settings-animate-modal-android {
65 | -webkit-transform: scale(0);
66 | transform: scale(0);
67 | opacity: 0; }
68 |
69 | .ionic-settings-animate-modal-android.ng-enter, .fade-in > .ng-enter {
70 | -webkit-transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 250ms;
71 | transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 250ms; }
72 |
73 | .ionic-settings-animate-modal-android.ng-enter-active,
74 | .ionic-settings-animate-modal-android > .ng-enter-active {
75 | -webkit-transform: scale(1);
76 | transform: scale(1);
77 | opacity: 1; }
78 |
79 | .ionic-settings-animate-modal-android.ng-leave, .fade-in > .ng-leave {
80 | -webkit-transition: all ease-in-out 250ms;
81 | transition: all ease-in-out 250ms; }
82 |
83 | @-webkit-keyframes zoomIn {
84 | from {
85 | opacity: 0;
86 | -webkit-transform: scale3d(0.3, 0.3, 0.3);
87 | transform: scale3d(0.3, 0.3, 0.3); }
88 | 50% {
89 | opacity: 1; } }
90 |
91 | @keyframes zoomIn {
92 | from {
93 | opacity: 0;
94 | -webkit-transform: scale3d(0.3, 0.3, 0.3);
95 | transform: scale3d(0.3, 0.3, 0.3); }
96 | 50% {
97 | opacity: 1; } }
98 |
99 | .zoomIn {
100 | -webkit-animation-name: zoomIn;
101 | animation-name: zoomIn; }
102 |
103 | @-webkit-keyframes zoomOut {
104 | from {
105 | opacity: 1; }
106 | 50% {
107 | opacity: 0;
108 | -webkit-transform: scale3d(0.3, 0.3, 0.3);
109 | transform: scale3d(0.3, 0.3, 0.3); }
110 | to {
111 | opacity: 0; } }
112 |
113 | @keyframes zoomOut {
114 | from {
115 | opacity: 1; }
116 | 50% {
117 | opacity: 0;
118 | -webkit-transform: scale3d(0.3, 0.3, 0.3);
119 | transform: scale3d(0.3, 0.3, 0.3); }
120 | to {
121 | opacity: 0; } }
122 |
123 | .zoomOut {
124 | -webkit-animation-name: zoomOut;
125 | animation-name: zoomOut; }
126 |
--------------------------------------------------------------------------------
/dist/ionic-settings.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2016 Ivan Weber
3 | *
4 | * ionic-settings, v1.0.2
5 | *
6 | * Licensed under the MIT license. Please see LICENSE for more information.
7 | *
8 | */
9 |
10 | /* global ionic, touchid */
11 |
12 | (function(angular) {
13 |
14 | angular.module("ionicSettings.constants", [])
15 | .constant("IONIC_SETTINGS_BUTTON", "button")
16 | .constant("IONIC_SETTINGS_PIN", "pin")
17 | .constant("IONIC_SETTINGS_PIN_EMPTY", "\u25CB")
18 | .constant("IONIC_SETTINGS_PIN_FILLED", "\u25CF")
19 | .constant("IONIC_SETTINGS_SELECTION", "selection")
20 | .constant("IONIC_SETTINGS_TEXT", "text");
21 |
22 | angular.module("ionicSettings.directives", [])
23 | .directive("ionSettings", [
24 | "$ionicSettings",
25 | "$ionicSettingsConfig",
26 | "IONIC_SETTINGS_PIN", function(
27 | $ionicSettings,
28 | $ionicSettingsConfig,
29 | IONIC_SETTINGS_PIN) {
30 | return {
31 | restrict: "E",
32 | scope: true,
33 | template: '' +
34 | '
{{item}}
' +
35 | '
' +
36 | '
' +
40 | '{{item.label}}
' +
41 | '{{item.value}}
' +
42 | '{{item.value}}
' +
43 | '' +
44 | '' +
45 | '' +
46 | '
' +
50 | '{{item.label}}' +
51 | '' +
52 | '' +
53 | '
' +
59 | '{{item.label}}' +
60 | '' +
61 | '' +
62 | '
' +
63 | '' +
64 | '' +
68 | '
' +
69 | '
' +
73 | '
' +
81 | '
' +
82 | '
',
83 | link: function($scope) {
84 | var active = {};
85 | var modals = {};
86 |
87 | $scope.data = $ionicSettings.getData();
88 | $scope.isAndroid = $ionicSettingsConfig.isAndroid;
89 | $scope.iconClose = $ionicSettingsConfig.iconClose;
90 | $scope.iconClosePosition = $ionicSettingsConfig.iconClosePosition;
91 | $scope.iconDone = $ionicSettingsConfig.iconDone;
92 | $scope.iconSelected = $ionicSettingsConfig.isAndroid ? "ion-android-done" : "ion-ios-checkmark-empty";
93 | $scope.pin = {
94 | active: false
95 | };
96 | $scope.showing = false;
97 | $scope.color = $ionicSettingsConfig.color;
98 | $scope.elementColor = $scope.color === "stable" || $scope.color === "default" || $scope.color === "light" ? "dark" : $scope.color;
99 | $scope.barColor = "bar-" + $scope.color;
100 |
101 | angular.forEach($scope.data, function(item, key) {
102 | modals[key] = $ionicSettings.createModal($scope, key);
103 |
104 | $scope.$watch("data." + key + ".value", function(newValue, oldValue) {
105 | if (angular.isDefined(newValue) && newValue !== oldValue) {
106 | $ionicSettings.store(key, newValue).then(function() {
107 | $scope.$root.$broadcast($ionicSettings.changed, {
108 | key: key,
109 | value: newValue
110 | });
111 | }, function() {
112 | $scope.data[key].value = oldValue;
113 | });
114 | }
115 | });
116 | });
117 |
118 | $scope.addValue = function(value) {
119 | if ($scope.value.length === 4) {
120 | $scope.value = "";
121 | }
122 | $scope.value += value;
123 | };
124 |
125 | $scope.clear = function() {
126 | $scope.value = "";
127 | };
128 |
129 | $scope.dismiss = function() {
130 | if (active.item.type === IONIC_SETTINGS_PIN && active.item.value.length !== 4) {
131 | $scope.clear();
132 | $scope.pin.active = false;
133 | }
134 | if (active.modal.isShown()) {
135 | active.modal.hide();
136 | }
137 | };
138 |
139 | $scope.hidePin = function() {
140 | $scope.showing = false;
141 | };
142 |
143 | $scope.doAction = function(key) {
144 | active.modal = modals[key];
145 | active.item = $scope.data[key];
146 | if (active.modal) {
147 | if (active.item.type === IONIC_SETTINGS_PIN) {
148 | if (active.item.value.length === 4) {
149 | active.item.value = "";
150 | } else {
151 | $scope.value = active.item.value;
152 | active.modal.show();
153 | }
154 | } else {
155 | active.modal.show();
156 | }
157 | } else {
158 | if (angular.isFunction(active.item.onClick)) {
159 | active.item.onClick();
160 | }
161 | }
162 | };
163 |
164 | $scope.savePin = function() {
165 | active.item.value = $scope.value;
166 | active.modal.hide();
167 | };
168 |
169 | $scope.select = function(value) {
170 | active.item.value = value;
171 | $scope.dismiss();
172 | };
173 |
174 | $scope.showPin = function() {
175 | $scope.showing = true;
176 | };
177 |
178 | $scope.$on("modal.hidden", $scope.dismiss);
179 |
180 | $scope.$on("$destroy", function() {
181 | for (var key in modals) {
182 | if (modals[key]) {
183 | modals[key].remove();
184 | }
185 | }
186 | });
187 | }
188 | };
189 | }])
190 | .directive("ionSettingsButton", [
191 | "$ionicSettingsConfig",
192 | "$ionicSettings", function(
193 | $ionicSettingsConfig,
194 | $ionicSettings) {
195 | return {
196 | template: '',
197 | $scope: true,
198 | link: function($scope) {
199 | $scope.barColor = "bar-" + $ionicSettingsConfig.color;
200 | $scope.icon = $ionicSettingsConfig.icon;
201 | $scope.iconClose = $ionicSettingsConfig.iconClose;
202 | $scope.iconClosePosition = $ionicSettingsConfig.iconClosePosition;
203 | $scope.settingsModal = $ionicSettings.createModal($scope);
204 | $scope.title = $ionicSettingsConfig.title;
205 |
206 | $scope.dismiss = function() {
207 | $scope.settingsModal.hide();
208 | };
209 | }
210 | };
211 | }])
212 | .directive("ngTouchstart", function () {
213 | return {
214 | controller: ["$scope", "$element", function ($scope, $element) {
215 | $element.bind("touchstart", onTouchStart);
216 | function onTouchStart(event) {
217 | var method = $element.attr("ng-touchstart");
218 | $scope.$event = event;
219 | $scope.$apply(method);
220 | }
221 |
222 | }]
223 | };
224 | })
225 | .directive("ngTouchend", function () {
226 | return {
227 | controller: ["$scope", "$element", function ($scope, $element) {
228 | $element.bind("touchend", onTouchEnd);
229 | function onTouchEnd(event) {
230 | var method = $element.attr("ng-touchend");
231 | $scope.$event = event;
232 | $scope.$apply(method);
233 | }
234 |
235 | }]
236 | };
237 | });
238 |
239 | angular.module("ionicSettings.providers", []).provider("$ionicSettingsConfig", function() {
240 | var android = ionic.Platform.isAndroid();
241 | var ios = ionic.Platform.isIOS();
242 | var color = "positive";
243 | var icon = android ? "ion-android-settings" : "ion-ios-gear";
244 | var iconClose = android ? "ion-android-close" : "ion-ios-close-empty";
245 | var iconClosePosition = "right";
246 | var iconDone = android ? "ion-android-done" : "ion-ios-checkmark-empty";
247 | var modalAnimation = ios ? "slide-in-up" : "ionic-settings-animate-modal-android";
248 | var title = "Settings";
249 | var touchID = true;
250 | return {
251 | setAndroid: function(_android) {
252 | android = _android;
253 | },
254 | setIcon: function(_icon) {
255 | icon = _icon;
256 | },
257 | setIconClose: function(_iconClose) {
258 | iconClose = _iconClose;
259 | },
260 | setIconClosePosition: function(_iconClosePosition) {
261 | iconClosePosition = _iconClosePosition;
262 | },
263 | setModalAnimation: function(_modalAnimation) {
264 | modalAnimation = _modalAnimation;
265 | },
266 | setColor: function(_color) {
267 | color = _color;
268 | },
269 | setTitle: function(_title) {
270 | title = _title;
271 | },
272 | setTouchID: function(_touchID) {
273 | touchID = _touchID;
274 | },
275 | $get: function() {
276 | return {
277 | color: color,
278 | icon: icon,
279 | iconClose: iconClose,
280 | iconClosePosition: iconClosePosition,
281 | iconDone: iconDone,
282 | isAndroid: android,
283 | modalAnimation: modalAnimation,
284 | title: title,
285 | touchID: touchID
286 | };
287 | }
288 | };
289 | });
290 |
291 | angular.module("ionicSettings.services", [])
292 | .factory("$ionicSettings", [
293 | "$ionicSettingsConfig",
294 | "$ionicModal",
295 | "$log",
296 | "$q",
297 | "$rootScope",
298 | "$window",
299 | "IONIC_SETTINGS_PIN",
300 | "IONIC_SETTINGS_SELECTION",
301 | "IONIC_SETTINGS_TEXT", function(
302 | $ionicSettingsConfig,
303 | $ionicModal,
304 | $log,
305 | $q,
306 | $rootScope,
307 | $window,
308 | IONIC_SETTINGS_PIN,
309 | IONIC_SETTINGS_SELECTION,
310 | IONIC_SETTINGS_TEXT) {
311 | var self = this;
312 | var data = {};
313 | var pin;
314 | var $cordovaSplashscreenOn = true;
315 |
316 | self.changed = "$ionicSettings.changed";
317 | self.onInvalidPin = "$ionicSettings.onInvalidPin";
318 | self.onValidPin = "$ionicSettings.onValidPin";
319 |
320 | self.createModal = function($scope, key) {
321 | var html;
322 | if (key) {
323 | var item = data[key];
324 | switch(item.type) {
325 | case IONIC_SETTINGS_SELECTION:
326 | html =
327 | '' +
328 | '' +
329 | '' +
330 | '' + item.label + '
' +
331 | '' +
332 | '' +
333 | '' +
334 | '' +
335 | '{{value}}' +
336 | '' +
337 | '' +
338 | '' +
339 | '';
340 | break;
341 | case IONIC_SETTINGS_PIN:
342 | html =
343 | '' +
344 | '' +
345 | '' +
346 | '' +
347 | '' +
348 | '
' +
349 | '' + item.label + '
' +
350 | '' +
351 | '' +
352 | '' +
353 | '
' +
354 | '' +
355 | '' +
356 | '' +
357 | '
' +
358 | '' +
359 | '{{value[$index]}}' +
360 | '
' +
361 | '
' +
362 | '' +
367 | '
' +
368 | '
' +
369 | '' +
370 | '' +
371 | '' +
379 | '
' +
380 | '
' +
381 | '' +
382 | '';
383 | break;
384 | case IONIC_SETTINGS_TEXT:
385 | html =
386 | '' +
387 | '' +
388 | '' +
389 | '' + item.label + '
' +
390 | '' +
391 | '' +
392 | '' + item.value + '' +
393 | '';
394 | angular.extend($scope, data[key].scopeObject);
395 | break;
396 | }
397 | } else {
398 | html = '' +
399 | '' +
400 | '' +
401 | '{{title}}
' +
402 | '' +
403 | '' +
404 | '' +
405 | '' +
406 | '' +
407 | '';
408 | }
409 | if (html) {
410 | return $ionicModal.fromTemplate(html, {
411 | scope: $scope,
412 | animation: $ionicSettingsConfig.modalAnimation,
413 | backdropClickToClose: false
414 | });
415 | }
416 | };
417 |
418 | self.fetch = function(key) {
419 | var q = $q.defer();
420 | if ($window.plugins) {
421 | $window.plugins.appPreferences.fetch(key).then(function(value) {
422 | if (value === null) {
423 | q.reject(value);
424 | } else {
425 | q.resolve({key: key, value: value});
426 | }
427 | }, function(error) {
428 | q.reject(new Error(error));
429 | });
430 | } else {
431 | var value = localStorage[key];
432 | if (value === undefined) {
433 | q.reject(value);
434 | } else {
435 | if (value === "true" || value === "false") {
436 | value = value === "true";
437 | }
438 | q.resolve({key: key, value: value});
439 | }
440 | }
441 | return q.promise;
442 | };
443 |
444 | self.fetchAll = function() {
445 | var fetching = [];
446 | for (var key in data) {
447 | var item = data[key];
448 | if (angular.isObject(item)) {
449 | fetching.push(self.fetch(key));
450 | }
451 | }
452 | return $q.all(fetching).then(function(result) {
453 | for (var i in result) {
454 | var resultItem = result[i];
455 | var key = resultItem.key;
456 | var value = resultItem.value;
457 |
458 | var item = data[key];
459 | item.value = value;
460 | if (item.type === IONIC_SETTINGS_PIN) {
461 | pin = {
462 | key: key,
463 | value: value
464 | };
465 | }
466 | }
467 | return data;
468 | });
469 | };
470 |
471 | self.get = function(key) {
472 | if (data[key]) {
473 | return data[key].value;
474 | } else {
475 | $log.error("Setting key " + key + " is not defined.");
476 | return;
477 | }
478 | };
479 |
480 | self.getData = function() {
481 | return data;
482 | };
483 |
484 | self.init = function(_data) {
485 | data = _data;
486 |
487 | var q = $q.defer();
488 |
489 | self.fetchAll().then(function(result) {
490 | if (pin && pin.value.length === 4) {
491 | var $scope = $rootScope.$new();
492 | var pinModal = self.createModal($scope, pin.key);
493 |
494 | $scope.color = $ionicSettingsConfig.color;
495 | $scope.barColor = "bar-" + $scope.color;
496 | $scope.start = true;
497 | $scope.value = "";
498 | $scope.valid = false;
499 |
500 | var pinItem = data[pin.key];
501 | if (pinItem.onValid) {
502 | $scope.$on(self.onValidPin, pinItem.onValid);
503 | }
504 | if (pinItem.onInvalid) {
505 | $scope.$on(self.onInvalidPin, pinItem.onInvalid);
506 | }
507 |
508 | $scope.addValue = function(value) {
509 | $scope.value += value;
510 | if ($scope.value.length === 4) {
511 | $scope.valid = $scope.value === pin.value;
512 | if ($scope.valid) {
513 | pinModal.remove().then(function() {
514 | q.resolve(result);
515 | $rootScope.$broadcast(self.onValidPin);
516 | if ($cordovaSplashscreenOn) {
517 | try {
518 | navigator.splashscreen.show();
519 | } catch(e) {}
520 | }
521 | });
522 | } else {
523 | $rootScope.$broadcast(self.onInvalidPin, $scope.value);
524 | $scope.clear();
525 | }
526 | }
527 | if ($scope.value.length > 4) {
528 | $scope.clear();
529 | $scope.value += value;
530 | }
531 | };
532 |
533 | $scope.clear = function() {
534 | $scope.value = "";
535 | };
536 |
537 | pinModal.show().then(function() {
538 | try {
539 | navigator.splashscreen.hide();
540 | } catch(e) {
541 | $cordovaSplashscreenOn = false;
542 | }
543 | if ($ionicSettingsConfig.touchID) {
544 | try {
545 | touchid.checkSupport(function() {
546 | touchid.authenticate(function() {
547 | q.resolve(result);
548 | }, function() {
549 | }, "Touch ID");
550 | });
551 | } catch(e) {}
552 | }
553 | });
554 | } else {
555 | q.resolve(result);
556 | }
557 | }, function() {
558 | self.storeAll(_data).then(function(result) {
559 | q.resolve(result);
560 | });
561 | });
562 |
563 | return q.promise;
564 | };
565 |
566 | self.set = function(key, value) {
567 | data[key].value = value;
568 | return self.store(key, value);
569 | };
570 |
571 | self.store = function(key, value) {
572 | var q = $q.defer();
573 | if ($window.plugins) {
574 | $window.plugins.appPreferences.store(key, value).then(function(value) {
575 | q.resolve(value);
576 | }, function(error) {
577 | q.reject(new Error(error));
578 | });
579 | } else {
580 | localStorage[key] = value;
581 | q.resolve(localStorage[key]);
582 | }
583 | return q.promise;
584 | };
585 |
586 | self.setSelectionValues = function(key, values) {
587 | angular.copy(values, data[key].values);
588 | return self.set(key, "");
589 | };
590 |
591 | self.storeAll = function() {
592 | var storing = [];
593 | for (var key in data) {
594 | var item = data[key];
595 | if (angular.isObject(item)) {
596 | storing.push(self.store(key, item.value));
597 | }
598 | }
599 | return $q.all(storing);
600 | };
601 |
602 | return self;
603 | }]);
604 |
605 | // App
606 | angular.module("ionicSettings", [
607 | "ionicSettings.constants",
608 | "ionicSettings.directives",
609 | "ionicSettings.providers",
610 | "ionicSettings.services"
611 | ]);
612 | })(angular);
613 |
--------------------------------------------------------------------------------
/dist/ionic-settings.min.css:
--------------------------------------------------------------------------------
1 | .ionic-settings-numbers>div{margin-top:20px!important}.ionic-settings-numbers-button{border-radius:50%!important;height:70px!important;width:72px!important;font-size:20px!important;margin:10px!important}.ionic-settings-numbers-dot{font-size:27px;margin:10px}.ionic-settings-numbers-row{display:flex;flex-direction:row;justify-content:center;width:100%;height:60px;margin-bottom:20px}.ionic-settings-numbers-value{font-size:38px;margin:10px}.ionic-settings-icon-smaller:before{font-size:27px}.ionic-settings-icon-larger:before{font-size:30px!important;margin-bottom:27px!important}.ionic-settings-left{float:left}.ionic-settings-right{float:right}.ionic-settings-android{font-size:14px;color:#696969;margin-top:25px}.ionic-settings-ios{color:gray}.animate-ng-show.ng-hide-add{animation:.5s zoomOut ease;-webkit-animation:.5s zoomOut ease}.animate-ng-show.ng-hide-remove{animation:.5s zoomIn ease;-webkit-animation:.5s zoomIn ease}.ionic-settings-animate-modal-android{-webkit-transform:scale(0);transform:scale(0);opacity:0}.fade-in>.ng-enter,.ionic-settings-animate-modal-android.ng-enter{-webkit-transition:all cubic-bezier(.1,.7,.1,1) 250ms;transition:all cubic-bezier(.1,.7,.1,1) 250ms}.ionic-settings-animate-modal-android.ng-enter-active,.ionic-settings-animate-modal-android>.ng-enter-active{-webkit-transform:scale(1);transform:scale(1);opacity:1}.fade-in>.ng-leave,.ionic-settings-animate-modal-android.ng-leave{-webkit-transition:all ease-in-out 250ms;transition:all ease-in-out 250ms}@-webkit-keyframes zoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes zoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}
--------------------------------------------------------------------------------
/dist/ionic-settings.min.js:
--------------------------------------------------------------------------------
1 | !function(n){n.module("ionicSettings.constants",[]).constant("IONIC_SETTINGS_BUTTON","button").constant("IONIC_SETTINGS_PIN","pin").constant("IONIC_SETTINGS_PIN_EMPTY","○").constant("IONIC_SETTINGS_PIN_FILLED","●").constant("IONIC_SETTINGS_SELECTION","selection").constant("IONIC_SETTINGS_TEXT","text"),n.module("ionicSettings.directives",[]).directive("ionSettings",["$ionicSettings","$ionicSettingsConfig","IONIC_SETTINGS_PIN",function(i,t,e){return{restrict:"E",scope:!0,template:'',link:function(o){var s={},l={};o.data=i.getData(),o.isAndroid=t.isAndroid,o.iconClose=t.iconClose,o.iconClosePosition=t.iconClosePosition,o.iconDone=t.iconDone,o.iconSelected=t.isAndroid?"ion-android-done":"ion-ios-checkmark-empty",o.pin={active:!1},o.showing=!1,o.color=t.color,o.elementColor="stable"===o.color||"default"===o.color||"light"===o.color?"dark":o.color,o.barColor="bar-"+o.color,n.forEach(o.data,function(t,e){l[e]=i.createModal(o,e),o.$watch("data."+e+".value",function(t,s){n.isDefined(t)&&t!==s&&i.store(e,t).then(function(){o.$root.$broadcast(i.changed,{key:e,value:t})},function(){o.data[e].value=s})})}),o.addValue=function(n){4===o.value.length&&(o.value=""),o.value+=n},o.clear=function(){o.value=""},o.dismiss=function(){s.item.type===e&&4!==s.item.value.length&&(o.clear(),o.pin.active=!1),s.modal.isShown()&&s.modal.hide()},o.hidePin=function(){o.showing=!1},o.doAction=function(i){s.modal=l[i],s.item=o.data[i],s.modal?s.item.type===e?4===s.item.value.length?s.item.value="":(o.value=s.item.value,s.modal.show()):s.modal.show():n.isFunction(s.item.onClick)&&s.item.onClick()},o.savePin=function(){s.item.value=o.value,s.modal.hide()},o.select=function(n){s.item.value=n,o.dismiss()},o.showPin=function(){o.showing=!0},o.$on("modal.hidden",o.dismiss),o.$on("$destroy",function(){for(var n in l)l[n]&&l[n].remove()})}}}]).directive("ionSettingsButton",["$ionicSettingsConfig","$ionicSettings",function(n,i){return{template:'',$scope:!0,link:function(t){t.barColor="bar-"+n.color,t.icon=n.icon,t.iconClose=n.iconClose,t.iconClosePosition=n.iconClosePosition,t.settingsModal=i.createModal(t),t.title=n.title,t.dismiss=function(){t.settingsModal.hide()}}}}]).directive("ngTouchstart",function(){return{controller:["$scope","$element",function(n,i){function t(t){var e=i.attr("ng-touchstart");n.$event=t,n.$apply(e)}i.bind("touchstart",t)}]}}).directive("ngTouchend",function(){return{controller:["$scope","$element",function(n,i){function t(t){var e=i.attr("ng-touchend");n.$event=t,n.$apply(e)}i.bind("touchend",t)}]}}),n.module("ionicSettings.providers",[]).provider("$ionicSettingsConfig",function(){var n=ionic.Platform.isAndroid(),i=ionic.Platform.isIOS(),t="positive",e=n?"ion-android-settings":"ion-ios-gear",o=n?"ion-android-close":"ion-ios-close-empty",s="right",l=n?"ion-android-done":"ion-ios-checkmark-empty",c=i?"slide-in-up":"ionic-settings-animate-modal-android",a="Settings",r=!0;return{setAndroid:function(i){n=i},setIcon:function(n){e=n},setIconClose:function(n){o=n},setIconClosePosition:function(n){s=n},setModalAnimation:function(n){c=n},setColor:function(n){t=n},setTitle:function(n){a=n},setTouchID:function(n){r=n},$get:function(){return{color:t,icon:e,iconClose:o,iconClosePosition:s,iconDone:l,isAndroid:n,modalAnimation:c,title:a,touchID:r}}}}),n.module("ionicSettings.services",[]).factory("$ionicSettings",["$ionicSettingsConfig","$ionicModal","$log","$q","$rootScope","$window","IONIC_SETTINGS_PIN","IONIC_SETTINGS_SELECTION","IONIC_SETTINGS_TEXT",function(i,t,e,o,s,l,c,a,r){var u,d=this,g={},m=!0;return d.changed="$ionicSettings.changed",d.onInvalidPin="$ionicSettings.onInvalidPin",d.onValidPin="$ionicSettings.onValidPin",d.createModal=function(e,o){var s;if(o){var l=g[o];switch(l.type){case a:s=''+l.label+'
{{value}}';break;case c:s=''+l.label+'
{{value[$index]}}
';break;case r:s=''+l.label+'
'+l.value+"",n.extend(e,g[o].scopeObject)}}else s='{{title}}
';return s?t.fromTemplate(s,{scope:e,animation:i.modalAnimation,backdropClickToClose:!1}):void 0},d.fetch=function(n){var i=o.defer();if(l.plugins)l.plugins.appPreferences.fetch(n).then(function(t){null===t?i.reject(t):i.resolve({key:n,value:t})},function(n){i.reject(new Error(n))});else{var t=localStorage[n];void 0===t?i.reject(t):(("true"===t||"false"===t)&&(t="true"===t),i.resolve({key:n,value:t}))}return i.promise},d.fetchAll=function(){var i=[];for(var t in g){var e=g[t];n.isObject(e)&&i.push(d.fetch(t))}return o.all(i).then(function(n){for(var i in n){var t=n[i],e=t.key,o=t.value,s=g[e];s.value=o,s.type===c&&(u={key:e,value:o})}return g})},d.get=function(n){return g[n]?g[n].value:(e.error("Setting key "+n+" is not defined."),void 0)},d.getData=function(){return g},d.init=function(n){g=n;var t=o.defer();return d.fetchAll().then(function(n){if(u&&4===u.value.length){var e=s.$new(),o=d.createModal(e,u.key);e.color=i.color,e.barColor="bar-"+e.color,e.start=!0,e.value="",e.valid=!1;var l=g[u.key];l.onValid&&e.$on(d.onValidPin,l.onValid),l.onInvalid&&e.$on(d.onInvalidPin,l.onInvalid),e.addValue=function(i){e.value+=i,4===e.value.length&&(e.valid=e.value===u.value,e.valid?o.remove().then(function(){if(t.resolve(n),s.$broadcast(d.onValidPin),m)try{navigator.splashscreen.show()}catch(i){}}):(s.$broadcast(d.onInvalidPin,e.value),e.clear())),e.value.length>4&&(e.clear(),e.value+=i)},e.clear=function(){e.value=""},o.show().then(function(){try{navigator.splashscreen.hide()}catch(e){m=!1}if(i.touchID)try{touchid.checkSupport(function(){touchid.authenticate(function(){t.resolve(n)},function(){},"Touch ID")})}catch(e){}})}else t.resolve(n)},function(){d.storeAll(n).then(function(n){t.resolve(n)})}),t.promise},d.set=function(n,i){return g[n].value=i,d.store(n,i)},d.store=function(n,i){var t=o.defer();return l.plugins?l.plugins.appPreferences.store(n,i).then(function(n){t.resolve(n)},function(n){t.reject(new Error(n))}):(localStorage[n]=i,t.resolve(localStorage[n])),t.promise},d.setSelectionValues=function(i,t){return n.copy(t,g[i].values),d.set(i,"")},d.storeAll=function(){var i=[];for(var t in g){var e=g[t];n.isObject(e)&&i.push(d.store(t,e.value))}return o.all(i)},d}]),n.module("ionicSettings",["ionicSettings.constants","ionicSettings.directives","ionicSettings.providers","ionicSettings.services"])}(angular);
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var karma = require('karma').server;
3 | var concat = require('gulp-concat');
4 | var sass = require('gulp-sass');
5 | var minifyCss = require('gulp-minify-css');
6 | var uglify = require('gulp-uglify');
7 | var rename = require('gulp-rename');
8 | var path = require('path');
9 | var plumber = require('gulp-plumber');
10 | var runSequence = require('run-sequence');
11 | var jshint = require('gulp-jshint');
12 |
13 | /**
14 | * File patterns
15 | **/
16 |
17 | // Root directory
18 | var rootDirectory = path.resolve('./');
19 |
20 | // Source directory for build process
21 | var sourceDirectory = path.join(rootDirectory, './src');
22 |
23 | // tests
24 | var testDirectory = path.join(rootDirectory, './test/unit');
25 |
26 | var sourceFiles = [
27 | // Make sure module files are handled first
28 | path.join(sourceDirectory, '/**/*.module.js'),
29 | // Then add all JavaScript files
30 | path.join(sourceDirectory, '/**/*.js')
31 | ];
32 |
33 | var lintFiles = [
34 | 'gulpfile.js',
35 | // Karma configuration
36 | 'karma-*.conf.js'
37 | ].concat(sourceFiles);
38 |
39 | gulp.task('build', function() {
40 | gulp.src(sourceFiles)
41 | .pipe(plumber())
42 | .pipe(concat('ionic-settings.js'))
43 | .pipe(gulp.dest('./dist/'))
44 | .pipe(uglify())
45 | .pipe(rename('ionic-settings.min.js'))
46 | .pipe(gulp.dest('./dist'));
47 | });
48 |
49 | gulp.task('sass', function(done) {
50 | gulp.src('./src/ionic-settings/ionic-settings.scss')
51 | .pipe(sass())
52 | .on('error', sass.logError)
53 | .pipe(gulp.dest('./dist/'))
54 | .pipe(minifyCss({
55 | keepSpecialComments: 0
56 | }))
57 | .pipe(rename({extname: '.min.css'}))
58 | .pipe(gulp.dest('./dist/'))
59 | .on('end', done);
60 | });
61 |
62 | /**
63 | * Watch task
64 | */
65 | gulp.task('watch', function() {
66 |
67 | // Watch JavaScript files
68 | gulp.watch(sourceFiles, ['process-all']);
69 |
70 | // watch test files and re-run unit tests when changed
71 | gulp.watch(path.join(testDirectory, '/**/*.js'), ['test-src']);
72 | });
73 |
74 | /**
75 | * Validate source JavaScript
76 | */
77 | gulp.task('jshint', function() {
78 | return gulp.src(lintFiles)
79 | .pipe(plumber())
80 | .pipe(jshint())
81 | .pipe(jshint.reporter('jshint-stylish'))
82 | .pipe(jshint.reporter('fail'));
83 | });
84 |
85 | /**
86 | * Run test once and exit
87 | */
88 | gulp.task('test-src', function(done) {
89 | karma.start({
90 | configFile: __dirname + '/karma-src.conf.js',
91 | singleRun: true
92 | }, done);
93 | });
94 |
95 | /**
96 | * Run test once and exit
97 | */
98 | gulp.task('test-dist-concatenated', function(done) {
99 | karma.start({
100 | configFile: __dirname + '/karma-dist-concatenated.conf.js',
101 | singleRun: true
102 | }, done);
103 | });
104 |
105 | /**
106 | * Run test once and exit
107 | */
108 | gulp.task('test-dist-minified', function(done) {
109 | karma.start({
110 | configFile: __dirname + '/karma-dist-minified.conf.js',
111 | singleRun: true
112 | }, done);
113 | });
114 |
115 | gulp.task('default', function(done) {
116 | runSequence('build', 'sass', 'test-src', done);
117 | });
118 |
--------------------------------------------------------------------------------
/karma-dist-concatenated.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Thu Aug 21 2014 10:24:39 GMT+0200 (CEST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['mocha', 'chai-jquery', 'jquery-1.8.3', 'sinon-chai'],
14 |
15 | plugins: [
16 | 'karma-mocha',
17 | 'karma-chai',
18 | 'karma-sinon-chai',
19 | 'karma-chrome-launcher',
20 | 'karma-phantomjs-launcher',
21 | 'karma-jquery',
22 | 'karma-chai-jquery'
23 | ],
24 |
25 | // list of files / patterns to load in the browser
26 | files: [
27 | 'bower/angular/angular.js',
28 | 'bower/angular-mocks/angular-mocks.js',
29 | 'dist/ionic-settings.js',
30 | 'test/unit/**/*.js'
31 | ],
32 |
33 |
34 | // list of files to exclude
35 | exclude: [
36 | ],
37 |
38 |
39 | // preprocess matching files before serving them to the browser
40 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
41 | preprocessors: {
42 | },
43 |
44 |
45 | // test results reporter to use
46 | // possible values: 'dots', 'progress'
47 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
48 | reporters: ['progress'],
49 |
50 |
51 | // web server port
52 | port: 9876,
53 |
54 |
55 | // enable / disable colors in the output (reporters and logs)
56 | colors: true,
57 |
58 |
59 | // level of logging
60 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
61 | logLevel: config.LOG_INFO,
62 |
63 |
64 | // enable / disable watching file and executing tests whenever any file changes
65 | autoWatch: true,
66 |
67 |
68 | // start these browsers
69 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
70 | browsers: ['PhantomJS'],
71 |
72 |
73 | // Continuous Integration mode
74 | // if true, Karma captures browsers, runs the tests and exits
75 | singleRun: false
76 | });
77 | };
78 |
--------------------------------------------------------------------------------
/karma-dist-minified.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Thu Aug 21 2014 10:24:39 GMT+0200 (CEST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['mocha', 'chai-jquery', 'jquery-1.8.3', 'sinon-chai'],
14 |
15 | plugins: [
16 | 'karma-mocha',
17 | 'karma-chai',
18 | 'karma-sinon-chai',
19 | 'karma-chrome-launcher',
20 | 'karma-phantomjs-launcher',
21 | 'karma-jquery',
22 | 'karma-chai-jquery'
23 | ],
24 |
25 | // list of files / patterns to load in the browser
26 | files: [
27 | 'bower/angular/angular.js',
28 | 'bower/angular-mocks/angular-mocks.js',
29 | 'dist/ionic-settings.min.js',
30 | 'test/unit/**/*.js'
31 | ],
32 |
33 |
34 | // list of files to exclude
35 | exclude: [
36 | ],
37 |
38 |
39 | // preprocess matching files before serving them to the browser
40 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
41 | preprocessors: {
42 | },
43 |
44 |
45 | // test results reporter to use
46 | // possible values: 'dots', 'progress'
47 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
48 | reporters: ['progress'],
49 |
50 |
51 | // web server port
52 | port: 9876,
53 |
54 |
55 | // enable / disable colors in the output (reporters and logs)
56 | colors: true,
57 |
58 |
59 | // level of logging
60 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
61 | logLevel: config.LOG_INFO,
62 |
63 |
64 | // enable / disable watching file and executing tests whenever any file changes
65 | autoWatch: true,
66 |
67 |
68 | // start these browsers
69 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
70 | browsers: ['PhantomJS'],
71 |
72 |
73 | // Continuous Integration mode
74 | // if true, Karma captures browsers, runs the tests and exits
75 | singleRun: false
76 | });
77 | };
78 |
--------------------------------------------------------------------------------
/karma-src.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Sun Jan 10 2016 18:55:04 GMT+0100 (CET)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path that will be used to resolve all patterns (eg. files, exclude)
7 | basePath: '',
8 | // frameworks to use
9 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
10 | frameworks: ['jasmine'],
11 | // list of files / patterns to load in the browser
12 | files: [
13 | 'lib/ionic/ionic.bundle.js',
14 | 'bower/angular/angular.js',
15 | 'bower/angular-mocks/angular-mocks.js',
16 | 'src/**/*.module.js',
17 | 'src/**/*.js',
18 | 'test/unit/**/*.js'
19 | ],
20 | // list of files to exclude
21 | exclude: [
22 | ],
23 | // preprocess matching files before serving them to the browser
24 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
25 | preprocessors: {
26 | },
27 | // test results reporter to use
28 | // possible values: 'dots', 'progress'
29 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
30 | reporters: ['progress'],
31 | // web server port
32 | port: 9876,
33 | // enable / disable colors in the output (reporters and logs)
34 | colors: true,
35 | // level of logging
36 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
37 | logLevel: config.LOG_INFO,
38 | // enable / disable watching file and executing tests whenever any file changes
39 | autoWatch: true,
40 | // start these browsers
41 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
42 | browsers: ['PhantomJS'],
43 | // Continuous Integration mode
44 | // if true, Karma captures browsers, runs the tests and exits
45 | singleRun: false
46 | });
47 | };
48 |
--------------------------------------------------------------------------------
/nbproject/project.properties:
--------------------------------------------------------------------------------
1 | auxiliary.org-netbeans-modules-css-prep.less_2e_compiler_2e_options=
2 | auxiliary.org-netbeans-modules-css-prep.less_2e_enabled=false
3 | auxiliary.org-netbeans-modules-css-prep.less_2e_mappings=/less:/css
4 | auxiliary.org-netbeans-modules-css-prep.sass_2e_compiler_2e_options=
5 | auxiliary.org-netbeans-modules-css-prep.sass_2e_configured=true
6 | auxiliary.org-netbeans-modules-css-prep.sass_2e_enabled=false
7 | auxiliary.org-netbeans-modules-css-prep.sass_2e_mappings=/dist:/dist
8 | auxiliary.org-netbeans-modules-javascript-gulp.action_2e_build=default
9 | auxiliary.org-netbeans-modules-javascript-nodejs.enabled=true
10 | auxiliary.org-netbeans-modules-javascript-nodejs.run_2e_enabled=true
11 | auxiliary.org-netbeans-modules-web-clientproject-api.js_2e_libs_2e_folder=js/libs
12 | file.reference.ionic-settings-src=src
13 | file.reference.ionic-settings-test=testd
14 | file.reference.ionic-settings-test-1=test
15 | files.encoding=UTF-8
16 | run.as=node.js
17 | source.folder=${file.reference.ionic-settings-src}
18 | test.folder=${file.reference.ionic-settings-test-1}
19 |
--------------------------------------------------------------------------------
/nbproject/project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | org.netbeans.modules.web.clientproject
4 |
5 |
6 | ionic-settings
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ionic-settings",
3 | "version": "1.0.2",
4 | "author": {
5 | "name": "Ivan Weber",
6 | "email": "ivan.weber@gmx.de"
7 | },
8 | "dependencies": {},
9 | "devDependencies": {
10 | "chai": "^1.9.1",
11 | "chai-jquery": "^1.2.3",
12 | "gulp": "^3.8.7",
13 | "gulp-concat": "^2.3.4",
14 | "gulp-jshint": "^1.8.4",
15 | "gulp-minify-css": "^1.2.3",
16 | "gulp-plumber": "^0.6.6",
17 | "gulp-rename": "^1.2.0",
18 | "gulp-sass": "^2.1.1",
19 | "gulp-uglify": "^0.3.1",
20 | "jshint-stylish": "^0.4.0",
21 | "karma": "^0.12.22",
22 | "karma-chai": "^0.1.0",
23 | "karma-chai-jquery": "^1.0.0",
24 | "karma-chrome-launcher": "^0.1.4",
25 | "karma-jasmine": "^0.1.5",
26 | "karma-jquery": "^0.1.0",
27 | "karma-mocha": "^0.1.8",
28 | "karma-phantomjs-launcher": "^0.1.4",
29 | "karma-sinon-chai": "^0.2.0",
30 | "karma-spec-reporter": "^0.0.18",
31 | "mocha": "^1.21.4",
32 | "run-sequence": "^1.0.2",
33 | "sinon": "^1.10.3",
34 | "sinon-chai": "^2.5.0"
35 | },
36 | "engines": {
37 | "node": ">=0.8.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/ionic-settings/ionic-settings.scss:
--------------------------------------------------------------------------------
1 |
2 | $inputColor: #696969;
3 | $settingsTextSize: 14px;
4 | $positive: #387ef5;
5 | $assertive: #ef473a;
6 |
7 | .ionic-settings-numbers > div {
8 | margin-top: 20px !important;
9 | }
10 |
11 | .ionic-settings-numbers-button {
12 | border-radius: 50% !important;
13 | height: 70px !important;
14 | width: 72px !important;
15 | font-size: 20px !important;
16 | margin: 10px !important;
17 | }
18 |
19 | .ionic-settings-numbers-dot {
20 | font-size: 27px;
21 | margin: 10px;
22 | }
23 |
24 | .ionic-settings-numbers-row {
25 | display: flex;
26 | flex-direction: row;
27 | justify-content: center;
28 | width: 100%;
29 | height: 60px;
30 | margin-bottom: 20px;
31 | }
32 |
33 | .ionic-settings-numbers-value {
34 | font-size: 38px;
35 | margin: 10px;
36 | }
37 |
38 | .ionic-settings-icon-smaller:before {
39 | font-size: 27px;
40 | }
41 |
42 | .ionic-settings-icon-larger:before {
43 | font-size: 30px !important;
44 | margin-bottom: 27px !important;
45 | }
46 |
47 | .ionic-settings-left {
48 | float: left;
49 | }
50 |
51 | .ionic-settings-right {
52 | float: right;
53 | }
54 |
55 | .ionic-settings-android {
56 | font-size: $settingsTextSize;
57 | color: $inputColor;
58 | margin-top: 25px;
59 | }
60 |
61 | .ionic-settings-ios {
62 | color: gray;
63 | }
64 |
65 | .animate-ng-show.ng-hide-add {
66 | animation: 0.5s zoomOut ease;
67 | -webkit-animation: 0.5s zoomOut ease;
68 | }
69 |
70 | .animate-ng-show.ng-hide-remove {
71 | animation: 0.5s zoomIn ease;
72 | -webkit-animation: 0.5s zoomIn ease;
73 | }
74 |
75 | .ionic-settings-animate-modal-android {
76 | -webkit-transform: scale(0.0);
77 | transform: scale(0.0);
78 | opacity: 0;
79 | }
80 |
81 | .ionic-settings-animate-modal-android.ng-enter, .fade-in > .ng-enter {
82 | -webkit-transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 250ms;
83 | transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 250ms;
84 | }
85 |
86 | .ionic-settings-animate-modal-android.ng-enter-active,
87 | .ionic-settings-animate-modal-android > .ng-enter-active {
88 | -webkit-transform: scale(1.0);
89 | transform: scale(1.0);
90 | opacity: 1;
91 | }
92 |
93 | .ionic-settings-animate-modal-android.ng-leave, .fade-in > .ng-leave {
94 | -webkit-transition: all ease-in-out 250ms;
95 | transition: all ease-in-out 250ms;
96 | }
97 |
98 | @-webkit-keyframes zoomIn {
99 | from {
100 | opacity: 0;
101 | -webkit-transform: scale3d(.3, .3, .3);
102 | transform: scale3d(.3, .3, .3);
103 | }
104 |
105 | 50% {
106 | opacity: 1;
107 | }
108 | }
109 |
110 | @keyframes zoomIn {
111 | from {
112 | opacity: 0;
113 | -webkit-transform: scale3d(.3, .3, .3);
114 | transform: scale3d(.3, .3, .3);
115 | }
116 |
117 | 50% {
118 | opacity: 1;
119 | }
120 | }
121 |
122 | .zoomIn {
123 | -webkit-animation-name: zoomIn;
124 | animation-name: zoomIn;
125 | }
126 |
127 | @-webkit-keyframes zoomOut {
128 | from {
129 | opacity: 1;
130 | }
131 |
132 | 50% {
133 | opacity: 0;
134 | -webkit-transform: scale3d(.3, .3, .3);
135 | transform: scale3d(.3, .3, .3);
136 | }
137 |
138 | to {
139 | opacity: 0;
140 | }
141 | }
142 |
143 | @keyframes zoomOut {
144 | from {
145 | opacity: 1;
146 | }
147 |
148 | 50% {
149 | opacity: 0;
150 | -webkit-transform: scale3d(.3, .3, .3);
151 | transform: scale3d(.3, .3, .3);
152 | }
153 |
154 | to {
155 | opacity: 0;
156 | }
157 | }
158 |
159 | .zoomOut {
160 | -webkit-animation-name: zoomOut;
161 | animation-name: zoomOut;
162 | }
163 |
--------------------------------------------------------------------------------
/src/ionic-settings/ionicSettings.module.js:
--------------------------------------------------------------------------------
1 | (function(angular) {
2 |
3 | angular.module("ionicSettings.constants", [])
4 | .constant("IONIC_SETTINGS_BUTTON", "button")
5 | .constant("IONIC_SETTINGS_PIN", "pin")
6 | .constant("IONIC_SETTINGS_PIN_EMPTY", "\u25CB")
7 | .constant("IONIC_SETTINGS_PIN_FILLED", "\u25CF")
8 | .constant("IONIC_SETTINGS_SELECTION", "selection")
9 | .constant("IONIC_SETTINGS_TEXT", "text");
10 |
11 | angular.module("ionicSettings.directives", [])
12 | .directive("ionSettings", [
13 | "$ionicSettings",
14 | "$ionicSettingsConfig",
15 | "IONIC_SETTINGS_PIN", function(
16 | $ionicSettings,
17 | $ionicSettingsConfig,
18 | IONIC_SETTINGS_PIN) {
19 | return {
20 | restrict: "E",
21 | scope: true,
22 | template: '' +
23 | '
{{item}}
' +
24 | '
' +
25 | '
' +
29 | '{{item.label}}
' +
30 | '{{item.value}}
' +
31 | '{{item.value}}
' +
32 | '' +
33 | '' +
34 | '' +
35 | '
' +
39 | '{{item.label}}' +
40 | '' +
41 | '' +
42 | '
' +
48 | '{{item.label}}' +
49 | '' +
50 | '' +
51 | '
' +
52 | '' +
53 | '' +
57 | '
' +
58 | '
' +
62 | '
' +
70 | '
' +
71 | '
',
72 | link: function($scope) {
73 | var active = {};
74 | var modals = {};
75 |
76 | $scope.data = $ionicSettings.getData();
77 | $scope.isAndroid = $ionicSettingsConfig.isAndroid;
78 | $scope.iconClose = $ionicSettingsConfig.iconClose;
79 | $scope.iconClosePosition = $ionicSettingsConfig.iconClosePosition;
80 | $scope.iconDone = $ionicSettingsConfig.iconDone;
81 | $scope.iconSelected = $ionicSettingsConfig.isAndroid ? "ion-android-done" : "ion-ios-checkmark-empty";
82 | $scope.pin = {
83 | active: false
84 | };
85 | $scope.showing = false;
86 | $scope.color = $ionicSettingsConfig.color;
87 | $scope.elementColor = $scope.color === "stable" || $scope.color === "default" || $scope.color === "light" ? "dark" : $scope.color;
88 | $scope.barColor = "bar-" + $scope.color;
89 |
90 | angular.forEach($scope.data, function(item, key) {
91 | modals[key] = $ionicSettings.createModal($scope, key);
92 |
93 | $scope.$watch("data." + key + ".value", function(newValue, oldValue) {
94 | if (angular.isDefined(newValue) && newValue !== oldValue) {
95 | $ionicSettings.store(key, newValue).then(function() {
96 | $scope.$root.$broadcast($ionicSettings.changed, {
97 | key: key,
98 | value: newValue
99 | });
100 | }, function() {
101 | $scope.data[key].value = oldValue;
102 | });
103 | }
104 | });
105 | });
106 |
107 | $scope.addValue = function(value) {
108 | if ($scope.value.length === 4) {
109 | $scope.value = "";
110 | }
111 | $scope.value += value;
112 | };
113 |
114 | $scope.clear = function() {
115 | $scope.value = "";
116 | };
117 |
118 | $scope.dismiss = function() {
119 | if (active.item.type === IONIC_SETTINGS_PIN && active.item.value.length !== 4) {
120 | $scope.clear();
121 | $scope.pin.active = false;
122 | }
123 | if (active.modal.isShown()) {
124 | active.modal.hide();
125 | }
126 | };
127 |
128 | $scope.hidePin = function() {
129 | $scope.showing = false;
130 | };
131 |
132 | $scope.doAction = function(key) {
133 | active.modal = modals[key];
134 | active.item = $scope.data[key];
135 | if (active.modal) {
136 | if (active.item.type === IONIC_SETTINGS_PIN) {
137 | if (active.item.value.length === 4) {
138 | active.item.value = "";
139 | } else {
140 | $scope.value = active.item.value;
141 | active.modal.show();
142 | }
143 | } else {
144 | active.modal.show();
145 | }
146 | } else {
147 | if (angular.isFunction(active.item.onClick)) {
148 | active.item.onClick();
149 | }
150 | }
151 | };
152 |
153 | $scope.savePin = function() {
154 | active.item.value = $scope.value;
155 | active.modal.hide();
156 | };
157 |
158 | $scope.select = function(value) {
159 | active.item.value = value;
160 | $scope.dismiss();
161 | };
162 |
163 | $scope.showPin = function() {
164 | $scope.showing = true;
165 | };
166 |
167 | $scope.$on("modal.hidden", $scope.dismiss);
168 |
169 | $scope.$on("$destroy", function() {
170 | for (var key in modals) {
171 | if (modals[key]) {
172 | modals[key].remove();
173 | }
174 | }
175 | });
176 | }
177 | };
178 | }])
179 | .directive("ionSettingsButton", [
180 | "$ionicSettingsConfig",
181 | "$ionicSettings", function(
182 | $ionicSettingsConfig,
183 | $ionicSettings) {
184 | return {
185 | template: '',
186 | $scope: true,
187 | link: function($scope) {
188 | $scope.barColor = "bar-" + $ionicSettingsConfig.color;
189 | $scope.icon = $ionicSettingsConfig.icon;
190 | $scope.iconClose = $ionicSettingsConfig.iconClose;
191 | $scope.iconClosePosition = $ionicSettingsConfig.iconClosePosition;
192 | $scope.settingsModal = $ionicSettings.createModal($scope);
193 | $scope.title = $ionicSettingsConfig.title;
194 |
195 | $scope.dismiss = function() {
196 | $scope.settingsModal.hide();
197 | };
198 | }
199 | };
200 | }])
201 | .directive("ngTouchstart", function () {
202 | return {
203 | controller: ["$scope", "$element", function ($scope, $element) {
204 | $element.bind("touchstart", onTouchStart);
205 | function onTouchStart(event) {
206 | var method = $element.attr("ng-touchstart");
207 | $scope.$event = event;
208 | $scope.$apply(method);
209 | }
210 |
211 | }]
212 | };
213 | })
214 | .directive("ngTouchend", function () {
215 | return {
216 | controller: ["$scope", "$element", function ($scope, $element) {
217 | $element.bind("touchend", onTouchEnd);
218 | function onTouchEnd(event) {
219 | var method = $element.attr("ng-touchend");
220 | $scope.$event = event;
221 | $scope.$apply(method);
222 | }
223 |
224 | }]
225 | };
226 | });
227 |
228 | angular.module("ionicSettings.providers", []).provider("$ionicSettingsConfig", function() {
229 | var android = ionic.Platform.isAndroid();
230 | var ios = ionic.Platform.isIOS();
231 | var color = "positive";
232 | var icon = android ? "ion-android-settings" : "ion-ios-gear";
233 | var iconClose = android ? "ion-android-close" : "ion-ios-close-empty";
234 | var iconClosePosition = "right";
235 | var iconDone = android ? "ion-android-done" : "ion-ios-checkmark-empty";
236 | var modalAnimation = ios ? "slide-in-up" : "ionic-settings-animate-modal-android";
237 | var title = "Settings";
238 | var touchID = true;
239 | return {
240 | setAndroid: function(_android) {
241 | android = _android;
242 | },
243 | setIcon: function(_icon) {
244 | icon = _icon;
245 | },
246 | setIconClose: function(_iconClose) {
247 | iconClose = _iconClose;
248 | },
249 | setIconClosePosition: function(_iconClosePosition) {
250 | iconClosePosition = _iconClosePosition;
251 | },
252 | setModalAnimation: function(_modalAnimation) {
253 | modalAnimation = _modalAnimation;
254 | },
255 | setColor: function(_color) {
256 | color = _color;
257 | },
258 | setTitle: function(_title) {
259 | title = _title;
260 | },
261 | setTouchID: function(_touchID) {
262 | touchID = _touchID;
263 | },
264 | $get: function() {
265 | return {
266 | color: color,
267 | icon: icon,
268 | iconClose: iconClose,
269 | iconClosePosition: iconClosePosition,
270 | iconDone: iconDone,
271 | isAndroid: android,
272 | modalAnimation: modalAnimation,
273 | title: title,
274 | touchID: touchID
275 | };
276 | }
277 | };
278 | });
279 |
280 | angular.module("ionicSettings.services", [])
281 | .factory("$ionicSettings", [
282 | "$ionicSettingsConfig",
283 | "$ionicModal",
284 | "$log",
285 | "$q",
286 | "$rootScope",
287 | "$window",
288 | "IONIC_SETTINGS_PIN",
289 | "IONIC_SETTINGS_SELECTION",
290 | "IONIC_SETTINGS_TEXT", function(
291 | $ionicSettingsConfig,
292 | $ionicModal,
293 | $log,
294 | $q,
295 | $rootScope,
296 | $window,
297 | IONIC_SETTINGS_PIN,
298 | IONIC_SETTINGS_SELECTION,
299 | IONIC_SETTINGS_TEXT) {
300 | var self = this;
301 | var data = {};
302 | var pin;
303 | var $cordovaSplashscreenOn = true;
304 |
305 | self.changed = "$ionicSettings.changed";
306 | self.onInvalidPin = "$ionicSettings.onInvalidPin";
307 | self.onValidPin = "$ionicSettings.onValidPin";
308 |
309 | self.createModal = function($scope, key) {
310 | var html;
311 | if (key) {
312 | var item = data[key];
313 | switch(item.type) {
314 | case IONIC_SETTINGS_SELECTION:
315 | html =
316 | '' +
317 | '' +
318 | '' +
319 | '' + item.label + '
' +
320 | '' +
321 | '' +
322 | '' +
323 | '' +
324 | '{{value}}' +
325 | '' +
326 | '' +
327 | '' +
328 | '';
329 | break;
330 | case IONIC_SETTINGS_PIN:
331 | html =
332 | '' +
333 | '' +
334 | '' +
335 | '' +
336 | '' +
337 | '
' +
338 | '' + item.label + '
' +
339 | '' +
340 | '' +
341 | '' +
342 | '
' +
343 | '' +
344 | '' +
345 | '' +
346 | '
' +
347 | '' +
348 | '{{value[$index]}}' +
349 | '
' +
350 | '
' +
351 | '' +
356 | '
' +
357 | '
' +
358 | '' +
359 | '' +
360 | '' +
368 | '
' +
369 | '
' +
370 | '' +
371 | '';
372 | break;
373 | case IONIC_SETTINGS_TEXT:
374 | html =
375 | '' +
376 | '' +
377 | '' +
378 | '' + item.label + '
' +
379 | '' +
380 | '' +
381 | '' + item.value + '' +
382 | '';
383 | angular.extend($scope, data[key].scopeObject);
384 | break;
385 | }
386 | } else {
387 | html = '' +
388 | '' +
389 | '' +
390 | '{{title}}
' +
391 | '' +
392 | '' +
393 | '' +
394 | '' +
395 | '' +
396 | '';
397 | }
398 | if (html) {
399 | return $ionicModal.fromTemplate(html, {
400 | scope: $scope,
401 | animation: $ionicSettingsConfig.modalAnimation,
402 | backdropClickToClose: false
403 | });
404 | }
405 | };
406 |
407 | self.fetch = function(key) {
408 | var q = $q.defer();
409 | if ($window.plugins) {
410 | $window.plugins.appPreferences.fetch(key).then(function(value) {
411 | if (value === null) {
412 | q.reject(value);
413 | } else {
414 | q.resolve({key: key, value: value});
415 | }
416 | }, function(error) {
417 | q.reject(new Error(error));
418 | });
419 | } else {
420 | var value = localStorage[key];
421 | if (value === undefined) {
422 | q.reject(value);
423 | } else {
424 | if (value === "true" || value === "false") {
425 | value = value === "true";
426 | }
427 | q.resolve({key: key, value: value});
428 | }
429 | }
430 | return q.promise;
431 | };
432 |
433 | self.fetchAll = function() {
434 | var fetching = [];
435 | for (var key in data) {
436 | var item = data[key];
437 | if (angular.isObject(item)) {
438 | fetching.push(self.fetch(key));
439 | }
440 | }
441 | return $q.all(fetching).then(function(result) {
442 | for (var i in result) {
443 | var resultItem = result[i];
444 | var key = resultItem.key;
445 | var value = resultItem.value;
446 |
447 | var item = data[key];
448 | item.value = value;
449 | if (item.type === IONIC_SETTINGS_PIN) {
450 | pin = {
451 | key: key,
452 | value: value
453 | };
454 | }
455 | }
456 | return data;
457 | });
458 | };
459 |
460 | self.get = function(key) {
461 | if (data[key]) {
462 | return data[key].value;
463 | } else {
464 | $log.error("Setting key " + key + " is not defined.");
465 | return;
466 | }
467 | };
468 |
469 | self.getData = function() {
470 | return data;
471 | };
472 |
473 | self.init = function(_data) {
474 | data = _data;
475 |
476 | var q = $q.defer();
477 |
478 | self.fetchAll().then(function(result) {
479 | if (pin && pin.value.length === 4) {
480 | var $scope = $rootScope.$new();
481 | var pinModal = self.createModal($scope, pin.key);
482 |
483 | $scope.color = $ionicSettingsConfig.color;
484 | $scope.barColor = "bar-" + $scope.color;
485 | $scope.start = true;
486 | $scope.value = "";
487 | $scope.valid = false;
488 |
489 | var pinItem = data[pin.key];
490 | if (pinItem.onValid) {
491 | $scope.$on(self.onValidPin, pinItem.onValid);
492 | }
493 | if (pinItem.onInvalid) {
494 | $scope.$on(self.onInvalidPin, pinItem.onInvalid);
495 | }
496 |
497 | $scope.addValue = function(value) {
498 | $scope.value += value;
499 | if ($scope.value.length === 4) {
500 | $scope.valid = $scope.value === pin.value;
501 | if ($scope.valid) {
502 | pinModal.remove().then(function() {
503 | q.resolve(result);
504 | $rootScope.$broadcast(self.onValidPin);
505 | if ($cordovaSplashscreenOn) {
506 | try {
507 | navigator.splashscreen.show();
508 | } catch(e) {}
509 | }
510 | });
511 | } else {
512 | $rootScope.$broadcast(self.onInvalidPin, $scope.value);
513 | $scope.clear();
514 | }
515 | }
516 | if ($scope.value.length > 4) {
517 | $scope.clear();
518 | $scope.value += value;
519 | }
520 | };
521 |
522 | $scope.clear = function() {
523 | $scope.value = "";
524 | };
525 |
526 | pinModal.show().then(function() {
527 | try {
528 | navigator.splashscreen.hide();
529 | } catch(e) {
530 | $cordovaSplashscreenOn = false;
531 | }
532 | if ($ionicSettingsConfig.touchID) {
533 | try {
534 | touchid.checkSupport(function() {
535 | touchid.authenticate(function() {
536 | q.resolve(result);
537 | }, function() {
538 | }, "Touch ID");
539 | });
540 | } catch(e) {}
541 | }
542 | });
543 | } else {
544 | q.resolve(result);
545 | }
546 | }, function() {
547 | self.storeAll(_data).then(function(result) {
548 | q.resolve(result);
549 | });
550 | });
551 |
552 | return q.promise;
553 | };
554 |
555 | self.set = function(key, value) {
556 | data[key].value = value;
557 | return self.store(key, value);
558 | };
559 |
560 | self.store = function(key, value) {
561 | var q = $q.defer();
562 | if ($window.plugins) {
563 | $window.plugins.appPreferences.store(key, value).then(function(value) {
564 | q.resolve(value);
565 | }, function(error) {
566 | q.reject(new Error(error));
567 | });
568 | } else {
569 | localStorage[key] = value;
570 | q.resolve(localStorage[key]);
571 | }
572 | return q.promise;
573 | };
574 |
575 | self.setSelectionValues = function(key, values) {
576 | angular.copy(values, data[key].values);
577 | return self.set(key, "");
578 | };
579 |
580 | self.storeAll = function() {
581 | var storing = [];
582 | for (var key in data) {
583 | var item = data[key];
584 | if (angular.isObject(item)) {
585 | storing.push(self.store(key, item.value));
586 | }
587 | }
588 | return $q.all(storing);
589 | };
590 |
591 | return self;
592 | }]);
593 |
594 | // App
595 | angular.module("ionicSettings", [
596 | "ionicSettings.constants",
597 | "ionicSettings.directives",
598 | "ionicSettings.providers",
599 | "ionicSettings.services"
600 | ]);
601 | })(angular);
602 |
--------------------------------------------------------------------------------
/test/unit/ionic-settings/ionicSettingsSpec.js:
--------------------------------------------------------------------------------
1 | /* global expect */
2 |
3 | "use strict";
4 |
5 |
6 | describe("modules", function() {
7 | var module;
8 | var dependencies;
9 | dependencies = [];
10 |
11 | var hasModule = function(module) {
12 | return dependencies.indexOf(module) >= 0;
13 | };
14 |
15 | beforeEach(function() {
16 | module = angular.module("ionicSettings");
17 | dependencies = module.requires;
18 | });
19 |
20 | it("should load constants module", function() {
21 | expect(hasModule("ionicSettings.constants")).toBeTruthy();
22 | });
23 |
24 | it("should load directives module", function() {
25 | expect(hasModule("ionicSettings.directives")).toBeTruthy();
26 | });
27 |
28 | it("should load providers module", function() {
29 | expect(hasModule("ionicSettings.providers")).toBeTruthy();
30 | });
31 |
32 | it("should load services module", function() {
33 | expect(hasModule("ionicSettings.services")).toBeTruthy();
34 | });
35 | });
--------------------------------------------------------------------------------