添加极光推送插件
authorqiaowei <jov123@163.com>
Wed, 7 Aug 2019 02:26:16 +0000 (10:26 +0800)
committerqiaowei <jov123@163.com>
Wed, 7 Aug 2019 02:26:16 +0000 (10:26 +0800)
162 files changed:
config.xml
package-lock.json
package.json
platforms/android/android.json
platforms/android/app/libs/jcore-android-2.1.2.jar [new file with mode: 0755]
platforms/android/app/libs/jpush-android-3.3.4.jar [new file with mode: 0755]
platforms/android/app/src/main/AndroidManifest.xml
platforms/android/app/src/main/assets/www/bindcard.html
platforms/android/app/src/main/assets/www/cordova_plugins.js
platforms/android/app/src/main/assets/www/js/bill.js
platforms/android/app/src/main/assets/www/js/bindcard.js
platforms/android/app/src/main/assets/www/js/index.js
platforms/android/app/src/main/assets/www/js/login.js
platforms/android/app/src/main/assets/www/js/qrcode.js
platforms/android/app/src/main/assets/www/js/security.js
platforms/android/app/src/main/assets/www/js/server.js
platforms/android/app/src/main/assets/www/qrcode.html
platforms/android/app/src/main/assets/www/security.html
platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushEventReceiver.java [new file with mode: 0644]
platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushPlugin.java [new file with mode: 0644]
platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushReceiver.java [new file with mode: 0644]
platforms/android/app/src/main/java/cn/jiguang/cordova/push/PushService.java [new file with mode: 0644]
platforms/android/app/src/main/jniLibs/arm64-v8a/libjcore212.so [new file with mode: 0755]
platforms/android/app/src/main/jniLibs/armeabi-v7a/libjcore212.so [new file with mode: 0755]
platforms/android/app/src/main/jniLibs/armeabi/libjcore212.so [new file with mode: 0755]
platforms/android/app/src/main/jniLibs/mips/libjcore212.so [new file with mode: 0755]
platforms/android/app/src/main/jniLibs/mips64/libjcore212.so [new file with mode: 0755]
platforms/android/app/src/main/jniLibs/x86/libjcore212.so [new file with mode: 0755]
platforms/android/app/src/main/jniLibs/x86_64/libjcore212.so [new file with mode: 0755]
platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png [new file with mode: 0755]
platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png [new file with mode: 0755]
platforms/android/app/src/main/res/drawable/jpush_richpush_btn_selector.xml [new file with mode: 0755]
platforms/android/app/src/main/res/drawable/jpush_richpush_progressbar.xml [new file with mode: 0755]
platforms/android/app/src/main/res/layout/jpush_popwin_layout.xml [new file with mode: 0755]
platforms/android/app/src/main/res/layout/jpush_webview_layout.xml [new file with mode: 0755]
platforms/android/app/src/main/res/layout/push_notification.xml [new file with mode: 0755]
platforms/android/app/src/main/res/values-zh/jpush_string.xml [new file with mode: 0755]
platforms/android/app/src/main/res/values/jpush_string.xml [new file with mode: 0755]
platforms/android/app/src/main/res/values/jpush_style.xml [new file with mode: 0755]
platforms/android/app/src/main/res/xml/config.xml
platforms/android/build.gradle
platforms/android/platform_www/cordova_plugins.js
platforms/android/platform_www/plugins/cordova-plugin-firebasex/www/firebase.js [new file with mode: 0644]
platforms/android/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js [new file with mode: 0644]
platforms/android/project.properties
platforms/browser/browser.json
platforms/browser/config.xml
platforms/browser/platform_www/cordova_plugins.js
platforms/browser/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js [new file with mode: 0644]
platforms/ios/Podfile.lock [new file with mode: 0644]
platforms/ios/Pods/Manifest.lock [new file with mode: 0644]
platforms/ios/Pods/Pods.xcodeproj/project.pbxproj [new file with mode: 0644]
platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-acknowledgements.markdown [new file with mode: 0644]
platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-acknowledgements.plist [new file with mode: 0644]
platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-dummy.m [new file with mode: 0644]
platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-frameworks.sh [new file with mode: 0755]
platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-resources.sh [new file with mode: 0755]
platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp.debug.xcconfig [new file with mode: 0644]
platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp.release.xcconfig [new file with mode: 0644]
platforms/ios/dlapp.xcodeproj/project.pbxproj [changed mode: 0755->0644]
platforms/ios/dlapp.xcworkspace/contents.xcworkspacedata
platforms/ios/dlapp/Entitlements-Debug.plist
platforms/ios/dlapp/Entitlements-Release.plist
platforms/ios/dlapp/Plugins/cordova-plugin-jcore/jcore-ios-2.1.1.a [new file with mode: 0755]
platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/AppDelegate+JPush.h [new file with mode: 0644]
platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/AppDelegate+JPush.m [new file with mode: 0644]
platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPUSHService.h [new file with mode: 0755]
platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushDefine.h [new file with mode: 0644]
platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushPlugin.h [new file with mode: 0644]
platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushPlugin.m [new file with mode: 0644]
platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/jpush-ios-3.2.1.a [new file with mode: 0755]
platforms/ios/dlapp/Resources/JPushConfig.plist [new file with mode: 0644]
platforms/ios/dlapp/config.xml
platforms/ios/dlapp/dlapp-Info.plist
platforms/ios/frameworks.json
platforms/ios/ios.json
platforms/ios/platform_www/cordova_plugins.js
platforms/ios/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js [new file with mode: 0644]
platforms/ios/pods-debug.xcconfig
platforms/ios/pods-release.xcconfig
plugins/android.json
plugins/browser.json
plugins/cordova-plugin-jcore/LICENSE [new file with mode: 0644]
plugins/cordova-plugin-jcore/doc/sdk_model.png [new file with mode: 0644]
plugins/cordova-plugin-jcore/package.json [new file with mode: 0644]
plugins/cordova-plugin-jcore/plugin.xml [new file with mode: 0644]
plugins/cordova-plugin-jcore/src/android/arm64-v8a/libjcore212.so [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/android/armeabi-v7a/libjcore212.so [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/android/armeabi/libjcore212.so [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/android/jcore-android-2.1.2.jar [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/android/mips/libjcore212.so [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/android/mips64/libjcore212.so [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/android/x86/libjcore212.so [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/android/x86_64/libjcore212.so [new file with mode: 0755]
plugins/cordova-plugin-jcore/src/ios/jcore-ios-2.1.1.a [new file with mode: 0755]
plugins/cordova-plugin-jcore/www/jcore.js [new file with mode: 0644]
plugins/fetch.json
plugins/ios.json
plugins/jpush-phonegap-plugin/.travis.yml [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/DismissActions_00.PNG [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_00.gif [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_01.png [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_02.png [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_03.png [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_04.png [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_05.png [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_06.png [new file with mode: 0644]
plugins/jpush-phonegap-plugin/doc/res/NotificationActions_00.png [new file with mode: 0644]
plugins/jpush-phonegap-plugin/example/css/index.css [new file with mode: 0755]
plugins/jpush-phonegap-plugin/example/css/jquery.mobile-1.1.1.css [new file with mode: 0755]
plugins/jpush-phonegap-plugin/example/css/mobiscroll.core-2.0.1.css [new file with mode: 0755]
plugins/jpush-phonegap-plugin/example/css/mobiscroll.jqm-2.0.1.css [new file with mode: 0755]
plugins/jpush-phonegap-plugin/example/index.html [new file with mode: 0644]
plugins/jpush-phonegap-plugin/example/js/jquery.js [new file with mode: 0755]
plugins/jpush-phonegap-plugin/example/js/jquery.mobile-1.1.1.js [new file with mode: 0755]
plugins/jpush-phonegap-plugin/hooks/apns.entitlements [new file with mode: 0644]
plugins/jpush-phonegap-plugin/hooks/common.js [new file with mode: 0644]
plugins/jpush-phonegap-plugin/hooks/iosDisablePush.js [new file with mode: 0644]
plugins/jpush-phonegap-plugin/hooks/iosEnablePush.js [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/example/src/app/app.component.ts [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/example/src/app/app.html [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/example/src/app/app.module.ts [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.html [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.scss [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.ts [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/index.ts [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/jpush/index.d.ts [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/jpush/index.js [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.d.ts [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.js [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.metadata.json [new file with mode: 0644]
plugins/jpush-phonegap-plugin/ionic/jpush/package.json [new file with mode: 0644]
plugins/jpush-phonegap-plugin/license [new file with mode: 0644]
plugins/jpush-phonegap-plugin/package.json [new file with mode: 0644]
plugins/jpush-phonegap-plugin/plugin.xml [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/android/JPushEventReceiver.java [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/android/JPushPlugin.java [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/android/JPushReceiver.java [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/android/PushService.java [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/android/libs/jpush-android-3.3.4.jar [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_richpush_btn_selector.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_richpush_progressbar.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/layout/jpush_popwin_layout.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/layout/jpush_webview_layout.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/layout/push_notification.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/values-zh/jpush_string.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/values/jpush_string.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/android/res/values/jpush_style.xml [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/ios/JPushConfig.plist [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/ios/Plugins/AppDelegate+JPush.h [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/ios/Plugins/AppDelegate+JPush.m [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushDefine.h [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushPlugin.h [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushPlugin.m [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/ios/lib/JPUSHService.h [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/ios/lib/jpush-ios-3.2.1.a [new file with mode: 0755]
plugins/jpush-phonegap-plugin/src/ios/notificationService/NotificationService.h [new file with mode: 0644]
plugins/jpush-phonegap-plugin/src/ios/notificationService/NotificationService.m [new file with mode: 0644]
plugins/jpush-phonegap-plugin/www/JPushPlugin.js [new file with mode: 0644]
www/js/index.js

index a20cd9e..4b754cd 100644 (file)
@@ -7,10 +7,9 @@
     <author email="dev@cordova.apache.org" href="http://cordova.io">
         Apache Cordova Team
     </author>
-    <preference name="AutoHideSplashScreen" value="true"/>
-    <preference name="SplashScreenDelay" value="0"/>
+    <preference name="AutoHideSplashScreen" value="true" />
+    <preference name="SplashScreenDelay" value="0" />
     <preference name="SplashShowOnlyFirstTime" value="true" />
-
     <content src="index.html" />
     <access origin="*" />
     <allow-intent href="http://*/*" />
     <allow-intent href="sms:*" />
     <allow-intent href="mailto:*" />
     <allow-intent href="geo:*" />
-
     <allow-navigation href="http://*/*" />
     <allow-navigation href="https://*/*" />
     <allow-navigation href="data:*" />
     <access origin="http://ip*" />
-
     <preference name="ErrorUrl" value="error.html" />
     <preference name="Orientation" value="portrait" />
     <preference name="UseSwiftLanguageVersion" value="4.0" />
     <preference name="DisallowOverscroll" value="true" />
     <preference name="UIWebViewBounce" value="false" />
-    <preference name="BackupWebStorage" value="local"/>
-    
+    <preference name="BackupWebStorage" value="local" />
     <platform name="android">
         <allow-intent href="market:*" />
     </platform>
     <feature name="TouchID">
         <param name="ios-package" value="TouchID" />
     </feature>
+    <edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
+        <string>APP需要使用您的相机权限,没有该权限将无法完成扫一扫功能</string>
+    </edit-config>
     <plugin name="cordova-plugin-fingerprint-aio" spec="~1.7.0">
         <variable name="FACEID_USAGE_DESCRIPTION" value="认证中..." />
     </plugin>
     <plugin name="cordova-plugin-touch-id" spec="~3.3.1">
         <variable name="FACEID_USAGE_DESCRIPTION" value=" " />
     </plugin>
-    <plugin name="cordova-plugin-whitelist" spec="1" />
+    <plugin name="cordova-plugin-whitelist" spec="^1.3.3" />
     <plugin name="cordova-plugin-advanced-http" spec="~2.1.1">
         <variable name="OKHTTP_VERSION" value="3.10.0" />
     </plugin>
-
-    <plugin name="cordova-plugin-statusbar" spec="~2.4.2" />
-    <edit-config target="NSCameraUsageDescription" file="*-Info.plist" mode="merge">
-        <string>APP需要使用您的相机权限,没有该权限将无法完成扫一扫功能</string>
-    </edit-config>
+    <plugin name="cordova-plugin-statusbar" spec="^2.4.2" />
+    <plugin name="cordova-plugin-disable-ios11-statusbar" spec="~1.0.0" />
+    <plugin name="cordova-plugin-qrscanner" spec="^3.0.1" />
+    <plugin name="cordova-plugin-camera" spec="~4.0.3" />
+    <plugin name="cordova-plugin-inappbrowser" spec="~3.0.0" />
+    <plugin name="cordova-plugin-device" spec="~2.0.2" />
+    <plugin name="cordova-plugin-themeablebrowser" spec="~0.2.17" />
 </widget>
index 9d3e4cb..092a907 100644 (file)
             "resolved": "https://registry.npmjs.org/cordova-plugin-inappbrowser/-/cordova-plugin-inappbrowser-3.0.0.tgz",
             "integrity": "sha1-1K4A02Z2IQdRBXrSWK5K1KkWGto="
         },
+        "cordova-plugin-jcore": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/cordova-plugin-jcore/-/cordova-plugin-jcore-1.3.0.tgz",
+            "integrity": "sha512-QmkdABlkIHFaMUBsrjPqhpaWS7wuboRPeeVEUNoc4FlZNRoQgw35v0MaW+SbOk4SzVsEGvN6IFSVqa4106ljUw=="
+        },
         "cordova-plugin-qrscanner": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/cordova-plugin-qrscanner/-/cordova-plugin-qrscanner-3.0.1.tgz",
             "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
             "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
         },
+        "jpush-phonegap-plugin": {
+            "version": "3.7.2",
+            "resolved": "https://registry.npmjs.org/jpush-phonegap-plugin/-/jpush-phonegap-plugin-3.7.2.tgz",
+            "integrity": "sha512-q5gGu0m1uf6Mf1puFd9o1hp32rok+/GgR39ZDGWdYULc6KX5zy932wdzLDlTYpjBW9Gec/HZmj1HTbQIMvppiA=="
+        },
         "jsonfile": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
index f5c8339..4ca4c6f 100644 (file)
     "cordova-plugin-file": "^6.0.1",
     "cordova-plugin-fingerprint-aio": "^1.7.0",
     "cordova-plugin-inappbrowser": "^3.0.0",
+    "cordova-plugin-jcore": "^1.3.0",
     "cordova-plugin-qrscanner": "^3.0.1",
     "cordova-plugin-statusbar": "^2.4.2",
     "cordova-plugin-themeablebrowser": "^0.2.18",
-    "cordova-plugin-touch-id": "^3.4.0"
+    "cordova-plugin-touch-id": "^3.4.0",
+    "jpush-phonegap-plugin": "^3.7.2"
   },
   "cordova": {
     "plugins": {
       "cordova-plugin-camera": {},
       "cordova-plugin-inappbrowser": {},
       "cordova-plugin-device": {},
-      "cordova-plugin-themeablebrowser": {}
+      "cordova-plugin-themeablebrowser": {},
+      "jpush-phonegap-plugin": {
+        "APP_KEY": "bd22b85f247a6e2ca307d3c5",
+        "CHANNEL": "developer-default"
+      }
     },
     "platforms": [
       "android",
index 825de01..6103bb9 100644 (file)
             {
               "xml": "<feature name=\"ThemeableBrowser\"><param name=\"android-package\" value=\"com.initialxy.cordova.themeablebrowser.ThemeableBrowser\" /></feature>",
               "count": 1
+            },
+            {
+              "xml": "<feature name=\"JPushPlugin\"><param name=\"android-package\" value=\"cn.jiguang.cordova.push.JPushPlugin\" /></feature>",
+              "count": 1
             }
           ]
         }
           "/manifest": [
             {
               "xml": "<uses-permission android:name=\"android.permission.INTERNET\" />",
+              "count": 2
+            },
+            {
+              "xml": "<permission android:name=\"com.supwisdom.dlapp.permission.JPUSH_MESSAGE\" android:protectionLevel=\"signature\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"com.supwisdom.dlapp.permission.JPUSH_MESSAGE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.RECEIVE_USER_PRESENT\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.VIBRATE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.ACCESS_BACKGROUND_LOCATION\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" />",
+              "count": 1
+            },
+            {
+              "xml": "<uses-permission android:name=\"android.permission.GET_TASKS\" />",
               "count": 1
             }
           ],
               "xml": "<provider android:authorities=\"${applicationId}.provider\" android:exported=\"false\" android:grantUriPermissions=\"true\" android:name=\"org.apache.cordova.camera.FileProvider\"><meta-data android:name=\"android.support.FILE_PROVIDER_PATHS\" android:resource=\"@xml/camera_provider_paths\" /></provider>",
               "count": 1
             }
+          ],
+          "/manifest/application": [
+            {
+              "xml": "<activity android:exported=\"false\" android:name=\"cn.jpush.android.ui.PopWinActivity\" android:theme=\"@style/MyDialogStyle\"></activity>",
+              "count": 1
+            },
+            {
+              "xml": "<activity android:configChanges=\"orientation|keyboardHidden\" android:exported=\"false\" android:name=\"cn.jpush.android.ui.PushActivity\" android:theme=\"@android:style/Theme.NoTitleBar\"><intent-filter><action android:name=\"cn.jpush.android.ui.PushActivity\" /><category android:name=\"android.intent.category.DEFAULT\" /><category android:name=\"com.supwisdom.dlapp\" /></intent-filter></activity>",
+              "count": 1
+            },
+            {
+              "xml": "<service android:exported=\"false\" android:name=\"cn.jpush.android.service.PushService\" android:process=\":pushcore\"><intent-filter><action android:name=\"cn.jpush.android.intent.REGISTER\" /><action android:name=\"cn.jpush.android.intent.REPORT\" /><action android:name=\"cn.jpush.android.intent.PushService\" /><action android:name=\"cn.jpush.android.intent.PUSH_TIME\" /></intent-filter></service>",
+              "count": 1
+            },
+            {
+              "xml": "<provider android:authorities=\"com.supwisdom.dlapp.DataProvider\" android:exported=\"false\" android:name=\"cn.jpush.android.service.DataProvider\" android:process=\":pushcore\" />",
+              "count": 1
+            },
+            {
+              "xml": "<service android:enabled=\"true\" android:exported=\"true\" android:name=\"cn.jpush.android.service.DaemonService\"><intent-filter><action android:name=\"cn.jpush.android.intent.DaemonService\" /><category android:name=\"com.supwisdom.dlapp\" /></intent-filter></service>",
+              "count": 1
+            },
+            {
+              "xml": "<provider android:authorities=\"com.supwisdom.dlapp.DownloadProvider\" android:exported=\"true\" android:name=\"cn.jpush.android.service.DownloadProvider\" />",
+              "count": 1
+            },
+            {
+              "xml": "<receiver android:enabled=\"true\" android:exported=\"false\" android:name=\"cn.jpush.android.service.PushReceiver\"><intent-filter android:priority=\"1000\"><action android:name=\"cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY\" /><category android:name=\"com.supwisdom.dlapp\" /></intent-filter><intent-filter><action android:name=\"android.intent.action.USER_PRESENT\" /><action android:name=\"android.net.conn.CONNECTIVITY_CHANGE\" /></intent-filter><intent-filter><action android:name=\"android.intent.action.PACKAGE_ADDED\" /><action android:name=\"android.intent.action.PACKAGE_REMOVED\" /><data android:scheme=\"package\" /></intent-filter></receiver>",
+              "count": 1
+            },
+            {
+              "xml": "<receiver android:exported=\"false\" android:name=\"cn.jpush.android.service.AlarmReceiver\" />",
+              "count": 1
+            },
+            {
+              "xml": "<receiver android:name=\"cn.jiguang.cordova.push.JPushEventReceiver\"><intent-filter><action android:name=\"cn.jpush.android.intent.RECEIVE_MESSAGE\" /><category android:name=\"com.supwisdom.dlapp\" /></intent-filter></receiver>",
+              "count": 1
+            },
+            {
+              "xml": "<activity android:exported=\"true\" android:name=\"cn.jpush.android.service.JNotifyActivity\" android:taskAffinity=\"jpush.custom\" android:theme=\"@android:style/Theme.Translucent.NoTitleBar\"><intent-filter><action android:name=\"cn.jpush.android.intent.JNotifyActivity\" /><category android:name=\"com.supwisdom.dlapp\" /></intent-filter></activity>",
+              "count": 1
+            },
+            {
+              "xml": "<service android:name=\"cn.jiguang.cordova.push.PushService\" android:process=\":pushcore\"><intent-filter><action android:name=\"cn.jiguang.user.service.action\" /></intent-filter></service>",
+              "count": 1
+            },
+            {
+              "xml": "<receiver android:enabled=\"true\" android:exported=\"false\" android:name=\"cn.jiguang.cordova.push.JPushReceiver\"><intent-filter><action android:name=\"cn.jpush.android.intent.REGISTRATION\" /><action android:name=\"cn.jpush.android.intent.MESSAGE_RECEIVED\" /><action android:name=\"cn.jpush.android.intent.NOTIFICATION_RECEIVED\" /><action android:name=\"cn.jpush.android.intent.NOTIFICATION_OPENED\" /><action android:name=\"cn.jpush.android.intent.CONNECTION\" /><category android:name=\"com.supwisdom.dlapp\" /></intent-filter></receiver>",
+              "count": 1
+            },
+            {
+              "xml": "<meta-data android:name=\"JPUSH_CHANNEL\" android:value=\"developer-default\" />",
+              "count": 1
+            },
+            {
+              "xml": "<meta-data android:name=\"JPUSH_APPKEY\" android:value=\"your_jpush_appkey\" />",
+              "count": 1
+            }
           ]
         }
       },
     },
     "cordova-plugin-themeablebrowser": {
       "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-jcore": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "jpush-phonegap-plugin": {
+      "APP_KEY": "your_jpush_appkey",
+      "CHANNEL": "developer-default",
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
     }
   },
   "dependent_plugins": {},
       "clobbers": [
         "cordova.ThemeableBrowser"
       ]
+    },
+    {
+      "id": "jpush-phonegap-plugin.JPushPlugin",
+      "file": "plugins/jpush-phonegap-plugin/www/JPushPlugin.js",
+      "pluginId": "jpush-phonegap-plugin",
+      "clobbers": [
+        "JPush"
+      ]
     }
   ],
   "plugin_metadata": {
     "cordova-plugin-camera": "4.0.3",
     "cordova-plugin-inappbrowser": "3.0.0",
     "cordova-plugin-device": "2.0.2",
-    "cordova-plugin-themeablebrowser": "0.2.17"
+    "cordova-plugin-themeablebrowser": "0.2.17",
+    "cordova-plugin-jcore": "1.3.0",
+    "jpush-phonegap-plugin": "3.7.2"
   }
 }
diff --git a/platforms/android/app/libs/jcore-android-2.1.2.jar b/platforms/android/app/libs/jcore-android-2.1.2.jar
new file mode 100755 (executable)
index 0000000..9a770dc
Binary files /dev/null and b/platforms/android/app/libs/jcore-android-2.1.2.jar differ
diff --git a/platforms/android/app/libs/jpush-android-3.3.4.jar b/platforms/android/app/libs/jpush-android-3.3.4.jar
new file mode 100755 (executable)
index 0000000..fef22f8
Binary files /dev/null and b/platforms/android/app/libs/jpush-android-3.3.4.jar differ
index f8296a8..0bc45cf 100644 (file)
@@ -1,7 +1,6 @@
 <?xml version='1.0' encoding='utf-8'?>
 <manifest android:hardwareAccelerated="true" android:versionCode="10000" android:versionName="1.0.0" package="com.supwisdom.dlapp" xmlns:android="http://schemas.android.com/apk/res/android">
     <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
-    <uses-permission android:name="android.permission.INTERNET" />
     <application android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/launcher_name" android:supportsRtl="true">
         <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:screenOrientation="portrait" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize">
             <intent-filter android:label="@string/launcher_name">
         <provider android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true" android:name="org.apache.cordova.camera.FileProvider">
             <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/camera_provider_paths" />
         </provider>
+        <activity android:exported="false" android:name="cn.jpush.android.ui.PopWinActivity" android:theme="@style/MyDialogStyle" />
+        <activity android:configChanges="orientation|keyboardHidden" android:exported="false" android:name="cn.jpush.android.ui.PushActivity" android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <action android:name="cn.jpush.android.ui.PushActivity" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="com.supwisdom.dlapp" />
+            </intent-filter>
+        </activity>
+        <service android:exported="false" android:name="cn.jpush.android.service.PushService" android:process=":pushcore">
+            <intent-filter>
+                <action android:name="cn.jpush.android.intent.REGISTER" />
+                <action android:name="cn.jpush.android.intent.REPORT" />
+                <action android:name="cn.jpush.android.intent.PushService" />
+                <action android:name="cn.jpush.android.intent.PUSH_TIME" />
+            </intent-filter>
+        </service>
+        <provider android:authorities="com.supwisdom.dlapp.DataProvider" android:exported="false" android:name="cn.jpush.android.service.DataProvider" android:process=":pushcore" />
+        <service android:enabled="true" android:exported="true" android:name="cn.jpush.android.service.DaemonService">
+            <intent-filter>
+                <action android:name="cn.jpush.android.intent.DaemonService" />
+                <category android:name="com.supwisdom.dlapp" />
+            </intent-filter>
+        </service>
+        <provider android:authorities="com.supwisdom.dlapp.DownloadProvider" android:exported="true" android:name="cn.jpush.android.service.DownloadProvider" />
+        <receiver android:enabled="true" android:exported="false" android:name="cn.jpush.android.service.PushReceiver">
+            <intent-filter android:priority="1000">
+                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" />
+                <category android:name="com.supwisdom.dlapp" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.USER_PRESENT" />
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.PACKAGE_ADDED" />
+                <action android:name="android.intent.action.PACKAGE_REMOVED" />
+                <data android:scheme="package" />
+            </intent-filter>
+        </receiver>
+        <receiver android:exported="false" android:name="cn.jpush.android.service.AlarmReceiver" />
+        <receiver android:name="cn.jiguang.cordova.push.JPushEventReceiver">
+            <intent-filter>
+                <action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
+                <category android:name="com.supwisdom.dlapp" />
+            </intent-filter>
+        </receiver>
+        <activity android:exported="true" android:name="cn.jpush.android.service.JNotifyActivity" android:taskAffinity="jpush.custom" android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="cn.jpush.android.intent.JNotifyActivity" />
+                <category android:name="com.supwisdom.dlapp" />
+            </intent-filter>
+        </activity>
+        <service android:name="cn.jiguang.cordova.push.PushService" android:process=":pushcore">
+            <intent-filter>
+                <action android:name="cn.jiguang.user.service.action" />
+            </intent-filter>
+        </service>
+        <receiver android:enabled="true" android:exported="false" android:name="cn.jiguang.cordova.push.JPushReceiver">
+            <intent-filter>
+                <action android:name="cn.jpush.android.intent.REGISTRATION" />
+                <action android:name="cn.jpush.android.intent.MESSAGE_RECEIVED" />
+                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED" />
+                <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" />
+                <action android:name="cn.jpush.android.intent.CONNECTION" />
+                <category android:name="com.supwisdom.dlapp" />
+            </intent-filter>
+        </receiver>
+        <meta-data android:name="JPUSH_CHANNEL" android:value="developer-default" />
+        <meta-data android:name="JPUSH_APPKEY" android:value="your_jpush_appkey" />
     </application>
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.CAMERA" android:required="false" />
     <uses-feature android:name="android.hardware.camera" android:required="false" />
     <uses-feature android:name="android.hardware.camera.front" android:required="false" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <permission android:name="com.supwisdom.dlapp.permission.JPUSH_MESSAGE" android:protectionLevel="signature" />
+    <uses-permission android:name="com.supwisdom.dlapp.permission.JPUSH_MESSAGE" />
+    <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.GET_TASKS" />
 </manifest>
index 3ec9d26..8d2ee2f 100644 (file)
                 </div>
             </div>
             <div class="weui-cell" style="padding:0 10px; "> 
-                <div class="weui-cell__hd"><label class="weui-label" style="width: 80px;font-size: 14px;">银行卡号</label></div>
+                <div class="weui-cell__hd"><label class="weui-label" style="width: 80px;font-size: 14px;">证件类型</label></div>
                 <div class="weui-cell__bd">
-                    <input class="weui-input" type="text" id="cardnum" style="font-size: 14px;" placeholder="市民卡对应的银行卡号">
+                    <input class="weui-input" type="text" id='idtype' placeholder="请选择证件类型" style="font-size: 14px;" />
                 </div>
             </div>
-        </div>
-
-        <div class="weui-cells__title">短信验证市民卡预留的手机号</div>
-        <div class="weui-cells weui-cells_form">
             <div class="weui-cell" style="padding:0 10px; "> 
-                <div class="weui-cell__hd"><label class="weui-label" style="width: 80px;font-size: 14px;">手机号</label></div>
+                <div class="weui-cell__hd"><label class="weui-label" style="width: 80px;font-size: 14px;">证件号</label></div>
                 <div class="weui-cell__bd">
-                    <input class="weui-input" type="tel" id="phone" style="font-size: 14px;" placeholder="请输入市民卡预留的手机号" disabled="disabled" >
+                    <input class="weui-input" type="text" id="idno" style="font-size: 14px;" placeholder="请输入您的证件号" >
                 </div>
             </div>
-            <div class="weui-cell weui-cell_vcode" style="padding:0 10px; ">
-                <div class="weui-cell__hd">
-                    <label class="weui-label" style="width: 80px;font-size: 14px;">验证码</label>
-                </div>
+            <div class="weui-cell" style="padding:0 10px; "> 
+                <div class="weui-cell__hd"><label class="weui-label" style="width: 80px;font-size: 14px;">银行卡号</label></div>
                 <div class="weui-cell__bd">
-                    <input class="weui-input" type="number" id="code" style="font-size: 14px;" placeholder="请输入验证码" maxlength="6">
-                </div>
-                <div class="weui-cell__ft">
-                    <button class="weui-vcode-btn" onclick="app.getCode()" id="codebtn" style="width: 100px;height: 1rem;font-size: 14px;">获取验证码</button>
+                    <input class="weui-input" type="text" id="cardnum" style="font-size: 14px;" placeholder="市民卡对应的银行卡号">
                 </div>
             </div>
         </div>
-
         <section class="aui-content-padded" style="margin-top: 30px;">
             <div class="aui-btn aui-btn-block aui-btn-info" tapmode onclick="app.doNext()">下一步</div>
         </section>
index 049ccd6..595ecb1 100644 (file)
@@ -319,6 +319,14 @@ cordova.define('cordova/plugin_list', function(require, exports, module) {
       "clobbers": [
         "cordova.ThemeableBrowser"
       ]
+    },
+    {
+      "id": "jpush-phonegap-plugin.JPushPlugin",
+      "file": "plugins/jpush-phonegap-plugin/www/JPushPlugin.js",
+      "pluginId": "jpush-phonegap-plugin",
+      "clobbers": [
+        "JPush"
+      ]
     }
   ];
   module.exports.metadata = {
@@ -334,6 +342,8 @@ cordova.define('cordova/plugin_list', function(require, exports, module) {
     "cordova-plugin-camera": "4.0.3",
     "cordova-plugin-inappbrowser": "3.0.0",
     "cordova-plugin-device": "2.0.2",
-    "cordova-plugin-themeablebrowser": "0.2.17"
+    "cordova-plugin-themeablebrowser": "0.2.17",
+    "cordova-plugin-jcore": "1.3.0",
+    "jpush-phonegap-plugin": "3.7.2"
   };
 });
\ No newline at end of file
index c0af3fd..8921bd6 100644 (file)
@@ -20,6 +20,7 @@ var app = {
         V1Bills(param,function(ok,ret){
             if(ok){
                 if(ret.code==200){
+                    $.hideLoading();
                     if(ret.page&&ret.page.count>0){
                         app.initBillView(ret.page)
                     }else{
index 649098d..e9cb86c 100644 (file)
@@ -1,25 +1,58 @@
+var dict;
 var app = {
 
     // Application Constructor
     initialize: function() {
         document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
     },
-  
+
     onDeviceReady: function() {
         var uid = window.localStorage.getItem("uid");
-        var phone =  window.localStorage.getItem("phoneX");
-        if(!isEmpty(phone)){
-            $("#phone").val(phone)
-        }else{
-            phone =  window.localStorage.getItem("phone");
-            $("#phone").val(phone)
-        }
+        this.loadData();
+    },
+    loadData: function() {
+        $.showLoading("正在加载");
+        V1Idtypes(function(ok, ret) {
+            if (ok) {
+                $.hideLoading();
+                if (ret.code == 200) {
+                    dict = ret.idtypes;
+                    let keys = Object.keys(dict);
+                    let vals = Object.values(dict);
+                    console.log(keys, vals)
+                    var items=[];
+                    for(var key of keys){
+                        var bean = {
+                            "title":dict[key],
+                            "value":key
+                        }
+                        items.push(bean)
+                    }
+                    $("#idtype").val(items[0].title)
+                    $("#idtype").attr("data-values",items[0].value)
+                    $("#idtype").select({
+                      title: "请选择证件类型",
+                      items: items
+                    });
+                } else {
+                    $.alert(ret.msg, "错误");
+                }
+            } else {
+                $.hideLoading();
+                $.alert("请求失败了 " + ret.status + ",请稍后再试", "错误");
+            }
+        })
     },
     doNext: function() {
-        var code =  $("#code").val();
-        var cardnum =  $("#cardnum").val();
-        var name =  $("#name").val();
-        if(isEmpty(name)||isEmpty(code)||isEmpty(cardnum)){
+        var cardnum = $("#cardnum").val();
+        var name = $("#name").val();
+        var idno = $("#idno").val();
+        if (isEmpty(name) || isEmpty(cardnum)||isEmpty(idno)) {
+            return;
+        }
+        var idtype = $("#idtype").attr("data-values")
+        if(isEmpty(idtype)){
+            $.alert("请选择证件类型", "错误");
             return;
         }
         /*var agree = $("input[type=checkbox]:checked").val();
@@ -28,62 +61,37 @@ var app = {
             return;
         }*/
         $.showLoading("正在处理");
-        var param={
-            "cardno":cardnum,
-            "code":code,
-            "name":name
+        var param = {
+            "cardno": cardnum,
+            "idtype": idtype,
+            "name": name,
+            "idno": idno
         }
-        V1Bindcard(param,function(ok,ret){
-            if(ok){
+        V1Bindcard(param, function(ok, ret) {
+            if (ok) {
                 $.hideLoading();
                 console.log(ret)
-                if(ret.code==200){
-                     window.localStorage.setItem("paypwdtype","new"); 
-                     window.localStorage.setItem("userid",ret.userid);
-                     window.localStorage.setItem("signed",ret.signed); 
-                     window.localStorage.setItem("name",name); 
-                     window.localStorage.setItem("paypwdset",ret.paypwdset); 
-                     if(ret.paypwdset){
-                        if(!isEmpty(ret.signed)&&signed=='yes'){
-                            window.location='main.html'
-                        }else{
-                            window.location='signxy.html'
-                        }
-                     }else{
-                        window.location="paypwdset.html";
-                     }
-                }else{
-                    if(ret.code==-1){
-                        $.alert(ret.msg, "提示",function(){
-                            window.location="main.html"
+                if (ret.code == 200) {
+                    window.localStorage.setItem("phoneX",ret.phonex);
+                    window.localStorage.setItem("name", name);
+                    window.localStorage.setItem("personid", ret.personid);
+                    window.localStorage.setItem("signed", ret.signed);
+                    window.localStorage.setItem("paypwdset", ret.paypwdset);
+                    window.location = "bindcheck.html";
+                } else {
+                    if (ret.code == -1) {
+                        $.alert(ret.msg, "提示", function() {
+                            window.location = "main.html"
                         });
-                    }else{
-                       $.alert(ret.msg, "错误");
+                    } else {
+                        $.alert(ret.msg, "错误");
                     }
-                } 
-            }else{
-                $.hideLoading();
-                $.alert("请求失败了 "+ret.status+",请稍后再试", "错误");
-            }
-        })
-    },
-    getCode :function(){
-        $.showLoading("请求中");
-        V1Code(function(ok,ret){
-            if(ok){
-                $.hideLoading();
-                if(ret.code==200){
-                    $("#codebtn").attr("disabled","disabled")
-                    $("#codebtn").addClass("vcodedisabled")
-                    btnTime('codebtn');
-                }else{
-                    $.alert(ret.msg, "错误");
-                } 
-            }else{
+                }
+            } else {
                 $.hideLoading();
-                $.alert("请求失败了"+ret.status+",请稍后再试", "错误");
+                $.alert("请求失败了 " + ret.status + ",请稍后再试", "错误");
             }
         })
     }
 };
-app.initialize();
+app.initialize();
\ No newline at end of file
index ba4ea86..112a927 100644 (file)
@@ -41,9 +41,10 @@ var app = {
                         if(ret.now-t>1000*60*10){
                              window.location = "login.html";
                         }else{
-                             
                              window.location = "main.html";
                         }
+                    }else{
+                        window.location = "login.html";
                     }
                 } else {
                     //alert('无法请求到服务器,请检查网络并稍后再试');
index ab55032..77b8b2a 100644 (file)
@@ -23,7 +23,6 @@ var app = {
         window.location = "findpwd.html";
     },
     login: function(){
-        window.location = "main.html";  
         //loading("正在处理");
         var phone = $("#phone").val()
         var pwd = $("#pwd").val()
index 35fe7a3..4664a9b 100644 (file)
@@ -7,17 +7,58 @@ var app = {
 
     onDeviceReady: function() {
         var uid = window.localStorage.getItem("token");
-        var qrcode = new QRCode(document.getElementById("qrcode"), {
-          text: uid,
-          width: 150,
-          height: 150,
-          colorDark : "#000000",
-          colorLight : "#ffffff",
-          correctLevel : QRCode.CorrectLevel.L
-      });
+        this.loadQrcode();
+        setInterval(function () {
+            window.location.reload();
+        },100000);
     },
-    toBillDetail :function(refno){
-        
+    loadQrcode: function() {
+        $.showLoading("加载中");
+        V1Qrcode(function(ok, ret) {
+            if (ok) {
+              console.log(ret)
+                if (ret.code == 200) {
+                    $.hideLoading();
+                    var qrcode = new QRCode(document.getElementById("qrcode"), {
+                      text: ret.qrcode,
+                      width: 150,
+                      height: 150,
+                      colorDark: "#000000",
+                      colorLight: "#ffffff",
+                      correctLevel: QRCode.CorrectLevel.L
+                    });
+                } else {
+                    $.hideLoading();
+                    $.alert("请求失败,请稍后再试", "错误");
+                }
+            } else {
+                $.hideLoading();
+                $.alert("请求失败了:" + ret.status + "请稍后再试", "错误");
+            }
+        })
+    },
+    refresh:function(){
+      V1Qrcode(function(ok, ret) {
+            if (ok) {
+                if (ret.code == 200) {
+                    $.hideLoading();
+                    var qrcode = new QRCode(document.getElementById("qrcode"), {
+                      text: ret.qrcode,
+                      width: 150,
+                      height: 150,
+                      colorDark: "#000000",
+                      colorLight: "#ffffff",
+                      correctLevel: QRCode.CorrectLevel.L
+                    });
+                } else {
+                    $.hideLoading();
+                    $.alert("请求失败,请稍后再试", "错误");
+                }
+            } else {
+                $.hideLoading();
+                $.alert("请求失败了:" + ret.status + "请稍后再试", "错误");
+            }
+        })
     }
 };
 app.initialize();
\ No newline at end of file
index 3586930..f12a1cc 100644 (file)
@@ -4,11 +4,7 @@ var app = {
     initialize: function() {
         document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
     },
-  
     onDeviceReady: function() {
-        var uid = window.localStorage.getItem("uid");
-        var phone =  window.localStorage.getItem("phoneX");
-        
     },
     editPwd: function() {
        window.location = "editpwd.html";
@@ -19,6 +15,13 @@ var app = {
     logout:function(){
         window.localStorage.removeItem("token");
         window.location = "login.html";
+    },
+    loginByTouchId: function() {
+       Fingerprint.isAvailable(function(result){
+            alert("Fingerprint available");
+       }, function(message){
+            alert(message);
+       });
     }
 };
 app.initialize();
index 3194de9..2bd6a73 100644 (file)
@@ -1,11 +1,17 @@
 var dev = true;
-var SERVER = "";
+var SERVER = "http://ykt.supwisdom.com:10010/payapi/mobileapi";
 var GLOBAL_TODAY="";
 var GLOBAL_YESTERDAY="";
 var CURRENT_INDEX=1;
 if (dev) {
     SERVER = "http://172.28.43.3:8099/payapi/mobileapi";
 }
+function V1Qrcode(callback) {
+    ajaxPost("/v1/qrcode", {}, callback)
+}
+function V1Idtypes(callback) {
+    ajaxPost("/v1/idtypes", {}, callback)
+}
 function V1Cardinfor(callback) {
     ajaxPost("/v1/cardinfor", {}, callback)
 }
index 3391537..ca6cb07 100644 (file)
     <div style="display: flex;flex-direction: row;justify-content: center;align-items: center;margin-top: 50px;">
         <div style="background: #fff;text-align: center;" id="qrcode"></div>
     </div>
-    <p style="text-align: center;margin-top:20px;color:#999">请将二维码对准扫描设备</p>
-    <div style="padding: 30px;">
-        <div class="aui-btn aui-btn-block aui-btn-info" tapmode onclick="app.refresh()">手动刷新二维码</div>
-    </div>
+    <p style="text-align: center;margin-top:40px;color:#999">请将二维码对准扫描设备</p>
+    <!--<div style="padding: 30px;">
+        <div class="aui-btn aui-btn-block aui-btn-info" tapmode onclick="window.location.reload();">刷新二维码</div>
+    </div>-->
 </body>
 </html>
 <script type="text/javascript" src="cordova.js"></script>
index 6994b22..049a988 100644 (file)
                     <div class="aui-list-item-right"></div>
                 </div>
             </li>
+            <!--<li class="aui-list-item" onclick="app.loginByTouchId()">
+                <div class="aui-list-item-label-icon">
+                    <i class="aui-iconfont aui-icon-lock aui-text-info"></i>
+                </div>
+                <div class="aui-list-item-inner ">
+                    <div class="aui-list-item-title">指纹\人脸登录</div>
+                    <div class="aui-list-item-right"><input type="checkbox" id="login" class="aui-switch" ></div>
+                </div>
+            </li>-->
             <li class="aui-list-item" onclick="app.toPayPwd()">
                 <div class="aui-list-item-label-icon">
                     <i class="aui-iconfont aui-icon-pencil aui-text-danger"></i>
diff --git a/platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushEventReceiver.java b/platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushEventReceiver.java
new file mode 100644 (file)
index 0000000..17184d9
--- /dev/null
@@ -0,0 +1,202 @@
+package cn.jiguang.cordova.push;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.apache.cordova.CallbackContext;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Map;
+import java.util.Set;
+
+import cn.jpush.android.api.CustomMessage;
+import cn.jpush.android.api.JPushInterface;
+import cn.jpush.android.api.JPushMessage;
+import cn.jpush.android.api.NotificationMessage;
+import cn.jpush.android.service.JPushMessageReceiver;
+
+public class JPushEventReceiver extends JPushMessageReceiver {
+
+    private static final String TAG = JPushEventReceiver.class.getSimpleName();
+
+    @Override
+    public void onTagOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onTagOperatorResult(context, jPushMessage);
+        //Log.e(TAG,"onTagOperatorResult:"+jPushMessage);
+        JSONObject resultJson = new JSONObject();
+
+        int sequence = jPushMessage.getSequence();
+        try {
+            resultJson.put("sequence", sequence);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        CallbackContext callback = JPushPlugin.eventCallbackMap.get(sequence);
+
+        if (callback == null) {
+            Log.i(TAG, "Unexpected error, callback is null!");
+            return;
+        }
+
+        if (jPushMessage.getErrorCode() == 0) { // success
+            Set<String> tags = jPushMessage.getTags();
+            JSONArray tagsJsonArr = new JSONArray();
+            for (String tag : tags) {
+                tagsJsonArr.put(tag);
+            }
+
+            try {
+                if (tagsJsonArr.length() != 0) {
+                    resultJson.put("tags", tagsJsonArr);
+                }
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.success(resultJson);
+
+        } else {
+            try {
+                resultJson.put("code", jPushMessage.getErrorCode());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.error(resultJson);
+        }
+
+        JPushPlugin.eventCallbackMap.remove(sequence);
+    }
+
+    @Override
+    public void onCheckTagOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onCheckTagOperatorResult(context, jPushMessage);
+
+        //Log.e(TAG,"onCheckTagOperatorResult:"+jPushMessage);
+        JSONObject resultJson = new JSONObject();
+
+        int sequence = jPushMessage.getSequence();
+        try {
+            resultJson.put("sequence", sequence);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        CallbackContext callback = JPushPlugin.eventCallbackMap.get(sequence);
+
+        if (callback == null) {
+            Log.i(TAG, "Unexpected error, callback is null!");
+            return;
+        }
+
+        if (jPushMessage.getErrorCode() == 0) {
+            try {
+                resultJson.put("tag", jPushMessage.getCheckTag());
+                resultJson.put("isBind", jPushMessage.getTagCheckStateResult());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+            callback.success(resultJson);
+
+        } else {
+            try {
+                resultJson.put("code", jPushMessage.getErrorCode());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+            callback.error(resultJson);
+        }
+
+        JPushPlugin.eventCallbackMap.remove(sequence);
+    }
+
+    @Override
+    public void onAliasOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onAliasOperatorResult(context, jPushMessage);
+
+        //Log.e(TAG,"onAliasOperatorResult:"+jPushMessage);
+        JSONObject resultJson = new JSONObject();
+
+        int sequence = jPushMessage.getSequence();
+        try {
+            resultJson.put("sequence", sequence);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        CallbackContext callback = JPushPlugin.eventCallbackMap.get(sequence);
+
+        if (callback == null) {
+            Log.i(TAG, "Unexpected error, callback is null!");
+            return;
+        }
+
+        if (jPushMessage.getErrorCode() == 0) { // success
+            try {
+                if (!TextUtils.isEmpty(jPushMessage.getAlias())) {
+                    resultJson.put("alias", jPushMessage.getAlias());
+                }
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.success(resultJson);
+
+        } else {
+            try {
+                resultJson.put("code", jPushMessage.getErrorCode());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.error(resultJson);
+        }
+
+        JPushPlugin.eventCallbackMap.remove(sequence);
+    }
+
+    @Override
+    public void onRegister(Context context, String regId) {
+        //Log.e(TAG,"onRegister:"+regId);
+        JPushPlugin.transmitReceiveRegistrationId(regId);
+    }
+
+    @Override
+    public void onMessage(Context context, CustomMessage customMessage) {
+        super.onMessage(context,customMessage);
+        //Log.e(TAG,"onMessage:"+customMessage);
+//        String msg = customMessage.message;//intent.getStringExtra(JPushInterface.EXTRA_MESSAGE);
+//        Map<String, Object> extras = getNotificationExtras(intent);
+//        JPushPlugin.transmitMessageReceive(msg, extras);
+    }
+
+    @Override
+    public void onNotifyMessageArrived(Context context, NotificationMessage notificationMessage) {
+        super.onNotifyMessageArrived(context, notificationMessage);
+
+        //Log.e(TAG,"onNotifyMessageArrived:"+notificationMessage);
+    }
+
+    @Override
+    public void onNotifyMessageOpened(Context context, NotificationMessage notificationMessage) {
+        super.onNotifyMessageOpened(context, notificationMessage);
+        //Log.e(TAG,"onNotifyMessageOpened:"+notificationMessage);
+    }
+
+    @Override
+    public void onMobileNumberOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onMobileNumberOperatorResult(context, jPushMessage);
+        //Log.e(TAG,"onMobileNumberOperatorResult:"+jPushMessage);
+    }
+
+    @Override
+    public void onMultiActionClicked(Context context, Intent intent) {
+        super.onMultiActionClicked(context, intent);
+        //Log.e(TAG,"onMultiActionClicked:"+intent);
+    }
+}
diff --git a/platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushPlugin.java b/platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushPlugin.java
new file mode 100644 (file)
index 0000000..867d511
--- /dev/null
@@ -0,0 +1,720 @@
+package cn.jiguang.cordova.push;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaWebView;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import cn.jpush.android.api.BasicPushNotificationBuilder;
+import cn.jpush.android.api.JPushInterface;
+import cn.jpush.android.api.TagAliasCallback;
+import cn.jpush.android.data.JPushLocalNotification;
+
+public class JPushPlugin extends CordovaPlugin {
+
+    private static final String TAG = JPushPlugin.class.getSimpleName();
+
+    private Context mContext;
+
+    private static JPushPlugin instance;
+    private static Activity cordovaActivity;
+
+    static String notificationTitle;
+    static String notificationAlert;
+    static Map<String, Object> notificationExtras = new HashMap<String, Object>();
+
+    static String openNotificationTitle;
+    static String openNotificationAlert;
+    static Map<String, Object> openNotificationExtras = new HashMap<String, Object>();
+
+    static Map<Integer, CallbackContext> eventCallbackMap = new HashMap<Integer, CallbackContext>();
+
+    public JPushPlugin() {
+        instance = this;
+    }
+
+    @Override
+    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
+        super.initialize(cordova, webView);
+        mContext = cordova.getActivity().getApplicationContext();
+
+        JPushInterface.init(mContext);
+
+        cordovaActivity = cordova.getActivity();
+
+        // 如果同时缓存了打开事件 openNotificationAlert 和 消息事件 notificationAlert,只向 UI 发打开事件。
+        // 这样做是为了和 iOS 统一。
+        if (openNotificationAlert != null) {
+            notificationAlert = null;
+            transmitNotificationOpen(openNotificationTitle, openNotificationAlert, openNotificationExtras);
+        }
+        if (notificationAlert != null) {
+            transmitNotificationReceive(notificationTitle, notificationAlert, notificationExtras);
+        }
+    }
+
+    public void onResume(boolean multitasking) {
+        if (openNotificationAlert != null) {
+            notificationAlert = null;
+            transmitNotificationOpen(openNotificationTitle, openNotificationAlert, openNotificationExtras);
+        }
+        if (notificationAlert != null) {
+            transmitNotificationReceive(notificationTitle, notificationAlert, notificationExtras);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        cordovaActivity = null;
+        instance = null;
+    }
+
+    private static JSONObject getMessageObject(String message, Map<String, Object> extras) {
+        JSONObject data = new JSONObject();
+        try {
+            data.put("message", message);
+            JSONObject jExtras = new JSONObject();
+            for (Entry<String, Object> entry : extras.entrySet()) {
+                if (entry.getKey().equals("cn.jpush.android.EXTRA")) {
+                    JSONObject jo;
+                    if (TextUtils.isEmpty((String) entry.getValue())) {
+                        jo = new JSONObject();
+                    } else {
+                        jo = new JSONObject((String) entry.getValue());
+                        String key;
+                        Iterator keys = jo.keys();
+                        while (keys.hasNext()) {
+                            key = keys.next().toString();
+                            jExtras.put(key, jo.getString(key));
+                        }
+                    }
+                    jExtras.put("cn.jpush.android.EXTRA", jo);
+                } else {
+                    jExtras.put(entry.getKey(), entry.getValue());
+                }
+            }
+            if (jExtras.length() > 0) {
+                data.put("extras", jExtras);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return data;
+    }
+
+    private static JSONObject getNotificationObject(String title, String alert, Map<String, Object> extras) {
+        JSONObject data = new JSONObject();
+        try {
+            data.put("title", title);
+            data.put("alert", alert);
+            JSONObject jExtras = new JSONObject();
+            for (Entry<String, Object> entry : extras.entrySet()) {
+                if (entry.getKey().equals("cn.jpush.android.EXTRA")) {
+                    JSONObject jo;
+                    if (TextUtils.isEmpty((String) entry.getValue())) {
+                        jo = new JSONObject();
+                    } else {
+                        jo = new JSONObject((String) entry.getValue());
+                        String key;
+                        Iterator keys = jo.keys();
+                        while (keys.hasNext()) {
+                            key = keys.next().toString();
+                            jExtras.put(key, jo.getString(key));
+                        }
+                    }
+                    jExtras.put("cn.jpush.android.EXTRA", jo);
+                } else {
+                    jExtras.put(entry.getKey(), entry.getValue());
+                }
+            }
+            if (jExtras.length() > 0) {
+                data.put("extras", jExtras);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return data;
+    }
+
+    static void transmitMessageReceive(String message, Map<String, Object> extras) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = getMessageObject(message, extras);
+        String format = "window.plugins.jPushPlugin.receiveMessageInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+    }
+
+    static void transmitNotificationOpen(String title, String alert, Map<String, Object> extras) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = getNotificationObject(title, alert, extras);
+        String format = "window.plugins.jPushPlugin.openNotificationInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+        JPushPlugin.openNotificationTitle = null;
+        JPushPlugin.openNotificationAlert = null;
+    }
+
+    static void transmitNotificationReceive(String title, String alert, Map<String, Object> extras) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = getNotificationObject(title, alert, extras);
+        String format = "window.plugins.jPushPlugin.receiveNotificationInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+        JPushPlugin.notificationTitle = null;
+        JPushPlugin.notificationAlert = null;
+    }
+
+    static void transmitReceiveRegistrationId(String rId) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = new JSONObject();
+        try {
+            data.put("registrationId", rId);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        String format = "window.plugins.jPushPlugin.receiveRegistrationIdInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+    }
+
+    @Override
+    public boolean execute(final String action, final JSONArray data, final CallbackContext callbackContext)
+            throws JSONException {
+        cordova.getThreadPool().execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Method method = JPushPlugin.class.getDeclaredMethod(action, JSONArray.class, CallbackContext.class);
+                    method.invoke(JPushPlugin.this, data, callbackContext);
+                } catch (Exception e) {
+                    Log.e(TAG, e.toString());
+                }
+            }
+        });
+        return true;
+    }
+
+    void init(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.init(mContext);
+    }
+
+    void setDebugMode(JSONArray data, CallbackContext callbackContext) {
+        boolean mode;
+        try {
+            mode = data.getBoolean(0);
+            JPushInterface.setDebugMode(mode);
+            callbackContext.success();
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    void stopPush(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.stopPush(mContext);
+        callbackContext.success();
+    }
+
+    void resumePush(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.resumePush(mContext);
+        callbackContext.success();
+    }
+
+    void isPushStopped(JSONArray data, CallbackContext callbackContext) {
+        boolean isStopped = JPushInterface.isPushStopped(mContext);
+        if (isStopped) {
+            callbackContext.success(1);
+        } else {
+            callbackContext.success(0);
+        }
+    }
+
+    void areNotificationEnabled(JSONArray data, final CallbackContext callback) {
+        int isEnabled;
+        if (hasPermission("OP_POST_NOTIFICATION")) {
+            isEnabled = 1;
+        } else {
+            isEnabled = 0;
+        }
+        callback.success(isEnabled);
+    }
+
+    void setLatestNotificationNum(JSONArray data, CallbackContext callbackContext) {
+        int num = -1;
+        try {
+            num = data.getInt(0);
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error reading num json");
+        }
+        if (num != -1) {
+            JPushInterface.setLatestNotificationNumber(mContext, num);
+        } else {
+            callbackContext.error("error num");
+        }
+    }
+
+    void setPushTime(JSONArray data, CallbackContext callbackContext) {
+        Set<Integer> days = new HashSet<Integer>();
+        JSONArray dayArray;
+        int startHour = -1;
+        int endHour = -1;
+        try {
+            dayArray = data.getJSONArray(0);
+            for (int i = 0; i < dayArray.length(); i++) {
+                days.add(dayArray.getInt(i));
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error reading days json");
+        }
+        try {
+            startHour = data.getInt(1);
+            endHour = data.getInt(2);
+        } catch (JSONException e) {
+            callbackContext.error("error reading hour json");
+        }
+        Context context = mContext;
+        JPushInterface.setPushTime(context, days, startHour, endHour);
+        callbackContext.success();
+    }
+
+    void getRegistrationID(JSONArray data, CallbackContext callbackContext) {
+        Context context = mContext;
+        String regID = JPushInterface.getRegistrationID(context);
+        callbackContext.success(regID);
+    }
+
+    void onResume(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.onResume(this.cordova.getActivity());
+    }
+
+    void onPause(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.onPause(this.cordova.getActivity());
+    }
+
+    void reportNotificationOpened(JSONArray data, CallbackContext callbackContext) {
+        try {
+            String msgID;
+            msgID = data.getString(0);
+            JPushInterface.reportNotificationOpened(this.cordova.getActivity(), msgID);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    void setAlias(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        String alias = null;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+            alias = params.getString("alias");
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.setAlias(mContext, sequence, alias);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void deleteAlias(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.deleteAlias(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void getAlias(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.getAlias(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void setTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        Set<String> tags = new HashSet<String>();
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+            JSONArray tagsArr = params.getJSONArray("tags");
+            for (int i = 0; i < tagsArr.length(); i++) {
+                tags.add(tagsArr.getString(i));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.setTags(mContext, sequence, tags);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void addTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        Set<String> tags = new HashSet<String>();
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+            JSONArray tagsArr = params.getJSONArray("tags");
+            for (int i = 0; i < tagsArr.length(); i++) {
+                tags.add(tagsArr.getString(i));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.addTags(mContext, sequence, tags);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void deleteTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        Set<String> tags = new HashSet<String>();
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+            JSONArray tagsArr = params.getJSONArray("tags");
+            for (int i = 0; i < tagsArr.length(); i++) {
+                tags.add(tagsArr.getString(i));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.deleteTags(mContext, sequence, tags);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void cleanTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.cleanTags(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void getAllTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.getAllTags(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void checkTagBindState(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        String tag = null;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+            tag = params.getString("tag");
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.checkTagBindState(mContext, sequence, tag);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void getConnectionState(JSONArray data, CallbackContext callback) {
+        boolean isConnected = JPushInterface.getConnectionState(cordovaActivity.getApplicationContext());
+        if (isConnected) {
+            callback.success(1);
+        } else {
+            callback.success(0);
+        }
+    }
+
+    /**
+     * 自定义通知行为,声音、震动、呼吸灯等。
+     */
+    void setBasicPushNotificationBuilder(JSONArray data, CallbackContext callbackContext) {
+        BasicPushNotificationBuilder builder = new BasicPushNotificationBuilder(this.cordova.getActivity());
+        builder.developerArg0 = "Basic builder 1";
+        JPushInterface.setPushNotificationBuilder(1, builder);
+        JSONObject obj = new JSONObject();
+        try {
+            obj.put("id", 1);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 自定义推送通知栏样式,需要自己实现具体代码。 http://docs.jiguang.cn/client/android_tutorials/#_11
+     */
+    void setCustomPushNotificationBuilder(JSONArray data, CallbackContext callbackContext) {
+        // CustomPushNotificationBuilder builder = new CustomPushNotificationBuilder(
+        // this.cordova.getActivity(), R.layout.test_notification_layout,
+        // R.id.icon, R.id.title, R.id.text);
+        // JPushInterface.setPushNotificationBuilder(2, builder);
+        // JPushInterface.setDefaultPushNotificationBuilder(builder);
+    }
+
+    void clearAllNotification(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.clearAllNotifications(this.cordova.getActivity());
+    }
+
+    void clearNotificationById(JSONArray data, CallbackContext callbackContext) {
+        int notificationId = -1;
+        try {
+            notificationId = data.getInt(0);
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error reading id json");
+            return;
+        }
+        if (notificationId != -1) {
+            JPushInterface.clearNotificationById(this.cordova.getActivity(), notificationId);
+        } else {
+            callbackContext.error("error id");
+        }
+    }
+
+    void addLocalNotification(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        int builderId = data.getInt(0);
+        String content = data.getString(1);
+        String title = data.getString(2);
+        int notificationID = data.getInt(3);
+        int broadcastTime = data.getInt(4);
+        String extrasStr = data.isNull(5) ? "" : data.getString(5);
+        JSONObject extras = new JSONObject();
+        if (!extrasStr.isEmpty()) {
+            extras = new JSONObject(extrasStr);
+        }
+
+        JPushLocalNotification ln = new JPushLocalNotification();
+        ln.setBuilderId(builderId);
+        ln.setContent(content);
+        ln.setTitle(title);
+        ln.setNotificationId(notificationID);
+        ln.setBroadcastTime(System.currentTimeMillis() + broadcastTime);
+        ln.setExtras(extras.toString());
+
+        JPushInterface.addLocalNotification(this.cordova.getActivity(), ln);
+    }
+
+    void removeLocalNotification(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        int notificationID = data.getInt(0);
+        JPushInterface.removeLocalNotification(this.cordova.getActivity(), notificationID);
+    }
+
+    void clearLocalNotifications(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.clearLocalNotifications(this.cordova.getActivity());
+    }
+
+    /**
+     * 设置通知静默时间 http://docs.jpush.io/client/android_api/#api_5
+     */
+    void setSilenceTime(JSONArray data, CallbackContext callbackContext) {
+        try {
+            int startHour = data.getInt(0);
+            int startMinute = data.getInt(1);
+            int endHour = data.getInt(2);
+            int endMinute = data.getInt(3);
+            if (!isValidHour(startHour) || !isValidMinute(startMinute)) {
+                callbackContext.error("开始时间数值错误");
+                return;
+            }
+            if (!isValidHour(endHour) || !isValidMinute(endMinute)) {
+                callbackContext.error("结束时间数值错误");
+                return;
+            }
+            JPushInterface.setSilenceTime(this.cordova.getActivity(), startHour, startMinute, endHour, endMinute);
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error: reading json data.");
+        }
+    }
+
+    void setGeofenceInterval(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        long interval = data.getLong(0);
+        JPushInterface.setGeofenceInterval(this.cordova.getActivity(), interval);
+    }
+
+    void setMaxGeofenceNumber(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        int maxNumber = data.getInt(0);
+        JPushInterface.setMaxGeofenceNumber(mContext, maxNumber);
+    }
+
+    private boolean isValidHour(int hour) {
+        return !(hour < 0 || hour > 23);
+    }
+
+    private boolean isValidMinute(int minute) {
+        return !(minute < 0 || minute > 59);
+    }
+
+    /**
+     * 用于 Android 6.0 以上系统申请权限,具体可参考:
+     * http://docs.Push.io/client/android_api/#android-60
+     */
+    void requestPermission(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.requestPermission(this.cordova.getActivity());
+    }
+
+    private final TagAliasCallback mTagWithAliasCallback = new TagAliasCallback() {
+        @Override
+        public void gotResult(int code, String alias, Set<String> tags) {
+            if (instance == null) {
+                return;
+            }
+            JSONObject data = new JSONObject();
+            try {
+                data.put("resultCode", code);
+                data.put("tags", tags);
+                data.put("alias", alias);
+                final String jsEvent = String.format("cordova.fireDocumentEvent('jpush.setTagsWithAlias',%s)",
+                        data.toString());
+                cordova.getActivity().runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        instance.webView.loadUrl("javascript:" + jsEvent);
+                    }
+                });
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+    };
+
+    private boolean hasPermission(String appOpsServiceId) {
+
+        Context context = cordova.getActivity().getApplicationContext();
+        if (Build.VERSION.SDK_INT >= 24) {
+            NotificationManager mNotificationManager = (NotificationManager) context
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            return mNotificationManager.areNotificationsEnabled();
+        } else {
+            AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+            ApplicationInfo appInfo = context.getApplicationInfo();
+
+            String pkg = context.getPackageName();
+            int uid = appInfo.uid;
+            Class appOpsClazz;
+
+            try {
+                appOpsClazz = Class.forName(AppOpsManager.class.getName());
+                Method checkOpNoThrowMethod = appOpsClazz.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE,
+                        String.class);
+                Field opValue = appOpsClazz.getDeclaredField(appOpsServiceId);
+                int value = opValue.getInt(Integer.class);
+                Object result = checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg);
+                return Integer.parseInt(result.toString()) == AppOpsManager.MODE_ALLOWED;
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (NoSuchMethodException e) {
+                e.printStackTrace();
+            } catch (NoSuchFieldException e) {
+                e.printStackTrace();
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushReceiver.java b/platforms/android/app/src/main/java/cn/jiguang/cordova/push/JPushReceiver.java
new file mode 100644 (file)
index 0000000..806a7fc
--- /dev/null
@@ -0,0 +1,86 @@
+package cn.jiguang.cordova.push;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import cn.jpush.android.api.JPushInterface;
+
+public class JPushReceiver extends BroadcastReceiver {
+
+    private static final List<String> IGNORED_EXTRAS_KEYS = Arrays.asList("cn.jpush.android.TITLE",
+            "cn.jpush.android.MESSAGE", "cn.jpush.android.APPKEY", "cn.jpush.android.NOTIFICATION_CONTENT_TITLE","key_show_entity","platform");
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (action.equals(JPushInterface.ACTION_REGISTRATION_ID)) {
+            String rId = intent.getStringExtra(JPushInterface.EXTRA_REGISTRATION_ID);
+            JPushPlugin.transmitReceiveRegistrationId(rId);
+        } else if (action.equals(JPushInterface.ACTION_MESSAGE_RECEIVED)) {
+            handlingMessageReceive(intent);
+        } else if (action.equals(JPushInterface.ACTION_NOTIFICATION_RECEIVED)) {
+            handlingNotificationReceive(context, intent);
+        } else if (action.equals(JPushInterface.ACTION_NOTIFICATION_OPENED)) {
+            handlingNotificationOpen(context, intent);
+        }
+    }
+
+    private void handlingMessageReceive(Intent intent) {
+        String msg = intent.getStringExtra(JPushInterface.EXTRA_MESSAGE);
+        Map<String, Object> extras = getNotificationExtras(intent);
+        JPushPlugin.transmitMessageReceive(msg, extras);
+    }
+
+    private void handlingNotificationOpen(Context context, Intent intent) {
+        String title = intent.getStringExtra(JPushInterface.EXTRA_NOTIFICATION_TITLE);
+        JPushPlugin.openNotificationTitle = title;
+
+        String alert = intent.getStringExtra(JPushInterface.EXTRA_ALERT);
+        JPushPlugin.openNotificationAlert = alert;
+
+        Map<String, Object> extras = getNotificationExtras(intent);
+        JPushPlugin.openNotificationExtras = extras;
+
+        JPushPlugin.transmitNotificationOpen(title, alert, extras);
+
+        Intent launch = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+        if (launch != null) {
+            launch.addCategory(Intent.CATEGORY_LAUNCHER);
+            launch.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            context.startActivity(launch);
+        }
+    }
+
+    private void handlingNotificationReceive(Context context, Intent intent) {
+        String title = intent.getStringExtra(JPushInterface.EXTRA_NOTIFICATION_TITLE);
+        JPushPlugin.notificationTitle = title;
+
+        String alert = intent.getStringExtra(JPushInterface.EXTRA_ALERT);
+        JPushPlugin.notificationAlert = alert;
+
+        Map<String, Object> extras = getNotificationExtras(intent);
+        JPushPlugin.notificationExtras = extras;
+
+        JPushPlugin.transmitNotificationReceive(title, alert, extras);
+    }
+
+    private Map<String, Object> getNotificationExtras(Intent intent) {
+        Map<String, Object> extrasMap = new HashMap<String, Object>();
+        for (String key : intent.getExtras().keySet()) {
+            if (!IGNORED_EXTRAS_KEYS.contains(key)) {
+                if (key.equals(JPushInterface.EXTRA_NOTIFICATION_ID)) {
+                    extrasMap.put(key, intent.getIntExtra(key, 0));
+                } else {
+                    extrasMap.put(key, intent.getStringExtra(key));
+                }
+            }
+        }
+        return extrasMap;
+    }
+}
diff --git a/platforms/android/app/src/main/java/cn/jiguang/cordova/push/PushService.java b/platforms/android/app/src/main/java/cn/jiguang/cordova/push/PushService.java
new file mode 100644 (file)
index 0000000..2b0974e
--- /dev/null
@@ -0,0 +1,6 @@
+package cn.jiguang.cordova.push;
+
+import cn.jpush.android.service.JCommonService;
+
+public class PushService extends JCommonService {
+}
diff --git a/platforms/android/app/src/main/jniLibs/arm64-v8a/libjcore212.so b/platforms/android/app/src/main/jniLibs/arm64-v8a/libjcore212.so
new file mode 100755 (executable)
index 0000000..7c7a83e
Binary files /dev/null and b/platforms/android/app/src/main/jniLibs/arm64-v8a/libjcore212.so differ
diff --git a/platforms/android/app/src/main/jniLibs/armeabi-v7a/libjcore212.so b/platforms/android/app/src/main/jniLibs/armeabi-v7a/libjcore212.so
new file mode 100755 (executable)
index 0000000..d09da8e
Binary files /dev/null and b/platforms/android/app/src/main/jniLibs/armeabi-v7a/libjcore212.so differ
diff --git a/platforms/android/app/src/main/jniLibs/armeabi/libjcore212.so b/platforms/android/app/src/main/jniLibs/armeabi/libjcore212.so
new file mode 100755 (executable)
index 0000000..0106c5a
Binary files /dev/null and b/platforms/android/app/src/main/jniLibs/armeabi/libjcore212.so differ
diff --git a/platforms/android/app/src/main/jniLibs/mips/libjcore212.so b/platforms/android/app/src/main/jniLibs/mips/libjcore212.so
new file mode 100755 (executable)
index 0000000..5110f46
Binary files /dev/null and b/platforms/android/app/src/main/jniLibs/mips/libjcore212.so differ
diff --git a/platforms/android/app/src/main/jniLibs/mips64/libjcore212.so b/platforms/android/app/src/main/jniLibs/mips64/libjcore212.so
new file mode 100755 (executable)
index 0000000..1df557b
Binary files /dev/null and b/platforms/android/app/src/main/jniLibs/mips64/libjcore212.so differ
diff --git a/platforms/android/app/src/main/jniLibs/x86/libjcore212.so b/platforms/android/app/src/main/jniLibs/x86/libjcore212.so
new file mode 100755 (executable)
index 0000000..8eae348
Binary files /dev/null and b/platforms/android/app/src/main/jniLibs/x86/libjcore212.so differ
diff --git a/platforms/android/app/src/main/jniLibs/x86_64/libjcore212.so b/platforms/android/app/src/main/jniLibs/x86_64/libjcore212.so
new file mode 100755 (executable)
index 0000000..c935dca
Binary files /dev/null and b/platforms/android/app/src/main/jniLibs/x86_64/libjcore212.so differ
diff --git a/platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png b/platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png
new file mode 100755 (executable)
index 0000000..c9f4e4d
Binary files /dev/null and b/platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png differ
diff --git a/platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png b/platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png
new file mode 100755 (executable)
index 0000000..f289651
Binary files /dev/null and b/platforms/android/app/src/main/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png differ
diff --git a/platforms/android/app/src/main/res/drawable/jpush_richpush_btn_selector.xml b/platforms/android/app/src/main/res/drawable/jpush_richpush_btn_selector.xml
new file mode 100755 (executable)
index 0000000..c6dd002
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?> 
+<selector xmlns:android="http://schemas.android.com/apk/res/android" > 
+    <!-- 获得焦点但未按下时的背景图片 --> 
+    <item 
+        android:state_focused="true" 
+        android:state_enabled="true" 
+        android:state_pressed="false" 
+        android:drawable="@drawable/jpush_ic_richpush_actionbar_back" />
+     <!-- 按下时的背景图片 --> 
+    <item 
+        android:state_enabled="true" 
+        android:state_pressed="true" 
+        android:drawable="@android:color/darker_gray" /> 
+    <!-- 按下时的背景图片 --> 
+    <item 
+        android:state_enabled="true" 
+        android:state_checked="true" 
+        android:drawable="@android:color/darker_gray" /> 
+    <!-- 默认时的背景图片 --> 
+    <item android:drawable="@drawable/jpush_ic_richpush_actionbar_back" />
+</selector> 
\ No newline at end of file
diff --git a/platforms/android/app/src/main/res/drawable/jpush_richpush_progressbar.xml b/platforms/android/app/src/main/res/drawable/jpush_richpush_progressbar.xml
new file mode 100755 (executable)
index 0000000..a1d9b8f
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <!-- 背景  gradient是渐变,corners定义的是圆角 -->
+    <item android:id="@android:id/background">
+        <shape>
+            <solid android:color="#ffffff" />
+        </shape>
+    </item>
+    
+    <!-- 进度条 -->
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <solid android:color="#4393ea" />
+            </shape>
+        </clip>
+    </item>
+
+</layer-list>
\ No newline at end of file
diff --git a/platforms/android/app/src/main/res/layout/jpush_popwin_layout.xml b/platforms/android/app/src/main/res/layout/jpush_popwin_layout.xml
new file mode 100755 (executable)
index 0000000..f43e478
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/popLayoutId"
+    style="@style/MyDialogStyle"
+    android:orientation="vertical"
+    android:layout_width="280dp"
+    android:layout_height="250dp" >
+
+        <WebView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/wvPopwin"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/platforms/android/app/src/main/res/layout/jpush_webview_layout.xml b/platforms/android/app/src/main/res/layout/jpush_webview_layout.xml
new file mode 100755 (executable)
index 0000000..bebdd61
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<cn.jpush.android.ui.FullScreenView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/actionbarLayoutId"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <RelativeLayout
+        android:id="@+id/rlRichpushTitleBar"
+        android:layout_width="match_parent"
+        android:layout_height="40.0dp"
+        android:background="#29313a">
+
+        <ImageButton
+            android:id="@+id/imgRichpushBtnBack"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginLeft="9dp"
+            android:layout_marginRight="10dp"
+            android:background="@drawable/jpush_richpush_btn_selector" />
+
+        <ImageView
+            android:id="@+id/imgView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toRightOf="@id/imgRichpushBtnBack"
+            android:clickable="false"
+            android:src="@drawable/jpush_ic_richpush_actionbar_divider" />
+
+        <TextView
+            android:id="@+id/tvRichpushTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginLeft="7dp"
+            android:layout_marginRight="5dp"
+            android:layout_toRightOf="@id/imgView"
+            android:clickable="false"
+            android:text=" "
+            android:textSize="20sp"
+            android:textColor="#ffffff" />
+    </RelativeLayout>
+
+    <ProgressBar
+        android:id="@+id/pushPrograssBar"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:progress="0"
+        android:progressDrawable="@drawable/jpush_richpush_progressbar"
+        style="?android:attr/progressBarStyleHorizontal" />
+    <WebView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/fullWebView"
+        android:background="#000000" />
+
+</cn.jpush.android.ui.FullScreenView>
\ No newline at end of file
diff --git a/platforms/android/app/src/main/res/layout/push_notification.xml b/platforms/android/app/src/main/res/layout/push_notification.xml
new file mode 100755 (executable)
index 0000000..299ea09
--- /dev/null
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+    android:id="@+id/push_root_view"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="4dp"
+    android:paddingTop="2dp"
+    android:paddingLeft="8dp"
+    android:paddingRight="8dp"
+    >
+
+    <ImageView
+        android:id="@+id/push_notification_bg"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="centerCrop"/>
+    <RelativeLayout
+        android:id="@+id/push_notification_style_default"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <LinearLayout
+            android:id="@+id/push_notification_layout_lefttop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:gravity="center_vertical"
+            android:orientation="horizontal">
+            <ImageView
+                android:id="@+id/push_notification_small_icon"
+                android:layout_width="18dp"
+                android:layout_height="18dp"
+                android:layout_marginLeft="6dp"
+                android:scaleType="centerInside"/>
+            <TextView
+                android:id="@+id/push_notification_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="4dp"
+                android:maxLines="1"
+                android:maxWidth="200dp"
+                android:maxLength="24"
+                android:textColor="@android:color/black"
+                android:textSize="12sp"/>
+            <TextView
+                android:id="@+id/push_notification_dot"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="4dp"
+                android:text="· "
+                android:textColor="@android:color/black"
+                android:textSize="20sp"/>
+            <TextView
+                android:maxLines="1"
+                android:id="@+id/push_notification_date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@android:color/black"
+                android:textSize="12sp"
+                />
+        </LinearLayout>
+
+
+        <ImageView
+            android:id="@+id/push_notification_big_icon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="8dp"
+            android:scaleType="centerInside"/>
+
+        <TextView
+            android:id="@+id/push_notification_sub_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="@android:color/black"
+            android:layout_below="@id/push_notification_layout_lefttop"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:layout_marginLeft="6dp"
+            android:layout_marginRight="4dp"
+            android:visibility="gone"
+            android:layout_marginTop="1dp"
+            android:textSize="13sp"
+            android:maxLines="1"
+            />
+
+        <TextView
+            android:id="@+id/push_notification_content"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="@android:color/black"
+            android:layout_below="@id/push_notification_sub_title"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:layout_marginLeft="6dp"
+            android:layout_marginRight="4dp"
+            android:layout_marginTop="1dp"
+            android:textSize="13sp"
+            android:maxLines="2"
+            android:ellipsize="end"
+            />
+        <TextView
+            android:id="@+id/push_notification_content_one_line"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            android:textColor="@android:color/black"
+            android:layout_below="@id/push_notification_sub_title"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:layout_marginLeft="6dp"
+            android:layout_marginRight="4dp"
+            android:layout_marginTop="1dp"
+            android:textSize="13sp"
+            android:maxLines="1"
+            android:ellipsize="end"
+            />
+    </RelativeLayout>
+    <RelativeLayout
+        android:id="@+id/push_notification_style_1"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <ImageView
+            android:id="@+id/push_notification_style_1_big_icon"
+            android:layout_width="50dp"
+            android:layout_height="50dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:scaleType="centerInside"/>
+        <LinearLayout
+            android:layout_marginLeft="6dp"
+            android:layout_toRightOf="@+id/push_notification_style_1_big_icon"
+            android:orientation="vertical"
+            android:layout_centerVertical="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+            <RelativeLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <TextView
+                    android:id="@+id/push_notification_style_1_date"
+                    android:textSize="12sp"
+                    android:layout_alignParentRight="true"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" />
+                <TextView
+                    android:id="@+id/push_notification_style_1_title"
+                    android:layout_alignParentLeft="true"
+                    android:layout_toLeftOf="@+id/push_notification_style_1_date"
+                    android:textSize="12sp"
+                    android:textStyle="bold"
+                    android:maxLines="1"
+                    android:layout_marginRight="8dp"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </RelativeLayout>
+            <TextView
+                android:id="@+id/push_notification_style_1_content"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@android:color/black"
+                android:layout_marginRight="4dp"
+                android:layout_marginTop="1dp"
+                android:textSize="13sp"
+                android:maxLines="2"
+                android:ellipsize="end" />
+        </LinearLayout>
+    </RelativeLayout>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/platforms/android/app/src/main/res/values-zh/jpush_string.xml b/platforms/android/app/src/main/res/values-zh/jpush_string.xml
new file mode 100755 (executable)
index 0000000..069b6f4
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="jg_channel_name_p_min">不重要</string>
+    <string name="jg_channel_name_p_low">不重要</string>
+    <string name="jg_channel_name_p_default">普通</string>
+    <string name="jg_channel_name_p_high">重要</string>
+</resources>
\ No newline at end of file
diff --git a/platforms/android/app/src/main/res/values/jpush_string.xml b/platforms/android/app/src/main/res/values/jpush_string.xml
new file mode 100755 (executable)
index 0000000..70a4ea7
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="jg_channel_name_p_min">LOW</string>
+    <string name="jg_channel_name_p_low">LOW</string>
+    <string name="jg_channel_name_p_default">NORMAL</string>
+    <string name="jg_channel_name_p_high">HIGH</string>
+
+</resources>
\ No newline at end of file
diff --git a/platforms/android/app/src/main/res/values/jpush_style.xml b/platforms/android/app/src/main/res/values/jpush_style.xml
new file mode 100755 (executable)
index 0000000..81dfdbb
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="MyDialogStyle">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+        <item name="android:backgroundDimEnabled">true</item>
+    </style>
+</resources>
\ No newline at end of file
index e81ee23..a0b93ed 100644 (file)
@@ -34,6 +34,9 @@
     <feature name="ThemeableBrowser">
         <param name="android-package" value="com.initialxy.cordova.themeablebrowser.ThemeableBrowser" />
     </feature>
+    <feature name="JPushPlugin">
+        <param name="android-package" value="cn.jiguang.cordova.push.JPushPlugin" />
+    </feature>
     <name short="大理市民卡">dlapp</name>
     <description>
         A sample Apache Cordova application that responds to the deviceready event.
@@ -60,6 +63,7 @@
     <preference name="loglevel" value="DEBUG" />
     <preference name="AutoHideSplashScreen" value="true" />
     <preference name="SplashScreenDelay" value="0" />
+    <preference name="SplashShowOnlyFirstTime" value="true" />
     <preference name="ErrorUrl" value="error.html" />
     <preference name="Orientation" value="portrait" />
     <preference name="UseSwiftLanguageVersion" value="4.0" />
index bea8a74..a3473f2 100644 (file)
@@ -29,6 +29,8 @@ buildscript {
         // in the individual module build.gradle files
 
         classpath 'com.android.tools.build:gradle:3.3.0'
+
+
     }
 }
 
index 049ccd6..595ecb1 100644 (file)
@@ -319,6 +319,14 @@ cordova.define('cordova/plugin_list', function(require, exports, module) {
       "clobbers": [
         "cordova.ThemeableBrowser"
       ]
+    },
+    {
+      "id": "jpush-phonegap-plugin.JPushPlugin",
+      "file": "plugins/jpush-phonegap-plugin/www/JPushPlugin.js",
+      "pluginId": "jpush-phonegap-plugin",
+      "clobbers": [
+        "JPush"
+      ]
     }
   ];
   module.exports.metadata = {
@@ -334,6 +342,8 @@ cordova.define('cordova/plugin_list', function(require, exports, module) {
     "cordova-plugin-camera": "4.0.3",
     "cordova-plugin-inappbrowser": "3.0.0",
     "cordova-plugin-device": "2.0.2",
-    "cordova-plugin-themeablebrowser": "0.2.17"
+    "cordova-plugin-themeablebrowser": "0.2.17",
+    "cordova-plugin-jcore": "1.3.0",
+    "jpush-phonegap-plugin": "3.7.2"
   };
 });
\ No newline at end of file
diff --git a/platforms/android/platform_www/plugins/cordova-plugin-firebasex/www/firebase.js b/platforms/android/platform_www/plugins/cordova-plugin-firebasex/www/firebase.js
new file mode 100644 (file)
index 0000000..4dd1f7e
--- /dev/null
@@ -0,0 +1,187 @@
+cordova.define("cordova-plugin-firebasex.FirebasePlugin", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+exports.getVerificationID = function (number, success, error) {
+  exec(success, error, "FirebasePlugin", "getVerificationID", [number]);
+};
+
+exports.getId = function (success, error) {
+  exec(success, error, "FirebasePlugin", "getId", []);
+};
+
+exports.getToken = function (success, error) {
+  exec(success, error, "FirebasePlugin", "getToken", []);
+};
+
+exports.onMessageReceived = function (success, error) {
+  exec(success, error, "FirebasePlugin", "onMessageReceived", []);
+};
+
+exports.onTokenRefresh = function (success, error) {
+  exec(success, error, "FirebasePlugin", "onTokenRefresh", []);
+};
+
+exports.setBadgeNumber = function (number, success, error) {
+  exec(success, error, "FirebasePlugin", "setBadgeNumber", [number]);
+};
+
+exports.getBadgeNumber = function (success, error) {
+  exec(success, error, "FirebasePlugin", "getBadgeNumber", []);
+};
+
+exports.subscribe = function (topic, success, error) {
+  exec(success, error, "FirebasePlugin", "subscribe", [topic]);
+};
+
+exports.unsubscribe = function (topic, success, error) {
+  exec(success, error, "FirebasePlugin", "unsubscribe", [topic]);
+};
+
+exports.unregister = function (success, error) {
+  exec(success, error, "FirebasePlugin", "unregister", []);
+};
+
+exports.logEvent = function (name, params, success, error) {
+  exec(success, error, "FirebasePlugin", "logEvent", [name, params]);
+};
+
+
+
+exports.setScreenName = function (name, success, error) {
+  exec(success, error, "FirebasePlugin", "setScreenName", [name]);
+};
+
+exports.setUserId = function (id, success, error) {
+  exec(success, error, "FirebasePlugin", "setUserId", [id]);
+};
+
+exports.setUserProperty = function (name, value, success, error) {
+  exec(success, error, "FirebasePlugin", "setUserProperty", [name, value]);
+};
+
+exports.activateFetched = function (success, error) {
+  exec(success, error, "FirebasePlugin", "activateFetched", []);
+};
+
+exports.fetch = function (cacheExpirationSeconds, success, error) {
+  var args = [];
+  if (typeof cacheExpirationSeconds === 'number') {
+    args.push(cacheExpirationSeconds);
+  } else {
+    error = success;
+    success = cacheExpirationSeconds;
+  }
+  exec(success, error, "FirebasePlugin", "fetch", args);
+};
+
+exports.getByteArray = function (key, success, error) {
+  exec(success, error, "FirebasePlugin", "getByteArray", [key]);
+};
+
+exports.getValue = function (key, success, error) {
+  exec(success, error, "FirebasePlugin", "getValue", [key]);
+};
+
+exports.getInfo = function (success, error) {
+  exec(success, error, "FirebasePlugin", "getInfo", []);
+};
+
+exports.setConfigSettings = function (settings, success, error) {
+  exec(success, error, "FirebasePlugin", "setConfigSettings", [settings]);
+};
+
+exports.setDefaults = function (defaults, success, error) {
+  exec(success, error, "FirebasePlugin", "setDefaults", [defaults]);
+};
+
+exports.startTrace = function (name, success, error) {
+  exec(success, error, "FirebasePlugin", "startTrace", [name]);
+};
+
+exports.incrementCounter = function (name, counterNamed, success, error) {
+  exec(success, error, "FirebasePlugin", "incrementCounter", [name, counterNamed]);
+};
+
+exports.stopTrace = function (name, success, error) {
+  exec(success, error, "FirebasePlugin", "stopTrace", [name]);
+};
+
+exports.setAnalyticsCollectionEnabled = function (enabled, success, error) {
+  exec(success, error, "FirebasePlugin", "setAnalyticsCollectionEnabled", [enabled]);
+};
+
+exports.setPerformanceCollectionEnabled = function (enabled, success, error) {
+  exec(success, error, "FirebasePlugin", "setPerformanceCollectionEnabled", [enabled]);
+};
+
+exports.verifyPhoneNumber = function (number, timeOutDuration, success, error) {
+  if (typeof timeOutDuration === 'function') {
+    // method being called with old signature: function(number, success, error)
+    // timeOutDuration is the success callback, success is the error callback
+    exec(timeOutDuration, success, "FirebasePlugin", "verifyPhoneNumber", [number]);
+  } else {
+    // method being called with new signature: function(number, timeOutDuration, success, error)
+    // callbacks are correctly named
+    exec(success, error, "FirebasePlugin", "verifyPhoneNumber", [number, timeOutDuration]);
+  }
+};
+
+exports.clearAllNotifications = function (success, error) {
+  exec(success, error, "FirebasePlugin", "clearAllNotifications", []);
+};
+
+
+// Crashlytics
+exports.logMessage = function (message, success, error) {
+    exec(success, error, "FirebasePlugin", "logMessage", [message]);
+};
+
+exports.sendCrash = function (success, error) {
+    exec(success, error, "FirebasePlugin", "sendCrash", []);
+};
+
+exports.logError = function (message, stackTrace, success, error) {
+  var args = [message];
+  // "stackTrace" is an optional arg that's an array of objects.
+  if (stackTrace) {
+    if (typeof stackTrace === 'function') {
+      error = success;
+      success = stackTrace;
+    } else {
+      args.push(stackTrace);
+    }
+  }
+  exec(success, error, "FirebasePlugin", "logError", args);
+};
+
+exports.setCrashlyticsUserId = function (userId, success, error) {
+    exec(success, error, "FirebasePlugin", "setCrashlyticsUserId", [userId]);
+};
+
+// iOS-only
+exports.grantPermission = function (success, error) {
+  exec(success, error, "FirebasePlugin", "grantPermission", []);
+};
+
+exports.hasPermission = function (success, error) {
+  exec(success, error, "FirebasePlugin", "hasPermission", []);
+};
+
+// Android-only
+exports.setDefaultChannel = function (options, success, error) {
+    exec(success, error, "FirebasePlugin", "setDefaultChannel", [options]);
+};
+
+exports.createChannel = function (options, success, error) {
+  exec(success, error, "FirebasePlugin", "createChannel", [options]);
+};
+
+exports.deleteChannel = function (channelID, success, error) {
+  exec(success, error, "FirebasePlugin", "deleteChannel", [channelID]);
+};
+
+exports.listChannels = function (success, error) {
+  exec(success, error, "FirebasePlugin", "listChannels", []);
+};
+
+});
diff --git a/platforms/android/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js b/platforms/android/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js
new file mode 100644 (file)
index 0000000..773b885
--- /dev/null
@@ -0,0 +1,489 @@
+cordova.define("jpush-phonegap-plugin.JPushPlugin", function(require, exports, module) {
+var JPushPlugin = function() {};
+
+// private plugin function
+
+JPushPlugin.prototype.receiveMessage = {};
+JPushPlugin.prototype.openNotification = {};
+JPushPlugin.prototype.receiveNotification = {};
+
+JPushPlugin.prototype.isPlatformIOS = function() {
+  return (
+    device.platform === "iPhone" ||
+    device.platform === "iPad" ||
+    device.platform === "iPod touch" ||
+    device.platform === "iOS"
+  );
+};
+
+JPushPlugin.prototype.errorCallback = function(msg) {
+  console.log("JPush Callback Error: " + msg);
+};
+
+JPushPlugin.prototype.callNative = function(
+  name,
+  args,
+  successCallback,
+  errorCallback
+) {
+  if (errorCallback) {
+    cordova.exec(successCallback, errorCallback, "JPushPlugin", name, args);
+  } else {
+    cordova.exec(
+      successCallback,
+      this.errorCallback,
+      "JPushPlugin",
+      name,
+      args
+    );
+  }
+};
+
+// Common methods
+JPushPlugin.prototype.init = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("initial", [], null);
+  } else {
+    this.callNative("init", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugMode = function(mode) {
+  if (device.platform === "Android") {
+    this.callNative("setDebugMode", [mode], null);
+  } else {
+    if (mode === true) {
+      this.setDebugModeFromIos();
+    } else {
+      this.setLogOFF();
+    }
+  }
+};
+
+JPushPlugin.prototype.getRegistrationID = function(successCallback) {
+  this.callNative("getRegistrationID", [], successCallback);
+};
+
+JPushPlugin.prototype.stopPush = function() {
+  this.callNative("stopPush", [], null);
+};
+
+JPushPlugin.prototype.resumePush = function() {
+  this.callNative("resumePush", [], null);
+};
+
+JPushPlugin.prototype.isPushStopped = function(successCallback) {
+  this.callNative("isPushStopped", [], successCallback);
+};
+
+JPushPlugin.prototype.clearLocalNotifications = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearLocalNotifications", [], null);
+  } else {
+    this.clearAllLocalNotifications();
+  }
+};
+
+/**
+ * 设置标签。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.setTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 新增标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.addTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("addTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除指定标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.deleteTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 清除所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.cleanTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("cleanTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAllTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAllTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询指定标签与当前用户的绑定状态。
+ *
+ * @param params = { 'sequence': number, 'tag': string }
+ */
+JPushPlugin.prototype.checkTagBindState = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative(
+    "checkTagBindState",
+    [params],
+    successCallback,
+    errorCallback
+  );
+};
+
+/**
+ * 设置别名。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'alias': string }
+ */
+JPushPlugin.prototype.setAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.deleteAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询当前绑定的别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAlias", [params], successCallback, errorCallback);
+};
+
+// 判断系统设置中是否对本应用启用通知。
+// iOS: 返回值如果大于 0,代表通知开启;0: 通知关闭。
+// UIRemoteNotificationTypeNone = 0,
+// UIRemoteNotificationTypeBadge = 1 << 0,
+// UIRemoteNotificationTypeSound = 1 << 1,
+// UIRemoteNotificationTypeAlert = 1 << 2,
+// UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3,
+// Android: 返回值 1 代表通知启用;0: 通知关闭。
+JPushPlugin.prototype.getUserNotificationSettings = function(successCallback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getUserNotificationSettings", [], successCallback);
+  } else if (device.platform === "Android") {
+    this.callNative("areNotificationEnabled", [], successCallback);
+  }
+};
+
+// iOS methods
+
+JPushPlugin.prototype.startJPushSDK = function() {
+  this.callNative("startJPushSDK", [], null);
+};
+
+JPushPlugin.prototype.setBadge = function(value) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setBadge", [value], null);
+  }
+};
+
+JPushPlugin.prototype.resetBadge = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("resetBadge", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugModeFromIos = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setDebugModeFromIos", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLogOFF = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLogOFF", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCrashLogON = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("crashLogON", [], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotificationForIOS = function(
+  delayTime,
+  content,
+  badge,
+  notificationID,
+  extras
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "setLocalNotification",
+      [delayTime, content, badge, notificationID, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.deleteLocalNotificationWithIdentifierKeyInIOS = function(
+  identifierKey
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "deleteLocalNotificationWithIdentifierKey",
+      [identifierKey],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.clearAllLocalNotifications = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("clearAllLocalNotifications", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLocation = function(latitude, longitude) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLocation", [latitude, longitude], null);
+  }
+};
+
+JPushPlugin.prototype.startLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("startLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.stopLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("stopLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.beginLogPageView = function(pageName, duration) {
+  if (this.isPlatformIOS()) {
+    this.callNative("beginLogPageView", [pageName, duration], null);
+  }
+};
+
+JPushPlugin.prototype.setApplicationIconBadgeNumber = function(badge) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setApplicationIconBadgeNumber", [badge], null);
+  }
+};
+
+JPushPlugin.prototype.getApplicationIconBadgeNumber = function(callback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getApplicationIconBadgeNumber", [], callback);
+  }
+};
+
+JPushPlugin.prototype.addDismissActions = function(actions, categoryId) {
+  this.callNative("addDismissActions", [actions, categoryId]);
+};
+
+JPushPlugin.prototype.addNotificationActions = function(actions, categoryId) {
+  this.callNative("addNotificationActions", [actions, categoryId]);
+};
+
+// Android methods
+JPushPlugin.prototype.getConnectionState = function(successCallback) {
+  if (device.platform === "Android") {
+    this.callNative("getConnectionState", [], successCallback);
+  }
+};
+
+JPushPlugin.prototype.setBasicPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setBasicPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCustomPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setCustomPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.receiveRegistrationIdInAndroidCallback = function(data) {
+  if (device.platform === "Android") {
+    data = JSON.stringify(data);
+    var event = JSON.parse(data);
+    cordova.fireDocumentEvent("jpush.receiveRegistrationId", event);
+  }
+};
+
+JPushPlugin.prototype.receiveMessageInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveMessage = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.receiveMessage", this.receiveMessage);
+};
+
+JPushPlugin.prototype.openNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.openNotification = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.openNotification", this.openNotification);
+};
+
+JPushPlugin.prototype.receiveNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveNotification = JSON.parse(data);
+  cordova.fireDocumentEvent(
+    "jpush.receiveNotification",
+    this.receiveNotification
+  );
+};
+
+JPushPlugin.prototype.clearAllNotification = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearAllNotification", [], null);
+  }
+};
+
+JPushPlugin.prototype.clearNotificationById = function(id) {
+  if (device.platform === "Android") {
+    this.callNative("clearNotificationById", [id], null);
+  }
+};
+
+JPushPlugin.prototype.setLatestNotificationNum = function(num) {
+  if (device.platform === "Android") {
+    this.callNative("setLatestNotificationNum", [num], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotification = function(
+  builderId,
+  content,
+  title,
+  notificationID,
+  broadcastTime,
+  extras
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "addLocalNotification",
+      [builderId, content, title, notificationID, broadcastTime, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.removeLocalNotification = function(notificationID) {
+  if (device.platform === "Android") {
+    this.callNative("removeLocalNotification", [notificationID], null);
+  }
+};
+
+JPushPlugin.prototype.reportNotificationOpened = function(msgID) {
+  if (device.platform === "Android") {
+    this.callNative("reportNotificationOpened", [msgID], null);
+  }
+};
+
+/**
+ * 用于在 Android 6.0 及以上系统,申请一些权限
+ * 具体可看:http://docs.jpush.io/client/android_api/#android-60
+ */
+JPushPlugin.prototype.requestPermission = function() {
+  if (device.platform === "Android") {
+    this.callNative("requestPermission", [], null);
+  }
+};
+
+JPushPlugin.prototype.setSilenceTime = function(
+  startHour,
+  startMinute,
+  endHour,
+  endMinute
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "setSilenceTime",
+      [startHour, startMinute, endHour, endMinute],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.setPushTime = function(weekdays, startHour, endHour) {
+  if (device.platform === "Android") {
+    this.callNative("setPushTime", [weekdays, startHour, endHour], null);
+  }
+};
+
+JPushPlugin.prototype.setGeofenceInterval = function(interval) {
+  if (device.platform === "Android") {
+    this.callNative("setGeofenceInterval", [interval], null);
+  }
+};
+
+JPushPlugin.prototype.setMaxGeofenceNumber = function(maxNumber) {
+  if (device.platform === "Android") {
+    this.callNative("setMaxGeofenceNumber", [maxNumber], null);
+  }
+};
+
+if (!window.plugins) {
+  window.plugins = {};
+}
+
+if (!window.plugins.jPushPlugin) {
+  window.plugins.jPushPlugin = new JPushPlugin();
+}
+
+module.exports = new JPushPlugin();
+
+});
index 661238d..8a65caa 100644 (file)
@@ -13,4 +13,4 @@ android.library.reference.1=CordovaLib
 android.library.reference.2=app
 cordova.system.library.1=com.squareup.okhttp3:okhttp-urlconnection:3.10.0
 cordova.gradle.include.1=cordova-plugin-qrscanner/dlapp-qrscanner.gradle
-cordova.system.library.2=com.android.support:support-v4:24.1.1+
\ No newline at end of file
+cordova.system.library.2=com.android.support:support-v4:24.1.1+
index 8ae268b..9416cfc 100644 (file)
     },
     "cordova-plugin-themeablebrowser": {
       "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-jcore": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "jpush-phonegap-plugin": {
+      "APP_KEY": "your_jpush_appkey",
+      "CHANNEL": "developer-default",
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
     }
   },
   "dependent_plugins": {},
       "id": "cordova-plugin-device.DeviceProxy",
       "pluginId": "cordova-plugin-device",
       "runs": true
+    },
+    {
+      "file": "plugins/jpush-phonegap-plugin/www/JPushPlugin.js",
+      "id": "jpush-phonegap-plugin.JPushPlugin",
+      "pluginId": "jpush-phonegap-plugin",
+      "clobbers": [
+        "JPush"
+      ]
     }
   ],
   "plugin_metadata": {
     "cordova-plugin-camera": "4.0.3",
     "cordova-plugin-inappbrowser": "3.0.0",
     "cordova-plugin-device": "2.0.2",
-    "cordova-plugin-themeablebrowser": "0.2.17"
+    "cordova-plugin-themeablebrowser": "0.2.17",
+    "cordova-plugin-jcore": "1.3.0",
+    "jpush-phonegap-plugin": "3.7.2"
   }
 }
index 05a30f1..11d63b1 100644 (file)
@@ -24,6 +24,7 @@
     </edit-config>
     <preference name="AutoHideSplashScreen" value="true" />
     <preference name="SplashScreenDelay" value="0" />
+    <preference name="SplashShowOnlyFirstTime" value="true" />
     <preference name="ErrorUrl" value="error.html" />
     <preference name="Orientation" value="portrait" />
     <preference name="UseSwiftLanguageVersion" value="4.0" />
index 2b5f2ff..30be43a 100644 (file)
@@ -345,6 +345,14 @@ module.exports = [
         "id": "cordova-plugin-device.DeviceProxy",
         "pluginId": "cordova-plugin-device",
         "runs": true
+    },
+    {
+        "file": "plugins/jpush-phonegap-plugin/www/JPushPlugin.js",
+        "id": "jpush-phonegap-plugin.JPushPlugin",
+        "pluginId": "jpush-phonegap-plugin",
+        "clobbers": [
+            "JPush"
+        ]
     }
 ];
 module.exports.metadata = 
@@ -362,7 +370,9 @@ module.exports.metadata =
     "cordova-plugin-camera": "4.0.3",
     "cordova-plugin-inappbrowser": "3.0.0",
     "cordova-plugin-device": "2.0.2",
-    "cordova-plugin-themeablebrowser": "0.2.17"
+    "cordova-plugin-themeablebrowser": "0.2.17",
+    "cordova-plugin-jcore": "1.3.0",
+    "jpush-phonegap-plugin": "3.7.2"
 }
 // BOTTOM OF METADATA
 });
\ No newline at end of file
diff --git a/platforms/browser/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js b/platforms/browser/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js
new file mode 100644 (file)
index 0000000..0ee62cc
--- /dev/null
@@ -0,0 +1,488 @@
+cordova.define("jpush-phonegap-plugin.JPushPlugin", function(require, exports, module) { var JPushPlugin = function() {};
+
+// private plugin function
+
+JPushPlugin.prototype.receiveMessage = {};
+JPushPlugin.prototype.openNotification = {};
+JPushPlugin.prototype.receiveNotification = {};
+
+JPushPlugin.prototype.isPlatformIOS = function() {
+  return (
+    device.platform === "iPhone" ||
+    device.platform === "iPad" ||
+    device.platform === "iPod touch" ||
+    device.platform === "iOS"
+  );
+};
+
+JPushPlugin.prototype.errorCallback = function(msg) {
+  console.log("JPush Callback Error: " + msg);
+};
+
+JPushPlugin.prototype.callNative = function(
+  name,
+  args,
+  successCallback,
+  errorCallback
+) {
+  if (errorCallback) {
+    cordova.exec(successCallback, errorCallback, "JPushPlugin", name, args);
+  } else {
+    cordova.exec(
+      successCallback,
+      this.errorCallback,
+      "JPushPlugin",
+      name,
+      args
+    );
+  }
+};
+
+// Common methods
+JPushPlugin.prototype.init = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("initial", [], null);
+  } else {
+    this.callNative("init", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugMode = function(mode) {
+  if (device.platform === "Android") {
+    this.callNative("setDebugMode", [mode], null);
+  } else {
+    if (mode === true) {
+      this.setDebugModeFromIos();
+    } else {
+      this.setLogOFF();
+    }
+  }
+};
+
+JPushPlugin.prototype.getRegistrationID = function(successCallback) {
+  this.callNative("getRegistrationID", [], successCallback);
+};
+
+JPushPlugin.prototype.stopPush = function() {
+  this.callNative("stopPush", [], null);
+};
+
+JPushPlugin.prototype.resumePush = function() {
+  this.callNative("resumePush", [], null);
+};
+
+JPushPlugin.prototype.isPushStopped = function(successCallback) {
+  this.callNative("isPushStopped", [], successCallback);
+};
+
+JPushPlugin.prototype.clearLocalNotifications = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearLocalNotifications", [], null);
+  } else {
+    this.clearAllLocalNotifications();
+  }
+};
+
+/**
+ * 设置标签。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.setTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 新增标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.addTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("addTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除指定标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.deleteTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 清除所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.cleanTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("cleanTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAllTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAllTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询指定标签与当前用户的绑定状态。
+ *
+ * @param params = { 'sequence': number, 'tag': string }
+ */
+JPushPlugin.prototype.checkTagBindState = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative(
+    "checkTagBindState",
+    [params],
+    successCallback,
+    errorCallback
+  );
+};
+
+/**
+ * 设置别名。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'alias': string }
+ */
+JPushPlugin.prototype.setAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.deleteAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询当前绑定的别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAlias", [params], successCallback, errorCallback);
+};
+
+// 判断系统设置中是否对本应用启用通知。
+// iOS: 返回值如果大于 0,代表通知开启;0: 通知关闭。
+// UIRemoteNotificationTypeNone = 0,
+// UIRemoteNotificationTypeBadge = 1 << 0,
+// UIRemoteNotificationTypeSound = 1 << 1,
+// UIRemoteNotificationTypeAlert = 1 << 2,
+// UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3,
+// Android: 返回值 1 代表通知启用;0: 通知关闭。
+JPushPlugin.prototype.getUserNotificationSettings = function(successCallback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getUserNotificationSettings", [], successCallback);
+  } else if (device.platform === "Android") {
+    this.callNative("areNotificationEnabled", [], successCallback);
+  }
+};
+
+// iOS methods
+
+JPushPlugin.prototype.startJPushSDK = function() {
+  this.callNative("startJPushSDK", [], null);
+};
+
+JPushPlugin.prototype.setBadge = function(value) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setBadge", [value], null);
+  }
+};
+
+JPushPlugin.prototype.resetBadge = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("resetBadge", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugModeFromIos = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setDebugModeFromIos", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLogOFF = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLogOFF", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCrashLogON = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("crashLogON", [], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotificationForIOS = function(
+  delayTime,
+  content,
+  badge,
+  notificationID,
+  extras
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "setLocalNotification",
+      [delayTime, content, badge, notificationID, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.deleteLocalNotificationWithIdentifierKeyInIOS = function(
+  identifierKey
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "deleteLocalNotificationWithIdentifierKey",
+      [identifierKey],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.clearAllLocalNotifications = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("clearAllLocalNotifications", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLocation = function(latitude, longitude) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLocation", [latitude, longitude], null);
+  }
+};
+
+JPushPlugin.prototype.startLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("startLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.stopLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("stopLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.beginLogPageView = function(pageName, duration) {
+  if (this.isPlatformIOS()) {
+    this.callNative("beginLogPageView", [pageName, duration], null);
+  }
+};
+
+JPushPlugin.prototype.setApplicationIconBadgeNumber = function(badge) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setApplicationIconBadgeNumber", [badge], null);
+  }
+};
+
+JPushPlugin.prototype.getApplicationIconBadgeNumber = function(callback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getApplicationIconBadgeNumber", [], callback);
+  }
+};
+
+JPushPlugin.prototype.addDismissActions = function(actions, categoryId) {
+  this.callNative("addDismissActions", [actions, categoryId]);
+};
+
+JPushPlugin.prototype.addNotificationActions = function(actions, categoryId) {
+  this.callNative("addNotificationActions", [actions, categoryId]);
+};
+
+// Android methods
+JPushPlugin.prototype.getConnectionState = function(successCallback) {
+  if (device.platform === "Android") {
+    this.callNative("getConnectionState", [], successCallback);
+  }
+};
+
+JPushPlugin.prototype.setBasicPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setBasicPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCustomPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setCustomPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.receiveRegistrationIdInAndroidCallback = function(data) {
+  if (device.platform === "Android") {
+    data = JSON.stringify(data);
+    var event = JSON.parse(data);
+    cordova.fireDocumentEvent("jpush.receiveRegistrationId", event);
+  }
+};
+
+JPushPlugin.prototype.receiveMessageInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveMessage = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.receiveMessage", this.receiveMessage);
+};
+
+JPushPlugin.prototype.openNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.openNotification = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.openNotification", this.openNotification);
+};
+
+JPushPlugin.prototype.receiveNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveNotification = JSON.parse(data);
+  cordova.fireDocumentEvent(
+    "jpush.receiveNotification",
+    this.receiveNotification
+  );
+};
+
+JPushPlugin.prototype.clearAllNotification = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearAllNotification", [], null);
+  }
+};
+
+JPushPlugin.prototype.clearNotificationById = function(id) {
+  if (device.platform === "Android") {
+    this.callNative("clearNotificationById", [id], null);
+  }
+};
+
+JPushPlugin.prototype.setLatestNotificationNum = function(num) {
+  if (device.platform === "Android") {
+    this.callNative("setLatestNotificationNum", [num], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotification = function(
+  builderId,
+  content,
+  title,
+  notificationID,
+  broadcastTime,
+  extras
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "addLocalNotification",
+      [builderId, content, title, notificationID, broadcastTime, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.removeLocalNotification = function(notificationID) {
+  if (device.platform === "Android") {
+    this.callNative("removeLocalNotification", [notificationID], null);
+  }
+};
+
+JPushPlugin.prototype.reportNotificationOpened = function(msgID) {
+  if (device.platform === "Android") {
+    this.callNative("reportNotificationOpened", [msgID], null);
+  }
+};
+
+/**
+ * 用于在 Android 6.0 及以上系统,申请一些权限
+ * 具体可看:http://docs.jpush.io/client/android_api/#android-60
+ */
+JPushPlugin.prototype.requestPermission = function() {
+  if (device.platform === "Android") {
+    this.callNative("requestPermission", [], null);
+  }
+};
+
+JPushPlugin.prototype.setSilenceTime = function(
+  startHour,
+  startMinute,
+  endHour,
+  endMinute
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "setSilenceTime",
+      [startHour, startMinute, endHour, endMinute],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.setPushTime = function(weekdays, startHour, endHour) {
+  if (device.platform === "Android") {
+    this.callNative("setPushTime", [weekdays, startHour, endHour], null);
+  }
+};
+
+JPushPlugin.prototype.setGeofenceInterval = function(interval) {
+  if (device.platform === "Android") {
+    this.callNative("setGeofenceInterval", [interval], null);
+  }
+};
+
+JPushPlugin.prototype.setMaxGeofenceNumber = function(maxNumber) {
+  if (device.platform === "Android") {
+    this.callNative("setMaxGeofenceNumber", [maxNumber], null);
+  }
+};
+
+if (!window.plugins) {
+  window.plugins = {};
+}
+
+if (!window.plugins.jPushPlugin) {
+  window.plugins.jPushPlugin = new JPushPlugin();
+}
+
+module.exports = new JPushPlugin();
+
+});
diff --git a/platforms/ios/Podfile.lock b/platforms/ios/Podfile.lock
new file mode 100644 (file)
index 0000000..61a59aa
--- /dev/null
@@ -0,0 +1,3 @@
+PODFILE CHECKSUM: e5038167266107ffa86081c2bbb7148c097e71eb
+
+COCOAPODS: 1.5.2
diff --git a/platforms/ios/Pods/Manifest.lock b/platforms/ios/Pods/Manifest.lock
new file mode 100644 (file)
index 0000000..61a59aa
--- /dev/null
@@ -0,0 +1,3 @@
+PODFILE CHECKSUM: e5038167266107ffa86081c2bbb7148c097e71eb
+
+COCOAPODS: 1.5.2
diff --git a/platforms/ios/Pods/Pods.xcodeproj/project.pbxproj b/platforms/ios/Pods/Pods.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..a03347f
--- /dev/null
@@ -0,0 +1,329 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 46;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               643D3B1E52B2984E46E7B2242856084D /* Pods-dlapp-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1154D50C03F2C4A4D1E1A6A35A4580D9 /* Pods-dlapp-dummy.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+               0B9FDFC65AF777A68D74F86F830E8D29 /* libPods-dlapp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-dlapp.a"; path = "libPods-dlapp.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+               1154D50C03F2C4A4D1E1A6A35A4580D9 /* Pods-dlapp-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-dlapp-dummy.m"; sourceTree = "<group>"; };
+               53416009C0EB96589449A0D152302159 /* Pods-dlapp-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-dlapp-acknowledgements.markdown"; sourceTree = "<group>"; };
+               93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+               AF5E9BBF20F1C626FFD56F1ACC2A945D /* Pods-dlapp-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-dlapp-frameworks.sh"; sourceTree = "<group>"; };
+               CB6583549252874668D1D65B0EC3158C /* Pods-dlapp-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-dlapp-resources.sh"; sourceTree = "<group>"; };
+               D2031FC780E4135865DBAC7B0BECDA29 /* Pods-dlapp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-dlapp.debug.xcconfig"; sourceTree = "<group>"; };
+               E60AB409C8228577C8314E69A54EC524 /* Pods-dlapp-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-dlapp-acknowledgements.plist"; sourceTree = "<group>"; };
+               EEC91C98D2D922E40FAE18A4F04D83AE /* Pods-dlapp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-dlapp.release.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               8C1A83A20A9DA90317678291D8688BF5 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               0F8D2E47FE03D3B91B51069F7C273AF4 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               548BE5040BA5F7460A0477C20076FED1 /* Pods-dlapp */ = {
+                       isa = PBXGroup;
+                       children = (
+                               53416009C0EB96589449A0D152302159 /* Pods-dlapp-acknowledgements.markdown */,
+                               E60AB409C8228577C8314E69A54EC524 /* Pods-dlapp-acknowledgements.plist */,
+                               1154D50C03F2C4A4D1E1A6A35A4580D9 /* Pods-dlapp-dummy.m */,
+                               AF5E9BBF20F1C626FFD56F1ACC2A945D /* Pods-dlapp-frameworks.sh */,
+                               CB6583549252874668D1D65B0EC3158C /* Pods-dlapp-resources.sh */,
+                               D2031FC780E4135865DBAC7B0BECDA29 /* Pods-dlapp.debug.xcconfig */,
+                               EEC91C98D2D922E40FAE18A4F04D83AE /* Pods-dlapp.release.xcconfig */,
+                       );
+                       name = "Pods-dlapp";
+                       path = "Target Support Files/Pods-dlapp";
+                       sourceTree = "<group>";
+               };
+               7DB346D0F39D3F0E887471402A8071AB = {
+                       isa = PBXGroup;
+                       children = (
+                               93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */,
+                               0F8D2E47FE03D3B91B51069F7C273AF4 /* Frameworks */,
+                               C2B1D5AB28132D1F1EC74A313228649B /* Products */,
+                               ABF54CA5D18420FEA3DD9419BE3E157A /* Targets Support Files */,
+                       );
+                       sourceTree = "<group>";
+               };
+               ABF54CA5D18420FEA3DD9419BE3E157A /* Targets Support Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               548BE5040BA5F7460A0477C20076FED1 /* Pods-dlapp */,
+                       );
+                       name = "Targets Support Files";
+                       sourceTree = "<group>";
+               };
+               C2B1D5AB28132D1F1EC74A313228649B /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               0B9FDFC65AF777A68D74F86F830E8D29 /* libPods-dlapp.a */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               095FEF9C74B1A68F66A3061771703C06 /* Pods-dlapp */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = F6A3DDA2CD5F1F9A4AEF4D6F6662EAD9 /* Build configuration list for PBXNativeTarget "Pods-dlapp" */;
+                       buildPhases = (
+                               A54DF46585559C2430C5DDBBDB2D11FF /* Sources */,
+                               8C1A83A20A9DA90317678291D8688BF5 /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = "Pods-dlapp";
+                       productName = "Pods-dlapp";
+                       productReference = 0B9FDFC65AF777A68D74F86F830E8D29 /* libPods-dlapp.a */;
+                       productType = "com.apple.product-type.library.static";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               D41D8CD98F00B204E9800998ECF8427E /* Project object */ = {
+                       isa = PBXProject;
+                       attributes = {
+                               LastSwiftUpdateCheck = 0930;
+                               LastUpgradeCheck = 0930;
+                       };
+                       buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */;
+                       compatibilityVersion = "Xcode 3.2";
+                       developmentRegion = English;
+                       hasScannedForEncodings = 0;
+                       knownRegions = (
+                               en,
+                       );
+                       mainGroup = 7DB346D0F39D3F0E887471402A8071AB;
+                       productRefGroup = C2B1D5AB28132D1F1EC74A313228649B /* Products */;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               095FEF9C74B1A68F66A3061771703C06 /* Pods-dlapp */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+               A54DF46585559C2430C5DDBBDB2D11FF /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               643D3B1E52B2984E46E7B2242856084D /* Pods-dlapp-dummy.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+               199D972A13F2B4C56847F7A89CCA83BC /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGNING_ALLOWED = NO;
+                               CODE_SIGNING_REQUIRED = NO;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "POD_CONFIGURATION_DEBUG=1",
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIP_INSTALLED_PRODUCT = NO;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+                               SYMROOT = "${SRCROOT}/../build";
+                       };
+                       name = Debug;
+               };
+               CB0C4A0438BB41F9A7F1E080D5B2AD40 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = D2031FC780E4135865DBAC7B0BECDA29 /* Pods-dlapp.debug.xcconfig */;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
+                               CODE_SIGN_IDENTITY = "iPhone Developer";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MACH_O_TYPE = staticlib;
+                               OTHER_LDFLAGS = "";
+                               OTHER_LIBTOOLFLAGS = "";
+                               PODS_ROOT = "$(SRCROOT)";
+                               PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Debug;
+               };
+               D410F425008A083F9E9694725B8E89FB /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = EEC91C98D2D922E40FAE18A4F04D83AE /* Pods-dlapp.release.xcconfig */;
+                       buildSettings = {
+                               ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
+                               CODE_SIGN_IDENTITY = "iPhone Developer";
+                               "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+                               "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MACH_O_TYPE = staticlib;
+                               OTHER_LDFLAGS = "";
+                               OTHER_LIBTOOLFLAGS = "";
+                               PODS_ROOT = "$(SRCROOT)";
+                               PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
+                               SDKROOT = iphoneos;
+                               SKIP_INSTALL = YES;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                       };
+                       name = Release;
+               };
+               FDB2FC4A1E5891381CD9D922145497F1 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGNING_ALLOWED = NO;
+                               CODE_SIGNING_REQUIRED = NO;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               ENABLE_NS_ASSERTIONS = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "POD_CONFIGURATION_RELEASE=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIP_INSTALLED_PRODUCT = NO;
+                               SYMROOT = "${SRCROOT}/../build";
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               199D972A13F2B4C56847F7A89CCA83BC /* Debug */,
+                               FDB2FC4A1E5891381CD9D922145497F1 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               F6A3DDA2CD5F1F9A4AEF4D6F6662EAD9 /* Build configuration list for PBXNativeTarget "Pods-dlapp" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               CB0C4A0438BB41F9A7F1E080D5B2AD40 /* Debug */,
+                               D410F425008A083F9E9694725B8E89FB /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
+}
diff --git a/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-acknowledgements.markdown b/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-acknowledgements.markdown
new file mode 100644 (file)
index 0000000..102af75
--- /dev/null
@@ -0,0 +1,3 @@
+# Acknowledgements
+This application makes use of the following third party libraries:
+Generated by CocoaPods - https://cocoapods.org
diff --git a/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-acknowledgements.plist b/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-acknowledgements.plist
new file mode 100644 (file)
index 0000000..7acbad1
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>PreferenceSpecifiers</key>
+       <array>
+               <dict>
+                       <key>FooterText</key>
+                       <string>This application makes use of the following third party libraries:</string>
+                       <key>Title</key>
+                       <string>Acknowledgements</string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+               <dict>
+                       <key>FooterText</key>
+                       <string>Generated by CocoaPods - https://cocoapods.org</string>
+                       <key>Title</key>
+                       <string></string>
+                       <key>Type</key>
+                       <string>PSGroupSpecifier</string>
+               </dict>
+       </array>
+       <key>StringsTable</key>
+       <string>Acknowledgements</string>
+       <key>Title</key>
+       <string>Acknowledgements</string>
+</dict>
+</plist>
diff --git a/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-dummy.m b/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-dummy.m
new file mode 100644 (file)
index 0000000..f22e3b1
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_Pods_dlapp : NSObject
+@end
+@implementation PodsDummy_Pods_dlapp
+@end
diff --git a/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-frameworks.sh b/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-frameworks.sh
new file mode 100755 (executable)
index 0000000..08e3eaa
--- /dev/null
@@ -0,0 +1,146 @@
+#!/bin/sh
+set -e
+set -u
+set -o pipefail
+
+if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
+    # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
+    # frameworks to, so exit 0 (signalling the script phase was successful).
+    exit 0
+fi
+
+echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+
+COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
+SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
+
+# Used as a return value for each invocation of `strip_invalid_archs` function.
+STRIP_BINARY_RETVAL=0
+
+# This protects against multiple targets copying the same framework dependency at the same time. The solution
+# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
+RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
+
+# Copies and strips a vendored framework
+install_framework()
+{
+  if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
+    local source="${BUILT_PRODUCTS_DIR}/$1"
+  elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
+    local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
+  elif [ -r "$1" ]; then
+    local source="$1"
+  fi
+
+  local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+
+  if [ -L "${source}" ]; then
+      echo "Symlinked..."
+      source="$(readlink "${source}")"
+  fi
+
+  # Use filter instead of exclude so missing patterns don't throw errors.
+  echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
+  rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
+
+  local basename
+  basename="$(basename -s .framework "$1")"
+  binary="${destination}/${basename}.framework/${basename}"
+  if ! [ -r "$binary" ]; then
+    binary="${destination}/${basename}"
+  fi
+
+  # Strip invalid architectures so "fat" simulator / device frameworks work on device
+  if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
+    strip_invalid_archs "$binary"
+  fi
+
+  # Resign the code if required by the build settings to avoid unstable apps
+  code_sign_if_enabled "${destination}/$(basename "$1")"
+
+  # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
+  if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
+    local swift_runtime_libs
+    swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u  && exit ${PIPESTATUS[0]})
+    for lib in $swift_runtime_libs; do
+      echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
+      rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
+      code_sign_if_enabled "${destination}/${lib}"
+    done
+  fi
+}
+
+# Copies and strips a vendored dSYM
+install_dsym() {
+  local source="$1"
+  if [ -r "$source" ]; then
+    # Copy the dSYM into a the targets temp dir.
+    echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
+    rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
+
+    local basename
+    basename="$(basename -s .framework.dSYM "$source")"
+    binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
+
+    # Strip invalid architectures so "fat" simulator / device frameworks work on device
+    if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
+      strip_invalid_archs "$binary"
+    fi
+
+    if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
+      # Move the stripped file into its final destination.
+      echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
+      rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
+    else
+      # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
+      touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
+    fi
+  fi
+}
+
+# Signs a framework with the provided identity
+code_sign_if_enabled() {
+  if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
+    # Use the current code_sign_identitiy
+    echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
+    local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
+
+    if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
+      code_sign_cmd="$code_sign_cmd &"
+    fi
+    echo "$code_sign_cmd"
+    eval "$code_sign_cmd"
+  fi
+}
+
+# Strip invalid architectures
+strip_invalid_archs() {
+  binary="$1"
+  # Get architectures for current target binary
+  binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
+  # Intersect them with the architectures we are building for
+  intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
+  # If there are no archs supported by this binary then warn the user
+  if [[ -z "$intersected_archs" ]]; then
+    echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
+    STRIP_BINARY_RETVAL=0
+    return
+  fi
+  stripped=""
+  for arch in $binary_archs; do
+    if ! [[ "${ARCHS}" == *"$arch"* ]]; then
+      # Strip non-valid architectures in-place
+      lipo -remove "$arch" -output "$binary" "$binary" || exit 1
+      stripped="$stripped $arch"
+    fi
+  done
+  if [[ "$stripped" ]]; then
+    echo "Stripped $binary of architectures:$stripped"
+  fi
+  STRIP_BINARY_RETVAL=1
+}
+
+if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
+  wait
+fi
diff --git a/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-resources.sh b/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp-resources.sh
new file mode 100755 (executable)
index 0000000..345301f
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/sh
+set -e
+set -u
+set -o pipefail
+
+if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then
+    # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy
+    # resources to, so exit 0 (signalling the script phase was successful).
+    exit 0
+fi
+
+mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+
+RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
+> "$RESOURCES_TO_COPY"
+
+XCASSET_FILES=()
+
+# This protects against multiple targets copying the same framework dependency at the same time. The solution
+# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
+RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
+
+case "${TARGETED_DEVICE_FAMILY:-}" in
+  1,2)
+    TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
+    ;;
+  1)
+    TARGET_DEVICE_ARGS="--target-device iphone"
+    ;;
+  2)
+    TARGET_DEVICE_ARGS="--target-device ipad"
+    ;;
+  3)
+    TARGET_DEVICE_ARGS="--target-device tv"
+    ;;
+  4)
+    TARGET_DEVICE_ARGS="--target-device watch"
+    ;;
+  *)
+    TARGET_DEVICE_ARGS="--target-device mac"
+    ;;
+esac
+
+install_resource()
+{
+  if [[ "$1" = /* ]] ; then
+    RESOURCE_PATH="$1"
+  else
+    RESOURCE_PATH="${PODS_ROOT}/$1"
+  fi
+  if [[ ! -e "$RESOURCE_PATH" ]] ; then
+    cat << EOM
+error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
+EOM
+    exit 1
+  fi
+  case $RESOURCE_PATH in
+    *.storyboard)
+      echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
+      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
+      ;;
+    *.xib)
+      echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
+      ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
+      ;;
+    *.framework)
+      echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
+      mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+      echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
+      rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
+      ;;
+    *.xcdatamodel)
+      echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
+      xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
+      ;;
+    *.xcdatamodeld)
+      echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
+      xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
+      ;;
+    *.xcmappingmodel)
+      echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
+      xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
+      ;;
+    *.xcassets)
+      ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
+      XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
+      ;;
+    *)
+      echo "$RESOURCE_PATH" || true
+      echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
+      ;;
+  esac
+}
+
+mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
+  mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+  rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+fi
+rm -f "$RESOURCES_TO_COPY"
+
+if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
+then
+  # Find all other xcassets (this unfortunately includes those of path pods and other targets).
+  OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
+  while read line; do
+    if [[ $line != "${PODS_ROOT}*" ]]; then
+      XCASSET_FILES+=("$line")
+    fi
+  done <<<"$OTHER_XCASSETS"
+
+  if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
+    printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
+  else
+    printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
+  fi
+fi
diff --git a/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp.debug.xcconfig b/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp.debug.xcconfig
new file mode 100644 (file)
index 0000000..729bac8
--- /dev/null
@@ -0,0 +1,6 @@
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -ObjC
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
diff --git a/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp.release.xcconfig b/platforms/ios/Pods/Target Support Files/Pods-dlapp/Pods-dlapp.release.xcconfig
new file mode 100644 (file)
index 0000000..729bac8
--- /dev/null
@@ -0,0 +1,6 @@
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+OTHER_LDFLAGS = $(inherited) -ObjC
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
+PODS_ROOT = ${SRCROOT}/Pods
old mode 100755 (executable)
new mode 100644 (file)
index f9c0c1b..6560ea3
@@ -5,7 +5,6 @@
        };
        objectVersion = 46;
        objects = {
-
 /* Begin PBXBuildFile section */
                0207DA581B56EA530066E2B4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0207DA571B56EA530066E2B4 /* Images.xcassets */; };
                0FBFC45D12B44FF79785B959 /* UIImage+CropScaleOrientation.m in Sources */ = {isa = PBXBuildFile; fileRef = 281DF600F6B747808087E4FC /* UIImage+CropScaleOrientation.m */; };
@@ -33,6 +32,7 @@
                9224D3CE70AE4072BBEB170D /* CDVLocalFilesystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A8DD2DDC8694A97A56F9FA7 /* CDVLocalFilesystem.m */; };
                94868B10923A4508B06A4310 /* CDVAssetLibraryFilesystem.m in Sources */ = {isa = PBXBuildFile; fileRef = A719A69FD70D4CC9B8104FF6 /* CDVAssetLibraryFilesystem.m */; };
                A42AE846FF7B482FB889AAEB /* QRScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8478FFD487C4004A1CEFCA8 /* QRScanner.swift */; };
+               A50EB04ADB24A5CBB680802F /* libPods-dlapp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D992375F52889311A88544EF /* libPods-dlapp.a */; };
                B69FF513D1EE4772AFF6050B /* Fingerprint.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2099240B64744E08C91C285 /* Fingerprint.swift */; };
                B8EFB66D03A44372A240B05B /* CDVCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = 80B448E71F8941268B78692C /* CDVCamera.m */; };
                BC39BA77AF0D4F94BC27666A /* CDVDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = A50DE9801E5E4302A78D569C /* CDVDevice.m */; };
                F46AE1B50D8B4A81B05D4100 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A673D8D8920E4086A9152FC4 /* Security.framework */; };
                F939AD8D22BB769B006B371B /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F939AD8C22BB769B006B371B /* libsqlite3.0.tbd */; };
                FDE92C386167415E8040F8AB /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = FDEE8379D7A34C55A616F700 /* AFURLResponseSerialization.m */; };
+               04347BD0ABAC4EE0B4CDDDB6 /* jcore-ios-2.1.1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 444DBF371D8346D7BC7EE35D /* jcore-ios-2.1.1.a */; };
+               3EBABAA2209349F6AF91DF03 /* JPushPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = CD3E9CB7CBA54989AB62BA7F /* JPushPlugin.m */; };
+               A54A9FED843144C887EA4286 /* AppDelegate+JPush.m in Sources */ = {isa = PBXBuildFile; fileRef = 91E044FC8377488C886C818B /* AppDelegate+JPush.m */; };
+               C04B4EC2FAAD4D328ABA0C95 /* jpush-ios-3.2.1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCDEFB0C0632436FA86F0F31 /* jpush-ios-3.2.1.a */; };
+               3BE6767AAFDC4926A08E6690 /* JPushConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9D260B7FB6C49A3AE6CD0DB /* JPushConfig.plist */; };
+               BFA0CF013EEC44F9B2C6FA6E /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0162975BB3964038A51EDED4 /* CFNetwork.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               7EC1B4999B2E48C098BA1BBB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C44E29C3B190445C98BBAB84 /* CoreFoundation.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               432FFA418F2F4247BE9C8472 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1F790D5B00B4F178F835DE1 /* CoreTelephony.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               7352A67D82B2499FBFFF8A2A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B080F410C164D6582A23E36 /* Foundation.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               A15A414BEE7A48538C758317 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D5F2FFCAD8E4A4392B781A3 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               D3E822CFC71546E191C10DA7 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 22DF35565BBA44B1980FFAF5 /* libz.tbd */; settings = {ATTRIBUTES = (Weak, ); }; };
+               CDD6F217CA274CF18EC146D3 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 048B6FC037E1479790969D83 /* AdSupport.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               77CDF61132F44B81B0064744 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59D1F618C1ED464283B7B2E6 /* UserNotifications.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               3C00FC06B74E49CFBABD0CFF /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CBD6EA74C612474DA862E146 /* libresolv.tbd */; settings = {ATTRIBUTES = (Weak, ); }; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                301BF534109A57CC0062928A /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
-                       containerPortal = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */;
+                       containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */;
                        proxyType = 2;
                        remoteGlobalIDString = D2AAC07E0554694100DB518D;
                        remoteInfo = CordovaLib;
                };
                301BF550109A68C00062928A /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
-                       containerPortal = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */;
+                       containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */;
                        proxyType = 1;
                        remoteGlobalIDString = D2AAC07D0554694100DB518D;
                        remoteInfo = CordovaLib;
                };
                907D8123214C687600058A10 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
-                       containerPortal = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */;
+                       containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */;
                        proxyType = 2;
                        remoteGlobalIDString = C0C01EB21E3911D50056E6CB;
                        remoteInfo = Cordova;
                28B91F31B6FD410AAF87BBDC /* CDVThemeableBrowser.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = CDVThemeableBrowser.m; path = "cordova-plugin-themeablebrowser/CDVThemeableBrowser.m"; sourceTree = "<group>"; };
                29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
                2F977C7D2E4448098AA27AE8 /* CDVAssetLibraryFilesystem.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = CDVAssetLibraryFilesystem.h; path = "cordova-plugin-file/CDVAssetLibraryFilesystem.h"; sourceTree = "<group>"; };
-               301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = CordovaLib/CordovaLib.xcodeproj; sourceTree = "<group>"; };
+               301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = CordovaLib/CordovaLib.xcodeproj; sourceTree = "<group>"; };
                301BF56E109A69640062928A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = SOURCE_ROOT; };
                302D95EE14D2391D003F00A1 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = "<group>"; };
                302D95EF14D2391D003F00A1 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = "<group>"; };
                C2099240B64744E08C91C285 /* Fingerprint.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = Fingerprint.swift; path = "cordova-plugin-fingerprint-aio/Fingerprint.swift"; sourceTree = "<group>"; };
                C8478FFD487C4004A1CEFCA8 /* QRScanner.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = QRScanner.swift; path = "cordova-plugin-qrscanner/QRScanner.swift"; sourceTree = "<group>"; };
                D4DBE4512BAD4694B7EB5217 /* SDNetworkActivityIndicator.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = SDNetworkActivityIndicator.h; path = "cordova-plugin-advanced-http/SDNetworkActivityIndicator.h"; sourceTree = "<group>"; };
+               D992375F52889311A88544EF /* libPods-dlapp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-dlapp.a"; sourceTree = BUILT_PRODUCTS_DIR; };
                DBDBB5C64D17465C9452AF28 /* TextRequestSerializer.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = TextRequestSerializer.m; path = "cordova-plugin-advanced-http/TextRequestSerializer.m"; sourceTree = "<group>"; };
                DD3651F245114E52ABEB9060 /* CDVFile.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = CDVFile.m; path = "cordova-plugin-file/CDVFile.m"; sourceTree = "<group>"; };
                DE50527E757F47D08F4E076C /* CDVFile.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = CDVFile.h; path = "cordova-plugin-file/CDVFile.h"; sourceTree = "<group>"; };
                F939AD8C22BB769B006B371B /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; };
                FCC7E279D80D4FB9BCF2530A /* Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Bridging-Header.h"; path = "cordova-plugin-fingerprint-aio/Bridging-Header.h"; sourceTree = "<group>"; };
                FDEE8379D7A34C55A616F700 /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = AFURLResponseSerialization.m; path = "cordova-plugin-advanced-http/AFURLResponseSerialization.m"; sourceTree = "<group>"; };
+               444DBF371D8346D7BC7EE35D /* jcore-ios-2.1.1.a */ = {isa = PBXFileReference; name = "jcore-ios-2.1.1.a"; path = "dlapp/Plugins/cordova-plugin-jcore/jcore-ios-2.1.1.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
+               CD3E9CB7CBA54989AB62BA7F /* JPushPlugin.m */ = {isa = PBXFileReference; name = "JPushPlugin.m"; path = "jpush-phonegap-plugin/JPushPlugin.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
+               91E044FC8377488C886C818B /* AppDelegate+JPush.m */ = {isa = PBXFileReference; name = "AppDelegate+JPush.m"; path = "jpush-phonegap-plugin/AppDelegate+JPush.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
+               CCDEFB0C0632436FA86F0F31 /* jpush-ios-3.2.1.a */ = {isa = PBXFileReference; name = "jpush-ios-3.2.1.a"; path = "dlapp/Plugins/jpush-phonegap-plugin/jpush-ios-3.2.1.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
+               FC8EA7F3637547AD8BC62127 /* JPushDefine.h */ = {isa = PBXFileReference; name = "JPushDefine.h"; path = "jpush-phonegap-plugin/JPushDefine.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
+               3291AEB63DCC45B58959915F /* JPushPlugin.h */ = {isa = PBXFileReference; name = "JPushPlugin.h"; path = "jpush-phonegap-plugin/JPushPlugin.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
+               0EFE153A83734473AE4D0E4C /* AppDelegate+JPush.h */ = {isa = PBXFileReference; name = "AppDelegate+JPush.h"; path = "jpush-phonegap-plugin/AppDelegate+JPush.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
+               A32F7333BFEB461B9C84F6E5 /* JPUSHService.h */ = {isa = PBXFileReference; name = "JPUSHService.h"; path = "jpush-phonegap-plugin/JPUSHService.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
+               E9D260B7FB6C49A3AE6CD0DB /* JPushConfig.plist */ = {isa = PBXFileReference; name = "JPushConfig.plist"; path = "JPushConfig.plist"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = text.plist.xml; explicitFileType = undefined; includeInIndex = 0; };
+               0162975BB3964038A51EDED4 /* CFNetwork.framework */ = {isa = PBXFileReference; name = "CFNetwork.framework"; path = "System/Library/Frameworks/CFNetwork.framework"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; };
+               C44E29C3B190445C98BBAB84 /* CoreFoundation.framework */ = {isa = PBXFileReference; name = "CoreFoundation.framework"; path = "System/Library/Frameworks/CoreFoundation.framework"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; };
+               B1F790D5B00B4F178F835DE1 /* CoreTelephony.framework */ = {isa = PBXFileReference; name = "CoreTelephony.framework"; path = "System/Library/Frameworks/CoreTelephony.framework"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; };
+               9B080F410C164D6582A23E36 /* Foundation.framework */ = {isa = PBXFileReference; name = "Foundation.framework"; path = "System/Library/Frameworks/Foundation.framework"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; };
+               6D5F2FFCAD8E4A4392B781A3 /* UIKit.framework */ = {isa = PBXFileReference; name = "UIKit.framework"; path = "System/Library/Frameworks/UIKit.framework"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; };
+               22DF35565BBA44B1980FFAF5 /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
+               048B6FC037E1479790969D83 /* AdSupport.framework */ = {isa = PBXFileReference; name = "AdSupport.framework"; path = "System/Library/Frameworks/AdSupport.framework"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; };
+               59D1F618C1ED464283B7B2E6 /* UserNotifications.framework */ = {isa = PBXFileReference; name = "UserNotifications.framework"; path = "System/Library/Frameworks/UserNotifications.framework"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = wrapper.framework; explicitFileType = undefined; includeInIndex = 0; };
+               CBD6EA74C612474DA862E146 /* libresolv.tbd */ = {isa = PBXFileReference; name = "libresolv.tbd"; path = "usr/lib/libresolv.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                                7D8430D010A848FBBA5FB7ED /* ImageIO.framework in Frameworks */,
                                D2F2072984AE43BCBD0A430D /* CoreLocation.framework in Frameworks */,
                                74A56CC7F6724870ABD136F2 /* AVFoundation.framework in Frameworks */,
+                               A50EB04ADB24A5CBB680802F /* libPods-dlapp.a in Frameworks */,
+                               04347BD0ABAC4EE0B4CDDDB6 /* jcore-ios-2.1.1.a in Frameworks */,
+                               C04B4EC2FAAD4D328ABA0C95 /* jpush-ios-3.2.1.a in Frameworks */,
+                               BFA0CF013EEC44F9B2C6FA6E /* CFNetwork.framework in Frameworks */,
+                               7EC1B4999B2E48C098BA1BBB /* CoreFoundation.framework in Frameworks */,
+                               432FFA418F2F4247BE9C8472 /* CoreTelephony.framework in Frameworks */,
+                               7352A67D82B2499FBFFF8A2A /* Foundation.framework in Frameworks */,
+                               A15A414BEE7A48538C758317 /* UIKit.framework in Frameworks */,
+                               D3E822CFC71546E191C10DA7 /* libz.tbd in Frameworks */,
+                               CDD6F217CA274CF18EC146D3 /* AdSupport.framework in Frameworks */,
+                               77CDF61132F44B81B0064744 /* UserNotifications.framework in Frameworks */,
+                               3C00FC06B74E49CFBABD0CFF /* libresolv.tbd in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        name = Products;
                        sourceTree = "<group>";
                };
-               29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
+               29B97314FDCFA39411CA2CEA = {
                        isa = PBXGroup;
                        children = (
                                EB87FDF41871DAF40020F90C /* config.xml */,
                                EB87FDF31871DA8E0020F90C /* www */,
                                EB87FDF11871DA420020F90C /* Staging */,
-                               301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */,
+                               301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */,
                                080E96DDFE201D6D7F000001 /* Classes */,
                                307C750510C5A3420062BCA9 /* Plugins */,
                                29B97315FDCFA39411CA2CEA /* Other Sources */,
                                29B97317FDCFA39411CA2CEA /* Resources */,
                                29B97323FDCFA39411CA2CEA /* Frameworks */,
                                19C28FACFE9D520D11CA2CBB /* Products */,
+                               BC4A1507117AEF25CBA069D3 /* Pods */,
                        );
                        name = CustomTemplate;
                        sourceTree = "<group>";
                                0207DA571B56EA530066E2B4 /* Images.xcassets */,
                                3047A50E1AB8057F00498E2A /* config */,
                                8D1107310486CEB800E47090 /* dlapp-Info.plist */,
+                               E9D260B7FB6C49A3AE6CD0DB /* JPushConfig.plist */,
                        );
                        name = Resources;
                        path = dlapp/Resources;
                                5192EB17EA164DFD80D433E4 /* ImageIO.framework */,
                                1D1F25845F964D87B880BFA9 /* CoreLocation.framework */,
                                11A34CB026314A2AA98A0044 /* AVFoundation.framework */,
+                               D992375F52889311A88544EF /* libPods-dlapp.a */,
+                               444DBF371D8346D7BC7EE35D /* jcore-ios-2.1.1.a */,
+                               CCDEFB0C0632436FA86F0F31 /* jpush-ios-3.2.1.a */,
+                               0162975BB3964038A51EDED4 /* CFNetwork.framework */,
+                               C44E29C3B190445C98BBAB84 /* CoreFoundation.framework */,
+                               B1F790D5B00B4F178F835DE1 /* CoreTelephony.framework */,
+                               9B080F410C164D6582A23E36 /* Foundation.framework */,
+                               6D5F2FFCAD8E4A4392B781A3 /* UIKit.framework */,
+                               22DF35565BBA44B1980FFAF5 /* libz.tbd */,
+                               048B6FC037E1479790969D83 /* AdSupport.framework */,
+                               59D1F618C1ED464283B7B2E6 /* UserNotifications.framework */,
+                               CBD6EA74C612474DA862E146 /* libresolv.tbd */,
                        );
                        name = Frameworks;
                        sourceTree = "<group>";
                                265BE52D975B49ACA2F7B3FE /* CDVDevice.h */,
                                28B91F31B6FD410AAF87BBDC /* CDVThemeableBrowser.m */,
                                EF1A4FA720C24397A32AF09C /* CDVThemeableBrowser.h */,
+                               CD3E9CB7CBA54989AB62BA7F /* JPushPlugin.m */,
+                               91E044FC8377488C886C818B /* AppDelegate+JPush.m */,
+                               FC8EA7F3637547AD8BC62127 /* JPushDefine.h */,
+                               3291AEB63DCC45B58959915F /* JPushPlugin.h */,
+                               0EFE153A83734473AE4D0E4C /* AppDelegate+JPush.h */,
+                               A32F7333BFEB461B9C84F6E5 /* JPUSHService.h */,
                        );
                        name = Plugins;
                        path = dlapp/Plugins;
                        sourceTree = SOURCE_ROOT;
                };
+               BC4A1507117AEF25CBA069D3 /* Pods */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       name = Pods;
+                       sourceTree = "<group>";
+               };
                EB87FDF11871DA420020F90C /* Staging */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXNativeTarget;
                        buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "dlapp" */;
                        buildPhases = (
+                               CE3CFE251273EDBCFA429761 /* [CP] Check Pods Manifest.lock */,
                                304B58A110DAC018002A0835 /* Copy www directory */,
                                1D60588D0D05DD3D006BFB54 /* Resources */,
                                1D60588E0D05DD3D006BFB54 /* Sources */,
                                English,
                                en,
                        );
-                       mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
+                       mainGroup = 29B97314FDCFA39411CA2CEA;
+                       productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */;
                        projectDirPath = "";
                        projectReferences = (
                                {
                                        ProductGroup = 301BF52E109A57CC0062928A /* Products */;
-                                       ProjectRef = 301BF52D109A57CC0062928A /* CordovaLib/CordovaLib.xcodeproj */;
+                                       ProjectRef = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */;
                                },
                        );
                        projectRoot = "";
                        files = (
                                302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */,
                                0207DA581B56EA530066E2B4 /* Images.xcassets in Resources */,
+                               3BE6767AAFDC4926A08E6690 /* JPushConfig.plist in Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        shellScript = "\"$SRCROOT/dlapp/Scripts/copy-www-build-step.sh\"";
                        showEnvVarsInLog = 0;
                };
+               CE3CFE251273EDBCFA429761 /* [CP] Check Pods Manifest.lock */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                               "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+                               "${PODS_ROOT}/Manifest.lock",
+                       );
+                       name = "[CP] Check Pods Manifest.lock";
+                       outputPaths = (
+                               "$(DERIVED_FILE_DIR)/Pods-dlapp-checkManifestLockResult.txt",
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+                       showEnvVarsInLog = 0;
+               };
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
                                6FB343627AC8409CB2AA79B7 /* CDVInAppBrowser.m in Sources */,
                                BC39BA77AF0D4F94BC27666A /* CDVDevice.m in Sources */,
                                4F83621E48F143429B20FB3A /* CDVThemeableBrowser.m in Sources */,
+                               3EBABAA2209349F6AF91DF03 /* JPushPlugin.m in Sources */,
+                               A54A9FED843144C887EA4286 /* AppDelegate+JPush.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                SWIFT_OPTIMIZATION_LEVEL = "-Onone";
                                SWIFT_VERSION = 4.0;
                                TARGETED_DEVICE_FAMILY = 1;
+                               LIBRARY_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "\"$(SRCROOT)/$(TARGET_NAME)/Plugins/cordova-plugin-jcore\"",
+                                       "\"$(SRCROOT)/$(TARGET_NAME)/Plugins/jpush-phonegap-plugin\"",
+                               );
                        };
                        name = Debug;
                };
                                SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/$(PROJECT_NAME)/Bridging-Header.h";
                                SWIFT_VERSION = 4.0;
                                TARGETED_DEVICE_FAMILY = 1;
+                               LIBRARY_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "\"$(SRCROOT)/$(TARGET_NAME)/Plugins/cordova-plugin-jcore\"",
+                                       "\"$(SRCROOT)/$(TARGET_NAME)/Plugins/jpush-phonegap-plugin\"",
+                               );
                        };
                        name = Release;
                };
index da2a5d5..408f199 100644 (file)
@@ -4,4 +4,7 @@
    <FileRef
       location = "group:dlapp.xcodeproj">
    </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
 </Workspace>
index 1ed4ae5..74c85d2 100644 (file)
@@ -1,24 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<!--
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
--->
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
-    <dict>
-    </dict>
-</plist>
+<dict>
+       <key>aps-environment</key>
+       <string>development</string>
+</dict>
+</plist>
\ No newline at end of file
index 1ed4ae5..4b18a83 100644 (file)
@@ -1,24 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<!--
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
--->
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
-    <dict>
-    </dict>
-</plist>
+<dict>
+       <key>aps-environment</key>
+       <string>production</string>
+</dict>
+</plist>
\ No newline at end of file
diff --git a/platforms/ios/dlapp/Plugins/cordova-plugin-jcore/jcore-ios-2.1.1.a b/platforms/ios/dlapp/Plugins/cordova-plugin-jcore/jcore-ios-2.1.1.a
new file mode 100755 (executable)
index 0000000..4cafd84
Binary files /dev/null and b/platforms/ios/dlapp/Plugins/cordova-plugin-jcore/jcore-ios-2.1.1.a differ
diff --git a/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/AppDelegate+JPush.h b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/AppDelegate+JPush.h
new file mode 100644 (file)
index 0000000..8072fa1
--- /dev/null
@@ -0,0 +1,16 @@
+//\r
+//  AppDelegate+JPush.h\r
+//  delegateExtention\r
+//\r
+//  Created by 张庆贺 on 15/8/3.\r
+//  Copyright (c) 2015年 JPush. All rights reserved.\r
+//\r
+\r
+#import "AppDelegate.h"\r
+#import <UserNotifications/UserNotifications.h>\r
+#import "JPUSHService.h"\r
+\r
+@interface AppDelegate (JPush) <JPUSHRegisterDelegate>\r
+-(void)registerForRemoteNotification;\r
+-(void)startJPushSDK;\r
+@end\r
diff --git a/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/AppDelegate+JPush.m b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/AppDelegate+JPush.m
new file mode 100644 (file)
index 0000000..81e7820
--- /dev/null
@@ -0,0 +1,213 @@
+//
+//  AppDelegate+JPush.m
+//  delegateExtention
+//
+//  Created by 张庆贺 on 15/8/3.
+//  Copyright (c) 2015年 JPush. All rights reserved.
+//
+
+#import "AppDelegate+JPush.h"
+#import "JPushPlugin.h"
+#import <objc/runtime.h>
+#import <AdSupport/AdSupport.h>
+#import <UserNotifications/UserNotifications.h>
+#import "JPushDefine.h"
+
+@implementation AppDelegate (JPush)
+
++(void)load{
+    Method origin1;
+    Method swizzle1;
+    origin1  = class_getInstanceMethod([self class],@selector(init));
+    swizzle1 = class_getInstanceMethod([self class], @selector(init_plus));
+    method_exchangeImplementations(origin1, swizzle1);
+}
+
+-(instancetype)init_plus{
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidLaunch:) name:UIApplicationDidFinishLaunchingNotification object:nil];
+    return [self init_plus];
+}
+
+NSDictionary *_launchOptions;
+-(void)applicationDidLaunch:(NSNotification *)notification{
+
+    if (!_jpushEventCache) {
+        _jpushEventCache = @{}.mutableCopy;
+    }
+
+    [JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
+      NSDictionary *event = @{@"registrationId": registrationID?:@""};
+      [JPushPlugin fireDocumentEvent:JPushDocumentEvent_receiveRegistrationId jsString:[event toJsonString]];
+    }];
+  
+  if (notification != nil &&
+      [[UIDevice currentDevice].systemVersion floatValue] < 10.0) {// iOS 10 以后通过 openNotification 这个回调触发事件。
+        if (notification.userInfo) {
+          
+          if ([notification.userInfo valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) {
+            [JPushPlugin fireDocumentEvent:JPushDocumentEvent_OpenNotification
+                                  jsString:[[self jpushFormatAPNSDic: notification.userInfo[UIApplicationLaunchOptionsRemoteNotificationKey]] toJsonString]];
+          }
+          
+          if ([notification.userInfo valueForKey:UIApplicationLaunchOptionsLocalNotificationKey]) {
+            UILocalNotification *localNotification = [notification.userInfo valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
+            NSMutableDictionary *localNotificationEvent = @{}.mutableCopy;
+            localNotificationEvent[@"content"] = localNotification.alertBody;
+            localNotificationEvent[@"badge"] = @(localNotification.applicationIconBadgeNumber);
+            localNotificationEvent[@"extras"] = localNotification.userInfo;
+
+            [JPushPlugin fireDocumentEvent:JPushDocumentEvent_OpenNotification jsString:[localNotificationEvent toJsonString]];
+          }
+        }
+    }
+  
+  [JPUSHService setDebugMode];
+  
+  NSString *plistPath = [[NSBundle mainBundle] pathForResource:JPushConfig_FileName ofType:@"plist"];
+  NSMutableDictionary *plistData = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
+  NSNumber *delay       = [plistData valueForKey:JPushConfig_Delay];
+  
+  _launchOptions = notification.userInfo;
+  
+  if (![delay boolValue]) {
+    [self startJPushSDK];
+  }
+}
+
+-(void)startJPushSDK{
+    [self registerForRemoteNotification];
+    [JPushPlugin setupJPushSDK:_launchOptions];
+}
+
+- (void)jpushSDKDidLoginNotification {
+  NSDictionary *event = @{@"registrationId": JPUSHService.registrationID};
+  [JPushPlugin fireDocumentEvent:JPushDocumentEvent_receiveRegistrationId jsString:[event toJsonString]];
+}
+
+- (NSMutableDictionary *)jpushFormatAPNSDic:(NSDictionary *)dic {
+  NSMutableDictionary *extras = @{}.mutableCopy;
+  for (NSString *key in dic) {
+    if([key isEqualToString:@"_j_business"]      ||
+       [key isEqualToString:@"_j_msgid"]         ||
+       [key isEqualToString:@"_j_uid"]           ||
+       [key isEqualToString:@"actionIdentifier"] ||
+       [key isEqualToString:@"aps"]) {
+      continue;
+    }
+    extras[key] = dic[key];
+  }
+  NSMutableDictionary *formatDic = dic.mutableCopy;
+  formatDic[@"extras"] = extras;
+  return formatDic;
+}
+
+-(void)registerForRemoteNotification{
+    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
+#ifdef NSFoundationVersionNumber_iOS_9_x_Max
+        JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
+        entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;
+        [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
+#endif
+    }else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
+        //可以添加自定义categories
+        [JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge |
+                                                          UIUserNotificationTypeSound |
+                                                          UIUserNotificationTypeAlert)
+                                              categories:nil];
+    } else if([[UIDevice currentDevice].systemVersion floatValue] < 8.0){
+        //categories 必须为nil
+        [JPUSHService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
+                                                          UIRemoteNotificationTypeSound |
+                                                          UIRemoteNotificationTypeAlert)
+                                              categories:nil];
+    }
+}
+
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+    [JPUSHService registerDeviceToken:deviceToken];
+}
+
+-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
+    [JPUSHService handleRemoteNotification:userInfo];
+
+    [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveNotification jsString:[userInfo toJsonString]];
+}
+
+-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
+    [JPUSHService handleRemoteNotification:userInfo];
+    NSString *eventName;
+    switch ([UIApplication sharedApplication].applicationState) {
+      case UIApplicationStateBackground:
+        eventName = JPushDocumentEvent_BackgroundNotification;
+        break;
+      default:
+        eventName = JPushDocumentEvent_ReceiveNotification;
+        break;
+    }
+
+    [JPushPlugin fireDocumentEvent:eventName jsString:[[self jpushFormatAPNSDic:userInfo] toJsonString]];
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+      completionHandler(UIBackgroundFetchResultNewData);
+    });
+}
+
+-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler{
+  NSMutableDictionary *userInfo = @{}.mutableCopy;
+  
+  if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
+    userInfo = [self jpushFormatAPNSDic:notification.request.content.userInfo];
+  } else {
+    UNNotificationContent *content = notification.request.content;
+    userInfo[@"content"] = content.body;
+    userInfo[@"badge"] = content.badge;
+    userInfo[@"extras"] = content.userInfo;
+    userInfo[@"identifier"] = notification.request.identifier;
+  }
+  
+  completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);
+  
+  if ([userInfo[@"aps"][@"content-available"] isEqualToNumber:@(1)]) {// content-available 当用户开启后台推送是,防止触发两次事件
+    return;
+  }
+  
+  [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveNotification jsString:[userInfo toJsonString]];
+  
+}
+
+-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{
+  UNNotification *notification = response.notification;
+  NSMutableDictionary *userInfo = @{}.mutableCopy;
+  
+  if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
+    userInfo = [self jpushFormatAPNSDic:notification.request.content.userInfo];
+  } else {
+    UNNotificationContent *content = notification.request.content;
+    userInfo[@"content"] = content.body;
+    userInfo[@"badge"] = content.badge;
+    userInfo[@"extras"] = content.userInfo;
+    userInfo[@"identifier"] = notification.request.identifier;
+  }
+  
+  [JPushPlugin fireDocumentEvent:JPushDocumentEvent_OpenNotification jsString:[userInfo toJsonString]];
+  completionHandler();
+}
+
+- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
+  NSMutableDictionary *localNotificationEvent = @{}.mutableCopy;
+  localNotificationEvent[@"content"] = notification.alertBody;
+  localNotificationEvent[@"badge"] = @(notification.applicationIconBadgeNumber);
+  localNotificationEvent[@"extras"] = notification.userInfo;
+  
+  [[NSNotificationCenter defaultCenter] postNotificationName:JPushDocumentEvent_ReceiveLocalNotification object:localNotificationEvent];
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+    //  [application setApplicationIconBadgeNumber:0];
+    //  [application cancelAllLocalNotifications];
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+    //  [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
+}
+
+@end
diff --git a/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPUSHService.h b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPUSHService.h
new file mode 100755 (executable)
index 0000000..904da9a
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ *     | |    | |  \ \  / /  | |    | |   / _______|
+ *     | |____| |   \ \/ /   | |____| |  / /
+ *     | |____| |    \  /    | |____| |  | |   _____
+ *     | |    | |    /  \    | |    | |  | |  |____ |
+ *  | |    | |   / /\ \   | |    | |  \ \______| |
+ *  | |    | |  /_/  \_\  | |    | |   \_________|
+ *
+ * Copyright (c) 2011 ~ 2017 Shenzhen HXHG. All rights reserved.
+ */
+
+#define JPUSH_VERSION_NUMBER 3.2.1
+
+#import <Foundation/Foundation.h>
+
+@class CLRegion;
+@class UILocalNotification;
+@class CLLocation;
+@class UNNotificationCategory;
+@class UNNotificationSettings;
+@class UNNotificationRequest;
+@class UNNotification;
+@protocol JPUSHRegisterDelegate;
+@protocol JPUSHGeofenceDelegate;
+
+typedef void (^JPUSHTagsOperationCompletion)(NSInteger iResCode, NSSet *iTags, NSInteger seq);
+typedef void (^JPUSHTagValidOperationCompletion)(NSInteger iResCode, NSSet *iTags, NSInteger seq, BOOL isBind);
+typedef void (^JPUSHAliasOperationCompletion)(NSInteger iResCode, NSString *iAlias, NSInteger seq);
+
+extern NSString *const kJPFNetworkIsConnectingNotification; // 正在连接中
+extern NSString *const kJPFNetworkDidSetupNotification;     // 建立连接
+extern NSString *const kJPFNetworkDidCloseNotification;     // 关闭连接
+extern NSString *const kJPFNetworkDidRegisterNotification;  // 注册成功
+extern NSString *const kJPFNetworkFailedRegisterNotification; //注册失败
+extern NSString *const kJPFNetworkDidLoginNotification;     // 登录成功
+extern NSString *const kJPFNetworkDidReceiveMessageNotification;         // 收到消息(非APNS)
+extern NSString *const kJPFServiceErrorNotification;  // 错误提示
+
+typedef NS_OPTIONS(NSUInteger, JPAuthorizationOptions) {
+    JPAuthorizationOptionNone    = 0,   // the application may not present any UI upon a notification being received
+    JPAuthorizationOptionBadge   = (1 << 0),    // the application may badge its icon upon a notification being received
+    JPAuthorizationOptionSound   = (1 << 1),    // the application may play a sound upon a notification being received
+    JPAuthorizationOptionAlert   = (1 << 2),    // the application may display an alert upon a notification being received
+    JPAuthorizationOptionCarPlay = (1 << 3),    // The ability to display notifications in a CarPlay environment.
+    JPAuthorizationOptionCriticalAlert NS_AVAILABLE_IOS(12.0) = (1 << 4) ,   //The ability to play sounds for critical alerts.
+    JPAuthorizationOptionProvidesAppNotificationSettings NS_AVAILABLE_IOS(12.0) = (1 << 5) ,      //An option indicating the system should display a button for in-app notification settings.
+    JPAuthorizationOptionProvisional NS_AVAILABLE_IOS(12.0) = (1 << 6) ,     //The ability to post noninterrupting notifications provisionally to the Notification Center.
+  
+};
+
+/*!
+ * 通知注册实体类
+ */
+@interface JPUSHRegisterEntity : NSObject
+
+/*!
+ * 支持的类型
+ * badge,sound,alert
+ */
+@property (nonatomic, assign) NSInteger types;
+/*!
+ * 注入的类别
+ * iOS10 UNNotificationCategory
+ * iOS8-iOS9 UIUserNotificationCategory
+ */
+@property (nonatomic, strong) NSSet *categories;
+@end
+
+/*!
+ * 进行删除、查找推送实体类
+ */
+@interface JPushNotificationIdentifier : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, copy) NSArray<NSString *> *identifiers; // 推送的标识数组
+@property (nonatomic, copy) UILocalNotification *notificationObj NS_DEPRECATED_IOS(4_0, 10_0);  // iOS10以下可以传UILocalNotification对象数据,iOS10以上无效
+@property (nonatomic, assign) BOOL delivered NS_AVAILABLE_IOS(10_0); // 在通知中心显示的或待推送的标志,默认为NO,YES表示在通知中心显示的,NO表示待推送的
+@property (nonatomic, copy) void (^findCompletionHandler)(NSArray *results); // 用于查询回调,调用[findNotification:]方法前必须设置,results为返回相应对象数组,iOS10以下返回UILocalNotification对象数组;iOS10以上根据delivered传入值返回UNNotification或UNNotificationRequest对象数组(delivered传入YES,则返回UNNotification对象数组,否则返回UNNotificationRequest对象数组)
+
+@end
+
+/*!
+ * 推送通知声音实体类
+ * iOS10以上有效
+ */
+@interface JPushNotificationSound : NSObject <NSCopying, NSCoding>
+@property (nonatomic, copy) NSString *soundName; //普通通知铃声
+@property (nonatomic, copy) NSString *criticalSoundName NS_AVAILABLE_IOS(12.0); //警告通知铃声
+@property (nonatomic, assign) float criticalSoundVolume NS_AVAILABLE_IOS(12.0); //警告通知铃声音量,有效值在0~1之间,默认为1
+@end
+
+
+/*!
+ * 推送内容实体类
+ */
+@interface JPushNotificationContent : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, copy) NSString *title;                // 推送标题
+@property (nonatomic, copy) NSString *subtitle;             // 推送副标题
+@property (nonatomic, copy) NSString *body;                 // 推送内容
+@property (nonatomic, copy) NSNumber *badge;                // 角标的数字。如果不需要改变角标传@(-1)
+@property (nonatomic, copy) NSString *action NS_DEPRECATED_IOS(8_0, 10_0); // 弹框的按钮显示的内容(IOS 8默认为"打开", 其他默认为"启动",iOS10以上无效)
+@property (nonatomic, copy) NSString *categoryIdentifier;   // 行为分类标识
+@property (nonatomic, copy) NSDictionary *userInfo;         // 本地推送时可以设置userInfo来增加附加信息,远程推送时设置的payload推送内容作为此userInfo
+@property (nonatomic, copy) NSString *sound;                // 声音名称,不设置则为默认声音
+@property (nonatomic, copy) JPushNotificationSound *soundSetting NS_AVAILABLE_IOS(10.0);   //推送声音实体
+@property (nonatomic, copy) NSArray *attachments NS_AVAILABLE_IOS(10_0);                 // 附件,iOS10以上有效,需要传入UNNotificationAttachment对象数组类型
+@property (nonatomic, copy) NSString *threadIdentifier NS_AVAILABLE_IOS(10_0); // 线程或与推送请求相关对话的标识,iOS10以上有效,可用来对推送进行分组
+@property (nonatomic, copy) NSString *launchImageName NS_AVAILABLE_IOS(10_0);  // 启动图片名,iOS10以上有效,从推送启动时将会用到
+@property (nonatomic, copy) NSString *summaryArgument NS_AVAILABLE_IOS(12.0);  //插入到通知摘要中的部分参数。iOS12以上有效。
+@property (nonatomic, assign) NSUInteger summaryArgumentCount NS_AVAILABLE_IOS(12.0); //插入到通知摘要中的项目数。iOS12以上有效。
+
+@end
+
+
+/*!
+ * 推送触发方式实体类
+ * 注:dateComponents、timeInterval、region在iOS10以上可选择其中一个参数传入有效值,如果同时传入值会根据优先级I、II、III使其中一种触发方式生效,fireDate为iOS10以下根据时间触发时须传入的参数
+ */
+@interface JPushNotificationTrigger : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, assign) BOOL repeat;                  // 设置是否重复,默认为NO
+@property (nonatomic, copy) NSDate *fireDate NS_DEPRECATED_IOS(2_0, 10_0);           // 用来设置触发推送的时间,iOS10以上无效
+@property (nonatomic, copy) CLRegion *region NS_AVAILABLE_IOS(8_0);                  // 用来设置触发推送的位置,iOS8以上有效,iOS10以上优先级为I,应用需要有允许使用定位的授权
+@property (nonatomic, copy) NSDateComponents *dateComponents NS_AVAILABLE_IOS(10_0); // 用来设置触发推送的日期时间,iOS10以上有效,优先级为II
+@property (nonatomic, assign) NSTimeInterval timeInterval NS_AVAILABLE_IOS(10_0);    // 用来设置触发推送的时间,iOS10以上有效,优先级为III
+
+@end
+
+/*!
+ * 注册或更新推送实体类
+ */
+@interface JPushNotificationRequest : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, copy) NSString *requestIdentifier;    // 推送请求标识
+@property (nonatomic, copy) JPushNotificationContent *content; // 设置推送的具体内容
+@property (nonatomic, copy) JPushNotificationTrigger *trigger; // 设置推送的触发方式
+@property (nonatomic, copy) void (^completionHandler)(id result); // 注册或更新推送成功回调,iOS10以上成功则result为UNNotificationRequest对象,失败则result为nil;iOS10以下成功result为UILocalNotification对象,失败则result为nil
+
+@end
+
+/*!
+ * JPush 核心头文件
+ */
+@interface JPUSHService : NSObject
+
+
+///----------------------------------------------------
+/// @name Setup 启动相关
+///----------------------------------------------------
+
+
+/*!
+ * @abstract 启动SDK
+ *
+ * @param launchingOption 启动参数.
+ * @param appKey 一个JPush 应用必须的,唯一的标识. 请参考 JPush 相关说明文档来获取这个标识.
+ * @param channel 发布渠道. 可选.
+ * @param isProduction 是否生产环境. 如果为开发状态,设置为 NO; 如果为生产状态,应改为 YES.
+ *                     App 证书环境取决于profile provision的配置,此处建议与证书环境保持一致.
+ *
+ * @discussion 提供SDK启动必须的参数, 来启动 SDK.
+ * 此接口必须在 App 启动时调用, 否则 JPush SDK 将无法正常工作.
+ */
++ (void)setupWithOption:(NSDictionary *)launchingOption
+                 appKey:(NSString *)appKey
+                channel:(NSString *)channel
+       apsForProduction:(BOOL)isProduction;
+
+/*!
+ * @abstract 启动SDK
+ *
+ * @param launchingOption 启动参数.
+ * @param appKey 一个JPush 应用必须的,唯一的标识. 请参考 JPush 相关说明文档来获取这个标识.
+ * @param channel 发布渠道. 可选.
+ * @param isProduction 是否生产环境. 如果为开发状态,设置为 NO; 如果为生产状态,应改为 YES.
+ *                     App 证书环境取决于profile provision的配置,此处建议与证书环境保持一致.
+ * @param advertisingId 广告标识符(IDFA) 如果不需要使用IDFA,传nil.
+ *
+ * @discussion 提供SDK启动必须的参数, 来启动 SDK.
+ * 此接口必须在 App 启动时调用, 否则 JPush SDK 将无法正常工作.
+ */
++ (void)setupWithOption:(NSDictionary *)launchingOption
+                 appKey:(NSString *)appKey
+                channel:(NSString *)channel
+       apsForProduction:(BOOL)isProduction
+  advertisingIdentifier:(NSString *)advertisingId;
+
+
+///----------------------------------------------------
+/// @name APNs about 通知相关
+///----------------------------------------------------
+
+/*!
+ * @abstract 注册要处理的远程通知类型
+ *
+ * @param types 通知类型
+ * @param categories 类别组
+ *
+ */
++ (void)registerForRemoteNotificationTypes:(NSUInteger)types
+                                categories:(NSSet *)categories;
+/*!
+ * @abstract 新版本的注册方法(兼容iOS10)
+ *
+ * @param config 注册通知配置
+ * @param delegate 代理
+ *
+ */
++ (void)registerForRemoteNotificationConfig:(JPUSHRegisterEntity *)config delegate:(id<JPUSHRegisterDelegate>)delegate;
+
+
++ (void)registerDeviceToken:(NSData *)deviceToken;
+
+
+/*!
+ * @abstract 处理收到的 APNs 消息
+ */
++ (void)handleRemoteNotification:(NSDictionary *)remoteInfo;
+
+/*!
+ * Tags操作接口
+ * 支持增加/覆盖/删除/清空/查询操作
+ * 详情请参考文档:https://docs.jiguang.cn/jpush/client/iOS/ios_api/)
+ */
+
+/**
+ 增加tags
+
+ @param tags 需要增加的tags集合
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)addTags:(NSSet<NSString *> *)tags
+     completion:(JPUSHTagsOperationCompletion)completion
+            seq:(NSInteger)seq;
+
+/**
+ 覆盖tags
+ 调用该接口会覆盖用户所有的tags
+
+ @param tags 需要设置的tags集合
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)setTags:(NSSet<NSString *> *)tags
+     completion:(JPUSHTagsOperationCompletion)completion
+            seq:(NSInteger)seq;
+
+/**
+ 删除指定tags
+
+ @param tags 需要删除的tags集合
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)deleteTags:(NSSet<NSString *> *)tags
+        completion:(JPUSHTagsOperationCompletion)completion
+               seq:(NSInteger)seq;
+
+/**
+ 清空所有tags
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)cleanTags:(JPUSHTagsOperationCompletion)completion
+              seq:(NSInteger)seq;
+
+/**
+ 查询全部tags
+
+ @param completion 响应回调,请在回调中获取查询结果
+ @param seq 请求序列号
+ */
++ (void)getAllTags:(JPUSHTagsOperationCompletion)completion
+               seq:(NSInteger)seq;
+
+/**
+ 验证tag是否绑定
+ @param completion 响应回调,回调中查看是否绑定
+ @param seq 请求序列号
+ */
++ (void)validTag:(NSString *)tag
+      completion:(JPUSHTagValidOperationCompletion)completion
+             seq:(NSInteger)seq;
+
+/**
+ 设置Alias
+
+ @param alias 需要设置的alias
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)setAlias:(NSString *)alias
+      completion:(JPUSHAliasOperationCompletion)completion
+             seq:(NSInteger)seq;
+
+/**
+ 删除alias
+
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)deleteAlias:(JPUSHAliasOperationCompletion)completion
+                seq:(NSInteger)seq;
+
+/**
+ 查询当前alias
+
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)getAlias:(JPUSHAliasOperationCompletion)completion
+             seq:(NSInteger)seq;
+
+
+/*!
+ * @abstract 过滤掉无效的 tags
+ *
+ * @discussion 如果 tags 数量超过限制数量, 则返回靠前的有效的 tags.
+ * 建议设置 tags 前用此接口校验. SDK 内部也会基于此接口来做过滤.
+ */
++ (NSSet *)filterValidTags:(NSSet *)tags;
+
+///----------------------------------------------------
+/// @name Stats 统计功能
+///----------------------------------------------------
+
+/*!
+ * @abstract 开始记录页面停留
+ *
+ * @param pageName 页面名称
+ * @discussion JCore 1.1.8 版本后,如需统计页面流,请使用 JAnalytics
+ */
++ (void)startLogPageView:(NSString *)pageName __attribute__((deprecated("JCore 1.1.8 版本已过期")));
+
+/*!
+ * @abstract 停止记录页面停留
+ *
+ * @param pageName 页面
+ * @discussion JCore 1.1.8 版本后,如需统计页面流,请使用 JAnalytics
+ */
++ (void)stopLogPageView:(NSString *)pageName __attribute__((deprecated("JCore 1.1.8 版本已过期")));
+
+/*!
+ * @abstract 直接上报在页面的停留时间
+ *
+ * @param pageName 页面
+ * @param seconds 停留的秒数
+ * @discussion JCore 1.1.8 版本后,如需统计页面流,请使用 JAnalytics
+ */
++ (void)beginLogPageView:(NSString *)pageName duration:(int)seconds __attribute__((deprecated("JCore 1.1.8 版本已过期")));
+
+/*!
+ * @abstract 开启Crash日志收集
+ *
+ * @discussion 默认是关闭状态.
+ */
++ (void)crashLogON;
+
+/*!
+ * @abstract 地理位置上报
+ *
+ * @param latitude 纬度.
+ * @param longitude 经度.
+ *
+ */
++ (void)setLatitude:(double)latitude longitude:(double)longitude;
+
+/*!
+ * @abstract 地理位置上报
+ *
+ * @param location 直接传递 CLLocation * 型的地理信息
+ *
+ * @discussion 需要链接 CoreLocation.framework 并且 #import <CoreLocation/CoreLocation.h>
+ */
++ (void)setLocation:(CLLocation *)location;
+
+/**
+ 设置地理围栏的最大个数
+ 默认值为 10 ,iOS系统默认地理围栏最大个数为20
+ @param count 个数 count
+ */
++ (void)setGeofenecMaxCount:(NSInteger)count;
+/**
+ 注册地理围栏的代理
+
+ @param delegate 代理
+ @param launchOptions app启动完成是收到的字段参数
+ */
++ (void)registerLbsGeofenceDelegate:(id<JPUSHGeofenceDelegate>)delegate withLaunchOptions:(NSDictionary *)launchOptions;
+
+/**
+ 删除地理围栏
+ @param geofenceId 地理围栏id
+ */
++ (void)removeGeofenceWithIdentifier:(NSString *)geofenceId;
+
+///----------------------------------------------------
+/// @name Local Notification 本地通知
+///----------------------------------------------------
+/*!
+ * @abstract 注册或更新推送 (支持iOS10,并兼容iOS10以下版本)
+ *
+ * JPush 2.1.9新接口
+ * @param request JPushNotificationRequest类型,设置推送的属性,设置已有推送的request.requestIdentifier即更新已有的推送,否则为注册新推送,更新推送仅仅在iOS10以上有效,结果通过request.completionHandler返回
+ * @discussion 旧的注册本地推送接口被废弃,使用此接口可以替换
+ *
+ */
++ (void)addNotification:(JPushNotificationRequest *)request;
+
+/*!
+ * @abstract 移除推送 (支持iOS10,并兼容iOS10以下版本)
+ *
+ * JPush 2.1.9新接口
+ * @param identifier JPushNotificationIdentifier类型,iOS10以上identifier设置为nil,则移除所有在通知中心显示推送和待推送请求,也可以通过设置identifier.delivered和identifier.identifiers来移除相应在通知中心显示推送或待推送请求,identifier.identifiers如果设置为nil或空数组则移除相应标志下所有在通知中心显示推送或待推送请求;iOS10以下identifier设置为nil,则移除所有推送,identifier.delivered属性无效,另外可以通过identifier.notificationObj传入特定推送对象来移除此推送。
+ * @discussion 旧的所有删除推送接口被废弃,使用此接口可以替换
+ *
+ */
++ (void)removeNotification:(JPushNotificationIdentifier *)identifier;
+
+/*!
+ * @abstract 查找推送 (支持iOS10,并兼容iOS10以下版本)
+ *
+ * JPush 2.1.9新接口
+ * @param identifier JPushNotificationIdentifier类型,iOS10以上可以通过设置identifier.delivered和identifier.identifiers来查找相应在通知中心显示推送或待推送请求,identifier.identifiers如果设置为nil或空数组则返回相应标志下所有在通知中心显示推送或待推送请求;iOS10以下identifier.delivered属性无效,identifier.identifiers如果设置nil或空数组则返回所有未触发的推送。须要设置identifier.findCompletionHandler回调才能得到查找结果,通过(NSArray *results)返回相应对象数组。
+ * @discussion 旧的查找推送接口被废弃,使用此接口可以替换
+ *
+ */
++ (void)findNotification:(JPushNotificationIdentifier *)identifier;
+
+/*!
+ * @abstract 本地推送,最多支持64个
+ *
+ * @param fireDate 本地推送触发的时间
+ * @param alertBody 本地推送需要显示的内容
+ * @param badge 角标的数字。如果不需要改变角标传-1
+ * @param alertAction 弹框的按钮显示的内容(IOS 8默认为"打开", 其他默认为"启动")
+ * @param notificationKey 本地推送标示符
+ * @param userInfo 自定义参数,可以用来标识推送和增加附加信息
+ * @param soundName 自定义通知声音,设置为nil为默认声音
+ *
+ * @discussion 最多支持 64 个定义,此方法被[addNotification:]方法取代
+ */
++ (UILocalNotification *)setLocalNotification:(NSDate *)fireDate
+                                    alertBody:(NSString *)alertBody
+                                        badge:(int)badge
+                                  alertAction:(NSString *)alertAction
+                                identifierKey:(NSString *)notificationKey
+                                     userInfo:(NSDictionary *)userInfo
+                                    soundName:(NSString *)soundName __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 本地推送 (支持 iOS8 新参数)
+ *
+ * IOS8新参数
+ * @param region 自定义参数
+ * @param regionTriggersOnce 自定义参数
+ * @param category 自定义参数
+ * @discussion 此方法被[addNotification:]方法取代
+ */
++ (UILocalNotification *)setLocalNotification:(NSDate *)fireDate
+                                    alertBody:(NSString *)alertBody
+                                        badge:(int)badge
+                                  alertAction:(NSString *)alertAction
+                                identifierKey:(NSString *)notificationKey
+                                     userInfo:(NSDictionary *)userInfo
+                                    soundName:(NSString *)soundName
+                                       region:(CLRegion *)region
+                           regionTriggersOnce:(BOOL)regionTriggersOnce
+                                     category:(NSString *)category NS_AVAILABLE_IOS(8_0) __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 前台展示本地推送
+ *
+ * @param notification 本地推送对象
+ * @param notificationKey 需要前台显示的本地推送通知的标示符
+ *
+ * @discussion 默认App在前台运行时不会进行弹窗,在程序接收通知调用此接口可实现指定的推送弹窗。--iOS10以下还可继续使用,iOS10以上在[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:]方法中调用completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);即可
+ */
++ (void)showLocalNotificationAtFront:(UILocalNotification *)notification
+                       identifierKey:(NSString *)notificationKey __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+/*!
+ * @abstract 删除本地推送定义
+ *
+ * @param notificationKey 本地推送标示符
+ * @discussion 此方法被[removeNotification:]方法取代
+ */
++ (void)deleteLocalNotificationWithIdentifierKey:(NSString *)notificationKey __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 删除本地推送定义
+ * @discussion 此方法被[removeNotification:]方法取代
+ */
++ (void)deleteLocalNotification:(UILocalNotification *)localNotification __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 获取指定通知
+ *
+ * @param notificationKey 本地推送标示符
+ * @return 本地推送对象数组, [array count]为0时表示没找到
+ * @discussion 此方法被[findNotification:]方法取代
+ */
++ (NSArray *)findLocalNotificationWithIdentifier:(NSString *)notificationKey __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 清除所有本地推送对象
+ * @discussion 此方法被[removeNotification:]方法取代
+ */
++ (void)clearAllLocalNotifications __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+
+///----------------------------------------------------
+/// @name Server badge 服务器端 badge 功能
+///----------------------------------------------------
+
+/*!
+ * @abstract 设置角标(到服务器)
+ *
+ * @param value 新的值. 会覆盖服务器上保存的值(这个用户)
+ *
+ * @discussion 本接口不会改变应用本地的角标值.
+ * 本地仍须调用 UIApplication:setApplicationIconBadgeNumber 函数来设置脚标.
+ *
+ * 本接口用于配合 JPush 提供的服务器端角标功能.
+ * 该功能解决的问题是, 服务器端推送 APNs 时, 并不知道客户端原来已经存在的角标是多少, 指定一个固定的数字不太合理.
+ *
+ * JPush 服务器端脚标功能提供:
+ *
+ * - 通过本 API 把当前客户端(当前这个用户的) 的实际 badge 设置到服务器端保存起来;
+ * - 调用服务器端 API 发 APNs 时(通常这个调用是批量针对大量用户),
+ *   使用 "+1" 的语义, 来表达需要基于目标用户实际的 badge 值(保存的) +1 来下发通知时带上新的 badge 值;
+ */
++ (BOOL)setBadge:(NSInteger)value;
+
+/*!
+ * @abstract 重置脚标(为0)
+ *
+ * @discussion 相当于 [setBadge:0] 的效果.
+ * 参考 [JPUSHService setBadge:] 说明来理解其作用.
+ */
++ (void)resetBadge;
+
+///----------------------------------------------------
+/// @name Other Feature 其他功能
+///----------------------------------------------------
+
+/*!
+ * @abstract 设置手机号码(到服务器)
+ *
+ * @param mobileNumber 手机号码. 会与用户信息一一对应。可为空,为空则清除号码
+ * @param completion 响应回调。成功则error为空,失败则error带有错误码及错误信息
+ *
+ * @discussion 设置手机号码后,可实现“推送不到短信到”的通知方式,提高推送达到率。结果信息通过completion异步返回,也可将completion设置为nil不处理结果信息。
+ *
+ */
++ (void)setMobileNumber:(NSString *)mobileNumber completion:(void (^)(NSError *error))completion;
+
+///----------------------------------------------------
+/// @name Logs and others 日志与其他
+///----------------------------------------------------
+
+/*!
+ * @abstract JPush标识此设备的 registrationID
+ *
+ * @discussion SDK注册成功后, 调用此接口获取到 registrationID 才能够获取到.
+ *
+ * JPush 支持根据 registrationID 来进行推送.
+ * 如果你需要此功能, 应该通过此接口获取到 registrationID 后, 上报到你自己的服务器端, 并保存下来.
+ * registrationIDCompletionHandler:是新增的获取registrationID的方法,需要在block中获取registrationID,resCode为返回码,模拟器调用此接口resCode返回1011,registrationID返回nil.
+ * 更多的理解请参考 JPush 的文档网站.
+ */
++ (NSString *)registrationID;
+
++ (void)registrationIDCompletionHandler:(void(^)(int resCode,NSString *registrationID))completionHandler;
+
+/*!
+ * @abstract 打开日志级别到 Debug
+ *
+ * @discussion JMessage iOS 的日志系统参考 Android 设计了级别.
+ * 从低到高是: Verbose, Debug, Info, Warning, Error.
+ * 对日志级别的进一步理解, 请参考 Android 相关的说明.
+ *
+ * SDK 默认开启的日志级别为: Info. 只显示必要的信息, 不打印调试日志.
+ *
+ * 请在SDK启动后调用本接口,调用本接口可打开日志级别为: Debug, 打印调试日志.
+ */
++ (void)setDebugMode;
+
+/*!
+ * @abstract 关闭日志
+ *
+ * @discussion 关于日志级别的说明, 参考 [JPUSHService setDebugMode]
+ *
+ * 虽说是关闭日志, 但还是会打印 Warning, Error 日志. 这二种日志级别, 在程序运行正常时, 不应有打印输出.
+ *
+ * 建议在发布的版本里, 调用此接口, 关闭掉日志打印.
+ */
++ (void)setLogOFF;
+
+///----------------------------------------------------
+///********************下列方法已过期********************
+///**************请使用新版tag/alias操作接口**************
+///----------------------------------------------------
+/// @name Tag alias setting 设置别名与标签
+///----------------------------------------------------
+
+/*!
+ * 下面的接口是可选的
+ * 设置标签和(或)别名(若参数为nil,则忽略;若是空对象,则清空;详情请参考文档:https://docs.jiguang.cn/jpush/client/iOS/ios_api/)
+ * setTags:alias:fetchCompletionHandle:是新的设置标签别名的方法,不再需要显示声明回调函数,只需要在block里面处理设置结果即可.
+ * WARN: 使用block时需要注意循环引用问题
+ */
++ (void) setTags:(NSSet *)tags
+           alias:(NSString *)alias
+callbackSelector:(SEL)cbSelector
+          target:(id)theTarget __attribute__((deprecated("JPush 2.1.1 版本已过期")));
++ (void) setTags:(NSSet *)tags
+           alias:(NSString *)alias
+callbackSelector:(SEL)cbSelector
+          object:(id)theTarget __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void) setTags:(NSSet *)tags
+callbackSelector:(SEL)cbSelector
+          object:(id)theTarget __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void)setTags:(NSSet *)tags
+          alias:(NSString *)alias
+fetchCompletionHandle:(void (^)(int iResCode, NSSet *iTags, NSString *iAlias))completionHandler __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void)  setTags:(NSSet *)tags
+aliasInbackground:(NSString *)alias __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void)setAlias:(NSString *)alias
+callbackSelector:(SEL)cbSelector
+          object:(id)theTarget __attribute__((deprecated("JPush 3.0.6 版本已过期")));
+
+@end
+
+@class UNUserNotificationCenter;
+@class UNNotificationResponse;
+
+@protocol JPUSHRegisterDelegate <NSObject>
+
+/*
+ * @brief handle UserNotifications.framework [willPresentNotification:withCompletionHandler:]
+ * @param center [UNUserNotificationCenter currentNotificationCenter] 新特性用户通知中心
+ * @param notification 前台得到的的通知对象
+ * @param completionHandler 该callback中的options 请使用UNNotificationPresentationOptions
+ */
+- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger options))completionHandler;
+/*
+ * @brief handle UserNotifications.framework [didReceiveNotificationResponse:withCompletionHandler:]
+ * @param center [UNUserNotificationCenter currentNotificationCenter] 新特性用户通知中心
+ * @param response 通知响应对象
+ * @param completionHandler
+ */
+- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler;
+
+/*
+ * @brief handle UserNotifications.framework [openSettingsForNotification:]
+ * @param center [UNUserNotificationCenter currentNotificationCenter] 新特性用户通知中心
+ * @param notification 当前管理的通知对象
+ */
+- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification NS_AVAILABLE_IOS(12.0);
+
+@end
+
+@protocol JPUSHGeofenceDelegate <NSObject>
+
+/**
+ 进入地理围栏区域
+ @param geofenceId 地理围栏id
+ @param userInfo 地理围栏触发时返回的信息
+ @param error 错误信息
+ */
+- (void)jpushGeofenceIdentifer:(NSString * _Nonnull)geofenceId didEnterRegion:(NSDictionary * _Nullable)userInfo error:(NSError * _Nullable)error;
+
+/**
+ 离开地理围栏区域
+ @param geofenceId 地理围栏id
+ @param userInfo 地理围栏触发时返回的信息
+ @param error 错误信息
+ */
+- (void)jpushGeofenceIdentifer:(NSString * _Nonnull)geofenceId didExitRegion:(NSDictionary * _Nullable)userInfo error:(NSError * _Nullable)error;
+
+@end
diff --git a/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushDefine.h b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushDefine.h
new file mode 100644 (file)
index 0000000..7307ea7
--- /dev/null
@@ -0,0 +1,32 @@
+//
+//  ConstantDef.h
+//  jmessage
+//
+//  Created by ljg on 16/1/19.
+//
+//
+
+#ifndef ConstantDef_h
+#define ConstantDef_h
+
+
+
+#endif /* ConstantDef_h */
+
+#define WEAK_SELF(weakSelf)  __weak __typeof(&*self)weakSelf = self;
+
+static NSString *const JPushConfig_FileName     = @"JPushConfig";
+static NSString *const JPushConfig_Appkey       = @"Appkey";
+static NSString *const JPushConfig_Channel      = @"Channel";
+static NSString *const JPushConfig_IsProduction = @"IsProduction";
+static NSString *const JPushConfig_IsIDFA       = @"IsIDFA";
+static NSString *const JPushConfig_Delay        = @"Delay";
+
+static NSString *const JPushDocumentEvent_ReceiveNotification       = @"receiveNotification";
+static NSString *const JPushDocumentEvent_OpenNotification          = @"openNotification";
+static NSString *const JPushDocumentEvent_BackgroundNotification    = @"backgroundNotification";
+static NSString *const JPushDocumentEvent_SetTagsWithAlias          = @"setTagsWithAlias";
+static NSString *const JPushDocumentEvent_ReceiveMessage            = @"receiveMessage";
+static NSString *const JPushDocumentEvent_ReceiveLocalNotification  = @"receiveLocalNotification";
+
+static NSString *const JPushDocumentEvent_receiveRegistrationId     = @"receiveRegistrationId";
diff --git a/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushPlugin.h b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushPlugin.h
new file mode 100644 (file)
index 0000000..2a7f932
--- /dev/null
@@ -0,0 +1,102 @@
+//
+//  PushTalkPlugin.h
+//  PushTalk
+//
+//  Created by zhangqinghe on 13-12-13.
+//
+//
+
+#import <Cordova/CDV.h>
+
+static NSMutableDictionary *_jpushEventCache;
+
+@interface JPushPlugin : CDVPlugin{
+
+}
+
+//注册通知服务并启动 SDK
+-(void)startJPushSDK:(CDVInvokedUrlCommand*)command;
+
+//以下为js中可调用接口
+//设置标签、别名
+-(void)setTags:(CDVInvokedUrlCommand*)command;
+-(void)addTags:(CDVInvokedUrlCommand*)command;
+-(void)deleteTags:(CDVInvokedUrlCommand*)command;
+-(void)cleanTags:(CDVInvokedUrlCommand*)command;
+-(void)getAllTags:(CDVInvokedUrlCommand*)command;
+-(void)checkTagBindState:(CDVInvokedUrlCommand*)command;
+
+-(void)setAlias:(CDVInvokedUrlCommand*)command;
+-(void)deleteAlias:(CDVInvokedUrlCommand*)command;
+-(void)getAlias:(CDVInvokedUrlCommand*)command;
+
+//获取 RegistrationID
+-(void)getRegistrationID:(CDVInvokedUrlCommand*)command;
+
+//页面统计
+-(void)startLogPageView:(CDVInvokedUrlCommand*)command;
+-(void)stopLogPageView:(CDVInvokedUrlCommand*)command;
+-(void)beginLogPageView:(CDVInvokedUrlCommand*)command;
+
+//设置角标到服务器,服务器下一次发消息时,会设置成这个值
+//本接口不会改变应用本地的角标值.
+-(void)setBadge:(CDVInvokedUrlCommand*)command;
+//相当于 [setBadge:0]
+-(void)resetBadge:(CDVInvokedUrlCommand*)command;
+
+//应用本地的角标值设置/获取
+-(void)setApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command;
+-(void)getApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command;
+
+//停止与恢复推送
+-(void)stopPush:(CDVInvokedUrlCommand*)command;
+-(void)resumePush:(CDVInvokedUrlCommand*)command;
+-(void)isPushStopped:(CDVInvokedUrlCommand*)command;
+
+//开关日志
+-(void)setDebugModeFromIos:(CDVInvokedUrlCommand*)command;
+-(void)setLogOFF:(CDVInvokedUrlCommand*)command;
+-(void)crashLogON:(CDVInvokedUrlCommand*)command;
+
+//本地推送
+-(void)setLocalNotification:(CDVInvokedUrlCommand*)command;
+-(void)deleteLocalNotificationWithIdentifierKey:(CDVInvokedUrlCommand*)command;
+-(void)clearAllLocalNotifications:(CDVInvokedUrlCommand*)command;
+
+//地理位置上报 [latitude,longitude]
+-(void)setLocation:(CDVInvokedUrlCommand*)command;
+
+//检查用户的推送设置情况
+-(void)getUserNotificationSettings:(CDVInvokedUrlCommand*)command;
+
+//ios 10 APIs
+-(void)addDismissActions:(CDVInvokedUrlCommand*)command;
+-(void)addNotificationActions:(CDVInvokedUrlCommand*)command;
+
+/*
+ *  以下为js中可监听到的事件
+ *  jpush.openNotification      点击推送消息启动或唤醒app
+ *  jpush.receiveMessage        收到自定义消息
+ *  jpush.receiveNotification   前台收到推送
+ *  jpush.backgroundNotification 后台收到推送
+ */
+
+# pragma mark - private
+
++(void)fireDocumentEvent:(NSString*)eventName jsString:(NSString*)jsString;
+
++(void)setupJPushSDK:(NSDictionary*)userInfo;
+
+@end
+
+static JPushPlugin *SharedJPushPlugin;
+
+@interface NSDictionary (JPush)
+-(NSString*)toJsonString;
+@end
+
+@interface NSString (JPush)
+-(NSDictionary*)toDictionary;
+@end
+
+
diff --git a/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushPlugin.m b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/JPushPlugin.m
new file mode 100644 (file)
index 0000000..33229a5
--- /dev/null
@@ -0,0 +1,576 @@
+#import "JPushPlugin.h"
+#import "JPUSHService.h"
+#import <UIKit/UIKit.h>
+#import <AdSupport/AdSupport.h>
+#import <UserNotifications/UserNotifications.h>
+#import "AppDelegate+JPush.h"
+#import "JPushDefine.h"
+
+@implementation NSDictionary (JPush)
+-(NSString*)toJsonString{
+    NSError  *error;
+    NSData   *data       = [NSJSONSerialization dataWithJSONObject:self options:0 error:&error];
+    NSString *jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
+    return jsonString;
+}
+@end
+
+@implementation NSString (JPush)
+-(NSDictionary*)toDictionary{
+    NSError      *error;
+    NSData       *jsonData = [self dataUsingEncoding:NSUTF8StringEncoding];
+    NSDictionary *dict     = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
+    return dict;
+}
+@end
+
+@interface JPushPlugin()
+
+@end
+
+@implementation JPushPlugin
+
+-(void)startJPushSDK:(CDVInvokedUrlCommand*)command{
+    [(AppDelegate*)[UIApplication sharedApplication].delegate startJPushSDK];
+}
+
+#pragma mark- 外部接口
+-(void)stopPush:(CDVInvokedUrlCommand*)command{
+    [[UIApplication sharedApplication]unregisterForRemoteNotifications];
+}
+
+-(void)resumePush:(CDVInvokedUrlCommand*)command{
+    [(AppDelegate*)[UIApplication sharedApplication].delegate registerForRemoteNotification];
+}
+
+-(void)isPushStopped:(CDVInvokedUrlCommand*)command{
+    NSNumber *result = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] ? @(0) : @(1);
+    [self handleResultWithValue:result command:command];
+}
+
+-(void)initial:(CDVInvokedUrlCommand*)command{
+    //do nithng,because Cordova plugin use lazy load mode.
+}
+
+#ifdef __CORDOVA_4_0_0
+
+- (void)pluginInitialize {
+    NSLog(@"### pluginInitialize ");
+    [self initPlugin];
+}
+
+#else
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView{
+    NSLog(@"### initWithWebView ");
+    if (self=[super initWithWebView:theWebView]) {
+    }
+    [self initPlugin];
+    return self;
+}
+
+#endif
+
+-(void)initPlugin{
+    if (!SharedJPushPlugin) {
+        SharedJPushPlugin = self;
+    }
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(networkDidReceiveMessage:)
+                                                 name:kJPFNetworkDidReceiveMessageNotification
+                                               object:nil];
+  [[NSNotificationCenter defaultCenter] addObserver:self
+                                           selector:@selector(receiveLocalNotification:)
+                                               name:JPushDocumentEvent_ReceiveLocalNotification
+                                             object:nil];
+  [self dispatchJPushCacheEvent];
+}
+
+- (void)dispatchJPushCacheEvent {
+  for (NSString* key in _jpushEventCache) {
+    NSArray *evenList = _jpushEventCache[key];
+    for (NSString *event in evenList) {
+        [JPushPlugin fireDocumentEvent:key jsString:event];
+    }
+  }
+}
+
++(void)fireDocumentEvent:(NSString*)eventName jsString:(NSString*)jsString{
+  if (SharedJPushPlugin) {
+    dispatch_async(dispatch_get_main_queue(), ^{
+      [SharedJPushPlugin.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.fireDocumentEvent('jpush.%@',%@)", eventName, jsString]];
+    });
+    return;
+  }
+  
+  if (!_jpushEventCache) {
+    _jpushEventCache = @{}.mutableCopy;
+  }
+  
+  if (!_jpushEventCache[eventName]) {
+    _jpushEventCache[eventName] = @[].mutableCopy;
+  }
+  
+  [_jpushEventCache[eventName] addObject: jsString];
+}
+
+-(void)setTags:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSArray* tags = params[@"tags"];
+  
+    [JPUSHService setTags:[NSSet setWithArray:tags]
+               completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+                   NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+                   [dic setObject:sequence forKey:@"sequence"];
+                   
+                   CDVPluginResult* result;
+                   
+                   if (iResCode == 0) { 
+                       [dic setObject:[iTags allObjects] forKey:@"tags"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+                   } else {
+                       [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+                   }
+                   
+                   [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+               } seq:[sequence integerValue]];
+}
+
+-(void)addTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSArray* tags = params[@"tags"];
+    
+    [JPUSHService addTags:[NSSet setWithArray:tags]
+               completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+                   NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+                   [dic setObject:sequence forKey:@"sequence"];
+                   
+                   CDVPluginResult* result;
+                   
+                   if (iResCode == 0) { 
+                       [dic setObject:[iTags allObjects] forKey:@"tags"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+                   } else {
+                       [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+                   }
+                   
+                   [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+               } seq:[sequence integerValue]];
+}
+
+-(void)deleteTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSArray* tags = params[@"tags"];
+    
+    [JPUSHService deleteTags:[NSSet setWithArray:tags]
+               completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+                   NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+                   [dic setObject:sequence forKey:@"sequence"];
+                   
+                   CDVPluginResult* result;
+                   
+                   if (iResCode == 0) { 
+                       [dic setObject:[iTags allObjects] forKey:@"tags"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+                   } else {
+                       [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+                   }
+                   
+                   [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+               } seq:[sequence integerValue]];
+}
+
+-(void)cleanTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService cleanTags:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)getAllTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService getAllTags:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) { 
+            [dic setObject:[iTags allObjects] forKey:@"tags"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)checkTagBindState:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSString* tag = params[@"tag"];
+    
+    [JPUSHService validTag:tag completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq, BOOL isBind) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) { 
+            dic[@"tag"] = tag;
+            [dic setObject:[NSNumber numberWithBool:isBind] forKey:@"isBind"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)setAlias:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSString* alias = params[@"alias"];
+    
+    [JPUSHService setAlias:alias completion:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            [dic setObject:iAlias forKey:@"alias"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+            
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)deleteAlias:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService deleteAlias:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)getAlias:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService getAlias:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            [dic setObject:iAlias forKey:@"alias"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)getRegistrationID:(CDVInvokedUrlCommand*)command{
+    NSString* registrationID = [JPUSHService registrationID];
+    [self handleResultWithValue:registrationID command:command];
+}
+
+-(void)startLogPageView:(CDVInvokedUrlCommand*)command{
+    NSString * pageName = [command argumentAtIndex:0];
+    [JPUSHService startLogPageView:pageName];
+}
+
+-(void)stopLogPageView:(CDVInvokedUrlCommand*)command{
+    NSString * pageName = [command argumentAtIndex:0];
+    [JPUSHService stopLogPageView:pageName];
+}
+
+-(void)beginLogPageView:(CDVInvokedUrlCommand*)command{
+    NSString *pageName = [command argumentAtIndex:0];
+    NSNumber *duration = [command argumentAtIndex:1];
+    [JPUSHService beginLogPageView:pageName duration:duration.intValue];
+}
+
+-(void)setBadge:(CDVInvokedUrlCommand*)command{
+    NSNumber *badge = [command argumentAtIndex:0];
+    [JPUSHService setBadge:badge.intValue];
+}
+
+-(void)resetBadge:(CDVInvokedUrlCommand*)command{
+    [JPUSHService resetBadge];
+}
+
+-(void)setApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command{
+    NSNumber *badge = [command argumentAtIndex:0];
+    [UIApplication sharedApplication].applicationIconBadgeNumber = badge.intValue;
+}
+
+-(void)getApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command {
+    NSInteger num = [UIApplication sharedApplication].applicationIconBadgeNumber;
+    NSNumber *number = [NSNumber numberWithInteger:num];
+    [self handleResultWithValue:number command:command];
+}
+
+-(void)setDebugModeFromIos:(CDVInvokedUrlCommand*)command{
+    [JPUSHService setDebugMode];
+}
+
+-(void)setLogOFF:(CDVInvokedUrlCommand*)command{
+    [JPUSHService setLogOFF];
+}
+
+-(void)crashLogON:(CDVInvokedUrlCommand*)command{
+    [JPUSHService crashLogON];
+}
+
+-(void)setLocalNotification:(CDVInvokedUrlCommand*)command{
+  NSNumber     *delay = [command argumentAtIndex:0];
+  NSString     *alert = [command argumentAtIndex:1];
+  NSNumber     *badge = [command argumentAtIndex:2];
+  NSString     *idKey = [command argumentAtIndex:3];
+  NSDictionary *userInfo  = [command argumentAtIndex:4];
+  
+  JPushNotificationContent *content = [[JPushNotificationContent alloc] init];
+  
+  if (alert) {
+    content.body = alert;
+  }
+  
+  if (badge) {
+    content.badge = badge;
+  }
+  
+  if (userInfo) {
+    content.userInfo = userInfo;
+  }
+  
+  JPushNotificationTrigger *trigger = [[JPushNotificationTrigger alloc] init];
+  // 由于 不支持 0 作为传入参数,在传入参数基础上添加一个极小的时间于 android 端保持一致。
+  if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
+    if (delay) {
+      trigger.timeInterval = [delay doubleValue] + 0.000001;
+    }
+  } else {
+    if (delay) {
+      trigger.fireDate = [NSDate dateWithTimeIntervalSinceNow:[[command argumentAtIndex:0] doubleValue] + 0.001];
+    }
+  }
+  
+  JPushNotificationRequest *request = [[JPushNotificationRequest alloc] init];
+  request.content = content;
+  request.trigger = trigger;
+  
+  if (idKey) {
+    request.requestIdentifier = idKey;
+  }
+  
+  request.completionHandler = ^(id result) {
+    NSLog(@"result");
+  };
+  
+  [JPUSHService addNotification:request];
+}
+
+-(void)deleteLocalNotificationWithIdentifierKey:(CDVInvokedUrlCommand*)command{
+    NSString *identifier = [command argumentAtIndex:0];
+    JPushNotificationIdentifier *jpid = [JPushNotificationIdentifier new];
+    jpid.identifiers = @[identifier];
+    [JPUSHService removeNotification:jpid];
+}
+
+-(void)clearAllLocalNotifications:(CDVInvokedUrlCommand*)command{
+    [JPUSHService removeNotification:nil];
+}
+
+-(void)setLocation:(CDVInvokedUrlCommand*)command{
+    NSNumber *latitude  = [command argumentAtIndex:0];
+    NSNumber *longitude = [command argumentAtIndex:1];
+    [JPUSHService setLatitude:latitude.doubleValue longitude:longitude.doubleValue];
+}
+
+-(void)getUserNotificationSettings:(CDVInvokedUrlCommand*)command{
+    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
+        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+        WEAK_SELF(weakSelf);
+        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
+            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+            dict[@"authorizationStatus"]       = @(settings.authorizationStatus);
+            dict[@"soundSetting"]              = @(settings.soundSetting);
+            dict[@"badgeSetting"]              = @(settings.badgeSetting);
+            dict[@"alertSetting"]              = @(settings.alertSetting);
+            dict[@"notificationCenterSetting"] = @(settings.notificationCenterSetting);
+            dict[@"lockScreenSetting"]         = @(settings.lockScreenSetting);
+            dict[@"carPlaySetting"]            = @(settings.carPlaySetting);
+            dict[@"alertStyle"]                = @(settings.alertStyle);
+            [weakSelf handleResultWithValue:dict command:command];
+        }];
+    }else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
+        UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
+        UIUserNotificationType type = settings.types;
+        NSNumber *number = [NSNumber numberWithInteger:type];
+        [self handleResultWithValue:number command:command];
+    }else{
+        UIRemoteNotificationType type = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
+        NSNumber *number = [NSNumber numberWithInteger:type];
+        [self handleResultWithValue:number command:command];
+    }
+}
+
+#pragma mark - ios 10 APIs
+
+-(void)addDismissActions:(CDVInvokedUrlCommand*)command{
+    [self addActions:command dismiss:YES];
+}
+
+-(void)addNotificationActions:(CDVInvokedUrlCommand*)command{
+    [self addActions:command dismiss:NO];
+}
+
+-(void)addActions:(CDVInvokedUrlCommand*)command dismiss:(BOOL)dimiss{
+    NSArray *actionsData     = [command argumentAtIndex:0];
+    NSString *categoryId     = [command argumentAtIndex:1];
+    NSMutableArray *actions  = [NSMutableArray array];
+    for (NSDictionary *dict in actionsData) {
+        NSString *title      = dict[@"title"];
+        NSString *identifier = dict[@"identifier"];
+        NSString *option     = dict[@"option"];
+        NSString *type       = dict[@"type"];
+        if ([type isEqualToString:@"textInput"]) {
+            NSString *textInputButtonTitle = dict[@"textInputButtonTitle"];
+            NSString *textInputPlaceholder = dict[@"textInputPlaceholder"];
+            UNTextInputNotificationAction *inputAction = [UNTextInputNotificationAction actionWithIdentifier:identifier title:title options:option.integerValue textInputButtonTitle:textInputButtonTitle textInputPlaceholder:textInputPlaceholder];
+            [actions addObject:inputAction];
+        } else {
+            UNNotificationAction *action = [UNNotificationAction actionWithIdentifier:title title:title options:option.integerValue];
+            [actions addObject:action];
+        }
+    }
+    UNNotificationCategory *category;
+    if (dimiss) {
+        category = [UNNotificationCategory categoryWithIdentifier:categoryId
+                                                          actions:actions
+                                                intentIdentifiers:@[]
+                                                          options:UNNotificationCategoryOptionCustomDismissAction];
+    } else {
+        category = [UNNotificationCategory categoryWithIdentifier:categoryId
+                                                          actions:actions
+                                                intentIdentifiers:@[]
+                                                          options:UNNotificationCategoryOptionNone];
+    }
+    [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:category]];
+}
+
+#pragma mark - 内部方法
+
++(void)setupJPushSDK:(NSDictionary*)userInfo{
+    NSString *plistPath = [[NSBundle mainBundle] pathForResource:JPushConfig_FileName ofType:@"plist"];
+    if (plistPath == nil) {
+        NSLog(@"error: PushConfig.plist not found");
+        assert(0);
+    }
+
+    NSMutableDictionary *plistData = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
+    NSString *appkey       = [plistData valueForKey:JPushConfig_Appkey];
+    NSString *channel      = [plistData valueForKey:JPushConfig_Channel];
+    NSNumber *isProduction = [plistData valueForKey:JPushConfig_IsProduction];
+    NSNumber *isIDFA       = [plistData valueForKey:JPushConfig_IsIDFA];
+
+    NSString *advertisingId = nil;
+    if(isIDFA.boolValue) {
+        advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
+    }
+    [JPUSHService setupWithOption:userInfo
+                           appKey:appkey
+                          channel:channel
+                 apsForProduction:[isProduction boolValue]
+            advertisingIdentifier:advertisingId];
+}
+
+#pragma mark 将参数返回给js
+-(void)handleResultWithValue:(id)value command:(CDVInvokedUrlCommand*)command {
+    CDVPluginResult *result = nil;
+    CDVCommandStatus status = CDVCommandStatus_OK;
+
+    if ([value isKindOfClass:[NSString class]]) {
+        value = [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+    } else if ([value isKindOfClass:[NSNull class]]) {
+        value = nil;
+    }
+
+    if ([value isKindOfClass:[NSObject class]]) {
+        result = [CDVPluginResult resultWithStatus:status messageAsString:value];//NSObject 类型都可以
+    } else {
+        NSLog(@"Cordova callback block returned unrecognized type: %@", NSStringFromClass([value class]));
+        result = nil;
+    }
+
+    if (!result) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+-(void)networkDidReceiveMessage:(NSNotification *)notification {
+    if (notification && notification.userInfo) {
+        [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveMessage
+                              jsString:[notification.userInfo toJsonString]];
+    }
+}
+
+-(void)receiveLocalNotification:(NSNotification *)notification {
+  if (notification && notification.object) {
+    [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveLocalNotification
+                          jsString:[notification.object toJsonString]];
+  }
+}
+@end
diff --git a/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/jpush-ios-3.2.1.a b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/jpush-ios-3.2.1.a
new file mode 100755 (executable)
index 0000000..6aa3beb
Binary files /dev/null and b/platforms/ios/dlapp/Plugins/jpush-phonegap-plugin/jpush-ios-3.2.1.a differ
diff --git a/platforms/ios/dlapp/Resources/JPushConfig.plist b/platforms/ios/dlapp/Resources/JPushConfig.plist
new file mode 100644 (file)
index 0000000..8d4a326
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>Appkey</key>
+       <string>your_jpush_appkey</string>
+       <key>Channel</key>
+       <string>channel name</string>
+       <key>IsProduction</key>
+       <false/>
+       <key>IsIDFA</key>
+       <false/>
+       <key>Delay</key>
+       <false/>
+</dict>
+</plist>
\ No newline at end of file
index ed6c95a..e4a0567 100755 (executable)
@@ -55,6 +55,9 @@
     <feature name="ThemeableBrowser">
         <param name="ios-package" value="CDVThemeableBrowser" />
     </feature>
+    <feature name="JPushPlugin">
+        <param name="ios-package" value="JPushPlugin" />
+    </feature>
     <name short="大理市民卡">dlapp</name>
     <description>
         A sample Apache Cordova application that responds to the deviceready event.
index 1e0b815..67584e9 100644 (file)
@@ -63,5 +63,9 @@
                <string>UIInterfaceOrientationPortrait</string>
                <string>UIInterfaceOrientationPortraitUpsideDown</string>
        </array>
+       <key>UIBackgroundModes</key>
+       <array>
+               <string>remote-notification</string>
+       </array>
 </dict>
 </plist>
\ No newline at end of file
index be943cc..d1a9e95 100644 (file)
@@ -1,8 +1,17 @@
 {
-    "Security.framework": 2,
-    "SystemConfiguration.framework": 1,
+    "Security.framework": 3,
+    "SystemConfiguration.framework": 2,
     "LocalAuthentication.framework": 1,
     "ImageIO.framework": 1,
     "CoreLocation.framework": 1,
-    "AVFoundation.framework": 1
+    "AVFoundation.framework": 1,
+    "CFNetwork.framework": 1,
+    "CoreFoundation.framework": 1,
+    "CoreTelephony.framework": 1,
+    "Foundation.framework": 1,
+    "UIKit.framework": 1,
+    "libz.tbd": 1,
+    "AdSupport.framework": 1,
+    "UserNotifications.framework": 1,
+    "libresolv.tbd": 1
 }
\ No newline at end of file
index ba7facb..8bd066d 100644 (file)
             {
               "xml": "<feature name=\"ThemeableBrowser\"><param name=\"ios-package\" value=\"CDVThemeableBrowser\" /></feature>",
               "count": 1
+            },
+            {
+              "xml": "<feature name=\"JPushPlugin\"><param name=\"ios-package\" value=\"JPushPlugin\" /></feature>",
+              "count": 1
             }
           ]
         }
               "mode": "merge",
               "id": "config.xml"
             }
+          ],
+          "UIBackgroundModes": [
+            {
+              "xml": "<array><string>remote-notification</string></array>",
+              "count": 1
+            }
+          ]
+        }
+      },
+      "*-Debug.plist": {
+        "parents": {
+          "aps-environment": [
+            {
+              "xml": "<string>development</string>",
+              "count": 1
+            }
+          ]
+        }
+      },
+      "*-Release.plist": {
+        "parents": {
+          "aps-environment": [
+            {
+              "xml": "<string>production</string>",
+              "count": 1
+            }
+          ]
+        }
+      },
+      "*JPushConfig.plist": {
+        "parents": {
+          "Appkey": [
+            {
+              "xml": "<string>your_jpush_appkey</string>",
+              "count": 1
+            }
           ]
         }
       }
     },
     "cordova-plugin-themeablebrowser": {
       "PACKAGE_NAME": "$(PRODUCT_BUNDLE_IDENTIFIER)"
+    },
+    "cordova-plugin-jcore": {
+      "PACKAGE_NAME": "$(PRODUCT_BUNDLE_IDENTIFIER)"
+    },
+    "jpush-phonegap-plugin": {
+      "APP_KEY": "your_jpush_appkey",
+      "CHANNEL": "developer-default",
+      "PACKAGE_NAME": "$(PRODUCT_BUNDLE_IDENTIFIER)"
     }
   },
   "dependent_plugins": {},
       "clobbers": [
         "cordova.ThemeableBrowser"
       ]
+    },
+    {
+      "id": "jpush-phonegap-plugin.JPushPlugin",
+      "file": "plugins/jpush-phonegap-plugin/www/JPushPlugin.js",
+      "pluginId": "jpush-phonegap-plugin",
+      "clobbers": [
+        "JPush"
+      ]
     }
   ],
   "plugin_metadata": {
     "cordova-plugin-camera": "4.0.3",
     "cordova-plugin-inappbrowser": "3.0.0",
     "cordova-plugin-device": "2.0.2",
-    "cordova-plugin-themeablebrowser": "0.2.17"
+    "cordova-plugin-themeablebrowser": "0.2.17",
+    "cordova-plugin-jcore": "1.3.0",
+    "jpush-phonegap-plugin": "3.7.2"
   }
 }
index 79df2d5..2a5d5a5 100644 (file)
@@ -319,6 +319,14 @@ cordova.define('cordova/plugin_list', function(require, exports, module) {
       "clobbers": [
         "cordova.ThemeableBrowser"
       ]
+    },
+    {
+      "id": "jpush-phonegap-plugin.JPushPlugin",
+      "file": "plugins/jpush-phonegap-plugin/www/JPushPlugin.js",
+      "pluginId": "jpush-phonegap-plugin",
+      "clobbers": [
+        "JPush"
+      ]
     }
   ];
   module.exports.metadata = {
@@ -334,6 +342,8 @@ cordova.define('cordova/plugin_list', function(require, exports, module) {
     "cordova-plugin-camera": "4.0.3",
     "cordova-plugin-inappbrowser": "3.0.0",
     "cordova-plugin-device": "2.0.2",
-    "cordova-plugin-themeablebrowser": "0.2.17"
+    "cordova-plugin-themeablebrowser": "0.2.17",
+    "cordova-plugin-jcore": "1.3.0",
+    "jpush-phonegap-plugin": "3.7.2"
   };
 });
\ No newline at end of file
diff --git a/platforms/ios/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js b/platforms/ios/platform_www/plugins/jpush-phonegap-plugin/www/JPushPlugin.js
new file mode 100644 (file)
index 0000000..773b885
--- /dev/null
@@ -0,0 +1,489 @@
+cordova.define("jpush-phonegap-plugin.JPushPlugin", function(require, exports, module) {
+var JPushPlugin = function() {};
+
+// private plugin function
+
+JPushPlugin.prototype.receiveMessage = {};
+JPushPlugin.prototype.openNotification = {};
+JPushPlugin.prototype.receiveNotification = {};
+
+JPushPlugin.prototype.isPlatformIOS = function() {
+  return (
+    device.platform === "iPhone" ||
+    device.platform === "iPad" ||
+    device.platform === "iPod touch" ||
+    device.platform === "iOS"
+  );
+};
+
+JPushPlugin.prototype.errorCallback = function(msg) {
+  console.log("JPush Callback Error: " + msg);
+};
+
+JPushPlugin.prototype.callNative = function(
+  name,
+  args,
+  successCallback,
+  errorCallback
+) {
+  if (errorCallback) {
+    cordova.exec(successCallback, errorCallback, "JPushPlugin", name, args);
+  } else {
+    cordova.exec(
+      successCallback,
+      this.errorCallback,
+      "JPushPlugin",
+      name,
+      args
+    );
+  }
+};
+
+// Common methods
+JPushPlugin.prototype.init = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("initial", [], null);
+  } else {
+    this.callNative("init", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugMode = function(mode) {
+  if (device.platform === "Android") {
+    this.callNative("setDebugMode", [mode], null);
+  } else {
+    if (mode === true) {
+      this.setDebugModeFromIos();
+    } else {
+      this.setLogOFF();
+    }
+  }
+};
+
+JPushPlugin.prototype.getRegistrationID = function(successCallback) {
+  this.callNative("getRegistrationID", [], successCallback);
+};
+
+JPushPlugin.prototype.stopPush = function() {
+  this.callNative("stopPush", [], null);
+};
+
+JPushPlugin.prototype.resumePush = function() {
+  this.callNative("resumePush", [], null);
+};
+
+JPushPlugin.prototype.isPushStopped = function(successCallback) {
+  this.callNative("isPushStopped", [], successCallback);
+};
+
+JPushPlugin.prototype.clearLocalNotifications = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearLocalNotifications", [], null);
+  } else {
+    this.clearAllLocalNotifications();
+  }
+};
+
+/**
+ * 设置标签。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.setTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 新增标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.addTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("addTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除指定标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.deleteTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 清除所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.cleanTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("cleanTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAllTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAllTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询指定标签与当前用户的绑定状态。
+ *
+ * @param params = { 'sequence': number, 'tag': string }
+ */
+JPushPlugin.prototype.checkTagBindState = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative(
+    "checkTagBindState",
+    [params],
+    successCallback,
+    errorCallback
+  );
+};
+
+/**
+ * 设置别名。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'alias': string }
+ */
+JPushPlugin.prototype.setAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.deleteAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询当前绑定的别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAlias", [params], successCallback, errorCallback);
+};
+
+// 判断系统设置中是否对本应用启用通知。
+// iOS: 返回值如果大于 0,代表通知开启;0: 通知关闭。
+// UIRemoteNotificationTypeNone = 0,
+// UIRemoteNotificationTypeBadge = 1 << 0,
+// UIRemoteNotificationTypeSound = 1 << 1,
+// UIRemoteNotificationTypeAlert = 1 << 2,
+// UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3,
+// Android: 返回值 1 代表通知启用;0: 通知关闭。
+JPushPlugin.prototype.getUserNotificationSettings = function(successCallback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getUserNotificationSettings", [], successCallback);
+  } else if (device.platform === "Android") {
+    this.callNative("areNotificationEnabled", [], successCallback);
+  }
+};
+
+// iOS methods
+
+JPushPlugin.prototype.startJPushSDK = function() {
+  this.callNative("startJPushSDK", [], null);
+};
+
+JPushPlugin.prototype.setBadge = function(value) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setBadge", [value], null);
+  }
+};
+
+JPushPlugin.prototype.resetBadge = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("resetBadge", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugModeFromIos = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setDebugModeFromIos", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLogOFF = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLogOFF", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCrashLogON = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("crashLogON", [], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotificationForIOS = function(
+  delayTime,
+  content,
+  badge,
+  notificationID,
+  extras
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "setLocalNotification",
+      [delayTime, content, badge, notificationID, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.deleteLocalNotificationWithIdentifierKeyInIOS = function(
+  identifierKey
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "deleteLocalNotificationWithIdentifierKey",
+      [identifierKey],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.clearAllLocalNotifications = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("clearAllLocalNotifications", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLocation = function(latitude, longitude) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLocation", [latitude, longitude], null);
+  }
+};
+
+JPushPlugin.prototype.startLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("startLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.stopLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("stopLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.beginLogPageView = function(pageName, duration) {
+  if (this.isPlatformIOS()) {
+    this.callNative("beginLogPageView", [pageName, duration], null);
+  }
+};
+
+JPushPlugin.prototype.setApplicationIconBadgeNumber = function(badge) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setApplicationIconBadgeNumber", [badge], null);
+  }
+};
+
+JPushPlugin.prototype.getApplicationIconBadgeNumber = function(callback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getApplicationIconBadgeNumber", [], callback);
+  }
+};
+
+JPushPlugin.prototype.addDismissActions = function(actions, categoryId) {
+  this.callNative("addDismissActions", [actions, categoryId]);
+};
+
+JPushPlugin.prototype.addNotificationActions = function(actions, categoryId) {
+  this.callNative("addNotificationActions", [actions, categoryId]);
+};
+
+// Android methods
+JPushPlugin.prototype.getConnectionState = function(successCallback) {
+  if (device.platform === "Android") {
+    this.callNative("getConnectionState", [], successCallback);
+  }
+};
+
+JPushPlugin.prototype.setBasicPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setBasicPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCustomPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setCustomPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.receiveRegistrationIdInAndroidCallback = function(data) {
+  if (device.platform === "Android") {
+    data = JSON.stringify(data);
+    var event = JSON.parse(data);
+    cordova.fireDocumentEvent("jpush.receiveRegistrationId", event);
+  }
+};
+
+JPushPlugin.prototype.receiveMessageInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveMessage = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.receiveMessage", this.receiveMessage);
+};
+
+JPushPlugin.prototype.openNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.openNotification = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.openNotification", this.openNotification);
+};
+
+JPushPlugin.prototype.receiveNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveNotification = JSON.parse(data);
+  cordova.fireDocumentEvent(
+    "jpush.receiveNotification",
+    this.receiveNotification
+  );
+};
+
+JPushPlugin.prototype.clearAllNotification = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearAllNotification", [], null);
+  }
+};
+
+JPushPlugin.prototype.clearNotificationById = function(id) {
+  if (device.platform === "Android") {
+    this.callNative("clearNotificationById", [id], null);
+  }
+};
+
+JPushPlugin.prototype.setLatestNotificationNum = function(num) {
+  if (device.platform === "Android") {
+    this.callNative("setLatestNotificationNum", [num], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotification = function(
+  builderId,
+  content,
+  title,
+  notificationID,
+  broadcastTime,
+  extras
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "addLocalNotification",
+      [builderId, content, title, notificationID, broadcastTime, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.removeLocalNotification = function(notificationID) {
+  if (device.platform === "Android") {
+    this.callNative("removeLocalNotification", [notificationID], null);
+  }
+};
+
+JPushPlugin.prototype.reportNotificationOpened = function(msgID) {
+  if (device.platform === "Android") {
+    this.callNative("reportNotificationOpened", [msgID], null);
+  }
+};
+
+/**
+ * 用于在 Android 6.0 及以上系统,申请一些权限
+ * 具体可看:http://docs.jpush.io/client/android_api/#android-60
+ */
+JPushPlugin.prototype.requestPermission = function() {
+  if (device.platform === "Android") {
+    this.callNative("requestPermission", [], null);
+  }
+};
+
+JPushPlugin.prototype.setSilenceTime = function(
+  startHour,
+  startMinute,
+  endHour,
+  endMinute
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "setSilenceTime",
+      [startHour, startMinute, endHour, endMinute],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.setPushTime = function(weekdays, startHour, endHour) {
+  if (device.platform === "Android") {
+    this.callNative("setPushTime", [weekdays, startHour, endHour], null);
+  }
+};
+
+JPushPlugin.prototype.setGeofenceInterval = function(interval) {
+  if (device.platform === "Android") {
+    this.callNative("setGeofenceInterval", [interval], null);
+  }
+};
+
+JPushPlugin.prototype.setMaxGeofenceNumber = function(maxNumber) {
+  if (device.platform === "Android") {
+    this.callNative("setMaxGeofenceNumber", [maxNumber], null);
+  }
+};
+
+if (!window.plugins) {
+  window.plugins = {};
+}
+
+if (!window.plugins.jPushPlugin) {
+  window.plugins.jPushPlugin = new JPushPlugin();
+}
+
+module.exports = new JPushPlugin();
+
+});
index 12c7065..5bd06f2 100644 (file)
@@ -1,20 +1,2 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-//  KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-//
-
 // DO NOT MODIFY -- auto-generated by Apache Cordova
+#include "Pods/Target Support Files/Pods-dlapp/Pods-dlapp.debug.xcconfig"
\ No newline at end of file
index 12e2b13..79d2243 100644 (file)
@@ -1,20 +1,2 @@
-//
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-//  KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-//
-
-// DO NOT MODIFY -- auto-generated by Apache Cordova
\ No newline at end of file
+// DO NOT MODIFY -- auto-generated by Apache Cordova
+#include "Pods/Target Support Files/Pods-dlapp/Pods-dlapp.release.xcconfig"
\ No newline at end of file
index a5de634..7acd36f 100644 (file)
     },
     "cordova-plugin-themeablebrowser": {
       "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "jpush-phonegap-plugin": {
+      "APP_KEY": "your_jpush_appkey",
+      "CHANNEL": "developer-default",
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
     }
   },
   "dependent_plugins": {
@@ -48,6 +53,9 @@
     },
     "cordova-plugin-file": {
       "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-jcore": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
     }
   }
 }
index 509cc38..a89cd8a 100644 (file)
     },
     "cordova-plugin-themeablebrowser": {
       "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "jpush-phonegap-plugin": {
+      "APP_KEY": "your_jpush_appkey",
+      "CHANNEL": "developer-default",
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
     }
   },
   "dependent_plugins": {
@@ -47,6 +52,9 @@
     },
     "cordova-plugin-file": {
       "PACKAGE_NAME": "com.supwisdom.dlapp"
+    },
+    "cordova-plugin-jcore": {
+      "PACKAGE_NAME": "com.supwisdom.dlapp"
     }
   }
 }
diff --git a/plugins/cordova-plugin-jcore/LICENSE b/plugins/cordova-plugin-jcore/LICENSE
new file mode 100644 (file)
index 0000000..aa4b1b3
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 极光开发者
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/plugins/cordova-plugin-jcore/doc/sdk_model.png b/plugins/cordova-plugin-jcore/doc/sdk_model.png
new file mode 100644 (file)
index 0000000..efcd5d5
Binary files /dev/null and b/plugins/cordova-plugin-jcore/doc/sdk_model.png differ
diff --git a/plugins/cordova-plugin-jcore/package.json b/plugins/cordova-plugin-jcore/package.json
new file mode 100644 (file)
index 0000000..7566bc0
--- /dev/null
@@ -0,0 +1,57 @@
+{
+  "_from": "cordova-plugin-jcore",
+  "_id": "cordova-plugin-jcore@1.3.0",
+  "_inBundle": false,
+  "_integrity": "sha512-QmkdABlkIHFaMUBsrjPqhpaWS7wuboRPeeVEUNoc4FlZNRoQgw35v0MaW+SbOk4SzVsEGvN6IFSVqa4106ljUw==",
+  "_location": "/cordova-plugin-jcore",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "cordova-plugin-jcore",
+    "name": "cordova-plugin-jcore",
+    "escapedName": "cordova-plugin-jcore",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/cordova-plugin-jcore/-/cordova-plugin-jcore-1.3.0.tgz",
+  "_shasum": "040e792f48039ee4c50631a9b2e0e5c4bf8aa967",
+  "_spec": "cordova-plugin-jcore",
+  "_where": "/Users/shuwei/works2/cordova/dlapp",
+  "author": {
+    "name": "wilhantian, jiguang"
+  },
+  "bugs": {
+    "url": "https://github.com/jpush/cordova-plugin-jcore/issues"
+  },
+  "bundleDependencies": false,
+  "cordova": {
+    "id": "cordova-plugin-jcore",
+    "platforms": [
+      "android",
+      "ios"
+    ]
+  },
+  "deprecated": false,
+  "description": "jcore for cordova plugin",
+  "homepage": "https://github.com/jpush/cordova-plugin-jcore#readme",
+  "keywords": [
+    "jcore",
+    "jpush",
+    "ecosystem:cordova",
+    "cordova-android",
+    "cordova-ios"
+  ],
+  "license": "MIT License",
+  "name": "cordova-plugin-jcore",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jpush/cordova-plugin-jcore.git"
+  },
+  "version": "1.3.0"
+}
diff --git a/plugins/cordova-plugin-jcore/plugin.xml b/plugins/cordova-plugin-jcore/plugin.xml
new file mode 100644 (file)
index 0000000..0d81e5f
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     id="cordova-plugin-jcore"
+     version="1.3.0">
+
+    <name>JCore</name>
+    <description>Cordova plugin for jcore</description>
+    <author>WilhanTian, JiGuang</author>
+    <keywords>jcore,jpush</keywords>
+    <license>MIT License</license>
+
+    <engines>
+        <engine name="cordova" version=">=3.0.0"/>
+    </engines>
+
+    <platform name="android">
+        <resource-file src="src/android/arm64-v8a/libjcore212.so" target="jniLibs/arm64-v8a/libjcore212.so"/>
+        <resource-file src="src/android/armeabi/libjcore212.so" target="jniLibs/armeabi/libjcore212.so"/>
+        <resource-file src="src/android/armeabi-v7a/libjcore212.so" target="jniLibs/armeabi-v7a/libjcore212.so"/>
+        <resource-file src="src/android/mips/libjcore212.so" target="jniLibs/mips/libjcore212.so"/>
+        <resource-file src="src/android/mips64/libjcore212.so" target="jniLibs/mips64/libjcore212.so"/>
+        <resource-file src="src/android/x86/libjcore212.so" target="jniLibs/x86/libjcore212.so"/>
+        <resource-file src="src/android/x86_64/libjcore212.so" target="jniLibs/x86_64/libjcore212.so"/>
+
+        <lib-file src="src/android/jcore-android-2.1.2.jar" />
+    </platform>
+
+    <platform name="ios">
+        <source-file src="src/ios/jcore-ios-2.1.1.a" framework="true" />
+    </platform>
+</plugin>
diff --git a/plugins/cordova-plugin-jcore/src/android/arm64-v8a/libjcore212.so b/plugins/cordova-plugin-jcore/src/android/arm64-v8a/libjcore212.so
new file mode 100755 (executable)
index 0000000..7c7a83e
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/arm64-v8a/libjcore212.so differ
diff --git a/plugins/cordova-plugin-jcore/src/android/armeabi-v7a/libjcore212.so b/plugins/cordova-plugin-jcore/src/android/armeabi-v7a/libjcore212.so
new file mode 100755 (executable)
index 0000000..d09da8e
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/armeabi-v7a/libjcore212.so differ
diff --git a/plugins/cordova-plugin-jcore/src/android/armeabi/libjcore212.so b/plugins/cordova-plugin-jcore/src/android/armeabi/libjcore212.so
new file mode 100755 (executable)
index 0000000..0106c5a
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/armeabi/libjcore212.so differ
diff --git a/plugins/cordova-plugin-jcore/src/android/jcore-android-2.1.2.jar b/plugins/cordova-plugin-jcore/src/android/jcore-android-2.1.2.jar
new file mode 100755 (executable)
index 0000000..9a770dc
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/jcore-android-2.1.2.jar differ
diff --git a/plugins/cordova-plugin-jcore/src/android/mips/libjcore212.so b/plugins/cordova-plugin-jcore/src/android/mips/libjcore212.so
new file mode 100755 (executable)
index 0000000..5110f46
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/mips/libjcore212.so differ
diff --git a/plugins/cordova-plugin-jcore/src/android/mips64/libjcore212.so b/plugins/cordova-plugin-jcore/src/android/mips64/libjcore212.so
new file mode 100755 (executable)
index 0000000..1df557b
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/mips64/libjcore212.so differ
diff --git a/plugins/cordova-plugin-jcore/src/android/x86/libjcore212.so b/plugins/cordova-plugin-jcore/src/android/x86/libjcore212.so
new file mode 100755 (executable)
index 0000000..8eae348
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/x86/libjcore212.so differ
diff --git a/plugins/cordova-plugin-jcore/src/android/x86_64/libjcore212.so b/plugins/cordova-plugin-jcore/src/android/x86_64/libjcore212.so
new file mode 100755 (executable)
index 0000000..c935dca
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/android/x86_64/libjcore212.so differ
diff --git a/plugins/cordova-plugin-jcore/src/ios/jcore-ios-2.1.1.a b/plugins/cordova-plugin-jcore/src/ios/jcore-ios-2.1.1.a
new file mode 100755 (executable)
index 0000000..4cafd84
Binary files /dev/null and b/plugins/cordova-plugin-jcore/src/ios/jcore-ios-2.1.1.a differ
diff --git a/plugins/cordova-plugin-jcore/www/jcore.js b/plugins/cordova-plugin-jcore/www/jcore.js
new file mode 100644 (file)
index 0000000..9146dd0
--- /dev/null
@@ -0,0 +1,5 @@
+var exec = require('cordova/exec')
+
+exports.coolMethod = function (arg0, success, error) {
+  exec(success, error, 'jcore', 'coolMethod', [arg0])
+}
index fa61382..e566969 100644 (file)
     },
     "is_top_level": true,
     "variables": {}
+  },
+  "jpush-phonegap-plugin": {
+    "source": {
+      "type": "registry",
+      "id": "jpush-phonegap-plugin"
+    },
+    "is_top_level": true,
+    "variables": {
+      "APP_KEY": "your_jpush_appkey",
+      "CHANNEL": "developer-default"
+    }
+  },
+  "cordova-plugin-jcore": {
+    "source": {
+      "type": "registry",
+      "id": "cordova-plugin-jcore"
+    },
+    "is_top_level": false,
+    "variables": {}
   }
 }
\ No newline at end of file
index b6404e4..103eff8 100644 (file)
     },
     "cordova-plugin-themeablebrowser": {
       "PACKAGE_NAME": "$(PRODUCT_BUNDLE_IDENTIFIER)"
+    },
+    "jpush-phonegap-plugin": {
+      "APP_KEY": "your_jpush_appkey",
+      "CHANNEL": "developer-default",
+      "PACKAGE_NAME": "$(PRODUCT_BUNDLE_IDENTIFIER)"
     }
   },
   "dependent_plugins": {
@@ -49,6 +54,9 @@
     },
     "cordova-plugin-file": {
       "PACKAGE_NAME": "$(PRODUCT_BUNDLE_IDENTIFIER)"
+    },
+    "cordova-plugin-jcore": {
+      "PACKAGE_NAME": "$(PRODUCT_BUNDLE_IDENTIFIER)"
     }
   }
 }
diff --git a/plugins/jpush-phonegap-plugin/.travis.yml b/plugins/jpush-phonegap-plugin/.travis.yml
new file mode 100644 (file)
index 0000000..c9d186a
--- /dev/null
@@ -0,0 +1,4 @@
+language: node_js
+sudo: false
+node_js:
+    - "4.2"
diff --git a/plugins/jpush-phonegap-plugin/doc/res/DismissActions_00.PNG b/plugins/jpush-phonegap-plugin/doc/res/DismissActions_00.PNG
new file mode 100644 (file)
index 0000000..9d9fa0c
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/DismissActions_00.PNG differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_00.gif b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_00.gif
new file mode 100644 (file)
index 0000000..20a7061
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_00.gif differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_01.png b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_01.png
new file mode 100644 (file)
index 0000000..9085ea0
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_01.png differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_02.png b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_02.png
new file mode 100644 (file)
index 0000000..83f0992
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_02.png differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_03.png b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_03.png
new file mode 100644 (file)
index 0000000..8fc7ef5
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_03.png differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_04.png b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_04.png
new file mode 100644 (file)
index 0000000..d14cc2d
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_04.png differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_05.png b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_05.png
new file mode 100644 (file)
index 0000000..d583cd8
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_05.png differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_06.png b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_06.png
new file mode 100644 (file)
index 0000000..5de590e
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/MediaAttachments_06.png differ
diff --git a/plugins/jpush-phonegap-plugin/doc/res/NotificationActions_00.png b/plugins/jpush-phonegap-plugin/doc/res/NotificationActions_00.png
new file mode 100644 (file)
index 0000000..ae482c6
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/doc/res/NotificationActions_00.png differ
diff --git a/plugins/jpush-phonegap-plugin/example/css/index.css b/plugins/jpush-phonegap-plugin/example/css/index.css
new file mode 100755 (executable)
index 0000000..51daa79
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+* {
+    -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
+}
+
+body {
+    -webkit-touch-callout: none;                /* prevent callout to copy image, etc when tap to hold */
+    -webkit-text-size-adjust: none;             /* prevent webkit from resizing text to fit */
+    -webkit-user-select: none;                  /* prevent copy paste, to allow, change 'none' to 'text' */
+    background-color:#E4E4E4;
+    background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
+    background-image:-webkit-gradient(
+        linear,
+        left top,
+        left bottom,
+        color-stop(0, #A7A7A7),
+        color-stop(0.51, #E4E4E4)
+    );
+    background-attachment:fixed;
+    font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
+    font-size:12px;
+    height:100%;
+    margin:0px;
+    padding:0px;
+    text-transform:uppercase;
+    width:100%;
+}
+
+/* Portrait layout (default) */
+.app {
+    background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
+    position:absolute;             /* position in the center of the screen */
+    left:50%;
+    top:50%;
+    height:50px;                   /* text area height */
+    width:225px;                   /* text area width */
+    text-align:center;
+    padding:180px 0px 0px 0px;     /* image height is 200px (bottom 20px are overlapped with text) */
+    margin:-115px 0px 0px -112px;  /* offset vertical: half of image height and text area height */
+                                   /* offset horizontal: half of text area width */
+}
+
+/* Landscape layout (with min-width) */
+@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
+    .app {
+        background-position:left center;
+        padding:75px 0px 75px 170px;  /* padding-top + padding-bottom + text area = image height */
+        margin:-90px 0px 0px -198px;  /* offset vertical: half of image height */
+                                      /* offset horizontal: half of image width and text area width */
+    }
+}
+
+h1 {
+    font-size:24px;
+    font-weight:normal;
+    margin:0px;
+    overflow:visible;
+    padding:0px;
+    text-align:center;
+}
+
+.event {
+    border-radius:4px;
+    -webkit-border-radius:4px;
+    color:#FFFFFF;
+    font-size:12px;
+    margin:0px 30px;
+    padding:2px 0px;
+}
+
+.event.listening {
+    background-color:#333333;
+    display:block;
+}
+
+.event.received {
+    background-color:#4B946A;
+    display:none;
+}
+
+@keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+@-webkit-keyframes fade {
+    from { opacity: 1.0; }
+    50% { opacity: 0.4; }
+    to { opacity: 1.0; }
+}
+.blink {
+    animation:fade 3000ms infinite;
+    -webkit-animation:fade 3000ms infinite;
+}
diff --git a/plugins/jpush-phonegap-plugin/example/css/jquery.mobile-1.1.1.css b/plugins/jpush-phonegap-plugin/example/css/jquery.mobile-1.1.1.css
new file mode 100755 (executable)
index 0000000..9e37d0d
--- /dev/null
@@ -0,0 +1,2140 @@
+/*
+* jQuery Mobile Framework 1.1.1 1981b3f5ec22675ae47df8f0bdf9622e7780e90e
+* http://jquerymobile.com
+*
+* Copyright 2012 jQuery Foundation and other contributors
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*
+*/
+/* Swatches */
+/* A
+-----------------------------------------------------------------------------------------------------------*/
+.ui-bar-a {
+       border: 1px solid               #333 /*{a-bar-border}*/;
+       background:                     #111111 /*{a-bar-background-color}*/;
+       color:                                  #ffffff /*{a-bar-color}*/;
+       font-weight: bold;
+       text-shadow: 0 /*{a-bar-shadow-x}*/ -1px /*{a-bar-shadow-y}*/ 1px /*{a-bar-shadow-radius}*/ #000000 /*{a-bar-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #3c3c3c /*{a-bar-background-start}*/), to( #111 /*{a-bar-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/);
+}
+.ui-bar-a, 
+.ui-bar-a input, 
+.ui-bar-a select, 
+.ui-bar-a textarea, 
+.ui-bar-a button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-bar-a .ui-link-inherit {
+       color: #fff /*{a-bar-color}*/;
+}
+.ui-bar-a a.ui-link {
+       color: #7cc4e7 /*{a-bar-link-color}*/;
+       font-weight: bold;
+}
+.ui-bar-a a.ui-link:visited {
+    color: #2489CE /*{a-bar-link-visited}*/;
+}
+.ui-bar-a a.ui-link:hover {
+       color: #2489CE /*{a-bar-link-hover}*/;
+}
+.ui-bar-a a.ui-link:active {
+       color: #2489CE /*{a-bar-link-active}*/;
+}
+.ui-body-a,
+.ui-overlay-a {
+       border: 1px solid               #444 /*{a-body-border}*/;
+       background:                     #222 /*{a-body-background-color}*/;
+       color:                                  #fff /*{a-body-color}*/;
+       text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 1px /*{a-body-shadow-radius}*/ #111 /*{a-body-shadow-color}*/;
+       font-weight: normal;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #444 /*{a-body-background-start}*/), to( #222 /*{a-body-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #444 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #444 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #444 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #444 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #444 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/);       
+}
+.ui-overlay-a {
+       background-image: none;
+       border-width: 0;
+}
+.ui-body-a,
+.ui-body-a input,
+.ui-body-a select,
+.ui-body-a textarea,
+.ui-body-a button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-body-a .ui-link-inherit {
+       color:  #fff /*{a-body-color}*/;
+}
+.ui-body-a .ui-link {
+       color: #2489CE /*{a-body-link-color}*/;
+       font-weight: bold;
+}
+.ui-body-a .ui-link:visited {
+    color: #2489CE /*{a-body-link-visited}*/;
+}
+.ui-body-a .ui-link:hover {
+       color: #2489CE /*{a-body-link-hover}*/;
+}
+.ui-body-a .ui-link:active {
+       color: #2489CE /*{a-body-link-active}*/;
+}
+.ui-btn-up-a {
+       border: 1px solid               #111 /*{a-bup-border}*/;
+       background:                     #333 /*{a-bup-background-color}*/;
+       font-weight: bold;
+       color:                                  #fff /*{a-bup-color}*/;
+       text-shadow: 0 /*{a-bup-shadow-x}*/ 1px /*{a-bup-shadow-y}*/ 1px /*{a-bup-shadow-radius}*/ #111 /*{a-bup-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #444444 /*{a-bup-background-start}*/), to( #2d2d2d /*{a-bup-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #444444 /*{a-bup-background-start}*/, #2d2d2d /*{a-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #444444 /*{a-bup-background-start}*/, #2d2d2d /*{a-bup-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #444444 /*{a-bup-background-start}*/, #2d2d2d /*{a-bup-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #444444 /*{a-bup-background-start}*/, #2d2d2d /*{a-bup-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #444444 /*{a-bup-background-start}*/, #2d2d2d /*{a-bup-background-end}*/);
+}
+.ui-btn-up-a:visited,
+.ui-btn-up-a a.ui-link-inherit {
+       color:                                  #fff /*{a-bup-color}*/;
+}
+.ui-btn-hover-a {
+       border: 1px solid               #000 /*{a-bhover-border}*/;
+       background:                     #444444 /*{a-bhover-background-color}*/;
+       font-weight: bold;
+       color:                                  #fff /*{a-bhover-color}*/;
+       text-shadow: 0 /*{a-bhover-shadow-x}*/ 1px /*{a-bhover-shadow-y}*/ 1px /*{a-bhover-shadow-radius}*/ #111 /*{a-bhover-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #555555 /*{a-bhover-background-start}*/), to( #383838 /*{a-bhover-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #555555 /*{a-bhover-background-start}*/, #383838 /*{a-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #555555 /*{a-bhover-background-start}*/, #383838 /*{a-bhover-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #555555 /*{a-bhover-background-start}*/, #383838 /*{a-bhover-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #555555 /*{a-bhover-background-start}*/, #383838 /*{a-bhover-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #555555 /*{a-bhover-background-start}*/, #383838 /*{a-bhover-background-end}*/);
+}
+.ui-btn-hover-a:visited,
+.ui-btn-hover-a:hover,
+.ui-btn-hover-a a.ui-link-inherit {
+       color:                                  #fff /*{a-bhover-color}*/;
+}
+.ui-btn-down-a {
+       border: 1px solid               #000 /*{a-bdown-border}*/;
+       background:                     #222 /*{a-bdown-background-color}*/;
+       font-weight: bold;
+       color:                                  #fff /*{a-bdown-color}*/;
+       text-shadow: 0 /*{a-bdown-shadow-x}*/ 1px /*{a-bdown-shadow-y}*/ 1px /*{a-bdown-shadow-radius}*/ #111 /*{a-bdown-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #202020 /*{a-bdown-background-start}*/), to( #2c2c2c /*{a-bdown-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #202020 /*{a-bdown-background-start}*/, #2c2c2c /*{a-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #202020 /*{a-bdown-background-start}*/, #2c2c2c /*{a-bdown-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #202020 /*{a-bdown-background-start}*/, #2c2c2c /*{a-bdown-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #202020 /*{a-bdown-background-start}*/, #2c2c2c /*{a-bdown-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #202020 /*{a-bdown-background-start}*/, #2c2c2c /*{a-bdown-background-end}*/);
+}
+.ui-btn-down-a:visited,
+.ui-btn-down-a:hover,
+.ui-btn-down-a a.ui-link-inherit {
+       color:                                  #fff /*{a-bdown-color}*/;
+}
+.ui-btn-up-a,
+.ui-btn-hover-a,
+.ui-btn-down-a {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+       text-decoration: none;
+}
+/* B
+-----------------------------------------------------------------------------------------------------------*/
+.ui-bar-b {
+       border: 1px solid               #456f9a /*{b-bar-border}*/;
+       background:                     #5e87b0 /*{b-bar-background-color}*/;
+       color:                                  #fff /*{b-bar-color}*/;
+       font-weight: bold;
+       text-shadow: 0 /*{b-bar-shadow-x}*/ 1px /*{b-bar-shadow-y}*/ 1px /*{b-bar-shadow-radius}*/ #3e6790 /*{b-bar-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #6facd5 /*{b-bar-background-start}*/), to( #497bae /*{b-bar-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #6facd5 /*{b-bar-background-start}*/, #497bae /*{b-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #6facd5 /*{b-bar-background-start}*/, #497bae /*{b-bar-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #6facd5 /*{b-bar-background-start}*/, #497bae /*{b-bar-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #6facd5 /*{b-bar-background-start}*/, #497bae /*{b-bar-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #6facd5 /*{b-bar-background-start}*/, #497bae /*{b-bar-background-end}*/);
+}
+.ui-bar-b,
+.ui-bar-b input,
+.ui-bar-b select,
+.ui-bar-b textarea,
+.ui-bar-b button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-bar-b .ui-link-inherit {
+       color:  #fff /*{b-bar-color}*/;
+}
+.ui-bar-b a.ui-link {
+       color: #ddf0f8 /*{b-bar-link-color}*/;
+       font-weight: bold;
+}
+.ui-bar-b a.ui-link:visited {
+    color: #ddf0f8 /*{b-bar-link-visited}*/;
+}
+.ui-bar-b a.ui-link:hover {
+       color: #ddf0f8 /*{b-bar-link-hover}*/;
+}
+.ui-bar-b a.ui-link:active {
+       color: #ddf0f8 /*{b-bar-link-active}*/;
+}
+.ui-body-b,
+.ui-overlay-b {
+       border: 1px solid               #999 /*{b-body-border}*/;
+       background:                     #f3f3f3 /*{b-body-background-color}*/;
+       color:                                  #222222 /*{b-body-color}*/;
+       text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #fff /*{b-body-shadow-color}*/;
+       font-weight: normal;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #ddd /*{b-body-background-start}*/), to( #ccc /*{b-body-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #ddd /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #ddd /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #ddd /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #ddd /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #ddd /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/);
+}
+.ui-overlay-b {
+       background-image: none;
+       border-width: 0;
+}
+.ui-body-b,
+.ui-body-b input,
+.ui-body-b select,
+.ui-body-b textarea,
+.ui-body-b button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-body-b .ui-link-inherit {
+       color:  #333333 /*{b-body-color}*/;
+}
+.ui-body-b .ui-link {
+       color: #2489CE /*{b-body-link-color}*/;
+       font-weight: bold;
+}
+.ui-body-b .ui-link:visited {
+    color: #2489CE /*{b-body-link-visited}*/;
+}
+.ui-body-b .ui-link:hover {
+       color: #2489CE /*{b-body-link-hover}*/;
+}
+.ui-body-b .ui-link:active {
+       color: #2489CE /*{b-body-link-active}*/;
+}
+.ui-btn-up-b {
+       border: 1px solid               #044062 /*{b-bup-border}*/;
+       background:                     #396b9e /*{b-bup-background-color}*/;
+       font-weight: bold;
+       color:                                  #fff /*{b-bup-color}*/;
+       text-shadow: 0 /*{b-bup-shadow-x}*/ 1px /*{b-bup-shadow-y}*/ 1px /*{b-bup-shadow-radius}*/ #194b7e /*{b-bup-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #5f9cc5 /*{b-bup-background-start}*/), to( #396b9e /*{b-bup-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/);
+}
+.ui-btn-up-b:visited,
+.ui-btn-up-b a.ui-link-inherit {
+       color:                                  #fff /*{b-bup-color}*/;
+}
+.ui-btn-hover-b {
+       border: 1px solid               #00415e /*{b-bhover-border}*/;
+       background:                     #4b88b6 /*{b-bhover-background-color}*/;
+       font-weight: bold;
+       color:                                  #fff /*{b-bhover-color}*/;
+       text-shadow: 0 /*{b-bhover-shadow-x}*/ 1px /*{b-bhover-shadow-y}*/ 1px /*{b-bhover-shadow-radius}*/ #194b7e /*{b-bhover-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #6facd5 /*{b-bhover-background-start}*/), to( #4272a4 /*{b-bhover-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #6facd5 /*{b-bhover-background-start}*/, #4272a4 /*{b-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #6facd5 /*{b-bhover-background-start}*/, #4272a4 /*{b-bhover-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #6facd5 /*{b-bhover-background-start}*/, #4272a4 /*{b-bhover-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #6facd5 /*{b-bhover-background-start}*/, #4272a4 /*{b-bhover-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #6facd5 /*{b-bhover-background-start}*/, #4272a4 /*{b-bhover-background-end}*/);
+}
+.ui-btn-hover-b:visited,
+.ui-btn-hover-a:hover,
+.ui-btn-hover-b a.ui-link-inherit {
+       color:                                  #fff /*{b-bhover-color}*/;
+}
+.ui-btn-down-b {
+       border: 1px solid               #225377 /*{b-bdown-border}*/;
+       background:                     #4e89c5 /*{b-bdown-background-color}*/;
+       font-weight: bold;
+       color:                                  #fff /*{b-bdown-color}*/;
+       text-shadow: 0 /*{b-bdown-shadow-x}*/ 1px /*{b-bdown-shadow-y}*/ 1px /*{b-bdown-shadow-radius}*/ #194b7e /*{b-bdown-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #295b8e /*{b-bdown-background-start}*/), to( #3e79b5 /*{b-bdown-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #295b8e /*{b-bdown-background-start}*/, #3e79b5 /*{b-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #295b8e /*{b-bdown-background-start}*/, #3e79b5 /*{b-bdown-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #295b8e /*{b-bdown-background-start}*/, #3e79b5 /*{b-bdown-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #295b8e /*{b-bdown-background-start}*/, #3e79b5 /*{b-bdown-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #295b8e /*{b-bdown-background-start}*/, #3e79b5 /*{b-bdown-background-end}*/);
+}
+.ui-btn-down-b:visited,
+.ui-btn-down-b:hover,
+.ui-btn-down-b a.ui-link-inherit {
+       color:                                  #fff /*{b-bdown-color}*/;
+}
+.ui-btn-up-b,
+.ui-btn-hover-b,
+.ui-btn-down-b {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+       text-decoration: none;
+}
+/* C
+-----------------------------------------------------------------------------------------------------------*/
+.ui-bar-c {
+       border: 1px solid               #B3B3B3 /*{c-bar-border}*/;
+       background:                     #eeeeee /*{c-bar-background-color}*/;
+       color:                                  #3E3E3E /*{c-bar-color}*/;
+       font-weight: bold;
+       text-shadow: 0 /*{c-bar-shadow-x}*/ 1px /*{c-bar-shadow-y}*/ 1px /*{c-bar-shadow-radius}*/      #fff /*{c-bar-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #f0f0f0 /*{c-bar-background-start}*/), to( #ddd /*{c-bar-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #f0f0f0 /*{c-bar-background-start}*/, #ddd /*{c-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #f0f0f0 /*{c-bar-background-start}*/, #ddd /*{c-bar-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #f0f0f0 /*{c-bar-background-start}*/, #ddd /*{c-bar-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #f0f0f0 /*{c-bar-background-start}*/, #ddd /*{c-bar-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #f0f0f0 /*{c-bar-background-start}*/, #ddd /*{c-bar-background-end}*/);
+}
+.ui-bar-c .ui-link-inherit {
+       color:  #3E3E3E /*{c-bar-color}*/;
+}
+.ui-bar-c a.ui-link {
+       color: #7cc4e7 /*{c-bar-link-color}*/;
+       font-weight: bold;
+}
+.ui-bar-c a.ui-link:visited {
+    color: #2489CE /*{c-bar-link-visited}*/;
+}
+.ui-bar-c a.ui-link:hover {
+       color: #2489CE /*{c-bar-link-hover}*/;
+}
+.ui-bar-c a.ui-link:active {
+       color: #2489CE /*{c-bar-link-active}*/;
+}
+.ui-bar-c,
+.ui-bar-c input,
+.ui-bar-c select,
+.ui-bar-c textarea,
+.ui-bar-c button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-body-c,
+.ui-overlay-c {
+       border: 1px solid               #aaa /*{c-body-border}*/;
+       color:                                  #333333 /*{c-body-color}*/;
+       text-shadow: 0 /*{c-body-shadow-x}*/ 1px /*{c-body-shadow-y}*/ 0 /*{c-body-shadow-radius}*/ #fff /*{c-body-shadow-color}*/;
+       background:                     #f9f9f9 /*{c-body-background-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #f9f9f9 /*{c-body-background-start}*/), to( #eeeeee /*{c-body-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #f9f9f9 /*{c-body-background-start}*/, #eeeeee /*{c-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #f9f9f9 /*{c-body-background-start}*/, #eeeeee /*{c-body-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #f9f9f9 /*{c-body-background-start}*/, #eeeeee /*{c-body-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #f9f9f9 /*{c-body-background-start}*/, #eeeeee /*{c-body-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #f9f9f9 /*{c-body-background-start}*/, #eeeeee /*{c-body-background-end}*/);
+}
+.ui-overlay-c {
+       background-image: none;
+       border-width: 0;
+}
+.ui-body-c,
+.ui-body-c input,
+.ui-body-c select,
+.ui-body-c textarea,
+.ui-body-c button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-body-c .ui-link-inherit {
+       color:  #333333 /*{c-body-color}*/;
+}
+.ui-body-c .ui-link {
+       color: #2489CE /*{c-body-link-color}*/;
+       font-weight: bold;
+}
+.ui-body-c .ui-link:visited {
+    color: #2489CE /*{c-body-link-visited}*/;
+}
+.ui-body-c .ui-link:hover {
+       color: #2489CE /*{c-body-link-hover}*/;
+}
+.ui-body-c .ui-link:active {
+       color: #2489CE /*{c-body-link-active}*/;
+}
+.ui-btn-up-c {
+       border: 1px solid               #ccc /*{c-bup-border}*/;
+       background:                     #eee /*{c-bup-background-color}*/;
+       font-weight: bold;
+       color:                                  #222 /*{c-bup-color}*/;
+       text-shadow: 0 /*{c-bup-shadow-x}*/ 1px /*{c-bup-shadow-y}*/ 0 /*{c-bup-shadow-radius}*/ #ffffff /*{c-bup-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #ffffff /*{c-bup-background-start}*/), to( #f1f1f1 /*{c-bup-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #ffffff /*{c-bup-background-start}*/, #f1f1f1 /*{c-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #ffffff /*{c-bup-background-start}*/, #f1f1f1 /*{c-bup-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #ffffff /*{c-bup-background-start}*/, #f1f1f1 /*{c-bup-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #ffffff /*{c-bup-background-start}*/, #f1f1f1 /*{c-bup-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #ffffff /*{c-bup-background-start}*/, #f1f1f1 /*{c-bup-background-end}*/);
+}
+.ui-btn-up-c:visited,
+.ui-btn-up-c a.ui-link-inherit {
+       color:                                  #2F3E46 /*{c-bup-color}*/;
+}
+.ui-btn-hover-c {
+       border: 1px solid               #bbb /*{c-bhover-border}*/;
+       background:                     #dfdfdf /*{c-bhover-background-color}*/;
+       font-weight: bold;
+       color:                                  #222 /*{c-bhover-color}*/;
+       text-shadow: 0 /*{c-bhover-shadow-x}*/ 1px /*{c-bhover-shadow-y}*/ 0 /*{c-bhover-shadow-radius}*/ #ffffff /*{c-bhover-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #f6f6f6 /*{c-bhover-background-start}*/), to( #e0e0e0 /*{c-bhover-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #f6f6f6 /*{c-bhover-background-start}*/, #e0e0e0 /*{c-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #f6f6f6 /*{c-bhover-background-start}*/, #e0e0e0 /*{c-bhover-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #f6f6f6 /*{c-bhover-background-start}*/, #e0e0e0 /*{c-bhover-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #f6f6f6 /*{c-bhover-background-start}*/, #e0e0e0 /*{c-bhover-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #f6f6f6 /*{c-bhover-background-start}*/, #e0e0e0 /*{c-bhover-background-end}*/);
+}
+.ui-btn-hover-c:visited,
+.ui-btn-hover-c:hover,
+.ui-btn-hover-c a.ui-link-inherit {
+       color:                                  #2F3E46 /*{c-bhover-color}*/;
+}
+.ui-btn-down-c {
+       border: 1px solid               #bbb /*{c-bdown-border}*/;
+       background:                     #d6d6d6 /*{c-bdown-background-color}*/;
+       font-weight: bold;
+       color:                                  #222 /*{c-bdown-color}*/;
+       text-shadow: 0 /*{c-bdown-shadow-x}*/ 1px /*{c-bdown-shadow-y}*/ 0 /*{c-bdown-shadow-radius}*/ #ffffff /*{c-bdown-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #d0d0d0 /*{c-bdown-background-start}*/), to( #dfdfdf /*{c-bdown-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #d0d0d0 /*{c-bdown-background-start}*/, #dfdfdf /*{c-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #d0d0d0 /*{c-bdown-background-start}*/, #dfdfdf /*{c-bdown-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #d0d0d0 /*{c-bdown-background-start}*/, #dfdfdf /*{c-bdown-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #d0d0d0 /*{c-bdown-background-start}*/, #dfdfdf /*{c-bdown-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #d0d0d0 /*{c-bdown-background-start}*/, #dfdfdf /*{c-bdown-background-end}*/);
+}
+.ui-btn-down-c:visited,
+.ui-btn-down-c:hover,
+.ui-btn-down-c a.ui-link-inherit {
+       color:                                  #2F3E46 /*{c-bdown-color}*/;
+}
+.ui-btn-up-c,
+.ui-btn-hover-c,
+.ui-btn-down-c {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+       text-decoration: none;
+}
+/* D
+-----------------------------------------------------------------------------------------------------------*/
+.ui-bar-d {
+       border: 1px solid               #bbb /*{d-bar-border}*/;
+       background:                     #bbb /*{d-bar-background-color}*/;
+       color:                                  #333 /*{d-bar-color}*/;
+       text-shadow: 0 /*{d-bar-shadow-x}*/ 1px /*{d-bar-shadow-y}*/ 0 /*{d-bar-shadow-radius}*/ #eee /*{d-bar-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #ddd /*{d-bar-background-start}*/), to( #bbb /*{d-bar-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/);
+}
+.ui-bar-d,
+.ui-bar-d input,
+.ui-bar-d select,
+.ui-bar-d textarea,
+.ui-bar-d button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-bar-d .ui-link-inherit {
+       color:  #333333 /*{d-bar-color}*/;
+}
+.ui-bar-d a.ui-link {
+       color: #2489CE /*{d-bar-link-color}*/;
+       font-weight: bold;
+}
+.ui-bar-d a.ui-link:visited {
+    color: #2489CE /*{d-bar-link-visited}*/;
+}
+.ui-bar-d a.ui-link:hover {
+       color: #2489CE /*{d-bar-link-hover}*/;
+}
+.ui-bar-d a.ui-link:active {
+       color: #2489CE /*{d-bar-link-active}*/;
+}
+.ui-body-d,
+.ui-overlay-d {
+       border: 1px solid               #bbb /*{d-body-border}*/;
+       color:                                  #333333 /*{d-body-color}*/;
+       text-shadow: 0 /*{d-body-shadow-x}*/ 1px /*{d-body-shadow-y}*/ 0 /*{d-body-shadow-radius}*/     #fff /*{d-body-shadow-color}*/;
+       background:                     #ffffff /*{d-body-background-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #fff), to( #fff /*{d-body-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/);
+}
+.ui-overlay-d {
+       background-image: none;
+       border-width: 0;
+}
+.ui-body-d,
+.ui-body-d input,
+.ui-body-d select,
+.ui-body-d textarea,
+.ui-body-d button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-body-d .ui-link-inherit {
+       color:  #333333 /*{d-body-color}*/;
+}
+.ui-body-d .ui-link {
+       color: #2489CE /*{d-body-link-color}*/;
+       font-weight: bold;
+}
+.ui-body-d .ui-link:visited {
+    color: #2489CE /*{d-body-link-visited}*/;
+}
+.ui-body-d .ui-link:hover {
+       color: #2489CE /*{d-body-link-hover}*/;
+}
+.ui-body-d .ui-link:active {
+       color: #2489CE /*{d-body-link-active}*/;
+}
+.ui-btn-up-d {
+       border: 1px solid               #bbb /*{d-bup-border}*/;
+       background:                     #fff /*{d-bup-background-color}*/;
+       font-weight: bold;
+       color:                                  #333 /*{d-bup-color}*/;
+       text-shadow: 0 /*{d-bup-shadow-x}*/ 1px /*{d-bup-shadow-y}*/ 0 /*{d-bup-shadow-radius}*/ #fff /*{d-bup-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #fafafa), to( #f6f6f6 /*{d-bup-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #fafafa /*{d-bup-background-start}*/, #f6f6f6 /*{d-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #fafafa /*{d-bup-background-start}*/, #f6f6f6 /*{d-bup-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #fafafa /*{d-bup-background-start}*/, #f6f6f6 /*{d-bup-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #fafafa /*{d-bup-background-start}*/, #f6f6f6 /*{d-bup-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #fafafa /*{d-bup-background-start}*/, #f6f6f6 /*{d-bup-background-end}*/);
+}
+.ui-btn-up-d:visited,
+.ui-btn-up-d a.ui-link-inherit {
+       color:                                  #333 /*{d-bup-color}*/;
+}
+.ui-btn-hover-d {
+       border: 1px solid               #aaa /*{d-bhover-border}*/;
+       background:                     #eeeeee /*{d-bhover-background-color}*/;
+       font-weight: bold;
+       color:                                  #333 /*{d-bhover-color}*/;
+       cursor: pointer;
+       text-shadow: 0 /*{d-bhover-shadow-x}*/ 1px /*{d-bhover-shadow-y}*/ 0 /*{d-bhover-shadow-radius}*/       #fff /*{d-bhover-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #eee), to( #fff /*{d-bhover-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #eee /*{d-bhover-background-start}*/, #fff /*{d-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #eee /*{d-bhover-background-start}*/, #fff /*{d-bhover-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #eee /*{d-bhover-background-start}*/, #fff /*{d-bhover-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #eee /*{d-bhover-background-start}*/, #fff /*{d-bhover-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #eee /*{d-bhover-background-start}*/, #fff /*{d-bhover-background-end}*/);
+}
+.ui-btn-hover-d:visited,
+.ui-btn-hover-d:hover,
+.ui-btn-hover-d a.ui-link-inherit {
+       color:                                  #333 /*{d-bhover-color}*/;
+}
+.ui-btn-down-d {
+       border: 1px solid               #aaa /*{d-bdown-border}*/;
+       background:                     #eee /*{d-bdown-background-color}*/;
+       font-weight: bold;
+       color:                                  #333 /*{d-bdown-color}*/;
+       text-shadow: 0 /*{d-bdown-shadow-x}*/ 1px /*{d-bdown-shadow-y}*/ 0 /*{d-bdown-shadow-radius}*/  #ffffff /*{d-bdown-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #e5e5e5 /*{d-bdown-background-start}*/), to( #f2f2f2 /*{d-bdown-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #e5e5e5 /*{d-bdown-background-start}*/, #f2f2f2 /*{d-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #e5e5e5 /*{d-bdown-background-start}*/, #f2f2f2 /*{d-bdown-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #e5e5e5 /*{d-bdown-background-start}*/, #f2f2f2 /*{d-bdown-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #e5e5e5 /*{d-bdown-background-start}*/, #f2f2f2 /*{d-bdown-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #e5e5e5 /*{d-bdown-background-start}*/, #f2f2f2 /*{d-bdown-background-end}*/);
+}
+.ui-btn-down-d:visited,
+.ui-btn-down-d:hover,
+.ui-btn-down-d a.ui-link-inherit {
+       color:                                  #333 /*{d-bdown-color}*/;
+}
+.ui-btn-up-d,
+.ui-btn-hover-d,
+.ui-btn-down-d {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+       text-decoration: none;
+}
+/* E
+-----------------------------------------------------------------------------------------------------------*/
+.ui-bar-e {
+       border: 1px solid               #F7C942 /*{e-bar-border}*/;
+       background:                     #fadb4e /*{e-bar-background-color}*/;
+       color:                                  #333 /*{e-bar-color}*/;
+       text-shadow: 0 /*{e-bar-shadow-x}*/ 1px /*{e-bar-shadow-y}*/ 0 /*{e-bar-shadow-radius}*/        #fff /*{e-bar-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #fceda7 /*{e-bar-background-start}*/), to( #fbef7e /*{e-bar-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #fceda7 /*{e-bar-background-start}*/, #fbef7e /*{e-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #fceda7 /*{e-bar-background-start}*/, #fbef7e /*{e-bar-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #fceda7 /*{e-bar-background-start}*/, #fbef7e /*{e-bar-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #fceda7 /*{e-bar-background-start}*/, #fbef7e /*{e-bar-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #fceda7 /*{e-bar-background-start}*/, #fbef7e /*{e-bar-background-end}*/);
+}
+.ui-bar-e,
+.ui-bar-e input,
+.ui-bar-e select,
+.ui-bar-e textarea,
+.ui-bar-e button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-bar-e .ui-link-inherit {
+       color:  #333333 /*{e-bar-color}*/;
+}
+.ui-bar-e a.ui-link {
+       color: #2489CE /*{e-bar-link-color}*/;
+       font-weight: bold;
+}
+.ui-bar-e a.ui-link:visited {
+    color: #2489CE /*{e-bar-link-visited}*/;
+}
+.ui-bar-e a.ui-link:hover {
+       color: #2489CE /*{e-bar-link-hover}*/;
+}
+.ui-bar-e a.ui-link:active {
+       color: #2489CE /*{e-bar-link-active}*/;
+}
+.ui-body-e,
+.ui-overlay-e {
+       border: 1px solid               #F7C942 /*{e-body-border}*/;
+       color:                                  #222222 /*{e-body-color}*/;
+       text-shadow: 0 /*{e-body-shadow-x}*/ 1px /*{e-body-shadow-y}*/ 0 /*{e-body-shadow-radius}*/     #fff /*{e-body-shadow-color}*/;
+       background:                     #fff9df /*{e-body-background-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #fffadf /*{e-body-background-start}*/), to( #fff3a5 /*{e-body-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #fffadf /*{e-body-background-start}*/, #fff3a5 /*{e-body-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #fffadf /*{e-body-background-start}*/, #fff3a5 /*{e-body-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #fffadf /*{e-body-background-start}*/, #fff3a5 /*{e-body-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #fffadf /*{e-body-background-start}*/, #fff3a5 /*{e-body-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #fffadf /*{e-body-background-start}*/, #fff3a5 /*{e-body-background-end}*/);
+}
+.ui-overlay-e {
+       background-image: none;
+       border-width: 0;
+}
+.ui-body-e,
+.ui-body-e input,
+.ui-body-e select,
+.ui-body-e textarea,
+.ui-body-e button {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-body-e .ui-link-inherit {
+       color:  #333333 /*{e-body-color}*/;
+}
+.ui-body-e .ui-link {
+       color: #2489CE /*{e-body-link-color}*/;
+       font-weight: bold;
+}
+.ui-body-e .ui-link:visited {
+    color: #2489CE /*{e-body-link-visited}*/;
+}
+.ui-body-e .ui-link:hover {
+       color: #2489CE /*{e-body-link-hover}*/;
+}
+.ui-body-e .ui-link:active {
+       color: #2489CE /*{e-body-link-active}*/;
+}
+.ui-btn-up-e {
+       border: 1px solid               #F4C63f /*{e-bup-border}*/;
+       background:                     #fadb4e /*{e-bup-background-color}*/;
+       font-weight: bold;
+       color:                                  #222 /*{e-bup-color}*/;
+       text-shadow: 0 /*{e-bup-shadow-x}*/ 1px /*{e-bup-shadow-y}*/ 0 /*{e-bup-shadow-radius}*/        #fff /*{e-bup-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #ffefaa /*{e-bup-background-start}*/), to( #ffe155 /*{e-bup-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #ffefaa /*{e-bup-background-start}*/, #ffe155 /*{e-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #ffefaa /*{e-bup-background-start}*/, #ffe155 /*{e-bup-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #ffefaa /*{e-bup-background-start}*/, #ffe155 /*{e-bup-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #ffefaa /*{e-bup-background-start}*/, #ffe155 /*{e-bup-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #ffefaa /*{e-bup-background-start}*/, #ffe155 /*{e-bup-background-end}*/);
+}
+.ui-btn-up-e:visited,
+.ui-btn-up-e a.ui-link-inherit {
+       color:                                  #222 /*{e-bup-color}*/;
+}
+.ui-btn-hover-e {
+       border: 1px solid               #F2C43d /*{e-bhover-border}*/;
+       background:                     #fbe26f /*{e-bhover-background-color}*/;
+       font-weight: bold;
+       color:                                  #111 /*{e-bhover-color}*/;
+       text-shadow: 0 /*{e-bhover-shadow-x}*/ 1px /*{e-bhover-shadow-y}*/ 0 /*{e-bhover-shadow-radius}*/       #fff /*{e-bhover-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #fff5ba /*{e-bhover-background-start}*/), to( #fbdd52 /*{e-bhover-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #fff5ba /*{e-bhover-background-start}*/, #fbdd52 /*{e-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #fff5ba /*{e-bhover-background-start}*/, #fbdd52 /*{e-bhover-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #fff5ba /*{e-bhover-background-start}*/, #fbdd52 /*{e-bhover-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #fff5ba /*{e-bhover-background-start}*/, #fbdd52 /*{e-bhover-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #fff5ba /*{e-bhover-background-start}*/, #fbdd52 /*{e-bhover-background-end}*/);
+}
+.ui-btn-hover-e:visited,
+.ui-btn-hover-e:hover,
+.ui-btn-hover-e a.ui-link-inherit {
+       color:                                  #333 /*{e-bhover-color}*/;
+}
+.ui-btn-down-e {
+       border: 1px solid               #F2C43d /*{e-bdown-border}*/;
+       background:                     #fceda7 /*{e-bdown-background-color}*/;
+       font-weight: bold;
+       color:                                  #111 /*{e-bdown-color}*/;
+       text-shadow: 0 /*{e-bdown-shadow-x}*/ 1px /*{e-bdown-shadow-y}*/ 0 /*{e-bdown-shadow-radius}*/  #ffffff /*{e-bdown-shadow-color}*/;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #f8d94c /*{e-bdown-background-start}*/), to( #fadb4e /*{e-bdown-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #f8d94c /*{e-bdown-background-start}*/, #fadb4e /*{e-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #f8d94c /*{e-bdown-background-start}*/, #fadb4e /*{e-bdown-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #f8d94c /*{e-bdown-background-start}*/, #fadb4e /*{e-bdown-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #f8d94c /*{e-bdown-background-start}*/, #fadb4e /*{e-bdown-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #f8d94c /*{e-bdown-background-start}*/, #fadb4e /*{e-bdown-background-end}*/);
+}
+.ui-btn-down-e:visited,
+.ui-btn-down-e:hover,
+.ui-btn-down-e a.ui-link-inherit {
+       color:                                  #333 /*{e-bdown-color}*/;
+}
+.ui-btn-up-e,
+.ui-btn-hover-e,
+.ui-btn-down-e {
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+       text-decoration: none;
+}
+/* Structure */
+/* links within "buttons" 
+-----------------------------------------------------------------------------------------------------------*/
+a.ui-link-inherit {
+       text-decoration: none !important;
+}
+/* Active class used as the "on" state across all themes
+-----------------------------------------------------------------------------------------------------------*/
+.ui-btn-active {
+       border: 1px solid               #2373a5 /*{global-active-border}*/;
+       background:                     #5393c5 /*{global-active-background-color}*/;
+       font-weight: bold;
+       color:                                  #fff /*{global-active-color}*/;
+       cursor: pointer;
+       text-shadow: 0 /*{global-active-shadow-x}*/ 1px /*{global-active-shadow-y}*/ 1px /*{global-active-shadow-radius}*/ #3373a5 /*{global-active-shadow-color}*/;
+       text-decoration: none;
+       background-image: -webkit-gradient(linear, left top, left bottom, from( #5393c5 /*{global-active-background-start}*/), to( #6facd5 /*{global-active-background-end}*/)); /* Saf4+, Chrome */
+       background-image: -webkit-linear-gradient( #5393c5 /*{global-active-background-start}*/, #6facd5 /*{global-active-background-end}*/); /* Chrome 10+, Saf5.1+ */
+       background-image:    -moz-linear-gradient( #5393c5 /*{global-active-background-start}*/, #6facd5 /*{global-active-background-end}*/); /* FF3.6 */
+       background-image:     -ms-linear-gradient( #5393c5 /*{global-active-background-start}*/, #6facd5 /*{global-active-background-end}*/); /* IE10 */
+       background-image:      -o-linear-gradient( #5393c5 /*{global-active-background-start}*/, #6facd5 /*{global-active-background-end}*/); /* Opera 11.10+ */
+       background-image:         linear-gradient( #5393c5 /*{global-active-background-start}*/, #6facd5 /*{global-active-background-end}*/);
+       font-family: Helvetica, Arial, sans-serif /*{global-font-family}*/;
+}
+.ui-btn-active:visited,
+.ui-btn-active:hover,
+.ui-btn-active a.ui-link-inherit {
+       color:                                  #fff /*{global-active-color}*/;
+}
+/* button inner top highlight
+-----------------------------------------------------------------------------------------------------------*/
+.ui-btn-inner {
+       border-top: 1px solid   #fff;
+       border-color:                   rgba(255,255,255,.3);
+}
+/* corner rounding classes
+-----------------------------------------------------------------------------------------------------------*/
+.ui-corner-tl {
+       -moz-border-radius-topleft:             .6em /*{global-radii-blocks}*/;
+       -webkit-border-top-left-radius:         .6em /*{global-radii-blocks}*/;
+       border-top-left-radius:                         .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-tr {
+       -moz-border-radius-topright:            .6em /*{global-radii-blocks}*/;
+       -webkit-border-top-right-radius:        .6em /*{global-radii-blocks}*/;
+       border-top-right-radius:                        .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-bl {
+       -moz-border-radius-bottomleft:          .6em /*{global-radii-blocks}*/;
+       -webkit-border-bottom-left-radius:      .6em /*{global-radii-blocks}*/;
+       border-bottom-left-radius:                      .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-br {
+       -moz-border-radius-bottomright:         .6em /*{global-radii-blocks}*/;
+       -webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+       border-bottom-right-radius:             .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-top {
+       -moz-border-radius-topleft:             .6em /*{global-radii-blocks}*/;
+       -webkit-border-top-left-radius:         .6em /*{global-radii-blocks}*/;
+       border-top-left-radius:                         .6em /*{global-radii-blocks}*/;
+       -moz-border-radius-topright:            .6em /*{global-radii-blocks}*/;
+       -webkit-border-top-right-radius:        .6em /*{global-radii-blocks}*/;
+       border-top-right-radius:                        .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-bottom {
+       -moz-border-radius-bottomleft:          .6em /*{global-radii-blocks}*/;
+       -webkit-border-bottom-left-radius:      .6em /*{global-radii-blocks}*/;
+       border-bottom-left-radius:                      .6em /*{global-radii-blocks}*/;
+       -moz-border-radius-bottomright:         .6em /*{global-radii-blocks}*/;
+       -webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+       border-bottom-right-radius:             .6em /*{global-radii-blocks}*/;
+       }
+.ui-corner-right {
+       -moz-border-radius-topright:            .6em /*{global-radii-blocks}*/;
+       -webkit-border-top-right-radius:        .6em /*{global-radii-blocks}*/;
+       border-top-right-radius:                        .6em /*{global-radii-blocks}*/;
+       -moz-border-radius-bottomright:         .6em /*{global-radii-blocks}*/;
+       -webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/;
+       border-bottom-right-radius:             .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-left {
+       -moz-border-radius-topleft:             .6em /*{global-radii-blocks}*/;
+       -webkit-border-top-left-radius:         .6em /*{global-radii-blocks}*/;
+       border-top-left-radius:                         .6em /*{global-radii-blocks}*/;
+       -moz-border-radius-bottomleft:          .6em /*{global-radii-blocks}*/;
+       -webkit-border-bottom-left-radius:      .6em /*{global-radii-blocks}*/;
+       border-bottom-left-radius:                      .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-all {
+       -moz-border-radius:                             .6em /*{global-radii-blocks}*/;
+       -webkit-border-radius:                          .6em /*{global-radii-blocks}*/;
+       border-radius:                                          .6em /*{global-radii-blocks}*/;
+}
+.ui-corner-none {
+       -moz-border-radius:                                0;
+       -webkit-border-radius:                             0;
+       border-radius:                                             0;
+}
+/* Form field separator
+-----------------------------------------------------------------------------------------------------------*/
+.ui-br {
+       border-bottom: rgb(130,130,130);
+       border-bottom: rgba(130,130,130,.3);
+       border-bottom-width: 1px;
+       border-bottom-style: solid;
+}
+/* Interaction cues
+-----------------------------------------------------------------------------------------------------------*/
+.ui-disabled {
+       opacity:                                                        .3;
+}
+.ui-disabled,
+.ui-disabled a {
+       cursor: default !important;
+       pointer-events: none;
+}
+.ui-disabled .ui-btn-text {
+       -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(opacity=30)";
+       filter: alpha(opacity=30);
+       zoom: 1;
+}
+/* Icons
+-----------------------------------------------------------------------------------------------------------*/
+.ui-icon,
+.ui-icon-searchfield:after {
+       background:                                             #666 /*{global-icon-color}*/;
+       background:                                             rgba(0,0,0,.4) /*{global-icon-disc}*/;
+       background-image: url(../images/icons-18-white.png) /*{global-icon-set}*/;
+       background-repeat: no-repeat;
+       -moz-border-radius:                             9px;
+       -webkit-border-radius:                          9px;
+       border-radius:                                          9px;
+}
+/* Alt icon color
+-----------------------------------------------------------------------------------------------------------*/
+.ui-icon-alt {
+       background:                                             #fff;
+       background:                                             rgba(255,255,255,.3);
+       background-image: url(images/icons-18-black.png);
+       background-repeat: no-repeat;
+}
+/* HD/"retina" sprite
+-----------------------------------------------------------------------------------------------------------*/
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
+       only screen and (min--moz-device-pixel-ratio: 1.5),
+       only screen and (min-resolution: 240dpi) {
+       
+       .ui-icon-plus, .ui-icon-minus, .ui-icon-delete, .ui-icon-arrow-r,
+       .ui-icon-arrow-l, .ui-icon-arrow-u, .ui-icon-arrow-d, .ui-icon-check,
+       .ui-icon-gear, .ui-icon-refresh, .ui-icon-forward, .ui-icon-back,
+       .ui-icon-grid, .ui-icon-star, .ui-icon-alert, .ui-icon-info, .ui-icon-home, .ui-icon-search, .ui-icon-searchfield:after, 
+       .ui-icon-checkbox-off, .ui-icon-checkbox-on, .ui-icon-radio-off, .ui-icon-radio-on {
+               background-image: url(images/icons-36-white.png);
+               -moz-background-size: 776px 18px;
+               -o-background-size: 776px 18px;
+               -webkit-background-size: 776px 18px;
+               background-size: 776px 18px;
+       }
+       .ui-icon-alt {
+               background-image: url(images/icons-36-black.png);
+       }
+}
+/* plus minus */
+.ui-icon-plus {
+       background-position:    -0 50%;
+}
+.ui-icon-minus {
+       background-position:    -36px 50%;
+}
+/* delete/close */
+.ui-icon-delete {
+       background-position:    -72px 50%;
+}
+/* arrows */
+.ui-icon-arrow-r {
+       background-position:    -108px 50%;
+}
+.ui-icon-arrow-l {
+       background-position:    -144px 50%;
+}
+.ui-icon-arrow-u {
+       background-position:    -180px 50%;
+}
+.ui-icon-arrow-d {
+       background-position:    -216px 50%;
+}
+/* misc */
+.ui-icon-check {
+       background-position:    -252px 50%;
+}
+.ui-icon-gear {
+       background-position:    -288px 50%;
+}
+.ui-icon-refresh {
+       background-position:    -324px 50%;
+}
+.ui-icon-forward {
+       background-position:    -360px 50%;
+}
+.ui-icon-back {
+       background-position:    -396px 50%;
+}
+.ui-icon-grid {
+       background-position:    -432px 50%;
+}
+.ui-icon-star {
+       background-position:    -468px 50%;
+}
+.ui-icon-alert {
+       background-position:    -504px 50%;
+}
+.ui-icon-info {
+       background-position:    -540px 50%;
+}
+.ui-icon-home {
+       background-position:    -576px 50%;
+}
+.ui-icon-search,
+.ui-icon-searchfield:after {
+       background-position:    -612px 50%;
+}
+.ui-icon-checkbox-off {
+       background-position:    -684px 50%;
+}
+.ui-icon-checkbox-on {
+       background-position:    -648px 50%;
+}
+.ui-icon-radio-off {
+       background-position:    -756px 50%;
+}
+.ui-icon-radio-on {
+       background-position:    -720px 50%;
+}
+/* checks,radios */
+.ui-checkbox .ui-icon {
+       -moz-border-radius: 3px;
+       -webkit-border-radius: 3px;
+       border-radius: 3px;
+}
+.ui-icon-checkbox-off,
+.ui-icon-radio-off {
+       background-color: transparent;  
+}
+.ui-checkbox-on .ui-icon,
+.ui-radio-on .ui-icon {
+       background-color: #4596ce /*{global-active-background-color}*/; /* NOTE: this hex should match the active state color. It's repeated here for cascade */
+}
+/* loading icon */
+.ui-icon-loading {
+       background: url(images/ajax-loader.gif);
+       background-size: 46px 46px;
+}
+/* Button corner classes
+-----------------------------------------------------------------------------------------------------------*/
+.ui-btn-corner-tl {
+       -moz-border-radius-topleft:             1em /*{global-radii-buttons}*/;
+       -webkit-border-top-left-radius:         1em /*{global-radii-buttons}*/;
+       border-top-left-radius:                         1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-tr {
+       -moz-border-radius-topright:            1em /*{global-radii-buttons}*/;
+       -webkit-border-top-right-radius:        1em /*{global-radii-buttons}*/;
+       border-top-right-radius:                        1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-bl {
+       -moz-border-radius-bottomleft:          1em /*{global-radii-buttons}*/;
+       -webkit-border-bottom-left-radius:      1em /*{global-radii-buttons}*/;
+       border-bottom-left-radius:                      1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-br {
+       -moz-border-radius-bottomright:         1em /*{global-radii-buttons}*/;
+       -webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+       border-bottom-right-radius:             1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-top {
+       -moz-border-radius-topleft:             1em /*{global-radii-buttons}*/;
+       -webkit-border-top-left-radius:         1em /*{global-radii-buttons}*/;
+       border-top-left-radius:                         1em /*{global-radii-buttons}*/;
+       -moz-border-radius-topright:            1em /*{global-radii-buttons}*/;
+       -webkit-border-top-right-radius:        1em /*{global-radii-buttons}*/;
+       border-top-right-radius:                        1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-bottom {
+       -moz-border-radius-bottomleft:          1em /*{global-radii-buttons}*/;
+       -webkit-border-bottom-left-radius:      1em /*{global-radii-buttons}*/;
+       border-bottom-left-radius:                      1em /*{global-radii-buttons}*/;
+       -moz-border-radius-bottomright:         1em /*{global-radii-buttons}*/;
+       -webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+       border-bottom-right-radius:             1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-right {
+        -moz-border-radius-topright:           1em /*{global-radii-buttons}*/;
+       -webkit-border-top-right-radius:        1em /*{global-radii-buttons}*/;
+       border-top-right-radius:                        1em /*{global-radii-buttons}*/;
+       -moz-border-radius-bottomright:         1em /*{global-radii-buttons}*/;
+       -webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/;
+       border-bottom-right-radius:             1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-left {
+       -moz-border-radius-topleft:             1em /*{global-radii-buttons}*/;
+       -webkit-border-top-left-radius:         1em /*{global-radii-buttons}*/;
+       border-top-left-radius:                         1em /*{global-radii-buttons}*/;
+       -moz-border-radius-bottomleft:          1em /*{global-radii-buttons}*/;
+       -webkit-border-bottom-left-radius:      1em /*{global-radii-buttons}*/;
+       border-bottom-left-radius:                      1em /*{global-radii-buttons}*/;
+}
+.ui-btn-corner-all {
+       -moz-border-radius:                             1em /*{global-radii-buttons}*/;
+       -webkit-border-radius:                          1em /*{global-radii-buttons}*/;
+       border-radius:                                          1em /*{global-radii-buttons}*/;
+}
+/* radius clip workaround for cleaning up corner trapping */
+.ui-corner-tl,
+.ui-corner-tr,
+.ui-corner-bl, 
+.ui-corner-br,
+.ui-corner-top,
+.ui-corner-bottom, 
+.ui-corner-right,
+.ui-corner-left,
+.ui-corner-all,
+.ui-btn-corner-tl,
+.ui-btn-corner-tr,
+.ui-btn-corner-bl, 
+.ui-btn-corner-br,
+.ui-btn-corner-top,
+.ui-btn-corner-bottom, 
+.ui-btn-corner-right,
+.ui-btn-corner-left,
+.ui-btn-corner-all {
+  -webkit-background-clip: padding-box;
+     -moz-background-clip: padding;
+          background-clip: padding-box;
+}
+/* Overlay / modal
+-----------------------------------------------------------------------------------------------------------*/
+.ui-overlay {
+       background: #666;
+       opacity: .5;
+       filter: Alpha(Opacity=50);
+       position: absolute;
+       width: 100%;
+       height: 100%;
+}
+.ui-overlay-shadow {
+       -moz-box-shadow: 0px 0px 12px                   rgba(0,0,0,.6);
+       -webkit-box-shadow: 0px 0px 12px                rgba(0,0,0,.6);
+       box-shadow: 0px 0px 12px                                rgba(0,0,0,.6);
+}
+.ui-shadow {
+       -moz-box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/                       rgba(0,0,0,.3) /*{global-box-shadow-color}*/;
+       -webkit-box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/            rgba(0,0,0,.3) /*{global-box-shadow-color}*/;
+       box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/                            rgba(0,0,0,.3) /*{global-box-shadow-color}*/;
+}
+.ui-bar-a .ui-shadow,
+.ui-bar-b .ui-shadow ,
+.ui-bar-c .ui-shadow  {
+       -moz-box-shadow: 0px 1px 0                              rgba(255,255,255,.3);
+       -webkit-box-shadow: 0px 1px 0                   rgba(255,255,255,.3);
+       box-shadow: 0px 1px 0                                   rgba(255,255,255,.3);
+}
+.ui-shadow-inset {
+       -moz-box-shadow: inset 0px 1px 4px              rgba(0,0,0,.2);
+       -webkit-box-shadow: inset 0px 1px 4px   rgba(0,0,0,.2);
+       box-shadow: inset 0px 1px 4px                   rgba(0,0,0,.2);
+}
+.ui-icon-shadow {
+       -moz-box-shadow: 0px 1px 0                              rgba(255,255,255,.4) /*{global-icon-shadow}*/;
+       -webkit-box-shadow: 0px 1px 0                   rgba(255,255,255,.4) /*{global-icon-shadow}*/;
+       box-shadow: 0px 1px 0                                   rgba(255,255,255,.4) /*{global-icon-shadow}*/;
+}
+/* Focus state - set here for specificity (note: these classes are added by JavaScript)
+-----------------------------------------------------------------------------------------------------------*/
+.ui-btn:focus, .ui-link-inherit:focus {
+       outline: 0;
+}
+.ui-btn.ui-focus {
+       z-index: 1;
+}
+.ui-focus,
+.ui-btn:focus {
+       -moz-box-shadow: inset 0px 0px 3px              #387bbe /*{global-active-background-color}*/, 0px 0px 9px               #387bbe /*{global-active-background-color}*/;
+       -webkit-box-shadow: inset 0px 0px 3px   #387bbe /*{global-active-background-color}*/, 0px 0px 9px               #387bbe /*{global-active-background-color}*/;
+       box-shadow: inset 0px 0px 3px                   #387bbe /*{global-active-background-color}*/, 0px 0px 9px               #387bbe /*{global-active-background-color}*/;
+}
+.ui-input-text.ui-focus,
+.ui-input-search.ui-focus {
+       -moz-box-shadow: 0px 0px 12px                   #387bbe /*{global-active-background-color}*/;
+       -webkit-box-shadow: 0px 0px 12px                #387bbe /*{global-active-background-color}*/;
+       box-shadow: 0px 0px 12px                                        #387bbe /*{global-active-background-color}*/;   
+}
+/* unset box shadow in browsers that don't do it right
+-----------------------------------------------------------------------------------------------------------*/
+.ui-mobile-nosupport-boxshadow * {
+       -moz-box-shadow: none !important;
+       -webkit-box-shadow: none !important;
+       box-shadow: none !important;
+}
+/* ...and bring back focus */
+.ui-mobile-nosupport-boxshadow .ui-focus,
+.ui-mobile-nosupport-boxshadow .ui-btn:focus,
+.ui-mobile-nosupport-boxshadow .ui-link-inherit:focus {
+       outline-width: 1px;
+       outline-style: auto;
+}
+/* some unsets - more probably needed */
+.ui-mobile, .ui-mobile body { height: 99.9%; }
+.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; }
+.ui-mobile a img, .ui-mobile fieldset { border-width: 0; }
+/* responsive page widths */
+.ui-mobile-viewport {  margin: 0; overflow-x: visible; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
+/* Issue #2066 */
+body.ui-mobile-viewport,
+div.ui-mobile-viewport { overflow-x: hidden; }
+/* "page" containers - full-screen views, one should always be in view post-pageload */
+.ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; }
+.ui-mobile .ui-page-active { display: block; overflow: visible; }
+/* on ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */
+.ui-page { outline: none; }
+/*orientations from js are available */
+@media screen and (orientation: portrait){
+.ui-mobile, .ui-mobile .ui-page { min-height: 420px; }
+}
+@media screen and (orientation: landscape){
+.ui-mobile, .ui-mobile .ui-page { min-height: 300px; }
+}
+/* loading screen */
+.ui-loading .ui-loader { display: block; }
+.ui-loader { display: none; z-index: 9999999; position: fixed; top: 50%; left: 50%; border:0; }
+.ui-loader-default { background: none; opacity: .18; width: 46px; height: 46px; margin-left: -23px; margin-top: -23px; }
+.ui-loader-verbose { width: 200px; opacity: .88; box-shadow: 0 1px 1px -1px #fff; height: auto; margin-left: -110px; margin-top: -43px; padding: 10px; }
+.ui-loader-default h1 { font-size: 0; width: 0; height: 0; overflow: hidden; }
+.ui-loader-verbose h1 { font-size: 16px; margin: 0; text-align: center; }
+.ui-loader .ui-icon { background-color: #000; display: block; margin: 0; width: 44px; height: 44px; padding: 1px; -webkit-border-radius: 36px; -moz-border-radius: 36px; border-radius: 36px; }
+.ui-loader-verbose .ui-icon { margin: 0 auto 10px; opacity: .75; }
+.ui-loader-textonly { padding: 15px; margin-left: -115px; }
+.ui-loader-textonly .ui-icon { display: none; }
+.ui-loader-fakefix { position: absolute; }
+/*fouc*/
+.ui-mobile-rendering > * { visibility: hidden; }
+/*headers, content panels*/
+.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; }
+.ui-bar { font-size: 16px; margin: 0; }
+.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; }
+.ui-header, .ui-footer { position: relative; border-left-width: 0; border-right-width: 0; zoom: 1; }
+.ui-header .ui-btn-left,
+.ui-header .ui-btn-right,
+.ui-footer .ui-btn-left,
+.ui-footer .ui-btn-right { position: absolute; top: 3px; }
+.ui-header .ui-btn-left,
+.ui-footer .ui-btn-left { left: 5px; }
+.ui-header .ui-btn-right,
+.ui-footer .ui-btn-right { right: 5px; }
+.ui-footer .ui-btn-icon-notext,
+.ui-header .ui-btn-icon-notext { top: 6px; }
+.ui-header .ui-title, .ui-footer .ui-title { min-height: 1.1em; text-align: center; font-size: 16px; display: block; margin: .6em 30% .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; }
+.ui-footer .ui-title { margin: .6em 15px .8em; }
+/*content area*/
+.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; }
+/* icons sizing */
+.ui-icon { width: 18px; height: 18px; }
+/* non-js content hiding */
+.ui-nojs { position: absolute; left: -9999px; }
+/* accessible content hiding */
+.ui-hide-label label.ui-input-text, .ui-hide-label label.ui-select, .ui-hide-label label.ui-slider, .ui-hide-label label.ui-submit, .ui-hide-label .ui-controlgroup-label,
+.ui-hidden-accessible { position: absolute !important; left: -9999px; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+/* Transitions originally inspired by those from jQtouch, nice work, folks */
+.ui-mobile-viewport-transitioning,
+.ui-mobile-viewport-transitioning .ui-page {
+       width: 100%;
+       height: 100%;
+       overflow: hidden;
+       -webkit-box-sizing: border-box;
+       -moz-box-sizing: border-box;
+       box-sizing: border-box;
+}
+.in {
+       -webkit-animation-timing-function: ease-out;
+       -webkit-animation-duration: 350ms;
+       -moz-animation-timing-function: ease-out;
+       -moz-animation-duration: 350ms;
+}
+.out {
+       -webkit-animation-timing-function: ease-in;
+       -webkit-animation-duration: 225ms;
+       -moz-animation-timing-function: ease-in;
+       -moz-animation-duration: 225;
+}
+@-webkit-keyframes fadein {
+    from { opacity: 0; }
+    to { opacity: 1; }
+}
+@-moz-keyframes fadein {
+    from { opacity: 0; }
+    to { opacity: 1; }
+}
+@-webkit-keyframes fadeout {
+    from { opacity: 1; }
+    to { opacity: 0; }
+}
+@-moz-keyframes fadeout {
+    from { opacity: 1; }
+    to { opacity: 0; }
+}
+.fade.out {
+       opacity: 0;
+       -webkit-animation-duration: 125ms;
+       -webkit-animation-name: fadeout;
+       -moz-animation-duration: 125ms;
+       -moz-animation-name: fadeout;
+}
+.fade.in {
+       opacity: 1;
+       -webkit-animation-duration: 225ms;
+       -webkit-animation-name: fadein;
+       -moz-animation-duration: 225ms;
+       -moz-animation-name: fadein;
+}
+.pop {
+       -webkit-transform-origin: 50% 50%;
+       -moz-transform-origin: 50% 50%;
+}
+.pop.in {
+       -webkit-transform: scale(1);
+       -moz-transform: scale(1);
+    opacity: 1;
+       -webkit-animation-name: popin;
+       -moz-animation-name: popin;
+       -webkit-animation-duration: 350ms;
+       -moz-animation-duration: 350ms;
+}
+.pop.out {
+       -webkit-animation-name: fadeout;
+       -moz-animation-name: fadeout;
+       opacity: 0;
+       -webkit-animation-duration: 100ms;
+       -moz-animation-duration: 100ms;
+}
+.pop.in.reverse {
+       -webkit-animation-name: fadein;
+       -moz-animation-name: fadein;
+}
+.pop.out.reverse {
+       -webkit-transform: scale(.8);
+       -moz-transform: scale(.8);
+       -webkit-animation-name: popout;
+       -moz-animation-name: popout;
+}
+@-webkit-keyframes popin {
+    from {
+        -webkit-transform: scale(.8);
+        opacity: 0;
+    }
+    to {
+        -webkit-transform: scale(1);
+        opacity: 1;
+    }
+}
+@-moz-keyframes popin {
+    from {
+        -moz-transform: scale(.8);
+        opacity: 0;
+    }
+    to {
+        -moz-transform: scale(1);
+        opacity: 1;
+    }
+}
+@-webkit-keyframes popout {
+    from {
+        -webkit-transform: scale(1);
+        opacity: 1;
+    }
+    to {
+        -webkit-transform: scale(.8);
+        opacity: 0;
+    }
+}
+@-moz-keyframes popout {
+    from {
+        -moz-transform: scale(1);
+        opacity: 1;
+    }
+    to {
+        -moz-transform: scale(.8);
+        opacity: 0;
+    }
+}
+/* keyframes for slidein from sides */
+@-webkit-keyframes slideinfromright {
+    from { -webkit-transform: translateX(100%); }
+    to { -webkit-transform: translateX(0); }
+}
+@-moz-keyframes slideinfromright {
+    from { -moz-transform: translateX(100%); }
+    to { -moz-transform: translateX(0); }
+}
+@-webkit-keyframes slideinfromleft {
+    from { -webkit-transform: translateX(-100%); }
+    to { -webkit-transform: translateX(0); }
+}
+@-moz-keyframes slideinfromleft {
+    from { -moz-transform: translateX(-100%); }
+    to { -moz-transform: translateX(0); }
+}
+/* keyframes for slideout to sides */
+@-webkit-keyframes slideouttoleft {
+    from { -webkit-transform: translateX(0); }
+    to { -webkit-transform: translateX(-100%); }
+}
+@-moz-keyframes slideouttoleft {
+    from { -moz-transform: translateX(0); }
+    to { -moz-transform: translateX(-100%); }
+}
+@-webkit-keyframes slideouttoright {
+    from { -webkit-transform: translateX(0); }
+    to { -webkit-transform: translateX(100%); }
+}
+@-moz-keyframes slideouttoright {
+    from { -moz-transform: translateX(0); }
+    to { -moz-transform: translateX(100%); }
+}
+.slide.out, .slide.in {
+       -webkit-animation-timing-function: ease-out;
+       -webkit-animation-duration: 350ms;
+       -moz-animation-timing-function: ease-out;
+       -moz-animation-duration: 350ms;
+}
+.slide.out {
+       -webkit-transform: translateX(-100%);
+       -webkit-animation-name: slideouttoleft;
+       -moz-transform: translateX(-100%);
+       -moz-animation-name: slideouttoleft;
+}
+.slide.in {
+       -webkit-transform: translateX(0);
+       -webkit-animation-name: slideinfromright;
+       -moz-transform: translateX(0);
+       -moz-animation-name: slideinfromright;
+}
+.slide.out.reverse {
+       -webkit-transform: translateX(100%);
+       -webkit-animation-name: slideouttoright;
+       -moz-transform: translateX(100%);
+       -moz-animation-name: slideouttoright;
+}
+.slide.in.reverse {
+       -webkit-transform: translateX(0);
+       -webkit-animation-name: slideinfromleft;
+       -moz-transform: translateX(0);
+       -moz-animation-name: slideinfromleft;
+}
+.slidefade.out {
+       -webkit-transform: translateX(-100%);
+       -webkit-animation-name: slideouttoleft;
+       -moz-transform: translateX(-100%);
+       -moz-animation-name: slideouttoleft;
+       -webkit-animation-duration: 225ms;
+       -moz-animation-duration: 225ms;
+}
+.slidefade.in {
+       -webkit-transform: translateX(0);
+       -webkit-animation-name: fadein;
+       -moz-transform: translateX(0);
+       -moz-animation-name: fadein;
+       -webkit-animation-duration: 200ms;
+       -moz-animation-duration: 200ms;
+}
+.slidefade.out.reverse {
+       -webkit-transform: translateX(100%);
+       -webkit-animation-name: slideouttoright;
+       -moz-transform: translateX(100%);
+       -moz-animation-name: slideouttoright;
+       -webkit-animation-duration: 200ms;
+       -moz-animation-duration: 200ms;
+}
+.slidefade.in.reverse {
+       -webkit-transform: translateX(0);
+       -webkit-animation-name: fadein;
+       -moz-transform: translateX(0);
+       -moz-animation-name: fadein;
+       -webkit-animation-duration: 200ms;
+       -moz-animation-duration: 200ms;
+}
+/* slide down */
+.slidedown.out {
+       -webkit-animation-name: fadeout;
+       -moz-animation-name: fadeout;
+       -webkit-animation-duration: 100ms;
+       -moz-animation-duration: 100ms;
+}
+.slidedown.in {
+       -webkit-transform: translateY(0);
+       -webkit-animation-name: slideinfromtop;
+       -moz-transform: translateY(0);
+       -moz-animation-name: slideinfromtop;
+       -webkit-animation-duration: 250ms;
+       -moz-animation-duration: 250ms;
+}
+.slidedown.in.reverse {
+       -webkit-animation-name: fadein;
+       -moz-animation-name: fadein;
+       -webkit-animation-duration: 150ms;
+       -moz-animation-duration: 150ms;
+}
+.slidedown.out.reverse {
+       -webkit-transform: translateY(-100%);
+       -moz-transform: translateY(-100%);
+       -webkit-animation-name: slideouttotop;
+       -moz-animation-name: slideouttotop;
+       -webkit-animation-duration: 200ms;
+       -moz-animation-duration: 200ms;
+}
+@-webkit-keyframes slideinfromtop {
+    from { -webkit-transform: translateY(-100%); }
+    to { -webkit-transform: translateY(0); }
+}
+@-moz-keyframes slideinfromtop {
+    from { -moz-transform: translateY(-100%); }
+    to { -moz-transform: translateY(0); }
+}
+@-webkit-keyframes slideouttotop {
+    from { -webkit-transform: translateY(0); }
+    to { -webkit-transform: translateY(-100%); }
+}
+@-moz-keyframes slideouttotop {
+    from { -moz-transform: translateY(0); }
+    to { -moz-transform: translateY(-100%); }
+}
+/* slide up */
+.slideup.out {
+       -webkit-animation-name: fadeout;
+       -moz-animation-name: fadeout;
+       -webkit-animation-duration: 100ms;
+       -moz-animation-duration: 100ms;
+}
+.slideup.in {
+       -webkit-transform: translateY(0);
+       -webkit-animation-name: slideinfrombottom;
+       -moz-transform: translateY(0);
+       -moz-animation-name: slideinfrombottom;
+       -webkit-animation-duration: 250ms;
+       -moz-animation-duration: 250ms;
+}
+.slideup.in.reverse {
+       -webkit-animation-name: fadein;
+       -moz-animation-name: fadein;
+       -webkit-animation-duration: 150ms;
+       -moz-animation-duration: 150ms;
+}
+.slideup.out.reverse {
+       -webkit-transform: translateY(100%);
+       -moz-transform: translateY(100%);
+       -webkit-animation-name: slideouttobottom;
+       -moz-animation-name: slideouttobottom;
+       -webkit-animation-duration: 200ms;
+       -moz-animation-duration: 200ms;
+}
+@-webkit-keyframes slideinfrombottom {
+    from { -webkit-transform: translateY(100%); }
+    to { -webkit-transform: translateY(0); }
+}
+@-moz-keyframes slideinfrombottom {
+    from { -moz-transform: translateY(100%); }
+    to { -moz-transform: translateY(0); }
+}
+@-webkit-keyframes slideouttobottom {
+    from { -webkit-transform: translateY(0); }
+    to { -webkit-transform: translateY(100%); }
+}
+@-moz-keyframes slideouttobottom {
+    from { -moz-transform: translateY(0); }
+    to { -moz-transform: translateY(100%); }
+}
+/* The properties in this rule are only necessary for the 'flip' transition.
+ * We need specify the perspective to create a projection matrix. This will add
+ * some depth as the element flips. The depth number represents the distance of
+ * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate
+ * value.
+ */
+.viewport-flip {
+       -webkit-perspective: 1000;
+       -moz-perspective: 1000;
+       position: absolute;
+}
+.flip {
+       -webkit-backface-visibility:hidden;
+       -webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
+       -moz-backface-visibility:hidden;
+       -moz-transform:translateX(0);
+}
+.flip.out {
+       -webkit-transform: rotateY(-90deg) scale(.9);
+       -webkit-animation-name: flipouttoleft;
+       -webkit-animation-duration: 175ms;
+       -moz-transform: rotateY(-90deg) scale(.9);
+       -moz-animation-name: flipouttoleft;
+       -moz-animation-duration: 175ms;
+}
+.flip.in {
+       -webkit-animation-name: flipintoright;
+       -webkit-animation-duration: 225ms;
+       -moz-animation-name: flipintoright;
+       -moz-animation-duration: 225ms;
+}
+.flip.out.reverse {
+       -webkit-transform: rotateY(90deg) scale(.9);
+       -webkit-animation-name: flipouttoright;
+       -moz-transform: rotateY(90deg) scale(.9);
+       -moz-animation-name: flipouttoright;
+}
+.flip.in.reverse {
+       -webkit-animation-name: flipintoleft;
+       -moz-animation-name: flipintoleft;
+}
+@-webkit-keyframes flipouttoleft {
+    from { -webkit-transform: rotateY(0); }
+    to { -webkit-transform: rotateY(-90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoleft {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(-90deg) scale(.9); }
+}
+@-webkit-keyframes flipouttoright {
+    from { -webkit-transform: rotateY(0) ; }
+    to { -webkit-transform: rotateY(90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoright {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(90deg) scale(.9); }
+}
+@-webkit-keyframes flipintoleft {
+    from { -webkit-transform: rotateY(-90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoleft {
+    from { -moz-transform: rotateY(-90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+@-webkit-keyframes flipintoright {
+    from { -webkit-transform: rotateY(90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoright {
+    from { -moz-transform: rotateY(90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+/* The properties in this rule are only necessary for the 'flip' transition.
+ * We need specify the perspective to create a projection matrix. This will add
+ * some depth as the element flips. The depth number represents the distance of
+ * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate
+ * value.
+ */
+.viewport-turn {
+       -webkit-perspective: 1000;
+       -moz-perspective: 1000;
+       position: absolute;
+}
+.turn {
+       -webkit-backface-visibility:hidden;
+       -webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
+       -webkit-transform-origin: 0;
+       
+       -moz-backface-visibility:hidden;
+       -moz-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
+       -moz-transform-origin: 0;
+}
+.turn.out {
+       -webkit-transform: rotateY(-90deg) scale(.9);
+       -webkit-animation-name: flipouttoleft;
+       -moz-transform: rotateY(-90deg) scale(.9);
+       -moz-animation-name: flipouttoleft;
+       -webkit-animation-duration: 125ms;
+       -moz-animation-duration: 125ms;
+}
+.turn.in {
+       -webkit-animation-name: flipintoright;
+       -moz-animation-name: flipintoright;
+       -webkit-animation-duration: 250ms;
+       -moz-animation-duration: 250ms;
+       
+}
+.turn.out.reverse {
+       -webkit-transform: rotateY(90deg) scale(.9);
+       -webkit-animation-name: flipouttoright;
+       -moz-transform: rotateY(90deg) scale(.9);
+       -moz-animation-name: flipouttoright;
+}
+.turn.in.reverse {
+       -webkit-animation-name: flipintoleft;
+       -moz-animation-name: flipintoleft;
+}
+@-webkit-keyframes flipouttoleft {
+    from { -webkit-transform: rotateY(0); }
+    to { -webkit-transform: rotateY(-90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoleft {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(-90deg) scale(.9); }
+}
+@-webkit-keyframes flipouttoright {
+    from { -webkit-transform: rotateY(0) ; }
+    to { -webkit-transform: rotateY(90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoright {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(90deg) scale(.9); }
+}
+@-webkit-keyframes flipintoleft {
+    from { -webkit-transform: rotateY(-90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoleft {
+    from { -moz-transform: rotateY(-90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+@-webkit-keyframes flipintoright {
+    from { -webkit-transform: rotateY(90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoright {
+    from { -moz-transform: rotateY(90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+/* flow transition */
+.flow {
+       -webkit-transform-origin: 50% 30%;
+       -moz-transform-origin: 50% 30%; 
+       -webkit-box-shadow: 0 0 20px rgba(0,0,0,.4);
+       -moz-box-shadow: 0 0 20px rgba(0,0,0,.4);
+}
+.ui-dialog.flow {
+       -webkit-transform-origin: none;
+       -moz-transform-origin: none;    
+       -webkit-box-shadow: none;
+       -moz-box-shadow: none;
+}
+.flow.out {
+       -webkit-transform: translateX(-100%) scale(.7);
+       -webkit-animation-name: flowouttoleft;
+       -webkit-animation-timing-function: ease;
+       -webkit-animation-duration: 350ms;
+       -moz-transform: translateX(-100%) scale(.7);
+       -moz-animation-name: flowouttoleft;
+       -moz-animation-timing-function: ease;
+       -moz-animation-duration: 350ms;
+}
+.flow.in {
+       -webkit-transform: translateX(0) scale(1);
+       -webkit-animation-name: flowinfromright;
+       -webkit-animation-timing-function: ease;
+       -webkit-animation-duration: 350ms;
+       -moz-transform: translateX(0) scale(1);
+       -moz-animation-name: flowinfromright;
+       -moz-animation-timing-function: ease;
+       -moz-animation-duration: 350ms;
+}
+.flow.out.reverse {
+       -webkit-transform: translateX(100%);
+       -webkit-animation-name: flowouttoright;
+       -moz-transform: translateX(100%);
+       -moz-animation-name: flowouttoright;
+}
+.flow.in.reverse {
+       -webkit-animation-name: flowinfromleft;
+       -moz-animation-name: flowinfromleft;
+}
+@-webkit-keyframes flowouttoleft {
+    0% { -webkit-transform: translateX(0) scale(1); }
+       60%, 70% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform: translateX(-100%) scale(.7); }
+}
+@-moz-keyframes flowouttoleft {
+    0% { -moz-transform: translateX(0) scale(1); }
+       60%, 70% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform:  translateX(-100%) scale(.7); }
+}
+@-webkit-keyframes flowouttoright {
+    0% { -webkit-transform: translateX(0) scale(1); }
+       60%, 70% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform:  translateX(100%) scale(.7); }
+}
+@-moz-keyframes flowouttoright {
+    0% { -moz-transform: translateX(0) scale(1); }
+       60%, 70% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform:  translateX(100%) scale(.7); }
+}
+@-webkit-keyframes flowinfromleft {
+    0% { -webkit-transform: translateX(-100%) scale(.7); }
+       30%, 40% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform: translateX(0) scale(1); }
+}
+@-moz-keyframes flowinfromleft {
+    0% { -moz-transform: translateX(-100%) scale(.7); }
+       30%, 40% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform: translateX(0) scale(1); }
+}
+@-webkit-keyframes flowinfromright {
+    0% { -webkit-transform: translateX(100%) scale(.7); }
+       30%, 40% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform: translateX(0) scale(1); }
+}
+@-moz-keyframes flowinfromright {
+    0% { -moz-transform: translateX(100%) scale(.7); }
+       30%, 40% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform: translateX(0) scale(1); }
+}
+/* content configurations. */
+.ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d { overflow: hidden; }
+.ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height: 1px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; }
+/* grid solo: 100 - single item fallback */
+.ui-grid-solo .ui-block-a { display: block; float: none; }
+/* Lower percentages for older browsers (i.e. IE7) to prevent wrapping. -.5px to fix BB5 wrap issue. */
+/* grid a: 50/50 */
+.ui-grid-a .ui-block-a, .ui-grid-a .ui-block-b { width: 49.95%; }
+.ui-grid-a > :nth-child(n) { width: 50%; margin-right: -.5px; }
+.ui-grid-a .ui-block-a { clear: left; padding-right:3px;}
+/* grid b: 33/33/33 */
+.ui-grid-b .ui-block-a, .ui-grid-b .ui-block-b, .ui-grid-b .ui-block-c { width: 33.25%; }
+.ui-grid-b > :nth-child(n) { width: 33.333%; margin-right: -.5px; }
+.ui-grid-b .ui-block-a { clear: left; }
+/* grid c: 25/25/25/25 */
+.ui-grid-c .ui-block-a, .ui-grid-c .ui-block-b, .ui-grid-c .ui-block-c, .ui-grid-c .ui-block-d { width: 24.925%; }
+.ui-grid-c > :nth-child(n) { width: 25%; margin-right: -.5px; }
+.ui-grid-c .ui-block-a { clear: left; }
+/* grid d: 20/20/20/20/20 */
+.ui-grid-d .ui-block-a, .ui-grid-d .ui-block-b, .ui-grid-d .ui-block-c, .ui-grid-d .ui-block-d, .ui-grid-d .ui-block-e { width: 19.925%; }
+.ui-grid-d > :nth-child(n) { width: 20%; }
+.ui-grid-d .ui-block-a { clear: left; }
+/* fixed page header & footer configuration */
+.ui-header-fixed,
+.ui-footer-fixed {
+       left: 0;
+       right: 0;
+       width: 100%;
+       position: fixed;
+       z-index: 1000;
+}
+.ui-page-pre-in {
+       opacity: 0;
+}
+.ui-header-fixed {
+       top: 0;
+}
+.ui-footer-fixed {
+       bottom: 0;
+}
+.ui-header-fullscreen,
+.ui-footer-fullscreen {
+       opacity: .9;
+}
+.ui-page-header-fixed {
+       padding-top: 2.6875em;
+}
+.ui-page-footer-fixed {
+       padding-bottom: 2.6875em;
+}
+.ui-page-header-fullscreen .ui-content,
+.ui-page-footer-fullscreen .ui-content {
+       padding: 0;
+}
+.ui-fixed-hidden {
+       position: absolute;
+}
+.ui-page-header-fullscreen .ui-fixed-hidden,
+.ui-page-footer-fullscreen .ui-fixed-hidden {
+       left: -99999em;
+}
+.ui-header-fixed .ui-btn,
+.ui-footer-fixed .ui-btn { 
+       z-index: 10;
+}
+.ui-navbar { max-width: 100%; }
+.ui-navbar ul { list-style:none; margin: 0; padding: 0; position: relative; display: block; border: 0; max-width: 100%; overflow:hidden; }
+.ui-navbar li .ui-btn { display: block; text-align: center; margin: 0 -1px 0 0; border-right-width: 0; }
+.ui-navbar li .ui-btn-icon-right .ui-icon { right: 6px; }
+/* add border if not in header/footer (full width) */
+.ui-navbar li:last-child .ui-btn,
+.ui-navbar .ui-grid-duo .ui-block-b .ui-btn { margin-right: 0; border-right-width: 1px; }
+.ui-header .ui-navbar li:last-child .ui-btn,
+.ui-footer .ui-navbar li:last-child .ui-btn,
+.ui-header .ui-navbar .ui-grid-duo .ui-block-b .ui-btn,
+.ui-footer .ui-navbar .ui-grid-duo .ui-block-b .ui-btn { margin-right: -1px; border-right-width: 0; }
+.ui-navbar .ui-grid-duo li.ui-block-a:last-child .ui-btn { margin-right: -1px; border-right-width: 1px; }
+.ui-header .ui-navbar li .ui-btn,
+.ui-footer .ui-navbar li .ui-btn { border-top-width: 0; border-bottom-width: 0; }
+/* fixing gaps caused by subpixel problem */
+.ui-header .ui-navbar .ui-grid-b li.ui-block-c .ui-btn,
+.ui-footer .ui-navbar .ui-grid-b li.ui-block-c .ui-btn { margin-right: -5px; }
+.ui-header .ui-navbar .ui-grid-c li.ui-block-d .ui-btn,
+.ui-footer .ui-navbar .ui-grid-c li.ui-block-d .ui-btn,
+.ui-header .ui-navbar .ui-grid-d li.ui-block-e .ui-btn,
+.ui-footer .ui-navbar .ui-grid-d li.ui-block-e .ui-btn { margin-right: -4px; }
+.ui-header .ui-navbar .ui-grid-b li.ui-block-c .ui-btn-icon-right .ui-icon,
+.ui-footer .ui-navbar .ui-grid-b li.ui-block-c .ui-btn-icon-right .ui-icon,
+.ui-header .ui-navbar .ui-grid-c li.ui-block-d .ui-btn-icon-right .ui-icon,
+.ui-footer .ui-navbar .ui-grid-c li.ui-block-d .ui-btn-icon-right .ui-icon,
+.ui-header .ui-navbar .ui-grid-d li.ui-block-e .ui-btn-icon-right .ui-icon,
+.ui-footer .ui-navbar .ui-grid-d li.ui-block-e .ui-btn-icon-right .ui-icon { right: 8px; }
+.ui-navbar li .ui-btn .ui-btn-inner { padding-top: .7em; padding-bottom: .8em }
+.ui-navbar li .ui-btn-icon-top .ui-btn-inner { padding-top: 30px; }
+.ui-navbar li .ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 30px; }
+.ui-btn { display: block; text-align: center; cursor:pointer; position: relative; margin: .5em 0; padding: 0; }
+.ui-btn.ui-mini { margin-top: .25em; margin-bottom: .25em; }
+.ui-btn-left, .ui-btn-right, .ui-input-clear, .ui-btn-inline,
+.ui-grid-a .ui-btn, .ui-grid-b .ui-btn, .ui-grid-c .ui-btn, .ui-grid-d .ui-btn, .ui-grid-e .ui-btn, .ui-grid-solo .ui-btn { margin-right: 5px; }
+.ui-btn-inner { font-size: 16px; padding: .6em 20px; min-width: .75em; display: block; position: relative; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; zoom: 1; }
+.ui-btn input, .ui-btn button { z-index: 2; }
+.ui-btn-left, .ui-btn-right, .ui-btn-inline { display: inline-block; vertical-align: middle; width:99%}
+.ui-btn-block { display: block; }
+.ui-header .ui-btn,
+.ui-footer .ui-btn { display: inline-block; margin: 0; }
+.ui-header .ui-btn-block,
+.ui-footer .ui-btn-block { display: block; }
+.ui-header .ui-btn-inner,
+.ui-footer .ui-btn-inner,
+.ui-mini .ui-btn-inner { font-size: 12.5px; padding: .55em 11px .5em; }
+.ui-header .ui-fullsize .ui-btn-inner,
+.ui-footer .ui-fullsize .ui-btn-inner { font-size: 16px; padding: .6em 25px; }
+.ui-btn-icon-notext { width: 24px; height: 24px; }
+.ui-btn-icon-notext .ui-btn-inner { padding: 0; height: 100%; }
+.ui-btn-icon-notext .ui-btn-inner .ui-icon { margin: 2px 1px 2px 3px; float: left; }
+.ui-btn-text { position: relative; z-index: 1; width: 100%; -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; }
+.ui-btn-icon-notext .ui-btn-text { position: absolute; left: -9999px; }
+.ui-btn-icon-left .ui-btn-inner { padding-left: 40px; }
+.ui-btn-icon-right .ui-btn-inner { padding-right: 40px; }
+.ui-btn-icon-top .ui-btn-inner { padding-top: 40px; }
+.ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 40px; }
+.ui-header .ui-btn-icon-left .ui-btn-inner,
+.ui-footer .ui-btn-icon-left .ui-btn-inner,
+.ui-mini.ui-btn-icon-left .ui-btn-inner,
+.ui-mini .ui-btn-icon-left .ui-btn-inner { padding-left: 30px; }
+.ui-header .ui-btn-icon-right .ui-btn-inner,
+.ui-footer .ui-btn-icon-right .ui-btn-inner,
+.ui-mini.ui-btn-icon-right .ui-btn-inner,
+.ui-mini .ui-btn-icon-right .ui-btn-inner { padding-right: 30px; }
+.ui-header .ui-btn-icon-top .ui-btn-inner,
+.ui-footer .ui-btn-icon-top .ui-btn-inner { padding: 30px 3px .5em 3px; }
+.ui-mini.ui-btn-icon-top .ui-btn-inner,
+.ui-mini .ui-btn-icon-top .ui-btn-inner { padding-top: 30px; }
+.ui-header .ui-btn-icon-bottom .ui-btn-inner,
+.ui-footer .ui-btn-icon-bottom .ui-btn-inner { padding: .55em 3px 30px 3px; }
+.ui-mini.ui-btn-icon-bottom .ui-btn-inner,
+.ui-mini .ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 30px; }
+/*btn icon positioning*/
+.ui-btn-icon-notext .ui-icon { display: block; z-index: 0;}
+.ui-btn-icon-left > .ui-btn-inner > .ui-icon, .ui-btn-icon-right > .ui-btn-inner > .ui-icon { position: absolute; top: 50%; margin-top: -9px; }
+.ui-btn-icon-top .ui-btn-inner .ui-icon, .ui-btn-icon-bottom .ui-btn-inner .ui-icon { position: absolute; left: 50%; margin-left: -9px; }
+.ui-btn-icon-left .ui-icon { left: 10px; }
+.ui-btn-icon-right .ui-icon { right: 10px; }
+.ui-btn-icon-top .ui-icon { top: 10px; }
+.ui-btn-icon-bottom .ui-icon { top: auto; bottom: 10px; }
+.ui-header .ui-btn-icon-left .ui-icon,
+.ui-footer .ui-btn-icon-left .ui-icon,
+.ui-mini.ui-btn-icon-left .ui-icon,
+.ui-mini .ui-btn-icon-left .ui-icon { left: 5px; }
+.ui-header .ui-btn-icon-right .ui-icon,
+.ui-footer .ui-btn-icon-right .ui-icon,
+.ui-mini.ui-btn-icon-right .ui-icon,
+.ui-mini .ui-btn-icon-right .ui-icon { right: 5px; }
+.ui-header .ui-btn-icon-top .ui-icon,
+.ui-footer .ui-btn-icon-top .ui-icon,
+.ui-mini.ui-btn-icon-top .ui-icon,
+.ui-mini .ui-btn-icon-top .ui-icon { top: 5px; }
+.ui-header .ui-btn-icon-bottom .ui-icon,
+.ui-footer .ui-btn-icon-bottom .ui-icon,
+.ui-mini.ui-btn-icon-bottom .ui-icon,
+.ui-mini .ui-btn-icon-bottom .ui-icon { bottom: 5px; }
+/*hiding native button,inputs */
+.ui-btn-hidden { position: absolute; top: 0; left: 0; width: 100%; height: 100%; -webkit-appearance: none; opacity: .1; cursor: pointer; background: #fff; background: rgba(255,255,255,0); filter: Alpha(Opacity=0); font-size: 1px; border: none; text-indent: -9999px; }
+.ui-field-contain .ui-btn.ui-submit { margin: 0; }
+label.ui-submit { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; }
+@media all and (min-width: 450px){
+       .ui-field-contain label.ui-submit { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
+       .ui-field-contain .ui-btn.ui-submit { width: 60%; display: inline-block; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; }
+       .ui-hide-label .ui-btn.ui-submit { width: auto; }
+}
+.ui-collapsible { margin: .5em 0; }
+.ui-collapsible-heading { font-size: 16px; display: block; margin: 0 -8px; padding: 0; border-width: 0 0 1px 0; position: relative; }
+.ui-collapsible-heading .ui-btn { text-align: left; margin: 0; }
+.ui-collapsible-heading .ui-btn-inner,
+.ui-collapsible-heading .ui-btn-icon-left .ui-btn-inner { padding-left: 40px; }
+.ui-collapsible-heading .ui-btn-icon-right .ui-btn-inner { padding-left: 12px; padding-right: 40px; }
+.ui-collapsible-heading .ui-btn-icon-top .ui-btn-inner,
+.ui-collapsible-heading .ui-btn-icon-bottom .ui-btn-inner { padding-right: 40px; text-align: center; }
+.ui-collapsible-heading .ui-btn span.ui-btn { position: absolute; left: 6px; top: 50%; margin: -12px 0 0 0; width: 20px; height: 20px; padding: 1px 0px 1px 2px; text-indent: -9999px; }
+.ui-collapsible-heading .ui-btn span.ui-btn .ui-btn-inner { padding: 10px 0; }
+.ui-collapsible-heading .ui-btn span.ui-btn .ui-icon { left: 0; margin-top: -10px; }
+.ui-collapsible-heading-status { position: absolute; top: -9999px; left:0px; }
+.ui-collapsible-content {
+       display: block;
+       margin:  0 -8px;
+       padding: 10px 16px;
+       border-top:  none;      /* Overrides ui-btn-up-* */
+       background-image: none; /* Overrides ui-btn-up-* */
+       font-weight: normal;    /* Overrides ui-btn-up-* */
+}
+.ui-collapsible-content-collapsed { display: none; }
+.ui-collapsible-set { margin: .5em 0; }
+.ui-collapsible-set .ui-collapsible { margin: -1px 0 0; }
+.ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0; zoom: 1; }
+.ui-controlgroup.ui-mini, fieldset.ui-controlgroup.ui-mini { margin: .25em 0; }
+.ui-field-contain .ui-controlgroup, .ui-field-contain fieldset.ui-controlgroup { margin: 0; }
+.ui-bar .ui-controlgroup { margin: 0 5px; }
+.ui-controlgroup-label { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .4em; }
+.ui-controlgroup-controls { display: block; width: 100%;}
+.ui-controlgroup li { list-style: none; }
+.ui-controlgroup-vertical .ui-btn,
+.ui-controlgroup-vertical .ui-checkbox, .ui-controlgroup-vertical .ui-radio { margin: 0; border-bottom-width: 0; }
+.ui-controlgroup-vertical .ui-controlgroup-last { border-bottom-width: 1px; }
+.ui-controlgroup-controls label.ui-select { position: absolute; left: -9999px; }
+.ui-controlgroup .ui-btn-icon-notext { width: 24px; height: 24px; }
+.ui-controlgroup .ui-btn-icon-notext .ui-btn-inner .ui-icon { position: absolute; top: 50%; right: 50%; margin: -9px -9px 0 0; }
+.ui-controlgroup-horizontal .ui-controlgroup-controls:before,
+.ui-controlgroup-horizontal .ui-controlgroup-controls:after { content: ""; display: table; }
+.ui-controlgroup-horizontal .ui-controlgroup-controls:after { clear: both; }
+.ui-controlgroup-horizontal .ui-controlgroup-controls { zoom: 1; }
+.ui-controlgroup-horizontal .ui-btn-inner { text-align: center; }
+.ui-controlgroup-horizontal .ui-btn, .ui-controlgroup-horizontal .ui-select,
+.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { float: left; clear: none; margin: 0 -1px 0 0; }
+.ui-controlgroup-horizontal .ui-select .ui-btn,
+.ui-controlgroup-horizontal .ui-checkbox .ui-btn, .ui-controlgroup-horizontal .ui-radio .ui-btn,
+.ui-controlgroup-horizontal .ui-checkbox:last-child, .ui-controlgroup-horizontal .ui-radio:last-child { margin-right: 0; }
+.ui-controlgroup-horizontal .ui-controlgroup-last { margin-right: 0; }
+.ui-controlgroup .ui-checkbox label, .ui-controlgroup .ui-radio label { font-size: 16px; }
+@media all and (min-width: 450px){
+       .ui-field-contain .ui-controlgroup-label { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
+       .ui-field-contain .ui-controlgroup-controls { width: 60%; display: inline-block; }
+       .ui-field-contain .ui-controlgroup .ui-select { width: 100%; display: block; } 
+       .ui-field-contain .ui-controlgroup-horizontal .ui-select { width: auto; }
+       .ui-hide-label .ui-controlgroup-controls { width: 100%; }
+}      
+.ui-dialog {
+        background: none !important; /* this is to ensure that dialog theming does not apply (by default at least) on the page div */
+}
+.ui-dialog-contain { width: 92.5%; max-width: 500px; margin: 10% auto 15px auto; padding: 0; }
+.ui-dialog .ui-header {
+       margin-top: 15%;
+       border: none;
+       overflow: hidden;
+}
+.ui-dialog .ui-header, 
+.ui-dialog .ui-content, 
+.ui-dialog .ui-footer { 
+       display: block;
+       position: relative; 
+       width: auto;
+}
+.ui-dialog .ui-header, 
+.ui-dialog .ui-footer  { 
+       z-index: 10; 
+       padding: 0;
+}
+.ui-dialog .ui-footer {
+       padding: 0 15px; 
+}
+.ui-dialog .ui-content { 
+       padding: 15px; 
+}
+.ui-dialog { 
+       margin-top: -15px;
+}
+.ui-checkbox, .ui-radio { position: relative; clear: both; margin: 0; z-index: 1; }
+.ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: .5em 0; text-align: left; z-index: 2; }
+.ui-checkbox .ui-btn.ui-mini, .ui-radio .ui-btn.ui-mini { margin: .25em 0; }
+.ui-controlgroup .ui-checkbox .ui-btn, .ui-controlgroup .ui-radio .ui-btn { margin: 0; }
+.ui-checkbox .ui-btn-inner, .ui-radio .ui-btn-inner { white-space: normal; }
+.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner { padding-left: 45px; }
+.ui-checkbox .ui-mini.ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-mini.ui-btn-icon-left .ui-btn-inner { padding-left: 36px; }
+.ui-checkbox .ui-btn-icon-right .ui-btn-inner, .ui-radio .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; }
+.ui-checkbox .ui-mini.ui-btn-icon-right .ui-btn-inner, .ui-radio .ui-mini.ui-btn-icon-right .ui-btn-inner { padding-right: 36px; }
+.ui-checkbox .ui-btn-icon-top .ui-btn-inner,.ui-radio .ui-btn-icon-top .ui-btn-inner { padding-right: 0; padding-left: 0; text-align: center; }
+.ui-checkbox .ui-btn-icon-bottom .ui-btn-inner, .ui-radio .ui-btn-icon-bottom .ui-btn-inner { padding-right: 0; padding-left: 0; text-align: center; }
+.ui-checkbox .ui-icon, .ui-radio .ui-icon { top: 1.1em; }
+.ui-checkbox .ui-btn-icon-left .ui-icon, .ui-radio .ui-btn-icon-left .ui-icon { left: 15px; }
+.ui-checkbox .ui-mini.ui-btn-icon-left .ui-icon, .ui-radio .ui-mini.ui-btn-icon-left .ui-icon { left: 9px; }
+.ui-checkbox .ui-btn-icon-right .ui-icon, .ui-radio .ui-btn-icon-right .ui-icon { right: 15px; }
+.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon, .ui-radio .ui-mini.ui-btn-icon-right .ui-icon { right: 9px; }
+.ui-checkbox .ui-btn-icon-top .ui-icon, .ui-radio .ui-btn-icon-top .ui-icon { top: 10px; }
+.ui-checkbox .ui-btn-icon-bottom .ui-icon, .ui-radio .ui-btn-icon-bottom .ui-icon { top: auto; bottom: 10px; }
+.ui-checkbox .ui-btn-icon-right .ui-icon, .ui-radio .ui-btn-icon-right .ui-icon { right: 15px; }
+.ui-checkbox .ui-mini.ui-btn-icon-right .ui-icon, .ui-radio .ui-mini.ui-btn-icon-right .ui-icon { right: 9px; }
+/* input, label positioning */
+.ui-checkbox input,.ui-radio input { position:absolute; left:20px; top:50%; width: 10px; height: 10px; margin:-5px 0 0 0; outline: 0 !important; z-index: 1; }
+.ui-field-contain, fieldset.ui-field-contain { padding: .8em 0; margin: 0; border-width: 0 0 1px 0; overflow: visible; }
+.ui-field-contain:last-child { border-bottom-width: 0; }
+.ui-field-contain { max-width: 100%; } /* This prevents horizontal scrollbar in IE7 */
+@media all and (min-width: 450px){
+       .ui-field-contain, .ui-mobile fieldset.ui-field-contain { border-width: 0; padding: 0; margin: 1em 0; }
+}
+.ui-select { display: block; position: relative; }
+.ui-select select { position: absolute; left: -9999px; top: -9999px; }
+.ui-select .ui-btn { overflow: hidden; opacity: 1; }
+.ui-field-contain .ui-select .ui-btn { margin: 0; }
+/* Fixes #2588 — When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select—including “inherit”—without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */
+.ui-select .ui-btn select { cursor: pointer; -webkit-appearance: none; left: 0; top:0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); z-index: 2; }
+.ui-select .ui-disabled { opacity: .3; }
+@-moz-document url-prefix() {.ui-select .ui-btn select { opacity: 0.0001; }}
+.ui-select .ui-btn.ui-select-nativeonly { border-radius: 0; }
+.ui-select .ui-btn.ui-select-nativeonly select { opacity: 1; text-indent: 0; }
+.ui-select .ui-btn-icon-right .ui-btn-inner, .ui-select .ui-li-has-count .ui-btn-inner { padding-right: 45px; }
+.ui-select .ui-mini.ui-btn-icon-right .ui-btn-inner { padding-right: 32px; }
+.ui-select .ui-btn-icon-right.ui-li-has-count .ui-btn-inner { padding-right: 80px; }
+.ui-select .ui-mini.ui-btn-icon-right.ui-li-has-count .ui-btn-inner { padding-right: 67px; }
+.ui-select .ui-btn-icon-right .ui-icon { right: 15px; }
+.ui-select .ui-mini.ui-btn-icon-right .ui-icon { right: 7px; }
+.ui-select .ui-btn-icon-right.ui-li-has-count .ui-li-count { right: 45px; }
+.ui-select .ui-mini.ui-btn-icon-right.ui-li-has-count .ui-li-count { right: 32px; }
+/* labels */
+label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; }
+/*listbox*/
+.ui-select .ui-btn-text, .ui-selectmenu .ui-btn-text { display: block; min-height: 1em; overflow: hidden !important;
+/* This !important is required for iPad Safari specifically. See https://github.com/jquery/jquery-mobile/issues/2647 */ }
+.ui-select .ui-btn-text { text-overflow: ellipsis; }
+.ui-selectmenu { position: absolute; padding: 0; z-index: 1100 !important; width: 80%; max-width: 350px; padding: 6px; }
+.ui-selectmenu .ui-listview { margin: 0; }
+.ui-selectmenu .ui-btn.ui-li-divider { cursor: default; }
+.ui-selectmenu-hidden { top: -99999px; left: -9999px; }
+.ui-selectmenu-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; }
+.ui-screen-hidden, .ui-selectmenu-list .ui-li .ui-icon { display: none; }
+.ui-selectmenu-list .ui-li .ui-icon { display: block; }
+.ui-li.ui-selectmenu-placeholder { display: none; }
+.ui-selectmenu .ui-header .ui-title { margin: 0.6em 46px 0.8em; }
+@media all and (min-width: 450px){
+       .ui-field-contain label.ui-select { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
+       .ui-field-contain .ui-select { width: 60%; display: inline-block; }
+       .ui-hide-label .ui-select { width: 100%; } 
+}
+/* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button.  this shim's content in there */
+.ui-selectmenu .ui-header h1:after { content: '.'; visibility: hidden; }
+label.ui-input-text { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; padding-right: 1em;}
+input.ui-input-text, textarea.ui-input-text { background-image: none; padding: .4em; margin: .5em 0; line-height: 1.4; font-size: 16px;  width: 100%; outline: 0; }
+input.ui-input-text.ui-mini, textarea.ui-input-text.ui-mini { margin: .25em 0; }
+.ui-field-contain input.ui-input-text, .ui-field-contain textarea.ui-input-text { margin: 0; }
+input.ui-input-text, textarea.ui-input-text, .ui-input-search { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; }
+input.ui-input-text { -webkit-appearance: none; }
+textarea.ui-input-text { height: 50px; -webkit-transition: height 200ms linear; -moz-transition: height 200ms linear; -o-transition: height 200ms linear; transition: height 200ms linear; }
+.ui-input-search { padding: 0 30px; margin: .5em 0; background-image: none; position: relative; }
+.ui-input-search.ui-mini { margin: .25em 0; }
+.ui-field-contain .ui-input-search { margin: 0; }
+.ui-icon-searchfield:after { position: absolute; left: 7px; top: 50%; margin-top: -9px; content: ""; width: 18px; height: 18px; opacity: .5; }
+.ui-input-search input.ui-input-text { border: none; width: 98%; padding: .4em 0; margin: 0; display: block; background: transparent none; outline: 0 !important; }
+.ui-input-search .ui-input-clear { position: absolute; right: 0; top: 50%; margin-top: -13px; }
+.ui-mini .ui-input-clear { right: -3px; }
+.ui-input-search .ui-input-clear-hidden { display: none; }
+input.ui-mini, .ui-mini input, textarea.ui-mini { font-size: 14px; }
+textarea.ui-mini { height: 45px; }
+@media all and (min-width: 450px){
+       .ui-field-contain label.ui-input-text  { vertical-align: top; width: 100%; margin: 0 2% 0 0 }
+       .ui-field-contain input.ui-input-text, 
+       .ui-field-contain textarea.ui-input-text, 
+       .ui-field-contain .ui-input-search { width: 100%; display: inline-block; } 
+       .ui-hide-label input.ui-input-text, 
+       .ui-hide-label textarea.ui-input-text, 
+       .ui-hide-label .ui-input-search { width: 100%; }
+       .ui-input-search input.ui-input-text { width: 98%; /*echos rule from above*/ }
+}
+.ui-listview { margin: 0; counter-reset: listnumbering; }
+.ui-content .ui-listview { margin: -15px; }
+.ui-content .ui-listview-inset { margin: 1em 0; }
+.ui-listview, .ui-li { list-style:none; padding:0; }
+.ui-li, .ui-li.ui-field-contain { display: block; margin:0; position: relative; overflow: visible; text-align: left; border-width: 0; border-top-width: 1px; }
+.ui-li .ui-btn-text a.ui-link-inherit { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
+.ui-li-divider, .ui-li-static { padding: .5em 15px; font-size: 14px; font-weight: bold; }
+/* Fixes #4254 to prevent inherit font-size from .ui-li-divider, .ui-li-static */
+.ui-li-divider .ui-btn-text, .ui-li-static .ui-btn-text { font-size: 16px; }
+.ui-li-divider .ui-mini .ui-btn-text, .ui-li-static .ui-mini .ui-btn-text { font-size: inherit; }
+.ui-li-divider { counter-reset: listnumbering; }
+ol.ui-listview .ui-link-inherit:before, ol.ui-listview .ui-li-static:before, .ui-li-dec { font-size: .8em; display: inline-block; padding-right: .3em; font-weight: normal;counter-increment: listnumbering; content: counter(listnumbering) ". "; }
+ol.ui-listview .ui-li-jsnumbering:before { content: "" !important; } /* to avoid chance of duplication */
+.ui-listview-inset .ui-li { border-right-width: 1px; border-left-width: 1px; }
+.ui-li:last-child, .ui-li.ui-field-contain:last-child { border-bottom-width: 1px; }
+.ui-li>.ui-btn-inner { display: block; position: relative; padding: 0; }
+.ui-li .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li { padding: .7em 15px; display: block; }
+.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-thumb  { min-height: 60px; padding-left: 100px; }
+.ui-li-has-icon .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-icon {  min-height: 20px; padding-left: 40px; }
+.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-count, .ui-li-divider.ui-li-has-count { padding-right: 45px; }
+.ui-li-has-arrow .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow { padding-right: 40px; }
+.ui-li-has-arrow.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow.ui-li-has-count { padding-right: 75px; }
+.ui-li-heading { font-size: 16px; font-weight: bold; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
+.ui-li-desc {  font-size: 12px; font-weight: normal; display: block; margin: -.5em 0 .6em; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }
+.ui-li-thumb, .ui-listview .ui-li-icon { position: absolute; left: 1px; top: 0; max-height: 80px; max-width: 80px; }
+.ui-listview .ui-li-icon { max-height: 16px; max-width: 16px; left: 10px; top: .9em; }
+.ui-li-thumb, .ui-listview .ui-li-icon, .ui-li-content { float: left; margin-right: 10px; }
+.ui-li-aside { float: right; width: 50%; text-align: right; margin: .3em 0; }
+@media all and (min-width: 480px){
+        .ui-li-aside { width: 45%; }
+}       
+.ui-li-divider { cursor: default; }
+.ui-li-has-alt .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-alt { padding-right: 53px; }
+.ui-li-has-alt.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-alt.ui-li-has-count { padding-right: 88px; }
+.ui-li-has-count .ui-li-count { position: absolute; font-size: 11px; font-weight: bold; padding: .2em .5em; top: 50%; margin-top: -.9em; right: 10px; }
+.ui-li-has-count.ui-li-divider .ui-li-count, .ui-li-has-count .ui-link-inherit .ui-li-count { margin-top: -.95em; }
+.ui-li-has-arrow.ui-li-has-count .ui-li-count { right: 40px; }
+.ui-li-has-alt.ui-li-has-count .ui-li-count { right: 53px; }
+.ui-li-link-alt { position: absolute; width: 40px; height: 100%; border-width: 0; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; }
+.ui-li-link-alt .ui-btn { overflow: hidden; position: absolute; right: 8px; top: 50%; margin: -13px 0 0 0; border-bottom-width: 1px; z-index: -1;}
+.ui-li-link-alt .ui-btn-inner { padding: 0; height: 100%; position: absolute; width: 100%; top: 0; left: 0;}
+.ui-li-link-alt .ui-btn .ui-icon { right: 50%; margin-right: -9px; }
+.ui-li-link-alt .ui-btn-icon-notext .ui-btn-inner .ui-icon { position: absolute; top: 50%; margin-top: -9px; }
+.ui-listview * .ui-btn-inner > .ui-btn > .ui-btn-inner { border-top: 0px; }
+.ui-listview-filter { border-width: 0; overflow: hidden; margin: -15px -15px 15px -15px }
+.ui-listview-filter .ui-input-search { margin: 5px; width: auto; display: block; }
+.ui-listview-filter-inset { margin: -15px -5px -15px -5px; background: transparent; }
+.ui-li.ui-screen-hidden{display:none;}
+/* Odd iPad positioning issue. */
+@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
+    .ui-li .ui-btn-text { overflow:  visible; }
+}
+label.ui-slider { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; }
+input.ui-slider-input,
+.ui-field-contain input.ui-slider-input { display: inline-block; width: 50px; background-image: none; padding: .4em; margin: .5em 0; line-height: 1.4; font-size: 16px; outline: 0; }
+input.ui-slider-input.ui-mini,
+.ui-field-contain input.ui-slider-input.ui-mini { width: 45px; margin: .25em 0; font-size: 14px; }
+.ui-field-contain input.ui-slider-input { margin: 0; }
+/* To do: Exclude ui-slider-input from textinput widget initSelector. The class ui-input-text is added to the slider input and label. When this is fixed, the rule below can be deleted. */
+input.ui-slider-input, .ui-field-contain input.ui-slider-input { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; -ms-box-sizing: content-box; box-sizing: content-box; }
+select.ui-slider-switch { display: none; }
+div.ui-slider { position: relative; display: inline-block; overflow: visible; height: 15px; padding: 0; margin: 0 2% 0 20px; top: 4px; width: 65%; }
+div.ui-slider-mini { height: 12px; margin-left: 10px; top: 2px; }
+div.ui-slider-bg { border: none; height: 100%; padding-right: 8px; }
+.ui-controlgroup a.ui-slider-handle, a.ui-slider-handle { position: absolute; z-index: 1; top: 50%; width: 28px; height: 28px; margin-top: -15px; margin-left: -15px; outline: 0; }
+a.ui-slider-handle .ui-btn-inner { padding: 0; height: 100%; }
+div.ui-slider-mini a.ui-slider-handle { height: 14px; width: 14px; margin: -8px 0 0 -7px; }
+div.ui-slider-mini a.ui-slider-handle .ui-btn-inner { height: 30px; width: 30px; padding: 0; margin: -9px 0 0 -9px; border-top: none; }
+@media all and (min-width: 450px){
+       .ui-field-contain label.ui-slider { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; }
+       .ui-field-contain div.ui-slider { width: 43%; }
+       .ui-field-contain div.ui-slider-switch { width: 5.5em; }
+}      
+div.ui-slider-switch { height: 32px; margin-left: 0; width: 5.0em; }
+a.ui-slider-handle-snapping { -webkit-transition: left 70ms linear; -moz-transition: left 70ms linear; }
+div.ui-slider-switch .ui-slider-handle {  margin-top: 1px; }
+.ui-slider-inneroffset { margin: 0 16px; position: relative; z-index: 1; }
+div.ui-slider-switch.ui-slider-mini { width: 5em; height: 29px; }
+div.ui-slider-switch.ui-slider-mini .ui-slider-inneroffset { margin: 0 15px 0 14px; }
+div.ui-slider-switch.ui-slider-mini .ui-slider-handle { width: 25px; height: 25px; margin: 1px 0 0 -13px; }
+div.ui-slider-switch.ui-slider-mini a.ui-slider-handle .ui-btn-inner { height: 30px; width: 30px; padding: 0; margin: 0; }
+span.ui-slider-label { position: absolute; text-align: center; width: 100%; overflow: hidden; font-size: 16px; top: 0; line-height: 2; min-height: 100%; border-width: 0; white-space: nowrap; }
+.ui-slider-mini span.ui-slider-label { font-size: 14px; }
+span.ui-slider-label-a { z-index: 1; left: 0; text-indent: -1.5em; }
+span.ui-slider-label-b { z-index: 0; right: 0; text-indent: 1.5em;}
+.ui-slider-inline { width: 120px; display: inline-block; }
diff --git a/plugins/jpush-phonegap-plugin/example/css/mobiscroll.core-2.0.1.css b/plugins/jpush-phonegap-plugin/example/css/mobiscroll.core-2.0.1.css
new file mode 100755 (executable)
index 0000000..077c31b
--- /dev/null
@@ -0,0 +1,276 @@
+/* Datewheel overlay */\r
+.dw {\r
+    min-width:170px;\r
+    padding: 0 10px;\r
+    position: absolute;\r
+    top: 5%;\r
+    left: 0;\r
+    z-index: 1001;\r
+    color: #000;\r
+    font-family: arial, verdana, sans-serif;\r
+    font-size: 12px;\r
+    text-shadow: none;\r
+}\r
+.dwi {\r
+    position: static;\r
+    margin: 5px;\r
+    display: inline-block;\r
+}\r
+.dwwr {\r
+    zoom: 1;\r
+}\r
+/* Datewheel overlay background */\r
+.dwo {\r
+    width: 100%;\r
+    background: #000;\r
+    position: absolute;\r
+    top: 0;\r
+    left: 0;\r
+    z-index: 1000;\r
+    opacity: .7;\r
+    filter:Alpha(Opacity=70);\r
+}\r
+/* Datewheel wheel container wrapper */\r
+.dwc {\r
+    float: left;\r
+    margin: 0 2px 5px 2px;\r
+    padding-top: 30px;\r
+}\r
+.dwcc {\r
+    clear: both;\r
+}\r
+/* Datewheel label */\r
+.dwl {\r
+    /*margin: 0 2px;*/\r
+    text-align: center;\r
+    line-height: 30px;\r
+    height: 30px;\r
+    white-space: nowrap;\r
+    position: absolute;\r
+    top: -30px;\r
+    width: 100%;\r
+}\r
+/* Datewheel value */\r
+.dwv {\r
+    padding: 10px 0;\r
+    border-bottom: 1px solid #000;\r
+}\r
+/* Datewheel wheel container */\r
+.dwrc {\r
+    -webkit-border-radius: 3px;\r
+    -moz-border-radius: 3px;\r
+    border-radius: 3px;\r
+}\r
+.dwwc {\r
+    margin: 0;\r
+    padding: 0 2px;\r
+    position: relative;\r
+    background: #000;\r
+    zoom:1;\r
+}\r
+/* Datewheel wheels */\r
+.dwwl {\r
+    margin: 4px 2px;\r
+    position: relative;\r
+    background: #888;\r
+    background: -webkit-gradient(linear,left bottom,left top,color-stop(0, #000),color-stop(0.35, #333),color-stop(0.50, #888),color-stop(0.65, #333),color-stop(1, #000));\r
+    background: -moz-linear-gradient(#000 0%,#333 35%, #888 50%,#333 65%,#000 100%);\r
+    background: -ms-linear-gradient(#000 0%,#333 35%, #888 50%,#333 65%,#000 100%);\r
+    background: -o-linear-gradient(#000 0%,#333 35%, #888 50%,#333 65%,#000 100%);\r
+}\r
+.dww {\r
+    margin: 0 2px;\r
+    overflow: hidden;\r
+    position: relative;\r
+    /*top: -30px;*/\r
+}\r
+.dwsc .dww {\r
+    color: #fff;\r
+    background: #444;\r
+    background: -webkit-gradient(linear,left bottom,left top,color-stop(0, #000),color-stop(0.45, #444),color-stop(0.55, #444),color-stop(1, #000));\r
+    background: -moz-linear-gradient(#000 0%,#444 45%, #444 55%, #000 100%);\r
+    background: -ms-linear-gradient(#000 0%,#444 45%, #444 55%, #000 100%);\r
+    background: -o-linear-gradient(#000 0%,#444 45%, #444 55%, #000 100%);\r
+}\r
+.dww ul {\r
+    list-style: none;\r
+    margin: 0;\r
+    padding: 0;\r
+    /*display: block;\r
+    width: 100%;*/\r
+    position: relative;\r
+    z-index: 2;\r
+}\r
+.dww li {\r
+    list-style: none;\r
+    margin: 0;\r
+    padding: 0 5px;\r
+    display: block;\r
+    text-align: center;\r
+    line-height: 40px;\r
+    font-size: 26px;\r
+    white-space: nowrap;\r
+    text-shadow: 0 1px 1px #000;\r
+    opacity: .3;\r
+    filter: Alpha(Opacity=30);\r
+}\r
+/* Valid entry */\r
+.dww li.dw-v {\r
+    opacity: 1;\r
+    filter: Alpha(Opacity=100);\r
+}\r
+/* Hidden entry */\r
+.dww li.dw-h {\r
+    visibility: hidden;\r
+}\r
+/* Wheel +/- buttons */\r
+.dwwb {\r
+    position: absolute;\r
+    z-index: 4;\r
+    left: 0;\r
+    cursor: pointer;\r
+    width: 100%;\r
+    height: 40px;\r
+    text-align: center;\r
+}\r
+.dwwbp {\r
+    top: 0;\r
+    -webkit-border-radius: 3px 3px 0 0;\r
+    -moz-border-radius: 3px 3px 0 0;\r
+    border-radius: 3px 3px 0 0;\r
+    font-size: 40px;\r
+}\r
+.dwwbm {\r
+    bottom: 0;\r
+    -webkit-border-radius: 0 0 3px 3px;\r
+    -moz-border-radius: 0 0 3px 3px;\r
+    border-radius: 0 0 3px 3px;\r
+    font-size: 32px;\r
+    font-weight: bold;\r
+}\r
+.dwpm .dwwc {\r
+    background: transparent;\r
+}\r
+.dwpm .dww {\r
+    margin: -1px;\r
+}\r
+.dwpm .dww li {\r
+    text-shadow: none;\r
+}\r
+.dwpm .dwwol {\r
+    display: none;\r
+}\r
+/* Datewheel wheel overlay */\r
+.dwwo {\r
+    position: absolute;\r
+    z-index: 3;\r
+    top: 0;\r
+    left: 0;\r
+    width: 100%;\r
+    height: 100%;\r
+    background: -webkit-gradient(linear,left bottom,left top,color-stop(0, #000),color-stop(0.52, rgba(44,44,44,0)),color-stop(0.48, rgba(44,44,44,0)),color-stop(1, #000));\r
+    background: -moz-linear-gradient(#000 0%,rgba(44,44,44,0) 52%, rgba(44,44,44,0) 48%, #000 100%);\r
+    background: -ms-linear-gradient(#000 0%,rgba(44,44,44,0) 52%, rgba(44,44,44,0) 48%, #000 100%);\r
+    background: -o-linear-gradient(#000 0%,rgba(44,44,44,0) 52%, rgba(44,44,44,0) 48%, #000 100%);\r
+}\r
+/* Background line */\r
+.dwwol {\r
+    position: absolute;\r
+    z-index: 1;\r
+    top: 50%;\r
+    left: 0;\r
+    width: 100%;\r
+    height: 0;\r
+    margin-top: -1px;\r
+    border-top: 1px solid #333;\r
+    border-bottom: 1px solid #555;\r
+}\r
+/* Datewheel button */\r
+.dwbg .dwb {\r
+    display: block;\r
+    height: 40px;\r
+    line-height: 40px;\r
+    padding: 0 15px;\r
+    margin: 0 2px;\r
+    font-size: 14px;\r
+    font-weight: bold;\r
+    text-decoration: none;\r
+    text-shadow:0 -1px 1px #000;\r
+    border-radius: 5px;\r
+    -moz-border-radius: 5px;\r
+    -webkit-border-radius:5px;\r
+    box-shadow:0 1px 3px rgba(0,0,0,0.5);\r
+    -moz-box-shadow:0 1px 3px rgba(0,0,0,0.5);\r
+    -webkit-box-shadow:0 1px 3px rgba(0,0,0,0.5);\r
+    color: #fff;\r
+    background:#000;\r
+    background:-webkit-gradient(linear,left bottom,left top,color-stop(0.5, #000),color-stop(0.5, #6e6e6e));\r
+    background:-moz-linear-gradient(#6e6e6e 50%,#000 50%);\r
+    background:-ms-linear-gradient(#6e6e6e 50%,#000 50%);\r
+    background:-o-linear-gradient(#6e6e6e 50%,#000 50%);\r
+}\r
+/* Datewheel button container */\r
+.dwbc {\r
+    /*margin-top: 5px;*/\r
+    padding: 5px 0;\r
+    text-align: center;\r
+    clear: both;\r
+}\r
+/* Datewheel button wrapper */\r
+.dwbw {\r
+    display: inline-block;\r
+    width: 50%;\r
+}\r
+/* Hidden label */\r
+.dwhl {\r
+    padding-top: 10px;\r
+}\r
+.dwhl .dwl {\r
+    display: none;\r
+}\r
+/* Backgrounds */\r
+.dwbg {\r
+    background: #fff;\r
+    border-radius: 3px;\r
+    -webkit-border-radius: 3px;\r
+    -moz-border-radius: 3px;\r
+}\r
+.dwbg .dwpm .dww {\r
+    color: #000;\r
+    background: #fff;\r
+    border: 1px solid #AAA;\r
+}\r
+.dwbg .dwwb {\r
+    background: #ccc;\r
+    color: #888;\r
+    text-shadow: 0 -1px 1px #333;\r
+    box-shadow: 0 0 5px #333;\r
+    -webkit-box-shadow: 0 0 5px #333;\r
+    -moz-box-shadow: 0 0 5px #333;\r
+}\r
+.dwbg .dwwbp {\r
+    background: -webkit-gradient(linear,left bottom,left top,color-stop(0, #bdbdbd),color-stop(1, #f7f7f7));\r
+    background: -moz-linear-gradient(#f7f7f7,#bdbdbd);\r
+    background: -ms-linear-gradient(#f7f7f7,#bdbdbd);\r
+    background: -o-linear-gradient(#f7f7f7,#bdbdbd);\r
+}\r
+.dwbg .dwwbm {\r
+    background: -webkit-gradient(linear,left bottom,left top,color-stop(0, #f7f7f7),color-stop(1, #bdbdbd));\r
+    background: -moz-linear-gradient(#bdbdbd,#f7f7f7);\r
+    background: -ms-linear-gradient(#bdbdbd,#f7f7f7);\r
+    background: -o-linear-gradient(#bdbdbd,#f7f7f7);\r
+}\r
+.dwbg .dwb-a {\r
+    background:#3c7500;\r
+    background:-webkit-gradient(linear,left bottom,left top,color-stop(0.5, #3c7500),color-stop(0.5, #94c840));\r
+    background:-moz-linear-gradient(#94c840 50%,#3c7500 50%);\r
+    background:-ms-linear-gradient(#94c840 50%,#3c7500 50%);\r
+    background:-o-linear-gradient(#94c840 50%,#3c7500 50%);\r
+}\r
+.dwbg .dwwl .dwb-a {\r
+    background:#3c7500;\r
+    background:-webkit-gradient(linear,left bottom,left top,color-stop(0, #3c7500),color-stop(1, #94c840));\r
+    background:-moz-linear-gradient(#94c840,#3c7500);\r
+    background:-ms-linear-gradient(#94c840,#3c7500);\r
+    background:-o-linear-gradient(#94c840,#3c7500);\r
+}\r
diff --git a/plugins/jpush-phonegap-plugin/example/css/mobiscroll.jqm-2.0.1.css b/plugins/jpush-phonegap-plugin/example/css/mobiscroll.jqm-2.0.1.css
new file mode 100755 (executable)
index 0000000..16ae6f9
--- /dev/null
@@ -0,0 +1,47 @@
+/* jQuery Mobile Theme */\r
+.jqm .dwo {\r
+    background: none;\r
+}\r
+.jqm .dw {\r
+    padding: 6px;\r
+    z-index: 1003;\r
+}\r
+.jqm .dwv {\r
+    position: static;\r
+    width: auto;\r
+    padding: .7em 15px .7em 15px;\r
+    border: 0;\r
+}\r
+.jqm .dwwr {\r
+    border: 0;\r
+}\r
+.jqm .dwpm .dwwo {\r
+    background: none;\r
+}\r
+.jqm .dwc {\r
+    margin: 0;\r
+    padding: 30px 5px 5px 5px;\r
+ }\r
+.jqm .dwhl {\r
+    padding: 5px;\r
+}\r
+.jqm .dwwb {\r
+    margin: 0;\r
+    border: 0;\r
+}\r
+.jqm .dwwb span {\r
+    padding: 0;\r
+}\r
+.jqm .dwwbp .ui-btn-inner {\r
+    -webkit-border-radius: 3px 3px 0 0;\r
+    -moz-border-radius: 3px 3px 0 0;\r
+    border-radius: 3px 3px 0 0;\r
+}\r
+.jqm .dwwbm .ui-btn-inner {\r
+    -webkit-border-radius: 0 0 3px 3px;\r
+    -moz-border-radius: 0 0 3px 3px;\r
+    border-radius: 0 0 3px 3px;\r
+}\r
+.jqm .dwwbp span {\r
+    font-weight: normal;\r
+}\r
diff --git a/plugins/jpush-phonegap-plugin/example/index.html b/plugins/jpush-phonegap-plugin/example/index.html
new file mode 100644 (file)
index 0000000..a5682c6
--- /dev/null
@@ -0,0 +1,266 @@
+<!DOCTYPE html>
+<html>
+
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+      <title>JPush Phonegap Simple Demo</title>
+      <link href="css/jquery.mobile-1.1.1.css" rel="stylesheet" type="text/css" />
+      <script type="text/javascript" src="js/jquery.js"></script>
+      <script type="text/javascript" src="js/jquery.mobile-1.1.1.js"></script>
+      <script type="text/javascript" src="cordova.js"></script>
+      <script type="text/javascript">
+
+        var onDeviceReady = function() {
+          document.addEventListener("jpush.receiveRegistrationId", function (event) {
+                alert("receiveRegistrationId" + JSON.stringify(event));
+                $("#registrationId").html(event.registrationId);
+            }, false)
+
+          initiateUI();
+        };
+
+        var getRegistrationID = function() {
+          window.JPush.getRegistrationID(onGetRegistrationID);
+        };
+
+        var onGetRegistrationID = function(data) {
+          try {
+            console.log("JPushPlugin:registrationID is " + data);
+
+            if (data.length == 0) {
+              var t1 = window.setTimeout(getRegistrationID, 1000);
+            }
+
+            $("#registrationId").html(data);
+          } catch (exception) {
+            console.log(exception);
+          }
+        };
+
+        var onTagsWithAlias = function(event) {
+          try {
+            console.log("onTagsWithAlias");
+            var result = "result code:" + event.resultCode + " ";
+            result += "tags:" + event.tags + " ";
+            result += "alias:" + event.alias + " ";
+            $("#tagAliasResult").html(result);
+          } catch (exception) {
+            console.log(exception)
+          }
+        };
+
+        var onOpenNotification = function(event) {
+          try {
+            var alertContent;
+            if (device.platform == "Android") {
+              alertContent = event.alert;
+            } else {
+              alertContent = event.aps.alert;
+            }
+            alert("open Notification:" + alertContent);
+          } catch (exception) {
+            console.log("JPushPlugin:onOpenNotification" + exception);
+          }
+        };
+
+        var onReceiveNotification = function(event) {
+          try {
+            var alertContent;
+            if (device.platform == "Android") {
+              alertContent = event.alert;
+            } else {
+              alertContent = event.aps.alert;
+            }
+            $("#notificationResult").html(alertContent);
+          } catch (exception) {
+            console.log(exception)
+          }
+        };
+
+        var onReceiveMessage = function(event) {
+          try {
+            var message;
+            if (device.platform == "Android") {
+              message = event.message;
+            } else {
+              message = event.content;
+            }
+            $("#messageResult").html(message);
+          } catch (exception) {
+            console.log("JPushPlugin:onReceiveMessage-->" + exception);
+          }
+        };
+
+        var initiateUI = function() {
+          try {
+            window.JPush.init();
+            window.JPush.setDebugMode(true);
+            window.setTimeout(getRegistrationID, 1000);
+
+            if (device.platform != "Android") {
+              window.JPush.setApplicationIconBadgeNumber(0);
+            }
+          } catch (exception) {
+            console.log(exception);
+          }
+
+          $("#setTags").click(function(ev) {
+            try {
+              var tag1 = $("#tagText1").val()
+              var tag2 = $("#tagText2").val()
+              var tag3 = $("#tagText3").val()
+              var tags = []
+
+              if (tag1) {
+                tags.push(tag1)
+              }
+              if (tag2) {
+                tags.push(tag2)
+              }
+              if (tag3) {
+                tags.push(tag3)
+              }
+
+              window.JPush.setTags({ sequence: 1, tags: tags },
+                function (result) {
+                  $("#tagsResult").html(JSON.stringify(result.tags))
+                }, function (error) {
+                  alert(error.code)
+                })
+            } catch (exception) {
+              console.log(exception)
+            }
+          })
+
+          $("#getAllTags").click(function (event) {
+            window.JPush.getAllTags({ sequence: 2 },
+              function (result) {
+                $("#tagsResult").html(JSON.stringify(result.tags))
+              }, function (error) {
+                alert(error.code)
+              })
+          })
+
+          $("#cleanTags").click(function (event) {
+            window.JPush.cleanTags({ sequence: 2 },
+              function (result) {
+                alert(result.sequence)
+                $("#tagsResult").html("")
+              }, function (error) {
+                alert(error.code)
+              })
+          })
+
+          $("#setAlias").click(function (event) {
+            var alias = $("#aliasText").val()
+            window.JPush.setAlias({ sequence: 1, alias: alias },
+              function (result) {
+                $("#aliasResult").html(result.alias)
+              }, function (error){
+                alert(error.code)
+              })
+          })
+
+          $("#getAlias").click(function (event) {
+            window.JPush.getAlias({ sequence: 2 },
+              function (result) {
+                alert(JSON.stringify(result));
+              }, function (error) {
+                alert(error.code)
+              })
+          });
+
+          $("#deleteAlias").click(function (event) {
+            window.JPush.deleteAlias({ sequence: 3 },
+              function (result) {
+                alert(JSON.stringify(result));
+              }, function (error) {
+                alert(error.code)
+              })
+          });
+        };
+
+        document.addEventListener("deviceready", onDeviceReady, false);
+        document.addEventListener("jpush.openNotification", onOpenNotification, false);
+        document.addEventListener("jpush.receiveNotification", onReceiveNotification, false);
+        document.addEventListener("jpush.receiveMessage", onReceiveMessage, false);
+      </script>
+  </head>
+
+  <body>
+    <div data-role="page" id="page">
+      <div data-role="content">
+        <form>
+          <div class="ui-body ui-body-b">
+            <div data-role="fieldcontain">
+              <center>
+                <h3>JPushPlugin Example</h3>
+              </center>
+              <span name="alias" id="alias"></span>
+              <hr/>
+              <label>RegistrationID: </label>
+              <label id="registrationId">null</label>
+            </div>
+            <div data-role="fieldcontain">
+              <label>Tags: </label>
+              <table>
+                <tr>
+                  <td>
+                    <input type="text" id="tagText1" />
+                  </td>
+                </tr>
+                <tr>
+                  <td>
+                    <input type="text" id="tagText2" />
+                  </td>
+                </tr>
+                <tr>
+                  <td>
+                    <input type="text" id="tagText3" />
+                  </td>
+                </tr>
+              </table>
+              <label>Alias: </label>
+              <table>
+                <tr>
+                  <td>
+                    <input type="text" id="aliasText" />
+                  </td>
+                </tr>
+              </table>
+            </div>
+
+            <div data-role="fieldcontain">
+              <input type="button" id="setTags" value="Set tags" />
+              <input type="button" id="getAllTags" value="Get all tags" />
+              <input type="button" id="cleanTags" value="Clean tags" />
+            </div>
+
+            <div data-role="fieldcontain">
+              <input type="button" id="setAlias" value="Set alias" />
+              <input type="button" id="getAlias" value="Get alias" />
+              <input type="button" id="deleteAlias" value="Delete alias" />
+            </div>
+
+            <div data-role="fieldcontain">
+              <label id="tagsPrompt">设置 Tag 的结果:</label>
+              <label id="tagsResult">null</label>
+            </div>
+            <div data-role="fieldcontain">
+              <label id="aliasPrompt">设置 Alias 的结果:</label>
+              <label id="aliasResult">null</label>
+            </div>
+            <div data-role="fieldcontain">
+              <label id="notificationPrompt">接受的通知内容:</label>
+              <label id="notificationResult">null</label>
+            </div>
+            <div data-role="fieldcontain">
+              <label id="messagePrompt">接受的自定义消息:</label>
+              <label id="messageResult">null</label>
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+  </body>
+</html>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/example/js/jquery.js b/plugins/jpush-phonegap-plugin/example/js/jquery.js
new file mode 100755 (executable)
index 0000000..681ea83
--- /dev/null
@@ -0,0 +1,9404 @@
+/*!
+ * jQuery JavaScript Library v1.7.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Wed Mar 21 12:46:34 2012 -0700
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+       navigator = window.navigator,
+       location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+               // The jQuery object is actually just the init constructor 'enhanced'
+               return new jQuery.fn.init( selector, context, rootjQuery );
+       },
+
+       // Map over jQuery in case of overwrite
+       _jQuery = window.jQuery,
+
+       // Map over the $ in case of overwrite
+       _$ = window.$,
+
+       // A central reference to the root jQuery(document)
+       rootjQuery,
+
+       // A simple way to check for HTML strings or ID strings
+       // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+       quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+       // Check if a string has a non-whitespace character in it
+       rnotwhite = /\S/,
+
+       // Used for trimming whitespace
+       trimLeft = /^\s+/,
+       trimRight = /\s+$/,
+
+       // Match a standalone tag
+       rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+       // JSON RegExp
+       rvalidchars = /^[\],:{}\s]*$/,
+       rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+       rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+       rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+       // Useragent RegExp
+       rwebkit = /(webkit)[ \/]([\w.]+)/,
+       ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+       rmsie = /(msie) ([\w.]+)/,
+       rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+       // Matches dashed string for camelizing
+       rdashAlpha = /-([a-z]|[0-9])/ig,
+       rmsPrefix = /^-ms-/,
+
+       // Used by jQuery.camelCase as callback to replace()
+       fcamelCase = function( all, letter ) {
+               return ( letter + "" ).toUpperCase();
+       },
+
+       // Keep a UserAgent string for use with jQuery.browser
+       userAgent = navigator.userAgent,
+
+       // For matching the engine and version of the browser
+       browserMatch,
+
+       // The deferred used on DOM ready
+       readyList,
+
+       // The ready event handler
+       DOMContentLoaded,
+
+       // Save a reference to some core methods
+       toString = Object.prototype.toString,
+       hasOwn = Object.prototype.hasOwnProperty,
+       push = Array.prototype.push,
+       slice = Array.prototype.slice,
+       trim = String.prototype.trim,
+       indexOf = Array.prototype.indexOf,
+
+       // [[Class]] -> type pairs
+       class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+       constructor: jQuery,
+       init: function( selector, context, rootjQuery ) {
+               var match, elem, ret, doc;
+
+               // Handle $(""), $(null), or $(undefined)
+               if ( !selector ) {
+                       return this;
+               }
+
+               // Handle $(DOMElement)
+               if ( selector.nodeType ) {
+                       this.context = this[0] = selector;
+                       this.length = 1;
+                       return this;
+               }
+
+               // The body element only exists once, optimize finding it
+               if ( selector === "body" && !context && document.body ) {
+                       this.context = document;
+                       this[0] = document.body;
+                       this.selector = selector;
+                       this.length = 1;
+                       return this;
+               }
+
+               // Handle HTML strings
+               if ( typeof selector === "string" ) {
+                       // Are we dealing with HTML string or an ID?
+                       if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+                               // Assume that strings that start and end with <> are HTML and skip the regex check
+                               match = [ null, selector, null ];
+
+                       } else {
+                               match = quickExpr.exec( selector );
+                       }
+
+                       // Verify a match, and that no context was specified for #id
+                       if ( match && (match[1] || !context) ) {
+
+                               // HANDLE: $(html) -> $(array)
+                               if ( match[1] ) {
+                                       context = context instanceof jQuery ? context[0] : context;
+                                       doc = ( context ? context.ownerDocument || context : document );
+
+                                       // If a single string is passed in and it's a single tag
+                                       // just do a createElement and skip the rest
+                                       ret = rsingleTag.exec( selector );
+
+                                       if ( ret ) {
+                                               if ( jQuery.isPlainObject( context ) ) {
+                                                       selector = [ document.createElement( ret[1] ) ];
+                                                       jQuery.fn.attr.call( selector, context, true );
+
+                                               } else {
+                                                       selector = [ doc.createElement( ret[1] ) ];
+                                               }
+
+                                       } else {
+                                               ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+                                               selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
+                                       }
+
+                                       return jQuery.merge( this, selector );
+
+                               // HANDLE: $("#id")
+                               } else {
+                                       elem = document.getElementById( match[2] );
+
+                                       // Check parentNode to catch when Blackberry 4.6 returns
+                                       // nodes that are no longer in the document #6963
+                                       if ( elem && elem.parentNode ) {
+                                               // Handle the case where IE and Opera return items
+                                               // by name instead of ID
+                                               if ( elem.id !== match[2] ) {
+                                                       return rootjQuery.find( selector );
+                                               }
+
+                                               // Otherwise, we inject the element directly into the jQuery object
+                                               this.length = 1;
+                                               this[0] = elem;
+                                       }
+
+                                       this.context = document;
+                                       this.selector = selector;
+                                       return this;
+                               }
+
+                       // HANDLE: $(expr, $(...))
+                       } else if ( !context || context.jquery ) {
+                               return ( context || rootjQuery ).find( selector );
+
+                       // HANDLE: $(expr, context)
+                       // (which is just equivalent to: $(context).find(expr)
+                       } else {
+                               return this.constructor( context ).find( selector );
+                       }
+
+               // HANDLE: $(function)
+               // Shortcut for document ready
+               } else if ( jQuery.isFunction( selector ) ) {
+                       return rootjQuery.ready( selector );
+               }
+
+               if ( selector.selector !== undefined ) {
+                       this.selector = selector.selector;
+                       this.context = selector.context;
+               }
+
+               return jQuery.makeArray( selector, this );
+       },
+
+       // Start with an empty selector
+       selector: "",
+
+       // The current version of jQuery being used
+       jquery: "1.7.2",
+
+       // The default length of a jQuery object is 0
+       length: 0,
+
+       // The number of elements contained in the matched element set
+       size: function() {
+               return this.length;
+       },
+
+       toArray: function() {
+               return slice.call( this, 0 );
+       },
+
+       // Get the Nth element in the matched element set OR
+       // Get the whole matched element set as a clean array
+       get: function( num ) {
+               return num == null ?
+
+                       // Return a 'clean' array
+                       this.toArray() :
+
+                       // Return just the object
+                       ( num < 0 ? this[ this.length + num ] : this[ num ] );
+       },
+
+       // Take an array of elements and push it onto the stack
+       // (returning the new matched element set)
+       pushStack: function( elems, name, selector ) {
+               // Build a new jQuery matched element set
+               var ret = this.constructor();
+
+               if ( jQuery.isArray( elems ) ) {
+                       push.apply( ret, elems );
+
+               } else {
+                       jQuery.merge( ret, elems );
+               }
+
+               // Add the old object onto the stack (as a reference)
+               ret.prevObject = this;
+
+               ret.context = this.context;
+
+               if ( name === "find" ) {
+                       ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
+               } else if ( name ) {
+                       ret.selector = this.selector + "." + name + "(" + selector + ")";
+               }
+
+               // Return the newly-formed element set
+               return ret;
+       },
+
+       // Execute a callback for every element in the matched set.
+       // (You can seed the arguments with an array of args, but this is
+       // only used internally.)
+       each: function( callback, args ) {
+               return jQuery.each( this, callback, args );
+       },
+
+       ready: function( fn ) {
+               // Attach the listeners
+               jQuery.bindReady();
+
+               // Add the callback
+               readyList.add( fn );
+
+               return this;
+       },
+
+       eq: function( i ) {
+               i = +i;
+               return i === -1 ?
+                       this.slice( i ) :
+                       this.slice( i, i + 1 );
+       },
+
+       first: function() {
+               return this.eq( 0 );
+       },
+
+       last: function() {
+               return this.eq( -1 );
+       },
+
+       slice: function() {
+               return this.pushStack( slice.apply( this, arguments ),
+                       "slice", slice.call(arguments).join(",") );
+       },
+
+       map: function( callback ) {
+               return this.pushStack( jQuery.map(this, function( elem, i ) {
+                       return callback.call( elem, i, elem );
+               }));
+       },
+
+       end: function() {
+               return this.prevObject || this.constructor(null);
+       },
+
+       // For internal use only.
+       // Behaves like an Array's method, not like a jQuery method.
+       push: push,
+       sort: [].sort,
+       splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+       var options, name, src, copy, copyIsArray, clone,
+               target = arguments[0] || {},
+               i = 1,
+               length = arguments.length,
+               deep = false;
+
+       // Handle a deep copy situation
+       if ( typeof target === "boolean" ) {
+               deep = target;
+               target = arguments[1] || {};
+               // skip the boolean and the target
+               i = 2;
+       }
+
+       // Handle case when target is a string or something (possible in deep copy)
+       if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+               target = {};
+       }
+
+       // extend jQuery itself if only one argument is passed
+       if ( length === i ) {
+               target = this;
+               --i;
+       }
+
+       for ( ; i < length; i++ ) {
+               // Only deal with non-null/undefined values
+               if ( (options = arguments[ i ]) != null ) {
+                       // Extend the base object
+                       for ( name in options ) {
+                               src = target[ name ];
+                               copy = options[ name ];
+
+                               // Prevent never-ending loop
+                               if ( target === copy ) {
+                                       continue;
+                               }
+
+                               // Recurse if we're merging plain objects or arrays
+                               if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+                                       if ( copyIsArray ) {
+                                               copyIsArray = false;
+                                               clone = src && jQuery.isArray(src) ? src : [];
+
+                                       } else {
+                                               clone = src && jQuery.isPlainObject(src) ? src : {};
+                                       }
+
+                                       // Never move original objects, clone them
+                                       target[ name ] = jQuery.extend( deep, clone, copy );
+
+                               // Don't bring in undefined values
+                               } else if ( copy !== undefined ) {
+                                       target[ name ] = copy;
+                               }
+                       }
+               }
+       }
+
+       // Return the modified object
+       return target;
+};
+
+jQuery.extend({
+       noConflict: function( deep ) {
+               if ( window.$ === jQuery ) {
+                       window.$ = _$;
+               }
+
+               if ( deep && window.jQuery === jQuery ) {
+                       window.jQuery = _jQuery;
+               }
+
+               return jQuery;
+       },
+
+       // Is the DOM ready to be used? Set to true once it occurs.
+       isReady: false,
+
+       // A counter to track how many items to wait for before
+       // the ready event fires. See #6781
+       readyWait: 1,
+
+       // Hold (or release) the ready event
+       holdReady: function( hold ) {
+               if ( hold ) {
+                       jQuery.readyWait++;
+               } else {
+                       jQuery.ready( true );
+               }
+       },
+
+       // Handle when the DOM is ready
+       ready: function( wait ) {
+               // Either a released hold or an DOMready/load event and not yet ready
+               if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
+                       // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+                       if ( !document.body ) {
+                               return setTimeout( jQuery.ready, 1 );
+                       }
+
+                       // Remember that the DOM is ready
+                       jQuery.isReady = true;
+
+                       // If a normal DOM Ready event fired, decrement, and wait if need be
+                       if ( wait !== true && --jQuery.readyWait > 0 ) {
+                               return;
+                       }
+
+                       // If there are functions bound, to execute
+                       readyList.fireWith( document, [ jQuery ] );
+
+                       // Trigger any bound ready events
+                       if ( jQuery.fn.trigger ) {
+                               jQuery( document ).trigger( "ready" ).off( "ready" );
+                       }
+               }
+       },
+
+       bindReady: function() {
+               if ( readyList ) {
+                       return;
+               }
+
+               readyList = jQuery.Callbacks( "once memory" );
+
+               // Catch cases where $(document).ready() is called after the
+               // browser event has already occurred.
+               if ( document.readyState === "complete" ) {
+                       // Handle it asynchronously to allow scripts the opportunity to delay ready
+                       return setTimeout( jQuery.ready, 1 );
+               }
+
+               // Mozilla, Opera and webkit nightlies currently support this event
+               if ( document.addEventListener ) {
+                       // Use the handy event callback
+                       document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+                       // A fallback to window.onload, that will always work
+                       window.addEventListener( "load", jQuery.ready, false );
+
+               // If IE event model is used
+               } else if ( document.attachEvent ) {
+                       // ensure firing before onload,
+                       // maybe late but safe also for iframes
+                       document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+                       // A fallback to window.onload, that will always work
+                       window.attachEvent( "onload", jQuery.ready );
+
+                       // If IE and not a frame
+                       // continually check to see if the document is ready
+                       var toplevel = false;
+
+                       try {
+                               toplevel = window.frameElement == null;
+                       } catch(e) {}
+
+                       if ( document.documentElement.doScroll && toplevel ) {
+                               doScrollCheck();
+                       }
+               }
+       },
+
+       // See test/unit/core.js for details concerning isFunction.
+       // Since version 1.3, DOM methods and functions like alert
+       // aren't supported. They return false on IE (#2968).
+       isFunction: function( obj ) {
+               return jQuery.type(obj) === "function";
+       },
+
+       isArray: Array.isArray || function( obj ) {
+               return jQuery.type(obj) === "array";
+       },
+
+       isWindow: function( obj ) {
+               return obj != null && obj == obj.window;
+       },
+
+       isNumeric: function( obj ) {
+               return !isNaN( parseFloat(obj) ) && isFinite( obj );
+       },
+
+       type: function( obj ) {
+               return obj == null ?
+                       String( obj ) :
+                       class2type[ toString.call(obj) ] || "object";
+       },
+
+       isPlainObject: function( obj ) {
+               // Must be an Object.
+               // Because of IE, we also have to check the presence of the constructor property.
+               // Make sure that DOM nodes and window objects don't pass through, as well
+               if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+                       return false;
+               }
+
+               try {
+                       // Not own constructor property must be Object
+                       if ( obj.constructor &&
+                               !hasOwn.call(obj, "constructor") &&
+                               !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+                               return false;
+                       }
+               } catch ( e ) {
+                       // IE8,9 Will throw exceptions on certain host objects #9897
+                       return false;
+               }
+
+               // Own properties are enumerated firstly, so to speed up,
+               // if last one is own, then all properties are own.
+
+               var key;
+               for ( key in obj ) {}
+
+               return key === undefined || hasOwn.call( obj, key );
+       },
+
+       isEmptyObject: function( obj ) {
+               for ( var name in obj ) {
+                       return false;
+               }
+               return true;
+       },
+
+       error: function( msg ) {
+               throw new Error( msg );
+       },
+
+       parseJSON: function( data ) {
+               if ( typeof data !== "string" || !data ) {
+                       return null;
+               }
+
+               // Make sure leading/trailing whitespace is removed (IE can't handle it)
+               data = jQuery.trim( data );
+
+               // Attempt to parse using the native JSON parser first
+               if ( window.JSON && window.JSON.parse ) {
+                       return window.JSON.parse( data );
+               }
+
+               // Make sure the incoming data is actual JSON
+               // Logic borrowed from http://json.org/json2.js
+               if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+                       .replace( rvalidtokens, "]" )
+                       .replace( rvalidbraces, "")) ) {
+
+                       return ( new Function( "return " + data ) )();
+
+               }
+               jQuery.error( "Invalid JSON: " + data );
+       },
+
+       // Cross-browser xml parsing
+       parseXML: function( data ) {
+               if ( typeof data !== "string" || !data ) {
+                       return null;
+               }
+               var xml, tmp;
+               try {
+                       if ( window.DOMParser ) { // Standard
+                               tmp = new DOMParser();
+                               xml = tmp.parseFromString( data , "text/xml" );
+                       } else { // IE
+                               xml = new ActiveXObject( "Microsoft.XMLDOM" );
+                               xml.async = "false";
+                               xml.loadXML( data );
+                       }
+               } catch( e ) {
+                       xml = undefined;
+               }
+               if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+                       jQuery.error( "Invalid XML: " + data );
+               }
+               return xml;
+       },
+
+       noop: function() {},
+
+       // Evaluates a script in a global context
+       // Workarounds based on findings by Jim Driscoll
+       // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+       globalEval: function( data ) {
+               if ( data && rnotwhite.test( data ) ) {
+                       // We use execScript on Internet Explorer
+                       // We use an anonymous function so that context is window
+                       // rather than jQuery in Firefox
+                       ( window.execScript || function( data ) {
+                               window[ "eval" ].call( window, data );
+                       } )( data );
+               }
+       },
+
+       // Convert dashed to camelCase; used by the css and data modules
+       // Microsoft forgot to hump their vendor prefix (#9572)
+       camelCase: function( string ) {
+               return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+       },
+
+       nodeName: function( elem, name ) {
+               return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+       },
+
+       // args is for internal usage only
+       each: function( object, callback, args ) {
+               var name, i = 0,
+                       length = object.length,
+                       isObj = length === undefined || jQuery.isFunction( object );
+
+               if ( args ) {
+                       if ( isObj ) {
+                               for ( name in object ) {
+                                       if ( callback.apply( object[ name ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( ; i < length; ) {
+                                       if ( callback.apply( object[ i++ ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       }
+
+               // A special, fast, case for the most common use of each
+               } else {
+                       if ( isObj ) {
+                               for ( name in object ) {
+                                       if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( ; i < length; ) {
+                                       if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               return object;
+       },
+
+       // Use native String.trim function wherever possible
+       trim: trim ?
+               function( text ) {
+                       return text == null ?
+                               "" :
+                               trim.call( text );
+               } :
+
+               // Otherwise use our own trimming functionality
+               function( text ) {
+                       return text == null ?
+                               "" :
+                               text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+               },
+
+       // results is for internal usage only
+       makeArray: function( array, results ) {
+               var ret = results || [];
+
+               if ( array != null ) {
+                       // The window, strings (and functions) also have 'length'
+                       // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+                       var type = jQuery.type( array );
+
+                       if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+                               push.call( ret, array );
+                       } else {
+                               jQuery.merge( ret, array );
+                       }
+               }
+
+               return ret;
+       },
+
+       inArray: function( elem, array, i ) {
+               var len;
+
+               if ( array ) {
+                       if ( indexOf ) {
+                               return indexOf.call( array, elem, i );
+                       }
+
+                       len = array.length;
+                       i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+                       for ( ; i < len; i++ ) {
+                               // Skip accessing in sparse arrays
+                               if ( i in array && array[ i ] === elem ) {
+                                       return i;
+                               }
+                       }
+               }
+
+               return -1;
+       },
+
+       merge: function( first, second ) {
+               var i = first.length,
+                       j = 0;
+
+               if ( typeof second.length === "number" ) {
+                       for ( var l = second.length; j < l; j++ ) {
+                               first[ i++ ] = second[ j ];
+                       }
+
+               } else {
+                       while ( second[j] !== undefined ) {
+                               first[ i++ ] = second[ j++ ];
+                       }
+               }
+
+               first.length = i;
+
+               return first;
+       },
+
+       grep: function( elems, callback, inv ) {
+               var ret = [], retVal;
+               inv = !!inv;
+
+               // Go through the array, only saving the items
+               // that pass the validator function
+               for ( var i = 0, length = elems.length; i < length; i++ ) {
+                       retVal = !!callback( elems[ i ], i );
+                       if ( inv !== retVal ) {
+                               ret.push( elems[ i ] );
+                       }
+               }
+
+               return ret;
+       },
+
+       // arg is for internal usage only
+       map: function( elems, callback, arg ) {
+               var value, key, ret = [],
+                       i = 0,
+                       length = elems.length,
+                       // jquery objects are treated as arrays
+                       isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+               // Go through the array, translating each of the items to their
+               if ( isArray ) {
+                       for ( ; i < length; i++ ) {
+                               value = callback( elems[ i ], i, arg );
+
+                               if ( value != null ) {
+                                       ret[ ret.length ] = value;
+                               }
+                       }
+
+               // Go through every key on the object,
+               } else {
+                       for ( key in elems ) {
+                               value = callback( elems[ key ], key, arg );
+
+                               if ( value != null ) {
+                                       ret[ ret.length ] = value;
+                               }
+                       }
+               }
+
+               // Flatten any nested arrays
+               return ret.concat.apply( [], ret );
+       },
+
+       // A global GUID counter for objects
+       guid: 1,
+
+       // Bind a function to a context, optionally partially applying any
+       // arguments.
+       proxy: function( fn, context ) {
+               if ( typeof context === "string" ) {
+                       var tmp = fn[ context ];
+                       context = fn;
+                       fn = tmp;
+               }
+
+               // Quick check to determine if target is callable, in the spec
+               // this throws a TypeError, but we will just return undefined.
+               if ( !jQuery.isFunction( fn ) ) {
+                       return undefined;
+               }
+
+               // Simulated bind
+               var args = slice.call( arguments, 2 ),
+                       proxy = function() {
+                               return fn.apply( context, args.concat( slice.call( arguments ) ) );
+                       };
+
+               // Set the guid of unique handler to the same of original handler, so it can be removed
+               proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+               return proxy;
+       },
+
+       // Mutifunctional method to get and set values to a collection
+       // The value/s can optionally be executed if it's a function
+       access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
+               var exec,
+                       bulk = key == null,
+                       i = 0,
+                       length = elems.length;
+
+               // Sets many values
+               if ( key && typeof key === "object" ) {
+                       for ( i in key ) {
+                               jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
+                       }
+                       chainable = 1;
+
+               // Sets one value
+               } else if ( value !== undefined ) {
+                       // Optionally, function values get executed if exec is true
+                       exec = pass === undefined && jQuery.isFunction( value );
+
+                       if ( bulk ) {
+                               // Bulk operations only iterate when executing function values
+                               if ( exec ) {
+                                       exec = fn;
+                                       fn = function( elem, key, value ) {
+                                               return exec.call( jQuery( elem ), value );
+                                       };
+
+                               // Otherwise they run against the entire set
+                               } else {
+                                       fn.call( elems, value );
+                                       fn = null;
+                               }
+                       }
+
+                       if ( fn ) {
+                               for (; i < length; i++ ) {
+                                       fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+                               }
+                       }
+
+                       chainable = 1;
+               }
+
+               return chainable ?
+                       elems :
+
+                       // Gets
+                       bulk ?
+                               fn.call( elems ) :
+                               length ? fn( elems[0], key ) : emptyGet;
+       },
+
+       now: function() {
+               return ( new Date() ).getTime();
+       },
+
+       // Use of jQuery.browser is frowned upon.
+       // More details: http://docs.jquery.com/Utilities/jQuery.browser
+       uaMatch: function( ua ) {
+               ua = ua.toLowerCase();
+
+               var match = rwebkit.exec( ua ) ||
+                       ropera.exec( ua ) ||
+                       rmsie.exec( ua ) ||
+                       ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+                       [];
+
+               return { browser: match[1] || "", version: match[2] || "0" };
+       },
+
+       sub: function() {
+               function jQuerySub( selector, context ) {
+                       return new jQuerySub.fn.init( selector, context );
+               }
+               jQuery.extend( true, jQuerySub, this );
+               jQuerySub.superclass = this;
+               jQuerySub.fn = jQuerySub.prototype = this();
+               jQuerySub.fn.constructor = jQuerySub;
+               jQuerySub.sub = this.sub;
+               jQuerySub.fn.init = function init( selector, context ) {
+                       if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+                               context = jQuerySub( context );
+                       }
+
+                       return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+               };
+               jQuerySub.fn.init.prototype = jQuerySub.fn;
+               var rootjQuerySub = jQuerySub(document);
+               return jQuerySub;
+       },
+
+       browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+       class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+       jQuery.browser[ browserMatch.browser ] = true;
+       jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+       jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+       trimLeft = /^[\s\xA0]+/;
+       trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+       DOMContentLoaded = function() {
+               document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+               jQuery.ready();
+       };
+
+} else if ( document.attachEvent ) {
+       DOMContentLoaded = function() {
+               // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+               if ( document.readyState === "complete" ) {
+                       document.detachEvent( "onreadystatechange", DOMContentLoaded );
+                       jQuery.ready();
+               }
+       };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+       if ( jQuery.isReady ) {
+               return;
+       }
+
+       try {
+               // If IE is used, use the trick by Diego Perini
+               // http://javascript.nwbox.com/IEContentLoaded/
+               document.documentElement.doScroll("left");
+       } catch(e) {
+               setTimeout( doScrollCheck, 1 );
+               return;
+       }
+
+       // and execute any waiting functions
+       jQuery.ready();
+}
+
+return jQuery;
+
+})();
+
+
+// String to Object flags format cache
+var flagsCache = {};
+
+// Convert String-formatted flags into Object-formatted ones and store in cache
+function createFlags( flags ) {
+       var object = flagsCache[ flags ] = {},
+               i, length;
+       flags = flags.split( /\s+/ );
+       for ( i = 0, length = flags.length; i < length; i++ ) {
+               object[ flags[i] ] = true;
+       }
+       return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ *     flags:  an optional list of space-separated flags that will change how
+ *                     the callback list behaves
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible flags:
+ *
+ *     once:                   will ensure the callback list can only be fired once (like a Deferred)
+ *
+ *     memory:                 will keep track of previous values and will call any callback added
+ *                                     after the list has been fired right away with the latest "memorized"
+ *                                     values (like a Deferred)
+ *
+ *     unique:                 will ensure a callback can only be added once (no duplicate in the list)
+ *
+ *     stopOnFalse:    interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( flags ) {
+
+       // Convert flags from String-formatted to Object-formatted
+       // (we check in cache first)
+       flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
+
+       var // Actual callback list
+               list = [],
+               // Stack of fire calls for repeatable lists
+               stack = [],
+               // Last fire value (for non-forgettable lists)
+               memory,
+               // Flag to know if list was already fired
+               fired,
+               // Flag to know if list is currently firing
+               firing,
+               // First callback to fire (used internally by add and fireWith)
+               firingStart,
+               // End of the loop when firing
+               firingLength,
+               // Index of currently firing callback (modified by remove if needed)
+               firingIndex,
+               // Add one or several callbacks to the list
+               add = function( args ) {
+                       var i,
+                               length,
+                               elem,
+                               type,
+                               actual;
+                       for ( i = 0, length = args.length; i < length; i++ ) {
+                               elem = args[ i ];
+                               type = jQuery.type( elem );
+                               if ( type === "array" ) {
+                                       // Inspect recursively
+                                       add( elem );
+                               } else if ( type === "function" ) {
+                                       // Add if not in unique mode and callback is not in
+                                       if ( !flags.unique || !self.has( elem ) ) {
+                                               list.push( elem );
+                                       }
+                               }
+                       }
+               },
+               // Fire callbacks
+               fire = function( context, args ) {
+                       args = args || [];
+                       memory = !flags.memory || [ context, args ];
+                       fired = true;
+                       firing = true;
+                       firingIndex = firingStart || 0;
+                       firingStart = 0;
+                       firingLength = list.length;
+                       for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+                               if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
+                                       memory = true; // Mark as halted
+                                       break;
+                               }
+                       }
+                       firing = false;
+                       if ( list ) {
+                               if ( !flags.once ) {
+                                       if ( stack && stack.length ) {
+                                               memory = stack.shift();
+                                               self.fireWith( memory[ 0 ], memory[ 1 ] );
+                                       }
+                               } else if ( memory === true ) {
+                                       self.disable();
+                               } else {
+                                       list = [];
+                               }
+                       }
+               },
+               // Actual Callbacks object
+               self = {
+                       // Add a callback or a collection of callbacks to the list
+                       add: function() {
+                               if ( list ) {
+                                       var length = list.length;
+                                       add( arguments );
+                                       // Do we need to add the callbacks to the
+                                       // current firing batch?
+                                       if ( firing ) {
+                                               firingLength = list.length;
+                                       // With memory, if we're not firing then
+                                       // we should call right away, unless previous
+                                       // firing was halted (stopOnFalse)
+                                       } else if ( memory && memory !== true ) {
+                                               firingStart = length;
+                                               fire( memory[ 0 ], memory[ 1 ] );
+                                       }
+                               }
+                               return this;
+                       },
+                       // Remove a callback from the list
+                       remove: function() {
+                               if ( list ) {
+                                       var args = arguments,
+                                               argIndex = 0,
+                                               argLength = args.length;
+                                       for ( ; argIndex < argLength ; argIndex++ ) {
+                                               for ( var i = 0; i < list.length; i++ ) {
+                                                       if ( args[ argIndex ] === list[ i ] ) {
+                                                               // Handle firingIndex and firingLength
+                                                               if ( firing ) {
+                                                                       if ( i <= firingLength ) {
+                                                                               firingLength--;
+                                                                               if ( i <= firingIndex ) {
+                                                                                       firingIndex--;
+                                                                               }
+                                                                       }
+                                                               }
+                                                               // Remove the element
+                                                               list.splice( i--, 1 );
+                                                               // If we have some unicity property then
+                                                               // we only need to do this once
+                                                               if ( flags.unique ) {
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               return this;
+                       },
+                       // Control if a given callback is in the list
+                       has: function( fn ) {
+                               if ( list ) {
+                                       var i = 0,
+                                               length = list.length;
+                                       for ( ; i < length; i++ ) {
+                                               if ( fn === list[ i ] ) {
+                                                       return true;
+                                               }
+                                       }
+                               }
+                               return false;
+                       },
+                       // Remove all callbacks from the list
+                       empty: function() {
+                               list = [];
+                               return this;
+                       },
+                       // Have the list do nothing anymore
+                       disable: function() {
+                               list = stack = memory = undefined;
+                               return this;
+                       },
+                       // Is it disabled?
+                       disabled: function() {
+                               return !list;
+                       },
+                       // Lock the list in its current state
+                       lock: function() {
+                               stack = undefined;
+                               if ( !memory || memory === true ) {
+                                       self.disable();
+                               }
+                               return this;
+                       },
+                       // Is it locked?
+                       locked: function() {
+                               return !stack;
+                       },
+                       // Call all callbacks with the given context and arguments
+                       fireWith: function( context, args ) {
+                               if ( stack ) {
+                                       if ( firing ) {
+                                               if ( !flags.once ) {
+                                                       stack.push( [ context, args ] );
+                                               }
+                                       } else if ( !( flags.once && memory ) ) {
+                                               fire( context, args );
+                                       }
+                               }
+                               return this;
+                       },
+                       // Call all the callbacks with the given arguments
+                       fire: function() {
+                               self.fireWith( this, arguments );
+                               return this;
+                       },
+                       // To know if the callbacks have already been called at least once
+                       fired: function() {
+                               return !!fired;
+                       }
+               };
+
+       return self;
+};
+
+
+
+
+var // Static reference to slice
+       sliceDeferred = [].slice;
+
+jQuery.extend({
+
+       Deferred: function( func ) {
+               var doneList = jQuery.Callbacks( "once memory" ),
+                       failList = jQuery.Callbacks( "once memory" ),
+                       progressList = jQuery.Callbacks( "memory" ),
+                       state = "pending",
+                       lists = {
+                               resolve: doneList,
+                               reject: failList,
+                               notify: progressList
+                       },
+                       promise = {
+                               done: doneList.add,
+                               fail: failList.add,
+                               progress: progressList.add,
+
+                               state: function() {
+                                       return state;
+                               },
+
+                               // Deprecated
+                               isResolved: doneList.fired,
+                               isRejected: failList.fired,
+
+                               then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
+                                       deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
+                                       return this;
+                               },
+                               always: function() {
+                                       deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+                                       return this;
+                               },
+                               pipe: function( fnDone, fnFail, fnProgress ) {
+                                       return jQuery.Deferred(function( newDefer ) {
+                                               jQuery.each( {
+                                                       done: [ fnDone, "resolve" ],
+                                                       fail: [ fnFail, "reject" ],
+                                                       progress: [ fnProgress, "notify" ]
+                                               }, function( handler, data ) {
+                                                       var fn = data[ 0 ],
+                                                               action = data[ 1 ],
+                                                               returned;
+                                                       if ( jQuery.isFunction( fn ) ) {
+                                                               deferred[ handler ](function() {
+                                                                       returned = fn.apply( this, arguments );
+                                                                       if ( returned && jQuery.isFunction( returned.promise ) ) {
+                                                                               returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
+                                                                       } else {
+                                                                               newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+                                                                       }
+                                                               });
+                                                       } else {
+                                                               deferred[ handler ]( newDefer[ action ] );
+                                                       }
+                                               });
+                                       }).promise();
+                               },
+                               // Get a promise for this deferred
+                               // If obj is provided, the promise aspect is added to the object
+                               promise: function( obj ) {
+                                       if ( obj == null ) {
+                                               obj = promise;
+                                       } else {
+                                               for ( var key in promise ) {
+                                                       obj[ key ] = promise[ key ];
+                                               }
+                                       }
+                                       return obj;
+                               }
+                       },
+                       deferred = promise.promise({}),
+                       key;
+
+               for ( key in lists ) {
+                       deferred[ key ] = lists[ key ].fire;
+                       deferred[ key + "With" ] = lists[ key ].fireWith;
+               }
+
+               // Handle state
+               deferred.done( function() {
+                       state = "resolved";
+               }, failList.disable, progressList.lock ).fail( function() {
+                       state = "rejected";
+               }, doneList.disable, progressList.lock );
+
+               // Call given func if any
+               if ( func ) {
+                       func.call( deferred, deferred );
+               }
+
+               // All done!
+               return deferred;
+       },
+
+       // Deferred helper
+       when: function( firstParam ) {
+               var args = sliceDeferred.call( arguments, 0 ),
+                       i = 0,
+                       length = args.length,
+                       pValues = new Array( length ),
+                       count = length,
+                       pCount = length,
+                       deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+                               firstParam :
+                               jQuery.Deferred(),
+                       promise = deferred.promise();
+               function resolveFunc( i ) {
+                       return function( value ) {
+                               args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+                               if ( !( --count ) ) {
+                                       deferred.resolveWith( deferred, args );
+                               }
+                       };
+               }
+               function progressFunc( i ) {
+                       return function( value ) {
+                               pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+                               deferred.notifyWith( promise, pValues );
+                       };
+               }
+               if ( length > 1 ) {
+                       for ( ; i < length; i++ ) {
+                               if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
+                                       args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
+                               } else {
+                                       --count;
+                               }
+                       }
+                       if ( !count ) {
+                               deferred.resolveWith( deferred, args );
+                       }
+               } else if ( deferred !== firstParam ) {
+                       deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+               }
+               return promise;
+       }
+});
+
+
+
+
+jQuery.support = (function() {
+
+       var support,
+               all,
+               a,
+               select,
+               opt,
+               input,
+               fragment,
+               tds,
+               events,
+               eventName,
+               i,
+               isSupported,
+               div = document.createElement( "div" ),
+               documentElement = document.documentElement;
+
+       // Preliminary tests
+       div.setAttribute("className", "t");
+       div.innerHTML = "   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+       all = div.getElementsByTagName( "*" );
+       a = div.getElementsByTagName( "a" )[ 0 ];
+
+       // Can't get basic test support
+       if ( !all || !all.length || !a ) {
+               return {};
+       }
+
+       // First batch of supports tests
+       select = document.createElement( "select" );
+       opt = select.appendChild( document.createElement("option") );
+       input = div.getElementsByTagName( "input" )[ 0 ];
+
+       support = {
+               // IE strips leading whitespace when .innerHTML is used
+               leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+               // Make sure that tbody elements aren't automatically inserted
+               // IE will insert them into empty tables
+               tbody: !div.getElementsByTagName("tbody").length,
+
+               // Make sure that link elements get serialized correctly by innerHTML
+               // This requires a wrapper element in IE
+               htmlSerialize: !!div.getElementsByTagName("link").length,
+
+               // Get the style information from getAttribute
+               // (IE uses .cssText instead)
+               style: /top/.test( a.getAttribute("style") ),
+
+               // Make sure that URLs aren't manipulated
+               // (IE normalizes it by default)
+               hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+               // Make sure that element opacity exists
+               // (IE uses filter instead)
+               // Use a regex to work around a WebKit issue. See #5145
+               opacity: /^0.55/.test( a.style.opacity ),
+
+               // Verify style float existence
+               // (IE uses styleFloat instead of cssFloat)
+               cssFloat: !!a.style.cssFloat,
+
+               // Make sure that if no value is specified for a checkbox
+               // that it defaults to "on".
+               // (WebKit defaults to "" instead)
+               checkOn: ( input.value === "on" ),
+
+               // Make sure that a selected-by-default option has a working selected property.
+               // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+               optSelected: opt.selected,
+
+               // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+               getSetAttribute: div.className !== "t",
+
+               // Tests for enctype support on a form(#6743)
+               enctype: !!document.createElement("form").enctype,
+
+               // Makes sure cloning an html5 element does not cause problems
+               // Where outerHTML is undefined, this still works
+               html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
+               // Will be defined later
+               submitBubbles: true,
+               changeBubbles: true,
+               focusinBubbles: false,
+               deleteExpando: true,
+               noCloneEvent: true,
+               inlineBlockNeedsLayout: false,
+               shrinkWrapBlocks: false,
+               reliableMarginRight: true,
+               pixelMargin: true
+       };
+
+       // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead
+       jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat");
+
+       // Make sure checked status is properly cloned
+       input.checked = true;
+       support.noCloneChecked = input.cloneNode( true ).checked;
+
+       // Make sure that the options inside disabled selects aren't marked as disabled
+       // (WebKit marks them as disabled)
+       select.disabled = true;
+       support.optDisabled = !opt.disabled;
+
+       // Test to see if it's possible to delete an expando from an element
+       // Fails in Internet Explorer
+       try {
+               delete div.test;
+       } catch( e ) {
+               support.deleteExpando = false;
+       }
+
+       if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+               div.attachEvent( "onclick", function() {
+                       // Cloning a node shouldn't copy over any
+                       // bound event handlers (IE does this)
+                       support.noCloneEvent = false;
+               });
+               div.cloneNode( true ).fireEvent( "onclick" );
+       }
+
+       // Check if a radio maintains its value
+       // after being appended to the DOM
+       input = document.createElement("input");
+       input.value = "t";
+       input.setAttribute("type", "radio");
+       support.radioValue = input.value === "t";
+
+       input.setAttribute("checked", "checked");
+
+       // #11217 - WebKit loses check when the name is after the checked attribute
+       input.setAttribute( "name", "t" );
+
+       div.appendChild( input );
+       fragment = document.createDocumentFragment();
+       fragment.appendChild( div.lastChild );
+
+       // WebKit doesn't clone checked state correctly in fragments
+       support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+       // Check if a disconnected checkbox will retain its checked
+       // value of true after appended to the DOM (IE6/7)
+       support.appendChecked = input.checked;
+
+       fragment.removeChild( input );
+       fragment.appendChild( div );
+
+       // Technique from Juriy Zaytsev
+       // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+       // We only care about the case where non-standard event systems
+       // are used, namely in IE. Short-circuiting here helps us to
+       // avoid an eval call (in setAttribute) which can cause CSP
+       // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+       if ( div.attachEvent ) {
+               for ( i in {
+                       submit: 1,
+                       change: 1,
+                       focusin: 1
+               }) {
+                       eventName = "on" + i;
+                       isSupported = ( eventName in div );
+                       if ( !isSupported ) {
+                               div.setAttribute( eventName, "return;" );
+                               isSupported = ( typeof div[ eventName ] === "function" );
+                       }
+                       support[ i + "Bubbles" ] = isSupported;
+               }
+       }
+
+       fragment.removeChild( div );
+
+       // Null elements to avoid leaks in IE
+       fragment = select = opt = div = input = null;
+
+       // Run tests that need a body at doc ready
+       jQuery(function() {
+               var container, outer, inner, table, td, offsetSupport,
+                       marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight,
+                       paddingMarginBorderVisibility, paddingMarginBorder,
+                       body = document.getElementsByTagName("body")[0];
+
+               if ( !body ) {
+                       // Return for frameset docs that don't have a body
+                       return;
+               }
+
+               conMarginTop = 1;
+               paddingMarginBorder = "padding:0;margin:0;border:";
+               positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;";
+               paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;";
+               style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;";
+               html = "<div " + style + "display:block;'><div style='" + paddingMarginBorder + "0;display:block;overflow:hidden;'></div></div>" +
+                       "<table " + style + "' cellpadding='0' cellspacing='0'>" +
+                       "<tr><td></td></tr></table>";
+
+               container = document.createElement("div");
+               container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
+               body.insertBefore( container, body.firstChild );
+
+               // Construct the test element
+               div = document.createElement("div");
+               container.appendChild( div );
+
+               // Check if table cells still have offsetWidth/Height when they are set
+               // to display:none and there are still other visible table cells in a
+               // table row; if so, offsetWidth/Height are not reliable for use when
+               // determining if an element has been hidden directly using
+               // display:none (it is still safe to use offsets if a parent element is
+               // hidden; don safety goggles and see bug #4512 for more information).
+               // (only IE 8 fails this test)
+               div.innerHTML = "<table><tr><td style='" + paddingMarginBorder + "0;display:none'></td><td>t</td></tr></table>";
+               tds = div.getElementsByTagName( "td" );
+               isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+               tds[ 0 ].style.display = "";
+               tds[ 1 ].style.display = "none";
+
+               // Check if empty table cells still have offsetWidth/Height
+               // (IE <= 8 fail this test)
+               support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+               // Check if div with explicit width and no margin-right incorrectly
+               // gets computed margin-right based on width of container. For more
+               // info see bug #3333
+               // Fails in WebKit before Feb 2011 nightlies
+               // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+               if ( window.getComputedStyle ) {
+                       div.innerHTML = "";
+                       marginDiv = document.createElement( "div" );
+                       marginDiv.style.width = "0";
+                       marginDiv.style.marginRight = "0";
+                       div.style.width = "2px";
+                       div.appendChild( marginDiv );
+                       support.reliableMarginRight =
+                               ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+               }
+
+               if ( typeof div.style.zoom !== "undefined" ) {
+                       // Check if natively block-level elements act like inline-block
+                       // elements when setting their display to 'inline' and giving
+                       // them layout
+                       // (IE < 8 does this)
+                       div.innerHTML = "";
+                       div.style.width = div.style.padding = "1px";
+                       div.style.border = 0;
+                       div.style.overflow = "hidden";
+                       div.style.display = "inline";
+                       div.style.zoom = 1;
+                       support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+                       // Check if elements with layout shrink-wrap their children
+                       // (IE 6 does this)
+                       div.style.display = "block";
+                       div.style.overflow = "visible";
+                       div.innerHTML = "<div style='width:5px;'></div>";
+                       support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+               }
+
+               div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility;
+               div.innerHTML = html;
+
+               outer = div.firstChild;
+               inner = outer.firstChild;
+               td = outer.nextSibling.firstChild.firstChild;
+
+               offsetSupport = {
+                       doesNotAddBorder: ( inner.offsetTop !== 5 ),
+                       doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
+               };
+
+               inner.style.position = "fixed";
+               inner.style.top = "20px";
+
+               // safari subtracts parent border width here which is 5px
+               offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
+               inner.style.position = inner.style.top = "";
+
+               outer.style.overflow = "hidden";
+               outer.style.position = "relative";
+
+               offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
+               offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
+
+               if ( window.getComputedStyle ) {
+                       div.style.marginTop = "1%";
+                       support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%";
+               }
+
+               if ( typeof container.style.zoom !== "undefined" ) {
+                       container.style.zoom = 1;
+               }
+
+               body.removeChild( container );
+               marginDiv = div = container = null;
+
+               jQuery.extend( support, offsetSupport );
+       });
+
+       return support;
+})();
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+       rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+       cache: {},
+
+       // Please use with caution
+       uuid: 0,
+
+       // Unique for each copy of jQuery on the page
+       // Non-digits removed to match rinlinejQuery
+       expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+       // The following elements throw uncatchable exceptions if you
+       // attempt to add expando properties to them.
+       noData: {
+               "embed": true,
+               // Ban all objects except for Flash (which handle expandos)
+               "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+               "applet": true
+       },
+
+       hasData: function( elem ) {
+               elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+               return !!elem && !isEmptyDataObject( elem );
+       },
+
+       data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+               if ( !jQuery.acceptData( elem ) ) {
+                       return;
+               }
+
+               var privateCache, thisCache, ret,
+                       internalKey = jQuery.expando,
+                       getByName = typeof name === "string",
+
+                       // We have to handle DOM nodes and JS objects differently because IE6-7
+                       // can't GC object references properly across the DOM-JS boundary
+                       isNode = elem.nodeType,
+
+                       // Only DOM nodes need the global jQuery cache; JS object data is
+                       // attached directly to the object so GC can occur automatically
+                       cache = isNode ? jQuery.cache : elem,
+
+                       // Only defining an ID for JS objects if its cache already exists allows
+                       // the code to shortcut on the same path as a DOM node with no cache
+                       id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
+                       isEvents = name === "events";
+
+               // Avoid doing any more work than we need to when trying to get data on an
+               // object that has no data at all
+               if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
+                       return;
+               }
+
+               if ( !id ) {
+                       // Only DOM nodes need a new unique ID for each element since their data
+                       // ends up in the global cache
+                       if ( isNode ) {
+                               elem[ internalKey ] = id = ++jQuery.uuid;
+                       } else {
+                               id = internalKey;
+                       }
+               }
+
+               if ( !cache[ id ] ) {
+                       cache[ id ] = {};
+
+                       // Avoids exposing jQuery metadata on plain JS objects when the object
+                       // is serialized using JSON.stringify
+                       if ( !isNode ) {
+                               cache[ id ].toJSON = jQuery.noop;
+                       }
+               }
+
+               // An object can be passed to jQuery.data instead of a key/value pair; this gets
+               // shallow copied over onto the existing cache
+               if ( typeof name === "object" || typeof name === "function" ) {
+                       if ( pvt ) {
+                               cache[ id ] = jQuery.extend( cache[ id ], name );
+                       } else {
+                               cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+                       }
+               }
+
+               privateCache = thisCache = cache[ id ];
+
+               // jQuery data() is stored in a separate object inside the object's internal data
+               // cache in order to avoid key collisions between internal data and user-defined
+               // data.
+               if ( !pvt ) {
+                       if ( !thisCache.data ) {
+                               thisCache.data = {};
+                       }
+
+                       thisCache = thisCache.data;
+               }
+
+               if ( data !== undefined ) {
+                       thisCache[ jQuery.camelCase( name ) ] = data;
+               }
+
+               // Users should not attempt to inspect the internal events object using jQuery.data,
+               // it is undocumented and subject to change. But does anyone listen? No.
+               if ( isEvents && !thisCache[ name ] ) {
+                       return privateCache.events;
+               }
+
+               // Check for both converted-to-camel and non-converted data property names
+               // If a data property was specified
+               if ( getByName ) {
+
+                       // First Try to find as-is property data
+                       ret = thisCache[ name ];
+
+                       // Test for null|undefined property data
+                       if ( ret == null ) {
+
+                               // Try to find the camelCased property
+                               ret = thisCache[ jQuery.camelCase( name ) ];
+                       }
+               } else {
+                       ret = thisCache;
+               }
+
+               return ret;
+       },
+
+       removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+               if ( !jQuery.acceptData( elem ) ) {
+                       return;
+               }
+
+               var thisCache, i, l,
+
+                       // Reference to internal data cache key
+                       internalKey = jQuery.expando,
+
+                       isNode = elem.nodeType,
+
+                       // See jQuery.data for more information
+                       cache = isNode ? jQuery.cache : elem,
+
+                       // See jQuery.data for more information
+                       id = isNode ? elem[ internalKey ] : internalKey;
+
+               // If there is already no cache entry for this object, there is no
+               // purpose in continuing
+               if ( !cache[ id ] ) {
+                       return;
+               }
+
+               if ( name ) {
+
+                       thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+                       if ( thisCache ) {
+
+                               // Support array or space separated string names for data keys
+                               if ( !jQuery.isArray( name ) ) {
+
+                                       // try the string as a key before any manipulation
+                                       if ( name in thisCache ) {
+                                               name = [ name ];
+                                       } else {
+
+                                               // split the camel cased version by spaces unless a key with the spaces exists
+                                               name = jQuery.camelCase( name );
+                                               if ( name in thisCache ) {
+                                                       name = [ name ];
+                                               } else {
+                                                       name = name.split( " " );
+                                               }
+                                       }
+                               }
+
+                               for ( i = 0, l = name.length; i < l; i++ ) {
+                                       delete thisCache[ name[i] ];
+                               }
+
+                               // If there is no data left in the cache, we want to continue
+                               // and let the cache object itself get destroyed
+                               if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+                                       return;
+                               }
+                       }
+               }
+
+               // See jQuery.data for more information
+               if ( !pvt ) {
+                       delete cache[ id ].data;
+
+                       // Don't destroy the parent cache unless the internal data object
+                       // had been the only thing left in it
+                       if ( !isEmptyDataObject(cache[ id ]) ) {
+                               return;
+                       }
+               }
+
+               // Browsers that fail expando deletion also refuse to delete expandos on
+               // the window, but it will allow it on all other JS objects; other browsers
+               // don't care
+               // Ensure that `cache` is not a window object #10080
+               if ( jQuery.support.deleteExpando || !cache.setInterval ) {
+                       delete cache[ id ];
+               } else {
+                       cache[ id ] = null;
+               }
+
+               // We destroyed the cache and need to eliminate the expando on the node to avoid
+               // false lookups in the cache for entries that no longer exist
+               if ( isNode ) {
+                       // IE does not allow us to delete expando properties from nodes,
+                       // nor does it have a removeAttribute function on Document nodes;
+                       // we must handle all of these cases
+                       if ( jQuery.support.deleteExpando ) {
+                               delete elem[ internalKey ];
+                       } else if ( elem.removeAttribute ) {
+                               elem.removeAttribute( internalKey );
+                       } else {
+                               elem[ internalKey ] = null;
+                       }
+               }
+       },
+
+       // For internal use only.
+       _data: function( elem, name, data ) {
+               return jQuery.data( elem, name, data, true );
+       },
+
+       // A method for determining if a DOM node can handle the data expando
+       acceptData: function( elem ) {
+               if ( elem.nodeName ) {
+                       var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+                       if ( match ) {
+                               return !(match === true || elem.getAttribute("classid") !== match);
+                       }
+               }
+
+               return true;
+       }
+});
+
+jQuery.fn.extend({
+       data: function( key, value ) {
+               var parts, part, attr, name, l,
+                       elem = this[0],
+                       i = 0,
+                       data = null;
+
+               // Gets all values
+               if ( key === undefined ) {
+                       if ( this.length ) {
+                               data = jQuery.data( elem );
+
+                               if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+                                       attr = elem.attributes;
+                                       for ( l = attr.length; i < l; i++ ) {
+                                               name = attr[i].name;
+
+                                               if ( name.indexOf( "data-" ) === 0 ) {
+                                                       name = jQuery.camelCase( name.substring(5) );
+
+                                                       dataAttr( elem, name, data[ name ] );
+                                               }
+                                       }
+                                       jQuery._data( elem, "parsedAttrs", true );
+                               }
+                       }
+
+                       return data;
+               }
+
+               // Sets multiple values
+               if ( typeof key === "object" ) {
+                       return this.each(function() {
+                               jQuery.data( this, key );
+                       });
+               }
+
+               parts = key.split( ".", 2 );
+               parts[1] = parts[1] ? "." + parts[1] : "";
+               part = parts[1] + "!";
+
+               return jQuery.access( this, function( value ) {
+
+                       if ( value === undefined ) {
+                               data = this.triggerHandler( "getData" + part, [ parts[0] ] );
+
+                               // Try to fetch any internally stored data first
+                               if ( data === undefined && elem ) {
+                                       data = jQuery.data( elem, key );
+                                       data = dataAttr( elem, key, data );
+                               }
+
+                               return data === undefined && parts[1] ?
+                                       this.data( parts[0] ) :
+                                       data;
+                       }
+
+                       parts[1] = value;
+                       this.each(function() {
+                               var self = jQuery( this );
+
+                               self.triggerHandler( "setData" + part, parts );
+                               jQuery.data( this, key, value );
+                               self.triggerHandler( "changeData" + part, parts );
+                       });
+               }, null, value, arguments.length > 1, null, false );
+       },
+
+       removeData: function( key ) {
+               return this.each(function() {
+                       jQuery.removeData( this, key );
+               });
+       }
+});
+
+function dataAttr( elem, key, data ) {
+       // If nothing was found internally, try to fetch any
+       // data from the HTML5 data-* attribute
+       if ( data === undefined && elem.nodeType === 1 ) {
+
+               var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+               data = elem.getAttribute( name );
+
+               if ( typeof data === "string" ) {
+                       try {
+                               data = data === "true" ? true :
+                               data === "false" ? false :
+                               data === "null" ? null :
+                               jQuery.isNumeric( data ) ? +data :
+                                       rbrace.test( data ) ? jQuery.parseJSON( data ) :
+                                       data;
+                       } catch( e ) {}
+
+                       // Make sure we set the data so it isn't changed later
+                       jQuery.data( elem, key, data );
+
+               } else {
+                       data = undefined;
+               }
+       }
+
+       return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+       for ( var name in obj ) {
+
+               // if the public data object is empty, the private is still empty
+               if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+                       continue;
+               }
+               if ( name !== "toJSON" ) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+       var deferDataKey = type + "defer",
+               queueDataKey = type + "queue",
+               markDataKey = type + "mark",
+               defer = jQuery._data( elem, deferDataKey );
+       if ( defer &&
+               ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
+               ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
+               // Give room for hard-coded callbacks to fire first
+               // and eventually mark/queue something else on the element
+               setTimeout( function() {
+                       if ( !jQuery._data( elem, queueDataKey ) &&
+                               !jQuery._data( elem, markDataKey ) ) {
+                               jQuery.removeData( elem, deferDataKey, true );
+                               defer.fire();
+                       }
+               }, 0 );
+       }
+}
+
+jQuery.extend({
+
+       _mark: function( elem, type ) {
+               if ( elem ) {
+                       type = ( type || "fx" ) + "mark";
+                       jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
+               }
+       },
+
+       _unmark: function( force, elem, type ) {
+               if ( force !== true ) {
+                       type = elem;
+                       elem = force;
+                       force = false;
+               }
+               if ( elem ) {
+                       type = type || "fx";
+                       var key = type + "mark",
+                               count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
+                       if ( count ) {
+                               jQuery._data( elem, key, count );
+                       } else {
+                               jQuery.removeData( elem, key, true );
+                               handleQueueMarkDefer( elem, type, "mark" );
+                       }
+               }
+       },
+
+       queue: function( elem, type, data ) {
+               var q;
+               if ( elem ) {
+                       type = ( type || "fx" ) + "queue";
+                       q = jQuery._data( elem, type );
+
+                       // Speed up dequeue by getting out quickly if this is just a lookup
+                       if ( data ) {
+                               if ( !q || jQuery.isArray(data) ) {
+                                       q = jQuery._data( elem, type, jQuery.makeArray(data) );
+                               } else {
+                                       q.push( data );
+                               }
+                       }
+                       return q || [];
+               }
+       },
+
+       dequeue: function( elem, type ) {
+               type = type || "fx";
+
+               var queue = jQuery.queue( elem, type ),
+                       fn = queue.shift(),
+                       hooks = {};
+
+               // If the fx queue is dequeued, always remove the progress sentinel
+               if ( fn === "inprogress" ) {
+                       fn = queue.shift();
+               }
+
+               if ( fn ) {
+                       // Add a progress sentinel to prevent the fx queue from being
+                       // automatically dequeued
+                       if ( type === "fx" ) {
+                               queue.unshift( "inprogress" );
+                       }
+
+                       jQuery._data( elem, type + ".run", hooks );
+                       fn.call( elem, function() {
+                               jQuery.dequeue( elem, type );
+                       }, hooks );
+               }
+
+               if ( !queue.length ) {
+                       jQuery.removeData( elem, type + "queue " + type + ".run", true );
+                       handleQueueMarkDefer( elem, type, "queue" );
+               }
+       }
+});
+
+jQuery.fn.extend({
+       queue: function( type, data ) {
+               var setter = 2;
+
+               if ( typeof type !== "string" ) {
+                       data = type;
+                       type = "fx";
+                       setter--;
+               }
+
+               if ( arguments.length < setter ) {
+                       return jQuery.queue( this[0], type );
+               }
+
+               return data === undefined ?
+                       this :
+                       this.each(function() {
+                               var queue = jQuery.queue( this, type, data );
+
+                               if ( type === "fx" && queue[0] !== "inprogress" ) {
+                                       jQuery.dequeue( this, type );
+                               }
+                       });
+       },
+       dequeue: function( type ) {
+               return this.each(function() {
+                       jQuery.dequeue( this, type );
+               });
+       },
+       // Based off of the plugin by Clint Helfers, with permission.
+       // http://blindsignals.com/index.php/2009/07/jquery-delay/
+       delay: function( time, type ) {
+               time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+               type = type || "fx";
+
+               return this.queue( type, function( next, hooks ) {
+                       var timeout = setTimeout( next, time );
+                       hooks.stop = function() {
+                               clearTimeout( timeout );
+                       };
+               });
+       },
+       clearQueue: function( type ) {
+               return this.queue( type || "fx", [] );
+       },
+       // Get a promise resolved when queues of a certain type
+       // are emptied (fx is the type by default)
+       promise: function( type, object ) {
+               if ( typeof type !== "string" ) {
+                       object = type;
+                       type = undefined;
+               }
+               type = type || "fx";
+               var defer = jQuery.Deferred(),
+                       elements = this,
+                       i = elements.length,
+                       count = 1,
+                       deferDataKey = type + "defer",
+                       queueDataKey = type + "queue",
+                       markDataKey = type + "mark",
+                       tmp;
+               function resolve() {
+                       if ( !( --count ) ) {
+                               defer.resolveWith( elements, [ elements ] );
+                       }
+               }
+               while( i-- ) {
+                       if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+                                       ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+                                               jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+                                       jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
+                               count++;
+                               tmp.add( resolve );
+                       }
+               }
+               resolve();
+               return defer.promise( object );
+       }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+       rspace = /\s+/,
+       rreturn = /\r/g,
+       rtype = /^(?:button|input)$/i,
+       rfocusable = /^(?:button|input|object|select|textarea)$/i,
+       rclickable = /^a(?:rea)?$/i,
+       rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+       getSetAttribute = jQuery.support.getSetAttribute,
+       nodeHook, boolHook, fixSpecified;
+
+jQuery.fn.extend({
+       attr: function( name, value ) {
+               return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+       },
+
+       removeAttr: function( name ) {
+               return this.each(function() {
+                       jQuery.removeAttr( this, name );
+               });
+       },
+
+       prop: function( name, value ) {
+               return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+       },
+
+       removeProp: function( name ) {
+               name = jQuery.propFix[ name ] || name;
+               return this.each(function() {
+                       // try/catch handles cases where IE balks (such as removing a property on window)
+                       try {
+                               this[ name ] = undefined;
+                               delete this[ name ];
+                       } catch( e ) {}
+               });
+       },
+
+       addClass: function( value ) {
+               var classNames, i, l, elem,
+                       setClass, c, cl;
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function( j ) {
+                               jQuery( this ).addClass( value.call(this, j, this.className) );
+                       });
+               }
+
+               if ( value && typeof value === "string" ) {
+                       classNames = value.split( rspace );
+
+                       for ( i = 0, l = this.length; i < l; i++ ) {
+                               elem = this[ i ];
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !elem.className && classNames.length === 1 ) {
+                                               elem.className = value;
+
+                                       } else {
+                                               setClass = " " + elem.className + " ";
+
+                                               for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+                                                       if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
+                                                               setClass += classNames[ c ] + " ";
+                                                       }
+                                               }
+                                               elem.className = jQuery.trim( setClass );
+                                       }
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       removeClass: function( value ) {
+               var classNames, i, l, elem, className, c, cl;
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function( j ) {
+                               jQuery( this ).removeClass( value.call(this, j, this.className) );
+                       });
+               }
+
+               if ( (value && typeof value === "string") || value === undefined ) {
+                       classNames = ( value || "" ).split( rspace );
+
+                       for ( i = 0, l = this.length; i < l; i++ ) {
+                               elem = this[ i ];
+
+                               if ( elem.nodeType === 1 && elem.className ) {
+                                       if ( value ) {
+                                               className = (" " + elem.className + " ").replace( rclass, " " );
+                                               for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+                                                       className = className.replace(" " + classNames[ c ] + " ", " ");
+                                               }
+                                               elem.className = jQuery.trim( className );
+
+                                       } else {
+                                               elem.className = "";
+                                       }
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       toggleClass: function( value, stateVal ) {
+               var type = typeof value,
+                       isBool = typeof stateVal === "boolean";
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function( i ) {
+                               jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+                       });
+               }
+
+               return this.each(function() {
+                       if ( type === "string" ) {
+                               // toggle individual class names
+                               var className,
+                                       i = 0,
+                                       self = jQuery( this ),
+                                       state = stateVal,
+                                       classNames = value.split( rspace );
+
+                               while ( (className = classNames[ i++ ]) ) {
+                                       // check each className given, space seperated list
+                                       state = isBool ? state : !self.hasClass( className );
+                                       self[ state ? "addClass" : "removeClass" ]( className );
+                               }
+
+                       } else if ( type === "undefined" || type === "boolean" ) {
+                               if ( this.className ) {
+                                       // store className if set
+                                       jQuery._data( this, "__className__", this.className );
+                               }
+
+                               // toggle whole className
+                               this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+                       }
+               });
+       },
+
+       hasClass: function( selector ) {
+               var className = " " + selector + " ",
+                       i = 0,
+                       l = this.length;
+               for ( ; i < l; i++ ) {
+                       if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       },
+
+       val: function( value ) {
+               var hooks, ret, isFunction,
+                       elem = this[0];
+
+               if ( !arguments.length ) {
+                       if ( elem ) {
+                               hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+                               if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+                                       return ret;
+                               }
+
+                               ret = elem.value;
+
+                               return typeof ret === "string" ?
+                                       // handle most common string cases
+                                       ret.replace(rreturn, "") :
+                                       // handle cases where value is null/undef or number
+                                       ret == null ? "" : ret;
+                       }
+
+                       return;
+               }
+
+               isFunction = jQuery.isFunction( value );
+
+               return this.each(function( i ) {
+                       var self = jQuery(this), val;
+
+                       if ( this.nodeType !== 1 ) {
+                               return;
+                       }
+
+                       if ( isFunction ) {
+                               val = value.call( this, i, self.val() );
+                       } else {
+                               val = value;
+                       }
+
+                       // Treat null/undefined as ""; convert numbers to string
+                       if ( val == null ) {
+                               val = "";
+                       } else if ( typeof val === "number" ) {
+                               val += "";
+                       } else if ( jQuery.isArray( val ) ) {
+                               val = jQuery.map(val, function ( value ) {
+                                       return value == null ? "" : value + "";
+                               });
+                       }
+
+                       hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+                       // If set returns undefined, fall back to normal setting
+                       if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+                               this.value = val;
+                       }
+               });
+       }
+});
+
+jQuery.extend({
+       valHooks: {
+               option: {
+                       get: function( elem ) {
+                               // attributes.value is undefined in Blackberry 4.7 but
+                               // uses .value. See #6932
+                               var val = elem.attributes.value;
+                               return !val || val.specified ? elem.value : elem.text;
+                       }
+               },
+               select: {
+                       get: function( elem ) {
+                               var value, i, max, option,
+                                       index = elem.selectedIndex,
+                                       values = [],
+                                       options = elem.options,
+                                       one = elem.type === "select-one";
+
+                               // Nothing was selected
+                               if ( index < 0 ) {
+                                       return null;
+                               }
+
+                               // Loop through all the selected options
+                               i = one ? index : 0;
+                               max = one ? index + 1 : options.length;
+                               for ( ; i < max; i++ ) {
+                                       option = options[ i ];
+
+                                       // Don't return options that are disabled or in a disabled optgroup
+                                       if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+                                                       (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+                                               // Get the specific value for the option
+                                               value = jQuery( option ).val();
+
+                                               // We don't need an array for one selects
+                                               if ( one ) {
+                                                       return value;
+                                               }
+
+                                               // Multi-Selects return an array
+                                               values.push( value );
+                                       }
+                               }
+
+                               // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+                               if ( one && !values.length && options.length ) {
+                                       return jQuery( options[ index ] ).val();
+                               }
+
+                               return values;
+                       },
+
+                       set: function( elem, value ) {
+                               var values = jQuery.makeArray( value );
+
+                               jQuery(elem).find("option").each(function() {
+                                       this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+                               });
+
+                               if ( !values.length ) {
+                                       elem.selectedIndex = -1;
+                               }
+                               return values;
+                       }
+               }
+       },
+
+       attrFn: {
+               val: true,
+               css: true,
+               html: true,
+               text: true,
+               data: true,
+               width: true,
+               height: true,
+               offset: true
+       },
+
+       attr: function( elem, name, value, pass ) {
+               var ret, hooks, notxml,
+                       nType = elem.nodeType;
+
+               // don't get/set attributes on text, comment and attribute nodes
+               if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+                       return;
+               }
+
+               if ( pass && name in jQuery.attrFn ) {
+                       return jQuery( elem )[ name ]( value );
+               }
+
+               // Fallback to prop when attributes are not supported
+               if ( typeof elem.getAttribute === "undefined" ) {
+                       return jQuery.prop( elem, name, value );
+               }
+
+               notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+               // All attributes are lowercase
+               // Grab necessary hook if one is defined
+               if ( notxml ) {
+                       name = name.toLowerCase();
+                       hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+               }
+
+               if ( value !== undefined ) {
+
+                       if ( value === null ) {
+                               jQuery.removeAttr( elem, name );
+                               return;
+
+                       } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+                               return ret;
+
+                       } else {
+                               elem.setAttribute( name, "" + value );
+                               return value;
+                       }
+
+               } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+                       return ret;
+
+               } else {
+
+                       ret = elem.getAttribute( name );
+
+                       // Non-existent attributes return null, we normalize to undefined
+                       return ret === null ?
+                               undefined :
+                               ret;
+               }
+       },
+
+       removeAttr: function( elem, value ) {
+               var propName, attrNames, name, l, isBool,
+                       i = 0;
+
+               if ( value && elem.nodeType === 1 ) {
+                       attrNames = value.toLowerCase().split( rspace );
+                       l = attrNames.length;
+
+                       for ( ; i < l; i++ ) {
+                               name = attrNames[ i ];
+
+                               if ( name ) {
+                                       propName = jQuery.propFix[ name ] || name;
+                                       isBool = rboolean.test( name );
+
+                                       // See #9699 for explanation of this approach (setting first, then removal)
+                                       // Do not do this for boolean attributes (see #10870)
+                                       if ( !isBool ) {
+                                               jQuery.attr( elem, name, "" );
+                                       }
+                                       elem.removeAttribute( getSetAttribute ? name : propName );
+
+                                       // Set corresponding property to false for boolean attributes
+                                       if ( isBool && propName in elem ) {
+                                               elem[ propName ] = false;
+                                       }
+                               }
+                       }
+               }
+       },
+
+       attrHooks: {
+               type: {
+                       set: function( elem, value ) {
+                               // We can't allow the type property to be changed (since it causes problems in IE)
+                               if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+                                       jQuery.error( "type property can't be changed" );
+                               } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+                                       // Setting the type on a radio button after the value resets the value in IE6-9
+                                       // Reset value to it's default in case type is set after value
+                                       // This is for element creation
+                                       var val = elem.value;
+                                       elem.setAttribute( "type", value );
+                                       if ( val ) {
+                                               elem.value = val;
+                                       }
+                                       return value;
+                               }
+                       }
+               },
+               // Use the value property for back compat
+               // Use the nodeHook for button elements in IE6/7 (#1954)
+               value: {
+                       get: function( elem, name ) {
+                               if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+                                       return nodeHook.get( elem, name );
+                               }
+                               return name in elem ?
+                                       elem.value :
+                                       null;
+                       },
+                       set: function( elem, value, name ) {
+                               if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+                                       return nodeHook.set( elem, value, name );
+                               }
+                               // Does not return so that setAttribute is also used
+                               elem.value = value;
+                       }
+               }
+       },
+
+       propFix: {
+               tabindex: "tabIndex",
+               readonly: "readOnly",
+               "for": "htmlFor",
+               "class": "className",
+               maxlength: "maxLength",
+               cellspacing: "cellSpacing",
+               cellpadding: "cellPadding",
+               rowspan: "rowSpan",
+               colspan: "colSpan",
+               usemap: "useMap",
+               frameborder: "frameBorder",
+               contenteditable: "contentEditable"
+       },
+
+       prop: function( elem, name, value ) {
+               var ret, hooks, notxml,
+                       nType = elem.nodeType;
+
+               // don't get/set properties on text, comment and attribute nodes
+               if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+                       return;
+               }
+
+               notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+               if ( notxml ) {
+                       // Fix name and attach hooks
+                       name = jQuery.propFix[ name ] || name;
+                       hooks = jQuery.propHooks[ name ];
+               }
+
+               if ( value !== undefined ) {
+                       if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+                               return ret;
+
+                       } else {
+                               return ( elem[ name ] = value );
+                       }
+
+               } else {
+                       if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+                               return ret;
+
+                       } else {
+                               return elem[ name ];
+                       }
+               }
+       },
+
+       propHooks: {
+               tabIndex: {
+                       get: function( elem ) {
+                               // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+                               // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+                               var attributeNode = elem.getAttributeNode("tabindex");
+
+                               return attributeNode && attributeNode.specified ?
+                                       parseInt( attributeNode.value, 10 ) :
+                                       rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+                                               0 :
+                                               undefined;
+                       }
+               }
+       }
+});
+
+// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
+jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
+
+// Hook for boolean attributes
+boolHook = {
+       get: function( elem, name ) {
+               // Align boolean attributes with corresponding properties
+               // Fall back to attribute presence where some booleans are not supported
+               var attrNode,
+                       property = jQuery.prop( elem, name );
+               return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+                       name.toLowerCase() :
+                       undefined;
+       },
+       set: function( elem, value, name ) {
+               var propName;
+               if ( value === false ) {
+                       // Remove boolean attributes when set to false
+                       jQuery.removeAttr( elem, name );
+               } else {
+                       // value is true since we know at this point it's type boolean and not false
+                       // Set boolean attributes to the same name and set the DOM property
+                       propName = jQuery.propFix[ name ] || name;
+                       if ( propName in elem ) {
+                               // Only set the IDL specifically if it already exists on the element
+                               elem[ propName ] = true;
+                       }
+
+                       elem.setAttribute( name, name.toLowerCase() );
+               }
+               return name;
+       }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+       fixSpecified = {
+               name: true,
+               id: true,
+               coords: true
+       };
+
+       // Use this for any attribute in IE6/7
+       // This fixes almost every IE6/7 issue
+       nodeHook = jQuery.valHooks.button = {
+               get: function( elem, name ) {
+                       var ret;
+                       ret = elem.getAttributeNode( name );
+                       return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
+                               ret.nodeValue :
+                               undefined;
+               },
+               set: function( elem, value, name ) {
+                       // Set the existing or create a new attribute node
+                       var ret = elem.getAttributeNode( name );
+                       if ( !ret ) {
+                               ret = document.createAttribute( name );
+                               elem.setAttributeNode( ret );
+                       }
+                       return ( ret.nodeValue = value + "" );
+               }
+       };
+
+       // Apply the nodeHook to tabindex
+       jQuery.attrHooks.tabindex.set = nodeHook.set;
+
+       // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+       // This is for removals
+       jQuery.each([ "width", "height" ], function( i, name ) {
+               jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+                       set: function( elem, value ) {
+                               if ( value === "" ) {
+                                       elem.setAttribute( name, "auto" );
+                                       return value;
+                               }
+                       }
+               });
+       });
+
+       // Set contenteditable to false on removals(#10429)
+       // Setting to empty string throws an error as an invalid value
+       jQuery.attrHooks.contenteditable = {
+               get: nodeHook.get,
+               set: function( elem, value, name ) {
+                       if ( value === "" ) {
+                               value = "false";
+                       }
+                       nodeHook.set( elem, value, name );
+               }
+       };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+       jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+               jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+                       get: function( elem ) {
+                               var ret = elem.getAttribute( name, 2 );
+                               return ret === null ? undefined : ret;
+                       }
+               });
+       });
+}
+
+if ( !jQuery.support.style ) {
+       jQuery.attrHooks.style = {
+               get: function( elem ) {
+                       // Return undefined in the case of empty string
+                       // Normalize to lowercase since IE uppercases css property names
+                       return elem.style.cssText.toLowerCase() || undefined;
+               },
+               set: function( elem, value ) {
+                       return ( elem.style.cssText = "" + value );
+               }
+       };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+       jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+               get: function( elem ) {
+                       var parent = elem.parentNode;
+
+                       if ( parent ) {
+                               parent.selectedIndex;
+
+                               // Make sure that it also works with optgroups, see #5701
+                               if ( parent.parentNode ) {
+                                       parent.parentNode.selectedIndex;
+                               }
+                       }
+                       return null;
+               }
+       });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+       jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+       jQuery.each([ "radio", "checkbox" ], function() {
+               jQuery.valHooks[ this ] = {
+                       get: function( elem ) {
+                               // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+                               return elem.getAttribute("value") === null ? "on" : elem.value;
+                       }
+               };
+       });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+       jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+               set: function( elem, value ) {
+                       if ( jQuery.isArray( value ) ) {
+                               return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+                       }
+               }
+       });
+});
+
+
+
+
+var rformElems = /^(?:textarea|input|select)$/i,
+       rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
+       rhoverHack = /(?:^|\s)hover(\.\S+)?\b/,
+       rkeyEvent = /^key/,
+       rmouseEvent = /^(?:mouse|contextmenu)|click/,
+       rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+       rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
+       quickParse = function( selector ) {
+               var quick = rquickIs.exec( selector );
+               if ( quick ) {
+                       //   0  1    2   3
+                       // [ _, tag, id, class ]
+                       quick[1] = ( quick[1] || "" ).toLowerCase();
+                       quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
+               }
+               return quick;
+       },
+       quickIs = function( elem, m ) {
+               var attrs = elem.attributes || {};
+               return (
+                       (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
+                       (!m[2] || (attrs.id || {}).value === m[2]) &&
+                       (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
+               );
+       },
+       hoverHack = function( events ) {
+               return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+       };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+       add: function( elem, types, handler, data, selector ) {
+
+               var elemData, eventHandle, events,
+                       t, tns, type, namespaces, handleObj,
+                       handleObjIn, quick, handlers, special;
+
+               // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+               if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
+                       return;
+               }
+
+               // Caller can pass in an object of custom data in lieu of the handler
+               if ( handler.handler ) {
+                       handleObjIn = handler;
+                       handler = handleObjIn.handler;
+                       selector = handleObjIn.selector;
+               }
+
+               // Make sure that the handler has a unique ID, used to find/remove it later
+               if ( !handler.guid ) {
+                       handler.guid = jQuery.guid++;
+               }
+
+               // Init the element's event structure and main handler, if this is the first
+               events = elemData.events;
+               if ( !events ) {
+                       elemData.events = events = {};
+               }
+               eventHandle = elemData.handle;
+               if ( !eventHandle ) {
+                       elemData.handle = eventHandle = function( e ) {
+                               // Discard the second event of a jQuery.event.trigger() and
+                               // when an event is called after a page has unloaded
+                               return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+                                       jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+                                       undefined;
+                       };
+                       // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+                       eventHandle.elem = elem;
+               }
+
+               // Handle multiple events separated by a space
+               // jQuery(...).bind("mouseover mouseout", fn);
+               types = jQuery.trim( hoverHack(types) ).split( " " );
+               for ( t = 0; t < types.length; t++ ) {
+
+                       tns = rtypenamespace.exec( types[t] ) || [];
+                       type = tns[1];
+                       namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+                       // If event changes its type, use the special event handlers for the changed type
+                       special = jQuery.event.special[ type ] || {};
+
+                       // If selector defined, determine special event api type, otherwise given type
+                       type = ( selector ? special.delegateType : special.bindType ) || type;
+
+                       // Update special based on newly reset type
+                       special = jQuery.event.special[ type ] || {};
+
+                       // handleObj is passed to all event handlers
+                       handleObj = jQuery.extend({
+                               type: type,
+                               origType: tns[1],
+                               data: data,
+                               handler: handler,
+                               guid: handler.guid,
+                               selector: selector,
+                               quick: selector && quickParse( selector ),
+                               namespace: namespaces.join(".")
+                       }, handleObjIn );
+
+                       // Init the event handler queue if we're the first
+                       handlers = events[ type ];
+                       if ( !handlers ) {
+                               handlers = events[ type ] = [];
+                               handlers.delegateCount = 0;
+
+                               // Only use addEventListener/attachEvent if the special events handler returns false
+                               if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+                                       // Bind the global event handler to the element
+                                       if ( elem.addEventListener ) {
+                                               elem.addEventListener( type, eventHandle, false );
+
+                                       } else if ( elem.attachEvent ) {
+                                               elem.attachEvent( "on" + type, eventHandle );
+                                       }
+                               }
+                       }
+
+                       if ( special.add ) {
+                               special.add.call( elem, handleObj );
+
+                               if ( !handleObj.handler.guid ) {
+                                       handleObj.handler.guid = handler.guid;
+                               }
+                       }
+
+                       // Add to the element's handler list, delegates in front
+                       if ( selector ) {
+                               handlers.splice( handlers.delegateCount++, 0, handleObj );
+                       } else {
+                               handlers.push( handleObj );
+                       }
+
+                       // Keep track of which events have ever been used, for event optimization
+                       jQuery.event.global[ type ] = true;
+               }
+
+               // Nullify elem to prevent memory leaks in IE
+               elem = null;
+       },
+
+       global: {},
+
+       // Detach an event or set of events from an element
+       remove: function( elem, types, handler, selector, mappedTypes ) {
+
+               var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+                       t, tns, type, origType, namespaces, origCount,
+                       j, events, special, handle, eventType, handleObj;
+
+               if ( !elemData || !(events = elemData.events) ) {
+                       return;
+               }
+
+               // Once for each type.namespace in types; type may be omitted
+               types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+               for ( t = 0; t < types.length; t++ ) {
+                       tns = rtypenamespace.exec( types[t] ) || [];
+                       type = origType = tns[1];
+                       namespaces = tns[2];
+
+                       // Unbind all events (on this namespace, if provided) for the element
+                       if ( !type ) {
+                               for ( type in events ) {
+                                       jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+                               }
+                               continue;
+                       }
+
+                       special = jQuery.event.special[ type ] || {};
+                       type = ( selector? special.delegateType : special.bindType ) || type;
+                       eventType = events[ type ] || [];
+                       origCount = eventType.length;
+                       namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+
+                       // Remove matching events
+                       for ( j = 0; j < eventType.length; j++ ) {
+                               handleObj = eventType[ j ];
+
+                               if ( ( mappedTypes || origType === handleObj.origType ) &&
+                                        ( !handler || handler.guid === handleObj.guid ) &&
+                                        ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+                                        ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+                                       eventType.splice( j--, 1 );
+
+                                       if ( handleObj.selector ) {
+                                               eventType.delegateCount--;
+                                       }
+                                       if ( special.remove ) {
+                                               special.remove.call( elem, handleObj );
+                                       }
+                               }
+                       }
+
+                       // Remove generic event handler if we removed something and no more handlers exist
+                       // (avoids potential for endless recursion during removal of special event handlers)
+                       if ( eventType.length === 0 && origCount !== eventType.length ) {
+                               if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+                                       jQuery.removeEvent( elem, type, elemData.handle );
+                               }
+
+                               delete events[ type ];
+                       }
+               }
+
+               // Remove the expando if it's no longer used
+               if ( jQuery.isEmptyObject( events ) ) {
+                       handle = elemData.handle;
+                       if ( handle ) {
+                               handle.elem = null;
+                       }
+
+                       // removeData also checks for emptiness and clears the expando if empty
+                       // so use it instead of delete
+                       jQuery.removeData( elem, [ "events", "handle" ], true );
+               }
+       },
+
+       // Events that are safe to short-circuit if no handlers are attached.
+       // Native DOM events should not be added, they may have inline handlers.
+       customEvent: {
+               "getData": true,
+               "setData": true,
+               "changeData": true
+       },
+
+       trigger: function( event, data, elem, onlyHandlers ) {
+               // Don't do events on text and comment nodes
+               if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+                       return;
+               }
+
+               // Event object or event type
+               var type = event.type || event,
+                       namespaces = [],
+                       cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
+
+               // focus/blur morphs to focusin/out; ensure we're not firing them right now
+               if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+                       return;
+               }
+
+               if ( type.indexOf( "!" ) >= 0 ) {
+                       // Exclusive events trigger only for the exact event (no namespaces)
+                       type = type.slice(0, -1);
+                       exclusive = true;
+               }
+
+               if ( type.indexOf( "." ) >= 0 ) {
+                       // Namespaced trigger; create a regexp to match event type in handle()
+                       namespaces = type.split(".");
+                       type = namespaces.shift();
+                       namespaces.sort();
+               }
+
+               if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+                       // No jQuery handlers for this event type, and it can't have inline handlers
+                       return;
+               }
+
+               // Caller can pass in an Event, Object, or just an event type string
+               event = typeof event === "object" ?
+                       // jQuery.Event object
+                       event[ jQuery.expando ] ? event :
+                       // Object literal
+                       new jQuery.Event( type, event ) :
+                       // Just the event type (string)
+                       new jQuery.Event( type );
+
+               event.type = type;
+               event.isTrigger = true;
+               event.exclusive = exclusive;
+               event.namespace = namespaces.join( "." );
+               event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+               ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+               // Handle a global trigger
+               if ( !elem ) {
+
+                       // TODO: Stop taunting the data cache; remove global events and always attach to document
+                       cache = jQuery.cache;
+                       for ( i in cache ) {
+                               if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+                                       jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+                               }
+                       }
+                       return;
+               }
+
+               // Clean up the event in case it is being reused
+               event.result = undefined;
+               if ( !event.target ) {
+                       event.target = elem;
+               }
+
+               // Clone any incoming data and prepend the event, creating the handler arg list
+               data = data != null ? jQuery.makeArray( data ) : [];
+               data.unshift( event );
+
+               // Allow special events to draw outside the lines
+               special = jQuery.event.special[ type ] || {};
+               if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+                       return;
+               }
+
+               // Determine event propagation path in advance, per W3C events spec (#9951)
+               // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+               eventPath = [[ elem, special.bindType || type ]];
+               if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+                       bubbleType = special.delegateType || type;
+                       cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+                       old = null;
+                       for ( ; cur; cur = cur.parentNode ) {
+                               eventPath.push([ cur, bubbleType ]);
+                               old = cur;
+                       }
+
+                       // Only add window if we got to document (e.g., not plain obj or detached DOM)
+                       if ( old && old === elem.ownerDocument ) {
+                               eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+                       }
+               }
+
+               // Fire handlers on the event path
+               for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+                       cur = eventPath[i][0];
+                       event.type = eventPath[i][1];
+
+                       handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+                       if ( handle ) {
+                               handle.apply( cur, data );
+                       }
+                       // Note that this is a bare JS function and not a jQuery handler
+                       handle = ontype && cur[ ontype ];
+                       if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
+                               event.preventDefault();
+                       }
+               }
+               event.type = type;
+
+               // If nobody prevented the default action, do it now
+               if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+                       if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+                               !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+                               // Call a native DOM method on the target with the same name name as the event.
+                               // Can't use an .isFunction() check here because IE6/7 fails that test.
+                               // Don't do default actions on window, that's where global variables be (#6170)
+                               // IE<9 dies on focus/blur to hidden element (#1486)
+                               if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
+
+                                       // Don't re-trigger an onFOO event when we call its FOO() method
+                                       old = elem[ ontype ];
+
+                                       if ( old ) {
+                                               elem[ ontype ] = null;
+                                       }
+
+                                       // Prevent re-triggering of the same event, since we already bubbled it above
+                                       jQuery.event.triggered = type;
+                                       elem[ type ]();
+                                       jQuery.event.triggered = undefined;
+
+                                       if ( old ) {
+                                               elem[ ontype ] = old;
+                                       }
+                               }
+                       }
+               }
+
+               return event.result;
+       },
+
+       dispatch: function( event ) {
+
+               // Make a writable jQuery.Event from the native event object
+               event = jQuery.event.fix( event || window.event );
+
+               var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+                       delegateCount = handlers.delegateCount,
+                       args = [].slice.call( arguments, 0 ),
+                       run_all = !event.exclusive && !event.namespace,
+                       special = jQuery.event.special[ event.type ] || {},
+                       handlerQueue = [],
+                       i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
+
+               // Use the fix-ed jQuery.Event rather than the (read-only) native event
+               args[0] = event;
+               event.delegateTarget = this;
+
+               // Call the preDispatch hook for the mapped type, and let it bail if desired
+               if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+                       return;
+               }
+
+               // Determine handlers that should run if there are delegated events
+               // Avoid non-left-click bubbling in Firefox (#3861)
+               if ( delegateCount && !(event.button && event.type === "click") ) {
+
+                       // Pregenerate a single jQuery object for reuse with .is()
+                       jqcur = jQuery(this);
+                       jqcur.context = this.ownerDocument || this;
+
+                       for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+
+                               // Don't process events on disabled elements (#6911, #8165)
+                               if ( cur.disabled !== true ) {
+                                       selMatch = {};
+                                       matches = [];
+                                       jqcur[0] = cur;
+                                       for ( i = 0; i < delegateCount; i++ ) {
+                                               handleObj = handlers[ i ];
+                                               sel = handleObj.selector;
+
+                                               if ( selMatch[ sel ] === undefined ) {
+                                                       selMatch[ sel ] = (
+                                                               handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
+                                                       );
+                                               }
+                                               if ( selMatch[ sel ] ) {
+                                                       matches.push( handleObj );
+                                               }
+                                       }
+                                       if ( matches.length ) {
+                                               handlerQueue.push({ elem: cur, matches: matches });
+                                       }
+                               }
+                       }
+               }
+
+               // Add the remaining (directly-bound) handlers
+               if ( handlers.length > delegateCount ) {
+                       handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+               }
+
+               // Run delegates first; they may want to stop propagation beneath us
+               for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+                       matched = handlerQueue[ i ];
+                       event.currentTarget = matched.elem;
+
+                       for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+                               handleObj = matched.matches[ j ];
+
+                               // Triggered event must either 1) be non-exclusive and have no namespace, or
+                               // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+                               if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+                                       event.data = handleObj.data;
+                                       event.handleObj = handleObj;
+
+                                       ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+                                                       .apply( matched.elem, args );
+
+                                       if ( ret !== undefined ) {
+                                               event.result = ret;
+                                               if ( ret === false ) {
+                                                       event.preventDefault();
+                                                       event.stopPropagation();
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // Call the postDispatch hook for the mapped type
+               if ( special.postDispatch ) {
+                       special.postDispatch.call( this, event );
+               }
+
+               return event.result;
+       },
+
+       // Includes some event props shared by KeyEvent and MouseEvent
+       // *** attrChange attrName relatedNode srcElement  are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+       props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+       fixHooks: {},
+
+       keyHooks: {
+               props: "char charCode key keyCode".split(" "),
+               filter: function( event, original ) {
+
+                       // Add which for key events
+                       if ( event.which == null ) {
+                               event.which = original.charCode != null ? original.charCode : original.keyCode;
+                       }
+
+                       return event;
+               }
+       },
+
+       mouseHooks: {
+               props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+               filter: function( event, original ) {
+                       var eventDoc, doc, body,
+                               button = original.button,
+                               fromElement = original.fromElement;
+
+                       // Calculate pageX/Y if missing and clientX/Y available
+                       if ( event.pageX == null && original.clientX != null ) {
+                               eventDoc = event.target.ownerDocument || document;
+                               doc = eventDoc.documentElement;
+                               body = eventDoc.body;
+
+                               event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+                               event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+                       }
+
+                       // Add relatedTarget, if necessary
+                       if ( !event.relatedTarget && fromElement ) {
+                               event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+                       }
+
+                       // Add which for click: 1 === left; 2 === middle; 3 === right
+                       // Note: button is not normalized, so don't use it
+                       if ( !event.which && button !== undefined ) {
+                               event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+                       }
+
+                       return event;
+               }
+       },
+
+       fix: function( event ) {
+               if ( event[ jQuery.expando ] ) {
+                       return event;
+               }
+
+               // Create a writable copy of the event object and normalize some properties
+               var i, prop,
+                       originalEvent = event,
+                       fixHook = jQuery.event.fixHooks[ event.type ] || {},
+                       copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+               event = jQuery.Event( originalEvent );
+
+               for ( i = copy.length; i; ) {
+                       prop = copy[ --i ];
+                       event[ prop ] = originalEvent[ prop ];
+               }
+
+               // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+               if ( !event.target ) {
+                       event.target = originalEvent.srcElement || document;
+               }
+
+               // Target should not be a text node (#504, Safari)
+               if ( event.target.nodeType === 3 ) {
+                       event.target = event.target.parentNode;
+               }
+
+               // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
+               if ( event.metaKey === undefined ) {
+                       event.metaKey = event.ctrlKey;
+               }
+
+               return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+       },
+
+       special: {
+               ready: {
+                       // Make sure the ready event is setup
+                       setup: jQuery.bindReady
+               },
+
+               load: {
+                       // Prevent triggered image.load events from bubbling to window.load
+                       noBubble: true
+               },
+
+               focus: {
+                       delegateType: "focusin"
+               },
+               blur: {
+                       delegateType: "focusout"
+               },
+
+               beforeunload: {
+                       setup: function( data, namespaces, eventHandle ) {
+                               // We only want to do this special case on windows
+                               if ( jQuery.isWindow( this ) ) {
+                                       this.onbeforeunload = eventHandle;
+                               }
+                       },
+
+                       teardown: function( namespaces, eventHandle ) {
+                               if ( this.onbeforeunload === eventHandle ) {
+                                       this.onbeforeunload = null;
+                               }
+                       }
+               }
+       },
+
+       simulate: function( type, elem, event, bubble ) {
+               // Piggyback on a donor event to simulate a different one.
+               // Fake originalEvent to avoid donor's stopPropagation, but if the
+               // simulated event prevents default then we do the same on the donor.
+               var e = jQuery.extend(
+                       new jQuery.Event(),
+                       event,
+                       { type: type,
+                               isSimulated: true,
+                               originalEvent: {}
+                       }
+               );
+               if ( bubble ) {
+                       jQuery.event.trigger( e, null, elem );
+               } else {
+                       jQuery.event.dispatch.call( elem, e );
+               }
+               if ( e.isDefaultPrevented() ) {
+                       event.preventDefault();
+               }
+       }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+       function( elem, type, handle ) {
+               if ( elem.removeEventListener ) {
+                       elem.removeEventListener( type, handle, false );
+               }
+       } :
+       function( elem, type, handle ) {
+               if ( elem.detachEvent ) {
+                       elem.detachEvent( "on" + type, handle );
+               }
+       };
+
+jQuery.Event = function( src, props ) {
+       // Allow instantiation without the 'new' keyword
+       if ( !(this instanceof jQuery.Event) ) {
+               return new jQuery.Event( src, props );
+       }
+
+       // Event object
+       if ( src && src.type ) {
+               this.originalEvent = src;
+               this.type = src.type;
+
+               // Events bubbling up the document may have been marked as prevented
+               // by a handler lower down the tree; reflect the correct value.
+               this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+                       src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+       // Event type
+       } else {
+               this.type = src;
+       }
+
+       // Put explicitly provided properties onto the event object
+       if ( props ) {
+               jQuery.extend( this, props );
+       }
+
+       // Create a timestamp if incoming event doesn't have one
+       this.timeStamp = src && src.timeStamp || jQuery.now();
+
+       // Mark it as fixed
+       this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+       return false;
+}
+function returnTrue() {
+       return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+       preventDefault: function() {
+               this.isDefaultPrevented = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+
+               // if preventDefault exists run it on the original event
+               if ( e.preventDefault ) {
+                       e.preventDefault();
+
+               // otherwise set the returnValue property of the original event to false (IE)
+               } else {
+                       e.returnValue = false;
+               }
+       },
+       stopPropagation: function() {
+               this.isPropagationStopped = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+               // if stopPropagation exists run it on the original event
+               if ( e.stopPropagation ) {
+                       e.stopPropagation();
+               }
+               // otherwise set the cancelBubble property of the original event to true (IE)
+               e.cancelBubble = true;
+       },
+       stopImmediatePropagation: function() {
+               this.isImmediatePropagationStopped = returnTrue;
+               this.stopPropagation();
+       },
+       isDefaultPrevented: returnFalse,
+       isPropagationStopped: returnFalse,
+       isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+}, function( orig, fix ) {
+       jQuery.event.special[ orig ] = {
+               delegateType: fix,
+               bindType: fix,
+
+               handle: function( event ) {
+                       var target = this,
+                               related = event.relatedTarget,
+                               handleObj = event.handleObj,
+                               selector = handleObj.selector,
+                               ret;
+
+                       // For mousenter/leave call the handler if related is outside the target.
+                       // NB: No relatedTarget if the mouse left/entered the browser window
+                       if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+                               event.type = handleObj.origType;
+                               ret = handleObj.handler.apply( this, arguments );
+                               event.type = fix;
+                       }
+                       return ret;
+               }
+       };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+       jQuery.event.special.submit = {
+               setup: function() {
+                       // Only need this for delegated form submit events
+                       if ( jQuery.nodeName( this, "form" ) ) {
+                               return false;
+                       }
+
+                       // Lazy-add a submit handler when a descendant form may potentially be submitted
+                       jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+                               // Node name check avoids a VML-related crash in IE (#9807)
+                               var elem = e.target,
+                                       form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+                               if ( form && !form._submit_attached ) {
+                                       jQuery.event.add( form, "submit._submit", function( event ) {
+                                               event._submit_bubble = true;
+                                       });
+                                       form._submit_attached = true;
+                               }
+                       });
+                       // return undefined since we don't need an event listener
+               },
+
+               postDispatch: function( event ) {
+                       // If form was submitted by the user, bubble the event up the tree
+                       if ( event._submit_bubble ) {
+                               delete event._submit_bubble;
+                               if ( this.parentNode && !event.isTrigger ) {
+                                       jQuery.event.simulate( "submit", this.parentNode, event, true );
+                               }
+                       }
+               },
+
+               teardown: function() {
+                       // Only need this for delegated form submit events
+                       if ( jQuery.nodeName( this, "form" ) ) {
+                               return false;
+                       }
+
+                       // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+                       jQuery.event.remove( this, "._submit" );
+               }
+       };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+       jQuery.event.special.change = {
+
+               setup: function() {
+
+                       if ( rformElems.test( this.nodeName ) ) {
+                               // IE doesn't fire change on a check/radio until blur; trigger it on click
+                               // after a propertychange. Eat the blur-change in special.change.handle.
+                               // This still fires onchange a second time for check/radio after blur.
+                               if ( this.type === "checkbox" || this.type === "radio" ) {
+                                       jQuery.event.add( this, "propertychange._change", function( event ) {
+                                               if ( event.originalEvent.propertyName === "checked" ) {
+                                                       this._just_changed = true;
+                                               }
+                                       });
+                                       jQuery.event.add( this, "click._change", function( event ) {
+                                               if ( this._just_changed && !event.isTrigger ) {
+                                                       this._just_changed = false;
+                                                       jQuery.event.simulate( "change", this, event, true );
+                                               }
+                                       });
+                               }
+                               return false;
+                       }
+                       // Delegated event; lazy-add a change handler on descendant inputs
+                       jQuery.event.add( this, "beforeactivate._change", function( e ) {
+                               var elem = e.target;
+
+                               if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
+                                       jQuery.event.add( elem, "change._change", function( event ) {
+                                               if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+                                                       jQuery.event.simulate( "change", this.parentNode, event, true );
+                                               }
+                                       });
+                                       elem._change_attached = true;
+                               }
+                       });
+               },
+
+               handle: function( event ) {
+                       var elem = event.target;
+
+                       // Swallow native change events from checkbox/radio, we already triggered them above
+                       if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+                               return event.handleObj.handler.apply( this, arguments );
+                       }
+               },
+
+               teardown: function() {
+                       jQuery.event.remove( this, "._change" );
+
+                       return rformElems.test( this.nodeName );
+               }
+       };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+       jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+               // Attach a single capturing handler while someone wants focusin/focusout
+               var attaches = 0,
+                       handler = function( event ) {
+                               jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+                       };
+
+               jQuery.event.special[ fix ] = {
+                       setup: function() {
+                               if ( attaches++ === 0 ) {
+                                       document.addEventListener( orig, handler, true );
+                               }
+                       },
+                       teardown: function() {
+                               if ( --attaches === 0 ) {
+                                       document.removeEventListener( orig, handler, true );
+                               }
+                       }
+               };
+       });
+}
+
+jQuery.fn.extend({
+
+       on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+               var origFn, type;
+
+               // Types can be a map of types/handlers
+               if ( typeof types === "object" ) {
+                       // ( types-Object, selector, data )
+                       if ( typeof selector !== "string" ) { // && selector != null
+                               // ( types-Object, data )
+                               data = data || selector;
+                               selector = undefined;
+                       }
+                       for ( type in types ) {
+                               this.on( type, selector, data, types[ type ], one );
+                       }
+                       return this;
+               }
+
+               if ( data == null && fn == null ) {
+                       // ( types, fn )
+                       fn = selector;
+                       data = selector = undefined;
+               } else if ( fn == null ) {
+                       if ( typeof selector === "string" ) {
+                               // ( types, selector, fn )
+                               fn = data;
+                               data = undefined;
+                       } else {
+                               // ( types, data, fn )
+                               fn = data;
+                               data = selector;
+                               selector = undefined;
+                       }
+               }
+               if ( fn === false ) {
+                       fn = returnFalse;
+               } else if ( !fn ) {
+                       return this;
+               }
+
+               if ( one === 1 ) {
+                       origFn = fn;
+                       fn = function( event ) {
+                               // Can use an empty set, since event contains the info
+                               jQuery().off( event );
+                               return origFn.apply( this, arguments );
+                       };
+                       // Use same guid so caller can remove using origFn
+                       fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+               }
+               return this.each( function() {
+                       jQuery.event.add( this, types, fn, data, selector );
+               });
+       },
+       one: function( types, selector, data, fn ) {
+               return this.on( types, selector, data, fn, 1 );
+       },
+       off: function( types, selector, fn ) {
+               if ( types && types.preventDefault && types.handleObj ) {
+                       // ( event )  dispatched jQuery.Event
+                       var handleObj = types.handleObj;
+                       jQuery( types.delegateTarget ).off(
+                               handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+                               handleObj.selector,
+                               handleObj.handler
+                       );
+                       return this;
+               }
+               if ( typeof types === "object" ) {
+                       // ( types-object [, selector] )
+                       for ( var type in types ) {
+                               this.off( type, selector, types[ type ] );
+                       }
+                       return this;
+               }
+               if ( selector === false || typeof selector === "function" ) {
+                       // ( types [, fn] )
+                       fn = selector;
+                       selector = undefined;
+               }
+               if ( fn === false ) {
+                       fn = returnFalse;
+               }
+               return this.each(function() {
+                       jQuery.event.remove( this, types, fn, selector );
+               });
+       },
+
+       bind: function( types, data, fn ) {
+               return this.on( types, null, data, fn );
+       },
+       unbind: function( types, fn ) {
+               return this.off( types, null, fn );
+       },
+
+       live: function( types, data, fn ) {
+               jQuery( this.context ).on( types, this.selector, data, fn );
+               return this;
+       },
+       die: function( types, fn ) {
+               jQuery( this.context ).off( types, this.selector || "**", fn );
+               return this;
+       },
+
+       delegate: function( selector, types, data, fn ) {
+               return this.on( types, selector, data, fn );
+       },
+       undelegate: function( selector, types, fn ) {
+               // ( namespace ) or ( selector, types [, fn] )
+               return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
+       },
+
+       trigger: function( type, data ) {
+               return this.each(function() {
+                       jQuery.event.trigger( type, data, this );
+               });
+       },
+       triggerHandler: function( type, data ) {
+               if ( this[0] ) {
+                       return jQuery.event.trigger( type, data, this[0], true );
+               }
+       },
+
+       toggle: function( fn ) {
+               // Save reference to arguments for access in closure
+               var args = arguments,
+                       guid = fn.guid || jQuery.guid++,
+                       i = 0,
+                       toggler = function( event ) {
+                               // Figure out which function to execute
+                               var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+                               jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+                               // Make sure that clicks stop
+                               event.preventDefault();
+
+                               // and execute the function
+                               return args[ lastToggle ].apply( this, arguments ) || false;
+                       };
+
+               // link all the functions, so any of them can unbind this click handler
+               toggler.guid = guid;
+               while ( i < args.length ) {
+                       args[ i++ ].guid = guid;
+               }
+
+               return this.click( toggler );
+       },
+
+       hover: function( fnOver, fnOut ) {
+               return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+       }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+       "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+       "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+       // Handle event binding
+       jQuery.fn[ name ] = function( data, fn ) {
+               if ( fn == null ) {
+                       fn = data;
+                       data = null;
+               }
+
+               return arguments.length > 0 ?
+                       this.on( name, null, data, fn ) :
+                       this.trigger( name );
+       };
+
+       if ( jQuery.attrFn ) {
+               jQuery.attrFn[ name ] = true;
+       }
+
+       if ( rkeyEvent.test( name ) ) {
+               jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+       }
+
+       if ( rmouseEvent.test( name ) ) {
+               jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+       }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ *  Copyright 2011, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+       expando = "sizcache" + (Math.random() + '').replace('.', ''),
+       done = 0,
+       toString = Object.prototype.toString,
+       hasDuplicate = false,
+       baseHasDuplicate = true,
+       rBackslash = /\\/g,
+       rReturn = /\r\n/g,
+       rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+//   Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+       baseHasDuplicate = false;
+       return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+       results = results || [];
+       context = context || document;
+
+       var origContext = context;
+
+       if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+               return [];
+       }
+
+       if ( !selector || typeof selector !== "string" ) {
+               return results;
+       }
+
+       var m, set, checkSet, extra, ret, cur, pop, i,
+               prune = true,
+               contextXML = Sizzle.isXML( context ),
+               parts = [],
+               soFar = selector;
+
+       // Reset the position of the chunker regexp (start from head)
+       do {
+               chunker.exec( "" );
+               m = chunker.exec( soFar );
+
+               if ( m ) {
+                       soFar = m[3];
+
+                       parts.push( m[1] );
+
+                       if ( m[2] ) {
+                               extra = m[3];
+                               break;
+                       }
+               }
+       } while ( m );
+
+       if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+               if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+                       set = posProcess( parts[0] + parts[1], context, seed );
+
+               } else {
+                       set = Expr.relative[ parts[0] ] ?
+                               [ context ] :
+                               Sizzle( parts.shift(), context );
+
+                       while ( parts.length ) {
+                               selector = parts.shift();
+
+                               if ( Expr.relative[ selector ] ) {
+                                       selector += parts.shift();
+                               }
+
+                               set = posProcess( selector, set, seed );
+                       }
+               }
+
+       } else {
+               // Take a shortcut and set the context if the root selector is an ID
+               // (but not if it'll be faster if the inner selector is an ID)
+               if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+                               Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+                       ret = Sizzle.find( parts.shift(), context, contextXML );
+                       context = ret.expr ?
+                               Sizzle.filter( ret.expr, ret.set )[0] :
+                               ret.set[0];
+               }
+
+               if ( context ) {
+                       ret = seed ?
+                               { expr: parts.pop(), set: makeArray(seed) } :
+                               Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+                       set = ret.expr ?
+                               Sizzle.filter( ret.expr, ret.set ) :
+                               ret.set;
+
+                       if ( parts.length > 0 ) {
+                               checkSet = makeArray( set );
+
+                       } else {
+                               prune = false;
+                       }
+
+                       while ( parts.length ) {
+                               cur = parts.pop();
+                               pop = cur;
+
+                               if ( !Expr.relative[ cur ] ) {
+                                       cur = "";
+                               } else {
+                                       pop = parts.pop();
+                               }
+
+                               if ( pop == null ) {
+                                       pop = context;
+                               }
+
+                               Expr.relative[ cur ]( checkSet, pop, contextXML );
+                       }
+
+               } else {
+                       checkSet = parts = [];
+               }
+       }
+
+       if ( !checkSet ) {
+               checkSet = set;
+       }
+
+       if ( !checkSet ) {
+               Sizzle.error( cur || selector );
+       }
+
+       if ( toString.call(checkSet) === "[object Array]" ) {
+               if ( !prune ) {
+                       results.push.apply( results, checkSet );
+
+               } else if ( context && context.nodeType === 1 ) {
+                       for ( i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+                                       results.push( set[i] );
+                               }
+                       }
+
+               } else {
+                       for ( i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+                                       results.push( set[i] );
+                               }
+                       }
+               }
+
+       } else {
+               makeArray( checkSet, results );
+       }
+
+       if ( extra ) {
+               Sizzle( extra, origContext, results, seed );
+               Sizzle.uniqueSort( results );
+       }
+
+       return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+       if ( sortOrder ) {
+               hasDuplicate = baseHasDuplicate;
+               results.sort( sortOrder );
+
+               if ( hasDuplicate ) {
+                       for ( var i = 1; i < results.length; i++ ) {
+                               if ( results[i] === results[ i - 1 ] ) {
+                                       results.splice( i--, 1 );
+                               }
+                       }
+               }
+       }
+
+       return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+       return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+       return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+       var set, i, len, match, type, left;
+
+       if ( !expr ) {
+               return [];
+       }
+
+       for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+               type = Expr.order[i];
+
+               if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+                       left = match[1];
+                       match.splice( 1, 1 );
+
+                       if ( left.substr( left.length - 1 ) !== "\\" ) {
+                               match[1] = (match[1] || "").replace( rBackslash, "" );
+                               set = Expr.find[ type ]( match, context, isXML );
+
+                               if ( set != null ) {
+                                       expr = expr.replace( Expr.match[ type ], "" );
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if ( !set ) {
+               set = typeof context.getElementsByTagName !== "undefined" ?
+                       context.getElementsByTagName( "*" ) :
+                       [];
+       }
+
+       return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+       var match, anyFound,
+               type, found, item, filter, left,
+               i, pass,
+               old = expr,
+               result = [],
+               curLoop = set,
+               isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+       while ( expr && set.length ) {
+               for ( type in Expr.filter ) {
+                       if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+                               filter = Expr.filter[ type ];
+                               left = match[1];
+
+                               anyFound = false;
+
+                               match.splice(1,1);
+
+                               if ( left.substr( left.length - 1 ) === "\\" ) {
+                                       continue;
+                               }
+
+                               if ( curLoop === result ) {
+                                       result = [];
+                               }
+
+                               if ( Expr.preFilter[ type ] ) {
+                                       match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+                                       if ( !match ) {
+                                               anyFound = found = true;
+
+                                       } else if ( match === true ) {
+                                               continue;
+                                       }
+                               }
+
+                               if ( match ) {
+                                       for ( i = 0; (item = curLoop[i]) != null; i++ ) {
+                                               if ( item ) {
+                                                       found = filter( item, match, i, curLoop );
+                                                       pass = not ^ found;
+
+                                                       if ( inplace && found != null ) {
+                                                               if ( pass ) {
+                                                                       anyFound = true;
+
+                                                               } else {
+                                                                       curLoop[i] = false;
+                                                               }
+
+                                                       } else if ( pass ) {
+                                                               result.push( item );
+                                                               anyFound = true;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               if ( found !== undefined ) {
+                                       if ( !inplace ) {
+                                               curLoop = result;
+                                       }
+
+                                       expr = expr.replace( Expr.match[ type ], "" );
+
+                                       if ( !anyFound ) {
+                                               return [];
+                                       }
+
+                                       break;
+                               }
+                       }
+               }
+
+               // Improper expression
+               if ( expr === old ) {
+                       if ( anyFound == null ) {
+                               Sizzle.error( expr );
+
+                       } else {
+                               break;
+                       }
+               }
+
+               old = expr;
+       }
+
+       return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+       throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Utility function for retreiving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+var getText = Sizzle.getText = function( elem ) {
+    var i, node,
+               nodeType = elem.nodeType,
+               ret = "";
+
+       if ( nodeType ) {
+               if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+                       // Use textContent || innerText for elements
+                       if ( typeof elem.textContent === 'string' ) {
+                               return elem.textContent;
+                       } else if ( typeof elem.innerText === 'string' ) {
+                               // Replace IE's carriage returns
+                               return elem.innerText.replace( rReturn, '' );
+                       } else {
+                               // Traverse it's children
+                               for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+                                       ret += getText( elem );
+                               }
+                       }
+               } else if ( nodeType === 3 || nodeType === 4 ) {
+                       return elem.nodeValue;
+               }
+       } else {
+
+               // If no nodeType, this is expected to be an array
+               for ( i = 0; (node = elem[i]); i++ ) {
+                       // Do not traverse comment nodes
+                       if ( node.nodeType !== 8 ) {
+                               ret += getText( node );
+                       }
+               }
+       }
+       return ret;
+};
+
+var Expr = Sizzle.selectors = {
+       order: [ "ID", "NAME", "TAG" ],
+
+       match: {
+               ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+               CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+               NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+               ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+               TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+               CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+               POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+               PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+       },
+
+       leftMatch: {},
+
+       attrMap: {
+               "class": "className",
+               "for": "htmlFor"
+       },
+
+       attrHandle: {
+               href: function( elem ) {
+                       return elem.getAttribute( "href" );
+               },
+               type: function( elem ) {
+                       return elem.getAttribute( "type" );
+               }
+       },
+
+       relative: {
+               "+": function(checkSet, part){
+                       var isPartStr = typeof part === "string",
+                               isTag = isPartStr && !rNonWord.test( part ),
+                               isPartStrNotTag = isPartStr && !isTag;
+
+                       if ( isTag ) {
+                               part = part.toLowerCase();
+                       }
+
+                       for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+                               if ( (elem = checkSet[i]) ) {
+                                       while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+                                       checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+                                               elem || false :
+                                               elem === part;
+                               }
+                       }
+
+                       if ( isPartStrNotTag ) {
+                               Sizzle.filter( part, checkSet, true );
+                       }
+               },
+
+               ">": function( checkSet, part ) {
+                       var elem,
+                               isPartStr = typeof part === "string",
+                               i = 0,
+                               l = checkSet.length;
+
+                       if ( isPartStr && !rNonWord.test( part ) ) {
+                               part = part.toLowerCase();
+
+                               for ( ; i < l; i++ ) {
+                                       elem = checkSet[i];
+
+                                       if ( elem ) {
+                                               var parent = elem.parentNode;
+                                               checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+                                       }
+                               }
+
+                       } else {
+                               for ( ; i < l; i++ ) {
+                                       elem = checkSet[i];
+
+                                       if ( elem ) {
+                                               checkSet[i] = isPartStr ?
+                                                       elem.parentNode :
+                                                       elem.parentNode === part;
+                                       }
+                               }
+
+                               if ( isPartStr ) {
+                                       Sizzle.filter( part, checkSet, true );
+                               }
+                       }
+               },
+
+               "": function(checkSet, part, isXML){
+                       var nodeCheck,
+                               doneName = done++,
+                               checkFn = dirCheck;
+
+                       if ( typeof part === "string" && !rNonWord.test( part ) ) {
+                               part = part.toLowerCase();
+                               nodeCheck = part;
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+               },
+
+               "~": function( checkSet, part, isXML ) {
+                       var nodeCheck,
+                               doneName = done++,
+                               checkFn = dirCheck;
+
+                       if ( typeof part === "string" && !rNonWord.test( part ) ) {
+                               part = part.toLowerCase();
+                               nodeCheck = part;
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+               }
+       },
+
+       find: {
+               ID: function( match, context, isXML ) {
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+                               // Check parentNode to catch when Blackberry 4.6 returns
+                               // nodes that are no longer in the document #6963
+                               return m && m.parentNode ? [m] : [];
+                       }
+               },
+
+               NAME: function( match, context ) {
+                       if ( typeof context.getElementsByName !== "undefined" ) {
+                               var ret = [],
+                                       results = context.getElementsByName( match[1] );
+
+                               for ( var i = 0, l = results.length; i < l; i++ ) {
+                                       if ( results[i].getAttribute("name") === match[1] ) {
+                                               ret.push( results[i] );
+                                       }
+                               }
+
+                               return ret.length === 0 ? null : ret;
+                       }
+               },
+
+               TAG: function( match, context ) {
+                       if ( typeof context.getElementsByTagName !== "undefined" ) {
+                               return context.getElementsByTagName( match[1] );
+                       }
+               }
+       },
+       preFilter: {
+               CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+                       match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+                       if ( isXML ) {
+                               return match;
+                       }
+
+                       for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+                               if ( elem ) {
+                                       if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+                                               if ( !inplace ) {
+                                                       result.push( elem );
+                                               }
+
+                                       } else if ( inplace ) {
+                                               curLoop[i] = false;
+                                       }
+                               }
+                       }
+
+                       return false;
+               },
+
+               ID: function( match ) {
+                       return match[1].replace( rBackslash, "" );
+               },
+
+               TAG: function( match, curLoop ) {
+                       return match[1].replace( rBackslash, "" ).toLowerCase();
+               },
+
+               CHILD: function( match ) {
+                       if ( match[1] === "nth" ) {
+                               if ( !match[2] ) {
+                                       Sizzle.error( match[0] );
+                               }
+
+                               match[2] = match[2].replace(/^\+|\s*/g, '');
+
+                               // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+                               var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+                                       match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+                                       !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+                               // calculate the numbers (first)n+(last) including if they are negative
+                               match[2] = (test[1] + (test[2] || 1)) - 0;
+                               match[3] = test[3] - 0;
+                       }
+                       else if ( match[2] ) {
+                               Sizzle.error( match[0] );
+                       }
+
+                       // TODO: Move to normal caching system
+                       match[0] = done++;
+
+                       return match;
+               },
+
+               ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+                       var name = match[1] = match[1].replace( rBackslash, "" );
+
+                       if ( !isXML && Expr.attrMap[name] ) {
+                               match[1] = Expr.attrMap[name];
+                       }
+
+                       // Handle if an un-quoted value was used
+                       match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+                       if ( match[2] === "~=" ) {
+                               match[4] = " " + match[4] + " ";
+                       }
+
+                       return match;
+               },
+
+               PSEUDO: function( match, curLoop, inplace, result, not ) {
+                       if ( match[1] === "not" ) {
+                               // If we're dealing with a complex expression, or a simple one
+                               if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+                                       match[3] = Sizzle(match[3], null, null, curLoop);
+
+                               } else {
+                                       var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+                                       if ( !inplace ) {
+                                               result.push.apply( result, ret );
+                                       }
+
+                                       return false;
+                               }
+
+                       } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+                               return true;
+                       }
+
+                       return match;
+               },
+
+               POS: function( match ) {
+                       match.unshift( true );
+
+                       return match;
+               }
+       },
+
+       filters: {
+               enabled: function( elem ) {
+                       return elem.disabled === false && elem.type !== "hidden";
+               },
+
+               disabled: function( elem ) {
+                       return elem.disabled === true;
+               },
+
+               checked: function( elem ) {
+                       return elem.checked === true;
+               },
+
+               selected: function( elem ) {
+                       // Accessing this property makes selected-by-default
+                       // options in Safari work properly
+                       if ( elem.parentNode ) {
+                               elem.parentNode.selectedIndex;
+                       }
+
+                       return elem.selected === true;
+               },
+
+               parent: function( elem ) {
+                       return !!elem.firstChild;
+               },
+
+               empty: function( elem ) {
+                       return !elem.firstChild;
+               },
+
+               has: function( elem, i, match ) {
+                       return !!Sizzle( match[3], elem ).length;
+               },
+
+               header: function( elem ) {
+                       return (/h\d/i).test( elem.nodeName );
+               },
+
+               text: function( elem ) {
+                       var attr = elem.getAttribute( "type" ), type = elem.type;
+                       // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+                       // use getAttribute instead to test this case
+                       return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
+               },
+
+               radio: function( elem ) {
+                       return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
+               },
+
+               checkbox: function( elem ) {
+                       return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
+               },
+
+               file: function( elem ) {
+                       return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
+               },
+
+               password: function( elem ) {
+                       return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
+               },
+
+               submit: function( elem ) {
+                       var name = elem.nodeName.toLowerCase();
+                       return (name === "input" || name === "button") && "submit" === elem.type;
+               },
+
+               image: function( elem ) {
+                       return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
+               },
+
+               reset: function( elem ) {
+                       var name = elem.nodeName.toLowerCase();
+                       return (name === "input" || name === "button") && "reset" === elem.type;
+               },
+
+               button: function( elem ) {
+                       var name = elem.nodeName.toLowerCase();
+                       return name === "input" && "button" === elem.type || name === "button";
+               },
+
+               input: function( elem ) {
+                       return (/input|select|textarea|button/i).test( elem.nodeName );
+               },
+
+               focus: function( elem ) {
+                       return elem === elem.ownerDocument.activeElement;
+               }
+       },
+       setFilters: {
+               first: function( elem, i ) {
+                       return i === 0;
+               },
+
+               last: function( elem, i, match, array ) {
+                       return i === array.length - 1;
+               },
+
+               even: function( elem, i ) {
+                       return i % 2 === 0;
+               },
+
+               odd: function( elem, i ) {
+                       return i % 2 === 1;
+               },
+
+               lt: function( elem, i, match ) {
+                       return i < match[3] - 0;
+               },
+
+               gt: function( elem, i, match ) {
+                       return i > match[3] - 0;
+               },
+
+               nth: function( elem, i, match ) {
+                       return match[3] - 0 === i;
+               },
+
+               eq: function( elem, i, match ) {
+                       return match[3] - 0 === i;
+               }
+       },
+       filter: {
+               PSEUDO: function( elem, match, i, array ) {
+                       var name = match[1],
+                               filter = Expr.filters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+
+                       } else if ( name === "contains" ) {
+                               return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+                       } else if ( name === "not" ) {
+                               var not = match[3];
+
+                               for ( var j = 0, l = not.length; j < l; j++ ) {
+                                       if ( not[j] === elem ) {
+                                               return false;
+                                       }
+                               }
+
+                               return true;
+
+                       } else {
+                               Sizzle.error( name );
+                       }
+               },
+
+               CHILD: function( elem, match ) {
+                       var first, last,
+                               doneName, parent, cache,
+                               count, diff,
+                               type = match[1],
+                               node = elem;
+
+                       switch ( type ) {
+                               case "only":
+                               case "first":
+                                       while ( (node = node.previousSibling) ) {
+                                               if ( node.nodeType === 1 ) {
+                                                       return false;
+                                               }
+                                       }
+
+                                       if ( type === "first" ) {
+                                               return true;
+                                       }
+
+                                       node = elem;
+
+                                       /* falls through */
+                               case "last":
+                                       while ( (node = node.nextSibling) ) {
+                                               if ( node.nodeType === 1 ) {
+                                                       return false;
+                                               }
+                                       }
+
+                                       return true;
+
+                               case "nth":
+                                       first = match[2];
+                                       last = match[3];
+
+                                       if ( first === 1 && last === 0 ) {
+                                               return true;
+                                       }
+
+                                       doneName = match[0];
+                                       parent = elem.parentNode;
+
+                                       if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+                                               count = 0;
+
+                                               for ( node = parent.firstChild; node; node = node.nextSibling ) {
+                                                       if ( node.nodeType === 1 ) {
+                                                               node.nodeIndex = ++count;
+                                                       }
+                                               }
+
+                                               parent[ expando ] = doneName;
+                                       }
+
+                                       diff = elem.nodeIndex - last;
+
+                                       if ( first === 0 ) {
+                                               return diff === 0;
+
+                                       } else {
+                                               return ( diff % first === 0 && diff / first >= 0 );
+                                       }
+                       }
+               },
+
+               ID: function( elem, match ) {
+                       return elem.nodeType === 1 && elem.getAttribute("id") === match;
+               },
+
+               TAG: function( elem, match ) {
+                       return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
+               },
+
+               CLASS: function( elem, match ) {
+                       return (" " + (elem.className || elem.getAttribute("class")) + " ")
+                               .indexOf( match ) > -1;
+               },
+
+               ATTR: function( elem, match ) {
+                       var name = match[1],
+                               result = Sizzle.attr ?
+                                       Sizzle.attr( elem, name ) :
+                                       Expr.attrHandle[ name ] ?
+                                       Expr.attrHandle[ name ]( elem ) :
+                                       elem[ name ] != null ?
+                                               elem[ name ] :
+                                               elem.getAttribute( name ),
+                               value = result + "",
+                               type = match[2],
+                               check = match[4];
+
+                       return result == null ?
+                               type === "!=" :
+                               !type && Sizzle.attr ?
+                               result != null :
+                               type === "=" ?
+                               value === check :
+                               type === "*=" ?
+                               value.indexOf(check) >= 0 :
+                               type === "~=" ?
+                               (" " + value + " ").indexOf(check) >= 0 :
+                               !check ?
+                               value && result !== false :
+                               type === "!=" ?
+                               value !== check :
+                               type === "^=" ?
+                               value.indexOf(check) === 0 :
+                               type === "$=" ?
+                               value.substr(value.length - check.length) === check :
+                               type === "|=" ?
+                               value === check || value.substr(0, check.length + 1) === check + "-" :
+                               false;
+               },
+
+               POS: function( elem, match, i, array ) {
+                       var name = match[2],
+                               filter = Expr.setFilters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+                       }
+               }
+       }
+};
+
+var origPOS = Expr.match.POS,
+       fescape = function(all, num){
+               return "\\" + (num - 0 + 1);
+       };
+
+for ( var type in Expr.match ) {
+       Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+       Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+// Expose origPOS
+// "global" as in regardless of relation to brackets/parens
+Expr.match.globalPOS = origPOS;
+
+var makeArray = function( array, results ) {
+       array = Array.prototype.slice.call( array, 0 );
+
+       if ( results ) {
+               results.push.apply( results, array );
+               return results;
+       }
+
+       return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+       Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+       makeArray = function( array, results ) {
+               var i = 0,
+                       ret = results || [];
+
+               if ( toString.call(array) === "[object Array]" ) {
+                       Array.prototype.push.apply( ret, array );
+
+               } else {
+                       if ( typeof array.length === "number" ) {
+                               for ( var l = array.length; i < l; i++ ) {
+                                       ret.push( array[i] );
+                               }
+
+                       } else {
+                               for ( ; array[i]; i++ ) {
+                                       ret.push( array[i] );
+                               }
+                       }
+               }
+
+               return ret;
+       };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+       sortOrder = function( a, b ) {
+               if ( a === b ) {
+                       hasDuplicate = true;
+                       return 0;
+               }
+
+               if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+                       return a.compareDocumentPosition ? -1 : 1;
+               }
+
+               return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+       };
+
+} else {
+       sortOrder = function( a, b ) {
+               // The nodes are identical, we can exit early
+               if ( a === b ) {
+                       hasDuplicate = true;
+                       return 0;
+
+               // Fallback to using sourceIndex (in IE) if it's available on both nodes
+               } else if ( a.sourceIndex && b.sourceIndex ) {
+                       return a.sourceIndex - b.sourceIndex;
+               }
+
+               var al, bl,
+                       ap = [],
+                       bp = [],
+                       aup = a.parentNode,
+                       bup = b.parentNode,
+                       cur = aup;
+
+               // If the nodes are siblings (or identical) we can do a quick check
+               if ( aup === bup ) {
+                       return siblingCheck( a, b );
+
+               // If no parents were found then the nodes are disconnected
+               } else if ( !aup ) {
+                       return -1;
+
+               } else if ( !bup ) {
+                       return 1;
+               }
+
+               // Otherwise they're somewhere else in the tree so we need
+               // to build up a full list of the parentNodes for comparison
+               while ( cur ) {
+                       ap.unshift( cur );
+                       cur = cur.parentNode;
+               }
+
+               cur = bup;
+
+               while ( cur ) {
+                       bp.unshift( cur );
+                       cur = cur.parentNode;
+               }
+
+               al = ap.length;
+               bl = bp.length;
+
+               // Start walking down the tree looking for a discrepancy
+               for ( var i = 0; i < al && i < bl; i++ ) {
+                       if ( ap[i] !== bp[i] ) {
+                               return siblingCheck( ap[i], bp[i] );
+                       }
+               }
+
+               // We ended someplace up the tree so do a sibling check
+               return i === al ?
+                       siblingCheck( a, bp[i], -1 ) :
+                       siblingCheck( ap[i], b, 1 );
+       };
+
+       siblingCheck = function( a, b, ret ) {
+               if ( a === b ) {
+                       return ret;
+               }
+
+               var cur = a.nextSibling;
+
+               while ( cur ) {
+                       if ( cur === b ) {
+                               return -1;
+                       }
+
+                       cur = cur.nextSibling;
+               }
+
+               return 1;
+       };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+       // We're going to inject a fake input element with a specified name
+       var form = document.createElement("div"),
+               id = "script" + (new Date()).getTime(),
+               root = document.documentElement;
+
+       form.innerHTML = "<a name='" + id + "'/>";
+
+       // Inject it into the root element, check its status, and remove it quickly
+       root.insertBefore( form, root.firstChild );
+
+       // The workaround has to do additional checks after a getElementById
+       // Which slows things down for other browsers (hence the branching)
+       if ( document.getElementById( id ) ) {
+               Expr.find.ID = function( match, context, isXML ) {
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+
+                               return m ?
+                                       m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+                                               [m] :
+                                               undefined :
+                                       [];
+                       }
+               };
+
+               Expr.filter.ID = function( elem, match ) {
+                       var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+                       return elem.nodeType === 1 && node && node.nodeValue === match;
+               };
+       }
+
+       root.removeChild( form );
+
+       // release memory in IE
+       root = form = null;
+})();
+
+(function(){
+       // Check to see if the browser returns only elements
+       // when doing getElementsByTagName("*")
+
+       // Create a fake element
+       var div = document.createElement("div");
+       div.appendChild( document.createComment("") );
+
+       // Make sure no comments are found
+       if ( div.getElementsByTagName("*").length > 0 ) {
+               Expr.find.TAG = function( match, context ) {
+                       var results = context.getElementsByTagName( match[1] );
+
+                       // Filter out possible comments
+                       if ( match[1] === "*" ) {
+                               var tmp = [];
+
+                               for ( var i = 0; results[i]; i++ ) {
+                                       if ( results[i].nodeType === 1 ) {
+                                               tmp.push( results[i] );
+                                       }
+                               }
+
+                               results = tmp;
+                       }
+
+                       return results;
+               };
+       }
+
+       // Check to see if an attribute returns normalized href attributes
+       div.innerHTML = "<a href='#'></a>";
+
+       if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+                       div.firstChild.getAttribute("href") !== "#" ) {
+
+               Expr.attrHandle.href = function( elem ) {
+                       return elem.getAttribute( "href", 2 );
+               };
+       }
+
+       // release memory in IE
+       div = null;
+})();
+
+if ( document.querySelectorAll ) {
+       (function(){
+               var oldSizzle = Sizzle,
+                       div = document.createElement("div"),
+                       id = "__sizzle__";
+
+               div.innerHTML = "<p class='TEST'></p>";
+
+               // Safari can't handle uppercase or unicode characters when
+               // in quirks mode.
+               if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+                       return;
+               }
+
+               Sizzle = function( query, context, extra, seed ) {
+                       context = context || document;
+
+                       // Only use querySelectorAll on non-XML documents
+                       // (ID selectors don't work in non-HTML documents)
+                       if ( !seed && !Sizzle.isXML(context) ) {
+                               // See if we find a selector to speed up
+                               var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+                               if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+                                       // Speed-up: Sizzle("TAG")
+                                       if ( match[1] ) {
+                                               return makeArray( context.getElementsByTagName( query ), extra );
+
+                                       // Speed-up: Sizzle(".CLASS")
+                                       } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+                                               return makeArray( context.getElementsByClassName( match[2] ), extra );
+                                       }
+                               }
+
+                               if ( context.nodeType === 9 ) {
+                                       // Speed-up: Sizzle("body")
+                                       // The body element only exists once, optimize finding it
+                                       if ( query === "body" && context.body ) {
+                                               return makeArray( [ context.body ], extra );
+
+                                       // Speed-up: Sizzle("#ID")
+                                       } else if ( match && match[3] ) {
+                                               var elem = context.getElementById( match[3] );
+
+                                               // Check parentNode to catch when Blackberry 4.6 returns
+                                               // nodes that are no longer in the document #6963
+                                               if ( elem && elem.parentNode ) {
+                                                       // Handle the case where IE and Opera return items
+                                                       // by name instead of ID
+                                                       if ( elem.id === match[3] ) {
+                                                               return makeArray( [ elem ], extra );
+                                                       }
+
+                                               } else {
+                                                       return makeArray( [], extra );
+                                               }
+                                       }
+
+                                       try {
+                                               return makeArray( context.querySelectorAll(query), extra );
+                                       } catch(qsaError) {}
+
+                               // qSA works strangely on Element-rooted queries
+                               // We can work around this by specifying an extra ID on the root
+                               // and working up from there (Thanks to Andrew Dupont for the technique)
+                               // IE 8 doesn't work on object elements
+                               } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+                                       var oldContext = context,
+                                               old = context.getAttribute( "id" ),
+                                               nid = old || id,
+                                               hasParent = context.parentNode,
+                                               relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+                                       if ( !old ) {
+                                               context.setAttribute( "id", nid );
+                                       } else {
+                                               nid = nid.replace( /'/g, "\\$&" );
+                                       }
+                                       if ( relativeHierarchySelector && hasParent ) {
+                                               context = context.parentNode;
+                                       }
+
+                                       try {
+                                               if ( !relativeHierarchySelector || hasParent ) {
+                                                       return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+                                               }
+
+                                       } catch(pseudoError) {
+                                       } finally {
+                                               if ( !old ) {
+                                                       oldContext.removeAttribute( "id" );
+                                               }
+                                       }
+                               }
+                       }
+
+                       return oldSizzle(query, context, extra, seed);
+               };
+
+               for ( var prop in oldSizzle ) {
+                       Sizzle[ prop ] = oldSizzle[ prop ];
+               }
+
+               // release memory in IE
+               div = null;
+       })();
+}
+
+(function(){
+       var html = document.documentElement,
+               matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+       if ( matches ) {
+               // Check to see if it's possible to do matchesSelector
+               // on a disconnected node (IE 9 fails this)
+               var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+                       pseudoWorks = false;
+
+               try {
+                       // This should fail with an exception
+                       // Gecko does not error, returns false instead
+                       matches.call( document.documentElement, "[test!='']:sizzle" );
+
+               } catch( pseudoError ) {
+                       pseudoWorks = true;
+               }
+
+               Sizzle.matchesSelector = function( node, expr ) {
+                       // Make sure that attribute selectors are quoted
+                       expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+                       if ( !Sizzle.isXML( node ) ) {
+                               try {
+                                       if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+                                               var ret = matches.call( node, expr );
+
+                                               // IE 9's matchesSelector returns false on disconnected nodes
+                                               if ( ret || !disconnectedMatch ||
+                                                               // As well, disconnected nodes are said to be in a document
+                                                               // fragment in IE 9, so check for that
+                                                               node.document && node.document.nodeType !== 11 ) {
+                                                       return ret;
+                                               }
+                                       }
+                               } catch(e) {}
+                       }
+
+                       return Sizzle(expr, null, null, [node]).length > 0;
+               };
+       }
+})();
+
+(function(){
+       var div = document.createElement("div");
+
+       div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+       // Opera can't find a second classname (in 9.6)
+       // Also, make sure that getElementsByClassName actually exists
+       if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+               return;
+       }
+
+       // Safari caches class attributes, doesn't catch changes (in 3.2)
+       div.lastChild.className = "e";
+
+       if ( div.getElementsByClassName("e").length === 1 ) {
+               return;
+       }
+
+       Expr.order.splice(1, 0, "CLASS");
+       Expr.find.CLASS = function( match, context, isXML ) {
+               if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+                       return context.getElementsByClassName(match[1]);
+               }
+       };
+
+       // release memory in IE
+       div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+
+               if ( elem ) {
+                       var match = false;
+
+                       elem = elem[dir];
+
+                       while ( elem ) {
+                               if ( elem[ expando ] === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 && !isXML ){
+                                       elem[ expando ] = doneName;
+                                       elem.sizset = i;
+                               }
+
+                               if ( elem.nodeName.toLowerCase() === cur ) {
+                                       match = elem;
+                                       break;
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+
+               if ( elem ) {
+                       var match = false;
+
+                       elem = elem[dir];
+
+                       while ( elem ) {
+                               if ( elem[ expando ] === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !isXML ) {
+                                               elem[ expando ] = doneName;
+                                               elem.sizset = i;
+                                       }
+
+                                       if ( typeof cur !== "string" ) {
+                                               if ( elem === cur ) {
+                                                       match = true;
+                                                       break;
+                                               }
+
+                                       } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+                                               match = elem;
+                                               break;
+                                       }
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+if ( document.documentElement.contains ) {
+       Sizzle.contains = function( a, b ) {
+               return a !== b && (a.contains ? a.contains(b) : true);
+       };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+       Sizzle.contains = function( a, b ) {
+               return !!(a.compareDocumentPosition(b) & 16);
+       };
+
+} else {
+       Sizzle.contains = function() {
+               return false;
+       };
+}
+
+Sizzle.isXML = function( elem ) {
+       // documentElement is verified for cases where it doesn't yet exist
+       // (such as loading iframes in IE - #4833)
+       var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+       return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context, seed ) {
+       var match,
+               tmpSet = [],
+               later = "",
+               root = context.nodeType ? [context] : context;
+
+       // Position selectors must be done after the filter
+       // And so must :not(positional) so we move all PSEUDOs to the end
+       while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+               later += match[0];
+               selector = selector.replace( Expr.match.PSEUDO, "" );
+       }
+
+       selector = Expr.relative[selector] ? selector + "*" : selector;
+
+       for ( var i = 0, l = root.length; i < l; i++ ) {
+               Sizzle( selector, root[i], tmpSet, seed );
+       }
+
+       return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+Sizzle.selectors.attrMap = {};
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+       rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+       // Note: This RegExp should be improved, or likely pulled from Sizzle
+       rmultiselector = /,/,
+       isSimple = /^.[^:#\[\.,]*$/,
+       slice = Array.prototype.slice,
+       POS = jQuery.expr.match.globalPOS,
+       // methods guaranteed to produce a unique set when starting from a unique set
+       guaranteedUnique = {
+               children: true,
+               contents: true,
+               next: true,
+               prev: true
+       };
+
+jQuery.fn.extend({
+       find: function( selector ) {
+               var self = this,
+                       i, l;
+
+               if ( typeof selector !== "string" ) {
+                       return jQuery( selector ).filter(function() {
+                               for ( i = 0, l = self.length; i < l; i++ ) {
+                                       if ( jQuery.contains( self[ i ], this ) ) {
+                                               return true;
+                                       }
+                               }
+                       });
+               }
+
+               var ret = this.pushStack( "", "find", selector ),
+                       length, n, r;
+
+               for ( i = 0, l = this.length; i < l; i++ ) {
+                       length = ret.length;
+                       jQuery.find( selector, this[i], ret );
+
+                       if ( i > 0 ) {
+                               // Make sure that the results are unique
+                               for ( n = length; n < ret.length; n++ ) {
+                                       for ( r = 0; r < length; r++ ) {
+                                               if ( ret[r] === ret[n] ) {
+                                                       ret.splice(n--, 1);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               return ret;
+       },
+
+       has: function( target ) {
+               var targets = jQuery( target );
+               return this.filter(function() {
+                       for ( var i = 0, l = targets.length; i < l; i++ ) {
+                               if ( jQuery.contains( this, targets[i] ) ) {
+                                       return true;
+                               }
+                       }
+               });
+       },
+
+       not: function( selector ) {
+               return this.pushStack( winnow(this, selector, false), "not", selector);
+       },
+
+       filter: function( selector ) {
+               return this.pushStack( winnow(this, selector, true), "filter", selector );
+       },
+
+       is: function( selector ) {
+               return !!selector && (
+                       typeof selector === "string" ?
+                               // If this is a positional selector, check membership in the returned set
+                               // so $("p:first").is("p:last") won't return true for a doc with two "p".
+                               POS.test( selector ) ?
+                                       jQuery( selector, this.context ).index( this[0] ) >= 0 :
+                                       jQuery.filter( selector, this ).length > 0 :
+                               this.filter( selector ).length > 0 );
+       },
+
+       closest: function( selectors, context ) {
+               var ret = [], i, l, cur = this[0];
+
+               // Array (deprecated as of jQuery 1.7)
+               if ( jQuery.isArray( selectors ) ) {
+                       var level = 1;
+
+                       while ( cur && cur.ownerDocument && cur !== context ) {
+                               for ( i = 0; i < selectors.length; i++ ) {
+
+                                       if ( jQuery( cur ).is( selectors[ i ] ) ) {
+                                               ret.push({ selector: selectors[ i ], elem: cur, level: level });
+                                       }
+                               }
+
+                               cur = cur.parentNode;
+                               level++;
+                       }
+
+                       return ret;
+               }
+
+               // String
+               var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+                               jQuery( selectors, context || this.context ) :
+                               0;
+
+               for ( i = 0, l = this.length; i < l; i++ ) {
+                       cur = this[i];
+
+                       while ( cur ) {
+                               if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+                                       ret.push( cur );
+                                       break;
+
+                               } else {
+                                       cur = cur.parentNode;
+                                       if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+               return this.pushStack( ret, "closest", selectors );
+       },
+
+       // Determine the position of an element within
+       // the matched set of elements
+       index: function( elem ) {
+
+               // No argument, return index in parent
+               if ( !elem ) {
+                       return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+               }
+
+               // index in selector
+               if ( typeof elem === "string" ) {
+                       return jQuery.inArray( this[0], jQuery( elem ) );
+               }
+
+               // Locate the position of the desired element
+               return jQuery.inArray(
+                       // If it receives a jQuery object, the first element is used
+                       elem.jquery ? elem[0] : elem, this );
+       },
+
+       add: function( selector, context ) {
+               var set = typeof selector === "string" ?
+                               jQuery( selector, context ) :
+                               jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+                       all = jQuery.merge( this.get(), set );
+
+               return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+                       all :
+                       jQuery.unique( all ) );
+       },
+
+       andSelf: function() {
+               return this.add( this.prevObject );
+       }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+       return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+       parent: function( elem ) {
+               var parent = elem.parentNode;
+               return parent && parent.nodeType !== 11 ? parent : null;
+       },
+       parents: function( elem ) {
+               return jQuery.dir( elem, "parentNode" );
+       },
+       parentsUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "parentNode", until );
+       },
+       next: function( elem ) {
+               return jQuery.nth( elem, 2, "nextSibling" );
+       },
+       prev: function( elem ) {
+               return jQuery.nth( elem, 2, "previousSibling" );
+       },
+       nextAll: function( elem ) {
+               return jQuery.dir( elem, "nextSibling" );
+       },
+       prevAll: function( elem ) {
+               return jQuery.dir( elem, "previousSibling" );
+       },
+       nextUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "nextSibling", until );
+       },
+       prevUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "previousSibling", until );
+       },
+       siblings: function( elem ) {
+               return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+       },
+       children: function( elem ) {
+               return jQuery.sibling( elem.firstChild );
+       },
+       contents: function( elem ) {
+               return jQuery.nodeName( elem, "iframe" ) ?
+                       elem.contentDocument || elem.contentWindow.document :
+                       jQuery.makeArray( elem.childNodes );
+       }
+}, function( name, fn ) {
+       jQuery.fn[ name ] = function( until, selector ) {
+               var ret = jQuery.map( this, fn, until );
+
+               if ( !runtil.test( name ) ) {
+                       selector = until;
+               }
+
+               if ( selector && typeof selector === "string" ) {
+                       ret = jQuery.filter( selector, ret );
+               }
+
+               ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+               if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+                       ret = ret.reverse();
+               }
+
+               return this.pushStack( ret, name, slice.call( arguments ).join(",") );
+       };
+});
+
+jQuery.extend({
+       filter: function( expr, elems, not ) {
+               if ( not ) {
+                       expr = ":not(" + expr + ")";
+               }
+
+               return elems.length === 1 ?
+                       jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+                       jQuery.find.matches(expr, elems);
+       },
+
+       dir: function( elem, dir, until ) {
+               var matched = [],
+                       cur = elem[ dir ];
+
+               while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+                       if ( cur.nodeType === 1 ) {
+                               matched.push( cur );
+                       }
+                       cur = cur[dir];
+               }
+               return matched;
+       },
+
+       nth: function( cur, result, dir, elem ) {
+               result = result || 1;
+               var num = 0;
+
+               for ( ; cur; cur = cur[dir] ) {
+                       if ( cur.nodeType === 1 && ++num === result ) {
+                               break;
+                       }
+               }
+
+               return cur;
+       },
+
+       sibling: function( n, elem ) {
+               var r = [];
+
+               for ( ; n; n = n.nextSibling ) {
+                       if ( n.nodeType === 1 && n !== elem ) {
+                               r.push( n );
+                       }
+               }
+
+               return r;
+       }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+       // Can't pass null or undefined to indexOf in Firefox 4
+       // Set to 0 to skip string check
+       qualifier = qualifier || 0;
+
+       if ( jQuery.isFunction( qualifier ) ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       var retVal = !!qualifier.call( elem, i, elem );
+                       return retVal === keep;
+               });
+
+       } else if ( qualifier.nodeType ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       return ( elem === qualifier ) === keep;
+               });
+
+       } else if ( typeof qualifier === "string" ) {
+               var filtered = jQuery.grep(elements, function( elem ) {
+                       return elem.nodeType === 1;
+               });
+
+               if ( isSimple.test( qualifier ) ) {
+                       return jQuery.filter(qualifier, filtered, !keep);
+               } else {
+                       qualifier = jQuery.filter( qualifier, filtered );
+               }
+       }
+
+       return jQuery.grep(elements, function( elem, i ) {
+               return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+       });
+}
+
+
+
+
+function createSafeFragment( document ) {
+       var list = nodeNames.split( "|" ),
+       safeFrag = document.createDocumentFragment();
+
+       if ( safeFrag.createElement ) {
+               while ( list.length ) {
+                       safeFrag.createElement(
+                               list.pop()
+                       );
+               }
+       }
+       return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+               "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+       rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+       rleadingWhitespace = /^\s+/,
+       rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+       rtagName = /<([\w:]+)/,
+       rtbody = /<tbody/i,
+       rhtml = /<|&#?\w+;/,
+       rnoInnerhtml = /<(?:script|style)/i,
+       rnocache = /<(?:script|object|embed|option|style)/i,
+       rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+       // checked="checked" or checked
+       rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+       rscriptType = /\/(java|ecma)script/i,
+       rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+       wrapMap = {
+               option: [ 1, "<select multiple='multiple'>", "</select>" ],
+               legend: [ 1, "<fieldset>", "</fieldset>" ],
+               thead: [ 1, "<table>", "</table>" ],
+               tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+               td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+               col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+               area: [ 1, "<map>", "</map>" ],
+               _default: [ 0, "", "" ]
+       },
+       safeFragment = createSafeFragment( document );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+       wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+       text: function( value ) {
+               return jQuery.access( this, function( value ) {
+                       return value === undefined ?
+                               jQuery.text( this ) :
+                               this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+               }, null, value, arguments.length );
+       },
+
+       wrapAll: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapAll( html.call(this, i) );
+                       });
+               }
+
+               if ( this[0] ) {
+                       // The elements to wrap the target around
+                       var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+                       if ( this[0].parentNode ) {
+                               wrap.insertBefore( this[0] );
+                       }
+
+                       wrap.map(function() {
+                               var elem = this;
+
+                               while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+                                       elem = elem.firstChild;
+                               }
+
+                               return elem;
+                       }).append( this );
+               }
+
+               return this;
+       },
+
+       wrapInner: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapInner( html.call(this, i) );
+                       });
+               }
+
+               return this.each(function() {
+                       var self = jQuery( this ),
+                               contents = self.contents();
+
+                       if ( contents.length ) {
+                               contents.wrapAll( html );
+
+                       } else {
+                               self.append( html );
+                       }
+               });
+       },
+
+       wrap: function( html ) {
+               var isFunction = jQuery.isFunction( html );
+
+               return this.each(function(i) {
+                       jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+               });
+       },
+
+       unwrap: function() {
+               return this.parent().each(function() {
+                       if ( !jQuery.nodeName( this, "body" ) ) {
+                               jQuery( this ).replaceWith( this.childNodes );
+                       }
+               }).end();
+       },
+
+       append: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 ) {
+                               this.appendChild( elem );
+                       }
+               });
+       },
+
+       prepend: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 ) {
+                               this.insertBefore( elem, this.firstChild );
+                       }
+               });
+       },
+
+       before: function() {
+               if ( this[0] && this[0].parentNode ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this );
+                       });
+               } else if ( arguments.length ) {
+                       var set = jQuery.clean( arguments );
+                       set.push.apply( set, this.toArray() );
+                       return this.pushStack( set, "before", arguments );
+               }
+       },
+
+       after: function() {
+               if ( this[0] && this[0].parentNode ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this.nextSibling );
+                       });
+               } else if ( arguments.length ) {
+                       var set = this.pushStack( this, "after", arguments );
+                       set.push.apply( set, jQuery.clean(arguments) );
+                       return set;
+               }
+       },
+
+       // keepData is for internal use only--do not document
+       remove: function( selector, keepData ) {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+                               if ( !keepData && elem.nodeType === 1 ) {
+                                       jQuery.cleanData( elem.getElementsByTagName("*") );
+                                       jQuery.cleanData( [ elem ] );
+                               }
+
+                               if ( elem.parentNode ) {
+                                       elem.parentNode.removeChild( elem );
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       empty: function() {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       // Remove element nodes and prevent memory leaks
+                       if ( elem.nodeType === 1 ) {
+                               jQuery.cleanData( elem.getElementsByTagName("*") );
+                       }
+
+                       // Remove any remaining nodes
+                       while ( elem.firstChild ) {
+                               elem.removeChild( elem.firstChild );
+                       }
+               }
+
+               return this;
+       },
+
+       clone: function( dataAndEvents, deepDataAndEvents ) {
+               dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+               deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+               return this.map( function () {
+                       return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+               });
+       },
+
+       html: function( value ) {
+               return jQuery.access( this, function( value ) {
+                       var elem = this[0] || {},
+                               i = 0,
+                               l = this.length;
+
+                       if ( value === undefined ) {
+                               return elem.nodeType === 1 ?
+                                       elem.innerHTML.replace( rinlinejQuery, "" ) :
+                                       null;
+                       }
+
+
+                       if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+                               ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+                               !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+                               value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+                               try {
+                                       for (; i < l; i++ ) {
+                                               // Remove element nodes and prevent memory leaks
+                                               elem = this[i] || {};
+                                               if ( elem.nodeType === 1 ) {
+                                                       jQuery.cleanData( elem.getElementsByTagName( "*" ) );
+                                                       elem.innerHTML = value;
+                                               }
+                                       }
+
+                                       elem = 0;
+
+                               // If using innerHTML throws an exception, use the fallback method
+                               } catch(e) {}
+                       }
+
+                       if ( elem ) {
+                               this.empty().append( value );
+                       }
+               }, null, value, arguments.length );
+       },
+
+       replaceWith: function( value ) {
+               if ( this[0] && this[0].parentNode ) {
+                       // Make sure that the elements are removed from the DOM before they are inserted
+                       // this can help fix replacing a parent with child elements
+                       if ( jQuery.isFunction( value ) ) {
+                               return this.each(function(i) {
+                                       var self = jQuery(this), old = self.html();
+                                       self.replaceWith( value.call( this, i, old ) );
+                               });
+                       }
+
+                       if ( typeof value !== "string" ) {
+                               value = jQuery( value ).detach();
+                       }
+
+                       return this.each(function() {
+                               var next = this.nextSibling,
+                                       parent = this.parentNode;
+
+                               jQuery( this ).remove();
+
+                               if ( next ) {
+                                       jQuery(next).before( value );
+                               } else {
+                                       jQuery(parent).append( value );
+                               }
+                       });
+               } else {
+                       return this.length ?
+                               this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+                               this;
+               }
+       },
+
+       detach: function( selector ) {
+               return this.remove( selector, true );
+       },
+
+       domManip: function( args, table, callback ) {
+               var results, first, fragment, parent,
+                       value = args[0],
+                       scripts = [];
+
+               // We can't cloneNode fragments that contain checked, in WebKit
+               if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+                       return this.each(function() {
+                               jQuery(this).domManip( args, table, callback, true );
+                       });
+               }
+
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               args[0] = value.call(this, i, table ? self.html() : undefined);
+                               self.domManip( args, table, callback );
+                       });
+               }
+
+               if ( this[0] ) {
+                       parent = value && value.parentNode;
+
+                       // If we're in a fragment, just use that instead of building a new one
+                       if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+                               results = { fragment: parent };
+
+                       } else {
+                               results = jQuery.buildFragment( args, this, scripts );
+                       }
+
+                       fragment = results.fragment;
+
+                       if ( fragment.childNodes.length === 1 ) {
+                               first = fragment = fragment.firstChild;
+                       } else {
+                               first = fragment.firstChild;
+                       }
+
+                       if ( first ) {
+                               table = table && jQuery.nodeName( first, "tr" );
+
+                               for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+                                       callback.call(
+                                               table ?
+                                                       root(this[i], first) :
+                                                       this[i],
+                                               // Make sure that we do not leak memory by inadvertently discarding
+                                               // the original fragment (which might have attached data) instead of
+                                               // using it; in addition, use the original fragment object for the last
+                                               // item instead of first because it can end up being emptied incorrectly
+                                               // in certain situations (Bug #8070).
+                                               // Fragments from the fragment cache must always be cloned and never used
+                                               // in place.
+                                               results.cacheable || ( l > 1 && i < lastIndex ) ?
+                                                       jQuery.clone( fragment, true, true ) :
+                                                       fragment
+                                       );
+                               }
+                       }
+
+                       if ( scripts.length ) {
+                               jQuery.each( scripts, function( i, elem ) {
+                                       if ( elem.src ) {
+                                               jQuery.ajax({
+                                                       type: "GET",
+                                                       global: false,
+                                                       url: elem.src,
+                                                       async: false,
+                                                       dataType: "script"
+                                               });
+                                       } else {
+                                               jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
+                                       }
+
+                                       if ( elem.parentNode ) {
+                                               elem.parentNode.removeChild( elem );
+                                       }
+                               });
+                       }
+               }
+
+               return this;
+       }
+});
+
+function root( elem, cur ) {
+       return jQuery.nodeName(elem, "table") ?
+               (elem.getElementsByTagName("tbody")[0] ||
+               elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+               elem;
+}
+
+function cloneCopyEvent( src, dest ) {
+
+       if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+               return;
+       }
+
+       var type, i, l,
+               oldData = jQuery._data( src ),
+               curData = jQuery._data( dest, oldData ),
+               events = oldData.events;
+
+       if ( events ) {
+               delete curData.handle;
+               curData.events = {};
+
+               for ( type in events ) {
+                       for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+                               jQuery.event.add( dest, type, events[ type ][ i ] );
+                       }
+               }
+       }
+
+       // make the cloned public data object a copy from the original
+       if ( curData.data ) {
+               curData.data = jQuery.extend( {}, curData.data );
+       }
+}
+
+function cloneFixAttributes( src, dest ) {
+       var nodeName;
+
+       // We do not need to do anything for non-Elements
+       if ( dest.nodeType !== 1 ) {
+               return;
+       }
+
+       // clearAttributes removes the attributes, which we don't want,
+       // but also removes the attachEvent events, which we *do* want
+       if ( dest.clearAttributes ) {
+               dest.clearAttributes();
+       }
+
+       // mergeAttributes, in contrast, only merges back on the
+       // original attributes, not the events
+       if ( dest.mergeAttributes ) {
+               dest.mergeAttributes( src );
+       }
+
+       nodeName = dest.nodeName.toLowerCase();
+
+       // IE6-8 fail to clone children inside object elements that use
+       // the proprietary classid attribute value (rather than the type
+       // attribute) to identify the type of content to display
+       if ( nodeName === "object" ) {
+               dest.outerHTML = src.outerHTML;
+
+       } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+               // IE6-8 fails to persist the checked state of a cloned checkbox
+               // or radio button. Worse, IE6-7 fail to give the cloned element
+               // a checked appearance if the defaultChecked value isn't also set
+               if ( src.checked ) {
+                       dest.defaultChecked = dest.checked = src.checked;
+               }
+
+               // IE6-7 get confused and end up setting the value of a cloned
+               // checkbox/radio button to an empty string instead of "on"
+               if ( dest.value !== src.value ) {
+                       dest.value = src.value;
+               }
+
+       // IE6-8 fails to return the selected option to the default selected
+       // state when cloning options
+       } else if ( nodeName === "option" ) {
+               dest.selected = src.defaultSelected;
+
+       // IE6-8 fails to set the defaultValue to the correct value when
+       // cloning other types of input fields
+       } else if ( nodeName === "input" || nodeName === "textarea" ) {
+               dest.defaultValue = src.defaultValue;
+
+       // IE blanks contents when cloning scripts
+       } else if ( nodeName === "script" && dest.text !== src.text ) {
+               dest.text = src.text;
+       }
+
+       // Event data gets referenced instead of copied if the expando
+       // gets copied too
+       dest.removeAttribute( jQuery.expando );
+
+       // Clear flags for bubbling special change/submit events, they must
+       // be reattached when the newly cloned events are first activated
+       dest.removeAttribute( "_submit_attached" );
+       dest.removeAttribute( "_change_attached" );
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+       var fragment, cacheable, cacheresults, doc,
+       first = args[ 0 ];
+
+       // nodes may contain either an explicit document object,
+       // a jQuery collection or context object.
+       // If nodes[0] contains a valid object to assign to doc
+       if ( nodes && nodes[0] ) {
+               doc = nodes[0].ownerDocument || nodes[0];
+       }
+
+       // Ensure that an attr object doesn't incorrectly stand in as a document object
+       // Chrome and Firefox seem to allow this to occur and will throw exception
+       // Fixes #8950
+       if ( !doc.createDocumentFragment ) {
+               doc = document;
+       }
+
+       // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+       // Cloning options loses the selected state, so don't cache them
+       // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+       // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+       // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
+       if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
+               first.charAt(0) === "<" && !rnocache.test( first ) &&
+               (jQuery.support.checkClone || !rchecked.test( first )) &&
+               (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+
+               cacheable = true;
+
+               cacheresults = jQuery.fragments[ first ];
+               if ( cacheresults && cacheresults !== 1 ) {
+                       fragment = cacheresults;
+               }
+       }
+
+       if ( !fragment ) {
+               fragment = doc.createDocumentFragment();
+               jQuery.clean( args, doc, fragment, scripts );
+       }
+
+       if ( cacheable ) {
+               jQuery.fragments[ first ] = cacheresults ? fragment : 1;
+       }
+
+       return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+       appendTo: "append",
+       prependTo: "prepend",
+       insertBefore: "before",
+       insertAfter: "after",
+       replaceAll: "replaceWith"
+}, function( name, original ) {
+       jQuery.fn[ name ] = function( selector ) {
+               var ret = [],
+                       insert = jQuery( selector ),
+                       parent = this.length === 1 && this[0].parentNode;
+
+               if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+                       insert[ original ]( this[0] );
+                       return this;
+
+               } else {
+                       for ( var i = 0, l = insert.length; i < l; i++ ) {
+                               var elems = ( i > 0 ? this.clone(true) : this ).get();
+                               jQuery( insert[i] )[ original ]( elems );
+                               ret = ret.concat( elems );
+                       }
+
+                       return this.pushStack( ret, name, insert.selector );
+               }
+       };
+});
+
+function getAll( elem ) {
+       if ( typeof elem.getElementsByTagName !== "undefined" ) {
+               return elem.getElementsByTagName( "*" );
+
+       } else if ( typeof elem.querySelectorAll !== "undefined" ) {
+               return elem.querySelectorAll( "*" );
+
+       } else {
+               return [];
+       }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+       if ( elem.type === "checkbox" || elem.type === "radio" ) {
+               elem.defaultChecked = elem.checked;
+       }
+}
+// Finds all inputs and passes them to fixDefaultChecked
+function findInputs( elem ) {
+       var nodeName = ( elem.nodeName || "" ).toLowerCase();
+       if ( nodeName === "input" ) {
+               fixDefaultChecked( elem );
+       // Skip scripts, get other children
+       } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
+               jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+       }
+}
+
+// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
+function shimCloneNode( elem ) {
+       var div = document.createElement( "div" );
+       safeFragment.appendChild( div );
+
+       div.innerHTML = elem.outerHTML;
+       return div.firstChild;
+}
+
+jQuery.extend({
+       clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+               var srcElements,
+                       destElements,
+                       i,
+                       // IE<=8 does not properly clone detached, unknown element nodes
+                       clone = jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ?
+                               elem.cloneNode( true ) :
+                               shimCloneNode( elem );
+
+               if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+                               (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+                       // IE copies events bound via attachEvent when using cloneNode.
+                       // Calling detachEvent on the clone will also remove the events
+                       // from the original. In order to get around this, we use some
+                       // proprietary methods to clear the events. Thanks to MooTools
+                       // guys for this hotness.
+
+                       cloneFixAttributes( elem, clone );
+
+                       // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
+                       srcElements = getAll( elem );
+                       destElements = getAll( clone );
+
+                       // Weird iteration because IE will replace the length property
+                       // with an element if you are cloning the body and one of the
+                       // elements on the page has a name or id of "length"
+                       for ( i = 0; srcElements[i]; ++i ) {
+                               // Ensure that the destination node is not null; Fixes #9587
+                               if ( destElements[i] ) {
+                                       cloneFixAttributes( srcElements[i], destElements[i] );
+                               }
+                       }
+               }
+
+               // Copy the events from the original to the clone
+               if ( dataAndEvents ) {
+                       cloneCopyEvent( elem, clone );
+
+                       if ( deepDataAndEvents ) {
+                               srcElements = getAll( elem );
+                               destElements = getAll( clone );
+
+                               for ( i = 0; srcElements[i]; ++i ) {
+                                       cloneCopyEvent( srcElements[i], destElements[i] );
+                               }
+                       }
+               }
+
+               srcElements = destElements = null;
+
+               // Return the cloned set
+               return clone;
+       },
+
+       clean: function( elems, context, fragment, scripts ) {
+               var checkScriptType, script, j,
+                               ret = [];
+
+               context = context || document;
+
+               // !context.createElement fails in IE with an error but returns typeof 'object'
+               if ( typeof context.createElement === "undefined" ) {
+                       context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+               }
+
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       if ( typeof elem === "number" ) {
+                               elem += "";
+                       }
+
+                       if ( !elem ) {
+                               continue;
+                       }
+
+                       // Convert html string into DOM nodes
+                       if ( typeof elem === "string" ) {
+                               if ( !rhtml.test( elem ) ) {
+                                       elem = context.createTextNode( elem );
+                               } else {
+                                       // Fix "XHTML"-style tags in all browsers
+                                       elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+                                       // Trim whitespace, otherwise indexOf won't work as expected
+                                       var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
+                                               wrap = wrapMap[ tag ] || wrapMap._default,
+                                               depth = wrap[0],
+                                               div = context.createElement("div"),
+                                               safeChildNodes = safeFragment.childNodes,
+                                               remove;
+
+                                       // Append wrapper element to unknown element safe doc fragment
+                                       if ( context === document ) {
+                                               // Use the fragment we've already created for this document
+                                               safeFragment.appendChild( div );
+                                       } else {
+                                               // Use a fragment created with the owner document
+                                               createSafeFragment( context ).appendChild( div );
+                                       }
+
+                                       // Go to html and back, then peel off extra wrappers
+                                       div.innerHTML = wrap[1] + elem + wrap[2];
+
+                                       // Move to the right depth
+                                       while ( depth-- ) {
+                                               div = div.lastChild;
+                                       }
+
+                                       // Remove IE's autoinserted <tbody> from table fragments
+                                       if ( !jQuery.support.tbody ) {
+
+                                               // String was a <table>, *may* have spurious <tbody>
+                                               var hasBody = rtbody.test(elem),
+                                                       tbody = tag === "table" && !hasBody ?
+                                                               div.firstChild && div.firstChild.childNodes :
+
+                                                               // String was a bare <thead> or <tfoot>
+                                                               wrap[1] === "<table>" && !hasBody ?
+                                                                       div.childNodes :
+                                                                       [];
+
+                                               for ( j = tbody.length - 1; j >= 0 ; --j ) {
+                                                       if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+                                                               tbody[ j ].parentNode.removeChild( tbody[ j ] );
+                                                       }
+                                               }
+                                       }
+
+                                       // IE completely kills leading whitespace when innerHTML is used
+                                       if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+                                               div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+                                       }
+
+                                       elem = div.childNodes;
+
+                                       // Clear elements from DocumentFragment (safeFragment or otherwise)
+                                       // to avoid hoarding elements. Fixes #11356
+                                       if ( div ) {
+                                               div.parentNode.removeChild( div );
+
+                                               // Guard against -1 index exceptions in FF3.6
+                                               if ( safeChildNodes.length > 0 ) {
+                                                       remove = safeChildNodes[ safeChildNodes.length - 1 ];
+
+                                                       if ( remove && remove.parentNode ) {
+                                                               remove.parentNode.removeChild( remove );
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Resets defaultChecked for any radios and checkboxes
+                       // about to be appended to the DOM in IE 6/7 (#8060)
+                       var len;
+                       if ( !jQuery.support.appendChecked ) {
+                               if ( elem[0] && typeof (len = elem.length) === "number" ) {
+                                       for ( j = 0; j < len; j++ ) {
+                                               findInputs( elem[j] );
+                                       }
+                               } else {
+                                       findInputs( elem );
+                               }
+                       }
+
+                       if ( elem.nodeType ) {
+                               ret.push( elem );
+                       } else {
+                               ret = jQuery.merge( ret, elem );
+                       }
+               }
+
+               if ( fragment ) {
+                       checkScriptType = function( elem ) {
+                               return !elem.type || rscriptType.test( elem.type );
+                       };
+                       for ( i = 0; ret[i]; i++ ) {
+                               script = ret[i];
+                               if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) {
+                                       scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script );
+
+                               } else {
+                                       if ( script.nodeType === 1 ) {
+                                               var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType );
+
+                                               ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+                                       }
+                                       fragment.appendChild( script );
+                               }
+                       }
+               }
+
+               return ret;
+       },
+
+       cleanData: function( elems ) {
+               var data, id,
+                       cache = jQuery.cache,
+                       special = jQuery.event.special,
+                       deleteExpando = jQuery.support.deleteExpando;
+
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+                               continue;
+                       }
+
+                       id = elem[ jQuery.expando ];
+
+                       if ( id ) {
+                               data = cache[ id ];
+
+                               if ( data && data.events ) {
+                                       for ( var type in data.events ) {
+                                               if ( special[ type ] ) {
+                                                       jQuery.event.remove( elem, type );
+
+                                               // This is a shortcut to avoid jQuery.event.remove's overhead
+                                               } else {
+                                                       jQuery.removeEvent( elem, type, data.handle );
+                                               }
+                                       }
+
+                                       // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+                                       if ( data.handle ) {
+                                               data.handle.elem = null;
+                                       }
+                               }
+
+                               if ( deleteExpando ) {
+                                       delete elem[ jQuery.expando ];
+
+                               } else if ( elem.removeAttribute ) {
+                                       elem.removeAttribute( jQuery.expando );
+                               }
+
+                               delete cache[ id ];
+                       }
+               }
+       }
+});
+
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+       ropacity = /opacity=([^)]*)/,
+       // fixed for IE9, see #8346
+       rupper = /([A-Z]|^ms)/g,
+       rnum = /^[\-+]?(?:\d*\.)?\d+$/i,
+       rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
+       rrelNum = /^([\-+])=([\-+.\de]+)/,
+       rmargin = /^margin/,
+
+       cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+
+       // order is important!
+       cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+
+       curCSS,
+
+       getComputedStyle,
+       currentStyle;
+
+jQuery.fn.css = function( name, value ) {
+       return jQuery.access( this, function( elem, name, value ) {
+               return value !== undefined ?
+                       jQuery.style( elem, name, value ) :
+                       jQuery.css( elem, name );
+       }, name, value, arguments.length > 1 );
+};
+
+jQuery.extend({
+       // Add in style property hooks for overriding the default
+       // behavior of getting and setting a style property
+       cssHooks: {
+               opacity: {
+                       get: function( elem, computed ) {
+                               if ( computed ) {
+                                       // We should always get a number back from opacity
+                                       var ret = curCSS( elem, "opacity" );
+                                       return ret === "" ? "1" : ret;
+
+                               } else {
+                                       return elem.style.opacity;
+                               }
+                       }
+               }
+       },
+
+       // Exclude the following css properties to add px
+       cssNumber: {
+               "fillOpacity": true,
+               "fontWeight": true,
+               "lineHeight": true,
+               "opacity": true,
+               "orphans": true,
+               "widows": true,
+               "zIndex": true,
+               "zoom": true
+       },
+
+       // Add in properties whose names you wish to fix before
+       // setting or getting the value
+       cssProps: {
+               // normalize float css property
+               "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+       },
+
+       // Get and set the style property on a DOM Node
+       style: function( elem, name, value, extra ) {
+               // Don't set styles on text and comment nodes
+               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+                       return;
+               }
+
+               // Make sure that we're working with the right name
+               var ret, type, origName = jQuery.camelCase( name ),
+                       style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+               name = jQuery.cssProps[ origName ] || origName;
+
+               // Check if we're setting a value
+               if ( value !== undefined ) {
+                       type = typeof value;
+
+                       // convert relative number strings (+= or -=) to relative numbers. #7345
+                       if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+                               value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
+                               // Fixes bug #9237
+                               type = "number";
+                       }
+
+                       // Make sure that NaN and null values aren't set. See: #7116
+                       if ( value == null || type === "number" && isNaN( value ) ) {
+                               return;
+                       }
+
+                       // If a number was passed in, add 'px' to the (except for certain CSS properties)
+                       if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+                               value += "px";
+                       }
+
+                       // If a hook was provided, use that value, otherwise just set the specified value
+                       if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+                               // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+                               // Fixes bug #5509
+                               try {
+                                       style[ name ] = value;
+                               } catch(e) {}
+                       }
+
+               } else {
+                       // If a hook was provided get the non-computed value from there
+                       if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+                               return ret;
+                       }
+
+                       // Otherwise just get the value from the style object
+                       return style[ name ];
+               }
+       },
+
+       css: function( elem, name, extra ) {
+               var ret, hooks;
+
+               // Make sure that we're working with the right name
+               name = jQuery.camelCase( name );
+               hooks = jQuery.cssHooks[ name ];
+               name = jQuery.cssProps[ name ] || name;
+
+               // cssFloat needs a special treatment
+               if ( name === "cssFloat" ) {
+                       name = "float";
+               }
+
+               // If a hook was provided get the computed value from there
+               if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
+                       return ret;
+
+               // Otherwise, if a way to get the computed value exists, use that
+               } else if ( curCSS ) {
+                       return curCSS( elem, name );
+               }
+       },
+
+       // A method for quickly swapping in/out CSS properties to get correct calculations
+       swap: function( elem, options, callback ) {
+               var old = {},
+                       ret, name;
+
+               // Remember the old values, and insert the new ones
+               for ( name in options ) {
+                       old[ name ] = elem.style[ name ];
+                       elem.style[ name ] = options[ name ];
+               }
+
+               ret = callback.call( elem );
+
+               // Revert the old values
+               for ( name in options ) {
+                       elem.style[ name ] = old[ name ];
+               }
+
+               return ret;
+       }
+});
+
+// DEPRECATED in 1.3, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+       getComputedStyle = function( elem, name ) {
+               var ret, defaultView, computedStyle, width,
+                       style = elem.style;
+
+               name = name.replace( rupper, "-$1" ).toLowerCase();
+
+               if ( (defaultView = elem.ownerDocument.defaultView) &&
+                               (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+
+                       ret = computedStyle.getPropertyValue( name );
+                       if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+                               ret = jQuery.style( elem, name );
+                       }
+               }
+
+               // A tribute to the "awesome hack by Dean Edwards"
+               // WebKit uses "computed value (percentage if specified)" instead of "used value" for margins
+               // which is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+               if ( !jQuery.support.pixelMargin && computedStyle && rmargin.test( name ) && rnumnonpx.test( ret ) ) {
+                       width = style.width;
+                       style.width = ret;
+                       ret = computedStyle.width;
+                       style.width = width;
+               }
+
+               return ret;
+       };
+}
+
+if ( document.documentElement.currentStyle ) {
+       currentStyle = function( elem, name ) {
+               var left, rsLeft, uncomputed,
+                       ret = elem.currentStyle && elem.currentStyle[ name ],
+                       style = elem.style;
+
+               // Avoid setting ret to empty string here
+               // so we don't default to auto
+               if ( ret == null && style && (uncomputed = style[ name ]) ) {
+                       ret = uncomputed;
+               }
+
+               // From the awesome hack by Dean Edwards
+               // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+               // If we're not dealing with a regular pixel number
+               // but a number that has a weird ending, we need to convert it to pixels
+               if ( rnumnonpx.test( ret ) ) {
+
+                       // Remember the original values
+                       left = style.left;
+                       rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
+
+                       // Put in the new values to get a computed value out
+                       if ( rsLeft ) {
+                               elem.runtimeStyle.left = elem.currentStyle.left;
+                       }
+                       style.left = name === "fontSize" ? "1em" : ret;
+                       ret = style.pixelLeft + "px";
+
+                       // Revert the changed values
+                       style.left = left;
+                       if ( rsLeft ) {
+                               elem.runtimeStyle.left = rsLeft;
+                       }
+               }
+
+               return ret === "" ? "auto" : ret;
+       };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWidthOrHeight( elem, name, extra ) {
+
+       // Start with offset property
+       var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+               i = name === "width" ? 1 : 0,
+               len = 4;
+
+       if ( val > 0 ) {
+               if ( extra !== "border" ) {
+                       for ( ; i < len; i += 2 ) {
+                               if ( !extra ) {
+                                       val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
+                               }
+                               if ( extra === "margin" ) {
+                                       val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0;
+                               } else {
+                                       val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+                               }
+                       }
+               }
+
+               return val + "px";
+       }
+
+       // Fall back to computed then uncomputed css if necessary
+       val = curCSS( elem, name );
+       if ( val < 0 || val == null ) {
+               val = elem.style[ name ];
+       }
+
+       // Computed unit is not pixels. Stop here and return.
+       if ( rnumnonpx.test(val) ) {
+               return val;
+       }
+
+       // Normalize "", auto, and prepare for extra
+       val = parseFloat( val ) || 0;
+
+       // Add padding, border, margin
+       if ( extra ) {
+               for ( ; i < len; i += 2 ) {
+                       val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
+                       if ( extra !== "padding" ) {
+                               val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+                       }
+                       if ( extra === "margin" ) {
+                               val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0;
+                       }
+               }
+       }
+
+       return val + "px";
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+       jQuery.cssHooks[ name ] = {
+               get: function( elem, computed, extra ) {
+                       if ( computed ) {
+                               if ( elem.offsetWidth !== 0 ) {
+                                       return getWidthOrHeight( elem, name, extra );
+                               } else {
+                                       return jQuery.swap( elem, cssShow, function() {
+                                               return getWidthOrHeight( elem, name, extra );
+                                       });
+                               }
+                       }
+               },
+
+               set: function( elem, value ) {
+                       return rnum.test( value ) ?
+                               value + "px" :
+                               value;
+               }
+       };
+});
+
+if ( !jQuery.support.opacity ) {
+       jQuery.cssHooks.opacity = {
+               get: function( elem, computed ) {
+                       // IE uses filters for opacity
+                       return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+                               ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+                               computed ? "1" : "";
+               },
+
+               set: function( elem, value ) {
+                       var style = elem.style,
+                               currentStyle = elem.currentStyle,
+                               opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+                               filter = currentStyle && currentStyle.filter || style.filter || "";
+
+                       // IE has trouble with opacity if it does not have layout
+                       // Force it by setting the zoom level
+                       style.zoom = 1;
+
+                       // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+                       if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
+
+                               // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+                               // if "filter:" is present at all, clearType is disabled, we want to avoid this
+                               // style.removeAttribute is IE Only, but so apparently is this code path...
+                               style.removeAttribute( "filter" );
+
+                               // if there there is no filter style applied in a css rule, we are done
+                               if ( currentStyle && !currentStyle.filter ) {
+                                       return;
+                               }
+                       }
+
+                       // otherwise, set new filter values
+                       style.filter = ralpha.test( filter ) ?
+                               filter.replace( ralpha, opacity ) :
+                               filter + " " + opacity;
+               }
+       };
+}
+
+jQuery(function() {
+       // This hook cannot be added until DOM ready because the support test
+       // for it is not run until after DOM ready
+       if ( !jQuery.support.reliableMarginRight ) {
+               jQuery.cssHooks.marginRight = {
+                       get: function( elem, computed ) {
+                               // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+                               // Work around by temporarily setting element display to inline-block
+                               return jQuery.swap( elem, { "display": "inline-block" }, function() {
+                                       if ( computed ) {
+                                               return curCSS( elem, "margin-right" );
+                                       } else {
+                                               return elem.style.marginRight;
+                                       }
+                               });
+                       }
+               };
+       }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.hidden = function( elem ) {
+               var width = elem.offsetWidth,
+                       height = elem.offsetHeight;
+
+               return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+       };
+
+       jQuery.expr.filters.visible = function( elem ) {
+               return !jQuery.expr.filters.hidden( elem );
+       };
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+       margin: "",
+       padding: "",
+       border: "Width"
+}, function( prefix, suffix ) {
+
+       jQuery.cssHooks[ prefix + suffix ] = {
+               expand: function( value ) {
+                       var i,
+
+                               // assumes a single number if not a string
+                               parts = typeof value === "string" ? value.split(" ") : [ value ],
+                               expanded = {};
+
+                       for ( i = 0; i < 4; i++ ) {
+                               expanded[ prefix + cssExpand[ i ] + suffix ] =
+                                       parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+                       }
+
+                       return expanded;
+               }
+       };
+});
+
+
+
+
+var r20 = /%20/g,
+       rbracket = /\[\]$/,
+       rCRLF = /\r?\n/g,
+       rhash = /#.*$/,
+       rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+       rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+       // #7653, #8125, #8152: local protocol detection
+       rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
+       rnoContent = /^(?:GET|HEAD)$/,
+       rprotocol = /^\/\//,
+       rquery = /\?/,
+       rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+       rselectTextarea = /^(?:select|textarea)/i,
+       rspacesAjax = /\s+/,
+       rts = /([?&])_=[^&]*/,
+       rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+
+       // Keep a copy of the old load method
+       _load = jQuery.fn.load,
+
+       /* Prefilters
+        * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+        * 2) These are called:
+        *    - BEFORE asking for a transport
+        *    - AFTER param serialization (s.data is a string if s.processData is true)
+        * 3) key is the dataType
+        * 4) the catchall symbol "*" can be used
+        * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+        */
+       prefilters = {},
+
+       /* Transports bindings
+        * 1) key is the dataType
+        * 2) the catchall symbol "*" can be used
+        * 3) selection will start with transport dataType and THEN go to "*" if needed
+        */
+       transports = {},
+
+       // Document location
+       ajaxLocation,
+
+       // Document location segments
+       ajaxLocParts,
+
+       // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+       allTypes = ["*/"] + ["*"];
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+       ajaxLocation = location.href;
+} catch( e ) {
+       // Use the href attribute of an A element
+       // since IE will modify it given document.location
+       ajaxLocation = document.createElement( "a" );
+       ajaxLocation.href = "";
+       ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+       // dataTypeExpression is optional and defaults to "*"
+       return function( dataTypeExpression, func ) {
+
+               if ( typeof dataTypeExpression !== "string" ) {
+                       func = dataTypeExpression;
+                       dataTypeExpression = "*";
+               }
+
+               if ( jQuery.isFunction( func ) ) {
+                       var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
+                               i = 0,
+                               length = dataTypes.length,
+                               dataType,
+                               list,
+                               placeBefore;
+
+                       // For each dataType in the dataTypeExpression
+                       for ( ; i < length; i++ ) {
+                               dataType = dataTypes[ i ];
+                               // We control if we're asked to add before
+                               // any existing element
+                               placeBefore = /^\+/.test( dataType );
+                               if ( placeBefore ) {
+                                       dataType = dataType.substr( 1 ) || "*";
+                               }
+                               list = structure[ dataType ] = structure[ dataType ] || [];
+                               // then we add to the structure accordingly
+                               list[ placeBefore ? "unshift" : "push" ]( func );
+                       }
+               }
+       };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+               dataType /* internal */, inspected /* internal */ ) {
+
+       dataType = dataType || options.dataTypes[ 0 ];
+       inspected = inspected || {};
+
+       inspected[ dataType ] = true;
+
+       var list = structure[ dataType ],
+               i = 0,
+               length = list ? list.length : 0,
+               executeOnly = ( structure === prefilters ),
+               selection;
+
+       for ( ; i < length && ( executeOnly || !selection ); i++ ) {
+               selection = list[ i ]( options, originalOptions, jqXHR );
+               // If we got redirected to another dataType
+               // we try there if executing only and not done already
+               if ( typeof selection === "string" ) {
+                       if ( !executeOnly || inspected[ selection ] ) {
+                               selection = undefined;
+                       } else {
+                               options.dataTypes.unshift( selection );
+                               selection = inspectPrefiltersOrTransports(
+                                               structure, options, originalOptions, jqXHR, selection, inspected );
+                       }
+               }
+       }
+       // If we're only executing or nothing was selected
+       // we try the catchall dataType if not done already
+       if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+               selection = inspectPrefiltersOrTransports(
+                               structure, options, originalOptions, jqXHR, "*", inspected );
+       }
+       // unnecessary when only executing (prefilters)
+       // but it'll be ignored by the caller in that case
+       return selection;
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+       var key, deep,
+               flatOptions = jQuery.ajaxSettings.flatOptions || {};
+       for ( key in src ) {
+               if ( src[ key ] !== undefined ) {
+                       ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
+               }
+       }
+       if ( deep ) {
+               jQuery.extend( true, target, deep );
+       }
+}
+
+jQuery.fn.extend({
+       load: function( url, params, callback ) {
+               if ( typeof url !== "string" && _load ) {
+                       return _load.apply( this, arguments );
+
+               // Don't do a request if no elements are being requested
+               } else if ( !this.length ) {
+                       return this;
+               }
+
+               var off = url.indexOf( " " );
+               if ( off >= 0 ) {
+                       var selector = url.slice( off, url.length );
+                       url = url.slice( 0, off );
+               }
+
+               // Default to a GET request
+               var type = "GET";
+
+               // If the second parameter was provided
+               if ( params ) {
+                       // If it's a function
+                       if ( jQuery.isFunction( params ) ) {
+                               // We assume that it's the callback
+                               callback = params;
+                               params = undefined;
+
+                       // Otherwise, build a param string
+                       } else if ( typeof params === "object" ) {
+                               params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+                               type = "POST";
+                       }
+               }
+
+               var self = this;
+
+               // Request the remote document
+               jQuery.ajax({
+                       url: url,
+                       type: type,
+                       dataType: "html",
+                       data: params,
+                       // Complete callback (responseText is used internally)
+                       complete: function( jqXHR, status, responseText ) {
+                               // Store the response as specified by the jqXHR object
+                               responseText = jqXHR.responseText;
+                               // If successful, inject the HTML into all the matched elements
+                               if ( jqXHR.isResolved() ) {
+                                       // #4825: Get the actual response in case
+                                       // a dataFilter is present in ajaxSettings
+                                       jqXHR.done(function( r ) {
+                                               responseText = r;
+                                       });
+                                       // See if a selector was specified
+                                       self.html( selector ?
+                                               // Create a dummy div to hold the results
+                                               jQuery("<div>")
+                                                       // inject the contents of the document in, removing the scripts
+                                                       // to avoid any 'Permission Denied' errors in IE
+                                                       .append(responseText.replace(rscript, ""))
+
+                                                       // Locate the specified elements
+                                                       .find(selector) :
+
+                                               // If not, just inject the full result
+                                               responseText );
+                               }
+
+                               if ( callback ) {
+                                       self.each( callback, [ responseText, status, jqXHR ] );
+                               }
+                       }
+               });
+
+               return this;
+       },
+
+       serialize: function() {
+               return jQuery.param( this.serializeArray() );
+       },
+
+       serializeArray: function() {
+               return this.map(function(){
+                       return this.elements ? jQuery.makeArray( this.elements ) : this;
+               })
+               .filter(function(){
+                       return this.name && !this.disabled &&
+                               ( this.checked || rselectTextarea.test( this.nodeName ) ||
+                                       rinput.test( this.type ) );
+               })
+               .map(function( i, elem ){
+                       var val = jQuery( this ).val();
+
+                       return val == null ?
+                               null :
+                               jQuery.isArray( val ) ?
+                                       jQuery.map( val, function( val, i ){
+                                               return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+                                       }) :
+                                       { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+               }).get();
+       }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+       jQuery.fn[ o ] = function( f ){
+               return this.on( o, f );
+       };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+       jQuery[ method ] = function( url, data, callback, type ) {
+               // shift arguments if data argument was omitted
+               if ( jQuery.isFunction( data ) ) {
+                       type = type || callback;
+                       callback = data;
+                       data = undefined;
+               }
+
+               return jQuery.ajax({
+                       type: method,
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       };
+});
+
+jQuery.extend({
+
+       getScript: function( url, callback ) {
+               return jQuery.get( url, undefined, callback, "script" );
+       },
+
+       getJSON: function( url, data, callback ) {
+               return jQuery.get( url, data, callback, "json" );
+       },
+
+       // Creates a full fledged settings object into target
+       // with both ajaxSettings and settings fields.
+       // If target is omitted, writes into ajaxSettings.
+       ajaxSetup: function( target, settings ) {
+               if ( settings ) {
+                       // Building a settings object
+                       ajaxExtend( target, jQuery.ajaxSettings );
+               } else {
+                       // Extending ajaxSettings
+                       settings = target;
+                       target = jQuery.ajaxSettings;
+               }
+               ajaxExtend( target, settings );
+               return target;
+       },
+
+       ajaxSettings: {
+               url: ajaxLocation,
+               isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+               global: true,
+               type: "GET",
+               contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+               processData: true,
+               async: true,
+               /*
+               timeout: 0,
+               data: null,
+               dataType: null,
+               username: null,
+               password: null,
+               cache: null,
+               traditional: false,
+               headers: {},
+               */
+
+               accepts: {
+                       xml: "application/xml, text/xml",
+                       html: "text/html",
+                       text: "text/plain",
+                       json: "application/json, text/javascript",
+                       "*": allTypes
+               },
+
+               contents: {
+                       xml: /xml/,
+                       html: /html/,
+                       json: /json/
+               },
+
+               responseFields: {
+                       xml: "responseXML",
+                       text: "responseText"
+               },
+
+               // List of data converters
+               // 1) key format is "source_type destination_type" (a single space in-between)
+               // 2) the catchall symbol "*" can be used for source_type
+               converters: {
+
+                       // Convert anything to text
+                       "* text": window.String,
+
+                       // Text to html (true = no transformation)
+                       "text html": true,
+
+                       // Evaluate text as a json expression
+                       "text json": jQuery.parseJSON,
+
+                       // Parse text as xml
+                       "text xml": jQuery.parseXML
+               },
+
+               // For options that shouldn't be deep extended:
+               // you can add your own custom options here if
+               // and when you create one that shouldn't be
+               // deep extended (see ajaxExtend)
+               flatOptions: {
+                       context: true,
+                       url: true
+               }
+       },
+
+       ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+       ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+       // Main method
+       ajax: function( url, options ) {
+
+               // If url is an object, simulate pre-1.5 signature
+               if ( typeof url === "object" ) {
+                       options = url;
+                       url = undefined;
+               }
+
+               // Force options to be an object
+               options = options || {};
+
+               var // Create the final options object
+                       s = jQuery.ajaxSetup( {}, options ),
+                       // Callbacks context
+                       callbackContext = s.context || s,
+                       // Context for global events
+                       // It's the callbackContext if one was provided in the options
+                       // and if it's a DOM node or a jQuery collection
+                       globalEventContext = callbackContext !== s &&
+                               ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+                                               jQuery( callbackContext ) : jQuery.event,
+                       // Deferreds
+                       deferred = jQuery.Deferred(),
+                       completeDeferred = jQuery.Callbacks( "once memory" ),
+                       // Status-dependent callbacks
+                       statusCode = s.statusCode || {},
+                       // ifModified key
+                       ifModifiedKey,
+                       // Headers (they are sent all at once)
+                       requestHeaders = {},
+                       requestHeadersNames = {},
+                       // Response headers
+                       responseHeadersString,
+                       responseHeaders,
+                       // transport
+                       transport,
+                       // timeout handle
+                       timeoutTimer,
+                       // Cross-domain detection vars
+                       parts,
+                       // The jqXHR state
+                       state = 0,
+                       // To know if global events are to be dispatched
+                       fireGlobals,
+                       // Loop variable
+                       i,
+                       // Fake xhr
+                       jqXHR = {
+
+                               readyState: 0,
+
+                               // Caches the header
+                               setRequestHeader: function( name, value ) {
+                                       if ( !state ) {
+                                               var lname = name.toLowerCase();
+                                               name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+                                               requestHeaders[ name ] = value;
+                                       }
+                                       return this;
+                               },
+
+                               // Raw string
+                               getAllResponseHeaders: function() {
+                                       return state === 2 ? responseHeadersString : null;
+                               },
+
+                               // Builds headers hashtable if needed
+                               getResponseHeader: function( key ) {
+                                       var match;
+                                       if ( state === 2 ) {
+                                               if ( !responseHeaders ) {
+                                                       responseHeaders = {};
+                                                       while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+                                                               responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+                                                       }
+                                               }
+                                               match = responseHeaders[ key.toLowerCase() ];
+                                       }
+                                       return match === undefined ? null : match;
+                               },
+
+                               // Overrides response content-type header
+                               overrideMimeType: function( type ) {
+                                       if ( !state ) {
+                                               s.mimeType = type;
+                                       }
+                                       return this;
+                               },
+
+                               // Cancel the request
+                               abort: function( statusText ) {
+                                       statusText = statusText || "abort";
+                                       if ( transport ) {
+                                               transport.abort( statusText );
+                                       }
+                                       done( 0, statusText );
+                                       return this;
+                               }
+                       };
+
+               // Callback for when everything is done
+               // It is defined here because jslint complains if it is declared
+               // at the end of the function (which would be more logical and readable)
+               function done( status, nativeStatusText, responses, headers ) {
+
+                       // Called once
+                       if ( state === 2 ) {
+                               return;
+                       }
+
+                       // State is "done" now
+                       state = 2;
+
+                       // Clear timeout if it exists
+                       if ( timeoutTimer ) {
+                               clearTimeout( timeoutTimer );
+                       }
+
+                       // Dereference transport for early garbage collection
+                       // (no matter how long the jqXHR object will be used)
+                       transport = undefined;
+
+                       // Cache response headers
+                       responseHeadersString = headers || "";
+
+                       // Set readyState
+                       jqXHR.readyState = status > 0 ? 4 : 0;
+
+                       var isSuccess,
+                               success,
+                               error,
+                               statusText = nativeStatusText,
+                               response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
+                               lastModified,
+                               etag;
+
+                       // If successful, handle type chaining
+                       if ( status >= 200 && status < 300 || status === 304 ) {
+
+                               // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+                               if ( s.ifModified ) {
+
+                                       if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+                                               jQuery.lastModified[ ifModifiedKey ] = lastModified;
+                                       }
+                                       if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+                                               jQuery.etag[ ifModifiedKey ] = etag;
+                                       }
+                               }
+
+                               // If not modified
+                               if ( status === 304 ) {
+
+                                       statusText = "notmodified";
+                                       isSuccess = true;
+
+                               // If we have data
+                               } else {
+
+                                       try {
+                                               success = ajaxConvert( s, response );
+                                               statusText = "success";
+                                               isSuccess = true;
+                                       } catch(e) {
+                                               // We have a parsererror
+                                               statusText = "parsererror";
+                                               error = e;
+                                       }
+                               }
+                       } else {
+                               // We extract error from statusText
+                               // then normalize statusText and status for non-aborts
+                               error = statusText;
+                               if ( !statusText || status ) {
+                                       statusText = "error";
+                                       if ( status < 0 ) {
+                                               status = 0;
+                                       }
+                               }
+                       }
+
+                       // Set data for the fake xhr object
+                       jqXHR.status = status;
+                       jqXHR.statusText = "" + ( nativeStatusText || statusText );
+
+                       // Success/Error
+                       if ( isSuccess ) {
+                               deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+                       } else {
+                               deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+                       }
+
+                       // Status-dependent callbacks
+                       jqXHR.statusCode( statusCode );
+                       statusCode = undefined;
+
+                       if ( fireGlobals ) {
+                               globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+                                               [ jqXHR, s, isSuccess ? success : error ] );
+                       }
+
+                       // Complete
+                       completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+                       if ( fireGlobals ) {
+                               globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+                               // Handle the global AJAX counter
+                               if ( !( --jQuery.active ) ) {
+                                       jQuery.event.trigger( "ajaxStop" );
+                               }
+                       }
+               }
+
+               // Attach deferreds
+               deferred.promise( jqXHR );
+               jqXHR.success = jqXHR.done;
+               jqXHR.error = jqXHR.fail;
+               jqXHR.complete = completeDeferred.add;
+
+               // Status-dependent callbacks
+               jqXHR.statusCode = function( map ) {
+                       if ( map ) {
+                               var tmp;
+                               if ( state < 2 ) {
+                                       for ( tmp in map ) {
+                                               statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+                                       }
+                               } else {
+                                       tmp = map[ jqXHR.status ];
+                                       jqXHR.then( tmp, tmp );
+                               }
+                       }
+                       return this;
+               };
+
+               // Remove hash character (#7531: and string promotion)
+               // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+               // We also use the url parameter if available
+               s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+               // Extract dataTypes list
+               s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
+
+               // Determine if a cross-domain request is in order
+               if ( s.crossDomain == null ) {
+                       parts = rurl.exec( s.url.toLowerCase() );
+                       s.crossDomain = !!( parts &&
+                               ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+                                       ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+                                               ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+                       );
+               }
+
+               // Convert data if not already a string
+               if ( s.data && s.processData && typeof s.data !== "string" ) {
+                       s.data = jQuery.param( s.data, s.traditional );
+               }
+
+               // Apply prefilters
+               inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+               // If request was aborted inside a prefilter, stop there
+               if ( state === 2 ) {
+                       return false;
+               }
+
+               // We can fire global events as of now if asked to
+               fireGlobals = s.global;
+
+               // Uppercase the type
+               s.type = s.type.toUpperCase();
+
+               // Determine if request has content
+               s.hasContent = !rnoContent.test( s.type );
+
+               // Watch for a new set of requests
+               if ( fireGlobals && jQuery.active++ === 0 ) {
+                       jQuery.event.trigger( "ajaxStart" );
+               }
+
+               // More options handling for requests with no content
+               if ( !s.hasContent ) {
+
+                       // If data is available, append data to url
+                       if ( s.data ) {
+                               s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+                               // #9682: remove data so that it's not used in an eventual retry
+                               delete s.data;
+                       }
+
+                       // Get ifModifiedKey before adding the anti-cache parameter
+                       ifModifiedKey = s.url;
+
+                       // Add anti-cache in url if needed
+                       if ( s.cache === false ) {
+
+                               var ts = jQuery.now(),
+                                       // try replacing _= if it is there
+                                       ret = s.url.replace( rts, "$1_=" + ts );
+
+                               // if nothing was replaced, add timestamp to the end
+                               s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+                       }
+               }
+
+               // Set the correct header, if data is being sent
+               if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+                       jqXHR.setRequestHeader( "Content-Type", s.contentType );
+               }
+
+               // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+               if ( s.ifModified ) {
+                       ifModifiedKey = ifModifiedKey || s.url;
+                       if ( jQuery.lastModified[ ifModifiedKey ] ) {
+                               jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+                       }
+                       if ( jQuery.etag[ ifModifiedKey ] ) {
+                               jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+                       }
+               }
+
+               // Set the Accepts header for the server, depending on the dataType
+               jqXHR.setRequestHeader(
+                       "Accept",
+                       s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+                               s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+                               s.accepts[ "*" ]
+               );
+
+               // Check for headers option
+               for ( i in s.headers ) {
+                       jqXHR.setRequestHeader( i, s.headers[ i ] );
+               }
+
+               // Allow custom headers/mimetypes and early abort
+               if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+                               // Abort if not done already
+                               jqXHR.abort();
+                               return false;
+
+               }
+
+               // Install callbacks on deferreds
+               for ( i in { success: 1, error: 1, complete: 1 } ) {
+                       jqXHR[ i ]( s[ i ] );
+               }
+
+               // Get transport
+               transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+               // If no transport, we auto-abort
+               if ( !transport ) {
+                       done( -1, "No Transport" );
+               } else {
+                       jqXHR.readyState = 1;
+                       // Send global event
+                       if ( fireGlobals ) {
+                               globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+                       }
+                       // Timeout
+                       if ( s.async && s.timeout > 0 ) {
+                               timeoutTimer = setTimeout( function(){
+                                       jqXHR.abort( "timeout" );
+                               }, s.timeout );
+                       }
+
+                       try {
+                               state = 1;
+                               transport.send( requestHeaders, done );
+                       } catch (e) {
+                               // Propagate exception as error if not done
+                               if ( state < 2 ) {
+                                       done( -1, e );
+                               // Simply rethrow otherwise
+                               } else {
+                                       throw e;
+                               }
+                       }
+               }
+
+               return jqXHR;
+       },
+
+       // Serialize an array of form elements or a set of
+       // key/values into a query string
+       param: function( a, traditional ) {
+               var s = [],
+                       add = function( key, value ) {
+                               // If value is a function, invoke it and return its value
+                               value = jQuery.isFunction( value ) ? value() : value;
+                               s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+                       };
+
+               // Set traditional to true for jQuery <= 1.3.2 behavior.
+               if ( traditional === undefined ) {
+                       traditional = jQuery.ajaxSettings.traditional;
+               }
+
+               // If an array was passed in, assume that it is an array of form elements.
+               if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+                       // Serialize the form elements
+                       jQuery.each( a, function() {
+                               add( this.name, this.value );
+                       });
+
+               } else {
+                       // If traditional, encode the "old" way (the way 1.3.2 or older
+                       // did it), otherwise encode params recursively.
+                       for ( var prefix in a ) {
+                               buildParams( prefix, a[ prefix ], traditional, add );
+                       }
+               }
+
+               // Return the resulting serialization
+               return s.join( "&" ).replace( r20, "+" );
+       }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+       if ( jQuery.isArray( obj ) ) {
+               // Serialize array item.
+               jQuery.each( obj, function( i, v ) {
+                       if ( traditional || rbracket.test( prefix ) ) {
+                               // Treat each array item as a scalar.
+                               add( prefix, v );
+
+                       } else {
+                               // If array item is non-scalar (array or object), encode its
+                               // numeric index to resolve deserialization ambiguity issues.
+                               // Note that rack (as of 1.0.0) can't currently deserialize
+                               // nested arrays properly, and attempting to do so may cause
+                               // a server error. Possible fixes are to modify rack's
+                               // deserialization algorithm or to provide an option or flag
+                               // to force array serialization to be shallow.
+                               buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+                       }
+               });
+
+       } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+               // Serialize object item.
+               for ( var name in obj ) {
+                       buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+               }
+
+       } else {
+               // Serialize scalar item.
+               add( prefix, obj );
+       }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+       // Counter for holding the number of active queries
+       active: 0,
+
+       // Last-Modified header cache for next request
+       lastModified: {},
+       etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+       var contents = s.contents,
+               dataTypes = s.dataTypes,
+               responseFields = s.responseFields,
+               ct,
+               type,
+               finalDataType,
+               firstDataType;
+
+       // Fill responseXXX fields
+       for ( type in responseFields ) {
+               if ( type in responses ) {
+                       jqXHR[ responseFields[type] ] = responses[ type ];
+               }
+       }
+
+       // Remove auto dataType and get content-type in the process
+       while( dataTypes[ 0 ] === "*" ) {
+               dataTypes.shift();
+               if ( ct === undefined ) {
+                       ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+               }
+       }
+
+       // Check if we're dealing with a known content-type
+       if ( ct ) {
+               for ( type in contents ) {
+                       if ( contents[ type ] && contents[ type ].test( ct ) ) {
+                               dataTypes.unshift( type );
+                               break;
+                       }
+               }
+       }
+
+       // Check to see if we have a response for the expected dataType
+       if ( dataTypes[ 0 ] in responses ) {
+               finalDataType = dataTypes[ 0 ];
+       } else {
+               // Try convertible dataTypes
+               for ( type in responses ) {
+                       if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+                               finalDataType = type;
+                               break;
+                       }
+                       if ( !firstDataType ) {
+                               firstDataType = type;
+                       }
+               }
+               // Or just use first one
+               finalDataType = finalDataType || firstDataType;
+       }
+
+       // If we found a dataType
+       // We add the dataType to the list if needed
+       // and return the corresponding response
+       if ( finalDataType ) {
+               if ( finalDataType !== dataTypes[ 0 ] ) {
+                       dataTypes.unshift( finalDataType );
+               }
+               return responses[ finalDataType ];
+       }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+       // Apply the dataFilter if provided
+       if ( s.dataFilter ) {
+               response = s.dataFilter( response, s.dataType );
+       }
+
+       var dataTypes = s.dataTypes,
+               converters = {},
+               i,
+               key,
+               length = dataTypes.length,
+               tmp,
+               // Current and previous dataTypes
+               current = dataTypes[ 0 ],
+               prev,
+               // Conversion expression
+               conversion,
+               // Conversion function
+               conv,
+               // Conversion functions (transitive conversion)
+               conv1,
+               conv2;
+
+       // For each dataType in the chain
+       for ( i = 1; i < length; i++ ) {
+
+               // Create converters map
+               // with lowercased keys
+               if ( i === 1 ) {
+                       for ( key in s.converters ) {
+                               if ( typeof key === "string" ) {
+                                       converters[ key.toLowerCase() ] = s.converters[ key ];
+                               }
+                       }
+               }
+
+               // Get the dataTypes
+               prev = current;
+               current = dataTypes[ i ];
+
+               // If current is auto dataType, update it to prev
+               if ( current === "*" ) {
+                       current = prev;
+               // If no auto and dataTypes are actually different
+               } else if ( prev !== "*" && prev !== current ) {
+
+                       // Get the converter
+                       conversion = prev + " " + current;
+                       conv = converters[ conversion ] || converters[ "* " + current ];
+
+                       // If there is no direct converter, search transitively
+                       if ( !conv ) {
+                               conv2 = undefined;
+                               for ( conv1 in converters ) {
+                                       tmp = conv1.split( " " );
+                                       if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
+                                               conv2 = converters[ tmp[1] + " " + current ];
+                                               if ( conv2 ) {
+                                                       conv1 = converters[ conv1 ];
+                                                       if ( conv1 === true ) {
+                                                               conv = conv2;
+                                                       } else if ( conv2 === true ) {
+                                                               conv = conv1;
+                                                       }
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       // If we found no converter, dispatch an error
+                       if ( !( conv || conv2 ) ) {
+                               jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
+                       }
+                       // If found converter is not an equivalence
+                       if ( conv !== true ) {
+                               // Convert with 1 or 2 converters accordingly
+                               response = conv ? conv( response ) : conv2( conv1(response) );
+                       }
+               }
+       }
+       return response;
+}
+
+
+
+
+var jsc = jQuery.now(),
+       jsre = /(\=)\?(&|$)|\?\?/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+       jsonp: "callback",
+       jsonpCallback: function() {
+               return jQuery.expando + "_" + ( jsc++ );
+       }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+       var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType );
+
+       if ( s.dataTypes[ 0 ] === "jsonp" ||
+               s.jsonp !== false && ( jsre.test( s.url ) ||
+                               inspectData && jsre.test( s.data ) ) ) {
+
+               var responseContainer,
+                       jsonpCallback = s.jsonpCallback =
+                               jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+                       previous = window[ jsonpCallback ],
+                       url = s.url,
+                       data = s.data,
+                       replace = "$1" + jsonpCallback + "$2";
+
+               if ( s.jsonp !== false ) {
+                       url = url.replace( jsre, replace );
+                       if ( s.url === url ) {
+                               if ( inspectData ) {
+                                       data = data.replace( jsre, replace );
+                               }
+                               if ( s.data === data ) {
+                                       // Add callback manually
+                                       url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
+                               }
+                       }
+               }
+
+               s.url = url;
+               s.data = data;
+
+               // Install callback
+               window[ jsonpCallback ] = function( response ) {
+                       responseContainer = [ response ];
+               };
+
+               // Clean-up function
+               jqXHR.always(function() {
+                       // Set callback back to previous value
+                       window[ jsonpCallback ] = previous;
+                       // Call if it was a function and we have a response
+                       if ( responseContainer && jQuery.isFunction( previous ) ) {
+                               window[ jsonpCallback ]( responseContainer[ 0 ] );
+                       }
+               });
+
+               // Use data converter to retrieve json after script execution
+               s.converters["script json"] = function() {
+                       if ( !responseContainer ) {
+                               jQuery.error( jsonpCallback + " was not called" );
+                       }
+                       return responseContainer[ 0 ];
+               };
+
+               // force json dataType
+               s.dataTypes[ 0 ] = "json";
+
+               // Delegate to script
+               return "script";
+       }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+       accepts: {
+               script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+       },
+       contents: {
+               script: /javascript|ecmascript/
+       },
+       converters: {
+               "text script": function( text ) {
+                       jQuery.globalEval( text );
+                       return text;
+               }
+       }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+       if ( s.cache === undefined ) {
+               s.cache = false;
+       }
+       if ( s.crossDomain ) {
+               s.type = "GET";
+               s.global = false;
+       }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+       // This transport only deals with cross domain requests
+       if ( s.crossDomain ) {
+
+               var script,
+                       head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+               return {
+
+                       send: function( _, callback ) {
+
+                               script = document.createElement( "script" );
+
+                               script.async = "async";
+
+                               if ( s.scriptCharset ) {
+                                       script.charset = s.scriptCharset;
+                               }
+
+                               script.src = s.url;
+
+                               // Attach handlers for all browsers
+                               script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+                                       if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+                                               // Handle memory leak in IE
+                                               script.onload = script.onreadystatechange = null;
+
+                                               // Remove the script
+                                               if ( head && script.parentNode ) {
+                                                       head.removeChild( script );
+                                               }
+
+                                               // Dereference the script
+                                               script = undefined;
+
+                                               // Callback if not abort
+                                               if ( !isAbort ) {
+                                                       callback( 200, "success" );
+                                               }
+                                       }
+                               };
+                               // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
+                               // This arises when a base node is used (#2709 and #4378).
+                               head.insertBefore( script, head.firstChild );
+                       },
+
+                       abort: function() {
+                               if ( script ) {
+                                       script.onload( 0, 1 );
+                               }
+                       }
+               };
+       }
+});
+
+
+
+
+var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+       xhrOnUnloadAbort = window.ActiveXObject ? function() {
+               // Abort all pending requests
+               for ( var key in xhrCallbacks ) {
+                       xhrCallbacks[ key ]( 0, 1 );
+               }
+       } : false,
+       xhrId = 0,
+       xhrCallbacks;
+
+// Functions to create xhrs
+function createStandardXHR() {
+       try {
+               return new window.XMLHttpRequest();
+       } catch( e ) {}
+}
+
+function createActiveXHR() {
+       try {
+               return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+       } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+       /* Microsoft failed to properly
+        * implement the XMLHttpRequest in IE7 (can't request local files),
+        * so we use the ActiveXObject when it is available
+        * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+        * we need a fallback.
+        */
+       function() {
+               return !this.isLocal && createStandardXHR() || createActiveXHR();
+       } :
+       // For all other browsers, use the standard XMLHttpRequest object
+       createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+       jQuery.extend( jQuery.support, {
+               ajax: !!xhr,
+               cors: !!xhr && ( "withCredentials" in xhr )
+       });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+       jQuery.ajaxTransport(function( s ) {
+               // Cross domain only allowed if supported through XMLHttpRequest
+               if ( !s.crossDomain || jQuery.support.cors ) {
+
+                       var callback;
+
+                       return {
+                               send: function( headers, complete ) {
+
+                                       // Get a new xhr
+                                       var xhr = s.xhr(),
+                                               handle,
+                                               i;
+
+                                       // Open the socket
+                                       // Passing null username, generates a login popup on Opera (#2865)
+                                       if ( s.username ) {
+                                               xhr.open( s.type, s.url, s.async, s.username, s.password );
+                                       } else {
+                                               xhr.open( s.type, s.url, s.async );
+                                       }
+
+                                       // Apply custom fields if provided
+                                       if ( s.xhrFields ) {
+                                               for ( i in s.xhrFields ) {
+                                                       xhr[ i ] = s.xhrFields[ i ];
+                                               }
+                                       }
+
+                                       // Override mime type if needed
+                                       if ( s.mimeType && xhr.overrideMimeType ) {
+                                               xhr.overrideMimeType( s.mimeType );
+                                       }
+
+                                       // X-Requested-With header
+                                       // For cross-domain requests, seeing as conditions for a preflight are
+                                       // akin to a jigsaw puzzle, we simply never set it to be sure.
+                                       // (it can always be set on a per-request basis or even using ajaxSetup)
+                                       // For same-domain requests, won't change header if already provided.
+                                       if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+                                               headers[ "X-Requested-With" ] = "XMLHttpRequest";
+                                       }
+
+                                       // Need an extra try/catch for cross domain requests in Firefox 3
+                                       try {
+                                               for ( i in headers ) {
+                                                       xhr.setRequestHeader( i, headers[ i ] );
+                                               }
+                                       } catch( _ ) {}
+
+                                       // Do send the request
+                                       // This may raise an exception which is actually
+                                       // handled in jQuery.ajax (so no try/catch here)
+                                       xhr.send( ( s.hasContent && s.data ) || null );
+
+                                       // Listener
+                                       callback = function( _, isAbort ) {
+
+                                               var status,
+                                                       statusText,
+                                                       responseHeaders,
+                                                       responses,
+                                                       xml;
+
+                                               // Firefox throws exceptions when accessing properties
+                                               // of an xhr when a network error occured
+                                               // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+                                               try {
+
+                                                       // Was never called and is aborted or complete
+                                                       if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+                                                               // Only called once
+                                                               callback = undefined;
+
+                                                               // Do not keep as active anymore
+                                                               if ( handle ) {
+                                                                       xhr.onreadystatechange = jQuery.noop;
+                                                                       if ( xhrOnUnloadAbort ) {
+                                                                               delete xhrCallbacks[ handle ];
+                                                                       }
+                                                               }
+
+                                                               // If it's an abort
+                                                               if ( isAbort ) {
+                                                                       // Abort it manually if needed
+                                                                       if ( xhr.readyState !== 4 ) {
+                                                                               xhr.abort();
+                                                                       }
+                                                               } else {
+                                                                       status = xhr.status;
+                                                                       responseHeaders = xhr.getAllResponseHeaders();
+                                                                       responses = {};
+                                                                       xml = xhr.responseXML;
+
+                                                                       // Construct response list
+                                                                       if ( xml && xml.documentElement /* #4958 */ ) {
+                                                                               responses.xml = xml;
+                                                                       }
+
+                                                                       // When requesting binary data, IE6-9 will throw an exception
+                                                                       // on any attempt to access responseText (#11426)
+                                                                       try {
+                                                                               responses.text = xhr.responseText;
+                                                                       } catch( _ ) {
+                                                                       }
+
+                                                                       // Firefox throws an exception when accessing
+                                                                       // statusText for faulty cross-domain requests
+                                                                       try {
+                                                                               statusText = xhr.statusText;
+                                                                       } catch( e ) {
+                                                                               // We normalize with Webkit giving an empty statusText
+                                                                               statusText = "";
+                                                                       }
+
+                                                                       // Filter status for non standard behaviors
+
+                                                                       // If the request is local and we have data: assume a success
+                                                                       // (success with no data won't get notified, that's the best we
+                                                                       // can do given current implementations)
+                                                                       if ( !status && s.isLocal && !s.crossDomain ) {
+                                                                               status = responses.text ? 200 : 404;
+                                                                       // IE - #1450: sometimes returns 1223 when it should be 204
+                                                                       } else if ( status === 1223 ) {
+                                                                               status = 204;
+                                                                       }
+                                                               }
+                                                       }
+                                               } catch( firefoxAccessException ) {
+                                                       if ( !isAbort ) {
+                                                               complete( -1, firefoxAccessException );
+                                                       }
+                                               }
+
+                                               // Call complete if needed
+                                               if ( responses ) {
+                                                       complete( status, statusText, responses, responseHeaders );
+                                               }
+                                       };
+
+                                       // if we're in sync mode or it's in cache
+                                       // and has been retrieved directly (IE6 & IE7)
+                                       // we need to manually fire the callback
+                                       if ( !s.async || xhr.readyState === 4 ) {
+                                               callback();
+                                       } else {
+                                               handle = ++xhrId;
+                                               if ( xhrOnUnloadAbort ) {
+                                                       // Create the active xhrs callbacks list if needed
+                                                       // and attach the unload handler
+                                                       if ( !xhrCallbacks ) {
+                                                               xhrCallbacks = {};
+                                                               jQuery( window ).unload( xhrOnUnloadAbort );
+                                                       }
+                                                       // Add to list of active xhrs callbacks
+                                                       xhrCallbacks[ handle ] = callback;
+                                               }
+                                               xhr.onreadystatechange = callback;
+                                       }
+                               },
+
+                               abort: function() {
+                                       if ( callback ) {
+                                               callback(0,1);
+                                       }
+                               }
+                       };
+               }
+       });
+}
+
+
+
+
+var elemdisplay = {},
+       iframe, iframeDoc,
+       rfxtypes = /^(?:toggle|show|hide)$/,
+       rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
+       timerId,
+       fxAttrs = [
+               // height animations
+               [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+               // width animations
+               [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+               // opacity animations
+               [ "opacity" ]
+       ],
+       fxNow;
+
+jQuery.fn.extend({
+       show: function( speed, easing, callback ) {
+               var elem, display;
+
+               if ( speed || speed === 0 ) {
+                       return this.animate( genFx("show", 3), speed, easing, callback );
+
+               } else {
+                       for ( var i = 0, j = this.length; i < j; i++ ) {
+                               elem = this[ i ];
+
+                               if ( elem.style ) {
+                                       display = elem.style.display;
+
+                                       // Reset the inline display of this element to learn if it is
+                                       // being hidden by cascaded rules or not
+                                       if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
+                                               display = elem.style.display = "";
+                                       }
+
+                                       // Set elements which have been overridden with display: none
+                                       // in a stylesheet to whatever the default browser style is
+                                       // for such an element
+                                       if ( (display === "" && jQuery.css(elem, "display") === "none") ||
+                                               !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+                                               jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+                                       }
+                               }
+                       }
+
+                       // Set the display of most of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( i = 0; i < j; i++ ) {
+                               elem = this[ i ];
+
+                               if ( elem.style ) {
+                                       display = elem.style.display;
+
+                                       if ( display === "" || display === "none" ) {
+                                               elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
+                                       }
+                               }
+                       }
+
+                       return this;
+               }
+       },
+
+       hide: function( speed, easing, callback ) {
+               if ( speed || speed === 0 ) {
+                       return this.animate( genFx("hide", 3), speed, easing, callback);
+
+               } else {
+                       var elem, display,
+                               i = 0,
+                               j = this.length;
+
+                       for ( ; i < j; i++ ) {
+                               elem = this[i];
+                               if ( elem.style ) {
+                                       display = jQuery.css( elem, "display" );
+
+                                       if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
+                                               jQuery._data( elem, "olddisplay", display );
+                                       }
+                               }
+                       }
+
+                       // Set the display of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( i = 0; i < j; i++ ) {
+                               if ( this[i].style ) {
+                                       this[i].style.display = "none";
+                               }
+                       }
+
+                       return this;
+               }
+       },
+
+       // Save the old toggle function
+       _toggle: jQuery.fn.toggle,
+
+       toggle: function( fn, fn2, callback ) {
+               var bool = typeof fn === "boolean";
+
+               if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+                       this._toggle.apply( this, arguments );
+
+               } else if ( fn == null || bool ) {
+                       this.each(function() {
+                               var state = bool ? fn : jQuery(this).is(":hidden");
+                               jQuery(this)[ state ? "show" : "hide" ]();
+                       });
+
+               } else {
+                       this.animate(genFx("toggle", 3), fn, fn2, callback);
+               }
+
+               return this;
+       },
+
+       fadeTo: function( speed, to, easing, callback ) {
+               return this.filter(":hidden").css("opacity", 0).show().end()
+                                       .animate({opacity: to}, speed, easing, callback);
+       },
+
+       animate: function( prop, speed, easing, callback ) {
+               var optall = jQuery.speed( speed, easing, callback );
+
+               if ( jQuery.isEmptyObject( prop ) ) {
+                       return this.each( optall.complete, [ false ] );
+               }
+
+               // Do not change referenced properties as per-property easing will be lost
+               prop = jQuery.extend( {}, prop );
+
+               function doAnimation() {
+                       // XXX 'this' does not always have a nodeName when running the
+                       // test suite
+
+                       if ( optall.queue === false ) {
+                               jQuery._mark( this );
+                       }
+
+                       var opt = jQuery.extend( {}, optall ),
+                               isElement = this.nodeType === 1,
+                               hidden = isElement && jQuery(this).is(":hidden"),
+                               name, val, p, e, hooks, replace,
+                               parts, start, end, unit,
+                               method;
+
+                       // will store per property easing and be used to determine when an animation is complete
+                       opt.animatedProperties = {};
+
+                       // first pass over propertys to expand / normalize
+                       for ( p in prop ) {
+                               name = jQuery.camelCase( p );
+                               if ( p !== name ) {
+                                       prop[ name ] = prop[ p ];
+                                       delete prop[ p ];
+                               }
+
+                               if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
+                                       replace = hooks.expand( prop[ name ] );
+                                       delete prop[ name ];
+
+                                       // not quite $.extend, this wont overwrite keys already present.
+                                       // also - reusing 'p' from above because we have the correct "name"
+                                       for ( p in replace ) {
+                                               if ( ! ( p in prop ) ) {
+                                                       prop[ p ] = replace[ p ];
+                                               }
+                                       }
+                               }
+                       }
+
+                       for ( name in prop ) {
+                               val = prop[ name ];
+                               // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
+                               if ( jQuery.isArray( val ) ) {
+                                       opt.animatedProperties[ name ] = val[ 1 ];
+                                       val = prop[ name ] = val[ 0 ];
+                               } else {
+                                       opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
+                               }
+
+                               if ( val === "hide" && hidden || val === "show" && !hidden ) {
+                                       return opt.complete.call( this );
+                               }
+
+                               if ( isElement && ( name === "height" || name === "width" ) ) {
+                                       // Make sure that nothing sneaks out
+                                       // Record all 3 overflow attributes because IE does not
+                                       // change the overflow attribute when overflowX and
+                                       // overflowY are set to the same value
+                                       opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+                                       // Set display property to inline-block for height/width
+                                       // animations on inline elements that are having width/height animated
+                                       if ( jQuery.css( this, "display" ) === "inline" &&
+                                                       jQuery.css( this, "float" ) === "none" ) {
+
+                                               // inline-level elements accept inline-block;
+                                               // block-level elements need to be inline with layout
+                                               if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
+                                                       this.style.display = "inline-block";
+
+                                               } else {
+                                                       this.style.zoom = 1;
+                                               }
+                                       }
+                               }
+                       }
+
+                       if ( opt.overflow != null ) {
+                               this.style.overflow = "hidden";
+                       }
+
+                       for ( p in prop ) {
+                               e = new jQuery.fx( this, opt, p );
+                               val = prop[ p ];
+
+                               if ( rfxtypes.test( val ) ) {
+
+                                       // Tracks whether to show or hide based on private
+                                       // data attached to the element
+                                       method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
+                                       if ( method ) {
+                                               jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
+                                               e[ method ]();
+                                       } else {
+                                               e[ val ]();
+                                       }
+
+                               } else {
+                                       parts = rfxnum.exec( val );
+                                       start = e.cur();
+
+                                       if ( parts ) {
+                                               end = parseFloat( parts[2] );
+                                               unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+
+                                               // We need to compute starting value
+                                               if ( unit !== "px" ) {
+                                                       jQuery.style( this, p, (end || 1) + unit);
+                                                       start = ( (end || 1) / e.cur() ) * start;
+                                                       jQuery.style( this, p, start + unit);
+                                               }
+
+                                               // If a +=/-= token was provided, we're doing a relative animation
+                                               if ( parts[1] ) {
+                                                       end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
+                                               }
+
+                                               e.custom( start, end, unit );
+
+                                       } else {
+                                               e.custom( start, val, "" );
+                                       }
+                               }
+                       }
+
+                       // For JS strict compliance
+                       return true;
+               }
+
+               return optall.queue === false ?
+                       this.each( doAnimation ) :
+                       this.queue( optall.queue, doAnimation );
+       },
+
+       stop: function( type, clearQueue, gotoEnd ) {
+               if ( typeof type !== "string" ) {
+                       gotoEnd = clearQueue;
+                       clearQueue = type;
+                       type = undefined;
+               }
+               if ( clearQueue && type !== false ) {
+                       this.queue( type || "fx", [] );
+               }
+
+               return this.each(function() {
+                       var index,
+                               hadTimers = false,
+                               timers = jQuery.timers,
+                               data = jQuery._data( this );
+
+                       // clear marker counters if we know they won't be
+                       if ( !gotoEnd ) {
+                               jQuery._unmark( true, this );
+                       }
+
+                       function stopQueue( elem, data, index ) {
+                               var hooks = data[ index ];
+                               jQuery.removeData( elem, index, true );
+                               hooks.stop( gotoEnd );
+                       }
+
+                       if ( type == null ) {
+                               for ( index in data ) {
+                                       if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
+                                               stopQueue( this, data, index );
+                                       }
+                               }
+                       } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
+                               stopQueue( this, data, index );
+                       }
+
+                       for ( index = timers.length; index--; ) {
+                               if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+                                       if ( gotoEnd ) {
+
+                                               // force the next step to be the last
+                                               timers[ index ]( true );
+                                       } else {
+                                               timers[ index ].saveState();
+                                       }
+                                       hadTimers = true;
+                                       timers.splice( index, 1 );
+                               }
+                       }
+
+                       // start the next in the queue if the last step wasn't forced
+                       // timers currently will call their complete callbacks, which will dequeue
+                       // but only if they were gotoEnd
+                       if ( !( gotoEnd && hadTimers ) ) {
+                               jQuery.dequeue( this, type );
+                       }
+               });
+       }
+
+});
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+       setTimeout( clearFxNow, 0 );
+       return ( fxNow = jQuery.now() );
+}
+
+function clearFxNow() {
+       fxNow = undefined;
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, num ) {
+       var obj = {};
+
+       jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
+               obj[ this ] = type;
+       });
+
+       return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+       slideDown: genFx( "show", 1 ),
+       slideUp: genFx( "hide", 1 ),
+       slideToggle: genFx( "toggle", 1 ),
+       fadeIn: { opacity: "show" },
+       fadeOut: { opacity: "hide" },
+       fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+       jQuery.fn[ name ] = function( speed, easing, callback ) {
+               return this.animate( props, speed, easing, callback );
+       };
+});
+
+jQuery.extend({
+       speed: function( speed, easing, fn ) {
+               var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+                       complete: fn || !fn && easing ||
+                               jQuery.isFunction( speed ) && speed,
+                       duration: speed,
+                       easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+               };
+
+               opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+                       opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+               // normalize opt.queue - true/undefined/null -> "fx"
+               if ( opt.queue == null || opt.queue === true ) {
+                       opt.queue = "fx";
+               }
+
+               // Queueing
+               opt.old = opt.complete;
+
+               opt.complete = function( noUnmark ) {
+                       if ( jQuery.isFunction( opt.old ) ) {
+                               opt.old.call( this );
+                       }
+
+                       if ( opt.queue ) {
+                               jQuery.dequeue( this, opt.queue );
+                       } else if ( noUnmark !== false ) {
+                               jQuery._unmark( this );
+                       }
+               };
+
+               return opt;
+       },
+
+       easing: {
+               linear: function( p ) {
+                       return p;
+               },
+               swing: function( p ) {
+                       return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
+               }
+       },
+
+       timers: [],
+
+       fx: function( elem, options, prop ) {
+               this.options = options;
+               this.elem = elem;
+               this.prop = prop;
+
+               options.orig = options.orig || {};
+       }
+
+});
+
+jQuery.fx.prototype = {
+       // Simple function for setting a style value
+       update: function() {
+               if ( this.options.step ) {
+                       this.options.step.call( this.elem, this.now, this );
+               }
+
+               ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
+       },
+
+       // Get the current size
+       cur: function() {
+               if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
+                       return this.elem[ this.prop ];
+               }
+
+               var parsed,
+                       r = jQuery.css( this.elem, this.prop );
+               // Empty strings, null, undefined and "auto" are converted to 0,
+               // complex values such as "rotate(1rad)" are returned as is,
+               // simple values such as "10px" are parsed to Float.
+               return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
+       },
+
+       // Start an animation from one number to another
+       custom: function( from, to, unit ) {
+               var self = this,
+                       fx = jQuery.fx;
+
+               this.startTime = fxNow || createFxNow();
+               this.end = to;
+               this.now = this.start = from;
+               this.pos = this.state = 0;
+               this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
+
+               function t( gotoEnd ) {
+                       return self.step( gotoEnd );
+               }
+
+               t.queue = this.options.queue;
+               t.elem = this.elem;
+               t.saveState = function() {
+                       if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
+                               if ( self.options.hide ) {
+                                       jQuery._data( self.elem, "fxshow" + self.prop, self.start );
+                               } else if ( self.options.show ) {
+                                       jQuery._data( self.elem, "fxshow" + self.prop, self.end );
+                               }
+                       }
+               };
+
+               if ( t() && jQuery.timers.push(t) && !timerId ) {
+                       timerId = setInterval( fx.tick, fx.interval );
+               }
+       },
+
+       // Simple 'show' function
+       show: function() {
+               var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
+
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
+               this.options.show = true;
+
+               // Begin the animation
+               // Make sure that we start at a small width/height to avoid any flash of content
+               if ( dataShow !== undefined ) {
+                       // This show is picking up where a previous hide or show left off
+                       this.custom( this.cur(), dataShow );
+               } else {
+                       this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
+               }
+
+               // Start by showing the element
+               jQuery( this.elem ).show();
+       },
+
+       // Simple 'hide' function
+       hide: function() {
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
+               this.options.hide = true;
+
+               // Begin the animation
+               this.custom( this.cur(), 0 );
+       },
+
+       // Each step of an animation
+       step: function( gotoEnd ) {
+               var p, n, complete,
+                       t = fxNow || createFxNow(),
+                       done = true,
+                       elem = this.elem,
+                       options = this.options;
+
+               if ( gotoEnd || t >= options.duration + this.startTime ) {
+                       this.now = this.end;
+                       this.pos = this.state = 1;
+                       this.update();
+
+                       options.animatedProperties[ this.prop ] = true;
+
+                       for ( p in options.animatedProperties ) {
+                               if ( options.animatedProperties[ p ] !== true ) {
+                                       done = false;
+                               }
+                       }
+
+                       if ( done ) {
+                               // Reset the overflow
+                               if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+
+                                       jQuery.each( [ "", "X", "Y" ], function( index, value ) {
+                                               elem.style[ "overflow" + value ] = options.overflow[ index ];
+                                       });
+                               }
+
+                               // Hide the element if the "hide" operation was done
+                               if ( options.hide ) {
+                                       jQuery( elem ).hide();
+                               }
+
+                               // Reset the properties, if the item has been hidden or shown
+                               if ( options.hide || options.show ) {
+                                       for ( p in options.animatedProperties ) {
+                                               jQuery.style( elem, p, options.orig[ p ] );
+                                               jQuery.removeData( elem, "fxshow" + p, true );
+                                               // Toggle data is no longer needed
+                                               jQuery.removeData( elem, "toggle" + p, true );
+                                       }
+                               }
+
+                               // Execute the complete function
+                               // in the event that the complete function throws an exception
+                               // we must ensure it won't be called twice. #5684
+
+                               complete = options.complete;
+                               if ( complete ) {
+
+                                       options.complete = false;
+                                       complete.call( elem );
+                               }
+                       }
+
+                       return false;
+
+               } else {
+                       // classical easing cannot be used with an Infinity duration
+                       if ( options.duration == Infinity ) {
+                               this.now = t;
+                       } else {
+                               n = t - this.startTime;
+                               this.state = n / options.duration;
+
+                               // Perform the easing function, defaults to swing
+                               this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
+                               this.now = this.start + ( (this.end - this.start) * this.pos );
+                       }
+                       // Perform the next step of the animation
+                       this.update();
+               }
+
+               return true;
+       }
+};
+
+jQuery.extend( jQuery.fx, {
+       tick: function() {
+               var timer,
+                       timers = jQuery.timers,
+                       i = 0;
+
+               for ( ; i < timers.length; i++ ) {
+                       timer = timers[ i ];
+                       // Checks the timer has not already been removed
+                       if ( !timer() && timers[ i ] === timer ) {
+                               timers.splice( i--, 1 );
+                       }
+               }
+
+               if ( !timers.length ) {
+                       jQuery.fx.stop();
+               }
+       },
+
+       interval: 13,
+
+       stop: function() {
+               clearInterval( timerId );
+               timerId = null;
+       },
+
+       speeds: {
+               slow: 600,
+               fast: 200,
+               // Default speed
+               _default: 400
+       },
+
+       step: {
+               opacity: function( fx ) {
+                       jQuery.style( fx.elem, "opacity", fx.now );
+               },
+
+               _default: function( fx ) {
+                       if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+                               fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+                       } else {
+                               fx.elem[ fx.prop ] = fx.now;
+                       }
+               }
+       }
+});
+
+// Ensure props that can't be negative don't go there on undershoot easing
+jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) {
+       // exclude marginTop, marginLeft, marginBottom and marginRight from this list
+       if ( prop.indexOf( "margin" ) ) {
+               jQuery.fx.step[ prop ] = function( fx ) {
+                       jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
+               };
+       }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.animated = function( elem ) {
+               return jQuery.grep(jQuery.timers, function( fn ) {
+                       return elem === fn.elem;
+               }).length;
+       };
+}
+
+// Try to restore the default display value of an element
+function defaultDisplay( nodeName ) {
+
+       if ( !elemdisplay[ nodeName ] ) {
+
+               var body = document.body,
+                       elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
+                       display = elem.css( "display" );
+               elem.remove();
+
+               // If the simple way fails,
+               // get element's real default display by attaching it to a temp iframe
+               if ( display === "none" || display === "" ) {
+                       // No iframe to use yet, so create it
+                       if ( !iframe ) {
+                               iframe = document.createElement( "iframe" );
+                               iframe.frameBorder = iframe.width = iframe.height = 0;
+                       }
+
+                       body.appendChild( iframe );
+
+                       // Create a cacheable copy of the iframe document on first call.
+                       // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+                       // document to it; WebKit & Firefox won't allow reusing the iframe document.
+                       if ( !iframeDoc || !iframe.createElement ) {
+                               iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+                               iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
+                               iframeDoc.close();
+                       }
+
+                       elem = iframeDoc.createElement( nodeName );
+
+                       iframeDoc.body.appendChild( elem );
+
+                       display = jQuery.css( elem, "display" );
+                       body.removeChild( iframe );
+               }
+
+               // Store the correct default display
+               elemdisplay[ nodeName ] = display;
+       }
+
+       return elemdisplay[ nodeName ];
+}
+
+
+
+
+var getOffset,
+       rtable = /^t(?:able|d|h)$/i,
+       rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+       getOffset = function( elem, doc, docElem, box ) {
+               try {
+                       box = elem.getBoundingClientRect();
+               } catch(e) {}
+
+               // Make sure we're not dealing with a disconnected DOM node
+               if ( !box || !jQuery.contains( docElem, elem ) ) {
+                       return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+               }
+
+               var body = doc.body,
+                       win = getWindow( doc ),
+                       clientTop  = docElem.clientTop  || body.clientTop  || 0,
+                       clientLeft = docElem.clientLeft || body.clientLeft || 0,
+                       scrollTop  = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop,
+                       scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
+                       top  = box.top  + scrollTop  - clientTop,
+                       left = box.left + scrollLeft - clientLeft;
+
+               return { top: top, left: left };
+       };
+
+} else {
+       getOffset = function( elem, doc, docElem ) {
+               var computedStyle,
+                       offsetParent = elem.offsetParent,
+                       prevOffsetParent = elem,
+                       body = doc.body,
+                       defaultView = doc.defaultView,
+                       prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+                       top = elem.offsetTop,
+                       left = elem.offsetLeft;
+
+               while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+                       if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
+                               break;
+                       }
+
+                       computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+                       top  -= elem.scrollTop;
+                       left -= elem.scrollLeft;
+
+                       if ( elem === offsetParent ) {
+                               top  += elem.offsetTop;
+                               left += elem.offsetLeft;
+
+                               if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+                                       top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+                                       left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+                               }
+
+                               prevOffsetParent = offsetParent;
+                               offsetParent = elem.offsetParent;
+                       }
+
+                       if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+                               top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+                               left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+                       }
+
+                       prevComputedStyle = computedStyle;
+               }
+
+               if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+                       top  += body.offsetTop;
+                       left += body.offsetLeft;
+               }
+
+               if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
+                       top  += Math.max( docElem.scrollTop, body.scrollTop );
+                       left += Math.max( docElem.scrollLeft, body.scrollLeft );
+               }
+
+               return { top: top, left: left };
+       };
+}
+
+jQuery.fn.offset = function( options ) {
+       if ( arguments.length ) {
+               return options === undefined ?
+                       this :
+                       this.each(function( i ) {
+                               jQuery.offset.setOffset( this, options, i );
+                       });
+       }
+
+       var elem = this[0],
+               doc = elem && elem.ownerDocument;
+
+       if ( !doc ) {
+               return null;
+       }
+
+       if ( elem === doc.body ) {
+               return jQuery.offset.bodyOffset( elem );
+       }
+
+       return getOffset( elem, doc, doc.documentElement );
+};
+
+jQuery.offset = {
+
+       bodyOffset: function( body ) {
+               var top = body.offsetTop,
+                       left = body.offsetLeft;
+
+               if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
+                       top  += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+                       left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+               }
+
+               return { top: top, left: left };
+       },
+
+       setOffset: function( elem, options, i ) {
+               var position = jQuery.css( elem, "position" );
+
+               // set position first, in-case top/left are set even on static elem
+               if ( position === "static" ) {
+                       elem.style.position = "relative";
+               }
+
+               var curElem = jQuery( elem ),
+                       curOffset = curElem.offset(),
+                       curCSSTop = jQuery.css( elem, "top" ),
+                       curCSSLeft = jQuery.css( elem, "left" ),
+                       calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+                       props = {}, curPosition = {}, curTop, curLeft;
+
+               // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+               if ( calculatePosition ) {
+                       curPosition = curElem.position();
+                       curTop = curPosition.top;
+                       curLeft = curPosition.left;
+               } else {
+                       curTop = parseFloat( curCSSTop ) || 0;
+                       curLeft = parseFloat( curCSSLeft ) || 0;
+               }
+
+               if ( jQuery.isFunction( options ) ) {
+                       options = options.call( elem, i, curOffset );
+               }
+
+               if ( options.top != null ) {
+                       props.top = ( options.top - curOffset.top ) + curTop;
+               }
+               if ( options.left != null ) {
+                       props.left = ( options.left - curOffset.left ) + curLeft;
+               }
+
+               if ( "using" in options ) {
+                       options.using.call( elem, props );
+               } else {
+                       curElem.css( props );
+               }
+       }
+};
+
+
+jQuery.fn.extend({
+
+       position: function() {
+               if ( !this[0] ) {
+                       return null;
+               }
+
+               var elem = this[0],
+
+               // Get *real* offsetParent
+               offsetParent = this.offsetParent(),
+
+               // Get correct offsets
+               offset       = this.offset(),
+               parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+               // Subtract element margins
+               // note: when an element has margin: auto the offsetLeft and marginLeft
+               // are the same in Safari causing offset.left to incorrectly be 0
+               offset.top  -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+               offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+               // Add offsetParent borders
+               parentOffset.top  += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+               parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+               // Subtract the two offsets
+               return {
+                       top:  offset.top  - parentOffset.top,
+                       left: offset.left - parentOffset.left
+               };
+       },
+
+       offsetParent: function() {
+               return this.map(function() {
+                       var offsetParent = this.offsetParent || document.body;
+                       while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+                               offsetParent = offsetParent.offsetParent;
+                       }
+                       return offsetParent;
+               });
+       }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+       var top = /Y/.test( prop );
+
+       jQuery.fn[ method ] = function( val ) {
+               return jQuery.access( this, function( elem, method, val ) {
+                       var win = getWindow( elem );
+
+                       if ( val === undefined ) {
+                               return win ? (prop in win) ? win[ prop ] :
+                                       jQuery.support.boxModel && win.document.documentElement[ method ] ||
+                                               win.document.body[ method ] :
+                                       elem[ method ];
+                       }
+
+                       if ( win ) {
+                               win.scrollTo(
+                                       !top ? val : jQuery( win ).scrollLeft(),
+                                        top ? val : jQuery( win ).scrollTop()
+                               );
+
+                       } else {
+                               elem[ method ] = val;
+                       }
+               }, method, val, arguments.length, null );
+       };
+});
+
+function getWindow( elem ) {
+       return jQuery.isWindow( elem ) ?
+               elem :
+               elem.nodeType === 9 ?
+                       elem.defaultView || elem.parentWindow :
+                       false;
+}
+
+
+
+
+// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+       var clientProp = "client" + name,
+               scrollProp = "scroll" + name,
+               offsetProp = "offset" + name;
+
+       // innerHeight and innerWidth
+       jQuery.fn[ "inner" + name ] = function() {
+               var elem = this[0];
+               return elem ?
+                       elem.style ?
+                       parseFloat( jQuery.css( elem, type, "padding" ) ) :
+                       this[ type ]() :
+                       null;
+       };
+
+       // outerHeight and outerWidth
+       jQuery.fn[ "outer" + name ] = function( margin ) {
+               var elem = this[0];
+               return elem ?
+                       elem.style ?
+                       parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
+                       this[ type ]() :
+                       null;
+       };
+
+       jQuery.fn[ type ] = function( value ) {
+               return jQuery.access( this, function( elem, type, value ) {
+                       var doc, docElemProp, orig, ret;
+
+                       if ( jQuery.isWindow( elem ) ) {
+                               // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
+                               doc = elem.document;
+                               docElemProp = doc.documentElement[ clientProp ];
+                               return jQuery.support.boxModel && docElemProp ||
+                                       doc.body && doc.body[ clientProp ] || docElemProp;
+                       }
+
+                       // Get document width or height
+                       if ( elem.nodeType === 9 ) {
+                               // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+                               doc = elem.documentElement;
+
+                               // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height]
+                               // so we can't use max, as it'll choose the incorrect offset[Width/Height]
+                               // instead we use the correct client[Width/Height]
+                               // support:IE6
+                               if ( doc[ clientProp ] >= doc[ scrollProp ] ) {
+                                       return doc[ clientProp ];
+                               }
+
+                               return Math.max(
+                                       elem.body[ scrollProp ], doc[ scrollProp ],
+                                       elem.body[ offsetProp ], doc[ offsetProp ]
+                               );
+                       }
+
+                       // Get width or height on the element
+                       if ( value === undefined ) {
+                               orig = jQuery.css( elem, type );
+                               ret = parseFloat( orig );
+                               return jQuery.isNumeric( ret ) ? ret : orig;
+                       }
+
+                       // Set the width or height on the element
+                       jQuery( elem ).css( type, value );
+               }, type, value, arguments.length, null );
+       };
+});
+
+
+
+
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+       define( "jquery", [], function () { return jQuery; } );
+}
+
+
+
+})( window );
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/example/js/jquery.mobile-1.1.1.js b/plugins/jpush-phonegap-plugin/example/js/jquery.mobile-1.1.1.js
new file mode 100755 (executable)
index 0000000..058936c
--- /dev/null
@@ -0,0 +1,7690 @@
+/*
+* jQuery Mobile Framework 1.1.1 1981b3f5ec22675ae47df8f0bdf9622e7780e90e
+* http://jquerymobile.com
+*
+* Copyright 2012 jQuery Foundation and other contributors
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*
+*/
+(function ( root, doc, factory ) {
+       if ( typeof define === "function" && define.amd ) {
+               // AMD. Register as an anonymous module.
+               define( [ "jquery" ], function ( $ ) {
+                       factory( $, root, doc );
+                       return $.mobile;
+               });
+       } else {
+               // Browser globals
+               factory( root.jQuery, root, doc );
+       }
+}( this, document, function ( jQuery, window, document, undefined ) {
+
+
+// This plugin is an experiment for abstracting away the touch and mouse
+// events so that developers don't have to worry about which method of input
+// the device their document is loaded on supports.
+//
+// The idea here is to allow the developer to register listeners for the
+// basic mouse events, such as mousedown, mousemove, mouseup, and click,
+// and the plugin will take care of registering the correct listeners
+// behind the scenes to invoke the listener at the fastest possible time
+// for that device, while still retaining the order of event firing in
+// the traditional mouse environment, should multiple handlers be registered
+// on the same element for different events.
+//
+// The current version exposes the following virtual events to jQuery bind methods:
+// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
+
+(function( $, window, document, undefined ) {
+
+var dataPropertyName = "virtualMouseBindings",
+       touchTargetPropertyName = "virtualTouchID",
+       virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
+       touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
+       mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
+       mouseEventProps = $.event.props.concat( mouseHookProps ),
+       activeDocHandlers = {},
+       resetTimerID = 0,
+       startX = 0,
+       startY = 0,
+       didScroll = false,
+       clickBlockList = [],
+       blockMouseTriggers = false,
+       blockTouchTriggers = false,
+       eventCaptureSupported = "addEventListener" in document,
+       $document = $( document ),
+       nextTouchID = 1,
+       lastTouchID = 0;
+
+$.vmouse = {
+       moveDistanceThreshold: 10,
+       clickDistanceThreshold: 10,
+       resetTimerDuration: 1500
+};
+
+function getNativeEvent( event ) {
+
+       while ( event && typeof event.originalEvent !== "undefined" ) {
+               event = event.originalEvent;
+       }
+       return event;
+}
+
+function createVirtualEvent( event, eventType ) {
+
+       var t = event.type,
+               oe, props, ne, prop, ct, touch, i, j;
+
+       event = $.Event(event);
+       event.type = eventType;
+
+       oe = event.originalEvent;
+       props = $.event.props;
+
+       // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
+       // https://github.com/jquery/jquery-mobile/issues/3280
+       if ( t.search( /^(mouse|click)/ ) > -1 ) {
+               props = mouseEventProps;
+       }
+
+       // copy original event properties over to the new event
+       // this would happen if we could call $.event.fix instead of $.Event
+       // but we don't have a way to force an event to be fixed multiple times
+       if ( oe ) {
+               for ( i = props.length, prop; i; ) {
+                       prop = props[ --i ];
+                       event[ prop ] = oe[ prop ];
+               }
+       }
+
+       // make sure that if the mouse and click virtual events are generated
+       // without a .which one is defined
+       if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ){
+               event.which = 1;
+       }
+
+       if ( t.search(/^touch/) !== -1 ) {
+               ne = getNativeEvent( oe );
+               t = ne.touches;
+               ct = ne.changedTouches;
+               touch = ( t && t.length ) ? t[0] : ( (ct && ct.length) ? ct[ 0 ] : undefined );
+
+               if ( touch ) {
+                       for ( j = 0, len = touchEventProps.length; j < len; j++){
+                               prop = touchEventProps[ j ];
+                               event[ prop ] = touch[ prop ];
+                       }
+               }
+       }
+
+       return event;
+}
+
+function getVirtualBindingFlags( element ) {
+
+       var flags = {},
+               b, k;
+
+       while ( element ) {
+
+               b = $.data( element, dataPropertyName );
+
+               for (  k in b ) {
+                       if ( b[ k ] ) {
+                               flags[ k ] = flags.hasVirtualBinding = true;
+                       }
+               }
+               element = element.parentNode;
+       }
+       return flags;
+}
+
+function getClosestElementWithVirtualBinding( element, eventType ) {
+       var b;
+       while ( element ) {
+
+               b = $.data( element, dataPropertyName );
+
+               if ( b && ( !eventType || b[ eventType ] ) ) {
+                       return element;
+               }
+               element = element.parentNode;
+       }
+       return null;
+}
+
+function enableTouchBindings() {
+       blockTouchTriggers = false;
+}
+
+function disableTouchBindings() {
+       blockTouchTriggers = true;
+}
+
+function enableMouseBindings() {
+       lastTouchID = 0;
+       clickBlockList.length = 0;
+       blockMouseTriggers = false;
+
+       // When mouse bindings are enabled, our
+       // touch bindings are disabled.
+       disableTouchBindings();
+}
+
+function disableMouseBindings() {
+       // When mouse bindings are disabled, our
+       // touch bindings are enabled.
+       enableTouchBindings();
+}
+
+function startResetTimer() {
+       clearResetTimer();
+       resetTimerID = setTimeout(function(){
+               resetTimerID = 0;
+               enableMouseBindings();
+       }, $.vmouse.resetTimerDuration );
+}
+
+function clearResetTimer() {
+       if ( resetTimerID ){
+               clearTimeout( resetTimerID );
+               resetTimerID = 0;
+       }
+}
+
+function triggerVirtualEvent( eventType, event, flags ) {
+       var ve;
+
+       if ( ( flags && flags[ eventType ] ) ||
+                               ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
+
+               ve = createVirtualEvent( event, eventType );
+
+               $( event.target).trigger( ve );
+       }
+
+       return ve;
+}
+
+function mouseEventCallback( event ) {
+       var touchID = $.data(event.target, touchTargetPropertyName);
+
+       if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
+               var ve = triggerVirtualEvent( "v" + event.type, event );
+               if ( ve ) {
+                       if ( ve.isDefaultPrevented() ) {
+                               event.preventDefault();
+                       }
+                       if ( ve.isPropagationStopped() ) {
+                               event.stopPropagation();
+                       }
+                       if ( ve.isImmediatePropagationStopped() ) {
+                               event.stopImmediatePropagation();
+                       }
+               }
+       }
+}
+
+function handleTouchStart( event ) {
+
+       var touches = getNativeEvent( event ).touches,
+               target, flags;
+
+       if ( touches && touches.length === 1 ) {
+
+               target = event.target;
+               flags = getVirtualBindingFlags( target );
+
+               if ( flags.hasVirtualBinding ) {
+
+                       lastTouchID = nextTouchID++;
+                       $.data( target, touchTargetPropertyName, lastTouchID );
+
+                       clearResetTimer();
+
+                       disableMouseBindings();
+                       didScroll = false;
+
+                       var t = getNativeEvent( event ).touches[ 0 ];
+                       startX = t.pageX;
+                       startY = t.pageY;
+
+                       triggerVirtualEvent( "vmouseover", event, flags );
+                       triggerVirtualEvent( "vmousedown", event, flags );
+               }
+       }
+}
+
+function handleScroll( event ) {
+       if ( blockTouchTriggers ) {
+               return;
+       }
+
+       if ( !didScroll ) {
+               triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
+       }
+
+       didScroll = true;
+       startResetTimer();
+}
+
+function handleTouchMove( event ) {
+       if ( blockTouchTriggers ) {
+               return;
+       }
+
+       var t = getNativeEvent( event ).touches[ 0 ],
+               didCancel = didScroll,
+               moveThreshold = $.vmouse.moveDistanceThreshold;
+               didScroll = didScroll ||
+                       ( Math.abs(t.pageX - startX) > moveThreshold ||
+                               Math.abs(t.pageY - startY) > moveThreshold ),
+               flags = getVirtualBindingFlags( event.target );
+
+       if ( didScroll && !didCancel ) {
+               triggerVirtualEvent( "vmousecancel", event, flags );
+       }
+
+       triggerVirtualEvent( "vmousemove", event, flags );
+       startResetTimer();
+}
+
+function handleTouchEnd( event ) {
+       if ( blockTouchTriggers ) {
+               return;
+       }
+
+       disableTouchBindings();
+
+       var flags = getVirtualBindingFlags( event.target ),
+               t;
+       triggerVirtualEvent( "vmouseup", event, flags );
+
+       if ( !didScroll ) {
+               var ve = triggerVirtualEvent( "vclick", event, flags );
+               if ( ve && ve.isDefaultPrevented() ) {
+                       // The target of the mouse events that follow the touchend
+                       // event don't necessarily match the target used during the
+                       // touch. This means we need to rely on coordinates for blocking
+                       // any click that is generated.
+                       t = getNativeEvent( event ).changedTouches[ 0 ];
+                       clickBlockList.push({
+                               touchID: lastTouchID,
+                               x: t.clientX,
+                               y: t.clientY
+                       });
+
+                       // Prevent any mouse events that follow from triggering
+                       // virtual event notifications.
+                       blockMouseTriggers = true;
+               }
+       }
+       triggerVirtualEvent( "vmouseout", event, flags);
+       didScroll = false;
+
+       startResetTimer();
+}
+
+function hasVirtualBindings( ele ) {
+       var bindings = $.data( ele, dataPropertyName ),
+               k;
+
+       if ( bindings ) {
+               for ( k in bindings ) {
+                       if ( bindings[ k ] ) {
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+function dummyMouseHandler(){}
+
+function getSpecialEventObject( eventType ) {
+       var realType = eventType.substr( 1 );
+
+       return {
+               setup: function( data, namespace ) {
+                       // If this is the first virtual mouse binding for this element,
+                       // add a bindings object to its data.
+
+                       if ( !hasVirtualBindings( this ) ) {
+                               $.data( this, dataPropertyName, {});
+                       }
+
+                       // If setup is called, we know it is the first binding for this
+                       // eventType, so initialize the count for the eventType to zero.
+                       var bindings = $.data( this, dataPropertyName );
+                       bindings[ eventType ] = true;
+
+                       // If this is the first virtual mouse event for this type,
+                       // register a global handler on the document.
+
+                       activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
+
+                       if ( activeDocHandlers[ eventType ] === 1 ) {
+                               $document.bind( realType, mouseEventCallback );
+                       }
+
+                       // Some browsers, like Opera Mini, won't dispatch mouse/click events
+                       // for elements unless they actually have handlers registered on them.
+                       // To get around this, we register dummy handlers on the elements.
+
+                       $( this ).bind( realType, dummyMouseHandler );
+
+                       // For now, if event capture is not supported, we rely on mouse handlers.
+                       if ( eventCaptureSupported ) {
+                               // If this is the first virtual mouse binding for the document,
+                               // register our touchstart handler on the document.
+
+                               activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
+
+                               if (activeDocHandlers[ "touchstart" ] === 1) {
+                                       $document.bind( "touchstart", handleTouchStart )
+                                               .bind( "touchend", handleTouchEnd )
+
+                                               // On touch platforms, touching the screen and then dragging your finger
+                                               // causes the window content to scroll after some distance threshold is
+                                               // exceeded. On these platforms, a scroll prevents a click event from being
+                                               // dispatched, and on some platforms, even the touchend is suppressed. To
+                                               // mimic the suppression of the click event, we need to watch for a scroll
+                                               // event. Unfortunately, some platforms like iOS don't dispatch scroll
+                                               // events until *AFTER* the user lifts their finger (touchend). This means
+                                               // we need to watch both scroll and touchmove events to figure out whether
+                                               // or not a scroll happenens before the touchend event is fired.
+
+                                               .bind( "touchmove", handleTouchMove )
+                                               .bind( "scroll", handleScroll );
+                               }
+                       }
+               },
+
+               teardown: function( data, namespace ) {
+                       // If this is the last virtual binding for this eventType,
+                       // remove its global handler from the document.
+
+                       --activeDocHandlers[ eventType ];
+
+                       if ( !activeDocHandlers[ eventType ] ) {
+                               $document.unbind( realType, mouseEventCallback );
+                       }
+
+                       if ( eventCaptureSupported ) {
+                               // If this is the last virtual mouse binding in existence,
+                               // remove our document touchstart listener.
+
+                               --activeDocHandlers[ "touchstart" ];
+
+                               if ( !activeDocHandlers[ "touchstart" ] ) {
+                                       $document.unbind( "touchstart", handleTouchStart )
+                                               .unbind( "touchmove", handleTouchMove )
+                                               .unbind( "touchend", handleTouchEnd )
+                                               .unbind( "scroll", handleScroll );
+                               }
+                       }
+
+                       var $this = $( this ),
+                               bindings = $.data( this, dataPropertyName );
+
+                       // teardown may be called when an element was
+                       // removed from the DOM. If this is the case,
+                       // jQuery core may have already stripped the element
+                       // of any data bindings so we need to check it before
+                       // using it.
+                       if ( bindings ) {
+                               bindings[ eventType ] = false;
+                       }
+
+                       // Unregister the dummy event handler.
+
+                       $this.unbind( realType, dummyMouseHandler );
+
+                       // If this is the last virtual mouse binding on the
+                       // element, remove the binding data from the element.
+
+                       if ( !hasVirtualBindings( this ) ) {
+                               $this.removeData( dataPropertyName );
+                       }
+               }
+       };
+}
+
+// Expose our custom events to the jQuery bind/unbind mechanism.
+
+for ( var i = 0; i < virtualEventNames.length; i++ ){
+       $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
+}
+
+// Add a capture click handler to block clicks.
+// Note that we require event capture support for this so if the device
+// doesn't support it, we punt for now and rely solely on mouse events.
+if ( eventCaptureSupported ) {
+       document.addEventListener( "click", function( e ){
+               var cnt = clickBlockList.length,
+                       target = e.target,
+                       x, y, ele, i, o, touchID;
+
+               if ( cnt ) {
+                       x = e.clientX;
+                       y = e.clientY;
+                       threshold = $.vmouse.clickDistanceThreshold;
+
+                       // The idea here is to run through the clickBlockList to see if
+                       // the current click event is in the proximity of one of our
+                       // vclick events that had preventDefault() called on it. If we find
+                       // one, then we block the click.
+                       //
+                       // Why do we have to rely on proximity?
+                       //
+                       // Because the target of the touch event that triggered the vclick
+                       // can be different from the target of the click event synthesized
+                       // by the browser. The target of a mouse/click event that is syntehsized
+                       // from a touch event seems to be implementation specific. For example,
+                       // some browsers will fire mouse/click events for a link that is near
+                       // a touch event, even though the target of the touchstart/touchend event
+                       // says the user touched outside the link. Also, it seems that with most
+                       // browsers, the target of the mouse/click event is not calculated until the
+                       // time it is dispatched, so if you replace an element that you touched
+                       // with another element, the target of the mouse/click will be the new
+                       // element underneath that point.
+                       //
+                       // Aside from proximity, we also check to see if the target and any
+                       // of its ancestors were the ones that blocked a click. This is necessary
+                       // because of the strange mouse/click target calculation done in the
+                       // Android 2.1 browser, where if you click on an element, and there is a
+                       // mouse/click handler on one of its ancestors, the target will be the
+                       // innermost child of the touched element, even if that child is no where
+                       // near the point of touch.
+
+                       ele = target;
+
+                       while ( ele ) {
+                               for ( i = 0; i < cnt; i++ ) {
+                                       o = clickBlockList[ i ];
+                                       touchID = 0;
+
+                                       if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
+                                                               $.data( ele, touchTargetPropertyName ) === o.touchID ) {
+                                               // XXX: We may want to consider removing matches from the block list
+                                               //      instead of waiting for the reset timer to fire.
+                                               e.preventDefault();
+                                               e.stopPropagation();
+                                               return;
+                                       }
+                               }
+                               ele = ele.parentNode;
+                       }
+               }
+       }, true);
+}
+})( jQuery, window, document );
+
+
+
+// Script: jQuery hashchange event
+// 
+// *Version: 1.3, Last updated: 7/21/2010*
+// 
+// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
+// GitHub       - http://github.com/cowboy/jquery-hashchange/
+// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
+// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
+// 
+// About: License
+// 
+// Copyright (c) 2010 "Cowboy" Ben Alman,
+// Dual licensed under the MIT and GPL licenses.
+// http://benalman.com/about/license/
+// 
+// About: Examples
+// 
+// These working examples, complete with fully commented code, illustrate a few
+// ways in which this plugin can be used.
+// 
+// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
+// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
+// 
+// About: Support and Testing
+// 
+// Information about what version or versions of jQuery this plugin has been
+// tested with, what browsers it has been tested in, and where the unit tests
+// reside (so you can test it yourself).
+// 
+// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
+// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
+//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
+// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
+// 
+// About: Known issues
+// 
+// While this jQuery hashchange event implementation is quite stable and
+// robust, there are a few unfortunate browser bugs surrounding expected
+// hashchange event-based behaviors, independent of any JavaScript
+// window.onhashchange abstraction. See the following examples for more
+// information:
+// 
+// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
+// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
+// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
+// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
+// 
+// Also note that should a browser natively support the window.onhashchange 
+// event, but not report that it does, the fallback polling loop will be used.
+// 
+// About: Release History
+// 
+// 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
+//         "removable" for mobile-only development. Added IE6/7 document.title
+//         support. Attempted to make Iframe as hidden as possible by using
+//         techniques from http://www.paciellogroup.com/blog/?p=604. Added 
+//         support for the "shortcut" format $(window).hashchange( fn ) and
+//         $(window).hashchange() like jQuery provides for built-in events.
+//         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
+//         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
+//         and <jQuery.fn.hashchange.src> properties plus document-domain.html
+//         file to address access denied issues when setting document.domain in
+//         IE6/7.
+// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
+//         from a page on another domain would cause an error in Safari 4. Also,
+//         IE6/7 Iframe is now inserted after the body (this actually works),
+//         which prevents the page from scrolling when the event is first bound.
+//         Event can also now be bound before DOM ready, but it won't be usable
+//         before then in IE6/7.
+// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
+//         where browser version is incorrectly reported as 8.0, despite
+//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
+// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
+//         window.onhashchange functionality into a separate plugin for users
+//         who want just the basic event & back button support, without all the
+//         extra awesomeness that BBQ provides. This plugin will be included as
+//         part of jQuery BBQ, but also be available separately.
+
+(function($,window,undefined){
+  // Reused string.
+  var str_hashchange = 'hashchange',
+    
+    // Method / object references.
+    doc = document,
+    fake_onhashchange,
+    special = $.event.special,
+    
+    // Does the browser support window.onhashchange? Note that IE8 running in
+    // IE7 compatibility mode reports true for 'onhashchange' in window, even
+    // though the event isn't supported, so also test document.documentMode.
+    doc_mode = doc.documentMode,
+    supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
+  
+  // Get location.hash (or what you'd expect location.hash to be) sans any
+  // leading #. Thanks for making this necessary, Firefox!
+  function get_fragment( url ) {
+    url = url || location.href;
+    return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
+  };
+  
+  // Method: jQuery.fn.hashchange
+  // 
+  // Bind a handler to the window.onhashchange event or trigger all bound
+  // window.onhashchange event handlers. This behavior is consistent with
+  // jQuery's built-in event handlers.
+  // 
+  // Usage:
+  // 
+  // > jQuery(window).hashchange( [ handler ] );
+  // 
+  // Arguments:
+  // 
+  //  handler - (Function) Optional handler to be bound to the hashchange
+  //    event. This is a "shortcut" for the more verbose form:
+  //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
+  //    all bound window.onhashchange event handlers will be triggered. This
+  //    is a shortcut for the more verbose
+  //    jQuery(window).trigger( 'hashchange' ). These forms are described in
+  //    the <hashchange event> section.
+  // 
+  // Returns:
+  // 
+  //  (jQuery) The initial jQuery collection of elements.
+  
+  // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
+  // $(elem).hashchange() for triggering, like jQuery does for built-in events.
+  $.fn[ str_hashchange ] = function( fn ) {
+    return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
+  };
+  
+  // Property: jQuery.fn.hashchange.delay
+  // 
+  // The numeric interval (in milliseconds) at which the <hashchange event>
+  // polling loop executes. Defaults to 50.
+  
+  // Property: jQuery.fn.hashchange.domain
+  // 
+  // If you're setting document.domain in your JavaScript, and you want hash
+  // history to work in IE6/7, not only must this property be set, but you must
+  // also set document.domain BEFORE jQuery is loaded into the page. This
+  // property is only applicable if you are supporting IE6/7 (or IE8 operating
+  // in "IE7 compatibility" mode).
+  // 
+  // In addition, the <jQuery.fn.hashchange.src> property must be set to the
+  // path of the included "document-domain.html" file, which can be renamed or
+  // modified if necessary (note that the document.domain specified must be the
+  // same in both your main JavaScript as well as in this file).
+  // 
+  // Usage:
+  // 
+  // jQuery.fn.hashchange.domain = document.domain;
+  
+  // Property: jQuery.fn.hashchange.src
+  // 
+  // If, for some reason, you need to specify an Iframe src file (for example,
+  // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
+  // do so using this property. Note that when using this property, history
+  // won't be recorded in IE6/7 until the Iframe src file loads. This property
+  // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
+  // compatibility" mode).
+  // 
+  // Usage:
+  // 
+  // jQuery.fn.hashchange.src = 'path/to/file.html';
+  
+  $.fn[ str_hashchange ].delay = 50;
+  /*
+  $.fn[ str_hashchange ].domain = null;
+  $.fn[ str_hashchange ].src = null;
+  */
+  
+  // Event: hashchange event
+  // 
+  // Fired when location.hash changes. In browsers that support it, the native
+  // HTML5 window.onhashchange event is used, otherwise a polling loop is
+  // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
+  // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
+  // compatibility" mode), a hidden Iframe is created to allow the back button
+  // and hash-based history to work.
+  // 
+  // Usage as described in <jQuery.fn.hashchange>:
+  // 
+  // > // Bind an event handler.
+  // > jQuery(window).hashchange( function(e) {
+  // >   var hash = location.hash;
+  // >   ...
+  // > });
+  // > 
+  // > // Manually trigger the event handler.
+  // > jQuery(window).hashchange();
+  // 
+  // A more verbose usage that allows for event namespacing:
+  // 
+  // > // Bind an event handler.
+  // > jQuery(window).bind( 'hashchange', function(e) {
+  // >   var hash = location.hash;
+  // >   ...
+  // > });
+  // > 
+  // > // Manually trigger the event handler.
+  // > jQuery(window).trigger( 'hashchange' );
+  // 
+  // Additional Notes:
+  // 
+  // * The polling loop and Iframe are not created until at least one handler
+  //   is actually bound to the 'hashchange' event.
+  // * If you need the bound handler(s) to execute immediately, in cases where
+  //   a location.hash exists on page load, via bookmark or page refresh for
+  //   example, use jQuery(window).hashchange() or the more verbose 
+  //   jQuery(window).trigger( 'hashchange' ).
+  // * The event can be bound before DOM ready, but since it won't be usable
+  //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
+  //   to bind it inside a DOM ready handler.
+  
+  // Override existing $.event.special.hashchange methods (allowing this plugin
+  // to be defined after jQuery BBQ in BBQ's source code).
+  special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
+    
+    // Called only when the first 'hashchange' event is bound to window.
+    setup: function() {
+      // If window.onhashchange is supported natively, there's nothing to do..
+      if ( supports_onhashchange ) { return false; }
+      
+      // Otherwise, we need to create our own. And we don't want to call this
+      // until the user binds to the event, just in case they never do, since it
+      // will create a polling loop and possibly even a hidden Iframe.
+      $( fake_onhashchange.start );
+    },
+    
+    // Called only when the last 'hashchange' event is unbound from window.
+    teardown: function() {
+      // If window.onhashchange is supported natively, there's nothing to do..
+      if ( supports_onhashchange ) { return false; }
+      
+      // Otherwise, we need to stop ours (if possible).
+      $( fake_onhashchange.stop );
+    }
+    
+  });
+  
+  // fake_onhashchange does all the work of triggering the window.onhashchange
+  // event for browsers that don't natively support it, including creating a
+  // polling loop to watch for hash changes and in IE 6/7 creating a hidden
+  // Iframe to enable back and forward.
+  fake_onhashchange = (function(){
+    var self = {},
+      timeout_id,
+      
+      // Remember the initial hash so it doesn't get triggered immediately.
+      last_hash = get_fragment(),
+      
+      fn_retval = function(val){ return val; },
+      history_set = fn_retval,
+      history_get = fn_retval;
+    
+    // Start the polling loop.
+    self.start = function() {
+      timeout_id || poll();
+    };
+    
+    // Stop the polling loop.
+    self.stop = function() {
+      timeout_id && clearTimeout( timeout_id );
+      timeout_id = undefined;
+    };
+    
+    // This polling loop checks every $.fn.hashchange.delay milliseconds to see
+    // if location.hash has changed, and triggers the 'hashchange' event on
+    // window when necessary.
+    function poll() {
+      var hash = get_fragment(),
+        history_hash = history_get( last_hash );
+      
+      if ( hash !== last_hash ) {
+        history_set( last_hash = hash, history_hash );
+        
+        $(window).trigger( str_hashchange );
+        
+      } else if ( history_hash !== last_hash ) {
+        location.href = location.href.replace( /#.*/, '' ) + history_hash;
+      }
+      
+      timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
+    };
+    
+    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+    // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
+    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+    $.browser.msie && !supports_onhashchange && (function(){
+      // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
+      // when running in "IE7 compatibility" mode.
+      
+      var iframe,
+        iframe_src;
+      
+      // When the event is bound and polling starts in IE 6/7, create a hidden
+      // Iframe for history handling.
+      self.start = function(){
+        if ( !iframe ) {
+          iframe_src = $.fn[ str_hashchange ].src;
+          iframe_src = iframe_src && iframe_src + get_fragment();
+          
+          // Create hidden Iframe. Attempt to make Iframe as hidden as possible
+          // by using techniques from http://www.paciellogroup.com/blog/?p=604.
+          iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
+            
+            // When Iframe has completely loaded, initialize the history and
+            // start polling.
+            .one( 'load', function(){
+              iframe_src || history_set( get_fragment() );
+              poll();
+            })
+            
+            // Load Iframe src if specified, otherwise nothing.
+            .attr( 'src', iframe_src || 'javascript:0' )
+            
+            // Append Iframe after the end of the body to prevent unnecessary
+            // initial page scrolling (yes, this works).
+            .insertAfter( 'body' )[0].contentWindow;
+          
+          // Whenever `document.title` changes, update the Iframe's title to
+          // prettify the back/next history menu entries. Since IE sometimes
+          // errors with "Unspecified error" the very first time this is set
+          // (yes, very useful) wrap this with a try/catch block.
+          doc.onpropertychange = function(){
+            try {
+              if ( event.propertyName === 'title' ) {
+                iframe.document.title = doc.title;
+              }
+            } catch(e) {}
+          };
+          
+        }
+      };
+      
+      // Override the "stop" method since an IE6/7 Iframe was created. Even
+      // if there are no longer any bound event handlers, the polling loop
+      // is still necessary for back/next to work at all!
+      self.stop = fn_retval;
+      
+      // Get history by looking at the hidden Iframe's location.hash.
+      history_get = function() {
+        return get_fragment( iframe.location.href );
+      };
+      
+      // Set a new history item by opening and then closing the Iframe
+      // document, *then* setting its location.hash. If document.domain has
+      // been set, update that as well.
+      history_set = function( hash, history_hash ) {
+        var iframe_doc = iframe.document,
+          domain = $.fn[ str_hashchange ].domain;
+        
+        if ( hash !== history_hash ) {
+          // Update Iframe with any initial `document.title` that might be set.
+          iframe_doc.title = doc.title;
+          
+          // Opening the Iframe's document after it has been closed is what
+          // actually adds a history entry.
+          iframe_doc.open();
+          
+          // Set document.domain for the Iframe document as well, if necessary.
+          domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
+          
+          iframe_doc.close();
+          
+          // Update the Iframe's hash, for great justice.
+          iframe.location.hash = hash;
+        }
+      };
+      
+    })();
+    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
+    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    
+    return self;
+  })();
+  
+})(jQuery,this);
+
+/*!
+ * jQuery UI Widget @VERSION
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+
+(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+       var _cleanData = $.cleanData;
+       $.cleanData = function( elems ) {
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       $( elem ).triggerHandler( "remove" );
+               }
+               _cleanData( elems );
+       };
+} else {
+       var _remove = $.fn.remove;
+       $.fn.remove = function( selector, keepData ) {
+               return this.each(function() {
+                       if ( !keepData ) {
+                               if ( !selector || $.filter( selector, [ this ] ).length ) {
+                                       $( "*", this ).add( [ this ] ).each(function() {
+                                               $( this ).triggerHandler( "remove" );
+                                       });
+                               }
+                       }
+                       return _remove.call( $(this), selector, keepData );
+               });
+       };
+}
+
+$.widget = function( name, base, prototype ) {
+       var namespace = name.split( "." )[ 0 ],
+               fullName;
+       name = name.split( "." )[ 1 ];
+       fullName = namespace + "-" + name;
+
+       if ( !prototype ) {
+               prototype = base;
+               base = $.Widget;
+       }
+
+       // create selector for plugin
+       $.expr[ ":" ][ fullName ] = function( elem ) {
+               return !!$.data( elem, name );
+       };
+
+       $[ namespace ] = $[ namespace ] || {};
+       $[ namespace ][ name ] = function( options, element ) {
+               // allow instantiation without initializing for simple inheritance
+               if ( arguments.length ) {
+                       this._createWidget( options, element );
+               }
+       };
+
+       var basePrototype = new base();
+       // we need to make the options hash a property directly on the new instance
+       // otherwise we'll modify the options hash on the prototype that we're
+       // inheriting from
+//     $.each( basePrototype, function( key, val ) {
+//             if ( $.isPlainObject(val) ) {
+//                     basePrototype[ key ] = $.extend( {}, val );
+//             }
+//     });
+       basePrototype.options = $.extend( true, {}, basePrototype.options );
+       $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+               namespace: namespace,
+               widgetName: name,
+               widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+               widgetBaseClass: fullName
+       }, prototype );
+
+       $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+       $.fn[ name ] = function( options ) {
+               var isMethodCall = typeof options === "string",
+                       args = Array.prototype.slice.call( arguments, 1 ),
+                       returnValue = this;
+
+               // allow multiple hashes to be passed on init
+               options = !isMethodCall && args.length ?
+                       $.extend.apply( null, [ true, options ].concat(args) ) :
+                       options;
+
+               // prevent calls to internal methods
+               if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+                       return returnValue;
+               }
+
+               if ( isMethodCall ) {
+                       this.each(function() {
+                               var instance = $.data( this, name );
+                               if ( !instance ) {
+                                       throw "cannot call methods on " + name + " prior to initialization; " +
+                                               "attempted to call method '" + options + "'";
+                               }
+                               if ( !$.isFunction( instance[options] ) ) {
+                                       throw "no such method '" + options + "' for " + name + " widget instance";
+                               }
+                               var methodValue = instance[ options ].apply( instance, args );
+                               if ( methodValue !== instance && methodValue !== undefined ) {
+                                       returnValue = methodValue;
+                                       return false;
+                               }
+                       });
+               } else {
+                       this.each(function() {
+                               var instance = $.data( this, name );
+                               if ( instance ) {
+                                       instance.option( options || {} )._init();
+                               } else {
+                                       $.data( this, name, new object( options, this ) );
+                               }
+                       });
+               }
+
+               return returnValue;
+       };
+};
+
+$.Widget = function( options, element ) {
+       // allow instantiation without initializing for simple inheritance
+       if ( arguments.length ) {
+               this._createWidget( options, element );
+       }
+};
+
+$.Widget.prototype = {
+       widgetName: "widget",
+       widgetEventPrefix: "",
+       options: {
+               disabled: false
+       },
+       _createWidget: function( options, element ) {
+               // $.widget.bridge stores the plugin instance, but we do it anyway
+               // so that it's stored even before the _create function runs
+               $.data( element, this.widgetName, this );
+               this.element = $( element );
+               this.options = $.extend( true, {},
+                       this.options,
+                       this._getCreateOptions(),
+                       options );
+
+               var self = this;
+               this.element.bind( "remove." + this.widgetName, function() {
+                       self.destroy();
+               });
+
+               this._create();
+               this._trigger( "create" );
+               this._init();
+       },
+       _getCreateOptions: function() {
+               var options = {};
+               if ( $.metadata ) {
+                       options = $.metadata.get( element )[ this.widgetName ];
+               }
+               return options;
+       },
+       _create: function() {},
+       _init: function() {},
+
+       destroy: function() {
+               this.element
+                       .unbind( "." + this.widgetName )
+                       .removeData( this.widgetName );
+               this.widget()
+                       .unbind( "." + this.widgetName )
+                       .removeAttr( "aria-disabled" )
+                       .removeClass(
+                               this.widgetBaseClass + "-disabled " +
+                               "ui-state-disabled" );
+       },
+
+       widget: function() {
+               return this.element;
+       },
+
+       option: function( key, value ) {
+               var options = key;
+
+               if ( arguments.length === 0 ) {
+                       // don't return a reference to the internal hash
+                       return $.extend( {}, this.options );
+               }
+
+               if  (typeof key === "string" ) {
+                       if ( value === undefined ) {
+                               return this.options[ key ];
+                       }
+                       options = {};
+                       options[ key ] = value;
+               }
+
+               this._setOptions( options );
+
+               return this;
+       },
+       _setOptions: function( options ) {
+               var self = this;
+               $.each( options, function( key, value ) {
+                       self._setOption( key, value );
+               });
+
+               return this;
+       },
+       _setOption: function( key, value ) {
+               this.options[ key ] = value;
+
+               if ( key === "disabled" ) {
+                       this.widget()
+                               [ value ? "addClass" : "removeClass"](
+                                       this.widgetBaseClass + "-disabled" + " " +
+                                       "ui-state-disabled" )
+                               .attr( "aria-disabled", value );
+               }
+
+               return this;
+       },
+
+       enable: function() {
+               return this._setOption( "disabled", false );
+       },
+       disable: function() {
+               return this._setOption( "disabled", true );
+       },
+
+       _trigger: function( type, event, data ) {
+               var callback = this.options[ type ];
+
+               event = $.Event( event );
+               event.type = ( type === this.widgetEventPrefix ?
+                       type :
+                       this.widgetEventPrefix + type ).toLowerCase();
+               data = data || {};
+
+               // copy original event properties over to the new event
+               // this would happen if we could call $.event.fix instead of $.Event
+               // but we don't have a way to force an event to be fixed multiple times
+               if ( event.originalEvent ) {
+                       for ( var i = $.event.props.length, prop; i; ) {
+                               prop = $.event.props[ --i ];
+                               event[ prop ] = event.originalEvent[ prop ];
+                       }
+               }
+
+               this.element.trigger( event, data );
+
+               return !( $.isFunction(callback) &&
+                       callback.call( this.element[0], event, data ) === false ||
+                       event.isDefaultPrevented() );
+       }
+};
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.widget( "mobile.widget", {
+       // decorate the parent _createWidget to trigger `widgetinit` for users
+       // who wish to do post post `widgetcreate` alterations/additions
+       //
+       // TODO create a pull request for jquery ui to trigger this event
+       // in the original _createWidget
+       _createWidget: function() {
+               $.Widget.prototype._createWidget.apply( this, arguments );
+               this._trigger( 'init' );
+       },
+
+       _getCreateOptions: function() {
+
+               var elem = this.element,
+                       options = {};
+
+               $.each( this.options, function( option ) {
+
+                       var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) {
+                                                       return "-" + c.toLowerCase();
+                                               })
+                                       );
+
+                       if ( value !== undefined ) {
+                               options[ option ] = value;
+                       }
+               });
+
+               return options;
+       },
+
+       enhanceWithin: function( target, useKeepNative ) {
+               this.enhance( $( this.options.initSelector, $( target )), useKeepNative );
+       },
+
+       enhance: function( targets, useKeepNative ) {
+               var page, keepNative, $widgetElements = $( targets ), self = this;
+
+               // if ignoreContentEnabled is set to true the framework should
+               // only enhance the selected elements when they do NOT have a
+               // parent with the data-namespace-ignore attribute
+               $widgetElements = $.mobile.enhanceable( $widgetElements );
+
+               if ( useKeepNative && $widgetElements.length ) {
+                       // TODO remove dependency on the page widget for the keepNative.
+                       // Currently the keepNative value is defined on the page prototype so
+                       // the method is as well
+                       page = $.mobile.closestPageData( $widgetElements );
+                       keepNative = (page && page.keepNativeSelector()) || "";
+
+                       $widgetElements = $widgetElements.not( keepNative );
+               }
+
+               $widgetElements[ this.widgetName ]();
+       },
+
+       raise: function( msg ) {
+               throw "Widget [" + this.widgetName + "]: " + msg;
+       }
+});
+
+})( jQuery );
+
+(function( $, window, undefined ) {
+
+       var nsNormalizeDict = {};
+
+       // jQuery.mobile configurable options
+       $.mobile = $.extend( {}, {
+
+               // Version of the jQuery Mobile Framework
+               version: "1.1.1",
+
+               // Namespace used framework-wide for data-attrs. Default is no namespace
+               ns: "",
+
+               // Define the url parameter used for referencing widget-generated sub-pages.
+               // Translates to to example.html&ui-page=subpageIdentifier
+               // hash segment before &ui-page= is used to make Ajax request
+               subPageUrlKey: "ui-page",
+
+               // Class assigned to page currently in view, and during transitions
+               activePageClass: "ui-page-active",
+
+               // Class used for "active" button state, from CSS framework
+               activeBtnClass: "ui-btn-active",
+
+               // Class used for "focus" form element state, from CSS framework
+               focusClass: "ui-focus",
+
+               // Automatically handle clicks and form submissions through Ajax, when same-domain
+               ajaxEnabled: true,
+
+               // Automatically load and show pages based on location.hash
+               hashListeningEnabled: true,
+
+               // disable to prevent jquery from bothering with links
+               linkBindingEnabled: true,
+
+               // Set default page transition - 'none' for no transitions
+               defaultPageTransition: "fade",
+
+               // Set maximum window width for transitions to apply - 'false' for no limit
+               maxTransitionWidth: false,
+
+               // Minimum scroll distance that will be remembered when returning to a page
+               minScrollBack: 250,
+
+               // DEPRECATED: the following property is no longer in use, but defined until 2.0 to prevent conflicts
+               touchOverflowEnabled: false,
+
+               // Set default dialog transition - 'none' for no transitions
+               defaultDialogTransition: "pop",
+
+               // Show loading message during Ajax requests
+               // if false, message will not appear, but loading classes will still be toggled on html el
+               loadingMessage: "loading",
+
+               // Error response message - appears when an Ajax page request fails
+               pageLoadErrorMessage: "Error Loading Page",
+
+               // Should the text be visble in the loading message?
+               loadingMessageTextVisible: false,
+
+               // When the text is visible, what theme does the loading box use?
+               loadingMessageTheme: "a",
+
+               // For error messages, which theme does the box uses?
+               pageLoadErrorMessageTheme: "e",
+
+               //automatically initialize the DOM when it's ready
+               autoInitializePage: true,
+
+               pushStateEnabled: true,
+
+               // allows users to opt in to ignoring content by marking a parent element as
+               // data-ignored
+               ignoreContentEnabled: false,
+
+               // turn of binding to the native orientationchange due to android orientation behavior
+               orientationChangeEnabled: true,
+
+               buttonMarkup: {
+                       hoverDelay: 200
+               },
+
+               // TODO might be useful upstream in jquery itself ?
+               keyCode: {
+                       ALT: 18,
+                       BACKSPACE: 8,
+                       CAPS_LOCK: 20,
+                       COMMA: 188,
+                       COMMAND: 91,
+                       COMMAND_LEFT: 91, // COMMAND
+                       COMMAND_RIGHT: 93,
+                       CONTROL: 17,
+                       DELETE: 46,
+                       DOWN: 40,
+                       END: 35,
+                       ENTER: 13,
+                       ESCAPE: 27,
+                       HOME: 36,
+                       INSERT: 45,
+                       LEFT: 37,
+                       MENU: 93, // COMMAND_RIGHT
+                       NUMPAD_ADD: 107,
+                       NUMPAD_DECIMAL: 110,
+                       NUMPAD_DIVIDE: 111,
+                       NUMPAD_ENTER: 108,
+                       NUMPAD_MULTIPLY: 106,
+                       NUMPAD_SUBTRACT: 109,
+                       PAGE_DOWN: 34,
+                       PAGE_UP: 33,
+                       PERIOD: 190,
+                       RIGHT: 39,
+                       SHIFT: 16,
+                       SPACE: 32,
+                       TAB: 9,
+                       UP: 38,
+                       WINDOWS: 91 // COMMAND
+               },
+
+               // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
+               silentScroll: function( ypos ) {
+                       if ( $.type( ypos ) !== "number" ) {
+                               ypos = $.mobile.defaultHomeScroll;
+                       }
+
+                       // prevent scrollstart and scrollstop events
+                       $.event.special.scrollstart.enabled = false;
+
+                       setTimeout(function() {
+                               window.scrollTo( 0, ypos );
+                               $( document ).trigger( "silentscroll", { x: 0, y: ypos });
+                       }, 20 );
+
+                       setTimeout(function() {
+                               $.event.special.scrollstart.enabled = true;
+                       }, 150 );
+               },
+
+               // Expose our cache for testing purposes.
+               nsNormalizeDict: nsNormalizeDict,
+
+               // Take a data attribute property, prepend the namespace
+               // and then camel case the attribute string. Add the result
+               // to our nsNormalizeDict so we don't have to do this again.
+               nsNormalize: function( prop ) {
+                       if ( !prop ) {
+                               return;
+                       }
+
+                       return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) );
+               },
+
+               // Find the closest parent with a theme class on it. Note that
+               // we are not using $.fn.closest() on purpose here because this
+               // method gets called quite a bit and we need it to be as fast
+               // as possible.
+               getInheritedTheme: function( el, defaultTheme ) {
+                       var e = el[ 0 ],
+                               ltr = "",
+                               re = /ui-(bar|body|overlay)-([a-z])\b/,
+                               c, m;
+
+                       while ( e ) {
+                               c = e.className || "";
+                               if ( c && ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) {
+                                       // We found a parent with a theme class
+                                       // on it so bail from this loop.
+                                       break;
+                               }
+
+                               e = e.parentNode;
+                       }
+
+                       // Return the theme letter we found, if none, return the
+                       // specified default.
+
+                       return ltr || defaultTheme || "a";
+               },
+
+               // TODO the following $ and $.fn extensions can/probably should be moved into jquery.mobile.core.helpers
+               //
+               // Find the closest javascript page element to gather settings data jsperf test
+               // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit
+               // possibly naive, but it shows that the parsing overhead for *just* the page selector vs
+               // the page and dialog selector is negligable. This could probably be speed up by
+               // doing a similar parent node traversal to the one found in the inherited theme code above
+               closestPageData: function( $target ) {
+                       return $target
+                               .closest(':jqmData(role="page"), :jqmData(role="dialog")')
+                               .data("page");
+               },
+
+               enhanceable: function( $set ) {
+                       return this.haveParents( $set, "enhance" );
+               },
+
+               hijackable: function( $set ) {
+                       return this.haveParents( $set, "ajax" );
+               },
+
+               haveParents: function( $set, attr ) {
+                       if( !$.mobile.ignoreContentEnabled ){
+                               return $set;
+                       }
+
+                       var count = $set.length,
+                               $newSet = $(),
+                               e, $element, excluded;
+
+                       for ( var i = 0; i < count; i++ ) {
+                               $element = $set.eq( i );
+                               excluded = false;
+                               e = $set[ i ];
+
+                               while ( e ) {
+                                       var c = e.getAttribute ? e.getAttribute( "data-" + $.mobile.ns + attr ) : "";
+
+                                       if ( c === "false" ) {
+                                               excluded = true;
+                                               break;
+                                       }
+
+                                       e = e.parentNode;
+                               }
+
+                               if ( !excluded ) {
+                                       $newSet = $newSet.add( $element );
+                               }
+                       }
+
+                       return $newSet;
+               },
+
+               getScreenHeight: function(){
+                       // Native innerHeight returns more accurate value for this across platforms,
+                       // jQuery version is here as a normalized fallback for platforms like Symbian
+                       return window.innerHeight || $( window ).height();
+               }
+       }, $.mobile );
+
+       // Mobile version of data and removeData and hasData methods
+       // ensures all data is set and retrieved using jQuery Mobile's data namespace
+       $.fn.jqmData = function( prop, value ) {
+               var result;
+               if ( typeof prop != "undefined" ) {
+                       if ( prop ) {
+                               prop = $.mobile.nsNormalize( prop );
+                       }
+                       result = this.data.apply( this, arguments.length < 2 ? [ prop ] : [ prop, value ] );
+               }
+               return result;
+       };
+
+       $.jqmData = function( elem, prop, value ) {
+               var result;
+               if ( typeof prop != "undefined" ) {
+                       result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value );
+               }
+               return result;
+       };
+
+       $.fn.jqmRemoveData = function( prop ) {
+               return this.removeData( $.mobile.nsNormalize( prop ) );
+       };
+
+       $.jqmRemoveData = function( elem, prop ) {
+               return $.removeData( elem, $.mobile.nsNormalize( prop ) );
+       };
+
+       $.fn.removeWithDependents = function() {
+               $.removeWithDependents( this );
+       };
+
+       $.removeWithDependents = function( elem ) {
+               var $elem = $( elem );
+
+               ( $elem.jqmData('dependents') || $() ).remove();
+               $elem.remove();
+       };
+
+       $.fn.addDependents = function( newDependents ) {
+               $.addDependents( $(this), newDependents );
+       };
+
+       $.addDependents = function( elem, newDependents ) {
+               var dependents = $(elem).jqmData( 'dependents' ) || $();
+
+               $(elem).jqmData( 'dependents', $.merge(dependents, newDependents) );
+       };
+
+       // note that this helper doesn't attempt to handle the callback
+       // or setting of an html elements text, its only purpose is
+       // to return the html encoded version of the text in all cases. (thus the name)
+       $.fn.getEncodedText = function() {
+               return $( "<div/>" ).text( $(this).text() ).html();
+       };
+
+       // fluent helper function for the mobile namespaced equivalent
+       $.fn.jqmEnhanceable = function() {
+               return $.mobile.enhanceable( this );
+       };
+
+       $.fn.jqmHijackable = function() {
+               return $.mobile.hijackable( this );
+       };
+
+       // Monkey-patching Sizzle to filter the :jqmData selector
+       var oldFind = $.find,
+               jqmDataRE = /:jqmData\(([^)]*)\)/g;
+
+       $.find = function( selector, context, ret, extra ) {
+               selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" );
+
+               return oldFind.call( this, selector, context, ret, extra );
+       };
+
+       $.extend( $.find, oldFind );
+
+       $.find.matches = function( expr, set ) {
+               return $.find( expr, null, null, set );
+       };
+
+       $.find.matchesSelector = function( node, expr ) {
+               return $.find( expr, null, null, [ node ] ).length > 0;
+       };
+})( jQuery, this );
+
+
+(function( $, undefined ) {
+
+var $window = $( window ),
+       $html = $( "html" );
+
+/* $.mobile.media method: pass a CSS media type or query and get a bool return
+       note: this feature relies on actual media query support for media queries, though types will work most anywhere
+       examples:
+               $.mobile.media('screen') // tests for screen media type
+               $.mobile.media('screen and (min-width: 480px)') // tests for screen media type with window width > 480px
+               $.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') // tests for webkit 2x pixel ratio (iPhone 4)
+*/
+$.mobile.media = (function() {
+       // TODO: use window.matchMedia once at least one UA implements it
+       var cache = {},
+               testDiv = $( "<div id='jquery-mediatest'></div>" ),
+               fakeBody = $( "<body>" ).append( testDiv );
+
+       return function( query ) {
+               if ( !( query in cache ) ) {
+                       var styleBlock = document.createElement( "style" ),
+                               cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";
+
+                       //must set type for IE!
+                       styleBlock.type = "text/css";
+
+                       if ( styleBlock.styleSheet  ){
+                               styleBlock.styleSheet.cssText = cssrule;
+                       } else {
+                               styleBlock.appendChild( document.createTextNode(cssrule) );
+                       }
+
+                       $html.prepend( fakeBody ).prepend( styleBlock );
+                       cache[ query ] = testDiv.css( "position" ) === "absolute";
+                       fakeBody.add( styleBlock ).remove();
+               }
+               return cache[ query ];
+       };
+})();
+
+})(jQuery);
+
+(function( $, undefined ) {
+
+var fakeBody = $( "<body>" ).prependTo( "html" ),
+       fbCSS = fakeBody[ 0 ].style,
+       vendors = [ "Webkit", "Moz", "O" ],
+       webos = "palmGetResource" in window, //only used to rule out scrollTop
+       opera = window.opera,
+       operamini = window.operamini && ({}).toString.call( window.operamini ) === "[object OperaMini]",
+       bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
+
+// thx Modernizr
+function propExists( prop ) {
+       var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
+               props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
+
+       for ( var v in props ){
+               if ( fbCSS[ props[ v ] ] !== undefined ) {
+                       return true;
+               }
+       }
+}
+
+function validStyle( prop, value, check_vend ) {
+       var div = document.createElement('div'),
+               uc = function( txt ) {
+                       return txt.charAt( 0 ).toUpperCase() + txt.substr( 1 )
+               },
+               vend_pref = function( vend ) {
+                       return  "-" + vend.charAt( 0 ).toLowerCase() + vend.substr( 1 ) + "-";
+               },
+               check_style = function( vend ) {
+                       var vend_prop = vend_pref( vend ) + prop + ": " + value + ";",
+                               uc_vend = uc( vend ),
+                               propStyle = uc_vend + uc( prop );
+               
+                       div.setAttribute( "style", vend_prop );
+               
+                       if( !!div.style[ propStyle ] ) {
+                               ret = true;
+                       }
+               },
+               check_vends = check_vend ? [ check_vend ] : vendors,
+               ret;
+
+       for( i = 0; i < check_vends.length; i++ ) {
+               check_style( check_vends[i] );
+       }
+       return !!ret;
+}
+
+// Thanks to Modernizr src for this test idea. `perspective` check is limited to Moz to prevent a false positive for 3D transforms on Android.
+function transform3dTest() {
+       var prop = "transform-3d";
+       return validStyle( 'perspective', '10px', 'moz' ) || $.mobile.media( "(-" + vendors.join( "-" + prop + "),(-" ) + "-" + prop + "),(" + prop + ")" );
+}
+
+// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
+function baseTagTest() {
+       var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
+               base = $( "head base" ),
+               fauxEle = null,
+               href = "",
+               link, rebase;
+
+       if ( !base.length ) {
+               base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" );
+       } else {
+               href = base.attr( "href" );
+       }
+
+       link = $( "<a href='testurl' />" ).prependTo( fakeBody );
+       rebase = link[ 0 ].href;
+       base[ 0 ].href = href || location.pathname;
+
+       if ( fauxEle ) {
+               fauxEle.remove();
+       }
+       return rebase.indexOf( fauxBase ) === 0;
+}
+
+// Thanks Modernizr
+function cssPointerEventsTest() {
+       var element = document.createElement('x'),
+               documentElement = document.documentElement,
+               getComputedStyle = window.getComputedStyle,
+               supports;
+
+       if( !( 'pointerEvents' in element.style ) ){
+               return false;
+       }
+
+       element.style.pointerEvents = 'auto';
+       element.style.pointerEvents = 'x';
+    documentElement.appendChild(element);
+       supports = getComputedStyle &&
+    getComputedStyle( element, '' ).pointerEvents === 'auto';
+       documentElement.removeChild( element );
+    return !!supports;
+}
+
+
+// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
+// allows for inclusion of IE 6+, including Windows Mobile 7
+$.extend( $.mobile, { browser: {} } );
+$.mobile.browser.ie = (function() {
+       var v = 3,
+       div = document.createElement( "div" ),
+       a = div.all || [];
+
+       // added {} to silence closure compiler warnings. registering my dislike of all things
+       // overly clever here for future reference
+       while ( div.innerHTML = "<!--[if gt IE " + ( ++v ) + "]><br><![endif]-->", a[ 0 ] ){};
+
+       return v > 4 ? v : !v;
+})();
+
+
+$.extend( $.support, {
+       orientation: "orientation" in window && "onorientationchange" in window,
+       touch: "ontouchend" in document,
+       cssTransitions: "WebKitTransitionEvent" in window || validStyle( 'transition', 'height 100ms linear' ) && !opera,
+       pushState: "pushState" in history && "replaceState" in history,
+       mediaquery: $.mobile.media( "only all" ),
+       cssPseudoElement: !!propExists( "content" ),
+       touchOverflow: !!propExists( "overflowScrolling" ),
+       cssTransform3d: transform3dTest(),
+       boxShadow: !!propExists( "boxShadow" ) && !bb,
+       scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos && !operamini,
+       dynamicBaseTag: baseTagTest(),
+       cssPointerEvents: cssPointerEventsTest()
+});
+
+fakeBody.remove();
+
+
+// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
+// or that generally work better browsing in regular http for full page refreshes (Opera Mini)
+// Note: This detection below is used as a last resort.
+// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
+var nokiaLTE7_3 = (function(){
+
+       var ua = window.navigator.userAgent;
+
+       //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
+       return ua.indexOf( "Nokia" ) > -1 &&
+                       ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) &&
+                       ua.indexOf( "AppleWebKit" ) > -1 &&
+                       ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ );
+})();
+
+// Support conditions that must be met in order to proceed
+// default enhanced qualifications are media query support OR IE 7+
+$.mobile.gradeA = function(){
+       return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7;
+};
+
+$.mobile.ajaxBlacklist =
+                       // BlackBerry browsers, pre-webkit
+                       window.blackberry && !window.WebKitPoint ||
+                       // Opera Mini
+                       operamini ||
+                       // Symbian webkits pre 7.3
+                       nokiaLTE7_3;
+
+// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
+// to render the stylesheets when they're referenced before this script, as we'd recommend doing.
+// This simply reappends the CSS in place, which for some reason makes it apply
+if ( nokiaLTE7_3 ) {
+       $(function() {
+               $( "head link[rel='stylesheet']" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" );
+       });
+}
+
+// For ruling out shadows via css
+if ( !$.support.boxShadow ) {
+       $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" );
+}
+
+})( jQuery );
+
+(function( $, window, undefined ) {
+
+// add new event shortcuts
+$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
+                                       "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
+
+       $.fn[ name ] = function( fn ) {
+               return fn ? this.bind( name, fn ) : this.trigger( name );
+       };
+
+       $.attrFn[ name ] = true;
+});
+
+var supportTouch = $.support.touch,
+       scrollEvent = "touchmove scroll",
+       touchStartEvent = supportTouch ? "touchstart" : "mousedown",
+       touchStopEvent = supportTouch ? "touchend" : "mouseup",
+       touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
+
+function triggerCustomEvent( obj, eventType, event ) {
+       var originalType = event.type;
+       event.type = eventType;
+       $.event.handle.call( obj, event );
+       event.type = originalType;
+}
+
+// also handles scrollstop
+$.event.special.scrollstart = {
+
+       enabled: true,
+
+       setup: function() {
+
+               var thisObject = this,
+                       $this = $( thisObject ),
+                       scrolling,
+                       timer;
+
+               function trigger( event, state ) {
+                       scrolling = state;
+                       triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
+               }
+
+               // iPhone triggers scroll after a small delay; use touchmove instead
+               $this.bind( scrollEvent, function( event ) {
+
+                       if ( !$.event.special.scrollstart.enabled ) {
+                               return;
+                       }
+
+                       if ( !scrolling ) {
+                               trigger( event, true );
+                       }
+
+                       clearTimeout( timer );
+                       timer = setTimeout(function() {
+                               trigger( event, false );
+                       }, 50 );
+               });
+       }
+};
+
+// also handles taphold
+$.event.special.tap = {
+       setup: function() {
+               var thisObject = this,
+                       $this = $( thisObject );
+
+               $this.bind( "vmousedown", function( event ) {
+
+                       if ( event.which && event.which !== 1 ) {
+                               return false;
+                       }
+
+                       var origTarget = event.target,
+                               origEvent = event.originalEvent,
+                               timer;
+
+                       function clearTapTimer() {
+                               clearTimeout( timer );
+                       }
+
+                       function clearTapHandlers() {
+                               clearTapTimer();
+
+                               $this.unbind( "vclick", clickHandler )
+                                       .unbind( "vmouseup", clearTapTimer );
+                               $( document ).unbind( "vmousecancel", clearTapHandlers );
+                       }
+
+                       function clickHandler(event) {
+                               clearTapHandlers();
+
+                               // ONLY trigger a 'tap' event if the start target is
+                               // the same as the stop target.
+                               if ( origTarget == event.target ) {
+                                       triggerCustomEvent( thisObject, "tap", event );
+                               }
+                       }
+
+                       $this.bind( "vmouseup", clearTapTimer )
+                               .bind( "vclick", clickHandler );
+                       $( document ).bind( "vmousecancel", clearTapHandlers );
+
+                       timer = setTimeout(function() {
+                                       triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
+                       }, 750 );
+               });
+       }
+};
+
+// also handles swipeleft, swiperight
+$.event.special.swipe = {
+       scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
+
+       durationThreshold: 1000, // More time than this, and it isn't a swipe.
+
+       horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.
+
+       verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.
+
+       setup: function() {
+               var thisObject = this,
+                       $this = $( thisObject );
+
+               $this.bind( touchStartEvent, function( event ) {
+                       var data = event.originalEvent.touches ?
+                                                               event.originalEvent.touches[ 0 ] : event,
+                               start = {
+                                       time: ( new Date() ).getTime(),
+                                       coords: [ data.pageX, data.pageY ],
+                                       origin: $( event.target )
+                               },
+                               stop;
+
+                       function moveHandler( event ) {
+
+                               if ( !start ) {
+                                       return;
+                               }
+
+                               var data = event.originalEvent.touches ?
+                                               event.originalEvent.touches[ 0 ] : event;
+
+                               stop = {
+                                       time: ( new Date() ).getTime(),
+                                       coords: [ data.pageX, data.pageY ]
+                               };
+
+                               // prevent scrolling
+                               if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
+                                       event.preventDefault();
+                               }
+                       }
+
+                       $this.bind( touchMoveEvent, moveHandler )
+                               .one( touchStopEvent, function( event ) {
+                                       $this.unbind( touchMoveEvent, moveHandler );
+
+                                       if ( start && stop ) {
+                                               if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
+                                                               Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
+                                                               Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
+
+                                                       start.origin.trigger( "swipe" )
+                                                               .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
+                                               }
+                                       }
+                                       start = stop = undefined;
+                               });
+               });
+       }
+};
+
+(function( $, window ) {
+       // "Cowboy" Ben Alman
+
+       var win = $( window ),
+               special_event,
+               get_orientation,
+               last_orientation,
+               initial_orientation_is_landscape,
+               initial_orientation_is_default,
+               portrait_map = { "0": true, "180": true };
+
+       // It seems that some device/browser vendors use window.orientation values 0 and 180 to
+       // denote the "default" orientation. For iOS devices, and most other smart-phones tested,
+       // the default orientation is always "portrait", but in some Android and RIM based tablets,
+       // the default orientation is "landscape". The following code attempts to use the window
+       // dimensions to figure out what the current orientation is, and then makes adjustments
+       // to the to the portrait_map if necessary, so that we can properly decode the
+       // window.orientation value whenever get_orientation() is called.
+       //
+       // Note that we used to use a media query to figure out what the orientation the browser
+       // thinks it is in:
+       //
+       //     initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");
+       //
+       // but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1,
+       // where the browser *ALWAYS* applied the landscape media query. This bug does not
+       // happen on iPad.
+
+       if ( $.support.orientation ) {
+
+               // Check the window width and height to figure out what the current orientation
+               // of the device is at this moment. Note that we've initialized the portrait map
+               // values to 0 and 180, *AND* we purposely check for landscape so that if we guess
+               // wrong, , we default to the assumption that portrait is the default orientation.
+               // We use a threshold check below because on some platforms like iOS, the iPhone
+               // form-factor can report a larger width than height if the user turns on the
+               // developer console. The actual threshold value is somewhat arbitrary, we just
+               // need to make sure it is large enough to exclude the developer console case.
+
+               var ww = window.innerWidth || $( window ).width(),
+                       wh = window.innerHeight || $( window ).height(),
+                       landscape_threshold = 50;
+
+               initial_orientation_is_landscape = ww > wh && ( ww - wh ) > landscape_threshold;
+
+
+               // Now check to see if the current window.orientation is 0 or 180.
+               initial_orientation_is_default = portrait_map[ window.orientation ];
+
+               // If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
+               // if the initial orientation is portrait, but window.orientation reports 90 or -90, we
+               // need to flip our portrait_map values because landscape is the default orientation for
+               // this device/browser.
+               if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) {
+                       portrait_map = { "-90": true, "90": true };
+               }
+       }
+
+       $.event.special.orientationchange = special_event = {
+               setup: function() {
+                       // If the event is supported natively, return false so that jQuery
+                       // will bind to the event using DOM methods.
+                       if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
+                               return false;
+                       }
+
+                       // Get the current orientation to avoid initial double-triggering.
+                       last_orientation = get_orientation();
+
+                       // Because the orientationchange event doesn't exist, simulate the
+                       // event by testing window dimensions on resize.
+                       win.bind( "throttledresize", handler );
+               },
+               teardown: function(){
+                       // If the event is supported natively, return false so that
+                       // jQuery will unbind the event using DOM methods.
+                       if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
+                               return false;
+                       }
+
+                       // Because the orientationchange event doesn't exist, unbind the
+                       // resize event handler.
+                       win.unbind( "throttledresize", handler );
+               },
+               add: function( handleObj ) {
+                       // Save a reference to the bound event handler.
+                       var old_handler = handleObj.handler;
+
+
+                       handleObj.handler = function( event ) {
+                               // Modify event object, adding the .orientation property.
+                               event.orientation = get_orientation();
+
+                               // Call the originally-bound event handler and return its result.
+                               return old_handler.apply( this, arguments );
+                       };
+               }
+       };
+
+       // If the event is not supported natively, this handler will be bound to
+       // the window resize event to simulate the orientationchange event.
+       function handler() {
+               // Get the current orientation.
+               var orientation = get_orientation();
+
+               if ( orientation !== last_orientation ) {
+                       // The orientation has changed, so trigger the orientationchange event.
+                       last_orientation = orientation;
+                       win.trigger( "orientationchange" );
+               }
+       }
+
+       // Get the current page orientation. This method is exposed publicly, should it
+       // be needed, as jQuery.event.special.orientationchange.orientation()
+       $.event.special.orientationchange.orientation = get_orientation = function() {
+               var isPortrait = true, elem = document.documentElement;
+
+               // prefer window orientation to the calculation based on screensize as
+               // the actual screen resize takes place before or after the orientation change event
+               // has been fired depending on implementation (eg android 2.3 is before, iphone after).
+               // More testing is required to determine if a more reliable method of determining the new screensize
+               // is possible when orientationchange is fired. (eg, use media queries + element + opacity)
+               if ( $.support.orientation ) {
+                       // if the window orientation registers as 0 or 180 degrees report
+                       // portrait, otherwise landscape
+                       isPortrait = portrait_map[ window.orientation ];
+               } else {
+                       isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
+               }
+
+               return isPortrait ? "portrait" : "landscape";
+       };
+
+})( jQuery, window );
+
+
+// throttled resize event
+(function() {
+
+       $.event.special.throttledresize = {
+               setup: function() {
+                       $( this ).bind( "resize", handler );
+               },
+               teardown: function(){
+                       $( this ).unbind( "resize", handler );
+               }
+       };
+
+       var throttle = 250,
+               handler = function() {
+                       curr = ( new Date() ).getTime();
+                       diff = curr - lastCall;
+
+                       if ( diff >= throttle ) {
+
+                               lastCall = curr;
+                               $( this ).trigger( "throttledresize" );
+
+                       } else {
+
+                               if ( heldCall ) {
+                                       clearTimeout( heldCall );
+                               }
+
+                               // Promise a held call will still execute
+                               heldCall = setTimeout( handler, throttle - diff );
+                       }
+               },
+               lastCall = 0,
+               heldCall,
+               curr,
+               diff;
+})();
+
+
+$.each({
+       scrollstop: "scrollstart",
+       taphold: "tap",
+       swipeleft: "swipe",
+       swiperight: "swipe"
+}, function( event, sourceEvent ) {
+
+       $.event.special[ event ] = {
+               setup: function() {
+                       $( this ).bind( sourceEvent, $.noop );
+               }
+       };
+});
+
+})( jQuery, this );
+
+(function( $, undefined ) {
+
+$.widget( "mobile.page", $.mobile.widget, {
+       options: {
+               theme: "c",
+               domCache: false,
+               keepNativeDefault: ":jqmData(role='none'), :jqmData(role='nojs')"
+       },
+
+       _create: function() {
+               
+               var self = this;
+               
+               // if false is returned by the callbacks do not create the page
+               if( self._trigger( "beforecreate" ) === false ){
+                       return false;
+               }
+
+               self.element
+                       .attr( "tabindex", "0" )
+                       .addClass( "ui-page ui-body-" + self.options.theme )
+                       .bind( "pagebeforehide", function(){
+                               self.removeContainerBackground();
+                       } )
+                       .bind( "pagebeforeshow", function(){
+                               self.setContainerBackground();
+                       } );
+
+       },
+       
+       removeContainerBackground: function(){
+               $.mobile.pageContainer.removeClass( "ui-overlay-" + $.mobile.getInheritedTheme( this.element.parent() ) );
+       },
+       
+       // set the page container background to the page theme
+       setContainerBackground: function( theme ){
+               if( this.options.theme ){
+                       $.mobile.pageContainer.addClass( "ui-overlay-" + ( theme || this.options.theme ) );
+               }
+       },
+
+       keepNativeSelector: function() {
+               var options = this.options,
+                       keepNativeDefined = options.keepNative && $.trim(options.keepNative);
+
+               if( keepNativeDefined && options.keepNative !== options.keepNativeDefault ){
+                       return [options.keepNative, options.keepNativeDefault].join(", ");
+               }
+
+               return options.keepNativeDefault;
+       }
+});
+})( jQuery );
+
+
+(function( $, window, undefined ) {
+
+var createHandler = function( sequential ){
+       
+       // Default to sequential
+       if( sequential === undefined ){
+               sequential = true;
+       }
+       
+       return function( name, reverse, $to, $from ) {
+
+               var deferred = new $.Deferred(),
+                       reverseClass = reverse ? " reverse" : "",
+                       active  = $.mobile.urlHistory.getActive(),
+                       toScroll = active.lastScroll || $.mobile.defaultHomeScroll,
+                       screenHeight = $.mobile.getScreenHeight(),
+                       maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $( window ).width() > $.mobile.maxTransitionWidth,
+                       none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none" || Math.max( $( window ).scrollTop(), toScroll ) > $.mobile.getMaxScrollForTransition(),
+                       toPreClass = " ui-page-pre-in",
+                       toggleViewportClass = function(){
+                               $.mobile.pageContainer.toggleClass( "ui-mobile-viewport-transitioning viewport-" + name );
+                       },
+                       scrollPage = function(){
+                               // By using scrollTo instead of silentScroll, we can keep things better in order
+                               // Just to be precautios, disable scrollstart listening like silentScroll would
+                               $.event.special.scrollstart.enabled = false;
+                               
+                               window.scrollTo( 0, toScroll );
+                               
+                               // reenable scrollstart listening like silentScroll would
+                               setTimeout(function() {
+                                       $.event.special.scrollstart.enabled = true;
+                               }, 150 );
+                       },
+                       cleanFrom = function(){
+                               $from
+                                       .removeClass( $.mobile.activePageClass + " out in reverse " + name )
+                                       .height( "" );
+                       },
+                       startOut = function(){
+                               // if it's not sequential, call the doneOut transition to start the TO page animating in simultaneously
+                               if( !sequential ){
+                                       doneOut();
+                               }
+                               else {
+                                       $from.animationComplete( doneOut );     
+                               }
+                               
+                               // Set the from page's height and start it transitioning out
+                               // Note: setting an explicit height helps eliminate tiling in the transitions
+                               $from
+                                       .height( screenHeight + $(window ).scrollTop() )
+                                       .addClass( name + " out" + reverseClass );
+                       },
+                       
+                       doneOut = function() {
+
+                               if ( $from && sequential ) {
+                                       cleanFrom();
+                               }
+                               
+                               startIn();
+                       },
+                       
+                       startIn = function(){   
+                       
+                               $to.addClass( $.mobile.activePageClass );                               
+                       
+                               // Send focus to page as it is now display: block
+                               $.mobile.focusPage( $to );
+
+                               // Set to page height
+                               $to.height( screenHeight + toScroll );
+                               
+                               scrollPage();
+                               
+                               if( !none ){
+                                       $to.animationComplete( doneIn );
+                               }
+                               
+                               $to.addClass( name + " in" + reverseClass );
+                               
+                               if( none ){
+                                       doneIn();
+                               }
+                               
+                       },
+               
+                       doneIn = function() {
+                       
+                               if ( !sequential ) {
+                                       
+                                       if( $from ){
+                                               cleanFrom();
+                                       }
+                               }
+                       
+                               $to
+                                       .removeClass( "out in reverse " + name )
+                                       .height( "" );
+                               
+                               toggleViewportClass();
+                               
+                               // In some browsers (iOS5), 3D transitions block the ability to scroll to the desired location during transition
+                               // This ensures we jump to that spot after the fact, if we aren't there already.
+                               if( $( window ).scrollTop() !== toScroll ){
+                                       scrollPage();
+                               }
+
+                               deferred.resolve( name, reverse, $to, $from, true );
+                       };
+
+               toggleViewportClass();
+       
+               if ( $from && !none ) {
+                       startOut();
+               }
+               else {
+                       doneOut();
+               }
+
+               return deferred.promise();
+       };
+}
+
+// generate the handlers from the above
+var sequentialHandler = createHandler(),
+       simultaneousHandler = createHandler( false ),
+       defaultGetMaxScrollForTransition = function() {
+               return $.mobile.getScreenHeight() * 3;
+       };
+
+// Make our transition handler the public default.
+$.mobile.defaultTransitionHandler = sequentialHandler;
+
+//transition handler dictionary for 3rd party transitions
+$.mobile.transitionHandlers = {
+       "default": $.mobile.defaultTransitionHandler,
+       "sequential": sequentialHandler,
+       "simultaneous": simultaneousHandler
+};
+
+$.mobile.transitionFallbacks = {};
+
+// Set the getMaxScrollForTransition to default if no implementation was set by user
+$.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defaultGetMaxScrollForTransition;
+})( jQuery, this );
+
+( function( $, undefined ) {
+
+       //define vars for interal use
+       var $window = $( window ),
+               $html = $( 'html' ),
+               $head = $( 'head' ),
+
+               //url path helpers for use in relative url management
+               path = {
+
+                       // This scary looking regular expression parses an absolute URL or its relative
+                       // variants (protocol, site, document, query, and hash), into the various
+                       // components (protocol, host, path, query, fragment, etc that make up the
+                       // URL as well as some other commonly used sub-parts. When used with RegExp.exec()
+                       // or String.match, it parses the URL into a results array that looks like this:
+                       //
+                       //     [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
+                       //     [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
+                       //     [2]: http://jblas:password@mycompany.com:8080/mail/inbox
+                       //     [3]: http://jblas:password@mycompany.com:8080
+                       //     [4]: http:
+                       //     [5]: //
+                       //     [6]: jblas:password@mycompany.com:8080
+                       //     [7]: jblas:password
+                       //     [8]: jblas
+                       //     [9]: password
+                       //    [10]: mycompany.com:8080
+                       //    [11]: mycompany.com
+                       //    [12]: 8080
+                       //    [13]: /mail/inbox
+                       //    [14]: /mail/
+                       //    [15]: inbox
+                       //    [16]: ?msg=1234&type=unread
+                       //    [17]: #msg-content
+                       //
+                       urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
+
+                       //Parse a URL into a structure that allows easy access to
+                       //all of the URL components by name.
+                       parseUrl: function( url ) {
+                               // If we're passed an object, we'll assume that it is
+                               // a parsed url object and just return it back to the caller.
+                               if ( $.type( url ) === "object" ) {
+                                       return url;
+                               }
+
+                               var matches = path.urlParseRE.exec( url || "" ) || [];
+
+                                       // Create an object that allows the caller to access the sub-matches
+                                       // by name. Note that IE returns an empty string instead of undefined,
+                                       // like all other browsers do, so we normalize everything so its consistent
+                                       // no matter what browser we're running on.
+                                       return {
+                                               href:         matches[  0 ] || "",
+                                               hrefNoHash:   matches[  1 ] || "",
+                                               hrefNoSearch: matches[  2 ] || "",
+                                               domain:       matches[  3 ] || "",
+                                               protocol:     matches[  4 ] || "",
+                                               doubleSlash:  matches[  5 ] || "",
+                                               authority:    matches[  6 ] || "",
+                                               username:     matches[  8 ] || "",
+                                               password:     matches[  9 ] || "",
+                                               host:         matches[ 10 ] || "",
+                                               hostname:     matches[ 11 ] || "",
+                                               port:         matches[ 12 ] || "",
+                                               pathname:     matches[ 13 ] || "",
+                                               directory:    matches[ 14 ] || "",
+                                               filename:     matches[ 15 ] || "",
+                                               search:       matches[ 16 ] || "",
+                                               hash:         matches[ 17 ] || ""
+                                       };
+                       },
+
+                       //Turn relPath into an asbolute path. absPath is
+                       //an optional absolute path which describes what
+                       //relPath is relative to.
+                       makePathAbsolute: function( relPath, absPath ) {
+                               if ( relPath && relPath.charAt( 0 ) === "/" ) {
+                                       return relPath;
+                               }
+
+                               relPath = relPath || "";
+                               absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
+
+                               var absStack = absPath ? absPath.split( "/" ) : [],
+                                       relStack = relPath.split( "/" );
+                               for ( var i = 0; i < relStack.length; i++ ) {
+                                       var d = relStack[ i ];
+                                       switch ( d ) {
+                                               case ".":
+                                                       break;
+                                               case "..":
+                                                       if ( absStack.length ) {
+                                                               absStack.pop();
+                                                       }
+                                                       break;
+                                               default:
+                                                       absStack.push( d );
+                                                       break;
+                                       }
+                               }
+                               return "/" + absStack.join( "/" );
+                       },
+
+                       //Returns true if both urls have the same domain.
+                       isSameDomain: function( absUrl1, absUrl2 ) {
+                               return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
+                       },
+
+                       //Returns true for any relative variant.
+                       isRelativeUrl: function( url ) {
+                               // All relative Url variants have one thing in common, no protocol.
+                               return path.parseUrl( url ).protocol === "";
+                       },
+
+                       //Returns true for an absolute url.
+                       isAbsoluteUrl: function( url ) {
+                               return path.parseUrl( url ).protocol !== "";
+                       },
+
+                       //Turn the specified realtive URL into an absolute one. This function
+                       //can handle all relative variants (protocol, site, document, query, fragment).
+                       makeUrlAbsolute: function( relUrl, absUrl ) {
+                               if ( !path.isRelativeUrl( relUrl ) ) {
+                                       return relUrl;
+                               }
+
+                               var relObj = path.parseUrl( relUrl ),
+                                       absObj = path.parseUrl( absUrl ),
+                                       protocol = relObj.protocol || absObj.protocol,
+                                       doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ),
+                                       authority = relObj.authority || absObj.authority,
+                                       hasPath = relObj.pathname !== "",
+                                       pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
+                                       search = relObj.search || ( !hasPath && absObj.search ) || "",
+                                       hash = relObj.hash;
+
+                               return protocol + doubleSlash + authority + pathname + search + hash;
+                       },
+
+                       //Add search (aka query) params to the specified url.
+                       addSearchParams: function( url, params ) {
+                               var u = path.parseUrl( url ),
+                                       p = ( typeof params === "object" ) ? $.param( params ) : params,
+                                       s = u.search || "?";
+                               return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" );
+                       },
+
+                       convertUrlToDataUrl: function( absUrl ) {
+                               var u = path.parseUrl( absUrl );
+                               if ( path.isEmbeddedPage( u ) ) {
+                                   // For embedded pages, remove the dialog hash key as in getFilePath(),
+                                   // otherwise the Data Url won't match the id of the embedded Page.
+                                       return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
+                               } else if ( path.isSameDomain( u, documentBase ) ) {
+                                       return u.hrefNoHash.replace( documentBase.domain, "" ).split( dialogHashKey )[0];
+                               }
+                               return absUrl;
+                       },
+
+                       //get path from current hash, or from a file path
+                       get: function( newPath ) {
+                               if( newPath === undefined ) {
+                                       newPath = location.hash;
+                               }
+                               return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
+                       },
+
+                       //return the substring of a filepath before the sub-page key, for making a server request
+                       getFilePath: function( path ) {
+                               var splitkey = '&' + $.mobile.subPageUrlKey;
+                               return path && path.split( splitkey )[0].split( dialogHashKey )[0];
+                       },
+
+                       //set location hash to path
+                       set: function( path ) {
+                               location.hash = path;
+                       },
+
+                       //test if a given url (string) is a path
+                       //NOTE might be exceptionally naive
+                       isPath: function( url ) {
+                               return ( /\// ).test( url );
+                       },
+
+                       //return a url path with the window's location protocol/hostname/pathname removed
+                       clean: function( url ) {
+                               return url.replace( documentBase.domain, "" );
+                       },
+
+                       //just return the url without an initial #
+                       stripHash: function( url ) {
+                               return url.replace( /^#/, "" );
+                       },
+
+                       //remove the preceding hash, any query params, and dialog notations
+                       cleanHash: function( hash ) {
+                               return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
+                       },
+
+                       isHashValid: function( hash ) {
+                               return /^#[^#]+$/.test(hash);
+                       },
+
+                       //check whether a url is referencing the same domain, or an external domain or different protocol
+                       //could be mailto, etc
+                       isExternal: function( url ) {
+                               var u = path.parseUrl( url );
+                               return u.protocol && u.domain !== documentUrl.domain ? true : false;
+                       },
+
+                       hasProtocol: function( url ) {
+                               return ( /^(:?\w+:)/ ).test( url );
+                       },
+
+                       //check if the specified url refers to the first page in the main application document.
+                       isFirstPageUrl: function( url ) {
+                               // We only deal with absolute paths.
+                               var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ),
+
+                                       // Does the url have the same path as the document?
+                                       samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
+
+                                       // Get the first page element.
+                                       fp = $.mobile.firstPage,
+
+                                       // Get the id of the first page element if it has one.
+                                       fpId = fp && fp[0] ? fp[0].id : undefined;
+
+                                       // The url refers to the first page if the path matches the document and
+                                       // it either has no hash value, or the hash is exactly equal to the id of the
+                                       // first page element.
+                                       return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
+                       },
+
+                       isEmbeddedPage: function( url ) {
+                               var u = path.parseUrl( url );
+
+                               //if the path is absolute, then we need to compare the url against
+                               //both the documentUrl and the documentBase. The main reason for this
+                               //is that links embedded within external documents will refer to the
+                               //application document, whereas links embedded within the application
+                               //document will be resolved against the document base.
+                               if ( u.protocol !== "" ) {
+                                       return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) );
+                               }
+                               return (/^#/).test( u.href );
+                       },
+
+
+                       // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
+                       // requests if the document doing the request was loaded via the file:// protocol.
+                       // This is usually to allow the application to "phone home" and fetch app specific
+                       // data. We normally let the browser handle external/cross-domain urls, but if the
+                       // allowCrossDomainPages option is true, we will allow cross-domain http/https
+                       // requests to go through our page loading logic.
+                       isPermittedCrossDomainRequest: function( docUrl, reqUrl ) {
+                               return $.mobile.allowCrossDomainPages
+                                       && docUrl.protocol === "file:"
+                                       && reqUrl.search( /^https?:/ ) != -1;
+                       }
+               },
+
+               //will be defined when a link is clicked and given an active class
+               $activeClickedLink = null,
+
+               //urlHistory is purely here to make guesses at whether the back or forward button was clicked
+               //and provide an appropriate transition
+               urlHistory = {
+                       // Array of pages that are visited during a single page load.
+                       // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs)
+                       stack: [],
+
+                       //maintain an index number for the active page in the stack
+                       activeIndex: 0,
+
+                       //get active
+                       getActive: function() {
+                               return urlHistory.stack[ urlHistory.activeIndex ];
+                       },
+
+                       getPrev: function() {
+                               return urlHistory.stack[ urlHistory.activeIndex - 1 ];
+                       },
+
+                       getNext: function() {
+                               return urlHistory.stack[ urlHistory.activeIndex + 1 ];
+                       },
+
+                       // addNew is used whenever a new page is added
+                       addNew: function( url, transition, title, pageUrl, role ) {
+                               //if there's forward history, wipe it
+                               if( urlHistory.getNext() ) {
+                                       urlHistory.clearForward();
+                               }
+
+                               urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
+
+                               urlHistory.activeIndex = urlHistory.stack.length - 1;
+                       },
+
+                       //wipe urls ahead of active index
+                       clearForward: function() {
+                               urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
+                       },
+
+                       directHashChange: function( opts ) {
+                               var back , forward, newActiveIndex, prev = this.getActive();
+
+                               // check if url is in history and if it's ahead or behind current page
+                               $.each( urlHistory.stack, function( i, historyEntry ) {
+
+                                       //if the url is in the stack, it's a forward or a back
+                                       if( opts.currentUrl === historyEntry.url ) {
+                                               //define back and forward by whether url is older or newer than current page
+                                               back = i < urlHistory.activeIndex;
+                                               forward = !back;
+                                               newActiveIndex = i;
+                                       }
+                               });
+
+                               // save new page index, null check to prevent falsey 0 result
+                               this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;
+
+                               if( back ) {
+                                       ( opts.either || opts.isBack )( true );
+                               } else if( forward ) {
+                                       ( opts.either || opts.isForward )( false );
+                               }
+                       },
+
+                       //disable hashchange event listener internally to ignore one change
+                       //toggled internally when location.hash is updated to match the url of a successful page load
+                       ignoreNextHashChange: false
+               },
+
+               //define first selector to receive focus when a page is shown
+               focusable = "[tabindex],a,button:visible,select:visible,input",
+
+               //queue to hold simultanious page transitions
+               pageTransitionQueue = [],
+
+               //indicates whether or not page is in process of transitioning
+               isPageTransitioning = false,
+
+               //nonsense hash change key for dialogs, so they create a history entry
+               dialogHashKey = "&ui-state=dialog",
+
+               //existing base tag?
+               $base = $head.children( "base" ),
+
+               //tuck away the original document URL minus any fragment.
+               documentUrl = path.parseUrl( location.href ),
+
+               //if the document has an embedded base tag, documentBase is set to its
+               //initial value. If a base tag does not exist, then we default to the documentUrl.
+               documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl,
+
+               //cache the comparison once.
+               documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash ),
+
+               getScreenHeight = $.mobile.getScreenHeight;
+
+               //base element management, defined depending on dynamic base tag support
+               var base = $.support.dynamicBaseTag ? {
+
+                       //define base element, for use in routing asset urls that are referenced in Ajax-requested markup
+                       element: ( $base.length ? $base : $( "<base>", { href: documentBase.hrefNoHash } ).prependTo( $head ) ),
+
+                       //set the generated BASE element's href attribute to a new page's base path
+                       set: function( href ) {
+                               base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) );
+                       },
+
+                       //set the generated BASE element's href attribute to a new page's base path
+                       reset: function() {
+                               base.element.attr( "href", documentBase.hrefNoHash );
+                       }
+
+               } : undefined;
+
+/*
+       internal utility functions
+--------------------------------------*/
+
+
+       //direct focus to the page title, or otherwise first focusable element
+       $.mobile.focusPage = function ( page ) {
+               var autofocus = page.find("[autofocus]"),
+                       pageTitle = page.find( ".ui-title:eq(0)" );
+
+               if( autofocus.length ) {
+                       autofocus.focus();
+                       return;
+               }
+
+               if( pageTitle.length ) {
+                       pageTitle.focus();
+               }
+               else{
+                       page.focus();
+               }
+       }
+
+       //remove active classes after page transition or error
+       function removeActiveLinkClass( forceRemoval ) {
+               if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) {
+                       $activeClickedLink.removeClass( $.mobile.activeBtnClass );
+               }
+               $activeClickedLink = null;
+       }
+
+       function releasePageTransitionLock() {
+               isPageTransitioning = false;
+               if( pageTransitionQueue.length > 0 ) {
+                       $.mobile.changePage.apply( null, pageTransitionQueue.pop() );
+               }
+       }
+
+       // Save the last scroll distance per page, before it is hidden
+       var setLastScrollEnabled = true,
+               setLastScroll, delayedSetLastScroll;
+
+       setLastScroll = function() {
+               // this barrier prevents setting the scroll value based on the browser
+               // scrolling the window based on a hashchange
+               if( !setLastScrollEnabled ) {
+                       return;
+               }
+
+               var active = $.mobile.urlHistory.getActive();
+
+               if( active ) {
+                       var lastScroll = $window.scrollTop();
+
+                       // Set active page's lastScroll prop.
+                       // If the location we're scrolling to is less than minScrollBack, let it go.
+                       active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
+               }
+       };
+
+       // bind to scrollstop to gather scroll position. The delay allows for the hashchange
+       // event to fire and disable scroll recording in the case where the browser scrolls
+       // to the hash targets location (sometimes the top of the page). once pagechange fires
+       // getLastScroll is again permitted to operate
+       delayedSetLastScroll = function() {
+               setTimeout( setLastScroll, 100 );
+       };
+
+       // disable an scroll setting when a hashchange has been fired, this only works
+       // because the recording of the scroll position is delayed for 100ms after
+       // the browser might have changed the position because of the hashchange
+       $window.bind( $.support.pushState ? "popstate" : "hashchange", function() {
+               setLastScrollEnabled = false;
+       });
+
+       // handle initial hashchange from chrome :(
+       $window.one( $.support.pushState ? "popstate" : "hashchange", function() {
+               setLastScrollEnabled = true;
+       });
+
+       // wait until the mobile page container has been determined to bind to pagechange
+       $window.one( "pagecontainercreate", function(){
+               // once the page has changed, re-enable the scroll recording
+               $.mobile.pageContainer.bind( "pagechange", function() {
+
+                       setLastScrollEnabled = true;
+
+                       // remove any binding that previously existed on the get scroll
+                       // which may or may not be different than the scroll element determined for
+                       // this page previously
+                       $window.unbind( "scrollstop", delayedSetLastScroll );
+
+                       // determine and bind to the current scoll element which may be the window
+                       // or in the case of touch overflow the element with touch overflow
+                       $window.bind( "scrollstop", delayedSetLastScroll );
+               });
+       });
+
+       // bind to scrollstop for the first page as "pagechange" won't be fired in that case
+       $window.bind( "scrollstop", delayedSetLastScroll );
+
+       //function for transitioning between two existing pages
+       function transitionPages( toPage, fromPage, transition, reverse ) {
+
+               if( fromPage ) {
+                       //trigger before show/hide events
+                       fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
+               }
+
+               toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
+
+               //clear page loader
+               $.mobile.hidePageLoadingMsg();
+
+               // If transition is defined, check if css 3D transforms are supported, and if not, if a fallback is specified
+               if( transition && !$.support.cssTransform3d && $.mobile.transitionFallbacks[ transition ] ){
+                       transition = $.mobile.transitionFallbacks[ transition ];
+               }
+
+               //find the transition handler for the specified transition. If there
+               //isn't one in our transitionHandlers dictionary, use the default one.
+               //call the handler immediately to kick-off the transition.
+               var th = $.mobile.transitionHandlers[ transition || "default" ] || $.mobile.defaultTransitionHandler,
+                       promise = th( transition, reverse, toPage, fromPage );
+
+               promise.done(function() {
+
+                       //trigger show/hide events
+                       if( fromPage ) {
+                               fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
+                       }
+
+                       //trigger pageshow, define prevPage as either fromPage or empty jQuery obj
+                       toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } );
+               });
+
+               return promise;
+       }
+
+       //simply set the active page's minimum height to screen height, depending on orientation
+       function resetActivePageHeight(){
+               var aPage = $( "." + $.mobile.activePageClass ),
+                       aPagePadT = parseFloat( aPage.css( "padding-top" ) ),
+                       aPagePadB = parseFloat( aPage.css( "padding-bottom" ) ),
+                       aPageBorderT = parseFloat( aPage.css( "border-top-width" ) ),
+                       aPageBorderB = parseFloat( aPage.css( "border-bottom-width" ) );
+
+               aPage.css( "min-height", getScreenHeight() - aPagePadT - aPagePadB - aPageBorderT - aPageBorderB );
+       }
+
+       //shared page enhancements
+       function enhancePage( $page, role ) {
+               // If a role was specified, make sure the data-role attribute
+               // on the page element is in sync.
+               if( role ) {
+                       $page.attr( "data-" + $.mobile.ns + "role", role );
+               }
+
+               //run page plugin
+               $page.page();
+       }
+
+/* exposed $.mobile methods     */
+
+       //animation complete callback
+       $.fn.animationComplete = function( callback ) {
+               if( $.support.cssTransitions ) {
+                       return $( this ).one( 'webkitAnimationEnd animationend', callback );
+               }
+               else{
+                       // defer execution for consistency between webkit/non webkit
+                       setTimeout( callback, 0 );
+                       return $( this );
+               }
+       };
+
+       //expose path object on $.mobile
+       $.mobile.path = path;
+
+       //expose base object on $.mobile
+       $.mobile.base = base;
+
+       //history stack
+       $.mobile.urlHistory = urlHistory;
+
+       $.mobile.dialogHashKey = dialogHashKey;
+
+
+
+       //enable cross-domain page support
+       $.mobile.allowCrossDomainPages = false;
+
+       //return the original document url
+       $.mobile.getDocumentUrl = function(asParsedObject) {
+               return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href;
+       };
+
+       //return the original document base url
+       $.mobile.getDocumentBase = function(asParsedObject) {
+               return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href;
+       };
+
+       $.mobile._bindPageRemove = function() {
+               var page = $(this);
+
+               // when dom caching is not enabled or the page is embedded bind to remove the page on hide
+               if( !page.data("page").options.domCache
+                               && page.is(":jqmData(external-page='true')") ) {
+
+                       page.bind( 'pagehide.remove', function() {
+                               var $this = $( this ),
+                                       prEvent = new $.Event( "pageremove" );
+
+                               $this.trigger( prEvent );
+
+                               if( !prEvent.isDefaultPrevented() ){
+                                       $this.removeWithDependents();
+                               }
+                       });
+               }
+       };
+
+       // Load a page into the DOM.
+       $.mobile.loadPage = function( url, options ) {
+               // This function uses deferred notifications to let callers
+               // know when the page is done loading, or if an error has occurred.
+               var deferred = $.Deferred(),
+
+                       // The default loadPage options with overrides specified by
+                       // the caller.
+                       settings = $.extend( {}, $.mobile.loadPage.defaults, options ),
+
+                       // The DOM element for the page after it has been loaded.
+                       page = null,
+
+                       // If the reloadPage option is true, and the page is already
+                       // in the DOM, dupCachedPage will be set to the page element
+                       // so that it can be removed after the new version of the
+                       // page is loaded off the network.
+                       dupCachedPage = null,
+
+                       // determine the current base url
+                       findBaseWithDefault = function(){
+                               var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) );
+                               return closestBase || documentBase.hrefNoHash;
+                       },
+
+                       // The absolute version of the URL passed into the function. This
+                       // version of the URL may contain dialog/subpage params in it.
+                       absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() );
+
+
+               // If the caller provided data, and we're using "get" request,
+               // append the data to the URL.
+               if ( settings.data && settings.type === "get" ) {
+                       absUrl = path.addSearchParams( absUrl, settings.data );
+                       settings.data = undefined;
+               }
+
+               // If the caller is using a "post" request, reloadPage must be true
+               if(  settings.data && settings.type === "post" ){
+                       settings.reloadPage = true;
+               }
+
+                       // The absolute version of the URL minus any dialog/subpage params.
+                       // In otherwords the real URL of the page to be loaded.
+               var fileUrl = path.getFilePath( absUrl ),
+
+                       // The version of the Url actually stored in the data-url attribute of
+                       // the page. For embedded pages, it is just the id of the page. For pages
+                       // within the same domain as the document base, it is the site relative
+                       // path. For cross-domain pages (Phone Gap only) the entire absolute Url
+                       // used to load the page.
+                       dataUrl = path.convertUrlToDataUrl( absUrl );
+
+               // Make sure we have a pageContainer to work with.
+               settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
+
+               // Check to see if the page already exists in the DOM.
+               page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
+
+               // If we failed to find the page, check to see if the url is a
+               // reference to an embedded page. If so, it may have been dynamically
+               // injected by a developer, in which case it would be lacking a data-url
+               // attribute and in need of enhancement.
+               if ( page.length === 0 && dataUrl && !path.isPath( dataUrl ) ) {
+                       page = settings.pageContainer.children( "#" + dataUrl )
+                               .attr( "data-" + $.mobile.ns + "url", dataUrl );
+               }
+
+               // If we failed to find a page in the DOM, check the URL to see if it
+               // refers to the first page in the application. If it isn't a reference
+               // to the first page and refers to non-existent embedded page, error out.
+               if ( page.length === 0 ) {
+                       if ( $.mobile.firstPage && path.isFirstPageUrl( fileUrl ) ) {
+                               // Check to make sure our cached-first-page is actually
+                               // in the DOM. Some user deployed apps are pruning the first
+                               // page from the DOM for various reasons, we check for this
+                               // case here because we don't want a first-page with an id
+                               // falling through to the non-existent embedded page error
+                               // case. If the first-page is not in the DOM, then we let
+                               // things fall through to the ajax loading code below so
+                               // that it gets reloaded.
+                               if ( $.mobile.firstPage.parent().length ) {
+                                       page = $( $.mobile.firstPage );
+                               }
+                       } else if ( path.isEmbeddedPage( fileUrl )  ) {
+                               deferred.reject( absUrl, options );
+                               return deferred.promise();
+                       }
+               }
+
+               // Reset base to the default document base.
+               if ( base ) {
+                       base.reset();
+               }
+
+               // If the page we are interested in is already in the DOM,
+               // and the caller did not indicate that we should force a
+               // reload of the file, we are done. Otherwise, track the
+               // existing page as a duplicated.
+               if ( page.length ) {
+                       if ( !settings.reloadPage ) {
+                               enhancePage( page, settings.role );
+                               deferred.resolve( absUrl, options, page );
+                               return deferred.promise();
+                       }
+                       dupCachedPage = page;
+               }
+
+               var mpc = settings.pageContainer,
+                       pblEvent = new $.Event( "pagebeforeload" ),
+                       triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings };
+
+               // Let listeners know we're about to load a page.
+               mpc.trigger( pblEvent, triggerData );
+
+               // If the default behavior is prevented, stop here!
+               if( pblEvent.isDefaultPrevented() ){
+                       return deferred.promise();
+               }
+
+               if ( settings.showLoadMsg ) {
+
+                       // This configurable timeout allows cached pages a brief delay to load without showing a message
+                       var loadMsgDelay = setTimeout(function(){
+                                       $.mobile.showPageLoadingMsg();
+                               }, settings.loadMsgDelay ),
+
+                               // Shared logic for clearing timeout and removing message.
+                               hideMsg = function(){
+
+                                       // Stop message show timer
+                                       clearTimeout( loadMsgDelay );
+
+                                       // Hide loading message
+                                       $.mobile.hidePageLoadingMsg();
+                               };
+               }
+
+               if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) {
+                       deferred.reject( absUrl, options );
+               } else {
+                       // Load the new page.
+                       $.ajax({
+                               url: fileUrl,
+                               type: settings.type,
+                               data: settings.data,
+                               dataType: "html",
+                               success: function( html, textStatus, xhr ) {
+                                       //pre-parse html to check for a data-url,
+                                       //use it as the new fileUrl, base path, etc
+                                       var all = $( "<div></div>" ),
+
+                                               //page title regexp
+                                               newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1,
+
+                                               // TODO handle dialogs again
+                                               pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)" ),
+                                               dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" );
+
+
+                                       // data-url must be provided for the base tag so resource requests can be directed to the
+                                       // correct url. loading into a temprorary element makes these requests immediately
+                                       if( pageElemRegex.test( html )
+                                                       && RegExp.$1
+                                                       && dataUrlRegex.test( RegExp.$1 )
+                                                       && RegExp.$1 ) {
+                                               url = fileUrl = path.getFilePath( RegExp.$1 );
+                                       }
+
+                                       if ( base ) {
+                                               base.set( fileUrl );
+                                       }
+
+                                       //workaround to allow scripts to execute when included in page divs
+                                       all.get( 0 ).innerHTML = html;
+                                       page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first();
+
+                                       //if page elem couldn't be found, create one and insert the body element's contents
+                                       if( !page.length ){
+                                               page = $( "<div data-" + $.mobile.ns + "role='page'>" + html.split( /<\/?body[^>]*>/gmi )[1] + "</div>" );
+                                       }
+
+                                       if ( newPageTitle && !page.jqmData( "title" ) ) {
+                                               if ( ~newPageTitle.indexOf( "&" ) ) {
+                                                       newPageTitle = $( "<div>" + newPageTitle + "</div>" ).text();
+                                               }
+                                               page.jqmData( "title", newPageTitle );
+                                       }
+
+                                       //rewrite src and href attrs to use a base url
+                                       if( !$.support.dynamicBaseTag ) {
+                                               var newPath = path.get( fileUrl );
+                                               page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() {
+                                                       var thisAttr = $( this ).is( '[href]' ) ? 'href' :
+                                                                       $(this).is('[src]') ? 'src' : 'action',
+                                                               thisUrl = $( this ).attr( thisAttr );
+
+                                                       // XXX_jblas: We need to fix this so that it removes the document
+                                                       //            base URL, and then prepends with the new page URL.
+                                                       //if full path exists and is same, chop it - helps IE out
+                                                       thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
+
+                                                       if( !/^(\w+:|#|\/)/.test( thisUrl ) ) {
+                                                               $( this ).attr( thisAttr, newPath + thisUrl );
+                                                       }
+                                               });
+                                       }
+
+                                       //append to page and enhance
+                                       // TODO taging a page with external to make sure that embedded pages aren't removed
+                                       //      by the various page handling code is bad. Having page handling code in many
+                                       //      places is bad. Solutions post 1.0
+                                       page
+                                               .attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) )
+                                               .attr( "data-" + $.mobile.ns + "external-page", true )
+                                               .appendTo( settings.pageContainer );
+
+                                       // wait for page creation to leverage options defined on widget
+                                       page.one( 'pagecreate', $.mobile._bindPageRemove );
+
+                                       enhancePage( page, settings.role );
+
+                                       // Enhancing the page may result in new dialogs/sub pages being inserted
+                                       // into the DOM. If the original absUrl refers to a sub-page, that is the
+                                       // real page we are interested in.
+                                       if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) {
+                                               page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
+                                       }
+
+                                       //bind pageHide to removePage after it's hidden, if the page options specify to do so
+
+                                       // Remove loading message.
+                                       if ( settings.showLoadMsg ) {
+                                               hideMsg();
+                                       }
+
+                                       // Add the page reference and xhr to our triggerData.
+                                       triggerData.xhr = xhr;
+                                       triggerData.textStatus = textStatus;
+                                       triggerData.page = page;
+
+                                       // Let listeners know the page loaded successfully.
+                                       settings.pageContainer.trigger( "pageload", triggerData );
+
+                                       deferred.resolve( absUrl, options, page, dupCachedPage );
+                               },
+                               error: function( xhr, textStatus, errorThrown ) {
+                                       //set base back to current path
+                                       if( base ) {
+                                               base.set( path.get() );
+                                       }
+
+                                       // Add error info to our triggerData.
+                                       triggerData.xhr = xhr;
+                                       triggerData.textStatus = textStatus;
+                                       triggerData.errorThrown = errorThrown;
+
+                                       var plfEvent = new $.Event( "pageloadfailed" );
+
+                                       // Let listeners know the page load failed.
+                                       settings.pageContainer.trigger( plfEvent, triggerData );
+
+                                       // If the default behavior is prevented, stop here!
+                                       // Note that it is the responsibility of the listener/handler
+                                       // that called preventDefault(), to resolve/reject the
+                                       // deferred object within the triggerData.
+                                       if( plfEvent.isDefaultPrevented() ){
+                                               return;
+                                       }
+
+                                       // Remove loading message.
+                                       if ( settings.showLoadMsg ) {
+
+                                               // Remove loading message.
+                                               hideMsg();
+
+                                               // show error message
+                                               $.mobile.showPageLoadingMsg( $.mobile.pageLoadErrorMessageTheme, $.mobile.pageLoadErrorMessage, true );
+
+                                               // hide after delay
+                                               setTimeout( $.mobile.hidePageLoadingMsg, 1500 );
+                                       }
+
+                                       deferred.reject( absUrl, options );
+                               }
+                       });
+               }
+
+               return deferred.promise();
+       };
+
+       $.mobile.loadPage.defaults = {
+               type: "get",
+               data: undefined,
+               reloadPage: false,
+               role: undefined, // By default we rely on the role defined by the @data-role attribute.
+               showLoadMsg: false,
+               pageContainer: undefined,
+               loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message.
+       };
+
+       // Show a specific page in the page container.
+       $.mobile.changePage = function( toPage, options ) {
+               // If we are in the midst of a transition, queue the current request.
+               // We'll call changePage() once we're done with the current transition to
+               // service the request.
+               if( isPageTransitioning ) {
+                       pageTransitionQueue.unshift( arguments );
+                       return;
+               }
+
+               var settings = $.extend( {}, $.mobile.changePage.defaults, options );
+
+               // Make sure we have a pageContainer to work with.
+               settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
+
+               // Make sure we have a fromPage.
+               settings.fromPage = settings.fromPage || $.mobile.activePage;
+
+               var mpc = settings.pageContainer,
+                       pbcEvent = new $.Event( "pagebeforechange" ),
+                       triggerData = { toPage: toPage, options: settings };
+
+               // Let listeners know we're about to change the current page.
+               mpc.trigger( pbcEvent, triggerData );
+
+               // If the default behavior is prevented, stop here!
+               if( pbcEvent.isDefaultPrevented() ){
+                       return;
+               }
+
+               // We allow "pagebeforechange" observers to modify the toPage in the trigger
+               // data to allow for redirects. Make sure our toPage is updated.
+
+               toPage = triggerData.toPage;
+
+               // Set the isPageTransitioning flag to prevent any requests from
+               // entering this method while we are in the midst of loading a page
+               // or transitioning.
+
+               isPageTransitioning = true;
+
+               // If the caller passed us a url, call loadPage()
+               // to make sure it is loaded into the DOM. We'll listen
+               // to the promise object it returns so we know when
+               // it is done loading or if an error ocurred.
+               if ( typeof toPage == "string" ) {
+                       $.mobile.loadPage( toPage, settings )
+                               .done(function( url, options, newPage, dupCachedPage ) {
+                                       isPageTransitioning = false;
+                                       options.duplicateCachedPage = dupCachedPage;
+                                       $.mobile.changePage( newPage, options );
+                               })
+                               .fail(function( url, options ) {
+                                       isPageTransitioning = false;
+
+                                       //clear out the active button state
+                                       removeActiveLinkClass( true );
+
+                                       //release transition lock so navigation is free again
+                                       releasePageTransitionLock();
+                                       settings.pageContainer.trigger( "pagechangefailed", triggerData );
+                               });
+                       return;
+               }
+
+               // If we are going to the first-page of the application, we need to make
+               // sure settings.dataUrl is set to the application document url. This allows
+               // us to avoid generating a document url with an id hash in the case where the
+               // first-page of the document has an id attribute specified.
+               if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) {
+                       settings.dataUrl = documentUrl.hrefNoHash;
+               }
+
+               // The caller passed us a real page DOM element. Update our
+               // internal state and then trigger a transition to the page.
+               var fromPage = settings.fromPage,
+                       url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ),
+                       // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
+                       pageUrl = url,
+                       fileUrl = path.getFilePath( url ),
+                       active = urlHistory.getActive(),
+                       activeIsInitialPage = urlHistory.activeIndex === 0,
+                       historyDir = 0,
+                       pageTitle = document.title,
+                       isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
+
+               // By default, we prevent changePage requests when the fromPage and toPage
+               // are the same element, but folks that generate content manually/dynamically
+               // and reuse pages want to be able to transition to the same page. To allow
+               // this, they will need to change the default value of allowSamePageTransition
+               // to true, *OR*, pass it in as an option when they manually call changePage().
+               // It should be noted that our default transition animations assume that the
+               // formPage and toPage are different elements, so they may behave unexpectedly.
+               // It is up to the developer that turns on the allowSamePageTransitiona option
+               // to either turn off transition animations, or make sure that an appropriate
+               // animation transition is used.
+               if( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) {
+                       isPageTransitioning = false;
+                       mpc.trigger( "pagechange", triggerData );
+
+                       // Even if there is no page change to be done, we should keep the urlHistory in sync with the hash changes
+                       if( settings.fromHashChange ) {
+                               urlHistory.directHashChange({
+                                       currentUrl:     url,
+                                       isBack:         function() {},
+                                       isForward:      function() {}
+                               });
+                       }
+
+                       return;
+               }
+
+               // We need to make sure the page we are given has already been enhanced.
+               enhancePage( toPage, settings.role );
+
+               // If the changePage request was sent from a hashChange event, check to see if the
+               // page is already within the urlHistory stack. If so, we'll assume the user hit
+               // the forward/back button and will try to match the transition accordingly.
+               if( settings.fromHashChange ) {
+                       urlHistory.directHashChange({
+                               currentUrl:     url,
+                               isBack:         function() { historyDir = -1; },
+                               isForward:      function() { historyDir = 1; }
+                       });
+               }
+
+               // Kill the keyboard.
+               // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead,
+               //            we should be tracking focus with a delegate() handler so we already have
+               //            the element in hand at this point.
+               // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement
+               // is undefined when we are in an IFrame.
+               try {
+                       if(document.activeElement && document.activeElement.nodeName.toLowerCase() != 'body') {
+                               $(document.activeElement).blur();
+                       } else {
+                               $( "input:focus, textarea:focus, select:focus" ).blur();
+                       }
+               } catch(e) {}
+
+               // Record whether we are at a place in history where a dialog used to be - if so, do not add a new history entry and do not change the hash either
+               var alreadyThere = false;
+
+               // If we're displaying the page as a dialog, we don't want the url
+               // for the dialog content to be used in the hash. Instead, we want
+               // to append the dialogHashKey to the url of the current page.
+               if ( isDialog && active ) {
+                       // on the initial page load active.url is undefined and in that case should
+                       // be an empty string. Moving the undefined -> empty string back into
+                       // urlHistory.addNew seemed imprudent given undefined better represents
+                       // the url state
+
+                       // If we are at a place in history that once belonged to a dialog, reuse
+                       // this state without adding to urlHistory and without modifying the hash.
+                       // However, if a dialog is already displayed at this point, and we're
+                       // about to display another dialog, then we must add another hash and
+                       // history entry on top so that one may navigate back to the original dialog
+                       if ( active.url.indexOf( dialogHashKey ) > -1 && !$.mobile.activePage.is( ".ui-dialog" ) ) {
+                               settings.changeHash = false;
+                               alreadyThere = true;
+                       }
+
+                       url = ( active.url || "" ) + dialogHashKey;
+
+                       // tack on another dialogHashKey if this is the same as the initial hash
+                       // this makes sure that a history entry is created for this dialog
+                       if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
+                               url += dialogHashKey;
+                       }
+               }
+
+               // Set the location hash.
+               if( settings.changeHash !== false && url ) {
+                       //disable hash listening temporarily
+                       urlHistory.ignoreNextHashChange = true;
+                       //update hash and history
+                       path.set( url );
+               }
+
+               // if title element wasn't found, try the page div data attr too
+               // If this is a deep-link or a reload ( active === undefined ) then just use pageTitle
+               var newPageTitle = ( !active )? pageTitle : toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).getEncodedText();
+               if( !!newPageTitle && pageTitle == document.title ) {
+                       pageTitle = newPageTitle;
+               }
+               if ( !toPage.jqmData( "title" ) ) {
+                       toPage.jqmData( "title", pageTitle );
+               }
+
+               // Make sure we have a transition defined.
+               settings.transition = settings.transition
+                       || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined )
+                       || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition );
+
+               //add page to history stack if it's not back or forward
+               if( !historyDir && !alreadyThere ) {
+                       urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
+               }
+
+               //set page title
+               document.title = urlHistory.getActive().title;
+
+               //set "toPage" as activePage
+               $.mobile.activePage = toPage;
+
+               // If we're navigating back in the URL history, set reverse accordingly.
+               settings.reverse = settings.reverse || historyDir < 0;
+
+               transitionPages( toPage, fromPage, settings.transition, settings.reverse )
+                       .done(function( name, reverse, $to, $from, alreadyFocused ) {
+                               removeActiveLinkClass();
+
+                               //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
+                               if ( settings.duplicateCachedPage ) {
+                                       settings.duplicateCachedPage.remove();
+                               }
+
+                               // Send focus to the newly shown page. Moved from promise .done binding in transitionPages
+                               // itself to avoid ie bug that reports offsetWidth as > 0 (core check for visibility)
+                               // despite visibility: hidden addresses issue #2965
+                               // https://github.com/jquery/jquery-mobile/issues/2965
+                               if( !alreadyFocused ){
+                                       $.mobile.focusPage( toPage );
+                               }
+
+                               releasePageTransitionLock();
+
+                               // Let listeners know we're all done changing the current page.
+                               mpc.trigger( "pagechange", triggerData );
+                       });
+       };
+
+       $.mobile.changePage.defaults = {
+               transition: undefined,
+               reverse: false,
+               changeHash: true,
+               fromHashChange: false,
+               role: undefined, // By default we rely on the role defined by the @data-role attribute.
+               duplicateCachedPage: undefined,
+               pageContainer: undefined,
+               showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
+               dataUrl: undefined,
+               fromPage: undefined,
+               allowSamePageTransition: false
+       };
+
+/* Event Bindings - hashchange, submit, and click */
+       function findClosestLink( ele )
+       {
+               while ( ele ) {
+                       // Look for the closest element with a nodeName of "a".
+                       // Note that we are checking if we have a valid nodeName
+                       // before attempting to access it. This is because the
+                       // node we get called with could have originated from within
+                       // an embedded SVG document where some symbol instance elements
+                       // don't have nodeName defined on them, or strings are of type
+                       // SVGAnimatedString.
+                       if ( ( typeof ele.nodeName === "string" ) && ele.nodeName.toLowerCase() == "a" ) {
+                               break;
+                       }
+                       ele = ele.parentNode;
+               }
+               return ele;
+       }
+
+       // The base URL for any given element depends on the page it resides in.
+       function getClosestBaseUrl( ele )
+       {
+               // Find the closest page and extract out its url.
+               var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ),
+                       base = documentBase.hrefNoHash;
+
+               if ( !url || !path.isPath( url ) ) {
+                       url = base;
+               }
+
+               return path.makeUrlAbsolute( url, base);
+       }
+
+       //The following event bindings should be bound after mobileinit has been triggered
+       //the following deferred is resolved in the init file
+       $.mobile.navreadyDeferred = $.Deferred();
+       $.mobile.navreadyDeferred.done( function(){
+               //bind to form submit events, handle with Ajax
+               $( document ).delegate( "form", "submit", function( event ) {
+                       var $this = $( this );
+
+                       if( !$.mobile.ajaxEnabled ||
+                                       // test that the form is, itself, ajax false
+                                       $this.is(":jqmData(ajax='false')") ||
+                                       // test that $.mobile.ignoreContentEnabled is set and
+                                       // the form or one of it's parents is ajax=false
+                                       !$this.jqmHijackable().length ) {
+                               return;
+                       }
+
+                       var type = $this.attr( "method" ),
+                               target = $this.attr( "target" ),
+                               url = $this.attr( "action" );
+
+                       // If no action is specified, browsers default to using the
+                       // URL of the document containing the form. Since we dynamically
+                       // pull in pages from external documents, the form should submit
+                       // to the URL for the source document of the page containing
+                       // the form.
+                       if ( !url ) {
+                               // Get the @data-url for the page containing the form.
+                               url = getClosestBaseUrl( $this );
+                               if ( url === documentBase.hrefNoHash ) {
+                                       // The url we got back matches the document base,
+                                       // which means the page must be an internal/embedded page,
+                                       // so default to using the actual document url as a browser
+                                       // would.
+                                       url = documentUrl.hrefNoSearch;
+                               }
+                       }
+
+                       url = path.makeUrlAbsolute(  url, getClosestBaseUrl($this) );
+
+                       if(( path.isExternal( url ) && !path.isPermittedCrossDomainRequest(documentUrl, url)) || target ) {
+                               return;
+                       }
+
+                       $.mobile.changePage(
+                               url,
+                               {
+                                       type:           type && type.length && type.toLowerCase() || "get",
+                                       data:           $this.serialize(),
+                                       transition:     $this.jqmData( "transition" ),
+                                       direction:      $this.jqmData( "direction" ),
+                                       reloadPage:     true
+                               }
+                       );
+                       event.preventDefault();
+               });
+
+               //add active state on vclick
+               $( document ).bind( "vclick", function( event ) {
+                       // if this isn't a left click we don't care. Its important to note
+                       // that when the virtual event is generated it will create the which attr
+                       if ( event.which > 1 || !$.mobile.linkBindingEnabled ) {
+                               return;
+                       }
+
+                       var link = findClosestLink( event.target );
+
+                       // split from the previous return logic to avoid find closest where possible
+                       // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
+                       // can be avoided
+                       if ( !$(link).jqmHijackable().length ) {
+                               return;
+                       }
+
+                       if ( link ) {
+                               if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
+                                       removeActiveLinkClass( true );
+                                       $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
+                                       $activeClickedLink.addClass( $.mobile.activeBtnClass );
+                               }
+                       }
+               });
+
+               // click routing - direct to HTTP or Ajax, accordingly
+               $( document ).bind( "click", function( event ) {
+                       if( !$.mobile.linkBindingEnabled ){
+                               return;
+                       }
+
+                       var link = findClosestLink( event.target ), $link = $( link ), httpCleanup;
+
+                       // If there is no link associated with the click or its not a left
+                       // click we want to ignore the click
+                       // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
+                       // can be avoided
+                       if ( !link || event.which > 1 || !$link.jqmHijackable().length ) {
+                               return;
+                       }
+
+                       //remove active link class if external (then it won't be there if you come back)
+                       httpCleanup = function(){
+                               window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
+                       };
+
+                       //if there's a data-rel=back attr, go back in history
+                       if( $link.is( ":jqmData(rel='back')" ) ) {
+                               window.history.back();
+                               return false;
+                       }
+
+                       var baseUrl = getClosestBaseUrl( $link ),
+
+                               //get href, if defined, otherwise default to empty hash
+                               href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
+
+                       //if ajax is disabled, exit early
+                       if( !$.mobile.ajaxEnabled && !path.isEmbeddedPage( href ) ){
+                               httpCleanup();
+                               //use default click handling
+                               return;
+                       }
+
+                       // XXX_jblas: Ideally links to application pages should be specified as
+                       //            an url to the application document with a hash that is either
+                       //            the site relative path or id to the page. But some of the
+                       //            internal code that dynamically generates sub-pages for nested
+                       //            lists and select dialogs, just write a hash in the link they
+                       //            create. This means the actual URL path is based on whatever
+                       //            the current value of the base tag is at the time this code
+                       //            is called. For now we are just assuming that any url with a
+                       //            hash in it is an application page reference.
+                       if ( href.search( "#" ) != -1 ) {
+                               href = href.replace( /[^#]*#/, "" );
+                               if ( !href ) {
+                                       //link was an empty hash meant purely
+                                       //for interaction, so we ignore it.
+                                       event.preventDefault();
+                                       return;
+                               } else if ( path.isPath( href ) ) {
+                                       //we have apath so make it the href we want to load.
+                                       href = path.makeUrlAbsolute( href, baseUrl );
+                               } else {
+                                       //we have a simple id so use the documentUrl as its base.
+                                       href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash );
+                               }
+                       }
+
+                               // Should we handle this link, or let the browser deal with it?
+                       var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ),
+
+                               // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
+                               // requests if the document doing the request was loaded via the file:// protocol.
+                               // This is usually to allow the application to "phone home" and fetch app specific
+                               // data. We normally let the browser handle external/cross-domain urls, but if the
+                               // allowCrossDomainPages option is true, we will allow cross-domain http/https
+                               // requests to go through our page loading logic.
+
+                               //check for protocol or rel and its not an embedded page
+                               //TODO overlap in logic from isExternal, rel=external check should be
+                               //     moved into more comprehensive isExternalLink
+                               isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !path.isPermittedCrossDomainRequest(documentUrl, href) );
+
+                       if( isExternal ) {
+                               httpCleanup();
+                               //use default click handling
+                               return;
+                       }
+
+                       //use ajax
+                       var transition = $link.jqmData( "transition" ),
+                               direction = $link.jqmData( "direction" ),
+                               reverse = ( direction && direction === "reverse" ) ||
+                                                       // deprecated - remove by 1.0
+                                                       $link.jqmData( "back" ),
+
+                               //this may need to be more specific as we use data-rel more
+                               role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
+
+                       $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } );
+                       event.preventDefault();
+               });
+
+               //prefetch pages when anchors with data-prefetch are encountered
+               $( document ).delegate( ".ui-page", "pageshow.prefetch", function() {
+                       var urls = [];
+                       $( this ).find( "a:jqmData(prefetch)" ).each(function(){
+                               var $link = $(this),
+                                       url = $link.attr( "href" );
+
+                               if ( url && $.inArray( url, urls ) === -1 ) {
+                                       urls.push( url );
+
+                                       $.mobile.loadPage( url, {role: $link.attr("data-" + $.mobile.ns + "rel")} );
+                               }
+                       });
+               });
+
+               $.mobile._handleHashChange = function( hash ) {
+                       //find first page via hash
+                       var to = path.stripHash( hash ),
+                               //transition is false if it's the first page, undefined otherwise (and may be overridden by default)
+                               transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
+
+                               // default options for the changPage calls made after examining the current state
+                               // of the page and the hash
+                               changePageOptions = {
+                                       transition: transition,
+                                       changeHash: false,
+                                       fromHashChange: true
+                               };
+
+                       if ( 0 === urlHistory.stack.length ) {
+                               urlHistory.initialDst = to;
+                       }
+
+                       //if listening is disabled (either globally or temporarily), or it's a dialog hash
+                       if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
+                               urlHistory.ignoreNextHashChange = false;
+                               return;
+                       }
+
+                       // special case for dialogs
+                       if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 && urlHistory.initialDst !== to ) {
+
+                               // If current active page is not a dialog skip the dialog and continue
+                               // in the same direction
+                               if(!$.mobile.activePage.is( ".ui-dialog" )) {
+                                       //determine if we're heading forward or backward and continue accordingly past
+                                       //the current dialog
+                                       urlHistory.directHashChange({
+                                               currentUrl: to,
+                                               isBack: function() { window.history.back(); },
+                                               isForward: function() { window.history.forward(); }
+                                       });
+
+                                       // prevent changePage()
+                                       return;
+                               } else {
+                                       // if the current active page is a dialog and we're navigating
+                                       // to a dialog use the dialog objected saved in the stack
+                                       urlHistory.directHashChange({
+                                               currentUrl: to,
+
+                                               // regardless of the direction of the history change
+                                               // do the following
+                                               either: function( isBack ) {
+                                                       var active = $.mobile.urlHistory.getActive();
+
+                                                       to = active.pageUrl;
+
+                                                       // make sure to set the role, transition and reversal
+                                                       // as most of this is lost by the domCache cleaning
+                                                       $.extend( changePageOptions, {
+                                                               role: active.role,
+                                                               transition:      active.transition,
+                                                               reverse: isBack
+                                                       });
+                                               }
+                                       });
+                               }
+                       }
+
+                       //if to is defined, load it
+                       if ( to ) {
+                               // At this point, 'to' can be one of 3 things, a cached page element from
+                               // a history stack entry, an id, or site-relative/absolute URL. If 'to' is
+                               // an id, we need to resolve it against the documentBase, not the location.href,
+                               // since the hashchange could've been the result of a forward/backward navigation
+                               // that crosses from an external page/dialog to an internal page/dialog.
+                               to = ( typeof to === "string" && !path.isPath( to ) ) ? ( path.makeUrlAbsolute( '#' + to, documentBase ) ) : to;
+                               $.mobile.changePage( to, changePageOptions );
+                       }       else {
+                               //there's no hash, go to the first page in the dom
+                               $.mobile.changePage( $.mobile.firstPage, changePageOptions );
+                       }
+               };
+
+               //hashchange event handler
+               $window.bind( "hashchange", function( e, triggered ) {
+                       $.mobile._handleHashChange( location.hash );
+               });
+
+               //set page min-heights to be device specific
+               $( document ).bind( "pageshow", resetActivePageHeight );
+               $( window ).bind( "throttledresize", resetActivePageHeight );
+
+       });//navreadyDeferred done callback
+
+})( jQuery );
+
+( function( $, window ) {
+       // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
+       // Scope self to pushStateHandler so we can reference it sanely within the
+       // methods handed off as event handlers
+       var     pushStateHandler = {},
+               self = pushStateHandler,
+               $win = $( window ),
+               url = $.mobile.path.parseUrl( location.href ),
+               mobileinitDeferred = $.Deferred(),
+               domreadyDeferred = $.Deferred();
+
+       $( document ).ready( $.proxy( domreadyDeferred, "resolve" ) );
+
+       $( document ).one( "mobileinit", $.proxy( mobileinitDeferred, "resolve" ) );
+
+       $.extend( pushStateHandler, {
+               // TODO move to a path helper, this is rather common functionality
+               initialFilePath: (function() {
+                       return url.pathname + url.search;
+               })(),
+
+               hashChangeTimeout: 200,
+
+               hashChangeEnableTimer: undefined,
+
+               initialHref: url.hrefNoHash,
+
+               state: function() {
+                       return {
+                               hash: location.hash || "#" + self.initialFilePath,
+                               title: document.title,
+
+                               // persist across refresh
+                               initialHref: self.initialHref
+                       };
+               },
+
+               resetUIKeys: function( url ) {
+                       var dialog = $.mobile.dialogHashKey,
+                               subkey = "&" + $.mobile.subPageUrlKey,
+                               dialogIndex = url.indexOf( dialog );
+
+                       if( dialogIndex > -1 ) {
+                               url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
+                       } else if( url.indexOf( subkey ) > -1 ) {
+                               url = url.split( subkey ).join( "#" + subkey );
+                       }
+
+                       return url;
+               },
+
+               // TODO sort out a single barrier to hashchange functionality
+               nextHashChangePrevented: function( value ) {
+                       $.mobile.urlHistory.ignoreNextHashChange = value;
+                       self.onHashChangeDisabled = value;
+               },
+
+               // on hash change we want to clean up the url
+               // NOTE this takes place *after* the vanilla navigation hash change
+               // handling has taken place and set the state of the DOM
+               onHashChange: function( e ) {
+                       // disable this hash change
+                       if( self.onHashChangeDisabled ){
+                               return;
+                       }
+
+                       var href, state,
+                               hash = location.hash,
+                               isPath = $.mobile.path.isPath( hash ),
+                               resolutionUrl = isPath ? location.href : $.mobile.getDocumentUrl();
+
+                       hash = isPath ? hash.replace( "#", "" ) : hash;
+
+
+                       // propulate the hash when its not available
+                       state = self.state();
+
+                       // make the hash abolute with the current href
+                       href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl );
+
+                       if ( isPath ) {
+                               href = self.resetUIKeys( href );
+                       }
+
+                       // replace the current url with the new href and store the state
+                       // Note that in some cases we might be replacing an url with the
+                       // same url. We do this anyways because we need to make sure that
+                       // all of our history entries have a state object associated with
+                       // them. This allows us to work around the case where window.history.back()
+                       // is called to transition from an external page to an embedded page.
+                       // In that particular case, a hashchange event is *NOT* generated by the browser.
+                       // Ensuring each history entry has a state object means that onPopState()
+                       // will always trigger our hashchange callback even when a hashchange event
+                       // is not fired.
+                       history.replaceState( state, document.title, href );
+               },
+
+               // on popstate (ie back or forward) we need to replace the hash that was there previously
+               // cleaned up by the additional hash handling
+               onPopState: function( e ) {
+                       var poppedState = e.originalEvent.state,
+                               fromHash, toHash, hashChanged;
+
+                       // if there's no state its not a popstate we care about, eg chrome's initial popstate
+                       if( poppedState ) {
+                               // if we get two pop states in under this.hashChangeTimeout
+                               // make sure to clear any timer set for the previous change
+                               clearTimeout( self.hashChangeEnableTimer );
+
+                               // make sure to enable hash handling for the the _handleHashChange call
+                               self.nextHashChangePrevented( false );
+
+                               // change the page based on the hash in the popped state
+                               $.mobile._handleHashChange( poppedState.hash );
+
+                               // prevent any hashchange in the next self.hashChangeTimeout
+                               self.nextHashChangePrevented( true );
+
+                               // re-enable hash change handling after swallowing a possible hash
+                               // change event that comes on all popstates courtesy of browsers like Android
+                               self.hashChangeEnableTimer = setTimeout( function() {
+                                       self.nextHashChangePrevented( false );
+                               }, self.hashChangeTimeout);
+                       }
+               },
+
+               init: function() {
+                       $win.bind( "hashchange", self.onHashChange );
+
+                       // Handle popstate events the occur through history changes
+                       $win.bind( "popstate", self.onPopState );
+
+                       // if there's no hash, we need to replacestate for returning to home
+                       if ( location.hash === "" ) {
+                               history.replaceState( self.state(), document.title, location.href );
+                       }
+               }
+       });
+
+       // We need to init when "mobileinit", "domready", and "navready" have all happened
+       $.when( domreadyDeferred, mobileinitDeferred, $.mobile.navreadyDeferred ).done( function() {
+               if( $.mobile.pushStateEnabled && $.support.pushState ){
+                       pushStateHandler.init();
+               }
+       });
+})( jQuery, this );
+
+/*
+* fallback transition for pop in non-3D supporting browsers (which tend to handle complex transitions poorly in general
+*/
+
+(function( $, window, undefined ) {
+
+$.mobile.transitionFallbacks.pop = "fade";
+
+})( jQuery, this );
+
+/*
+* fallback transition for slide in non-3D supporting browsers (which tend to handle complex transitions poorly in general
+*/
+
+(function( $, window, undefined ) {
+
+// Use the simultaneous transition handler for slide transitions
+$.mobile.transitionHandlers.slide = $.mobile.transitionHandlers.simultaneous;
+
+// Set the slide transition's fallback to "fade"
+$.mobile.transitionFallbacks.slide = "fade";
+
+})( jQuery, this );
+
+/*
+* fallback transition for slidedown in non-3D supporting browsers (which tend to handle complex transitions poorly in general
+*/
+
+(function( $, window, undefined ) {
+
+$.mobile.transitionFallbacks.slidedown = "fade";
+
+})( jQuery, this );
+
+/*
+* fallback transition for slideup in non-3D supporting browsers (which tend to handle complex transitions poorly in general
+*/
+
+(function( $, window, undefined ) {
+
+$.mobile.transitionFallbacks.slideup = "fade";
+
+})( jQuery, this );
+
+/*
+* fallback transition for flip in non-3D supporting browsers (which tend to handle complex transitions poorly in general
+*/
+
+(function( $, window, undefined ) {
+
+$.mobile.transitionFallbacks.flip = "fade";
+
+})( jQuery, this );
+
+/*
+* fallback transition for flow in non-3D supporting browsers (which tend to handle complex transitions poorly in general
+*/
+
+(function( $, window, undefined ) {
+
+$.mobile.transitionFallbacks.flow = "fade";
+
+})( jQuery, this );
+
+/*
+* fallback transition for turn in non-3D supporting browsers (which tend to handle complex transitions poorly in general
+*/
+
+(function( $, window, undefined ) {
+
+$.mobile.transitionFallbacks.turn = "fade";
+
+})( jQuery, this );
+
+(function( $, undefined ) {
+
+$.mobile.page.prototype.options.degradeInputs = {
+       color: false,
+       date: false,
+       datetime: false,
+       "datetime-local": false,
+       email: false,
+       month: false,
+       number: false,
+       range: "number",
+       search: "text",
+       tel: false,
+       time: false,
+       url: false,
+       week: false
+};
+
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+
+       var page = $.mobile.closestPageData($(e.target)), options;
+
+       if( !page ) {
+               return;
+       }
+
+       options = page.options;
+
+       // degrade inputs to avoid poorly implemented native functionality
+       $( e.target ).find( "input" ).not( page.keepNativeSelector() ).each(function() {
+               var $this = $( this ),
+                       type = this.getAttribute( "type" ),
+                       optType = options.degradeInputs[ type ] || "text";
+
+               if ( options.degradeInputs[ type ] ) {
+                       var html = $( "<div>" ).html( $this.clone() ).html(),
+                               // In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead
+                               hasType = html.indexOf( " type=" ) > -1,
+                               findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/,
+                               repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + ( hasType ? "" : ">" );
+
+                       $this.replaceWith( html.replace( findstr, repstr ) );
+               }
+       });
+
+});
+
+})( jQuery );
+
+(function( $, window, undefined ) {
+
+$.widget( "mobile.dialog", $.mobile.widget, {
+       options: {
+               closeBtnText    : "Close",
+               overlayTheme    : "a",
+               initSelector    : ":jqmData(role='dialog')"
+       },
+       _create: function() {
+               var self = this,
+                       $el = this.element,
+                       headerCloseButton = $( "<a href='#' data-" + $.mobile.ns + "icon='delete' data-" + $.mobile.ns + "iconpos='notext'>"+ this.options.closeBtnText + "</a>" ),
+                       dialogWrap = $("<div/>", {
+                                       "role" : "dialog",
+                                       "class" : "ui-dialog-contain ui-corner-all ui-overlay-shadow"
+                               });
+
+               $el.addClass( "ui-dialog ui-overlay-" + this.options.overlayTheme );
+               
+               // Class the markup for dialog styling
+               // Set aria role
+               $el
+                       .wrapInner( dialogWrap )
+                       .children()
+                               .find( ":jqmData(role='header')" )
+                                       .prepend( headerCloseButton )
+                               .end()
+                               .children( ':first-child')
+                                       .addClass( "ui-corner-top" )
+                               .end()
+                               .children( ":last-child" )
+                                       .addClass( "ui-corner-bottom" );
+
+               // this must be an anonymous function so that select menu dialogs can replace
+               // the close method. This is a change from previously just defining data-rel=back
+               // on the button and letting nav handle it
+               //
+               // Use click rather than vclick in order to prevent the possibility of unintentionally
+               // reopening the dialog if the dialog opening item was directly under the close button.
+               headerCloseButton.bind( "click", function() {
+                       self.close();
+               });
+
+               /* bind events
+                       - clicks and submits should use the closing transition that the dialog opened with
+                         unless a data-transition is specified on the link/form
+                       - if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally
+               */
+               $el.bind( "vclick submit", function( event ) {
+                       var $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ),
+                               active;
+
+                       if ( $target.length && !$target.jqmData( "transition" ) ) {
+
+                               active = $.mobile.urlHistory.getActive() || {};
+
+                               $target.attr( "data-" + $.mobile.ns + "transition", ( active.transition || $.mobile.defaultDialogTransition ) )
+                                       .attr( "data-" + $.mobile.ns + "direction", "reverse" );
+                       }
+               })
+               .bind( "pagehide", function( e, ui ) {
+                       self._isClosed = false;
+                       $( this ).find( "." + $.mobile.activeBtnClass ).not( ".ui-slider-bg" ).removeClass( $.mobile.activeBtnClass );
+               })
+               // Override the theme set by the page plugin on pageshow
+               .bind( "pagebeforeshow", function(){
+                       if( self.options.overlayTheme ){
+                               self.element
+                                       .page( "removeContainerBackground" )
+                                       .page( "setContainerBackground", self.options.overlayTheme );
+                       }
+               });
+       },
+
+       // Close method goes back in history
+       close: function() {
+               if ( !this._isClosed ) {
+                       this._isClosed = true;
+                       if ( $.mobile.hashListeningEnabled ) {
+                               window.history.back();
+                       }
+                       else {
+                               $.mobile.changePage( $.mobile.urlHistory.getPrev().url );
+                       }
+               }
+       }
+});
+
+//auto self-init widgets
+$( document ).delegate( $.mobile.dialog.prototype.options.initSelector, "pagecreate", function(){
+       $.mobile.dialog.prototype.enhance( this );
+});
+
+})( jQuery, this );
+
+(function( $, undefined ) {
+
+$.mobile.page.prototype.options.backBtnText  = "Back";
+$.mobile.page.prototype.options.addBackBtn   = false;
+$.mobile.page.prototype.options.backBtnTheme = null;
+$.mobile.page.prototype.options.headerTheme  = "a";
+$.mobile.page.prototype.options.footerTheme  = "a";
+$.mobile.page.prototype.options.contentTheme = null;
+
+// NOTE bind used to force this binding to run before the buttonMarkup binding
+//      which expects .ui-footer top be applied in its gigantic selector 
+// TODO remove the buttonMarkup giant selector and move it to the various modules
+//      on which it depends
+$( document ).bind( "pagecreate", function( e ) {
+       var $page = $( e.target ),
+               o = $page.data( "page" ).options,
+               pageRole = $page.jqmData( "role" ),
+               pageTheme = o.theme;
+
+       $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", $page )
+               .jqmEnhanceable()
+               .each(function() {
+
+               var $this = $( this ),
+                       role = $this.jqmData( "role" ),
+                       theme = $this.jqmData( "theme" ),
+                       contentTheme = theme || o.contentTheme || ( pageRole === "dialog" && pageTheme ),
+                       $headeranchors,
+                       leftbtn,
+                       rightbtn,
+                       backBtn;
+
+               $this.addClass( "ui-" + role );
+
+               //apply theming and markup modifications to page,header,content,footer
+               if ( role === "header" || role === "footer" ) {
+
+                       var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
+
+                       $this
+                               //add theme class
+                               .addClass( "ui-bar-" + thisTheme )
+                               // Add ARIA role
+                               .attr( "role", role === "header" ? "banner" : "contentinfo" );
+
+                       if( role === "header") {
+                               // Right,left buttons
+                               $headeranchors  = $this.children( "a" );
+                               leftbtn = $headeranchors.hasClass( "ui-btn-left" );
+                               rightbtn = $headeranchors.hasClass( "ui-btn-right" );
+
+                               leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
+
+                               rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
+                       }
+
+                       // Auto-add back btn on pages beyond first view
+                       if ( o.addBackBtn &&
+                               role === "header" &&
+                               $( ".ui-page" ).length > 1 &&
+                               $page.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
+                               !leftbtn ) {
+
+                               backBtn = $( "<a href='javascript:void(0);' class='ui-btn-left' data-"+ $.mobile.ns +"rel='back' data-"+ $.mobile.ns +"icon='arrow-l'>"+ o.backBtnText +"</a>" )
+                                       // If theme is provided, override default inheritance
+                                       .attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme )
+                                       .prependTo( $this );
+                       }
+
+                       // Page title
+                       $this.children( "h1, h2, h3, h4, h5, h6" )
+                               .addClass( "ui-title" )
+                               // Regardless of h element number in src, it becomes h1 for the enhanced page
+                               .attr({
+                                       "role": "heading",
+                                       "aria-level": "1"
+                               });
+
+               } else if ( role === "content" ) {
+                       if ( contentTheme ) {
+                           $this.addClass( "ui-body-" + ( contentTheme ) );
+                       }
+
+                       // Add ARIA role
+                       $this.attr( "role", "main" );
+               }
+       });
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+// filter function removes whitespace between label and form element so we can use inline-block (nodeType 3 = text)
+$.fn.fieldcontain = function( options ) {
+       return this
+               .addClass( "ui-field-contain ui-body ui-br" )
+               .contents().filter( function() {
+                       return ( this.nodeType === 3 && !/\S/.test( this.nodeValue ) );
+               }).remove();
+};
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $( ":jqmData(role='fieldcontain')", e.target ).jqmEnhanceable().fieldcontain();
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.fn.grid = function( options ) {
+       return this.each(function() {
+
+               var $this = $( this ),
+                       o = $.extend({
+                               grid: null
+                       },options),
+                       $kids = $this.children(),
+                       gridCols = {solo:1, a:2, b:3, c:4, d:5},
+                       grid = o.grid,
+                       iterator;
+
+                       if ( !grid ) {
+                               if ( $kids.length <= 5 ) {
+                                       for ( var letter in gridCols ) {
+                                               if ( gridCols[ letter ] === $kids.length ) {
+                                                       grid = letter;
+                                               }
+                                       }
+                               } else {
+                                       grid = "a";
+                                       $this.addClass( "ui-grid-duo" );
+                               }
+                       }
+                       iterator = gridCols[grid];
+
+               $this.addClass( "ui-grid-" + grid );
+
+               $kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" );
+
+               if ( iterator > 1 ) {
+                       $kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" );
+               }
+               if ( iterator > 2 ) {
+                       $kids.filter( ":nth-child(3n+3)" ).addClass( "ui-block-c" );
+               }
+               if ( iterator > 3 ) {
+                       $kids.filter( ":nth-child(4n+4)" ).addClass( "ui-block-d" );
+               }
+               if ( iterator > 4 ) {
+                       $kids.filter( ":nth-child(5n+5)" ).addClass( "ui-block-e" );
+               }
+       });
+};
+})( jQuery );
+
+(function( $, undefined ) {
+
+$( document ).bind( "pagecreate create", function( e ){
+       $( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" );
+       
+});
+
+})( jQuery );
+
+( function( $, undefined ) {
+
+$.fn.buttonMarkup = function( options ) {
+       var $workingSet = this;
+
+       // Enforce options to be of type string
+       options = ( options && ( $.type( options ) == "object" ) )? options : {};
+       for ( var i = 0; i < $workingSet.length; i++ ) {
+               var el = $workingSet.eq( i ),
+                       e = el[ 0 ],
+                       o = $.extend( {}, $.fn.buttonMarkup.defaults, {
+                               icon:       options.icon       !== undefined ? options.icon       : el.jqmData( "icon" ),
+                               iconpos:    options.iconpos    !== undefined ? options.iconpos    : el.jqmData( "iconpos" ),
+                               theme:      options.theme      !== undefined ? options.theme      : el.jqmData( "theme" ) || $.mobile.getInheritedTheme( el, "c" ),
+                               inline:     options.inline     !== undefined ? options.inline     : el.jqmData( "inline" ),
+                               shadow:     options.shadow     !== undefined ? options.shadow     : el.jqmData( "shadow" ),
+                               corners:    options.corners    !== undefined ? options.corners    : el.jqmData( "corners" ),
+                               iconshadow: options.iconshadow !== undefined ? options.iconshadow : el.jqmData( "iconshadow" ),
+                               mini:       options.mini       !== undefined ? options.mini       : el.jqmData( "mini" )
+                       }, options ),
+
+                       // Classes Defined
+                       innerClass = "ui-btn-inner",
+                       textClass = "ui-btn-text",
+                       buttonClass, iconClass,
+                       // Button inner markup
+                       buttonInner,
+                       buttonText,
+                       buttonIcon,
+                       buttonElements;
+
+               $.each(o, function(key, value) {
+                       e.setAttribute( "data-" + $.mobile.ns + key, value );
+                       el.jqmData(key, value);
+               });
+
+               // Check if this element is already enhanced
+               buttonElements = $.data(((e.tagName === "INPUT" || e.tagName === "BUTTON") ? e.parentNode : e), "buttonElements");
+
+               if (buttonElements) {
+                       e = buttonElements.outer;
+                       el = $(e);
+                       buttonInner = buttonElements.inner;
+                       buttonText = buttonElements.text;
+                       // We will recreate this icon below
+                       $(buttonElements.icon).remove();
+                       buttonElements.icon = null;
+               }
+               else {
+                       buttonInner = document.createElement( o.wrapperEls );
+                       buttonText = document.createElement( o.wrapperEls );
+               }
+               buttonIcon = o.icon ? document.createElement( "span" ) : null;
+
+               if ( attachEvents && !buttonElements) {
+                       attachEvents();
+               }
+               
+               // if not, try to find closest theme container  
+               if ( !o.theme ) {
+                       o.theme = $.mobile.getInheritedTheme( el, "c" );        
+               }               
+
+               buttonClass = "ui-btn ui-btn-up-" + o.theme;
+               buttonClass += o.inline ? " ui-btn-inline" : "";
+               buttonClass += o.shadow ? " ui-shadow" : "";
+               buttonClass += o.corners ? " ui-btn-corner-all" : "";
+
+               if ( o.mini !== undefined ) {
+                       // Used to control styling in headers/footers, where buttons default to `mini` style.
+                       buttonClass += o.mini ? " ui-mini" : " ui-fullsize";
+               }
+               
+               if ( o.inline !== undefined ) {                 
+                       // Used to control styling in headers/footers, where buttons default to `mini` style.
+                       buttonClass += o.inline === false ? " ui-btn-block" : " ui-btn-inline";
+               }
+               
+               
+               if ( o.icon ) {
+                       o.icon = "ui-icon-" + o.icon;
+                       o.iconpos = o.iconpos || "left";
+
+                       iconClass = "ui-icon " + o.icon;
+
+                       if ( o.iconshadow ) {
+                               iconClass += " ui-icon-shadow";
+                       }
+               }
+
+               if ( o.iconpos ) {
+                       buttonClass += " ui-btn-icon-" + o.iconpos;
+
+                       if ( o.iconpos == "notext" && !el.attr( "title" ) ) {
+                               el.attr( "title", el.getEncodedText() );
+                       }
+               }
+    
+               innerClass += o.corners ? " ui-btn-corner-all" : "";
+
+               if ( o.iconpos && o.iconpos === "notext" && !el.attr( "title" ) ) {
+                       el.attr( "title", el.getEncodedText() );
+               }
+
+               if ( buttonElements ) {
+                       el.removeClass( buttonElements.bcls || "" );
+               }
+               el.removeClass( "ui-link" ).addClass( buttonClass );
+
+               buttonInner.className = innerClass;
+
+               buttonText.className = textClass;
+               if ( !buttonElements ) {
+                       buttonInner.appendChild( buttonText );
+               }
+               if ( buttonIcon ) {
+                       buttonIcon.className = iconClass;
+                       if ( !(buttonElements && buttonElements.icon) ) {
+                               buttonIcon.appendChild( document.createTextNode("\u00a0") );
+                               buttonInner.appendChild( buttonIcon );
+                       }
+               }
+
+               while ( e.firstChild && !buttonElements) {
+                       buttonText.appendChild( e.firstChild );
+               }
+
+               if ( !buttonElements ) {
+                       e.appendChild( buttonInner );
+               }
+
+               // Assign a structure containing the elements of this button to the elements of this button. This
+               // will allow us to recognize this as an already-enhanced button in future calls to buttonMarkup().
+               buttonElements = {
+                       bcls  : buttonClass,
+                       outer : e,
+                       inner : buttonInner,
+                       text  : buttonText,
+                       icon  : buttonIcon
+               };
+
+               $.data(e,           'buttonElements', buttonElements);
+               $.data(buttonInner, 'buttonElements', buttonElements);
+               $.data(buttonText,  'buttonElements', buttonElements);
+               if (buttonIcon) {
+                       $.data(buttonIcon, 'buttonElements', buttonElements);
+               }
+       }
+
+       return this;
+};
+
+$.fn.buttonMarkup.defaults = {
+       corners: true,
+       shadow: true,
+       iconshadow: true,
+       wrapperEls: "span"
+};
+
+function closestEnabledButton( element ) {
+    var cname;
+
+    while ( element ) {
+               // Note that we check for typeof className below because the element we
+               // handed could be in an SVG DOM where className on SVG elements is defined to
+               // be of a different type (SVGAnimatedString). We only operate on HTML DOM
+               // elements, so we look for plain "string".
+        cname = ( typeof element.className === 'string' ) && (element.className + ' ');
+        if ( cname && cname.indexOf("ui-btn ") > -1 && cname.indexOf("ui-disabled ") < 0 ) {
+            break;
+        }
+
+        element = element.parentNode;
+    }
+
+    return element;
+}
+
+var attachEvents = function() {
+       var hoverDelay = $.mobile.buttonMarkup.hoverDelay, hov, foc;
+
+       $( document ).bind( {
+               "vmousedown vmousecancel vmouseup vmouseover vmouseout focus blur scrollstart": function( event ) {
+                       var theme,
+                               $btn = $( closestEnabledButton( event.target ) ),
+                               evt = event.type;
+               
+                       if ( $btn.length ) {
+                               theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
+               
+                               if ( evt === "vmousedown" ) {
+                                       if ( $.support.touch ) {
+                                               hov = setTimeout(function() {
+                                                       $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
+                                               }, hoverDelay );
+                                       } else {
+                                               $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
+                                       }
+                               } else if ( evt === "vmousecancel" || evt === "vmouseup" ) {
+                                       $btn.removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
+                               } else if ( evt === "vmouseover" || evt === "focus" ) {
+                                       if ( $.support.touch ) {
+                                               foc = setTimeout(function() {
+                                                       $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme );
+                                               }, hoverDelay );
+                                       } else {
+                                               $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme );
+                                       }
+                               } else if ( evt === "vmouseout" || evt === "blur" || evt === "scrollstart" ) {
+                                       $btn.removeClass( "ui-btn-hover-" + theme  + " ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
+                                       if ( hov ) {
+                                               clearTimeout( hov );
+                                       }
+                                       if ( foc ) {
+                                               clearTimeout( foc );
+                                       }
+                               }
+                       }
+               },
+               "focusin focus": function( event ){
+                       $( closestEnabledButton( event.target ) ).addClass( $.mobile.focusClass );
+               },
+               "focusout blur": function( event ){
+                       $( closestEnabledButton( event.target ) ).removeClass( $.mobile.focusClass );
+               }
+       });
+
+       attachEvents = null;
+};
+
+//links in bars, or those with  data-role become buttons
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+
+       $( ":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a", e.target )
+               .not( ".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')" )
+               .buttonMarkup();
+});
+
+})( jQuery );
+
+
+(function( $, undefined ) {
+
+$.widget( "mobile.collapsible", $.mobile.widget, {
+       options: {
+               expandCueText: " click to expand contents",
+               collapseCueText: " click to collapse contents",
+               collapsed: true,
+               heading: "h1,h2,h3,h4,h5,h6,legend",
+               theme: null,
+               contentTheme: null,
+               iconTheme: "d",
+               mini: false,
+               initSelector: ":jqmData(role='collapsible')"
+       },
+       _create: function() {
+
+               var $el = this.element,
+                       o = this.options,
+                       collapsible = $el.addClass( "ui-collapsible" ),
+                       collapsibleHeading = $el.children( o.heading ).first(),
+                       collapsibleContent = collapsible.wrapInner( "<div class='ui-collapsible-content'></div>" ).find( ".ui-collapsible-content" ),
+                       collapsibleSet = $el.closest( ":jqmData(role='collapsible-set')" ).addClass( "ui-collapsible-set" );
+
+               // Replace collapsibleHeading if it's a legend
+               if ( collapsibleHeading.is( "legend" ) ) {
+                       collapsibleHeading = $( "<div role='heading'>"+ collapsibleHeading.html() +"</div>" ).insertBefore( collapsibleHeading );
+                       collapsibleHeading.next().remove();
+               }
+
+               // If we are in a collapsible set
+               if ( collapsibleSet.length ) {
+                       // Inherit the theme from collapsible-set
+                       if ( !o.theme ) {
+                               o.theme = collapsibleSet.jqmData("theme") || $.mobile.getInheritedTheme( collapsibleSet, "c" );
+                       }
+                       // Inherit the content-theme from collapsible-set
+                       if ( !o.contentTheme ) {
+                               o.contentTheme = collapsibleSet.jqmData( "content-theme" );
+                       }
+
+                       // Gets the preference icon position in the set
+                       if ( !o.iconPos ) {
+                               o.iconPos = collapsibleSet.jqmData( "iconpos" );
+                       }
+
+                       if( !o.mini ) {
+                               o.mini = collapsibleSet.jqmData( "mini" );
+                       }
+               }
+               collapsibleContent.addClass( ( o.contentTheme ) ? ( "ui-body-" + o.contentTheme ) : "");
+
+               collapsibleHeading
+                       //drop heading in before content
+                       .insertBefore( collapsibleContent )
+                       //modify markup & attributes
+                       .addClass( "ui-collapsible-heading" )
+                       .append( "<span class='ui-collapsible-heading-status'></span>" )
+                       .wrapInner( "<a href='#' class='ui-collapsible-heading-toggle'></a>" )
+                       .find( "a" )
+                               .first()
+                               .buttonMarkup({
+                                       shadow: false,
+                                       corners: false,
+                                       iconpos: $el.jqmData( "iconpos" ) || o.iconPos || "left",
+                                       icon: "plus",
+                                       mini: o.mini,
+                                       theme: o.theme
+                               })
+                       .add( ".ui-btn-inner", $el )
+                               .addClass( "ui-corner-top ui-corner-bottom" );
+
+               //events
+               collapsible
+                       .bind( "expand collapse", function( event ) {
+                               if ( !event.isDefaultPrevented() ) {
+
+                                       event.preventDefault();
+
+                                       var $this = $( this ),
+                                               isCollapse = ( event.type === "collapse" ),
+                                           contentTheme = o.contentTheme;
+
+                                       collapsibleHeading
+                                               .toggleClass( "ui-collapsible-heading-collapsed", isCollapse)
+                                               .find( ".ui-collapsible-heading-status" )
+                                                       .text( isCollapse ? o.expandCueText : o.collapseCueText )
+                                               .end()
+                                               .find( ".ui-icon" )
+                                                       .toggleClass( "ui-icon-minus", !isCollapse )
+                                                       .toggleClass( "ui-icon-plus", isCollapse )
+                                               .end()
+                                               .find( "a" ).first().removeClass( $.mobile.activeBtnClass );
+
+                                       $this.toggleClass( "ui-collapsible-collapsed", isCollapse );
+                                       collapsibleContent.toggleClass( "ui-collapsible-content-collapsed", isCollapse ).attr( "aria-hidden", isCollapse );
+
+                                       if ( contentTheme && ( !collapsibleSet.length || collapsible.jqmData( "collapsible-last" ) ) ) {
+                                               collapsibleHeading
+                                                       .find( "a" ).first().add( collapsibleHeading.find( ".ui-btn-inner" ) )
+                                                       .toggleClass( "ui-corner-bottom", isCollapse );
+                                               collapsibleContent.toggleClass( "ui-corner-bottom", !isCollapse );
+                                       }
+                                       collapsibleContent.trigger( "updatelayout" );
+                               }
+                       })
+                       .trigger( o.collapsed ? "collapse" : "expand" );
+
+               collapsibleHeading
+                       .bind( "tap", function( event ) {
+                               collapsibleHeading.find( "a" ).first().addClass( $.mobile.activeBtnClass );
+                       })
+                       .bind( "click", function( event ) {
+
+                               var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ?
+                                                                               "expand" : "collapse";
+
+                               collapsible.trigger( type );
+
+                               event.preventDefault();
+                               event.stopPropagation();
+                       });
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.collapsible.prototype.enhanceWithin( e.target );
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.widget( "mobile.collapsibleset", $.mobile.widget, {
+       options: {
+               initSelector: ":jqmData(role='collapsible-set')"
+       },
+       _create: function() {
+               var $el = this.element.addClass( "ui-collapsible-set" ),
+                       o = this.options;
+
+               // Inherit the theme from collapsible-set
+               if ( !o.theme ) {
+                       o.theme = $.mobile.getInheritedTheme( $el, "c" );
+               }
+               // Inherit the content-theme from collapsible-set
+               if ( !o.contentTheme ) {
+                       o.contentTheme = $el.jqmData( "content-theme" );
+               }
+
+               if ( !o.corners ) {
+                       o.corners = $el.jqmData( "corners" ) === undefined ? true : false;
+               }
+
+               // Initialize the collapsible set if it's not already initialized
+               if ( !$el.jqmData( "collapsiblebound" ) ) {
+                       $el
+                               .jqmData( "collapsiblebound", true )
+                               .bind( "expand collapse", function( event ) {
+                                       var isCollapse = ( event.type === "collapse" ),
+                                               collapsible = $( event.target ).closest( ".ui-collapsible" ),
+                                               widget = collapsible.data( "collapsible" ),
+                                           contentTheme = widget.options.contentTheme;
+                                       if ( contentTheme && collapsible.jqmData( "collapsible-last" ) ) {
+                                               collapsible.find( widget.options.heading ).first()
+                                                       .find( "a" ).first()
+                                                       .toggleClass( "ui-corner-bottom", isCollapse )
+                                                       .find( ".ui-btn-inner" )
+                                                       .toggleClass( "ui-corner-bottom", isCollapse );
+                                               collapsible.find( ".ui-collapsible-content" ).toggleClass( "ui-corner-bottom", !isCollapse );
+                                       }
+                               })
+                               .bind( "expand", function( event ) {
+                                       $( event.target )
+                                               .closest( ".ui-collapsible" )
+                                               .siblings( ".ui-collapsible" )
+                                               .trigger( "collapse" );
+                               });
+               }
+       },
+
+       _init: function() {
+               this.refresh();
+       },
+
+       refresh: function() {
+               var $el = this.element,
+                       o = this.options,
+                       collapsiblesInSet = $el.children( ":jqmData(role='collapsible')" );
+
+               $.mobile.collapsible.prototype.enhance( collapsiblesInSet.not( ".ui-collapsible" ) );
+
+               // clean up borders
+               collapsiblesInSet.each( function() {
+                       $( this ).find( $.mobile.collapsible.prototype.options.heading )
+                               .find( "a" ).first()
+                               .removeClass( "ui-corner-top ui-corner-bottom" )
+                               .find( ".ui-btn-inner" )
+                               .removeClass( "ui-corner-top ui-corner-bottom" );
+               });
+
+               collapsiblesInSet.first()
+                       .find( "a" )
+                               .first()
+                               .addClass( o.corners ? "ui-corner-top" : "" )
+                               .find( ".ui-btn-inner" )
+                                       .addClass( "ui-corner-top" );
+
+               collapsiblesInSet.last()
+                       .jqmData( "collapsible-last", true )
+                       .find( "a" )
+                               .first()
+                               .addClass( o.corners ? "ui-corner-bottom" : "" )
+                               .find( ".ui-btn-inner" )
+                                       .addClass( "ui-corner-bottom" );
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.collapsibleset.prototype.enhanceWithin( e.target );
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.widget( "mobile.navbar", $.mobile.widget, {
+       options: {
+               iconpos: "top",
+               grid: null,
+               initSelector: ":jqmData(role='navbar')"
+       },
+
+       _create: function(){
+
+               var $navbar = this.element,
+                       $navbtns = $navbar.find( "a" ),
+                       iconpos = $navbtns.filter( ":jqmData(icon)" ).length ?
+                                                                       this.options.iconpos : undefined;
+
+               $navbar.addClass( "ui-navbar ui-mini" )
+                       .attr( "role","navigation" )
+                       .find( "ul" )
+                       .jqmEnhanceable()
+                       .grid({ grid: this.options.grid });
+
+               $navbtns.buttonMarkup({
+                       corners:        false,
+                       shadow:         false,
+                       inline:     true,
+                       iconpos:        iconpos
+               });
+
+               $navbar.delegate( "a", "vclick", function( event ) {
+                       if( !$(event.target).hasClass("ui-disabled") ) {
+                               $navbtns.removeClass( $.mobile.activeBtnClass );
+                               $( this ).addClass( $.mobile.activeBtnClass );
+                       }
+               });
+
+               // Buttons in the navbar with ui-state-persist class should regain their active state before page show
+               $navbar.closest( ".ui-page" ).bind( "pagebeforeshow", function() {
+                       $navbtns.filter( ".ui-state-persist" ).addClass( $.mobile.activeBtnClass );
+               });
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.navbar.prototype.enhanceWithin( e.target );
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+//Keeps track of the number of lists per page UID
+//This allows support for multiple nested list in the same page
+//https://github.com/jquery/jquery-mobile/issues/1617
+var listCountPerPage = {};
+
+$.widget( "mobile.listview", $.mobile.widget, {
+
+       options: {
+               theme: null,
+               countTheme: "c",
+               headerTheme: "b",
+               dividerTheme: "b",
+               splitIcon: "arrow-r",
+               splitTheme: "b",
+               inset: false,
+               initSelector: ":jqmData(role='listview')"
+       },
+
+       _create: function() {
+               var t = this,
+                       listviewClasses = "";
+                       
+               listviewClasses += t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "";
+
+               // create listview markup
+               t.element.addClass(function( i, orig ) {
+                       return orig + " ui-listview " + listviewClasses;
+               });
+
+               t.refresh( true );
+       },
+
+       _removeCorners: function( li, which ) {
+               var top = "ui-corner-top ui-corner-tr ui-corner-tl",
+                       bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
+
+               li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
+
+               if ( which === "top" ) {
+                       li.removeClass( top );
+               } else if ( which === "bottom" ) {
+                       li.removeClass( bot );
+               } else {
+                       li.removeClass( top + " " + bot );
+               }
+       },
+
+       _refreshCorners: function( create ) {
+               var $li,
+                       $visibleli,
+                       $topli,
+                       $bottomli;
+
+               if ( this.options.inset ) {
+                       $li = this.element.children( "li" );
+                       // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
+                       $visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
+
+                       this._removeCorners( $li );
+
+                       // Select the first visible li element
+                       $topli = $visibleli.first()
+                               .addClass( "ui-corner-top" );
+
+                       $topli.add( $topli.find( ".ui-btn-inner" )
+                                       .not( ".ui-li-link-alt span:first-child" ) )
+                                .addClass( "ui-corner-top" )
+                                .end()
+                               .find( ".ui-li-link-alt, .ui-li-link-alt span:first-child" )
+                                       .addClass( "ui-corner-tr" )
+                               .end()
+                               .find( ".ui-li-thumb" )
+                                       .not(".ui-li-icon")
+                                       .addClass( "ui-corner-tl" );
+
+                       // Select the last visible li element
+                       $bottomli = $visibleli.last()
+                               .addClass( "ui-corner-bottom" );
+
+                       $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
+                               .find( ".ui-li-link-alt" )
+                                       .addClass( "ui-corner-br" )
+                               .end()
+                               .find( ".ui-li-thumb" )
+                                       .not(".ui-li-icon")
+                                       .addClass( "ui-corner-bl" );
+               }
+               if ( !create ) {
+                       this.element.trigger( "updatelayout" );
+               }
+       },
+
+       // This is a generic utility method for finding the first
+       // node with a given nodeName. It uses basic DOM traversal
+       // to be fast and is meant to be a substitute for simple
+       // $.fn.closest() and $.fn.children() calls on a single
+       // element. Note that callers must pass both the lowerCase
+       // and upperCase version of the nodeName they are looking for.
+       // The main reason for this is that this function will be
+       // called many times and we want to avoid having to lowercase
+       // the nodeName from the element every time to ensure we have
+       // a match. Note that this function lives here for now, but may
+       // be moved into $.mobile if other components need a similar method.
+       _findFirstElementByTagName: function( ele, nextProp, lcName, ucName )
+       {
+               var dict = {};
+               dict[ lcName ] = dict[ ucName ] = true;
+               while ( ele ) {
+                       if ( dict[ ele.nodeName ] ) {
+                               return ele;
+                       }
+                       ele = ele[ nextProp ];
+               }
+               return null;
+       },
+       _getChildrenByTagName: function( ele, lcName, ucName )
+       {
+               var results = [],
+                       dict = {};
+               dict[ lcName ] = dict[ ucName ] = true;
+               ele = ele.firstChild;
+               while ( ele ) {
+                       if ( dict[ ele.nodeName ] ) {
+                               results.push( ele );
+                       }
+                       ele = ele.nextSibling;
+               }
+               return $( results );
+       },
+
+       _addThumbClasses: function( containers )
+       {
+               var i, img, len = containers.length;
+               for ( i = 0; i < len; i++ ) {
+                       img = $( this._findFirstElementByTagName( containers[ i ].firstChild, "nextSibling", "img", "IMG" ) );
+                       if ( img.length ) {
+                               img.addClass( "ui-li-thumb" );
+                               $( this._findFirstElementByTagName( img[ 0 ].parentNode, "parentNode", "li", "LI" ) ).addClass( img.is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
+                       }
+               }
+       },
+
+       refresh: function( create ) {
+               this.parentPage = this.element.closest( ".ui-page" );
+               this._createSubPages();
+
+               var o = this.options,
+                       $list = this.element,
+                       self = this,
+                       dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
+                       listsplittheme = $list.jqmData( "splittheme" ),
+                       listspliticon = $list.jqmData( "spliticon" ),
+                       li = this._getChildrenByTagName( $list[ 0 ], "li", "LI" ),
+                       counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
+                       itemClassDict = {},
+                       item, itemClass, itemTheme,
+                       a, last, splittheme, countParent, icon, imgParents, img, linkIcon;
+
+               if ( counter ) {
+                       $list.find( ".ui-li-dec" ).remove();
+               }
+
+               if ( !o.theme ) {
+                       o.theme = $.mobile.getInheritedTheme( this.element, "c" );
+               }
+
+               for ( var pos = 0, numli = li.length; pos < numli; pos++ ) {
+                       item = li.eq( pos );
+                       itemClass = "ui-li";
+
+                       // If we're creating the element, we update it regardless
+                       if ( create || !item.hasClass( "ui-li" ) ) {
+                               itemTheme = item.jqmData("theme") || o.theme;
+                               a = this._getChildrenByTagName( item[ 0 ], "a", "A" );
+                               var isDivider = ( item.jqmData( "role" ) === "list-divider" );
+
+                               if ( a.length && !isDivider ) {
+                                       icon = item.jqmData("icon");
+
+                                       item.buttonMarkup({
+                                               wrapperEls: "div",
+                                               shadow: false,
+                                               corners: false,
+                                               iconpos: "right",
+                                               icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
+                                               theme: itemTheme
+                                       });
+
+                                       if ( ( icon != false ) && ( a.length == 1 ) ) {
+                                               item.addClass( "ui-li-has-arrow" );
+                                       }
+
+                                       a.first().removeClass( "ui-link" ).addClass( "ui-link-inherit" );
+
+                                       if ( a.length > 1 ) {
+                                               itemClass += " ui-li-has-alt";
+
+                                               last = a.last();
+                                               splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
+                                               linkIcon = last.jqmData("icon");
+
+                                               last.appendTo(item)
+                                                       .attr( "title", last.getEncodedText() )
+                                                       .addClass( "ui-li-link-alt" )
+                                                       .empty()
+                                                       .buttonMarkup({
+                                                               shadow: false,
+                                                               corners: false,
+                                                               theme: itemTheme,
+                                                               icon: false,
+                                                               iconpos: "notext"
+                                                       })
+                                                       .find( ".ui-btn-inner" )
+                                                               .append(
+                                                                       $( document.createElement( "span" ) ).buttonMarkup({
+                                                                               shadow: true,
+                                                                               corners: true,
+                                                                               theme: splittheme,
+                                                                               iconpos: "notext",
+                                                                               // link icon overrides list item icon overrides ul element overrides options
+                                                                               icon: linkIcon || icon || listspliticon || o.splitIcon
+                                                                       })
+                                                               );
+                                       }
+                               } else if ( isDivider ) {
+
+                                       itemClass += " ui-li-divider ui-bar-" + dividertheme;
+                                       item.attr( "role", "heading" );
+
+                                       //reset counter when a divider heading is encountered
+                                       if ( counter ) {
+                                               counter = 1;
+                                       }
+
+                               } else {
+                                       itemClass += " ui-li-static ui-body-" + itemTheme;
+                               }
+                       }
+
+                       if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
+                               countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
+
+                               countParent.addClass( "ui-li-jsnumbering" )
+                                       .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
+                       }
+
+                       // Instead of setting item class directly on the list item and its
+                       // btn-inner at this point in time, push the item into a dictionary
+                       // that tells us what class to set on it so we can do this after this
+                       // processing loop is finished.
+
+                       if ( !itemClassDict[ itemClass ] ) {
+                               itemClassDict[ itemClass ] = [];
+                       }
+
+                       itemClassDict[ itemClass ].push( item[ 0 ] );
+               }
+
+               // Set the appropriate listview item classes on each list item
+               // and their btn-inner elements. The main reason we didn't do this
+               // in the for-loop above is because we can eliminate per-item function overhead
+               // by calling addClass() and children() once or twice afterwards. This
+               // can give us a significant boost on platforms like WP7.5.
+
+               for ( itemClass in itemClassDict ) {
+                       $( itemClassDict[ itemClass ] ).addClass( itemClass ).children( ".ui-btn-inner" ).addClass( itemClass );
+               }
+
+               $list.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" )
+                       .end()
+
+                       .find( "p, dl" ).addClass( "ui-li-desc" )
+                       .end()
+
+                       .find( ".ui-li-aside" ).each(function() {
+                                       var $this = $(this);
+                                       $this.prependTo( $this.parent() ); //shift aside to front for css float
+                               })
+                       .end()
+
+                       .find( ".ui-li-count" ).each( function() {
+                                       $( this ).closest( "li" ).addClass( "ui-li-has-count" );
+                               }).addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme) + " ui-btn-corner-all" );
+
+               // The idea here is to look at the first image in the list item
+               // itself, and any .ui-link-inherit element it may contain, so we
+               // can place the appropriate classes on the image and list item.
+               // Note that we used to use something like:
+               //
+               //    li.find(">img:eq(0), .ui-link-inherit>img:eq(0)").each( ... );
+               //
+               // But executing a find() like that on Windows Phone 7.5 took a
+               // really long time. Walking things manually with the code below
+               // allows the 400 listview item page to load in about 3 seconds as
+               // opposed to 30 seconds.
+
+               this._addThumbClasses( li );
+               this._addThumbClasses( $list.find( ".ui-link-inherit" ) );
+
+               this._refreshCorners( create );
+       },
+
+       //create a string for ID/subpage url creation
+       _idStringEscape: function( str ) {
+               return str.replace(/[^a-zA-Z0-9]/g, '-');
+       },
+
+       _createSubPages: function() {
+               var parentList = this.element,
+                       parentPage = parentList.closest( ".ui-page" ),
+                       parentUrl = parentPage.jqmData( "url" ),
+                       parentId = parentUrl || parentPage[ 0 ][ $.expando ],
+                       parentListId = parentList.attr( "id" ),
+                       o = this.options,
+                       dns = "data-" + $.mobile.ns,
+                       self = this,
+                       persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
+                       hasSubPages;
+
+               if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
+                       listCountPerPage[ parentId ] = -1;
+               }
+
+               parentListId = parentListId || ++listCountPerPage[ parentId ];
+
+               $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
+                       var self = this,
+                               list = $( this ),
+                               listId = list.attr( "id" ) || parentListId + "-" + i,
+                               parent = list.parent(),
+                               nodeEls = $( list.prevAll().toArray().reverse() ),
+                               nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" ),
+                               title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
+                               id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
+                               theme = list.jqmData( "theme" ) || o.theme,
+                               countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
+                               newPage, anchor;
+
+                       //define hasSubPages for use in later removal
+                       hasSubPages = true;
+
+                       newPage = list.detach()
+                                               .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
+                                               .parent()
+                                                       .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
+                                                       .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='"+ persistentFooterID +"'>") : "" )
+                                                       .parent()
+                                                               .appendTo( $.mobile.pageContainer );
+
+                       newPage.page();
+
+                       anchor = parent.find('a:first');
+
+                       if ( !anchor.length ) {
+                               anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
+                       }
+
+                       anchor.attr( "href", "#" + id );
+
+               }).listview();
+
+               // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
+               // and aren't embedded
+               if( hasSubPages &&
+                       parentPage.is( ":jqmData(external-page='true')" ) &&
+                       parentPage.data("page").options.domCache === false ) {
+
+                       var newRemove = function( e, ui ){
+                               var nextPage = ui.nextPage, npURL,
+                                       prEvent = new $.Event( "pageremove" );
+
+                               if( ui.nextPage ){
+                                       npURL = nextPage.jqmData( "url" );
+                                       if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){
+                                               self.childPages().remove();
+                                               parentPage.trigger( prEvent );
+                                               if( !prEvent.isDefaultPrevented() ){
+                                                       parentPage.removeWithDependents();
+                                               }
+                                       }
+                               }
+                       };
+
+                       // unbind the original page remove and replace with our specialized version
+                       parentPage
+                               .unbind( "pagehide.remove" )
+                               .bind( "pagehide.remove", newRemove);
+               }
+       },
+
+       // TODO sort out a better way to track sub pages of the listview this is brittle
+       childPages: function(){
+               var parentUrl = this.parentPage.jqmData( "url" );
+
+               return $( ":jqmData(url^='"+  parentUrl + "&" + $.mobile.subPageUrlKey +"')");
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.listview.prototype.enhanceWithin( e.target );
+});
+
+})( jQuery );
+
+/*
+* "checkboxradio" plugin
+*/
+
+(function( $, undefined ) {
+
+$.widget( "mobile.checkboxradio", $.mobile.widget, {
+       options: {
+               theme: null,
+               initSelector: "input[type='checkbox'],input[type='radio']"
+       },
+       _create: function() {
+               var self = this,
+                       input = this.element,
+                       inheritAttr = function( input, dataAttr ) {
+                               return input.jqmData( dataAttr ) || input.closest( "form,fieldset" ).jqmData( dataAttr )
+                       },
+                       // NOTE: Windows Phone could not find the label through a selector
+                       // filter works though.
+                       parentLabel = $( input ).closest( "label" ),
+                       label = parentLabel.length ? parentLabel : $( input ).closest( "form,fieldset,:jqmData(role='page'),:jqmData(role='dialog')" ).find( "label" ).filter( "[for='" + input[0].id + "']" ),
+                       inputtype = input[0].type,
+                       mini = inheritAttr( input, "mini" ),
+                       checkedState = inputtype + "-on",
+                       uncheckedState = inputtype + "-off",
+                       icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState,
+                       iconpos = inheritAttr( input, "iconpos" ),
+                       activeBtn = icon ? "" : " " + $.mobile.activeBtnClass,
+                       checkedClass = "ui-" + checkedState + activeBtn,
+                       uncheckedClass = "ui-" + uncheckedState,
+                       checkedicon = "ui-icon-" + checkedState,
+                       uncheckedicon = "ui-icon-" + uncheckedState;
+
+               if ( inputtype !== "checkbox" && inputtype !== "radio" ) {
+                       return;
+               }
+
+               // Expose for other methods
+               $.extend( this, {
+                       label: label,
+                       inputtype: inputtype,
+                       checkedClass: checkedClass,
+                       uncheckedClass: uncheckedClass,
+                       checkedicon: checkedicon,
+                       uncheckedicon: uncheckedicon
+               });
+
+               // If there's no selected theme check the data attr
+               if( !this.options.theme ) {
+                       this.options.theme = $.mobile.getInheritedTheme( this.element, "c" );
+               }
+
+               label.buttonMarkup({
+                       theme: this.options.theme,
+                       icon: icon,
+                       shadow: false,
+                       mini: mini,
+                       iconpos: iconpos
+               });
+
+               // Wrap the input + label in a div
+               var wrapper = document.createElement('div');
+               wrapper.className = 'ui-' + inputtype;
+
+               input.add( label ).wrapAll( wrapper );
+
+               label.bind({
+                       vmouseover: function( event ) {
+                               if ( $( this ).parent().is( ".ui-disabled" ) ) {
+                                       event.stopPropagation();
+                               }
+                       },
+
+                       vclick: function( event ) {
+                               if ( input.is( ":disabled" ) ) {
+                                       event.preventDefault();
+                                       return;
+                               }
+
+                               self._cacheVals();
+
+                               input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) );
+
+                               // trigger click handler's bound directly to the input as a substitute for
+                               // how label clicks behave normally in the browsers
+                               // TODO: it would be nice to let the browser's handle the clicks and pass them
+                               //       through to the associate input. we can swallow that click at the parent
+                               //       wrapper element level
+                               input.triggerHandler( 'click' );
+
+                               // Input set for common radio buttons will contain all the radio
+                               // buttons, but will not for checkboxes. clearing the checked status
+                               // of other radios ensures the active button state is applied properly
+                               self._getInputSet().not( input ).prop( "checked", false );
+
+                               self._updateAll();
+                               return false;
+                       }
+               });
+
+               input
+                       .bind({
+                               vmousedown: function() {
+                                       self._cacheVals();
+                               },
+
+                               vclick: function() {
+                                       var $this = $(this);
+
+                                       // Adds checked attribute to checked input when keyboard is used
+                                       if ( $this.is( ":checked" ) ) {
+
+                                               $this.prop( "checked", true);
+                                               self._getInputSet().not($this).prop( "checked", false );
+                                       } else {
+
+                                               $this.prop( "checked", false );
+                                       }
+
+                                       self._updateAll();
+                               },
+
+                               focus: function() {
+                                       label.addClass( $.mobile.focusClass );
+                               },
+
+                               blur: function() {
+                                       label.removeClass( $.mobile.focusClass );
+                               }
+                       });
+
+               this.refresh();
+       },
+
+       _cacheVals: function() {
+               this._getInputSet().each(function() {
+                       $(this).jqmData( "cacheVal", this.checked );
+               });
+       },
+
+       //returns either a set of radios with the same name attribute, or a single checkbox
+       _getInputSet: function(){
+               if(this.inputtype === "checkbox") {
+                       return this.element;
+               }
+
+               return this.element.closest( "form,fieldset,:jqmData(role='page')" )
+                       .find( "input[name='"+ this.element[0].name +"'][type='"+ this.inputtype +"']" );
+       },
+
+       _updateAll: function() {
+               var self = this;
+
+               this._getInputSet().each(function() {
+                       var $this = $(this);
+
+                       if ( this.checked || self.inputtype === "checkbox" ) {
+                               $this.trigger( "change" );
+                       }
+               })
+               .checkboxradio( "refresh" );
+       },
+
+       refresh: function() {
+               var input = this.element[0],
+                       label = this.label,
+                       icon = label.find( ".ui-icon" );
+
+               if ( input.checked ) {
+                       label.addClass( this.checkedClass ).removeClass( this.uncheckedClass );
+                       icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon );
+               } else {
+                       label.removeClass( this.checkedClass ).addClass( this.uncheckedClass );
+                       icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon );
+               }
+
+               if ( input.disabled ) {
+                       this.disable();
+               } else {
+                       this.enable();
+               }
+       },
+
+       disable: function() {
+               this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" );
+       },
+
+       enable: function() {
+               this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" );
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.checkboxradio.prototype.enhanceWithin( e.target, true );
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.widget( "mobile.button", $.mobile.widget, {
+       options: {
+               theme: null,
+               icon: null,
+               iconpos: null,
+               inline: false,
+               corners: true,
+               shadow: true,
+               iconshadow: true,
+               initSelector: "button, [type='button'], [type='submit'], [type='reset'], [type='image']",
+               mini: false
+       },
+       _create: function() {
+               var $el = this.element,
+                       $button,
+                       o = this.options,
+                       type,
+                       name,
+                       classes = "",
+                       $buttonPlaceholder;
+
+               // if this is a link, check if it's been enhanced and, if not, use the right function
+               if( $el[ 0 ].tagName === "A" ) {
+                       !$el.hasClass( "ui-btn" ) && $el.buttonMarkup();
+                       return;
+               }
+
+               // get the inherited theme
+               // TODO centralize for all widgets
+               if ( !this.options.theme ) {
+                       this.options.theme = $.mobile.getInheritedTheme( this.element, "c" );
+               }
+
+               // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577
+               /* if( $el[0].className.length ) {
+                       classes = $el[0].className;
+               } */
+               if( !!~$el[0].className.indexOf( "ui-btn-left" ) ) {
+                       classes = "ui-btn-left";
+               }
+
+               if(  !!~$el[0].className.indexOf( "ui-btn-right" ) ) {
+                       classes = "ui-btn-right";
+               }
+
+               if(  $el.attr( "type" ) === "submit" || $el.attr( "type" ) === "reset" ) {
+                       classes ? classes += " ui-submit" :  classes = "ui-submit";
+               }
+               
+               $( "label[for='" + $el.attr( "id" ) + "']" ).addClass( "ui-submit" );
+
+               // Add ARIA role
+               this.button = $( "<div></div>" )
+                       .text( $el.text() || $el.val() )
+                       .insertBefore( $el )
+                       .buttonMarkup({
+                               theme: o.theme,
+                               icon: o.icon,
+                               iconpos: o.iconpos,
+                               inline: o.inline,
+                               corners: o.corners,
+                               shadow: o.shadow,
+                               iconshadow: o.iconshadow,
+                               mini: o.mini
+                       })
+                       .addClass( classes )
+                       .append( $el.addClass( "ui-btn-hidden" ) );
+
+        $button = this.button;
+               type = $el.attr( "type" );
+               name = $el.attr( "name" );
+
+               // Add hidden input during submit if input type="submit" has a name.
+               if ( type !== "button" && type !== "reset" && name ) {
+                               $el.bind( "vclick", function() {
+                                       // Add hidden input if it doesn't already exist.
+                                       if( $buttonPlaceholder === undefined ) {
+                                               $buttonPlaceholder = $( "<input>", {
+                                                       type: "hidden",
+                                                       name: $el.attr( "name" ),
+                                                       value: $el.attr( "value" )
+                                               }).insertBefore( $el );
+
+                                               // Bind to doc to remove after submit handling
+                                               $( document ).one("submit", function(){
+                                                       $buttonPlaceholder.remove();
+
+                                                       // reset the local var so that the hidden input
+                                                       // will be re-added on subsequent clicks
+                                                       $buttonPlaceholder = undefined;
+                                               });
+                                       }
+                               });
+               }
+
+        $el.bind({
+            focus: function() {
+                $button.addClass( $.mobile.focusClass );
+            },
+
+            blur: function() {
+                $button.removeClass( $.mobile.focusClass );
+            }
+        });
+
+               this.refresh();
+       },
+
+       enable: function() {
+               this.element.attr( "disabled", false );
+               this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
+               return this._setOption( "disabled", false );
+       },
+
+       disable: function() {
+               this.element.attr( "disabled", true );
+               this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true );
+               return this._setOption( "disabled", true );
+       },
+
+       refresh: function() {
+               var $el = this.element;
+
+               if ( $el.prop("disabled") ) {
+                       this.disable();
+               } else {
+                       this.enable();
+               }
+
+               // Grab the button's text element from its implementation-independent data item
+               $( this.button.data( 'buttonElements' ).text ).text( $el.text() || $el.val() );
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.button.prototype.enhanceWithin( e.target, true );
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.fn.controlgroup = function( options ) {
+       function flipClasses( els, flCorners  ) {
+               els.removeClass( "ui-btn-corner-all ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-controlgroup-last ui-shadow" )
+                       .eq( 0 ).addClass( flCorners[ 0 ] )
+                       .end()
+                       .last().addClass( flCorners[ 1 ] ).addClass( "ui-controlgroup-last" );
+       }
+
+       return this.each(function() {
+               var $el = $( this ),
+                       o = $.extend({
+                                               direction: $el.jqmData( "type" ) || "vertical",
+                                               shadow: false,
+                                               excludeInvisible: true,
+                                               mini: $el.jqmData( "mini" )
+                                       }, options ),
+                       groupheading = $el.children( "legend" ),
+                       flCorners = o.direction == "horizontal" ? [ "ui-corner-left", "ui-corner-right" ] : [ "ui-corner-top", "ui-corner-bottom" ],
+                       type = $el.find( "input" ).first().attr( "type" );
+                       
+               $el.wrapInner( "<div class='ui-controlgroup-controls'></div>" );
+
+               // Replace legend with more stylable replacement div
+               if ( groupheading.length ) {
+                       $( "<div role='heading' class='ui-controlgroup-label'>" + groupheading.html() + "</div>" ).insertBefore( $el.children(0) );
+                       groupheading.remove();
+               }
+
+               $el.addClass( "ui-corner-all ui-controlgroup ui-controlgroup-" + o.direction );
+
+               flipClasses( $el.find( ".ui-btn" + ( o.excludeInvisible ? ":visible" : "" ) ).not('.ui-slider-handle'), flCorners );
+               flipClasses( $el.find( ".ui-btn-inner" ), flCorners );
+
+               if ( o.shadow ) {
+                       $el.addClass( "ui-shadow" );
+               }
+
+               if ( o.mini ) {
+                       $el.addClass( "ui-mini" );
+               }
+
+       });
+};
+
+// The pagecreate handler for controlgroup is in jquery.mobile.init because of the soft-dependency on the wrapped widgets
+
+})(jQuery);
+
+(function( $, undefined ) {
+
+$( document ).bind( "pagecreate create", function( e ){
+
+       //links within content areas, tests included with page
+       $( e.target )
+               .find( "a" )
+               .jqmEnhanceable()
+               .not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
+               .addClass( "ui-link" );
+
+});
+
+})( jQuery );
+
+
+( function( $ ) {
+       var     meta = $( "meta[name=viewport]" ),
+        initialContent = meta.attr( "content" ),
+        disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no",
+        enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes",
+               disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test( initialContent );
+       
+       $.mobile.zoom = $.extend( {}, {
+               enabled: !disabledInitially,
+               locked: false,
+               disable: function( lock ) {
+                       if( !disabledInitially && !$.mobile.zoom.locked ){
+                       meta.attr( "content", disabledZoom );
+                       $.mobile.zoom.enabled = false;
+                               $.mobile.zoom.locked = lock || false;
+                       }
+               },
+               enable: function( unlock ) {
+                       if( !disabledInitially && ( !$.mobile.zoom.locked || unlock === true ) ){
+                       meta.attr( "content", enabledZoom );
+                       $.mobile.zoom.enabled = true;
+                               $.mobile.zoom.locked = false;
+                       }
+               },
+               restore: function() {
+                       if( !disabledInitially ){
+                       meta.attr( "content", initialContent );
+                       $.mobile.zoom.enabled = true;
+                       }
+               }
+       });
+
+}( jQuery ));
+
+(function( $, undefined ) {
+
+$.widget( "mobile.textinput", $.mobile.widget, {
+       options: {
+               theme: null,
+               // This option defaults to true on iOS devices.
+               preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1,
+               initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])",
+               clearSearchButtonText: "clear text"
+       },
+
+       _create: function() {
+
+               var input = this.element,
+                       o = this.options,
+                       theme = o.theme || $.mobile.getInheritedTheme( this.element, "c" ),
+                       themeclass  = " ui-body-" + theme,
+                       mini = input.jqmData("mini") == true,
+                       miniclass = mini ? " ui-mini" : "",
+                       focusedEl, clearbtn;
+
+               $( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" );
+
+               focusedEl = input.addClass("ui-input-text ui-body-"+ theme );
+
+               // XXX: Temporary workaround for issue 785 (Apple bug 8910589).
+               //      Turn off autocorrect and autocomplete on non-iOS 5 devices
+               //      since the popup they use can't be dismissed by the user. Note
+               //      that we test for the presence of the feature by looking for
+               //      the autocorrect property on the input element. We currently
+               //      have no test for iOS 5 or newer so we're temporarily using
+               //      the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - jblas
+               if ( typeof input[0].autocorrect !== "undefined" && !$.support.touchOverflow ) {
+                       // Set the attribute instead of the property just in case there
+                       // is code that attempts to make modifications via HTML.
+                       input[0].setAttribute( "autocorrect", "off" );
+                       input[0].setAttribute( "autocomplete", "off" );
+               }
+
+
+               //"search" input widget
+               if ( input.is( "[type='search'],:jqmData(type='search')" ) ) {
+
+                       focusedEl = input.wrap( "<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield" + themeclass + miniclass + "'></div>" ).parent();
+                       clearbtn = $( "<a href='#' class='ui-input-clear' title='" + o.clearSearchButtonText + "'>" + o.clearSearchButtonText + "</a>" )
+                               .bind('click', function( event ) {
+                                       input
+                                               .val( "" )
+                                               .focus()
+                                               .trigger( "change" );
+                                       clearbtn.addClass( "ui-input-clear-hidden" );
+                                       event.preventDefault();
+                               })
+                               .appendTo( focusedEl )
+                               .buttonMarkup({
+                                       icon: "delete",
+                                       iconpos: "notext",
+                                       corners: true,
+                                       shadow: true,
+                                       mini: mini
+                               });
+
+                       function toggleClear() {
+                               setTimeout(function() {
+                                       clearbtn.toggleClass( "ui-input-clear-hidden", !input.val() );
+                               }, 0);
+                       }
+
+                       toggleClear();
+
+                       input.bind('paste cut keyup focus change blur', toggleClear);
+
+               } else {
+                       input.addClass( "ui-corner-all ui-shadow-inset" + themeclass + miniclass );
+               }
+
+               input.focus(function() {
+                               focusedEl.addClass( $.mobile.focusClass );
+                       })
+                       .blur(function(){
+                               focusedEl.removeClass( $.mobile.focusClass );
+                       })
+                       // In many situations, iOS will zoom into the select upon tap, this prevents that from happening
+                       .bind( "focus", function() {
+                               if( o.preventFocusZoom ){
+                                       $.mobile.zoom.disable( true );
+                               }
+                       })
+                       .bind( "blur", function() {
+                               if( o.preventFocusZoom ){
+                                       $.mobile.zoom.enable( true );
+                               }
+                       });
+
+               // Autogrow
+               if ( input.is( "textarea" ) ) {
+                       var extraLineHeight = 15,
+                               keyupTimeoutBuffer = 100,
+                               keyup = function() {
+                                       var scrollHeight = input[ 0 ].scrollHeight,
+                                               clientHeight = input[ 0 ].clientHeight;
+
+                                       if ( clientHeight < scrollHeight ) {
+                                               input.height(scrollHeight + extraLineHeight);
+                                       }
+                               },
+                               keyupTimeout;
+
+                       input.keyup(function() {
+                               clearTimeout( keyupTimeout );
+                               keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer );
+                       });
+
+                       // binding to pagechange here ensures that for pages loaded via
+                       // ajax the height is recalculated without user input
+                       $( document ).one( "pagechange", keyup );
+
+                       // Issue 509: the browser is not providing scrollHeight properly until the styles load
+                       if ( $.trim( input.val() ) ) {
+                               // bind to the window load to make sure the height is calculated based on BOTH
+                               // the DOM and CSS
+                               $( window ).load( keyup );
+                       }
+               }
+       },
+
+       disable: function(){
+               ( this.element.attr( "disabled", true ).is( "[type='search'],:jqmData(type='search')" ) ?
+                       this.element.parent() : this.element ).addClass( "ui-disabled" );
+       },
+
+       enable: function(){
+               ( this.element.attr( "disabled", false).is( "[type='search'],:jqmData(type='search')" ) ?
+                       this.element.parent() : this.element ).removeClass( "ui-disabled" );
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.textinput.prototype.enhanceWithin( e.target, true );
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.mobile.listview.prototype.options.filter = false;
+$.mobile.listview.prototype.options.filterPlaceholder = "Filter items...";
+$.mobile.listview.prototype.options.filterTheme = "c";
+$.mobile.listview.prototype.options.filterCallback = function( text, searchValue ){
+       return text.toLowerCase().indexOf( searchValue ) === -1;
+};
+
+$( document ).delegate( ":jqmData(role='listview')", "listviewcreate", function() {
+
+       var list = $( this ),
+               listview = list.data( "listview" );
+
+       if ( !listview.options.filter ) {
+               return;
+       }
+
+       var wrapper = $( "<form>", {
+                       "class": "ui-listview-filter ui-bar-" + listview.options.filterTheme,
+                       "role": "search"
+               }),
+               search = $( "<input>", {
+                       placeholder: listview.options.filterPlaceholder
+               })
+               .attr( "data-" + $.mobile.ns + "type", "search" )
+               .jqmData( "lastval", "" )
+               .bind( "keyup change", function() {
+
+                       var $this = $(this),
+                               val = this.value.toLowerCase(),
+                               listItems = null,
+                               lastval = $this.jqmData( "lastval" ) + "",
+                               childItems = false,
+                               itemtext = "",
+                               item;
+
+                       // Change val as lastval for next execution
+                       $this.jqmData( "lastval" , val );
+                       if ( val.length < lastval.length || val.indexOf(lastval) !== 0 ) {
+
+                               // Removed chars or pasted something totally different, check all items
+                               listItems = list.children();
+                       } else {
+
+                               // Only chars added, not removed, only use visible subset
+                               listItems = list.children( ":not(.ui-screen-hidden)" );
+                       }
+
+                       if ( val ) {
+
+                               // This handles hiding regular rows without the text we search for
+                               // and any list dividers without regular rows shown under it
+
+                               for ( var i = listItems.length - 1; i >= 0; i-- ) {
+                                       item = $( listItems[ i ] );
+                                       itemtext = item.jqmData( "filtertext" ) || item.text();
+
+                                       if ( item.is( "li:jqmData(role=list-divider)" ) ) {
+
+                                               item.toggleClass( "ui-filter-hidequeue" , !childItems );
+
+                                               // New bucket!
+                                               childItems = false;
+
+                                       } else if ( listview.options.filterCallback( itemtext, val ) ) {
+
+                                               //mark to be hidden
+                                               item.toggleClass( "ui-filter-hidequeue" , true );
+                                       } else {
+
+                                               // There's a shown item in the bucket
+                                               childItems = true;
+                                       }
+                               }
+
+                               // Show items, not marked to be hidden
+                               listItems
+                                       .filter( ":not(.ui-filter-hidequeue)" )
+                                       .toggleClass( "ui-screen-hidden", false );
+
+                               // Hide items, marked to be hidden
+                               listItems
+                                       .filter( ".ui-filter-hidequeue" )
+                                       .toggleClass( "ui-screen-hidden", true )
+                                       .toggleClass( "ui-filter-hidequeue", false );
+
+                       } else {
+
+                               //filtervalue is empty => show all
+                               listItems.toggleClass( "ui-screen-hidden", false );
+                       }
+                       listview._refreshCorners();
+               })
+               .appendTo( wrapper )
+               .textinput();
+
+       if ( listview.options.inset ) {
+               wrapper.addClass( "ui-listview-filter-inset" );
+       }
+
+       wrapper.bind( "submit", function() {
+               return false;
+       })
+       .insertBefore( list );
+});
+
+})( jQuery );
+
+( function( $, undefined ) {
+
+$.widget( "mobile.slider", $.mobile.widget, {
+       options: {
+               theme: null,
+               trackTheme: null,
+               disabled: false,
+               initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",
+               mini: false
+       },
+
+       _create: function() {
+
+               // TODO: Each of these should have comments explain what they're for
+               var self = this,
+
+                       control = this.element,
+
+                       parentTheme = $.mobile.getInheritedTheme( control, "c" ),
+
+                       theme = this.options.theme || parentTheme,
+
+                       trackTheme = this.options.trackTheme || parentTheme,
+
+                       cType = control[ 0 ].nodeName.toLowerCase(),
+
+                       selectClass = ( cType == "select" ) ? "ui-slider-switch" : "",
+
+                       controlID = control.attr( "id" ),
+
+                       $label = $( "[for='" + controlID + "']" ),
+
+                       labelID = $label.attr( "id" ) || controlID + "-label",
+
+                       label = $label.attr( "id", labelID ),
+
+                       val = function() {
+                               return  cType == "input"  ? parseFloat( control.val() ) : control[0].selectedIndex;
+                       },
+
+                       min =  cType == "input" ? parseFloat( control.attr( "min" ) ) : 0,
+
+                       max =  cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1,
+
+                       step = window.parseFloat( control.attr( "step" ) || 1 ),
+
+                       inlineClass = ( this.options.inline || control.jqmData("inline") == true ) ? " ui-slider-inline" : "",
+
+                       miniClass = ( this.options.mini || control.jqmData("mini") ) ? " ui-slider-mini" : "",
+
+
+                       domHandle = document.createElement('a'),
+                       handle = $( domHandle ),
+                       domSlider = document.createElement('div'),
+                       slider = $( domSlider ),
+
+                       valuebg = control.jqmData("highlight") && cType != "select" ? (function() {
+                               var bg = document.createElement('div');
+                               bg.className = 'ui-slider-bg ' + $.mobile.activeBtnClass + ' ui-btn-corner-all';
+                               return $( bg ).prependTo( slider );
+                       })() : false,
+
+                       options;
+
+        domHandle.setAttribute( 'href', "#" );
+               domSlider.setAttribute('role','application');
+               domSlider.className = ['ui-slider ',selectClass," ui-btn-down-",trackTheme,' ui-btn-corner-all', inlineClass, miniClass].join("");
+               domHandle.className = 'ui-slider-handle';
+               domSlider.appendChild(domHandle);
+
+               handle.buttonMarkup({ corners: true, theme: theme, shadow: true })
+                               .attr({
+                                       "role": "slider",
+                                       "aria-valuemin": min,
+                                       "aria-valuemax": max,
+                                       "aria-valuenow": val(),
+                                       "aria-valuetext": val(),
+                                       "title": val(),
+                                       "aria-labelledby": labelID
+                               });
+
+               $.extend( this, {
+                       slider: slider,
+                       handle: handle,
+                       valuebg: valuebg,
+                       dragging: false,
+                       beforeStart: null,
+                       userModified: false,
+                       mouseMoved: false
+               });
+
+               if ( cType == "select" ) {
+                       var wrapper = document.createElement('div');
+                       wrapper.className = 'ui-slider-inneroffset';
+
+                       for(var j = 0,length = domSlider.childNodes.length;j < length;j++){
+                               wrapper.appendChild(domSlider.childNodes[j]);
+                       }
+
+                       domSlider.appendChild(wrapper);
+
+                       // slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );
+
+                       // make the handle move with a smooth transition
+                       handle.addClass( "ui-slider-handle-snapping" );
+
+                       options = control.find( "option" );
+
+                       for(var i = 0, optionsCount = options.length; i < optionsCount; i++){
+                               var side = !i ? "b":"a",
+                                       sliderTheme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass ),
+                                       sliderLabel = document.createElement('div'),
+                                       sliderImg = document.createElement('span');
+
+                               sliderImg.className = ['ui-slider-label ui-slider-label-',side,sliderTheme," ui-btn-corner-all"].join("");
+                               sliderImg.setAttribute('role','img');
+                               sliderImg.appendChild(document.createTextNode(options[i].innerHTML));
+                               $(sliderImg).prependTo( slider );
+                       }
+
+                       self._labels = $( ".ui-slider-label", slider );
+
+               }
+
+               label.addClass( "ui-slider" );
+
+               // monitor the input for updated values
+               control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
+                       .change( function() {
+                               // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
+                               if (!self.mouseMoved) {
+                                       self.refresh( val(), true );
+                               }
+                       })
+                       .keyup( function() { // necessary?
+                               self.refresh( val(), true, true );
+                       })
+                       .blur( function() {
+                               self.refresh( val(), true );
+                       });
+
+               // prevent screen drag when slider activated
+               $( document ).bind( "vmousemove", function( event ) {
+                       if ( self.dragging ) {
+                               // self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
+                               self.mouseMoved = true;
+
+                               if ( cType === "select" ) {
+                                       // make the handle move in sync with the mouse
+                                       handle.removeClass( "ui-slider-handle-snapping" );
+                               }
+
+                               self.refresh( event );
+
+                               // only after refresh() you can calculate self.userModified
+                               self.userModified = self.beforeStart !== control[0].selectedIndex;
+                               return false;
+                       }
+               });
+
+               slider.bind( "vmousedown", function( event ) {
+                       self.dragging = true;
+                       self.userModified = false;
+                       self.mouseMoved = false;
+
+                       if ( cType === "select" ) {
+                               self.beforeStart = control[0].selectedIndex;
+                       }
+
+                       self.refresh( event );
+                       return false;
+               })
+               .bind( "vclick", false );
+
+               slider.add( document )
+                       .bind( "vmouseup", function() {
+                               if ( self.dragging ) {
+
+                                       self.dragging = false;
+
+                                       if ( cType === "select") {
+
+                                               // make the handle move with a smooth transition
+                                               handle.addClass( "ui-slider-handle-snapping" );
+
+                                               if ( self.mouseMoved ) {
+
+                                                       // this is a drag, change the value only if user dragged enough
+                                                       if ( self.userModified ) {
+                                                               self.refresh( self.beforeStart == 0 ? 1 : 0 );
+                                                       }
+                                                       else {
+                                                               self.refresh( self.beforeStart );
+                                                       }
+
+                                               }
+                                               else {
+                                                       // this is just a click, change the value
+                                                       self.refresh( self.beforeStart == 0 ? 1 : 0 );
+                                               }
+
+                                       }
+
+                                       self.mouseMoved = false;
+
+                                       return false;
+                               }
+                       });
+
+               slider.insertAfter( control );
+
+               // Only add focus class to toggle switch, sliders get it automatically from ui-btn
+               if( cType == 'select' ) {
+                       this.handle.bind({
+                               focus: function() {
+                                       slider.addClass( $.mobile.focusClass );
+                               },
+
+                               blur: function() {
+                                       slider.removeClass( $.mobile.focusClass );
+                               }
+                       });
+               }
+
+               this.handle.bind({
+                       // NOTE force focus on handle
+                       vmousedown: function() {
+                               $( this ).focus();
+                       },
+
+                       vclick: false,
+
+                       keydown: function( event ) {
+                               var index = val();
+
+                               if ( self.options.disabled ) {
+                                       return;
+                               }
+
+                               // In all cases prevent the default and mark the handle as active
+                               switch ( event.keyCode ) {
+                                       case $.mobile.keyCode.HOME:
+                                       case $.mobile.keyCode.END:
+                                       case $.mobile.keyCode.PAGE_UP:
+                                       case $.mobile.keyCode.PAGE_DOWN:
+                                       case $.mobile.keyCode.UP:
+                                       case $.mobile.keyCode.RIGHT:
+                                       case $.mobile.keyCode.DOWN:
+                                       case $.mobile.keyCode.LEFT:
+                                               event.preventDefault();
+
+                                               if ( !self._keySliding ) {
+                                                       self._keySliding = true;
+                                                       $( this ).addClass( "ui-state-active" );
+                                               }
+                                               break;
+                               }
+
+                               // move the slider according to the keypress
+                               switch ( event.keyCode ) {
+                                       case $.mobile.keyCode.HOME:
+                                               self.refresh( min );
+                                               break;
+                                       case $.mobile.keyCode.END:
+                                               self.refresh( max );
+                                               break;
+                                       case $.mobile.keyCode.PAGE_UP:
+                                       case $.mobile.keyCode.UP:
+                                       case $.mobile.keyCode.RIGHT:
+                                               self.refresh( index + step );
+                                               break;
+                                       case $.mobile.keyCode.PAGE_DOWN:
+                                       case $.mobile.keyCode.DOWN:
+                                       case $.mobile.keyCode.LEFT:
+                                               self.refresh( index - step );
+                                               break;
+                               }
+                       }, // remove active mark
+
+                       keyup: function( event ) {
+                               if ( self._keySliding ) {
+                                       self._keySliding = false;
+                                       $( this ).removeClass( "ui-state-active" );
+                               }
+                       }
+                       });
+
+               this.refresh(undefined, undefined, true);
+       },
+
+       refresh: function( val, isfromControl, preventInputUpdate ) {
+
+               if ( this.options.disabled || this.element.attr('disabled')) {
+                       this.disable();
+               }
+
+               var control = this.element, percent,
+                       cType = control[0].nodeName.toLowerCase(),
+                       min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0,
+                       max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1,
+                       step = (cType === "input" && parseFloat( control.attr( "step" ) ) > 0) ? parseFloat(control.attr("step")) : 1;
+
+               if ( typeof val === "object" ) {
+                       var data = val,
+                               // a slight tolerance helped get to the ends of the slider
+                               tol = 8;
+                       if ( !this.dragging ||
+                                       data.pageX < this.slider.offset().left - tol ||
+                                       data.pageX > this.slider.offset().left + this.slider.width() + tol ) {
+                               return;
+                       }
+                       percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 );
+               } else {
+                       if ( val == null ) {
+                               val = cType === "input" ? parseFloat( control.val() || 0 ) : control[0].selectedIndex;
+                       }
+                       percent = ( parseFloat( val ) - min ) / ( max - min ) * 100;
+               }
+
+               if ( isNaN( percent ) ) {
+                       return;
+               }
+
+               if ( percent < 0 ) {
+                       percent = 0;
+               }
+
+               if ( percent > 100 ) {
+                       percent = 100;
+               }
+
+               var newval = ( percent / 100 ) * ( max - min ) + min;
+
+               //from jQuery UI slider, the following source will round to the nearest step
+               var valModStep = ( newval - min ) % step;
+               var alignValue = newval - valModStep;
+
+               if ( Math.abs( valModStep ) * 2 >= step ) {
+                       alignValue += ( valModStep > 0 ) ? step : ( -step );
+               }
+               // Since JavaScript has problems with large floats, round
+               // the final value to 5 digits after the decimal point (see jQueryUI: #4124)
+               newval = parseFloat( alignValue.toFixed(5) );
+
+               if ( newval < min ) {
+                       newval = min;
+               }
+
+               if ( newval > max ) {
+                       newval = max;
+               }
+
+               this.handle.css( "left", percent + "%" );
+               this.handle.attr( {
+                               "aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ),
+                               "aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText(),
+                               title: cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText()
+                       });
+               this.valuebg && this.valuebg.css( "width", percent + "%" );
+
+               // drag the label widths
+               if ( this._labels ) {
+                       var handlePercent = this.handle.width() / this.slider.width() * 100,
+                               aPercent = percent && handlePercent + ( 100 - handlePercent ) * percent / 100,
+                               bPercent = percent === 100 ? 0 : Math.min( handlePercent + 100 - aPercent, 100 );
+
+                       this._labels.each(function(){
+                               var ab = $(this).is( ".ui-slider-label-a" );
+                               $( this ).width( ( ab ? aPercent : bPercent  ) + "%" );
+                       });
+               }
+
+               if ( !preventInputUpdate ) {
+                       var valueChanged = false;
+
+                       // update control"s value
+                       if ( cType === "input" ) {
+                               valueChanged = control.val() !== newval;
+                               control.val( newval );
+                       } else {
+                               valueChanged = control[ 0 ].selectedIndex !== newval;
+                               control[ 0 ].selectedIndex = newval;
+                       }
+                       if ( !isfromControl && valueChanged ) {
+                               control.trigger( "change" );
+                       }
+               }
+       },
+
+       enable: function() {
+               this.element.attr( "disabled", false );
+               this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
+               return this._setOption( "disabled", false );
+       },
+
+       disable: function() {
+               this.element.attr( "disabled", true );
+               this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true );
+               return this._setOption( "disabled", true );
+       }
+
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.slider.prototype.enhanceWithin( e.target, true );
+});
+
+})( jQuery );
+
+(function( $, undefined ) {
+
+$.widget( "mobile.selectmenu", $.mobile.widget, {
+       options: {
+               theme: null,
+               disabled: false,
+               icon: "arrow-d",
+               iconpos: "right",
+               inline: false,
+               corners: true,
+               shadow: true,
+               iconshadow: true,
+               overlayTheme: "a",
+               hidePlaceholderMenuItems: true,
+               closeText: "Close",
+               nativeMenu: true,
+               // This option defaults to true on iOS devices.
+               preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1,
+               initSelector: "select:not(:jqmData(role='slider'))",
+               mini: false
+       },
+
+       _button: function(){
+               return $( "<div/>" );
+       },
+
+       _setDisabled: function( value ) {
+               this.element.attr( "disabled", value );
+               this.button.attr( "aria-disabled", value );
+               return this._setOption( "disabled", value );
+       },
+
+       _focusButton : function() {
+               var self = this;
+
+               setTimeout( function() {
+                       self.button.focus();
+               }, 40);
+       },
+
+  _selectOptions: function() {
+    return this.select.find( "option" );
+  },
+
+       // setup items that are generally necessary for select menu extension
+       _preExtension: function(){
+               var classes = "";
+               // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577
+               /* if( $el[0].className.length ) {
+                       classes = $el[0].className;
+               } */
+               if( !!~this.element[0].className.indexOf( "ui-btn-left" ) ) {
+                       classes =  " ui-btn-left";
+               }
+               
+               if(  !!~this.element[0].className.indexOf( "ui-btn-right" ) ) {
+                       classes = " ui-btn-right";
+               }
+               
+               this.select = this.element.wrap( "<div class='ui-select" + classes + "'>" );
+               this.selectID  = this.select.attr( "id" );
+               this.label = $( "label[for='"+ this.selectID +"']" ).addClass( "ui-select" );
+               this.isMultiple = this.select[ 0 ].multiple;
+               if ( !this.options.theme ) {
+                       this.options.theme = $.mobile.getInheritedTheme( this.select, "c" );
+               }
+       },
+
+       _create: function() {
+               this._preExtension();
+
+               // Allows for extension of the native select for custom selects and other plugins
+               // see select.custom for example extension
+               // TODO explore plugin registration
+               this._trigger( "beforeCreate" );
+
+               this.button = this._button();
+
+               var self = this,
+
+                       options = this.options,
+
+                       inline = options.inline || this.select.jqmData( "inline" ),
+                       mini = options.mini || this.select.jqmData( "mini" ),                   
+                       iconpos = options.icon ? ( options.iconpos || this.select.jqmData( "iconpos" ) ) : false,
+
+                       // IE throws an exception at options.item() function when
+                       // there is no selected item
+                       // select first in this case
+                       selectedIndex = this.select[ 0 ].selectedIndex == -1 ? 0 : this.select[ 0 ].selectedIndex,
+
+                       // TODO values buttonId and menuId are undefined here
+                       button = this.button
+                               .text( $( this.select[ 0 ].options.item( selectedIndex ) ).text() )
+                               .insertBefore( this.select )
+                               .buttonMarkup( {
+                                       theme: options.theme,
+                                       icon: options.icon,
+                                       iconpos: iconpos,
+                                       inline: inline,
+                                       corners: options.corners,
+                                       shadow: options.shadow,
+                                       iconshadow: options.iconshadow,
+                                       mini: mini
+                               });
+
+               // Opera does not properly support opacity on select elements
+               // In Mini, it hides the element, but not its text
+               // On the desktop,it seems to do the opposite
+               // for these reasons, using the nativeMenu option results in a full native select in Opera
+               if ( options.nativeMenu && window.opera && window.opera.version ) {
+                       button.addClass( "ui-select-nativeonly" );
+               }       
+
+               // Add counter for multi selects
+               if ( this.isMultiple ) {
+                       this.buttonCount = $( "<span>" )
+                               .addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" )
+                               .hide()
+                               .appendTo( button.addClass('ui-li-has-count') );
+               }
+
+               // Disable if specified
+               if ( options.disabled || this.element.attr('disabled')) {
+                       this.disable();
+               }
+
+               // Events on native select
+               this.select.change( function() {
+                       self.refresh();
+               });
+
+               this.build();
+       },
+
+       build: function() {
+               var self = this;
+
+               this.select
+                       .appendTo( self.button )
+                       .bind( "vmousedown", function() {
+                               // Add active class to button
+                               self.button.addClass( $.mobile.activeBtnClass );
+                       })
+            .bind( "focus", function() {
+                self.button.addClass( $.mobile.focusClass );
+            })
+            .bind( "blur", function() {
+                self.button.removeClass( $.mobile.focusClass );
+            })
+                       .bind( "focus vmouseover", function() {
+                               self.button.trigger( "vmouseover" );
+                       })
+                       .bind( "vmousemove", function() {
+                               // Remove active class on scroll/touchmove
+                               self.button.removeClass( $.mobile.activeBtnClass );
+                       })
+                       .bind( "change blur vmouseout", function() {
+                               self.button.trigger( "vmouseout" )
+                                       .removeClass( $.mobile.activeBtnClass );
+                       })
+                       .bind( "change blur", function() {
+                               self.button.removeClass( "ui-btn-down-" + self.options.theme );
+                       });
+
+               // In many situations, iOS will zoom into the select upon tap, this prevents that from happening
+               self.button.bind( "vmousedown", function() {
+                       if( self.options.preventFocusZoom ){
+                               $.mobile.zoom.disable( true );
+                       }
+               })
+               .bind( "mouseup", function() {
+                       if( self.options.preventFocusZoom ){
+                               $.mobile.zoom.enable( true );
+                       }
+               });
+       },
+
+       selected: function() {
+               return this._selectOptions().filter( ":selected" );
+       },
+
+       selectedIndices: function() {
+               var self = this;
+
+               return this.selected().map( function() {
+                       return self._selectOptions().index( this );
+               }).get();
+       },
+
+       setButtonText: function() {
+               var self = this, selected = this.selected();
+
+               this.button.find( ".ui-btn-text" ).text( function() {
+                       if ( !self.isMultiple ) {
+                               return selected.text();
+                       }
+
+                       return selected.length ? selected.map( function() {
+                               return $( this ).text();
+                       }).get().join( ", " ) : self.placeholder;
+               });
+       },
+
+       setButtonCount: function() {
+               var selected = this.selected();
+
+               // multiple count inside button
+               if ( this.isMultiple ) {
+                       this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
+               }
+       },
+
+       refresh: function() {
+               this.setButtonText();
+               this.setButtonCount();
+       },
+
+       // open and close preserved in native selects
+       // to simplify users code when looping over selects
+       open: $.noop,
+       close: $.noop,
+
+       disable: function() {
+               this._setDisabled( true );
+               this.button.addClass( "ui-disabled" );
+       },
+
+       enable: function() {
+               this._setDisabled( false );
+               this.button.removeClass( "ui-disabled" );
+       }
+});
+
+//auto self-init widgets
+$( document ).bind( "pagecreate create", function( e ){
+       $.mobile.selectmenu.prototype.enhanceWithin( e.target, true );
+});
+})( jQuery );
+
+/*
+* custom "selectmenu" plugin
+*/
+
+(function( $, undefined ) {
+       var extendSelect = function( widget ){
+
+               var select = widget.select,
+                       selectID  = widget.selectID,
+                       label = widget.label,
+                       thisPage = widget.select.closest( ".ui-page" ),
+                       screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"} ).appendTo( thisPage ),
+                       selectOptions = widget._selectOptions(),
+                       isMultiple = widget.isMultiple = widget.select[ 0 ].multiple,
+                       buttonId = selectID + "-button",
+                       menuId = selectID + "-menu",
+                       menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' data-" +$.mobile.ns + "theme='"+ widget.options.theme +"' data-" +$.mobile.ns + "overlay-theme='"+ widget.options.overlayTheme +"'>" +
+                               "<div data-" + $.mobile.ns + "role='header'>" +
+                               "<div class='ui-title'>" + label.getEncodedText() + "</div>"+
+                               "</div>"+
+                               "<div data-" + $.mobile.ns + "role='content'></div>"+
+                               "</div>" ),
+
+                       listbox =  $("<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + widget.options.overlayTheme + " " + $.mobile.defaultDialogTransition } ).insertAfter(screen),
+
+                       list = $( "<ul>", {
+                               "class": "ui-selectmenu-list",
+                               "id": menuId,
+                               "role": "listbox",
+                               "aria-labelledby": buttonId
+                       }).attr( "data-" + $.mobile.ns + "theme", widget.options.theme ).appendTo( listbox ),
+
+                       header = $( "<div>", {
+                               "class": "ui-header ui-bar-" + widget.options.theme
+                       }).prependTo( listbox ),
+
+                       headerTitle = $( "<h1>", {
+                               "class": "ui-title"
+                       }).appendTo( header ),
+
+                       menuPageContent,
+                       menuPageClose,
+                       headerClose;
+
+               if( widget.isMultiple ) {
+                       headerClose = $( "<a>", {
+                               "text": widget.options.closeText,
+                               "href": "#",
+                               "class": "ui-btn-left"
+                       }).attr( "data-" + $.mobile.ns + "iconpos", "notext" ).attr( "data-" + $.mobile.ns + "icon", "delete" ).appendTo( header ).buttonMarkup();
+               }
+
+               $.extend( widget, {
+                       select: widget.select,
+                       selectID: selectID,
+                       buttonId: buttonId,
+                       menuId: menuId,
+                       thisPage: thisPage,
+                       menuPage: menuPage,
+                       label: label,
+                       screen: screen,
+                       selectOptions: selectOptions,
+                       isMultiple: isMultiple,
+                       theme: widget.options.theme,
+                       listbox: listbox,
+                       list: list,
+                       header: header,
+                       headerTitle: headerTitle,
+                       headerClose: headerClose,
+                       menuPageContent: menuPageContent,
+                       menuPageClose: menuPageClose,
+                       placeholder: "",
+
+                       build: function() {
+                               var self = this;
+
+                               // Create list from select, update state
+                               self.refresh();
+
+                               self.select.attr( "tabindex", "-1" ).focus(function() {
+                                       $( this ).blur();
+                                       self.button.focus();
+                               });
+
+                               // Button events
+                               self.button.bind( "vclick keydown" , function( event ) {
+                                       if ( event.type == "vclick" ||
+                                                        event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
+                                                                                                                                       event.keyCode === $.mobile.keyCode.SPACE ) ) {
+
+                                               self.open();
+                                               event.preventDefault();
+                                       }
+                               });
+
+                               // Events for list items
+                               self.list.attr( "role", "listbox" )
+                                       .bind( "focusin", function( e ){
+                                               $( e.target )
+                                                       .attr( "tabindex", "0" )
+                                                       .trigger( "vmouseover" );
+
+                                       })
+                                       .bind( "focusout", function( e ){
+                                               $( e.target )
+                                                       .attr( "tabindex", "-1" )
+                                                       .trigger( "vmouseout" );
+                                       })
+                                       .delegate( "li:not(.ui-disabled, .ui-li-divider)", "click", function( event ) {
+
+                                               // index of option tag to be selected
+                                               var oldIndex = self.select[ 0 ].selectedIndex,
+                                                       newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
+                                                       option = self._selectOptions().eq( newIndex )[ 0 ];
+
+                                               // toggle selected status on the tag for multi selects
+                                               option.selected = self.isMultiple ? !option.selected : true;
+
+                                               // toggle checkbox class for multiple selects
+                                               if ( self.isMultiple ) {
+                                                       $( this ).find( ".ui-icon" )
+                                                               .toggleClass( "ui-icon-checkbox-on", option.selected )
+                                                               .toggleClass( "ui-icon-checkbox-off", !option.selected );
+                                               }
+
+                                               // trigger change if value changed
+                                               if ( self.isMultiple || oldIndex !== newIndex ) {
+                                                       self.select.trigger( "change" );
+                                               }
+
+                                               // hide custom select for single selects only - otherwise focus clicked item
+                                               // We need to grab the clicked item the hard way, because the list may have been rebuilt
+                                               if ( self.isMultiple ) {
+                                                       self.list.find( "li:not(.ui-li-divider)" ).eq( newIndex )
+                                                               .addClass( "ui-btn-down-" + widget.options.theme ).find( "a" ).first().focus();
+                                               }
+                                               else {
+                                                       self.close();
+                                               }
+
+                                               event.preventDefault();
+                                       })
+                                       .keydown(function( event ) {  //keyboard events for menu items
+                                               var target = $( event.target ),
+                                                       li = target.closest( "li" ),
+                                                       prev, next;
+
+                                               // switch logic based on which key was pressed
+                                               switch ( event.keyCode ) {
+                                                       // up or left arrow keys
+                                                case 38:
+                                                       prev = li.prev().not( ".ui-selectmenu-placeholder" );
+
+                                                       if( prev.is( ".ui-li-divider" ) ) {
+                                                               prev = prev.prev();
+                                                       }
+
+                                                       // if there's a previous option, focus it
+                                                       if ( prev.length ) {
+                                                               target
+                                                                       .blur()
+                                                                       .attr( "tabindex", "-1" );
+
+                                                               prev.addClass( "ui-btn-down-" + widget.options.theme ).find( "a" ).first().focus();
+                                                       }
+
+                                                       return false;
+                                                       break;
+
+                                                       // down or right arrow keys
+                                                case 40:
+                                                       next = li.next();
+
+                                                       if( next.is( ".ui-li-divider" ) ) {
+                                                               next = next.next();
+                                                       }
+
+                                                       // if there's a next option, focus it
+                                                       if ( next.length ) {
+                                                               target
+                                                                       .blur()
+                                                                       .attr( "tabindex", "-1" );
+
+                                                               next.addClass( "ui-btn-down-" + widget.options.theme ).find( "a" ).first().focus();
+                                                       }
+
+                                                       return false;
+                                                       break;
+
+                                                       // If enter or space is pressed, trigger click
+                                                case 13:
+                                                case 32:
+                                                       target.trigger( "click" );
+
+                                                       return false;
+                                                       break;
+                                               }
+                                       });
+
+                               // button refocus ensures proper height calculation
+                               // by removing the inline style and ensuring page inclusion
+                               self.menuPage.bind( "pagehide", function() {
+                                       self.list.appendTo( self.listbox );
+                                       self._focusButton();
+
+                                       // TODO centralize page removal binding / handling in the page plugin.
+                                       // Suggestion from @jblas to do refcounting
+                                       //
+                                       // TODO extremely confusing dependency on the open method where the pagehide.remove
+                                       // bindings are stripped to prevent the parent page from disappearing. The way
+                                       // we're keeping pages in the DOM right now sucks
+                                       //
+                                       // rebind the page remove that was unbound in the open function
+                                       // to allow for the parent page removal from actions other than the use
+                                       // of a dialog sized custom select
+                                       //
+                                       // doing this here provides for the back button on the custom select dialog
+                                       $.mobile._bindPageRemove.call( self.thisPage );
+                               });
+
+                               // Events on "screen" overlay
+                               self.screen.bind( "vclick", function( event ) {
+                                       self.close();
+                               });
+
+                               // Close button on small overlays
+                               if( self.isMultiple ){
+                                       self.headerClose.click( function() {
+                                               if ( self.menuType == "overlay" ) {
+                                                       self.close();
+                                                       return false;
+                                               }
+                                       });
+                               }
+
+                               // track this dependency so that when the parent page
+                               // is removed on pagehide it will also remove the menupage
+                               self.thisPage.addDependents( this.menuPage );
+                       },
+
+                       _isRebuildRequired: function() {
+                               var list = this.list.find( "li" ),
+                                       options = this._selectOptions();
+
+                               // TODO exceedingly naive method to determine difference
+                               // ignores value changes etc in favor of a forcedRebuild
+                               // from the user in the refresh method
+                               return options.text() !== list.text();
+                       },
+
+                       selected: function() {
+                               return this._selectOptions().filter( ":selected:not(:jqmData(placeholder='true'))" );
+                       },
+
+                       refresh: function( forceRebuild , foo ){
+                               var self = this,
+                               select = this.element,
+                               isMultiple = this.isMultiple,
+                               indicies;
+
+                               if (  forceRebuild || this._isRebuildRequired() ) {
+                                       self._buildList();
+                               }
+
+                               indicies = this.selectedIndices();
+
+                               self.setButtonText();
+                               self.setButtonCount();
+
+                               self.list.find( "li:not(.ui-li-divider)" )
+                                       .removeClass( $.mobile.activeBtnClass )
+                                       .attr( "aria-selected", false )
+                                       .each(function( i ) {
+
+                                               if ( $.inArray( i, indicies ) > -1 ) {
+                                                       var item = $( this );
+
+                                                       // Aria selected attr
+                                                       item.attr( "aria-selected", true );
+
+                                                       // Multiple selects: add the "on" checkbox state to the icon
+                                                       if ( self.isMultiple ) {
+                                                               item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
+                                                       } else {
+                                                               if( item.is( ".ui-selectmenu-placeholder" ) ) {
+                                                                       item.next().addClass( $.mobile.activeBtnClass );
+                                                               } else {
+                                                                       item.addClass( $.mobile.activeBtnClass );
+                                                               }
+                                                       }
+                                               }
+                                       });
+                       },
+
+                       close: function() {
+                               if ( this.options.disabled || !this.isOpen ) {
+                                       return;
+                               }
+
+                               var self = this;
+
+                               if ( self.menuType == "page" ) {
+                                       // doesn't solve the possible issue with calling change page
+                                       // where the objects don't define data urls which prevents dialog key
+                                       // stripping - changePage has incoming refactor
+                                       window.history.back();
+                               } else {
+                                       self.screen.addClass( "ui-screen-hidden" );
+                                       self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
+                                       self.list.appendTo( self.listbox );
+                                       self._focusButton();
+                               }
+
+                               // allow the dialog to be closed again
+                               self.isOpen = false;
+                       },
+
+                       open: function() {
+                               if ( this.options.disabled ) {
+                                       return;
+                               }
+
+                               var self = this,
+          $window = $( window ),
+          selfListParent = self.list.parent(),
+                                       menuHeight = selfListParent.outerHeight(),
+                                       menuWidth = selfListParent.outerWidth(),
+                                       activePage = $( ".ui-page-active" ),
+                                       tScrollElem = activePage,
+                                       scrollTop = $window.scrollTop(),
+                                       btnOffset = self.button.offset().top,
+                                       screenHeight = $window.height(),
+                                       screenWidth = $window.width();
+
+                               //add active class to button
+                               self.button.addClass( $.mobile.activeBtnClass );
+
+                               //remove after delay
+                               setTimeout( function() {
+                                       self.button.removeClass( $.mobile.activeBtnClass );
+                               }, 300);
+
+                               function focusMenuItem() {
+                                       var selector = self.list.find( "." + $.mobile.activeBtnClass + " a" );
+                                       if ( selector.length === 0 ) {
+                                               selector = self.list.find( "li.ui-btn:not(:jqmData(placeholder='true')) a" );
+                                       }
+                                       selector.first().focus().closest( "li" ).addClass( "ui-btn-down-" + widget.options.theme );
+                               }
+
+                               if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
+
+                                       self.menuPage.appendTo( $.mobile.pageContainer ).page();
+                                       self.menuPageContent = menuPage.find( ".ui-content" );
+                                       self.menuPageClose = menuPage.find( ".ui-header a" );
+
+                                       // prevent the parent page from being removed from the DOM,
+                                       // otherwise the results of selecting a list item in the dialog
+                                       // fall into a black hole
+                                       self.thisPage.unbind( "pagehide.remove" );
+
+                                       //for WebOS/Opera Mini (set lastscroll using button offset)
+                                       if ( scrollTop == 0 && btnOffset > screenHeight ) {
+                                               self.thisPage.one( "pagehide", function() {
+                                                       $( this ).jqmData( "lastScroll", btnOffset );
+                                               });
+                                       }
+
+                                       self.menuPage.one( "pageshow", function() {
+                                               focusMenuItem();
+                                               self.isOpen = true;
+                                       });
+
+                                       self.menuType = "page";
+                                       self.menuPageContent.append( self.list );
+                                       self.menuPage.find("div .ui-title").text(self.label.text());
+                                       $.mobile.changePage( self.menuPage, {
+                                               transition: $.mobile.defaultDialogTransition
+                                       });
+                               } else {
+                                       self.menuType = "overlay";
+
+                                       self.screen.height( $(document).height() )
+                                               .removeClass( "ui-screen-hidden" );
+
+                                       // Try and center the overlay over the button
+                                       var roomtop = btnOffset - scrollTop,
+                                               roombot = scrollTop + screenHeight - btnOffset,
+                                               halfheight = menuHeight / 2,
+                                               maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
+                                               newtop, newleft;
+
+                                       if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
+                                               newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
+                                       } else {
+                                               // 30px tolerance off the edges
+                                               newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
+                                       }
+
+                                       // If the menuwidth is smaller than the screen center is
+                                       if ( menuWidth < maxwidth ) {
+                                               newleft = ( screenWidth - menuWidth ) / 2;
+                                       } else {
+
+                                               //otherwise insure a >= 30px offset from the left
+                                               newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
+
+                                               // 30px tolerance off the edges
+                                               if ( newleft < 30 ) {
+                                                       newleft = 30;
+                                               } else if ( (newleft + menuWidth) > screenWidth ) {
+                                                       newleft = screenWidth - menuWidth - 30;
+                                               }
+                                       }
+
+                                       self.listbox.append( self.list )
+                                               .removeClass( "ui-selectmenu-hidden" )
+                                               .css({
+                                                       top: newtop,
+                                                       left: newleft
+                                               })
+                                               .addClass( "in" );
+
+                                       focusMenuItem();
+
+                                       // duplicate with value set in page show for dialog sized selects
+                                       self.isOpen = true;
+                               }
+                       },
+
+                       _buildList: function() {
+                               var self = this,
+                                       o = this.options,
+                                       placeholder = this.placeholder,
+                                       needPlaceholder = true,
+                                       optgroups = [],
+                                       lis = [],
+                                       dataIcon = self.isMultiple ? "checkbox-off" : "false";
+
+                               self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
+
+                               var $options = self.select.find("option"),
+                                       numOptions = $options.length,
+                                       select = this.select[ 0 ],
+                                       dataPrefix = 'data-' + $.mobile.ns,
+                                       dataIndexAttr = dataPrefix + 'option-index',
+                                       dataIconAttr = dataPrefix + 'icon',
+                                       dataRoleAttr = dataPrefix + 'role',
+                                       dataPlaceholderAttr = dataPrefix + 'placeholder',
+                                       fragment = document.createDocumentFragment(),
+                                       isPlaceholderItem = false,
+                                       optGroup;
+
+                               for (var i = 0; i < numOptions;i++, isPlaceholderItem = false){
+                                       var option = $options[i],
+                                               $option = $(option),
+                                               parent = option.parentNode,
+                                               text = $option.text(),
+                                               anchor  = document.createElement('a'),
+                                               classes = [];
+
+                                       anchor.setAttribute('href','#');
+                                       anchor.appendChild(document.createTextNode(text));
+
+                                       // Are we inside an optgroup?
+                                       if (parent !== select && parent.nodeName.toLowerCase() === "optgroup"){
+                                               var optLabel = parent.getAttribute('label');
+                                               if ( optLabel != optGroup) {
+                                                       var divider = document.createElement('li');
+                                                       divider.setAttribute(dataRoleAttr,'list-divider');
+                                                       divider.setAttribute('role','option');
+                                                       divider.setAttribute('tabindex','-1');
+                                                       divider.appendChild(document.createTextNode(optLabel));
+                                                       fragment.appendChild(divider);
+                                                       optGroup = optLabel;
+                                               }
+                                       }
+
+                                       if (needPlaceholder && (!option.getAttribute( "value" ) || text.length == 0 || $option.jqmData( "placeholder" ))) {
+                                               needPlaceholder = false;
+                                               isPlaceholderItem = true;
+
+                                               // If we have identified a placeholder, mark it retroactively in the select as well
+                                               option.setAttribute( dataPlaceholderAttr, true );
+                                               if ( o.hidePlaceholderMenuItems ) {
+                                                       classes.push( "ui-selectmenu-placeholder" );
+                                               }
+                                               if (!placeholder) {
+                                                       placeholder = self.placeholder = text;
+                                               }
+                                       }
+
+                                       var item = document.createElement('li');
+                                       if ( option.disabled ) {
+                                               classes.push( "ui-disabled" );
+                                               item.setAttribute('aria-disabled',true);
+                                       }
+                                       item.setAttribute(dataIndexAttr,i);
+                                       item.setAttribute(dataIconAttr,dataIcon);
+                                       if ( isPlaceholderItem ) {
+                                               item.setAttribute( dataPlaceholderAttr, true );
+                                       }
+                                       item.className = classes.join(" ");
+                                       item.setAttribute('role','option');
+                                       anchor.setAttribute('tabindex','-1');
+                                       item.appendChild(anchor);
+                                       fragment.appendChild(item);
+                               }
+
+                               self.list[0].appendChild(fragment);
+
+                               // Hide header if it's not a multiselect and there's no placeholder
+                               if ( !this.isMultiple && !placeholder.length ) {
+                                       this.header.hide();
+                               } else {
+                                       this.headerTitle.text( this.placeholder );
+                               }
+
+                               // Now populated, create listview
+                               self.list.listview();
+                       },
+
+                       _button: function(){
+                               return $( "<a>", {
+                                       "href": "#",
+                                       "role": "button",
+                                       // TODO value is undefined at creation
+                                       "id": this.buttonId,
+                                       "aria-haspopup": "true",
+
+                                       // TODO value is undefined at creation
+                                       "aria-owns": this.menuId
+                               });
+                       }
+               });
+       };
+
+       // issue #3894 - core doesn't triggered events on disabled delegates
+       $( document ).bind( "selectmenubeforecreate", function( event ){
+               var selectmenuWidget = $( event.target ).data( "selectmenu" );
+
+               if( !selectmenuWidget.options.nativeMenu ){
+                       extendSelect( selectmenuWidget );
+               }
+       });
+})( jQuery );
+
+(function( $, undefined ) {
+
+
+       $.widget( "mobile.fixedtoolbar", $.mobile.widget, {
+               options: {
+                       visibleOnPageShow: true,
+                       disablePageZoom: true,
+                       transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
+                       fullscreen: false,
+                       tapToggle: true,
+                       tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
+                       hideDuringFocus: "input, textarea, select",
+                       updatePagePadding: true,
+                       trackPersistentToolbars: true,
+
+                       // Browser detection! Weeee, here we go...
+                       // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
+                       // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
+                       // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
+                       // The following function serves to rule out some popular browsers with known fixed-positioning issues
+                       // This is a plugin option like any other, so feel free to improve or overwrite it
+                       supportBlacklist: function(){
+                               var w = window,
+                                       ua = navigator.userAgent,
+                                       platform = navigator.platform,
+                                       // Rendering engine is Webkit, and capture major version
+                                       wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
+                                       wkversion = !!wkmatch && wkmatch[ 1 ],
+                                       ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
+                                       ffversion = !!ffmatch && ffmatch[ 1 ],
+                                       operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ),
+                                       omversion = !!operammobilematch && operammobilematch[ 1 ];
+
+                               if(
+                                       // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
+                                       ( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1  || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 )
+                                       ||
+                                       // Opera Mini
+                                       ( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" )
+                                       ||
+                                       ( operammobilematch && omversion < 7458 )
+                                       ||
+                                       //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
+                                       ( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 )
+                                       ||
+                                       // Firefox Mobile before 6.0 -
+                                       ( ffversion && ffversion < 6 )
+                                       ||
+                                       // WebOS less than 3
+                                       ( "palmGetResource" in window && wkversion && wkversion < 534 )
+                                       ||
+                                       // MeeGo
+                                       ( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 )
+                               ){
+                                       return true;
+                               }
+
+                               return false;
+                       },
+                       initSelector: ":jqmData(position='fixed')"
+               },
+
+               _create: function() {
+
+                       var self = this,
+                               o = self.options,
+                               $el = self.element,
+                               tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer",
+                               $page = $el.closest(".ui-page");
+
+                       // Feature detecting support for
+                       if( o.supportBlacklist() ){
+                               self.destroy();
+                               return;
+                       }
+
+                       $el.addClass( "ui-"+ tbtype +"-fixed" );
+
+                       // "fullscreen" overlay positioning
+                       if( o.fullscreen ){
+                               $el.addClass( "ui-"+ tbtype +"-fullscreen" );
+                               $page.addClass( "ui-page-" + tbtype + "-fullscreen" );
+                       }
+                       // If not fullscreen, add class to page to set top or bottom padding
+                       else{
+                               $page.addClass( "ui-page-" + tbtype + "-fixed" );
+                       }
+
+                       self._addTransitionClass();
+                       self._bindPageEvents();
+                       self._bindToggleHandlers();
+               },
+
+               _addTransitionClass: function(){
+                       var tclass = this.options.transition;
+
+                       if( tclass && tclass !== "none" ){
+                               // use appropriate slide for header or footer
+                               if( tclass === "slide" ){
+                                       tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
+                               }
+
+                               this.element.addClass( tclass );
+                       }
+               },
+
+               _bindPageEvents: function(){
+                       var self = this,
+                               o = self.options,
+                               $el = self.element;
+
+                       //page event bindings
+                       // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
+                       // This method is meant to disable zoom while a fixed-positioned toolbar page is visible
+                       $el.closest( ".ui-page" )
+                               .bind( "pagebeforeshow", function(){
+                                       if( o.disablePageZoom ){
+                                               $.mobile.zoom.disable( true );
+                                       }
+                                       if( !o.visibleOnPageShow ){
+                                               self.hide( true );
+                                       }
+                               } )
+                               .bind( "webkitAnimationStart animationstart updatelayout", function(){
+                                       var thisPage = this;
+                                       if( o.updatePagePadding ){
+                                               self.updatePagePadding( thisPage );
+                                       }
+                               })
+                               .bind( "pageshow", function(){
+                                       var thisPage = this;
+                                       self.updatePagePadding( thisPage );
+                                       if( o.updatePagePadding ){
+                                               $( window ).bind( "throttledresize." + self.widgetName, function(){
+                                                       self.updatePagePadding( thisPage );
+                                               });
+                                       }
+                               })
+                               .bind( "pagebeforehide", function( e, ui ){
+                                       if( o.disablePageZoom ){
+                                               $.mobile.zoom.enable( true );
+                                       }
+                                       if( o.updatePagePadding ){
+                                               $( window ).unbind( "throttledresize." + self.widgetName );
+                                       }
+
+                                       if( o.trackPersistentToolbars ){
+                                               var thisFooter = $( ".ui-footer-fixed:jqmData(id)", this ),
+                                                       thisHeader = $( ".ui-header-fixed:jqmData(id)", this ),
+                                                       nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ),
+                                                       nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage );
+
+                                               nextFooter = nextFooter || $();
+
+                                                       if( nextFooter.length || nextHeader.length ){
+
+                                                               nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer );
+
+                                                               ui.nextPage.one( "pageshow", function(){
+                                                                       nextFooter.add( nextHeader ).appendTo( this );
+                                                               });
+                                                       }
+                                       }
+                               });
+               },
+
+               _visible: true,
+
+               // This will set the content element's top or bottom padding equal to the toolbar's height
+               updatePagePadding: function( tbPage ) {
+                       var $el = this.element,
+                               header = $el.is( ".ui-header" );
+
+                       // This behavior only applies to "fixed", not "fullscreen"
+                       if( this.options.fullscreen ){ return; }
+
+                       tbPage = tbPage || $el.closest( ".ui-page" );
+                       $( tbPage ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() );
+               },
+               
+               _useTransition: function( notransition ){
+                       var $win = $( window ),
+                               $el = this.element,
+                               scroll = $win.scrollTop(),
+                               elHeight = $el.height(),
+                               pHeight = $el.closest( ".ui-page" ).height(),
+                               viewportHeight = $.mobile.getScreenHeight(),
+                               tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer";
+                               
+                       return !notransition &&
+                               ( this.options.transition && this.options.transition !== "none" &&
+                               (
+                                       ( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
+                                       ( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
+                               ) || this.options.fullscreen
+                               );
+               },
+
+               show: function( notransition ){
+                       var hideClass = "ui-fixed-hidden",
+                               $el = this.element;
+
+                               if( this._useTransition( notransition ) ){
+                               $el
+                                       .removeClass( "out " + hideClass )
+                                       .addClass( "in" );
+                       }
+                       else {
+                               $el.removeClass( hideClass );
+                       }
+                       this._visible = true;
+               },
+
+               hide: function( notransition ){
+                       var hideClass = "ui-fixed-hidden",
+                               $el = this.element,
+                               // if it's a slide transition, our new transitions need the reverse class as well to slide outward
+                               outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" );
+
+                       if( this._useTransition( notransition ) ){
+                               $el
+                                       .addClass( outclass )
+                                       .removeClass( "in" )
+                                       .animationComplete( function(){
+                                               $el.addClass( hideClass ).removeClass( outclass );
+                                       });
+                       }
+                       else {
+                               $el.addClass( hideClass ).removeClass( outclass );
+                       }
+                       this._visible = false;
+               },
+
+               toggle: function(){
+                       this[ this._visible ? "hide" : "show" ]();
+               },
+
+               _bindToggleHandlers: function(){
+                       var self = this,
+                               o = self.options,
+                               $el = self.element;
+
+                       // tap toggle
+                       $el.closest( ".ui-page" )
+                               .bind( "vclick", function( e ){
+                                       if( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ){
+                                               self.toggle();
+                                       }
+                               })
+                               .bind( "focusin focusout", function( e ){
+                                       if( screen.width < 500 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ){
+                                               self[ ( e.type === "focusin" && self._visible ) ? "hide" : "show" ]();
+                                       }
+                               });
+               },
+
+               destroy: function(){
+                       this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
+                       this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
+               }
+
+       });
+
+       //auto self-init widgets
+       $( document )
+               .bind( "pagecreate create", function( e ){
+                       
+                       // DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
+                       // This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
+                       if( $( e.target ).jqmData( "fullscreen" ) ){
+                               $( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true );
+                       }
+                       
+                       $.mobile.fixedtoolbar.prototype.enhanceWithin( e.target );
+               });
+
+})( jQuery );
+
+( function( $, window ) {
+       
+       // This fix addresses an iOS bug, so return early if the UA claims it's something else.
+       if( !(/iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1 ) ){
+               return;
+       }
+       
+    var zoom = $.mobile.zoom,
+               evt, x, y, z, aig;
+       
+    function checkTilt( e ){
+               evt = e.originalEvent;
+               aig = evt.accelerationIncludingGravity;
+               
+               x = Math.abs( aig.x );
+               y = Math.abs( aig.y );
+               z = Math.abs( aig.z );
+                               
+               // If portrait orientation and in one of the danger zones
+        if( !window.orientation && ( x > 7 || ( ( z > 6 && y < 8 || z < 8 && y > 6 ) && x > 5 ) ) ){
+                       if( zoom.enabled ){
+                               zoom.disable();
+                       }               
+        }
+               else if( !zoom.enabled ){
+                       zoom.enable();
+        }
+    }
+
+    $( window )
+               .bind( "orientationchange.iosorientationfix", zoom.enable )
+               .bind( "devicemotion.iosorientationfix", checkTilt );
+
+}( jQuery, this ));
+
+( function( $, window, undefined ) {
+       var     $html = $( "html" ),
+                       $head = $( "head" ),
+                       $window = $( window );
+
+       // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
+       $( window.document ).trigger( "mobileinit" );
+
+       // support conditions
+       // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
+       // otherwise, proceed with the enhancements
+       if ( !$.mobile.gradeA() ) {
+               return;
+       }
+
+       // override ajaxEnabled on platforms that have known conflicts with hash history updates
+       // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
+       if ( $.mobile.ajaxBlacklist ) {
+               $.mobile.ajaxEnabled = false;
+       }
+
+       // Add mobile, initial load "rendering" classes to docEl
+       $html.addClass( "ui-mobile ui-mobile-rendering" );
+
+       // This is a fallback. If anything goes wrong (JS errors, etc), or events don't fire,
+       // this ensures the rendering class is removed after 5 seconds, so content is visible and accessible
+       setTimeout( hideRenderingClass, 5000 );
+
+       // loading div which appears during Ajax requests
+       // will not appear if $.mobile.loadingMessage is false
+       var loaderClass = "ui-loader",
+               $loader = $( "<div class='" + loaderClass + "'><span class='ui-icon ui-icon-loading'></span><h1></h1></div>" );
+
+       // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
+       function fakeFixLoader(){
+               var activeBtn = $( "." + $.mobile.activeBtnClass ).first();
+
+               $loader
+                       .css({
+                               top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
+                               activeBtn.length && activeBtn.offset().top || 100
+                       });
+       }
+
+       // check position of loader to see if it appears to be "fixed" to center
+       // if not, use abs positioning
+       function checkLoaderPosition(){
+               var offset = $loader.offset(),
+                       scrollTop = $window.scrollTop(),
+                       screenHeight = $.mobile.getScreenHeight();
+
+               if( offset.top < scrollTop || (offset.top - scrollTop) > screenHeight ) {
+                       $loader.addClass( "ui-loader-fakefix" );
+                       fakeFixLoader();
+                       $window
+                               .unbind( "scroll", checkLoaderPosition )
+                               .bind( "scroll", fakeFixLoader );
+               }
+       }
+
+       //remove initial build class (only present on first pageshow)
+       function hideRenderingClass(){
+               $html.removeClass( "ui-mobile-rendering" );
+       }
+
+       $.extend($.mobile, {
+               // turn on/off page loading message.
+               showPageLoadingMsg: function( theme, msgText, textonly ) {
+                       $html.addClass( "ui-loading" );
+
+                       if ( $.mobile.loadingMessage ) {
+                               // text visibility from argument takes priority
+                               var textVisible = textonly || $.mobile.loadingMessageTextVisible;
+
+                               theme = theme || $.mobile.loadingMessageTheme,
+
+                               $loader
+                                       .attr( "class", loaderClass + " ui-corner-all ui-body-" + ( theme || "a" ) + " ui-loader-" + ( textVisible ? "verbose" : "default" ) + ( textonly ? " ui-loader-textonly" : "" ) )
+                                       .find( "h1" )
+                                               .text( msgText || $.mobile.loadingMessage )
+                                               .end()
+                                       .appendTo( $.mobile.pageContainer );
+
+                               checkLoaderPosition();
+                               $window.bind( "scroll", checkLoaderPosition );
+                       }
+               },
+
+               hidePageLoadingMsg: function() {
+                       $html.removeClass( "ui-loading" );
+
+                       if( $.mobile.loadingMessage ){
+                               $loader.removeClass( "ui-loader-fakefix" );
+                       }
+
+                       $( window ).unbind( "scroll", fakeFixLoader );
+                       $( window ).unbind( "scroll", checkLoaderPosition );
+               },
+
+               // find and enhance the pages in the dom and transition to the first page.
+               initializePage: function() {
+                       // find present pages
+                       var $pages = $( ":jqmData(role='page'), :jqmData(role='dialog')" );
+
+                       // if no pages are found, create one with body's inner html
+                       if ( !$pages.length ) {
+                               $pages = $( "body" ).wrapInner( "<div data-" + $.mobile.ns + "role='page'></div>" ).children( 0 );
+                       }
+
+                       // add dialogs, set data-url attrs
+                       $pages.each(function() {
+                               var $this = $(this);
+
+                               // unless the data url is already set set it to the pathname
+                               if ( !$this.jqmData("url") ) {
+                                       $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) || location.pathname + location.search );
+                               }
+                       });
+
+                       // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
+                       $.mobile.firstPage = $pages.first();
+
+                       // define page container
+                       $.mobile.pageContainer = $pages.first().parent().addClass( "ui-mobile-viewport" );
+
+                       // alert listeners that the pagecontainer has been determined for binding
+                       // to events triggered on it
+                       $window.trigger( "pagecontainercreate" );
+
+                       // cue page loading message
+                       $.mobile.showPageLoadingMsg();
+
+                       //remove initial build class (only present on first pageshow)
+                       hideRenderingClass();
+
+                       // if hashchange listening is disabled, there's no hash deeplink,
+                       // the hash is not valid (contains more than one # or does not start with #)
+                       // or there is no page with that hash, change to the first page in the DOM
+                       // Remember, however, that the hash can also be a path!
+                       if ( ! ( $.mobile.hashListeningEnabled &&
+                                $.mobile.path.isHashValid( location.hash ) &&
+                                ( $( location.hash + ':jqmData(role="page")' ).length ||
+                                  $.mobile.path.isPath( location.hash ) ) ) ) {
+                               $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true } );
+                       }
+                       // otherwise, trigger a hashchange to load a deeplink
+                       else {
+                               $window.trigger( "hashchange", [ true ] );
+                       }
+               }
+       });
+
+       // initialize events now, after mobileinit has occurred
+       $.mobile.navreadyDeferred.resolve();
+
+       // check which scrollTop value should be used by scrolling to 1 immediately at domready
+       // then check what the scroll top is. Android will report 0... others 1
+       // note that this initial scroll won't hide the address bar. It's just for the check.
+       $(function() {
+               window.scrollTo( 0, 1 );
+
+               // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
+               // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
+               // so if it's 1, use 0 from now on
+               $.mobile.defaultHomeScroll = ( !$.support.scrollTop || $(window).scrollTop() === 1 ) ? 0 : 1;
+
+
+               // TODO: Implement a proper registration mechanism with dependency handling in order to not have exceptions like the one below
+               //auto self-init widgets for those widgets that have a soft dependency on others
+               if ( $.fn.controlgroup ) {
+                       $( document ).bind( "pagecreate create", function( e ){
+                               $( ":jqmData(role='controlgroup')", e.target )
+                                       .jqmEnhanceable()
+                                       .controlgroup({ excludeInvisible: false });
+                       });
+               }
+
+               //dom-ready inits
+               if( $.mobile.autoInitializePage ){
+                       $.mobile.initializePage();
+               }
+
+               // window load event
+               // hide iOS browser chrome on load
+               $window.load( $.mobile.silentScroll );
+
+               if ( !$.support.cssPointerEvents ) {
+                       // IE and Opera don't support CSS pointer-events: none that we use to disable link-based buttons
+                       // by adding the 'ui-disabled' class to them. Using a JavaScript workaround for those browser.
+                       // https://github.com/jquery/jquery-mobile/issues/3558
+
+                       $( document ).delegate( ".ui-disabled", "vclick",
+                               function( e ) {
+                                       e.preventDefault();
+                                       e.stopImmediatePropagation();
+                               }
+                       );
+               }
+       });
+}( jQuery, this ));
+
+
+}));
diff --git a/plugins/jpush-phonegap-plugin/hooks/apns.entitlements b/plugins/jpush-phonegap-plugin/hooks/apns.entitlements
new file mode 100644 (file)
index 0000000..92267aa
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+       <dict>
+               <key>aps-environment</key>
+               <string>development</string>
+       </dict>
+</plist>
diff --git a/plugins/jpush-phonegap-plugin/hooks/common.js b/plugins/jpush-phonegap-plugin/hooks/common.js
new file mode 100644 (file)
index 0000000..b66db45
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Author: Derek Chia <snipking@gmail.com>
+ * common functions for cordova plugin after hook
+ */
+const fs = require('fs');
+const path = require('path');
+
+module.exports.addAPNSinEntitlements = (entitlementPath, isProduction) => {
+    if( fs.existsSync(entitlementPath) ) {
+        fs.readFile(entitlementPath, "utf8", function(err, data) {
+            if (err) {
+                throw err;
+            }
+
+            console.log("Reading entitlements file asynchronously");
+
+            let toInsert = '<key>aps-environment</key>\n' +
+                           '\t\t<string>development</string>';
+            if(isProduction) {
+                toInsert = '<key>aps-environment</key>\n' +
+                           '\t\t<string>production</string>';
+            }
+
+            let re1 = new RegExp('<key>aps-environment<\/key>(.|[\r\n])*<string>.*<\/string>');
+            let matched = data.match(re1);
+            let result;
+            if (matched === null) {
+                if(data.match(/<\/dict>/g)) {
+                    result = data.replace(/<\/dict>/, '\t' + toInsert + '\n\t</dict>');
+                } else if(data.match(/<dict\/>/g)) {
+                    result = data.replace(/<dict\/>/, '\t<dict>\n\t\t' + toInsert + '\n\t</dict>');
+                }
+            } else {
+                result = data.replace(re1, toInsert);
+            }
+
+            // write result to entitlements file
+            fs.writeFile(entitlementPath, result, {"encoding": 'utf8'}, function(err) {
+                if (err) {
+                    throw err;
+                }
+                console.log(entitlementPath + " written successfully");
+            });
+        });
+    } else {
+        console.log("Entitlement File '" + entitlementPath + "' not found. Make sure your ios platform upper than 4.3.0");
+    }
+}
+
+module.exports.removeAPNSinEntitlements = (entitlementPath) => {
+    if( fs.existsSync(entitlementPath) ) {
+        fs.readFile(entitlementPath, "utf8", function(err, data) {
+            if (err) {
+                throw err;
+            }
+
+            console.log("Reading entitlements file asynchronously");
+
+            let re1 = new RegExp('<key>aps-environment<\/key>(.|[\r\n])*<string>.*<\/string>');
+            let matched = data.match(re1);
+            let result;
+            if (matched != null) {
+                result = data.replace(re1, "");
+            }
+
+            // write result to entitlements file
+            fs.writeFile(entitlementPath, result, {"encoding": 'utf8'}, function(err) {
+                if (err) {
+                    throw err;
+                }
+                console.log(entitlementPath + " written successfully");
+            });
+        });
+    } else {
+        console.log("Entitlement File '" + entitlementPath + "' not found. Make sure your ios platform upper than 4.3.0");
+    }
+}
+
+module.exports.getXcodeProjName = (searchPath) => {
+    if(searchPath == null || searchPath == undefined) {
+        searchPath = './';
+    }
+    let resultFolderName = null;
+    let folderNames = fs.readdirSync(searchPath).filter(file => fs.lstatSync(path.join(searchPath, file)).isDirectory());
+    let folderNamesReg = new RegExp('.*\.xcodeproj', 'g')  // get filder name like `*.xcodeproj`
+    for(let folderName of folderNames) {
+        if(folderName.match(folderNamesReg)) {
+            resultFolderName = folderName;
+            break;
+        }
+    }
+    return resultFolderName.substr(0, resultFolderName.length - 10);
+}
diff --git a/plugins/jpush-phonegap-plugin/hooks/iosDisablePush.js b/plugins/jpush-phonegap-plugin/hooks/iosDisablePush.js
new file mode 100644 (file)
index 0000000..a633355
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Author: Derek Chia <snipking@gmail.com>
+ * Cordova plugin after hook to disable `Push Notification` capability for XCode 8
+ */
+
+const fs = require('fs');
+const path = require('path');
+let commonFuncs = require('./common');
+
+/**
+ * remove APNS env from cordova project Entitlements-Debug.plist and Entitlements-Release.plist
+ * This two file will work when xcode archive app
+ */
+let disablePushNotificationForCI = (basePath, xcodeprojName) => {
+    commonFuncs.removeAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Debug.plist');
+    commonFuncs.removeAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Release.plist');
+}
+
+/**
+ * remove APNS env to entitlement file; disable Push Notification capability in .pbxproj file
+ * This two file will work when xcode archive app
+ */
+let disablePushNotificationForXCode = (entitlementsPath, pbxprojPath) => {
+    /**
+     * remove APNS env to entitlement file
+     */
+    if( fs.existsSync(entitlementsPath) ) {
+        commonFuncs.removeAPNSinEntitlements(entitlementsPath);
+    }
+
+    /**
+     * disable Push Notification capability in .pbxproj file
+     * equally disable "Push Notification" switch in xcode
+     */
+    fs.readFile(pbxprojPath, "utf8", function(err, data) {
+        if (err) {
+            throw err;
+        }
+        console.log("Reading pbxproj file asynchronously");
+
+        // turn off Push Notification Capability
+        let re4rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities(.|[\r\n])*com\.apple\.Push = {(.|[\r\n])*enabled = [01]');
+        let parts = re4rep.exec(data);
+        if(parts !== null && parts !== undefined && parts.length > 0) {
+            result = data.replace(re4rep, parts[0].substr(0, parts[0].length - 1) + '0');
+        
+            // write result to project.pbxproj
+            fs.writeFile(pbxprojPath, result, {"encoding": 'utf8'}, function(err) {
+                if (err) {
+                    throw err;
+                }
+                console.log(pbxprojPath + " written successfully");
+            });
+        }
+    });
+}
+
+let basePath = './platforms/ios/';
+let buildType = 'dev';
+let xcodeprojName = commonFuncs.getXcodeProjName(basePath);
+let pbxprojPath = basePath + xcodeprojName + '.xcodeproj/project.pbxproj';
+let entitlementsPath = basePath + xcodeprojName + '/' + xcodeprojName + '.entitlements';
+
+disablePushNotificationForCI(basePath, xcodeprojName);
+
+disablePushNotificationForXCode(entitlementsPath, pbxprojPath);
diff --git a/plugins/jpush-phonegap-plugin/hooks/iosEnablePush.js b/plugins/jpush-phonegap-plugin/hooks/iosEnablePush.js
new file mode 100644 (file)
index 0000000..a4b47ca
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Author: Derek Chia <snipking@gmail.com>
+ * Cordova plugin after hook to enable `Push Notification` capability for XCode 8
+ */
+
+const fs = require('fs');
+const path = require('path');
+let commonFuncs = require('./common');
+
+/**
+ * add APNS env to cordova project Entitlements-Debug.plist and Entitlements-Release.plist
+ * This two file will work when xcode archive app
+ */
+let enablePushNotificationForCI = (basePath, xcodeprojName) => {
+    commonFuncs.addAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Debug.plist', false);
+    commonFuncs.addAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Release.plist', true);
+}
+
+/**
+ * add APNS env to entitlement file; enable Push Notification capability in .pbxproj file
+ * This two file will work when xcode archive app
+ */
+let enablePushNotificationForXCode = (entitlementsPath, pbxprojPath, cordovaBuildConfig) => {
+    console.log('will enable push notification capability for XCode');
+    let needAddEntitlementToPbxproj = false;
+    /**
+     * add APNS env to entitlement file
+     * without this file will cause a worning in xcode
+     */
+    if( fs.existsSync(entitlementsPath) ) {
+        commonFuncs.addAPNSinEntitlements(entitlementsPath, false);
+    } else {
+        // copy default entitlements file
+        fs.readFile(__dirname + '/apns.entitlements', 'utf8', function(err, data) {
+            if (err) {
+                throw err;
+            }
+
+            fs.writeFileSync(entitlementsPath, data);
+            console.log(entitlementsPath + " written successfully");
+        });
+
+        needAddEntitlementToPbxproj = true;
+    }
+
+    /**
+     * enable Push Notification capability in .pbxproj file
+     * equally enable "Push Notification" switch in xcode
+     */
+    fs.readFile(pbxprojPath, "utf8", function(err, data) {
+        if (err) {
+            throw err;
+        }
+        console.log("Reading pbxproj file asynchronously");
+
+        // add Push Notification Capability
+        let re1 = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes', 'g');
+        let re1rep = new RegExp('isa = PBXProject;(.|[\r\n])*attributes = {', 'g');
+        let re2 = new RegExp('(?:isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*)SystemCapabilities', 'g');
+        let re2rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes = {', 'g');
+        let re3 = new RegExp('(?:isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities(.|[\r\n])*)com\.apple\.Push', 'g');
+        let re3rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities = {', 'g');
+        let re4rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities(.|[\r\n])*com\.apple\.Push = {(.|[\r\n])*enabled = [01]');
+
+        let matched = data.match(re1);
+        let result;
+        if (matched === null) {
+            console.log('re1 not match, no TargetAttributes');
+            result = data.replace(re1rep,   'isa = PBXProject;\n' +
+                                            '\t\t\tattributes = {\n' +
+                                            '\t\t\t\tTargetAttributes = {\n' +
+                                            '\t\t\t\t\t1D6058900D05DD3D006BFB54 = {\n' +
+                                            '\t\t\t\t\t\tDevelopmentTeam = ' + cordovaBuildConfig.ios.release.developmentTeam + ';\n' +
+                                            '\t\t\t\t\t\tSystemCapabilities = {\n' +
+                                            '\t\t\t\t\t\t\tcom.apple.Push = {\n' +
+                                            '\t\t\t\t\t\t\t\tenabled = 1;\n' +
+                                            '\t\t\t\t\t\t\t};\n' +
+                                            '\t\t\t\t\t\t};\n' +
+                                            '\t\t\t\t\t};\n' +
+                                            '\t\t\t\t};');
+        } else {
+            matched = data.match(re2);
+            if(matched === null) {
+                console.log('re2 not match, nothing under TargetAttributes');
+                let parts = re2rep.exec(data);
+                result = data.replace(re2rep, parts[0] + '\n' + '\t\t\t\t\t1D6058900D05DD3D006BFB54 = {\n' +
+                                                                '\t\t\t\t\t\tDevelopmentTeam = ' + cordovaBuildConfig.ios.release.developmentTeam + ';\n' +
+                                                                '\t\t\t\t\t\tSystemCapabilities = {\n' +
+                                                                '\t\t\t\t\t\t\tcom.apple.Push = {\n' +
+                                                                '\t\t\t\t\t\t\t\tenabled = 1;\n' +
+                                                                '\t\t\t\t\t\t\t};\n' +
+                                                                '\t\t\t\t\t\t};\n' +
+                                                                '\t\t\t\t\t};');
+            } else {
+                matched = data.match(re3);
+                if(matched === null) {
+                    console.log('re3 not match, no com.apple.Push defined');
+                    let parts = re3rep.exec(data);
+                    result = data.replace(re3rep, parts[0] + '\n' + '\t\t\t\t\t\t\tcom.apple.Push = {\n' +
+                                                                    '\t\t\t\t\t\t\t\tenabled = 1;\n' +
+                                                                    '\t\t\t\t\t\t\t};');
+                } else {
+                    console.log('just enable com.apple.Push');
+                    let parts = re4rep.exec(data);
+                    result = data.replace(re4rep, parts[0].substr(0, parts[0].length - 1) + '1');
+                }
+            }
+        }
+
+        // add entitlements
+        if (needAddEntitlementToPbxproj) {
+
+            let pathArray = entitlementsPath.split("/");
+            let entitlementsFileName = pathArray[pathArray.length - 1];
+            let projectFolderName = pathArray[pathArray.length - 2];
+
+            result = result.replace(new RegExp('\\/\\* Begin PBXFileReference section \\*\\/'),  '/* Begin PBXFileReference section */\n' +
+                                                                        '\t\tD7BB385F1E4DB54800345BF4 /* ' + entitlementsFileName + ' */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = "' + entitlementsFileName + '"; path = "' + projectFolderName + '/' + entitlementsFileName + '"; sourceTree = "<group>"; };');
+            result = result.replace(new RegExp('\\/\\* CustomTemplate \\*\\/.*\n.*isa = PBXGroup;.*\n.*children = \\('), '/* CustomTemplate */ = {\n' +
+                            '\t\t\tisa = PBXGroup;\n' +
+                            '\t\t\tchildren = (\n' +
+                            '\t\t\t\tD7BB385F1E4DB54800345BF4 /* ' + entitlementsFileName + ' */,');
+            let re5rep = new RegExp('\\/\\* Debug \\*\\/.*\n.*isa = XCBuildConfiguration;.*\n.*\n.*buildSettings = {');
+            let parts = result.match(re5rep);
+            result = result.replace(re5rep, parts + '\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = "' + projectFolderName + '/' + entitlementsFileName + '";');
+
+            let re6rep = new RegExp('\\/\\* Release \\*\\/.*\n.*isa = XCBuildConfiguration;.*\n.*\n.*buildSettings = {');
+            parts = result.match(re6rep);
+            result = result.replace(re6rep, parts + '\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = "' + projectFolderName + '/' + entitlementsFileName + '";');
+        }
+
+        // write result to project.pbxproj
+        fs.writeFile(pbxprojPath, result, {"encoding": 'utf8'}, function(err) {
+            if (err) {
+                throw err;
+            }
+            console.log(pbxprojPath + " written successfully");
+        });
+    });
+}
+
+module.exports = (context) => {
+    let basePath = './platforms/ios/';
+    let buildType = 'dev';
+    let xcodeprojName = commonFuncs.getXcodeProjName(basePath);
+    let pbxprojPath = basePath + xcodeprojName + '.xcodeproj/project.pbxproj';
+    let entitlementsPath = basePath + xcodeprojName + '/' + xcodeprojName + '.entitlements';
+
+    let cordovaBuildConfigPath = './build.json'
+    let cordovaBuildConfig = null;
+    let willEnablePushNotificationForXCode = true;
+    try { // try to read ios developmentTeam from build.json
+        cordovaBuildConfig = JSON.parse(fs.readFileSync(cordovaBuildConfigPath, "utf8"));
+        if(cordovaBuildConfig.ios.release.developmentTeam === null && cordovaBuildConfig.ios.release.developmentTeam === undefined) {
+            throw 'no valid developmentTeam found in build.json';
+        }
+    } catch(e) {
+        console.log("Do not detected 'build.json' or ios.release.developmentTeam not avaliable in 'build.json' \n" +
+                "Will not enable XCode Push Notification Capability. \n" +
+                "Will only enable Push Notification for CI by add config to '" + basePath + xcodeprojName + "/Entitlements-Debug.plist' and '" + basePath + xcodeprojName + "/Entitlements-Release.plist' \n" +
+                "Please add 'build.json' to cordova project root folder to make after hook fully functional. \n" +
+                "Reference [1]https://cordova.apache.org/docs/en/latest/reference/cordova-cli/#cordova-build-command \n" +
+                "Reference [2]https://cordova.apache.org/docs/en/latest/guide/platforms/ios/#signing-an-app");
+        willEnablePushNotificationForXCode = false;
+    }
+
+    enablePushNotificationForCI(basePath, xcodeprojName);
+
+    if(willEnablePushNotificationForXCode) {
+        enablePushNotificationForXCode(entitlementsPath, pbxprojPath, cordovaBuildConfig);
+    }
+}
diff --git a/plugins/jpush-phonegap-plugin/ionic/example/src/app/app.component.ts b/plugins/jpush-phonegap-plugin/ionic/example/src/app/app.component.ts
new file mode 100644 (file)
index 0000000..5283370
--- /dev/null
@@ -0,0 +1,25 @@
+import { Component } from '@angular/core';
+import { Platform } from 'ionic-angular';
+import { StatusBar } from '@ionic-native/status-bar';
+import { SplashScreen } from '@ionic-native/splash-screen';
+import { JPush } from '@jiguang-ionic/jpush';
+
+import { HomePage } from '../pages/home/home';
+@Component({
+  templateUrl: 'app.html'
+})
+export class MyApp {
+  rootPage:any = HomePage;
+
+  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, jpush: JPush) {
+    platform.ready().then(() => {
+      // Okay, so the platform is ready and our plugins are available.
+      // Here you can do any higher level native things you might need.
+      statusBar.styleDefault();
+      splashScreen.hide();
+
+      jpush.init();
+      jpush.setDebugMode(true);
+    });
+  }
+}
diff --git a/plugins/jpush-phonegap-plugin/ionic/example/src/app/app.html b/plugins/jpush-phonegap-plugin/ionic/example/src/app/app.html
new file mode 100644 (file)
index 0000000..7b88c96
--- /dev/null
@@ -0,0 +1 @@
+<ion-nav [root]="rootPage"></ion-nav>
diff --git a/plugins/jpush-phonegap-plugin/ionic/example/src/app/app.module.ts b/plugins/jpush-phonegap-plugin/ionic/example/src/app/app.module.ts
new file mode 100644 (file)
index 0000000..ce364d8
--- /dev/null
@@ -0,0 +1,34 @@
+import { BrowserModule } from '@angular/platform-browser';
+import { ErrorHandler, NgModule } from '@angular/core';
+import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
+import { SplashScreen } from '@ionic-native/splash-screen';
+import { StatusBar } from '@ionic-native/status-bar';
+import { Device } from '@ionic-native/device';
+import { JPush } from '@jiguang-ionic/jpush';
+
+import { MyApp } from './app.component';
+import { HomePage } from '../pages/home/home';
+
+@NgModule({
+  declarations: [
+    MyApp,
+    HomePage
+  ],
+  imports: [
+    BrowserModule,
+    IonicModule.forRoot(MyApp)
+  ],
+  bootstrap: [IonicApp],
+  entryComponents: [
+    MyApp,
+    HomePage
+  ],
+  providers: [
+    StatusBar,
+    SplashScreen,
+    Device,
+    JPush,
+    {provide: ErrorHandler, useClass: IonicErrorHandler}
+  ]
+})
+export class AppModule {}
diff --git a/plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.html b/plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.html
new file mode 100644 (file)
index 0000000..6219446
--- /dev/null
@@ -0,0 +1,36 @@
+<ion-header>
+  <ion-navbar>
+    <ion-title>
+      JPush Ionic Example
+    </ion-title>
+  </ion-navbar>
+</ion-header>
+
+<ion-content padding>
+
+  <ion-list>
+    <ion-item>
+      <div>Registration Id: {{registrationId}}</div>
+      <button ion-button full (click)="getRegistrationID()">Get Registration Id</button>
+    </ion-item>
+
+    <ion-item>
+      <button ion-button full (click)="setTags()">Set tags - Tag1, Tag2</button>
+      <button ion-button full (click)="addTags()">Add tags - Tag3, Tag4</button>
+      <button ion-button full (click)="checkTagBindState()">Check tag bind state - Tag1</button>
+      <button ion-button full (click)="deleteTags()">Delete tags - Tag4</button>
+      <button ion-button full (click)="getAllTags()">Get all tags</button>
+      <button ion-button full (click)="cleanTags()">Clean tags</button>
+    </ion-item>
+
+    <ion-item>
+      <button ion-button full (click)="setAlias()">Set Alias - TestAlias</button>
+      <button ion-button full (click)="getAlias()">Get Alias</button>
+      <button ion-button full (click)="deleteAlias()">Delete Alias</button>
+    </ion-item>
+
+    <ion-item>
+      <button ion-button full (click)="addLocalNotification()">Trigger local notification after 5 seconds</button>
+    </ion-item>
+  </ion-list>
+</ion-content>
diff --git a/plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.scss b/plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.scss
new file mode 100644 (file)
index 0000000..d4cc8fc
--- /dev/null
@@ -0,0 +1,3 @@
+page-home {
+
+}
diff --git a/plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.ts b/plugins/jpush-phonegap-plugin/ionic/example/src/pages/home/home.ts
new file mode 100644 (file)
index 0000000..02d91fe
--- /dev/null
@@ -0,0 +1,176 @@
+import { Component } from "@angular/core";
+import { NavController } from "ionic-angular";
+import { JPush } from "@jiguang-ionic/jpush";
+import { Device } from "@ionic-native/device";
+
+@Component({
+  selector: "page-home",
+  templateUrl: "home.html"
+})
+export class HomePage {
+  public registrationId: string;
+
+  devicePlatform: string;
+  sequence: number = 0;
+
+  tagResultHandler = function(result) {
+    var sequence: number = result.sequence;
+    var tags: Array<string> = result.tags == null ? [] : result.tags;
+    alert(
+      "Success!" + "\nSequence: " + sequence + "\nTags: " + tags.toString()
+    );
+  };
+
+  aliasResultHandler = function(result) {
+    var sequence: number = result.sequence;
+    var alias: string = result.alias;
+    alert("Success!" + "\nSequence: " + sequence + "\nAlias: " + alias);
+  };
+
+  errorHandler = function(err) {
+    var sequence: number = err.sequence;
+    var code = err.code;
+    alert("Error!" + "\nSequence: " + sequence + "\nCode: " + code);
+  };
+
+  constructor(
+    public navCtrl: NavController,
+    public jpush: JPush,
+    device: Device
+  ) {
+    this.devicePlatform = device.platform;
+
+    document.addEventListener(
+      "jpush.receiveNotification",
+      (event: any) => {
+        var content;
+        if (this.devicePlatform == "Android") {
+          content = event.alert;
+        } else {
+          content = event.aps.alert;
+        }
+        alert("Receive notification: " + JSON.stringify(event));
+      },
+      false
+    );
+
+    document.addEventListener(
+      "jpush.openNotification",
+      (event: any) => {
+        var content;
+        if (this.devicePlatform == "Android") {
+          content = event.alert;
+        } else {
+          // iOS
+          if (event.aps == undefined) {
+            // 本地通知
+            content = event.content;
+          } else {
+            // APNS
+            content = event.aps.alert;
+          }
+        }
+        alert("open notification: " + JSON.stringify(event));
+      },
+      false
+    );
+
+    document.addEventListener(
+      "jpush.receiveLocalNotification",
+      (event: any) => {
+        // iOS(*,9) Only , iOS(10,*) 将在 jpush.openNotification 和 jpush.receiveNotification 中触发。
+        var content;
+        if (this.devicePlatform == "Android") {
+        } else {
+          content = event.content;
+        }
+        alert("receive local notification: " + JSON.stringify(event));
+      },
+      false
+    );
+  }
+
+  getRegistrationID() {
+    this.jpush.getRegistrationID().then(rId => {
+      this.registrationId = rId;
+    });
+  }
+
+  setTags() {
+    this.jpush
+      .setTags({ sequence: this.sequence++, tags: ["Tag1", "Tag2"] })
+      .then(this.tagResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  addTags() {
+    this.jpush
+      .addTags({ sequence: this.sequence++, tags: ["Tag3", "Tag4"] })
+      .then(this.tagResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  checkTagBindState() {
+    this.jpush
+      .checkTagBindState({ sequence: this.sequence++, tag: "Tag1" })
+      .then(result => {
+        var sequence = result.sequence;
+        var tag = result.tag;
+        var isBind = result.isBind;
+        alert(
+          "Sequence: " + sequence + "\nTag: " + tag + "\nIsBind: " + isBind
+        );
+      })
+      .catch(this.errorHandler);
+  }
+
+  deleteTags() {
+    this.jpush
+      .deleteTags({ sequence: this.sequence++, tags: ["Tag4"] })
+      .then(this.tagResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  getAllTags() {
+    this.jpush
+      .getAllTags({ sequence: this.sequence++ })
+      .then(this.tagResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  cleanTags() {
+    this.jpush
+      .cleanTags({ sequence: this.sequence++ })
+      .then(this.tagResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  setAlias() {
+    this.jpush
+      .setAlias({ sequence: this.sequence++, alias: "TestAlias" })
+      .then(this.aliasResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  getAlias() {
+    this.jpush
+      .getAlias({ sequence: this.sequence++ })
+      .then(this.aliasResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  deleteAlias() {
+    this.jpush
+      .deleteAlias({ sequence: this.sequence++ })
+      .then(this.aliasResultHandler)
+      .catch(this.errorHandler);
+  }
+
+  addLocalNotification() {
+    if (this.devicePlatform == "Android") {
+      this.jpush.addLocalNotification(0, "Hello JPush", "JPush", 1, 5000);
+    } else {
+      this.jpush.addLocalNotificationForIOS(5, "Hello JPush", 1, "localNoti1");
+    }
+  }
+}
diff --git a/plugins/jpush-phonegap-plugin/ionic/index.ts b/plugins/jpush-phonegap-plugin/ionic/index.ts
new file mode 100644 (file)
index 0000000..0da14e7
--- /dev/null
@@ -0,0 +1,210 @@
+import { Plugin, Cordova, IonicNativePlugin } from '@ionic-native/core';
+import { Injectable } from '@angular/core';
+
+export interface TagOptions {
+  sequence: number;
+  tags?: Array<string>;
+}
+
+export interface AliasOptions {
+  sequence: number;
+  alias?: string;
+}
+
+@Plugin({
+  pluginName: 'JPush',
+  plugin: 'jpush-phonegap-plugin',
+  pluginRef: 'plugins.jPushPlugin',
+  repo: 'https://github.com/jpush/jpush-phonegap-plugin',
+  install: 'ionic cordova plugin add jpush-phonegap-plugin --variable APP_KEY=your_app_key',
+  installVariables: ['APP_KEY'],
+  platforms: ['Android', 'iOS']
+})
+@Injectable()
+export class JPush extends IonicNativePlugin {
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS', 'Android']
+   })
+  init(): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS', 'Android']
+   })
+  setDebugMode(enable: boolean): void {  }
+
+  @Cordova()
+  getRegistrationID(): Promise<any> { return; }
+
+  @Cordova()
+  stopPush(): Promise<any> { return; }
+
+  @Cordova()
+  resumePush(): Promise<any> { return; }
+
+  @Cordova()
+  isPushStopped(): Promise<any> { return; }
+
+  @Cordova()
+  setTags(params: TagOptions): Promise<any> { return; }
+
+  @Cordova()
+  addTags(params: TagOptions): Promise<any> { return; }
+
+  @Cordova()
+  deleteTags(params: TagOptions): Promise<any> { return; }
+
+  @Cordova()
+  cleanTags(params: TagOptions): Promise<any> { return; }
+
+  @Cordova()
+  getAllTags(params: TagOptions): Promise<any> { return; }
+
+  /**
+   * @param params { sequence: number, tag: string }
+   */
+  @Cordova()
+  checkTagBindState(params: object): Promise<any> { return; }
+
+  @Cordova()
+  setAlias(params: AliasOptions): Promise<any> { return; }
+
+  @Cordova()
+  deleteAlias(params: AliasOptions): Promise<any> { return; }
+
+  @Cordova()
+  getAlias(params: AliasOptions): Promise<any> { return; }
+
+  /**
+   * Determinate whether the application notification has been opened.
+   * 
+   * iOS: 0: closed; >1: opened.
+   *  UIRemoteNotificationTypeNone = 0,
+   *  UIRemoteNotificationTypeBadge = 1 << 0,
+   *  UIRemoteNotificationTypeSound = 1 << 1,
+   *  UIRemoteNotificationTypeAlert = 1 << 2,
+   *  UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3
+   * 
+   * Android: 0: closed; 1: opened.
+   */
+  @Cordova()
+  getUserNotificationSettings(): Promise<any> { return; }
+
+  @Cordova()
+  clearLocalNotifications(): Promise<any> { return; }
+
+  // iOS API - start
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  setBadge(badge: number): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  resetBadge(): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  setApplicationIconBadgeNumber(badge: number): void {  }
+
+  @Cordova()
+  getApplicationIconBadgeNumber(): Promise<any> { return; }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  addLocalNotificationForIOS(delayTime: number, content: string, badge: number, identifierKey: string, extras?: object): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  deleteLocalNotificationWithIdentifierKeyInIOS(identifierKey: string): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  addDismissActions(actions: Array<object>, categoryId: string): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  addNotificationActions(actions: Array<object>, categoryId: string): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  setLocation(latitude: number, longitude: number): void {  }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  startLogPageView(pageName: string): void { return; }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  stopLogPageView(pageName: string): void { return; }
+
+  @Cordova({
+    sync: true,
+    platforms: ['iOS']
+   })
+  beginLogPageView(pageName: string, duration: number): void { return; }
+
+  // iOS API - end
+
+  // Android API - start
+
+  @Cordova()
+  getConnectionState(): Promise<any> { return; }
+
+  @Cordova()
+  setBasicPushNotificationBuilder(): Promise<any> { return; }
+
+  @Cordova()
+  setCustomPushNotificationBuilder(): Promise<any> { return; }
+
+  @Cordova()
+  clearAllNotification(): Promise<any> { return; }
+
+  @Cordova()
+  clearNotificationById(id: number): Promise<any> { return; }
+
+  @Cordova()
+  setLatestNotificationNum(num: number): Promise<any> { return; }
+
+  @Cordova()
+  addLocalNotification(builderId: number, content: string, title: string, notificationId: number, broadcastTime: number, extras?: string): Promise<any> { return; }
+
+  @Cordova()
+  removeLocalNotification(notificationId: number): Promise<any> { return; }
+
+  @Cordova()
+  reportNotificationOpened(msgId: number): Promise<any> { return; }
+
+  @Cordova()
+  requestPermission(): Promise<any> { return; }
+
+  @Cordova()
+  setSilenceTime(startHour: number, startMinute: number, endHour: number, endMinute: number): Promise<any> { return; }
+
+  @Cordova()
+  setPushTime(weekdays: Array<string>, startHour: number, endHour: number): Promise<any> { return; }
+
+  // Android API - end
+}
diff --git a/plugins/jpush-phonegap-plugin/ionic/jpush/index.d.ts b/plugins/jpush-phonegap-plugin/ionic/jpush/index.d.ts
new file mode 100644 (file)
index 0000000..e74dbc6
--- /dev/null
@@ -0,0 +1,97 @@
+import { IonicNativePlugin } from '@ionic-native/core';
+export interface TagOptions {
+    sequence: number;
+    tags?: Array<string>;
+}
+export interface AliasOptions {
+    sequence: number;
+    alias?: string;
+}
+/**
+ * @name jpush
+ * @description
+ * This plugin does something
+ *
+ * @usage
+ * ```typescript
+ * import { jpush } from '@ionic-native/jpush';
+ *
+ *
+ * constructor(private jpush: jpush) { }
+ *
+ * ...
+ *
+ *
+ * this.jpush.functionName('Hello', 123)
+ *   .then((res: any) => console.log(res))
+ *   .catch((error: any) => console.error(error));
+ *
+ * ```
+ */
+export declare class JPushOriginal extends IonicNativePlugin {
+    /**
+     * This function does something
+     * @param arg1 {string} Some param to configure something
+     * @param arg2 {number} Another param to configure something
+     * @return {Promise<any>} Returns a promise that resolves when something happens
+     */
+    functionName(arg1: string, arg2: number): Promise<any>;
+    init(): void;
+    setDebugMode(enable: boolean): void;
+    getRegistrationID(): Promise<any>;
+    stopPush(): Promise<any>;
+    resumePush(): Promise<any>;
+    isPushStopped(): Promise<any>;
+    setTags(params: TagOptions): Promise<any>;
+    addTags(params: TagOptions): Promise<any>;
+    deleteTags(params: TagOptions): Promise<any>;
+    cleanTags(params: TagOptions): Promise<any>;
+    getAllTags(params: TagOptions): Promise<any>;
+    /**
+     * @param params { sequence: number, tag: string }
+     */
+    checkTagBindState(params: object): Promise<any>;
+    setAlias(params: AliasOptions): Promise<any>;
+    deleteAlias(params: AliasOptions): Promise<any>;
+    getAlias(params: AliasOptions): Promise<any>;
+    /**
+     * Determinate whether the application notification has been opened.
+     *
+     * iOS: 0: closed; >1: opened.
+     *  UIRemoteNotificationTypeNone = 0,
+     *  UIRemoteNotificationTypeBadge = 1 << 0,
+     *  UIRemoteNotificationTypeSound = 1 << 1,
+     *  UIRemoteNotificationTypeAlert = 1 << 2,
+     *  UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3
+     *
+     * Android: 0: closed; 1: opened.
+     */
+    getUserNotificationSettings(): Promise<any>;
+    clearLocalNotifications(): Promise<any>;
+    setBadge(badge: number): void;
+    resetBadge(): void;
+    setApplicationIconBadgeNumber(badge: number): void;
+    getApplicationIconBadgeNumber(): Promise<any>;
+    addLocalNotificationForIOS(delayTime: number, content: string, badge: number, identifierKey: string, extras?: object): void;
+    deleteLocalNotificationWithIdentifierKeyInIOS(identifierKey: string): void;
+    addDismissActions(actions: Array<object>, categoryId: string): void;
+    addNotificationActions(actions: Array<object>, categoryId: string): void;
+    setLocation(latitude: number, longitude: number): void;
+    startLogPageView(pageName: string): void;
+    stopLogPageView(pageName: string): void;
+    beginLogPageView(pageName: string, duration: number): void;
+    getConnectionState(): Promise<any>;
+    setBasicPushNotificationBuilder(): Promise<any>;
+    setCustomPushNotificationBuilder(): Promise<any>;
+    clearAllNotification(): Promise<any>;
+    clearNotificationById(id: number): Promise<any>;
+    setLatestNotificationNum(num: number): Promise<any>;
+    addLocalNotification(builderId: number, content: string, title: string, notificationId: number, broadcastTime: number, extras?: string): Promise<any>;
+    removeLocalNotification(notificationId: number): Promise<any>;
+    reportNotificationOpened(msgId: number): Promise<any>;
+    requestPermission(): Promise<any>;
+    setSilenceTime(startHour: number, startMinute: number, endHour: number, endMinute: number): Promise<any>;
+    setPushTime(weekdays: Array<string>, startHour: number, endHour: number): Promise<any>;
+}
+
+export declare const JPush: JPushOriginal;
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/ionic/jpush/index.js b/plugins/jpush-phonegap-plugin/ionic/jpush/index.js
new file mode 100644 (file)
index 0000000..0e1c15e
--- /dev/null
@@ -0,0 +1,73 @@
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+import { IonicNativePlugin, cordova } from '@ionic-native/core';
+var JPushOriginal = /** @class */ (function (_super) {
+    __extends(JPushOriginal, _super);
+    function JPushOriginal() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    JPushOriginal.prototype.functionName = function (arg1, arg2) { return cordova(this, "functionName", {}, arguments); };
+    JPushOriginal.prototype.init = function () { return cordova(this, "init", { "sync": true, "platforms": ["iOS", "Android"] }, arguments); };
+    JPushOriginal.prototype.setDebugMode = function (enable) { return cordova(this, "setDebugMode", { "sync": true, "platforms": ["iOS", "Android"] }, arguments); };
+    JPushOriginal.prototype.getRegistrationID = function () { return cordova(this, "getRegistrationID", {}, arguments); };
+    JPushOriginal.prototype.stopPush = function () { return cordova(this, "stopPush", {}, arguments); };
+    JPushOriginal.prototype.resumePush = function () { return cordova(this, "resumePush", {}, arguments); };
+    JPushOriginal.prototype.isPushStopped = function () { return cordova(this, "isPushStopped", {}, arguments); };
+    JPushOriginal.prototype.setTags = function (params) { return cordova(this, "setTags", {}, arguments); };
+    JPushOriginal.prototype.addTags = function (params) { return cordova(this, "addTags", {}, arguments); };
+    JPushOriginal.prototype.deleteTags = function (params) { return cordova(this, "deleteTags", {}, arguments); };
+    JPushOriginal.prototype.cleanTags = function (params) { return cordova(this, "cleanTags", {}, arguments); };
+    JPushOriginal.prototype.getAllTags = function (params) { return cordova(this, "getAllTags", {}, arguments); };
+    JPushOriginal.prototype.checkTagBindState = function (params) { return cordova(this, "checkTagBindState", {}, arguments); };
+    JPushOriginal.prototype.setAlias = function (params) { return cordova(this, "setAlias", {}, arguments); };
+    JPushOriginal.prototype.deleteAlias = function (params) { return cordova(this, "deleteAlias", {}, arguments); };
+    JPushOriginal.prototype.getAlias = function (params) { return cordova(this, "getAlias", {}, arguments); };
+    JPushOriginal.prototype.getUserNotificationSettings = function () { return cordova(this, "getUserNotificationSettings", {}, arguments); };
+    JPushOriginal.prototype.clearLocalNotifications = function () { return cordova(this, "clearLocalNotifications", {}, arguments); };
+    JPushOriginal.prototype.setBadge = function (badge) { return cordova(this, "setBadge", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.resetBadge = function () { return cordova(this, "resetBadge", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.setApplicationIconBadgeNumber = function (badge) { return cordova(this, "setApplicationIconBadgeNumber", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.getApplicationIconBadgeNumber = function () { return cordova(this, "getApplicationIconBadgeNumber", {}, arguments); };
+    JPushOriginal.prototype.addLocalNotificationForIOS = function (delayTime, content, badge, identifierKey, extras) { return cordova(this, "addLocalNotificationForIOS", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.deleteLocalNotificationWithIdentifierKeyInIOS = function (identifierKey) { return cordova(this, "deleteLocalNotificationWithIdentifierKeyInIOS", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.addDismissActions = function (actions, categoryId) { return cordova(this, "addDismissActions", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.addNotificationActions = function (actions, categoryId) { return cordova(this, "addNotificationActions", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.setLocation = function (latitude, longitude) { return cordova(this, "setLocation", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.startLogPageView = function (pageName) { return cordova(this, "startLogPageView", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.stopLogPageView = function (pageName) { return cordova(this, "stopLogPageView", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.beginLogPageView = function (pageName, duration) { return cordova(this, "beginLogPageView", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPushOriginal.prototype.getConnectionState = function () { return cordova(this, "getConnectionState", {}, arguments); };
+    JPushOriginal.prototype.setBasicPushNotificationBuilder = function () { return cordova(this, "setBasicPushNotificationBuilder", {}, arguments); };
+    JPushOriginal.prototype.setCustomPushNotificationBuilder = function () { return cordova(this, "setCustomPushNotificationBuilder", {}, arguments); };
+    JPushOriginal.prototype.clearAllNotification = function () { return cordova(this, "clearAllNotification", {}, arguments); };
+    JPushOriginal.prototype.clearNotificationById = function (id) { return cordova(this, "clearNotificationById", {}, arguments); };
+    JPushOriginal.prototype.setLatestNotificationNum = function (num) { return cordova(this, "setLatestNotificationNum", {}, arguments); };
+    JPushOriginal.prototype.addLocalNotification = function (builderId, content, title, notificationId, broadcastTime, extras) { return cordova(this, "addLocalNotification", {}, arguments); };
+    JPushOriginal.prototype.removeLocalNotification = function (notificationId) { return cordova(this, "removeLocalNotification", {}, arguments); };
+    JPushOriginal.prototype.reportNotificationOpened = function (msgId) { return cordova(this, "reportNotificationOpened", {}, arguments); };
+    JPushOriginal.prototype.requestPermission = function () { return cordova(this, "requestPermission", {}, arguments); };
+    JPushOriginal.prototype.setSilenceTime = function (startHour, startMinute, endHour, endMinute) { return cordova(this, "setSilenceTime", {}, arguments); };
+    JPushOriginal.prototype.setPushTime = function (weekdays, startHour, endHour) { return cordova(this, "setPushTime", {}, arguments); };
+    JPushOriginal.pluginName = "JPush";
+    JPushOriginal.plugin = "jpush-phonegap-plugin";
+    JPushOriginal.pluginRef = "plugins.jPushPlugin";
+    JPushOriginal.repo = "https://github.com/jpush/jpush-phonegap-plugin";
+    JPushOriginal.install = "ionic cordova plugin add jpush-phonegap-plugin --variable APP_KEY=your_app_key";
+    JPushOriginal.installVariables = ["APP_KEY"];
+    JPushOriginal.platforms = ["Android", "iOS"];
+    return JPushOriginal;
+}(IonicNativePlugin));
+var JPush = new JPushOriginal();
+export { JPush };
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvQGlvbmljLW5hdGl2ZS9wbHVnaW5zL2pwdXNoL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7QUFZQSxPQUFPLDhCQUEwRixNQUFNLG9CQUFvQixDQUFDOztJQXVEakcseUJBQWlCOzs7O0lBUzFDLDRCQUFZLGFBQUMsSUFBWSxFQUFFLElBQVk7SUFRdkMsb0JBQUk7SUFNSiw0QkFBWSxhQUFDLE1BQWU7SUFHNUIsaUNBQWlCO0lBR2pCLHdCQUFRO0lBR1IsMEJBQVU7SUFHViw2QkFBYTtJQUdiLHVCQUFPLGFBQUMsTUFBa0I7SUFHMUIsdUJBQU8sYUFBQyxNQUFrQjtJQUcxQiwwQkFBVSxhQUFDLE1BQWtCO0lBRzdCLHlCQUFTLGFBQUMsTUFBa0I7SUFHNUIsMEJBQVUsYUFBQyxNQUFrQjtJQU03QixpQ0FBaUIsYUFBQyxNQUFjO0lBR2hDLHdCQUFRLGFBQUMsTUFBb0I7SUFHN0IsMkJBQVcsYUFBQyxNQUFvQjtJQUdoQyx3QkFBUSxhQUFDLE1BQW9CO0lBZTdCLDJDQUEyQjtJQUczQix1Q0FBdUI7SUFRdkIsd0JBQVEsYUFBQyxLQUFhO0lBTXRCLDBCQUFVO0lBTVYsNkNBQTZCLGFBQUMsS0FBYTtJQUczQyw2Q0FBNkI7SUFNN0IsMENBQTBCLGFBQUMsU0FBaUIsRUFBRSxPQUFlLEVBQUUsS0FBYSxFQUFFLGFBQXFCLEVBQUUsTUFBZTtJQU1wSCw2REFBNkMsYUFBQyxhQUFxQjtJQU1uRSxpQ0FBaUIsYUFBQyxPQUFzQixFQUFFLFVBQWtCO0lBTTVELHNDQUFzQixhQUFDLE9BQXNCLEVBQUUsVUFBa0I7SUFNakUsMkJBQVcsYUFBQyxRQUFnQixFQUFFLFNBQWlCO0lBTS9DLGdDQUFnQixhQUFDLFFBQWdCO0lBTWpDLCtCQUFlLGFBQUMsUUFBZ0I7SUFNaEMsZ0NBQWdCLGFBQUMsUUFBZ0IsRUFBRSxRQUFnQjtJQU9uRCxrQ0FBa0I7SUFHbEIsK0NBQStCO0lBRy9CLGdEQUFnQztJQUdoQyxvQ0FBb0I7SUFHcEIscUNBQXFCLGFBQUMsRUFBVTtJQUdoQyx3Q0FBd0IsYUFBQyxHQUFXO0lBR3BDLG9DQUFvQixhQUFDLFNBQWlCLEVBQUUsT0FBZSxFQUFFLEtBQWEsRUFBRSxjQUFzQixFQUFFLGFBQXFCLEVBQUUsTUFBZTtJQUd0SSx1Q0FBdUIsYUFBQyxjQUFzQjtJQUc5Qyx3Q0FBd0IsYUFBQyxLQUFhO0lBR3RDLGlDQUFpQjtJQUdqQiw4QkFBYyxhQUFDLFNBQWlCLEVBQUUsV0FBbUIsRUFBRSxPQUFlLEVBQUUsU0FBaUI7SUFHekYsMkJBQVcsYUFBQyxRQUF1QixFQUFFLFNBQWlCLEVBQUUsT0FBZTs7Ozs7Ozs7Z0JBclF6RTtFQW1FMkIsaUJBQWlCO1NBQS9CLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRoaXMgaXMgYSB0ZW1wbGF0ZSBmb3IgbmV3IHBsdWdpbiB3cmFwcGVyc1xuICpcbiAqIFRPRE86XG4gKiAtIEFkZC9DaGFuZ2UgaW5mb3JtYXRpb24gYmVsb3dcbiAqIC0gRG9jdW1lbnQgdXNhZ2UgKGltcG9ydGluZywgZXhlY3V0aW5nIG1haW4gZnVuY3Rpb25hbGl0eSlcbiAqIC0gUmVtb3ZlIGFueSBpbXBvcnRzIHRoYXQgeW91IGFyZSBub3QgdXNpbmdcbiAqIC0gUmVtb3ZlIGFsbCB0aGUgY29tbWVudHMgaW5jbHVkZWQgaW4gdGhpcyB0ZW1wbGF0ZSwgRVhDRVBUIHRoZSBAUGx1Z2luIHdyYXBwZXIgZG9jcyBhbmQgYW55IG90aGVyIGRvY3MgeW91IGFkZGVkXG4gKiAtIFJlbW92ZSB0aGlzIG5vdGVcbiAqXG4gKi9cbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFBsdWdpbiwgQ29yZG92YSwgQ29yZG92YVByb3BlcnR5LCBDb3Jkb3ZhSW5zdGFuY2UsIEluc3RhbmNlUHJvcGVydHksIElvbmljTmF0aXZlUGx1Z2luIH0gZnJvbSAnQGlvbmljLW5hdGl2ZS9jb3JlJztcbmltcG9ydCB7IE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcblxuZXhwb3J0IGludGVyZmFjZSBUYWdPcHRpb25zIHtcbiAgc2VxdWVuY2U6IG51bWJlcjtcbiAgdGFncz86IEFycmF5PHN0cmluZz47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQWxpYXNPcHRpb25zIHtcbiAgc2VxdWVuY2U6IG51bWJlcjtcbiAgYWxpYXM/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogQG5hbWUganB1c2hcbiAqIEBkZXNjcmlwdGlvblxuICogVGhpcyBwbHVnaW4gZG9lcyBzb21ldGhpbmdcbiAqXG4gKiBAdXNhZ2VcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCB7IGpwdXNoIH0gZnJvbSAnQGlvbmljLW5hdGl2ZS9qcHVzaCc7XG4gKlxuICpcbiAqIGNvbnN0cnVjdG9yKHByaXZhdGUganB1c2g6IGpwdXNoKSB7IH1cbiAqXG4gKiAuLi5cbiAqXG4gKlxuICogdGhpcy5qcHVzaC5mdW5jdGlvbk5hbWUoJ0hlbGxvJywgMTIzKVxuICogICAudGhlbigocmVzOiBhbnkpID0+IGNvbnNvbGUubG9nKHJlcykpXG4gKiAgIC5jYXRjaCgoZXJyb3I6IGFueSkgPT4gY29uc29sZS5lcnJvcihlcnJvcikpO1xuICpcbiAqIGBgYFxuICovXG4vLyBAUGx1Z2luKHtcbi8vICAgcGx1Z2luTmFtZTogJ2pwdXNoJyxcbi8vICAgcGx1Z2luOiAnJywgLy8gbnBtIHBhY2thZ2UgbmFtZSwgZXhhbXBsZTogY29yZG92YS1wbHVnaW4tY2FtZXJhXG4vLyAgIHBsdWdpblJlZjogJycsIC8vIHRoZSB2YXJpYWJsZSByZWZlcmVuY2UgdG8gY2FsbCB0aGUgcGx1Z2luLCBleGFtcGxlOiBuYXZpZ2F0b3IuZ2VvbG9jYXRpb25cbi8vICAgcmVwbzogJycsIC8vIHRoZSBnaXRodWIgcmVwb3NpdG9yeSBVUkwgZm9yIHRoZSBwbHVnaW5cbi8vICAgaW5zdGFsbDogJycsIC8vIE9QVElPTkFMIGluc3RhbGwgY29tbWFuZCwgaW4gY2FzZSB0aGUgcGx1Z2luIHJlcXVpcmVzIHZhcmlhYmxlc1xuLy8gICBpbnN0YWxsVmFyaWFibGVzOiBbXSwgLy8gT1BUSU9OQUwgdGhlIHBsdWdpbiByZXF1aXJlcyB2YXJpYWJsZXNcbi8vICAgcGxhdGZvcm1zOiBbXSAvLyBBcnJheSBvZiBwbGF0Zm9ybXMgc3VwcG9ydGVkLCBleGFtcGxlOiBbJ0FuZHJvaWQnLCAnaU9TJ11cbi8vIH0pXG5AUGx1Z2luKHtcbiAgcGx1Z2luTmFtZTogJ0pQdXNoJyxcbiAgcGx1Z2luOiAnanB1c2gtcGhvbmVnYXAtcGx1Z2luJyxcbiAgcGx1Z2luUmVmOiAncGx1Z2lucy5qUHVzaFBsdWdpbicsXG4gIHJlcG86ICdodHRwczovL2dpdGh1Yi5jb20vanB1c2gvanB1c2gtcGhvbmVnYXAtcGx1Z2luJyxcbiAgaW5zdGFsbDogJ2lvbmljIGNvcmRvdmEgcGx1Z2luIGFkZCBqcHVzaC1waG9uZWdhcC1wbHVnaW4gLS12YXJpYWJsZSBBUFBfS0VZPXlvdXJfYXBwX2tleScsXG4gIGluc3RhbGxWYXJpYWJsZXM6IFsnQVBQX0tFWSddLFxuICBwbGF0Zm9ybXM6IFsnQW5kcm9pZCcsICdpT1MnXVxufSlcbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIEpQdXNoIGV4dGVuZHMgSW9uaWNOYXRpdmVQbHVnaW4ge1xuXG4gIC8qKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGRvZXMgc29tZXRoaW5nXG4gICAqIEBwYXJhbSBhcmcxIHtzdHJpbmd9IFNvbWUgcGFyYW0gdG8gY29uZmlndXJlIHNvbWV0aGluZ1xuICAgKiBAcGFyYW0gYXJnMiB7bnVtYmVyfSBBbm90aGVyIHBhcmFtIHRvIGNvbmZpZ3VyZSBzb21ldGhpbmdcbiAgICogQHJldHVybiB7UHJvbWlzZTxhbnk+fSBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gc29tZXRoaW5nIGhhcHBlbnNcbiAgICovXG4gIEBDb3Jkb3ZhKClcbiAgZnVuY3Rpb25OYW1lKGFyZzE6IHN0cmluZywgYXJnMjogbnVtYmVyKTogUHJvbWlzZTxhbnk+IHtcbiAgICByZXR1cm47IC8vIFdlIGFkZCByZXR1cm47IGhlcmUgdG8gYXZvaWQgYW55IElERSAvIENvbXBpbGVyIGVycm9yc1xuICB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUycsICdBbmRyb2lkJ11cbiAgIH0pXG4gIGluaXQoKTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUycsICdBbmRyb2lkJ11cbiAgIH0pXG4gIHNldERlYnVnTW9kZShlbmFibGU6IGJvb2xlYW4pOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSgpXG4gIGdldFJlZ2lzdHJhdGlvbklEKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgc3RvcFB1c2goKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICByZXN1bWVQdXNoKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgaXNQdXNoU3RvcHBlZCgpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHNldFRhZ3MocGFyYW1zOiBUYWdPcHRpb25zKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBhZGRUYWdzKHBhcmFtczogVGFnT3B0aW9ucyk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgZGVsZXRlVGFncyhwYXJhbXM6IFRhZ09wdGlvbnMpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGNsZWFuVGFncyhwYXJhbXM6IFRhZ09wdGlvbnMpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGdldEFsbFRhZ3MocGFyYW1zOiBUYWdPcHRpb25zKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSBwYXJhbXMgeyBzZXF1ZW5jZTogbnVtYmVyLCB0YWc6IHN0cmluZyB9XG4gICAqL1xuICBAQ29yZG92YSgpXG4gIGNoZWNrVGFnQmluZFN0YXRlKHBhcmFtczogb2JqZWN0KTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBzZXRBbGlhcyhwYXJhbXM6IEFsaWFzT3B0aW9ucyk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgZGVsZXRlQWxpYXMocGFyYW1zOiBBbGlhc09wdGlvbnMpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGdldEFsaWFzKHBhcmFtczogQWxpYXNPcHRpb25zKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgLyoqXG4gICAqIERldGVybWluYXRlIHdoZXRoZXIgdGhlIGFwcGxpY2F0aW9uIG5vdGlmaWNhdGlvbiBoYXMgYmVlbiBvcGVuZWQuXG4gICAqIFxuICAgKiBpT1M6IDA6IGNsb3NlZDsgPjE6IG9wZW5lZC5cbiAgICogIFVJUmVtb3RlTm90aWZpY2F0aW9uVHlwZU5vbmUgPSAwLFxuICAgKiAgVUlSZW1vdGVOb3RpZmljYXRpb25UeXBlQmFkZ2UgPSAxIDw8IDAsXG4gICAqICBVSVJlbW90ZU5vdGlmaWNhdGlvblR5cGVTb3VuZCA9IDEgPDwgMSxcbiAgICogIFVJUmVtb3RlTm90aWZpY2F0aW9uVHlwZUFsZXJ0ID0gMSA8PCAyLFxuICAgKiAgVUlSZW1vdGVOb3RpZmljYXRpb25UeXBlTmV3c3N0YW5kQ29udGVudEF2YWlsYWJpbGl0eSA9IDEgPDwgM1xuICAgKiBcbiAgICogQW5kcm9pZDogMDogY2xvc2VkOyAxOiBvcGVuZWQuXG4gICAqL1xuICBAQ29yZG92YSgpXG4gIGdldFVzZXJOb3RpZmljYXRpb25TZXR0aW5ncygpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGNsZWFyTG9jYWxOb3RpZmljYXRpb25zKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIC8vIGlPUyBBUEkgLSBzdGFydFxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnXVxuICAgfSlcbiAgc2V0QmFkZ2UoYmFkZ2U6IG51bWJlcik6IHZvaWQgeyAgfVxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnXVxuICAgfSlcbiAgcmVzZXRCYWRnZSgpOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIHNldEFwcGxpY2F0aW9uSWNvbkJhZGdlTnVtYmVyKGJhZGdlOiBudW1iZXIpOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSgpXG4gIGdldEFwcGxpY2F0aW9uSWNvbkJhZGdlTnVtYmVyKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnXVxuICAgfSlcbiAgYWRkTG9jYWxOb3RpZmljYXRpb25Gb3JJT1MoZGVsYXlUaW1lOiBudW1iZXIsIGNvbnRlbnQ6IHN0cmluZywgYmFkZ2U6IG51bWJlciwgaWRlbnRpZmllcktleTogc3RyaW5nLCBleHRyYXM/OiBvYmplY3QpOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIGRlbGV0ZUxvY2FsTm90aWZpY2F0aW9uV2l0aElkZW50aWZpZXJLZXlJbklPUyhpZGVudGlmaWVyS2V5OiBzdHJpbmcpOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIGFkZERpc21pc3NBY3Rpb25zKGFjdGlvbnM6IEFycmF5PG9iamVjdD4sIGNhdGVnb3J5SWQ6IHN0cmluZyk6IHZvaWQgeyAgfVxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnXVxuICAgfSlcbiAgYWRkTm90aWZpY2F0aW9uQWN0aW9ucyhhY3Rpb25zOiBBcnJheTxvYmplY3Q+LCBjYXRlZ29yeUlkOiBzdHJpbmcpOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIHNldExvY2F0aW9uKGxhdGl0dWRlOiBudW1iZXIsIGxvbmdpdHVkZTogbnVtYmVyKTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUyddXG4gICB9KVxuICBzdGFydExvZ1BhZ2VWaWV3KHBhZ2VOYW1lOiBzdHJpbmcpOiB2b2lkIHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUyddXG4gICB9KVxuICBzdG9wTG9nUGFnZVZpZXcocGFnZU5hbWU6IHN0cmluZyk6IHZvaWQgeyByZXR1cm47IH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIGJlZ2luTG9nUGFnZVZpZXcocGFnZU5hbWU6IHN0cmluZywgZHVyYXRpb246IG51bWJlcik6IHZvaWQgeyByZXR1cm47IH1cblxuICAvLyBpT1MgQVBJIC0gZW5kXG5cbiAgLy8gQW5kcm9pZCBBUEkgLSBzdGFydFxuXG4gIEBDb3Jkb3ZhKClcbiAgZ2V0Q29ubmVjdGlvblN0YXRlKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgc2V0QmFzaWNQdXNoTm90aWZpY2F0aW9uQnVpbGRlcigpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHNldEN1c3RvbVB1c2hOb3RpZmljYXRpb25CdWlsZGVyKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgY2xlYXJBbGxOb3RpZmljYXRpb24oKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBjbGVhck5vdGlmaWNhdGlvbkJ5SWQoaWQ6IG51bWJlcik6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgc2V0TGF0ZXN0Tm90aWZpY2F0aW9uTnVtKG51bTogbnVtYmVyKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBhZGRMb2NhbE5vdGlmaWNhdGlvbihidWlsZGVySWQ6IG51bWJlciwgY29udGVudDogc3RyaW5nLCB0aXRsZTogc3RyaW5nLCBub3RpZmljYXRpb25JZDogbnVtYmVyLCBicm9hZGNhc3RUaW1lOiBudW1iZXIsIGV4dHJhcz86IHN0cmluZyk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgcmVtb3ZlTG9jYWxOb3RpZmljYXRpb24obm90aWZpY2F0aW9uSWQ6IG51bWJlcik6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgcmVwb3J0Tm90aWZpY2F0aW9uT3BlbmVkKG1zZ0lkOiBudW1iZXIpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHJlcXVlc3RQZXJtaXNzaW9uKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgc2V0U2lsZW5jZVRpbWUoc3RhcnRIb3VyOiBudW1iZXIsIHN0YXJ0TWludXRlOiBudW1iZXIsIGVuZEhvdXI6IG51bWJlciwgZW5kTWludXRlOiBudW1iZXIpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHNldFB1c2hUaW1lKHdlZWtkYXlzOiBBcnJheTxzdHJpbmc+LCBzdGFydEhvdXI6IG51bWJlciwgZW5kSG91cjogbnVtYmVyKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgLy8gQW5kcm9pZCBBUEkgLSBlbmRcblxufVxuIl19
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.d.ts b/plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.d.ts
new file mode 100644 (file)
index 0000000..7aaabf5
--- /dev/null
@@ -0,0 +1,95 @@
+import { IonicNativePlugin } from '@ionic-native/core';
+export interface TagOptions {
+    sequence: number;
+    tags?: Array<string>;
+}
+export interface AliasOptions {
+    sequence: number;
+    alias?: string;
+}
+/**
+ * @name jpush
+ * @description
+ * This plugin does something
+ *
+ * @usage
+ * ```typescript
+ * import { jpush } from '@ionic-native/jpush';
+ *
+ *
+ * constructor(private jpush: jpush) { }
+ *
+ * ...
+ *
+ *
+ * this.jpush.functionName('Hello', 123)
+ *   .then((res: any) => console.log(res))
+ *   .catch((error: any) => console.error(error));
+ *
+ * ```
+ */
+export declare class JPush extends IonicNativePlugin {
+    /**
+     * This function does something
+     * @param arg1 {string} Some param to configure something
+     * @param arg2 {number} Another param to configure something
+     * @return {Promise<any>} Returns a promise that resolves when something happens
+     */
+    functionName(arg1: string, arg2: number): Promise<any>;
+    init(): void;
+    setDebugMode(enable: boolean): void;
+    getRegistrationID(): Promise<any>;
+    stopPush(): Promise<any>;
+    resumePush(): Promise<any>;
+    isPushStopped(): Promise<any>;
+    setTags(params: TagOptions): Promise<any>;
+    addTags(params: TagOptions): Promise<any>;
+    deleteTags(params: TagOptions): Promise<any>;
+    cleanTags(params: TagOptions): Promise<any>;
+    getAllTags(params: TagOptions): Promise<any>;
+    /**
+     * @param params { sequence: number, tag: string }
+     */
+    checkTagBindState(params: object): Promise<any>;
+    setAlias(params: AliasOptions): Promise<any>;
+    deleteAlias(params: AliasOptions): Promise<any>;
+    getAlias(params: AliasOptions): Promise<any>;
+    /**
+     * Determinate whether the application notification has been opened.
+     *
+     * iOS: 0: closed; >1: opened.
+     *  UIRemoteNotificationTypeNone = 0,
+     *  UIRemoteNotificationTypeBadge = 1 << 0,
+     *  UIRemoteNotificationTypeSound = 1 << 1,
+     *  UIRemoteNotificationTypeAlert = 1 << 2,
+     *  UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3
+     *
+     * Android: 0: closed; 1: opened.
+     */
+    getUserNotificationSettings(): Promise<any>;
+    clearLocalNotifications(): Promise<any>;
+    setBadge(badge: number): void;
+    resetBadge(): void;
+    setApplicationIconBadgeNumber(badge: number): void;
+    getApplicationIconBadgeNumber(): Promise<any>;
+    addLocalNotificationForIOS(delayTime: number, content: string, badge: number, identifierKey: string, extras?: object): void;
+    deleteLocalNotificationWithIdentifierKeyInIOS(identifierKey: string): void;
+    addDismissActions(actions: Array<object>, categoryId: string): void;
+    addNotificationActions(actions: Array<object>, categoryId: string): void;
+    setLocation(latitude: number, longitude: number): void;
+    startLogPageView(pageName: string): void;
+    stopLogPageView(pageName: string): void;
+    beginLogPageView(pageName: string, duration: number): void;
+    getConnectionState(): Promise<any>;
+    setBasicPushNotificationBuilder(): Promise<any>;
+    setCustomPushNotificationBuilder(): Promise<any>;
+    clearAllNotification(): Promise<any>;
+    clearNotificationById(id: number): Promise<any>;
+    setLatestNotificationNum(num: number): Promise<any>;
+    addLocalNotification(builderId: number, content: string, title: string, notificationId: number, broadcastTime: number, extras?: string): Promise<any>;
+    removeLocalNotification(notificationId: number): Promise<any>;
+    reportNotificationOpened(msgId: number): Promise<any>;
+    requestPermission(): Promise<any>;
+    setSilenceTime(startHour: number, startMinute: number, endHour: number, endMinute: number): Promise<any>;
+    setPushTime(weekdays: Array<string>, startHour: number, endHour: number): Promise<any>;
+}
diff --git a/plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.js b/plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.js
new file mode 100644 (file)
index 0000000..01cc8b1
--- /dev/null
@@ -0,0 +1,95 @@
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+/**
+ * This is a template for new plugin wrappers
+ *
+ * TODO:
+ * - Add/Change information below
+ * - Document usage (importing, executing main functionality)
+ * - Remove any imports that you are not using
+ * - Remove all the comments included in this template, EXCEPT the @Plugin wrapper docs and any other docs you added
+ * - Remove this note
+ *
+ */
+import { Injectable } from '@angular/core';
+import { IonicNativePlugin, cordova } from '@ionic-native/core';
+var JPush = /** @class */ (function (_super) {
+    __extends(JPush, _super);
+    function JPush() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    JPush.prototype.functionName = function (arg1, arg2) { return cordova(this, "functionName", {}, arguments); };
+    JPush.prototype.init = function () { return cordova(this, "init", { "sync": true, "platforms": ["iOS", "Android"] }, arguments); };
+    JPush.prototype.setDebugMode = function (enable) { return cordova(this, "setDebugMode", { "sync": true, "platforms": ["iOS", "Android"] }, arguments); };
+    JPush.prototype.getRegistrationID = function () { return cordova(this, "getRegistrationID", {}, arguments); };
+    JPush.prototype.stopPush = function () { return cordova(this, "stopPush", {}, arguments); };
+    JPush.prototype.resumePush = function () { return cordova(this, "resumePush", {}, arguments); };
+    JPush.prototype.isPushStopped = function () { return cordova(this, "isPushStopped", {}, arguments); };
+    JPush.prototype.setTags = function (params) { return cordova(this, "setTags", {}, arguments); };
+    JPush.prototype.addTags = function (params) { return cordova(this, "addTags", {}, arguments); };
+    JPush.prototype.deleteTags = function (params) { return cordova(this, "deleteTags", {}, arguments); };
+    JPush.prototype.cleanTags = function (params) { return cordova(this, "cleanTags", {}, arguments); };
+    JPush.prototype.getAllTags = function (params) { return cordova(this, "getAllTags", {}, arguments); };
+    JPush.prototype.checkTagBindState = function (params) { return cordova(this, "checkTagBindState", {}, arguments); };
+    JPush.prototype.setAlias = function (params) { return cordova(this, "setAlias", {}, arguments); };
+    JPush.prototype.deleteAlias = function (params) { return cordova(this, "deleteAlias", {}, arguments); };
+    JPush.prototype.getAlias = function (params) { return cordova(this, "getAlias", {}, arguments); };
+    JPush.prototype.getUserNotificationSettings = function () { return cordova(this, "getUserNotificationSettings", {}, arguments); };
+    JPush.prototype.clearLocalNotifications = function () { return cordova(this, "clearLocalNotifications", {}, arguments); };
+    JPush.prototype.setBadge = function (badge) { return cordova(this, "setBadge", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.resetBadge = function () { return cordova(this, "resetBadge", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.setApplicationIconBadgeNumber = function (badge) { return cordova(this, "setApplicationIconBadgeNumber", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.getApplicationIconBadgeNumber = function () { return cordova(this, "getApplicationIconBadgeNumber", {}, arguments); };
+    JPush.prototype.addLocalNotificationForIOS = function (delayTime, content, badge, identifierKey, extras) { return cordova(this, "addLocalNotificationForIOS", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.deleteLocalNotificationWithIdentifierKeyInIOS = function (identifierKey) { return cordova(this, "deleteLocalNotificationWithIdentifierKeyInIOS", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.addDismissActions = function (actions, categoryId) { return cordova(this, "addDismissActions", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.addNotificationActions = function (actions, categoryId) { return cordova(this, "addNotificationActions", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.setLocation = function (latitude, longitude) { return cordova(this, "setLocation", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.startLogPageView = function (pageName) { return cordova(this, "startLogPageView", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.stopLogPageView = function (pageName) { return cordova(this, "stopLogPageView", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.beginLogPageView = function (pageName, duration) { return cordova(this, "beginLogPageView", { "sync": true, "platforms": ["iOS"] }, arguments); };
+    JPush.prototype.getConnectionState = function () { return cordova(this, "getConnectionState", {}, arguments); };
+    JPush.prototype.setBasicPushNotificationBuilder = function () { return cordova(this, "setBasicPushNotificationBuilder", {}, arguments); };
+    JPush.prototype.setCustomPushNotificationBuilder = function () { return cordova(this, "setCustomPushNotificationBuilder", {}, arguments); };
+    JPush.prototype.clearAllNotification = function () { return cordova(this, "clearAllNotification", {}, arguments); };
+    JPush.prototype.clearNotificationById = function (id) { return cordova(this, "clearNotificationById", {}, arguments); };
+    JPush.prototype.setLatestNotificationNum = function (num) { return cordova(this, "setLatestNotificationNum", {}, arguments); };
+    JPush.prototype.addLocalNotification = function (builderId, content, title, notificationId, broadcastTime, extras) { return cordova(this, "addLocalNotification", {}, arguments); };
+    JPush.prototype.removeLocalNotification = function (notificationId) { return cordova(this, "removeLocalNotification", {}, arguments); };
+    JPush.prototype.reportNotificationOpened = function (msgId) { return cordova(this, "reportNotificationOpened", {}, arguments); };
+    JPush.prototype.requestPermission = function () { return cordova(this, "requestPermission", {}, arguments); };
+    JPush.prototype.setSilenceTime = function (startHour, startMinute, endHour, endMinute) { return cordova(this, "setSilenceTime", {}, arguments); };
+    JPush.prototype.setPushTime = function (weekdays, startHour, endHour) { return cordova(this, "setPushTime", {}, arguments); };
+    JPush.pluginName = "JPush";
+    JPush.plugin = "jpush-phonegap-plugin";
+    JPush.pluginRef = "plugins.jPushPlugin";
+    JPush.repo = "https://github.com/jpush/jpush-phonegap-plugin";
+    JPush.install = "ionic cordova plugin add jpush-phonegap-plugin --variable APP_KEY=your_app_key";
+    JPush.installVariables = ["APP_KEY"];
+    JPush.platforms = ["Android", "iOS"];
+    JPush = __decorate([
+        Injectable({
+            providedIn: 'root'
+        })
+    ], JPush);
+    return JPush;
+}(IonicNativePlugin));
+export { JPush };
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvQGlvbmljLW5hdGl2ZS9wbHVnaW5zL2pwdXNoL25neC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7Ozs7Ozs7Ozs7R0FVRztBQUNILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyw4QkFBMEYsTUFBTSxvQkFBb0IsQ0FBQzs7SUF1RGpHLHlCQUFpQjs7OztJQVMxQyw0QkFBWSxhQUFDLElBQVksRUFBRSxJQUFZO0lBUXZDLG9CQUFJO0lBTUosNEJBQVksYUFBQyxNQUFlO0lBRzVCLGlDQUFpQjtJQUdqQix3QkFBUTtJQUdSLDBCQUFVO0lBR1YsNkJBQWE7SUFHYix1QkFBTyxhQUFDLE1BQWtCO0lBRzFCLHVCQUFPLGFBQUMsTUFBa0I7SUFHMUIsMEJBQVUsYUFBQyxNQUFrQjtJQUc3Qix5QkFBUyxhQUFDLE1BQWtCO0lBRzVCLDBCQUFVLGFBQUMsTUFBa0I7SUFNN0IsaUNBQWlCLGFBQUMsTUFBYztJQUdoQyx3QkFBUSxhQUFDLE1BQW9CO0lBRzdCLDJCQUFXLGFBQUMsTUFBb0I7SUFHaEMsd0JBQVEsYUFBQyxNQUFvQjtJQWU3QiwyQ0FBMkI7SUFHM0IsdUNBQXVCO0lBUXZCLHdCQUFRLGFBQUMsS0FBYTtJQU10QiwwQkFBVTtJQU1WLDZDQUE2QixhQUFDLEtBQWE7SUFHM0MsNkNBQTZCO0lBTTdCLDBDQUEwQixhQUFDLFNBQWlCLEVBQUUsT0FBZSxFQUFFLEtBQWEsRUFBRSxhQUFxQixFQUFFLE1BQWU7SUFNcEgsNkRBQTZDLGFBQUMsYUFBcUI7SUFNbkUsaUNBQWlCLGFBQUMsT0FBc0IsRUFBRSxVQUFrQjtJQU01RCxzQ0FBc0IsYUFBQyxPQUFzQixFQUFFLFVBQWtCO0lBTWpFLDJCQUFXLGFBQUMsUUFBZ0IsRUFBRSxTQUFpQjtJQU0vQyxnQ0FBZ0IsYUFBQyxRQUFnQjtJQU1qQywrQkFBZSxhQUFDLFFBQWdCO0lBTWhDLGdDQUFnQixhQUFDLFFBQWdCLEVBQUUsUUFBZ0I7SUFPbkQsa0NBQWtCO0lBR2xCLCtDQUErQjtJQUcvQixnREFBZ0M7SUFHaEMsb0NBQW9CO0lBR3BCLHFDQUFxQixhQUFDLEVBQVU7SUFHaEMsd0NBQXdCLGFBQUMsR0FBVztJQUdwQyxvQ0FBb0IsYUFBQyxTQUFpQixFQUFFLE9BQWUsRUFBRSxLQUFhLEVBQUUsY0FBc0IsRUFBRSxhQUFxQixFQUFFLE1BQWU7SUFHdEksdUNBQXVCLGFBQUMsY0FBc0I7SUFHOUMsd0NBQXdCLGFBQUMsS0FBYTtJQUd0QyxpQ0FBaUI7SUFHakIsOEJBQWMsYUFBQyxTQUFpQixFQUFFLFdBQW1CLEVBQUUsT0FBZSxFQUFFLFNBQWlCO0lBR3pGLDJCQUFXLGFBQUMsUUFBdUIsRUFBRSxTQUFpQixFQUFFLE9BQWU7Ozs7Ozs7O0lBbE01RCxLQUFLO1FBSGpCLFVBQVUsQ0FBQztZQUNWLFVBQVUsRUFBRSxNQUFNO1NBQ25CLENBQUM7T0FDVyxLQUFLO2dCQW5FbEI7RUFtRTJCLGlCQUFpQjtTQUEvQixLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaGlzIGlzIGEgdGVtcGxhdGUgZm9yIG5ldyBwbHVnaW4gd3JhcHBlcnNcbiAqXG4gKiBUT0RPOlxuICogLSBBZGQvQ2hhbmdlIGluZm9ybWF0aW9uIGJlbG93XG4gKiAtIERvY3VtZW50IHVzYWdlIChpbXBvcnRpbmcsIGV4ZWN1dGluZyBtYWluIGZ1bmN0aW9uYWxpdHkpXG4gKiAtIFJlbW92ZSBhbnkgaW1wb3J0cyB0aGF0IHlvdSBhcmUgbm90IHVzaW5nXG4gKiAtIFJlbW92ZSBhbGwgdGhlIGNvbW1lbnRzIGluY2x1ZGVkIGluIHRoaXMgdGVtcGxhdGUsIEVYQ0VQVCB0aGUgQFBsdWdpbiB3cmFwcGVyIGRvY3MgYW5kIGFueSBvdGhlciBkb2NzIHlvdSBhZGRlZFxuICogLSBSZW1vdmUgdGhpcyBub3RlXG4gKlxuICovXG5pbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBQbHVnaW4sIENvcmRvdmEsIENvcmRvdmFQcm9wZXJ0eSwgQ29yZG92YUluc3RhbmNlLCBJbnN0YW5jZVByb3BlcnR5LCBJb25pY05hdGl2ZVBsdWdpbiB9IGZyb20gJ0Bpb25pYy1uYXRpdmUvY29yZSc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVGFnT3B0aW9ucyB7XG4gIHNlcXVlbmNlOiBudW1iZXI7XG4gIHRhZ3M/OiBBcnJheTxzdHJpbmc+O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEFsaWFzT3B0aW9ucyB7XG4gIHNlcXVlbmNlOiBudW1iZXI7XG4gIGFsaWFzPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIEBuYW1lIGpwdXNoXG4gKiBAZGVzY3JpcHRpb25cbiAqIFRoaXMgcGx1Z2luIGRvZXMgc29tZXRoaW5nXG4gKlxuICogQHVzYWdlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBqcHVzaCB9IGZyb20gJ0Bpb25pYy1uYXRpdmUvanB1c2gnO1xuICpcbiAqXG4gKiBjb25zdHJ1Y3Rvcihwcml2YXRlIGpwdXNoOiBqcHVzaCkgeyB9XG4gKlxuICogLi4uXG4gKlxuICpcbiAqIHRoaXMuanB1c2guZnVuY3Rpb25OYW1lKCdIZWxsbycsIDEyMylcbiAqICAgLnRoZW4oKHJlczogYW55KSA9PiBjb25zb2xlLmxvZyhyZXMpKVxuICogICAuY2F0Y2goKGVycm9yOiBhbnkpID0+IGNvbnNvbGUuZXJyb3IoZXJyb3IpKTtcbiAqXG4gKiBgYGBcbiAqL1xuLy8gQFBsdWdpbih7XG4vLyAgIHBsdWdpbk5hbWU6ICdqcHVzaCcsXG4vLyAgIHBsdWdpbjogJycsIC8vIG5wbSBwYWNrYWdlIG5hbWUsIGV4YW1wbGU6IGNvcmRvdmEtcGx1Z2luLWNhbWVyYVxuLy8gICBwbHVnaW5SZWY6ICcnLCAvLyB0aGUgdmFyaWFibGUgcmVmZXJlbmNlIHRvIGNhbGwgdGhlIHBsdWdpbiwgZXhhbXBsZTogbmF2aWdhdG9yLmdlb2xvY2F0aW9uXG4vLyAgIHJlcG86ICcnLCAvLyB0aGUgZ2l0aHViIHJlcG9zaXRvcnkgVVJMIGZvciB0aGUgcGx1Z2luXG4vLyAgIGluc3RhbGw6ICcnLCAvLyBPUFRJT05BTCBpbnN0YWxsIGNvbW1hbmQsIGluIGNhc2UgdGhlIHBsdWdpbiByZXF1aXJlcyB2YXJpYWJsZXNcbi8vICAgaW5zdGFsbFZhcmlhYmxlczogW10sIC8vIE9QVElPTkFMIHRoZSBwbHVnaW4gcmVxdWlyZXMgdmFyaWFibGVzXG4vLyAgIHBsYXRmb3JtczogW10gLy8gQXJyYXkgb2YgcGxhdGZvcm1zIHN1cHBvcnRlZCwgZXhhbXBsZTogWydBbmRyb2lkJywgJ2lPUyddXG4vLyB9KVxuQFBsdWdpbih7XG4gIHBsdWdpbk5hbWU6ICdKUHVzaCcsXG4gIHBsdWdpbjogJ2pwdXNoLXBob25lZ2FwLXBsdWdpbicsXG4gIHBsdWdpblJlZjogJ3BsdWdpbnMualB1c2hQbHVnaW4nLFxuICByZXBvOiAnaHR0cHM6Ly9naXRodWIuY29tL2pwdXNoL2pwdXNoLXBob25lZ2FwLXBsdWdpbicsXG4gIGluc3RhbGw6ICdpb25pYyBjb3Jkb3ZhIHBsdWdpbiBhZGQganB1c2gtcGhvbmVnYXAtcGx1Z2luIC0tdmFyaWFibGUgQVBQX0tFWT15b3VyX2FwcF9rZXknLFxuICBpbnN0YWxsVmFyaWFibGVzOiBbJ0FQUF9LRVknXSxcbiAgcGxhdGZvcm1zOiBbJ0FuZHJvaWQnLCAnaU9TJ11cbn0pXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBKUHVzaCBleHRlbmRzIElvbmljTmF0aXZlUGx1Z2luIHtcblxuICAvKipcbiAgICogVGhpcyBmdW5jdGlvbiBkb2VzIHNvbWV0aGluZ1xuICAgKiBAcGFyYW0gYXJnMSB7c3RyaW5nfSBTb21lIHBhcmFtIHRvIGNvbmZpZ3VyZSBzb21ldGhpbmdcbiAgICogQHBhcmFtIGFyZzIge251bWJlcn0gQW5vdGhlciBwYXJhbSB0byBjb25maWd1cmUgc29tZXRoaW5nXG4gICAqIEByZXR1cm4ge1Byb21pc2U8YW55Pn0gUmV0dXJucyBhIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHNvbWV0aGluZyBoYXBwZW5zXG4gICAqL1xuICBAQ29yZG92YSgpXG4gIGZ1bmN0aW9uTmFtZShhcmcxOiBzdHJpbmcsIGFyZzI6IG51bWJlcik6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuOyAvLyBXZSBhZGQgcmV0dXJuOyBoZXJlIHRvIGF2b2lkIGFueSBJREUgLyBDb21waWxlciBlcnJvcnNcbiAgfVxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnLCAnQW5kcm9pZCddXG4gICB9KVxuICBpbml0KCk6IHZvaWQgeyAgfVxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnLCAnQW5kcm9pZCddXG4gICB9KVxuICBzZXREZWJ1Z01vZGUoZW5hYmxlOiBib29sZWFuKTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoKVxuICBnZXRSZWdpc3RyYXRpb25JRCgpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHN0b3BQdXNoKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgcmVzdW1lUHVzaCgpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGlzUHVzaFN0b3BwZWQoKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBzZXRUYWdzKHBhcmFtczogVGFnT3B0aW9ucyk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgYWRkVGFncyhwYXJhbXM6IFRhZ09wdGlvbnMpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGRlbGV0ZVRhZ3MocGFyYW1zOiBUYWdPcHRpb25zKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBjbGVhblRhZ3MocGFyYW1zOiBUYWdPcHRpb25zKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBnZXRBbGxUYWdzKHBhcmFtczogVGFnT3B0aW9ucyk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0gcGFyYW1zIHsgc2VxdWVuY2U6IG51bWJlciwgdGFnOiBzdHJpbmcgfVxuICAgKi9cbiAgQENvcmRvdmEoKVxuICBjaGVja1RhZ0JpbmRTdGF0ZShwYXJhbXM6IG9iamVjdCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgc2V0QWxpYXMocGFyYW1zOiBBbGlhc09wdGlvbnMpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGRlbGV0ZUFsaWFzKHBhcmFtczogQWxpYXNPcHRpb25zKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBnZXRBbGlhcyhwYXJhbXM6IEFsaWFzT3B0aW9ucyk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIC8qKlxuICAgKiBEZXRlcm1pbmF0ZSB3aGV0aGVyIHRoZSBhcHBsaWNhdGlvbiBub3RpZmljYXRpb24gaGFzIGJlZW4gb3BlbmVkLlxuICAgKiBcbiAgICogaU9TOiAwOiBjbG9zZWQ7ID4xOiBvcGVuZWQuXG4gICAqICBVSVJlbW90ZU5vdGlmaWNhdGlvblR5cGVOb25lID0gMCxcbiAgICogIFVJUmVtb3RlTm90aWZpY2F0aW9uVHlwZUJhZGdlID0gMSA8PCAwLFxuICAgKiAgVUlSZW1vdGVOb3RpZmljYXRpb25UeXBlU291bmQgPSAxIDw8IDEsXG4gICAqICBVSVJlbW90ZU5vdGlmaWNhdGlvblR5cGVBbGVydCA9IDEgPDwgMixcbiAgICogIFVJUmVtb3RlTm90aWZpY2F0aW9uVHlwZU5ld3NzdGFuZENvbnRlbnRBdmFpbGFiaWxpdHkgPSAxIDw8IDNcbiAgICogXG4gICAqIEFuZHJvaWQ6IDA6IGNsb3NlZDsgMTogb3BlbmVkLlxuICAgKi9cbiAgQENvcmRvdmEoKVxuICBnZXRVc2VyTm90aWZpY2F0aW9uU2V0dGluZ3MoKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBjbGVhckxvY2FsTm90aWZpY2F0aW9ucygpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICAvLyBpT1MgQVBJIC0gc3RhcnRcblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIHNldEJhZGdlKGJhZGdlOiBudW1iZXIpOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIHJlc2V0QmFkZ2UoKTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUyddXG4gICB9KVxuICBzZXRBcHBsaWNhdGlvbkljb25CYWRnZU51bWJlcihiYWRnZTogbnVtYmVyKTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoKVxuICBnZXRBcHBsaWNhdGlvbkljb25CYWRnZU51bWJlcigpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIGFkZExvY2FsTm90aWZpY2F0aW9uRm9ySU9TKGRlbGF5VGltZTogbnVtYmVyLCBjb250ZW50OiBzdHJpbmcsIGJhZGdlOiBudW1iZXIsIGlkZW50aWZpZXJLZXk6IHN0cmluZywgZXh0cmFzPzogb2JqZWN0KTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUyddXG4gICB9KVxuICBkZWxldGVMb2NhbE5vdGlmaWNhdGlvbldpdGhJZGVudGlmaWVyS2V5SW5JT1MoaWRlbnRpZmllcktleTogc3RyaW5nKTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUyddXG4gICB9KVxuICBhZGREaXNtaXNzQWN0aW9ucyhhY3Rpb25zOiBBcnJheTxvYmplY3Q+LCBjYXRlZ29yeUlkOiBzdHJpbmcpOiB2b2lkIHsgIH1cblxuICBAQ29yZG92YSh7XG4gICAgc3luYzogdHJ1ZSxcbiAgICBwbGF0Zm9ybXM6IFsnaU9TJ11cbiAgIH0pXG4gIGFkZE5vdGlmaWNhdGlvbkFjdGlvbnMoYWN0aW9uczogQXJyYXk8b2JqZWN0PiwgY2F0ZWdvcnlJZDogc3RyaW5nKTogdm9pZCB7ICB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUyddXG4gICB9KVxuICBzZXRMb2NhdGlvbihsYXRpdHVkZTogbnVtYmVyLCBsb25naXR1ZGU6IG51bWJlcik6IHZvaWQgeyAgfVxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnXVxuICAgfSlcbiAgc3RhcnRMb2dQYWdlVmlldyhwYWdlTmFtZTogc3RyaW5nKTogdm9pZCB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKHtcbiAgICBzeW5jOiB0cnVlLFxuICAgIHBsYXRmb3JtczogWydpT1MnXVxuICAgfSlcbiAgc3RvcExvZ1BhZ2VWaWV3KHBhZ2VOYW1lOiBzdHJpbmcpOiB2b2lkIHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoe1xuICAgIHN5bmM6IHRydWUsXG4gICAgcGxhdGZvcm1zOiBbJ2lPUyddXG4gICB9KVxuICBiZWdpbkxvZ1BhZ2VWaWV3KHBhZ2VOYW1lOiBzdHJpbmcsIGR1cmF0aW9uOiBudW1iZXIpOiB2b2lkIHsgcmV0dXJuOyB9XG5cbiAgLy8gaU9TIEFQSSAtIGVuZFxuXG4gIC8vIEFuZHJvaWQgQVBJIC0gc3RhcnRcblxuICBAQ29yZG92YSgpXG4gIGdldENvbm5lY3Rpb25TdGF0ZSgpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHNldEJhc2ljUHVzaE5vdGlmaWNhdGlvbkJ1aWxkZXIoKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBzZXRDdXN0b21QdXNoTm90aWZpY2F0aW9uQnVpbGRlcigpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIGNsZWFyQWxsTm90aWZpY2F0aW9uKCk6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgY2xlYXJOb3RpZmljYXRpb25CeUlkKGlkOiBudW1iZXIpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHNldExhdGVzdE5vdGlmaWNhdGlvbk51bShudW06IG51bWJlcik6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIEBDb3Jkb3ZhKClcbiAgYWRkTG9jYWxOb3RpZmljYXRpb24oYnVpbGRlcklkOiBudW1iZXIsIGNvbnRlbnQ6IHN0cmluZywgdGl0bGU6IHN0cmluZywgbm90aWZpY2F0aW9uSWQ6IG51bWJlciwgYnJvYWRjYXN0VGltZTogbnVtYmVyLCBleHRyYXM/OiBzdHJpbmcpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHJlbW92ZUxvY2FsTm90aWZpY2F0aW9uKG5vdGlmaWNhdGlvbklkOiBudW1iZXIpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHJlcG9ydE5vdGlmaWNhdGlvbk9wZW5lZChtc2dJZDogbnVtYmVyKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICByZXF1ZXN0UGVybWlzc2lvbigpOiBQcm9taXNlPGFueT4geyByZXR1cm47IH1cblxuICBAQ29yZG92YSgpXG4gIHNldFNpbGVuY2VUaW1lKHN0YXJ0SG91cjogbnVtYmVyLCBzdGFydE1pbnV0ZTogbnVtYmVyLCBlbmRIb3VyOiBudW1iZXIsIGVuZE1pbnV0ZTogbnVtYmVyKTogUHJvbWlzZTxhbnk+IHsgcmV0dXJuOyB9XG5cbiAgQENvcmRvdmEoKVxuICBzZXRQdXNoVGltZSh3ZWVrZGF5czogQXJyYXk8c3RyaW5nPiwgc3RhcnRIb3VyOiBudW1iZXIsIGVuZEhvdXI6IG51bWJlcik6IFByb21pc2U8YW55PiB7IHJldHVybjsgfVxuXG4gIC8vIEFuZHJvaWQgQVBJIC0gZW5kXG5cbn1cbiJdfQ==
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.metadata.json b/plugins/jpush-phonegap-plugin/ionic/jpush/ngx/index.metadata.json
new file mode 100644 (file)
index 0000000..73891e7
--- /dev/null
@@ -0,0 +1 @@
+[{"__symbolic":"module","version":4,"metadata":{"TagOptions":{"__symbolic":"interface"},"AliasOptions":{"__symbolic":"interface"},"JPush":{"__symbolic":"class","extends":{"__symbolic":"reference","module":"@ionic-native/core","name":"IonicNativePlugin","line":67,"character":27},"decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable","line":64,"character":1},"arguments":[{"providedIn":"root"}]}],"members":{"functionName":[{"__symbolic":"method"}],"init":[{"__symbolic":"method"}],"setDebugMode":[{"__symbolic":"method"}],"getRegistrationID":[{"__symbolic":"method"}],"stopPush":[{"__symbolic":"method"}],"resumePush":[{"__symbolic":"method"}],"isPushStopped":[{"__symbolic":"method"}],"setTags":[{"__symbolic":"method"}],"addTags":[{"__symbolic":"method"}],"deleteTags":[{"__symbolic":"method"}],"cleanTags":[{"__symbolic":"method"}],"getAllTags":[{"__symbolic":"method"}],"checkTagBindState":[{"__symbolic":"method"}],"setAlias":[{"__symbolic":"method"}],"deleteAlias":[{"__symbolic":"method"}],"getAlias":[{"__symbolic":"method"}],"getUserNotificationSettings":[{"__symbolic":"method"}],"clearLocalNotifications":[{"__symbolic":"method"}],"setBadge":[{"__symbolic":"method"}],"resetBadge":[{"__symbolic":"method"}],"setApplicationIconBadgeNumber":[{"__symbolic":"method"}],"getApplicationIconBadgeNumber":[{"__symbolic":"method"}],"addLocalNotificationForIOS":[{"__symbolic":"method"}],"deleteLocalNotificationWithIdentifierKeyInIOS":[{"__symbolic":"method"}],"addDismissActions":[{"__symbolic":"method"}],"addNotificationActions":[{"__symbolic":"method"}],"setLocation":[{"__symbolic":"method"}],"startLogPageView":[{"__symbolic":"method"}],"stopLogPageView":[{"__symbolic":"method"}],"beginLogPageView":[{"__symbolic":"method"}],"getConnectionState":[{"__symbolic":"method"}],"setBasicPushNotificationBuilder":[{"__symbolic":"method"}],"setCustomPushNotificationBuilder":[{"__symbolic":"method"}],"clearAllNotification":[{"__symbolic":"method"}],"clearNotificationById":[{"__symbolic":"method"}],"setLatestNotificationNum":[{"__symbolic":"method"}],"addLocalNotification":[{"__symbolic":"method"}],"removeLocalNotification":[{"__symbolic":"method"}],"reportNotificationOpened":[{"__symbolic":"method"}],"requestPermission":[{"__symbolic":"method"}],"setSilenceTime":[{"__symbolic":"method"}],"setPushTime":[{"__symbolic":"method"}]}}}}]
diff --git a/plugins/jpush-phonegap-plugin/ionic/jpush/package.json b/plugins/jpush-phonegap-plugin/ionic/jpush/package.json
new file mode 100644 (file)
index 0000000..d25c4c6
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "name": "@jiguang-ionic/jpush",
+  "version": "2.0.0",
+  "description": "JPush support for ionic-native",
+  "module": "index.js",
+  "typings": "index.d.ts",
+  "author": "hevin",
+  "license": "MIT",
+  "peerDependencies": {
+    "@ionic-native/core": "^5.1.0",
+    "@angular/core": "*",
+    "rxjs": "^6.3.0"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jpush/jpush-phonegap-plugin"
+  }
+}
diff --git a/plugins/jpush-phonegap-plugin/license b/plugins/jpush-phonegap-plugin/license
new file mode 100644 (file)
index 0000000..1221272
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) JiGuang <support@jpush.cn> (jiguang.cn)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/plugins/jpush-phonegap-plugin/package.json b/plugins/jpush-phonegap-plugin/package.json
new file mode 100644 (file)
index 0000000..6e22eb8
--- /dev/null
@@ -0,0 +1,62 @@
+{
+  "_from": "jpush-phonegap-plugin",
+  "_id": "jpush-phonegap-plugin@3.7.2",
+  "_inBundle": false,
+  "_integrity": "sha512-q5gGu0m1uf6Mf1puFd9o1hp32rok+/GgR39ZDGWdYULc6KX5zy932wdzLDlTYpjBW9Gec/HZmj1HTbQIMvppiA==",
+  "_location": "/jpush-phonegap-plugin",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "jpush-phonegap-plugin",
+    "name": "jpush-phonegap-plugin",
+    "escapedName": "jpush-phonegap-plugin",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/jpush-phonegap-plugin/-/jpush-phonegap-plugin-3.7.2.tgz",
+  "_shasum": "59c085065a0d5d636965f20ed2fdcd30f8a23b2c",
+  "_spec": "jpush-phonegap-plugin",
+  "_where": "/Users/shuwei/works2/cordova/dlapp",
+  "author": {
+    "name": "JiGuang"
+  },
+  "bugs": {
+    "url": "https://github.com/jpush/jpush-phonegap-plugin/issues"
+  },
+  "bundleDependencies": false,
+  "cordova": {
+    "id": "jpush-phonegap-plugin",
+    "platforms": [
+      "ios",
+      "android"
+    ]
+  },
+  "deprecated": false,
+  "description": "JPush for cordova plugin",
+  "devDependencies": {
+    "cordova-plugin-device": "*",
+    "cordova-plugin-jcore": ">=1.3.0"
+  },
+  "homepage": "https://github.com/jpush/jpush-phonegap-plugin#readme",
+  "keywords": [
+    "JPush",
+    "push",
+    "Push",
+    "ecosystem:cordova",
+    "cordova-ios",
+    "cordova-android"
+  ],
+  "license": "MIT",
+  "name": "jpush-phonegap-plugin",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jpush/jpush-phonegap-plugin.git"
+  },
+  "version": "3.7.2"
+}
diff --git a/plugins/jpush-phonegap-plugin/plugin.xml b/plugins/jpush-phonegap-plugin/plugin.xml
new file mode 100644 (file)
index 0000000..b902c26
--- /dev/null
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
+  xmlns:android="http://schemas.android.com/apk/res/android"
+  id="jpush-phonegap-plugin"
+  version="3.7.2">
+
+    <name>JPush</name>
+    <description>JPush for cordova plugin</description>
+    <author>JPush</author>
+    <keywords>JPush,push</keywords>
+    <license>MIT License</license>
+
+    <preference name="APP_KEY" />
+    <preference name="CHANNEL" default="developer-default" />
+
+    <engines>
+        <engine name="cordova" version=">=3.0" />
+    </engines>
+
+    <!-- dependencies -->
+    <dependency id="cordova-plugin-device" />
+    <dependency id="cordova-plugin-jcore" />
+
+    <js-module src="www/JPushPlugin.js" name="JPushPlugin">
+        <clobbers target="JPush" />
+    </js-module>
+
+    <platform name="ios">
+        <config-file target="config.xml" parent="/*">
+            <feature name="JPushPlugin">
+                <param name="ios-package" value="JPushPlugin" />
+            </feature>
+        </config-file>
+        <config-file target="*-Info.plist" parent="UIBackgroundModes">
+            <array>
+                <string>remote-notification</string>
+            </array>
+        </config-file>
+        <config-file target="*-Debug.plist" parent="aps-environment">
+            <string>development</string>
+        </config-file>
+        <config-file target="*-Release.plist" parent="aps-environment">
+            <string>production</string>
+        </config-file>
+        <header-file src="src/ios/Plugins/JPushDefine.h" />
+        <header-file src="src/ios/Plugins/JPushPlugin.h" />
+        <source-file src="src/ios/Plugins/JPushPlugin.m" />
+        <header-file src="src/ios/Plugins/AppDelegate+JPush.h" />
+        <source-file src="src/ios/Plugins/AppDelegate+JPush.m" />
+
+        <header-file src="src/ios/lib/JPUSHService.h" />
+        <source-file src="src/ios/lib/jpush-ios-3.2.1.a" framework="true" />
+        <resource-file src="src/ios/JPushConfig.plist" />
+
+        <framework src="CFNetwork.framework" weak="true" />
+        <framework src="CoreFoundation.framework" weak="true" />
+        <framework src="CoreTelephony.framework" weak="true" />
+        <framework src="SystemConfiguration.framework" weak="true" />
+        <framework src="CoreGraphics.framework" weak="true" />
+        <framework src="Foundation.framework" weak="true" />
+        <framework src="UIKit.framework" weak="true" />
+        <framework src="Security.framework" weak="true" />
+        <framework src="libz.tbd" weak="true" />
+        <framework src="AdSupport.framework" weak="true" />
+        <framework src="UserNotifications.framework" weak="true" />
+        <framework src="libresolv.tbd" weak="true" />
+
+        <config-file target="*JPushConfig.plist" parent="Appkey">
+            <string>$APP_KEY</string>
+        </config-file>
+    </platform>
+
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="/*">
+            <feature name="JPushPlugin">
+                <param name="android-package" value="cn.jiguang.cordova.push.JPushPlugin" />
+            </feature>
+        </config-file>
+
+        <config-file target="AndroidManifest.xml" parent="/manifest" mode="merge">
+            <!-- Required  一些系统要求的权限,如访问网络等 -->
+            <permission android:name="$PACKAGE_NAME.permission.JPUSH_MESSAGE"
+               android:protectionLevel="signature" />
+
+            <!-- Required  一些系统要求的权限,如访问网络等-->
+            <uses-permission android:name="$PACKAGE_NAME.permission.JPUSH_MESSAGE" />
+            <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
+            <uses-permission android:name="android.permission.INTERNET" />
+            <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+            <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+            <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+            <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+            <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+
+
+
+            <!-- Optional for location -->
+            <uses-permission android:name="android.permission.VIBRATE" />
+            <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 用于开启 debug 版本的应用在6.0 系统上 层叠窗口权限 -->
+            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+            <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+            <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+            <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /><!-- Android Q后台定位权限-->
+            <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+            <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+            <uses-permission android:name="android.permission.GET_TASKS" />
+        </config-file>
+
+        <config-file target="AndroidManifest.xml" parent="/manifest/application" mode="merge">
+
+
+            <!-- Rich push 核心功能 since 2.0.6-->
+            <activity
+                    android:name="cn.jpush.android.ui.PopWinActivity"
+                    android:theme="@style/MyDialogStyle"
+                    android:exported="false">
+            </activity>
+
+            <!-- Required SDK核心功能-->
+            <activity
+                    android:name="cn.jpush.android.ui.PushActivity"
+                    android:configChanges="orientation|keyboardHidden"
+                    android:theme="@android:style/Theme.NoTitleBar"
+                    android:exported="false">
+                <intent-filter>
+                    <action android:name="cn.jpush.android.ui.PushActivity" />
+                    <category android:name="android.intent.category.DEFAULT" />
+                    <category android:name="$PACKAGE_NAME" />
+                </intent-filter>
+            </activity>
+
+            <!-- Required SDK 核心功能-->
+            <!-- 可配置android:process参数将PushService放在其他进程中 -->
+            <service
+                    android:name="cn.jpush.android.service.PushService"
+                    android:process=":pushcore"
+                    android:exported="false">
+                <intent-filter>
+                    <action android:name="cn.jpush.android.intent.REGISTER" />
+                    <action android:name="cn.jpush.android.intent.REPORT" />
+                    <action android:name="cn.jpush.android.intent.PushService" />
+                    <action android:name="cn.jpush.android.intent.PUSH_TIME" />
+                </intent-filter>
+            </service>
+
+            <!-- since 3.0.9 Required SDK 核心功能-->
+            <provider
+                    android:authorities="$PACKAGE_NAME.DataProvider"
+                    android:name="cn.jpush.android.service.DataProvider"
+                    android:process=":pushcore"
+                    android:exported="false"
+            />
+
+            <!-- since 1.8.0 option 可选项。用于同一设备中不同应用的JPush服务相互拉起的功能。 -->
+            <!-- 若不启用该功能可删除该组件,将不拉起其他应用也不能被其他应用拉起 -->
+            <service
+                    android:name="cn.jpush.android.service.DaemonService"
+                    android:enabled="true"
+                    android:exported="true">
+                <intent-filter>
+                    <action android:name="cn.jpush.android.intent.DaemonService" />
+                    <category android:name="$PACKAGE_NAME" />
+                </intent-filter>
+
+            </service>
+
+            <!-- since 3.1.0 Required SDK 核心功能-->
+            <provider
+                    android:authorities="$PACKAGE_NAME.DownloadProvider"
+                    android:name="cn.jpush.android.service.DownloadProvider"
+                    android:exported="true"
+            />
+
+            <!-- Required SDK核心功能-->
+            <receiver
+                    android:name="cn.jpush.android.service.PushReceiver"
+                    android:enabled="true"
+                    android:exported="false">
+                <intent-filter android:priority="1000">
+                    <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" />   <!--Required  显示通知栏 -->
+                    <category android:name="$PACKAGE_NAME" />
+                </intent-filter>
+                <intent-filter>
+                    <action android:name="android.intent.action.USER_PRESENT" />
+                    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+                </intent-filter>
+                <!-- Optional -->
+                <intent-filter>
+                    <action android:name="android.intent.action.PACKAGE_ADDED" />
+                    <action android:name="android.intent.action.PACKAGE_REMOVED" />
+
+                    <data android:scheme="package" />
+                </intent-filter>
+            </receiver>
+
+            <!-- Required SDK核心功能-->
+            <receiver android:name="cn.jpush.android.service.AlarmReceiver" android:exported="false"/>
+
+            <!--since 3.3.0 接收JPush相关事件-->
+            <receiver android:name="cn.jiguang.cordova.push.JPushEventReceiver">
+                <intent-filter>
+                    <action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
+                    <category android:name="$PACKAGE_NAME"></category>
+                </intent-filter>
+            </receiver>
+
+            <!--since 3.3.0 Required SDK核心功能-->
+            <activity
+                    android:name="cn.jpush.android.service.JNotifyActivity"
+                    android:exported="true"
+                    android:taskAffinity="jpush.custom"
+                    android:theme="@android:style/Theme.Translucent.NoTitleBar">
+                <intent-filter>
+                    <action android:name="cn.jpush.android.intent.JNotifyActivity" />
+                    <category android:name="$PACKAGE_NAME" />
+                </intent-filter>
+            </activity>
+
+            <!-- since 3.3.0 Required SDK 核心功能-->
+            <!-- 可配置android:process参数将PushService放在其他进程中 -->
+            <!--User defined.  For test only 继承自cn.jpush.android.service.JCommonService-->
+            <service android:name="cn.jiguang.cordova.push.PushService"
+                     android:process=":pushcore">
+                <intent-filter>
+                    <action android:name="cn.jiguang.user.service.action" />
+                </intent-filter>
+            </service>
+
+
+            <receiver
+                    android:name="cn.jiguang.cordova.push.JPushReceiver"
+                    android:enabled="true"
+                    android:exported="false">
+                <intent-filter>
+                    <action android:name="cn.jpush.android.intent.REGISTRATION" />
+                    <action android:name="cn.jpush.android.intent.MESSAGE_RECEIVED" />
+                    <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED" />
+                    <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" />
+                    <action android:name="cn.jpush.android.intent.CONNECTION" />
+
+                    <category android:name="$PACKAGE_NAME" />
+                </intent-filter>
+            </receiver>
+
+            <!-- Required  . Enable it you can get statistics data with channel -->
+            <meta-data android:name="JPUSH_CHANNEL" android:value="$CHANNEL"/>
+            <meta-data android:name="JPUSH_APPKEY" android:value="$APP_KEY" /> <!--  </>值来自开发者平台取得的AppKey-->
+
+        </config-file>
+
+        <lib-file src="src/android/libs/jpush-android-3.3.4.jar" />
+
+        <source-file src="src/android/PushService.java" target-dir="src/cn/jiguang/cordova/push" />
+        <source-file src="src/android/JPushPlugin.java" target-dir="src/cn/jiguang/cordova/push" />
+        <source-file src="src/android/JPushReceiver.java" target-dir="src/cn/jiguang/cordova/push" />
+        <source-file src="src/android/JPushEventReceiver.java" target-dir="src/cn/jiguang/cordova/push" />
+
+        <resource-file src="src/android/res/drawable-hdpi/jpush_richpush_btn_selector.xml"
+          target="res/drawable/jpush_richpush_btn_selector.xml" />
+        <resource-file src="src/android/res/drawable-hdpi/jpush_richpush_progressbar.xml"
+          target="res/drawable/jpush_richpush_progressbar.xml" />
+
+        <resource-file src="src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png"
+          target="res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png" />
+        <resource-file src="src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png"
+          target="res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png" />
+
+        <resource-file src="src/android/res/layout/jpush_popwin_layout.xml"
+          target="res/layout/jpush_popwin_layout.xml" />
+        <resource-file src="src/android/res/layout/jpush_webview_layout.xml"
+          target="res/layout/jpush_webview_layout.xml" />
+        <resource-file src="src/android/res/layout/push_notification.xml"
+          target="res/layout/push_notification.xml" />
+
+        <resource-file src="src/android/res/values/jpush_style.xml"
+                       target="res/values/jpush_style.xml" />
+        <resource-file src="src/android/res/values/jpush_string.xml"
+                       target="res/values/jpush_string.xml" />
+
+        <resource-file src="src/android/res/values-zh/jpush_string.xml"
+                       target="res/values-zh/jpush_string.xml" />
+    </platform>
+</plugin>
diff --git a/plugins/jpush-phonegap-plugin/src/android/JPushEventReceiver.java b/plugins/jpush-phonegap-plugin/src/android/JPushEventReceiver.java
new file mode 100644 (file)
index 0000000..17184d9
--- /dev/null
@@ -0,0 +1,202 @@
+package cn.jiguang.cordova.push;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.apache.cordova.CallbackContext;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Map;
+import java.util.Set;
+
+import cn.jpush.android.api.CustomMessage;
+import cn.jpush.android.api.JPushInterface;
+import cn.jpush.android.api.JPushMessage;
+import cn.jpush.android.api.NotificationMessage;
+import cn.jpush.android.service.JPushMessageReceiver;
+
+public class JPushEventReceiver extends JPushMessageReceiver {
+
+    private static final String TAG = JPushEventReceiver.class.getSimpleName();
+
+    @Override
+    public void onTagOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onTagOperatorResult(context, jPushMessage);
+        //Log.e(TAG,"onTagOperatorResult:"+jPushMessage);
+        JSONObject resultJson = new JSONObject();
+
+        int sequence = jPushMessage.getSequence();
+        try {
+            resultJson.put("sequence", sequence);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        CallbackContext callback = JPushPlugin.eventCallbackMap.get(sequence);
+
+        if (callback == null) {
+            Log.i(TAG, "Unexpected error, callback is null!");
+            return;
+        }
+
+        if (jPushMessage.getErrorCode() == 0) { // success
+            Set<String> tags = jPushMessage.getTags();
+            JSONArray tagsJsonArr = new JSONArray();
+            for (String tag : tags) {
+                tagsJsonArr.put(tag);
+            }
+
+            try {
+                if (tagsJsonArr.length() != 0) {
+                    resultJson.put("tags", tagsJsonArr);
+                }
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.success(resultJson);
+
+        } else {
+            try {
+                resultJson.put("code", jPushMessage.getErrorCode());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.error(resultJson);
+        }
+
+        JPushPlugin.eventCallbackMap.remove(sequence);
+    }
+
+    @Override
+    public void onCheckTagOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onCheckTagOperatorResult(context, jPushMessage);
+
+        //Log.e(TAG,"onCheckTagOperatorResult:"+jPushMessage);
+        JSONObject resultJson = new JSONObject();
+
+        int sequence = jPushMessage.getSequence();
+        try {
+            resultJson.put("sequence", sequence);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        CallbackContext callback = JPushPlugin.eventCallbackMap.get(sequence);
+
+        if (callback == null) {
+            Log.i(TAG, "Unexpected error, callback is null!");
+            return;
+        }
+
+        if (jPushMessage.getErrorCode() == 0) {
+            try {
+                resultJson.put("tag", jPushMessage.getCheckTag());
+                resultJson.put("isBind", jPushMessage.getTagCheckStateResult());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+            callback.success(resultJson);
+
+        } else {
+            try {
+                resultJson.put("code", jPushMessage.getErrorCode());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+            callback.error(resultJson);
+        }
+
+        JPushPlugin.eventCallbackMap.remove(sequence);
+    }
+
+    @Override
+    public void onAliasOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onAliasOperatorResult(context, jPushMessage);
+
+        //Log.e(TAG,"onAliasOperatorResult:"+jPushMessage);
+        JSONObject resultJson = new JSONObject();
+
+        int sequence = jPushMessage.getSequence();
+        try {
+            resultJson.put("sequence", sequence);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        CallbackContext callback = JPushPlugin.eventCallbackMap.get(sequence);
+
+        if (callback == null) {
+            Log.i(TAG, "Unexpected error, callback is null!");
+            return;
+        }
+
+        if (jPushMessage.getErrorCode() == 0) { // success
+            try {
+                if (!TextUtils.isEmpty(jPushMessage.getAlias())) {
+                    resultJson.put("alias", jPushMessage.getAlias());
+                }
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.success(resultJson);
+
+        } else {
+            try {
+                resultJson.put("code", jPushMessage.getErrorCode());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+
+            callback.error(resultJson);
+        }
+
+        JPushPlugin.eventCallbackMap.remove(sequence);
+    }
+
+    @Override
+    public void onRegister(Context context, String regId) {
+        //Log.e(TAG,"onRegister:"+regId);
+        JPushPlugin.transmitReceiveRegistrationId(regId);
+    }
+
+    @Override
+    public void onMessage(Context context, CustomMessage customMessage) {
+        super.onMessage(context,customMessage);
+        //Log.e(TAG,"onMessage:"+customMessage);
+//        String msg = customMessage.message;//intent.getStringExtra(JPushInterface.EXTRA_MESSAGE);
+//        Map<String, Object> extras = getNotificationExtras(intent);
+//        JPushPlugin.transmitMessageReceive(msg, extras);
+    }
+
+    @Override
+    public void onNotifyMessageArrived(Context context, NotificationMessage notificationMessage) {
+        super.onNotifyMessageArrived(context, notificationMessage);
+
+        //Log.e(TAG,"onNotifyMessageArrived:"+notificationMessage);
+    }
+
+    @Override
+    public void onNotifyMessageOpened(Context context, NotificationMessage notificationMessage) {
+        super.onNotifyMessageOpened(context, notificationMessage);
+        //Log.e(TAG,"onNotifyMessageOpened:"+notificationMessage);
+    }
+
+    @Override
+    public void onMobileNumberOperatorResult(Context context, JPushMessage jPushMessage) {
+        super.onMobileNumberOperatorResult(context, jPushMessage);
+        //Log.e(TAG,"onMobileNumberOperatorResult:"+jPushMessage);
+    }
+
+    @Override
+    public void onMultiActionClicked(Context context, Intent intent) {
+        super.onMultiActionClicked(context, intent);
+        //Log.e(TAG,"onMultiActionClicked:"+intent);
+    }
+}
diff --git a/plugins/jpush-phonegap-plugin/src/android/JPushPlugin.java b/plugins/jpush-phonegap-plugin/src/android/JPushPlugin.java
new file mode 100644 (file)
index 0000000..867d511
--- /dev/null
@@ -0,0 +1,720 @@
+package cn.jiguang.cordova.push;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaWebView;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import cn.jpush.android.api.BasicPushNotificationBuilder;
+import cn.jpush.android.api.JPushInterface;
+import cn.jpush.android.api.TagAliasCallback;
+import cn.jpush.android.data.JPushLocalNotification;
+
+public class JPushPlugin extends CordovaPlugin {
+
+    private static final String TAG = JPushPlugin.class.getSimpleName();
+
+    private Context mContext;
+
+    private static JPushPlugin instance;
+    private static Activity cordovaActivity;
+
+    static String notificationTitle;
+    static String notificationAlert;
+    static Map<String, Object> notificationExtras = new HashMap<String, Object>();
+
+    static String openNotificationTitle;
+    static String openNotificationAlert;
+    static Map<String, Object> openNotificationExtras = new HashMap<String, Object>();
+
+    static Map<Integer, CallbackContext> eventCallbackMap = new HashMap<Integer, CallbackContext>();
+
+    public JPushPlugin() {
+        instance = this;
+    }
+
+    @Override
+    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
+        super.initialize(cordova, webView);
+        mContext = cordova.getActivity().getApplicationContext();
+
+        JPushInterface.init(mContext);
+
+        cordovaActivity = cordova.getActivity();
+
+        // 如果同时缓存了打开事件 openNotificationAlert 和 消息事件 notificationAlert,只向 UI 发打开事件。
+        // 这样做是为了和 iOS 统一。
+        if (openNotificationAlert != null) {
+            notificationAlert = null;
+            transmitNotificationOpen(openNotificationTitle, openNotificationAlert, openNotificationExtras);
+        }
+        if (notificationAlert != null) {
+            transmitNotificationReceive(notificationTitle, notificationAlert, notificationExtras);
+        }
+    }
+
+    public void onResume(boolean multitasking) {
+        if (openNotificationAlert != null) {
+            notificationAlert = null;
+            transmitNotificationOpen(openNotificationTitle, openNotificationAlert, openNotificationExtras);
+        }
+        if (notificationAlert != null) {
+            transmitNotificationReceive(notificationTitle, notificationAlert, notificationExtras);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        cordovaActivity = null;
+        instance = null;
+    }
+
+    private static JSONObject getMessageObject(String message, Map<String, Object> extras) {
+        JSONObject data = new JSONObject();
+        try {
+            data.put("message", message);
+            JSONObject jExtras = new JSONObject();
+            for (Entry<String, Object> entry : extras.entrySet()) {
+                if (entry.getKey().equals("cn.jpush.android.EXTRA")) {
+                    JSONObject jo;
+                    if (TextUtils.isEmpty((String) entry.getValue())) {
+                        jo = new JSONObject();
+                    } else {
+                        jo = new JSONObject((String) entry.getValue());
+                        String key;
+                        Iterator keys = jo.keys();
+                        while (keys.hasNext()) {
+                            key = keys.next().toString();
+                            jExtras.put(key, jo.getString(key));
+                        }
+                    }
+                    jExtras.put("cn.jpush.android.EXTRA", jo);
+                } else {
+                    jExtras.put(entry.getKey(), entry.getValue());
+                }
+            }
+            if (jExtras.length() > 0) {
+                data.put("extras", jExtras);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return data;
+    }
+
+    private static JSONObject getNotificationObject(String title, String alert, Map<String, Object> extras) {
+        JSONObject data = new JSONObject();
+        try {
+            data.put("title", title);
+            data.put("alert", alert);
+            JSONObject jExtras = new JSONObject();
+            for (Entry<String, Object> entry : extras.entrySet()) {
+                if (entry.getKey().equals("cn.jpush.android.EXTRA")) {
+                    JSONObject jo;
+                    if (TextUtils.isEmpty((String) entry.getValue())) {
+                        jo = new JSONObject();
+                    } else {
+                        jo = new JSONObject((String) entry.getValue());
+                        String key;
+                        Iterator keys = jo.keys();
+                        while (keys.hasNext()) {
+                            key = keys.next().toString();
+                            jExtras.put(key, jo.getString(key));
+                        }
+                    }
+                    jExtras.put("cn.jpush.android.EXTRA", jo);
+                } else {
+                    jExtras.put(entry.getKey(), entry.getValue());
+                }
+            }
+            if (jExtras.length() > 0) {
+                data.put("extras", jExtras);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return data;
+    }
+
+    static void transmitMessageReceive(String message, Map<String, Object> extras) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = getMessageObject(message, extras);
+        String format = "window.plugins.jPushPlugin.receiveMessageInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+    }
+
+    static void transmitNotificationOpen(String title, String alert, Map<String, Object> extras) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = getNotificationObject(title, alert, extras);
+        String format = "window.plugins.jPushPlugin.openNotificationInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+        JPushPlugin.openNotificationTitle = null;
+        JPushPlugin.openNotificationAlert = null;
+    }
+
+    static void transmitNotificationReceive(String title, String alert, Map<String, Object> extras) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = getNotificationObject(title, alert, extras);
+        String format = "window.plugins.jPushPlugin.receiveNotificationInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+        JPushPlugin.notificationTitle = null;
+        JPushPlugin.notificationAlert = null;
+    }
+
+    static void transmitReceiveRegistrationId(String rId) {
+        if (instance == null) {
+            return;
+        }
+        JSONObject data = new JSONObject();
+        try {
+            data.put("registrationId", rId);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        String format = "window.plugins.jPushPlugin.receiveRegistrationIdInAndroidCallback(%s);";
+        final String js = String.format(format, data.toString());
+        cordovaActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                instance.webView.loadUrl("javascript:" + js);
+            }
+        });
+    }
+
+    @Override
+    public boolean execute(final String action, final JSONArray data, final CallbackContext callbackContext)
+            throws JSONException {
+        cordova.getThreadPool().execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Method method = JPushPlugin.class.getDeclaredMethod(action, JSONArray.class, CallbackContext.class);
+                    method.invoke(JPushPlugin.this, data, callbackContext);
+                } catch (Exception e) {
+                    Log.e(TAG, e.toString());
+                }
+            }
+        });
+        return true;
+    }
+
+    void init(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.init(mContext);
+    }
+
+    void setDebugMode(JSONArray data, CallbackContext callbackContext) {
+        boolean mode;
+        try {
+            mode = data.getBoolean(0);
+            JPushInterface.setDebugMode(mode);
+            callbackContext.success();
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    void stopPush(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.stopPush(mContext);
+        callbackContext.success();
+    }
+
+    void resumePush(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.resumePush(mContext);
+        callbackContext.success();
+    }
+
+    void isPushStopped(JSONArray data, CallbackContext callbackContext) {
+        boolean isStopped = JPushInterface.isPushStopped(mContext);
+        if (isStopped) {
+            callbackContext.success(1);
+        } else {
+            callbackContext.success(0);
+        }
+    }
+
+    void areNotificationEnabled(JSONArray data, final CallbackContext callback) {
+        int isEnabled;
+        if (hasPermission("OP_POST_NOTIFICATION")) {
+            isEnabled = 1;
+        } else {
+            isEnabled = 0;
+        }
+        callback.success(isEnabled);
+    }
+
+    void setLatestNotificationNum(JSONArray data, CallbackContext callbackContext) {
+        int num = -1;
+        try {
+            num = data.getInt(0);
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error reading num json");
+        }
+        if (num != -1) {
+            JPushInterface.setLatestNotificationNumber(mContext, num);
+        } else {
+            callbackContext.error("error num");
+        }
+    }
+
+    void setPushTime(JSONArray data, CallbackContext callbackContext) {
+        Set<Integer> days = new HashSet<Integer>();
+        JSONArray dayArray;
+        int startHour = -1;
+        int endHour = -1;
+        try {
+            dayArray = data.getJSONArray(0);
+            for (int i = 0; i < dayArray.length(); i++) {
+                days.add(dayArray.getInt(i));
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error reading days json");
+        }
+        try {
+            startHour = data.getInt(1);
+            endHour = data.getInt(2);
+        } catch (JSONException e) {
+            callbackContext.error("error reading hour json");
+        }
+        Context context = mContext;
+        JPushInterface.setPushTime(context, days, startHour, endHour);
+        callbackContext.success();
+    }
+
+    void getRegistrationID(JSONArray data, CallbackContext callbackContext) {
+        Context context = mContext;
+        String regID = JPushInterface.getRegistrationID(context);
+        callbackContext.success(regID);
+    }
+
+    void onResume(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.onResume(this.cordova.getActivity());
+    }
+
+    void onPause(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.onPause(this.cordova.getActivity());
+    }
+
+    void reportNotificationOpened(JSONArray data, CallbackContext callbackContext) {
+        try {
+            String msgID;
+            msgID = data.getString(0);
+            JPushInterface.reportNotificationOpened(this.cordova.getActivity(), msgID);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    void setAlias(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        String alias = null;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+            alias = params.getString("alias");
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.setAlias(mContext, sequence, alias);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void deleteAlias(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.deleteAlias(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void getAlias(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.getAlias(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void setTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        Set<String> tags = new HashSet<String>();
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+            JSONArray tagsArr = params.getJSONArray("tags");
+            for (int i = 0; i < tagsArr.length(); i++) {
+                tags.add(tagsArr.getString(i));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.setTags(mContext, sequence, tags);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void addTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        Set<String> tags = new HashSet<String>();
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+            JSONArray tagsArr = params.getJSONArray("tags");
+            for (int i = 0; i < tagsArr.length(); i++) {
+                tags.add(tagsArr.getString(i));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.addTags(mContext, sequence, tags);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void deleteTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        Set<String> tags = new HashSet<String>();
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+            JSONArray tagsArr = params.getJSONArray("tags");
+            for (int i = 0; i < tagsArr.length(); i++) {
+                tags.add(tagsArr.getString(i));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.deleteTags(mContext, sequence, tags);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void cleanTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.cleanTags(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void getAllTags(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.getAllTags(mContext, sequence);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void checkTagBindState(JSONArray data, CallbackContext callbackContext) {
+        int sequence = -1;
+        String tag = null;
+
+        try {
+            JSONObject params = data.getJSONObject(0);
+            sequence = params.getInt("sequence");
+            tag = params.getString("tag");
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("Parameters error.");
+        }
+
+        JPushInterface.checkTagBindState(mContext, sequence, tag);
+        eventCallbackMap.put(sequence, callbackContext);
+    }
+
+    void getConnectionState(JSONArray data, CallbackContext callback) {
+        boolean isConnected = JPushInterface.getConnectionState(cordovaActivity.getApplicationContext());
+        if (isConnected) {
+            callback.success(1);
+        } else {
+            callback.success(0);
+        }
+    }
+
+    /**
+     * 自定义通知行为,声音、震动、呼吸灯等。
+     */
+    void setBasicPushNotificationBuilder(JSONArray data, CallbackContext callbackContext) {
+        BasicPushNotificationBuilder builder = new BasicPushNotificationBuilder(this.cordova.getActivity());
+        builder.developerArg0 = "Basic builder 1";
+        JPushInterface.setPushNotificationBuilder(1, builder);
+        JSONObject obj = new JSONObject();
+        try {
+            obj.put("id", 1);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 自定义推送通知栏样式,需要自己实现具体代码。 http://docs.jiguang.cn/client/android_tutorials/#_11
+     */
+    void setCustomPushNotificationBuilder(JSONArray data, CallbackContext callbackContext) {
+        // CustomPushNotificationBuilder builder = new CustomPushNotificationBuilder(
+        // this.cordova.getActivity(), R.layout.test_notification_layout,
+        // R.id.icon, R.id.title, R.id.text);
+        // JPushInterface.setPushNotificationBuilder(2, builder);
+        // JPushInterface.setDefaultPushNotificationBuilder(builder);
+    }
+
+    void clearAllNotification(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.clearAllNotifications(this.cordova.getActivity());
+    }
+
+    void clearNotificationById(JSONArray data, CallbackContext callbackContext) {
+        int notificationId = -1;
+        try {
+            notificationId = data.getInt(0);
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error reading id json");
+            return;
+        }
+        if (notificationId != -1) {
+            JPushInterface.clearNotificationById(this.cordova.getActivity(), notificationId);
+        } else {
+            callbackContext.error("error id");
+        }
+    }
+
+    void addLocalNotification(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        int builderId = data.getInt(0);
+        String content = data.getString(1);
+        String title = data.getString(2);
+        int notificationID = data.getInt(3);
+        int broadcastTime = data.getInt(4);
+        String extrasStr = data.isNull(5) ? "" : data.getString(5);
+        JSONObject extras = new JSONObject();
+        if (!extrasStr.isEmpty()) {
+            extras = new JSONObject(extrasStr);
+        }
+
+        JPushLocalNotification ln = new JPushLocalNotification();
+        ln.setBuilderId(builderId);
+        ln.setContent(content);
+        ln.setTitle(title);
+        ln.setNotificationId(notificationID);
+        ln.setBroadcastTime(System.currentTimeMillis() + broadcastTime);
+        ln.setExtras(extras.toString());
+
+        JPushInterface.addLocalNotification(this.cordova.getActivity(), ln);
+    }
+
+    void removeLocalNotification(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        int notificationID = data.getInt(0);
+        JPushInterface.removeLocalNotification(this.cordova.getActivity(), notificationID);
+    }
+
+    void clearLocalNotifications(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.clearLocalNotifications(this.cordova.getActivity());
+    }
+
+    /**
+     * 设置通知静默时间 http://docs.jpush.io/client/android_api/#api_5
+     */
+    void setSilenceTime(JSONArray data, CallbackContext callbackContext) {
+        try {
+            int startHour = data.getInt(0);
+            int startMinute = data.getInt(1);
+            int endHour = data.getInt(2);
+            int endMinute = data.getInt(3);
+            if (!isValidHour(startHour) || !isValidMinute(startMinute)) {
+                callbackContext.error("开始时间数值错误");
+                return;
+            }
+            if (!isValidHour(endHour) || !isValidMinute(endMinute)) {
+                callbackContext.error("结束时间数值错误");
+                return;
+            }
+            JPushInterface.setSilenceTime(this.cordova.getActivity(), startHour, startMinute, endHour, endMinute);
+        } catch (JSONException e) {
+            e.printStackTrace();
+            callbackContext.error("error: reading json data.");
+        }
+    }
+
+    void setGeofenceInterval(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        long interval = data.getLong(0);
+        JPushInterface.setGeofenceInterval(this.cordova.getActivity(), interval);
+    }
+
+    void setMaxGeofenceNumber(JSONArray data, CallbackContext callbackContext) throws JSONException {
+        int maxNumber = data.getInt(0);
+        JPushInterface.setMaxGeofenceNumber(mContext, maxNumber);
+    }
+
+    private boolean isValidHour(int hour) {
+        return !(hour < 0 || hour > 23);
+    }
+
+    private boolean isValidMinute(int minute) {
+        return !(minute < 0 || minute > 59);
+    }
+
+    /**
+     * 用于 Android 6.0 以上系统申请权限,具体可参考:
+     * http://docs.Push.io/client/android_api/#android-60
+     */
+    void requestPermission(JSONArray data, CallbackContext callbackContext) {
+        JPushInterface.requestPermission(this.cordova.getActivity());
+    }
+
+    private final TagAliasCallback mTagWithAliasCallback = new TagAliasCallback() {
+        @Override
+        public void gotResult(int code, String alias, Set<String> tags) {
+            if (instance == null) {
+                return;
+            }
+            JSONObject data = new JSONObject();
+            try {
+                data.put("resultCode", code);
+                data.put("tags", tags);
+                data.put("alias", alias);
+                final String jsEvent = String.format("cordova.fireDocumentEvent('jpush.setTagsWithAlias',%s)",
+                        data.toString());
+                cordova.getActivity().runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        instance.webView.loadUrl("javascript:" + jsEvent);
+                    }
+                });
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+    };
+
+    private boolean hasPermission(String appOpsServiceId) {
+
+        Context context = cordova.getActivity().getApplicationContext();
+        if (Build.VERSION.SDK_INT >= 24) {
+            NotificationManager mNotificationManager = (NotificationManager) context
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            return mNotificationManager.areNotificationsEnabled();
+        } else {
+            AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+            ApplicationInfo appInfo = context.getApplicationInfo();
+
+            String pkg = context.getPackageName();
+            int uid = appInfo.uid;
+            Class appOpsClazz;
+
+            try {
+                appOpsClazz = Class.forName(AppOpsManager.class.getName());
+                Method checkOpNoThrowMethod = appOpsClazz.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE,
+                        String.class);
+                Field opValue = appOpsClazz.getDeclaredField(appOpsServiceId);
+                int value = opValue.getInt(Integer.class);
+                Object result = checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg);
+                return Integer.parseInt(result.toString()) == AppOpsManager.MODE_ALLOWED;
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (NoSuchMethodException e) {
+                e.printStackTrace();
+            } catch (NoSuchFieldException e) {
+                e.printStackTrace();
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/plugins/jpush-phonegap-plugin/src/android/JPushReceiver.java b/plugins/jpush-phonegap-plugin/src/android/JPushReceiver.java
new file mode 100644 (file)
index 0000000..806a7fc
--- /dev/null
@@ -0,0 +1,86 @@
+package cn.jiguang.cordova.push;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import cn.jpush.android.api.JPushInterface;
+
+public class JPushReceiver extends BroadcastReceiver {
+
+    private static final List<String> IGNORED_EXTRAS_KEYS = Arrays.asList("cn.jpush.android.TITLE",
+            "cn.jpush.android.MESSAGE", "cn.jpush.android.APPKEY", "cn.jpush.android.NOTIFICATION_CONTENT_TITLE","key_show_entity","platform");
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (action.equals(JPushInterface.ACTION_REGISTRATION_ID)) {
+            String rId = intent.getStringExtra(JPushInterface.EXTRA_REGISTRATION_ID);
+            JPushPlugin.transmitReceiveRegistrationId(rId);
+        } else if (action.equals(JPushInterface.ACTION_MESSAGE_RECEIVED)) {
+            handlingMessageReceive(intent);
+        } else if (action.equals(JPushInterface.ACTION_NOTIFICATION_RECEIVED)) {
+            handlingNotificationReceive(context, intent);
+        } else if (action.equals(JPushInterface.ACTION_NOTIFICATION_OPENED)) {
+            handlingNotificationOpen(context, intent);
+        }
+    }
+
+    private void handlingMessageReceive(Intent intent) {
+        String msg = intent.getStringExtra(JPushInterface.EXTRA_MESSAGE);
+        Map<String, Object> extras = getNotificationExtras(intent);
+        JPushPlugin.transmitMessageReceive(msg, extras);
+    }
+
+    private void handlingNotificationOpen(Context context, Intent intent) {
+        String title = intent.getStringExtra(JPushInterface.EXTRA_NOTIFICATION_TITLE);
+        JPushPlugin.openNotificationTitle = title;
+
+        String alert = intent.getStringExtra(JPushInterface.EXTRA_ALERT);
+        JPushPlugin.openNotificationAlert = alert;
+
+        Map<String, Object> extras = getNotificationExtras(intent);
+        JPushPlugin.openNotificationExtras = extras;
+
+        JPushPlugin.transmitNotificationOpen(title, alert, extras);
+
+        Intent launch = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+        if (launch != null) {
+            launch.addCategory(Intent.CATEGORY_LAUNCHER);
+            launch.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            context.startActivity(launch);
+        }
+    }
+
+    private void handlingNotificationReceive(Context context, Intent intent) {
+        String title = intent.getStringExtra(JPushInterface.EXTRA_NOTIFICATION_TITLE);
+        JPushPlugin.notificationTitle = title;
+
+        String alert = intent.getStringExtra(JPushInterface.EXTRA_ALERT);
+        JPushPlugin.notificationAlert = alert;
+
+        Map<String, Object> extras = getNotificationExtras(intent);
+        JPushPlugin.notificationExtras = extras;
+
+        JPushPlugin.transmitNotificationReceive(title, alert, extras);
+    }
+
+    private Map<String, Object> getNotificationExtras(Intent intent) {
+        Map<String, Object> extrasMap = new HashMap<String, Object>();
+        for (String key : intent.getExtras().keySet()) {
+            if (!IGNORED_EXTRAS_KEYS.contains(key)) {
+                if (key.equals(JPushInterface.EXTRA_NOTIFICATION_ID)) {
+                    extrasMap.put(key, intent.getIntExtra(key, 0));
+                } else {
+                    extrasMap.put(key, intent.getStringExtra(key));
+                }
+            }
+        }
+        return extrasMap;
+    }
+}
diff --git a/plugins/jpush-phonegap-plugin/src/android/PushService.java b/plugins/jpush-phonegap-plugin/src/android/PushService.java
new file mode 100644 (file)
index 0000000..2b0974e
--- /dev/null
@@ -0,0 +1,6 @@
+package cn.jiguang.cordova.push;
+
+import cn.jpush.android.service.JCommonService;
+
+public class PushService extends JCommonService {
+}
diff --git a/plugins/jpush-phonegap-plugin/src/android/libs/jpush-android-3.3.4.jar b/plugins/jpush-phonegap-plugin/src/android/libs/jpush-android-3.3.4.jar
new file mode 100755 (executable)
index 0000000..fef22f8
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/src/android/libs/jpush-android-3.3.4.jar differ
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png b/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png
new file mode 100755 (executable)
index 0000000..c9f4e4d
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_back.png differ
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png b/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png
new file mode 100755 (executable)
index 0000000..f289651
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_ic_richpush_actionbar_divider.png differ
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_richpush_btn_selector.xml b/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_richpush_btn_selector.xml
new file mode 100755 (executable)
index 0000000..c6dd002
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?> 
+<selector xmlns:android="http://schemas.android.com/apk/res/android" > 
+    <!-- 获得焦点但未按下时的背景图片 --> 
+    <item 
+        android:state_focused="true" 
+        android:state_enabled="true" 
+        android:state_pressed="false" 
+        android:drawable="@drawable/jpush_ic_richpush_actionbar_back" />
+     <!-- 按下时的背景图片 --> 
+    <item 
+        android:state_enabled="true" 
+        android:state_pressed="true" 
+        android:drawable="@android:color/darker_gray" /> 
+    <!-- 按下时的背景图片 --> 
+    <item 
+        android:state_enabled="true" 
+        android:state_checked="true" 
+        android:drawable="@android:color/darker_gray" /> 
+    <!-- 默认时的背景图片 --> 
+    <item android:drawable="@drawable/jpush_ic_richpush_actionbar_back" />
+</selector> 
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_richpush_progressbar.xml b/plugins/jpush-phonegap-plugin/src/android/res/drawable-hdpi/jpush_richpush_progressbar.xml
new file mode 100755 (executable)
index 0000000..a1d9b8f
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <!-- 背景  gradient是渐变,corners定义的是圆角 -->
+    <item android:id="@android:id/background">
+        <shape>
+            <solid android:color="#ffffff" />
+        </shape>
+    </item>
+    
+    <!-- 进度条 -->
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape>
+                <solid android:color="#4393ea" />
+            </shape>
+        </clip>
+    </item>
+
+</layer-list>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/layout/jpush_popwin_layout.xml b/plugins/jpush-phonegap-plugin/src/android/res/layout/jpush_popwin_layout.xml
new file mode 100755 (executable)
index 0000000..f43e478
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/popLayoutId"
+    style="@style/MyDialogStyle"
+    android:orientation="vertical"
+    android:layout_width="280dp"
+    android:layout_height="250dp" >
+
+        <WebView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/wvPopwin"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/layout/jpush_webview_layout.xml b/plugins/jpush-phonegap-plugin/src/android/res/layout/jpush_webview_layout.xml
new file mode 100755 (executable)
index 0000000..bebdd61
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<cn.jpush.android.ui.FullScreenView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/actionbarLayoutId"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <RelativeLayout
+        android:id="@+id/rlRichpushTitleBar"
+        android:layout_width="match_parent"
+        android:layout_height="40.0dp"
+        android:background="#29313a">
+
+        <ImageButton
+            android:id="@+id/imgRichpushBtnBack"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginLeft="9dp"
+            android:layout_marginRight="10dp"
+            android:background="@drawable/jpush_richpush_btn_selector" />
+
+        <ImageView
+            android:id="@+id/imgView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toRightOf="@id/imgRichpushBtnBack"
+            android:clickable="false"
+            android:src="@drawable/jpush_ic_richpush_actionbar_divider" />
+
+        <TextView
+            android:id="@+id/tvRichpushTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginLeft="7dp"
+            android:layout_marginRight="5dp"
+            android:layout_toRightOf="@id/imgView"
+            android:clickable="false"
+            android:text=" "
+            android:textSize="20sp"
+            android:textColor="#ffffff" />
+    </RelativeLayout>
+
+    <ProgressBar
+        android:id="@+id/pushPrograssBar"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:progress="0"
+        android:progressDrawable="@drawable/jpush_richpush_progressbar"
+        style="?android:attr/progressBarStyleHorizontal" />
+    <WebView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/fullWebView"
+        android:background="#000000" />
+
+</cn.jpush.android.ui.FullScreenView>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/layout/push_notification.xml b/plugins/jpush-phonegap-plugin/src/android/res/layout/push_notification.xml
new file mode 100755 (executable)
index 0000000..299ea09
--- /dev/null
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+    android:id="@+id/push_root_view"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="4dp"
+    android:paddingTop="2dp"
+    android:paddingLeft="8dp"
+    android:paddingRight="8dp"
+    >
+
+    <ImageView
+        android:id="@+id/push_notification_bg"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="centerCrop"/>
+    <RelativeLayout
+        android:id="@+id/push_notification_style_default"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <LinearLayout
+            android:id="@+id/push_notification_layout_lefttop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:gravity="center_vertical"
+            android:orientation="horizontal">
+            <ImageView
+                android:id="@+id/push_notification_small_icon"
+                android:layout_width="18dp"
+                android:layout_height="18dp"
+                android:layout_marginLeft="6dp"
+                android:scaleType="centerInside"/>
+            <TextView
+                android:id="@+id/push_notification_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="4dp"
+                android:maxLines="1"
+                android:maxWidth="200dp"
+                android:maxLength="24"
+                android:textColor="@android:color/black"
+                android:textSize="12sp"/>
+            <TextView
+                android:id="@+id/push_notification_dot"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="4dp"
+                android:text="· "
+                android:textColor="@android:color/black"
+                android:textSize="20sp"/>
+            <TextView
+                android:maxLines="1"
+                android:id="@+id/push_notification_date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@android:color/black"
+                android:textSize="12sp"
+                />
+        </LinearLayout>
+
+
+        <ImageView
+            android:id="@+id/push_notification_big_icon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:layout_marginRight="8dp"
+            android:scaleType="centerInside"/>
+
+        <TextView
+            android:id="@+id/push_notification_sub_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="@android:color/black"
+            android:layout_below="@id/push_notification_layout_lefttop"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:layout_marginLeft="6dp"
+            android:layout_marginRight="4dp"
+            android:visibility="gone"
+            android:layout_marginTop="1dp"
+            android:textSize="13sp"
+            android:maxLines="1"
+            />
+
+        <TextView
+            android:id="@+id/push_notification_content"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="@android:color/black"
+            android:layout_below="@id/push_notification_sub_title"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:layout_marginLeft="6dp"
+            android:layout_marginRight="4dp"
+            android:layout_marginTop="1dp"
+            android:textSize="13sp"
+            android:maxLines="2"
+            android:ellipsize="end"
+            />
+        <TextView
+            android:id="@+id/push_notification_content_one_line"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            android:textColor="@android:color/black"
+            android:layout_below="@id/push_notification_sub_title"
+            android:layout_toLeftOf="@+id/push_notification_big_icon"
+            android:layout_marginLeft="6dp"
+            android:layout_marginRight="4dp"
+            android:layout_marginTop="1dp"
+            android:textSize="13sp"
+            android:maxLines="1"
+            android:ellipsize="end"
+            />
+    </RelativeLayout>
+    <RelativeLayout
+        android:id="@+id/push_notification_style_1"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <ImageView
+            android:id="@+id/push_notification_style_1_big_icon"
+            android:layout_width="50dp"
+            android:layout_height="50dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:scaleType="centerInside"/>
+        <LinearLayout
+            android:layout_marginLeft="6dp"
+            android:layout_toRightOf="@+id/push_notification_style_1_big_icon"
+            android:orientation="vertical"
+            android:layout_centerVertical="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+            <RelativeLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <TextView
+                    android:id="@+id/push_notification_style_1_date"
+                    android:textSize="12sp"
+                    android:layout_alignParentRight="true"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" />
+                <TextView
+                    android:id="@+id/push_notification_style_1_title"
+                    android:layout_alignParentLeft="true"
+                    android:layout_toLeftOf="@+id/push_notification_style_1_date"
+                    android:textSize="12sp"
+                    android:textStyle="bold"
+                    android:maxLines="1"
+                    android:layout_marginRight="8dp"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </RelativeLayout>
+            <TextView
+                android:id="@+id/push_notification_style_1_content"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@android:color/black"
+                android:layout_marginRight="4dp"
+                android:layout_marginTop="1dp"
+                android:textSize="13sp"
+                android:maxLines="2"
+                android:ellipsize="end" />
+        </LinearLayout>
+    </RelativeLayout>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/values-zh/jpush_string.xml b/plugins/jpush-phonegap-plugin/src/android/res/values-zh/jpush_string.xml
new file mode 100755 (executable)
index 0000000..069b6f4
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="jg_channel_name_p_min">不重要</string>
+    <string name="jg_channel_name_p_low">不重要</string>
+    <string name="jg_channel_name_p_default">普通</string>
+    <string name="jg_channel_name_p_high">重要</string>
+</resources>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/values/jpush_string.xml b/plugins/jpush-phonegap-plugin/src/android/res/values/jpush_string.xml
new file mode 100755 (executable)
index 0000000..70a4ea7
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="jg_channel_name_p_min">LOW</string>
+    <string name="jg_channel_name_p_low">LOW</string>
+    <string name="jg_channel_name_p_default">NORMAL</string>
+    <string name="jg_channel_name_p_high">HIGH</string>
+
+</resources>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/android/res/values/jpush_style.xml b/plugins/jpush-phonegap-plugin/src/android/res/values/jpush_style.xml
new file mode 100755 (executable)
index 0000000..81dfdbb
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="MyDialogStyle">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+        <item name="android:backgroundDimEnabled">true</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/plugins/jpush-phonegap-plugin/src/ios/JPushConfig.plist b/plugins/jpush-phonegap-plugin/src/ios/JPushConfig.plist
new file mode 100644 (file)
index 0000000..e5a23bf
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>Appkey</key>
+       <string></string>
+       <key>Channel</key>
+       <string>channel name</string>
+       <key>IsProduction</key>
+       <false/>
+       <key>IsIDFA</key>
+       <false/>
+       <key>Delay</key>
+       <false/>
+</dict>
+</plist>
diff --git a/plugins/jpush-phonegap-plugin/src/ios/Plugins/AppDelegate+JPush.h b/plugins/jpush-phonegap-plugin/src/ios/Plugins/AppDelegate+JPush.h
new file mode 100644 (file)
index 0000000..8072fa1
--- /dev/null
@@ -0,0 +1,16 @@
+//\r
+//  AppDelegate+JPush.h\r
+//  delegateExtention\r
+//\r
+//  Created by 张庆贺 on 15/8/3.\r
+//  Copyright (c) 2015年 JPush. All rights reserved.\r
+//\r
+\r
+#import "AppDelegate.h"\r
+#import <UserNotifications/UserNotifications.h>\r
+#import "JPUSHService.h"\r
+\r
+@interface AppDelegate (JPush) <JPUSHRegisterDelegate>\r
+-(void)registerForRemoteNotification;\r
+-(void)startJPushSDK;\r
+@end\r
diff --git a/plugins/jpush-phonegap-plugin/src/ios/Plugins/AppDelegate+JPush.m b/plugins/jpush-phonegap-plugin/src/ios/Plugins/AppDelegate+JPush.m
new file mode 100644 (file)
index 0000000..81e7820
--- /dev/null
@@ -0,0 +1,213 @@
+//
+//  AppDelegate+JPush.m
+//  delegateExtention
+//
+//  Created by 张庆贺 on 15/8/3.
+//  Copyright (c) 2015年 JPush. All rights reserved.
+//
+
+#import "AppDelegate+JPush.h"
+#import "JPushPlugin.h"
+#import <objc/runtime.h>
+#import <AdSupport/AdSupport.h>
+#import <UserNotifications/UserNotifications.h>
+#import "JPushDefine.h"
+
+@implementation AppDelegate (JPush)
+
++(void)load{
+    Method origin1;
+    Method swizzle1;
+    origin1  = class_getInstanceMethod([self class],@selector(init));
+    swizzle1 = class_getInstanceMethod([self class], @selector(init_plus));
+    method_exchangeImplementations(origin1, swizzle1);
+}
+
+-(instancetype)init_plus{
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidLaunch:) name:UIApplicationDidFinishLaunchingNotification object:nil];
+    return [self init_plus];
+}
+
+NSDictionary *_launchOptions;
+-(void)applicationDidLaunch:(NSNotification *)notification{
+
+    if (!_jpushEventCache) {
+        _jpushEventCache = @{}.mutableCopy;
+    }
+
+    [JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
+      NSDictionary *event = @{@"registrationId": registrationID?:@""};
+      [JPushPlugin fireDocumentEvent:JPushDocumentEvent_receiveRegistrationId jsString:[event toJsonString]];
+    }];
+  
+  if (notification != nil &&
+      [[UIDevice currentDevice].systemVersion floatValue] < 10.0) {// iOS 10 以后通过 openNotification 这个回调触发事件。
+        if (notification.userInfo) {
+          
+          if ([notification.userInfo valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) {
+            [JPushPlugin fireDocumentEvent:JPushDocumentEvent_OpenNotification
+                                  jsString:[[self jpushFormatAPNSDic: notification.userInfo[UIApplicationLaunchOptionsRemoteNotificationKey]] toJsonString]];
+          }
+          
+          if ([notification.userInfo valueForKey:UIApplicationLaunchOptionsLocalNotificationKey]) {
+            UILocalNotification *localNotification = [notification.userInfo valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
+            NSMutableDictionary *localNotificationEvent = @{}.mutableCopy;
+            localNotificationEvent[@"content"] = localNotification.alertBody;
+            localNotificationEvent[@"badge"] = @(localNotification.applicationIconBadgeNumber);
+            localNotificationEvent[@"extras"] = localNotification.userInfo;
+
+            [JPushPlugin fireDocumentEvent:JPushDocumentEvent_OpenNotification jsString:[localNotificationEvent toJsonString]];
+          }
+        }
+    }
+  
+  [JPUSHService setDebugMode];
+  
+  NSString *plistPath = [[NSBundle mainBundle] pathForResource:JPushConfig_FileName ofType:@"plist"];
+  NSMutableDictionary *plistData = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
+  NSNumber *delay       = [plistData valueForKey:JPushConfig_Delay];
+  
+  _launchOptions = notification.userInfo;
+  
+  if (![delay boolValue]) {
+    [self startJPushSDK];
+  }
+}
+
+-(void)startJPushSDK{
+    [self registerForRemoteNotification];
+    [JPushPlugin setupJPushSDK:_launchOptions];
+}
+
+- (void)jpushSDKDidLoginNotification {
+  NSDictionary *event = @{@"registrationId": JPUSHService.registrationID};
+  [JPushPlugin fireDocumentEvent:JPushDocumentEvent_receiveRegistrationId jsString:[event toJsonString]];
+}
+
+- (NSMutableDictionary *)jpushFormatAPNSDic:(NSDictionary *)dic {
+  NSMutableDictionary *extras = @{}.mutableCopy;
+  for (NSString *key in dic) {
+    if([key isEqualToString:@"_j_business"]      ||
+       [key isEqualToString:@"_j_msgid"]         ||
+       [key isEqualToString:@"_j_uid"]           ||
+       [key isEqualToString:@"actionIdentifier"] ||
+       [key isEqualToString:@"aps"]) {
+      continue;
+    }
+    extras[key] = dic[key];
+  }
+  NSMutableDictionary *formatDic = dic.mutableCopy;
+  formatDic[@"extras"] = extras;
+  return formatDic;
+}
+
+-(void)registerForRemoteNotification{
+    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
+#ifdef NSFoundationVersionNumber_iOS_9_x_Max
+        JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
+        entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;
+        [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
+#endif
+    }else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
+        //可以添加自定义categories
+        [JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge |
+                                                          UIUserNotificationTypeSound |
+                                                          UIUserNotificationTypeAlert)
+                                              categories:nil];
+    } else if([[UIDevice currentDevice].systemVersion floatValue] < 8.0){
+        //categories 必须为nil
+        [JPUSHService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
+                                                          UIRemoteNotificationTypeSound |
+                                                          UIRemoteNotificationTypeAlert)
+                                              categories:nil];
+    }
+}
+
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+    [JPUSHService registerDeviceToken:deviceToken];
+}
+
+-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
+    [JPUSHService handleRemoteNotification:userInfo];
+
+    [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveNotification jsString:[userInfo toJsonString]];
+}
+
+-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
+    [JPUSHService handleRemoteNotification:userInfo];
+    NSString *eventName;
+    switch ([UIApplication sharedApplication].applicationState) {
+      case UIApplicationStateBackground:
+        eventName = JPushDocumentEvent_BackgroundNotification;
+        break;
+      default:
+        eventName = JPushDocumentEvent_ReceiveNotification;
+        break;
+    }
+
+    [JPushPlugin fireDocumentEvent:eventName jsString:[[self jpushFormatAPNSDic:userInfo] toJsonString]];
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+      completionHandler(UIBackgroundFetchResultNewData);
+    });
+}
+
+-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler{
+  NSMutableDictionary *userInfo = @{}.mutableCopy;
+  
+  if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
+    userInfo = [self jpushFormatAPNSDic:notification.request.content.userInfo];
+  } else {
+    UNNotificationContent *content = notification.request.content;
+    userInfo[@"content"] = content.body;
+    userInfo[@"badge"] = content.badge;
+    userInfo[@"extras"] = content.userInfo;
+    userInfo[@"identifier"] = notification.request.identifier;
+  }
+  
+  completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);
+  
+  if ([userInfo[@"aps"][@"content-available"] isEqualToNumber:@(1)]) {// content-available 当用户开启后台推送是,防止触发两次事件
+    return;
+  }
+  
+  [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveNotification jsString:[userInfo toJsonString]];
+  
+}
+
+-(void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{
+  UNNotification *notification = response.notification;
+  NSMutableDictionary *userInfo = @{}.mutableCopy;
+  
+  if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
+    userInfo = [self jpushFormatAPNSDic:notification.request.content.userInfo];
+  } else {
+    UNNotificationContent *content = notification.request.content;
+    userInfo[@"content"] = content.body;
+    userInfo[@"badge"] = content.badge;
+    userInfo[@"extras"] = content.userInfo;
+    userInfo[@"identifier"] = notification.request.identifier;
+  }
+  
+  [JPushPlugin fireDocumentEvent:JPushDocumentEvent_OpenNotification jsString:[userInfo toJsonString]];
+  completionHandler();
+}
+
+- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
+  NSMutableDictionary *localNotificationEvent = @{}.mutableCopy;
+  localNotificationEvent[@"content"] = notification.alertBody;
+  localNotificationEvent[@"badge"] = @(notification.applicationIconBadgeNumber);
+  localNotificationEvent[@"extras"] = notification.userInfo;
+  
+  [[NSNotificationCenter defaultCenter] postNotificationName:JPushDocumentEvent_ReceiveLocalNotification object:localNotificationEvent];
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+    //  [application setApplicationIconBadgeNumber:0];
+    //  [application cancelAllLocalNotifications];
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+    //  [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
+}
+
+@end
diff --git a/plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushDefine.h b/plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushDefine.h
new file mode 100644 (file)
index 0000000..7307ea7
--- /dev/null
@@ -0,0 +1,32 @@
+//
+//  ConstantDef.h
+//  jmessage
+//
+//  Created by ljg on 16/1/19.
+//
+//
+
+#ifndef ConstantDef_h
+#define ConstantDef_h
+
+
+
+#endif /* ConstantDef_h */
+
+#define WEAK_SELF(weakSelf)  __weak __typeof(&*self)weakSelf = self;
+
+static NSString *const JPushConfig_FileName     = @"JPushConfig";
+static NSString *const JPushConfig_Appkey       = @"Appkey";
+static NSString *const JPushConfig_Channel      = @"Channel";
+static NSString *const JPushConfig_IsProduction = @"IsProduction";
+static NSString *const JPushConfig_IsIDFA       = @"IsIDFA";
+static NSString *const JPushConfig_Delay        = @"Delay";
+
+static NSString *const JPushDocumentEvent_ReceiveNotification       = @"receiveNotification";
+static NSString *const JPushDocumentEvent_OpenNotification          = @"openNotification";
+static NSString *const JPushDocumentEvent_BackgroundNotification    = @"backgroundNotification";
+static NSString *const JPushDocumentEvent_SetTagsWithAlias          = @"setTagsWithAlias";
+static NSString *const JPushDocumentEvent_ReceiveMessage            = @"receiveMessage";
+static NSString *const JPushDocumentEvent_ReceiveLocalNotification  = @"receiveLocalNotification";
+
+static NSString *const JPushDocumentEvent_receiveRegistrationId     = @"receiveRegistrationId";
diff --git a/plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushPlugin.h b/plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushPlugin.h
new file mode 100644 (file)
index 0000000..2a7f932
--- /dev/null
@@ -0,0 +1,102 @@
+//
+//  PushTalkPlugin.h
+//  PushTalk
+//
+//  Created by zhangqinghe on 13-12-13.
+//
+//
+
+#import <Cordova/CDV.h>
+
+static NSMutableDictionary *_jpushEventCache;
+
+@interface JPushPlugin : CDVPlugin{
+
+}
+
+//注册通知服务并启动 SDK
+-(void)startJPushSDK:(CDVInvokedUrlCommand*)command;
+
+//以下为js中可调用接口
+//设置标签、别名
+-(void)setTags:(CDVInvokedUrlCommand*)command;
+-(void)addTags:(CDVInvokedUrlCommand*)command;
+-(void)deleteTags:(CDVInvokedUrlCommand*)command;
+-(void)cleanTags:(CDVInvokedUrlCommand*)command;
+-(void)getAllTags:(CDVInvokedUrlCommand*)command;
+-(void)checkTagBindState:(CDVInvokedUrlCommand*)command;
+
+-(void)setAlias:(CDVInvokedUrlCommand*)command;
+-(void)deleteAlias:(CDVInvokedUrlCommand*)command;
+-(void)getAlias:(CDVInvokedUrlCommand*)command;
+
+//获取 RegistrationID
+-(void)getRegistrationID:(CDVInvokedUrlCommand*)command;
+
+//页面统计
+-(void)startLogPageView:(CDVInvokedUrlCommand*)command;
+-(void)stopLogPageView:(CDVInvokedUrlCommand*)command;
+-(void)beginLogPageView:(CDVInvokedUrlCommand*)command;
+
+//设置角标到服务器,服务器下一次发消息时,会设置成这个值
+//本接口不会改变应用本地的角标值.
+-(void)setBadge:(CDVInvokedUrlCommand*)command;
+//相当于 [setBadge:0]
+-(void)resetBadge:(CDVInvokedUrlCommand*)command;
+
+//应用本地的角标值设置/获取
+-(void)setApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command;
+-(void)getApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command;
+
+//停止与恢复推送
+-(void)stopPush:(CDVInvokedUrlCommand*)command;
+-(void)resumePush:(CDVInvokedUrlCommand*)command;
+-(void)isPushStopped:(CDVInvokedUrlCommand*)command;
+
+//开关日志
+-(void)setDebugModeFromIos:(CDVInvokedUrlCommand*)command;
+-(void)setLogOFF:(CDVInvokedUrlCommand*)command;
+-(void)crashLogON:(CDVInvokedUrlCommand*)command;
+
+//本地推送
+-(void)setLocalNotification:(CDVInvokedUrlCommand*)command;
+-(void)deleteLocalNotificationWithIdentifierKey:(CDVInvokedUrlCommand*)command;
+-(void)clearAllLocalNotifications:(CDVInvokedUrlCommand*)command;
+
+//地理位置上报 [latitude,longitude]
+-(void)setLocation:(CDVInvokedUrlCommand*)command;
+
+//检查用户的推送设置情况
+-(void)getUserNotificationSettings:(CDVInvokedUrlCommand*)command;
+
+//ios 10 APIs
+-(void)addDismissActions:(CDVInvokedUrlCommand*)command;
+-(void)addNotificationActions:(CDVInvokedUrlCommand*)command;
+
+/*
+ *  以下为js中可监听到的事件
+ *  jpush.openNotification      点击推送消息启动或唤醒app
+ *  jpush.receiveMessage        收到自定义消息
+ *  jpush.receiveNotification   前台收到推送
+ *  jpush.backgroundNotification 后台收到推送
+ */
+
+# pragma mark - private
+
++(void)fireDocumentEvent:(NSString*)eventName jsString:(NSString*)jsString;
+
++(void)setupJPushSDK:(NSDictionary*)userInfo;
+
+@end
+
+static JPushPlugin *SharedJPushPlugin;
+
+@interface NSDictionary (JPush)
+-(NSString*)toJsonString;
+@end
+
+@interface NSString (JPush)
+-(NSDictionary*)toDictionary;
+@end
+
+
diff --git a/plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushPlugin.m b/plugins/jpush-phonegap-plugin/src/ios/Plugins/JPushPlugin.m
new file mode 100644 (file)
index 0000000..33229a5
--- /dev/null
@@ -0,0 +1,576 @@
+#import "JPushPlugin.h"
+#import "JPUSHService.h"
+#import <UIKit/UIKit.h>
+#import <AdSupport/AdSupport.h>
+#import <UserNotifications/UserNotifications.h>
+#import "AppDelegate+JPush.h"
+#import "JPushDefine.h"
+
+@implementation NSDictionary (JPush)
+-(NSString*)toJsonString{
+    NSError  *error;
+    NSData   *data       = [NSJSONSerialization dataWithJSONObject:self options:0 error:&error];
+    NSString *jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
+    return jsonString;
+}
+@end
+
+@implementation NSString (JPush)
+-(NSDictionary*)toDictionary{
+    NSError      *error;
+    NSData       *jsonData = [self dataUsingEncoding:NSUTF8StringEncoding];
+    NSDictionary *dict     = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
+    return dict;
+}
+@end
+
+@interface JPushPlugin()
+
+@end
+
+@implementation JPushPlugin
+
+-(void)startJPushSDK:(CDVInvokedUrlCommand*)command{
+    [(AppDelegate*)[UIApplication sharedApplication].delegate startJPushSDK];
+}
+
+#pragma mark- 外部接口
+-(void)stopPush:(CDVInvokedUrlCommand*)command{
+    [[UIApplication sharedApplication]unregisterForRemoteNotifications];
+}
+
+-(void)resumePush:(CDVInvokedUrlCommand*)command{
+    [(AppDelegate*)[UIApplication sharedApplication].delegate registerForRemoteNotification];
+}
+
+-(void)isPushStopped:(CDVInvokedUrlCommand*)command{
+    NSNumber *result = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] ? @(0) : @(1);
+    [self handleResultWithValue:result command:command];
+}
+
+-(void)initial:(CDVInvokedUrlCommand*)command{
+    //do nithng,because Cordova plugin use lazy load mode.
+}
+
+#ifdef __CORDOVA_4_0_0
+
+- (void)pluginInitialize {
+    NSLog(@"### pluginInitialize ");
+    [self initPlugin];
+}
+
+#else
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView{
+    NSLog(@"### initWithWebView ");
+    if (self=[super initWithWebView:theWebView]) {
+    }
+    [self initPlugin];
+    return self;
+}
+
+#endif
+
+-(void)initPlugin{
+    if (!SharedJPushPlugin) {
+        SharedJPushPlugin = self;
+    }
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(networkDidReceiveMessage:)
+                                                 name:kJPFNetworkDidReceiveMessageNotification
+                                               object:nil];
+  [[NSNotificationCenter defaultCenter] addObserver:self
+                                           selector:@selector(receiveLocalNotification:)
+                                               name:JPushDocumentEvent_ReceiveLocalNotification
+                                             object:nil];
+  [self dispatchJPushCacheEvent];
+}
+
+- (void)dispatchJPushCacheEvent {
+  for (NSString* key in _jpushEventCache) {
+    NSArray *evenList = _jpushEventCache[key];
+    for (NSString *event in evenList) {
+        [JPushPlugin fireDocumentEvent:key jsString:event];
+    }
+  }
+}
+
++(void)fireDocumentEvent:(NSString*)eventName jsString:(NSString*)jsString{
+  if (SharedJPushPlugin) {
+    dispatch_async(dispatch_get_main_queue(), ^{
+      [SharedJPushPlugin.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.fireDocumentEvent('jpush.%@',%@)", eventName, jsString]];
+    });
+    return;
+  }
+  
+  if (!_jpushEventCache) {
+    _jpushEventCache = @{}.mutableCopy;
+  }
+  
+  if (!_jpushEventCache[eventName]) {
+    _jpushEventCache[eventName] = @[].mutableCopy;
+  }
+  
+  [_jpushEventCache[eventName] addObject: jsString];
+}
+
+-(void)setTags:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSArray* tags = params[@"tags"];
+  
+    [JPUSHService setTags:[NSSet setWithArray:tags]
+               completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+                   NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+                   [dic setObject:sequence forKey:@"sequence"];
+                   
+                   CDVPluginResult* result;
+                   
+                   if (iResCode == 0) { 
+                       [dic setObject:[iTags allObjects] forKey:@"tags"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+                   } else {
+                       [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+                   }
+                   
+                   [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+               } seq:[sequence integerValue]];
+}
+
+-(void)addTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSArray* tags = params[@"tags"];
+    
+    [JPUSHService addTags:[NSSet setWithArray:tags]
+               completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+                   NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+                   [dic setObject:sequence forKey:@"sequence"];
+                   
+                   CDVPluginResult* result;
+                   
+                   if (iResCode == 0) { 
+                       [dic setObject:[iTags allObjects] forKey:@"tags"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+                   } else {
+                       [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+                   }
+                   
+                   [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+               } seq:[sequence integerValue]];
+}
+
+-(void)deleteTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSArray* tags = params[@"tags"];
+    
+    [JPUSHService deleteTags:[NSSet setWithArray:tags]
+               completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+                   NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+                   [dic setObject:sequence forKey:@"sequence"];
+                   
+                   CDVPluginResult* result;
+                   
+                   if (iResCode == 0) { 
+                       [dic setObject:[iTags allObjects] forKey:@"tags"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+                   } else {
+                       [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+                       result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+                   }
+                   
+                   [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+               } seq:[sequence integerValue]];
+}
+
+-(void)cleanTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService cleanTags:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)getAllTags:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService getAllTags:^(NSInteger iResCode, NSSet *iTags, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) { 
+            [dic setObject:[iTags allObjects] forKey:@"tags"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)checkTagBindState:(CDVInvokedUrlCommand *)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSString* tag = params[@"tag"];
+    
+    [JPUSHService validTag:tag completion:^(NSInteger iResCode, NSSet *iTags, NSInteger seq, BOOL isBind) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) { 
+            dic[@"tag"] = tag;
+            [dic setObject:[NSNumber numberWithBool:isBind] forKey:@"isBind"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)setAlias:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    NSString* alias = params[@"alias"];
+    
+    [JPUSHService setAlias:alias completion:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            [dic setObject:iAlias forKey:@"alias"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+            
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)deleteAlias:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService deleteAlias:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)getAlias:(CDVInvokedUrlCommand*)command {
+    NSDictionary* params = [command.arguments objectAtIndex:0];
+    NSNumber* sequence = params[@"sequence"];
+    
+    [JPUSHService getAlias:^(NSInteger iResCode, NSString *iAlias, NSInteger seq) {
+        NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
+        [dic setObject:sequence forKey:@"sequence"];
+        
+        CDVPluginResult* result;
+        
+        if (iResCode == 0) {
+            [dic setObject:iAlias forKey:@"alias"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dic];
+        } else {
+            [dic setValue:[NSNumber numberWithUnsignedInteger:iResCode] forKey:@"code"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dic];
+        }
+        
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    } seq:[sequence integerValue]];
+}
+
+-(void)getRegistrationID:(CDVInvokedUrlCommand*)command{
+    NSString* registrationID = [JPUSHService registrationID];
+    [self handleResultWithValue:registrationID command:command];
+}
+
+-(void)startLogPageView:(CDVInvokedUrlCommand*)command{
+    NSString * pageName = [command argumentAtIndex:0];
+    [JPUSHService startLogPageView:pageName];
+}
+
+-(void)stopLogPageView:(CDVInvokedUrlCommand*)command{
+    NSString * pageName = [command argumentAtIndex:0];
+    [JPUSHService stopLogPageView:pageName];
+}
+
+-(void)beginLogPageView:(CDVInvokedUrlCommand*)command{
+    NSString *pageName = [command argumentAtIndex:0];
+    NSNumber *duration = [command argumentAtIndex:1];
+    [JPUSHService beginLogPageView:pageName duration:duration.intValue];
+}
+
+-(void)setBadge:(CDVInvokedUrlCommand*)command{
+    NSNumber *badge = [command argumentAtIndex:0];
+    [JPUSHService setBadge:badge.intValue];
+}
+
+-(void)resetBadge:(CDVInvokedUrlCommand*)command{
+    [JPUSHService resetBadge];
+}
+
+-(void)setApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command{
+    NSNumber *badge = [command argumentAtIndex:0];
+    [UIApplication sharedApplication].applicationIconBadgeNumber = badge.intValue;
+}
+
+-(void)getApplicationIconBadgeNumber:(CDVInvokedUrlCommand*)command {
+    NSInteger num = [UIApplication sharedApplication].applicationIconBadgeNumber;
+    NSNumber *number = [NSNumber numberWithInteger:num];
+    [self handleResultWithValue:number command:command];
+}
+
+-(void)setDebugModeFromIos:(CDVInvokedUrlCommand*)command{
+    [JPUSHService setDebugMode];
+}
+
+-(void)setLogOFF:(CDVInvokedUrlCommand*)command{
+    [JPUSHService setLogOFF];
+}
+
+-(void)crashLogON:(CDVInvokedUrlCommand*)command{
+    [JPUSHService crashLogON];
+}
+
+-(void)setLocalNotification:(CDVInvokedUrlCommand*)command{
+  NSNumber     *delay = [command argumentAtIndex:0];
+  NSString     *alert = [command argumentAtIndex:1];
+  NSNumber     *badge = [command argumentAtIndex:2];
+  NSString     *idKey = [command argumentAtIndex:3];
+  NSDictionary *userInfo  = [command argumentAtIndex:4];
+  
+  JPushNotificationContent *content = [[JPushNotificationContent alloc] init];
+  
+  if (alert) {
+    content.body = alert;
+  }
+  
+  if (badge) {
+    content.badge = badge;
+  }
+  
+  if (userInfo) {
+    content.userInfo = userInfo;
+  }
+  
+  JPushNotificationTrigger *trigger = [[JPushNotificationTrigger alloc] init];
+  // 由于 不支持 0 作为传入参数,在传入参数基础上添加一个极小的时间于 android 端保持一致。
+  if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
+    if (delay) {
+      trigger.timeInterval = [delay doubleValue] + 0.000001;
+    }
+  } else {
+    if (delay) {
+      trigger.fireDate = [NSDate dateWithTimeIntervalSinceNow:[[command argumentAtIndex:0] doubleValue] + 0.001];
+    }
+  }
+  
+  JPushNotificationRequest *request = [[JPushNotificationRequest alloc] init];
+  request.content = content;
+  request.trigger = trigger;
+  
+  if (idKey) {
+    request.requestIdentifier = idKey;
+  }
+  
+  request.completionHandler = ^(id result) {
+    NSLog(@"result");
+  };
+  
+  [JPUSHService addNotification:request];
+}
+
+-(void)deleteLocalNotificationWithIdentifierKey:(CDVInvokedUrlCommand*)command{
+    NSString *identifier = [command argumentAtIndex:0];
+    JPushNotificationIdentifier *jpid = [JPushNotificationIdentifier new];
+    jpid.identifiers = @[identifier];
+    [JPUSHService removeNotification:jpid];
+}
+
+-(void)clearAllLocalNotifications:(CDVInvokedUrlCommand*)command{
+    [JPUSHService removeNotification:nil];
+}
+
+-(void)setLocation:(CDVInvokedUrlCommand*)command{
+    NSNumber *latitude  = [command argumentAtIndex:0];
+    NSNumber *longitude = [command argumentAtIndex:1];
+    [JPUSHService setLatitude:latitude.doubleValue longitude:longitude.doubleValue];
+}
+
+-(void)getUserNotificationSettings:(CDVInvokedUrlCommand*)command{
+    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
+        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+        WEAK_SELF(weakSelf);
+        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
+            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+            dict[@"authorizationStatus"]       = @(settings.authorizationStatus);
+            dict[@"soundSetting"]              = @(settings.soundSetting);
+            dict[@"badgeSetting"]              = @(settings.badgeSetting);
+            dict[@"alertSetting"]              = @(settings.alertSetting);
+            dict[@"notificationCenterSetting"] = @(settings.notificationCenterSetting);
+            dict[@"lockScreenSetting"]         = @(settings.lockScreenSetting);
+            dict[@"carPlaySetting"]            = @(settings.carPlaySetting);
+            dict[@"alertStyle"]                = @(settings.alertStyle);
+            [weakSelf handleResultWithValue:dict command:command];
+        }];
+    }else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
+        UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
+        UIUserNotificationType type = settings.types;
+        NSNumber *number = [NSNumber numberWithInteger:type];
+        [self handleResultWithValue:number command:command];
+    }else{
+        UIRemoteNotificationType type = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
+        NSNumber *number = [NSNumber numberWithInteger:type];
+        [self handleResultWithValue:number command:command];
+    }
+}
+
+#pragma mark - ios 10 APIs
+
+-(void)addDismissActions:(CDVInvokedUrlCommand*)command{
+    [self addActions:command dismiss:YES];
+}
+
+-(void)addNotificationActions:(CDVInvokedUrlCommand*)command{
+    [self addActions:command dismiss:NO];
+}
+
+-(void)addActions:(CDVInvokedUrlCommand*)command dismiss:(BOOL)dimiss{
+    NSArray *actionsData     = [command argumentAtIndex:0];
+    NSString *categoryId     = [command argumentAtIndex:1];
+    NSMutableArray *actions  = [NSMutableArray array];
+    for (NSDictionary *dict in actionsData) {
+        NSString *title      = dict[@"title"];
+        NSString *identifier = dict[@"identifier"];
+        NSString *option     = dict[@"option"];
+        NSString *type       = dict[@"type"];
+        if ([type isEqualToString:@"textInput"]) {
+            NSString *textInputButtonTitle = dict[@"textInputButtonTitle"];
+            NSString *textInputPlaceholder = dict[@"textInputPlaceholder"];
+            UNTextInputNotificationAction *inputAction = [UNTextInputNotificationAction actionWithIdentifier:identifier title:title options:option.integerValue textInputButtonTitle:textInputButtonTitle textInputPlaceholder:textInputPlaceholder];
+            [actions addObject:inputAction];
+        } else {
+            UNNotificationAction *action = [UNNotificationAction actionWithIdentifier:title title:title options:option.integerValue];
+            [actions addObject:action];
+        }
+    }
+    UNNotificationCategory *category;
+    if (dimiss) {
+        category = [UNNotificationCategory categoryWithIdentifier:categoryId
+                                                          actions:actions
+                                                intentIdentifiers:@[]
+                                                          options:UNNotificationCategoryOptionCustomDismissAction];
+    } else {
+        category = [UNNotificationCategory categoryWithIdentifier:categoryId
+                                                          actions:actions
+                                                intentIdentifiers:@[]
+                                                          options:UNNotificationCategoryOptionNone];
+    }
+    [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObject:category]];
+}
+
+#pragma mark - 内部方法
+
++(void)setupJPushSDK:(NSDictionary*)userInfo{
+    NSString *plistPath = [[NSBundle mainBundle] pathForResource:JPushConfig_FileName ofType:@"plist"];
+    if (plistPath == nil) {
+        NSLog(@"error: PushConfig.plist not found");
+        assert(0);
+    }
+
+    NSMutableDictionary *plistData = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
+    NSString *appkey       = [plistData valueForKey:JPushConfig_Appkey];
+    NSString *channel      = [plistData valueForKey:JPushConfig_Channel];
+    NSNumber *isProduction = [plistData valueForKey:JPushConfig_IsProduction];
+    NSNumber *isIDFA       = [plistData valueForKey:JPushConfig_IsIDFA];
+
+    NSString *advertisingId = nil;
+    if(isIDFA.boolValue) {
+        advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
+    }
+    [JPUSHService setupWithOption:userInfo
+                           appKey:appkey
+                          channel:channel
+                 apsForProduction:[isProduction boolValue]
+            advertisingIdentifier:advertisingId];
+}
+
+#pragma mark 将参数返回给js
+-(void)handleResultWithValue:(id)value command:(CDVInvokedUrlCommand*)command {
+    CDVPluginResult *result = nil;
+    CDVCommandStatus status = CDVCommandStatus_OK;
+
+    if ([value isKindOfClass:[NSString class]]) {
+        value = [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+    } else if ([value isKindOfClass:[NSNull class]]) {
+        value = nil;
+    }
+
+    if ([value isKindOfClass:[NSObject class]]) {
+        result = [CDVPluginResult resultWithStatus:status messageAsString:value];//NSObject 类型都可以
+    } else {
+        NSLog(@"Cordova callback block returned unrecognized type: %@", NSStringFromClass([value class]));
+        result = nil;
+    }
+
+    if (!result) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+-(void)networkDidReceiveMessage:(NSNotification *)notification {
+    if (notification && notification.userInfo) {
+        [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveMessage
+                              jsString:[notification.userInfo toJsonString]];
+    }
+}
+
+-(void)receiveLocalNotification:(NSNotification *)notification {
+  if (notification && notification.object) {
+    [JPushPlugin fireDocumentEvent:JPushDocumentEvent_ReceiveLocalNotification
+                          jsString:[notification.object toJsonString]];
+  }
+}
+@end
diff --git a/plugins/jpush-phonegap-plugin/src/ios/lib/JPUSHService.h b/plugins/jpush-phonegap-plugin/src/ios/lib/JPUSHService.h
new file mode 100755 (executable)
index 0000000..904da9a
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ *     | |    | |  \ \  / /  | |    | |   / _______|
+ *     | |____| |   \ \/ /   | |____| |  / /
+ *     | |____| |    \  /    | |____| |  | |   _____
+ *     | |    | |    /  \    | |    | |  | |  |____ |
+ *  | |    | |   / /\ \   | |    | |  \ \______| |
+ *  | |    | |  /_/  \_\  | |    | |   \_________|
+ *
+ * Copyright (c) 2011 ~ 2017 Shenzhen HXHG. All rights reserved.
+ */
+
+#define JPUSH_VERSION_NUMBER 3.2.1
+
+#import <Foundation/Foundation.h>
+
+@class CLRegion;
+@class UILocalNotification;
+@class CLLocation;
+@class UNNotificationCategory;
+@class UNNotificationSettings;
+@class UNNotificationRequest;
+@class UNNotification;
+@protocol JPUSHRegisterDelegate;
+@protocol JPUSHGeofenceDelegate;
+
+typedef void (^JPUSHTagsOperationCompletion)(NSInteger iResCode, NSSet *iTags, NSInteger seq);
+typedef void (^JPUSHTagValidOperationCompletion)(NSInteger iResCode, NSSet *iTags, NSInteger seq, BOOL isBind);
+typedef void (^JPUSHAliasOperationCompletion)(NSInteger iResCode, NSString *iAlias, NSInteger seq);
+
+extern NSString *const kJPFNetworkIsConnectingNotification; // 正在连接中
+extern NSString *const kJPFNetworkDidSetupNotification;     // 建立连接
+extern NSString *const kJPFNetworkDidCloseNotification;     // 关闭连接
+extern NSString *const kJPFNetworkDidRegisterNotification;  // 注册成功
+extern NSString *const kJPFNetworkFailedRegisterNotification; //注册失败
+extern NSString *const kJPFNetworkDidLoginNotification;     // 登录成功
+extern NSString *const kJPFNetworkDidReceiveMessageNotification;         // 收到消息(非APNS)
+extern NSString *const kJPFServiceErrorNotification;  // 错误提示
+
+typedef NS_OPTIONS(NSUInteger, JPAuthorizationOptions) {
+    JPAuthorizationOptionNone    = 0,   // the application may not present any UI upon a notification being received
+    JPAuthorizationOptionBadge   = (1 << 0),    // the application may badge its icon upon a notification being received
+    JPAuthorizationOptionSound   = (1 << 1),    // the application may play a sound upon a notification being received
+    JPAuthorizationOptionAlert   = (1 << 2),    // the application may display an alert upon a notification being received
+    JPAuthorizationOptionCarPlay = (1 << 3),    // The ability to display notifications in a CarPlay environment.
+    JPAuthorizationOptionCriticalAlert NS_AVAILABLE_IOS(12.0) = (1 << 4) ,   //The ability to play sounds for critical alerts.
+    JPAuthorizationOptionProvidesAppNotificationSettings NS_AVAILABLE_IOS(12.0) = (1 << 5) ,      //An option indicating the system should display a button for in-app notification settings.
+    JPAuthorizationOptionProvisional NS_AVAILABLE_IOS(12.0) = (1 << 6) ,     //The ability to post noninterrupting notifications provisionally to the Notification Center.
+  
+};
+
+/*!
+ * 通知注册实体类
+ */
+@interface JPUSHRegisterEntity : NSObject
+
+/*!
+ * 支持的类型
+ * badge,sound,alert
+ */
+@property (nonatomic, assign) NSInteger types;
+/*!
+ * 注入的类别
+ * iOS10 UNNotificationCategory
+ * iOS8-iOS9 UIUserNotificationCategory
+ */
+@property (nonatomic, strong) NSSet *categories;
+@end
+
+/*!
+ * 进行删除、查找推送实体类
+ */
+@interface JPushNotificationIdentifier : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, copy) NSArray<NSString *> *identifiers; // 推送的标识数组
+@property (nonatomic, copy) UILocalNotification *notificationObj NS_DEPRECATED_IOS(4_0, 10_0);  // iOS10以下可以传UILocalNotification对象数据,iOS10以上无效
+@property (nonatomic, assign) BOOL delivered NS_AVAILABLE_IOS(10_0); // 在通知中心显示的或待推送的标志,默认为NO,YES表示在通知中心显示的,NO表示待推送的
+@property (nonatomic, copy) void (^findCompletionHandler)(NSArray *results); // 用于查询回调,调用[findNotification:]方法前必须设置,results为返回相应对象数组,iOS10以下返回UILocalNotification对象数组;iOS10以上根据delivered传入值返回UNNotification或UNNotificationRequest对象数组(delivered传入YES,则返回UNNotification对象数组,否则返回UNNotificationRequest对象数组)
+
+@end
+
+/*!
+ * 推送通知声音实体类
+ * iOS10以上有效
+ */
+@interface JPushNotificationSound : NSObject <NSCopying, NSCoding>
+@property (nonatomic, copy) NSString *soundName; //普通通知铃声
+@property (nonatomic, copy) NSString *criticalSoundName NS_AVAILABLE_IOS(12.0); //警告通知铃声
+@property (nonatomic, assign) float criticalSoundVolume NS_AVAILABLE_IOS(12.0); //警告通知铃声音量,有效值在0~1之间,默认为1
+@end
+
+
+/*!
+ * 推送内容实体类
+ */
+@interface JPushNotificationContent : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, copy) NSString *title;                // 推送标题
+@property (nonatomic, copy) NSString *subtitle;             // 推送副标题
+@property (nonatomic, copy) NSString *body;                 // 推送内容
+@property (nonatomic, copy) NSNumber *badge;                // 角标的数字。如果不需要改变角标传@(-1)
+@property (nonatomic, copy) NSString *action NS_DEPRECATED_IOS(8_0, 10_0); // 弹框的按钮显示的内容(IOS 8默认为"打开", 其他默认为"启动",iOS10以上无效)
+@property (nonatomic, copy) NSString *categoryIdentifier;   // 行为分类标识
+@property (nonatomic, copy) NSDictionary *userInfo;         // 本地推送时可以设置userInfo来增加附加信息,远程推送时设置的payload推送内容作为此userInfo
+@property (nonatomic, copy) NSString *sound;                // 声音名称,不设置则为默认声音
+@property (nonatomic, copy) JPushNotificationSound *soundSetting NS_AVAILABLE_IOS(10.0);   //推送声音实体
+@property (nonatomic, copy) NSArray *attachments NS_AVAILABLE_IOS(10_0);                 // 附件,iOS10以上有效,需要传入UNNotificationAttachment对象数组类型
+@property (nonatomic, copy) NSString *threadIdentifier NS_AVAILABLE_IOS(10_0); // 线程或与推送请求相关对话的标识,iOS10以上有效,可用来对推送进行分组
+@property (nonatomic, copy) NSString *launchImageName NS_AVAILABLE_IOS(10_0);  // 启动图片名,iOS10以上有效,从推送启动时将会用到
+@property (nonatomic, copy) NSString *summaryArgument NS_AVAILABLE_IOS(12.0);  //插入到通知摘要中的部分参数。iOS12以上有效。
+@property (nonatomic, assign) NSUInteger summaryArgumentCount NS_AVAILABLE_IOS(12.0); //插入到通知摘要中的项目数。iOS12以上有效。
+
+@end
+
+
+/*!
+ * 推送触发方式实体类
+ * 注:dateComponents、timeInterval、region在iOS10以上可选择其中一个参数传入有效值,如果同时传入值会根据优先级I、II、III使其中一种触发方式生效,fireDate为iOS10以下根据时间触发时须传入的参数
+ */
+@interface JPushNotificationTrigger : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, assign) BOOL repeat;                  // 设置是否重复,默认为NO
+@property (nonatomic, copy) NSDate *fireDate NS_DEPRECATED_IOS(2_0, 10_0);           // 用来设置触发推送的时间,iOS10以上无效
+@property (nonatomic, copy) CLRegion *region NS_AVAILABLE_IOS(8_0);                  // 用来设置触发推送的位置,iOS8以上有效,iOS10以上优先级为I,应用需要有允许使用定位的授权
+@property (nonatomic, copy) NSDateComponents *dateComponents NS_AVAILABLE_IOS(10_0); // 用来设置触发推送的日期时间,iOS10以上有效,优先级为II
+@property (nonatomic, assign) NSTimeInterval timeInterval NS_AVAILABLE_IOS(10_0);    // 用来设置触发推送的时间,iOS10以上有效,优先级为III
+
+@end
+
+/*!
+ * 注册或更新推送实体类
+ */
+@interface JPushNotificationRequest : NSObject<NSCopying, NSCoding>
+
+@property (nonatomic, copy) NSString *requestIdentifier;    // 推送请求标识
+@property (nonatomic, copy) JPushNotificationContent *content; // 设置推送的具体内容
+@property (nonatomic, copy) JPushNotificationTrigger *trigger; // 设置推送的触发方式
+@property (nonatomic, copy) void (^completionHandler)(id result); // 注册或更新推送成功回调,iOS10以上成功则result为UNNotificationRequest对象,失败则result为nil;iOS10以下成功result为UILocalNotification对象,失败则result为nil
+
+@end
+
+/*!
+ * JPush 核心头文件
+ */
+@interface JPUSHService : NSObject
+
+
+///----------------------------------------------------
+/// @name Setup 启动相关
+///----------------------------------------------------
+
+
+/*!
+ * @abstract 启动SDK
+ *
+ * @param launchingOption 启动参数.
+ * @param appKey 一个JPush 应用必须的,唯一的标识. 请参考 JPush 相关说明文档来获取这个标识.
+ * @param channel 发布渠道. 可选.
+ * @param isProduction 是否生产环境. 如果为开发状态,设置为 NO; 如果为生产状态,应改为 YES.
+ *                     App 证书环境取决于profile provision的配置,此处建议与证书环境保持一致.
+ *
+ * @discussion 提供SDK启动必须的参数, 来启动 SDK.
+ * 此接口必须在 App 启动时调用, 否则 JPush SDK 将无法正常工作.
+ */
++ (void)setupWithOption:(NSDictionary *)launchingOption
+                 appKey:(NSString *)appKey
+                channel:(NSString *)channel
+       apsForProduction:(BOOL)isProduction;
+
+/*!
+ * @abstract 启动SDK
+ *
+ * @param launchingOption 启动参数.
+ * @param appKey 一个JPush 应用必须的,唯一的标识. 请参考 JPush 相关说明文档来获取这个标识.
+ * @param channel 发布渠道. 可选.
+ * @param isProduction 是否生产环境. 如果为开发状态,设置为 NO; 如果为生产状态,应改为 YES.
+ *                     App 证书环境取决于profile provision的配置,此处建议与证书环境保持一致.
+ * @param advertisingId 广告标识符(IDFA) 如果不需要使用IDFA,传nil.
+ *
+ * @discussion 提供SDK启动必须的参数, 来启动 SDK.
+ * 此接口必须在 App 启动时调用, 否则 JPush SDK 将无法正常工作.
+ */
++ (void)setupWithOption:(NSDictionary *)launchingOption
+                 appKey:(NSString *)appKey
+                channel:(NSString *)channel
+       apsForProduction:(BOOL)isProduction
+  advertisingIdentifier:(NSString *)advertisingId;
+
+
+///----------------------------------------------------
+/// @name APNs about 通知相关
+///----------------------------------------------------
+
+/*!
+ * @abstract 注册要处理的远程通知类型
+ *
+ * @param types 通知类型
+ * @param categories 类别组
+ *
+ */
++ (void)registerForRemoteNotificationTypes:(NSUInteger)types
+                                categories:(NSSet *)categories;
+/*!
+ * @abstract 新版本的注册方法(兼容iOS10)
+ *
+ * @param config 注册通知配置
+ * @param delegate 代理
+ *
+ */
++ (void)registerForRemoteNotificationConfig:(JPUSHRegisterEntity *)config delegate:(id<JPUSHRegisterDelegate>)delegate;
+
+
++ (void)registerDeviceToken:(NSData *)deviceToken;
+
+
+/*!
+ * @abstract 处理收到的 APNs 消息
+ */
++ (void)handleRemoteNotification:(NSDictionary *)remoteInfo;
+
+/*!
+ * Tags操作接口
+ * 支持增加/覆盖/删除/清空/查询操作
+ * 详情请参考文档:https://docs.jiguang.cn/jpush/client/iOS/ios_api/)
+ */
+
+/**
+ 增加tags
+
+ @param tags 需要增加的tags集合
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)addTags:(NSSet<NSString *> *)tags
+     completion:(JPUSHTagsOperationCompletion)completion
+            seq:(NSInteger)seq;
+
+/**
+ 覆盖tags
+ 调用该接口会覆盖用户所有的tags
+
+ @param tags 需要设置的tags集合
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)setTags:(NSSet<NSString *> *)tags
+     completion:(JPUSHTagsOperationCompletion)completion
+            seq:(NSInteger)seq;
+
+/**
+ 删除指定tags
+
+ @param tags 需要删除的tags集合
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)deleteTags:(NSSet<NSString *> *)tags
+        completion:(JPUSHTagsOperationCompletion)completion
+               seq:(NSInteger)seq;
+
+/**
+ 清空所有tags
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)cleanTags:(JPUSHTagsOperationCompletion)completion
+              seq:(NSInteger)seq;
+
+/**
+ 查询全部tags
+
+ @param completion 响应回调,请在回调中获取查询结果
+ @param seq 请求序列号
+ */
++ (void)getAllTags:(JPUSHTagsOperationCompletion)completion
+               seq:(NSInteger)seq;
+
+/**
+ 验证tag是否绑定
+ @param completion 响应回调,回调中查看是否绑定
+ @param seq 请求序列号
+ */
++ (void)validTag:(NSString *)tag
+      completion:(JPUSHTagValidOperationCompletion)completion
+             seq:(NSInteger)seq;
+
+/**
+ 设置Alias
+
+ @param alias 需要设置的alias
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)setAlias:(NSString *)alias
+      completion:(JPUSHAliasOperationCompletion)completion
+             seq:(NSInteger)seq;
+
+/**
+ 删除alias
+
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)deleteAlias:(JPUSHAliasOperationCompletion)completion
+                seq:(NSInteger)seq;
+
+/**
+ 查询当前alias
+
+ @param completion 响应回调
+ @param seq 请求序列号
+ */
++ (void)getAlias:(JPUSHAliasOperationCompletion)completion
+             seq:(NSInteger)seq;
+
+
+/*!
+ * @abstract 过滤掉无效的 tags
+ *
+ * @discussion 如果 tags 数量超过限制数量, 则返回靠前的有效的 tags.
+ * 建议设置 tags 前用此接口校验. SDK 内部也会基于此接口来做过滤.
+ */
++ (NSSet *)filterValidTags:(NSSet *)tags;
+
+///----------------------------------------------------
+/// @name Stats 统计功能
+///----------------------------------------------------
+
+/*!
+ * @abstract 开始记录页面停留
+ *
+ * @param pageName 页面名称
+ * @discussion JCore 1.1.8 版本后,如需统计页面流,请使用 JAnalytics
+ */
++ (void)startLogPageView:(NSString *)pageName __attribute__((deprecated("JCore 1.1.8 版本已过期")));
+
+/*!
+ * @abstract 停止记录页面停留
+ *
+ * @param pageName 页面
+ * @discussion JCore 1.1.8 版本后,如需统计页面流,请使用 JAnalytics
+ */
++ (void)stopLogPageView:(NSString *)pageName __attribute__((deprecated("JCore 1.1.8 版本已过期")));
+
+/*!
+ * @abstract 直接上报在页面的停留时间
+ *
+ * @param pageName 页面
+ * @param seconds 停留的秒数
+ * @discussion JCore 1.1.8 版本后,如需统计页面流,请使用 JAnalytics
+ */
++ (void)beginLogPageView:(NSString *)pageName duration:(int)seconds __attribute__((deprecated("JCore 1.1.8 版本已过期")));
+
+/*!
+ * @abstract 开启Crash日志收集
+ *
+ * @discussion 默认是关闭状态.
+ */
++ (void)crashLogON;
+
+/*!
+ * @abstract 地理位置上报
+ *
+ * @param latitude 纬度.
+ * @param longitude 经度.
+ *
+ */
++ (void)setLatitude:(double)latitude longitude:(double)longitude;
+
+/*!
+ * @abstract 地理位置上报
+ *
+ * @param location 直接传递 CLLocation * 型的地理信息
+ *
+ * @discussion 需要链接 CoreLocation.framework 并且 #import <CoreLocation/CoreLocation.h>
+ */
++ (void)setLocation:(CLLocation *)location;
+
+/**
+ 设置地理围栏的最大个数
+ 默认值为 10 ,iOS系统默认地理围栏最大个数为20
+ @param count 个数 count
+ */
++ (void)setGeofenecMaxCount:(NSInteger)count;
+/**
+ 注册地理围栏的代理
+
+ @param delegate 代理
+ @param launchOptions app启动完成是收到的字段参数
+ */
++ (void)registerLbsGeofenceDelegate:(id<JPUSHGeofenceDelegate>)delegate withLaunchOptions:(NSDictionary *)launchOptions;
+
+/**
+ 删除地理围栏
+ @param geofenceId 地理围栏id
+ */
++ (void)removeGeofenceWithIdentifier:(NSString *)geofenceId;
+
+///----------------------------------------------------
+/// @name Local Notification 本地通知
+///----------------------------------------------------
+/*!
+ * @abstract 注册或更新推送 (支持iOS10,并兼容iOS10以下版本)
+ *
+ * JPush 2.1.9新接口
+ * @param request JPushNotificationRequest类型,设置推送的属性,设置已有推送的request.requestIdentifier即更新已有的推送,否则为注册新推送,更新推送仅仅在iOS10以上有效,结果通过request.completionHandler返回
+ * @discussion 旧的注册本地推送接口被废弃,使用此接口可以替换
+ *
+ */
++ (void)addNotification:(JPushNotificationRequest *)request;
+
+/*!
+ * @abstract 移除推送 (支持iOS10,并兼容iOS10以下版本)
+ *
+ * JPush 2.1.9新接口
+ * @param identifier JPushNotificationIdentifier类型,iOS10以上identifier设置为nil,则移除所有在通知中心显示推送和待推送请求,也可以通过设置identifier.delivered和identifier.identifiers来移除相应在通知中心显示推送或待推送请求,identifier.identifiers如果设置为nil或空数组则移除相应标志下所有在通知中心显示推送或待推送请求;iOS10以下identifier设置为nil,则移除所有推送,identifier.delivered属性无效,另外可以通过identifier.notificationObj传入特定推送对象来移除此推送。
+ * @discussion 旧的所有删除推送接口被废弃,使用此接口可以替换
+ *
+ */
++ (void)removeNotification:(JPushNotificationIdentifier *)identifier;
+
+/*!
+ * @abstract 查找推送 (支持iOS10,并兼容iOS10以下版本)
+ *
+ * JPush 2.1.9新接口
+ * @param identifier JPushNotificationIdentifier类型,iOS10以上可以通过设置identifier.delivered和identifier.identifiers来查找相应在通知中心显示推送或待推送请求,identifier.identifiers如果设置为nil或空数组则返回相应标志下所有在通知中心显示推送或待推送请求;iOS10以下identifier.delivered属性无效,identifier.identifiers如果设置nil或空数组则返回所有未触发的推送。须要设置identifier.findCompletionHandler回调才能得到查找结果,通过(NSArray *results)返回相应对象数组。
+ * @discussion 旧的查找推送接口被废弃,使用此接口可以替换
+ *
+ */
++ (void)findNotification:(JPushNotificationIdentifier *)identifier;
+
+/*!
+ * @abstract 本地推送,最多支持64个
+ *
+ * @param fireDate 本地推送触发的时间
+ * @param alertBody 本地推送需要显示的内容
+ * @param badge 角标的数字。如果不需要改变角标传-1
+ * @param alertAction 弹框的按钮显示的内容(IOS 8默认为"打开", 其他默认为"启动")
+ * @param notificationKey 本地推送标示符
+ * @param userInfo 自定义参数,可以用来标识推送和增加附加信息
+ * @param soundName 自定义通知声音,设置为nil为默认声音
+ *
+ * @discussion 最多支持 64 个定义,此方法被[addNotification:]方法取代
+ */
++ (UILocalNotification *)setLocalNotification:(NSDate *)fireDate
+                                    alertBody:(NSString *)alertBody
+                                        badge:(int)badge
+                                  alertAction:(NSString *)alertAction
+                                identifierKey:(NSString *)notificationKey
+                                     userInfo:(NSDictionary *)userInfo
+                                    soundName:(NSString *)soundName __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 本地推送 (支持 iOS8 新参数)
+ *
+ * IOS8新参数
+ * @param region 自定义参数
+ * @param regionTriggersOnce 自定义参数
+ * @param category 自定义参数
+ * @discussion 此方法被[addNotification:]方法取代
+ */
++ (UILocalNotification *)setLocalNotification:(NSDate *)fireDate
+                                    alertBody:(NSString *)alertBody
+                                        badge:(int)badge
+                                  alertAction:(NSString *)alertAction
+                                identifierKey:(NSString *)notificationKey
+                                     userInfo:(NSDictionary *)userInfo
+                                    soundName:(NSString *)soundName
+                                       region:(CLRegion *)region
+                           regionTriggersOnce:(BOOL)regionTriggersOnce
+                                     category:(NSString *)category NS_AVAILABLE_IOS(8_0) __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 前台展示本地推送
+ *
+ * @param notification 本地推送对象
+ * @param notificationKey 需要前台显示的本地推送通知的标示符
+ *
+ * @discussion 默认App在前台运行时不会进行弹窗,在程序接收通知调用此接口可实现指定的推送弹窗。--iOS10以下还可继续使用,iOS10以上在[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:]方法中调用completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);即可
+ */
++ (void)showLocalNotificationAtFront:(UILocalNotification *)notification
+                       identifierKey:(NSString *)notificationKey __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+/*!
+ * @abstract 删除本地推送定义
+ *
+ * @param notificationKey 本地推送标示符
+ * @discussion 此方法被[removeNotification:]方法取代
+ */
++ (void)deleteLocalNotificationWithIdentifierKey:(NSString *)notificationKey __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 删除本地推送定义
+ * @discussion 此方法被[removeNotification:]方法取代
+ */
++ (void)deleteLocalNotification:(UILocalNotification *)localNotification __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 获取指定通知
+ *
+ * @param notificationKey 本地推送标示符
+ * @return 本地推送对象数组, [array count]为0时表示没找到
+ * @discussion 此方法被[findNotification:]方法取代
+ */
++ (NSArray *)findLocalNotificationWithIdentifier:(NSString *)notificationKey __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+/*!
+ * @abstract 清除所有本地推送对象
+ * @discussion 此方法被[removeNotification:]方法取代
+ */
++ (void)clearAllLocalNotifications __attribute__((deprecated("JPush 2.1.9 版本已过期")));
+
+
+///----------------------------------------------------
+/// @name Server badge 服务器端 badge 功能
+///----------------------------------------------------
+
+/*!
+ * @abstract 设置角标(到服务器)
+ *
+ * @param value 新的值. 会覆盖服务器上保存的值(这个用户)
+ *
+ * @discussion 本接口不会改变应用本地的角标值.
+ * 本地仍须调用 UIApplication:setApplicationIconBadgeNumber 函数来设置脚标.
+ *
+ * 本接口用于配合 JPush 提供的服务器端角标功能.
+ * 该功能解决的问题是, 服务器端推送 APNs 时, 并不知道客户端原来已经存在的角标是多少, 指定一个固定的数字不太合理.
+ *
+ * JPush 服务器端脚标功能提供:
+ *
+ * - 通过本 API 把当前客户端(当前这个用户的) 的实际 badge 设置到服务器端保存起来;
+ * - 调用服务器端 API 发 APNs 时(通常这个调用是批量针对大量用户),
+ *   使用 "+1" 的语义, 来表达需要基于目标用户实际的 badge 值(保存的) +1 来下发通知时带上新的 badge 值;
+ */
++ (BOOL)setBadge:(NSInteger)value;
+
+/*!
+ * @abstract 重置脚标(为0)
+ *
+ * @discussion 相当于 [setBadge:0] 的效果.
+ * 参考 [JPUSHService setBadge:] 说明来理解其作用.
+ */
++ (void)resetBadge;
+
+///----------------------------------------------------
+/// @name Other Feature 其他功能
+///----------------------------------------------------
+
+/*!
+ * @abstract 设置手机号码(到服务器)
+ *
+ * @param mobileNumber 手机号码. 会与用户信息一一对应。可为空,为空则清除号码
+ * @param completion 响应回调。成功则error为空,失败则error带有错误码及错误信息
+ *
+ * @discussion 设置手机号码后,可实现“推送不到短信到”的通知方式,提高推送达到率。结果信息通过completion异步返回,也可将completion设置为nil不处理结果信息。
+ *
+ */
++ (void)setMobileNumber:(NSString *)mobileNumber completion:(void (^)(NSError *error))completion;
+
+///----------------------------------------------------
+/// @name Logs and others 日志与其他
+///----------------------------------------------------
+
+/*!
+ * @abstract JPush标识此设备的 registrationID
+ *
+ * @discussion SDK注册成功后, 调用此接口获取到 registrationID 才能够获取到.
+ *
+ * JPush 支持根据 registrationID 来进行推送.
+ * 如果你需要此功能, 应该通过此接口获取到 registrationID 后, 上报到你自己的服务器端, 并保存下来.
+ * registrationIDCompletionHandler:是新增的获取registrationID的方法,需要在block中获取registrationID,resCode为返回码,模拟器调用此接口resCode返回1011,registrationID返回nil.
+ * 更多的理解请参考 JPush 的文档网站.
+ */
++ (NSString *)registrationID;
+
++ (void)registrationIDCompletionHandler:(void(^)(int resCode,NSString *registrationID))completionHandler;
+
+/*!
+ * @abstract 打开日志级别到 Debug
+ *
+ * @discussion JMessage iOS 的日志系统参考 Android 设计了级别.
+ * 从低到高是: Verbose, Debug, Info, Warning, Error.
+ * 对日志级别的进一步理解, 请参考 Android 相关的说明.
+ *
+ * SDK 默认开启的日志级别为: Info. 只显示必要的信息, 不打印调试日志.
+ *
+ * 请在SDK启动后调用本接口,调用本接口可打开日志级别为: Debug, 打印调试日志.
+ */
++ (void)setDebugMode;
+
+/*!
+ * @abstract 关闭日志
+ *
+ * @discussion 关于日志级别的说明, 参考 [JPUSHService setDebugMode]
+ *
+ * 虽说是关闭日志, 但还是会打印 Warning, Error 日志. 这二种日志级别, 在程序运行正常时, 不应有打印输出.
+ *
+ * 建议在发布的版本里, 调用此接口, 关闭掉日志打印.
+ */
++ (void)setLogOFF;
+
+///----------------------------------------------------
+///********************下列方法已过期********************
+///**************请使用新版tag/alias操作接口**************
+///----------------------------------------------------
+/// @name Tag alias setting 设置别名与标签
+///----------------------------------------------------
+
+/*!
+ * 下面的接口是可选的
+ * 设置标签和(或)别名(若参数为nil,则忽略;若是空对象,则清空;详情请参考文档:https://docs.jiguang.cn/jpush/client/iOS/ios_api/)
+ * setTags:alias:fetchCompletionHandle:是新的设置标签别名的方法,不再需要显示声明回调函数,只需要在block里面处理设置结果即可.
+ * WARN: 使用block时需要注意循环引用问题
+ */
++ (void) setTags:(NSSet *)tags
+           alias:(NSString *)alias
+callbackSelector:(SEL)cbSelector
+          target:(id)theTarget __attribute__((deprecated("JPush 2.1.1 版本已过期")));
++ (void) setTags:(NSSet *)tags
+           alias:(NSString *)alias
+callbackSelector:(SEL)cbSelector
+          object:(id)theTarget __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void) setTags:(NSSet *)tags
+callbackSelector:(SEL)cbSelector
+          object:(id)theTarget __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void)setTags:(NSSet *)tags
+          alias:(NSString *)alias
+fetchCompletionHandle:(void (^)(int iResCode, NSSet *iTags, NSString *iAlias))completionHandler __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void)  setTags:(NSSet *)tags
+aliasInbackground:(NSString *)alias __attribute__((deprecated("JPush 3.0.6 版本已过期")));
++ (void)setAlias:(NSString *)alias
+callbackSelector:(SEL)cbSelector
+          object:(id)theTarget __attribute__((deprecated("JPush 3.0.6 版本已过期")));
+
+@end
+
+@class UNUserNotificationCenter;
+@class UNNotificationResponse;
+
+@protocol JPUSHRegisterDelegate <NSObject>
+
+/*
+ * @brief handle UserNotifications.framework [willPresentNotification:withCompletionHandler:]
+ * @param center [UNUserNotificationCenter currentNotificationCenter] 新特性用户通知中心
+ * @param notification 前台得到的的通知对象
+ * @param completionHandler 该callback中的options 请使用UNNotificationPresentationOptions
+ */
+- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger options))completionHandler;
+/*
+ * @brief handle UserNotifications.framework [didReceiveNotificationResponse:withCompletionHandler:]
+ * @param center [UNUserNotificationCenter currentNotificationCenter] 新特性用户通知中心
+ * @param response 通知响应对象
+ * @param completionHandler
+ */
+- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler;
+
+/*
+ * @brief handle UserNotifications.framework [openSettingsForNotification:]
+ * @param center [UNUserNotificationCenter currentNotificationCenter] 新特性用户通知中心
+ * @param notification 当前管理的通知对象
+ */
+- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification NS_AVAILABLE_IOS(12.0);
+
+@end
+
+@protocol JPUSHGeofenceDelegate <NSObject>
+
+/**
+ 进入地理围栏区域
+ @param geofenceId 地理围栏id
+ @param userInfo 地理围栏触发时返回的信息
+ @param error 错误信息
+ */
+- (void)jpushGeofenceIdentifer:(NSString * _Nonnull)geofenceId didEnterRegion:(NSDictionary * _Nullable)userInfo error:(NSError * _Nullable)error;
+
+/**
+ 离开地理围栏区域
+ @param geofenceId 地理围栏id
+ @param userInfo 地理围栏触发时返回的信息
+ @param error 错误信息
+ */
+- (void)jpushGeofenceIdentifer:(NSString * _Nonnull)geofenceId didExitRegion:(NSDictionary * _Nullable)userInfo error:(NSError * _Nullable)error;
+
+@end
diff --git a/plugins/jpush-phonegap-plugin/src/ios/lib/jpush-ios-3.2.1.a b/plugins/jpush-phonegap-plugin/src/ios/lib/jpush-ios-3.2.1.a
new file mode 100755 (executable)
index 0000000..6aa3beb
Binary files /dev/null and b/plugins/jpush-phonegap-plugin/src/ios/lib/jpush-ios-3.2.1.a differ
diff --git a/plugins/jpush-phonegap-plugin/src/ios/notificationService/NotificationService.h b/plugins/jpush-phonegap-plugin/src/ios/notificationService/NotificationService.h
new file mode 100644 (file)
index 0000000..a43c878
--- /dev/null
@@ -0,0 +1,13 @@
+//
+//  NotificationService.h
+//  jpushNotificationService
+//
+//  Created by wuxingchen on 16/10/10.
+//
+//
+
+#import <UserNotifications/UserNotifications.h>
+
+@interface NotificationService : UNNotificationServiceExtension
+
+@end
diff --git a/plugins/jpush-phonegap-plugin/src/ios/notificationService/NotificationService.m b/plugins/jpush-phonegap-plugin/src/ios/notificationService/NotificationService.m
new file mode 100644 (file)
index 0000000..845a7eb
--- /dev/null
@@ -0,0 +1,44 @@
+//
+//  NotificationService.m
+//  jpushNotificationService
+//
+//  Created by wuxingchen on 16/10/10.
+//
+//
+
+#import "NotificationService.h"
+#import <UIKit/UIKit.h>
+
+@interface NotificationService ()
+
+@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
+@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
+
+@end
+
+@implementation NotificationService
+
+- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
+
+    self.contentHandler = contentHandler;
+    self.bestAttemptContent = [request.content mutableCopy];
+
+    @try {
+        NSString *urlStr = [request.content.userInfo valueForKey:@"JPushPluginAttachment"];
+        NSArray *urls = [urlStr componentsSeparatedByString:@"."];
+        NSURL *urlNative = [[NSBundle mainBundle] URLForResource:urls[0] withExtension:urls[1]];
+        UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:urlStr URL:urlNative options:nil error:nil];
+        self.bestAttemptContent.attachments = @[attachment];
+    } @catch (NSException *exception) {
+    }
+
+    self.contentHandler(self.bestAttemptContent);
+}
+
+- (void)serviceExtensionTimeWillExpire {
+    // Called just before the extension will be terminated by the system.
+    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
+    self.contentHandler(self.bestAttemptContent);
+}
+
+@end
diff --git a/plugins/jpush-phonegap-plugin/www/JPushPlugin.js b/plugins/jpush-phonegap-plugin/www/JPushPlugin.js
new file mode 100644 (file)
index 0000000..b34050f
--- /dev/null
@@ -0,0 +1,486 @@
+var JPushPlugin = function() {};
+
+// private plugin function
+
+JPushPlugin.prototype.receiveMessage = {};
+JPushPlugin.prototype.openNotification = {};
+JPushPlugin.prototype.receiveNotification = {};
+
+JPushPlugin.prototype.isPlatformIOS = function() {
+  return (
+    device.platform === "iPhone" ||
+    device.platform === "iPad" ||
+    device.platform === "iPod touch" ||
+    device.platform === "iOS"
+  );
+};
+
+JPushPlugin.prototype.errorCallback = function(msg) {
+  console.log("JPush Callback Error: " + msg);
+};
+
+JPushPlugin.prototype.callNative = function(
+  name,
+  args,
+  successCallback,
+  errorCallback
+) {
+  if (errorCallback) {
+    cordova.exec(successCallback, errorCallback, "JPushPlugin", name, args);
+  } else {
+    cordova.exec(
+      successCallback,
+      this.errorCallback,
+      "JPushPlugin",
+      name,
+      args
+    );
+  }
+};
+
+// Common methods
+JPushPlugin.prototype.init = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("initial", [], null);
+  } else {
+    this.callNative("init", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugMode = function(mode) {
+  if (device.platform === "Android") {
+    this.callNative("setDebugMode", [mode], null);
+  } else {
+    if (mode === true) {
+      this.setDebugModeFromIos();
+    } else {
+      this.setLogOFF();
+    }
+  }
+};
+
+JPushPlugin.prototype.getRegistrationID = function(successCallback) {
+  this.callNative("getRegistrationID", [], successCallback);
+};
+
+JPushPlugin.prototype.stopPush = function() {
+  this.callNative("stopPush", [], null);
+};
+
+JPushPlugin.prototype.resumePush = function() {
+  this.callNative("resumePush", [], null);
+};
+
+JPushPlugin.prototype.isPushStopped = function(successCallback) {
+  this.callNative("isPushStopped", [], successCallback);
+};
+
+JPushPlugin.prototype.clearLocalNotifications = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearLocalNotifications", [], null);
+  } else {
+    this.clearAllLocalNotifications();
+  }
+};
+
+/**
+ * 设置标签。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.setTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 新增标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.addTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("addTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除指定标签。
+ *
+ * @param params = { 'sequence': number, 'tags': ['tag1', 'tag2'] }
+ */
+JPushPlugin.prototype.deleteTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 清除所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.cleanTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("cleanTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询所有标签。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAllTags = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAllTags", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询指定标签与当前用户的绑定状态。
+ *
+ * @param params = { 'sequence': number, 'tag': string }
+ */
+JPushPlugin.prototype.checkTagBindState = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative(
+    "checkTagBindState",
+    [params],
+    successCallback,
+    errorCallback
+  );
+};
+
+/**
+ * 设置别名。
+ * 注意:该接口是覆盖逻辑,而不是增量逻辑。即新的调用会覆盖之前的设置。
+ *
+ * @param params = { 'sequence': number, 'alias': string }
+ */
+JPushPlugin.prototype.setAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("setAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 删除别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.deleteAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("deleteAlias", [params], successCallback, errorCallback);
+};
+
+/**
+ * 查询当前绑定的别名。
+ *
+ * @param params = { 'sequence': number }
+ */
+JPushPlugin.prototype.getAlias = function(
+  params,
+  successCallback,
+  errorCallback
+) {
+  this.callNative("getAlias", [params], successCallback, errorCallback);
+};
+
+// 判断系统设置中是否对本应用启用通知。
+// iOS: 返回值如果大于 0,代表通知开启;0: 通知关闭。
+// UIRemoteNotificationTypeNone = 0,
+// UIRemoteNotificationTypeBadge = 1 << 0,
+// UIRemoteNotificationTypeSound = 1 << 1,
+// UIRemoteNotificationTypeAlert = 1 << 2,
+// UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3,
+// Android: 返回值 1 代表通知启用;0: 通知关闭。
+JPushPlugin.prototype.getUserNotificationSettings = function(successCallback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getUserNotificationSettings", [], successCallback);
+  } else if (device.platform === "Android") {
+    this.callNative("areNotificationEnabled", [], successCallback);
+  }
+};
+
+// iOS methods
+
+JPushPlugin.prototype.startJPushSDK = function() {
+  this.callNative("startJPushSDK", [], null);
+};
+
+JPushPlugin.prototype.setBadge = function(value) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setBadge", [value], null);
+  }
+};
+
+JPushPlugin.prototype.resetBadge = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("resetBadge", [], null);
+  }
+};
+
+JPushPlugin.prototype.setDebugModeFromIos = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setDebugModeFromIos", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLogOFF = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLogOFF", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCrashLogON = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("crashLogON", [], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotificationForIOS = function(
+  delayTime,
+  content,
+  badge,
+  notificationID,
+  extras
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "setLocalNotification",
+      [delayTime, content, badge, notificationID, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.deleteLocalNotificationWithIdentifierKeyInIOS = function(
+  identifierKey
+) {
+  if (this.isPlatformIOS()) {
+    this.callNative(
+      "deleteLocalNotificationWithIdentifierKey",
+      [identifierKey],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.clearAllLocalNotifications = function() {
+  if (this.isPlatformIOS()) {
+    this.callNative("clearAllLocalNotifications", [], null);
+  }
+};
+
+JPushPlugin.prototype.setLocation = function(latitude, longitude) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setLocation", [latitude, longitude], null);
+  }
+};
+
+JPushPlugin.prototype.startLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("startLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.stopLogPageView = function(pageName) {
+  if (this.isPlatformIOS()) {
+    this.callNative("stopLogPageView", [pageName], null);
+  }
+};
+
+JPushPlugin.prototype.beginLogPageView = function(pageName, duration) {
+  if (this.isPlatformIOS()) {
+    this.callNative("beginLogPageView", [pageName, duration], null);
+  }
+};
+
+JPushPlugin.prototype.setApplicationIconBadgeNumber = function(badge) {
+  if (this.isPlatformIOS()) {
+    this.callNative("setApplicationIconBadgeNumber", [badge], null);
+  }
+};
+
+JPushPlugin.prototype.getApplicationIconBadgeNumber = function(callback) {
+  if (this.isPlatformIOS()) {
+    this.callNative("getApplicationIconBadgeNumber", [], callback);
+  }
+};
+
+JPushPlugin.prototype.addDismissActions = function(actions, categoryId) {
+  this.callNative("addDismissActions", [actions, categoryId]);
+};
+
+JPushPlugin.prototype.addNotificationActions = function(actions, categoryId) {
+  this.callNative("addNotificationActions", [actions, categoryId]);
+};
+
+// Android methods
+JPushPlugin.prototype.getConnectionState = function(successCallback) {
+  if (device.platform === "Android") {
+    this.callNative("getConnectionState", [], successCallback);
+  }
+};
+
+JPushPlugin.prototype.setBasicPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setBasicPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.setCustomPushNotificationBuilder = function() {
+  if (device.platform === "Android") {
+    this.callNative("setCustomPushNotificationBuilder", [], null);
+  }
+};
+
+JPushPlugin.prototype.receiveRegistrationIdInAndroidCallback = function(data) {
+  if (device.platform === "Android") {
+    data = JSON.stringify(data);
+    var event = JSON.parse(data);
+    cordova.fireDocumentEvent("jpush.receiveRegistrationId", event);
+  }
+};
+
+JPushPlugin.prototype.receiveMessageInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveMessage = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.receiveMessage", this.receiveMessage);
+};
+
+JPushPlugin.prototype.openNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.openNotification = JSON.parse(data);
+  cordova.fireDocumentEvent("jpush.openNotification", this.openNotification);
+};
+
+JPushPlugin.prototype.receiveNotificationInAndroidCallback = function(data) {
+  data = JSON.stringify(data);
+  this.receiveNotification = JSON.parse(data);
+  cordova.fireDocumentEvent(
+    "jpush.receiveNotification",
+    this.receiveNotification
+  );
+};
+
+JPushPlugin.prototype.clearAllNotification = function() {
+  if (device.platform === "Android") {
+    this.callNative("clearAllNotification", [], null);
+  }
+};
+
+JPushPlugin.prototype.clearNotificationById = function(id) {
+  if (device.platform === "Android") {
+    this.callNative("clearNotificationById", [id], null);
+  }
+};
+
+JPushPlugin.prototype.setLatestNotificationNum = function(num) {
+  if (device.platform === "Android") {
+    this.callNative("setLatestNotificationNum", [num], null);
+  }
+};
+
+JPushPlugin.prototype.addLocalNotification = function(
+  builderId,
+  content,
+  title,
+  notificationID,
+  broadcastTime,
+  extras
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "addLocalNotification",
+      [builderId, content, title, notificationID, broadcastTime, extras],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.removeLocalNotification = function(notificationID) {
+  if (device.platform === "Android") {
+    this.callNative("removeLocalNotification", [notificationID], null);
+  }
+};
+
+JPushPlugin.prototype.reportNotificationOpened = function(msgID) {
+  if (device.platform === "Android") {
+    this.callNative("reportNotificationOpened", [msgID], null);
+  }
+};
+
+/**
+ * 用于在 Android 6.0 及以上系统,申请一些权限
+ * 具体可看:http://docs.jpush.io/client/android_api/#android-60
+ */
+JPushPlugin.prototype.requestPermission = function() {
+  if (device.platform === "Android") {
+    this.callNative("requestPermission", [], null);
+  }
+};
+
+JPushPlugin.prototype.setSilenceTime = function(
+  startHour,
+  startMinute,
+  endHour,
+  endMinute
+) {
+  if (device.platform === "Android") {
+    this.callNative(
+      "setSilenceTime",
+      [startHour, startMinute, endHour, endMinute],
+      null
+    );
+  }
+};
+
+JPushPlugin.prototype.setPushTime = function(weekdays, startHour, endHour) {
+  if (device.platform === "Android") {
+    this.callNative("setPushTime", [weekdays, startHour, endHour], null);
+  }
+};
+
+JPushPlugin.prototype.setGeofenceInterval = function(interval) {
+  if (device.platform === "Android") {
+    this.callNative("setGeofenceInterval", [interval], null);
+  }
+};
+
+JPushPlugin.prototype.setMaxGeofenceNumber = function(maxNumber) {
+  if (device.platform === "Android") {
+    this.callNative("setMaxGeofenceNumber", [maxNumber], null);
+  }
+};
+
+if (!window.plugins) {
+  window.plugins = {};
+}
+
+if (!window.plugins.jPushPlugin) {
+  window.plugins.jPushPlugin = new JPushPlugin();
+}
+
+module.exports = new JPushPlugin();
index 112a927..fe23a8d 100644 (file)
@@ -22,6 +22,9 @@ var app = {
     // Application Constructor
     initialize: function() {
         document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
+        document.addEventListener('jpush.receiveRegistrationId', function (event) {
+          console.log(event.registrationId)
+        }, false)
     },
 
     onDeviceReady: function() {