text: /*!
   * ngCordova
   * v0.1.20-alpha
   * Copyright 2015 Drifty Co. http://drifty.com/
   * See LICENSE in this repository for license information
   */
  (function () {
  
    angular.module('ngCordova', [
      'ngCordova.plugins'
    ]);
  
  // install  :     cordova plugin add https://github.com/EddyVerbruggen/cordova-plugin-actionsheet.git
  // link     :     https://github.com/EddyVerbruggen/cordova-plugin-actionsheet
  
    angular.module('ngCordova.plugins.actionSheet', [])
  
      .factory('$cordovaActionSheet', ['$q', '$window', function ($q, $window) {
  
        return {
          show: function (options) {
            var q = $q.defer();
  
            $window.plugins.actionsheet.show(options, function (result) {
              q.resolve(result);
            });
  
            return q.promise;
          },
  
          hide: function () {
            return $window.plugins.actionsheet.hide();
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-plugin-admob.git
  // link     :     https://github.com/floatinghotpot/cordova-plugin-admob
  
    angular.module('ngCordova.plugins.adMob', [])
  
      .factory('$cordovaAdMob', ['$q', '$window', function ($q, $window) {
  
        return {
          createBannerView: function (options) {
            var d = $q.defer();
  
            $window.plugins.AdMob.createBannerView(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createInterstitialView: function (options) {
            var d = $q.defer();
  
            $window.plugins.AdMob.createInterstitialView(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          requestAd: function (options) {
            var d = $q.defer();
  
            $window.plugins.AdMob.requestAd(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showAd: function (options) {
            var d = $q.defer();
  
            $window.plugins.AdMob.showAd(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          requestInterstitialAd: function (options) {
            var d = $q.defer();
  
            $window.plugins.AdMob.requestInterstitialAd(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/ohh2ahh/AppAvailability.git
  // link     :     https://github.com/ohh2ahh/AppAvailability
  
    /* globals appAvailability: true */
    angular.module('ngCordova.plugins.appAvailability', [])
  
      .factory('$cordovaAppAvailability', ['$q', function ($q) {
  
        return {
          check: function (urlScheme) {
            var q = $q.defer();
  
            appAvailability.check(urlScheme, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/pushandplay/cordova-plugin-apprate.git
  // link     :     https://github.com/pushandplay/cordova-plugin-apprate
  
    /* globals AppRate: true */
    angular.module('ngCordova.plugins.appRate', [])
  
      .provider('$cordovaAppRate', [function () {
  
        /**
         * Set defaults settings to AppRate
         *
         * @param {Object} defaults - AppRate default settings
         * @param {string} defaults.language
         * @param {string} defaults.appName
         * @param {boolean} defaults.promptForNewVersion
         * @param {boolean} defaults.openStoreInApp
         * @param {number} defaults.usesUntilPrompt
         * @param {boolean} defaults.useCustomRateDialog
         * @param {string} defaults.iosURL
         * @param {string} defaults.androidURL
         * @param {string} defaults.blackberryURL
         * @param {string} defaults.windowsURL
         */
        this.setPreferences = function (defaults) {
          if (!defaults || !angular.isObject(defaults)) {
            return;
          }
  
          AppRate.preferences.useLanguage = defaults.language || null;
          AppRate.preferences.displayAppName = defaults.appName || '';
          AppRate.preferences.promptAgainForEachNewVersion = defaults.promptForNewVersion || true;
          AppRate.preferences.openStoreInApp = defaults.openStoreInApp || false;
          AppRate.preferences.usesUntilPrompt = defaults.usesUntilPrompt || 3;
          AppRate.preferences.useCustomRateDialog = defaults.useCustomRateDialog || false;
          AppRate.preferences.storeAppURL.ios = defaults.iosURL || null;
          AppRate.preferences.storeAppURL.android = defaults.androidURL || null;
          AppRate.preferences.storeAppURL.blackberry = defaults.blackberryURL || null;
          AppRate.preferences.storeAppURL.windows8 = defaults.windowsURL || null;
        };
  
        /**
         * Set custom locale
         *
         * @param {Object} customObj
         * @param {string} customObj.title
         * @param {string} customObj.cancelButtonLabel
         * @param {string} customObj.laterButtonLabel
         * @param {string} customObj.rateButtonLabel
         */
        this.setCustomLocale = function (customObj) {
          var strings = {
            title: 'Rate %@',
            message: 'If you enjoy using %@, would you mind taking a moment to rate it? It won’t take more than a minute. Thanks for your support!',
            cancelButtonLabel: 'No, Thanks',
            laterButtonLabel: 'Remind Me Later',
            rateButtonLabel: 'Rate It Now'
          };
  
          strings = angular.extend(strings, customObj);
  
          AppRate.preferences.customLocale = strings;
        };
  
        this.$get = ['$q', function ($q) {
          return {
            promptForRating: function (immediate) {
              var q = $q.defer();
              var prompt = AppRate.promptForRating(immediate);
              q.resolve(prompt);
  
              return q.promise;
            },
  
            navigateToAppStore: function () {
              var q = $q.defer();
              var navigate = AppRate.navigateToAppStore();
              q.resolve(navigate);
  
              return q.promise;
            },
  
            onButtonClicked: function (cb) {
              AppRate.onButtonClicked = function (buttonIndex) {
                cb.call(this, buttonIndex);
              };
            },
  
            onRateDialogShow: function (cb) {
              AppRate.onRateDialogShow = cb();
            }
          };
        }];
      }]);
  
  // install   :     cordova plugin add https://github.com/whiteoctober/cordova-plugin-app-version.git
  // link      :     https://github.com/whiteoctober/cordova-plugin-app-version
  
    angular.module('ngCordova.plugins.appVersion', [])
  
      .factory('$cordovaAppVersion', ['$q', function ($q) {
  
        return {
          getVersionNumber: function () {
            var q = $q.defer();
            cordova.getAppVersion.getVersionNumber(function (version) {
              q.resolve(version);
            });
  
            return q.promise;
          },
  
          getVersionCode: function () {
            var q = $q.defer();
            cordova.getAppVersion.getVersionCode(function (code) {
              q.resolve(code);
            });
  
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add https://github.com/christocracy/cordova-plugin-background-geolocation.git
  // link      :     https://github.com/christocracy/cordova-plugin-background-geolocation
  
    angular.module('ngCordova.plugins.backgroundGeolocation', [])
  
      .factory('$cordovaBackgroundGeolocation', ['$q', '$window', function ($q, $window) {
  
        return {
  
          init: function () {
            $window.navigator.geolocation.getCurrentPosition(function (location) {
              return location;
            });
          },
  
          configure: function (options) {
  
            this.init();
            var q = $q.defer();
  
            $window.plugins.backgroundGeoLocation.configure(
              function (result) {
                q.notify(result);
                $window.plugins.backgroundGeoLocation.finish();
              },
              function (err) {
                q.reject(err);
              }, options);
  
            this.start();
  
            return q.promise;
          },
  
          start: function () {
            var q = $q.defer();
  
            $window.plugins.backgroundGeoLocation.start(
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              });
  
            return q.promise;
          },
  
          stop: function () {
            var q = $q.defer();
  
            $window.plugins.backgroundGeoLocation.stop(
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              });
  
            return q.promise;
          }
        };
      }
  
      ]);
  
  // install  :     cordova plugin add https://github.com/katzer/cordova-plugin-badge.git
  // link     :     https://github.com/katzer/cordova-plugin-badge
  
    angular.module('ngCordova.plugins.badge', [])
  
      .factory('$cordovaBadge', ['$q', function ($q) {
  
        return {
          hasPermission: function () {
            var q = $q.defer();
            cordova.plugins.notification.badge.hasPermission(function (permission) {
              if (permission) {
                q.resolve(true);
              } else {
                q.reject('You do not have permission');
              }
            });
  
            return q.promise;
          },
  
          promptForPermission: function () {
            return cordova.plugins.notification.badge.promptForPermission();
          },
  
          set: function (badge, callback, scope) {
            var q = $q.defer();
  
            cordova.plugins.notification.badge.hasPermission(function (permission) {
              if (permission) {
                q.resolve(
                  cordova.plugins.notification.badge.set(badge, callback, scope)
                );
              } else {
                q.reject('You do not have permission to set Badge');
              }
            });
            return q.promise;
          },
  
          get: function () {
            var q = $q.defer();
            cordova.plugins.notification.badge.hasPermission(function (permission) {
              if (permission) {
                cordova.plugins.notification.badge.get(function (badge) {
                  q.resolve(badge);
                });
              } else {
                q.reject('You do not have permission to get Badge');
              }
            });
  
            return q.promise;
          },
  
          clear: function (callback, scope) {
            var q = $q.defer();
  
            cordova.plugins.notification.badge.hasPermission(function (permission) {
              if (permission) {
                q.resolve(cordova.plugins.notification.badge.clear(callback, scope));
              } else {
                q.reject('You do not have permission to clear Badge');
              }
            });
            return q.promise;
          },
  
          increase: function (count, callback, scope) {
            var q = $q.defer();
  
            this.hasPermission().then(function () {
              q.resolve(
                cordova.plugins.notification.badge.increase(count, callback, scope)
              );
            }, function () {
              q.reject('You do not have permission to increase Badge');
            });
  
            return q.promise;
          },
  
          decrease: function (count, callback, scope) {
            var q = $q.defer();
  
            this.hasPermission().then(function () {
              q.resolve(
                cordova.plugins.notification.badge.decrease(count, callback, scope)
              );
            }, function () {
              q.reject('You do not have permission to decrease Badge');
            });
  
            return q.promise;
          },
  
          configure: function (config) {
            return cordova.plugins.notification.badge.configure(config);
          }
        };
      }]);
  
  // install  :    cordova plugin add https://github.com/wildabeast/BarcodeScanner.git
  // link     :    https://github.com/wildabeast/BarcodeScanner
  
    angular.module('ngCordova.plugins.barcodeScanner', [])
  
      .factory('$cordovaBarcodeScanner', ['$q', function ($q) {
  
        return {
          scan: function (config) {
            var q = $q.defer();
  
            cordova.plugins.barcodeScanner.scan(function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            }, config);
  
            return q.promise;
          },
  
          encode: function (type, data) {
            var q = $q.defer();
            type = type || 'TEXT_TYPE';
  
            cordova.plugins.barcodeScanner.encode(type, data, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          }
        };
      }]);
  
  //  install   :   cordova plugin add cordova-plugin-battery-status
  //  link      :   https://github.com/apache/cordova-plugin-battery-status
  
    angular.module('ngCordova.plugins.batteryStatus', [])
  
      .factory('$cordovaBatteryStatus', ['$rootScope', '$window', '$timeout', function ($rootScope, $window, $timeout) {
  
        /**
         * @param {string} status
         */
        var batteryStatus = function (status) {
          $timeout(function () {
            $rootScope.$broadcast('$cordovaBatteryStatus:status', status);
          });
        };
  
        /**
         * @param {string} status
         */
        var batteryCritical = function (status) {
          $timeout(function () {
            $rootScope.$broadcast('$cordovaBatteryStatus:critical', status);
          });
        };
  
        /**
         * @param {string} status
         */
        var batteryLow = function (status) {
          $timeout(function () {
            $rootScope.$broadcast('$cordovaBatteryStatus:low', status);
          });
        };
  
        document.addEventListener('deviceready', function () {
          if (navigator.battery) {
            $window.addEventListener('batterystatus', batteryStatus, false);
            $window.addEventListener('batterycritical', batteryCritical, false);
            $window.addEventListener('batterylow', batteryLow, false);
  
          }
        }, false);
        return true;
      }])
      .run(['$injector', function ($injector) {
        $injector.get('$cordovaBatteryStatus'); //ensure the factory and subsequent event listeners get initialised
      }]);
  
  //  install   :   cordova plugin add https://github.com/don/cordova-plugin-ble-central.git
  //  link      :   https://github.com/don/cordova-plugin-ble-central
  
    /* globals ble: true */
    angular.module('ngCordova.plugins.ble', [])
  
      .factory('$cordovaBLE', ['$q', '$timeout', function ($q, $timeout) {
  
        return {
          scan: function (services, seconds) {
            var q = $q.defer();
  
            ble.startScan(services, function (result) {
              q.notify(result);
            }, function (error) {
              q.reject(error);
            });
  
            $timeout(function () {
              ble.stopScan(function () {
                q.resolve();
              }, function (error) {
                q.reject(error);
              });
            }, seconds * 1000);
  
            return q.promise;
          },
  
          connect: function (deviceID) {
            var q = $q.defer();
            ble.connect(deviceID, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          disconnect: function (deviceID) {
            var q = $q.defer();
            ble.disconnect(deviceID, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          read: function (deviceID, serviceUUID, characteristicUUID) {
            var q = $q.defer();
            ble.read(deviceID, serviceUUID, characteristicUUID, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          write: function (deviceID, serviceUUID, characteristicUUID, data) {
            var q = $q.defer();
            ble.write(deviceID, serviceUUID, characteristicUUID, data, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          writeCommand: function (deviceID, serviceUUID, characteristicUUID, data) {
            var q = $q.defer();
            ble.writeCommand(deviceID, serviceUUID, characteristicUUID, data, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          startNotification: function (deviceID, serviceUUID, characteristicUUID) {
            var q = $q.defer();
            ble.startNotification(deviceID, serviceUUID, characteristicUUID, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          stopNotification: function (deviceID, serviceUUID, characteristicUUID) {
            var q = $q.defer();
            ble.stopNotification(deviceID, serviceUUID, characteristicUUID, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          isConnected: function (deviceID) {
            var q = $q.defer();
            ble.isConnected(deviceID, function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          isEnabled: function () {
            var q = $q.defer();
            ble.isEnabled(function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add https://github.com/don/BluetoothSerial.git
  // link      :     https://github.com/don/BluetoothSerial
  
    angular.module('ngCordova.plugins.bluetoothSerial', [])
  
      .factory('$cordovaBluetoothSerial', ['$q', '$window', function ($q, $window) {
  
        return {
          connect: function (address) {
            var q = $q.defer();
            var disconnectionPromise = $q.defer();
            var isConnected = false;
            $window.bluetoothSerial.connect(address, function () {
              isConnected = true;
              q.resolve(disconnectionPromise);
            }, function (error) {
              if (isConnected === false) {
                disconnectionPromise.reject(error);
              }
              q.reject(error);
            });
            return q.promise;
          },
  
          // not supported on iOS
          connectInsecure: function (address) {
            var q = $q.defer();
            $window.bluetoothSerial.connectInsecure(address, function () {
              q.resolve();
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          disconnect: function () {
            var q = $q.defer();
            $window.bluetoothSerial.disconnect(function () {
              q.resolve();
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          list: function () {
            var q = $q.defer();
            $window.bluetoothSerial.list(function (data) {
              q.resolve(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          discoverUnpaired: function () {
            var q = $q.defer();
            $window.bluetoothSerial.discoverUnpaired(function (data) {
              q.resolve(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          setDeviceDiscoveredListener: function () {
            var q = $q.defer();
            $window.bluetoothSerial.setDeviceDiscoveredListener(function (data) {
              q.notify(data);
            });
            return q.promise;
          },
  
          clearDeviceDiscoveredListener: function () {
            $window.bluetoothSerial.clearDeviceDiscoveredListener();
          },
  
          showBluetoothSettings: function () {
            var q = $q.defer();
            $window.bluetoothSerial.showBluetoothSettings(function () {
              q.resolve();
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          isEnabled: function () {
            var q = $q.defer();
            $window.bluetoothSerial.isEnabled(function () {
              q.resolve();
            }, function () {
              q.reject();
            });
            return q.promise;
          },
  
          enable: function () {
            var q = $q.defer();
            $window.bluetoothSerial.enable(function () {
              q.resolve();
            }, function () {
              q.reject();
            });
            return q.promise;
          },
  
          isConnected: function () {
            var q = $q.defer();
            $window.bluetoothSerial.isConnected(function () {
              q.resolve();
            }, function () {
              q.reject();
            });
            return q.promise;
          },
  
          available: function () {
            var q = $q.defer();
            $window.bluetoothSerial.available(function (data) {
              q.resolve(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          read: function () {
            var q = $q.defer();
            $window.bluetoothSerial.read(function (data) {
              q.resolve(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          readUntil: function (delimiter) {
            var q = $q.defer();
            $window.bluetoothSerial.readUntil(delimiter, function (data) {
              q.resolve(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          write: function (data) {
            var q = $q.defer();
            $window.bluetoothSerial.write(data, function () {
              q.resolve();
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          subscribe: function (delimiter) {
            var q = $q.defer();
            $window.bluetoothSerial.subscribe(delimiter, function (data) {
              q.notify(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          subscribeRawData: function () {
            var q = $q.defer();
            $window.bluetoothSerial.subscribeRawData(function (data) {
              q.notify(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          unsubscribe: function () {
            var q = $q.defer();
            $window.bluetoothSerial.unsubscribe(function () {
              q.resolve();
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          unsubscribeRawData: function () {
            var q = $q.defer();
            $window.bluetoothSerial.unsubscribeRawData(function () {
              q.resolve();
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          clear: function () {
            var q = $q.defer();
            $window.bluetoothSerial.clear(function () {
              q.resolve();
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          readRSSI: function () {
            var q = $q.defer();
            $window.bluetoothSerial.readRSSI(function (data) {
              q.resolve(data);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          }
        };
      }]);
  
  // install  :    cordova plugin add https://github.com/fiscal-cliff/phonegap-plugin-brightness.git
  // link     :    https://github.com/fiscal-cliff/phonegap-plugin-brightness
  
    angular.module('ngCordova.plugins.brightness', [])
  
      .factory('$cordovaBrightness', ['$q', '$window', function ($q, $window) {
  
        return {
          get: function () {
            var q = $q.defer();
  
            if (!$window.cordova) {
              q.reject('Not supported without cordova.js');
            } else {
              $window.cordova.plugins.brightness.getBrightness(function (result) {
                q.resolve(result);
              }, function (err) {
                q.reject(err);
              });
            }
  
            return q.promise;
          },
  
          set: function (data) {
            var q = $q.defer();
  
            if (!$window.cordova) {
              q.reject('Not supported without cordova.js');
            } else {
              $window.cordova.plugins.brightness.setBrightness(data, function (result) {
                q.resolve(result);
              }, function (err) {
                q.reject(err);
              });
            }
  
            return q.promise;
          },
  
          setKeepScreenOn: function (bool) {
            var q = $q.defer();
  
            if (!$window.cordova) {
              q.reject('Not supported without cordova.js');
            } else {
              $window.cordova.plugins.brightness.setKeepScreenOn(bool, function (result) {
                q.resolve(result);
              }, function (err) {
                q.reject(err);
              });
            }
  
            return q.promise;
          }
        };
      }]);
  
  
  // install  :     cordova plugin add https://github.com/EddyVerbruggen/Calendar-PhoneGap-Plugin.git
  // link     :     https://github.com/EddyVerbruggen/Calendar-PhoneGap-Plugin
  
    angular.module('ngCordova.plugins.calendar', [])
  
      .factory('$cordovaCalendar', ['$q', '$window', function ($q, $window) {
  
        return {
          createCalendar: function (options) {
            var d = $q.defer(),
              createCalOptions = $window.plugins.calendar.getCreateCalendarOptions();
  
            if (typeof options === 'string') {
              createCalOptions.calendarName = options;
            } else {
              createCalOptions = angular.extend(createCalOptions, options);
            }
  
            $window.plugins.calendar.createCalendar(createCalOptions, function (message) {
              d.resolve(message);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          deleteCalendar: function (calendarName) {
            var d = $q.defer();
  
            $window.plugins.calendar.deleteCalendar(calendarName, function (message) {
              d.resolve(message);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          createEvent: function (options) {
            var d = $q.defer(),
              defaultOptions = {
                title: null,
                location: null,
                notes: null,
                startDate: null,
                endDate: null
              };
  
            defaultOptions = angular.extend(defaultOptions, options);
  
            $window.plugins.calendar.createEvent(
              defaultOptions.title,
              defaultOptions.location,
              defaultOptions.notes,
              new Date(defaultOptions.startDate),
              new Date(defaultOptions.endDate),
              function (message) {
                d.resolve(message);
              }, function (error) {
                d.reject(error);
              }
            );
  
            return d.promise;
          },
  
          createEventWithOptions: function (options) {
            var d = $q.defer(),
              defaultOptionKeys = [],
              calOptions = window.plugins.calendar.getCalendarOptions(),
              defaultOptions = {
                title: null,
                location: null,
                notes: null,
                startDate: null,
                endDate: null
              };
  
            defaultOptionKeys = Object.keys(defaultOptions);
  
            for (var key in options) {
              if (defaultOptionKeys.indexOf(key) === -1) {
                calOptions[key] = options[key];
              } else {
                defaultOptions[key] = options[key];
              }
            }
  
            $window.plugins.calendar.createEventWithOptions(
              defaultOptions.title,
              defaultOptions.location,
              defaultOptions.notes,
              new Date(defaultOptions.startDate),
              new Date(defaultOptions.endDate),
              calOptions,
              function (message) {
                d.resolve(message);
              }, function (error) {
                d.reject(error);
              }
            );
  
            return d.promise;
          },
  
          createEventInteractively: function (options) {
            var d = $q.defer(),
              defaultOptions = {
                title: null,
                location: null,
                notes: null,
                startDate: null,
                endDate: null
              };
  
            defaultOptions = angular.extend(defaultOptions, options);
  
            $window.plugins.calendar.createEventInteractively(
              defaultOptions.title,
              defaultOptions.location,
              defaultOptions.notes,
              new Date(defaultOptions.startDate),
              new Date(defaultOptions.endDate),
              function (message) {
                d.resolve(message);
              }, function (error) {
                d.reject(error);
              }
            );
  
            return d.promise;
          },
  
          createEventInNamedCalendar: function (options) {
            var d = $q.defer(),
              defaultOptions = {
                title: null,
                location: null,
                notes: null,
                startDate: null,
                endDate: null,
                calendarName: null
              };
  
            defaultOptions = angular.extend(defaultOptions, options);
  
            $window.plugins.calendar.createEventInNamedCalendar(
              defaultOptions.title,
              defaultOptions.location,
              defaultOptions.notes,
              new Date(defaultOptions.startDate),
              new Date(defaultOptions.endDate),
              defaultOptions.calendarName,
              function (message) {
                d.resolve(message);
              }, function (error) {
                d.reject(error);
              }
            );
  
            return d.promise;
          },
  
          findEvent: function (options) {
            var d = $q.defer(),
              defaultOptions = {
                title: null,
                location: null,
                notes: null,
                startDate: null,
                endDate: null
              };
  
            defaultOptions = angular.extend(defaultOptions, options);
  
            $window.plugins.calendar.findEvent(
              defaultOptions.title,
              defaultOptions.location,
              defaultOptions.notes,
              new Date(defaultOptions.startDate),
              new Date(defaultOptions.endDate),
              function (foundEvent) {
                d.resolve(foundEvent);
              }, function (error) {
                d.reject(error);
              }
            );
  
            return d.promise;
          },
  
          listEventsInRange: function (startDate, endDate) {
            var d = $q.defer();
  
            $window.plugins.calendar.listEventsInRange(startDate, endDate, function (events) {
              d.resolve(events);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          listCalendars: function () {
            var d = $q.defer();
  
            $window.plugins.calendar.listCalendars(function (calendars) {
              d.resolve(calendars);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          findAllEventsInNamedCalendar: function (calendarName) {
            var d = $q.defer();
  
            $window.plugins.calendar.findAllEventsInNamedCalendar(calendarName, function (events) {
              d.resolve(events);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          modifyEvent: function (options) {
            var d = $q.defer(),
              defaultOptions = {
                title: null,
                location: null,
                notes: null,
                startDate: null,
                endDate: null,
                newTitle: null,
                newLocation: null,
                newNotes: null,
                newStartDate: null,
                newEndDate: null
              };
  
            defaultOptions = angular.extend(defaultOptions, options);
  
            $window.plugins.calendar.modifyEvent(
              defaultOptions.title,
              defaultOptions.location,
              defaultOptions.notes,
              new Date(defaultOptions.startDate),
              new Date(defaultOptions.endDate),
              defaultOptions.newTitle,
              defaultOptions.newLocation,
              defaultOptions.newNotes,
              new Date(defaultOptions.newStartDate),
              new Date(defaultOptions.newEndDate),
              function (message) {
                d.resolve(message);
              }, function (error) {
                d.reject(error);
              }
            );
  
            return d.promise;
          },
  
          deleteEvent: function (options) {
            var d = $q.defer(),
              defaultOptions = {
                newTitle: null,
                location: null,
                notes: null,
                startDate: null,
                endDate: null
              };
  
            defaultOptions = angular.extend(defaultOptions, options);
  
            $window.plugins.calendar.deleteEvent(
              defaultOptions.newTitle,
              defaultOptions.location,
              defaultOptions.notes,
              new Date(defaultOptions.startDate),
              new Date(defaultOptions.endDate),
              function (message) {
                d.resolve(message);
              }, function (error) {
                d.reject(error);
              }
            );
  
            return d.promise;
          }
        };
      }]);
  
  // install   :   cordova plugin add cordova-plugin-camera
  // link      :   https://github.com/apache/cordova-plugin-camera
  
    angular.module('ngCordova.plugins.camera', [])
  
      .factory('$cordovaCamera', ['$q', function ($q) {
  
        return {
          getPicture: function (options) {
            var q = $q.defer();
  
            if (!navigator.camera) {
              q.resolve(null);
              return q.promise;
            }
  
            navigator.camera.getPicture(function (imageData) {
              q.resolve(imageData);
            }, function (err) {
              q.reject(err);
            }, options);
  
            return q.promise;
          },
  
          cleanup: function () {
            var q = $q.defer();
  
            navigator.camera.cleanup(function () {
              q.resolve();
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          }
        };
      }]);
  
  // install   :    cordova plugin add cordova-plugin-media-capture
  // link      :    https://github.com/apache/cordova-plugin-media-capture
  
    angular.module('ngCordova.plugins.capture', [])
  
      .factory('$cordovaCapture', ['$q', function ($q) {
  
        return {
          captureAudio: function (options) {
            var q = $q.defer();
  
            if (!navigator.device.capture) {
              q.resolve(null);
              return q.promise;
            }
  
            navigator.device.capture.captureAudio(function (audioData) {
              q.resolve(audioData);
            }, function (err) {
              q.reject(err);
            }, options);
  
            return q.promise;
          },
          captureImage: function (options) {
            var q = $q.defer();
  
            if (!navigator.device.capture) {
              q.resolve(null);
              return q.promise;
            }
  
            navigator.device.capture.captureImage(function (imageData) {
              q.resolve(imageData);
            }, function (err) {
              q.reject(err);
            }, options);
  
            return q.promise;
          },
          captureVideo: function (options) {
            var q = $q.defer();
  
            if (!navigator.device.capture) {
              q.resolve(null);
              return q.promise;
            }
  
            navigator.device.capture.captureVideo(function (videoData) {
              q.resolve(videoData);
            }, function (err) {
              q.reject(err);
            }, options);
  
            return q.promise;
          }
        };
      }]);
  
  // install : cordova plugin add https://github.com/vkeepe/card.io.git
  // link    : https://github.com/vkeepe/card.io.git
  
    /* globals CardIO: true */
    angular.module('ngCordova.plugins.cardIO', [])
  
      .provider(
      '$cordovaNgCardIO', [function () {
  
        /**
         * Default array of response data from cardIO scan card
         */
        var defaultRespFields = [
          'card_type',
          'redacted_card_number',
          'card_number',
          'expiry_month',
          'expiry_year',
          'short_expiry_year',
          'cvv',
          'zip'
        ];
  
        /**
         * Default config for cardIO scan function
         */
        var defaultScanConfig = {
          'expiry': true,
          'cvv': true,
          'zip': false,
          'suppressManual': false,
          'suppressConfirm': false,
          'hideLogo': true
        };
  
        /**
         * Configuring defaultRespFields using $cordovaNgCardIOProvider
         *
         */
        this.setCardIOResponseFields = function (filelds) {
          if (!filelds || !angular.isArray(filelds)) {
            return;
          }
          defaultRespFields = filelds;
        };
  
        /**
         *
         * Configuring defaultScanConfig using $cordovaNgCardIOProvider
         */
        this.setScanerConfig = function (config) {
          if (!config || !angular.isObject(config)) {
            return;
          }
  
          defaultScanConfig.expiry = config.expiry || true;
          defaultScanConfig.cvv = config.cvv || true;
          defaultScanConfig.zip = config.zip || false;
          defaultScanConfig.suppressManual = config.suppressManual || false;
          defaultScanConfig.suppressConfirm = config.suppressConfirm || false;
          defaultScanConfig.hideLogo = config.hideLogo || true;
        };
  
        /**
         * Function scanCard for $cordovaNgCardIO service to make scan of card
         *
         */
        this.$get = ['$q', function ($q) {
          return {
            scanCard: function () {
  
              var deferred = $q.defer();
              CardIO.scan(
                defaultScanConfig,
                function (response) {
  
                  if (response === null) {
                    deferred.reject(null);
                  } else {
  
                    var respData = {};
                    for (
                      var i = 0, len = defaultRespFields.length; i < len; i++) {
                      var field = defaultRespFields[i];
  
                      if (field === 'short_expiry_year') {
                        respData[field] = String(response.expiry_year).substr( // jshint ignore:line
                            2, 2
                          ) || '';
                      } else {
                        respData[field] = response[field] || '';
                      }
                    }
                    deferred.resolve(respData);
                  }
                },
                function () {
                  deferred.reject(null);
                }
              );
              return deferred.promise;
            }
          };
        }];
      }]
    );
  
  // install   :     cordova plugin add https://github.com/VersoSolutions/CordovaClipboard.git
  // link      :     https://github.com/VersoSolutions/CordovaClipboard
  
    angular.module('ngCordova.plugins.clipboard', [])
  
      .factory('$cordovaClipboard', ['$q', '$window', function ($q, $window) {
  
        return {
          copy: function (text) {
            var q = $q.defer();
  
            $window.cordova.plugins.clipboard.copy(text,
              function () {
                q.resolve();
              }, function () {
                q.reject();
              });
  
            return q.promise;
          },
  
          paste: function () {
            var q = $q.defer();
  
            $window.cordova.plugins.clipboard.paste(function (text) {
              q.resolve(text);
            }, function () {
              q.reject();
            });
  
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-contacts
  // link      :     https://github.com/apache/cordova-plugin-contacts
  
    angular.module('ngCordova.plugins.contacts', [])
  
      .factory('$cordovaContacts', ['$q', function ($q) {
  
        return {
          save: function (contact) {
            var q = $q.defer();
            var deviceContact = navigator.contacts.create(contact);
  
            deviceContact.save(function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
            return q.promise;
          },
  
          remove: function (contact) {
            var q = $q.defer();
            var deviceContact = navigator.contacts.create(contact);
  
            deviceContact.remove(function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
            return q.promise;
          },
  
          clone: function (contact) {
            var deviceContact = navigator.contacts.create(contact);
            return deviceContact.clone(contact);
          },
  
          find: function (options) {
            var q = $q.defer();
            var fields = options.fields || ['id', 'displayName'];
            delete options.fields;
            if (Object.keys(options).length === 0) {
              navigator.contacts.find(fields, function (results) {
                q.resolve(results);
              }, function (err) {
                q.reject(err);
              });
            }
            else {
              navigator.contacts.find(fields, function (results) {
                q.resolve(results);
              }, function (err) {
                q.reject(err);
              }, options);
            }
            return q.promise;
          },
  
          pickContact: function () {
            var q = $q.defer();
  
            navigator.contacts.pickContact(function (contact) {
              q.resolve(contact);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          }
  
          // TODO: method to set / get ContactAddress
          // TODO: method to set / get ContactError
          // TODO: method to set / get ContactField
          // TODO: method to set / get ContactName
          // TODO: method to set / get ContactOrganization
        };
      }]);
  
  // install   :      cordova plugin add https://github.com/VitaliiBlagodir/cordova-plugin-datepicker.git
  // link      :      https://github.com/VitaliiBlagodir/cordova-plugin-datepicker
  
    angular.module('ngCordova.plugins.datePicker', [])
  
      .factory('$cordovaDatePicker', ['$window', '$q', function ($window, $q) {
  
        return {
          show: function (options) {
            var q = $q.defer();
            options = options || {date: new Date(), mode: 'date'};
            $window.datePicker.show(options, function (date) {
              q.resolve(date);
            });
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-device
  // link      :     https://github.com/apache/cordova-plugin-device
  
    /* globals device: true */
    angular.module('ngCordova.plugins.device', [])
  
      .factory('$cordovaDevice', [function () {
  
        return {
          /**
           * Returns the whole device object.
           * @see https://github.com/apache/cordova-plugin-device
           * @returns {Object} The device object.
           */
          getDevice: function () {
            return device;
          },
  
          /**
           * Returns the Cordova version.
           * @see https://github.com/apache/cordova-plugin-device#devicecordova
           * @returns {String} The Cordova version.
           */
          getCordova: function () {
            return device.cordova;
          },
  
          /**
           * Returns the name of the device's model or product.
           * @see https://github.com/apache/cordova-plugin-device#devicemodel
           * @returns {String} The name of the device's model or product.
           */
          getModel: function () {
            return device.model;
          },
  
          /**
           * @deprecated device.name is deprecated as of version 2.3.0. Use device.model instead.
           * @returns {String}
           */
          getName: function () {
            return device.name;
          },
  
          /**
           * Returns the device's operating system name.
           * @see https://github.com/apache/cordova-plugin-device#deviceplatform
           * @returns {String} The device's operating system name.
           */
          getPlatform: function () {
            return device.platform;
          },
  
          /**
           * Returns the device's Universally Unique Identifier.
           * @see https://github.com/apache/cordova-plugin-device#deviceuuid
           * @returns {String} The device's Universally Unique Identifier
           */
          getUUID: function () {
            return device.uuid;
          },
  
          /**
           * Returns the operating system version.
           * @see https://github.com/apache/cordova-plugin-device#deviceversion
           * @returns {String}
           */
          getVersion: function () {
            return device.version;
          },
  
          /**
           * Returns the device manufacturer.
           * @returns {String}
           */
          getManufacturer: function () {
            return device.manufacturer;
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-device-motion
  // link      :     https://github.com/apache/cordova-plugin-device-motion
  
    angular.module('ngCordova.plugins.deviceMotion', [])
  
      .factory('$cordovaDeviceMotion', ['$q', function ($q) {
  
        return {
          getCurrentAcceleration: function () {
            var q = $q.defer();
  
            if (angular.isUndefined(navigator.accelerometer) || !angular.isFunction(navigator.accelerometer.getCurrentAcceleration)) {
              q.reject('Device do not support watchAcceleration');
            }
  
            navigator.accelerometer.getCurrentAcceleration(function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          watchAcceleration: function (options) {
            var q = $q.defer();
  
            if (angular.isUndefined(navigator.accelerometer) || !angular.isFunction(navigator.accelerometer.watchAcceleration)) {
              q.reject('Device do not support watchAcceleration');
            }
  
            var watchID = navigator.accelerometer.watchAcceleration(function (result) {
              q.notify(result);
            }, function (err) {
              q.reject(err);
            }, options);
  
            q.promise.cancel = function () {
              navigator.accelerometer.clearWatch(watchID);
            };
  
            q.promise.clearWatch = function (id) {
              navigator.accelerometer.clearWatch(id || watchID);
            };
  
            q.promise.watchID = watchID;
  
            return q.promise;
          },
  
          clearWatch: function (watchID) {
            return navigator.accelerometer.clearWatch(watchID);
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-device-orientation
  // link      :     https://github.com/apache/cordova-plugin-device-orientation
  
    angular.module('ngCordova.plugins.deviceOrientation', [])
  
      .factory('$cordovaDeviceOrientation', ['$q', function ($q) {
  
        var defaultOptions = {
          frequency: 3000 // every 3s
        };
  
        return {
          getCurrentHeading: function () {
            var q = $q.defer();
  
            if (!navigator.compass) {
              q.reject('No compass on Device');
              return q.promise;
            }
  
            navigator.compass.getCurrentHeading(function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          watchHeading: function (options) {
            var q = $q.defer();
  
            if (!navigator.compass) {
              q.reject('No compass on Device');
              return q.promise;
            }
  
            var _options = angular.extend(defaultOptions, options);
            var watchID = navigator.compass.watchHeading(function (result) {
              q.notify(result);
            }, function (err) {
              q.reject(err);
            }, _options);
  
            q.promise.cancel = function () {
              navigator.compass.clearWatch(watchID);
            };
  
            q.promise.clearWatch = function (id) {
              navigator.compass.clearWatch(id || watchID);
            };
  
            q.promise.watchID = watchID;
  
            return q.promise;
          },
  
          clearWatch: function (watchID) {
            return navigator.compass.clearWatch(watchID);
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-dialogs
  // link      :     https://github.com/apache/cordova-plugin-dialogs
  
    angular.module('ngCordova.plugins.dialogs', [])
  
      .factory('$cordovaDialogs', ['$q', '$window', function ($q, $window) {
  
        return {
          alert: function (message, title, buttonName) {
            var q = $q.defer();
  
            if (!$window.navigator.notification) {
              $window.alert(message);
              q.resolve();
            } else {
              navigator.notification.alert(message, function () {
                q.resolve();
              }, title, buttonName);
            }
  
            return q.promise;
          },
  
          confirm: function (message, title, buttonLabels) {
            var q = $q.defer();
  
            if (!$window.navigator.notification) {
              if ($window.confirm(message)) {
                q.resolve(1);
              } else {
                q.resolve(2);
              }
            } else {
              navigator.notification.confirm(message, function (buttonIndex) {
                q.resolve(buttonIndex);
              }, title, buttonLabels);
            }
  
            return q.promise;
          },
  
          prompt: function (message, title, buttonLabels, defaultText) {
            var q = $q.defer();
  
            if (!$window.navigator.notification) {
              var res = $window.prompt(message, defaultText);
              if (res !== null) {
                q.resolve({input1: res, buttonIndex: 1});
              } else {
                q.resolve({input1: res, buttonIndex: 2});
              }
            } else {
              navigator.notification.prompt(message, function (result) {
                q.resolve(result);
              }, title, buttonLabels, defaultText);
            }
            return q.promise;
          },
  
          beep: function (times) {
            return navigator.notification.beep(times);
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/katzer/cordova-plugin-email-composer.git
  // link     :     https://github.com/katzer/cordova-plugin-email-composer
  
    angular.module('ngCordova.plugins.emailComposer', [])
  
      .factory('$cordovaEmailComposer', ['$q', function ($q) {
  
        return {
          isAvailable: function () {
            var q = $q.defer();
  
            cordova.plugins.email.isAvailable(function (isAvailable) {
              if (isAvailable) {
                q.resolve();
              } else {
                q.reject();
              }
            });
  
            return q.promise;
          },
  
          open: function (properties) {
            var q = $q.defer();
  
            cordova.plugins.email.open(properties, function () {
              q.reject(); // user closed email composer
            });
  
            return q.promise;
          },
  
          addAlias: function (app, schema) {
            cordova.plugins.email.addAlias(app, schema);
          }
        };
      }]);
  
  // install   :   cordova -d plugin add https://github.com/Wizcorp/phonegap-facebook-plugin.git --variable APP_ID="123456789" --variable APP_NAME="myApplication"
  // link      :   https://github.com/Wizcorp/phonegap-facebook-plugin
  
    /* globals facebookConnectPlugin: true */
    angular.module('ngCordova.plugins.facebook', [])
  
      .provider('$cordovaFacebook', [function () {
  
        /**
         * Init browser settings for Facebook plugin
         *
         * @param {number} id
         * @param {string} version
         */
        this.browserInit = function (id, version) {
          this.appID = id;
          this.appVersion = version || 'v2.0';
          facebookConnectPlugin.browserInit(this.appID, this.appVersion);
        };
  
        this.$get = ['$q', function ($q) {
          return {
            login: function (permissions) {
              var q = $q.defer();
              facebookConnectPlugin.login(permissions, function (res) {
                q.resolve(res);
              }, function (res) {
                q.reject(res);
              });
  
              return q.promise;
            },
  
            showDialog: function (options) {
              var q = $q.defer();
              facebookConnectPlugin.showDialog(options, function (res) {
                q.resolve(res);
              }, function (err) {
                q.reject(err);
              });
              return q.promise;
            },
  
            api: function (path, permissions) {
              var q = $q.defer();
              facebookConnectPlugin.api(path, permissions, function (res) {
                q.resolve(res);
              }, function (err) {
                q.reject(err);
              });
              return q.promise;
            },
  
            getAccessToken: function () {
              var q = $q.defer();
              facebookConnectPlugin.getAccessToken(function (res) {
                q.resolve(res);
              }, function (err) {
                q.reject(err);
              });
              return q.promise;
            },
  
            getLoginStatus: function () {
              var q = $q.defer();
              facebookConnectPlugin.getLoginStatus(function (res) {
                q.resolve(res);
              }, function (err) {
                q.reject(err);
              });
              return q.promise;
            },
  
            logout: function () {
              var q = $q.defer();
              facebookConnectPlugin.logout(function (res) {
                q.resolve(res);
              }, function (err) {
                q.reject(err);
              });
              return q.promise;
            }
          };
        }];
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-plugin-facebookads.git
  // link     :     https://github.com/floatinghotpot/cordova-plugin-facebookads
  
    angular.module('ngCordova.plugins.facebookAds', [])
  
      .factory('$cordovaFacebookAds', ['$q', '$window', function ($q, $window) {
  
        return {
          setOptions: function (options) {
            var d = $q.defer();
  
            $window.FacebookAds.setOptions(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createBanner: function (options) {
            var d = $q.defer();
  
            $window.FacebookAds.createBanner(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          removeBanner: function () {
            var d = $q.defer();
  
            $window.FacebookAds.removeBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBanner: function (position) {
            var d = $q.defer();
  
            $window.FacebookAds.showBanner(position, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBannerAtXY: function (x, y) {
            var d = $q.defer();
  
            $window.FacebookAds.showBannerAtXY(x, y, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          hideBanner: function () {
            var d = $q.defer();
  
            $window.FacebookAds.hideBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          prepareInterstitial: function (options) {
            var d = $q.defer();
  
            $window.FacebookAds.prepareInterstitial(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showInterstitial: function () {
            var d = $q.defer();
  
            $window.FacebookAds.showInterstitial(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-file
  // link      :     https://github.com/apache/cordova-plugin-file
  
    angular.module('ngCordova.plugins.file', [])
  
      .constant('$cordovaFileError', {
        1: 'NOT_FOUND_ERR',
        2: 'SECURITY_ERR',
        3: 'ABORT_ERR',
        4: 'NOT_READABLE_ERR',
        5: 'ENCODING_ERR',
        6: 'NO_MODIFICATION_ALLOWED_ERR',
        7: 'INVALID_STATE_ERR',
        8: 'SYNTAX_ERR',
        9: 'INVALID_MODIFICATION_ERR',
        10: 'QUOTA_EXCEEDED_ERR',
        11: 'TYPE_MISMATCH_ERR',
        12: 'PATH_EXISTS_ERR'
      })
  
      .provider('$cordovaFile', [function () {
  
        this.$get = ['$q', '$window', '$cordovaFileError', function ($q, $window, $cordovaFileError) {
  
          return {
  
            getFreeDiskSpace: function () {
              var q = $q.defer();
              cordova.exec(function (result) {
                q.resolve(result);
              }, function (error) {
                q.reject(error);
              }, 'File', 'getFreeDiskSpace', []);
              return q.promise;
            },
  
            checkDir: function (path, dir) {
              var q = $q.defer();
  
              if ((/^\//.test(dir))) {
                q.reject('directory cannot start with \/');
              }
  
              try {
                var directory = path + dir;
                $window.resolveLocalFileSystemURL(directory, function (fileSystem) {
                  if (fileSystem.isDirectory === true) {
                    q.resolve(fileSystem);
                  } else {
                    q.reject({code: 13, message: 'input is not a directory'});
                  }
                }, function (error) {
                  error.message = $cordovaFileError[error.code];
                  q.reject(error);
                });
              } catch (err) {
                err.message = $cordovaFileError[err.code];
                q.reject(err);
              }
  
              return q.promise;
            },
  
            checkFile: function (path, file) {
              var q = $q.defer();
  
              if ((/^\//.test(file))) {
                q.reject('directory cannot start with \/');
              }
  
              try {
                var directory = path + file;
                $window.resolveLocalFileSystemURL(directory, function (fileSystem) {
                  if (fileSystem.isFile === true) {
                    q.resolve(fileSystem);
                  } else {
                    q.reject({code: 13, message: 'input is not a file'});
                  }
                }, function (error) {
                  error.message = $cordovaFileError[error.code];
                  q.reject(error);
                });
              } catch (err) {
                err.message = $cordovaFileError[err.code];
                q.reject(err);
              }
  
              return q.promise;
            },
  
            createDir: function (path, dirName, replaceBool) {
              var q = $q.defer();
  
              if ((/^\//.test(dirName))) {
                q.reject('directory cannot start with \/');
              }
  
              replaceBool = replaceBool ? false : true;
  
              var options = {
                create: true,
                exclusive: replaceBool
              };
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getDirectory(dirName, options, function (result) {
                    q.resolve(result);
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
  
              return q.promise;
            },
  
            createFile: function (path, fileName, replaceBool) {
              var q = $q.defer();
  
              if ((/^\//.test(fileName))) {
                q.reject('file-name cannot start with \/');
              }
  
              replaceBool = replaceBool ? false : true;
  
              var options = {
                create: true,
                exclusive: replaceBool
              };
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(fileName, options, function (result) {
                    q.resolve(result);
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
              return q.promise;
            },
  
            removeDir: function (path, dirName) {
              var q = $q.defer();
  
              if ((/^\//.test(dirName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getDirectory(dirName, {create: false}, function (dirEntry) {
                    dirEntry.remove(function () {
                      q.resolve({success: true, fileRemoved: dirEntry});
                    }, function (error) {
                      error.message = $cordovaFileError[error.code];
                      q.reject(error);
                    });
                  }, function (err) {
                    err.message = $cordovaFileError[err.code];
                    q.reject(err);
                  });
                }, function (er) {
                  er.message = $cordovaFileError[er.code];
                  q.reject(er);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
              return q.promise;
            },
  
            removeFile: function (path, fileName) {
              var q = $q.defer();
  
              if ((/^\//.test(fileName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(fileName, {create: false}, function (fileEntry) {
                    fileEntry.remove(function () {
                      q.resolve({success: true, fileRemoved: fileEntry});
                    }, function (error) {
                      error.message = $cordovaFileError[error.code];
                      q.reject(error);
                    });
                  }, function (err) {
                    err.message = $cordovaFileError[err.code];
                    q.reject(err);
                  });
                }, function (er) {
                  er.message = $cordovaFileError[er.code];
                  q.reject(er);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
              return q.promise;
            },
  
            removeRecursively: function (path, dirName) {
              var q = $q.defer();
  
              if ((/^\//.test(dirName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getDirectory(dirName, {create: false}, function (dirEntry) {
                    dirEntry.removeRecursively(function () {
                      q.resolve({success: true, fileRemoved: dirEntry});
                    }, function (error) {
                      error.message = $cordovaFileError[error.code];
                      q.reject(error);
                    });
                  }, function (err) {
                    err.message = $cordovaFileError[err.code];
                    q.reject(err);
                  });
                }, function (er) {
                  er.message = $cordovaFileError[er.code];
                  q.reject(er);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
              return q.promise;
            },
  
            writeFile: function (path, fileName, text, replaceBool) {
              var q = $q.defer();
  
              if ((/^\//.test(fileName))) {
                q.reject('file-name cannot start with \/');
              }
  
              replaceBool = replaceBool ? false : true;
  
              var options = {
                create: true,
                exclusive: replaceBool
              };
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(fileName, options, function (fileEntry) {
                    fileEntry.createWriter(function (writer) {
                      if (options.append === true) {
                        writer.seek(writer.length);
                      }
  
                      if (options.truncate) {
                        writer.truncate(options.truncate);
                      }
  
                      writer.onwriteend = function (evt) {
                        if (this.error) {
                          q.reject(this.error);
                        } else {
                          q.resolve(evt);
                        }
                      };
  
                      writer.write(text);
  
                      q.promise.abort = function () {
                        writer.abort();
                      };
                    });
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
  
              return q.promise;
            },
  
            writeExistingFile: function (path, fileName, text) {
              var q = $q.defer();
  
              if ((/^\//.test(fileName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(fileName, {create: false}, function (fileEntry) {
                    fileEntry.createWriter(function (writer) {
                      writer.seek(writer.length);
  
                      writer.onwriteend = function (evt) {
                        if (this.error) {
                          q.reject(this.error);
                        } else {
                          q.resolve(evt);
                        }
                      };
  
                      writer.write(text);
  
                      q.promise.abort = function () {
                        writer.abort();
                      };
                    });
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
  
              return q.promise;
            },
  
            readAsText: function (path, file) {
              var q = $q.defer();
  
              if ((/^\//.test(file))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(file, {create: false}, function (fileEntry) {
                    fileEntry.file(function (fileData) {
                      var reader = new FileReader();
  
                      reader.onloadend = function (evt) {
                        if (evt.target.result !== undefined || evt.target.result !== null) {
                          q.resolve(evt.target.result);
                        } else if (evt.target.error !== undefined || evt.target.error !== null) {
                          q.reject(evt.target.error);
                        } else {
                          q.reject({code: null, message: 'READER_ONLOADEND_ERR'});
                        }
                      };
  
                      reader.readAsText(fileData);
                    });
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
  
              return q.promise;
            },
  
            readAsDataURL: function (path, file) {
              var q = $q.defer();
  
              if ((/^\//.test(file))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(file, {create: false}, function (fileEntry) {
                    fileEntry.file(function (fileData) {
                      var reader = new FileReader();
                      reader.onloadend = function (evt) {
                        if (evt.target.result !== undefined || evt.target.result !== null) {
                          q.resolve(evt.target.result);
                        } else if (evt.target.error !== undefined || evt.target.error !== null) {
                          q.reject(evt.target.error);
                        } else {
                          q.reject({code: null, message: 'READER_ONLOADEND_ERR'});
                        }
                      };
                      reader.readAsDataURL(fileData);
                    });
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
  
              return q.promise;
            },
  
            readAsBinaryString: function (path, file) {
              var q = $q.defer();
  
              if ((/^\//.test(file))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(file, {create: false}, function (fileEntry) {
                    fileEntry.file(function (fileData) {
                      var reader = new FileReader();
                      reader.onloadend = function (evt) {
                        if (evt.target.result !== undefined || evt.target.result !== null) {
                          q.resolve(evt.target.result);
                        } else if (evt.target.error !== undefined || evt.target.error !== null) {
                          q.reject(evt.target.error);
                        } else {
                          q.reject({code: null, message: 'READER_ONLOADEND_ERR'});
                        }
                      };
                      reader.readAsBinaryString(fileData);
                    });
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
  
              return q.promise;
            },
  
            readAsArrayBuffer: function (path, file) {
              var q = $q.defer();
  
              if ((/^\//.test(file))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(file, {create: false}, function (fileEntry) {
                    fileEntry.file(function (fileData) {
                      var reader = new FileReader();
                      reader.onloadend = function (evt) {
                        if (evt.target.result !== undefined || evt.target.result !== null) {
                          q.resolve(evt.target.result);
                        } else if (evt.target.error !== undefined || evt.target.error !== null) {
                          q.reject(evt.target.error);
                        } else {
                          q.reject({code: null, message: 'READER_ONLOADEND_ERR'});
                        }
                      };
                      reader.readAsArrayBuffer(fileData);
                    });
                  }, function (error) {
                    error.message = $cordovaFileError[error.code];
                    q.reject(error);
                  });
                }, function (err) {
                  err.message = $cordovaFileError[err.code];
                  q.reject(err);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
  
              return q.promise;
            },
  
            moveFile: function (path, fileName, newPath, newFileName) {
              var q = $q.defer();
  
              newFileName = newFileName || fileName;
  
              if ((/^\//.test(fileName)) || (/^\//.test(newFileName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(fileName, {create: false}, function (fileEntry) {
                    $window.resolveLocalFileSystemURL(newPath, function (newFileEntry) {
                      fileEntry.moveTo(newFileEntry, newFileName, function (result) {
                        q.resolve(result);
                      }, function (error) {
                        q.reject(error);
                      });
                    }, function (err) {
                      q.reject(err);
                    });
                  }, function (err) {
                    q.reject(err);
                  });
                }, function (er) {
                  q.reject(er);
                });
              } catch (e) {
                q.reject(e);
              }
              return q.promise;
            },
  
            moveDir: function (path, dirName, newPath, newDirName) {
              var q = $q.defer();
  
              newDirName = newDirName || dirName;
  
              if (/^\//.test(dirName) || (/^\//.test(newDirName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getDirectory(dirName, {create: false}, function (dirEntry) {
                    $window.resolveLocalFileSystemURL(newPath, function (newDirEntry) {
                      dirEntry.moveTo(newDirEntry, newDirName, function (result) {
                        q.resolve(result);
                      }, function (error) {
                        q.reject(error);
                      });
                    }, function (erro) {
                      q.reject(erro);
                    });
                  }, function (err) {
                    q.reject(err);
                  });
                }, function (er) {
                  q.reject(er);
                });
              } catch (e) {
                q.reject(e);
              }
              return q.promise;
            },
  
            copyDir: function (path, dirName, newPath, newDirName) {
              var q = $q.defer();
  
              newDirName = newDirName || dirName;
  
              if (/^\//.test(dirName) || (/^\//.test(newDirName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getDirectory(dirName, {create: false, exclusive: false}, function (dirEntry) {
  
                    $window.resolveLocalFileSystemURL(newPath, function (newDirEntry) {
                      dirEntry.copyTo(newDirEntry, newDirName, function (result) {
                        q.resolve(result);
                      }, function (error) {
                        error.message = $cordovaFileError[error.code];
                        q.reject(error);
                      });
                    }, function (erro) {
                      erro.message = $cordovaFileError[erro.code];
                      q.reject(erro);
                    });
                  }, function (err) {
                    err.message = $cordovaFileError[err.code];
                    q.reject(err);
                  });
                }, function (er) {
                  er.message = $cordovaFileError[er.code];
                  q.reject(er);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
              return q.promise;
            },
  
            copyFile: function (path, fileName, newPath, newFileName) {
              var q = $q.defer();
  
              newFileName = newFileName || fileName;
  
              if ((/^\//.test(fileName))) {
                q.reject('file-name cannot start with \/');
              }
  
              try {
                $window.resolveLocalFileSystemURL(path, function (fileSystem) {
                  fileSystem.getFile(fileName, {create: false, exclusive: false}, function (fileEntry) {
  
                    $window.resolveLocalFileSystemURL(newPath, function (newFileEntry) {
                      fileEntry.copyTo(newFileEntry, newFileName, function (result) {
                        q.resolve(result);
                      }, function (error) {
                        error.message = $cordovaFileError[error.code];
                        q.reject(error);
                      });
                    }, function (erro) {
                      erro.message = $cordovaFileError[erro.code];
                      q.reject(erro);
                    });
                  }, function (err) {
                    err.message = $cordovaFileError[err.code];
                    q.reject(err);
                  });
                }, function (er) {
                  er.message = $cordovaFileError[er.code];
                  q.reject(er);
                });
              } catch (e) {
                e.message = $cordovaFileError[e.code];
                q.reject(e);
              }
              return q.promise;
            }
  
            /*
             listFiles: function (path, dir) {
  
             },
  
             listDir: function (path, dirName) {
             var q = $q.defer();
  
             try {
             $window.resolveLocalFileSystemURL(path, function (fileSystem) {
             fileSystem.getDirectory(dirName, options, function (parent) {
             var reader = parent.createReader();
             reader.readEntries(function (entries) {
             q.resolve(entries);
             }, function () {
             q.reject('DIR_READ_ERROR : ' + path + dirName);
             });
             }, function (error) {
             error.message = $cordovaFileError[error.code];
             q.reject(error);
             });
             }, function (err) {
             err.message = $cordovaFileError[err.code];
             q.reject(err);
             });
             } catch (e) {
             e.message = $cordovaFileError[e.code];
             q.reject(e);
             }
  
             return q.promise;
             },
  
             readFileMetadata: function (filePath) {
             //return getFile(filePath, {create: false});
             }
             */
          };
  
        }];
      }]);
  
  // install   :      cordova plugin add https://github.com/pwlin/cordova-plugin-file-opener2.git
  // link      :      https://github.com/pwlin/cordova-plugin-file-opener2
  
    angular.module('ngCordova.plugins.fileOpener2', [])
  
      .factory('$cordovaFileOpener2', ['$q', function ($q) {
  
        return {
          open: function (file, type) {
            var q = $q.defer();
            cordova.plugins.fileOpener2.open(file, type, {
              error: function (e) {
                q.reject(e);
              }, success: function () {
                q.resolve();
              }
            });
            return q.promise;
          },
  
          uninstall: function (pack) {
            var q = $q.defer();
            cordova.plugins.fileOpener2.uninstall(pack, {
              error: function (e) {
                q.reject(e);
              }, success: function () {
                q.resolve();
              }
            });
            return q.promise;
          },
  
          appIsInstalled: function (pack) {
            var q = $q.defer();
            cordova.plugins.fileOpener2.appIsInstalled(pack, {
              success: function (res) {
                q.resolve(res);
              }
            });
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-file-transfer
  // link      :     https://github.com/apache/cordova-plugin-file-transfer
  
    /* globals FileTransfer: true */
    angular.module('ngCordova.plugins.fileTransfer', [])
  
      .factory('$cordovaFileTransfer', ['$q', '$timeout', function ($q, $timeout) {
        return {
          download: function (source, filePath, options, trustAllHosts) {
            var q = $q.defer();
            var ft = new FileTransfer();
            var uri = (options && options.encodeURI === false) ? source : encodeURI(source);
  
            if (options && options.timeout !== undefined && options.timeout !== null) {
              $timeout(function () {
                ft.abort();
              }, options.timeout);
              options.timeout = null;
            }
  
            ft.onprogress = function (progress) {
              q.notify(progress);
            };
  
            q.promise.abort = function () {
              ft.abort();
            };
  
            ft.download(uri, filePath, q.resolve, q.reject, trustAllHosts, options);
            return q.promise;
          },
  
          upload: function (server, filePath, options, trustAllHosts) {
            var q = $q.defer();
            var ft = new FileTransfer();
            var uri = (options && options.encodeURI === false) ? server : encodeURI(server);
  
            if (options && options.timeout !== undefined && options.timeout !== null) {
              $timeout(function () {
                ft.abort();
              }, options.timeout);
              options.timeout = null;
            }
  
            ft.onprogress = function (progress) {
              q.notify(progress);
            };
  
            q.promise.abort = function () {
              ft.abort();
            };
  
            ft.upload(filePath, uri, q.resolve, q.reject, options, trustAllHosts);
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add https://github.com/EddyVerbruggen/Flashlight-PhoneGap-Plugin.git
  // link      :     https://github.com/EddyVerbruggen/Flashlight-PhoneGap-Plugin
  
    angular.module('ngCordova.plugins.flashlight', [])
  
      .factory('$cordovaFlashlight', ['$q', '$window', function ($q, $window) {
  
        return {
          available: function () {
            var q = $q.defer();
            $window.plugins.flashlight.available(function (isAvailable) {
              q.resolve(isAvailable);
            });
            return q.promise;
          },
  
          switchOn: function () {
            var q = $q.defer();
            $window.plugins.flashlight.switchOn(function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          switchOff: function () {
            var q = $q.defer();
            $window.plugins.flashlight.switchOff(function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          toggle: function () {
            var q = $q.defer();
            $window.plugins.flashlight.toggle(function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-plugin-flurry.git
  // link     :     https://github.com/floatinghotpot/cordova-plugin-flurry
  
    angular.module('ngCordova.plugins.flurryAds', [])
      .factory('$cordovaFlurryAds', ['$q', '$window', function ($q, $window) {
  
        return {
          setOptions: function (options) {
            var d = $q.defer();
  
            $window.FlurryAds.setOptions(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createBanner: function (options) {
            var d = $q.defer();
  
            $window.FlurryAds.createBanner(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          removeBanner: function () {
            var d = $q.defer();
  
            $window.FlurryAds.removeBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBanner: function (position) {
            var d = $q.defer();
  
            $window.FlurryAds.showBanner(position, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBannerAtXY: function (x, y) {
            var d = $q.defer();
  
            $window.FlurryAds.showBannerAtXY(x, y, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          hideBanner: function () {
            var d = $q.defer();
  
            $window.FlurryAds.hideBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          prepareInterstitial: function (options) {
            var d = $q.defer();
  
            $window.FlurryAds.prepareInterstitial(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showInterstitial: function () {
            var d = $q.defer();
  
            $window.FlurryAds.showInterstitial(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add https://github.com/phonegap-build/GAPlugin.git
  // link      :     https://github.com/phonegap-build/GAPlugin
  
    angular.module('ngCordova.plugins.ga', [])
  
      .factory('$cordovaGA', ['$q', '$window', function ($q, $window) {
  
        return {
          init: function (id, mingap) {
            var q = $q.defer();
            mingap = (mingap >= 0) ? mingap : 10;
            $window.plugins.gaPlugin.init(function (result) {
                q.resolve(result);
              },
              function (error) {
                q.reject(error);
              },
              id, mingap);
            return q.promise;
          },
  
          trackEvent: function (success, fail, category, eventAction, eventLabel, eventValue) {
            var q = $q.defer();
            $window.plugins.gaPlugin.trackEvent(function (result) {
                q.resolve(result);
              },
              function (error) {
                q.reject(error);
              },
              category, eventAction, eventLabel, eventValue);
            return q.promise;
          },
  
          trackPage: function (success, fail, pageURL) {
            var q = $q.defer();
            $window.plugins.gaPlugin.trackPage(function (result) {
                q.resolve(result);
              },
              function (error) {
                q.reject(error);
              },
              pageURL);
            return q.promise;
          },
  
          setVariable: function (success, fail, index, value) {
            var q = $q.defer();
            $window.plugins.gaPlugin.setVariable(function (result) {
                q.resolve(result);
              },
              function (error) {
                q.reject(error);
              },
              index, value);
            return q.promise;
          },
  
          exit: function () {
            var q = $q.defer();
            $window.plugins.gaPlugin.exit(function (result) {
                q.resolve(result);
              },
              function (error) {
                q.reject(error);
              });
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-geolocation
  // link      :     https://github.com/apache/cordova-plugin-geolocation
  
    angular.module('ngCordova.plugins.geolocation', [])
  
      .factory('$cordovaGeolocation', ['$q', function ($q) {
  
        return {
          getCurrentPosition: function (options) {
            var q = $q.defer();
  
            navigator.geolocation.getCurrentPosition(function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            }, options);
  
            return q.promise;
          },
  
          watchPosition: function (options) {
            var q = $q.defer();
  
            var watchID = navigator.geolocation.watchPosition(function (result) {
              q.notify(result);
            }, function (err) {
              q.reject(err);
            }, options);
  
            q.promise.cancel = function () {
              navigator.geolocation.clearWatch(watchID);
            };
  
            q.promise.clearWatch = function (id) {
              navigator.geolocation.clearWatch(id || watchID);
            };
  
            q.promise.watchID = watchID;
  
            return q.promise;
          },
  
          clearWatch: function (watchID) {
            return navigator.geolocation.clearWatch(watchID);
          }
        };
      }]);
  
  // install   :      cordova plugin add cordova-plugin-globalization
  // link      :      https://github.com/apache/cordova-plugin-globalization
  
    angular.module('ngCordova.plugins.globalization', [])
  
      .factory('$cordovaGlobalization', ['$q', function ($q) {
  
        return {
          getPreferredLanguage: function () {
            var q = $q.defer();
  
            navigator.globalization.getPreferredLanguage(function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              });
            return q.promise;
          },
  
          getLocaleName: function () {
            var q = $q.defer();
  
            navigator.globalization.getLocaleName(function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              });
            return q.promise;
          },
  
          getFirstDayOfWeek: function () {
            var q = $q.defer();
  
            navigator.globalization.getFirstDayOfWeek(function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              });
            return q.promise;
          },
  
          // "date" parameter must be a JavaScript Date Object.
          dateToString: function (date, options) {
            var q = $q.defer();
  
            navigator.globalization.dateToString(
              date,
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              },
              options);
            return q.promise;
          },
  
          stringToDate: function (dateString, options) {
            var q = $q.defer();
  
            navigator.globalization.stringToDate(
              dateString,
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              },
              options);
            return q.promise;
          },
  
          getDatePattern: function (options) {
            var q = $q.defer();
  
            navigator.globalization.getDatePattern(
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              },
              options);
            return q.promise;
          },
  
          getDateNames: function (options) {
            var q = $q.defer();
  
            navigator.globalization.getDateNames(
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              },
              options);
            return q.promise;
          },
  
          // "date" parameter must be a JavaScript Date Object.
          isDayLightSavingsTime: function (date) {
            var q = $q.defer();
  
            navigator.globalization.isDayLightSavingsTime(
              date,
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              });
            return q.promise;
          },
  
          numberToString: function (number, options) {
            var q = $q.defer();
  
            navigator.globalization.numberToString(
              number,
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              },
              options);
            return q.promise;
          },
  
          stringToNumber: function (numberString, options) {
            var q = $q.defer();
  
            navigator.globalization.stringToNumber(
              numberString,
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              },
              options);
            return q.promise;
          },
  
          getNumberPattern: function (options) {
            var q = $q.defer();
  
            navigator.globalization.getNumberPattern(
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              },
              options);
            return q.promise;
          },
  
          getCurrencyPattern: function (currencyCode) {
            var q = $q.defer();
  
            navigator.globalization.getCurrencyPattern(
              currencyCode,
              function (result) {
                q.resolve(result);
              },
              function (err) {
                q.reject(err);
              });
            return q.promise;
          }
  
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-admob-pro.git
  // link     :     https://github.com/floatinghotpot/cordova-admob-pro
  
    angular.module('ngCordova.plugins.googleAds', [])
  
      .factory('$cordovaGoogleAds', ['$q', '$window', function ($q, $window) {
  
        return {
          setOptions: function (options) {
            var d = $q.defer();
  
            $window.AdMob.setOptions(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createBanner: function (options) {
            var d = $q.defer();
  
            $window.AdMob.createBanner(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          removeBanner: function () {
            var d = $q.defer();
  
            $window.AdMob.removeBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBanner: function (position) {
            var d = $q.defer();
  
            $window.AdMob.showBanner(position, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBannerAtXY: function (x, y) {
            var d = $q.defer();
  
            $window.AdMob.showBannerAtXY(x, y, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          hideBanner: function () {
            var d = $q.defer();
  
            $window.AdMob.hideBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          prepareInterstitial: function (options) {
            var d = $q.defer();
  
            $window.AdMob.prepareInterstitial(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showInterstitial: function () {
            var d = $q.defer();
  
            $window.AdMob.showInterstitial(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add https://github.com/danwilson/google-analytics-plugin.git
  // link      :     https://github.com/danwilson/google-analytics-plugin
  
    angular.module('ngCordova.plugins.googleAnalytics', [])
  
      .factory('$cordovaGoogleAnalytics', ['$q', '$window', function ($q, $window) {
  
        return {
          startTrackerWithId: function (id) {
            var d = $q.defer();
  
            $window.analytics.startTrackerWithId(id, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          setUserId: function (id) {
            var d = $q.defer();
  
            $window.analytics.setUserId(id, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          debugMode: function () {
            var d = $q.defer();
  
            $window.analytics.debugMode(function (response) {
              d.resolve(response);
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          trackView: function (screenName) {
            var d = $q.defer();
  
            $window.analytics.trackView(screenName, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          addCustomDimension: function (key, value) {
            var d = $q.defer();
  
            $window.analytics.addCustomDimension(key, value, function () {
              d.resolve();
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          trackEvent: function (category, action, label, value) {
            var d = $q.defer();
  
            $window.analytics.trackEvent(category, action, label, value, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          trackException: function (description, fatal) {
            var d = $q.defer();
  
            $window.analytics.trackException(description, fatal, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          trackTiming: function (category, milliseconds, variable, label) {
            var d = $q.defer();
  
            $window.analytics.trackTiming(category, milliseconds, variable, label, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          addTransaction: function (transactionId, affiliation, revenue, tax, shipping, currencyCode) {
            var d = $q.defer();
  
            $window.analytics.addTransaction(transactionId, affiliation, revenue, tax, shipping, currencyCode, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          },
  
          addTransactionItem: function (transactionId, name, sku, category, price, quantity, currencyCode) {
            var d = $q.defer();
  
            $window.analytics.addTransactionItem(transactionId, name, sku, category, price, quantity, currencyCode, function (response) {
              d.resolve(response);
            }, function (error) {
              d.reject(error);
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install   :
  // link      :
  
  // Google Maps needs ALOT of work!
  // Not for production use
  
    angular.module('ngCordova.plugins.googleMap', [])
  
      .factory('$cordovaGoogleMap', ['$q', '$window', function ($q, $window) {
  
        var map = null;
  
        return {
          getMap: function (options) {
            var q = $q.defer();
  
            if (!$window.plugin.google.maps) {
              q.reject(null);
            } else {
              var div = document.getElementById('map_canvas');
              map = $window.plugin.google.maps.Map.getMap(options);
              map.setDiv(div);
              q.resolve(map);
            }
            return q.promise;
          },
  
          isMapLoaded: function () { // check if an instance of the map exists
            return !!map;
          },
          addMarker: function (markerOptions) { // add a marker to the map with given markerOptions
            var q = $q.defer();
            map.addMarker(markerOptions, function (marker) {
              q.resolve(marker);
            });
  
            return q.promise;
          },
          getMapTypeIds: function () {
            return $window.plugin.google.maps.mapTypeId;
          },
          setVisible: function (isVisible) {
            var q = $q.defer();
            map.setVisible(isVisible);
            return q.promise;
          },
          // I don't know how to deallocate te map and the google map plugin.
          cleanup: function () {
            map = null;
            // delete map;
          }
        };
      }]);
  
  // install   :   cordova plugin add https://github.com/ptgamr/cordova-google-play-game.git --variable APP_ID=123456789
  // link      :   https://github.com/ptgamr/cordova-google-play-game
  
    /* globals googleplaygame: true */
    angular.module('ngCordova.plugins.googlePlayGame', [])
  
      .factory('$cordovaGooglePlayGame', ['$q', function ($q) {
  
        return {
          auth: function () {
            var q = $q.defer();
  
            googleplaygame.auth(function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          signout: function () {
            var q = $q.defer();
  
            googleplaygame.signout(function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          isSignedIn: function () {
            var q = $q.defer();
  
            googleplaygame.isSignedIn(function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          showPlayer: function () {
            var q = $q.defer();
  
            googleplaygame.showPlayer(function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          submitScore: function (data) {
            var q = $q.defer();
  
            googleplaygame.submitScore(data, function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          showAllLeaderboards: function () {
            var q = $q.defer();
  
            googleplaygame.showAllLeaderboards(function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          showLeaderboard: function (data) {
            var q = $q.defer();
  
            googleplaygame.showLeaderboard(data, function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          unlockAchievement: function (data) {
            var q = $q.defer();
  
            googleplaygame.unlockAchievement(data, function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          incrementAchievement: function (data) {
            var q = $q.defer();
  
            googleplaygame.incrementAchievement(data, function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          },
          showAchievements: function () {
            var q = $q.defer();
  
            googleplaygame.showAchievements(function (success) {
              return q.resolve(success);
            }, function (err) {
              return q.reject(err);
            });
  
            return q.promise;
          }
        };
  
      }]);
  
  // install  :     cordova plugin add https://github.com/EddyVerbruggen/cordova-plugin-googleplus.git
  // link     :     https://github.com/EddyVerbruggen/cordova-plugin-googleplus
  
    angular.module('ngCordova.plugins.googlePlus', [])
  
      .factory('$cordovaGooglePlus', ['$q', '$window', function ($q, $window) {
  
        return {
          login: function (iosKey) {
            var q = $q.defer();
  
            if (iosKey === undefined) {
              iosKey = {};
            }
            $window.plugins.googleplus.login({'iOSApiKey': iosKey}, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
  
            return q.promise;
          },
  
          silentLogin: function (iosKey) {
            var q = $q.defer();
  
            if (iosKey === undefined) {
              iosKey = {};
            }
            $window.plugins.googleplus.trySilentLogin({'iOSApiKey': iosKey}, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
  
            return q.promise;
          },
  
          logout: function () {
            var q = $q.defer();
            $window.plugins.googleplus.logout(function (response) {
              q.resolve(response);
            });
          },
  
          disconnect: function () {
            var q = $q.defer();
            $window.plugins.googleplus.disconnect(function (response) {
              q.resolve(response);
            });
          },
  
          isAvailable: function () {
            var q = $q.defer();
            $window.plugins.googleplus.isAvailable(function (available) {
              if (available) {
                q.resolve(available);
              } else {
                q.reject(available);
              }
            });
  
            return q.promise;
          }
        };
  
      }]);
  
  // install   :      cordova plugin add https://github.com/Telerik-Verified-Plugins/HealthKit.git
  // link      :      https://github.com/Telerik-Verified-Plugins/HealthKit
  
    angular.module('ngCordova.plugins.healthKit', [])
  
      .factory('$cordovaHealthKit', ['$q', '$window', function ($q, $window) {
  
        return {
          isAvailable: function () {
            var q = $q.defer();
  
            $window.plugins.healthkit.available(function (success) {
              q.resolve(success);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          /**
           * Check whether or not the user granted your app access to a specific HealthKit type.
           * Reference for possible types:
           * https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/
           */
          checkAuthStatus: function (type) {
            var q = $q.defer();
  
            type = type || 'HKQuantityTypeIdentifierHeight';
  
            $window.plugins.healthkit.checkAuthStatus({
              'type': type
            }, function (success) {
              q.resolve(success);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          /**
           * Request authorization to access HealthKit data. See the full HealthKit constants
           * reference for possible read and write types:
           * https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Constants/
           */
          requestAuthorization: function (readTypes, writeTypes) {
            var q = $q.defer();
  
            readTypes = readTypes || [
                'HKCharacteristicTypeIdentifierDateOfBirth', 'HKQuantityTypeIdentifierActiveEnergyBurned', 'HKQuantityTypeIdentifierHeight'
              ];
            writeTypes = writeTypes || [
                'HKQuantityTypeIdentifierActiveEnergyBurned', 'HKQuantityTypeIdentifierHeight', 'HKQuantityTypeIdentifierDistanceCycling'
              ];
  
            $window.plugins.healthkit.requestAuthorization({
              'readTypes': readTypes,
              'writeTypes': writeTypes
            }, function (success) {
              q.resolve(success);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          readDateOfBirth: function () {
            var q = $q.defer();
            $window.plugins.healthkit.readDateOfBirth(
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
  
            return q.promise;
          },
  
          readGender: function () {
            var q = $q.defer();
            $window.plugins.healthkit.readGender(
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
  
            return q.promise;
          },
  
          saveWeight: function (value, units, date) {
            var q = $q.defer();
            $window.plugins.healthkit.saveWeight({
                'unit': units || 'lb',
                'amount': value,
                'date': date || new Date()
              },
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
            return q.promise;
          },
  
          readWeight: function (units) {
            var q = $q.defer();
            $window.plugins.healthkit.readWeight({
                'unit': units || 'lb'
              },
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
  
            return q.promise;
          },
          saveHeight: function (value, units, date) {
            var q = $q.defer();
            $window.plugins.healthkit.saveHeight({
                'unit': units || 'in',
                'amount': value,
                'date': date || new Date()
              },
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
            return q.promise;
          },
          readHeight: function (units) {
            var q = $q.defer();
            $window.plugins.healthkit.readHeight({
                'unit': units || 'in'
              },
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
  
            return q.promise;
          },
  
          findWorkouts: function () {
            var q = $q.defer();
            $window.plugins.healthkit.findWorkouts({},
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
            return q.promise;
          },
  
          /**
           * Save a workout.
           *
           * Workout param should be of the format:
           {
           'activityType': 'HKWorkoutActivityTypeCycling', // HKWorkoutActivityType constant (https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKWorkout_Class/#//apple_ref/c/tdef/HKWorkoutActivityType)
           'quantityType': 'HKQuantityTypeIdentifierDistanceCycling',
           'startDate': new Date(), // mandatory
           'endDate': null, // optional, use either this or duration
           'duration': 3600, // in seconds, optional, use either this or endDate
           'energy': 300, //
           'energyUnit': 'kcal', // J|cal|kcal
           'distance': 11, // optional
           'distanceUnit': 'km' // probably useful with the former param
           // 'extraData': "", // Not sure how necessary this is
         },
           */
          saveWorkout: function (workout) {
            var q = $q.defer();
            $window.plugins.healthkit.saveWorkout(workout,
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
            return q.promise;
          },
  
          /**
           * Sample any kind of health data through a given date range.
           * sampleQuery of the format:
           {
  									'startDate': yesterday, // mandatory
  									'endDate': tomorrow, // mandatory
  									'sampleType': 'HKQuantityTypeIdentifierHeight',
  									'unit' : 'cm'
  							},
           */
          querySampleType: function (sampleQuery) {
            var q = $q.defer();
            $window.plugins.healthkit.querySampleType(sampleQuery,
              function (success) {
                q.resolve(success);
              },
              function (err) {
                q.resolve(err);
              }
            );
            return q.promise;
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-httpd.git
  // link     :     https://github.com/floatinghotpot/cordova-httpd
  
    angular.module('ngCordova.plugins.httpd', [])
  
      .factory('$cordovaHttpd', ['$q', function ($q) {
  
        return {
          startServer: function (options) {
            var d = $q.defer();
  
            cordova.plugins.CorHttpd.startServer(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          stopServer: function () {
            var d = $q.defer();
  
            cordova.plugins.CorHttpd.stopServer(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          getURL: function () {
            var d = $q.defer();
  
            cordova.plugins.CorHttpd.getURL(function (url) {
              d.resolve(url);
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          getLocalPath: function () {
            var d = $q.defer();
  
            cordova.plugins.CorHttpd.getLocalPath(function (path) {
              d.resolve(path);
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
  
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-plugin-iad.git
  // link     :     https://github.com/floatinghotpot/cordova-plugin-iad
  
    angular.module('ngCordova.plugins.iAd', [])
      .factory('$cordovaiAd', ['$q', '$window', function ($q, $window) {
  
        return {
          setOptions: function (options) {
            var d = $q.defer();
  
            $window.iAd.setOptions(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createBanner: function (options) {
            var d = $q.defer();
  
            $window.iAd.createBanner(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          removeBanner: function () {
            var d = $q.defer();
  
            $window.iAd.removeBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBanner: function (position) {
            var d = $q.defer();
  
            $window.iAd.showBanner(position, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBannerAtXY: function (x, y) {
            var d = $q.defer();
  
            $window.iAd.showBannerAtXY(x, y, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          hideBanner: function () {
            var d = $q.defer();
  
            $window.iAd.hideBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          prepareInterstitial: function (options) {
            var d = $q.defer();
  
            $window.iAd.prepareInterstitial(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showInterstitial: function () {
            var d = $q.defer();
  
            $window.iAd.showInterstitial(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/wymsee/cordova-imagePicker.git
  // link     :     https://github.com/wymsee/cordova-imagePicker
  
    angular.module('ngCordova.plugins.imagePicker', [])
  
      .factory('$cordovaImagePicker', ['$q', '$window', function ($q, $window) {
  
        return {
          getPictures: function (options) {
            var q = $q.defer();
  
            $window.imagePicker.getPictures(function (results) {
              q.resolve(results);
            }, function (error) {
              q.reject(error);
            }, options);
  
            return q.promise;
          }
        };
      }]);
  
  // install   :     cordova plugin add cordova-plugin-inappbrowser
  // link      :     https://github.com/apache/cordova-plugin-inappbrowser
  
    angular.module('ngCordova.plugins.inAppBrowser', [])
  
      .provider('$cordovaInAppBrowser', [function () {
  
        var ref;
        var defaultOptions = this.defaultOptions = {};
  
        this.setDefaultOptions = function (config) {
          defaultOptions = angular.extend(defaultOptions, config);
        };
  
        this.$get = ['$rootScope', '$q', '$window', '$timeout', function ($rootScope, $q, $window, $timeout) {
          return {
            open: function (url, target, requestOptions) {
              var q = $q.defer();
  
              if (requestOptions && !angular.isObject(requestOptions)) {
                q.reject('options must be an object');
                return q.promise;
              }
  
              var options = angular.extend({}, defaultOptions, requestOptions);
  
              var opt = [];
              angular.forEach(options, function (value, key) {
                opt.push(key + '=' + value);
              });
              var optionsString = opt.join();
  
              ref = $window.open(url, target, optionsString);
  
              ref.addEventListener('loadstart', function (event) {
                $timeout(function () {
                  $rootScope.$broadcast('$cordovaInAppBrowser:loadstart', event);
                });
              }, false);
  
              ref.addEventListener('loadstop', function (event) {
                q.resolve(event);
                $timeout(function () {
                  $rootScope.$broadcast('$cordovaInAppBrowser:loadstop', event);
                });
              }, false);
  
              ref.addEventListener('loaderror', function (event) {
                q.reject(event);
                $timeout(function () {
                  $rootScope.$broadcast('$cordovaInAppBrowser:loaderror', event);
                });
              }, false);
  
              ref.addEventListener('exit', function (event) {
                $timeout(function () {
                  $rootScope.$broadcast('$cordovaInAppBrowser:exit', event);
                });
              }, false);
  
              return q.promise;
            },
  
            close: function () {
              ref.close();
              ref = null;
            },
  
            show: function () {
              ref.show();
            },
  
            executeScript: function (details) {
              var q = $q.defer();
  
              ref.executeScript(details, function (result) {
                q.resolve(result);
              });
  
              return q.promise;
            },
  
            insertCSS: function (details) {
              var q = $q.defer();
  
              ref.insertCSS(details, function (result) {
                q.resolve(result);
              });
  
              return q.promise;
            }
          };
        }];
      }]);
  
  // install  :     cordova plugin add https://github.com/EddyVerbruggen/Insomnia-PhoneGap-Plugin.git
  // link     :     https://github.com/EddyVerbruggen/Insomnia-PhoneGap-Plugin
    angular.module('ngCordova.plugins.insomnia', [])
  
      .factory('$cordovaInsomnia', ['$window', function ($window) {
  
        return {
          keepAwake: function () {
            return $window.plugins.insomnia.keepAwake();
          },
          allowSleepAgain: function () {
            return $window.plugins.insomnia.allowSleepAgain();
          }
        };
  
      }]);
  
  // install   :   cordova plugins add https://github.com/vstirbu/InstagramPlugin.git
  // link      :   https://github.com/vstirbu/InstagramPlugin
  
    /* globals Instagram: true */
    angular.module('ngCordova.plugins.instagram', [])
  
      .factory('$cordovaInstagram', ['$q', function ($q) {
  
        return {
          share: function (options) {
            var q = $q.defer();
  
            if (!window.Instagram) {
              console.error('Tried to call Instagram.share but the Instagram plugin isn\'t installed!');
              q.resolve(null);
              return q.promise;
            }
  
            Instagram.share(options.image, options.caption, function (err) {
              if (err) {
                q.reject(err);
              } else {
                q.resolve(true);
              }
            });
            return q.promise;
          },
          isInstalled: function () {
            var q = $q.defer();
  
            if (!window.Instagram) {
              console.error('Tried to call Instagram.isInstalled but the Instagram plugin isn\'t installed!');
              q.resolve(null);
              return q.promise;
            }
  
            Instagram.isInstalled(function (err, installed) {
              if (err) {
                q.reject(err);
              } else {
                q.resolve(installed || true);
              }
            });
            return q.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add https://github.com/driftyco/ionic-plugins-keyboard.git
  // link      :      https://github.com/driftyco/ionic-plugins-keyboard
  
    angular.module('ngCordova.plugins.keyboard', [])
  
      .factory('$cordovaKeyboard', ['$rootScope', function ($rootScope) {
  
        var keyboardShowEvent = function () {
          $rootScope.$evalAsync(function () {
            $rootScope.$broadcast('$cordovaKeyboard:show');
          });
        };
  
        var keyboardHideEvent = function () {
          $rootScope.$evalAsync(function () {
            $rootScope.$broadcast('$cordovaKeyboard:hide');
          });
        };
  
        document.addEventListener('deviceready', function () {
          if (cordova.plugins.Keyboard) {
            window.addEventListener('native.keyboardshow', keyboardShowEvent, false);
            window.addEventListener('native.keyboardhide', keyboardHideEvent, false);
          }
        });
  
        return {
          hideAccessoryBar: function (bool) {
            return cordova.plugins.Keyboard.hideKeyboardAccessoryBar(bool);
          },
  
          close: function () {
            return cordova.plugins.Keyboard.close();
          },
  
          show: function () {
            return cordova.plugins.Keyboard.show();
          },
  
          disableScroll: function (bool) {
            return cordova.plugins.Keyboard.disableScroll(bool);
          },
  
          isVisible: function () {
            return cordova.plugins.Keyboard.isVisible;
          },
  
          clearShowWatch: function () {
            document.removeEventListener('native.keyboardshow', keyboardShowEvent);
            $rootScope.$$listeners['$cordovaKeyboard:show'] = [];
          },
  
          clearHideWatch: function () {
            document.removeEventListener('native.keyboardhide', keyboardHideEvent);
            $rootScope.$$listeners['$cordovaKeyboard:hide'] = [];
          }
        };
      }]);
  
  // install   :      cordova plugin add https://github.com/shazron/KeychainPlugin.git
  // link      :      https://github.com/shazron/KeychainPlugin
  
    /* globals Keychain: true */
    angular.module('ngCordova.plugins.keychain', [])
  
      .factory('$cordovaKeychain', ['$q', function ($q) {
  
        return {
          getForKey: function (key, serviceName) {
            var defer = $q.defer(),
              kc = new Keychain();
  
            kc.getForKey(defer.resolve, defer.reject, key, serviceName);
  
            return defer.promise;
          },
  
          setForKey: function (key, serviceName, value) {
            var defer = $q.defer(),
              kc = new Keychain();
  
            kc.setForKey(defer.resolve, defer.reject, key, serviceName, value);
  
            return defer.promise;
          },
  
          removeForKey: function (key, serviceName) {
            var defer = $q.defer(),
              kc = new Keychain();
  
            kc.removeForKey(defer.resolve, defer.reject, key, serviceName);
  
            return defer.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add uk.co.workingedge.phonegap.plugin.launchnavigator
  // link      :      https://github.com/dpa99c/phonegap-launch-navigator
  
    /* globals launchnavigator: true */
    angular.module('ngCordova.plugins.launchNavigator', [])
  
      .factory('$cordovaLaunchNavigator', ['$q', function ($q) {
  
        return {
          navigate: function (destination, start, successFn, errorFn, options) {
            var q = $q.defer();
            launchnavigator.navigate(
              destination,
              start,
              function () {
                q.resolve();
              },
              function (error) {
                q.reject(error);
              },
              options);
            return q.promise;
          }
        };
  
      }]);
  
  // install   :  cordova plugin add https://github.com/katzer/cordova-plugin-local-notifications.git
  // link      :  https://github.com/katzer/cordova-plugin-local-notifications
  
    angular.module('ngCordova.plugins.localNotification', [])
  
      .factory('$cordovaLocalNotification', ['$q', '$window', '$rootScope', '$timeout', function ($q, $window, $rootScope, $timeout) {
        document.addEventListener('deviceready', function () {
          if ($window.cordova &&
            $window.cordova.plugins &&
            $window.cordova.plugins.notification &&
            $window.cordova.plugins.notification.local) {
            // ----- "Scheduling" events
  
            // A local notification was scheduled
            $window.cordova.plugins.notification.local.on('schedule', function (notification, state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:schedule', notification, state);
              });
            });
  
            // A local notification was triggered
            $window.cordova.plugins.notification.local.on('trigger', function (notification, state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:trigger', notification, state);
              });
            });
  
            // ----- "Update" events
  
            // A local notification was updated
            $window.cordova.plugins.notification.local.on('update', function (notification, state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:update', notification, state);
              });
            });
  
            // ----- "Clear" events
  
            // A local notification was cleared from the notification center
            $window.cordova.plugins.notification.local.on('clear', function (notification, state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:clear', notification, state);
              });
            });
  
            // All local notifications were cleared from the notification center
            $window.cordova.plugins.notification.local.on('clearall', function (state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:clearall', state);
              });
            });
  
            // ----- "Cancel" events
  
            // A local notification was cancelled
            $window.cordova.plugins.notification.local.on('cancel', function (notification, state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:cancel', notification, state);
              });
            });
  
            // All local notifications were cancelled
            $window.cordova.plugins.notification.local.on('cancelall', function (state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:cancelall', state);
              });
            });
  
            // ----- Other events
  
            // A local notification was clicked
            $window.cordova.plugins.notification.local.on('click', function (notification, state) {
              $timeout(function () {
                $rootScope.$broadcast('$cordovaLocalNotification:click', notification, state);
              });
            });
          }
        }, false);
        return {
          schedule: function (options, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.schedule(options, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          add: function (options, scope) {
            console.warn('Deprecated: use "schedule" instead.');
  
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.schedule(options, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          update: function (options, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.update(options, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          clear: function (ids, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.clear(ids, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          clearAll: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.clearAll(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          cancel: function (ids, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.cancel(ids, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          cancelAll: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.cancelAll(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          isPresent: function (id, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.isPresent(id, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          isScheduled: function (id, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.isScheduled(id, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          isTriggered: function (id, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.isTriggered(id, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          hasPermission: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.hasPermission(function (result) {
              if (result) {
                q.resolve(result);
              } else {
                q.reject(result);
              }
            }, scope);
  
            return q.promise;
          },
  
          registerPermission: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.registerPermission(function (result) {
              if (result) {
                q.resolve(result);
              } else {
                q.reject(result);
              }
            }, scope);
  
            return q.promise;
          },
  
          promptForPermission: function (scope) {
            console.warn('Deprecated: use "registerPermission" instead.');
  
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.registerPermission(function (result) {
              if (result) {
                q.resolve(result);
              } else {
                q.reject(result);
              }
            }, scope);
  
            return q.promise;
          },
  
          getAllIds: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getAllIds(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getIds: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getIds(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getScheduledIds: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getScheduledIds(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getTriggeredIds: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getTriggeredIds(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          get: function (ids, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.get(ids, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getAll: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getAll(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getScheduled: function (ids, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getScheduled(ids, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getAllScheduled: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getAllScheduled(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getTriggered: function (ids, scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getTriggered(ids, function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getAllTriggered: function (scope) {
            var q = $q.defer();
            scope = scope || null;
  
            $window.cordova.plugins.notification.local.getAllTriggered(function (result) {
              q.resolve(result);
            }, scope);
  
            return q.promise;
          },
  
          getDefaults: function () {
            return $window.cordova.plugins.notification.local.getDefaults();
          },
  
          setDefaults: function (Object) {
            $window.cordova.plugins.notification.local.setDefaults(Object);
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-plugin-mmedia.git
  // link     :     https://github.com/floatinghotpot/cordova-plugin-mmedia
  
    angular.module('ngCordova.plugins.mMediaAds', [])
  
      .factory('$cordovaMMediaAds', ['$q', '$window', function ($q, $window) {
  
        return {
          setOptions: function (options) {
            var d = $q.defer();
  
            $window.mMedia.setOptions(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createBanner: function (options) {
            var d = $q.defer();
  
            $window.mMedia.createBanner(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          removeBanner: function () {
            var d = $q.defer();
  
            $window.mMedia.removeBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBanner: function (position) {
            var d = $q.defer();
  
            $window.mMedia.showBanner(position, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBannerAtXY: function (x, y) {
            var d = $q.defer();
  
            $window.mMedia.showBannerAtXY(x, y, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          hideBanner: function () {
            var d = $q.defer();
  
            $window.mMedia.hideBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          prepareInterstitial: function (options) {
            var d = $q.defer();
  
            $window.mMedia.prepareInterstitial(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showInterstitial: function () {
            var d = $q.defer();
  
            $window.mMedia.showInterstitial(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add cordova-plugin-media
  // link      :      https://github.com/apache/cordova-plugin-media
  
    /* globals Media: true */
    angular.module('ngCordova.plugins.media', [])
  
      .service('NewMedia', ['$q', '$interval', function ($q, $interval) {
        var q, q2, q3, mediaStatus = null, mediaPosition = -1, mediaTimer, mediaDuration = -1;
  
        function setTimer(media) {
          if (angular.isDefined(mediaTimer)) {
            return;
          }
  
          mediaTimer = $interval(function () {
            if (mediaDuration < 0) {
              mediaDuration = media.getDuration();
              if (q && mediaDuration > 0) {
                q.notify({duration: mediaDuration});
              }
            }
  
            media.getCurrentPosition(
              // success callback
              function (position) {
                if (position > -1) {
                  mediaPosition = position;
                }
              },
              // error callback
              function (e) {
                console.log('Error getting pos=' + e);
              });
  
            if (q) {
              q.notify({position: mediaPosition});
            }
  
          }, 1000);
        }
  
        function clearTimer() {
          if (angular.isDefined(mediaTimer)) {
            $interval.cancel(mediaTimer);
            mediaTimer = undefined;
          }
        }
  
        function resetValues() {
          mediaPosition = -1;
          mediaDuration = -1;
        }
  
        function NewMedia(src) {
          this.media = new Media(src,
            function (success) {
              clearTimer();
              resetValues();
              q.resolve(success);
            }, function (error) {
              clearTimer();
              resetValues();
              q.reject(error);
            }, function (status) {
              mediaStatus = status;
              q.notify({status: mediaStatus});
            });
        }
  
        // iOS quirks :
        // -  myMedia.play({ numberOfLoops: 2 }) -> looping
        // -  myMedia.play({ playAudioWhenScreenIsLocked : false })
        NewMedia.prototype.play = function (options) {
          q = $q.defer();
  
          if (typeof options !== 'object') {
            options = {};
          }
  
          this.media.play(options);
  
          setTimer(this.media);
  
          return q.promise;
        };
  
        NewMedia.prototype.pause = function () {
          clearTimer();
          this.media.pause();
        };
  
        NewMedia.prototype.stop = function () {
          this.media.stop();
        };
  
        NewMedia.prototype.release = function () {
          this.media.release();
          this.media = undefined;
        };
  
        NewMedia.prototype.seekTo = function (timing) {
          this.media.seekTo(timing);
        };
  
        NewMedia.prototype.setVolume = function (volume) {
          this.media.setVolume(volume);
        };
  
        NewMedia.prototype.startRecord = function () {
          this.media.startRecord();
        };
  
        NewMedia.prototype.stopRecord = function () {
          this.media.stopRecord();
        };
  
        NewMedia.prototype.currentTime = function () {
          q2 = $q.defer();
          this.media.getCurrentPosition(function (position) {
            q2.resolve(position);
          });
          return q2.promise;
        };
  
        NewMedia.prototype.getDuration = function () {
          q3 = $q.defer();
          this.media.getDuration(function (duration) {
            q3.resolve(duration);
          });
          return q3.promise;
        };
  
        return NewMedia;
  
      }])
      .factory('$cordovaMedia', ['NewMedia', function (NewMedia) {
        return {
          newMedia: function (src) {
            return new NewMedia(src);
          }
        };
      }]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-mobfox-pro.git
  // link     :     https://github.com/floatinghotpot/cordova-mobfox-pro
  
    angular.module('ngCordova.plugins.mobfoxAds', [])
  
      .factory('$cordovaMobFoxAds', ['$q', '$window', function ($q, $window) {
  
        return {
          setOptions: function (options) {
            var d = $q.defer();
  
            $window.MobFox.setOptions(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createBanner: function (options) {
            var d = $q.defer();
  
            $window.MobFox.createBanner(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          removeBanner: function () {
            var d = $q.defer();
  
            $window.MobFox.removeBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBanner: function (position) {
            var d = $q.defer();
  
            $window.MobFox.showBanner(position, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBannerAtXY: function (x, y) {
            var d = $q.defer();
  
            $window.MobFox.showBannerAtXY(x, y, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          hideBanner: function () {
            var d = $q.defer();
  
            $window.MobFox.hideBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          prepareInterstitial: function (options) {
            var d = $q.defer();
  
            $window.MobFox.prepareInterstitial(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showInterstitial: function () {
            var d = $q.defer();
  
            $window.MobFox.showInterstitial(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
    angular.module('ngCordova.plugins', [
      'ngCordova.plugins.actionSheet',
      'ngCordova.plugins.adMob',
      'ngCordova.plugins.appAvailability',
      'ngCordova.plugins.appRate',
      'ngCordova.plugins.appVersion',
      'ngCordova.plugins.backgroundGeolocation',
      'ngCordova.plugins.badge',
      'ngCordova.plugins.barcodeScanner',
      'ngCordova.plugins.batteryStatus',
      'ngCordova.plugins.ble',
      'ngCordova.plugins.bluetoothSerial',
      'ngCordova.plugins.brightness',
      'ngCordova.plugins.calendar',
      'ngCordova.plugins.camera',
      'ngCordova.plugins.capture',
      'ngCordova.plugins.clipboard',
      'ngCordova.plugins.contacts',
      'ngCordova.plugins.datePicker',
      'ngCordova.plugins.device',
      'ngCordova.plugins.deviceMotion',
      'ngCordova.plugins.deviceOrientation',
      'ngCordova.plugins.dialogs',
      'ngCordova.plugins.emailComposer',
      'ngCordova.plugins.facebook',
      'ngCordova.plugins.facebookAds',
      'ngCordova.plugins.file',
      'ngCordova.plugins.fileTransfer',
      'ngCordova.plugins.fileOpener2',
      'ngCordova.plugins.flashlight',
      'ngCordova.plugins.flurryAds',
      'ngCordova.plugins.ga',
      'ngCordova.plugins.geolocation',
      'ngCordova.plugins.globalization',
      'ngCordova.plugins.googleAds',
      'ngCordova.plugins.googleAnalytics',
      'ngCordova.plugins.googleMap',
      'ngCordova.plugins.googlePlayGame',
      'ngCordova.plugins.googlePlus',
      'ngCordova.plugins.healthKit',
      'ngCordova.plugins.httpd',
      'ngCordova.plugins.iAd',
      'ngCordova.plugins.imagePicker',
      'ngCordova.plugins.inAppBrowser',
      'ngCordova.plugins.instagram',
      'ngCordova.plugins.keyboard',
      'ngCordova.plugins.keychain',
      'ngCordova.plugins.launchNavigator',
      'ngCordova.plugins.localNotification',
      'ngCordova.plugins.media',
      'ngCordova.plugins.mMediaAds',
      'ngCordova.plugins.mobfoxAds',
      'ngCordova.plugins.mopubAds',
      'ngCordova.plugins.nativeAudio',
      'ngCordova.plugins.network',
      'ngCordovaOauth',
      'ngCordova.plugins.pinDialog',
      'ngCordova.plugins.prefs',
      'ngCordova.plugins.printer',
      'ngCordova.plugins.progressIndicator',
      'ngCordova.plugins.push',
      'ngCordova.plugins.sms',
      'ngCordova.plugins.socialSharing',
      'ngCordova.plugins.spinnerDialog',
      'ngCordova.plugins.splashscreen',
      'ngCordova.plugins.sqlite',
      'ngCordova.plugins.statusbar',
      'ngCordova.plugins.toast',
      'ngCordova.plugins.touchid',
      'ngCordova.plugins.vibration',
      'ngCordova.plugins.videoCapturePlus',
      'ngCordova.plugins.zip',
      'ngCordova.plugins.insomnia'
    ]);
  
  // install  :     cordova plugin add https://github.com/floatinghotpot/cordova-plugin-mopub.git
  // link     :     https://github.com/floatinghotpot/cordova-plugin-mopub
  
    angular.module('ngCordova.plugins.mopubAds', [])
      .factory('$cordovaMoPubAds', ['$q', '$window', function ($q, $window) {
  
        return {
          setOptions: function (options) {
            var d = $q.defer();
  
            $window.MoPub.setOptions(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          createBanner: function (options) {
            var d = $q.defer();
  
            $window.MoPub.createBanner(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          removeBanner: function () {
            var d = $q.defer();
  
            $window.MoPub.removeBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBanner: function (position) {
            var d = $q.defer();
  
            $window.MoPub.showBanner(position, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showBannerAtXY: function (x, y) {
            var d = $q.defer();
  
            $window.MoPub.showBannerAtXY(x, y, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          hideBanner: function () {
            var d = $q.defer();
  
            $window.MoPub.hideBanner(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          prepareInterstitial: function (options) {
            var d = $q.defer();
  
            $window.MoPub.prepareInterstitial(options, function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          },
  
          showInterstitial: function () {
            var d = $q.defer();
  
            $window.MoPub.showInterstitial(function () {
              d.resolve();
            }, function () {
              d.reject();
            });
  
            return d.promise;
          }
        };
      }]);
  
  // install   : cordova plugin add https://github.com/sidneys/cordova-plugin-nativeaudio.git
  // link      : https://github.com/sidneys/cordova-plugin-nativeaudio
  
    angular.module('ngCordova.plugins.nativeAudio', [])
  
      .factory('$cordovaNativeAudio', ['$q', '$window', function ($q, $window) {
  
        return {
          preloadSimple: function (id, assetPath) {
            var q = $q.defer();
            $window.plugins.NativeAudio.preloadSimple(id, assetPath, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          preloadComplex: function (id, assetPath, volume, voices) {
            var q = $q.defer();
            $window.plugins.NativeAudio.preloadComplex(id, assetPath, volume, voices, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          play: function (id, completeCallback) {
            var q = $q.defer();
            $window.plugins.NativeAudio.play(id, completeCallback, function (err) {
              q.reject(err);
            }, function (result) {
              q.resolve(result);
            });
  
            return q.promise;
          },
  
          stop: function (id) {
            var q = $q.defer();
            $window.plugins.NativeAudio.stop(id, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
            return q.promise;
          },
  
          loop: function (id) {
            var q = $q.defer();
            $window.plugins.NativeAudio.loop(id, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          unload: function (id) {
            var q = $q.defer();
            $window.plugins.NativeAudio.unload(id, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          setVolumeForComplexAsset: function (id, volume) {
            var q = $q.defer();
            $window.plugins.NativeAudio.setVolumeForComplexAsset(id, volume, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add cordova-plugin-network-information
  // link      :      https://github.com/apache/cordova-plugin-network-information
  
    /* globals Connection: true */
    angular.module('ngCordova.plugins.network', [])
  
      .factory('$cordovaNetwork', ['$rootScope', '$timeout', function ($rootScope, $timeout) {
  
        /**
         * Fires offline a event
         */
        var offlineEvent = function () {
          var networkState = navigator.connection.type;
          $timeout(function () {
            $rootScope.$broadcast('$cordovaNetwork:offline', networkState);
          });
        };
  
        /**
         * Fires online a event
         */
        var onlineEvent = function () {
          var networkState = navigator.connection.type;
          $timeout(function () {
            $rootScope.$broadcast('$cordovaNetwork:online', networkState);
          });
        };
  
        document.addEventListener('deviceready', function () {
          if (navigator.connection) {
            document.addEventListener('offline', offlineEvent, false);
            document.addEventListener('online', onlineEvent, false);
          }
        });
  
        return {
          getNetwork: function () {
            return navigator.connection.type;
          },
  
          isOnline: function () {
            var networkState = navigator.connection.type;
            return networkState !== Connection.UNKNOWN && networkState !== Connection.NONE;
          },
  
          isOffline: function () {
            var networkState = navigator.connection.type;
            return networkState === Connection.UNKNOWN || networkState === Connection.NONE;
          },
  
          clearOfflineWatch: function () {
            document.removeEventListener('offline', offlineEvent);
            $rootScope.$$listeners['$cordovaNetwork:offline'] = [];
          },
  
          clearOnlineWatch: function () {
            document.removeEventListener('online', onlineEvent);
            $rootScope.$$listeners['$cordovaNetwork:online'] = [];
          }
        };
      }])
      .run(['$injector', function ($injector) {
        $injector.get('$cordovaNetwork'); //ensure the factory always gets initialised
      }]);
  
  // install   :      cordova plugin add https://github.com/Paldom/PinDialog.git
  // link      :      https://github.com/Paldom/PinDialog
  
    angular.module('ngCordova.plugins.pinDialog', [])
  
      .factory('$cordovaPinDialog', ['$q', '$window', function ($q, $window) {
  
        return {
          prompt: function (message, title, buttons) {
            var q = $q.defer();
  
            $window.plugins.pinDialog.prompt(message, function (res) {
              q.resolve(res);
            }, title, buttons);
  
            return q.promise;
          }
        };
      }]);
  
  // install   :
  // link      :
  
    angular.module('ngCordova.plugins.prefs', [])
  
      .factory('$cordovaPreferences', ['$window', '$q', function ($window, $q) {
  
        return {
          set: function (key, value) {
            var q = $q.defer();
  
            $window.appgiraffe.plugins.applicationPreferences.set(key, value, function (result) {
              q.resolve(result);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          },
  
          get: function (key) {
            var q = $q.defer();
  
            $window.appgiraffe.plugins.applicationPreferences.get(key, function (value) {
              q.resolve(value);
            }, function (err) {
              q.reject(err);
            });
  
            return q.promise;
          }
        };
      }]);
  
  // install   : cordova plugin add https://github.com/katzer/cordova-plugin-printer.git
  // link      : https://github.com/katzer/cordova-plugin-printer
  
    angular.module('ngCordova.plugins.printer', [])
  
      .factory('$cordovaPrinter', ['$q', '$window', function ($q, $window) {
  
        return {
          isAvailable: function () {
            var q = $q.defer();
  
            $window.plugin.printer.isAvailable(function (isAvailable) {
              q.resolve(isAvailable);
            });
  
            return q.promise;
          },
  
          print: function (doc, options) {
            var q = $q.defer();
            $window.plugin.printer.print(doc, options, function () {
              q.resolve();
            });
            return q.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add https://github.com/pbernasconi/cordova-progressIndicator.git
  // link      :      http://pbernasconi.github.io/cordova-progressIndicator/
  
    /* globals ProgressIndicator: true */
    angular.module('ngCordova.plugins.progressIndicator', [])
  
      .factory('$cordovaProgress', [function () {
  
        return {
          show: function (_message) {
            var message = _message || 'Please wait...';
            return ProgressIndicator.show(message);
          },
  
          showSimple: function (_dim) {
            var dim = _dim || false;
            return ProgressIndicator.showSimple(dim);
          },
  
          showSimpleWithLabel: function (_dim, _label) {
            var dim = _dim || false;
            var label = _label || 'Loading...';
            return ProgressIndicator.showSimpleWithLabel(dim, label);
          },
  
          showSimpleWithLabelDetail: function (_dim, _label, _detail) {
            var dim = _dim || false;
            var label = _label || 'Loading...';
            var detail = _detail || 'Please wait';
            return ProgressIndicator.showSimpleWithLabelDetail(dim, label, detail);
          },
  
          showDeterminate: function (_dim, _timeout) {
            var dim = _dim || false;
            var timeout = _timeout || 50000;
            return ProgressIndicator.showDeterminate(dim, timeout);
          },
  
          showDeterminateWithLabel: function (_dim, _timeout, _label) {
            var dim = _dim || false;
            var timeout = _timeout || 50000;
            var label = _label || 'Loading...';
  
            return ProgressIndicator.showDeterminateWithLabel(dim, timeout, label);
          },
  
          showAnnular: function (_dim, _timeout) {
            var dim = _dim || false;
            var timeout = _timeout || 50000;
            return ProgressIndicator.showAnnular(dim, timeout);
          },
  
          showAnnularWithLabel: function (_dim, _timeout, _label) {
            var dim = _dim || false;
            var timeout = _timeout || 50000;
            var label = _label || 'Loading...';
            return ProgressIndicator.showAnnularWithLabel(dim, timeout, label);
          },
  
          showBar: function (_dim, _timeout) {
            var dim = _dim || false;
            var timeout = _timeout || 50000;
            return ProgressIndicator.showBar(dim, timeout);
          },
  
          showBarWithLabel: function (_dim, _timeout, _label) {
            var dim = _dim || false;
            var timeout = _timeout || 50000;
            var label = _label || 'Loading...';
            return ProgressIndicator.showBarWithLabel(dim, timeout, label);
          },
  
          showSuccess: function (_dim, _label) {
            var dim = _dim || false;
            var label = _label || 'Success';
            return ProgressIndicator.showSuccess(dim, label);
          },
  
          showText: function (_dim, _text, _position) {
            var dim = _dim || false;
            var text = _text || 'Warning';
            var position = _position || 'center';
            return ProgressIndicator.showText(dim, text, position);
          },
  
          hide: function () {
            return ProgressIndicator.hide();
          }
        };
  
      }]);
  
  // install   :      cordova plugin add https://github.com/phonegap-build/PushPlugin.git
  // link      :      https://github.com/phonegap-build/PushPlugin
  
    angular.module('ngCordova.plugins.push', [])
  
      .factory('$cordovaPush', ['$q', '$window', '$rootScope', '$timeout', function ($q, $window, $rootScope, $timeout) {
  
        return {
          onNotification: function (notification) {
            $timeout(function () {
              $rootScope.$broadcast('$cordovaPush:notificationReceived', notification);
            });
          },
  
          register: function (config) {
            var q = $q.defer();
            var injector;
            if (config !== undefined && config.ecb === undefined) {
              if (document.querySelector('[ng-app]') === null) {
                injector = 'document.body';
              }
              else {
                injector = 'document.querySelector(\'[ng-app]\')';
              }
              config.ecb = 'angular.element(' + injector + ').injector().get(\'$cordovaPush\').onNotification';
            }
  
            $window.plugins.pushNotification.register(function (token) {
              q.resolve(token);
            }, function (error) {
              q.reject(error);
            }, config);
  
            return q.promise;
          },
  
          unregister: function (options) {
            var q = $q.defer();
            $window.plugins.pushNotification.unregister(function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            }, options);
  
            return q.promise;
          },
  
          // iOS only
          setBadgeNumber: function (number) {
            var q = $q.defer();
            $window.plugins.pushNotification.setApplicationIconBadgeNumber(function (result) {
              q.resolve(result);
            }, function (error) {
              q.reject(error);
            }, number);
            return q.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add https://github.com/cordova-sms/cordova-sms-plugin.git
  // link      :      https://github.com/cordova-sms/cordova-sms-plugin
  
    /* globals sms: true */
    angular.module('ngCordova.plugins.sms', [])
  
      .factory('$cordovaSms', ['$q', function ($q) {
  
        return {
          send: function (number, message, options) {
            var q = $q.defer();
            sms.send(number, message, options, function (res) {
              q.resolve(res);
            }, function (err) {
              q.reject(err);
            });
            return q.promise;
          }
        };
  
      }]);
  
  // install   :      cordova plugin add https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin.git
  // link      :      https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin
  
  // NOTE: shareViaEmail -> if user cancels sharing email, success is still called
  // TODO: add support for iPad
  
    angular.module('ngCordova.plugins.socialSharing', [])
  
      .factory('$cordovaSocialSharing', ['$q', '$window', function ($q, $window) {
  
        return {
          share: function (message, subject, file, link) {
            var q = $q.defer();
            subject = subject || null;
            file = file || null;
            link = link || null;
            $window.plugins.socialsharing.share(message, subject, file, link, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          shareViaTwitter: function (message, file, link) {
            var q = $q.defer();
            file = file || null;
            link = link || null;
            $window.plugins.socialsharing.shareViaTwitter(message, file, link, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          shareViaWhatsApp: function (message, file, link) {
            var q = $q.defer();
            file = file || null;
            link = link || null;
            $window.plugins.socialsharing.shareViaWhatsApp(message, file, link, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          shareViaFacebook: function (message, file, link) {
            var q = $q.defer();
            message = message || null;
            file = file || null;
            link = link || null;
            $window.plugins.socialsharing.shareViaFacebook(message, file, link, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          shareViaFacebookWithPasteMessageHint: function (message, file, link, pasteMessageHint) {
            var q = $q.defer();
            file = file || null;
            link = link || null;
            $window.plugins.socialsharing.shareViaFacebookWithPasteMessageHint(message, file, link, pasteMessageHint, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          shareViaSMS: function (message, commaSeparatedPhoneNumbers) {
            var q = $q.defer();
            $window.plugins.socialsharing.shareViaSMS(message, commaSeparatedPhoneNumbers, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          shareViaEmail: function (message, subject, toArr, ccArr, bccArr, fileArr) {
            var q = $q.defer();
            toArr = toArr || null;
            ccArr = ccArr || null;
            bccArr = bccArr || null;
            fileArr = fileArr || null;
            $window.plugins.socialsharing.shareViaEmail(message, subject, toArr, ccArr, bccArr, fileArr, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          shareVia: function (via, message, subject, file, link) {
            var q = $q.defer();
            message = message || null;
            subject = subject || null;
            file = file || null;
            link = link || null;
            $window.plugins.socialsharing.shareVia(via, message, subject, file, link, function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          canShareViaEmail: function () {
            var q = $q.defer();
            $window.plugins.socialsharing.canShareViaEmail(function () {
              q.resolve(true);
            }, function () {
              q.reject(false);
            });
            return q.promise;
          },
  
          canShareVia: function (via, message, subject, file, link) {
            var q = $q.defer();
            $window.plugins.socialsharing.canShareVia(via, message, subject, file, link, function (success) {
              q.resolve(success);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          available: function () {
            var q = $q.defer();
            window.plugins.socialsharing.available(function (isAvailable) {
              if (isAvailable) {
                q.resolve();
              }
              else {
                q.reject();
              }
            });
          }
        };
      }]);
  
  // install   :       cordova plugin add https://github.com/Paldom/SpinnerDialog.git
  // link      :       https://github.com/Paldom/SpinnerDialog
  
    angular.module('ngCordova.plugins.spinnerDialog', [])
  
      .factory('$cordovaSpinnerDialog', ['$window', function ($window) {
  
        return {
          show: function (title, message, fixed) {
            fixed = fixed || false;
            return $window.plugins.spinnerDialog.show(title, message, fixed);
          },
          hide: function () {
            return $window.plugins.spinnerDialog.hide();
          }
        };
  
      }]);
  
  // install   :      cordova plugin add cordova-plugin-splashscreen
  // link      :      https://github.com/apache/cordova-plugin-splashscreen
  
    angular.module('ngCordova.plugins.splashscreen', [])
  
      .factory('$cordovaSplashscreen', [function () {
  
        return {
          hide: function () {
            return navigator.splashscreen.hide();
          },
  
          show: function () {
            return navigator.splashscreen.show();
          }
        };
  
      }]);
  
  // install   :      cordova plugin add https://github.com/litehelpers/Cordova-sqlite-storage.git
  // link      :      https://github.com/litehelpers/Cordova-sqlite-storage
  
    angular.module('ngCordova.plugins.sqlite', [])
  
      .factory('$cordovaSQLite', ['$q', '$window', function ($q, $window) {
  
        return {
          openDB: function (options, background) {
            if (typeof options !== 'object') {
              options = {name: options};
            }
            if (typeof background !== 'undefined') {
              options.bgType = background;
            }
  
            return $window.sqlitePlugin.openDatabase(options);
          },
  
          execute: function (db, query, binding) {
            var q = $q.defer();
            db.transaction(function (tx) {
              tx.executeSql(query, binding, function (tx, result) {
                  q.resolve(result);
                },
                function (transaction, error) {
                  q.reject(error);
                });
            });
            return q.promise;
          },
  
          insertCollection: function (db, query, bindings) {
            var q = $q.defer();
            var coll = bindings.slice(0); // clone collection
  
            db.transaction(function (tx) {
              (function insertOne() {
                var record = coll.splice(0, 1)[0]; // get the first record of coll and reduce coll by one
                try {
                  tx.executeSql(query, record, function (tx, result) {
                    if (coll.length === 0) {
                      q.resolve(result);
                    } else {
                      insertOne();
                    }
                  }, function (transaction, error) {
                    q.reject(error);
                    return;
                  });
                } catch (exception) {
                  q.reject(exception);
                }
              })();
            });
            return q.promise;
          },
  
          nestedExecute: function (db, query1, query2, binding1, binding2) {
            var q = $q.defer();
  
            db.transaction(function (tx) {
                tx.executeSql(query1, binding1, function (tx, result) {
                  q.resolve(result);
                  tx.executeSql(query2, binding2, function (tx, res) {
                    q.resolve(res);
                  });
                });
              },
              function (transaction, error) {
                q.reject(error);
              });
  
            return q.promise;
          },
  
          deleteDB: function (dbName) {
            var q = $q.defer();
  
            $window.sqlitePlugin.deleteDatabase(dbName, function (success) {
              q.resolve(success);
            }, function (error) {
              q.reject(error);
            });
  
            return q.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add cordova-plugin-statusbar
  // link      :      https://github.com/apache/cordova-plugin-statusbar
  
    /* globals StatusBar: true */
    angular.module('ngCordova.plugins.statusbar', [])
  
      .factory('$cordovaStatusbar', [function () {
  
        return {
  
          /**
           * @param {boolean} bool
           */
          overlaysWebView: function (bool) {
            return StatusBar.overlaysWebView(!!bool);
          },
  
          STYLES: {
            DEFAULT: 0,
            LIGHT_CONTENT: 1,
            BLACK_TRANSLUCENT: 2,
            BLACK_OPAQUE: 3
          },
  
          /**
           * @param {number} style
           */
          style: function (style) {
            switch (style) {
              // Default
              case 0:
                return StatusBar.styleDefault();
  
              // LightContent
              case 1:
                return StatusBar.styleLightContent();
  
              // BlackTranslucent
              case 2:
                return StatusBar.styleBlackTranslucent();
  
              // BlackOpaque
              case 3:
                return StatusBar.styleBlackOpaque();
  
              default:
                return StatusBar.styleDefault();
            }
          },
  
          // supported names:
          // black, darkGray, lightGray, white, gray, red, green,
          // blue, cyan, yellow, magenta, orange, purple, brown
          styleColor: function (color) {
            return StatusBar.backgroundColorByName(color);
          },
  
          styleHex: function (colorHex) {
            return StatusBar.backgroundColorByHexString(colorHex);
          },
  
          hide: function () {
            return StatusBar.hide();
          },
  
          show: function () {
            return StatusBar.show();
          },
  
          isVisible: function () {
            return StatusBar.isVisible;
          }
        };
      }]);
  
  // install   :      cordova plugin add https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin.git
  // link      :      https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin
  
    angular.module('ngCordova.plugins.toast', [])
  
      .factory('$cordovaToast', ['$q', '$window', function ($q, $window) {
  
        return {
          showShortTop: function (message) {
            var q = $q.defer();
            $window.plugins.toast.showShortTop(message, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          showShortCenter: function (message) {
            var q = $q.defer();
            $window.plugins.toast.showShortCenter(message, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          showShortBottom: function (message) {
            var q = $q.defer();
            $window.plugins.toast.showShortBottom(message, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          showLongTop: function (message) {
            var q = $q.defer();
            $window.plugins.toast.showLongTop(message, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          showLongCenter: function (message) {
            var q = $q.defer();
            $window.plugins.toast.showLongCenter(message, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
          showLongBottom: function (message) {
            var q = $q.defer();
            $window.plugins.toast.showLongBottom(message, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          },
  
  
          show: function (message, duration, position) {
            var q = $q.defer();
            $window.plugins.toast.show(message, duration, position, function (response) {
              q.resolve(response);
            }, function (error) {
              q.reject(error);
            });
            return q.promise;
          }
        };
  
      }]);
  
  // install   :      cordova plugin add https://github.com/leecrossley/cordova-plugin-touchid.git
  // link      :      https://github.com/leecrossley/cordova-plugin-touchid
  
    /* globals touchid: true */
    angular.module('ngCordova.plugins.touchid', [])
  
      .factory('$cordovaTouchID', ['$q', function ($q) {
  
        return {
          checkSupport: function () {
            var defer = $q.defer();
            if (!window.cordova) {
              defer.reject('Not supported without cordova.js');
            } else {
              touchid.checkSupport(function (value) {
                defer.resolve(value);
              }, function (err) {
                defer.reject(err);
              });
            }
  
            return defer.promise;
          },
  
          authenticate: function (authReasonText) {
            var defer = $q.defer();
            if (!window.cordova) {
              defer.reject('Not supported without cordova.js');
            } else {
              touchid.authenticate(function (value) {
                defer.resolve(value);
              }, function (err) {
                defer.reject(err);
              }, authReasonText);
            }
  
            return defer.promise;
          }
        };
      }]);
  
  // install   :      cordova plugin add cordova-plugin-vibration
  // link      :      https://github.com/apache/cordova-plugin-vibration
  
    angular.module('ngCordova.plugins.vibration', [])
  
      .factory('$cordovaVibration', [function () {
  
        return {
          vibrate: function (times) {
            return navigator.notification.vibrate(times);
          },
          vibrateWithPattern: function (pattern, repeat) {
            return navigator.notification.vibrateWithPattern(pattern, repeat);
          },
          cancelVibration: function () {
            return navigator.notification.cancelVibration();
          }
        };
      }]);
  
  // install   :    cordova plugin add https://github.com/EddyVerbruggen/VideoCapturePlus-PhoneGap-Plugin.git
  // link      :    https://github.com/EddyVerbruggen/VideoCapturePlus-PhoneGap-Plugin
  
    angular.module('ngCordova.plugins.videoCapturePlus', [])
  
      .provider('$cordovaVideoCapturePlus', [function () {
  
        var defaultOptions = {};
  
  
        /**
         * the nr of videos to record, default 1 (on iOS always 1)
         *
         * @param limit
         */
        this.setLimit = function setLimit(limit) {
          defaultOptions.limit = limit;
        };
  
  
        /**
         * max duration in seconds, default 0, which is 'forever'
         *
         * @param seconds
         */
        this.setMaxDuration = function setMaxDuration(seconds) {
          defaultOptions.duration = seconds;
        };
  
  
        /**
         * set to true to override the default low quality setting
         *
         * @param {Boolean} highquality
         */
        this.setHighQuality = function setHighQuality(highquality) {
          defaultOptions.highquality = highquality;
        };
  
        /**
         * you'll want to sniff the user-Agent/device and pass the best overlay based on that..
         * set to true to override the default backfacing camera setting. iOS: works fine, Android: YMMV (#18)
         *
         * @param {Boolean} frontcamera
         */
        this.useFrontCamera = function useFrontCamera(frontcamera) {
          defaultOptions.frontcamera = frontcamera;
        };
  
  
        /**
         * put the png in your www folder
         *
         * @param {String} imageUrl
         */
        this.setPortraitOverlay = function setPortraitOverlay(imageUrl) {
          defaultOptions.portraitOverlay = imageUrl;
        };
  
  
        /**
         *
         * @param {String} imageUrl
         */
        this.setLandscapeOverlay = function setLandscapeOverlay(imageUrl) {
          defaultOptions.landscapeOverlay = imageUrl;
        };
  
  
        /**
         * iOS only
         *
         * @param text
         */
        this.setOverlayText = function setOverlayText(text) {
          defaultOptions.overlayText = text;
        };
  
  
        this.$get = ['$q', '$window', function ($q, $window) {
          return {
            captureVideo: function (options) {
              var q = $q.defer();
  
              if (!$window.plugins.videocaptureplus) {
                q.resolve(null);
                return q.promise;
              }
  
              $window.plugins.videocaptureplus.captureVideo(q.resolve, q.reject,
                angular.extend({}, defaultOptions, options));
  
              return q.promise;
            }
          };
        }];
      }]);
  
  // install  :     cordova plugin add https://github.com/MobileChromeApps/zip.git
  // link     :     https://github.com/MobileChromeApps/zip
  
    angular.module('ngCordova.plugins.zip', [])
  
      .factory('$cordovaZip', ['$q', '$window', function ($q, $window) {
  
        return {
          unzip: function (source, destination) {
            var q = $q.defer();
  
            $window.zip.unzip(source, destination, function (isError) {
              if (isError === 0) {
                q.resolve();
              } else {
                q.reject();
              }
            }, function (progressEvent) {
              q.notify(progressEvent);
            });
  
            return q.promise;
          }
        };
      }]);
  
    angular.module("oauth.providers", ["oauth.utils"])
  
      .factory("$cordovaOauth", ["$q", '$http', "$cordovaOauthUtility", function ($q, $http, $cordovaOauthUtility) {
  
        return {
  
          /*
           * Sign into the ADFS service (ADFS 3.0 onwards)
           *
           * @param    string clientId (client registered in ADFS, with redirect_uri configured to: http://localhost/callback)
           * @param	 string adfsServer (url of the ADFS Server)
           * @param	 string relyingPartyId (url of the Relying Party (resource relying on ADFS for authentication) configured in ADFS)
           * @return   promise
           */
          adfs: function (clientId, adfsServer, relyingPartyId) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var browserRef = window.open(adfsServer + '/adfs/oauth2/authorize?response_type=code&client_id=' + clientId + '&redirect_uri=http://localhost/callback&resource=' + relyingPartyId, '_blank', 'location=no');
  
                browserRef.addEventListener("loadstart", function (event) {
                  if ((event.url).indexOf('http://localhost/callback') === 0) {
                    var requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: adfsServer + "/adfs/oauth2/token",
                      data: "client_id=" + clientId + "&code=" + requestToken + "&redirect_uri=http://localhost/callback&grant_type=authorization_code"
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Dropbox service
           *
           * @param    string appKey
           * @param    object options
           * @return   promise
           */
          dropbox: function (appKey, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open("https://www.dropbox.com/1/oauth2/authorize?client_id=" + appKey + "&redirect_uri=" + redirect_uri + "&response_type=token", "_blank", "location=no,clearsessioncache=yes,clearcache=yes");
                browserRef.addEventListener("loadstart", function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({
                        access_token: parameterMap.access_token,
                        token_type: parameterMap.token_type,
                        uid: parameterMap.uid
                      });
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Digital Ocean service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    object options
           * @return   promise
           */
          digitalOcean: function (clientId, clientSecret, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open("https://cloud.digitalocean.com/v1/oauth/authorize?client_id=" + clientId + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=read%20write", "_blank", "location=no,clearsessioncache=yes,clearcache=yes");
                browserRef.addEventListener("loadstart", function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    var requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: "https://cloud.digitalocean.com/v1/oauth/token",
                      data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&redirect_uri=" + redirect_uri + "&grant_type=authorization_code" + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Google service
           *
           * @param    string clientId
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          google: function (clientId, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://accounts.google.com/o/oauth2/auth?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&scope=' + appScope.join(" ") + '&approval_prompt=force&response_type=token', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener("loadstart", function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({
                        access_token: parameterMap.access_token,
                        token_type: parameterMap.token_type,
                        expires_in: parameterMap.expires_in
                      });
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the GitHub service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          github: function (clientId, clientSecret, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://github.com/login/oauth/authorize?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&scope=' + appScope.join(","), '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http.defaults.headers.post.accept = 'application/json';
                    $http({
                      method: "post",
                      url: "https://github.com/login/oauth/access_token",
                      data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&redirect_uri=" + redirect_uri + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Facebook service
           *
           * @param    string clientId
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          facebook: function (clientId, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var flowUrl = "https://www.facebook.com/v2.0/dialog/oauth?client_id=" + clientId + "&redirect_uri=" + redirect_uri + "&response_type=token&scope=" + appScope.join(",");
                if (options !== undefined && options.hasOwnProperty("auth_type")) {
                  flowUrl += "&auth_type=" + options.auth_type;
                }
                var browserRef = window.open(flowUrl, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({access_token: parameterMap.access_token, expires_in: parameterMap.expires_in});
                    } else {
                      if ((event.url).indexOf("error_code=100") !== 0)
                        deferred.reject("Facebook returned error_code=100: Invalid permissions");
                      else
                        deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the LinkedIn service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    array appScope
           * @param    string state
           * @param    object options
           * @return   promise
           */
          linkedin: function (clientId, clientSecret, appScope, state, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
  
                var browserRef = window.open('https://www.linkedin.com/uas/oauth2/authorization?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&scope=' + appScope.join(" ") + '&response_type=code&state=' + state, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
  
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: "https://www.linkedin.com/uas/oauth2/accessToken",
                      data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&redirect_uri=" + redirect_uri + "&grant_type=authorization_code" + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Instagram service
           *
           * @param    string clientId
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          instagram: function (clientId, appScope, options) {
            var deferred = $q.defer();
  
            var split_tokens = {
              'code': '?',
              'token': '#'
            };
  
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                var response_type = "token";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                  if (options.hasOwnProperty("response_type")) {
                    response_type = options.response_type;
                  }
                }
  
                var browserRef = window.open('https://api.instagram.com/oauth/authorize/?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&scope=' + appScope.join(" ") + '&response_type=' + response_type, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split(split_tokens[response_type])[1];
                    var parameterMap = $cordovaOauthUtility.parseResponseParameters(callbackResponse);
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({access_token: parameterMap.access_token});
                    } else if (parameterMap.code !== undefined && parameterMap.code !== null) {
                      deferred.resolve({code: parameterMap.code});
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Box service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    string appState
           * @param    object options
           * @return   promise
           */
          box: function (clientId, clientSecret, appState, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://app.box.com/api/oauth2/authorize/?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&state=' + appState + '&response_type=code', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: "https://app.box.com/api/oauth2/token",
                      data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&redirect_uri=" + redirect_uri + "&grant_type=authorization_code" + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Reddit service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          reddit: function (clientId, clientSecret, appScope, compact, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://ssl.reddit.com/api/v1/authorize?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&duration=permanent&state=ngcordovaoauth&scope=' + appScope.join(",") + '&response_type=code', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http.defaults.headers.post.Authorization = 'Basic ' + btoa(clientId + ":" + clientSecret);
                    $http({
                      method: "post",
                      url: "https://ssl.reddit.com/api/v1/access_token",
                      data: "redirect_uri=" + redirect_uri + "&grant_type=authorization_code" + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Twitter service
           * Note that this service requires jsSHA for generating HMAC-SHA1 Oauth 1.0 signatures
           *
           * @param    string clientId
           * @param    string clientSecret
           * @return   promise
           */
          twitter: function (clientId, clientSecret, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
  
                if (typeof jsSHA !== "undefined") {
                  var oauthObject = {
                    oauth_consumer_key: clientId,
                    oauth_nonce: $cordovaOauthUtility.createNonce(10),
                    oauth_signature_method: "HMAC-SHA1",
                    oauth_timestamp: Math.round((new Date()).getTime() / 1000.0),
                    oauth_version: "1.0"
                  };
                  var signatureObj = $cordovaOauthUtility.createSignature("POST", "https://api.twitter.com/oauth/request_token", oauthObject, {oauth_callback: redirect_uri}, clientSecret);
                  $http({
                    method: "post",
                    url: "https://api.twitter.com/oauth/request_token",
                    headers: {
                      "Authorization": signatureObj.authorization_header,
                      "Content-Type": "application/x-www-form-urlencoded"
                    },
                    data: "oauth_callback=" + encodeURIComponent(redirect_uri)
                  })
                    .success(function (requestTokenResult) {
                      var requestTokenParameters = (requestTokenResult).split("&");
                      var parameterMap = {};
                      for (var i = 0; i < requestTokenParameters.length; i++) {
                        parameterMap[requestTokenParameters[i].split("=")[0]] = requestTokenParameters[i].split("=")[1];
                      }
                      if (parameterMap.hasOwnProperty("oauth_token") === false) {
                        deferred.reject("Oauth request token was not received");
                      }
                      var browserRef = window.open('https://api.twitter.com/oauth/authenticate?oauth_token=' + parameterMap.oauth_token, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                      browserRef.addEventListener('loadstart', function (event) {
                        if ((event.url).indexOf(redirect_uri) === 0) {
                          var callbackResponse = (event.url).split("?")[1];
                          var responseParameters = (callbackResponse).split("&");
                          var parameterMap = {};
                          for (var i = 0; i < responseParameters.length; i++) {
                            parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                          }
                          if (parameterMap.hasOwnProperty("oauth_verifier") === false) {
                            deferred.reject("Browser authentication failed to complete.  No oauth_verifier was returned");
                          }
                          delete oauthObject.oauth_signature;
                          oauthObject.oauth_token = parameterMap.oauth_token;
                          var signatureObj = $cordovaOauthUtility.createSignature("POST", "https://api.twitter.com/oauth/access_token", oauthObject, {oauth_verifier: parameterMap.oauth_verifier}, clientSecret);
                          $http({
                            method: "post",
                            url: "https://api.twitter.com/oauth/access_token",
                            headers: {
                              "Authorization": signatureObj.authorization_header
                            },
                            params: {
                              "oauth_verifier": parameterMap.oauth_verifier
                            }
                          })
                            .success(function (result) {
                              var accessTokenParameters = result.split("&");
                              var parameterMap = {};
                              for (var i = 0; i < accessTokenParameters.length; i++) {
                                parameterMap[accessTokenParameters[i].split("=")[0]] = accessTokenParameters[i].split("=")[1];
                              }
                              if (parameterMap.hasOwnProperty("oauth_token_secret") === false) {
                                deferred.reject("Oauth access token was not received");
                              }
                              deferred.resolve(parameterMap);
                            })
                            .error(function (error) {
                              deferred.reject(error);
                            })
                            .finally(function () {
                              setTimeout(function () {
                                browserRef.close();
                              }, 10);
                            });
                        }
                      });
                      browserRef.addEventListener('exit', function (event) {
                        deferred.reject("The sign in flow was canceled");
                      });
                    })
                    .error(function (error) {
                      deferred.reject(error);
                    });
                } else {
                  deferred.reject("Missing jsSHA JavaScript library");
                }
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Meetup service
           *
           * @param    string clientId
           * @param    object options
           * @return   promise
           */
          meetup: function (clientId, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://secure.meetup.com/oauth2/authorize/?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&response_type=token', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = {};
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve(parameterMap);
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Salesforce service
           *
           * Suggestion: use salesforce oauth with forcetk.js(as SDK)
           *
           * @param    string loginUrl (such as: https://login.salesforce.com ; please notice community login)
           * @param    string clientId (copy from connection app info)
           * @param    string redirectUri (callback url in connection app info)
           * @return   promise
           */
          salesforce: function (loginUrl, clientId) {
            var redirectUri = 'http://localhost/callback';
            var getAuthorizeUrl = function (loginUrl, clientId, redirectUri) {
              return loginUrl + 'services/oauth2/authorize?display=touch' +
                '&response_type=token&client_id=' + escape(clientId) +
                '&redirect_uri=' + escape(redirectUri);
            };
            var startWith = function (string, str) {
              return (string.substr(0, str.length) === str);
            };
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var browserRef = window.open(getAuthorizeUrl(loginUrl, clientId, redirectUri), "_blank", "location=no,clearsessioncache=yes,clearcache=yes");
                browserRef.addEventListener("loadstart", function (event) {
                  if (startWith(event.url, redirectUri)) {
                    var oauthResponse = {};
  
                    var fragment = (event.url).split('#')[1];
  
                    if (fragment) {
                      var nvps = fragment.split('&');
                      for (var nvp in nvps) {
                        var parts = nvps[nvp].split('=');
                        oauthResponse[parts[0]] = unescape(parts[1]);
                      }
                    }
  
                    if (typeof oauthResponse === 'undefined' ||
                      typeof oauthResponse.access_token === 'undefined') {
                      deferred.reject("Problem authenticating");
                    } else {
                      deferred.resolve(oauthResponse);
                    }
                    setTimeout(function () {
                      browserRef.close();
                    }, 10);
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Strava service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          strava: function (clientId, clientSecret, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://www.strava.com/oauth/authorize?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&scope=' + appScope.join(",") + '&response_type=code&approval_prompt=force', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: "https://www.strava.com/oauth/token",
                      data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Withings service
           * Note that this service requires jsSHA for generating HMAC-SHA1 Oauth 1.0 signatures
           *
           * @param    string clientId
           * @param    string clientSecret
           * @return   promise
           */
          withings: function (clientId, clientSecret) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                if (typeof jsSHA !== "undefined") {
  
                  // Step 1 : get a oAuth "request token"
                  var oauthObject = $cordovaOauthUtility.generateOauthParametersInstance(clientId);
                  oauthObject.oauth_callback = 'http://localhost/callback';
  
                  var requestTokenUrlBase = "https://oauth.withings.com/account/request_token";
                  var signatureObj = $cordovaOauthUtility.createSignature("GET", requestTokenUrlBase, {}, oauthObject, clientSecret);
                  oauthObject.oauth_signature = signatureObj.signature;
  
                  var requestTokenParameters = $cordovaOauthUtility.generateUrlParameters(oauthObject);
  
                  $http({method: "get", url: requestTokenUrlBase + "?" + requestTokenParameters})
                    .success(function (requestTokenResult) {
  
                      // Step 2 : End-user authorization
                      var parameterMap = $cordovaOauthUtility.parseResponseParameters(requestTokenResult);
                      if (parameterMap.hasOwnProperty("oauth_token") === false) {
                        deferred.reject("Oauth request token was not received");
                      }
                      var oauthObject = $cordovaOauthUtility.generateOauthParametersInstance(clientId);
                      oauthObject.oauth_token = parameterMap.oauth_token;
  
                      // used in step 3
                      var oauthTokenSecret = parameterMap.oauth_token_secret;
  
                      var authorizeUrlBase = "https://oauth.withings.com/account/authorize";
                      var signatureObj = $cordovaOauthUtility.createSignature("GET", authorizeUrlBase, {}, oauthObject, clientSecret);
                      oauthObject.oauth_signature = signatureObj.signature;
  
                      var authorizeParameters = $cordovaOauthUtility.generateUrlParameters(oauthObject);
                      var browserRef = window.open(authorizeUrlBase + '?' + authorizeParameters, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
  
                      // STEP 3: User Data Access token
                      browserRef.addEventListener('loadstart', function (event) {
                        if ((event.url).indexOf("http://localhost/callback") === 0) {
                          var callbackResponse = (event.url).split("?")[1];
                          parameterMap = $cordovaOauthUtility.parseResponseParameters(callbackResponse);
                          if (parameterMap.hasOwnProperty("oauth_verifier") === false) {
                            deferred.reject("Browser authentication failed to complete.  No oauth_verifier was returned");
                          }
  
                          var oauthObject = $cordovaOauthUtility.generateOauthParametersInstance(clientId);
                          oauthObject.oauth_token = parameterMap.oauth_token;
  
                          var accessTokenUrlBase = "https://oauth.withings.com/account/access_token";
                          var signatureObj = $cordovaOauthUtility.createSignature("GET", accessTokenUrlBase, {}, oauthObject, clientSecret, oauthTokenSecret);
                          oauthObject.oauth_signature = signatureObj.signature;
  
                          var accessTokenParameters = $cordovaOauthUtility.generateUrlParameters(oauthObject);
  
                          $http({method: "get", url: accessTokenUrlBase + '?' + accessTokenParameters})
                            .success(function (result) {
                              var parameterMap = $cordovaOauthUtility.parseResponseParameters(result);
                              if (parameterMap.hasOwnProperty("oauth_token_secret") === false) {
                                deferred.reject("Oauth access token was not received");
                              }
                              deferred.resolve(parameterMap);
                            })
                            .error(function (error) {
                              deferred.reject(error);
                            })
                            .finally(function () {
                              setTimeout(function () {
                                browserRef.close();
                              }, 10);
                            });
                        }
                      });
                      browserRef.addEventListener('exit', function (event) {
                        deferred.reject("The sign in flow was canceled");
                      });
                    })
                    .error(function (error) {
                      deferred.reject(error);
                    });
                } else {
                  deferred.reject("Missing jsSHA JavaScript library");
                }
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Foursquare service
           *
           * @param    string clientId
           * @param    object options
           * @return   promise
           */
          foursquare: function (clientId, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://foursquare.com/oauth2/authenticate?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&response_type=token', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      var promiseResponse = {
                        access_token: parameterMap.access_token,
                        expires_in: parameterMap.expires_in
                      };
                      deferred.resolve(promiseResponse);
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Magento service
           * Note that this service requires jsSHA for generating HMAC-SHA1 Oauth 1.0 signatures
           *
           * @param    string baseUrl
           * @param    string clientId
           * @param    string clientSecret
           * @return   promise
           */
          magento: function (baseUrl, clientId, clientSecret) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                if (typeof jsSHA !== "undefined") {
                  var oauthObject = {
                    oauth_callback: "http://localhost/callback",
                    oauth_consumer_key: clientId,
                    oauth_nonce: $cordovaOauthUtility.createNonce(5),
                    oauth_signature_method: "HMAC-SHA1",
                    oauth_timestamp: Math.round((new Date()).getTime() / 1000.0),
                    oauth_version: "1.0"
                  };
                  var signatureObj = $cordovaOauthUtility.createSignature("POST", baseUrl + "/oauth/initiate", oauthObject, {oauth_callback: "http://localhost/callback"}, clientSecret);
                  $http.defaults.headers.post.Authorization = signatureObj.authorization_header;
                  $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                  $http({
                    method: "post",
                    url: baseUrl + "/oauth/initiate",
                    data: "oauth_callback=http://localhost/callback"
                  })
                    .success(function (requestTokenResult) {
                      var requestTokenParameters = (requestTokenResult).split("&");
                      var parameterMap = {};
                      for (var i = 0; i < requestTokenParameters.length; i++) {
                        parameterMap[requestTokenParameters[i].split("=")[0]] = requestTokenParameters[i].split("=")[1];
                      }
                      if (parameterMap.hasOwnProperty("oauth_token") === false) {
                        deferred.reject("Oauth request token was not received");
                      }
                      var tokenSecret = parameterMap.oauth_token_secret;
                      var browserRef = window.open(baseUrl + '/oauth/authorize?oauth_token=' + parameterMap.oauth_token, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                      browserRef.addEventListener('loadstart', function (event) {
                        if ((event.url).indexOf("http://localhost/callback") === 0) {
                          var callbackResponse = (event.url).split("?")[1];
                          var responseParameters = (callbackResponse).split("&");
                          var parameterMap = {};
                          for (var i = 0; i < responseParameters.length; i++) {
                            parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                          }
                          if (parameterMap.hasOwnProperty("oauth_verifier") === false) {
                            deferred.reject("Browser authentication failed to complete.  No oauth_verifier was returned");
                          }
                          delete oauthObject.oauth_signature;
                          delete oauthObject.oauth_callback;
                          oauthObject.oauth_token = parameterMap.oauth_token;
                          oauthObject.oauth_nonce = $cordovaOauthUtility.createNonce(5);
                          oauthObject.oauth_verifier = parameterMap.oauth_verifier;
                          var signatureObj = $cordovaOauthUtility.createSignature("POST", baseUrl + "/oauth/token", oauthObject, {}, clientSecret, tokenSecret);
                          $http.defaults.headers.post.Authorization = signatureObj.authorization_header;
                          $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                          $http({method: "post", url: baseUrl + "/oauth/token"})
                            .success(function (result) {
                              var accessTokenParameters = result.split("&");
                              var parameterMap = {};
                              for (var i = 0; i < accessTokenParameters.length; i++) {
                                parameterMap[accessTokenParameters[i].split("=")[0]] = accessTokenParameters[i].split("=")[1];
                              }
                              if (parameterMap.hasOwnProperty("oauth_token_secret") === false) {
                                deferred.reject("Oauth access token was not received");
                              }
                              deferred.resolve(parameterMap);
                            })
                            .error(function (error) {
                              deferred.reject(error);
                            })
                            .finally(function () {
                              setTimeout(function () {
                                browserRef.close();
                              }, 10);
                            });
                        }
                      });
                      browserRef.addEventListener('exit', function (event) {
                        deferred.reject("The sign in flow was canceled");
                      });
                    })
                    .error(function (error) {
                      deferred.reject(error);
                    });
                } else {
                  deferred.reject("Missing jsSHA JavaScript library");
                }
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Vkontakte service
           *
           * @param    string clientId
           * @param    array appScope (for example: "friends,wall,photos,messages")
           * @return   promise
           */
          vkontakte: function (clientId, appScope) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var browserRef = window.open('https://oauth.vk.com/authorize?client_id=' + clientId + '&redirect_uri=http://oauth.vk.com/blank.html&response_type=token&scope=' + appScope.join(",") + '&display=touch&response_type=token', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  var tmp = (event.url).split("#");
                  if (tmp[0] == 'https://oauth.vk.com/blank.html' || tmp[0] == 'http://oauth.vk.com/blank.html') {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      var output = {access_token: parameterMap.access_token, expires_in: parameterMap.expires_in};
                      if (parameterMap.email !== undefined && parameterMap.email !== null) {
                        output.email = parameterMap.email;
                      }
                      deferred.resolve(output);
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Odnoklassniki service
           *
           * @param    string clientId
           * @param    array appScope (for example: "VALUABLE_ACCESS ,GROUP_CONTENT,VIDEO_CONTENT")
           * @return   promise
           */
          odnoklassniki: function (clientId, appScope) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var browserRef = window.open('http://www.odnoklassniki.ru/oauth/authorize?client_id=' + clientId + '&scope=' + appScope.join(",") + '&response_type=token&redirect_uri=http://localhost/callback' + '&layout=m', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf("http://localhost/callback") === 0) {
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({
                        access_token: parameterMap.access_token,
                        session_secret_key: parameterMap.session_secret_key
                      });
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                    setTimeout(function () {
                      browserRef.close();
                    }, 10);
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
  
          /*
           * Sign into the Imgur service
           *
           * @param    string clientId
           * @param    object options
           * @return   promise
           */
          imgur: function (clientId, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://api.imgur.com/oauth2/authorize?client_id=' + clientId + '&response_type=token', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({
                        access_token: parameterMap.access_token,
                        expires_in: parameterMap.expires_in,
                        account_username: parameterMap.account_username
                      });
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Spotify service
           *
           * @param    string clientId
           * @param    object options
           * @return   promise
           */
          spotify: function (clientId, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://accounts.spotify.com/authorize?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&response_type=token&scope=' + appScope.join(" "), '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({
                        access_token: parameterMap.access_token,
                        expires_in: parameterMap.expires_in,
                        account_username: parameterMap.account_username
                      });
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Uber service
           *
           * @param    string clientId
           * @param    appScope array
           * @param    object options
           * @return   promise
           */
          uber: function (clientId, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://login.uber.com/oauth/authorize?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&response_type=token&scope=' + appScope.join(" "), '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({
                        access_token: parameterMap.access_token,
                        token_type: parameterMap.token_type,
                        expires_in: parameterMap.expires_in,
                        scope: parameterMap.scope
                      });
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Windows Live Connect service
           *
           * @param    string clientId
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          windowsLive: function (clientId, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "https://login.live.com/oauth20_desktop.srf";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://login.live.com/oauth20_authorize.srf?client_id=' + clientId + "&scope=" + appScope.join(",") + '&response_type=token&display=touch' + '&redirect_uri=' + redirect_uri, '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({access_token: parameterMap.access_token, expires_in: parameterMap.expires_in});
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Yammer service
           *
           * @param    string clientId
           * @param    object options
           * @return   promise
           */
          yammer: function (clientId, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://www.yammer.com/dialog/oauth?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&response_type=token', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({access_token: parameterMap.access_token});
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Venmo service
           *
           * @param    string clientId
           * @param    array appScope
           * @param    object options
           * @return   promise
           */
          venmo: function (clientId, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://api.venmo.com/v1/oauth/authorize?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&response_type=token&scope=' + appScope.join(" "), '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({access_token: parameterMap.access_token, expires_in: parameterMap.expires_in});
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Stripe service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    string appScope
           * @param    object options
           * @return   promise
           */
          stripe: function (clientId, clientSecret, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://connect.stripe.com/oauth/authorize?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&scope=' + appScope + '&response_type=code', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf("http://localhost/callback") === 0) {
                    requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: "https://connect.stripe.com/oauth/token",
                      data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&redirect_uri=" + redirect_uri + "&grant_type=authorization_code" + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Rally service
           *
           * @param    string clientId
           * @param    string clientSecret
           * @param    string appScope
           * @param    object options
           * @return   promise
           */
          rally: function (clientId, clientSecret, appScope, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://rally1.rallydev.com/login/oauth2/auth?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&scope=' + appScope + '&response_type=code', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf("http://localhost/callback") === 0) {
                    requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: "https://rally1.rallydev.com/login/oauth2/auth",
                      data: "client_id=" + clientId + "&client_secret=" + clientSecret + "&redirect_uri=" + redirect_uri + "&grant_type=authorization_code" + "&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the FamilySearch service
           *
           * @param    string clientId
           * @param    object options
           * @return   promise
           */
          familySearch: function (clientId, state, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if (cordovaMetadata.hasOwnProperty("cordova-plugin-inappbrowser") === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open("https://ident.familysearch.org/cis-web/oauth2/v3/authorization?client_id=" + clientId + "&redirect_uri=" + redirect_uri + "&response_type=code&state=" + state, "_blank", "location=no,clearsessioncache=yes,clearcache=yes");
                browserRef.addEventListener("loadstart", function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    var requestToken = (event.url).split("code=")[1];
                    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
                    $http({
                      method: "post",
                      url: "https://ident.familysearch.org/cis-web/oauth2/v3/token",
                      data: "client_id=" + clientId + "&redirect_uri=" + redirect_uri + "&grant_type=authorization_code&code=" + requestToken
                    })
                      .success(function (data) {
                        deferred.resolve(data);
                      })
                      .error(function (data, status) {
                        deferred.reject("Problem authenticating");
                      })
                      .finally(function () {
                        setTimeout(function () {
                          browserRef.close();
                        }, 10);
                      });
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          },
  
          /*
           * Sign into the Envato service
           *
           * @param    string clientId
           * @param    object options
           * @return   promise
           */
          envato: function (clientId, options) {
            var deferred = $q.defer();
            if (window.cordova) {
              var cordovaMetadata = cordova.require("cordova/plugin_list").metadata;
              if ($cordovaOauthUtility.isInAppBrowserInstalled(cordovaMetadata) === true) {
                var redirect_uri = "http://localhost/callback";
                if (options !== undefined) {
                  if (options.hasOwnProperty("redirect_uri")) {
                    redirect_uri = options.redirect_uri;
                  }
                }
                var browserRef = window.open('https://api.envato.com/authorization?client_id=' + clientId + '&redirect_uri=' + redirect_uri + '&response_type=token', '_blank', 'location=no,clearsessioncache=yes,clearcache=yes');
                browserRef.addEventListener('loadstart', function (event) {
                  if ((event.url).indexOf(redirect_uri) === 0) {
                    browserRef.removeEventListener("exit", function (event) {
                    });
                    browserRef.close();
                    var callbackResponse = (event.url).split("#")[1];
                    var responseParameters = (callbackResponse).split("&");
                    var parameterMap = [];
                    for (var i = 0; i < responseParameters.length; i++) {
                      parameterMap[responseParameters[i].split("=")[0]] = responseParameters[i].split("=")[1];
                    }
                    if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
                      deferred.resolve({access_token: parameterMap.access_token, expires_in: parameterMap.expires_in});
                    } else {
                      deferred.reject("Problem authenticating");
                    }
                  }
                });
                browserRef.addEventListener('exit', function (event) {
                  deferred.reject("The sign in flow was canceled");
                });
              } else {
                deferred.reject("Could not find InAppBrowser plugin");
              }
            } else {
              deferred.reject("Cannot authenticate via a web browser");
            }
            return deferred.promise;
          }
  
        };
  
      }]);
  
    /*
     * Cordova AngularJS Oauth
     *
     * Created by Nic Raboy
     * http://www.nraboy.com
     *
     *
     *
     * DESCRIPTION:
     *
     * Use Oauth sign in for various web services.
     *
     *
     * REQUIRES:
     *
     *    Apache Cordova 3.5+
     *    Apache InAppBrowser Plugin
     *    Apache Cordova Whitelist Plugin
     *
     *
     * SUPPORTS:
     *
     *    Dropbox
     *    Digital Ocean
     *    Google
     *    GitHub
     *    Facebook
     *    LinkedIn
     *    Instagram
     *    Box
     *    Reddit
     *    Twitter
     *    Meetup
     *    Salesforce
     *    Strava
     *    Withings
     *    Foursquare
     *    Magento
     *    vkontakte
     *    Odnoklassniki
     *    ADFS
     *    Imgur
     *    Spotify
     *    Uber
     *    Windows Live Connect
     *    Yammer
     *    Venmo
     *    Stripe
     *    Rally
     *    Family Search
     *    Envato
     */
  
    angular.module("ngCordovaOauth", [
      "oauth.providers",
      "oauth.utils"
    ]);
  
    angular.module("oauth.utils", [])
  
      .factory("$cordovaOauthUtility", ["$q", function ($q) {
  
        return {
  
          /*
           * Check to see if the mandatory InAppBrowser plugin is installed
           *
           * @param
           * @return   boolean
           */
          isInAppBrowserInstalled: function (cordovaMetadata) {
            var inAppBrowserNames = ["cordova-plugin-inappbrowser", "org.apache.cordova.inappbrowser"];
  
            return inAppBrowserNames.some(function (name) {
              return cordovaMetadata.hasOwnProperty(name);
            });
          },
  
          /*
           * Sign an Oauth 1.0 request
           *
           * @param    string method
           * @param    string endPoint
           * @param    object headerParameters
           * @param    object bodyParameters
           * @param    string secretKey
           * @param    string tokenSecret (optional)
           * @return   object
           */
          createSignature: function (method, endPoint, headerParameters, bodyParameters, secretKey, tokenSecret) {
            if (typeof jsSHA !== "undefined") {
              var headerAndBodyParameters = angular.copy(headerParameters);
              var bodyParameterKeys = Object.keys(bodyParameters);
              for (var i = 0; i < bodyParameterKeys.length; i++) {
                headerAndBodyParameters[bodyParameterKeys[i]] = encodeURIComponent(bodyParameters[bodyParameterKeys[i]]);
              }
              var signatureBaseString = method + "&" + encodeURIComponent(endPoint) + "&";
              var headerAndBodyParameterKeys = (Object.keys(headerAndBodyParameters)).sort();
              for (i = 0; i < headerAndBodyParameterKeys.length; i++) {
                if (i == headerAndBodyParameterKeys.length - 1) {
                  signatureBaseString += encodeURIComponent(headerAndBodyParameterKeys[i] + "=" + headerAndBodyParameters[headerAndBodyParameterKeys[i]]);
                } else {
                  signatureBaseString += encodeURIComponent(headerAndBodyParameterKeys[i] + "=" + headerAndBodyParameters[headerAndBodyParameterKeys[i]] + "&");
                }
              }
              var oauthSignatureObject = new jsSHA(signatureBaseString, "TEXT");
  
              var encodedTokenSecret = '';
              if (tokenSecret) {
                encodedTokenSecret = encodeURIComponent(tokenSecret);
              }
  
              headerParameters.oauth_signature = encodeURIComponent(oauthSignatureObject.getHMAC(encodeURIComponent(secretKey) + "&" + encodedTokenSecret, "TEXT", "SHA-1", "B64"));
              var headerParameterKeys = Object.keys(headerParameters);
              var authorizationHeader = 'OAuth ';
              for (i = 0; i < headerParameterKeys.length; i++) {
                if (i == headerParameterKeys.length - 1) {
                  authorizationHeader += headerParameterKeys[i] + '="' + headerParameters[headerParameterKeys[i]] + '"';
                } else {
                  authorizationHeader += headerParameterKeys[i] + '="' + headerParameters[headerParameterKeys[i]] + '",';
                }
              }
              return {
                signature_base_string: signatureBaseString,
                authorization_header: authorizationHeader,
                signature: headerParameters.oauth_signature
              };
            } else {
              return "Missing jsSHA JavaScript library";
            }
          },
  
          /*
           * Create Random String Nonce
           *
           * @param    integer length
           * @return   string
           */
          createNonce: function (length) {
            var text = "";
            var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            for (var i = 0; i < length; i++) {
              text += possible.charAt(Math.floor(Math.random() * possible.length));
            }
            return text;
          },
  
          generateUrlParameters: function (parameters) {
            var sortedKeys = Object.keys(parameters);
            sortedKeys.sort();
  
            var params = "";
            var amp = "";
  
            for (var i = 0; i < sortedKeys.length; i++) {
              params += amp + sortedKeys[i] + "=" + parameters[sortedKeys[i]];
              amp = "&";
            }
  
            return params;
          },
  
          parseResponseParameters: function (response) {
            if (response.split) {
              var parameters = response.split("&");
              var parameterMap = {};
              for (var i = 0; i < parameters.length; i++) {
                parameterMap[parameters[i].split("=")[0]] = parameters[i].split("=")[1];
              }
              return parameterMap;
            }
            else {
              return {};
            }
          },
  
          generateOauthParametersInstance: function (consumerKey) {
            var nonceObj = new jsSHA(Math.round((new Date()).getTime() / 1000.0), "TEXT");
            var oauthObject = {
              oauth_consumer_key: consumerKey,
              oauth_nonce: nonceObj.getHash("SHA-1", "HEX"),
              oauth_signature_method: "HMAC-SHA1",
              oauth_timestamp: Math.round((new Date()).getTime() / 1000.0),
              oauth_version: "1.0"
            };
            return oauthObject;
          }
  
        };
  
      }]);
  
  })();
  