From b2f133ffe639ecd7d6fb8158341208e2b24d7fb5 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 30 Nov 2015 22:46:52 -0500 Subject: geo --- StoneIsland/www/css/blogs.css | 4 ++++ StoneIsland/www/index.html | 2 ++ StoneIsland/www/js/index.js | 1 + StoneIsland/www/js/lib/etc/geo.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 StoneIsland/www/js/lib/etc/geo.js diff --git a/StoneIsland/www/css/blogs.css b/StoneIsland/www/css/blogs.css index 19e3f8fd..455b76d5 100644 --- a/StoneIsland/www/css/blogs.css +++ b/StoneIsland/www/css/blogs.css @@ -226,6 +226,10 @@ ul.links { transform: translateZ(0) translateX(-50%) translateY(-50%); } +#archive .menu .items { + border-top: 1px solid transparent; +} + #archive.menu .menu { opacity: 1; pointer-events: auto; diff --git a/StoneIsland/www/index.html b/StoneIsland/www/index.html index 5bab617f..7bd73c84 100644 --- a/StoneIsland/www/index.html +++ b/StoneIsland/www/index.html @@ -1063,6 +1063,8 @@ + + diff --git a/StoneIsland/www/js/index.js b/StoneIsland/www/js/index.js index 208eab21..1f2de128 100644 --- a/StoneIsland/www/js/index.js +++ b/StoneIsland/www/js/index.js @@ -64,6 +64,7 @@ var app = (function(){ app.ready = function(){ if (window.cordova) { cordova.plugins.Keyboard.disableScroll(true) + geo.fetch() } app.view = null diff --git a/StoneIsland/www/js/lib/etc/geo.js b/StoneIsland/www/js/lib/etc/geo.js new file mode 100644 index 00000000..0270d681 --- /dev/null +++ b/StoneIsland/www/js/lib/etc/geo.js @@ -0,0 +1,33 @@ +var geo = (function(){ + var geo = {} + + geo.fetch = function(){ + navigator.geolocation.getCurrentPosition(geo.success, geo.error, {timeout: 15000}) + } + + geo.success = function(position){ + var lat_str = as_degrees( position.coords.latitude ) + var lng_str = as_degrees( position.coords.longitude ) + } + + geo.error = function(error){ + $(".latlng").html( "+40° 58' 90\" -74° 04' 46\"" ) + } + + function as_degrees (n) { + var s = "" + if (n >= 0) s += "+" + s += Math.floor(n) + "° " + + n %= 1 + n *= 60 + s += Math.floor(n) + "'" + + n %= 1 + n *= 60 + + s += Math.floor(n) + '"' + } + + return geo +})() \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ef14c875d7056f90be606ebf1f389c112b947f81 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 30 Nov 2015 23:07:12 -0500 Subject: canadian country code --- .../ios/StoneIsland/StoneIsland-Info.plist | 9 + StoneIsland/platforms/ios/www/cordova_plugins.js | 10 +- .../www/ios/LaunchMyApp.js | 11 + .../LaunchMyApp-PhoneGap-Plugin.iws | 1242 ++++++++++++++++++++ .../cordova-plugin-customurlscheme/README.md | 201 ++++ .../atlassian-ide-plugin.xml | 5 + .../cordova-plugin-customurlscheme/package.json | 43 + .../cordova-plugin-customurlscheme/plugin.xml | 107 ++ .../android/nl/xservices/plugins/LaunchMyApp.java | 136 +++ .../www/android/LaunchMyApp.js | 24 + .../www/ios/LaunchMyApp.js | 9 + .../www/windows/LaunchMyApp.js | 10 + StoneIsland/plugins/fetch.json | 10 + StoneIsland/plugins/ios.json | 10 + StoneIsland/www/js/lib/cart/CartShipping.js | 2 +- 15 files changed, 1827 insertions(+), 2 deletions(-) create mode 100644 StoneIsland/platforms/ios/www/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/LaunchMyApp-PhoneGap-Plugin.iws create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/README.md create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/atlassian-ide-plugin.xml create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/package.json create mode 100755 StoneIsland/plugins/cordova-plugin-customurlscheme/plugin.xml create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/src/android/nl/xservices/plugins/LaunchMyApp.java create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/www/android/LaunchMyApp.js create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js create mode 100644 StoneIsland/plugins/cordova-plugin-customurlscheme/www/windows/LaunchMyApp.js diff --git a/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist b/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist index 160c02b8..cc7d9214 100644 --- a/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist +++ b/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist @@ -182,5 +182,14 @@ remote-notification + CFBundleURLTypes + + + CFBundleURLSchemes + + stoneisland + + + \ No newline at end of file diff --git a/StoneIsland/platforms/ios/www/cordova_plugins.js b/StoneIsland/platforms/ios/www/cordova_plugins.js index fbdc76c9..99a9e96f 100644 --- a/StoneIsland/platforms/ios/www/cordova_plugins.js +++ b/StoneIsland/platforms/ios/www/cordova_plugins.js @@ -91,6 +91,13 @@ module.exports = [ "clobbers": [ "PushNotification" ] + }, + { + "file": "plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js", + "id": "cordova-plugin-customurlscheme.LaunchMyApp", + "clobbers": [ + "window.plugins.launchmyapp" + ] } ]; module.exports.metadata = @@ -104,7 +111,8 @@ module.exports.metadata = "cordova-plugin-geolocation": "1.0.1", "cordova-plugin-network-information": "1.0.1", "cordova-plugin-splashscreen": "2.1.0", - "phonegap-plugin-push": "1.4.4" + "phonegap-plugin-push": "1.4.4", + "cordova-plugin-customurlscheme": "4.0.0" } // BOTTOM OF METADATA }); \ No newline at end of file diff --git a/StoneIsland/platforms/ios/www/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js b/StoneIsland/platforms/ios/www/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js new file mode 100644 index 00000000..3568c73f --- /dev/null +++ b/StoneIsland/platforms/ios/www/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js @@ -0,0 +1,11 @@ +cordova.define("cordova-plugin-customurlscheme.LaunchMyApp", function(require, exports, module) { "use strict"; + +/* + Q: Why an empty file? + A: iOS doesn't need plumbing to get the plugin to work, so.. + - Including no file would mean the import in index.html would differ per platform. + - Also, using one version and adding a userAgent check for Android feels wrong. + - And if you're not using PhoneGap Build, you could paste your handleOpenUrl JS function here. +*/ + +}); diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/LaunchMyApp-PhoneGap-Plugin.iws b/StoneIsland/plugins/cordova-plugin-customurlscheme/LaunchMyApp-PhoneGap-Plugin.iws new file mode 100644 index 00000000..9879f7f4 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/LaunchMyApp-PhoneGap-Plugin.iws @@ -0,0 +1,1242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + 5050 + + + + + + + + + + 1380298653954 + + + 1380299229689 + + + 1380302935690 + + + 1380305538088 + + + 1380305862564 + + + 1380306489891 + + + 1380307532008 + + + 1380307669209 + + + 1380311306948 + + + 1380311921427 + + + 1380317008739 + + + 1380317829887 + + + 1380353871771 + + + 1380358237732 + + + 1380358605885 + + + 1380376149014 + + + 1380378153190 + + + 1380383948466 + + + 1380393159906 + + + 1380393508799 + + + 1380393757355 + + + 1380393772924 + + + 1380402190419 + + + 1380402398063 + + + 1380402815702 + + + 1380488689110 + + + 1380488797067 + + + 1380488875151 + + + 1380489049263 + + + 1380489820453 + + + 1380567506465 + + + 1380567576964 + + + 1380567665822 + + + 1383169425770 + + + 1383172326587 + + + 1383174045796 + + + 1390857761557 + + + 1390857936690 + + + 1391028041914 + + + 1402048017666 + + + 1417632494865 + + + 1417703047123 + + + 1417706007258 + + + 1434477311221 + + + 1434530307503 + + + 1434577022103 + + + 1435161053225 + + + 1440664137793 + + + 1440664585797 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/README.md b/StoneIsland/plugins/cordova-plugin-customurlscheme/README.md new file mode 100644 index 00000000..2de829c2 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/README.md @@ -0,0 +1,201 @@ +# Custom URL scheme PhoneGap Plugin +#### launch your app by a link like this: `mycoolapp://` +for iOS and Android, by [Eddy Verbruggen](http://www.x-services.nl) +- This repo is for PhoneGap 3.0.0 and up +- For PhoneGap 2.9.0 and lower, [switch to the phonegap-2.9.0-and-lower branch](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin/tree/phonegap-2.9.0-and-lower) + +1. [Description](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#1-description) +2. [Installation](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#2-installation) + 2. [Automatically (CLI / Plugman)](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#automatically-cli--plugman) + 2. [Manually](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#manually) + 2. [PhoneGap Build](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#phonegap-build) +3. [Usage](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#3-usage) +4. [URL Scheme hints](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#4-url-scheme-hints) +5. [License](https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin#5-license) + + +### BEWARE: [This Apache Cordova issue](https://issues.apache.org/jira/browse/CB-7606) causes problems with Cordova-iOS 3.7.0: the `handleOpenURL` function is not invoked upon cold start. Use a higher or lower version than 3.7.0. + +## 1. Description + +This plugin allows you to start your app by calling it with a URL like `mycoolapp://path?foo=bar` + +* Compatible with [Cordova Plugman](https://github.com/apache/cordova-plugman) +* Submitted and waiting for approval at PhoneGap Build ([more information](https://build.phonegap.com/plugins)) + +### iOS specifics +* Forget about [using config.xml to define a URL scheme](https://build.phonegap.com/docs/config-xml#url_schemes). This plugin adds 2 essential enhancements: + - Uniform URL scheme with Android (for which there is no option to define a URL scheme via PhoneGap configuration at all). + - You still need to wire up the Javascript to handle incoming events. This plugin assists you with that. +* Tested on iOS 5.1, 6 and 7. + +### Android specifics +* Unlike iOS, there is no way to use config.xml to define a scheme for your app. Now there is. +* Tested on Android 4.3, will most likely work with 2.2 and up. + + +## 2. Installation + +### Automatically (CLI / Plugman) +LaunchMyApp is compatible with [Cordova Plugman](https://github.com/apache/cordova-plugman). +Replace `mycoolapp` by a nice scheme you want to have your app listen to: + +Latest release on npm: +``` +$ cordova plugin add cordova-plugin-customurlscheme --variable URL_SCHEME=mycoolapp +``` + +Bleeding edge master version from Github: +``` +$ cordova plugin add https://github.com/EddyVerbruggen/LaunchMyApp-PhoneGap-Plugin.git --variable URL_SCHEME=mycoolapp +``` +(Note that the Phonegap CLI didn't support `--variable` before version 3.6.3, so please use the Cordova CLI as shown above in case you're on an older version) + +The LaunchMyApp.js file is brought in automatically. + +Note for iOS: there was a bug in CLI which caused an error in your `*-Info.plist`. +Please manually remove the blank line and whitespace (if any) from `NSMainNibFile` and `NSMainNibFile~ipad` (or your app won't start at all). + + +### Manually +Don't shoot yourself in the foot - use the CLI! That being said, here goes: + +#### iOS +1\. `Copy www/ios/LaunchMyApp.js` to `www/js/plugins/LaunchMyApp.js` and reference it in your `index.html`: +```html + +``` + +2\. Add this to your `*-Info.plist` (replace `URL_SCHEME` by a nice scheme you want to have your app listen to, like `mycoolapp`): +```xml +CFBundleURLTypes + + + CFBundleURLSchemes + + URL_SCHEME + + + +``` + +#### Android +1\. Copy www/android/LaunchMyApp.js to www/js/plugins/LaunchMyApp.js and reference it in your `index.html`: +```html + +``` + +2\. Add the following xml to your `config.xml` to always use the latest version of this plugin: +```xml + +``` + +3\. Copy `LaunchMyApp.java` to `platforms/android/src/nl/xservices/plugins` (create the folders) + +4\. Add the following to your `AndroidManifest.xml` inside the `/manifest/application/activity` node (replace `URL_SCHEME` by a nice scheme you want to have your app listen to, like `mycoolapp`): +```xml + + + + + + +``` + +5\. In `AndroidManifest.xml` set the launchMode to singleTask to avoid issues like [#24]. ` + + +``` + +The LaunchMyApp.js file is brought in automatically. + +NOTE: When Hydration is enabled at PGB, this plugin may not work. + +### Restoring cordova plugin settings on plugin add or update +In order to be able to restore the plugin settings on `cordova plugin add`, one need to add the following feature into config.xml. Note that if you added the plugin with the `--save` param you will find this in your `config.xml` already, except for the `variable` tag which is likely a `param` tag. [Change that.](https://github.com/EddyVerbruggen/Custom-URL-scheme/issues/76) +```xml + + + + + +``` + +Please notice that URL_SCHEME is saved as `variable`, not as `prop`. However if you do `cordova plugin add` with a --save option, cordova will write the URL_SCHEME as a `prop`, you need to change the tag name from `param` to `variable` in this case. + +These plugin restore instructions are tested on: +cordova-cli 4.3.+ and cordova-android 3.7.1+ + + +## 3. Usage + +1a\. Your app can be launced by linking to it like this from a website or an email for example (all of these will work): +```html +Open my app +Open my app +Open my app +Open my app +``` + +`mycoolapp` is the value of URL_SCHEME you used while installing this plugin. + +1b\. If you're trying to open your app from another PhoneGap app, use the InAppBrowser plugin and launch the receiving app like this, to avoid a 'protocol not supported' error: +```html + +``` + +2\. When your app is launched by a URL, you probably want to do something based on the path and parameters in the URL. For that, you could implement the (optional) `handleOpenURL(url)` method, which receives the URL that was used to launch your app. +```javascript +function handleOpenURL(url) { + console.log("received url: " + url); +} +``` + +If you want to alert the URL for testing the plugin, at least on iOS you need to wrap it in a timeout like this: +```javascript +function handleOpenURL(url) { + setTimeout(function() { + alert("received url: " + url); + }, 0); +} +``` +A more useful implementation would mean parsing the URL, saving any params to sessionStorage and redirecting the app to the correct page inside your app. +All this happens before the first page is loaded. + +## 4. URL Scheme hints +Please choose a URL_SCHEME which which complies to these restrictions: +- Don't use an already registered scheme (like `fb`, `twitter`, `comgooglemaps`, etc). +- Use only lowercase characters. +- Don't use a dash `-` because on Android it will become underscore `_`. +- Use only 1 word (no spaces). + +TIP: test your scheme by installing the app on a device or simulator and typing yourscheme:// in the browser URL bar, or create a test HTML page with a link to your app to impress your buddies. + + +## 5. License + +[The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html) + +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/StoneIsland/plugins/cordova-plugin-customurlscheme/atlassian-ide-plugin.xml b/StoneIsland/plugins/cordova-plugin-customurlscheme/atlassian-ide-plugin.xml new file mode 100644 index 00000000..858eed55 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/atlassian-ide-plugin.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/package.json b/StoneIsland/plugins/cordova-plugin-customurlscheme/package.json new file mode 100644 index 00000000..de90c8e2 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/package.json @@ -0,0 +1,43 @@ +{ + "name": "cordova-plugin-customurlscheme", + "version": "4.0.0", + "description": "Launch your app by using this URL: mycoolapp://, you can add a path and even pass params like this: mycoolerapp://somepath?foo=bar", + "cordova": { + "id": "cordova-plugin-customurlscheme", + "platforms": [ + "ios", + "android", + "windows8", + "windows" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/EddyVerbruggen/Custom-URL-scheme.git" + }, + "keywords": [ + "Custom URL Scheme", + "URLscheme", + "URL scheme", + "Custom URL", + "Launch My App", + "Launch App", + "ecosystem:cordova", + "cordova-ios", + "cordova-android", + "cordova-windows", + "cordova-windows8" + ], + "engines": [ + { + "name": "cordova", + "version": ">=3.0.0" + } + ], + "author": "Eddy Verbruggen (https://github.com/EddyVerbruggen)", + "license": "MIT", + "bugs": { + "url": "https://github.com/EddyVerbruggen/Custom-URL-scheme/issues" + }, + "homepage": "https://github.com/EddyVerbruggen/Custom-URL-scheme#readme" +} \ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/plugin.xml b/StoneIsland/plugins/cordova-plugin-customurlscheme/plugin.xml new file mode 100755 index 00000000..1e43e724 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/plugin.xml @@ -0,0 +1,107 @@ + + + + Custom URL scheme + + + Launch your app by using this URL: mycoolapp:// + You can add a path and even pass params like this: mycoolerapp://somepath?foo=bar + + + Eddy Verbruggen + + MIT + + Custom URL Scheme, URLscheme, URL scheme, Custom URL, Launch My App, Launch App + + https://github.com/EddyVerbruggen/Custom-URL-scheme.git + + https://github.com/EddyVerbruggen/Custom-URL-scheme/issues + + + + + + + + + + + + + + + + + CFBundleURLSchemes + + $URL_SCHEME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/src/android/nl/xservices/plugins/LaunchMyApp.java b/StoneIsland/plugins/cordova-plugin-customurlscheme/src/android/nl/xservices/plugins/LaunchMyApp.java new file mode 100644 index 00000000..9d3f3d63 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/src/android/nl/xservices/plugins/LaunchMyApp.java @@ -0,0 +1,136 @@ +package nl.xservices.plugins; + +import android.content.Intent; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaActivity; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Locale; + +public class LaunchMyApp extends CordovaPlugin { + + private static final String ACTION_CHECKINTENT = "checkIntent"; + + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if (ACTION_CHECKINTENT.equalsIgnoreCase(action)) { + final Intent intent = ((CordovaActivity) this.webView.getContext()).getIntent(); + final String intentString = intent.getDataString(); + if (intentString != null && intentString.contains("://") && intent.getScheme() != null) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, intent.getDataString())); + intent.setData(null); + } else { + callbackContext.error("App was not started via the launchmyapp URL scheme. Ignoring this errorcallback is the best approach."); + } + return true; + } else { + callbackContext.error("This plugin only responds to the " + ACTION_CHECKINTENT + " action."); + return false; + } + } + + @Override + public void onNewIntent(Intent intent) { + final String intentString = intent.getDataString(); + if (intentString != null && intentString.contains("://") && intent.getScheme() != null) { + intent.setData(null); + try { + StringWriter writer = new StringWriter(intentString.length() * 2); + escapeJavaStyleString(writer, intentString, true, false); + webView.loadUrl("javascript:handleOpenURL('" + writer.toString() + "');"); + } catch (IOException ignore) { + } + } + } + + // Taken from commons StringEscapeUtils + private static void escapeJavaStyleString(Writer out, String str, boolean escapeSingleQuote, + boolean escapeForwardSlash) throws IOException { + if (out == null) { + throw new IllegalArgumentException("The Writer must not be null"); + } + if (str == null) { + return; + } + int sz; + sz = str.length(); + for (int i = 0; i < sz; i++) { + char ch = str.charAt(i); + + // handle unicode + if (ch > 0xfff) { + out.write("\\u" + hex(ch)); + } else if (ch > 0xff) { + out.write("\\u0" + hex(ch)); + } else if (ch > 0x7f) { + out.write("\\u00" + hex(ch)); + } else if (ch < 32) { + switch (ch) { + case '\b': + out.write('\\'); + out.write('b'); + break; + case '\n': + out.write('\\'); + out.write('n'); + break; + case '\t': + out.write('\\'); + out.write('t'); + break; + case '\f': + out.write('\\'); + out.write('f'); + break; + case '\r': + out.write('\\'); + out.write('r'); + break; + default: + if (ch > 0xf) { + out.write("\\u00" + hex(ch)); + } else { + out.write("\\u000" + hex(ch)); + } + break; + } + } else { + switch (ch) { + case '\'': + if (escapeSingleQuote) { + out.write('\\'); + } + out.write('\''); + break; + case '"': + out.write('\\'); + out.write('"'); + break; + case '\\': + out.write('\\'); + out.write('\\'); + break; + case '/': + if (escapeForwardSlash) { + out.write('\\'); + } + out.write('/'); + break; + default: + out.write(ch); + break; + } + } + } + } + + private static String hex(char ch) { + return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH); + } +} \ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/www/android/LaunchMyApp.js b/StoneIsland/plugins/cordova-plugin-customurlscheme/www/android/LaunchMyApp.js new file mode 100644 index 00000000..19dbdc0b --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/www/android/LaunchMyApp.js @@ -0,0 +1,24 @@ +(function () { + "use strict"; + + var remainingAttempts = 10; + + function waitForAndCallHandlerFunction(url) { + if (typeof window.handleOpenURL == "function") { + window.handleOpenURL(url); + } else if (remainingAttempts-- > 0) { + setTimeout(function(){waitForAndCallHandlerFunction(url)}, 500); + } + } + + function triggerOpenURL() { + cordova.exec( + waitForAndCallHandlerFunction, + null, + "LaunchMyApp", + "checkIntent", + []); + } + + document.addEventListener("deviceready", triggerOpenURL, false); +}()); diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js b/StoneIsland/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js new file mode 100644 index 00000000..a1d60818 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/www/ios/LaunchMyApp.js @@ -0,0 +1,9 @@ +"use strict"; + +/* + Q: Why an empty file? + A: iOS doesn't need plumbing to get the plugin to work, so.. + - Including no file would mean the import in index.html would differ per platform. + - Also, using one version and adding a userAgent check for Android feels wrong. + - And if you're not using PhoneGap Build, you could paste your handleOpenUrl JS function here. +*/ diff --git a/StoneIsland/plugins/cordova-plugin-customurlscheme/www/windows/LaunchMyApp.js b/StoneIsland/plugins/cordova-plugin-customurlscheme/www/windows/LaunchMyApp.js new file mode 100644 index 00000000..a70d5e94 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-customurlscheme/www/windows/LaunchMyApp.js @@ -0,0 +1,10 @@ +(function () { + function activatedHandler(e) { + if (typeof handleOpenURL == 'function' && e.uri) { + handleOpenURL(e.uri.rawUri); + } + }; + + var wui = Windows.UI.WebUI.WebUIApplication; + wui.addEventListener("activated", activatedHandler, false); +}()); diff --git a/StoneIsland/plugins/fetch.json b/StoneIsland/plugins/fetch.json index b57114f3..59ddd3e7 100644 --- a/StoneIsland/plugins/fetch.json +++ b/StoneIsland/plugins/fetch.json @@ -70,5 +70,15 @@ }, "is_top_level": true, "variables": {} + }, + "cordova-plugin-customurlscheme": { + "source": { + "type": "registry", + "id": "cordova-plugin-customurlscheme" + }, + "is_top_level": true, + "variables": { + "URL_SCHEME": "stoneisland" + } } } \ No newline at end of file diff --git a/StoneIsland/plugins/ios.json b/StoneIsland/plugins/ios.json index 337f2532..2a7a09ab 100644 --- a/StoneIsland/plugins/ios.json +++ b/StoneIsland/plugins/ios.json @@ -84,6 +84,12 @@ "xml": "remote-notification", "count": 1 } + ], + "CFBundleURLTypes": [ + { + "xml": "CFBundleURLSchemesstoneisland", + "count": 1 + } ] } } @@ -116,6 +122,10 @@ }, "phonegap-plugin-push": { "PACKAGE_NAME": "io.cordova.hellocordova" + }, + "cordova-plugin-customurlscheme": { + "URL_SCHEME": "stoneisland", + "PACKAGE_NAME": "io.cordova.hellocordova" } }, "dependent_plugins": {} diff --git a/StoneIsland/www/js/lib/cart/CartShipping.js b/StoneIsland/www/js/lib/cart/CartShipping.js index 1a9653e1..9b8205c1 100644 --- a/StoneIsland/www/js/lib/cart/CartShipping.js +++ b/StoneIsland/www/js/lib/cart/CartShipping.js @@ -101,7 +101,7 @@ var CartShipping = FormView.extend({ shipping_info.City = address_data.City shipping_info.Province = address_data.Province shipping_info.Region = address_data.Province - shipping_info.CountryCode = "US" + shipping_info.CountryCode = CANADIAN_LOOKUP[ address_data.Province ] ? "CA" : "US" return shipping_info }, -- cgit v1.2.3-70-g09d2 From 83a884197ff0226631a46e4894f22b8468b15598 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 30 Nov 2015 23:11:28 -0500 Subject: add deep linking lib --- StoneIsland/www/index.html | 2 ++ StoneIsland/www/js/index.js | 2 +- StoneIsland/www/js/lib/_router.js | 11 +++++++++++ StoneIsland/www/js/lib/etc/deeplink.js | 3 +++ StoneIsland/www/js/lib/etc/push.js | 1 + 5 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 StoneIsland/www/js/lib/etc/deeplink.js create mode 100644 StoneIsland/www/js/lib/etc/push.js diff --git a/StoneIsland/www/index.html b/StoneIsland/www/index.html index 7bd73c84..03d4a958 100644 --- a/StoneIsland/www/index.html +++ b/StoneIsland/www/index.html @@ -1063,6 +1063,8 @@ + + diff --git a/StoneIsland/www/js/index.js b/StoneIsland/www/js/index.js index 1f2de128..e6bdf49f 100644 --- a/StoneIsland/www/js/index.js +++ b/StoneIsland/www/js/index.js @@ -69,7 +69,7 @@ var app = (function(){ app.view = null app.router = new SiteRouter () - app.account.connect( app.router.route.bind(app.router) ) + app.account.connect( app.router.launch.bind(app.router) ) $("body").removeClass("loading") } diff --git a/StoneIsland/www/js/lib/_router.js b/StoneIsland/www/js/lib/_router.js index b70d9be8..23daf4c7 100644 --- a/StoneIsland/www/js/lib/_router.js +++ b/StoneIsland/www/js/lib/_router.js @@ -47,6 +47,17 @@ var SiteRouter = Router.extend({ } } }, + + initial_route: null, + launch: function(){ + if (this.initial_route) { + this.parseRoute( this.initial_route ) + } + else { + this.route() + } + this.initial_route = null + }, go: function(url){ if (app.view && app.view.hide) { diff --git a/StoneIsland/www/js/lib/etc/deeplink.js b/StoneIsland/www/js/lib/etc/deeplink.js new file mode 100644 index 00000000..648dd167 --- /dev/null +++ b/StoneIsland/www/js/lib/etc/deeplink.js @@ -0,0 +1,3 @@ +function handleOpenURL (url) { + app.router.initial_route = url +} \ No newline at end of file diff --git a/StoneIsland/www/js/lib/etc/push.js b/StoneIsland/www/js/lib/etc/push.js new file mode 100644 index 00000000..ab0c0141 --- /dev/null +++ b/StoneIsland/www/js/lib/etc/push.js @@ -0,0 +1 @@ +// \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f6e7c07078252d72b0c7725350a83adfff740b99 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 30 Nov 2015 23:19:28 -0500 Subject: plugins --- .../ios/StoneIsland.xcodeproj/project.pbxproj | 14 + .../cordova-plugin-x-socialsharing/SocialSharing.h | 27 + .../cordova-plugin-x-socialsharing/SocialSharing.m | 715 +++++++++++++++++++++ .../ios/StoneIsland/StoneIsland-Info.plist | 6 + StoneIsland/platforms/ios/StoneIsland/config.xml | 10 +- StoneIsland/platforms/ios/www/cordova_plugins.js | 10 +- StoneIsland/platforms/ios/www/css/account.css | 26 +- StoneIsland/platforms/ios/www/css/blogs.css | 46 +- StoneIsland/platforms/ios/www/css/cart.css | 40 ++ StoneIsland/platforms/ios/www/css/nav.css | 6 +- StoneIsland/platforms/ios/www/css/products.css | 22 +- StoneIsland/platforms/ios/www/img/compass-logo.png | Bin 137677 -> 75721 bytes .../platforms/ios/www/img/compass-logo.png.old | Bin 0 -> 137677 bytes StoneIsland/platforms/ios/www/img/spinner.gif | Bin 0 -> 5694 bytes StoneIsland/platforms/ios/www/index.html | 124 ++-- StoneIsland/platforms/ios/www/js/index.js | 5 +- StoneIsland/platforms/ios/www/js/lib/_router.js | 11 + .../platforms/ios/www/js/lib/account/OrdersView.js | 173 ++++- .../ios/www/js/lib/account/ProfileView.js | 7 +- .../ios/www/js/lib/account/orders/OrderList.js | 17 - .../ios/www/js/lib/account/orders/SingleOrder.js | 18 - .../platforms/ios/www/js/lib/auth/SignupView.js | 3 +- .../platforms/ios/www/js/lib/blogs/ArchiveView.js | 2 + .../platforms/ios/www/js/lib/blogs/BlogView.js | 13 + .../platforms/ios/www/js/lib/blogs/HubView.js | 11 + .../platforms/ios/www/js/lib/cart/CartShipping.js | 2 +- .../platforms/ios/www/js/lib/cart/CartSummary.js | 6 +- .../platforms/ios/www/js/lib/cart/CartThanks.js | 2 + .../platforms/ios/www/js/lib/etc/deeplink.js | 3 + StoneIsland/platforms/ios/www/js/lib/etc/geo.js | 33 + StoneIsland/platforms/ios/www/js/lib/etc/push.js | 1 + .../platforms/ios/www/js/lib/nav/AddressView.js | 16 + .../platforms/ios/www/js/lib/nav/CreditCardView.js | 11 +- .../ios/www/js/lib/products/CollectionView.js | 5 +- .../ios/www/js/lib/products/GalleryView.js | 8 +- .../ios/www/js/lib/products/ProductView.js | 22 +- .../platforms/ios/www/js/lib/view/Scrollable.js | 11 +- StoneIsland/platforms/ios/www/js/sdk/account.js | 32 + StoneIsland/platforms/ios/www/js/sdk/cart.js | 16 + .../www/SocialSharing.js | 117 ++++ .../cordova-plugin-x-socialsharing/README.md | 471 ++++++++++++++ .../cordova-plugin-x-socialsharing/package.json | 46 ++ .../cordova-plugin-x-socialsharing/plugin.xml | 85 +++ .../screenshots/screenshot-android-share.png | Bin 0 -> 142454 bytes .../screenshots/screenshot-ios6-share.png | Bin 0 -> 48752 bytes .../screenshots/screenshot-ios7-ipad-share.png | Bin 0 -> 47118 bytes .../screenshots/screenshot-ios7-share.png | Bin 0 -> 342109 bytes .../screenshots/screenshot-wp8-share.jpg | Bin 0 -> 33347 bytes .../screenshots/screenshots-ios7-shareconfig.png | Bin 0 -> 183133 bytes .../nl/xservices/plugins/SocialSharing.java | 533 +++++++++++++++ .../src/ios/SocialSharing.h | 27 + .../src/ios/SocialSharing.m | 715 +++++++++++++++++++++ .../src/windows/SocialSharingProxy.js | 114 ++++ .../src/wp8/SocialSharing.cs | 103 +++ .../tests/plugin.xml | 13 + .../cordova-plugin-x-socialsharing/tests/test.js | 363 +++++++++++ .../www/SocialSharing.js | 115 ++++ StoneIsland/plugins/fetch.json | 8 + StoneIsland/plugins/ios.json | 19 + 59 files changed, 4094 insertions(+), 109 deletions(-) create mode 100755 StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h create mode 100755 StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m mode change 100644 => 100755 StoneIsland/platforms/ios/www/img/compass-logo.png create mode 100644 StoneIsland/platforms/ios/www/img/compass-logo.png.old create mode 100644 StoneIsland/platforms/ios/www/img/spinner.gif delete mode 100644 StoneIsland/platforms/ios/www/js/lib/account/orders/OrderList.js delete mode 100644 StoneIsland/platforms/ios/www/js/lib/account/orders/SingleOrder.js create mode 100644 StoneIsland/platforms/ios/www/js/lib/etc/deeplink.js create mode 100644 StoneIsland/platforms/ios/www/js/lib/etc/geo.js create mode 100644 StoneIsland/platforms/ios/www/js/lib/etc/push.js create mode 100644 StoneIsland/platforms/ios/www/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/README.md create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json create mode 100755 StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java create mode 100755 StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h create mode 100755 StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js create mode 100644 StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js diff --git a/StoneIsland/platforms/ios/StoneIsland.xcodeproj/project.pbxproj b/StoneIsland/platforms/ios/StoneIsland.xcodeproj/project.pbxproj index e75b8393..53038bdc 100644 --- a/StoneIsland/platforms/ios/StoneIsland.xcodeproj/project.pbxproj +++ b/StoneIsland/platforms/ios/StoneIsland.xcodeproj/project.pbxproj @@ -55,6 +55,9 @@ C2BF0352F9A246A886C16676 /* CDVViewController+SplashScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5B2F329484022BEE14D58 /* CDVViewController+SplashScreen.m */; }; 4A80D26AF40046D7AD4979A9 /* AppDelegate+notification.m in Sources */ = {isa = PBXBuildFile; fileRef = EE6BC93769C04564BA35B9B4 /* AppDelegate+notification.m */; }; 4ABEABC16E324DEEB7A72E3B /* PushPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 450F6AEB11F34A6080834666 /* PushPlugin.m */; }; + 6084459294A8473FB8AA20F3 /* SocialSharing.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D2A7B40020F4AAEBE6DCEB1 /* SocialSharing.m */; }; + 98F1852ABB5E4B118CC6C27B /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88275732925E4321970403F3 /* Social.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 4C059853D8B843F88F01C944 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A186FB94DE714DEC95EA29D9 /* MessageUI.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -148,6 +151,10 @@ 450F6AEB11F34A6080834666 /* PushPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "PushPlugin.m"; path = "phonegap-plugin-push/PushPlugin.m"; sourceTree = ""; fileEncoding = 4; }; 9F746E936D46499681DECD6E /* AppDelegate+notification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "AppDelegate+notification.h"; path = "phonegap-plugin-push/AppDelegate+notification.h"; sourceTree = ""; fileEncoding = 4; }; A3C287A7B1C74C02A8585FC2 /* PushPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "PushPlugin.h"; path = "phonegap-plugin-push/PushPlugin.h"; sourceTree = ""; fileEncoding = 4; }; + 0D2A7B40020F4AAEBE6DCEB1 /* SocialSharing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "SocialSharing.m"; path = "cordova-plugin-x-socialsharing/SocialSharing.m"; sourceTree = ""; fileEncoding = 4; }; + 8A80AE176DAB4F84AC8031D7 /* SocialSharing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SocialSharing.h"; path = "cordova-plugin-x-socialsharing/SocialSharing.h"; sourceTree = ""; fileEncoding = 4; }; + 88275732925E4321970403F3 /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "Social.framework"; path = "System/Library/Frameworks/Social.framework"; sourceTree = SDKROOT; fileEncoding = 4; }; + A186FB94DE714DEC95EA29D9 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "MessageUI.framework"; path = "System/Library/Frameworks/MessageUI.framework"; sourceTree = SDKROOT; fileEncoding = 4; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -162,6 +169,8 @@ 05EBE3ED6EA64212BCC52906 /* AudioToolbox.framework in Frameworks */, 85552B118FDD4F9286C3D33C /* CoreLocation.framework in Frameworks */, 28FA0D2F417F4E6891D4A2A3 /* SystemConfiguration.framework in Frameworks */, + 98F1852ABB5E4B118CC6C27B /* Social.framework in Frameworks */, + 4C059853D8B843F88F01C944 /* MessageUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -238,6 +247,8 @@ 270ADA31CA7B4A42B185E451 /* AudioToolbox.framework */, 88109882DED84831BEC5BBB0 /* CoreLocation.framework */, E63CFDA045E649E8A2E41A6E /* SystemConfiguration.framework */, + 88275732925E4321970403F3 /* Social.framework */, + A186FB94DE714DEC95EA29D9 /* MessageUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -277,6 +288,8 @@ 450F6AEB11F34A6080834666 /* PushPlugin.m */, 9F746E936D46499681DECD6E /* AppDelegate+notification.h */, A3C287A7B1C74C02A8585FC2 /* PushPlugin.h */, + 0D2A7B40020F4AAEBE6DCEB1 /* SocialSharing.m */, + 8A80AE176DAB4F84AC8031D7 /* SocialSharing.h */, ); name = Plugins; path = "StoneIsland/Plugins"; @@ -474,6 +487,7 @@ C2BF0352F9A246A886C16676 /* CDVViewController+SplashScreen.m in Sources */, 4A80D26AF40046D7AD4979A9 /* AppDelegate+notification.m in Sources */, 4ABEABC16E324DEEB7A72E3B /* PushPlugin.m in Sources */, + 6084459294A8473FB8AA20F3 /* SocialSharing.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h b/StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h new file mode 100755 index 00000000..b51474d3 --- /dev/null +++ b/StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h @@ -0,0 +1,27 @@ +#import +#import + +@interface SocialSharing : CDVPlugin + +@property (nonatomic, strong) MFMailComposeViewController *globalMailComposer; +@property (retain) UIDocumentInteractionController * documentInteractionController; +@property (retain) NSString * tempStoredFile; +@property (retain) CDVInvokedUrlCommand * command; + +- (void)available:(CDVInvokedUrlCommand*)command; +- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command; +- (void)share:(CDVInvokedUrlCommand*)command; +- (void)canShareVia:(CDVInvokedUrlCommand*)command; +- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command; +- (void)shareVia:(CDVInvokedUrlCommand*)command; +- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command; +- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command; +- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command; +- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command; +- (void)shareViaSMS:(CDVInvokedUrlCommand*)command; +- (void)shareViaEmail:(CDVInvokedUrlCommand*)command; +- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command; + +- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command; + +@end diff --git a/StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m b/StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m new file mode 100755 index 00000000..cd0913a4 --- /dev/null +++ b/StoneIsland/platforms/ios/StoneIsland/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m @@ -0,0 +1,715 @@ +#import "SocialSharing.h" +#import +#import +#import +#import +#import +#import + +@implementation SocialSharing { + UIPopoverController *_popover; + NSString *_popupCoordinates; +} + +- (void)pluginInitialize { + if ([self isEmailAvailable]) { + [self cycleTheGlobalMailComposer]; + } +} + +- (void)available:(CDVInvokedUrlCommand*)command { + BOOL avail = NO; + if (NSClassFromString(@"UIActivityViewController")) { + avail = YES; + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:avail]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (NSString*)getIPadPopupCoordinates { + if (_popupCoordinates != nil) { + return _popupCoordinates; + } + if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { + return [(UIWebView*)self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.socialsharing.iPadPopupCoordinates();"]; + } else { + // prolly a wkwebview, ignoring for now + return nil; + } +} + +- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command { + _popupCoordinates = [command.arguments objectAtIndex:0]; +} + +- (CGRect)getPopupRectFromIPadPopupCoordinates:(NSArray*)comps { + CGRect rect = CGRectZero; + if ([comps count] == 4) { + rect = CGRectMake([[comps objectAtIndex:0] integerValue], [[comps objectAtIndex:1] integerValue], [[comps objectAtIndex:2] integerValue], [[comps objectAtIndex:3] integerValue]); + } + return rect; +} + +- (void)share:(CDVInvokedUrlCommand*)command { + [self.commandDelegate runInBackground:^{ //avoid main thread block especially if sharing big files from url + if (!NSClassFromString(@"UIActivityViewController")) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + NSString *message = [command.arguments objectAtIndex:0]; + NSString *subject = [command.arguments objectAtIndex:1]; + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + + NSMutableArray *activityItems = [[NSMutableArray alloc] init]; + [activityItems addObject:message]; + + NSMutableArray *files = [[NSMutableArray alloc] init]; + if (filenames != (id)[NSNull null] && filenames.count > 0) { + for (NSString* filename in filenames) { + NSObject *file = [self getImage:filename]; + if (file == nil) { + file = [self getFile:filename]; + } + if (file != nil) { + [files addObject:file]; + } + } + [activityItems addObjectsFromArray:files]; + } + + if (urlString != (id)[NSNull null]) { + [activityItems addObject:[NSURL URLWithString:urlString]]; + } + + UIActivity *activity = [[UIActivity alloc] init]; + NSArray *applicationActivities = [[NSArray alloc] initWithObjects:activity, nil]; + UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities]; + if (subject != (id)[NSNull null]) { + [activityVC setValue:subject forKey:@"subject"]; + } + + // TODO deprecated in iOS 8.0, change this some day + [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) { + [self cleanupStoredFiles]; + NSLog(@"SocialSharing app selected: %@", activityType); + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:completed]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + NSArray * socialSharingExcludeActivities = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SocialSharingExcludeActivities"]; + if (socialSharingExcludeActivities!=nil && [socialSharingExcludeActivities count] > 0) { + activityVC.excludedActivityTypes = socialSharingExcludeActivities; + } + + dispatch_async(dispatch_get_main_queue(), ^(void){ + // iPad on iOS >= 8 needs a different approach + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { + NSString* iPadCoords = [self getIPadPopupCoordinates]; + if (iPadCoords != nil && ![iPadCoords isEqual:@"-1,-1,-1,-1"]) { + NSArray *comps = [iPadCoords componentsSeparatedByString:@","]; + CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps]; + if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported + activityVC.popoverPresentationController.sourceView = self.webView; + activityVC.popoverPresentationController.sourceRect = rect; +#endif + } else { + _popover = [[UIPopoverController alloc] initWithContentViewController:activityVC]; + _popover.delegate = self; + [_popover presentPopoverFromRect:rect inView:self.webView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; + } + } else if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported + activityVC.popoverPresentationController.sourceView = self.webView; + // position the popup at the bottom, just like iOS < 8 did (and iPhone still does on iOS 8) + NSArray *comps = [NSArray arrayWithObjects: + [NSNumber numberWithInt:(self.viewController.view.frame.size.width/2)-200], + [NSNumber numberWithInt:self.viewController.view.frame.size.height], + [NSNumber numberWithInt:400], + [NSNumber numberWithInt:400], + nil]; + CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps]; + activityVC.popoverPresentationController.sourceRect = rect; +#endif + } + } + [[self getTopMostViewController] presentViewController:activityVC animated:YES completion:nil]; + }); + }]; +} + +- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:SLServiceTypeTwitter]; +} + +- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:SLServiceTypeFacebook]; +} + +- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command { + // If Fb app is installed a message is not prefilled. + // When shared through the default iOS widget (iOS Settings > Facebook) the message is prefilled already. + NSString *message = [command.arguments objectAtIndex:0]; + if (message != (id)[NSNull null]) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1000 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + BOOL fbAppInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]]; // requires whitelisting on iOS9 + if (fbAppInstalled) { + UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; + [pasteboard setValue:message forPasteboardType:@"public.text"]; + NSString *hint = [command.arguments objectAtIndex:4]; + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:hint delegate:nil cancelButtonTitle:nil otherButtonTitles:nil]; + [alert show]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2800 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [alert dismissWithClickedButtonIndex:-1 animated:YES]; + }); + } + }); + } + [self shareViaInternal:command type:SLServiceTypeFacebook]; +} + +- (void)shareVia:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:[command.arguments objectAtIndex:4]]; +} + +- (void)canShareVia:(CDVInvokedUrlCommand*)command { + NSString *via = [command.arguments objectAtIndex:4]; + CDVPluginResult * pluginResult; + if ([@"sms" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaSMS]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"email" caseInsensitiveCompare:via] == NSOrderedSame && [self isEmailAvailable]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"whatsapp" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaWhatsApp]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"instagram" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaInstagram]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([self isAvailableForSharing:command type:via]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command { + if ([self isEmailAvailable]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (bool)isEmailAvailable { + Class messageClass = (NSClassFromString(@"MFMailComposeViewController")); + return messageClass != nil && [messageClass canSendMail]; +} + +- (bool)isAvailableForSharing:(CDVInvokedUrlCommand*)command + type:(NSString *) type { + // isAvailableForServiceType returns true if you pass it a type that is not + // in the defined constants, this is probably a bug on apples part + if(!([type isEqualToString:SLServiceTypeFacebook] + || [type isEqualToString:SLServiceTypeTwitter] + || [type isEqualToString:SLServiceTypeTencentWeibo] + || [type isEqualToString:SLServiceTypeSinaWeibo])) { + return false; + } + // wrapped in try-catch, because isAvailableForServiceType may crash if an invalid type is passed + @try { + return [SLComposeViewController isAvailableForServiceType:type]; + } + @catch (NSException* exception) { + return false; + } +} + +- (void)shareViaInternal:(CDVInvokedUrlCommand*)command + type:(NSString *) type { + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + + // boldly invoke the target app, because the phone will display a nice message asking to configure the app + SLComposeViewController *composeViewController = [SLComposeViewController composeViewControllerForServiceType:type]; + if (message != (id)[NSNull null]) { + [composeViewController setInitialText:message]; + } + + for (NSString* filename in filenames) { + UIImage* image = [self getImage:filename]; + if (image != nil) { + [composeViewController addImage:image]; + } + } + + if (urlString != (id)[NSNull null]) { + [composeViewController addURL:[NSURL URLWithString:urlString]]; + } + + [composeViewController setCompletionHandler:^(SLComposeViewControllerResult result) { + if (SLComposeViewControllerResultCancelled == result) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"cancelled"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else if ([self isAvailableForSharing:command type:type]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:SLComposeViewControllerResultDone == result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + // required for iOS6 (issues #162 and #167) + [self.viewController dismissViewControllerAnimated:YES completion:nil]; + }]; + [[self getTopMostViewController] presentViewController:composeViewController animated:YES completion:nil]; +} + +- (void)shareViaEmail:(CDVInvokedUrlCommand*)command { + if ([self isEmailAvailable]) { + + if (TARGET_IPHONE_SIMULATOR && IsAtLeastiOSVersion(@"8.0")) { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"SocialSharing plugin" + message:@"Sharing via email is not supported on the iOS 8 simulator." + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alert show]; + return; + } + + self.globalMailComposer.mailComposeDelegate = self; + + if ([command.arguments objectAtIndex:0] != (id)[NSNull null]) { + NSString *message = [command.arguments objectAtIndex:0]; + BOOL isHTML = [message rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch].location != NSNotFound; + [self.globalMailComposer setMessageBody:message isHTML:isHTML]; + } + + if ([command.arguments objectAtIndex:1] != (id)[NSNull null]) { + [self.globalMailComposer setSubject: [command.arguments objectAtIndex:1]]; + } + + if ([command.arguments objectAtIndex:2] != (id)[NSNull null]) { + [self.globalMailComposer setToRecipients:[command.arguments objectAtIndex:2]]; + } + + if ([command.arguments objectAtIndex:3] != (id)[NSNull null]) { + [self.globalMailComposer setCcRecipients:[command.arguments objectAtIndex:3]]; + } + + if ([command.arguments objectAtIndex:4] != (id)[NSNull null]) { + [self.globalMailComposer setBccRecipients:[command.arguments objectAtIndex:4]]; + } + + if ([command.arguments objectAtIndex:5] != (id)[NSNull null]) { + NSArray* attachments = [command.arguments objectAtIndex:5]; + NSFileManager* fileManager = [NSFileManager defaultManager]; + for (NSString* path in attachments) { + NSURL *file = [self getFile:path]; + NSData* data = [fileManager contentsAtPath:file.path]; + + NSString* fileName; + NSString* mimeType; + NSString* basename = [self getBasenameFromAttachmentPath:path]; + + if ([basename hasPrefix:@"data:"]) { + mimeType = (NSString*)[[[basename substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0]; + fileName = @"attachment."; + fileName = [fileName stringByAppendingString:(NSString*)[[mimeType componentsSeparatedByString: @"/"] lastObject]]; + NSString *base64content = (NSString*)[[basename componentsSeparatedByString: @","] lastObject]; + data = [SocialSharing dataFromBase64String:base64content]; + } else { + fileName = [basename pathComponents].lastObject; + mimeType = [self getMimeTypeFromFileExtension:[basename pathExtension]]; + } + [self.globalMailComposer addAttachmentData:data mimeType:mimeType fileName:fileName]; + } + } + + // remember the command, because we need it in the didFinishWithResult method + _command = command; + + [self.commandDelegate runInBackground:^{ + [[self getTopMostViewController] presentViewController:self.globalMailComposer animated:YES completion:nil]; + }]; + + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (UIViewController*) getTopMostViewController { + UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController; + while (presentingViewController.presentedViewController != nil) { + presentingViewController = presentingViewController.presentedViewController; + } + return presentingViewController; +} + +- (NSString*) getBasenameFromAttachmentPath:(NSString*)path { + if ([path hasPrefix:@"base64:"]) { + NSString* pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:" withString:@""]; + return [pathWithoutPrefix substringToIndex:[pathWithoutPrefix rangeOfString:@"//"].location]; + } + return path; +} + +- (NSString*) getMimeTypeFromFileExtension:(NSString*)extension { + if (!extension) { + return nil; + } + // Get the UTI from the file's extension + CFStringRef ext = (CFStringRef)CFBridgingRetain(extension); + CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL); + // Converting UTI to a mime type + NSString *result = (NSString*)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType)); + CFRelease(ext); + CFRelease(type); + return result; +} + +/** + * Delegate will be called after the mail composer did finish an action + * to dismiss the view. + */ +- (void) mailComposeController:(MFMailComposeViewController*)controller + didFinishWithResult:(MFMailComposeResult)result + error:(NSError*)error { + bool ok = result == MFMailComposeResultSent; + [self.globalMailComposer dismissViewControllerAnimated:YES completion:^{[self cycleTheGlobalMailComposer];}]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; +} + +-(void)cycleTheGlobalMailComposer { + // we are cycling the damned GlobalMailComposer: http://stackoverflow.com/questions/25604552/i-have-real-misunderstanding-with-mfmailcomposeviewcontroller-in-swift-ios8-in/25604976#25604976 + self.globalMailComposer = nil; + self.globalMailComposer = [[MFMailComposeViewController alloc] init]; +} + +- (bool)canShareViaSMS { + Class messageClass = (NSClassFromString(@"MFMessageComposeViewController")); + return messageClass != nil && [messageClass canSendText]; +} + +- (void)shareViaSMS:(CDVInvokedUrlCommand*)command { + if ([self canShareViaSMS]) { + NSDictionary* options = [command.arguments objectAtIndex:0]; + NSString *phonenumbers = [command.arguments objectAtIndex:1]; + NSString *message = [options objectForKey:@"message"]; + NSString *subject = [options objectForKey:@"subject"]; + NSString *image = [options objectForKey:@"image"]; + + MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init]; + picker.messageComposeDelegate = (id) self; + if (message != (id)[NSNull null]) { + picker.body = message; + } + if (subject != (id)[NSNull null]) { + [picker setSubject:subject]; + } + if (image != nil && image != (id)[NSNull null]) { + BOOL canSendAttachments = [[MFMessageComposeViewController class] respondsToSelector:@selector(canSendAttachments)]; + if (canSendAttachments) { + NSURL *file = [self getFile:image]; + if (file != nil) { + [picker addAttachmentURL:file withAlternateFilename:nil]; + } + } + } + + if (phonenumbers != (id)[NSNull null]) { + [picker setRecipients:[phonenumbers componentsSeparatedByString:@","]]; + } + // remember the command, because we need it in the didFinishWithResult method + _command = command; + [self.commandDelegate runInBackground:^{ + picker.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + [[self getTopMostViewController] presentViewController:picker animated:YES completion:nil]; + }]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +// Dismisses the SMS composition interface when users taps Cancel or Send +- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result { + bool ok = result == MessageComposeResultSent; + [[self getTopMostViewController] dismissViewControllerAnimated:YES completion:nil]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; +} + +- (bool)canShareViaInstagram { + return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"instagram://app"]]; // requires whitelisting on iOS9 +} + +- (bool)canShareViaWhatsApp { + return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"whatsapp://app"]]; // requires whitelisting on iOS9 +} + +// this is only an internal test method for now, can be used to open a share sheet with 'Open in xx' links for tumblr, drive, dropbox, .. +- (void)openImage:(NSString *)imageName { + UIImage* image =[self getImage:imageName]; + if (image != nil) { + NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myTempImage.jpg"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES]; + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]]; + _documentInteractionController.UTI = @""; // TODO find the scheme for google drive and create a shareViaGoogleDrive function + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES]; + } +} + +- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command { + + // on iOS9 canShareVia('instagram'..) will only work if instagram:// is whitelisted. + // If it's not, this method will ask permission to the user on iOS9 for opening the app, + // which is of course better than Instagram sharing not working at all because you forgot to whitelist it. + // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('instagram'..) + if (!IsAtLeastiOSVersion(@"9.0")) { + if (![self canShareViaInstagram]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + } + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + + // only use the first image (for now.. maybe we can share in a loop?) + UIImage* image = nil; + for (NSString* filename in filenames) { + image = [self getImage:filename]; + break; + } + +// NSData *imageObj = [NSData dataFromBase64String:objectAtIndex0]; + NSString *tmpDir = NSTemporaryDirectory(); + NSString *path = [tmpDir stringByAppendingPathComponent:@"instagram.igo"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES]; + + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]]; + _documentInteractionController.delegate = self; + _documentInteractionController.UTI = @"com.instagram.exclusivegram"; + + if (message != (id)[NSNull null]) { + // no longer working, so .. + _documentInteractionController.annotation = @{@"InstagramCaption" : message}; + + // .. we put the message on the clipboard (you app can prompt the user to paste it) + UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; + [pasteboard setValue:message forPasteboardType:@"public.text"]; + } + + // remember the command for the delegate method + _command = command; + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.webView animated:YES]; +} + +- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command { + + // on iOS9 canShareVia('whatsapp'..) will only work if whatsapp:// is whitelisted. + // If it's not, this method will ask permission to the user on iOS9 for opening the app, + // which is of course better than WhatsApp sharing not working at all because you forgot to whitelist it. + // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('whatsapp'..) + if (!IsAtLeastiOSVersion(@"9.0")) { + if (![self canShareViaWhatsApp]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + } + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + + // only use the first image (for now.. maybe we can share in a loop?) + UIImage* image = nil; + for (NSString* filename in filenames) { + image = [self getImage:filename]; + break; + } + + // with WhatsApp, we can share an image OR text+url.. image wins if set + if (image != nil) { + NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/whatsAppTmp.wai"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES]; + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]]; + _documentInteractionController.UTI = @"net.whatsapp.image"; + _documentInteractionController.delegate = self; + _command = command; + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES]; + } else { + // append an url to a message, if both are passed + NSString * shareString = @""; + if (message != (id)[NSNull null]) { + shareString = message; + } + if (urlString != (id)[NSNull null]) { + if ([shareString isEqual: @""]) { + shareString = urlString; + } else { + shareString = [NSString stringWithFormat:@"%@ %@", shareString, urlString]; + } + } + NSString * encodedShareString = [shareString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + // also encode the '=' character + encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"]; + encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"&" withString:@"%26"]; + NSString * encodedShareStringForWhatsApp = [NSString stringWithFormat:@"whatsapp://send?text=%@", encodedShareString]; + + NSURL *whatsappURL = [NSURL URLWithString:encodedShareStringForWhatsApp]; + [[UIApplication sharedApplication] openURL: whatsappURL]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command { + self.command = command; + NSArray *filenames = [command.arguments objectAtIndex:0]; + [self.commandDelegate runInBackground:^{ + bool shared = false; + for (NSString* filename in filenames) { + UIImage* image = [self getImage:filename]; + if (image != nil) { + shared = true; + UIImageWriteToSavedPhotosAlbum(image, self, @selector(thisImage:wasSavedToPhotoAlbumWithError:contextInfo:), nil); + } + } + if (!shared) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no valid image was passed"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } + }]; +} + +// called from saveToPhotoAlbum, note that we only send feedback for the first image that's being saved (not keeping the callback) +// but since the UIImageWriteToSavedPhotosAlbum function is only called with valid images that should not be a problem +- (void)thisImage:(UIImage *)image wasSavedToPhotoAlbumWithError:(NSError *)error contextInfo:(void*)ctxInfo { + if (error) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } +} + +-(UIImage*)getImage: (NSString *)imageName { + UIImage *image = nil; + if (imageName != (id)[NSNull null]) { + if ([imageName hasPrefix:@"http"]) { + image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]]]; + } else if ([imageName hasPrefix:@"www/"]) { + image = [UIImage imageNamed:imageName]; + } else if ([imageName hasPrefix:@"file://"]) { + image = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSURL URLWithString:imageName] path]]]; + } else if ([imageName hasPrefix:@"data:"]) { + // using a base64 encoded string + NSURL *imageURL = [NSURL URLWithString:imageName]; + NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; + image = [UIImage imageWithData:imageData]; + } else if ([imageName hasPrefix:@"assets-library://"]) { + // use assets-library + NSURL *imageURL = [NSURL URLWithString:imageName]; + NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; + image = [UIImage imageWithData:imageData]; + } else { + // assume anywhere else, on the local filesystem + image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imageName]]; + } + } + return image; +} + +-(NSURL*)getFile: (NSString *)fileName { + NSURL *file = nil; + if (fileName != (id)[NSNull null]) { + if ([fileName hasPrefix:@"http"]) { + NSURL *url = [NSURL URLWithString:fileName]; + NSData *fileData = [NSData dataWithContentsOfURL:url]; + file = [NSURL fileURLWithPath:[self storeInFile:(NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject] fileData:fileData]]; + } else if ([fileName hasPrefix:@"www/"]) { + NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; + NSString *fullPath = [NSString stringWithFormat:@"%@/%@", bundlePath, fileName]; + file = [NSURL fileURLWithPath:fullPath]; + } else if ([fileName hasPrefix:@"file://"]) { + // stripping the first 6 chars, because the path should start with / instead of file:// + file = [NSURL fileURLWithPath:[fileName substringFromIndex:6]]; + } else if ([fileName hasPrefix:@"data:"]) { + // using a base64 encoded string + // extract some info from the 'fileName', which is for example: data:text/calendar;base64, + NSString *fileType = (NSString*)[[[fileName substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0]; + fileType = (NSString*)[[fileType componentsSeparatedByString: @"/"] lastObject]; + NSString *base64content = (NSString*)[[fileName componentsSeparatedByString: @","] lastObject]; + NSData *fileData = [SocialSharing dataFromBase64String:base64content]; + file = [NSURL fileURLWithPath:[self storeInFile:[NSString stringWithFormat:@"%@.%@", @"file", fileType] fileData:fileData]]; + } else { + // assume anywhere else, on the local filesystem + file = [NSURL fileURLWithPath:fileName]; + } + } + return file; +} + +-(NSString*) storeInFile: (NSString*) fileName + fileData: (NSData*) fileData { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName]; + [fileData writeToFile:filePath atomically:YES]; + _tempStoredFile = filePath; + return filePath; +} + +- (void) cleanupStoredFiles { + if (_tempStoredFile != nil) { + NSError *error; + [[NSFileManager defaultManager]removeItemAtPath:_tempStoredFile error:&error]; + } +} + ++ (NSData*) dataFromBase64String:(NSString*)aString { + return [[NSData alloc] initWithBase64EncodedString:aString options:0]; +} + +#pragma mark - UIPopoverControllerDelegate methods + +- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView **)view { + NSArray *comps = [[self getIPadPopupCoordinates] componentsSeparatedByString:@","]; + CGRect newRect = [self getPopupRectFromIPadPopupCoordinates:comps]; + rect->origin = newRect.origin; +} + +- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { + _popover = nil; +} + +#pragma mark - UIDocumentInteractionControllerDelegate methods + +- (void) documentInteractionController: (UIDocumentInteractionController *) controller willBeginSendingToApplication: (NSString *) application { + // note that the application actually contains the app bundle id which was picked (for whatsapp and instagram only) + NSLog(@"SocialSharing app selected: %@", application); +} + +- (void) documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *) controller { + if (self.command != nil) { + CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:result callbackId: self.command.callbackId]; + } +} + +@end diff --git a/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist b/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist index cc7d9214..1e1baf13 100644 --- a/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist +++ b/StoneIsland/platforms/ios/StoneIsland/StoneIsland-Info.plist @@ -190,6 +190,12 @@ stoneisland + + CFBundleURLSchemes + + stoneisland + + \ No newline at end of file diff --git a/StoneIsland/platforms/ios/StoneIsland/config.xml b/StoneIsland/platforms/ios/StoneIsland/config.xml index 3eaf2fb1..2fccdd54 100755 --- a/StoneIsland/platforms/ios/StoneIsland/config.xml +++ b/StoneIsland/platforms/ios/StoneIsland/config.xml @@ -36,6 +36,13 @@ + + + + + + + @@ -63,7 +70,4 @@ - - - diff --git a/StoneIsland/platforms/ios/www/cordova_plugins.js b/StoneIsland/platforms/ios/www/cordova_plugins.js index 99a9e96f..f3038524 100644 --- a/StoneIsland/platforms/ios/www/cordova_plugins.js +++ b/StoneIsland/platforms/ios/www/cordova_plugins.js @@ -98,6 +98,13 @@ module.exports = [ "clobbers": [ "window.plugins.launchmyapp" ] + }, + { + "file": "plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js", + "id": "cordova-plugin-x-socialsharing.SocialSharing", + "clobbers": [ + "window.plugins.socialsharing" + ] } ]; module.exports.metadata = @@ -112,7 +119,8 @@ module.exports.metadata = "cordova-plugin-network-information": "1.0.1", "cordova-plugin-splashscreen": "2.1.0", "phonegap-plugin-push": "1.4.4", - "cordova-plugin-customurlscheme": "4.0.0" + "cordova-plugin-customurlscheme": "4.0.0", + "cordova-plugin-x-socialsharing": "5.0.7" } // BOTTOM OF METADATA }); \ No newline at end of file diff --git a/StoneIsland/platforms/ios/www/css/account.css b/StoneIsland/platforms/ios/www/css/account.css index 31433871..3c04e1b1 100644 --- a/StoneIsland/platforms/ios/www/css/account.css +++ b/StoneIsland/platforms/ios/www/css/account.css @@ -38,15 +38,17 @@ #orders { display: none; } - -#orders.list { +#orders #order_list { + display: block; } -#orders.single { +#orders #single_order { + display: none; } - -#order_list { +#orders.single #order_list { + display: none; } -#single_order { +#orders.single #single_order { + display: block; } .settings #settings { display: block } @@ -403,4 +405,16 @@ input.switch:checked + label:after { color: #a9a9a9; padding-top: 9px; font-size: 14px; +} + +.container-row input:first-child { +margin-top:10px +} + +.container-row .half-input input { +margin-top:0px +} + +#login .container-row input:first-child { +margin-bottom:9px!important } \ No newline at end of file diff --git a/StoneIsland/platforms/ios/www/css/blogs.css b/StoneIsland/platforms/ios/www/css/blogs.css index 0dd14166..0b9f8eed 100644 --- a/StoneIsland/platforms/ios/www/css/blogs.css +++ b/StoneIsland/platforms/ios/www/css/blogs.css @@ -53,18 +53,23 @@ padding: 10px; } -#content .content .body { +#hub .content .body { letter-spacing:0.35px; font-size:12px; width:calc(100vw - 40px); box-sizing:border-box; margin:10px auto 20px; + clear:both } -#content .content .body:last-child { +#hub .content .body:last-child { margin:10px auto 100px; } +.hub_item { + position: relative; +} + .content-header { width:calc(100vw - 40px); box-sizing:border-box; @@ -221,6 +226,10 @@ ul.links { transform: translateZ(0) translateX(-50%) translateY(-50%); } +#archive .menu .items { + border-top: 1px solid transparent; +} + #archive.menu .menu { opacity: 1; pointer-events: auto; @@ -237,6 +246,7 @@ ul.links { } #archive .menu .items { width: 100%; + } #archive .menu .item:first-of-type { border-top: 1px solid black; @@ -246,6 +256,9 @@ ul.links { text-align: center; border-bottom: 1px solid black; background: white; + padding:12px 0; + font-size:14px; + letter-spacing:0.7px } #archive .scroll { @@ -266,3 +279,32 @@ ul.links { #archive .row .text { width: 80%; } + +.gallery-video-post { +position:relative; +} + +.gallery-video-post .play { +width:60px; +height:60px; +border-radius:100px; +background:white; +box-shadow:0px 0px 2px #000; +position:absolute; +transform:translateY(-50%) translateX(-50%); +top:50%; +left:50%; +} + +.gallery-video-post .play:before { +content:''; + width: 0; + height: 0; + border-top: 8px solid transparent; + border-bottom: 8px solid transparent; + border-left: 8px solid black; + position:absolute; + top:50%; + left:50%; + transform:translateY(-50%) translateX(-50%); +} \ No newline at end of file diff --git a/StoneIsland/platforms/ios/www/css/cart.css b/StoneIsland/platforms/ios/www/css/cart.css index a927f511..52b2a4cb 100644 --- a/StoneIsland/platforms/ios/www/css/cart.css +++ b/StoneIsland/platforms/ios/www/css/cart.css @@ -128,6 +128,42 @@ padding-bottom:5px; padding-left:5px; } +.cart_item_price .remove { +display:block; +width:20px; +height:20px; +border:1px solid #d2d2d2; +float:right; +margin-bottom:10px; +position:relative; +} + +.cart_item_price .remove:after { +content:''; +width:1px; +height:22px; +background:#bbb; +position:absolute; +top:50%; +left:50%; +z-index:3; +transform-origin:top left; +transform:rotate(45deg) translateX(-50%) translateY(-50%) +} + +.cart_item_price .remove:before { +content:''; +width:1px; +height:22px; +background:#bbb; +position:absolute; +top:50%; +left:50%; +z-index:3; +transform-origin:top left; +transform:rotate(-45deg) translateX(-50%) translateY(-50%) +} + .cart_item_price .price { font-size:11px; font-weight:bold; @@ -251,3 +287,7 @@ color:#000; .address .save_as_default { display: none; } + +#cart_shipping h3 { +margin-left:5px; +} diff --git a/StoneIsland/platforms/ios/www/css/nav.css b/StoneIsland/platforms/ios/www/css/nav.css index 3d2278d4..61834f8b 100644 --- a/StoneIsland/platforms/ios/www/css/nav.css +++ b/StoneIsland/platforms/ios/www/css/nav.css @@ -58,13 +58,15 @@ padding-bottom:45px!important; } -#nav .main_menu { display: block; } +#nav .main_menu { display: block; animation: mfadein 0.3s; } #nav .account_menu { display: none; } #nav .faq_menu { display: none; } #nav.faq .main_menu, #nav.account .main_menu { display: none; } #nav.account .account_menu { display: block; } -#nav.faq .faq_menu { display: block; } +#nav.faq .faq_menu { display: block; animation: mfadein 0.3s; } + +@keyframes mfadein { 0% { display: none; opacity: 0; } 1% { display:block; opacity: 0; } 100% { opacity: 1; } } #nav .submenu { position: absolute; diff --git a/StoneIsland/platforms/ios/www/css/products.css b/StoneIsland/platforms/ios/www/css/products.css index 2e9ead53..78d02811 100644 --- a/StoneIsland/platforms/ios/www/css/products.css +++ b/StoneIsland/platforms/ios/www/css/products.css @@ -12,6 +12,9 @@ width: 49vw; height: 63vw; } +#collection.gray { + background: rgba(245,245,245,1.0); +} .product #product { display: block } #product { @@ -170,7 +173,12 @@ letter-spacing:0.5px; text-decoration:underline; text-transform:uppercase; - padding:12px 0 2px; + padding:12px 0 0; + +} + +.product .content .fit { + } #product #gallery::before { @@ -215,3 +223,15 @@ display:block; } +#product .content .body { + letter-spacing:0.35px; + font-size:12px; + box-sizing:border-box; + margin:0px auto 20px; + clear:both; + padding:13px 0; +} + +#collection h1 { +background:white +} diff --git a/StoneIsland/platforms/ios/www/img/compass-logo.png b/StoneIsland/platforms/ios/www/img/compass-logo.png old mode 100644 new mode 100755 index d280a7fa..bdc8d946 Binary files a/StoneIsland/platforms/ios/www/img/compass-logo.png and b/StoneIsland/platforms/ios/www/img/compass-logo.png differ diff --git a/StoneIsland/platforms/ios/www/img/compass-logo.png.old b/StoneIsland/platforms/ios/www/img/compass-logo.png.old new file mode 100644 index 00000000..d280a7fa Binary files /dev/null and b/StoneIsland/platforms/ios/www/img/compass-logo.png.old differ diff --git a/StoneIsland/platforms/ios/www/img/spinner.gif b/StoneIsland/platforms/ios/www/img/spinner.gif new file mode 100644 index 00000000..d9e986d3 Binary files /dev/null and b/StoneIsland/platforms/ios/www/img/spinner.gif differ diff --git a/StoneIsland/platforms/ios/www/index.html b/StoneIsland/platforms/ios/www/index.html index 1f5a8f55..03d4a958 100644 --- a/StoneIsland/platforms/ios/www/index.html +++ b/StoneIsland/platforms/ios/www/index.html @@ -138,8 +138,10 @@

HUB

+ +
@@ -301,7 +305,7 @@
- +
@@ -342,14 +346,6 @@ -
- GENDER - -

PASSWORD

@@ -403,15 +399,6 @@ -
- GENDER - -
-

CHANGE PASSWORD

@@ -514,31 +501,75 @@
-

ORDERS

-
-
-
-
-
-
-
+ +
+
+

ORDER SUMMARY

+ +
+ +
+
+
+ SUB TOTAL + +
+
+ ESTIMATED SHIPPING
& HANDLING
+ +
+
+ TAX + +
+
+ TOTAL + +
+
- + +
+

SHIP TO

+ +
+
+
+
@@ -582,7 +613,7 @@
- x Remove from cart + {{price}}
@@ -753,6 +784,10 @@
+
+

PLEASE ENTER YOUR SECURITY CODE TO CONFIRM

+ +
@@ -764,6 +799,7 @@
+



@@ -958,13 +994,27 @@ + + + + + + + + + + + + + +
- UNITED STATES + UNITED STATES + + + +``` +```xml + + + + +``` +```xml + + + + +``` + +For sharing remote images (or other files) on Android, the file needs to be stored locally first, so add this permission to `AndroidManifest.xml`: +```xml + +``` + +For iOS, you'll need to add the `Social.framework` and `MessageUI.framework` to your project. Click your project, Build Phases, Link Binary With Libraries, search for and add `Social.framework` and `MessageUI.framework`. + +2\. Grab a copy of SocialSharing.js, add it to your project and reference it in `index.html`: +```html + +``` + +3\. Download the source files for iOS and/or Android and copy them to your project. + +iOS: Copy `SocialSharing.h` and `SocialSharing.m` to `platforms/ios//Plugins` + +Android: Copy `SocialSharing.java` to `platforms/android/src/nl/xservices/plugins` (create the folders) + +Window Phone: Copy `SocialSharing.cs` to `platforms/wp8/Plugins/nl.x-services.plugins.socialsharing` (create the folders) + +### PhoneGap Build +Just add the following xml to your `config.xml` to always use the latest version of this plugin (which is published to plugins.cordova.io these days): +```xml + +``` +or to use an older version, hosted at phonegap build: +```xml + +``` + +SocialSharing.js is brought in automatically. Make sure though you include a reference to cordova.js in your index.html's head: +```html + +``` + +## 4a. Usage on iOS and Android +You can share text, a subject (in case the user selects the email application), (any type and location of) file (like an image), and a link. +However, what exactly gets shared, depends on the application the user chooses to complete the action. A few examples: +- Mail: message, subject, file. +- Twitter: message, image (other filetypes are not supported), link (which is automatically shortened if the Twitter client deems it necessary). +- Google+ / Hangouts (Android only): message, subject, link +- Flickr: message, image (an image is required for this option to show up). +- Facebook Android: sharing a message is not possible. You can share either a link or an image (not both), but a description can not be prefilled. See [this Facebook issue which they won't solve](https://developers.facebook.com/x/bugs/332619626816423/). As an alternative you can use `shareViaFacebookWithPasteMessageHint` since plugin version 4.3.4. See below for details. Also note that sharing a URL on a non standard domain (like .fail) [may not work on Android](https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin/issues/253). Make sure you test this. You can use a [link shortener](https://goo.gl) to workaround this issue. +- Facebook iOS: message, image (other filetypes are not supported), link. Beware that since a Fb update in April 2015 sharing a prefilled message is no longer possible when the Fb app is installed (like Android), see #344. Alternative: use `shareViaFacebookWithPasteMessageHint`. + +### Using the share sheet +Here are some examples you can copy-paste to test the various combinations: +```html + + + + + +// Beware: passing a base64 file as 'data:' is not supported on Android 2.x: https://code.google.com/p/android/issues/detail?id=7901#c43 +// Hint: when sharing a base64 encoded file on Android you can set the filename by passing it as the subject (second param) + +// Hint: you can share multiple files by using an array as thirds param: ['file 1','file 2', ..], but beware of this Android Kitkat Facebook issue: [#164] + + + +``` + +Example: share a PDF file from the local www folder: +```html + +``` + +### Sharing directly to.. + +####Twitter +```html + + + +``` + +####Facebook +```html + +``` + +Facebook with prefilled message - as a workaround for [this Facebook (Android) bug](https://developers.facebook.com/x/bugs/332619626816423/). BEWARE: it's against Facebooks policy to prefil the message, or even hint the user to prefill a message for them. [See this page for details.](https://developers.facebook.com/docs/apps/review/prefill) + +* On Android the user will see a Toast message with a message you control (default: "If you like you can paste a message from your clipboard"). +* On iOS this function used to behave the same as `shareViaFacebook`, but since 4.3.18 a short message is shown prompting the user to paste a message (like Android). This message is not shown in case the Fb app is not installed since the internal iOS Fb share widget still supports prefilling the message. +* iOS9 quirk: if you want to use this method, you need to whitelist `fb://` in your .plist file. +```html + +``` + +Whitelisting Facebook in your app's .plist: +```xml +LSApplicationQueriesSchemes + + fb + +``` + +####Instagram +```html + +``` + +Quirks: +* Instagram no longer permits prefilling a message - you can prompt the user to paste the message you've passed to the plugin because we're adding it to the clipboard for you. + +iOS Quirks: +* Before trying to invoke `shareViaInstagram` please use `canShareVia('instagram'..` AND whitelist the urlscheme (see below). +* Although this plugin follows the Instagram sharing guidelines, the user may not only see Instagram in the share sheet, but also other apps that listen to the "Instagram sharing ID". Just google "com.instagram.exclusivegram" and you see what I mean. + + +#### WhatsApp +* Note that on iOS when sharing an image and text, only the image is shared - let's hope WhatsApp creates a proper iOS extension to fix this. +* Before using this method you may want to use `canShareVia('whatsapp'..` (see below). +```html + +``` + +####SMS +Note that on Android, SMS via Hangouts may not behave correctly +```html + + + + + + +``` + +####Email +Code inspired by the [EmailComposer plugin](https://github.com/katzer/cordova-plugin-email-composer), note that this is not supported on the iOS 8 simulator (an alert will be shown if your try to). +```js +window.plugins.socialsharing.shareViaEmail( + 'Message', // can contain HTML tags, but support on Android is rather limited: http://stackoverflow.com/questions/15136480/how-to-send-html-content-with-image-through-android-default-email-client + 'Subject', + ['to@person1.com', 'to@person2.com'], // TO: must be null or an array + ['cc@person1.com'], // CC: must be null or an array + null, // BCC: must be null or an array + ['https://www.google.nl/images/srpr/logo4w.png','www/localimage.png'], // FILES: can be null, a string, or an array + onSuccess, // called when sharing worked, but also when the user cancelled sharing via email (I've found no way to detect the difference) + onError // called when sh*t hits the fan +); +``` + +If Facebook, Twitter, Instagram, WhatsApp, SMS or Email is not available, the errorCallback is called with the text 'not available'. + +If you feel lucky, you can even try to start any application with the `shareVia` function: +```html + + + + + + + + +``` + +What can we pass to the `shareVia` function? +* iOS: You are limited to 'com.apple.social.[facebook | twitter | sinaweibo | tencentweibo]'. If an app does not exist, the errorcallback is invoked and iOS shows a popup message asking the user to configure the app. +* Android: Anything that would otherwise appear in the sharing dialoge (in case the `share` function was used. Pass a (part of the) packagename of the app you want to share to. The `shareViaFacebook` function for instance uses `com.facebook.katana` as the packagename fragment. Things like `weibo`, `pinterest` and `com.google.android.apps.plus` (Google+) should work just fine. + +You can even test if a sharing option is available with `canShareVia`! +You'll need to pass everything you want to share, because (at least on Android) some apps may only become available when an image is added. +The function will invoke the successCallback when it can be shared to via `shareVia`, and the errorCallback if not. As a bonus on Android, the errorCallback contains a JSON Array of available packages you can pass to shareVia. +You can even specify the activity if the app offers multiple sharing ways, passing 'packageName/activityName'. (for example, WeChat, passing 'com.tencent.mm' or 'com.tencent.mm/com.tencent.mm.ui.tools.ShareImgUI' to share to chat, passing 'com.tencent.mm/com.tencent.mm.ui.tools.ShareToTimeLineUI' to share to moments). + +```html + + +// this one requires whitelisting of whatsapp:// on iOS9 in your .plist file + + + + + +``` + +Want to share images from a local folder (like an image you just selected from the CameraRoll)? +```javascript +// use a local image from inside the www folder: +window.plugins.socialsharing.share(null, null, 'www/image.gif', null); // success/error callback params may be added as 5th and 6th param +// .. or a local image from anywhere else (if permitted): +// local-iOS: +window.plugins.socialsharing.share(null, null, '/Users/username/Library/Application Support/iPhone/6.1/Applications/25A1E7CF-079F-438D-823B-55C6F8CD2DC0/Documents/.nl.x-services.appname/pics/img.jpg'); +// local-iOS-alt: +window.plugins.socialsharing.share(null, null, 'file:///Users/username/Library/Application Support/iPhone/6.1/Applications/25A1E7CF-079F-438D-823B-55C6F8CD2DC0/Documents/.nl.x-services.appname/pics/img.jpg'); +// local-Android: +window.plugins.socialsharing.share(null, null, 'file:///storage/emulated/0/nl.xservices.testapp/5359/Photos/16832/Thumb.jpg'); +// .. or an image from the internet: +window.plugins.socialsharing.share(null, null, 'http://domain.com/image.jpg'); +``` + +If your app still supports iOS5, you'll want to check whether or not the plugin is available as it only supports iOS6 and up. +```javascript +window.plugins.socialsharing.available(function(isAvailable) { + // the boolean is only false on iOS < 6 + if (isAvailable) { + // now use any of the share() functions + } +}); +``` + +If you can't get the plugin to work, have a look at [this demo project](https://github.com/EddyVerbruggen/X-Services-PhoneGap-Build-Plugins-Demo). + +#### Notes about the successCallback (you can just ignore the callbacks if you like) +Since version 3.8 the plugin passes a boolean to the successCallback to let the app know whether or not content was actually shared, or the share widget was closed by the user. +On iOS this works as expected (except for Facebook, in case the app is installed), but on Android some sharing targets may return false, even though sharing succeeded. This is not a limitation of the plugin, it's the target app which doesn't play nice. +To make it more confusing, when sharing via SMS on Android, you'll likely always have the successCallback invoked. Thanks Google. + +#### Sharing multiple images (or other files) +Since version 4.3.0 of this plugin you can pass an array of files to the share and shareVia functions. +```js +// sharing multiple images via Facebook (you can mix protocols and file locations) +window.plugins.socialsharing.shareViaFacebook( + 'Optional message, may be ignored by Facebook app', + ['https://www.google.nl/images/srpr/logo4w.png','www/image.gif'], + null); + +// sharing a PDF and an image +window.plugins.socialsharing.share( + 'Optional message', + 'Optional title', + ['www/manual.pdf','https://www.google.nl/images/srpr/logo4w.png'], + 'http://www.myurl.com'); +``` + +Note that a lot of apps support sharing multiple files, but Twitter just doesn't accept more that one file. + +#### Saving images to the photo album (iOS only currently) +Since version 4.3.16 of this plugin you can save an array of images to the camera roll: +```js +window.plugins.socialsharing.saveToPhotoAlbum( + ['https://www.google.nl/images/srpr/logo4w.png','www/image.gif'], + onSuccess, // optional success function + onError // optional error function +); +``` + +#### iOS quirk (with camera plugin) +When using this plugin in the callback of the Phonegap camera plugin, wrap the call to `share()` in a `setTimeout()`. +The share widget has the same limitation as the alert dialogue [mentioned in the Phonegap documentation](http://docs.phonegap.com/en/2.9.0/cordova_camera_camera.md.html#camera.getPicture_ios_quirks). + +#### Excluding some options from the widget +If you want to exclude (for example) the assign-to-contact and copy-to-pasteboard options, add this to your main plist file: + +```xml +SocialSharingExcludeActivities + + com.apple.UIKit.activity.AssignToContact + com.apple.UIKit.activity.CopyToPasteboard + +``` + +Here's the list of available activities you can disable : + + - com.apple.UIKit.activity.PostToFacebook + - com.apple.UIKit.activity.PostToTwitter + - com.apple.UIKit.activity.PostToFlickr + - com.apple.UIKit.activity.PostToWeibo + - com.apple.UIKit.activity.PostToVimeo + - com.apple.UIKit.activity.TencentWeibo + - com.apple.UIKit.activity.Message + - com.apple.UIKit.activity.Mail + - com.apple.UIKit.activity.Print + - com.apple.UIKit.activity.CopyToPasteboard + - com.apple.UIKit.activity.AssignToContact + - com.apple.UIKit.activity.SaveToCameraRoll + - com.apple.UIKit.activity.AddToReadingList + - com.apple.UIKit.activity.AirDrop + + +## 4b. Usage on Windows Phone +The available methods on WP8 are: `available`, `canShareViaEmail`, `share`, `shareViaEmail` and `shareViaSMS`. +Currently the first two always return true, but this may change in the future in case I can find a way to truly detect the availability. + +The `share` function on WP8 supports two flavours: message only, or a combination of message, title and link. + +Beware: for now please pass null values for all non used attributes, like in the examples below. + +Sharing a message: +```html + +``` + +Sharing a link: +```html + +``` + +Sharing an image (only images from the internet are supported). If you pass more than one image as an array, only the first one is used: +```html + +``` + +## 4c. Share-popover on iPad +Carlos Sola-Llonch, a user of this plugin, pointed me at an [iOS document](https://developer.apple.com/library/ios/documentation/uikit/reference/UIActivityViewController_Class/Reference/Reference.html) +stating "On iPad, you must present the view controller in a popover. On iPhone and iPod touch, you must present it modally." + +He also provided me with the required code to do so (thanks!). I've adapted it a little to make sure current behaviour is +not altered, but with a little extra effort you can use this new popover feature. + +The trick is overriding the function `window.plugins.socialsharing.iPadPopupCoordinates` by your own implementation +to tell the iPad where to show the popup exactly. It need to be a string like "100,200,300,300" (left,top,width,height). + +You need to override this method after `deviceready` has fired. + +You have various options, like checking the click event on a button and determine the event.clientX and event.clientY, +or use this code Carlos showed me to grab the coordinates of a static button somewhere on your page: + +```js +window.plugins.socialsharing.iPadPopupCoordinates = function() { + var rect = document.getElementById('share_button').getBoundingClientRect(); + return rect.left + "," + rect.top + "," + rect.width + "," + rect.height; +}; +``` + +Note that since iOS 8 this popup is the only way Apple allows you to share stuff, so this plugin has been adjusted to use this plugin as standard for iOS 8 and positions +the popup at the bottom of the screen (seems like a logical default because that's where it previously was as well). +You can however override this position in the same way as explained above. + +**Note**: when using the [WkWebView polyfill](https://github.com/Telerik-Verified-Plugins/WKWebView) the `iPadPopupCoordinates` overrides [doesn't work](https://github.com/Telerik-Verified-Plugins/WKWebView/issues/77) so you can call the alternative `setIPadPopupCoordinates` method to define the popup position just before you call the `share` method. + +example : + +```js +var targetRect = event.targetElement.getBoundingClientRect(), + targetBounds = targetRect.left + ',' + targetRect.top + ',' + targetRect.width + ',' + targetRect.height; + +window.plugins.socialsharing.setIPadPopupCoordinates(targetBounds); +window.plugins.socialsharing.share('Hello from iOS :)') +``` + +## 5. Credits ## + +This plugin was enhanced for Plugman / PhoneGap Build by [Eddy Verbruggen](http://www.x-services.nl). +The Android and Windows Phone code was entirely created by the author. +The first iteration of the iOS code was inspired by [Cameron Lerch](https://github.com/bfcam/phonegap-ios-social-plugin). + + +## 6. License + +[The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html) + +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/StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json b/StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json new file mode 100644 index 00000000..66bc9646 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/package.json @@ -0,0 +1,46 @@ +{ + "name": "cordova-plugin-x-socialsharing", + "version": "5.0.7", + "description": "Share text, images (and other files), or a link via the native sharing widget of your device. Android is fully supported, as well as iOS 6 and up. WP8 has somewhat limited support.", + "cordova": { + "id": "cordova-plugin-x-socialsharing", + "platforms": [ + "ios", + "android", + "wp8" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin.git" + }, + "keywords": [ + "Social", + "Share", + "Twitter", + "Facebook", + "Email", + "SMS", + "WhatsApp", + "Tumblr", + "Pocket", + "LinkedIn", + "cordova", + "ecosystem:cordova", + "cordova-ios", + "cordova-android", + "cordova-wp8" + ], + "engines": [ + { + "name": "cordova", + "version": ">=3.0.0" + } + ], + "author": "Eddy Verbruggen - @EddyVerbruggen", + "license": "MIT", + "bugs": { + "url": "https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin/issues" + }, + "homepage": "https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin#readme" +} diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml b/StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml new file mode 100755 index 00000000..ba7183cb --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/plugin.xml @@ -0,0 +1,85 @@ + + + + SocialSharing + + + Share text, images (and other files), or a link via the native sharing widget of your device. + Android is fully supported, as well as iOS 6 and up. WP8 has somewhat limited support. + + + Eddy Verbruggen + + MIT + + Social, Share, Twitter, Facebook, Email, SMS, WhatsApp, Tumblr, Pocket, LinkedIn + + https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin.git + + https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin/issues + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png new file mode 100644 index 00000000..f3ab135f Binary files /dev/null and b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-android-share.png differ diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png new file mode 100644 index 00000000..63b0b4c5 Binary files /dev/null and b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios6-share.png differ diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png new file mode 100644 index 00000000..640c94f8 Binary files /dev/null and b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-ipad-share.png differ diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png new file mode 100644 index 00000000..7545d10d Binary files /dev/null and b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-ios7-share.png differ diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg new file mode 100644 index 00000000..0ced2eb5 Binary files /dev/null and b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshot-wp8-share.jpg differ diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png new file mode 100644 index 00000000..3afe5fef Binary files /dev/null and b/StoneIsland/plugins/cordova-plugin-x-socialsharing/screenshots/screenshots-ios7-shareconfig.png differ diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java new file mode 100644 index 00000000..f1168f89 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/android/nl/xservices/plugins/SocialSharing.java @@ -0,0 +1,533 @@ +package nl.xservices.plugins; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.text.Html; +import android.util.Base64; +import android.view.Gravity; +import android.widget.Toast; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SocialSharing extends CordovaPlugin { + + private static final String ACTION_AVAILABLE_EVENT = "available"; + private static final String ACTION_SHARE_EVENT = "share"; + private static final String ACTION_CAN_SHARE_VIA = "canShareVia"; + private static final String ACTION_CAN_SHARE_VIA_EMAIL = "canShareViaEmail"; + private static final String ACTION_SHARE_VIA = "shareVia"; + private static final String ACTION_SHARE_VIA_TWITTER_EVENT = "shareViaTwitter"; + private static final String ACTION_SHARE_VIA_FACEBOOK_EVENT = "shareViaFacebook"; + private static final String ACTION_SHARE_VIA_FACEBOOK_WITH_PASTEMESSAGEHINT = "shareViaFacebookWithPasteMessageHint"; + private static final String ACTION_SHARE_VIA_WHATSAPP_EVENT = "shareViaWhatsApp"; + private static final String ACTION_SHARE_VIA_INSTAGRAM_EVENT = "shareViaInstagram"; + private static final String ACTION_SHARE_VIA_SMS_EVENT = "shareViaSMS"; + private static final String ACTION_SHARE_VIA_EMAIL_EVENT = "shareViaEmail"; + + private static final int ACTIVITY_CODE_SENDVIAEMAIL = 2; + + private CallbackContext _callbackContext; + + private String pasteMessage; + + private abstract class SocialSharingRunnable implements Runnable { + public CallbackContext callbackContext; + SocialSharingRunnable(CallbackContext cb) { + this.callbackContext = cb; + } + } + + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + this._callbackContext = callbackContext; // only used for onActivityResult + this.pasteMessage = null; + if (ACTION_AVAILABLE_EVENT.equals(action)) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); + return true; + } else if (ACTION_SHARE_EVENT.equals(action)) { + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), null, false); + } else if (ACTION_SHARE_VIA_TWITTER_EVENT.equals(action)) { + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "twitter", false); + } else if (ACTION_SHARE_VIA_FACEBOOK_EVENT.equals(action)) { + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "com.facebook.katana", false); + } else if (ACTION_SHARE_VIA_FACEBOOK_WITH_PASTEMESSAGEHINT.equals(action)) { + this.pasteMessage = args.getString(4); + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "com.facebook.katana", false); + } else if (ACTION_SHARE_VIA_WHATSAPP_EVENT.equals(action)) { + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "whatsapp", false); + } else if (ACTION_SHARE_VIA_INSTAGRAM_EVENT.equals(action)) { + if (notEmpty(args.getString(0))) { + copyHintToClipboard(args.getString(0), "Instagram paste message"); + } + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), "instagram", false); + } else if (ACTION_CAN_SHARE_VIA.equals(action)) { + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), args.getString(4), true); + } else if (ACTION_CAN_SHARE_VIA_EMAIL.equals(action)) { + if (isEmailAvailable()) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); + return true; + } else { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "not available")); + return false; + } + } else if (ACTION_SHARE_VIA.equals(action)) { + return doSendIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), args.getString(4), false); + } else if (ACTION_SHARE_VIA_SMS_EVENT.equals(action)) { + return invokeSMSIntent(callbackContext, args.getJSONObject(0), args.getString(1)); + } else if (ACTION_SHARE_VIA_EMAIL_EVENT.equals(action)) { + return invokeEmailIntent(callbackContext, args.getString(0), args.getString(1), args.getJSONArray(2), args.isNull(3) ? null : args.getJSONArray(3), args.isNull(4) ? null : args.getJSONArray(4), args.isNull(5) ? null : args.getJSONArray(5)); + } else { + callbackContext.error("socialSharing." + action + " is not a supported function. Did you mean '" + ACTION_SHARE_EVENT + "'?"); + return false; + } + } + + private boolean isEmailAvailable() { + final Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "someone@domain.com", null)); + return cordova.getActivity().getPackageManager().queryIntentActivities(intent, 0).size() > 0; + } + + private boolean invokeEmailIntent(final CallbackContext callbackContext, final String message, final String subject, final JSONArray to, final JSONArray cc, final JSONArray bcc, final JSONArray files) throws JSONException { + + final SocialSharing plugin = this; + cordova.getThreadPool().execute(new SocialSharingRunnable(callbackContext) { + public void run() { + final Intent draft = new Intent(Intent.ACTION_SEND_MULTIPLE); + if (notEmpty(message)) { + Pattern htmlPattern = Pattern.compile(".*\\<[^>]+>.*", Pattern.DOTALL); + if (htmlPattern.matcher(message).matches()) { + draft.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml(message)); + draft.setType("text/html"); + } else { + draft.putExtra(android.content.Intent.EXTRA_TEXT, message); + draft.setType("text/plain"); + } + } + if (notEmpty(subject)) { + draft.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); + } + try { + if (to != null && to.length() > 0) { + draft.putExtra(android.content.Intent.EXTRA_EMAIL, toStringArray(to)); + } + if (cc != null && cc.length() > 0) { + draft.putExtra(android.content.Intent.EXTRA_CC, toStringArray(cc)); + } + if (bcc != null && bcc.length() > 0) { + draft.putExtra(android.content.Intent.EXTRA_BCC, toStringArray(bcc)); + } + if (files.length() > 0) { + final String dir = getDownloadDir(); + if (dir != null) { + ArrayList fileUris = new ArrayList(); + for (int i = 0; i < files.length(); i++) { + final Uri fileUri = getFileUriAndSetType(draft, dir, files.getString(i), subject, i); + if (fileUri != null) { + fileUris.add(fileUri); + } + } + if (!fileUris.isEmpty()) { + draft.putExtra(Intent.EXTRA_STREAM, fileUris); + } + } + } + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + + draft.setType("application/octet-stream"); + cordova.startActivityForResult(plugin, Intent.createChooser(draft, "Choose Email App"), ACTIVITY_CODE_SENDVIAEMAIL); + } + }); + + return true; + } + + private String getDownloadDir() throws IOException { + // better check, otherwise it may crash the app + if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + // we need to use external storage since we need to share to another app + final String dir = webView.getContext().getExternalFilesDir(null) + "/socialsharing-downloads"; + createOrCleanDir(dir); + return dir; + } else { + return null; + } + } + + private boolean doSendIntent(final CallbackContext callbackContext, final String msg, final String subject, final JSONArray files, final String url, final String appPackageName, final boolean peek) { + + final CordovaInterface mycordova = cordova; + final CordovaPlugin plugin = this; + + cordova.getThreadPool().execute(new SocialSharingRunnable(callbackContext) { + public void run() { + String message = msg; + final boolean hasMultipleAttachments = files.length() > 1; + final Intent sendIntent = new Intent(hasMultipleAttachments ? Intent.ACTION_SEND_MULTIPLE : Intent.ACTION_SEND); + sendIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + + try { + if (files.length() > 0 && !"".equals(files.getString(0))) { + final String dir = getDownloadDir(); + if (dir != null) { + ArrayList fileUris = new ArrayList(); + Uri fileUri = null; + for (int i = 0; i < files.length(); i++) { + fileUri = getFileUriAndSetType(sendIntent, dir, files.getString(i), subject, i); + if (fileUri != null) { + fileUris.add(fileUri); + } + } + if (!fileUris.isEmpty()) { + if (hasMultipleAttachments) { + sendIntent.putExtra(Intent.EXTRA_STREAM, fileUris); + } else { + sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri); + } + } + } else { + sendIntent.setType("text/plain"); + } + } else { + sendIntent.setType("text/plain"); + } + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + + if (notEmpty(subject)) { + sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject); + } + // add the URL to the message, as there seems to be no separate field + if (notEmpty(url)) { + if (notEmpty(message)) { + message += " " + url; + } else { + message = url; + } + } + if (notEmpty(message)) { + sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, message); + // sometimes required when the user picks share via sms + if (Build.VERSION.SDK_INT < 21) { // LOLLIPOP + sendIntent.putExtra("sms_body", message); + } + } + + if (appPackageName != null) { + String packageName = appPackageName; + String passedActivityName = null; + if (packageName.contains("/")) { + String[] items = appPackageName.split("/"); + packageName = items[0]; + passedActivityName = items[1]; + } + final ActivityInfo activity = getActivity(callbackContext, sendIntent, packageName); + if (activity != null) { + if (peek) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); + } else { + sendIntent.addCategory(Intent.CATEGORY_LAUNCHER); + sendIntent.setComponent(new ComponentName(activity.applicationInfo.packageName, + passedActivityName != null ? passedActivityName : activity.name)); + mycordova.startActivityForResult(plugin, sendIntent, 0); + + if (pasteMessage != null) { + // add a little delay because target app (facebook only atm) needs to be started first + new Timer().schedule(new TimerTask() { + public void run() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + copyHintToClipboard(msg, pasteMessage); + showPasteMessage(pasteMessage); + } + }); + } + }, 2000); + } + } + } + } else { + if (peek) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK)); + } else { + mycordova.startActivityForResult(plugin, Intent.createChooser(sendIntent, null), 1); + } + } + } + }); + return true; + } + + @SuppressLint("NewApi") + private void copyHintToClipboard(String msg, String label) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + return; + } + final ClipboardManager clipboard = (android.content.ClipboardManager) cordova.getActivity().getSystemService(Context.CLIPBOARD_SERVICE); + final ClipData clip = android.content.ClipData.newPlainText(label, msg); + clipboard.setPrimaryClip(clip); + } + + @SuppressLint("NewApi") + private void showPasteMessage(String label) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + return; + } + final Toast toast = Toast.makeText(webView.getContext(), label, Toast.LENGTH_LONG); + toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0); + toast.show(); + } + + private Uri getFileUriAndSetType(Intent sendIntent, String dir, String image, String subject, int nthFile) throws IOException { + // we're assuming an image, but this can be any filetype you like + String localImage = image; + sendIntent.setType("image/*"); + if (image.startsWith("http") || image.startsWith("www/")) { + String filename = getFileName(image); + localImage = "file://" + dir + "/" + filename; + if (image.startsWith("http")) { + // filename optimisation taken from https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin/pull/56 + URLConnection connection = new URL(image).openConnection(); + String disposition = connection.getHeaderField("Content-Disposition"); + if (disposition != null) { + final Pattern dispositionPattern = Pattern.compile("filename=([^;]+)"); + Matcher matcher = dispositionPattern.matcher(disposition); + if (matcher.find()) { + filename = matcher.group(1).replaceAll("[^a-zA-Z0-9._-]", ""); + localImage = "file://" + dir + "/" + filename; + } + } + saveFile(getBytes(connection.getInputStream()), dir, filename); + } else { + saveFile(getBytes(webView.getContext().getAssets().open(image)), dir, filename); + } + } else if (image.startsWith("data:")) { + // safeguard for https://code.google.com/p/android/issues/detail?id=7901#c43 + if (!image.contains(";base64,")) { + sendIntent.setType("text/plain"); + return null; + } + // image looks like this: data:image/png;base64,R0lGODlhDAA... + final String encodedImg = image.substring(image.indexOf(";base64,") + 8); + // correct the intent type if anything else was passed, like a pdf: data:application/pdf;base64,.. + if (!image.contains("data:image/")) { + sendIntent.setType(image.substring(image.indexOf("data:") + 5, image.indexOf(";base64"))); + } + // the filename needs a valid extension, so it renders correctly in target apps + final String imgExtension = image.substring(image.indexOf("/") + 1, image.indexOf(";base64")); + String fileName; + // if a subject was passed, use it as the filename + // filenames must be unique when passing in multiple files [#158] + if (notEmpty(subject)) { + fileName = sanitizeFilename(subject) + (nthFile == 0 ? "" : "_" + nthFile) + "." + imgExtension; + } else { + fileName = "file" + (nthFile == 0 ? "" : "_" + nthFile) + "." + imgExtension; + } + saveFile(Base64.decode(encodedImg, Base64.DEFAULT), dir, fileName); + localImage = "file://" + dir + "/" + fileName; + } else if (image.startsWith("df:")) { + // safeguard for https://code.google.com/p/android/issues/detail?id=7901#c43 + if (!image.contains(";base64,")) { + sendIntent.setType("text/plain"); + return null; + } + // format looks like this : df:filename.txt;data:image/png;base64,R0lGODlhDAA... + final String fileName = image.substring(image.indexOf("df:") + 3, image.indexOf(";data:")); + final String fileType = image.substring(image.indexOf(";data:") + 6, image.indexOf(";base64,")); + final String encodedImg = image.substring(image.indexOf(";base64,") + 8); + sendIntent.setType(fileType); + saveFile(Base64.decode(encodedImg, Base64.DEFAULT), dir, sanitizeFilename(fileName)); + localImage = "file://" + dir + "/" + fileName; + } else if (!image.startsWith("file://")) { + throw new IllegalArgumentException("URL_NOT_SUPPORTED"); + } + return Uri.parse(localImage); + } + + private boolean invokeSMSIntent(final CallbackContext callbackContext, JSONObject options, String p_phonenumbers) { + final String message = options.optString("message"); + // TODO test this on a real SMS enabled device before releasing it +// final String subject = options.optString("subject"); +// final String image = options.optString("image"); + final String subject = null; //options.optString("subject"); + final String image = null; // options.optString("image"); + final String phonenumbers = getPhoneNumbersWithManufacturerSpecificSeparators(p_phonenumbers); + final SocialSharing plugin = this; + cordova.getThreadPool().execute(new SocialSharingRunnable(callbackContext) { + public void run() { + Intent intent; + + if (Build.VERSION.SDK_INT >= 19) { // Build.VERSION_CODES.KITKAT) { + // passing in no phonenumbers for kitkat may result in an error, + // but it may also work for some devices, so documentation will need to cover this case + intent = new Intent(Intent.ACTION_SENDTO); + intent.setData(Uri.parse("smsto:" + (notEmpty(phonenumbers) ? phonenumbers : ""))); + } else { + intent = new Intent(Intent.ACTION_VIEW); + intent.setType("vnd.android-dir/mms-sms"); + if (phonenumbers != null) { + intent.putExtra("address", phonenumbers); + } + } + intent.putExtra("sms_body", message); + intent.putExtra("sms_subject", subject); + + try { + if (image != null && !"".equals(image)) { + final Uri fileUri = getFileUriAndSetType(intent, getDownloadDir(), image, subject, 0); + if (fileUri != null) { + intent.putExtra(Intent.EXTRA_STREAM, fileUri); + } + } + cordova.startActivityForResult(plugin, intent, 0); + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + return true; + } + + private static String getPhoneNumbersWithManufacturerSpecificSeparators(String phonenumbers) { + if (notEmpty(phonenumbers)) { + char separator; + if (android.os.Build.MANUFACTURER.equalsIgnoreCase("samsung")) { + separator = ','; + } else { + separator = ';'; + } + return phonenumbers.replace(';', separator).replace(',', separator); + } + return null; + } + + private ActivityInfo getActivity(final CallbackContext callbackContext, final Intent shareIntent, final String appPackageName) { + final PackageManager pm = webView.getContext().getPackageManager(); + List activityList = pm.queryIntentActivities(shareIntent, 0); + for (final ResolveInfo app : activityList) { + if ((app.activityInfo.packageName).contains(appPackageName)) { + return app.activityInfo; + } + } + // no matching app found + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, getShareActivities(activityList))); + return null; + } + + private JSONArray getShareActivities(List activityList) { + List packages = new ArrayList(); + for (final ResolveInfo app : activityList) { + packages.add(app.activityInfo.packageName); + } + return new JSONArray(packages); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + if (_callbackContext != null) { + if (ACTIVITY_CODE_SENDVIAEMAIL == requestCode) { + _callbackContext.success(); + } else { + _callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, resultCode == Activity.RESULT_OK)); + } + } + } + + private void createOrCleanDir(final String downloadDir) throws IOException { + final File dir = new File(downloadDir); + if (!dir.exists()) { + if (!dir.mkdirs()) { + throw new IOException("CREATE_DIRS_FAILED"); + } + } else { + cleanupOldFiles(dir); + } + } + + private static String getFileName(String url) { + final String pattern = ".*/([^?#]+)?"; + Pattern r = Pattern.compile(pattern); + Matcher m = r.matcher(url); + if (m.find()) { + return m.group(1); + } else { + return null; + } + } + + private byte[] getBytes(InputStream is) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[16384]; + while ((nRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + return buffer.toByteArray(); + } + + private void saveFile(byte[] bytes, String dirName, String fileName) throws IOException { + final File dir = new File(dirName); + final FileOutputStream fos = new FileOutputStream(new File(dir, fileName)); + fos.write(bytes); + fos.flush(); + fos.close(); + } + + /** + * As file.deleteOnExit does not work on Android, we need to delete files manually. + * Deleting them in onActivityResult is not a good idea, because for example a base64 encoded file + * will not be available for upload to Facebook (it's deleted before it's uploaded). + * So the best approach is deleting old files when saving (sharing) a new one. + */ + private void cleanupOldFiles(File dir) { + for (File f : dir.listFiles()) { + //noinspection ResultOfMethodCallIgnored + f.delete(); + } + } + + private static boolean notEmpty(String what) { + return what != null && + !"".equals(what) && + !"null".equalsIgnoreCase(what); + } + + private static String[] toStringArray(JSONArray jsonArray) throws JSONException { + String[] result = new String[jsonArray.length()]; + for (int i = 0; i < jsonArray.length(); i++) { + result[i] = jsonArray.getString(i); + } + return result; + } + + public static String sanitizeFilename(String name) { + return name.replaceAll("[:\\\\/*?|<> ]", "_"); + } +} diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h new file mode 100755 index 00000000..b51474d3 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.h @@ -0,0 +1,27 @@ +#import +#import + +@interface SocialSharing : CDVPlugin + +@property (nonatomic, strong) MFMailComposeViewController *globalMailComposer; +@property (retain) UIDocumentInteractionController * documentInteractionController; +@property (retain) NSString * tempStoredFile; +@property (retain) CDVInvokedUrlCommand * command; + +- (void)available:(CDVInvokedUrlCommand*)command; +- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command; +- (void)share:(CDVInvokedUrlCommand*)command; +- (void)canShareVia:(CDVInvokedUrlCommand*)command; +- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command; +- (void)shareVia:(CDVInvokedUrlCommand*)command; +- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command; +- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command; +- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command; +- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command; +- (void)shareViaSMS:(CDVInvokedUrlCommand*)command; +- (void)shareViaEmail:(CDVInvokedUrlCommand*)command; +- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command; + +- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command; + +@end diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m new file mode 100755 index 00000000..cd0913a4 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/ios/SocialSharing.m @@ -0,0 +1,715 @@ +#import "SocialSharing.h" +#import +#import +#import +#import +#import +#import + +@implementation SocialSharing { + UIPopoverController *_popover; + NSString *_popupCoordinates; +} + +- (void)pluginInitialize { + if ([self isEmailAvailable]) { + [self cycleTheGlobalMailComposer]; + } +} + +- (void)available:(CDVInvokedUrlCommand*)command { + BOOL avail = NO; + if (NSClassFromString(@"UIActivityViewController")) { + avail = YES; + } + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:avail]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (NSString*)getIPadPopupCoordinates { + if (_popupCoordinates != nil) { + return _popupCoordinates; + } + if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { + return [(UIWebView*)self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.socialsharing.iPadPopupCoordinates();"]; + } else { + // prolly a wkwebview, ignoring for now + return nil; + } +} + +- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command { + _popupCoordinates = [command.arguments objectAtIndex:0]; +} + +- (CGRect)getPopupRectFromIPadPopupCoordinates:(NSArray*)comps { + CGRect rect = CGRectZero; + if ([comps count] == 4) { + rect = CGRectMake([[comps objectAtIndex:0] integerValue], [[comps objectAtIndex:1] integerValue], [[comps objectAtIndex:2] integerValue], [[comps objectAtIndex:3] integerValue]); + } + return rect; +} + +- (void)share:(CDVInvokedUrlCommand*)command { + [self.commandDelegate runInBackground:^{ //avoid main thread block especially if sharing big files from url + if (!NSClassFromString(@"UIActivityViewController")) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + + NSString *message = [command.arguments objectAtIndex:0]; + NSString *subject = [command.arguments objectAtIndex:1]; + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + + NSMutableArray *activityItems = [[NSMutableArray alloc] init]; + [activityItems addObject:message]; + + NSMutableArray *files = [[NSMutableArray alloc] init]; + if (filenames != (id)[NSNull null] && filenames.count > 0) { + for (NSString* filename in filenames) { + NSObject *file = [self getImage:filename]; + if (file == nil) { + file = [self getFile:filename]; + } + if (file != nil) { + [files addObject:file]; + } + } + [activityItems addObjectsFromArray:files]; + } + + if (urlString != (id)[NSNull null]) { + [activityItems addObject:[NSURL URLWithString:urlString]]; + } + + UIActivity *activity = [[UIActivity alloc] init]; + NSArray *applicationActivities = [[NSArray alloc] initWithObjects:activity, nil]; + UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities]; + if (subject != (id)[NSNull null]) { + [activityVC setValue:subject forKey:@"subject"]; + } + + // TODO deprecated in iOS 8.0, change this some day + [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) { + [self cleanupStoredFiles]; + NSLog(@"SocialSharing app selected: %@", activityType); + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:completed]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; + + NSArray * socialSharingExcludeActivities = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SocialSharingExcludeActivities"]; + if (socialSharingExcludeActivities!=nil && [socialSharingExcludeActivities count] > 0) { + activityVC.excludedActivityTypes = socialSharingExcludeActivities; + } + + dispatch_async(dispatch_get_main_queue(), ^(void){ + // iPad on iOS >= 8 needs a different approach + if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { + NSString* iPadCoords = [self getIPadPopupCoordinates]; + if (iPadCoords != nil && ![iPadCoords isEqual:@"-1,-1,-1,-1"]) { + NSArray *comps = [iPadCoords componentsSeparatedByString:@","]; + CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps]; + if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported + activityVC.popoverPresentationController.sourceView = self.webView; + activityVC.popoverPresentationController.sourceRect = rect; +#endif + } else { + _popover = [[UIPopoverController alloc] initWithContentViewController:activityVC]; + _popover.delegate = self; + [_popover presentPopoverFromRect:rect inView:self.webView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; + } + } else if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported + activityVC.popoverPresentationController.sourceView = self.webView; + // position the popup at the bottom, just like iOS < 8 did (and iPhone still does on iOS 8) + NSArray *comps = [NSArray arrayWithObjects: + [NSNumber numberWithInt:(self.viewController.view.frame.size.width/2)-200], + [NSNumber numberWithInt:self.viewController.view.frame.size.height], + [NSNumber numberWithInt:400], + [NSNumber numberWithInt:400], + nil]; + CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps]; + activityVC.popoverPresentationController.sourceRect = rect; +#endif + } + } + [[self getTopMostViewController] presentViewController:activityVC animated:YES completion:nil]; + }); + }]; +} + +- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:SLServiceTypeTwitter]; +} + +- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:SLServiceTypeFacebook]; +} + +- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command { + // If Fb app is installed a message is not prefilled. + // When shared through the default iOS widget (iOS Settings > Facebook) the message is prefilled already. + NSString *message = [command.arguments objectAtIndex:0]; + if (message != (id)[NSNull null]) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1000 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + BOOL fbAppInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]]; // requires whitelisting on iOS9 + if (fbAppInstalled) { + UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; + [pasteboard setValue:message forPasteboardType:@"public.text"]; + NSString *hint = [command.arguments objectAtIndex:4]; + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:hint delegate:nil cancelButtonTitle:nil otherButtonTitles:nil]; + [alert show]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2800 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [alert dismissWithClickedButtonIndex:-1 animated:YES]; + }); + } + }); + } + [self shareViaInternal:command type:SLServiceTypeFacebook]; +} + +- (void)shareVia:(CDVInvokedUrlCommand*)command { + [self shareViaInternal:command type:[command.arguments objectAtIndex:4]]; +} + +- (void)canShareVia:(CDVInvokedUrlCommand*)command { + NSString *via = [command.arguments objectAtIndex:4]; + CDVPluginResult * pluginResult; + if ([@"sms" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaSMS]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"email" caseInsensitiveCompare:via] == NSOrderedSame && [self isEmailAvailable]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"whatsapp" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaWhatsApp]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([@"instagram" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaInstagram]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([self isAvailableForSharing:command type:via]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command { + if ([self isEmailAvailable]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (bool)isEmailAvailable { + Class messageClass = (NSClassFromString(@"MFMailComposeViewController")); + return messageClass != nil && [messageClass canSendMail]; +} + +- (bool)isAvailableForSharing:(CDVInvokedUrlCommand*)command + type:(NSString *) type { + // isAvailableForServiceType returns true if you pass it a type that is not + // in the defined constants, this is probably a bug on apples part + if(!([type isEqualToString:SLServiceTypeFacebook] + || [type isEqualToString:SLServiceTypeTwitter] + || [type isEqualToString:SLServiceTypeTencentWeibo] + || [type isEqualToString:SLServiceTypeSinaWeibo])) { + return false; + } + // wrapped in try-catch, because isAvailableForServiceType may crash if an invalid type is passed + @try { + return [SLComposeViewController isAvailableForServiceType:type]; + } + @catch (NSException* exception) { + return false; + } +} + +- (void)shareViaInternal:(CDVInvokedUrlCommand*)command + type:(NSString *) type { + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + + // boldly invoke the target app, because the phone will display a nice message asking to configure the app + SLComposeViewController *composeViewController = [SLComposeViewController composeViewControllerForServiceType:type]; + if (message != (id)[NSNull null]) { + [composeViewController setInitialText:message]; + } + + for (NSString* filename in filenames) { + UIImage* image = [self getImage:filename]; + if (image != nil) { + [composeViewController addImage:image]; + } + } + + if (urlString != (id)[NSNull null]) { + [composeViewController addURL:[NSURL URLWithString:urlString]]; + } + + [composeViewController setCompletionHandler:^(SLComposeViewControllerResult result) { + if (SLComposeViewControllerResultCancelled == result) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"cancelled"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else if ([self isAvailableForSharing:command type:type]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:SLComposeViewControllerResultDone == result]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } + // required for iOS6 (issues #162 and #167) + [self.viewController dismissViewControllerAnimated:YES completion:nil]; + }]; + [[self getTopMostViewController] presentViewController:composeViewController animated:YES completion:nil]; +} + +- (void)shareViaEmail:(CDVInvokedUrlCommand*)command { + if ([self isEmailAvailable]) { + + if (TARGET_IPHONE_SIMULATOR && IsAtLeastiOSVersion(@"8.0")) { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"SocialSharing plugin" + message:@"Sharing via email is not supported on the iOS 8 simulator." + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alert show]; + return; + } + + self.globalMailComposer.mailComposeDelegate = self; + + if ([command.arguments objectAtIndex:0] != (id)[NSNull null]) { + NSString *message = [command.arguments objectAtIndex:0]; + BOOL isHTML = [message rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch].location != NSNotFound; + [self.globalMailComposer setMessageBody:message isHTML:isHTML]; + } + + if ([command.arguments objectAtIndex:1] != (id)[NSNull null]) { + [self.globalMailComposer setSubject: [command.arguments objectAtIndex:1]]; + } + + if ([command.arguments objectAtIndex:2] != (id)[NSNull null]) { + [self.globalMailComposer setToRecipients:[command.arguments objectAtIndex:2]]; + } + + if ([command.arguments objectAtIndex:3] != (id)[NSNull null]) { + [self.globalMailComposer setCcRecipients:[command.arguments objectAtIndex:3]]; + } + + if ([command.arguments objectAtIndex:4] != (id)[NSNull null]) { + [self.globalMailComposer setBccRecipients:[command.arguments objectAtIndex:4]]; + } + + if ([command.arguments objectAtIndex:5] != (id)[NSNull null]) { + NSArray* attachments = [command.arguments objectAtIndex:5]; + NSFileManager* fileManager = [NSFileManager defaultManager]; + for (NSString* path in attachments) { + NSURL *file = [self getFile:path]; + NSData* data = [fileManager contentsAtPath:file.path]; + + NSString* fileName; + NSString* mimeType; + NSString* basename = [self getBasenameFromAttachmentPath:path]; + + if ([basename hasPrefix:@"data:"]) { + mimeType = (NSString*)[[[basename substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0]; + fileName = @"attachment."; + fileName = [fileName stringByAppendingString:(NSString*)[[mimeType componentsSeparatedByString: @"/"] lastObject]]; + NSString *base64content = (NSString*)[[basename componentsSeparatedByString: @","] lastObject]; + data = [SocialSharing dataFromBase64String:base64content]; + } else { + fileName = [basename pathComponents].lastObject; + mimeType = [self getMimeTypeFromFileExtension:[basename pathExtension]]; + } + [self.globalMailComposer addAttachmentData:data mimeType:mimeType fileName:fileName]; + } + } + + // remember the command, because we need it in the didFinishWithResult method + _command = command; + + [self.commandDelegate runInBackground:^{ + [[self getTopMostViewController] presentViewController:self.globalMailComposer animated:YES completion:nil]; + }]; + + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (UIViewController*) getTopMostViewController { + UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController; + while (presentingViewController.presentedViewController != nil) { + presentingViewController = presentingViewController.presentedViewController; + } + return presentingViewController; +} + +- (NSString*) getBasenameFromAttachmentPath:(NSString*)path { + if ([path hasPrefix:@"base64:"]) { + NSString* pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:" withString:@""]; + return [pathWithoutPrefix substringToIndex:[pathWithoutPrefix rangeOfString:@"//"].location]; + } + return path; +} + +- (NSString*) getMimeTypeFromFileExtension:(NSString*)extension { + if (!extension) { + return nil; + } + // Get the UTI from the file's extension + CFStringRef ext = (CFStringRef)CFBridgingRetain(extension); + CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL); + // Converting UTI to a mime type + NSString *result = (NSString*)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType)); + CFRelease(ext); + CFRelease(type); + return result; +} + +/** + * Delegate will be called after the mail composer did finish an action + * to dismiss the view. + */ +- (void) mailComposeController:(MFMailComposeViewController*)controller + didFinishWithResult:(MFMailComposeResult)result + error:(NSError*)error { + bool ok = result == MFMailComposeResultSent; + [self.globalMailComposer dismissViewControllerAnimated:YES completion:^{[self cycleTheGlobalMailComposer];}]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; +} + +-(void)cycleTheGlobalMailComposer { + // we are cycling the damned GlobalMailComposer: http://stackoverflow.com/questions/25604552/i-have-real-misunderstanding-with-mfmailcomposeviewcontroller-in-swift-ios8-in/25604976#25604976 + self.globalMailComposer = nil; + self.globalMailComposer = [[MFMailComposeViewController alloc] init]; +} + +- (bool)canShareViaSMS { + Class messageClass = (NSClassFromString(@"MFMessageComposeViewController")); + return messageClass != nil && [messageClass canSendText]; +} + +- (void)shareViaSMS:(CDVInvokedUrlCommand*)command { + if ([self canShareViaSMS]) { + NSDictionary* options = [command.arguments objectAtIndex:0]; + NSString *phonenumbers = [command.arguments objectAtIndex:1]; + NSString *message = [options objectForKey:@"message"]; + NSString *subject = [options objectForKey:@"subject"]; + NSString *image = [options objectForKey:@"image"]; + + MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init]; + picker.messageComposeDelegate = (id) self; + if (message != (id)[NSNull null]) { + picker.body = message; + } + if (subject != (id)[NSNull null]) { + [picker setSubject:subject]; + } + if (image != nil && image != (id)[NSNull null]) { + BOOL canSendAttachments = [[MFMessageComposeViewController class] respondsToSelector:@selector(canSendAttachments)]; + if (canSendAttachments) { + NSURL *file = [self getFile:image]; + if (file != nil) { + [picker addAttachmentURL:file withAlternateFilename:nil]; + } + } + } + + if (phonenumbers != (id)[NSNull null]) { + [picker setRecipients:[phonenumbers componentsSeparatedByString:@","]]; + } + // remember the command, because we need it in the didFinishWithResult method + _command = command; + [self.commandDelegate runInBackground:^{ + picker.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + [[self getTopMostViewController] presentViewController:picker animated:YES completion:nil]; + }]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +// Dismisses the SMS composition interface when users taps Cancel or Send +- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result { + bool ok = result == MessageComposeResultSent; + [[self getTopMostViewController] dismissViewControllerAnimated:YES completion:nil]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; +} + +- (bool)canShareViaInstagram { + return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"instagram://app"]]; // requires whitelisting on iOS9 +} + +- (bool)canShareViaWhatsApp { + return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"whatsapp://app"]]; // requires whitelisting on iOS9 +} + +// this is only an internal test method for now, can be used to open a share sheet with 'Open in xx' links for tumblr, drive, dropbox, .. +- (void)openImage:(NSString *)imageName { + UIImage* image =[self getImage:imageName]; + if (image != nil) { + NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myTempImage.jpg"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES]; + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]]; + _documentInteractionController.UTI = @""; // TODO find the scheme for google drive and create a shareViaGoogleDrive function + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES]; + } +} + +- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command { + + // on iOS9 canShareVia('instagram'..) will only work if instagram:// is whitelisted. + // If it's not, this method will ask permission to the user on iOS9 for opening the app, + // which is of course better than Instagram sharing not working at all because you forgot to whitelist it. + // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('instagram'..) + if (!IsAtLeastiOSVersion(@"9.0")) { + if (![self canShareViaInstagram]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + } + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + + // only use the first image (for now.. maybe we can share in a loop?) + UIImage* image = nil; + for (NSString* filename in filenames) { + image = [self getImage:filename]; + break; + } + +// NSData *imageObj = [NSData dataFromBase64String:objectAtIndex0]; + NSString *tmpDir = NSTemporaryDirectory(); + NSString *path = [tmpDir stringByAppendingPathComponent:@"instagram.igo"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES]; + + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]]; + _documentInteractionController.delegate = self; + _documentInteractionController.UTI = @"com.instagram.exclusivegram"; + + if (message != (id)[NSNull null]) { + // no longer working, so .. + _documentInteractionController.annotation = @{@"InstagramCaption" : message}; + + // .. we put the message on the clipboard (you app can prompt the user to paste it) + UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; + [pasteboard setValue:message forPasteboardType:@"public.text"]; + } + + // remember the command for the delegate method + _command = command; + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.webView animated:YES]; +} + +- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command { + + // on iOS9 canShareVia('whatsapp'..) will only work if whatsapp:// is whitelisted. + // If it's not, this method will ask permission to the user on iOS9 for opening the app, + // which is of course better than WhatsApp sharing not working at all because you forgot to whitelist it. + // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('whatsapp'..) + if (!IsAtLeastiOSVersion(@"9.0")) { + if (![self canShareViaWhatsApp]) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + } + + NSString *message = [command.arguments objectAtIndex:0]; + // subject is not supported by the SLComposeViewController + NSArray *filenames = [command.arguments objectAtIndex:2]; + NSString *urlString = [command.arguments objectAtIndex:3]; + + // only use the first image (for now.. maybe we can share in a loop?) + UIImage* image = nil; + for (NSString* filename in filenames) { + image = [self getImage:filename]; + break; + } + + // with WhatsApp, we can share an image OR text+url.. image wins if set + if (image != nil) { + NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/whatsAppTmp.wai"]; + [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES]; + _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]]; + _documentInteractionController.UTI = @"net.whatsapp.image"; + _documentInteractionController.delegate = self; + _command = command; + [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES]; + } else { + // append an url to a message, if both are passed + NSString * shareString = @""; + if (message != (id)[NSNull null]) { + shareString = message; + } + if (urlString != (id)[NSNull null]) { + if ([shareString isEqual: @""]) { + shareString = urlString; + } else { + shareString = [NSString stringWithFormat:@"%@ %@", shareString, urlString]; + } + } + NSString * encodedShareString = [shareString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + // also encode the '=' character + encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"]; + encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"&" withString:@"%26"]; + NSString * encodedShareStringForWhatsApp = [NSString stringWithFormat:@"whatsapp://send?text=%@", encodedShareString]; + + NSURL *whatsappURL = [NSURL URLWithString:encodedShareStringForWhatsApp]; + [[UIApplication sharedApplication] openURL: whatsappURL]; + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } +} + +- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command { + self.command = command; + NSArray *filenames = [command.arguments objectAtIndex:0]; + [self.commandDelegate runInBackground:^{ + bool shared = false; + for (NSString* filename in filenames) { + UIImage* image = [self getImage:filename]; + if (image != nil) { + shared = true; + UIImageWriteToSavedPhotosAlbum(image, self, @selector(thisImage:wasSavedToPhotoAlbumWithError:contextInfo:), nil); + } + } + if (!shared) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no valid image was passed"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } + }]; +} + +// called from saveToPhotoAlbum, note that we only send feedback for the first image that's being saved (not keeping the callback) +// but since the UIImageWriteToSavedPhotosAlbum function is only called with valid images that should not be a problem +- (void)thisImage:(UIImage *)image wasSavedToPhotoAlbumWithError:(NSError *)error contextInfo:(void*)ctxInfo { + if (error) { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } else { + CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId]; + } +} + +-(UIImage*)getImage: (NSString *)imageName { + UIImage *image = nil; + if (imageName != (id)[NSNull null]) { + if ([imageName hasPrefix:@"http"]) { + image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]]]; + } else if ([imageName hasPrefix:@"www/"]) { + image = [UIImage imageNamed:imageName]; + } else if ([imageName hasPrefix:@"file://"]) { + image = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSURL URLWithString:imageName] path]]]; + } else if ([imageName hasPrefix:@"data:"]) { + // using a base64 encoded string + NSURL *imageURL = [NSURL URLWithString:imageName]; + NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; + image = [UIImage imageWithData:imageData]; + } else if ([imageName hasPrefix:@"assets-library://"]) { + // use assets-library + NSURL *imageURL = [NSURL URLWithString:imageName]; + NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; + image = [UIImage imageWithData:imageData]; + } else { + // assume anywhere else, on the local filesystem + image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imageName]]; + } + } + return image; +} + +-(NSURL*)getFile: (NSString *)fileName { + NSURL *file = nil; + if (fileName != (id)[NSNull null]) { + if ([fileName hasPrefix:@"http"]) { + NSURL *url = [NSURL URLWithString:fileName]; + NSData *fileData = [NSData dataWithContentsOfURL:url]; + file = [NSURL fileURLWithPath:[self storeInFile:(NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject] fileData:fileData]]; + } else if ([fileName hasPrefix:@"www/"]) { + NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; + NSString *fullPath = [NSString stringWithFormat:@"%@/%@", bundlePath, fileName]; + file = [NSURL fileURLWithPath:fullPath]; + } else if ([fileName hasPrefix:@"file://"]) { + // stripping the first 6 chars, because the path should start with / instead of file:// + file = [NSURL fileURLWithPath:[fileName substringFromIndex:6]]; + } else if ([fileName hasPrefix:@"data:"]) { + // using a base64 encoded string + // extract some info from the 'fileName', which is for example: data:text/calendar;base64, + NSString *fileType = (NSString*)[[[fileName substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0]; + fileType = (NSString*)[[fileType componentsSeparatedByString: @"/"] lastObject]; + NSString *base64content = (NSString*)[[fileName componentsSeparatedByString: @","] lastObject]; + NSData *fileData = [SocialSharing dataFromBase64String:base64content]; + file = [NSURL fileURLWithPath:[self storeInFile:[NSString stringWithFormat:@"%@.%@", @"file", fileType] fileData:fileData]]; + } else { + // assume anywhere else, on the local filesystem + file = [NSURL fileURLWithPath:fileName]; + } + } + return file; +} + +-(NSString*) storeInFile: (NSString*) fileName + fileData: (NSData*) fileData { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName]; + [fileData writeToFile:filePath atomically:YES]; + _tempStoredFile = filePath; + return filePath; +} + +- (void) cleanupStoredFiles { + if (_tempStoredFile != nil) { + NSError *error; + [[NSFileManager defaultManager]removeItemAtPath:_tempStoredFile error:&error]; + } +} + ++ (NSData*) dataFromBase64String:(NSString*)aString { + return [[NSData alloc] initWithBase64EncodedString:aString options:0]; +} + +#pragma mark - UIPopoverControllerDelegate methods + +- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView **)view { + NSArray *comps = [[self getIPadPopupCoordinates] componentsSeparatedByString:@","]; + CGRect newRect = [self getPopupRectFromIPadPopupCoordinates:comps]; + rect->origin = newRect.origin; +} + +- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { + _popover = nil; +} + +#pragma mark - UIDocumentInteractionControllerDelegate methods + +- (void) documentInteractionController: (UIDocumentInteractionController *) controller willBeginSendingToApplication: (NSString *) application { + // note that the application actually contains the app bundle id which was picked (for whatsapp and instagram only) + NSLog(@"SocialSharing app selected: %@", application); +} + +- (void) documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *) controller { + if (self.command != nil) { + CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:result callbackId: self.command.callbackId]; + } +} + +@end diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js new file mode 100644 index 00000000..99e0af15 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/windows/SocialSharingProxy.js @@ -0,0 +1,114 @@ +var cordova = require('cordova'); + +module.exports = { + share: function (win, fail, args) { + //Text Message + var message = args[0]; + //Title + var subject = args[1]; + //File(s) Path + var fileOrFileArray = args[2]; + //Web link + var url = args[3]; + + var doShare = function (e) { + e.request.data.properties.title = subject?subject: "Sharing"; + if (message) e.request.data.setText(message); + if (url) e.request.data.setWebLink(new Windows.Foundation.Uri(url)); + if (fileOrFileArray.length > 0) { + var deferral = e.request.getDeferral(); + var storageItems = []; + var filesCount = fileOrFileArray.length; + for (var i = 0; i < fileOrFileArray.length; i++) { + Windows.Storage.StorageFile.getFileFromPathAsync(fileOrFileArray[i]).done( + function (file) { + storageItems.push(file); + if (!--filesCount) { + e.request.data.setStorageItems(storageItems); + deferral.complete(); + } + }, + function() { + if (!--filesCount) { + e.request.data.setStorageItems(storageItems); + deferral.complete(); + } + } + ); + } + } + } + + + var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView(); + + dataTransferManager.addEventListener("datarequested", doShare); + + try { + Windows.ApplicationModel.DataTransfer.DataTransferManager.showShareUI(); + win(true); + } catch (err) { + fail(err); + } + }, + + canShareViaEmail: function (win, fail, args) { + win(true); + }, + + shareViaEmail: function (win, fail, args) { + //Text Message + var message = args[0]; + //Title + var subject = args[1]; + //File(s) Path + var fileOrFileArray = args[5]; + + var doShare = function (e) { + e.request.data.properties.title = subject ? subject : "Sharing"; + if (message) { + var htmlFormat = Windows.ApplicationModel.DataTransfer.HtmlFormatHelper.createHtmlFormat(message); + e.request.data.setHtmlFormat(htmlFormat); + } + + if (fileOrFileArray.length > 0) { + var deferral = e.request.getDeferral(); + var storageItems = []; + var filesCount = fileOrFileArray.length; + for (var i = 0; i < fileOrFileArray.length; i++) { + Windows.Storage.StorageFile.getFileFromPathAsync(fileOrFileArray[i]).done( + function (index, file) { + var path = fileOrFileArray[index]; + var streamRef = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(file); + e.request.data.resourceMap[path] = streamRef; + storageItems.push(file); + if (!--filesCount) { + e.request.data.setStorageItems(storageItems); + deferral.complete(); + } + }.bind(this, i), + function () { + if (!--filesCount) { + e.request.data.setStorageItems(storageItems); + deferral.complete(); + } + } + ); + } + } + } + + var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView(); + + dataTransferManager.addEventListener("datarequested", doShare); + + try { + Windows.ApplicationModel.DataTransfer.DataTransferManager.showShareUI(); + win(true); + } catch (err) { + fail(err); + } + } +}; + +require("cordova/exec/proxy").add("SocialSharing", module.exports); \ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs new file mode 100644 index 00000000..1a165127 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/src/wp8/SocialSharing.cs @@ -0,0 +1,103 @@ +using Microsoft.Phone.Tasks; + +using WPCordovaClassLib.Cordova; +using WPCordovaClassLib.Cordova.Commands; +using WPCordovaClassLib.Cordova.JSON; + +using Newtonsoft.Json; + +namespace Cordova.Extension.Commands +{ + public class SocialSharing : BaseCommand + { + + public void available(string jsonArgs) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK)); + } + + public void share(string jsonArgs) + { + + var options = JsonHelper.Deserialize(jsonArgs); + + var message = options[0]; + var title = options[1]; + var files = JsonHelper.Deserialize(options[2]); + var link = options[3]; + + if (!"null".Equals(link)) + { + ShareLinkTask shareLinkTask = new ShareLinkTask(); + shareLinkTask.Title = title; + shareLinkTask.LinkUri = new System.Uri(link, System.UriKind.Absolute); + shareLinkTask.Message = message; + shareLinkTask.Show(); + } + else if (files != null && files.Length > 0) + { + ShareLinkTask shareLinkTask = new ShareLinkTask(); + shareLinkTask.Title = title; + shareLinkTask.LinkUri = new System.Uri(files[0], System.UriKind.Absolute); + shareLinkTask.Message = message; + shareLinkTask.Show(); + } + else + { + var shareStatusTask = new ShareStatusTask { Status = message }; + shareStatusTask.Show(); + } + // unfortunately, there is no way to tell if something was shared, so just invoke the successCallback + DispatchCommandResult(new PluginResult(PluginResult.Status.OK)); + } + + public void canShareViaEmail(string jsonArgs) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.OK)); + } + + // HTML and attachments are currently not supported on WP8 + public void shareViaEmail(string jsonArgs) + { + var options = JsonHelper.Deserialize(jsonArgs); + EmailComposeTask draft = new EmailComposeTask(); + draft.Body = options[0]; + draft.Subject = options[1]; + if (!"null".Equals(options[2])) + { + draft.To = string.Join(",", options[2]); + } + if (!"null".Equals(options[3])) + { + draft.Cc = string.Join(",", options[3]); + } + if (!"null".Equals(options[4])) + { + draft.Bcc = string.Join(",", options[4]); + } + draft.Show(); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true)); + } + + public void shareViaSMS(string jsonArgs) + { + var options = JsonHelper.Deserialize(jsonArgs); + + SmsComposeTask smsComposeTask = new SmsComposeTask(); + + smsComposeTask.To = options[1]; + SMSMessageClass m = JsonConvert.DeserializeObject(options[0]); + smsComposeTask.Body = m.message; + + smsComposeTask.Show(); + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, true)); + } + } + + public class SMSMessageClass + { + public string message { get; set; } + } + +} \ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml new file mode 100644 index 00000000..1ccb2217 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/plugin.xml @@ -0,0 +1,13 @@ + + + + SocialSharing Tests + Nicolas Oliver + MIT + + + \ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js new file mode 100644 index 00000000..630899db --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/tests/test.js @@ -0,0 +1,363 @@ +/** + * Jasmine Based test suites + * + * Several of SocialSharing APIs cannot be automatically tested, because + * they depend on user interaction in order to call success or fail + * handlers. For most of them, there is a basic test that assert the presence of + * the API. + * + * There are some cases that test automation can be applied, i.e in "canShareVia", + * "canShareViaEmail" and "available" methods. For those cases, there is some level + * of automatic test coverage. + */ +exports.defineAutoTests = function () { + 'use strict'; + + describe('socialsharing', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing).toBeDefined(); + }); + + describe('share', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.share).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.share).toEqual('function'); + }); + }); + + describe('shareVia', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.shareVia).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.shareVia).toEqual('function'); + }); + }); + + describe('shareViaTwitter', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.shareViaTwitter).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.shareViaTwitter).toEqual('function'); + }); + }); + + describe('shareViaFacebook', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.shareViaFacebook).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.shareViaFacebook).toEqual('function'); + }); + }); + + describe('shareViaFacebookWithPasteMessageHint', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.shareViaFacebookWithPasteMessageHint).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.shareViaFacebookWithPasteMessageHint).toEqual('function'); + }); + }); + + describe('shareViaWhatsApp', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.shareViaWhatsApp).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.shareViaWhatsApp).toEqual('function'); + }); + }); + + describe('shareViaSMS', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.shareViaSMS).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.shareViaSMS).toEqual('function'); + }); + }); + + describe('shareViaEmail', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.shareViaEmail).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.shareViaEmail).toEqual('function'); + }); + }); + + describe('canShareVia', function () { + it('should be defined', function () { + expect(window.plugins.socialsharing.canShareVia).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.canShareVia).toEqual('function'); + }); + + it('should always call callback or error function', function (done) { + function onSuccess(data){ + expect(data).not.toEqual(null); + done(); + } + + function onError(error){ + expect(error).not.toEqual(null); + done(); + } + + window.plugins.socialsharing.canShareVia('dummytarget','dummymessage', null, null, null, onSuccess, onError); + }); + }); + + describe('canshareViaEmail', function(){ + it('should be defined', function () { + expect(window.plugins.socialsharing.canShareViaEmail).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.canShareViaEmail).toEqual('function'); + }); + + it('should always call callback or error function', function (done) { + function onSuccess(data){ + expect(data).not.toEqual(null); + done(); + } + + function onError(error){ + expect(error).not.toEqual(null); + done(); + } + + window.plugins.socialsharing.canShareViaEmail(onSuccess, onError); + }); + }); + + describe('availabe', function(){ + it('should be defined', function () { + expect(window.plugins.socialsharing.available).toBeDefined(); + }); + + it('should be a function', function () { + expect(typeof window.plugins.socialsharing.available).toEqual('function'); + }); + + it('should return a boolean when called', function(done){ + window.plugins.socialsharing.available(function(isAvailable) { + expect(typeof isAvailable).toEqual('boolean'); + done(); + }); + }); + }); + }); +}; + +/** + * Manual tests suites + * + * Some actions buttons to execute SocialSharing plugin methods + */ +exports.defineManualTests = function (contentEl, createActionButton) { + 'use strict'; + + /** helper function to log messages in the log div element */ + function logMessage(message, color) { + var log = document.getElementById('info'), + logLine = document.createElement('div'); + + if (color) { + logLine.style.color = color; + } + + logLine.innerHTML = message; + log.appendChild(logLine); + } + + /** helper function to clear the log div element */ + function clearLog() { + var log = document.getElementById('info'); + log.innerHTML = ''; + } + + /** helper function to declare a not implemented test */ + function testNotImplemented(testName) { + return function () { + console.error(testName, 'test not implemented'); + }; + } + + /** init method called on deviceready event */ + function init() {} + + /** object to hold properties and configs */ + var TestSuite = {}; + + TestSuite.getCanShareViaTarget = function(){ + return document.getElementById('inputCanShareVia').value; + }; + + TestSuite.$markup = '' + + '
' + + 'Available Tests' + + + '

Available

' + + '
' + + 'Expected result: Should log if the plugin is available or not' + + '
' + + + '
' + + 'Share Tests' + + + '

Share Message

' + + '
' + + 'Expected result: Should display share widget, and the message to share should contain "Message body"' + + + '

Share Message with Subject

' + + '
' + + 'Expected result: Should display share widget, and the message to share should contain "Message body", and the subject should be "Message subject"' + + + '

Share Link

' + + '' + + 'Expected result: Should display share widget, and the message to share should contain "http://www.x-services.nl"' + + + '

Share Message with Link

' + + '' + + 'Expected result: Should display share widget, and the message to share should contain "Message body http://www.x-services.nl"' + + + '

Share Image

' + + '
' + + 'Expected result: Should display share widget, and the message to share should contain an image' + + + '

Share Image in base 64

' + + '
' + + 'Expected result: Should display share widget, and the message to share should contain an image. The image is encoded in base 64' + + + '

Share Image with Message

' + + '
' + + 'Expected result: Should display share widget, and the message to share should contain "Message body" and an image' + + + '

Share Image with Message and Link

' + + '' + + 'Expected result: Should display share widget, and the message to share should contain "Message body http://www.x-services.nl" and an image' + + + '

Share Image with Message, Subject and Link

' + + '' + + 'Expected result: Should display share widget, and the message to share should contain "Message body http://www.x-services.nl", "Message subject" as subject, and an image' + + '
' + + + '
' + + 'Can Share Tests' + + + 'Target:
' + + + '

Can Share via

' + + '
' + + 'Expected result: should log OK if can share, or should log a list of available share targets' + + + '

Can Share via Email

' + + '
' + + 'Expected result: should log OK if can share' + + '
' + + ''; + + contentEl.innerHTML = '
' + TestSuite.$markup; + + createActionButton('availabe', function () { + clearLog(); + window.plugins.socialsharing.available(function(isAvailable) { + var message = 'is this plugin available? '; + message += isAvailable? 'Yes' : 'No'; + + logMessage(message, isAvailable? 'green' : 'red'); + }); + }, 'buttonIsAvailable'); + + createActionButton('share message', function () { + window.plugins.socialsharing.share('Message body'); + }, 'buttonShareMessage'); + + createActionButton('share message and subject', function () { + window.plugins.socialsharing.share('Message body', 'Message subject'); + }, 'buttonShareMessageWithSubject'); + + createActionButton('share link', function () { + window.plugins.socialsharing.share(null, null, null, 'http://www.x-services.nl'); + }, 'buttonShareLink'); + + createActionButton('share message and link', function () { + window.plugins.socialsharing.share('Message body', null, null, 'http://www.x-services.nl'); + }, 'buttonShareMessageAndLink'); + + createActionButton('share image', function () { + window.plugins.socialsharing.share(null, null, 'https://www.google.nl/images/srpr/logo4w.png', null); + }, 'buttonShareImage'); + + createActionButton('share image base 64', function () { + window.plugins.socialsharing.share(null, 'Android filename', 'data:image/png;base64,R0lGODlhDAAMALMBAP8AAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAUKAAEALAAAAAAMAAwAQAQZMMhJK7iY4p3nlZ8XgmNlnibXdVqolmhcRQA7', null); + }, 'buttonShareImageBase64'); + + createActionButton('share message with image', function () { + window.plugins.socialsharing.share('Message body', null, 'https://www.google.nl/images/srpr/logo4w.png', null); + }, 'buttonShareMessageImage'); + + createActionButton('share message, image, and link', function () { + window.plugins.socialsharing.share('Message body', null, 'https://www.google.nl/images/srpr/logo4w.png', 'http://www.x-services.nl'); + }, 'buttonShareMessageImageLink'); + + createActionButton('share message, subject, image, and link', function () { + window.plugins.socialsharing.share('Message body', 'Message subject', 'https://www.google.nl/images/srpr/logo4w.png', 'http://www.x-services.nl'); + }, 'buttonShareMessageSubjectImageLink'); + + createActionButton('can share via', function () { + var target = TestSuite.getCanShareViaTarget(); + + if(!target){ + console.error('must have a canShareVia target'); + } + else { + clearLog(); + window.plugins.socialsharing.canShareVia(target, 'msg', null, null, null, function(e){ + console.log('canShareVia success, see log for more information'); + logMessage('canShareVia: ' + e,'green'); + }, function(e){ + console.error('canShareVia fail, see log for more information'); + var message = "Share targets
"; + + message += "
    "; + + e.forEach(function(target){ + message += "
  • " + target + "
  • "; + }); + + message += "
"; + logMessage(message,'red'); + }); + } + }, 'buttonCanShareVia'); + + createActionButton('can share via email', function () { + clearLog(); + window.plugins.socialsharing.canShareViaEmail(function(e){ + console.log('canShareViaEmail success, see log for more information'); + logMessage('canShareViaEmail: ' + e,'green'); + }, function(e){ + console.error('canShareViaEmail fail, see log for more information'); + logMessage('canShareViaEmail: ' + e,'red'); + }); + }, 'buttonCanShareViaEmail'); + + document.addEventListener('deviceready', init, false); +}; diff --git a/StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js b/StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js new file mode 100644 index 00000000..6ccd567b --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-x-socialsharing/www/SocialSharing.js @@ -0,0 +1,115 @@ +var cordova = require('cordova'); + +function SocialSharing() { +} + +// Override this method (after deviceready) to set the location where you want the iPad popup arrow to appear. +// If not overridden with different values, the popup is not used. Example: +// +// window.plugins.socialsharing.iPadPopupCoordinates = function() { +// return "100,100,200,300"; +// }; +SocialSharing.prototype.iPadPopupCoordinates = function () { + // left,top,width,height + return "-1,-1,-1,-1"; +}; + +SocialSharing.prototype.setIPadPopupCoordinates = function (coords) { + // left,top,width,height + cordova.exec(function() {}, this._getErrorCallback(function() {}, "setIPadPopupCoordinates"), "SocialSharing", "setIPadPopupCoordinates", [coords]); +}; + +SocialSharing.prototype.available = function (callback) { + cordova.exec(function (avail) { + callback(avail ? true : false); + }, null, "SocialSharing", "available", []); +}; + +SocialSharing.prototype.share = function (message, subject, fileOrFileArray, url, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "share"), "SocialSharing", "share", [message, subject, this._asArray(fileOrFileArray), url]); +}; + +SocialSharing.prototype.shareViaTwitter = function (message, file /* multiple not allowed by twitter */, url, successCallback, errorCallback) { + var fileArray = this._asArray(file); + var ecb = this._getErrorCallback(errorCallback, "shareViaTwitter"); + if (fileArray.length > 1) { + ecb("shareViaTwitter supports max one file"); + } else { + cordova.exec(successCallback, ecb, "SocialSharing", "shareViaTwitter", [message, null, fileArray, url]); + } +}; + +SocialSharing.prototype.shareViaFacebook = function (message, fileOrFileArray, url, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaFacebook"), "SocialSharing", "shareViaFacebook", [message, null, this._asArray(fileOrFileArray), url]); +}; + +SocialSharing.prototype.shareViaFacebookWithPasteMessageHint = function (message, fileOrFileArray, url, pasteMessageHint, successCallback, errorCallback) { + pasteMessageHint = pasteMessageHint || "If you like you can paste a message from your clipboard"; + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaFacebookWithPasteMessageHint"), "SocialSharing", "shareViaFacebookWithPasteMessageHint", [message, null, this._asArray(fileOrFileArray), url, pasteMessageHint]); +}; + +SocialSharing.prototype.shareViaWhatsApp = function (message, fileOrFileArray, url, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaWhatsApp"), "SocialSharing", "shareViaWhatsApp", [message, null, this._asArray(fileOrFileArray), url]); +}; + +SocialSharing.prototype.shareViaSMS = function (options, phonenumbers, successCallback, errorCallback) { + var opts = options; + if (typeof options == "string") { + opts = {"message":options}; // for backward compatibility as the options param used to be the message + } + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaSMS"), "SocialSharing", "shareViaSMS", [opts, phonenumbers]); +}; + +SocialSharing.prototype.shareViaEmail = function (message, subject, toArray, ccArray, bccArray, fileOrFileArray, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaEmail"), "SocialSharing", "shareViaEmail", [message, subject, this._asArray(toArray), this._asArray(ccArray), this._asArray(bccArray), this._asArray(fileOrFileArray)]); +}; + +SocialSharing.prototype.canShareVia = function (via, message, subject, fileOrFileArray, url, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "canShareVia"), "SocialSharing", "canShareVia", [message, subject, this._asArray(fileOrFileArray), url, via]); +}; + +SocialSharing.prototype.canShareViaEmail = function (successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "canShareViaEmail"), "SocialSharing", "canShareViaEmail", []); +}; + +SocialSharing.prototype.shareViaInstagram = function (message, fileOrFileArray, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareViaInstagram"), "SocialSharing", "shareViaInstagram", [message, null, this._asArray(fileOrFileArray), null]); +}; + +SocialSharing.prototype.shareVia = function (via, message, subject, fileOrFileArray, url, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "shareVia"), "SocialSharing", "shareVia", [message, subject, this._asArray(fileOrFileArray), url, via]); +}; + +SocialSharing.prototype.saveToPhotoAlbum = function (fileOrFileArray, successCallback, errorCallback) { + cordova.exec(successCallback, this._getErrorCallback(errorCallback, "saveToPhotoAlbum"), "SocialSharing", "saveToPhotoAlbum", [this._asArray(fileOrFileArray)]); +}; + +SocialSharing.prototype._asArray = function (param) { + if (param == null) { + param = []; + } else if (typeof param === 'string') { + param = new Array(param); + } + return param; +}; + +SocialSharing.prototype._getErrorCallback = function (ecb, functionName) { + if (typeof ecb === 'function') { + return ecb; + } else { + return function (result) { + console.log("The injected error callback of '" + functionName + "' received: " + JSON.stringify(result)); + } + } +}; + +SocialSharing.install = function () { + if (!window.plugins) { + window.plugins = {}; + } + + window.plugins.socialsharing = new SocialSharing(); + return window.plugins.socialsharing; +}; + +cordova.addConstructor(SocialSharing.install); diff --git a/StoneIsland/plugins/fetch.json b/StoneIsland/plugins/fetch.json index 59ddd3e7..b27f2f2d 100644 --- a/StoneIsland/plugins/fetch.json +++ b/StoneIsland/plugins/fetch.json @@ -80,5 +80,13 @@ "variables": { "URL_SCHEME": "stoneisland" } + }, + "cordova-plugin-x-socialsharing": { + "source": { + "type": "registry", + "id": "cordova-plugin-x-socialsharing" + }, + "is_top_level": true, + "variables": {} } } \ No newline at end of file diff --git a/StoneIsland/plugins/ios.json b/StoneIsland/plugins/ios.json index 2a7a09ab..77cd0086 100644 --- a/StoneIsland/plugins/ios.json +++ b/StoneIsland/plugins/ios.json @@ -39,6 +39,10 @@ { "xml": "", "count": 1 + }, + { + "xml": "", + "count": 1 } ] } @@ -68,6 +72,18 @@ "xml": false, "count": 1 } + ], + "Social.framework": [ + { + "xml": true, + "count": 1 + } + ], + "MessageUI.framework": [ + { + "xml": true, + "count": 1 + } ] } }, @@ -126,6 +142,9 @@ "cordova-plugin-customurlscheme": { "URL_SCHEME": "stoneisland", "PACKAGE_NAME": "io.cordova.hellocordova" + }, + "cordova-plugin-x-socialsharing": { + "PACKAGE_NAME": "io.cordova.hellocordova" } }, "dependent_plugins": {} -- cgit v1.2.3-70-g09d2 From 2c063f920983c2b8b4584a21471c70521a391009 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 30 Nov 2015 23:25:05 -0500 Subject: share links --- StoneIsland/www/js/lib/blogs/HubView.js | 5 +++++ StoneIsland/www/js/lib/products/ProductView.js | 1 + 2 files changed, 6 insertions(+) diff --git a/StoneIsland/www/js/lib/blogs/HubView.js b/StoneIsland/www/js/lib/blogs/HubView.js index a6ae958e..001b8161 100644 --- a/StoneIsland/www/js/lib/blogs/HubView.js +++ b/StoneIsland/www/js/lib/blogs/HubView.js @@ -4,6 +4,7 @@ var HubView = ScrollableView.extend({ template: $("#hub .template").html(), events: { + "click .share": "content-share", "click .store": "store_link", "click .gallery-left": "gallery_left", "click .gallery-right": "gallery_right", @@ -95,4 +96,8 @@ var HubView = ScrollableView.extend({ $(e.currentTarget).closest("hub_item").flickity('next') }, + share: function(){ + window.plugins.socialsharing.share( this.item['ModelNames'], null, null, "http://stoneisland.com/") + }, + }) \ No newline at end of file diff --git a/StoneIsland/www/js/lib/products/ProductView.js b/StoneIsland/www/js/lib/products/ProductView.js index 92a0e0f7..d35f6385 100644 --- a/StoneIsland/www/js/lib/products/ProductView.js +++ b/StoneIsland/www/js/lib/products/ProductView.js @@ -263,6 +263,7 @@ var ProductView = ScrollableView.extend({ }, share: function(){ + window.plugins.socialsharing.share( this.item['ModelNames'], null, null, "http://stoneisland.com/") }, }) -- cgit v1.2.3-70-g09d2 From 848730bdbc1e1b2c81274385d85fd385422323d8 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 30 Nov 2015 23:51:15 -0500 Subject: use_stored_credit_card --- StoneIsland/www/js/lib/auth/SignupView.js | 6 ++-- StoneIsland/www/js/lib/cart/CartPayment.js | 56 ++++++++++++++++++++--------- StoneIsland/www/js/lib/cart/CartShipping.js | 5 ++- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/StoneIsland/www/js/lib/auth/SignupView.js b/StoneIsland/www/js/lib/auth/SignupView.js index 22b310de..a78af233 100644 --- a/StoneIsland/www/js/lib/auth/SignupView.js +++ b/StoneIsland/www/js/lib/auth/SignupView.js @@ -4,7 +4,8 @@ var SignupView = FormView.extend({ action: sdk.account.signup, last_data: null, - + +/* test_data: { "Email": "testit.account" + Math.floor(Math.random() * 10000000) + "@yoox.com", "Password": "TestPassword", @@ -16,7 +17,8 @@ var SignupView = FormView.extend({ "DataProfiling": true, "DataProfiling2": true, }, - +*/ + events: { "click .privacy-msg": "privacy_link", "submit form": "save", diff --git a/StoneIsland/www/js/lib/cart/CartPayment.js b/StoneIsland/www/js/lib/cart/CartPayment.js index fec5e1d1..222e2ac9 100644 --- a/StoneIsland/www/js/lib/cart/CartPayment.js +++ b/StoneIsland/www/js/lib/cart/CartPayment.js @@ -9,6 +9,7 @@ var CartPayment = FormView.extend({ address_list_mode: false, cc_list_mode: false, + data: {}, events: { "change [name=SameAsShipping]": "toggle_shipping", @@ -28,6 +29,7 @@ var CartPayment = FormView.extend({ this.$cc_list = this.$(".cc_list") this.$cc_form = this.$(".cc") this.$cc_dropdown = this.$(".cc_dropdown") + this.$cc_confirm = this.$(".cc_confirm") this.address = new AddressView ({ parent: this, checkPhone: false }) this.cc = new CreditCardView ({ parent: this }) @@ -75,6 +77,7 @@ var CartPayment = FormView.extend({ this.cc.disabled = this.cc_list_mode this.$cc_form.toggle(! this.cc_list_mode) this.$cc_list.toggle(this.cc_list_mode) + this.$cc_confirm.toggle(this.cc_list_mode) }, populate: function(){ @@ -107,37 +110,56 @@ var CartPayment = FormView.extend({ }, finalize: function(data){ - var shipping_info = {}, address_data, address_id, cc_info = {}, cc_data, cc_id + var shipping_info = {}, address_data, address_id, cc_info = {}, cc_data, cc_id var shipping_type = $("[name=ShippingType]").filter(function(){ return $(this).prop("checked") }).val() - if (this.list_mode) { - address_id = $("[name=AddressId]").filter(function(){ return $(this).prop("checked") }).val() + if (this.address_list_mode) { + address_id = this.$("[name=AddressId]").filter(function(){ return $(this).prop("checked") }).val() address_data = app.account.addressLookup[ address_id ] } else { address_data = data } + if (this.cc_list_mode) { - cc_id = $("[name=CCId]").filter(function(){ return $(this).prop("checked") }).val() + cc_id = this.$("[name=CCId]").filter(function(){ return $(this).prop("checked") }).val() cc_data = app.account.ccLookup[ cc_id ] + + var card_on_file = { + "guid": cc_data.Guid, + "cvv": this.$("[name=CvvConfirm]"), + } + app.curtain.show("loading") + promise(sdk.cart.use_stored_credit_card, { data: card_on_file }).then(function(data){ + app.curtain.hide("loading") + this.success() + }.bind(this)).error(function(data){ + app.curtain.hide("loading") + }.bind(this)) + + return } else { cc_data = data } - shipping_info.Name = address_data.Name - shipping_info.Surname = address_data.Surname - shipping_info.Email = auth.user.Email - shipping_info.Phone = address_data.Phone - shipping_info.Mobile = address_data.Phone - shipping_info.StreetWithNumber = address_data.Address - shipping_info.PostalCode = address_data.ZipCode - shipping_info.City = address_data.City - shipping_info.Province = address_data.Province - shipping_info.Region = address_data.Province - shipping_info.CountryCode = "US" - - return shipping_info + var credit_info = { + "HolderName": address_data.Name, + "HolderSurname": address_data.Surname, + "HolderAddress": address_data.Adress, + "HolderCity": address_data.City, + "HolderProvince": address_data.Province, + "HolderZip": address_data.ZipCode, + "HolderISOCountry": CANADIAN_LOOKUP[ address_data.Province ] ? "CA" : "US", + "HolderEmail": auth.user.Email, + "CardNumber": "", + "Type": cc_data.Type, + "ExpirationMonth": cc_data.ExpirationMonth, + "ExpirationYear": cc_data.ExpirationYear, + "Cvv": cc_data.Cvv, + } + + return credit_info }, success: function(){ diff --git a/StoneIsland/www/js/lib/cart/CartShipping.js b/StoneIsland/www/js/lib/cart/CartShipping.js index 9b8205c1..ec9394da 100644 --- a/StoneIsland/www/js/lib/cart/CartShipping.js +++ b/StoneIsland/www/js/lib/cart/CartShipping.js @@ -5,6 +5,7 @@ var CartShipping = FormView.extend({ action: sdk.cart.set_shipping_address, list_mode: true, + data: {}, template: $("#cart_shipping .template").html(), @@ -84,7 +85,7 @@ var CartShipping = FormView.extend({ }) if (this.list_mode) { - address_id = $("[name=AddressId]").filter(function(){ return $(this).prop("checked") }).val() + address_id = this.$("[name=AddressId]").filter(function(){ return $(this).prop("checked") }).val() address_data = app.account.addressLookup[ address_id ] } else { @@ -103,6 +104,8 @@ var CartShipping = FormView.extend({ shipping_info.Region = address_data.Province shipping_info.CountryCode = CANADIAN_LOOKUP[ address_data.Province ] ? "CA" : "US" + this.data = shipping_info + return shipping_info }, -- cgit v1.2.3-70-g09d2 From 881c5de5b7709d06e88fdf554a021b8f1eddb9ed Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Mon, 30 Nov 2015 23:58:19 -0500 Subject: more cart --- StoneIsland/www/css/nav.css | 5 +++++ StoneIsland/www/js/lib/cart/CartPayment.js | 9 ++++++++- StoneIsland/www/js/lib/cart/CartSummary.js | 2 +- StoneIsland/www/js/lib/nav/CreditCardView.js | 1 + StoneIsland/www/js/lib/view/Serializable.js | 8 +++++--- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/StoneIsland/www/css/nav.css b/StoneIsland/www/css/nav.css index 61834f8b..814a8c3f 100644 --- a/StoneIsland/www/css/nav.css +++ b/StoneIsland/www/css/nav.css @@ -334,3 +334,8 @@ h1 { letter-spacing: 1px; font-weight: bold; } + +.msg { + padding: 20px; + display: inline-block; +} diff --git a/StoneIsland/www/js/lib/cart/CartPayment.js b/StoneIsland/www/js/lib/cart/CartPayment.js index 222e2ac9..e65c7e8e 100644 --- a/StoneIsland/www/js/lib/cart/CartPayment.js +++ b/StoneIsland/www/js/lib/cart/CartPayment.js @@ -52,6 +52,7 @@ var CartPayment = FormView.extend({ setTimeout(function(){ var state = this.$same_as_shipping.prop("checked") this.$billing_address_rapper.toggle( ! state ) + this.address.disabled = state }.bind(this)) }, @@ -113,7 +114,10 @@ var CartPayment = FormView.extend({ var shipping_info = {}, address_data, address_id, cc_info = {}, cc_data, cc_id var shipping_type = $("[name=ShippingType]").filter(function(){ return $(this).prop("checked") }).val() - if (this.address_list_mode) { + if (this.$same_as_shipping.prop("checked")) { + address_data = app.cart.shipping.data + } + else if (this.address_list_mode) { address_id = this.$("[name=AddressId]").filter(function(){ return $(this).prop("checked") }).val() address_data = app.account.addressLookup[ address_id ] } @@ -129,6 +133,9 @@ var CartPayment = FormView.extend({ "guid": cc_data.Guid, "cvv": this.$("[name=CvvConfirm]"), } + + console.log("got card on file") + app.curtain.show("loading") promise(sdk.cart.use_stored_credit_card, { data: card_on_file }).then(function(data){ app.curtain.hide("loading") diff --git a/StoneIsland/www/js/lib/cart/CartSummary.js b/StoneIsland/www/js/lib/cart/CartSummary.js index 72c44405..cfd34bd7 100644 --- a/StoneIsland/www/js/lib/cart/CartSummary.js +++ b/StoneIsland/www/js/lib/cart/CartSummary.js @@ -133,7 +133,7 @@ var CartSummary = ScrollableView.extend({ updateTotals: function(){ var subtotal = this.data.Cart.Totals.TotalWithoutPromotions var shipping_cost = this.data.Cart.DeliveryMethod.Selected.Amount.Total - var tax = 0 + var tax = this.data.Cart.Totals.TotalSalesTax var total = this.data.Cart.Totals.TotalToPay this.$subtotal.html( as_cash(subtotal) ) diff --git a/StoneIsland/www/js/lib/nav/CreditCardView.js b/StoneIsland/www/js/lib/nav/CreditCardView.js index ba3ac54a..63784618 100644 --- a/StoneIsland/www/js/lib/nav/CreditCardView.js +++ b/StoneIsland/www/js/lib/nav/CreditCardView.js @@ -40,6 +40,7 @@ var CreditCardView = SerializableView.extend({ }, validate_fields: function(data, errors){ + if (this.disabled) { return } var card = this.$number.validateCreditCard(this.cardOptions) if (! card.valid) { errors.push([ "Number", "Your card number is invalid." ]) } if (! data.ExpirationMonth || data.ExpirationMonth == "NONE") { errors.push([ "ExpirationMonth", "Please enter the expiration month." ]) } diff --git a/StoneIsland/www/js/lib/view/Serializable.js b/StoneIsland/www/js/lib/view/Serializable.js index b1e095d3..6ef8eda3 100644 --- a/StoneIsland/www/js/lib/view/Serializable.js +++ b/StoneIsland/www/js/lib/view/Serializable.js @@ -68,9 +68,11 @@ var SerializableView = View.extend({ var data = data || this.serialize() var errors = errors || [] var presence_msgs = this.validate_presence || {} - Object.keys(presence_msgs).forEach(function(k){ - if (! data[k]) errors.push( [ k, presence_msgs[k] ] ) - }) + if (! this.disabled) { + Object.keys(presence_msgs).forEach(function(k){ + if (! data[k]) errors.push( [ k, presence_msgs[k] ] ) + }) + } this.validate_fields && this.validate_fields(data, errors) this.cc && this.cc.validate(data, errors) this.address && this.address.validate(data, errors) -- cgit v1.2.3-70-g09d2 From bc3d47e6f7f93640e6f183ab2664235c00057e6c Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 1 Dec 2015 00:10:02 -0500 Subject: show curtain when adding to cart.. --- StoneIsland/www/js/lib/products/ProductView.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/StoneIsland/www/js/lib/products/ProductView.js b/StoneIsland/www/js/lib/products/ProductView.js index d35f6385..5b7ee6d2 100644 --- a/StoneIsland/www/js/lib/products/ProductView.js +++ b/StoneIsland/www/js/lib/products/ProductView.js @@ -240,14 +240,18 @@ var ProductView = ScrollableView.extend({ app.last_view = app.cart } else if ( ! auth.has_cart() ) { + app.curtain.show("loading") auth.create_cart(function(){ auth.add_deferred_product_to_cart(function(){ + app.curtain.hide("loading") app.router.go("cart") }) }) } else { + app.curtain.show("loading") auth.add_deferred_product_to_cart(function(){ + app.curtain.hide("loading") if (opt.route) { app.router.go("cart") } -- cgit v1.2.3-70-g09d2 From 7b14d7a87af7d0bb9563672b5880c2a4042509d2 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 1 Dec 2015 00:35:28 -0500 Subject: load cart shipping from api --- StoneIsland/www/js/lib/cart/CartConfirm.js | 4 ++++ StoneIsland/www/js/lib/cart/CartPayment.js | 4 +--- StoneIsland/www/js/lib/cart/CartShipping.js | 15 +++++++++++++++ StoneIsland/www/js/lib/cart/CartSummary.js | 4 ++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/StoneIsland/www/js/lib/cart/CartConfirm.js b/StoneIsland/www/js/lib/cart/CartConfirm.js index aa6ec9e4..8a6b3c0e 100644 --- a/StoneIsland/www/js/lib/cart/CartConfirm.js +++ b/StoneIsland/www/js/lib/cart/CartConfirm.js @@ -16,9 +16,13 @@ var CartConfirm = FormView.extend({ app.footer.show("PLACE ORDER", "CANCEL") window.location.hash = "#/cart/confirm" this.deferScrollToTop() + + app.curtain.show("loading") + promise(sdk.cart.get_status).then( this.populate.bind(this) ) }, populate: function(){ + app.curtain.hide("loading") }, save: function(){ diff --git a/StoneIsland/www/js/lib/cart/CartPayment.js b/StoneIsland/www/js/lib/cart/CartPayment.js index e65c7e8e..749594bc 100644 --- a/StoneIsland/www/js/lib/cart/CartPayment.js +++ b/StoneIsland/www/js/lib/cart/CartPayment.js @@ -134,8 +134,6 @@ var CartPayment = FormView.extend({ "cvv": this.$("[name=CvvConfirm]"), } - console.log("got card on file") - app.curtain.show("loading") promise(sdk.cart.use_stored_credit_card, { data: card_on_file }).then(function(data){ app.curtain.hide("loading") @@ -159,7 +157,7 @@ var CartPayment = FormView.extend({ "HolderZip": address_data.ZipCode, "HolderISOCountry": CANADIAN_LOOKUP[ address_data.Province ] ? "CA" : "US", "HolderEmail": auth.user.Email, - "CardNumber": "", + "CardNumber": cc_data['Number'], "Type": cc_data.Type, "ExpirationMonth": cc_data.ExpirationMonth, "ExpirationYear": cc_data.ExpirationYear, diff --git a/StoneIsland/www/js/lib/cart/CartShipping.js b/StoneIsland/www/js/lib/cart/CartShipping.js index ec9394da..dfc5cb3a 100644 --- a/StoneIsland/www/js/lib/cart/CartShipping.js +++ b/StoneIsland/www/js/lib/cart/CartShipping.js @@ -54,6 +54,21 @@ var CartShipping = FormView.extend({ }.bind(this)) }, + load_form: function(cart_data){ + var data = cart_data.Cart.Receiver + var addy = data.StreetWithNumber.split("\n") + data.Address1 = addy[0] || "" + data.Address2 = addy[1] || "" + data.ZipCode = data.PostalCode + data.Province = data.Region + this.load_data(data) + + if (cart_data.DeliveryMethod.Selected && cart_data.DeliveryMethod.Type) { + $("#standard-shipping").prop("checked", cart_data.DeliveryMethod.Type == 1) + $("#express-shipping").prop("checked", cart_data.DeliveryMethod.Type == 2) + } + }, + toggle_dropdown: function(state){ if (! app.account.addresses.length) { state = false diff --git a/StoneIsland/www/js/lib/cart/CartSummary.js b/StoneIsland/www/js/lib/cart/CartSummary.js index cfd34bd7..0ad57020 100644 --- a/StoneIsland/www/js/lib/cart/CartSummary.js +++ b/StoneIsland/www/js/lib/cart/CartSummary.js @@ -119,6 +119,10 @@ var CartSummary = ScrollableView.extend({ }.bind(this)) }.bind(this)) + if (data.Cart.Receiver && data.Cart.Receiver.City) { + app.cart.shipping.load_form( data ) + } + this.updateTotals() this.el.className = "full" -- cgit v1.2.3-70-g09d2 From 3078a4c252135f7e8e15389522b0d5c79fc3bd8e Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 1 Dec 2015 00:41:20 -0500 Subject: advance to confirm page --- StoneIsland/www/js/lib/cart/CartPayment.js | 8 +++++--- StoneIsland/www/js/lib/cart/CartShipping.js | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/StoneIsland/www/js/lib/cart/CartPayment.js b/StoneIsland/www/js/lib/cart/CartPayment.js index 749594bc..11d6cddf 100644 --- a/StoneIsland/www/js/lib/cart/CartPayment.js +++ b/StoneIsland/www/js/lib/cart/CartPayment.js @@ -151,18 +151,20 @@ var CartPayment = FormView.extend({ var credit_info = { "HolderName": address_data.Name, "HolderSurname": address_data.Surname, - "HolderAddress": address_data.Adress, + "HolderAddress": address_data.Address || address_data.StreetWithNumber, "HolderCity": address_data.City, "HolderProvince": address_data.Province, - "HolderZip": address_data.ZipCode, + "HolderZip": address_data.PostalCode || address_data.ZipCode, "HolderISOCountry": CANADIAN_LOOKUP[ address_data.Province ] ? "CA" : "US", "HolderEmail": auth.user.Email, "CardNumber": cc_data['Number'], "Type": cc_data.Type, "ExpirationMonth": cc_data.ExpirationMonth, - "ExpirationYear": cc_data.ExpirationYear, + "ExpirationYear": cc_data.ExpirationYear.substr(2,3), "Cvv": cc_data.Cvv, } + + console.log( credit_info ) return credit_info }, diff --git a/StoneIsland/www/js/lib/cart/CartShipping.js b/StoneIsland/www/js/lib/cart/CartShipping.js index dfc5cb3a..ef906804 100644 --- a/StoneIsland/www/js/lib/cart/CartShipping.js +++ b/StoneIsland/www/js/lib/cart/CartShipping.js @@ -63,9 +63,10 @@ var CartShipping = FormView.extend({ data.Province = data.Region this.load_data(data) - if (cart_data.DeliveryMethod.Selected && cart_data.DeliveryMethod.Type) { - $("#standard-shipping").prop("checked", cart_data.DeliveryMethod.Type == 1) - $("#express-shipping").prop("checked", cart_data.DeliveryMethod.Type == 2) + this.data = data + if (cart_data.Cart.DeliveryMethod && cart_data.Cart.DeliveryMethod.Selected && cart_data.Cart.DeliveryMethod.Type) { + $("#standard-shipping").prop("checked", cart_data.Cart.DeliveryMethod.Type == 1) + $("#express-shipping").prop("checked", cart_data.Cart.DeliveryMethod.Type == 2) } }, -- cgit v1.2.3-70-g09d2 From 9497b50fa02f3cfa9cb263ce3a96fa725282d60d Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 1 Dec 2015 01:06:58 -0500 Subject: populate cartconfirm page --- StoneIsland/www/index.html | 1 - StoneIsland/www/js/lib/cart/CartConfirm.js | 103 ++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/StoneIsland/www/index.html b/StoneIsland/www/index.html index 03d4a958..45a04bdf 100644 --- a/StoneIsland/www/index.html +++ b/StoneIsland/www/index.html @@ -861,7 +861,6 @@
-
diff --git a/StoneIsland/www/js/lib/cart/CartConfirm.js b/StoneIsland/www/js/lib/cart/CartConfirm.js index 8a6b3c0e..171f41a1 100644 --- a/StoneIsland/www/js/lib/cart/CartConfirm.js +++ b/StoneIsland/www/js/lib/cart/CartConfirm.js @@ -2,11 +2,26 @@ var CartConfirm = FormView.extend({ el: "#cart_confirm", + template: $("#cart_confirm .template").html(), + events: { }, initialize: function(opt){ this.parent = opt.parent + this.$rows = this.$(".rows") + this.$subtotal = this.$(".subtotal") + this.$shipping = this.$(".shipping") + this.$tax = this.$(".tax") + this.$total = this.$(".total") + + this.$shipping_address = this.$(".shipping_address") + this.$shipping_method = this.$(".shipping_method") + + this.$payment_name = this.$(".payment_name") + this.$payment_method = this.$(".payment_method") + this.$payment_address = this.$(".payment_address") + this.scroller = new IScroll('#cart_confirm', app.iscroll_options) }, @@ -21,7 +36,93 @@ var CartConfirm = FormView.extend({ promise(sdk.cart.get_status).then( this.populate.bind(this) ) }, - populate: function(){ + populate: function(data){ + console.log(data) + + data = data.Cart + + this.$rows.empty() + + data.Items.forEach(function(item){ + var $el = $("
") + this.$rows.append($el) + var code_ten = item.Code10 + var size_id = item.Size + + var code = code_ten.substr(0, 8) + app.product.find(code, function(data, details){ + var descriptions = app.product.get_descriptions( details ) + + var name_partz = descriptions['ModelNames'].split(' ') + var num = name_partz.shift() + var title = name_partz.join(' ') + var type = title_case( descriptions['MicroCategory'] ) + + var color_name, size_name + + details.Item.ModelColors.some(function(color){ + if (color['Code10'] == code_ten) { + color_name = color['ColorDescription'] + return true + } + return false + }) + + details.Item.ModelSizes.some(function(size){ + if (size['SizeId'] == size_id) { + // console.log(size) + size_name = size['Default']['Text'] + size_name = SIZE_LOOKUP[ size_name ] || size_name + if (! size_name && ! size['Default']['Labeled']) { + size_name = size['Default']['Text'] + " " + size['Default']['ClassFamily'] + } + + return true + } + return false + }) + +// size_name = item.DefaultSize + " " + item.DefaultSizeClassFamily + + var t = this.template + .replace(/{{image}}/, sdk.image(item['Code10'], '11_f')) + .replace(/{{sku}}/, num) + .replace(/{{title}}/, title) + .replace(/{{type}}/, type) + .replace(/{{size}}/, size_name || "DEFAULT") + .replace(/{{color}}/, color_name || "DEFAULT") + .replace(/{{quantity}}/, 1) + .replace(/{{price}}/, as_cash(details.Item.Price.DiscountedPrice)) + $el.data("price", details.Item.Price.DiscountedPrice) + $el.html(t) + this.refreshScroller() + }.bind(this)) + }.bind(this)) + + var subtotal = data.Totals.TotalWithoutPromotions + var shipping_cost = data.DeliveryMethod.Selected.Amount.Total + var tax = data.Totals.TotalSalesTax + var total = data.Totals.TotalToPay + + this.$subtotal.html( as_cash(subtotal) ) + this.$shipping.html( as_cash(shipping_cost) ) + this.$tax.html( as_cash(tax) ) + this.$total.html( as_cash(total) ) + + var street = data.Receiver.StreetWithNumber.replace(/\n$/,"").replace("\n", ", ") + var address = data.Receiver.Name.toUpperCase() + " " + data.Receiver.Surname.toUpperCase() + "
" + street + ", " + address += data.Receiver.City + ", " + data.Receiver.Region + " " + data.Receiver.PostalCode + + this.$shipping_address.html(address) + this.$shipping_method.html(data.DeliveryMethod.Selected.Type == 1 ? "* STANDARD SHIPPING" : "* EXPRESS SHIPPING") + + var cc = data.Payment.CreditCard + var cc_street = cc.HolderAddress.replace(/\n$/,"").replace("\n", ", ") + var cc_type = cc.Type == "AmericanExpress" ? "American Express" : cc.Type + + this.$payment_name.html( cc.HolderName.toUpperCase() + " " + cc.HolderSurname.toUpperCase() ) + this.$payment_method.html( cc_type.toUpperCase() + " XXXX-XXXX-XXXX-" + cc.Last4 ) + app.curtain.hide("loading") }, -- cgit v1.2.3-70-g09d2