s-num lines-num-new"> 2
+var gutil = require('gulp-util');
3
+var bower = require('bower');
4
+var concat = require('gulp-concat');
5
+var sass = require('gulp-sass');
6
+var minifyCss = require('gulp-minify-css');
7
+var rename = require('gulp-rename');
8
+var sh = require('shelljs');
9
+
10
+var paths = {
11
+  sass: ['./scss/**/*.scss']
12
+};
13
+
14
+gulp.task('default', ['sass']);
15
+
16
+gulp.task('sass', function(done) {
17
+  gulp.src('./scss/ionic.app.scss')
18
+    .pipe(sass())
19
+    .on('error', sass.logError)
20
+    .pipe(gulp.dest('./www/css/'))
21
+    .pipe(minifyCss({
22
+      keepSpecialComments: 0
23
+    }))
24
+    .pipe(rename({ extname: '.min.css' }))
25
+    .pipe(gulp.dest('./www/css/'))
26
+    .on('end', done);
27
+});
28
+
29
+gulp.task('watch', function() {
30
+  gulp.watch(paths.sass, ['sass']);
31
+});
32
+
33
+gulp.task('install', ['git-check'], function() {
34
+  return bower.commands.install()
35
+    .on('log', function(data) {
36
+      gutil.log('bower', gutil.colors.cyan(data.id), data.message);
37
+    });
38
+});
39
+
40
+gulp.task('git-check', function(done) {
41
+  if (!sh.which('git')) {
42
+    console.log(
43
+      '  ' + gutil.colors.red('Git is not installed.'),
44
+      '\n  Git, the version control system, is required to download Ionic.',
45
+      '\n  Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.',
46
+      '\n  Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.'
47
+    );
48
+    process.exit(1);
49
+  }
50
+  done();
51
+});

+ 83 - 0
hooks/README.md

@@ -0,0 +1,83 @@
1
+<!--
2
+#
3
+# Licensed to the Apache Software Foundation (ASF) under one
4
+# or more contributor license agreements.  See the NOTICE file
5
+# distributed with this work for additional information
6
+# regarding copyright ownership.  The ASF licenses this file
7
+# to you under the Apache License, Version 2.0 (the
8
+# "License"); you may not use this file except in compliance
9
+# with the License.  You may obtain a copy of the License at
10
+#
11
+# http://www.apache.org/licenses/LICENSE-2.0
12
+#
13
+# Unless required by applicable law or agreed to in writing,
14
+# software distributed under the License is distributed on an
15
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+#  KIND, either express or implied.  See the License for the
17
+# specific language governing permissions and limitations
18
+# under the License.
19
+#
20
+-->
21
+# Cordova Hooks
22
+
23
+This directory may contain scripts used to customize cordova commands. This
24
+directory used to exist at `.cordova/hooks`, but has now been moved to the
25
+project root. Any scripts you add to these directories will be executed before
26
+and after the commands corresponding to the directory name. Useful for
27
+integrating your own build systems or integrating with version control systems.
28
+
29
+__Remember__: Make your scripts executable.
30
+
31
+## Hook Directories
32
+The following subdirectories will be used for hooks:
33
+
34
+    after_build/
35
+    after_compile/
36
+    after_docs/
37
+    after_emulate/
38
+    after_platform_add/
39
+    after_platform_rm/
40
+    after_platform_ls/
41
+    after_plugin_add/
42
+    after_plugin_ls/
43
+    after_plugin_rm/
44
+    after_plugin_search/
45
+    after_prepare/
46
+    after_run/
47
+    after_serve/
48
+    before_build/
49
+    before_compile/
50
+    before_docs/
51
+    before_emulate/
52
+    before_platform_add/
53
+    before_platform_rm/
54
+    before_platform_ls/
55
+    before_plugin_add/
56
+    before_plugin_ls/
57
+    before_plugin_rm/
58
+    before_plugin_search/
59
+    before_prepare/
60
+    before_run/
61
+    before_serve/
62
+    pre_package/ <-- Windows 8 and Windows Phone only.
63
+
64
+## Script Interface
65
+
66
+All scripts are run 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:
67
+
68
+* CORDOVA_VERSION - The version of the Cordova-CLI.
69
+* CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios).
70
+* CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer)
71
+* CORDOVA_HOOK - Path to the hook that is being executed.
72
+* CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate)
73
+
74
+If a script returns a non-zero exit code, then the parent cordova command will be aborted.
75
+
76
+
77
+## Writing hooks
78
+
79
+We highly recommend writting your hooks using Node.js so that they are
80
+cross-platform. Some good examples are shown here:
81
+
82
+[http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/)
83
+

+ 94 - 0
hooks/after_prepare/010_add_platform_class.js

@@ -0,0 +1,94 @@
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(/<body(?=[\s>])(.*?)>/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<platforms.length; x++) {
74
+    // open up the index.html file at the www root
75
+    try {
76
+      var platform = platforms[x].trim().toLowerCase();
77
+      var indexPath;
78
+
79
+      if(platform == 'android') {
80
+        indexPath = path.join('platforms', platform, 'assets', 'www', 'index.html');
81
+      } else {
82
+        indexPath = path.join('platforms', platform, 'www', 'index.html');
83
+      }
84
+
85
+      if(fs.existsSync(indexPath)) {
86
+        addPlatformBodyTag(indexPath, platform);
87
+      }
88
+
89
+    } catch(e) {
90
+      process.stdout.write(e);
91
+    }
92
+  }
93
+
94
+}

+ 4 - 0
ionic.project

@@ -0,0 +1,4 @@
1
+{
2
+  "name": "lively-app",
3
+  "app_id": ""
4
+}

File diff suppressed because it is too large
+ 3236 - 0
package-lock.json


+ 29 - 0
package.json

@@ -0,0 +1,29 @@
1
+{
2
+  "name": "lively-app",
3
+  "version": "1.1.1",
4
+  "description": "lively-app: An Ionic project",
5
+  "dependencies": {
6
+    "gulp": "^3.5.6",
7
+    "gulp-sass": "^2.0.4",
8
+    "gulp-concat": "^2.2.0",
9
+    "gulp-minify-css": "^0.3.0",
10
+    "gulp-rename": "^1.2.0"
11
+  },
12
+  "devDependencies": {
13
+    "bower": "^1.3.3",
14
+    "gulp-util": "^2.2.14",
15
+    "shelljs": "^0.3.0"
16
+  },
17
+  "cordovaPlugins": [
18
+    "cordova-plugin-device",
19
+    "cordova-plugin-console",
20
+    "cordova-plugin-whitelist",
21
+    "cordova-plugin-splashscreen",
22
+    "cordova-plugin-statusbar",
23
+    "ionic-plugin-keyboard"
24
+  ],
25
+  "cordovaPlatforms": [
26
+    "android",
27
+    "ios"
28
+  ]
29
+}

BIN
resources/android/icon/drawable-hdpi-icon.png


BIN
resources/android/icon/drawable-ldpi-icon.png


BIN
resources/android/icon/drawable-mdpi-icon.png


BIN
resources/android/icon/drawable-xhdpi-icon.png


BIN
resources/android/icon/drawable-xxhdpi-icon.png


BIN
resources/android/icon/drawable-xxxhdpi-icon.png


BIN
resources/android/splash/drawable-land-hdpi-screen.png


BIN
resources/android/splash/drawable-land-ldpi-screen.png


BIN
resources/android/splash/drawable-land-mdpi-screen.png


BIN
resources/android/splash/drawable-land-xhdpi-screen.png


BIN
resources/android/splash/drawable-land-xxhdpi-screen.png


BIN
resources/android/splash/drawable-land-xxxhdpi-screen.png


BIN
resources/android/splash/drawable-port-hdpi-screen.png


BIN
resources/android/splash/drawable-port-ldpi-screen.png


BIN
resources/android/splash/drawable-port-mdpi-screen.png


BIN
resources/android/splash/drawable-port-xhdpi-screen.png


BIN
resources/android/splash/drawable-port-xxhdpi-screen.png


BIN
resources/android/splash/drawable-port-xxxhdpi-screen.png


BIN
resources/icon.png


BIN
resources/ios/icon/iTunesArtwork.png


BIN
resources/ios/icon/iTunesArtwork@2x.png


BIN
resources/ios/icon/icon-40.png


BIN
resources/ios/icon/icon-40@2x.png


BIN
resources/ios/icon/icon-40@3x.png


BIN
resources/ios/icon/icon-50.png


BIN
resources/ios/icon/icon-50@2x.png


BIN
resources/ios/icon/icon-60.png


BIN
resources/ios/icon/icon-60@2x.png


BIN
resources/ios/icon/icon-60@3x.png


BIN
resources/ios/icon/icon-72.png


BIN
resources/ios/icon/icon-72@2x.png


BIN
resources/ios/icon/icon-76.png


BIN
resources/ios/icon/icon-76@2x.png


BIN
resources/ios/icon/icon-83.5@2x.png


BIN
resources/ios/icon/icon-Small.png


BIN
resources/ios/icon/icon-Small@2x.png


BIN
resources/ios/icon/icon-small-1.png


BIN
resources/ios/icon/icon-small@2x-1.png


BIN
resources/ios/icon/icon-small@3x.png


BIN
resources/ios/icon/icon.png


BIN
resources/ios/icon/icon@2x.png


BIN
resources/ios/splash/Default-568h@2x~iphone.png


BIN
resources/ios/splash/Default-667h.png


BIN
resources/ios/splash/Default-736h.png


BIN
resources/ios/splash/Default-Landscape-736h.png


BIN
resources/ios/splash/Default-Landscape@2x~ipad.png


BIN
resources/ios/splash/Default-Landscape~ipad.png


BIN
resources/ios/splash/Default-Portrait@2x~ipad.png


BIN
resources/ios/splash/Default-Portrait~ipad.png


BIN
resources/ios/splash/Default@2x~iphone.png


BIN
resources/ios/splash/Default~iphone.png


BIN
resources/splash.png


+ 22 - 0
scss/ionic.app.scss

@@ -0,0 +1,22 @@
1
+/*
2
+To customize the look and feel of Ionic, you can override the variables
3
+in ionic's _variables.scss file.
4
+
5
+For example, you might change some of the default colors:
6
+
7
+$light:                           #fff !default;
8
+$stable:                          #f8f8f8 !default;
9
+$positive:                        #387ef5 !default;
10
+$calm:                            #11c1f3 !default;
11
+$balanced:                        #33cd5f !default;
12
+$energized:                       #ffc900 !default;
13
+$assertive:                       #ef473a !default;
14
+$royal:                           #886aea !default;
15
+$dark:                            #444 !default;
16
+*/
17
+
18
+// The path for our ionicons font files, relative to the built CSS in www/css
19
+$ionicons-font-path: "../lib/ionic/fonts" !default;
20
+@import "www/lib/ionic/scss/ionic";
21
+@import "www/lib/font-awesome/scss/font-awesome";  
22
+

BIN
www.zip


File diff suppressed because it is too large
+ 12007 - 0
www/css/ionic.app.css


File diff suppressed because it is too large
+ 1 - 0
www/css/ionic.app.min.css


BIN
www/css/lively-bg.png


+ 138 - 0
www/css/style.css

@@ -0,0 +1,138 @@
1
+/* Empty. Add your own CSS if you like */
2
+.custom-icon .icon-badge {
3
+  position: absolute;
4
+  top: 3px;
5
+  right: 0px;
6
+  font-size: 8px;
7
+  padding: 2px 6px;
8
+}
9
+ion-content iframe {
10
+    width:100%;
11
+}
12
+.facebook {
13
+    background-color:#3b5998;
14
+    color:#fff;
15
+}
16
+.social-button:hover {
17
+    color:#fff;
18
+}
19
+.ig {
20
+    background-color:#517fa4;
21
+    color:#fff;
22
+}
23
+.center-block {
24
+    display:block;
25
+    margin:0 auto;
26
+
27
+    width:100%;
28
+}
29
+.center-block img {
30
+}
31
+.lively-bg {
32
+    background: url("./lively-bg.png") no-repeat top left;
33
+    background-size:cover;
34
+}
35
+.lively-bg .input-label {
36
+    color:#fff;
37
+    font-weight:bold;
38
+}
39
+.lively-bg .item-input {
40
+    background:none;
41
+    color:#fff;
42
+    border-top:none;
43
+}
44
+.img-padding {
45
+    padding:10px 20px;
46
+}
47
+.lively-bg .item-input input[type=text],
48
+.lively-bg .item-input input[type=password] 
49
+{
50
+    color:#fff;
51
+
52
+}
53
+.lively-bg .item {
54
+    background:none;
55
+}
56
+.label-content {
57
+    color:#fff;
58
+    font-size:larger;
59
+    font-weight:bold;
60
+}
61
+.borderless {
62
+    border:none;
63
+}
64
+.no-padding a.item-content {
65
+    padding: 0px;
66
+    border: none;
67
+}
68
+
69
+ion-item.no-padding.item {
70
+    border: none;
71
+}
72
+
73
+.list.card {
74
+    box-shadow: none;
75
+	margin-right:0px;
76
+	margin-left:0px;
77
+}
78
+.category {
79
+	background-color:#000;
80
+	color:#fff;
81
+	display:inline-block;
82
+	padding:5px 10px;
83
+}
84
+h2.title {
85
+    margin-top: 10px;
86
+    font-size: larger;
87
+}
88
+ion-content.lively.scroll-content.ionic-scroll.has-header {
89
+    top: 20px;
90
+}
91
+ion-list#lively-menu {}
92
+
93
+#lively-menu ion-item.item {
94
+    color: #fff;
95
+    background-color: #343434;
96
+    border-color: #565656;
97
+}
98
+
99
+#lively-menu a.item-content {
100
+    background-color: #343434;
101
+}
102
+i.icon-perspective {
103
+	background: url('../img/perspective.png') no-repeat center left;
104
+	background-size: contain;
105
+	width: 33px;	
106
+}
107
+hr.zig, hr.zag {
108
+  border: none;
109
+  height: 30px;
110
+  margin: 0 0px;
111
+}
112
+
113
+hr.zig{
114
+  background: linear-gradient(-135deg, #FFF 20px, rgba(0, 0, 0, 0) 0) 0 5px, linear-gradient(135deg, #FFF 20px, rgba(0, 0, 0, 0) 0) 0 5px;
115
+  background-color: rgba(0, 0, 0, 0);
116
+  background-position: center bottom;
117
+  background-repeat: repeat-x;
118
+  background-size: 20px 40px;
119
+  z-index: 100;
120
+  position: relative;
121
+}
122
+
123
+hr.zag {
124
+  background: linear-gradient(-135deg, #eee 20px, rgba(0, 0, 0, 0) 0) 0 5px, linear-gradient(135deg, #eee 20px, #FFF 0) 0 5px;
125
+  background-color: rgba(0, 0, 0, 0);
126
+  background-position: center bottom;
127
+  background-repeat: repeat-x;
128
+  background-size: 20px 40px;
129
+  z-index: 50;
130
+  margin-top: -28px;
131
+}
132
+.author-avatar {
133
+    width:60px;
134
+    height:60px;
135
+    border-radius: 50%;
136
+}
137
+.post-body { font-size:120%; }
138
+img { max-width:100%; }

BIN
www/img/Icon-Facebook.png


BIN
www/img/big-logo.png


BIN
www/img/default.png


BIN
www/img/ionic.png


BIN
www/img/lively-bg.png


BIN
www/img/logo.png


BIN
www/img/perspective.png


BIN
www/img/star.png


+ 38 - 0
www/index.html

@@ -0,0 +1,38 @@
1
+<!DOCTYPE html>
2
+<html>
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
6
+    <title></title>
7
+    <!--
8
+    <link href="lib/ionic/css/ionic.css" rel="stylesheet"> -->
9
+    <link href="css/ionic.app.css" rel="stylesheet">
10
+    <link href="css/style.css" rel="stylesheet">
11
+    <link href="lib/Ionicons/css/ionicons.css">
12
+    <!-- 
13
+    <link href="lib/font-awesome/css/font-awesome.css"> -->
14
+
15
+
16
+    <!-- ionic/angularjs js -->
17
+    <script src="lib/ionic/js/ionic.bundle.js"></script>
18
+    <!-- cordova script (this will be a 404 during development) -->
19
+    <script src="lib/ngCordova/dist/ng-cordova.js"></script>
20
+    
21
+    <script src="cordova.js"></script>
22
+    <script type="text/javascript" charset="utf-8">
23
+    var env = "prod";
24
+    </script>
25
+    <!-- your app's js -->
26
+    <script src="lib/humanize/humanize.js"></script>
27
+    <script src="lib/angularjs-humanize/src/angular-humanize.js"></script>
28
+    <script src="lib/jsSHA/src/sha1.js"></script>
29
+	<script src="lib/ng-cordova-oauth/dist/ng-cordova-oauth.js"></script>
30
+    <script src="http://cdn.mcot.net/publicscript/js/filters.js"></script>
31
+    <script src="js/app.js"></script>
32
+    <script src="js/controllers.js"></script>
33
+  </head>
34
+
35
+  <body ng-app="starter">
36
+    <ion-nav-view></ion-nav-view>
37
+  </body>
38
+</html>

+ 579 - 0
www/js/app.js

@@ -0,0 +1,579 @@
1
+// Ionic Starter App
2
+
3
+// angular.module is a global place for creating, registering and retrieving Angular modules
4
+// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
5
+// the 2nd parameter is an array of 'requires'
6
+// 'starter.controllers' is found in controllers.js
7
+var db;
8
+angular.module('starter', ['ionic', 'ngCordova', 'ngCordovaOauth',  'starter.controllers'])
9
+.run(function($ionicPlatform, $cordovaSQLite) {
10
+  $ionicPlatform.ready(function() {
11
+    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
12
+    // for form inputs)
13
+    if (window.cordova && window.cordova.plugins.Keyboard) {
14
+      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
15
+      cordova.plugins.Keyboard.disableScroll(true);
16
+
17
+    }
18
+    if (window.StatusBar) {
19
+      // org.apache.cordova.statusbar required
20
+      StatusBar.styleDefault();
21
+    }
22
+	if (window.cordova) {
23
+	    try {
24
+	        db = $cordovaSQLite.openDB({
25
+	            name: "tna.db",
26
+	            location: 'default'
27
+	        });
28
+	    } catch (error) {
29
+	        alert(error);
30
+	    }
31
+	    $cordovaSQLite.execute(db, 'CREATE TABLE IF NOT EXISTS Messages (id INTEGER PRIMARY KEY AUTOINCREMENT, message TEXT)');
32
+	    $cordovaSQLite.execute(db, 'CREATE TABLE IF NOT EXISTS Personal(key TEXT PRIMARY KEY, value TEXT)');
33
+	}
34
+ });
35
+})
36
+.filter('humanize', function(){
37
+    return function humanize(number) {
38
+        if(number < 1000) {
39
+            return number;
40
+        }
41
+        var si = ['K', 'M', 'G', 'T', 'P', 'H'];
42
+        var exp = Math.floor(Math.log(number) / Math.log(1000));
43
+        var result = number / Math.pow(1000, exp);
44
+        result = (result % 1 > (1 / Math.pow(1000, exp - 1))) ? result.toFixed(2) : result.toFixed(0);
45
+        return result + si[exp - 1];
46
+    };
47
+})
48
+.filter('get_url', ['IMG_URI', function(IMG_URI){
49
+    return function(url) {
50
+        return IMG_URI + url;
51
+    };
52
+}])
53
+.filter('map_link',[function(){
54
+    return function(geocode) {
55
+        var isIOS = ionic.Platform.isIOS();
56
+        var isAndroid = ionic.Platform.isAndroid();
57
+        if( isIOS ) {
58
+            return "maps://?q="+geocode;
59
+        }
60
+        if( isAnroid ) {
61
+            return "geo:"+geocode;
62
+        }
63
+    };
64
+}])
65
+.filter('titleCase', function() {
66
+    return function(input) {
67
+      input = input || '';
68
+      return input.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
69
+    };
70
+  })
71
+.filter('get_last_array', function(){
72
+    return function(arr) {
73
+        if( arr.length > 0 ){
74
+            var v  = arr[arr.length-1];
75
+            if( v == ""){
76
+                return "Untitled";
77
+            }else {
78
+                return v;
79
+            }
80
+        } else
81
+            return "Untitled";
82
+    };
83
+})
84
+.value('THROTTLE_MILLISECONDS', 4000)
85
+//.constant('API_URI', 'http://localhost:5050')
86
+.constant('API_URI', 'http://mcotn-api.simplico.net')
87
+//.constant('IMG_URI', 'http://simplico.net:5060')
88
+.constant('IMG_URI', 'http://mcotn-backend.simplico.net')
89
+//.constant('GEN_USER_API_URI', 'http://localhost:5052')
90
+.constant('GEN_USER_API_URI', 'http://mcotn-simplitic.simplico.net')
91
+.constant('SEARCH_API', '/api/v1.0/search?collection=posts')
92
+.constant('SHARE_API', '/api/v1.0/share')
93
+.constant('USER_API', '/api/v1.0/users_social')
94
+.constant('CMS_USER_API', '/api/v1.0/users')
95
+.constant('SETTING_API', '/api/v1.0/setting')
96
+.constant('FAV_API', '/api/v1.0/fav')
97
+.constant('NOTI_API', '/api/v1.0/noti')
98
+.constant('AUTH_API', '/auth')
99
+.constant('PAGE_LIMIT', 10)
100
+.constant('DB', 'lively.db')
101
+.constant('PUBLIC_TOKEN', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoicHVibGljIiwicm9sZXMiOlsicHVibGljIl0sImV4cCI6MTczNDE2NTcxMCwiaWF0IjoxNDc0OTY1NzEwLCJuYmYiOjE0NzQ5NjU3MTAsImlkZW50aXR5IjoiNTdlOWVmMjZjMjU0ZmQ4N2Q3ZmQ4MzM2In0.-9fcm5s8qYbdqBDUX7cZJ5J3AX91fe6VrCLs_S-_eBU')
102
+.config(function($stateProvider, $urlRouterProvider, $cordovaInAppBrowserProvider, $ionicConfigProvider) {
103
+  var defaultOptions = {
104
+      location: 'no',
105
+      clearcache: 'no',
106
+      toolbar: 'yes'
107
+  };
108
+$ionicConfigProvider.backButton.previousTitleText(false).text('');
109
+  $cordovaInAppBrowserProvider.setDefaultOptions(defaultOptions);
110
+ $stateProvider
111
+
112
+    .state('app', {
113
+    url: '/app',
114
+    abstract: true,
115
+    templateUrl: 'templates/menu.html',
116
+    controller: 'AppCtrl'
117
+  })
118
+
119
+  .state('app.search', {
120
+    url: '/search',
121
+    views: {
122
+      'menuContent': {
123
+        templateUrl: 'templates/search.html',
124
+        controller: 'SearchCtrl'
125
+      }
126
+    }
127
+  })
128
+
129
+  .state('app.login', {
130
+    url: '/login',
131
+    views: {
132
+        'menuContent': {
133
+            templateUrl: 'templates/login.html',
134
+            controller: 'LoginCtrl'
135
+        }
136
+    }
137
+  })
138
+  .state('app.view', {
139
+    url: '/view/:id',
140
+    views: {
141
+      'menuContent': {
142
+        templateUrl: 'templates/view.html',
143
+        controller: 'ViewCtrl'
144
+      }
145
+    }
146
+  })
147
+
148
+  .state('app.browse', {
149
+      url: '/browse',
150
+      views: {
151
+        'menuContent': {
152
+          templateUrl: 'templates/browse.html'
153
+        }
154
+      }
155
+    })
156
+    .state('app.playlists', {
157
+      url: '/playlists',
158
+      views: {
159
+        'menuContent': {
160
+          templateUrl: 'templates/playlists.html',
161
+          controller: 'PlaylistsCtrl'
162
+        }
163
+      }
164
+    })
165
+    .state('app.settings', {
166
+      url: '/settings',
167
+      cache: false,
168
+      views: {
169
+        'menuContent': {
170
+          templateUrl: 'templates/settings.html',
171
+          controller: 'SettingCtrl'
172
+        }
173
+      }
174
+    })
175
+
176
+    .state('app.index', {
177
+      url: '/index',
178
+      views: {
179
+        'menuContent': {
180
+          templateUrl: 'templates/index_page.html',
181
+          controller: 'IndexPageCtrl'
182
+        }
183
+      }
184
+    })
185
+    .state('app.fav', {
186
+      url: '/fav',
187
+      cache: false,
188
+      views: {
189
+        'menuContent': {
190
+          templateUrl: 'templates/index_page.html',
191
+          controller: 'FavCtrl'
192
+        }
193
+      }
194
+    })
195
+    .state('app.byauthor', {
196
+        url: '/byauthor/:uid',
197
+      views: {
198
+        'menuContent': {
199
+          templateUrl: 'templates/byauthor.html',
200
+          controller: 'ByAuthorCtrl'
201
+        }
202
+      }
203
+    })
204
+    .state('app.cat', {
205
+        url: '/cat/:catname',
206
+      views: {
207
+        'menuContent': {
208
+          templateUrl: 'templates/index_page.html',
209
+          controller: 'ListPageCtrl'
210
+        }
211
+      }
212
+    })
213
+    .state('app.nearby', {
214
+        url: '/nearby',
215
+        cache: false,
216
+      views: {
217
+        'menuContent': {
218
+          templateUrl: 'templates/nearby.html',
219
+          controller: 'NearbyCtrl'
220
+        }
221
+      }
222
+    })
223
+
224
+  .state('app.single', {
225
+    url: '/playlists/:playlistId',
226
+    views: {
227
+      'menuContent': {
228
+        templateUrl: 'templates/playlist.html',
229
+        controller: 'PlaylistCtrl'
230
+      }
231
+    }
232
+  });
233
+  // if none of the above states are matched, use this as the fallback
234
+  $urlRouterProvider.otherwise('/app/index');
235
+})
236
+.service('mcotcms', function($http, $httpParamSerializer, API_URI, SEARCH_API, PAGE_LIMIT, SHARE_API, USER_API,GEN_USER_API_URI, AUTH_API, $cordovaSQLite, SETTING_API, FAV_API, NOTI_API, CMS_USER_API) {
237
+
238
+    this.all_posts = function(){
239
+        console.log("all posts");
240
+		return $http({
241
+            method: 'GET',
242
+            url: API_URI + SEARCH_API
243
+		});
244
+    }
245
+    this.get_share = function(id){
246
+        var params = {
247
+            'id': id,
248
+            'action': 'FETCH'
249
+        };
250
+
251
+        var qs = $httpParamSerializer(params);
252
+		return $http({
253
+            method: 'POST',
254
+            url: GEN_USER_API_URI + SHARE_API,
255
+            data: params
256
+		});
257
+    }
258
+    this.update_share = function(id){
259
+        var params = {
260
+            'id': id,
261
+            'action': 'update'
262
+        };
263
+
264
+        var qs = $httpParamSerializer(params);
265
+		return $http({
266
+            method: 'POST',
267
+            url: GEN_USER_API_URI + SHARE_API,
268
+            data: params
269
+		});
270
+    }
271
+    this.clear_data = function(){
272
+        console.log("clear data");
273
+        window.localStorage.clear();
274
+
275
+    }
276
+    this.load_data = function(){
277
+	    return $cordovaSQLite.execute(db, 'SELECT * FROM Messages ORDER BY id DESC');
278
+    }
279
+    this.load_personal_data = function(key){
280
+	    return $cordovaSQLite.execute(db, 'SELECT *  FROM Personal where key = ?', [key]);
281
+    }
282
+    this.get_settings = function(token){
283
+        var params = {
284
+            action: 'GET'
285
+        };
286
+        var qs = $httpParamSerializer(params);
287
+        return $http({
288
+            method: 'POST',
289
+            url: GEN_USER_API_URI + SETTING_API,
290
+            headers: {
291
+                Authorization: 'JWT '+token,
292
+            },
293
+            data: params
294
+        });
295
+    }
296
+    this.post_api = function(token, action, model){
297
+        var params = {
298
+            'model': model,
299
+            'action': action
300
+        };
301
+        return $http({
302
+            method: 'POST',
303
+            url: API_URI + "/api/v2.0/posts",
304
+            headers: {
305
+                Authorization: 'JWT '+token,
306
+            },
307
+            data: params
308
+        });
309
+    }
310
+    this.by_author = function(author_id){
311
+        var params = {
312
+            'id': author_id,
313
+        };
314
+        var qs = $httpParamSerializer(params);
315
+		return $http({
316
+            method: 'GET',
317
+            url: API_URI + CMS_USER_API + "?id=" + author_id,
318
+            data: params
319
+		});
320
+    }
321
+    this.more_on = function(oid, options){
322
+        var params = {
323
+            'method': 'moreon',
324
+            'id': oid,
325
+            /*
326
+            'query': {
327
+                'meta.location': {
328
+                    '$near': {
329
+                        '$geometry': {'type': "Point", 'coordinates':  [position.longitude, position.latitude]},
330
+                        '$maxDistance': 5000
331
+                    }
332
+                }
333
+            },*/
334
+            'type': options.type,
335
+            'version': 'short'
336
+
337
+        };
338
+        var qs = $httpParamSerializer(params);
339
+		return $http({
340
+            method: 'POST',
341
+            url: API_URI + SEARCH_API,
342
+            data: params
343
+		});
344
+    }
345
+    this.nearby = function(position, options){
346
+        var page = options.page;
347
+        var skip = page * PAGE_LIMIT;
348
+        var limit = PAGE_LIMIT;
349
+        var params = {
350
+            'query': {
351
+                'location': {
352
+                    '$near': {
353
+                        '$geometry': {'type': "Point", 'coordinates':  [position.longitude, position.latitude]},
354
+                        '$maxDistance': 5000
355
+                    }
356
+                }
357
+            },
358
+            'type': options.type,
359
+            'skip': skip,
360
+            'page': page,
361
+            'limit': limit,
362
+            'version': 'short'
363
+
364
+        };
365
+        var qs = $httpParamSerializer(params);
366
+		return $http({
367
+            method: 'POST',
368
+            url: API_URI + SEARCH_API,
369
+            data: params
370
+		});
371
+    }
372
+    this.save_settings = function(token, cats){
373
+        var params = {
374
+            'cats': cats,
375
+            'action': 'UPDATE'
376
+        };
377
+        var qs = $httpParamSerializer(params);
378
+        return $http({
379
+            method: 'POST',
380
+            url: GEN_USER_API_URI + SETTING_API,
381
+            headers: {
382
+                Authorization: 'JWT '+token,
383
+            },
384
+            data: params
385
+        });
386
+    }
387
+    this.get_fav  = function(token, options){
388
+
389
+        var page = options.page;
390
+        var skip = page * PAGE_LIMIT;
391
+        var limit = PAGE_LIMIT;
392
+        var params = {
393
+            'action': 'GET',
394
+            'type': options.type,
395
+            'skip': skip,
396
+            'page': page,
397
+            'limit': limit
398
+        };
399
+        var qs = $httpParamSerializer(params);
400
+        return $http({
401
+            method: 'POST',
402
+            url: GEN_USER_API_URI + FAV_API,
403
+            headers: {
404
+                Authorization: 'JWT '+token,
405
+            },
406
+            data: params
407
+        });
408
+    }
409
+    this.fetch_noti = function(token, options) {
410
+        var params = {
411
+            'action': 'FETCH',
412
+            'type': options.type,
413
+        };
414
+        var qs = $httpParamSerializer(params);
415
+        return $http({
416
+            method: 'POST',
417
+            url: GEN_USER_API_URI + NOTI_API,
418
+            headers: {
419
+                Authorization: 'JWT '+token,
420
+            },
421
+            data: params
422
+        });
423
+    }
424
+    this.add_fav = function(token, post_id, type){
425
+        var params = {
426
+            'post_id': post_id,
427
+            'action': 'ADD',
428
+            'type': type
429
+        };
430
+        var qs = $httpParamSerializer(params);
431
+        return $http({
432
+            method: 'POST',
433
+            url: GEN_USER_API_URI + FAV_API,
434
+            headers: {
435
+                Authorization: 'JWT '+token,
436
+            },
437
+            data: params
438
+        });
439
+    }
440
+    this.store_token = function(token){
441
+        var storage = window.localStorage;
442
+        console.log("token is ", token);
443
+        storage.setItem("token", token);
444
+        return $cordovaSQLite.execute(db, 'INSERT or REPLACE INTO Personal(key, value) VALUES (?, ?)', ['token', token]);
445
+    }
446
+
447
+    this.get_token  = function(){
448
+        return window.localStorage.getItem("token");
449
+    }
450
+    this.get_post_by_id = function(id){
451
+
452
+        var params = {
453
+            'id': id,
454
+            'version': 'full'
455
+        };
456
+        var qs = $httpParamSerializer(params);
457
+		return $http({
458
+            method: 'POST',
459
+            url: API_URI + SEARCH_API,
460
+            data: params
461
+		});
462
+    }
463
+
464
+    this.login = function(username, pass) {
465
+
466
+        var params = {
467
+            'username': username,
468
+            'password': pass
469
+        };
470
+        var qs = $httpParamSerializer(params);
471
+        return $http({
472
+            method: 'POST',
473
+            url: GEN_USER_API_URI + AUTH_API,
474
+            data: params
475
+        });
476
+    }
477
+    this.add_user = function(user_obj){
478
+        var params = {
479
+            'obj': user_obj,
480
+            'action': 'add'
481
+        };
482
+        var qs = $httpParamSerializer(params);
483
+        return $http({
484
+            method: 'POST',
485
+            url: GEN_USER_API_URI + USER_API,
486
+            data: params
487
+        });
488
+    }
489
+    this.add_user_by_form = function(user_obj){
490
+        var params = {
491
+            'obj': user_obj,
492
+            'action': 'register_by_form'
493
+        };
494
+        var qs = $httpParamSerializer(params);
495
+        return $http({
496
+            method: 'POST',
497
+            url: GEN_USER_API_URI + USER_API,
498
+            data: params
499
+        });
500
+    }
501
+    this.get_posts = function(option) {
502
+        var query = option.query;
503
+        var page = option.page;
504
+        var skip = page * PAGE_LIMIT;
505
+        var limit = PAGE_LIMIT;
506
+        var version = option.version;
507
+        var params = {
508
+            'query': query,
509
+            'page': page,
510
+            'skip': skip,
511
+            'limit': limit,
512
+            'version': version,
513
+			'sort': 'desc'
514
+        };
515
+        var qs = $httpParamSerializer(params);
516
+        console.log(qs);
517
+		return $http({
518
+            method: 'POST',
519
+            url: API_URI + SEARCH_API,
520
+            data: params
521
+		});
522
+    }
523
+
524
+})
525
+.directive('searchBar', [function () {
526
+	return {
527
+		scope: {
528
+			ngModel: '='
529
+		},
530
+		require: ['^ionNavBar', '?ngModel'],
531
+		restrict: 'E',
532
+		replace: true,
533
+		template: '<ion-nav-buttons side="right">'+
534
+						'<div class="searchBar">'+
535
+							'<div class="searchTxt" ng-show="ngModel.show">'+
536
+						  		'<div class="bgdiv"></div>'+
537
+						  		'<div class="bgtxt">'+
538
+						  			'<input type="text" placeholder="Procurar..." ng-model="ngModel.txt">'+
539
+						  		'</div>'+
540
+					  		'</div>'+
541
+						  	'<i class="icon placeholder-icon" ng-click="ngModel.txt=\'\';ngModel.show=!ngModel.show"></i>'+
542
+						'</div>'+
543
+					'</ion-nav-buttons>',
544
+
545
+		compile: function (element, attrs) {
546
+			var icon=attrs.icon
547
+					|| (ionic.Platform.isAndroid() && 'ion-android-search')
548
+					|| (ionic.Platform.isIOS()     && 'ion-ios7-search')
549
+					|| 'ion-search';
550
+			angular.element(element[0].querySelector('.icon')).addClass(icon);
551
+
552
+			return function($scope, $element, $attrs, ctrls) {
553
+				var navBarCtrl = ctrls[0];
554
+				$scope.navElement = $attrs.side === 'right' ? navBarCtrl.rightButtonsElement : navBarCtrl.leftButtonsElement;
555
+
556
+			};
557
+		},
558
+		controller: ['$scope','$ionicNavBarDelegate', function($scope,$ionicNavBarDelegate){
559
+			var title, definedClass;
560
+			$scope.$watch('ngModel.show', function(showing, oldVal, scope) {
561
+				if(showing!==oldVal) {
562
+					if(showing) {
563
+						if(!definedClass) {
564
+							var numicons=$scope.navElement.children().length;
565
+							angular.element($scope.navElement[0].querySelector('.searchBar')).addClass('numicons'+numicons);
566
+						}
567
+
568
+						title = $ionicNavBarDelegate.getTitle();
569
+						$ionicNavBarDelegate.setTitle('');
570
+					} else {
571
+						$ionicNavBarDelegate.setTitle(title);
572
+					}
573
+				} else if (!title) {
574
+					title = $ionicNavBarDelegate.getTitle();
575
+				}
576
+			});
577
+		}]
578
+	};
579
+}]);

+ 873 - 0
www/js/controllers.js

@@ -0,0 +1,873 @@
1
+angular.module('starter.controllers', ["angular-humanize", 'mcot.filters'])
2
+
3
+.controller('AppCtrl', function($scope, $ionicModal, $timeout, $cordovaFacebook, mcotcms, $cordovaSQLite, $location, $ionicHistory, $ionicPopup, $state, $ionicLoading, $cordovaLocalNotification, $rootScope, $interval, $cordovaOauth, $cordovaInAppBrowser, $http) {
4
+        $scope.data = {
5
+            bcount: 0
6
+        };
7
+        $scope.igLogin = function(){
8
+            $cordovaOauth.instagram("aede22fcf7a145779da9a3cd094069f5", ["basic"])
9
+                .then(function(success){
10
+                    console.log(success);
11
+                    var token = success.access_token;
12
+                    $http.get("https://api.instagram.com/v1/users/self/?access_token="+token)
13
+                        .then(function(success){
14
+                            console.log(success);
15
+                        }, function(error){
16
+                            console.log(error);
17
+                        });
18
+                },function(error){
19
+                    console.log(error);
20
+                });
21
+        };
22
+		$scope.twitterlogin = function(){
23
+			console.log("twitter login");
24
+			var api_key = "VNA6F7wAAwn10KJ7vqhdUPVzR"; //Enter your Consumer Key (API Key)
25
+			var api_secret = "bJDNQhpZPlScH45WuARaJtvv0fMTR68AVTI3VbsfImeht8tt3S"; // Enter your Consumer Secret (API Secret)
26
+			console.log("twitterlogin function got called");
27
+            /*
28
+			 var options = {
29
+			     location: 'yes',
30
+			     clearcache: 'yes',
31
+			     toolbar: 'no'
32
+			 };
33
+	$cordovaInAppBrowser.open('http://ngcordova.com', '_blank')
34
+	    .then(function(event) {
35
+	        // success
36
+	    })
37
+	    .catch(function(event) {
38
+	        // error
39
+	    });*/
40
+           /*
41
+			$cordovaOauth.twitter(api_key, api_secret).then(function(result) {
42
+			console.log(result);
43
+			}, function(error){
44
+				console.log(error);
45
+			});*/
46
+            $cordovaOauth.google("314112088577-mqifo59b09psg38fg5l3s27e8h2ihj2c.apps.googleusercontent.com", ["email"]).then(function(result) {
47
+                    console.log("Response Object -> " + JSON.stringify(result));
48
+            }, function(error) {
49
+                    console.log("Error -> " + error);
50
+            });
51
+		};
52
+ $rootScope.$on('$cordovaInAppBrowser:loaderror', function(e, event) {
53
+     console.log("load error");
54
+     console.log(e);
55
+     console.log(event);
56
+ });
57
+
58
+		$scope.authenticate = function(provider) {
59
+		    $auth.authenticate(provider);
60
+		};
61
+      /*
62
+        $interval(function() {
63
+            //$scope.fetchNotification();
64
+            $cordovaLocalNotification.schedule({
65
+                id: Math.floor(Date.now() / 1000),
66
+                title: 'test noti',
67
+                text: 'description',
68
+            }).then(function(result) {
69
+                console.log(result);
70
+            });
71
+            console.log("noti");
72
+        }, 5000);*/
73
+	    $scope.fetchNotification =  function(){
74
+            var token = mcotcms.get_token();
75
+            if( token !== null ){
76
+                mcotcms.fetch_noti(token, {type: 'post'})
77
+                    .then(function(success){
78
+                        console.log("fetch_noti");
79
+                        $scope.data.bcount = success.data.output.length;
80
+                        for(var i = 0; i < success.data.output.length; i++) {
81
+                            post = success.data.output[i];
82
+                            console.log("the post");
83
+                            console.log(post);
84
+                            $cordovaLocalNotification.schedule({
85
+                                id: post._id.$oid,
86
+                                title: post.title,
87
+                                text: post.description,
88
+                                data: {
89
+                                    oid: success.data.output[i]._id.$oid
90
+                                }
91
+                            }).then(function(result) {
92
+                                console.log(result);
93
+                            });
94
+                        }
95
+                    }, function(error){
96
+
97
+                    });
98
+            }
99
+        };
100
+        $scope.scheduleSingleNotification = function() {
101
+            var now = new Date().getTime();
102
+            var _10SecondsFromNow = new Date(now + 10 * 1000);
103
+            console.log("run notification");
104
+            /*
105
+			$cordovaLocalNotification.schedule({
106
+				id: 1,
107
+				title: 'Title here',
108
+				text: 'Text here',
109
+				at: _10SecondsFromNow
110
+			}).then(function(result) {
111
+				console.log(result);
112
+			});*/
113
+        };
114
+        $rootScope.$on('$cordovaLocalNotification:trigger',
115
+            function(event, notification, state) {
116
+                console.log("trigger");
117
+                console.log(event);
118
+                console.log(notification);
119
+                console.log(state);
120
+            });
121
+        $rootScope.$on('$cordovaLocalNotification:update',
122
+            function(event, notification, state) {
123
+                console.log("update");
124
+                console.log(event);
125
+                console.log(notification);
126
+                console.log(state);
127
+            });
128
+        $rootScope.$on('$cordovaLocalNotification:click',
129
+            function(event, notification, state) {
130
+                console.log("click");
131
+                console.log(event);
132
+                console.log(notification);
133
+                console.log(state);
134
+                var d = angular.fromJson(notification.data);
135
+                console.log(d);
136
+                $state.go("app.view", { id: d.oid});
137
+            });
138
+        $scope.loadData = function() {
139
+            //console.log(db);
140
+            console.log(window.localStorage);
141
+            console.log("load call");
142
+            mcotcms.load_personal_data("token")
143
+                .then(
144
+                    function(res) {
145
+
146
+                        if (res.rows.length > 0) {
147
+
148
+                            $scope.newMessage = res.rows.item(0);
149
+                            $scope.statusMessage = "Message loaded successful, cheers!";
150
+                        }
151
+                    },
152
+                    function(error) {
153
+                        $scope.statusMessage = "Error on loading: " + error.message;
154
+                    }
155
+                );
156
+        };
157
+        // With the new view caching in Ionic, Controllers are only called
158
+        // when they are recreated or on app start, instead of every page change.
159
+        // To listen for when this page is active (for example, to refresh data),
160
+        // listen for the $ionicView.enter event:
161
+        //$scope.$on('$ionicView.enter', function(e) {
162
+        //});
163
+
164
+        // Form data for the login modal
165
+        $scope.loginData = {};
166
+        $scope.signupData = {};
167
+        $scope.showAlert = function(title, text) {
168
+            var alertPopup = $ionicPopup.alert({
169
+                title: title,
170
+                template: text
171
+            });
172
+
173
+            alertPopup.then(function(res) {
174
+                console.log('Thank you for not eating my delicious ice cream cone');
175
+            });
176
+        };
177
+        $scope.logout = function() {
178
+            if (angular.isDefined($scope.modal))
179
+                $scope.modal.hide();
180
+            if (angular.isDefined($scope.signup_modal))
181
+                $scope.signup_modal.hide();
182
+
183
+            mcotcms.clear_data();
184
+            $ionicHistory.nextViewOptions({
185
+                disableBack: true
186
+            });
187
+            $state.go('app.index');
188
+
189
+            $cordovaFacebook.logout()
190
+                .then(function(success) {
191
+                    // success
192
+                    console.log("logout");
193
+                    console.log(success);
194
+                }, function(error) {
195
+                    // error
196
+                });
197
+        };
198
+        $scope.doSignUp = function() {
199
+
200
+            if ($scope.signupData.password == $scope.signupData.confirm_password) {
201
+                $scope.signupData.message = "Success";
202
+                mcotcms.add_user_by_form({
203
+                        email: $scope.signupData.email,
204
+                        password: $scope.signupData.password
205
+                    })
206
+                    .then(function(success) {
207
+                        console.log("add user ");
208
+                        //mcotcms.execute();
209
+                        //mcotcms.select();
210
+                        console.log(success);
211
+                        $scope.signup_modal.hide();
212
+                        $scope.$emit('requireLogin', {})
213
+                            /*
214
+						  $cordovaSQLite.execute(db, 'INSERT INTO Messages (message) VALUES (?)', ["tum"])
215
+						      .then(function(result) {
216
+						          $scope.statusMessage = "Message saved successful, cheers!";
217
+						      }, function(error) {
218
+						          $scope.statusMessage = "Error on saving: " + error.message;
219
+						      })*/
220
+                    }, function(error) {
221
+                        console.log("add erro user ");
222
+                        console.log(error);
223
+                        $scope.$emit("signupError", {
224
+                                msg: error.data.error
225
+                            })
226
+                            //console.log(error);
227
+                            //$scope.statusMessage = err.data.error;
228
+                            //console.log(err);
229
+                    });
230
+            } else {
231
+                $scope.signupData.message = "Not Matched";
232
+            }
233
+        };
234
+        $scope.getStatus = function() {
235
+            $cordovaFacebook.getLoginStatus()
236
+                .then(function(success) {
237
+                    console.log("get status");
238
+                    console.log(success);
239
+                    /*
240
+                    { authResponse: {
241
+                        userID: "12345678912345",
242
+                        accessToken: "kgkh3g42kh4g23kh4g2kh34g2kg4k2h4gkh3g4k2h4gk23h4gk2h34gk234gk2h34AndSoOn",
243
+                        session_Key: true,
244
+                        expiresIn: "5183738",
245
+                        sig: "..."
246
+                        },
247
+                        status: "connected"
248
+                    }
249
+                    */
250
+                }, function(error) {
251
+                    // error
252
+                });
253
+        };
254
+        $scope.fblogin = function() {
255
+            $cordovaFacebook.login(["public_profile", "email", "user_friends"])
256
+                .then(function(login_obj) {
257
+                    console.log("fb login");
258
+                    console.log(login_obj);
259
+                    $cordovaFacebook.api("me", ["public_profile"])
260
+                        .then(function(me_obj) {
261
+                            console.log("me");
262
+                            //console.log(me_obj);
263
+                            mcotcms.add_user({
264
+                                    'login': login_obj,
265
+                                    'me': me_obj
266
+                                })
267
+                                .then(function(result) {
268
+                                    console.log(" success add_user ");
269
+                                    console.log(result.data.token);
270
+                                    mcotcms.store_token(result.data.token)
271
+                                        .then(function(result) {
272
+                                            $scope.$emit('signinSuccess', {});
273
+                                        }, function(error) {
274
+                                            console.log(error);
275
+                                        });
276
+                                }, function(err) {
277
+                                    console.log(err);
278
+                                });
279
+
280
+                        }, function(error) {
281
+                            console.log("error fb");
282
+                        });
283
+
284
+                    // { id: "634565435",
285
+                    //   lastName: "bob"
286
+                    //   ...
287
+                    // }
288
+                }, function(error) {
289
+                    console.log("error fb");
290
+                });
291
+        };
292
+        // Create the login modal that we will use later
293
+        $ionicModal.fromTemplateUrl('templates/login.html', {
294
+            scope: $scope
295
+        }).then(function(modal) {
296
+            $scope.modal = modal;
297
+        });
298
+        $scope.openLoginDialog = function() {
299
+            console.log("modal");
300
+            console.log($scope.modal);
301
+        };
302
+        // Create the login modal that we will use later
303
+        $scope.$on('requireLogin', function(event, args) {
304
+            if( angular.isDefined($scope.modal)) {
305
+                $scope.modal.remove();
306
+            }
307
+            $ionicModal.fromTemplateUrl('templates/login.html', {
308
+                scope: $scope
309
+            }).then(function(modal) {
310
+
311
+                $scope.modal = modal;
312
+                $scope.modal.show();
313
+            });
314
+            //$scope.openLoginDialog();
315
+        });
316
+        $scope.$on('logoutEvent', function(event, args) {
317
+            $scope.logout();
318
+        });
319
+        $scope.$on('signinError', function(event, args) {
320
+            console.log("Sign In Error");
321
+            $scope.showAlert("Sign In Error", args.msg);
322
+        });
323
+        $scope.$on('showLoading', function(event, args) {
324
+            console.log("show loading");
325
+            $ionicLoading.show({
326
+                template: args.text
327
+            }).then(function() {
328
+                console.log("The loading indicator is now displayed");
329
+            });
330
+        });
331
+
332
+        $scope.$on('hideLoading', function(event, args) {
333
+            $ionicLoading.hide().then(function() {
334
+                console.log("The loading indicator is now hidden");
335
+            });
336
+        });
337
+        $scope.$on('signupError', function(event, args) {
338
+            console.log("Sign Up Error");
339
+            $scope.showAlert("Sign Up Error", args.msg);
340
+        });
341
+        $scope.$on('signinSuccess', function(event, args) {
342
+            console.log("Sign in success");
343
+            $state.go("app.index");
344
+            $scope.closeLogin();
345
+            $scope.closeSignUp();
346
+        });
347
+
348
+        $ionicModal.fromTemplateUrl('templates/signup.html', {
349
+            scope: $scope
350
+        }).then(function(su_modal) {
351
+            $scope.signup_modal = su_modal;
352
+        });
353
+
354
+        // Triggered in the login modal to close it
355
+        $scope.closeLogin = function() {
356
+            console.log("close sigin");
357
+            if(angular.isDefined($scope.modal))
358
+                $scope.modal.hide();
359
+
360
+            var token = mcotcms.get_token();
361
+
362
+            console.log(token);
363
+            if (token == 'null') {
364
+                console.log('null ?');
365
+                $ionicHistory.nextViewOptions({
366
+                    disableBack: true
367
+                });
368
+                $state.go('app.index');
369
+            }
370
+        };
371
+        $scope.closeSignUp = function() {
372
+            if( angular.isDefined($scope.signup_modal))
373
+                $scope.signup_modal.hide();
374
+            var token = mcotcms.get_token();
375
+            if (token == null) {
376
+                $ionicHistory.nextViewOptions({
377
+                    disableBack: true
378
+                });
379
+                $state.go('app.index');
380
+            }
381
+        };
382
+
383
+        // Open the login modal
384
+        $scope.login = function() {
385
+            $scope.modal.show();
386
+        };
387
+
388
+        // Perform the login action when the user submits the login form
389
+        $scope.doLogin = function() {
390
+            console.log('Doing login', $scope.loginData);
391
+
392
+            // Simulate a login delay. Remove this and replace with your login
393
+            // code if using a login system
394
+            mcotcms.login($scope.loginData.username, $scope.loginData.password)
395
+                .then(function(success) {
396
+                    console.log(success);
397
+                    mcotcms.store_token(success.data.access_token)
398
+                        .then(function(result) {
399
+                            console.log(result);
400
+                        }, function(error) {
401
+                            console.log(error);
402
+                        });
403
+                }, function(error) {
404
+                    $scope.$emit("signinError", {
405
+                        msg: error.data.description
406
+                    })
407
+                    console.log(error);
408
+                });
409
+            $timeout(function() {
410
+                $scope.closeLogin();
411
+            }, 1000);
412
+        };
413
+    })
414
+    .controller('RequireLoginCtrl', function($scope, mcotcms, $location) {
415
+        console.log("check login ...");
416
+        var token = mcotcms.get_token();
417
+        if (token == 'null' || token == null)  {
418
+            console.log("token null");
419
+            mcotcms.store_token(null);
420
+            $scope.$emit('requireLogin', {});
421
+        }
422
+
423
+    })
424
+    .controller('UtilCtrl', function($scope, mcotcms, $location) {
425
+
426
+        $scope.toDate = function(mongoDate) {
427
+          console.log(mongoDate)
428
+			if( angular.isDefined(mongoDate) ) {
429
+            	return new Date(mongoDate.$date);
430
+			}else {
431
+            	return new Date();
432
+			}
433
+        };
434
+		$scope.go = function ( path ) {
435
+			$location.path( path );
436
+		};
437
+        $scope.fav = function(post_id, type) {
438
+            var token = mcotcms.get_token();
439
+            mcotcms.add_fav(token, post_id, type)
440
+                .then(function(success) {
441
+                    console.log(success);
442
+                }, function(error) {
443
+                    console.log(error);
444
+                });
445
+        };
446
+    })
447
+    .controller('LoadMoreCtrl', function($scope, mcotcms, PAGE_LIMIT) {
448
+        $scope.current_page = 0;
449
+        $scope.is_empty = true;
450
+        $scope.moreDataCanBeLoaded = function() {
451
+            console.log("more data canbe loaded");
452
+            return $scope.is_empty == false;
453
+        };
454
+        $scope.firstLoad = function() {
455
+            console.log("load 1");
456
+            $scope.is_empty = true;
457
+            if (angular.isUndefined($scope.results)) {
458
+                $scope.results = [];
459
+            }
460
+            if (angular.isDefined($scope.special_action)) {
461
+                console.log("wait im");
462
+                if ($scope.special_action == "get_fav") {
463
+                    var the_query = mcotcms.get_fav($scope.token, {
464
+                        'page': $scope.current_page++,
465
+                        'type': 'post'
466
+                    });
467
+                } else if ($scope.special_action == "get_nearby") {
468
+                    console.log("get nearby");
469
+                    var the_query = mcotcms.nearby($scope.position, {
470
+                        'page': $scope.current_page++,
471
+                        'type': 'post'
472
+                    });
473
+                }
474
+            } else {
475
+                var the_query = mcotcms.get_posts({
476
+                    'query': $scope.query,
477
+                    'page': $scope.current_page++,
478
+                    'version': $scope.post_version
479
+                });
480
+            }
481
+            the_query.then(function successCallback(response) {
482
+                // this callback will be called asynchronously
483
+                // when the response is available
484
+                console.log("first load");
485
+                console.log(response.data)
486
+                if (response.data.output.length == 0) {
487
+                    $scope.is_empty = true;
488
+                    return;
489
+                }
490
+                var output = [];
491
+                // for(var i = 0; i < 10; i++ ){
492
+                output = output.concat(response.data.output);
493
+                //}
494
+                console.log("update results");
495
+                $scope.results = $scope.results.concat(output);
496
+                console.log($scope.results);
497
+                $scope.is_empty = false;
498
+                //$scope.$broadcast('scroll.infiniteScrollComplete');
499
+                //$scope.results = response.data.output;
500
+            }, function errorCallback(response) {
501
+                // called asynchronously if an error occurs
502
+                // or server returns response with an error status.
503
+            });
504
+        };
505
+        $scope.firstLoad();
506
+        $scope.loadMoreData = function() {
507
+            console.log("load more 2");
508
+            if ($scope.is_empty == true) {
509
+                return;
510
+            }
511
+            if (angular.isUndefined($scope.results)) {
512
+                console.log("recreate ");
513
+                $scope.results = [];
514
+            }
515
+            if (angular.isDefined($scope.special_action)) {
516
+                console.log("wait im");
517
+                if ($scope.special_action == "get_fav") {
518
+                    var the_query = mcotcms.get_fav($scope.token, {
519
+                        'page': $scope.current_page++,
520
+                        'type': 'post'
521
+                    });
522
+                } else if ($scope.special_action == "get_nearby") {
523
+                    var the_query = mcotcms.nearby($scope.position, {
524
+                        'page': $scope.current_page++,
525
+                        'type': 'post'
526
+                    });
527
+                }
528
+            } else {
529
+                var the_query = mcotcms.get_posts({
530
+                    'query': $scope.query,
531
+                    'page': $scope.current_page++,
532
+                    'version': $scope.post_version
533
+                })
534
+            }
535
+            the_query.then(function successCallback(response) {
536
+                // this callback will be called asynchronously
537
+                // when the response is available
538
+                console.log("... =>");
539
+                console.log(response.data.output.length);
540
+                if (response.data.output.length == 0) {
541
+                    $scope.is_empty = true;
542
+                    return;
543
+                }
544
+                console.log("not empty ");
545
+                var output = [];
546
+                /*
547
+                for(var i = 0; i < 10; i++ ){*/
548
+                output = output.concat(response.data.output);
549
+                /*}*/
550
+                console.log("update results");
551
+                $scope.results = $scope.results.concat(output);
552
+                $scope.is_empty = false;
553
+                $scope.$broadcast('scroll.infiniteScrollComplete');
554
+                //$scope.results = response.data.output;
555
+            }, function errorCallback(response) {
556
+                // called asynchronously if an error occurs
557
+                // or server returns response with an error status.
558
+            });
559
+        };
560
+        $scope.$on('$stateChangeSuccess', function() {
561
+            $scope.loadMoreData();
562
+        });
563
+    })
564
+    .controller('FavCtrl', function($scope, mcotcms, $controller) {
565
+        $scope.query = {};
566
+        $scope.post_version = "short";
567
+        $scope.special_action = "get_fav";
568
+        $controller('RequireLoginCtrl', {
569
+            $scope: $scope
570
+        });
571
+        $scope.token = mcotcms.get_token();
572
+        if ($scope.token != 'null') {
573
+            $controller('LoadMoreCtrl', {
574
+                $scope: $scope
575
+            });
576
+        }
577
+    })
578
+    .controller('ByAuthorCtrl', function($scope, $stateParams, $controller, mcotcms) {
579
+        console.log($stateParams);
580
+        $scope.catname = $stateParams.uid;
581
+        $scope.uid = $stateParams.uid;
582
+        $scope.query = { 'author': $stateParams.uid };
583
+        $scope.post_version = "short";
584
+		mcotcms.by_author($scope.uid)
585
+			.then(function(success){
586
+				console.log("author");
587
+				console.log(success);
588
+				$scope.by_author = success.data.output;
589
+			},function(error){
590
+			});
591
+        $controller('LoadMoreCtrl', {
592
+            $scope: $scope
593
+        });
594
+    })
595
+    .controller('NearbyCtrl', function($scope, mcotcms, $controller, $cordovaGeolocation, $ionicLoading) {
596
+        $scope.query = {};
597
+        var posOptions = {
598
+            timeout: 10000,
599
+            enableHighAccuracy: false
600
+        };
601
+        $scope.$emit("showLoading", {
602
+            text: "Getting Current Position"
603
+        });
604
+        $cordovaGeolocation
605
+            .getCurrentPosition(posOptions)
606
+            .then(function(position) {
607
+                var lat = position.coords.latitude
608
+                var long = position.coords.longitude
609
+                $scope.position = position.coords;
610
+                $scope.post_version = "short";
611
+                $scope.special_action = "get_nearby";
612
+                console.log(position.coords);
613
+                console.log("get nearby call");
614
+                $scope.$emit("hideLoading", {});
615
+                $controller('LoadMoreCtrl', {
616
+                    $scope: $scope
617
+                });
618
+            }, function(err) {
619
+                // error
620
+            });
621
+    })
622
+    .controller('PlaylistsCtrl', function($scope) {
623
+        $scope.playlists = [{
624
+            title: 'Reggae',
625
+            id: 1
626
+        }, {
627
+            title: 'Chill',
628
+            id: 2
629
+        }, {
630
+            title: 'Dubstep',
631
+            id: 3
632
+        }, {
633
+            title: 'Indie',
634
+            id: 4
635
+        }, {
636
+            title: 'Rap',
637
+            id: 5
638
+        }, {
639
+            title: 'Cowbell',
640
+            id: 6
641
+        }];
642
+        $scope.settingsList = [{
643
+            text: "Wireless",
644
+            checked: true
645
+        }, {
646
+            text: "GPS",
647
+            checked: false
648
+        }, {
649
+            text: "Bluetooth",
650
+            checked: false
651
+        }];
652
+    })
653
+    .controller('SettingCtrl', function($scope, mcotcms, $controller) {
654
+        $controller('RequireLoginCtrl', {
655
+            $scope: $scope
656
+        });
657
+        var token = mcotcms.get_token();
658
+        $scope.token = token;
659
+        $scope.openUserLink = function() {
660
+            window.open('http://mcot-simplitic.simplico.net/user_panel/'+$scope.token, '_system');
661
+        };
662
+        console.log("token ", token);
663
+        $scope.categories = [{
664
+            title: "nearme",
665
+            value: true
666
+        }, {
667
+            title: "Breaking News",
668
+            value: true
669
+        }, {
670
+            title: "ข่าวพาดหัว",
671
+            value: true
672
+        }, {
673
+            title: "health",
674
+            value: true
675
+        }, {
676
+            title: "fashion",
677
+            value: true
678
+        }, {
679
+            title: "perspective",
680
+            value: true
681
+        }, {
682
+            title: "idea",
683
+            value: true
684
+        }, {
685
+            title: "outlook",
686
+            value: true
687
+        }, {
688
+            title: "phototalk",
689
+            value: true
690
+        }, ];
691
+        $scope.$on('$destroy', function() {
692
+            console.log("leaving from setting");
693
+            console.log($scope.categories);
694
+            mcotcms.save_settings(token, $scope.categories);
695
+        });
696
+        mcotcms.get_settings(token)
697
+            .then(function(success) {
698
+                var cat_serv = success.data.output;
699
+                console.log(cat_serv);
700
+                for (var i in $scope.categories) {
701
+                    c = $scope.categories[i];
702
+                    console.log(cat_serv[c.title]);
703
+                    if (cat_serv[c.title] != true) {
704
+                        c.value = false;
705
+                    }
706
+                }
707
+            }, function(error) {
708
+                console.log("errror ..", error);
709
+                mcotcms.store_token(null);
710
+                //$scope.$emit("requireLogin", {});
711
+            });
712
+        $scope.logout = function() {
713
+            console.log("logout ...");
714
+            $scope.$emit('logoutEvent', {});
715
+        };
716
+    })
717
+    .controller('IndexPageCtrl', function($scope, mcotcms, $controller) {
718
+        $scope.query = { 'categories.text': 'TNA'};
719
+        $scope.post_version = "short";
720
+
721
+        $controller('UtilCtrl', {
722
+            $scope: $scope
723
+        });
724
+        $controller('LoadMoreCtrl', {
725
+            $scope: $scope
726
+        });
727
+        /*
728
+        mcotcms.all_posts()
729
+        .then(function successCallback(response) {
730
+            // this callback will be called asynchronously
731
+            // when the response is available
732
+            var output = [];
733
+            for(var i = 0; i < 10; i++ ){
734
+                output = output.concat(response.data.output);
735
+            }
736
+            $scope.results = output ;
737
+            //$scope.results = response.data.output;
738
+        }, function errorCallback(response) {
739
+            // called asynchronously if an error occurs
740
+            // or server returns response with an error status.
741
+        });*/
742
+
743
+    })
744
+
745
+.controller('PlaylistCtrl', function($scope, $stateParams) {})
746
+    .controller('LoginCtrl', function($scope, $stateParams, $cordovaFacebook) {
747
+        $scope.login = function() {
748
+            $cordovaFacebook.login(["public_profile", "email", "user_friends"])
749
+                .then(function(success) {
750
+                    console.log(success);
751
+                    // { id: "634565435",
752
+                    //   lastName: "bob"
753
+                    //   ...
754
+                    // }
755
+                }, function(error) {
756
+                    // error
757
+                });
758
+        };
759
+    })
760
+    .controller('ViewCtrl', function($scope, $stateParams, mcotcms, $cordovaDatePicker, $cordovaSocialSharing, $filter, $controller, $sce, PUBLIC_TOKEN) {
761
+        //var deviceType = (navigator.userAgent.match(/iPad/i))  == "iPad" ? "iPad" : (navigator.userAgent.match(/iPhone/i))  == "iPhone" ? "iPhone" : (navigator.userAgent.match(/Android/i)) == "Android" ? "Android" : (navigator.userAgent.match(/BlackBerry/i)) == "BlackBerry" ? "BlackBerry" : "null";
762
+        var isIOS = ionic.Platform.isIOS();
763
+        var isAndroid = ionic.Platform.isAndroid();
764
+        //console.log(deviceType);
765
+        console.log("is IOS ", isIOS);
766
+        console.log("is ANd ", isAndroid);
767
+
768
+        var id = $stateParams.id;
769
+
770
+        $controller('UtilCtrl', {
771
+            $scope: $scope
772
+        });
773
+        $scope.openMap = function(loc) {
774
+            window.open("https://www.google.com/maps?q="+loc[1]+","+loc[0],'_system');
775
+        };
776
+        mcotcms.get_share(id)
777
+            .then(function(success){
778
+                console.log(success);
779
+                $scope.shareCounts = success.data.output;
780
+            },function(error){
781
+            });
782
+        mcotcms.get_post_by_id(id)
783
+            .then(function successCallback(response) {
784
+                $scope.post = response.data.output[0];
785
+				$scope.post_body = $sce.trustAsHtml($scope.post.body);
786
+                mcotcms.by_author($scope.post['author'])
787
+                    .then(function(success){
788
+                        console.log("author");
789
+                        console.log(success);
790
+                        $scope.by_author = success.data.output;
791
+                    },function(error){
792
+                    });
793
+                mcotcms.post_api(PUBLIC_TOKEN, "LIST",  { query: {'categories.text': $scope.post.categories[$scope.post.categories.length - 1].text} })
794
+                    .then(function(success){
795
+                        $scope.more_ons = success.data.output;
796
+                    }, function(error){
797
+
798
+                    });
799
+            }, function errorCallback(response) {});
800
+
801
+        $scope.doSomething = function() {
802
+            var options = {
803
+                date: new Date(),
804
+                mode: 'date', // or 'time'
805
+                minDate: new Date() - 10000,
806
+                allowOldDates: true,
807
+                allowFutureDates: false,
808
+                doneButtonLabel: 'DONE',
809
+                doneButtonColor: '#F2F3F4',
810
+                cancelButtonLabel: 'CANCEL',
811
+                cancelButtonColor: '#000000'
812
+            };
813
+            $cordovaDatePicker.show(options).then(function(date) {
814
+                alert(date);
815
+            });
816
+        };
817
+
818
+        $scope.share = function() {
819
+            var link_url = "http://www.tnamcot.com/view/" + $scope.post._id.$oid;
820
+
821
+            $cordovaSocialSharing
822
+                //.share($scope.post.title + " | " + $scope.post.description + ' ' + link_url, null, null, link_url) // Share via native share sheet
823
+                .share(null, null, null, link_url) // Share via native share sheet
824
+                .then(function(result) {
825
+                    // Success!
826
+                    console.log("share result ", result);
827
+                    if (result == true) {
828
+                        console.log("share true !!");
829
+                        mcotcms.update_share($scope.post._id.$oid)
830
+                            .then(function successCallBack(response) {
831
+                                console.log("share ok");
832
+                            }, function errorCallback(response) {
833
+                                console.log("fail");
834
+                            });
835
+                    }
836
+                    console.log("Share completed? ", result.completed); // On Android apps mostly return false even while it's true
837
+                    console.log("Shared to app: ", result.app); // On Android result.app is currently empty. On iOS it's empty when sharing is cancelled (result.completed=false)
838
+                }, function(err) {
839
+                    // An error occured. Show a message to the user
840
+                    console.log("share error ", err);
841
+                });
842
+        };
843
+
844
+
845
+    })
846
+    .controller('ListPageCtrl', function($scope, $stateParams, $controller, mcotcms) {
847
+        console.log($stateParams);
848
+        $scope.catname = $stateParams.catname;
849
+        $scope.query = {'categories.text': $stateParams.catname};
850
+        $controller('LoadMoreCtrl', {
851
+            $scope: $scope
852
+        });
853
+    })
854
+    .controller('SearchCtrl', function($scope, $stateParams, $http, $ionicHistory, mcotcms, $controller) {
855
+        console.log("search ctrl");
856
+        $scope.search = "";
857
+        $scope.search2 = "";
858
+        $scope.query = {'categories.text': 'TNA'};
859
+        $scope.post_version = "short";
860
+        $controller('UtilCtrl', {
861
+            $scope: $scope
862
+        });
863
+        $controller('LoadMoreCtrl', {
864
+            $scope: $scope
865
+        });
866
+        $scope.change = function(v) {
867
+            $scope.query = {'title': {'$regex': v} };
868
+            $scope.results = [];
869
+            $scope.current_page = 0;
870
+            $scope.firstLoad();
871
+
872
+        };
873
+    });

+ 41 - 0
www/lib/Ionicons/.bower.json

@@ -0,0 +1,41 @@
1
+{
2
+  "ignore": [
3
+    "**/.*",
4
+    "builder",
5
+    "node_modules",
6
+    "bower_components",
7
+    "test",
8
+    "tests"
9
+  ],
10
+  "version": "2.0.1",
11
+  "name": "Ionicons",
12
+  "license": "MIT",
13
+  "authors": [
14
+    "Ben Sperry <ben@drifty.com>",
15
+    "Adam Bradley <adam@drifty.com>",
16
+    "Max Lynch <max@drifty.com>"
17
+  ],
18
+  "keywords": [
19
+    "fonts",
20
+    "icon font",
21
+    "icons",
22
+    "ionic",
23
+    "web font"
24
+  ],
25
+  "main": [
26
+    "css/ionicons.css",
27
+    "fonts/*"
28
+  ],
29
+  "homepage": "https://github.com/driftyco/ionicons",
30
+  "description": "Ionicons - free and beautiful icons from the creators of Ionic Framework",
31
+  "_release": "2.0.1",
32
+  "_resolution": {
33
+    "type": "version",
34
+    "tag": "v2.0.1",
35
+    "commit": "ecb4b806831005c25b97ed9089fbb1d7dcc0879c"
36
+  },
37
+  "_source": "https://github.com/driftyco/ionicons.git",
38
+  "_target": "^2.0.1",
39
+  "_originalSource": "ionicons",
40
+  "_direct": true
41
+}

+ 21 - 0
www/lib/Ionicons/LICENSE

@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2014 Drifty (http://drifty.com/)
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.

+ 31 - 0
www/lib/Ionicons/bower.json

@@ -0,0 +1,31 @@
1
+{
2
+    "ignore": [
3
+        "**/.*",
4
+        "builder",
5
+        "node_modules",
6
+        "bower_components",
7
+        "test",
8
+        "tests"
9
+    ],
10
+    "version": "2.0.0",
11
+    "name": "Ionicons",
12
+    "license": "MIT",
13
+    "authors": [
14
+        "Ben Sperry <ben@drifty.com>",
15
+        "Adam Bradley <adam@drifty.com>",
16
+        "Max Lynch <max@drifty.com>"
17
+    ],
18
+    "keywords": [
19
+        "fonts",
20
+        "icon font",
21
+        "icons",
22
+        "ionic",
23
+        "web font"
24
+    ],
25
+    "main": [
26
+        "css/ionicons.css",
27
+        "fonts/*"
28
+    ],
29
+    "homepage": "https://github.com/driftyco/ionicons",
30
+    "description": "Ionicons - free and beautiful icons from the creators of Ionic Framework"
31
+}

File diff suppressed because it is too large
+ 28009 - 0
www/lib/Ionicons/cheatsheet.html


+ 19 - 0
www/lib/Ionicons/component.json

@@ -0,0 +1,19 @@
1
+{
2
+    "repo": "driftyco/ionicons",
3
+    "development": {},
4
+    "version": "2.0.0",
5
+    "styles": [
6
+        "css/ionicons.css"
7
+    ],
8
+    "name": "Ionicons",
9
+    "dependencies": {},
10
+    "keywords": [],
11
+    "license": "MIT",
12
+    "fonts": [
13
+        "fonts/ionicons.eot",
14
+        "fonts/ionicons.svg",
15
+        "fonts/ionicons.ttf",
16
+        "fonts/ionicons.woff"
17
+    ],
18
+    "description": "The premium icon font for Ionic Framework."
19
+}

+ 36 - 0
www/lib/Ionicons/composer.json

@@ -0,0 +1,36 @@
1
+{
2
+    "name": "driftyco/ionicons",
3
+    "license": [
4
+        "MIT"
5
+    ],
6
+    "extra": {},
7
+    "authors": [
8
+        {
9
+            "homepage": "https://twitter.com/benjsperry",
10
+            "role": "Designer",
11
+            "name": "Ben Sperry",
12
+            "email": "ben@drifty.com"
13
+        },
14
+        {
15
+            "homepage": "https://twitter.com/adamdbradley",
16
+            "role": "Developer",
17
+            "name": "Adam Bradley",
18
+            "email": "adam@drifty.com"
19
+        },
20
+        {
21
+            "homepage": "https://twitter.com/maxlynch",
22
+            "role": "Developer",
23
+            "name": "Max Lynch",
24
+            "email": "max@drifty.com"
25
+        }
26
+    ],
27
+    "keywords": [
28
+        "fonts",
29
+        "icon font",
30
+        "icons",
31
+        "ionic",
32
+        "web font"
33
+    ],
34
+    "homepage": "http://ionicons.com/",
35
+    "description": "The premium icon font for Ionic Framework."
36
+}

File diff suppressed because it is too large
+ 1480 - 0
www/lib/Ionicons/css/ionicons.css


File diff suppressed because it is too large
+ 11 - 0
www/lib/Ionicons/css/ionicons.min.css


BIN
www/lib/Ionicons/fonts/ionicons.eot


File diff suppressed because it is too large
+ 2230 - 0
www/lib/Ionicons/fonts/ionicons.svg


BIN
www/lib/Ionicons/fonts/ionicons.ttf


BIN
www/lib/Ionicons/fonts/ionicons.woff


+ 27 - 0
www/lib/Ionicons/less/_ionicons-font.less

@@ -0,0 +1,27 @@
1
+// Ionicons Font Path
2
+// --------------------------
3
+
4
+@font-face {
5
+ font-family: @ionicons-font-family;
6
+ src:url("@{ionicons-font-path}/ionicons.eot?v=@{ionicons-version}");
7
+ src:url("@{ionicons-font-path}/ionicons.eot?v=@{ionicons-version}#iefix") format("embedded-opentype"),
8
+  url("@{ionicons-font-path}/ionicons.ttf?v=@{ionicons-version}") format("truetype"),
9
+  url("@{ionicons-font-path}/ionicons.woff?v=@{ionicons-version}") format("woff"),
10
+  url("@{ionicons-font-path}/ionicons.svg?v=@{ionicons-version}#Ionicons") format("svg");
11
+ font-weight: normal;
12
+ font-style: normal;
13
+}
14
+
15
+.ion {
16
+  display: inline-block;
17
+  font-family: @ionicons-font-family;
18
+  speak: none;
19
+  font-style: normal;
20
+  font-weight: normal;
21
+  font-variant: normal;
22
+  text-transform: none;
23
+  text-rendering: auto;
24
+  line-height: 1;
25
+  -webkit-font-smoothing: antialiased;
26
+  -moz-osx-font-smoothing: grayscale;
27
+}

File diff suppressed because it is too large
+ 1473 - 0
www/lib/Ionicons/less/_ionicons-icons.less


+ 747 - 0
www/lib/Ionicons/less/_ionicons-variables.less

@@ -0,0 +1,747 @@
1
+/*!
2
+Ionicons, v2.0.0
3
+Created by Ben Sperry for the Ionic Framework, http://ionicons.com/
4
+https://twitter.com/benjsperry  https://twitter.com/ionicframework
5
+MIT License: https://github.com/driftyco/ionicons
6
+*/
7
+// Ionicons Variables
8
+// --------------------------
9
+
10
+@ionicons-font-path: "../fonts";
11
+@ionicons-font-family: "Ionicons";
12
+@ionicons-version: "2.0.0";
13
+@ionicons-prefix: ion-;
14
+
15
+@ionicon-var-alert: "\f101";
16
+@ionicon-var-alert-circled: "\f100";
17
+@ionicon-var-android-add: "\f2c7";
18
+@ionicon-var-android-add-circle: "\f359";
19
+@ionicon-var-android-alarm-clock: "\f35a";
20
+@ionicon-var-android-alert: "\f35b";
21
+@ionicon-var-android-apps: "\f35c";
22
+@ionicon-var-android-archive: "\f2c9";
23
+@ionicon-var-android-arrow-back: "\f2ca";
24
+@ionicon-var-android-arrow-down: "\f35d";
25
+@ionicon-var-android-arrow-dropdown: "\f35f";
26
+@ionicon-var-android-arrow-dropdown-circle: "\f35e";
27
+@ionicon-var-android-arrow-dropleft: "\f361";
28
+@ionicon-var-android-arrow-dropleft-circle: "\f360";
29
+@ionicon-var-android-arrow-dropright: "\f363";
30
+@ionicon-var-android-arrow-dropright-circle: "\f362";
31
+@ionicon-var-android-arrow-dropup: "\f365";
32
+@ionicon-var-android-arrow-dropup-circle: "\f364";
33
+@ionicon-var-android-arrow-forward: "\f30f";
34
+@ionicon-var-android-arrow-up: "\f366";
35
+@ionicon-var-android-attach: "\f367";
36
+@ionicon-var-android-bar: "\f368";
37
+@ionicon-var-android-bicycle: "\f369";
38
+@ionicon-var-android-boat: "\f36a";
39
+@ionicon-var-android-bookmark: "\f36b";
40
+@ionicon-var-android-bulb: "\f36c";
41
+@ionicon-var-android-bus: "\f36d";
42
+@ionicon-var-android-calendar: "\f2d1";
43
+@ionicon-var-android-call: "\f2d2";
44
+@ionicon-var-android-camera: "\f2d3";
45
+@ionicon-var-android-cancel: "\f36e";
46
+@ionicon-var-android-car: "\f36f";
47
+@ionicon-var-android-cart: "\f370";
48
+@ionicon-var-android-chat: "\f2d4";
49
+@ionicon-var-android-checkbox: "\f374";
50
+@ionicon-var-android-checkbox-blank: "\f371";
51
+@ionicon-var-android-checkbox-outline: "\f373";
52
+@ionicon-var-android-checkbox-outline-blank: "\f372";
53
+@ionicon-var-android-checkmark-circle: "\f375";
54
+@ionicon-var-android-clipboard: "\f376";
55
+@ionicon-var-android-close: "\f2d7";
56
+@ionicon-var-android-cloud: "\f37a";
57
+@ionicon-var-android-cloud-circle: "\f377";
58
+@ionicon-var-android-cloud-done: "\f378";
59
+@ionicon-var-android-cloud-outline: "\f379";
60
+@ionicon-var-android-color-palette: "\f37b";
61
+@ionicon-var-android-compass: "\f37c";
62
+@ionicon-var-android-contact: "\f2d8";
63
+@ionicon-var-android-contacts: "\f2d9";
64
+@ionicon-var-android-contract: "\f37d";
65
+@ionicon-var-android-create: "\f37e";
66
+@ionicon-var-android-delete: "\f37f";
67
+@ionicon-var-android-desktop: "\f380";
68
+@ionicon-var-android-document: "\f381";
69
+@ionicon-var-android-done: "\f383";
70
+@ionicon-var-android-done-all: "\f382";
71
+@ionicon-var-android-download: "\f2dd";
72
+@ionicon-var-android-drafts: "\f384";
73
+@ionicon-var-android-exit: "\f385";
74
+@ionicon-var-android-expand: "\f386";
75
+@ionicon-var-android-favorite: "\f388";
76
+@ionicon-var-android-favorite-outline: "\f387";
77
+@ionicon-var-android-film: "\f389";
78
+@ionicon-var-android-folder: "\f2e0";
79
+@ionicon-var-android-folder-open: "\f38a";
80
+@ionicon-var-android-funnel: "\f38b";
81
+@ionicon-var-android-globe: "\f38c";
82
+@ionicon-var-android-hand: "\f2e3";
83
+@ionicon-var-android-hangout: "\f38d";
84
+@ionicon-var-android-happy: "\f38e";
85
+@ionicon-var-android-home: "\f38f";
86
+@ionicon-var-android-image: "\f2e4";
87
+@ionicon-var-android-laptop: "\f390";
88
+@ionicon-var-android-list: "\f391";
89
+@ionicon-var-android-locate: "\f2e9";
90
+@ionicon-var-android-lock: "\f392";
91
+@ionicon-var-android-mail: "\f2eb";
92
+@ionicon-var-android-map: "\f393";
93
+@ionicon-var-android-menu: "\f394";
94
+@ionicon-var-android-microphone: "\f2ec";
95
+@ionicon-var-android-microphone-off: "\f395";
96
+@ionicon-var-android-more-horizontal: "\f396";
97
+@ionicon-var-android-more-vertical: "\f397";
98
+@ionicon-var-android-navigate: "\f398";
99
+@ionicon-var-android-notifications: "\f39b";
100
+@ionicon-var-android-notifications-none: "\f399";
101
+@ionicon-var-android-notifications-off: "\f39a";
102
+@ionicon-var-android-open: "\f39c";
103
+@ionicon-var-android-options: "\f39d";
104
+@ionicon-var-android-people: "\f39e";
105
+@ionicon-var-android-person: "\f3a0";
106
+@ionicon-var-android-person-add: "\f39f";
107
+@ionicon-var-android-phone-landscape: "\f3a1";
108
+@ionicon-var-android-phone-portrait: "\f3a2";
109
+@ionicon-var-android-pin: "\f3a3";
110
+@ionicon-var-android-plane: "\f3a4";
111
+@ionicon-var-android-playstore: "\f2f0";
112
+@ionicon-var-android-print: "\f3a5";
113
+@ionicon-var-android-radio-button-off: "\f3a6";
114
+@ionicon-var-android-radio-button-on: "\f3a7";
115
+@ionicon-var-android-refresh: "\f3a8";
116
+@ionicon-var-android-remove: "\f2f4";
117
+@ionicon-var-android-remove-circle: "\f3a9";
118
+@ionicon-var-android-restaurant: "\f3aa";
119
+@ionicon-var-android-sad: "\f3ab";
120
+@ionicon-var-android-search: "\f2f5";
121
+@ionicon-var-android-send: "\f2f6";
122
+@ionicon-var-android-settings: "\f2f7";
123
+@ionicon-var-android-share: "\f2f8";
124
+@ionicon-var-android-share-alt: "\f3ac";
125
+@ionicon-var-android-star: "\f2fc";
126
+@ionicon-var-android-star-half: "\f3ad";
127
+@ionicon-var-android-star-outline: "\f3ae";
128
+@ionicon-var-android-stopwatch: "\f2fd";
129
+@ionicon-var-android-subway: "\f3af";
130
+@ionicon-var-android-sunny: "\f3b0";
131
+@ionicon-var-android-sync: "\f3b1";
132
+@ionicon-var-android-textsms: "\f3b2";
133
+@ionicon-var-android-time: "\f3b3";
134
+@ionicon-var-android-train: "\f3b4";
135
+@ionicon-var-android-unlock: "\f3b5";
136
+@ionicon-var-android-upload: "\f3b6";
137
+@ionicon-var-android-volume-down: "\f3b7";
138
+@ionicon-var-android-volume-mute: "\f3b8";
139
+@ionicon-var-android-volume-off: "\f3b9";
140
+@ionicon-var-android-volume-up: "\f3ba";
141
+@ionicon-var-android-walk: "\f3bb";
142
+@ionicon-var-android-warning: "\f3bc";
143
+@ionicon-var-android-watch: "\f3bd";
144
+@ionicon-var-android-wifi: "\f305";
145
+@ionicon-var-aperture: "\f313";
146
+@ionicon-var-archive: "\f102";
147
+@ionicon-var-arrow-down-a: "\f103";
148
+@ionicon-var-arrow-down-b: "\f104";
149
+@ionicon-var-arrow-down-c: "\f105";
150
+@ionicon-var-arrow-expand: "\f25e";
151
+@ionicon-var-arrow-graph-down-left: "\f25f";
152
+@ionicon-var-arrow-graph-down-right: "\f260";
153
+@ionicon-var-arrow-graph-up-left: "\f261";
154
+@ionicon-var-arrow-graph-up-right: "\f262";
155
+@ionicon-var-arrow-left-a: "\f106";
156
+@ionicon-var-arrow-left-b: "\f107";
157
+@ionicon-var-arrow-left-c: "\f108";
158
+@ionicon-var-arrow-move: "\f263";
159
+@ionicon-var-arrow-resize: "\f264";
160
+@ionicon-var-arrow-return-left: "\f265";
161
+@ionicon-var-arrow-return-right: "\f266";
162
+@ionicon-var-arrow-right-a: "\f109";
163
+@ionicon-var-arrow-right-b: "\f10a";
164
+@ionicon-var-arrow-right-c: "\f10b";
165
+@ionicon-var-arrow-shrink: "\f267";
166
+@ionicon-var-arrow-swap: "\f268";
167
+@ionicon-var-arrow-up-a: "\f10c";
168
+@ionicon-var-arrow-up-b: "\f10d";
169
+@ionicon-var-arrow-up-c: "\f10e";
170
+@ionicon-var-asterisk: "\f314";
171
+@ionicon-var-at: "\f10f";
172
+@ionicon-var-backspace: "\f3bf";
173
+@ionicon-var-backspace-outline: "\f3be";
174
+@ionicon-var-bag: "\f110";
175
+@ionicon-var-battery-charging: "\f111";
176
+@ionicon-var-battery-empty: "\f112";
177
+@ionicon-var-battery-full: "\f113";
178
+@ionicon-var-battery-half: "\f114";
179
+@ionicon-var-battery-low: "\f115";
180
+@ionicon-var-beaker: "\f269";
181
+@ionicon-var-beer: "\f26a";
182
+@ionicon-var-bluetooth: "\f116";
183
+@ionicon-var-bonfire: "\f315";
184
+@ionicon-var-bookmark: "\f26b";
185
+@ionicon-var-bowtie: "\f3c0";
186
+@ionicon-var-briefcase: "\f26c";
187
+@ionicon-var-bug: "\f2be";
188
+@ionicon-var-calculator: "\f26d";
189
+@ionicon-var-calendar: "\f117";
190
+@ionicon-var-camera: "\f118";
191
+@ionicon-var-card: "\f119";
192
+@ionicon-var-cash: "\f316";
193
+@ionicon-var-chatbox: "\f11b";
194
+@ionicon-var-chatbox-working: "\f11a";
195
+@ionicon-var-chatboxes: "\f11c";
196
+@ionicon-var-chatbubble: "\f11e";
197
+@ionicon-var-chatbubble-working: "\f11d";
198
+@ionicon-var-chatbubbles: "\f11f";
199
+@ionicon-var-checkmark: "\f122";
200
+@ionicon-var-checkmark-circled: "\f120";
201
+@ionicon-var-checkmark-round: "\f121";
202
+@ionicon-var-chevron-down: "\f123";
203
+@ionicon-var-chevron-left: "\f124";
204
+@ionicon-var-chevron-right: "\f125";
205
+@ionicon-var-chevron-up: "\f126";
206
+@ionicon-var-clipboard: "\f127";
207
+@ionicon-var-clock: "\f26e";
208
+@ionicon-var-close: "\f12a";
209
+@ionicon-var-close-circled: "\f128";
210
+@ionicon-var-close-round: "\f129";
211
+@ionicon-var-closed-captioning: "\f317";
212
+@ionicon-var-cloud: "\f12b";
213
+@ionicon-var-code: "\f271";
214
+@ionicon-var-code-download: "\f26f";
215
+@ionicon-var-code-working: "\f270";
216
+@ionicon-var-coffee: "\f272";
217
+@ionicon-var-compass: "\f273";
218
+@ionicon-var-compose: "\f12c";
219
+@ionicon-var-connection-bars: "\f274";
220
+@ionicon-var-contrast: "\f275";
221
+@ionicon-var-crop: "\f3c1";
222
+@ionicon-var-cube: "\f318";
223
+@ionicon-var-disc: "\f12d";
224
+@ionicon-var-document: "\f12f";
225
+@ionicon-var-document-text: "\f12e";
226
+@ionicon-var-drag: "\f130";
227
+@ionicon-var-earth: "\f276";
228
+@ionicon-var-easel: "\f3c2";
229
+@ionicon-var-edit: "\f2bf";
230
+@ionicon-var-egg: "\f277";
231
+@ionicon-var-eject: "\f131";
232
+@ionicon-var-email: "\f132";
233
+@ionicon-var-email-unread: "\f3c3";
234
+@ionicon-var-erlenmeyer-flask: "\f3c5";
235
+@ionicon-var-erlenmeyer-flask-bubbles: "\f3c4";
236
+@ionicon-var-eye: "\f133";
237
+@ionicon-var-eye-disabled: "\f306";
238
+@ionicon-var-female: "\f278";
239
+@ionicon-var-filing: "\f134";
240
+@ionicon-var-film-marker: "\f135";
241
+@ionicon-var-fireball: "\f319";
242
+@ionicon-var-flag: "\f279";
243
+@ionicon-var-flame: "\f31a";
244
+@ionicon-var-flash: "\f137";
245
+@ionicon-var-flash-off: "\f136";
246
+@ionicon-var-folder: "\f139";
247
+@ionicon-var-fork: "\f27a";
248
+@ionicon-var-fork-repo: "\f2c0";
249
+@ionicon-var-forward: "\f13a";
250
+@ionicon-var-funnel: "\f31b";
251
+@ionicon-var-gear-a: "\f13d";
252
+@ionicon-var-gear-b: "\f13e";
253
+@ionicon-var-grid: "\f13f";
254
+@ionicon-var-hammer: "\f27b";
255
+@ionicon-var-happy: "\f31c";
256
+@ionicon-var-happy-outline: "\f3c6";
257
+@ionicon-var-headphone: "\f140";
258
+@ionicon-var-heart: "\f141";
259
+@ionicon-var-heart-broken: "\f31d";
260
+@ionicon-var-help: "\f143";
261
+@ionicon-var-help-buoy: "\f27c";
262
+@ionicon-var-help-circled: "\f142";
263
+@ionicon-var-home: "\f144";
264
+@ionicon-var-icecream: "\f27d";
265
+@ionicon-var-image: "\f147";
266
+@ionicon-var-images: "\f148";
267
+@ionicon-var-information: "\f14a";
268
+@ionicon-var-information-circled: "\f149";
269
+@ionicon-var-ionic: "\f14b";
270
+@ionicon-var-ios-alarm: "\f3c8";
271
+@ionicon-var-ios-alarm-outline: "\f3c7";
272
+@ionicon-var-ios-albums: "\f3ca";
273
+@ionicon-var-ios-albums-outline: "\f3c9";
274
+@ionicon-var-ios-americanfootball: "\f3cc";
275
+@ionicon-var-ios-americanfootball-outline: "\f3cb";
276
+@ionicon-var-ios-analytics: "\f3ce";
277
+@ionicon-var-ios-analytics-outline: "\f3cd";
278
+@ionicon-var-ios-arrow-back: "\f3cf";
279
+@ionicon-var-ios-arrow-down: "\f3d0";
280
+@ionicon-var-ios-arrow-forward: "\f3d1";
281
+@ionicon-var-ios-arrow-left: "\f3d2";
282
+@ionicon-var-ios-arrow-right: "\f3d3";
283
+@ionicon-var-ios-arrow-thin-down: "\f3d4";
284
+@ionicon-var-ios-arrow-thin-left: "\f3d5";
285
+@ionicon-var-ios-arrow-thin-right: "\f3d6";
286
+@ionicon-var-ios-arrow-thin-up: "\f3d7";
287
+@ionicon-var-ios-arrow-up: "\f3d8";
288
+@ionicon-var-ios-at: "\f3da";
289
+@ionicon-var-ios-at-outline: "\f3d9";
290
+@ionicon-var-ios-barcode: "\f3dc";
291
+@ionicon-var-ios-barcode-outline: "\f3db";
292
+@ionicon-var-ios-baseball: "\f3de";
293
+@ionicon-var-ios-baseball-outline: "\f3dd";
294
+@ionicon-var-ios-basketball: "\f3e0";
295
+@ionicon-var-ios-basketball-outline: "\f3df";
296
+@ionicon-var-ios-bell: "\f3e2";
297
+@ionicon-var-ios-bell-outline: "\f3e1";
298
+@ionicon-var-ios-body: "\f3e4";
299
+@ionicon-var-ios-body-outline: "\f3e3";
300
+@ionicon-var-ios-bolt: "\f3e6";
301
+@ionicon-var-ios-bolt-outline: "\f3e5";
302
+@ionicon-var-ios-book: "\f3e8";
303
+@ionicon-var-ios-book-outline: "\f3e7";
304
+@ionicon-var-ios-bookmarks: "\f3ea";
305
+@ionicon-var-ios-bookmarks-outline: "\f3e9";
306
+@ionicon-var-ios-box: "\f3ec";
307
+@ionicon-var-ios-box-outline: "\f3eb";
308
+@ionicon-var-ios-briefcase: "\f3ee";
309
+@ionicon-var-ios-briefcase-outline: "\f3ed";
310
+@ionicon-var-ios-browsers: "\f3f0";
311
+@ionicon-var-ios-browsers-outline: "\f3ef";
312
+@ionicon-var-ios-calculator: "\f3f2";
313
+@ionicon-var-ios-calculator-outline: "\f3f1";
314
+@ionicon-var-ios-calendar: "\f3f4";
315
+@ionicon-var-ios-calendar-outline: "\f3f3";
316
+@ionicon-var-ios-camera: "\f3f6";
317
+@ionicon-var-ios-camera-outline: "\f3f5";
318
+@ionicon-var-ios-cart: "\f3f8";
319
+@ionicon-var-ios-cart-outline: "\f3f7";
320
+@ionicon-var-ios-chatboxes: "\f3fa";
321
+@ionicon-var-ios-chatboxes-outline: "\f3f9";
322
+@ionicon-var-ios-chatbubble: "\f3fc";
323
+@ionicon-var-ios-chatbubble-outline: "\f3fb";
324
+@ionicon-var-ios-checkmark: "\f3ff";
325
+@ionicon-var-ios-checkmark-empty: "\f3fd";
326
+@ionicon-var-ios-checkmark-outline: "\f3fe";
327
+@ionicon-var-ios-circle-filled: "\f400";
328
+@ionicon-var-ios-circle-outline: "\f401";
329
+@ionicon-var-ios-clock: "\f403";
330
+@ionicon-var-ios-clock-outline: "\f402";
331
+@ionicon-var-ios-close: "\f406";
332
+@ionicon-var-ios-close-empty: "\f404";
333
+@ionicon-var-ios-close-outline: "\f405";
334
+@ionicon-var-ios-cloud: "\f40c";
335
+@ionicon-var-ios-cloud-download: "\f408";
336
+@ionicon-var-ios-cloud-download-outline: "\f407";
337
+@ionicon-var-ios-cloud-outline: "\f409";
338
+@ionicon-var-ios-cloud-upload: "\f40b";
339
+@ionicon-var-ios-cloud-upload-outline: "\f40a";
340
+@ionicon-var-ios-cloudy: "\f410";
341
+@ionicon-var-ios-cloudy-night: "\f40e";
342
+@ionicon-var-ios-cloudy-night-outline: "\f40d";
343
+@ionicon-var-ios-cloudy-outline: "\f40f";
344
+@ionicon-var-ios-cog: "\f412";
345
+@ionicon-var-ios-cog-outline: "\f411";
346
+@ionicon-var-ios-color-filter: "\f414";
347
+@ionicon-var-ios-color-filter-outline: "\f413";
348
+@ionicon-var-ios-color-wand: "\f416";
349
+@ionicon-var-ios-color-wand-outline: "\f415";
350
+@ionicon-var-ios-compose: "\f418";
351
+@ionicon-var-ios-compose-outline: "\f417";
352
+@ionicon-var-ios-contact: "\f41a";
353
+@ionicon-var-ios-contact-outline: "\f419";
354
+@ionicon-var-ios-copy: "\f41c";
355
+@ionicon-var-ios-copy-outline: "\f41b";
356
+@ionicon-var-ios-crop: "\f41e";
357
+@ionicon-var-ios-crop-strong: "\f41d";
358
+@ionicon-var-ios-download: "\f420";
359
+@ionicon-var-ios-download-outline: "\f41f";
360
+@ionicon-var-ios-drag: "\f421";
361
+@ionicon-var-ios-email: "\f423";
362
+@ionicon-var-ios-email-outline: "\f422";
363
+@ionicon-var-ios-eye: "\f425";
364
+@ionicon-var-ios-eye-outline: "\f424";
365
+@ionicon-var-ios-fastforward: "\f427";
366
+@ionicon-var-ios-fastforward-outline: "\f426";
367
+@ionicon-var-ios-filing: "\f429";
368
+@ionicon-var-ios-filing-outline: "\f428";
369
+@ionicon-var-ios-film: "\f42b";
370
+@ionicon-var-ios-film-outline: "\f42a";
371
+@ionicon-var-ios-flag: "\f42d";
372
+@ionicon-var-ios-flag-outline: "\f42c";
373
+@ionicon-var-ios-flame: "\f42f";
374
+@ionicon-var-ios-flame-outline: "\f42e";
375
+@ionicon-var-ios-flask: "\f431";
376
+@ionicon-var-ios-flask-outline: "\f430";
377
+@ionicon-var-ios-flower: "\f433";
378
+@ionicon-var-ios-flower-outline: "\f432";
379
+@ionicon-var-ios-folder: "\f435";
380
+@ionicon-var-ios-folder-outline: "\f434";
381
+@ionicon-var-ios-football: "\f437";
382
+@ionicon-var-ios-football-outline: "\f436";
383
+@ionicon-var-ios-game-controller-a: "\f439";
384
+@ionicon-var-ios-game-controller-a-outline: "\f438";
385
+@ionicon-var-ios-game-controller-b: "\f43b";
386
+@ionicon-var-ios-game-controller-b-outline: "\f43a";
387
+@ionicon-var-ios-gear: "\f43d";
388
+@ionicon-var-ios-gear-outline: "\f43c";
389
+@ionicon-var-ios-glasses: "\f43f";
390
+@ionicon-var-ios-glasses-outline: "\f43e";
391
+@ionicon-var-ios-grid-view: "\f441";
392
+@ionicon-var-ios-grid-view-outline: "\f440";
393
+@ionicon-var-ios-heart: "\f443";
394
+@ionicon-var-ios-heart-outline: "\f442";
395
+@ionicon-var-ios-help: "\f446";
396
+@ionicon-var-ios-help-empty: "\f444";
397
+@ionicon-var-ios-help-outline: "\f445";
398
+@ionicon-var-ios-home: "\f448";
399
+@ionicon-var-ios-home-outline: "\f447";
400
+@ionicon-var-ios-infinite: "\f44a";
401
+@ionicon-var-ios-infinite-outline: "\f449";
402
+@ionicon-var-ios-information: "\f44d";
403
+@ionicon-var-ios-information-empty: "\f44b";
404
+@ionicon-var-ios-information-outline: "\f44c";
405
+@ionicon-var-ios-ionic-outline: "\f44e";
406
+@ionicon-var-ios-keypad: "\f450";
407
+@ionicon-var-ios-keypad-outline: "\f44f";
408
+@ionicon-var-ios-lightbulb: "\f452";
409
+@ionicon-var-ios-lightbulb-outline: "\f451";
410
+@ionicon-var-ios-list: "\f454";
411
+@ionicon-var-ios-list-outline: "\f453";
412
+@ionicon-var-ios-location: "\f456";
413
+@ionicon-var-ios-location-outline: "\f455";
414
+@ionicon-var-ios-locked: "\f458";
415
+@ionicon-var-ios-locked-outline: "\f457";
416
+@ionicon-var-ios-loop: "\f45a";
417
+@ionicon-var-ios-loop-strong: "\f459";
418
+@ionicon-var-ios-medical: "\f45c";
419
+@ionicon-var-ios-medical-outline: "\f45b";
420
+@ionicon-var-ios-medkit: "\f45e";
421
+@ionicon-var-ios-medkit-outline: "\f45d";
422
+@ionicon-var-ios-mic: "\f461";
423
+@ionicon-var-ios-mic-off: "\f45f";
424
+@ionicon-var-ios-mic-outline: "\f460";
425
+@ionicon-var-ios-minus: "\f464";
426
+@ionicon-var-ios-minus-empty: "\f462";
427
+@ionicon-var-ios-minus-outline: "\f463";
428
+@ionicon-var-ios-monitor: "\f466";
429
+@ionicon-var-ios-monitor-outline: "\f465";
430
+@ionicon-var-ios-moon: "\f468";
431
+@ionicon-var-ios-moon-outline: "\f467";
432
+@ionicon-var-ios-more: "\f46a";
433
+@ionicon-var-ios-more-outline: "\f469";
434
+@ionicon-var-ios-musical-note: "\f46b";
435
+@ionicon-var-ios-musical-notes: "\f46c";
436
+@ionicon-var-ios-navigate: "\f46e";
437
+@ionicon-var-ios-navigate-outline: "\f46d";
438
+@ionicon-var-ios-nutrition: "\f470";
439
+@ionicon-var-ios-nutrition-outline: "\f46f";
440
+@ionicon-var-ios-paper: "\f472";
441
+@ionicon-var-ios-paper-outline: "\f471";
442
+@ionicon-var-ios-paperplane: "\f474";
443
+@ionicon-var-ios-paperplane-outline: "\f473";
444
+@ionicon-var-ios-partlysunny: "\f476";
445
+@ionicon-var-ios-partlysunny-outline: "\f475";
446
+@ionicon-var-ios-pause: "\f478";
447
+@ionicon-var-ios-pause-outline: "\f477";
448
+@ionicon-var-ios-paw: "\f47a";
449
+@ionicon-var-ios-paw-outline: "\f479";
450
+@ionicon-var-ios-people: "\f47c";
451
+@ionicon-var-ios-people-outline: "\f47b";
452
+@ionicon-var-ios-person: "\f47e";
453
+@ionicon-var-ios-person-outline: "\f47d";
454
+@ionicon-var-ios-personadd: "\f480";
455
+@ionicon-var-ios-personadd-outline: "\f47f";
456
+@ionicon-var-ios-photos: "\f482";
457
+@ionicon-var-ios-photos-outline: "\f481";
458
+@ionicon-var-ios-pie: "\f484";
459
+@ionicon-var-ios-pie-outline: "\f483";
460
+@ionicon-var-ios-pint: "\f486";
461
+@ionicon-var-ios-pint-outline: "\f485";
462
+@ionicon-var-ios-play: "\f488";
463
+@ionicon-var-ios-play-outline: "\f487";
464
+@ionicon-var-ios-plus: "\f48b";
465
+@ionicon-var-ios-plus-empty: "\f489";
466
+@ionicon-var-ios-plus-outline: "\f48a";
467
+@ionicon-var-ios-pricetag: "\f48d";
468
+@ionicon-var-ios-pricetag-outline: "\f48c";
469
+@ionicon-var-ios-pricetags: "\f48f";
470
+@ionicon-var-ios-pricetags-outline: "\f48e";
471
+@ionicon-var-ios-printer: "\f491";
472
+@ionicon-var-ios-printer-outline: "\f490";
473
+@ionicon-var-ios-pulse: "\f493";
474
+@ionicon-var-ios-pulse-strong: "\f492";
475
+@ionicon-var-ios-rainy: "\f495";
476
+@ionicon-var-ios-rainy-outline: "\f494";
477
+@ionicon-var-ios-recording: "\f497";
478
+@ionicon-var-ios-recording-outline: "\f496";
479
+@ionicon-var-ios-redo: "\f499";
480
+@ionicon-var-ios-redo-outline: "\f498";
481
+@ionicon-var-ios-refresh: "\f49c";
482
+@ionicon-var-ios-refresh-empty: "\f49a";
483
+@ionicon-var-ios-refresh-outline: "\f49b";
484
+@ionicon-var-ios-reload: "\f49d";
485
+@ionicon-var-ios-reverse-camera: "\f49f";
486
+@ionicon-var-ios-reverse-camera-outline: "\f49e";
487
+@ionicon-var-ios-rewind: "\f4a1";
488
+@ionicon-var-ios-rewind-outline: "\f4a0";
489
+@ionicon-var-ios-rose: "\f4a3";
490
+@ionicon-var-ios-rose-outline: "\f4a2";
491
+@ionicon-var-ios-search: "\f4a5";
492
+@ionicon-var-ios-search-strong: "\f4a4";
493
+@ionicon-var-ios-settings: "\f4a7";
494
+@ionicon-var-ios-settings-strong: "\f4a6";
495
+@ionicon-var-ios-shuffle: "\f4a9";
496
+@ionicon-var-ios-shuffle-strong: "\f4a8";
497
+@ionicon-var-ios-skipbackward: "\f4ab";
498
+@ionicon-var-ios-skipbackward-outline: "\f4aa";
499
+@ionicon-var-ios-skipforward: "\f4ad";
500
+@ionicon-var-ios-skipforward-outline: "\f4ac";
501
+@ionicon-var-ios-snowy: "\f4ae";
502
+@ionicon-var-ios-speedometer: "\f4b0";
503
+@ionicon-var-ios-speedometer-outline: "\f4af";
504
+@ionicon-var-ios-star: "\f4b3";
505
+@ionicon-var-ios-star-half: "\f4b1";
506
+@ionicon-var-ios-star-outline: "\f4b2";
507
+@ionicon-var-ios-stopwatch: "\f4b5";
508
+@ionicon-var-ios-stopwatch-outline: "\f4b4";
509
+@ionicon-var-ios-sunny: "\f4b7";
510
+@ionicon-var-ios-sunny-outline: "\f4b6";
511
+@ionicon-var-ios-telephone: "\f4b9";
512
+@ionicon-var-ios-telephone-outline: "\f4b8";
513
+@ionicon-var-ios-tennisball: "\f4bb";
514
+@ionicon-var-ios-tennisball-outline: "\f4ba";
515
+@ionicon-var-ios-thunderstorm: "\f4bd";
516
+@ionicon-var-ios-thunderstorm-outline: "\f4bc";
517
+@ionicon-var-ios-time: "\f4bf";
518
+@ionicon-var-ios-time-outline: "\f4be";
519
+@ionicon-var-ios-timer: "\f4c1";
520
+@ionicon-var-ios-timer-outline: "\f4c0";
521
+@ionicon-var-ios-toggle: "\f4c3";
522
+@ionicon-var-ios-toggle-outline: "\f4c2";
523
+@ionicon-var-ios-trash: "\f4c5";
524
+@ionicon-var-ios-trash-outline: "\f4c4";
525
+@ionicon-var-ios-undo: "\f4c7";
526
+@ionicon-var-ios-undo-outline: "\f4c6";
527
+@ionicon-var-ios-unlocked: "\f4c9";
528
+@ionicon-var-ios-unlocked-outline: "\f4c8";
529
+@ionicon-var-ios-upload: "\f4cb";
530
+@ionicon-var-ios-upload-outline: "\f4ca";
531
+@ionicon-var-ios-videocam: "\f4cd";
532
+@ionicon-var-ios-videocam-outline: "\f4cc";
533
+@ionicon-var-ios-volume-high: "\f4ce";
534
+@ionicon-var-ios-volume-low: "\f4cf";
535
+@ionicon-var-ios-wineglass: "\f4d1";
536
+@ionicon-var-ios-wineglass-outline: "\f4d0";
537
+@ionicon-var-ios-world: "\f4d3";
538
+@ionicon-var-ios-world-outline: "\f4d2";
539
+@ionicon-var-ipad: "\f1f9";
540
+@ionicon-var-iphone: "\f1fa";
541
+@ionicon-var-ipod: "\f1fb";
542
+@ionicon-var-jet: "\f295";
543
+@ionicon-var-key: "\f296";
544
+@ionicon-var-knife: "\f297";
545
+@ionicon-var-laptop: "\f1fc";
546
+@ionicon-var-leaf: "\f1fd";
547
+@ionicon-var-levels: "\f298";
548
+@ionicon-var-lightbulb: "\f299";
549
+@ionicon-var-link: "\f1fe";
550
+@ionicon-var-load-a: "\f29a";
551
+@ionicon-var-load-b: "\f29b";
552
+@ionicon-var-load-c: "\f29c";
553
+@ionicon-var-load-d: "\f29d";
554
+@ionicon-var-location: "\f1ff";
555
+@ionicon-var-lock-combination: "\f4d4";
556
+@ionicon-var-locked: "\f200";
557
+@ionicon-var-log-in: "\f29e";
558
+@ionicon-var-log-out: "\f29f";
559
+@ionicon-var-loop: "\f201";
560
+@ionicon-var-magnet: "\f2a0";
561
+@ionicon-var-male: "\f2a1";
562
+@ionicon-var-man: "\f202";
563
+@ionicon-var-map: "\f203";
564
+@ionicon-var-medkit: "\f2a2";
565
+@ionicon-var-merge: "\f33f";
566
+@ionicon-var-mic-a: "\f204";
567
+@ionicon-var-mic-b: "\f205";
568
+@ionicon-var-mic-c: "\f206";
569
+@ionicon-var-minus: "\f209";
570
+@ionicon-var-minus-circled: "\f207";
571
+@ionicon-var-minus-round: "\f208";
572
+@ionicon-var-model-s: "\f2c1";
573
+@ionicon-var-monitor: "\f20a";
574
+@ionicon-var-more: "\f20b";
575
+@ionicon-var-mouse: "\f340";
576
+@ionicon-var-music-note: "\f20c";
577
+@ionicon-var-navicon: "\f20e";
578
+@ionicon-var-navicon-round: "\f20d";
579
+@ionicon-var-navigate: "\f2a3";
580
+@ionicon-var-network: "\f341";
581
+@ionicon-var-no-smoking: "\f2c2";
582
+@ionicon-var-nuclear: "\f2a4";
583
+@ionicon-var-outlet: "\f342";
584
+@ionicon-var-paintbrush: "\f4d5";
585
+@ionicon-var-paintbucket: "\f4d6";
586
+@ionicon-var-paper-airplane: "\f2c3";
587
+@ionicon-var-paperclip: "\f20f";
588
+@ionicon-var-pause: "\f210";
589
+@ionicon-var-person: "\f213";
590
+@ionicon-var-person-add: "\f211";
591
+@ionicon-var-person-stalker: "\f212";
592
+@ionicon-var-pie-graph: "\f2a5";
593
+@ionicon-var-pin: "\f2a6";
594
+@ionicon-var-pinpoint: "\f2a7";
595
+@ionicon-var-pizza: "\f2a8";
596
+@ionicon-var-plane: "\f214";
597
+@ionicon-var-planet: "\f343";
598
+@ionicon-var-play: "\f215";
599
+@ionicon-var-playstation: "\f30a";
600
+@ionicon-var-plus: "\f218";
601
+@ionicon-var-plus-circled: "\f216";
602
+@ionicon-var-plus-round: "\f217";
603
+@ionicon-var-podium: "\f344";
604
+@ionicon-var-pound: "\f219";
605
+@ionicon-var-power: "\f2a9";
606
+@ionicon-var-pricetag: "\f2aa";
607
+@ionicon-var-pricetags: "\f2ab";
608
+@ionicon-var-printer: "\f21a";
609
+@ionicon-var-pull-request: "\f345";
610
+@ionicon-var-qr-scanner: "\f346";
611
+@ionicon-var-quote: "\f347";
612
+@ionicon-var-radio-waves: "\f2ac";
613
+@ionicon-var-record: "\f21b";
614
+@ionicon-var-refresh: "\f21c";
615
+@ionicon-var-reply: "\f21e";
616
+@ionicon-var-reply-all: "\f21d";
617
+@ionicon-var-ribbon-a: "\f348";
618
+@ionicon-var-ribbon-b: "\f349";
619
+@ionicon-var-sad: "\f34a";
620
+@ionicon-var-sad-outline: "\f4d7";
621
+@ionicon-var-scissors: "\f34b";
622
+@ionicon-var-search: "\f21f";
623
+@ionicon-var-settings: "\f2ad";
624
+@ionicon-var-share: "\f220";
625
+@ionicon-var-shuffle: "\f221";
626
+@ionicon-var-skip-backward: "\f222";
627
+@ionicon-var-skip-forward: "\f223";
628
+@ionicon-var-social-android: "\f225";
629
+@ionicon-var-social-android-outline: "\f224";
630
+@ionicon-var-social-angular: "\f4d9";
631
+@ionicon-var-social-angular-outline: "\f4d8";
632
+@ionicon-var-social-apple: "\f227";
633
+@ionicon-var-social-apple-outline: "\f226";
634
+@ionicon-var-social-bitcoin: "\f2af";
635
+@ionicon-var-social-bitcoin-outline: "\f2ae";
636
+@ionicon-var-social-buffer: "\f229";
637
+@ionicon-var-social-buffer-outline: "\f228";
638
+@ionicon-var-social-chrome: "\f4db";
639
+@ionicon-var-social-chrome-outline: "\f4da";
640
+@ionicon-var-social-codepen: "\f4dd";
641
+@ionicon-var-social-codepen-outline: "\f4dc";
642
+@ionicon-var-social-css3: "\f4df";
643
+@ionicon-var-social-css3-outline: "\f4de";
644
+@ionicon-var-social-designernews: "\f22b";
645
+@ionicon-var-social-designernews-outline: "\f22a";
646
+@ionicon-var-social-dribbble: "\f22d";
647
+@ionicon-var-social-dribbble-outline: "\f22c";
648
+@ionicon-var-social-dropbox: "\f22f";
649
+@ionicon-var-social-dropbox-outline: "\f22e";
650
+@ionicon-var-social-euro: "\f4e1";
651
+@ionicon-var-social-euro-outline: "\f4e0";
652
+@ionicon-var-social-facebook: "\f231";
653
+@ionicon-var-social-facebook-outline: "\f230";
654
+@ionicon-var-social-foursquare: "\f34d";
655
+@ionicon-var-social-foursquare-outline: "\f34c";
656
+@ionicon-var-social-freebsd-devil: "\f2c4";
657
+@ionicon-var-social-github: "\f233";
658
+@ionicon-var-social-github-outline: "\f232";
659
+@ionicon-var-social-google: "\f34f";
660
+@ionicon-var-social-google-outline: "\f34e";
661
+@ionicon-var-social-googleplus: "\f235";
662
+@ionicon-var-social-googleplus-outline: "\f234";
663
+@ionicon-var-social-hackernews: "\f237";
664
+@ionicon-var-social-hackernews-outline: "\f236";
665
+@ionicon-var-social-html5: "\f4e3";
666
+@ionicon-var-social-html5-outline: "\f4e2";
667
+@ionicon-var-social-instagram: "\f351";
668
+@ionicon-var-social-instagram-outline: "\f350";
669
+@ionicon-var-social-javascript: "\f4e5";
670
+@ionicon-var-social-javascript-outline: "\f4e4";
671
+@ionicon-var-social-linkedin: "\f239";
672
+@ionicon-var-social-linkedin-outline: "\f238";
673
+@ionicon-var-social-markdown: "\f4e6";
674
+@ionicon-var-social-nodejs: "\f4e7";
675
+@ionicon-var-social-octocat: "\f4e8";
676
+@ionicon-var-social-pinterest: "\f2b1";
677
+@ionicon-var-social-pinterest-outline: "\f2b0";
678
+@ionicon-var-social-python: "\f4e9";
679
+@ionicon-var-social-reddit: "\f23b";
680
+@ionicon-var-social-reddit-outline: "\f23a";
681
+@ionicon-var-social-rss: "\f23d";
682
+@ionicon-var-social-rss-outline: "\f23c";
683
+@ionicon-var-social-sass: "\f4ea";
684
+@ionicon-var-social-skype: "\f23f";
685
+@ionicon-var-social-skype-outline: "\f23e";
686
+@ionicon-var-social-snapchat: "\f4ec";
687
+@ionicon-var-social-snapchat-outline: "\f4eb";
688
+@ionicon-var-social-tumblr: "\f241";
689
+@ionicon-var-social-tumblr-outline: "\f240";
690
+@ionicon-var-social-tux: "\f2c5";
691
+@ionicon-var-social-twitch: "\f4ee";
692
+@ionicon-var-social-twitch-outline: "\f4ed";
693
+@ionicon-var-social-twitter: "\f243";
694
+@ionicon-var-social-twitter-outline: "\f242";
695
+@ionicon-var-social-usd: "\f353";
696
+@ionicon-var-social-usd-outline: "\f352";
697
+@ionicon-var-social-vimeo: "\f245";
698
+@ionicon-var-social-vimeo-outline: "\f244";
699
+@ionicon-var-social-whatsapp: "\f4f0";
700
+@ionicon-var-social-whatsapp-outline: "\f4ef";
701
+@ionicon-var-social-windows: "\f247";
702
+@ionicon-var-social-windows-outline: "\f246";
703
+@ionicon-var-social-wordpress: "\f249";
704
+@ionicon-var-social-wordpress-outline: "\f248";
705
+@ionicon-var-social-yahoo: "\f24b";
706
+@ionicon-var-social-yahoo-outline: "\f24a";
707
+@ionicon-var-social-yen: "\f4f2";
708
+@ionicon-var-social-yen-outline: "\f4f1";
709
+@ionicon-var-social-youtube: "\f24d";
710
+@ionicon-var-social-youtube-outline: "\f24c";
711
+@ionicon-var-soup-can: "\f4f4";
712
+@ionicon-var-soup-can-outline: "\f4f3";
713
+@ionicon-var-speakerphone: "\f2b2";
714
+@ionicon-var-speedometer: "\f2b3";
715
+@ionicon-var-spoon: "\f2b4";
716
+@ionicon-var-star: "\f24e";
717
+@ionicon-var-stats-bars: "\f2b5";
718
+@ionicon-var-steam: "\f30b";
719
+@ionicon-var-stop: "\f24f";
720
+@ionicon-var-thermometer: "\f2b6";
721
+@ionicon-var-thumbsdown: "\f250";
722
+@ionicon-var-thumbsup: "\f251";
723
+@ionicon-var-toggle: "\f355";
724
+@ionicon-var-toggle-filled: "\f354";
725
+@ionicon-var-transgender: "\f4f5";
726
+@ionicon-var-trash-a: "\f252";
727
+@ionicon-var-trash-b: "\f253";
728
+@ionicon-var-trophy: "\f356";
729
+@ionicon-var-tshirt: "\f4f7";
730
+@ionicon-var-tshirt-outline: "\f4f6";
731
+@ionicon-var-umbrella: "\f2b7";
732
+@ionicon-var-university: "\f357";
733
+@ionicon-var-unlocked: "\f254";
734
+@ionicon-var-upload: "\f255";
735
+@ionicon-var-usb: "\f2b8";
736
+@ionicon-var-videocamera: "\f256";
737
+@ionicon-var-volume-high: "\f257";
738
+@ionicon-var-volume-low: "\f258";
739
+@ionicon-var-volume-medium: "\f259";
740
+@ionicon-var-volume-mute: "\f25a";
741
+@ionicon-var-wand: "\f358";
742
+@ionicon-var-waterdrop: "\f25b";
743
+@ionicon-var-wifi: "\f25c";
744
+@ionicon-var-wineglass: "\f2b9";
745
+@ionicon-var-woman: "\f25d";
746
+@ionicon-var-wrench: "\f2ba";
747
+@ionicon-var-xbox: "\f30c";

+ 3 - 0
www/lib/Ionicons/less/ionicons.less

@@ -0,0 +1,3 @@
1
+@import "_ionicons-variables";
2
+@import "_ionicons-font";
3
+@import "_ionicons-icons";

BIN
www/lib/Ionicons/png/512/alert-circled.png


+ 0 - 0
www/lib/Ionicons/png/512/alert.png


Some files were not shown because too many files changed in this diff

tum/whitesports - Gogs: Simplico Git Service

Geen omschrijving

provider.php 49KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. <?php
  2. use NSL\Notices;
  3. use NSL\Persistent\Persistent;
  4. require_once dirname(__FILE__) . '/provider-admin.php';
  5. require_once dirname(__FILE__) . '/provider-dummy.php';
  6. require_once dirname(__FILE__) . '/user.php';
  7. abstract class NextendSocialProvider extends NextendSocialProviderDummy {
  8. protected $dbID;
  9. protected $optionKey;
  10. protected $enabled = false;
  11. /** @var NextendSocialAuth */
  12. protected $client;
  13. protected $authUserData = array();
  14. protected $requiredFields = array();
  15. protected $svg = '';
  16. protected $sync_fields = array();
  17. /**
  18. * NextendSocialProvider constructor.
  19. *
  20. * @param $defaultSettings
  21. */
  22. public function __construct($defaultSettings) {
  23. if (empty($this->dbID)) {
  24. $this->dbID = $this->id;
  25. }
  26. $this->optionKey = 'nsl_' . $this->id;
  27. do_action('nsl_provider_init', $this);
  28. $this->sync_fields = apply_filters('nsl_' . $this->getId() . '_sync_fields', $this->sync_fields);
  29. $extraSettings = apply_filters('nsl_' . $this->getId() . '_extra_settings', array(
  30. 'ask_email' => 'when-empty',
  31. 'ask_user' => 'never',
  32. 'ask_password' => 'never',
  33. 'auto_link' => 'email',
  34. 'disabled_roles' => array(),
  35. 'register_roles' => array(
  36. 'default'
  37. )
  38. ));
  39. foreach ($this->getSyncFields() as $field_name => $fieldData) {
  40. $extraSettings['sync_fields/fields/' . $field_name . '/enabled'] = 0;
  41. $extraSettings['sync_fields/fields/' . $field_name . '/meta_key'] = $this->id . '_' . $field_name;
  42. }
  43. $this->settings = new NextendSocialLoginSettings($this->optionKey, array_merge(array(
  44. 'settings_saved' => '0',
  45. 'tested' => '0',
  46. 'custom_default_button' => '',
  47. 'custom_icon_button' => '',
  48. 'login_label' => '',
  49. 'register_label' => '',
  50. 'link_label' => '',
  51. 'unlink_label' => '',
  52. 'user_prefix' => '',
  53. 'user_fallback' => '',
  54. 'oauth_redirect_url' => '',
  55. 'terms' => '',
  56. 'sync_fields/link' => 0,
  57. 'sync_fields/login' => 0
  58. ), $extraSettings, $defaultSettings));
  59. $this->admin = new NextendSocialProviderAdmin($this);
  60. add_action('rest_api_init', array(
  61. $this,
  62. 'registerRedirectRESTRoute'
  63. ));
  64. }
  65. public function needPro() {
  66. return false;
  67. }
  68. /**
  69. * @return string
  70. */
  71. public function getDbID() {
  72. return $this->dbID;
  73. }
  74. public function getOptionKey() {
  75. return $this->optionKey;
  76. }
  77. public function getRawDefaultButton() {
  78. return '<div class="nsl-button nsl-button-default nsl-button-' . $this->id . '" style="background-color:' . $this->color . ';"><div class="nsl-button-svg-container">' . $this->svg . '</div><div class="nsl-button-label-container">{{label}}</div></div>';
  79. }
  80. public function getRawIconButton() {
  81. return '<div class="nsl-button nsl-button-icon nsl-button-' . $this->id . '" style="background-color:' . $this->color . ';"><div class="nsl-button-svg-container">' . $this->svg . '</div></div>';
  82. }
  83. public function getDefaultButton($label) {
  84. $button = $this->settings->get('custom_default_button');
  85. if (!empty($button)) {
  86. return str_replace('{{label}}', __($label, 'nextend-facebook-connect'), $button);
  87. }
  88. return str_replace('{{label}}', __($label, 'nextend-facebook-connect'), $this->getRawDefaultButton());
  89. }
  90. public function getIconButton() {
  91. $button = $this->settings->get('custom_icon_button');
  92. if (!empty($button)) {
  93. return $button;
  94. }
  95. return $this->getRawIconButton();
  96. }
  97. public function getLoginUrl() {
  98. $args = array('loginSocial' => $this->getId());
  99. if (isset($_REQUEST['interim-login'])) {
  100. $args['interim-login'] = 1;
  101. }
  102. return add_query_arg($args, NextendSocialLogin::getLoginUrl());
  103. }
  104. /**
  105. * Returns the url where the Provider App should redirect during the OAuth flow.
  106. *
  107. * @return string
  108. */
  109. public function getRedirectUriForOAuthFlow() {
  110. if ($this->oauthRedirectBehavior === 'rest_redirect') {
  111. return rest_url('/nextend-social-login/v1/' . $this->id . '/redirect_uri');
  112. }
  113. $args = array('loginSocial' => $this->id);
  114. return add_query_arg($args, NextendSocialLogin::getLoginUrl());
  115. }
  116. /**
  117. * Returns a single redirect URL that:
  118. * - we us as default redirect uri suggestion in the Getting Started and Fixed redirect uri pages.
  119. * - we store to detect the OAuth redirect url changes
  120. *
  121. * @return string
  122. */
  123. public function getBaseRedirectUriForAppCreation() {
  124. $redirectUri = $this->getRedirectUriForOAuthFlow();
  125. if ($this->oauthRedirectBehavior === 'default_redirect_but_app_has_restriction') {
  126. $parts = explode('?', $redirectUri);
  127. return $parts[0];
  128. }
  129. return $redirectUri;
  130. }
  131. /**
  132. * This function should return an array of URLs generated from getRedirectUri().
  133. *
  134. * We display the generated results in the Getting Started section and the Fixed redirect uri pages.
  135. * Also we use these for the OAuth redirect uri change checking.
  136. *
  137. * @return array
  138. */
  139. public function getAllRedirectUrisForAppCreation() {
  140. /**
  141. * Parameters:
  142. * 1: Array with an URL that should be added to the App by default.
  143. *
  144. * 2: The provider instance
  145. */
  146. return apply_filters('nsl_redirect_uri_override', array($this->getBaseRedirectUriForAppCreation()), $this);
  147. }
  148. /**
  149. * Enable the selected provider.
  150. *
  151. * @return bool
  152. */
  153. public function enable() {
  154. $this->enabled = true;
  155. do_action('nsl_' . $this->getId() . '_enabled');
  156. return true;
  157. }
  158. /**
  159. * Check if provider is enabled.
  160. *
  161. * @return bool
  162. */
  163. public function isEnabled() {
  164. return $this->enabled;
  165. }
  166. /**
  167. * Check if provider is verified.
  168. *
  169. * @return bool
  170. */
  171. public function isTested() {
  172. return !!$this->settings->get('tested');
  173. }
  174. /**
  175. * Check if the current redirect url of the provider matches with the one that we stored when the provider was
  176. * configured. Returns "false" if they are different, so a new URL needs to be added to the App.
  177. *
  178. * @return bool
  179. */
  180. public function checkOauthRedirectUrl() {
  181. $oauth_redirect_url = $this->settings->get('oauth_redirect_url');
  182. $redirectUrls = $this->getAllRedirectUrisForAppCreation();
  183. if (is_array($redirectUrls)) {
  184. /**
  185. * Before 3.1.2 we saved the default redirect url of the provider ( e.g.:
  186. * https://example.com/wp-login.php?loginSocial=twitter ) for the OAuth check. However, some providers ( e.g.
  187. * Microsoft ) can use the REST API URL as redirect url. In these cases if the URL of the OAuth page was changed,
  188. * we gave a false warning for such providers.
  189. *
  190. * We shouldn't throw warnings for users who have the redirect uri stored still with the old format.
  191. * For this reason we need to push the legacy redirect url into the $redirectUrls array, too!
  192. */
  193. $legacyRedirectURL = add_query_arg(array('loginSocial' => $this->getId()), NextendSocialLogin::getLoginUrl());
  194. if (!in_array($legacyRedirectURL, $redirectUrls)) {
  195. $redirectUrls[] = $legacyRedirectURL;
  196. }
  197. if (in_array($oauth_redirect_url, $redirectUrls)) {
  198. return true;
  199. }
  200. }
  201. return false;
  202. }
  203. public function updateOauthRedirectUrl() {
  204. $this->settings->update(array(
  205. 'oauth_redirect_url' => $this->getBaseRedirectUriForAppCreation()
  206. ));
  207. }
  208. /**
  209. * @return array
  210. */
  211. public function getRequiredFields() {
  212. return $this->requiredFields;
  213. }
  214. /**
  215. * Get the current state of a Provider.
  216. *
  217. * @return string
  218. */
  219. public function getState() {
  220. foreach ($this->requiredFields as $name => $label) {
  221. $value = $this->settings->get($name);
  222. if (empty($value)) {
  223. return 'not-configured';
  224. }
  225. }
  226. if (!$this->isTested()) {
  227. return 'not-tested';
  228. }
  229. if (!$this->isEnabled()) {
  230. return 'disabled';
  231. }
  232. return 'enabled';
  233. }
  234. /**
  235. * Authenticate and connect with the provider.
  236. */
  237. public function connect() {
  238. try {
  239. $this->doAuthenticate();
  240. } catch (NSLContinuePageRenderException $e) {
  241. // This is not an error. We allow the page to continue the normal display flow and later we inject our things.
  242. // Used by Theme my login function where we override the shortcode and we display our email request.
  243. } catch (Exception $e) {
  244. $this->onError($e);
  245. }
  246. }
  247. /**
  248. * @return NextendSocialAuth
  249. */
  250. protected abstract function getClient();
  251. public function getTestUrl() {
  252. return $this->getClient()
  253. ->getTestUrl();
  254. }
  255. /**
  256. * @throws NSLContinuePageRenderException
  257. */
  258. protected function doAuthenticate() {
  259. if (!headers_sent()) {
  260. //All In One WP Security sets a LOCATION header, so we need to remove it to do a successful test.
  261. if (function_exists('header_remove')) {
  262. header_remove("LOCATION");
  263. } else {
  264. header('LOCATION:', true); //Under PHP 5.3
  265. }
  266. }
  267. //If it is a real login action, add the actions for the connection.
  268. if (!$this->isTest()) {
  269. add_action($this->id . '_login_action_before', array(
  270. $this,
  271. 'liveConnectBefore'
  272. ));
  273. add_action($this->id . '_login_action_redirect', array(
  274. $this,
  275. 'liveConnectRedirect'
  276. ));
  277. add_action($this->id . '_login_action_get_user_profile', array(
  278. $this,
  279. 'liveConnectGetUserProfile'
  280. ));
  281. $interim_login = isset($_REQUEST['interim-login']);
  282. if ($interim_login) {
  283. Persistent::set($this->id . '_interim_login', 1);
  284. }
  285. /**
  286. * Store the settings for the provider login.
  287. */
  288. $display = isset($_REQUEST['display']);
  289. if ($display && $_REQUEST['display'] == 'popup') {
  290. Persistent::set($this->id . '_display', 'popup');
  291. }
  292. } else { //This is just to verify the settings.
  293. add_action($this->id . '_login_action_get_user_profile', array(
  294. $this,
  295. 'testConnectGetUserProfile'
  296. ));
  297. }
  298. // Redirect if the registration is blocked by another Plugin like Cerber.
  299. if (function_exists('cerber_is_allowed')) {
  300. $allowed = cerber_is_allowed();
  301. if (!$allowed) {
  302. global $wp_cerber;
  303. $error = $wp_cerber->getErrorMsg();
  304. Notices::addError($error);
  305. $this->redirectToLoginForm();
  306. }
  307. }
  308. do_action($this->id . '_login_action_before', $this);
  309. $client = $this->getClient();
  310. $accessTokenData = $this->getAnonymousAccessToken();
  311. $client->checkError();
  312. do_action($this->id . '_login_action_redirect', $this);
  313. /**
  314. * Check if we have an accessToken and a code.
  315. * If there is no access token and code it redirects to the Authorization Url.
  316. */
  317. if (!$accessTokenData && !$client->hasAuthenticateData()) {
  318. header('LOCATION: ' . $client->createAuthUrl());
  319. exit;
  320. } else {
  321. /**
  322. * If the code is OK but there is no access token, authentication is necessary.
  323. */
  324. if (!$accessTokenData) {
  325. $accessTokenData = $client->authenticate();
  326. $accessTokenData = $this->requestLongLivedToken($accessTokenData);
  327. /**
  328. * store the access token
  329. */
  330. $this->setAnonymousAccessToken($accessTokenData);
  331. } else {
  332. $client->setAccessTokenData($accessTokenData);
  333. }
  334. /**
  335. * if the login display was in popup window,
  336. * in the source window the user is redirected to the login url.
  337. * and the popup window must be closed
  338. */
  339. if (Persistent::get($this->id . '_display') == 'popup') {
  340. Persistent::delete($this->id . '_display');
  341. ?>
  342. <!doctype html>
  343. <html lang=en>
  344. <head>
  345. <meta charset=utf-8>
  346. <title><?php _e('Authentication successful', 'nextend-facebook-connect'); ?></title>
  347. <script type="text/javascript">
  348. try {
  349. if (window.opener !== null && window.opener !== window) {
  350. var sameOrigin = true;
  351. try {
  352. var currentOrigin = window.location.protocol + '//' + window.location.hostname;
  353. if (window.opener.location.href.substring(0, currentOrigin.length) !== currentOrigin) {
  354. sameOrigin = false;
  355. }
  356. } catch (e) {
  357. /**
  358. * Blocked cross origin
  359. */
  360. sameOrigin = false;
  361. }
  362. if (sameOrigin) {
  363. var url = <?php echo wp_json_encode($this->getLoginUrl()); ?>;
  364. if (typeof window.opener.nslRedirect === 'function') {
  365. window.opener.nslRedirect(url);
  366. } else {
  367. window.opener.location = url;
  368. }
  369. window.close();
  370. } else {
  371. window.location.reload(true);
  372. }
  373. } else {
  374. window.location.reload(true);
  375. }
  376. } catch (e) {
  377. window.location.reload(true);
  378. }
  379. </script>
  380. </head>
  381. <body><a href="<?php echo esc_url($this->getLoginUrl()); ?>"><?php echo 'Continue...'; ?></a></body>
  382. </html>
  383. <?php
  384. exit;
  385. }
  386. /**
  387. * Retrieves the userinfo trough the REST API and connect with the provider.
  388. * Redirects to the last location.
  389. */
  390. $this->authUserData = $this->getCurrentUserInfo();
  391. do_action($this->id . '_login_action_get_user_profile', $accessTokenData);
  392. }
  393. }
  394. /**
  395. * @param $access_token
  396. * Connect with the selected provider.
  397. * After a successful login, we no longer need the previous persistent data.
  398. */
  399. public function liveConnectGetUserProfile($access_token) {
  400. $socialUser = new NextendSocialUser($this, $access_token);
  401. $socialUser->liveConnectGetUserProfile();
  402. $this->deleteLoginPersistentData();
  403. $this->redirectToLastLocationOther(true);
  404. }
  405. /**
  406. * @param $user_id
  407. * @param $providerIdentifier
  408. * @param $isRegister
  409. * Insert the userid into the wp_social_users table,
  410. * in this way a link is created between user accounts and the providers.
  411. *
  412. * @return bool
  413. */
  414. public function linkUserToProviderIdentifier($user_id, $providerIdentifier, $isRegister = false) {
  415. /** @var $wpdb WPDB */ global $wpdb;
  416. $connectedProviderID = $this->getProviderIdentifierByUserID($user_id);
  417. if ($connectedProviderID !== null) {
  418. if ($connectedProviderID == $providerIdentifier) {
  419. // This provider already linked to this user
  420. return true;
  421. }
  422. // User already have this provider attached to his account with different provider id.
  423. return false;
  424. }
  425. if ($isRegister) {
  426. /**
  427. * This is a register action.
  428. */
  429. $wpdb->insert($wpdb->prefix . 'social_users', array(
  430. 'ID' => $user_id,
  431. 'type' => $this->dbID,
  432. 'identifier' => $providerIdentifier,
  433. 'register_date' => current_time('mysql'),
  434. 'link_date' => current_time('mysql'),
  435. ), array(
  436. '%d',
  437. '%s',
  438. '%s',
  439. '%s',
  440. '%s'
  441. ));
  442. } else {
  443. /**
  444. * This is a link action.
  445. */
  446. $wpdb->insert($wpdb->prefix . 'social_users', array(
  447. 'ID' => $user_id,
  448. 'type' => $this->dbID,
  449. 'identifier' => $providerIdentifier,
  450. 'link_date' => current_time('mysql'),
  451. ), array(
  452. '%d',
  453. '%s',
  454. '%s',
  455. '%s'
  456. ));
  457. }
  458. do_action('nsl_' . $this->getId() . '_link_user', $user_id, $this->getId());
  459. return true;
  460. }
  461. public function getUserIDByProviderIdentifier($identifier) {
  462. /** @var $wpdb WPDB */ global $wpdb;
  463. return $wpdb->get_var($wpdb->prepare('SELECT ID FROM `' . $wpdb->prefix . 'social_users` WHERE type = %s AND identifier = %s', array(
  464. $this->dbID,
  465. $identifier
  466. )));
  467. }
  468. protected function getProviderIdentifierByUserID($user_id) {
  469. /** @var $wpdb WPDB */ global $wpdb;
  470. return $wpdb->get_var($wpdb->prepare('SELECT identifier FROM `' . $wpdb->prefix . 'social_users` WHERE type = %s AND ID = %s', array(
  471. $this->dbID,
  472. $user_id
  473. )));
  474. }
  475. /**
  476. * @param $user_id
  477. * Delete the link between the user account and the provider.
  478. */
  479. public function removeConnectionByUserID($user_id) {
  480. /** @var $wpdb WPDB */ global $wpdb;
  481. $wpdb->query($wpdb->prepare('DELETE FROM `' . $wpdb->prefix . 'social_users` WHERE type = %s AND ID = %d', array(
  482. $this->dbID,
  483. $user_id
  484. )));
  485. }
  486. protected function unlinkUser() {
  487. //Filter to disable unlinking social accounts
  488. $unlinkAllowed = apply_filters('nsl_allow_unlink', true);
  489. if ($unlinkAllowed) {
  490. $user_info = wp_get_current_user();
  491. if ($user_info->ID) {
  492. $this->removeConnectionByUserID($user_info->ID);
  493. do_action('nsl_unlink_user', $user_info->ID, $this->getId());
  494. return true;
  495. }
  496. }
  497. return false;
  498. }
  499. /**
  500. * If the current user has linked the account with a provider return the user identifier else false.
  501. *
  502. * @return bool|null|string
  503. */
  504. public function isCurrentUserConnected() {
  505. /** @var $wpdb WPDB */ global $wpdb;
  506. $current_user = wp_get_current_user();
  507. $ID = $wpdb->get_var($wpdb->prepare('SELECT identifier FROM `' . $wpdb->prefix . 'social_users` WHERE type LIKE %s AND ID = %d', array(
  508. $this->dbID,
  509. $current_user->ID
  510. )));
  511. if ($ID === null) {
  512. return false;
  513. }
  514. return $ID;
  515. }
  516. /**
  517. * @param $user_id
  518. * If a user has linked the account with a provider return the user identifier else false.
  519. *
  520. * @return bool|null|string
  521. */
  522. public function isUserConnected($user_id) {
  523. /** @var $wpdb WPDB */ global $wpdb;
  524. $ID = $wpdb->get_var($wpdb->prepare('SELECT identifier FROM `' . $wpdb->prefix . 'social_users` WHERE type LIKE %s AND ID = %d', array(
  525. $this->dbID,
  526. $user_id
  527. )));
  528. if ($ID === null) {
  529. return false;
  530. }
  531. return $ID;
  532. }
  533. public function findUserByAccessToken($access_token) {
  534. return $this->getUserIDByProviderIdentifier($this->findSocialIDByAccessToken($access_token));
  535. }
  536. public function findSocialIDByAccessToken($access_token) {
  537. $client = $this->getClient();
  538. $client->setAccessTokenData($access_token);
  539. $this->authUserData = $this->getCurrentUserInfo();
  540. return $this->getAuthUserData('id');
  541. }
  542. public function getConnectButton($buttonStyle = 'default', $redirectTo = null, $trackerData = false, $labelType = 'login') {
  543. $arg = array();
  544. if (!empty($redirectTo)) {
  545. $arg['redirect'] = urlencode($redirectTo);
  546. } else if (!empty($_GET['redirect_to'])) {
  547. $arg['redirect'] = urlencode($_GET['redirect_to']);
  548. } else {
  549. $currentPageUrl = NextendSocialLogin::getCurrentPageURL();
  550. if ($currentPageUrl !== false) {
  551. $arg['redirect'] = urlencode($currentPageUrl);
  552. }
  553. }
  554. if ($trackerData !== false) {
  555. $arg['trackerdata'] = urlencode($trackerData);
  556. $arg['trackerdata_hash'] = urlencode(wp_hash($trackerData));
  557. }
  558. $label = $this->settings->get('login_label');
  559. $useCustomRegisterLabel = NextendSocialLogin::$settings->get('custom_register_label');
  560. if ($labelType == 'register' && $useCustomRegisterLabel) {
  561. $label = $this->settings->get('register_label');;
  562. }
  563. switch ($buttonStyle) {
  564. case 'icon':
  565. $button = $this->getIconButton();
  566. break;
  567. default:
  568. $button = $this->getDefaultButton($label);
  569. break;
  570. }
  571. return '<a href="' . esc_url(add_query_arg($arg, $this->getLoginUrl())) . '" rel="nofollow" aria-label="' . esc_attr__($label) . '" data-plugin="nsl" data-action="connect" data-provider="' . esc_attr($this->getId()) . '" data-popupwidth="' . $this->getPopupWidth() . '" data-popupheight="' . $this->getPopupHeight() . '">' . $button . '</a>';
  572. }
  573. public function getLinkButton() {
  574. $args = array(
  575. 'action' => 'link'
  576. );
  577. $redirect = NextendSocialLogin::getCurrentPageURL();
  578. if ($redirect !== false) {
  579. $args['redirect'] = urlencode($redirect);
  580. }
  581. return '<a href="' . esc_url(add_query_arg($args, $this->getLoginUrl())) . '" style="text-decoration:none;display:inline-block;box-shadow:none;" data-plugin="nsl" data-action="link" data-provider="' . esc_attr($this->getId()) . '" data-popupwidth="' . $this->getPopupWidth() . '" data-popupheight="' . $this->getPopupHeight() . '" aria-label="' . esc_attr__($this->settings->get('link_label')) . '">' . $this->getDefaultButton($this->settings->get('link_label')) . '</a>';
  582. }
  583. public function getUnLinkButton() {
  584. $args = array(
  585. 'action' => 'unlink'
  586. );
  587. $redirect = NextendSocialLogin::getCurrentPageURL();
  588. if ($redirect !== false) {
  589. $args['redirect'] = urlencode($redirect);
  590. }
  591. return '<a href="' . esc_url(add_query_arg($args, $this->getLoginUrl())) . '" style="text-decoration:none;display:inline-block;box-shadow:none;" data-plugin="nsl" data-action="unlink" data-provider="' . esc_attr($this->getId()) . '" aria-label="' . esc_attr__($this->settings->get('unlink_label')) . '">' . $this->getDefaultButton($this->settings->get('unlink_label')) . '</a>';
  592. }
  593. public function redirectToLoginForm() {
  594. self::redirect(__('Authentication error', 'nextend-facebook-connect'), NextendSocialLogin::enableNoticeForUrl(NextendSocialLogin::getLoginUrl()));
  595. }
  596. /**
  597. * -Allows for logged in users to unlink their account from a provider, if it was linked, and
  598. * redirects to the last location.
  599. * -During linking process, store the action as link. After the linking process is finished,
  600. * delete this stored info and redirects to the last location.
  601. */
  602. public function liveConnectBefore() {
  603. if (is_user_logged_in() && $this->isCurrentUserConnected()) {
  604. if (isset($_GET['action']) && $_GET['action'] == 'unlink') {
  605. if ($this->unlinkUser()) {
  606. Notices::addSuccess(__('Unlink successful.', 'nextend-facebook-connect'));
  607. } else {
  608. Notices::addError(__('Unlink is not allowed!', 'nextend-facebook-connect'));
  609. }
  610. }
  611. $this->redirectToLastLocationOther(true);
  612. exit;
  613. }
  614. if (isset($_GET['action']) && $_GET['action'] == 'link') {
  615. Persistent::set($this->id . '_action', 'link');
  616. }
  617. if (is_user_logged_in() && Persistent::get($this->id . '_action') != 'link') {
  618. $this->deleteLoginPersistentData();
  619. $this->redirectToLastLocationOther();
  620. exit;
  621. }
  622. }
  623. /**
  624. * Store where the user logged in.
  625. */
  626. public function liveConnectRedirect() {
  627. if (!empty($_GET['trackerdata']) && !empty($_GET['trackerdata_hash'])) {
  628. if (wp_hash($_GET['trackerdata']) === $_GET['trackerdata_hash']) {
  629. Persistent::set('trackerdata', $_GET['trackerdata']);
  630. }
  631. }
  632. if (!empty($_GET['redirect'])) {
  633. Persistent::set('redirect', $_GET['redirect']);
  634. }
  635. }
  636. public function redirectToLastLocation($notice = false) {
  637. $url = $this->getLastLocationRedirectTo();
  638. if (Persistent::get($this->id . '_interim_login') == 1) {
  639. $this->deleteLoginPersistentData();
  640. $args['interim_login'] = 'nsl';
  641. $url = add_query_arg($args, NextendSocialLogin::getLoginUrl('login'));
  642. if ($notice) {
  643. $url = NextendSocialLogin::enableNoticeForUrl($url);
  644. }
  645. self::redirect(__('Authentication successful', 'nextend-facebook-connect'), $url);
  646. exit;
  647. }
  648. if ($notice) {
  649. $url = NextendSocialLogin::enableNoticeForUrl($url);
  650. }
  651. self::redirect(__('Authentication successful', 'nextend-facebook-connect'), $url);
  652. }
  653. /**
  654. * @param bool $notice
  655. */
  656. protected function redirectToLastLocationOther($notice = false) {
  657. $this->redirectToLastLocation($notice);
  658. }
  659. protected function validateRedirect($location) {
  660. $location = wp_sanitize_redirect($location);
  661. return wp_validate_redirect($location, apply_filters('wp_safe_redirect_fallback', admin_url(), 302));
  662. }
  663. public function hasFixedRedirect() {
  664. if (NextendSocialLogin::$WPLoginCurrentFlow == 'register') {
  665. $fixedRedirect = NextendSocialLogin::$settings->get('redirect_reg');
  666. $fixedRedirect = apply_filters($this->id . '_register_redirect_url', $fixedRedirect, $this);
  667. if (!empty($fixedRedirect)) {
  668. return true;
  669. }
  670. } else if (NextendSocialLogin::$WPLoginCurrentFlow == 'login') {
  671. $fixedRedirect = NextendSocialLogin::$settings->get('redirect');
  672. $fixedRedirect = apply_filters($this->id . '_login_redirect_url', $fixedRedirect, $this);
  673. if (!empty($fixedRedirect)) {
  674. return true;
  675. }
  676. }
  677. return false;
  678. }
  679. /**
  680. * If fixed redirect url is set, redirect to fixed redirect url.
  681. * If fixed redirect url is not set, but redirect is in the url redirect to the $_GET['redirect'].
  682. * If fixed redirect url is not set and there is no redirect in the url, redirects to the default redirect url if it
  683. * is set.
  684. * Else redirect to the site url.
  685. *
  686. * @return mixed|void
  687. */
  688. protected function getLastLocationRedirectTo() {
  689. $redirect_to = '';
  690. $requested_redirect_to = '';
  691. $fixedRedirect = '';
  692. if (NextendSocialLogin::$WPLoginCurrentFlow == 'register') {
  693. $fixedRedirect = NextendSocialLogin::$settings->get('redirect_reg');
  694. $fixedRedirect = apply_filters($this->id . '_register_redirect_url', $fixedRedirect, $this);
  695. } else if (NextendSocialLogin::$WPLoginCurrentFlow == 'login') {
  696. $fixedRedirect = NextendSocialLogin::$settings->get('redirect');
  697. $fixedRedirect = apply_filters($this->id . '_login_redirect_url', $fixedRedirect, $this);
  698. }
  699. if (!empty($fixedRedirect)) {
  700. $redirect_to = $fixedRedirect;
  701. } else {
  702. $requested_redirect_to = Persistent::get('redirect');
  703. if (!empty($requested_redirect_to)) {
  704. if (empty($requested_redirect_to) || !NextendSocialLogin::isAllowedRedirectUrl($requested_redirect_to)) {
  705. if (!empty($_GET['redirect']) && NextendSocialLogin::isAllowedRedirectUrl($_GET['redirect'])) {
  706. $requested_redirect_to = $_GET['redirect'];
  707. } else {
  708. $requested_redirect_to = '';
  709. }
  710. }
  711. if (empty($requested_redirect_to)) {
  712. $redirect_to = site_url();
  713. } else {
  714. $redirect_to = $requested_redirect_to;
  715. }
  716. $redirect_to = wp_sanitize_redirect($redirect_to);
  717. $redirect_to = wp_validate_redirect($redirect_to, site_url());
  718. $redirect_to = $this->validateRedirect($redirect_to);
  719. } else if (!empty($_GET['redirect']) && NextendSocialLogin::isAllowedRedirectUrl($_GET['redirect'])) {
  720. $redirect_to = $_GET['redirect'];
  721. $redirect_to = wp_sanitize_redirect($redirect_to);
  722. $redirect_to = wp_validate_redirect($redirect_to, site_url());
  723. $redirect_to = $this->validateRedirect($redirect_to);
  724. }
  725. if (empty($redirect_to)) {
  726. $defaultRedirect = '';
  727. if (NextendSocialLogin::$WPLoginCurrentFlow == 'register') {
  728. $defaultRedirect = NextendSocialLogin::$settings->get('default_redirect_reg');
  729. $defaultRedirect = apply_filters($this->id . '_default_register_redirect_url', $defaultRedirect, $this);
  730. } else if (NextendSocialLogin::$WPLoginCurrentFlow == 'login') {
  731. $defaultRedirect = NextendSocialLogin::$settings->get('default_redirect');
  732. $defaultRedirect = apply_filters($this->id . '_default_[login_redirect_url', $defaultRedirect, $this);
  733. }
  734. if ((!empty($defaultRedirect))) {
  735. $redirect_to = $defaultRedirect;
  736. }
  737. }
  738. $redirect_to = apply_filters('nsl_' . $this->getId() . 'default_last_location_redirect', $redirect_to, $requested_redirect_to);
  739. }
  740. if ($redirect_to == '' || $redirect_to == $this->getLoginUrl()) {
  741. $redirect_to = site_url();
  742. }
  743. Persistent::delete('redirect');
  744. return apply_filters('nsl_' . $this->getId() . 'last_location_redirect', $redirect_to, $requested_redirect_to);
  745. }
  746. /**
  747. * @param $user_id
  748. * @param $provider NextendSocialProvider
  749. * @param $access_token string
  750. */
  751. public function syncProfile($user_id, $provider, $access_token) {
  752. }
  753. /**
  754. * Check if a logged in user with manage_options capability, want to verify their provider settings.
  755. *
  756. * @return bool
  757. */
  758. public function isTest() {
  759. if (is_user_logged_in() && current_user_can('manage_options')) {
  760. if (isset($_REQUEST['test'])) {
  761. Persistent::set('test', 1);
  762. return true;
  763. } else if (Persistent::get('test') == 1) {
  764. return true;
  765. }
  766. }
  767. return false;
  768. }
  769. /**
  770. * Make the current provider in verified mode, and update the oauth_redirect_url.
  771. */
  772. public function testConnectGetUserProfile() {
  773. $this->deleteLoginPersistentData();
  774. $this->settings->update(array(
  775. 'tested' => 1,
  776. 'oauth_redirect_url' => $this->getBaseRedirectUriForAppCreation()
  777. ));
  778. Notices::addSuccess(__('The test was successful', 'nextend-facebook-connect'));
  779. ?>
  780. <!doctype html>
  781. <html lang=en>
  782. <head>
  783. <meta charset=utf-8>
  784. <title><?php _e('The test was successful', 'nextend-facebook-connect'); ?></title>
  785. <script type="text/javascript">
  786. window.opener.location.reload(true);
  787. window.close();
  788. </script>
  789. </head>
  790. </html>
  791. <?php
  792. exit;
  793. }
  794. /**
  795. * @param $accessToken
  796. * Store the accessToken data.
  797. */
  798. protected function setAnonymousAccessToken($accessToken) {
  799. Persistent::set($this->id . '_at', $accessToken);
  800. }
  801. protected function getAnonymousAccessToken() {
  802. return Persistent::get($this->id . '_at');
  803. }
  804. public function deleteLoginPersistentData() {
  805. Persistent::delete($this->id . '_at');
  806. Persistent::delete($this->id . '_interim_login');
  807. Persistent::delete($this->id . '_display');
  808. Persistent::delete($this->id . '_action');
  809. Persistent::delete('test');
  810. }
  811. /**
  812. * @param $e Exception
  813. */
  814. protected function onError($e) {
  815. if (NextendSocialLogin::$settings->get('debug') == 1 || $this->isTest()) {
  816. header('HTTP/1.0 401 Unauthorized');
  817. echo "Error: " . $e->getMessage() . "\n";
  818. } else {
  819. //@TODO we might need to make difference between user cancelled auth and error and redirect the user based on that.
  820. $url = $this->getLastLocationRedirectTo();
  821. ?>
  822. <!doctype html>
  823. <html lang=en>
  824. <head>
  825. <meta charset=utf-8>
  826. <title><?php echo __('Authentication failed', 'nextend-facebook-connect'); ?></title>
  827. <script type="text/javascript">
  828. try {
  829. if (window.opener !== null && window.opener !== window) {
  830. var sameOrigin = true;
  831. try {
  832. var currentOrigin = window.location.protocol + '//' + window.location.hostname;
  833. if (window.opener.location.href.substring(0, currentOrigin.length) !== currentOrigin) {
  834. sameOrigin = false;
  835. }
  836. } catch (e) {
  837. /**
  838. * Blocked cross origin
  839. */
  840. sameOrigin = false;
  841. }
  842. if (sameOrigin) {
  843. window.close();
  844. }
  845. }
  846. } catch (e) {
  847. }
  848. window.location = <?php echo wp_json_encode($url); ?>;
  849. </script>
  850. <meta http-equiv="refresh" content="0;<?php echo esc_attr($url); ?>">
  851. </head>
  852. <body>
  853. </body>
  854. </html>
  855. <?php
  856. }
  857. $this->deleteLoginPersistentData();
  858. exit;
  859. }
  860. protected function saveUserData($user_id, $key, $data) {
  861. update_user_meta($user_id, $this->id . '_' . $key, $data);
  862. }
  863. protected function getUserData($user_id, $key) {
  864. return get_user_meta($user_id, $this->id . '_' . $key, true);
  865. }
  866. public function getAccessToken($user_id) {
  867. return $this->getUserData($user_id, 'access_token');
  868. }
  869. /**
  870. * @param $user_id
  871. *
  872. * @return bool
  873. * @deprecated
  874. *
  875. */
  876. public function getAvatar($user_id) {
  877. return false;
  878. }
  879. /**
  880. * @return array
  881. */
  882. protected function getCurrentUserInfo() {
  883. return array();
  884. }
  885. protected function requestLongLivedToken($accessTokenData) {
  886. return $accessTokenData;
  887. }
  888. /**
  889. * @param $key
  890. *
  891. * @return string
  892. */
  893. public function getAuthUserData($key) {
  894. return '';
  895. }
  896. /**
  897. * @param $title
  898. * @param $url
  899. * Redirect the source of the popup window to a specified url.
  900. */
  901. public static function redirect($title, $url) {
  902. ?>
  903. <!doctype html>
  904. <html lang=en>
  905. <head>
  906. <meta charset=utf-8>
  907. <title><?php echo $title; ?></title>
  908. <script type="text/javascript">
  909. try {
  910. if (window.opener !== null && window.opener !== window) {
  911. var sameOrigin = true;
  912. try {
  913. var currentOrigin = window.location.protocol + '//' + window.location.hostname;
  914. if (window.opener.location.href.substring(0, currentOrigin.length) !== currentOrigin) {
  915. sameOrigin = false;
  916. }
  917. } catch (e) {
  918. /**
  919. * Blocked cross origin
  920. */
  921. sameOrigin = false;
  922. }
  923. if (sameOrigin) {
  924. window.opener.location = <?php echo wp_json_encode($url); ?>;
  925. window.close();
  926. }
  927. }
  928. } catch (e) {
  929. }
  930. window.location = <?php echo wp_json_encode($url); ?>;
  931. </script>
  932. <meta http-equiv="refresh" content="0;<?php echo esc_attr($url); ?>">
  933. </head>
  934. <body>
  935. </body>
  936. </html>
  937. <?php
  938. exit;
  939. }
  940. public function getSyncFields() {
  941. return $this->sync_fields;
  942. }
  943. public function hasSyncFields() {
  944. return !empty($this->sync_fields);
  945. }
  946. public function validateSettings($newData, $postedData) {
  947. return $newData;
  948. }
  949. protected function needUpdateAvatar($user_id) {
  950. return apply_filters('nsl_avatar_store', NextendSocialLogin::$settings->get('avatar_store'), $user_id, $this);
  951. }
  952. protected function updateAvatar($user_id, $url) {
  953. do_action('nsl_update_avatar', $this, $user_id, $url);
  954. }
  955. public function exportPersonalData($userID) {
  956. $data = array();
  957. $socialID = $this->isUserConnected($userID);
  958. if ($socialID !== false) {
  959. $data[] = array(
  960. 'name' => $this->getLabel() . ' ' . __('Identifier', 'nextend-facebook-connect'),
  961. 'value' => $socialID,
  962. );
  963. }
  964. $accessToken = $this->getAccessToken($userID);
  965. if (!empty($accessToken)) {
  966. $data[] = array(
  967. 'name' => $this->getLabel() . ' ' . __('Access token', 'nextend-facebook-connect'),
  968. 'value' => $accessToken,
  969. );
  970. }
  971. $profilePicture = $this->getUserData($userID, 'profile_picture');
  972. if (!empty($profilePicture)) {
  973. $data[] = array(
  974. 'name' => $this->getLabel() . ' ' . __('Profile Picture'),
  975. 'value' => $profilePicture,
  976. );
  977. }
  978. foreach ($this->getSyncFields() as $fieldName => $fieldData) {
  979. $meta_key = $this->settings->get('sync_fields/fields/' . $fieldName . '/meta_key');
  980. if (!empty($meta_key)) {
  981. $value = get_user_meta($userID, $meta_key, true);
  982. if (!empty($value)) {
  983. $data[] = array(
  984. 'name' => $this->getLabel() . ' ' . $fieldData['label'],
  985. 'value' => $value
  986. );
  987. }
  988. }
  989. }
  990. return $data;
  991. }
  992. protected function storeAccessToken($userID, $accessToken) {
  993. if (NextendSocialLogin::$settings->get('store_access_token') == 1) {
  994. $this->saveUserData($userID, 'access_token', $accessToken);
  995. }
  996. }
  997. public function getSyncDataFieldDescription($fieldName) {
  998. return '';
  999. }
  1000. /**
  1001. * @param $user_id
  1002. * Update social_users table with login date of the user.
  1003. */
  1004. public function logLoginDate($user_id) {
  1005. /** @var $wpdb WPDB */ global $wpdb;
  1006. $wpdb->update($wpdb->prefix . 'social_users', array('login_date' => current_time('mysql'),), array(
  1007. 'ID' => $user_id,
  1008. 'type' => $this->dbID
  1009. ), array(
  1010. '%s',
  1011. '%s'
  1012. ));
  1013. }
  1014. public function registerRedirectRESTRoute() {
  1015. if ($this->oauthRedirectBehavior === 'rest_redirect') {
  1016. register_rest_route('nextend-social-login/v1', $this->id . '/redirect_uri', array(
  1017. 'methods' => WP_REST_Server::READABLE,
  1018. 'callback' => array(
  1019. $this,
  1020. 'redirectToProviderEndpointWithStateAndCode'
  1021. ),
  1022. 'args' => array(
  1023. 'state' => array(
  1024. 'required' => true,
  1025. ),
  1026. 'code' => array(
  1027. 'required' => true,
  1028. )
  1029. ),
  1030. 'permission_callback' => '__return_true',
  1031. ));
  1032. }
  1033. }
  1034. /**
  1035. * @param WP_REST_Request $request Full details about the request.
  1036. *
  1037. * Registers a REST API endpoints for a provider. This endpoint handles the redirect to the login endpoint of the
  1038. * currently used provider. The state and code GET parameters will be added to the login URL, so we can imitate as
  1039. * if the provider would already returned the state and code parameters to the original login url.
  1040. *
  1041. * @return WP_Error|WP_REST_Response
  1042. */
  1043. public function redirectToProviderEndpointWithStateAndCode($request) {
  1044. $params = $request->get_params();
  1045. $errorMessage = '';
  1046. if (!empty($params['state']) && !empty($params['code'])) {
  1047. $provider = NextendSocialLogin::$allowedProviders[$this->id];
  1048. try {
  1049. $providerEndpoint = $provider->getLoginUrl();
  1050. if (defined('WPML_PLUGIN_BASENAME')) {
  1051. $providerEndpoint = $provider->getTranslatedLoginURLForRestRedirect();
  1052. }
  1053. $providerEndpointWithStateAndCode = add_query_arg(array(
  1054. 'state' => $params['state'],
  1055. 'code' => $params['code']
  1056. ), $providerEndpoint);
  1057. wp_safe_redirect($providerEndpointWithStateAndCode);
  1058. exit;
  1059. } catch (Exception $e) {
  1060. $errorMessage = $e->getMessage();
  1061. }
  1062. } else {
  1063. if (empty($params['state']) && empty($params['code'])) {
  1064. $errorMessage = 'The code and state parameters are empty!';
  1065. } else if (empty($params['state'])) {
  1066. $errorMessage = 'The state parameter is empty!';
  1067. } else {
  1068. $errorMessage = 'The code parameter is empty!';
  1069. }
  1070. }
  1071. return new WP_Error('error', $errorMessage);
  1072. }
  1073. /**
  1074. * Generates a single translated login URL where the REST /redirect_uri endpoint of the currently used provider
  1075. * should redirect to instead of the original login url.
  1076. *
  1077. * @return string
  1078. */
  1079. public function getTranslatedLoginURLForRestRedirect() {
  1080. $originalLoginUrl = $this->getLoginUrl();
  1081. /**
  1082. * We should attempt to generate translated login URLs only if WPML is active and there is a language code defined.
  1083. */
  1084. if (defined('WPML_PLUGIN_BASENAME') && defined('ICL_LANGUAGE_CODE')) {
  1085. global $sitepress;
  1086. $languageCode = ICL_LANGUAGE_CODE;
  1087. if ($sitepress && method_exists($sitepress, 'get_active_languages') && $languageCode) {
  1088. $WPML_active_languages = $sitepress->get_active_languages();
  1089. if (count($WPML_active_languages) > 1) {
  1090. /**
  1091. * Fix:
  1092. * When WPML has the language URL format set to "Language name added as a parameter",
  1093. * we can not pass that parameter in the Authorization request in some cases ( e.g.: Microsoft ).
  1094. * In these cases the user will end up redirected to the redirect URL without language parameter,
  1095. * so after the login we won't be able to redirect them to registration flow page of the corresponding language.
  1096. * In these cases we need to use the language code according to the url where we should redirect after the login.
  1097. */
  1098. $WPML_language_url_format = false;
  1099. if (method_exists($sitepress, 'get_setting')) {
  1100. $WPML_language_url_format = $sitepress->get_setting('language_negotiation_type');
  1101. }
  1102. if ($WPML_language_url_format && $WPML_language_url_format == 3) {
  1103. $persistentRedirect = Persistent::get('redirect');
  1104. if ($persistentRedirect) {
  1105. $persistentRedirectQueryParams = array();
  1106. $persistentRedirectQueryString = parse_url($persistentRedirect, PHP_URL_QUERY);
  1107. parse_str($persistentRedirectQueryString, $persistentRedirectQueryParams);
  1108. if (isset($persistentRedirectQueryParams['lang']) && !empty($persistentRedirectQueryParams['lang'])) {
  1109. $languageParam = sanitize_text_field($persistentRedirectQueryParams['lang']);
  1110. if (in_array($languageParam, array_keys($WPML_active_languages))) {
  1111. /**
  1112. * The language code that we got from the persistent redirect url is a valid language code for WPML,
  1113. * so we can use this code.
  1114. */
  1115. $languageCode = $languageParam;
  1116. }
  1117. }
  1118. }
  1119. }
  1120. $args = array('loginSocial' => $this->getId());
  1121. $proxyPage = NextendSocialLogin::getProxyPage();
  1122. if ($proxyPage) {
  1123. //OAuth flow handled over OAuth redirect uri proxy page
  1124. $convertedURL = get_permalink(apply_filters('wpml_object_id', $proxyPage, 'page', false, $languageCode));
  1125. if ($convertedURL) {
  1126. $convertedURL = add_query_arg($args, $convertedURL);
  1127. return $convertedURL;
  1128. }
  1129. } else {
  1130. //OAuth flow handled over wp-login.php
  1131. if ($WPML_language_url_format && $WPML_language_url_format == 3 && (!class_exists('\WPML\UrlHandling\WPLoginUrlConverter') || (class_exists('\WPML\UrlHandling\WPLoginUrlConverter') && (!get_option(\WPML\UrlHandling\WPLoginUrlConverter::SETTINGS_KEY, false))))) {
  1132. /**
  1133. * We need to display the original redirect url when the
  1134. * Language URL format is set to "Language name added as a parameter and:
  1135. * -when the WPLoginUrlConverter class doesn't exists, since that case it is an old WPML version that can not translate the /wp-login.php page
  1136. * -if "Login and registration pages - Allow translating the login and registration pages" is disabled
  1137. */
  1138. return $originalLoginUrl;
  1139. } else {
  1140. global $wpml_url_converter;
  1141. /**
  1142. * When the language URL format is set to "Different languages in directories" or "A different domain per language", then the Redirect URI will be different for each languages
  1143. * Also when the language URL format is set to "Language name added as a parameter" and the "Login and registration pages - Allow translating the login and registration pages" setting is enabled, the urls will be different.
  1144. */
  1145. if ($wpml_url_converter && method_exists($wpml_url_converter, 'convert_url')) {
  1146. $convertedURL = $wpml_url_converter->convert_url(site_url('wp-login.php'), $languageCode);
  1147. $convertedURL = add_query_arg($args, $convertedURL);
  1148. return $convertedURL;
  1149. }
  1150. }
  1151. }
  1152. }
  1153. }
  1154. }
  1155. return $originalLoginUrl;
  1156. }
  1157. }