You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by mw...@apache.org on 2013/05/15 22:35:56 UTC
[17/37] Add WP7 and WP8 platform files.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f59ddbbd/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs b/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs
new file mode 100644
index 0000000..e585895
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/FileTransfer.cs
@@ -0,0 +1,526 @@
+/*
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Net;
+using System.Runtime.Serialization;
+using System.Windows;
+using System.Security;
+using System.Diagnostics;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ public class FileTransfer : BaseCommand
+ {
+ public class DownloadRequestState
+ {
+ // This class stores the State of the request.
+ public HttpWebRequest request;
+ public DownloadOptions options;
+
+ public DownloadRequestState()
+ {
+ request = null;
+ options = null;
+ }
+ }
+
+ /// <summary>
+ /// Boundary symbol
+ /// </summary>
+ private string Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
+
+ // Error codes
+ public const int FileNotFoundError = 1;
+ public const int InvalidUrlError = 2;
+ public const int ConnectionError = 3;
+
+ /// <summary>
+ /// Options for downloading file
+ /// </summary>
+ [DataContract]
+ public class DownloadOptions
+ {
+ /// <summary>
+ /// File path to download to
+ /// </summary>
+ [DataMember(Name = "filePath", IsRequired = true)]
+ public string FilePath { get; set; }
+
+ /// <summary>
+ /// Server address to the file to download
+ /// </summary>
+ [DataMember(Name = "url", IsRequired = true)]
+ public string Url { get; set; }
+ }
+
+ /// <summary>
+ /// Options for uploading file
+ /// </summary>
+ [DataContract]
+ public class UploadOptions
+ {
+ /// <summary>
+ /// File path to upload
+ /// </summary>
+ [DataMember(Name = "filePath", IsRequired = true)]
+ public string FilePath { get; set; }
+
+ /// <summary>
+ /// Server address
+ /// </summary>
+ [DataMember(Name = "server", IsRequired = true)]
+ public string Server { get; set; }
+
+ /// <summary>
+ /// File key
+ /// </summary>
+ [DataMember(Name = "fileKey")]
+ public string FileKey { get; set; }
+
+ /// <summary>
+ /// File name on the server
+ /// </summary>
+ [DataMember(Name = "fileName")]
+ public string FileName { get; set; }
+
+ /// <summary>
+ /// File Mime type
+ /// </summary>
+ [DataMember(Name = "mimeType")]
+ public string MimeType { get; set; }
+
+
+ /// <summary>
+ /// Additional options
+ /// </summary>
+ [DataMember(Name = "params")]
+ public string Params { get; set; }
+
+ /// <summary>
+ /// Flag to recognize if we should trust every host (only in debug environments)
+ /// </summary>
+ [DataMember(Name = "debug")]
+ public bool Debug { get; set; }
+
+ /// <summary>
+ /// Creates options object with default parameters
+ /// </summary>
+ public UploadOptions()
+ {
+ this.SetDefaultValues(new StreamingContext());
+ }
+
+ /// <summary>
+ /// Initializes default values for class fields.
+ /// Implemented in separate method because default constructor is not invoked during deserialization.
+ /// </summary>
+ /// <param name="context"></param>
+ [OnDeserializing()]
+ public void SetDefaultValues(StreamingContext context)
+ {
+ this.FileKey = "file";
+ this.FileName = "image.jpg";
+ this.MimeType = "image/jpeg";
+ }
+
+ }
+
+ /// <summary>
+ /// Uploading response info
+ /// </summary>
+ [DataContract]
+ public class FileUploadResult
+ {
+ /// <summary>
+ /// Amount of sent bytes
+ /// </summary>
+ [DataMember(Name = "bytesSent")]
+ public long BytesSent { get; set; }
+
+ /// <summary>
+ /// Server response code
+ /// </summary>
+ [DataMember(Name = "responseCode")]
+ public long ResponseCode { get; set; }
+
+ /// <summary>
+ /// Server response
+ /// </summary>
+ [DataMember(Name = "response", EmitDefaultValue = false)]
+ public string Response { get; set; }
+
+ /// <summary>
+ /// Creates FileUploadResult object with response values
+ /// </summary>
+ /// <param name="bytesSent">Amount of sent bytes</param>
+ /// <param name="responseCode">Server response code</param>
+ /// <param name="response">Server response</param>
+ public FileUploadResult(long bytesSent, long responseCode, string response)
+ {
+ this.BytesSent = bytesSent;
+ this.ResponseCode = responseCode;
+ this.Response = response;
+ }
+ }
+
+ /// <summary>
+ /// Represents transfer error codes for callback
+ /// </summary>
+ [DataContract]
+ public class FileTransferError
+ {
+ /// <summary>
+ /// Error code
+ /// </summary>
+ [DataMember(Name = "code", IsRequired = true)]
+ public int Code { get; set; }
+
+ /// <summary>
+ /// The source URI
+ /// </summary>
+ [DataMember(Name = "source", IsRequired = true)]
+ public string Source { get; set; }
+
+ /// <summary>
+ /// The target URI
+ /// </summary>
+ [DataMember(Name = "target", IsRequired = true)]
+ public string Target { get; set; }
+
+ /// <summary>
+ /// The http status code response from the remote URI
+ /// </summary>
+ [DataMember(Name = "http_status", IsRequired = true)]
+ public int HttpStatus { get; set; }
+
+ /// <summary>
+ /// Creates FileTransferError object
+ /// </summary>
+ /// <param name="errorCode">Error code</param>
+ public FileTransferError(int errorCode)
+ {
+ this.Code = errorCode;
+ this.Source = null;
+ this.Target = null;
+ this.HttpStatus = 0;
+ }
+ public FileTransferError(int errorCode, string source, string target, int status)
+ {
+ this.Code = errorCode;
+ this.Source = source;
+ this.Target = target;
+ this.HttpStatus = status;
+ }
+ }
+
+ /// <summary>
+ /// Upload options
+ /// </summary>
+ private UploadOptions uploadOptions;
+
+ /// <summary>
+ /// Bytes sent
+ /// </summary>
+ private long bytesSent;
+
+ /// <summary>
+ /// sends a file to a server
+ /// </summary>
+ /// <param name="options">Upload options</param>
+ public void upload(string options)
+ {
+ Debug.WriteLine("options = " + options);
+ options = options.Replace("{}", "null");
+
+ try
+ {
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ uploadOptions = JSON.JsonHelper.Deserialize<UploadOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ Uri serverUri;
+ try
+ {
+ serverUri = new Uri(uploadOptions.Server);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, uploadOptions.Server, null, 0)));
+ return;
+ }
+ HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(serverUri);
+ webRequest.ContentType = "multipart/form-data;boundary=" + Boundary;
+ webRequest.Method = "POST";
+ webRequest.BeginGetRequestStream(WriteCallback, webRequest);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+ }
+ }
+
+ public void download(string options)
+ {
+ DownloadOptions downloadOptions = null;
+ HttpWebRequest webRequest = null;
+
+ try
+ {
+ string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
+
+ downloadOptions = new DownloadOptions();// JSON.JsonHelper.Deserialize<DownloadOptions>(options);
+ downloadOptions.Url = optionStrings[0];
+ downloadOptions.FilePath = optionStrings[1];
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ webRequest = (HttpWebRequest)WebRequest.Create(downloadOptions.Url);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, downloadOptions.Url, null, 0)));
+ return;
+ }
+
+ if (downloadOptions != null && webRequest != null)
+ {
+ DownloadRequestState state = new DownloadRequestState();
+ state.options = downloadOptions;
+ state.request = webRequest;
+ webRequest.BeginGetResponse(new AsyncCallback(downloadCallback), state);
+ }
+
+
+
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="asynchronousResult"></param>
+ private void downloadCallback(IAsyncResult asynchronousResult)
+ {
+ DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState;
+ HttpWebRequest request = reqState.request;
+
+ try
+ {
+ HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
+
+ using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+ {
+ // create the file if not exists
+ if (!isoFile.FileExists(reqState.options.FilePath))
+ {
+ var file = isoFile.CreateFile(reqState.options.FilePath);
+ file.Close();
+ }
+
+ using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, FileAccess.Write, isoFile))
+ {
+ long totalBytes = response.ContentLength;
+ int bytesRead = 0;
+ using (BinaryReader reader = new BinaryReader(response.GetResponseStream()))
+ {
+
+ using (BinaryWriter writer = new BinaryWriter(fileStream))
+ {
+ int BUFFER_SIZE = 1024;
+ byte[] buffer;
+
+ while (true)
+ {
+ buffer = reader.ReadBytes(BUFFER_SIZE);
+ // fire a progress event ?
+ bytesRead += buffer.Length;
+ if (buffer.Length > 0)
+ {
+ writer.Write(buffer);
+ }
+ else
+ {
+ writer.Close();
+ reader.Close();
+ fileStream.Close();
+ break;
+ }
+ }
+ }
+
+ }
+
+
+ }
+ }
+ WPCordovaClassLib.Cordova.Commands.File.FileEntry entry = new WPCordovaClassLib.Cordova.Commands.File.FileEntry(reqState.options.FilePath);
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry));
+ }
+ catch (IsolatedStorageException)
+ {
+ // Trying to write the file somewhere within the IsoStorage.
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+ }
+ catch (SecurityException)
+ {
+ // Trying to write the file somewhere not allowed.
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+ }
+ catch (WebException webex)
+ {
+ // TODO: probably need better work here to properly respond with all http status codes back to JS
+ // Right now am jumping through hoops just to detect 404.
+ if ((webex.Status == WebExceptionStatus.ProtocolError && ((HttpWebResponse)webex.Response).StatusCode == HttpStatusCode.NotFound) || webex.Status == WebExceptionStatus.UnknownError)
+ {
+ // Weird MSFT detection of 404... seriously... just give us the f(*&#$@ status code as a number ffs!!!
+ // "Numbers for HTTP status codes? Nah.... let's create our own set of enums/structs to abstract that stuff away."
+ // FACEPALM
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError, null, null, 404)));
+ }
+ else
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+ }
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)));
+ }
+ }
+
+
+
+ /// <summary>
+ /// Read file from Isolated Storage and sends it to server
+ /// </summary>
+ /// <param name="asynchronousResult"></param>
+ private void WriteCallback(IAsyncResult asynchronousResult)
+ {
+ try
+ {
+ HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
+ using (Stream requestStream = (webRequest.EndGetRequestStream(asynchronousResult)))
+ {
+ string lineStart = "--";
+ string lineEnd = Environment.NewLine;
+ byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes(lineStart + Boundary + lineEnd);
+ string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"" + lineEnd + lineEnd + "{1}" + lineEnd;
+
+ if (uploadOptions.Params != null)
+ {
+
+ string[] arrParams = uploadOptions.Params.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (string param in arrParams)
+ {
+ string[] split = param.Split('=');
+ string key = split[0];
+ string val = split[1];
+ requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+ string formItem = string.Format(formdataTemplate, key, val);
+ byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(formItem);
+ requestStream.Write(formItemBytes, 0, formItemBytes.Length);
+ }
+ requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+ }
+ using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
+ {
+ if (!isoFile.FileExists(uploadOptions.FilePath))
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError, uploadOptions.Server, uploadOptions.FilePath, 0)));
+ return;
+ }
+
+ using (FileStream fileStream = new IsolatedStorageFileStream(uploadOptions.FilePath, FileMode.Open, isoFile))
+ {
+ string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + lineEnd + "Content-Type: {2}" + lineEnd + lineEnd;
+ string header = string.Format(headerTemplate, uploadOptions.FileKey, uploadOptions.FileName, uploadOptions.MimeType);
+ byte[] headerBytes = System.Text.Encoding.UTF8.GetBytes(header);
+ requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
+ requestStream.Write(headerBytes, 0, headerBytes.Length);
+ byte[] buffer = new byte[4096];
+ int bytesRead = 0;
+
+ while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
+ {
+ requestStream.Write(buffer, 0, bytesRead);
+ bytesSent += bytesRead;
+ }
+ }
+ byte[] endRequest = System.Text.Encoding.UTF8.GetBytes(lineEnd + lineStart + Boundary + lineStart + lineEnd);
+ requestStream.Write(endRequest, 0, endRequest.Length);
+ }
+ }
+ webRequest.BeginGetResponse(ReadCallback, webRequest);
+ }
+ catch (Exception)
+ {
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)));
+ });
+ }
+ }
+
+ /// <summary>
+ /// Reads response into FileUploadResult
+ /// </summary>
+ /// <param name="asynchronousResult"></param>
+ private void ReadCallback(IAsyncResult asynchronousResult)
+ {
+ try
+ {
+ HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
+ using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult))
+ {
+ using (Stream streamResponse = response.GetResponseStream())
+ {
+ using (StreamReader streamReader = new StreamReader(streamResponse))
+ {
+ string responseString = streamReader.ReadToEnd();
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileUploadResult(bytesSent, (long)response.StatusCode, responseString)));
+ });
+ }
+ }
+ }
+ }
+ catch (Exception)
+ {
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ FileTransferError transferError = new FileTransferError(ConnectionError, uploadOptions.Server, uploadOptions.FilePath, 403);
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, transferError));
+ });
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f59ddbbd/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs b/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs
new file mode 100644
index 0000000..c53cb29
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/GeoLocation.cs
@@ -0,0 +1,34 @@
+/*
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Device.Location;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ /// <summary>
+ /// This is a command stub, the browser provides the correct implementation. We use this to trigger the static analyzer that we require this permission
+ /// </summary>
+ public class GeoLocation
+ {
+ /* Unreachable code, by design -jm */
+ private void triggerGeoInclusion()
+ {
+ new GeoCoordinateWatcher();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f59ddbbd/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs b/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs
new file mode 100644
index 0000000..2c2f468
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/Globalization.cs
@@ -0,0 +1,1177 @@
+/*
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+using System;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ /// <summary>
+ /// Provides information about system locale, culture settings, number formats, ect.
+ /// </summary>
+ public class Globalization : BaseCommand
+ {
+
+ #region Globalization errors
+
+ /// <summary>
+ /// Globalization error codes.
+ /// </summary>
+ public enum ErrorCode : int
+ {
+ UnknownError = 0,
+ FormattingError = 1,
+ ParsingError = 2,
+ PatternError = 3
+ }
+
+ /// <summary>
+ /// Represents globalization error object.
+ /// </summary>
+ [DataContract]
+ public class GlobalizationError
+ {
+ #region Error messages
+ /// <summary>
+ /// Error messages
+ /// </summary>
+ public const string UnknownError = "UNKNOWN_ERROR";
+ public const string FormattingError = "FORMATTIN_ERROR";
+ public const string ParsingError = "PARSING_ERROR";
+ public const string PatternError = "PATTERN_ERROR";
+
+ #endregion
+
+ /// <summary>
+ /// Error code
+ /// </summary>
+ [DataMember(Name = "code", IsRequired = false)]
+ public ErrorCode Code { get; set; }
+
+ /// <summary>
+ /// Error message
+ /// </summary>
+ [DataMember(Name = "message", IsRequired = false)]
+ public string Message { get; set; }
+
+ /// <summary>
+ /// Default constructor
+ /// </summary>
+ public GlobalizationError()
+ {
+ this.Code = ErrorCode.UnknownError;
+ this.Message = UnknownError;
+ }
+
+ /// <summary>
+ /// Constructor setting error code
+ /// </summary>
+ public GlobalizationError(ErrorCode error)
+ {
+ this.Code = error;
+
+ switch (error)
+ {
+ case ErrorCode.ParsingError:
+ {
+ this.Message = ParsingError;
+ break;
+ }
+ case ErrorCode.FormattingError:
+ {
+ this.Message = FormattingError;
+ break;
+ }
+ case ErrorCode.PatternError:
+ {
+ this.Message = PatternError;
+ break;
+ }
+ default:
+ {
+ this.Message = UnknownError;
+ break;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Globalization options
+
+ /// <summary>
+ /// Represents globalization options.
+ /// </summary>
+ [DataContract]
+ public class GlobalizationOptions
+ {
+ #region available option values
+ /// <summary>
+ /// Number pattern types.
+ /// </summary>
+ public const string Percent = "percent";
+ public const string Currency = "currency";
+ public const string Decimal = "decimal";
+
+ /// <summary>
+ /// Format length types
+ /// </summary>
+ public const string Short = "short";
+ public const string Medium = "medium";
+ public const string Long = "long";
+ public const string Full = "full";
+
+ /// <summary>
+ /// Selector types
+ /// </summary>
+ public const string TimeSelector = "time";
+ public const string DateSelector = "date";
+ public const string DateAndTimeSelector = "date and time";
+
+ /// <summary>
+ /// Date name types
+ /// </summary>
+ public const string Narrow = "narrow";
+ public const string Wide = "wide";
+
+ /// <summary>
+ /// Date name items
+ /// </summary>
+ public const string Months = "months";
+ public const string Days = "days";
+
+ #endregion
+
+ /// <summary>
+ /// Additional options
+ /// </summary>
+ [DataMember(Name = "options", IsRequired = false)]
+ public Options AdditionalOptions { get; set; }
+
+ /// <summary>
+ /// Date to convert
+ /// </summary>
+ [DataMember(Name = "date", IsRequired = false)]
+ public long Date { get; set; }
+
+ /// <summary>
+ /// Date as stirng
+ /// </summary>
+ [DataMember(Name = "dateString", IsRequired = false)]
+ public string DateString { get; set; }
+
+ /// <summary>
+ /// Currency code
+ /// </summary>
+ [DataMember(Name = "currencyCode", IsRequired = false)]
+ public string CurrencyCode { get; set; }
+
+ /// <summary>
+ /// Number as string
+ /// </summary>
+ [DataMember(Name = "numberString", IsRequired = false)]
+ public string NumberString { get; set; }
+
+ /// <summary>
+ /// Number to convert
+ /// </summary>
+ [DataMember(Name = "number", IsRequired = false)]
+ public double Number { get; set; }
+ }
+
+ /// <summary>
+ /// Represents additional options
+ /// </summary>
+ [DataContract]
+ public class Options
+ {
+ /// <summary>
+ /// Pattern type
+ /// </summary>
+ [DataMember(Name = "type", IsRequired = false)]
+ public string Type { get; set; }
+
+ /// <summary>
+ /// Format length
+ /// </summary>
+ [DataMember(Name = "formatLength", IsRequired = false)]
+ public string FormatLength { get; set; }
+
+ /// <summary>
+ /// Selector
+ /// </summary>
+ [DataMember(Name = "selector", IsRequired = false)]
+ public string Selector { get; set; }
+
+ /// <summary>
+ /// Date name item
+ /// </summary>
+ [DataMember(Name = "item", IsRequired = false)]
+ public string Item { get; set; }
+ }
+
+ #endregion
+
+ #region returned objects
+
+ #region Number pattern object
+
+ /// <summary>
+ /// Represents number pattern
+ /// </summary>
+ [DataContract]
+ public class NumberPattern
+ {
+ /// <summary>
+ /// Pattern
+ /// </summary>
+ [DataMember(Name = "pattern", IsRequired = false)]
+ public string Pattern { get; set; }
+
+ /// <summary>
+ /// Symbol
+ /// </summary>
+ [DataMember(Name = "symbol", IsRequired = false)]
+ public string Symbol { get; set; }
+
+ /// <summary>
+ /// Fraction
+ /// </summary>
+ [DataMember(Name = "fraction", IsRequired = false)]
+ public int Fraction { get; set; }
+
+ /// <summary>
+ /// Positive
+ /// </summary>
+ [DataMember(Name = "positive", IsRequired = false)]
+ public string Positive { get; set; }
+
+ /// <summary>
+ /// Negative
+ /// </summary>
+ [DataMember(Name = "negative", IsRequired = false)]
+ public string Negative { get; set; }
+
+ /// <summary>
+ /// Rounding
+ /// </summary>
+ [DataMember(Name = "rounding", IsRequired = false)]
+ public int Rounding { get; set; }
+
+ /// <summary>
+ /// Decimal
+ /// </summary>
+ [DataMember(Name = "decimal", IsRequired = false)]
+ public string Decimal { get; set; }
+
+ /// <summary>
+ /// Grouping
+ /// </summary>
+ [DataMember(Name = "grouping", IsRequired = false)]
+ public string Grouping { get; set; }
+
+ /// <summary>
+ /// Constructor of the class
+ /// </summary>
+ /// <param name="pattern"></param>
+ /// <param name="symbol"></param>
+ /// <param name="fraction"></param>
+ /// <param name="positive"></param>
+ /// <param name="negative"></param>
+ /// <param name="rounding"></param>
+ /// <param name="dec"></param>
+ /// <param name="grouping"></param>
+ public NumberPattern(string pattern, string symbol, int fraction, string positive, string negative, int rounding, string dec, string grouping)
+ {
+ this.Pattern = pattern;
+ this.Symbol = symbol;
+ this.Fraction = fraction;
+ this.Positive = positive;
+ this.Negative = negative;
+ this.Rounding = rounding;
+ this.Decimal = dec;
+ this.Grouping = grouping;
+ }
+ }
+ #endregion
+
+ #region Date format object
+
+ /// <summary>
+ /// Represents date format
+ /// </summary>
+ [DataContract]
+ public class DateFormat
+ {
+ /// <summary>
+ /// Year
+ /// </summary>
+ [DataMember(Name = "year", IsRequired = false)]
+ public int Year { get; set; }
+
+ /// <summary>
+ /// Month
+ /// </summary>
+ [DataMember(Name = "month", IsRequired = false)]
+ public int Month { get; set; }
+
+ /// <summary>
+ /// Day
+ /// </summary>
+ [DataMember(Name = "day", IsRequired = false)]
+ public int Day { get; set; }
+
+ /// <summary>
+ /// Hour
+ /// </summary>
+ [DataMember(Name = "hour", IsRequired = false)]
+ public int Hour { get; set; }
+
+ /// <summary>
+ /// Minute
+ /// </summary>
+ [DataMember(Name = "minute", IsRequired = false)]
+ public int Minute { get; set; }
+
+ /// <summary>
+ /// Second
+ /// </summary>
+ [DataMember(Name = "second", IsRequired = false)]
+ public int Second { get; set; }
+
+ /// <summary>
+ /// Millisecond
+ /// </summary>
+ [DataMember(Name = "millisecond", IsRequired = false)]
+ public int Millisecond { get; set; }
+
+ public DateFormat(int year, int month, int day, int hour, int minute, int second, int millisecond)
+ {
+ this.Year = year;
+ this.Month = month;
+ this.Day = day;
+ this.Hour = hour;
+ this.Minute = minute;
+ this.Millisecond = millisecond;
+ }
+
+ }
+ #endregion
+
+ #region Date pattern object
+
+ /// <summary>
+ /// Represents date pattern object
+ /// </summary>
+ [DataContract]
+ public class DatePattern
+ {
+
+ /// <summary>
+ /// Date pattern
+ /// </summary>
+ [DataMember(Name = "pattern", IsRequired = false)]
+ public string Pattern { get; set; }
+
+ /// <summary>
+ /// TimeZone
+ /// </summary>
+ [DataMember(Name = "timezone", IsRequired = false)]
+ public string TimeZone { get; set; }
+
+ /// <summary>
+ /// UTC offset
+ /// </summary>
+ [DataMember(Name = "utc_offset", IsRequired = false)]
+ public double UtcOffset { get; set; }
+
+ /// <summary>
+ /// Dst offset
+ /// </summary>
+ [DataMember(Name = "dst_offset", IsRequired = false)]
+ public double DstOffset { get; set; }
+
+ /// <summary>
+ /// Constructor of the class
+ /// </summary>
+ /// <param name="pattern"></param>
+ /// <param name="timezone"></param>
+ /// <param name="utcOffset"></param>
+ /// <param name="dstOffset"></param>
+ public DatePattern(string pattern, string timezone, double utcOffset, double dstOffset)
+ {
+ this.Pattern = pattern;
+ this.TimeZone = timezone;
+ this.UtcOffset = utcOffset;
+ this.DstOffset = dstOffset;
+ }
+
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Locale info
+
+ /// <summary>
+ /// Gets the string identifier for the client's current locale setting.
+ /// </summary>
+ /// <param name="options"></param>
+ public void getLocaleName(string options)
+ {
+ try
+ {
+ var locale = RegionInfo.CurrentRegion.TwoLetterISORegionName;
+ PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(locale));
+ this.DispatchCommandResult(result);
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+ }
+ }
+
+ /// <summary>
+ /// Gets the string identifier for the client's current language.
+ /// </summary>
+ /// <param name="options"></param>
+ public void getPreferredLanguage(string options)
+ {
+ try
+ {
+ var language = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
+ PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(language));
+ this.DispatchCommandResult(result);
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+ }
+ }
+
+ #endregion
+
+ #region Date and time info
+
+ /// <summary>
+ /// Gets whether daylight savings time is in effect for a given date using the client's
+ /// time zone and calendar.
+ /// </summary>
+ /// <param name="opitons">Date to daylight savings check.</param>
+ public void isDayLightSavingsTime(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ DateTime start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ DateTime date = start.AddMilliseconds(globalOptions.Date).ToLocalTime();
+ TimeZoneInfo localZone = TimeZoneInfo.Local;
+ bool isDaylightSavingTime = localZone.IsDaylightSavingTime(date);
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(isDaylightSavingTime, "dst")));
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+ }
+ }
+
+ /// <summary>
+ /// Gets the first day of the week according to the client's user preferences and calendar.
+ /// The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
+ /// </summary>
+ /// <param name="options"></param>
+ public void getFirstDayOfWeek(string options)
+ {
+ try
+ {
+ // DateTimeFormat returns days of the week numbered from zero, so we have to increase returned value by one.
+ var firstDayOfWeek = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek + 1;
+ PluginResult result = new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(firstDayOfWeek));
+ this.DispatchCommandResult(result);
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+ }
+ }
+
+ #endregion
+
+ #region Formatting
+
+ /// <summary>
+ /// Gets a date formatted as a string according to the client's user preferences and calendar using the time zone of the client.
+ /// </summary>
+ /// <param name="options"></param>
+ public void dateToString(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ DateTime start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ DateTime date = start.AddMilliseconds(globalOptions.Date).ToLocalTime();
+
+ string format = "{0:M/dd/yy H:m:s}"; //short datetime by default
+ int formatLength = 0; //default format
+ int selector = 0; //default selector
+
+ if (globalOptions.AdditionalOptions != null)
+ {
+ if (globalOptions.AdditionalOptions.FormatLength != null)
+ {
+ string t = globalOptions.AdditionalOptions.FormatLength;
+
+ if (t.Equals(GlobalizationOptions.Full))
+ {
+ formatLength++;
+ }
+ }
+
+ if (globalOptions.AdditionalOptions.Selector != null)
+ {
+ string t = globalOptions.AdditionalOptions.Selector;
+
+ if (t.Equals(GlobalizationOptions.DateSelector))
+ {
+ selector += 10;
+ }
+ else if (t.Equals(GlobalizationOptions.TimeSelector))
+ {
+ selector += 20;
+ }
+ }
+
+ //determine return value
+ int method = formatLength + selector;
+
+ switch (method)
+ {
+ case 1: // full datetime
+ {
+ format = "{0:MMMM/dddd/yyyy HH:mm:ss tt}";
+ break;
+ }
+ case 10: // short date
+ {
+ format = "{0:d}";
+ break;
+ }
+ case 11: // full date
+ {
+ format = "{0:D}";
+ break;
+ }
+ case 20: // short time
+ {
+ format = "{0:t}";
+ break;
+ }
+ case 21: // full time
+ {
+ format = "{0:T}";
+ break;
+ }
+ default: // short datetime
+ {
+ format = "{0:M/dd/yy H:m:s}";
+ break;
+ }
+ }
+ }
+
+ string formattedValue = string.Format(CultureInfo.CurrentCulture, format, date);
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(formattedValue)));
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+ }
+ }
+
+ /// <summary>
+ /// Parses a date formatted as a string according to the client's user preferences and calendar using the time zone of the client and returns the corresponding date object
+ /// </summary>
+ /// <param name="options"></param>
+ public void stringToDate(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ if (string.IsNullOrEmpty(globalOptions.DateString))
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ string format = "M/dd/yy H:m:s"; // short datetime by default
+ int formatLength = 0; //default format
+ int selector = 0; //default selector
+
+ if (globalOptions.AdditionalOptions != null)
+ {
+ if (globalOptions.AdditionalOptions.FormatLength != null)
+ {
+ string t = globalOptions.AdditionalOptions.FormatLength;
+
+ if (t.Equals(GlobalizationOptions.Full))
+ {
+ formatLength++;
+ }
+ }
+
+ if (globalOptions.AdditionalOptions.Selector != null)
+ {
+ string t = globalOptions.AdditionalOptions.Selector;
+
+ if (t.Equals(GlobalizationOptions.DateSelector))
+ {
+ selector += 10;
+ }
+ else if (t.Equals(GlobalizationOptions.TimeSelector))
+ {
+ selector += 20;
+ }
+ }
+
+ //determine return value
+ int method = formatLength + selector;
+
+ switch (method)
+ {
+ case 1: // full datetime
+ {
+ format = "MMMM/dddd/yyyy HH:mm:ss tt";
+ break;
+ }
+ case 10: // short date
+ {
+ format = "d";
+ break;
+ }
+ case 11: // full date
+ {
+ format = "D";
+ break;
+ }
+ case 20: // short time
+ {
+ format = "t";
+ break;
+ }
+ case 21: // full time
+ {
+ format = "T";
+ break;
+ }
+ default: // short datetime
+ {
+ format = "M/dd/yy H:m:s";
+ break;
+ }
+ }
+ }
+
+ DateTime date = DateTime.ParseExact(globalOptions.DateString, format, CultureInfo.CurrentCulture);
+ DateFormat dateFormat = new DateFormat(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Millisecond);
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, dateFormat));
+
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.ParsingError)));
+ }
+ }
+
+ /// <summary>
+ /// Gets a pattern string for formatting and parsing dates according to the client's user preferences.
+ /// </summary>
+ /// <param name="options"></param>
+ public void getDatePattern(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ DateTimeFormatInfo dateFormatInfo = DateTimeFormatInfo.CurrentInfo;
+ string pattern = dateFormatInfo.FullDateTimePattern; // full datetime by default
+ int formatLength = 0; //default format
+ int selector = 0; //default selector
+
+ if (globalOptions.AdditionalOptions != null)
+ {
+ if (globalOptions.AdditionalOptions.FormatLength != null)
+ {
+ string t = globalOptions.AdditionalOptions.FormatLength;
+
+ if (t.Equals(GlobalizationOptions.Full))
+ {
+ formatLength++;
+ }
+ }
+
+ if (globalOptions.AdditionalOptions.Selector != null)
+ {
+ string t = globalOptions.AdditionalOptions.Selector;
+
+ if (t.Equals(GlobalizationOptions.DateSelector))
+ {
+ selector += 10;
+ }
+ else if (t.Equals(GlobalizationOptions.TimeSelector))
+ {
+ selector += 20;
+ }
+ }
+
+ //determine return value
+ int method = formatLength + selector;
+
+ switch (method)
+ {
+ case 1: // full datetime
+ {
+ pattern = dateFormatInfo.FullDateTimePattern;
+ break;
+ }
+ case 10: // short date
+ {
+ pattern = dateFormatInfo.ShortDatePattern;
+ break;
+ }
+ case 11: // full date
+ {
+ pattern = dateFormatInfo.LongDatePattern;
+ break;
+ }
+ case 20: // short time
+ {
+ pattern = dateFormatInfo.ShortTimePattern;
+ break;
+ }
+ case 21: // full time
+ {
+ pattern = dateFormatInfo.LongTimePattern;
+ break;
+ }
+ default: // short datetime
+ {
+ // Seems like C# doesn't support short datetime pattern so we use full format
+ // http://msdn.microsoft.com/en-us/library/1at0z4ew%28v=vs.71%29.aspx
+ pattern = dateFormatInfo.FullDateTimePattern;
+ break;
+ }
+ }
+ }
+
+ TimeZoneInfo localZone = TimeZoneInfo.Local;
+ DatePattern datePattern = new DatePattern(pattern, localZone.DisplayName, localZone.BaseUtcOffset.TotalSeconds, 0);
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, datePattern));
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.PatternError)));
+ }
+ }
+
+ /// <summary>
+ /// Gets an array of either the names of the months or days of the week according to the client's user preferences and calendar.
+ /// </summary>
+ /// <param name="options"></param>
+ public void getDateNames(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ int type = 0; //default wide
+ int item = 0; //default months
+
+ if (globalOptions.AdditionalOptions != null)
+ {
+ if (globalOptions.AdditionalOptions.Type != null)
+ {
+ string t = globalOptions.AdditionalOptions.Type;
+
+ if (t.Equals(GlobalizationOptions.Narrow))
+ {
+ type++;
+ }
+ }
+
+ if (globalOptions.AdditionalOptions.Item != null)
+ {
+ string t = globalOptions.AdditionalOptions.Item;
+
+ if (t.Equals(GlobalizationOptions.Days))
+ {
+ item += 10;
+ }
+ }
+ }
+
+ //determine return value
+ int method = item + type;
+ string[] namesArray;
+ CultureInfo currentCulture = CultureInfo.CurrentCulture;
+
+ if (method == 1) //months and narrow
+ {
+ namesArray = currentCulture.DateTimeFormat.AbbreviatedMonthNames;
+ }
+ else if (method == 10) //days and wide
+ {
+ namesArray = currentCulture.DateTimeFormat.DayNames;
+ }
+ else if (method == 11) //days and narrow
+ {
+ namesArray = currentCulture.DateTimeFormat.AbbreviatedDayNames;
+ }
+ else //default: months and wide
+ {
+ namesArray = currentCulture.DateTimeFormat.MonthNames;
+ }
+
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(namesArray)));
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError()));
+ }
+ }
+
+ /// <summary>
+ /// Gets a number formatted as a string according to the client's user preferences.
+ /// </summary>
+ /// <param name="options"></param>
+ public void numberToString(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ string format = string.Empty;
+ string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+ GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+
+ switch (numberFormatType)
+ {
+ case GlobalizationOptions.Percent:
+ {
+ format = "{0:p}";
+ break;
+ }
+
+ case GlobalizationOptions.Currency:
+ {
+ format = "{0:c}";
+ break;
+ }
+
+ default:
+ {
+ format = "{0:f}";
+ break;
+ }
+ }
+
+ string formattedValue = string.Format(CultureInfo.CurrentCulture, format, globalOptions.Number);
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(formattedValue)));
+
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+ }
+ }
+
+ /// <summary>
+ /// Gets a number formatted as a string according to the client's user preferences and returns the corresponding number.
+ /// </summary>
+ /// <param name="options"></param>
+ public void stringToNumber(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ if (string.IsNullOrEmpty(globalOptions.NumberString))
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ string numberString = globalOptions.NumberString;
+ string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+ GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+
+ NumberStyles numberStyle;
+
+ switch (numberFormatType)
+ {
+ case GlobalizationOptions.Percent:
+ {
+ numberStyle = NumberStyles.Any;
+ numberString = numberString.Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PercentSymbol, "");
+ break;
+ }
+
+ case GlobalizationOptions.Currency:
+ {
+ numberStyle = NumberStyles.Currency;
+ break;
+ }
+
+ default:
+ {
+ numberStyle = NumberStyles.Number;
+ break;
+ }
+ }
+
+ double value = double.Parse(numberString, numberStyle, CultureInfo.CurrentCulture);
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, this.WrapIntoJSON(value)));
+
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.ParsingError)));
+ }
+ }
+
+
+ /// <summary>
+ /// Gets a pattern string for formatting and parsing numbers according to the client's user preferences.
+ /// </summary>
+ /// <param name="options"></param>
+ public void getNumberPattern(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ CultureInfo cultureInfo = CultureInfo.CurrentCulture;
+ NumberFormatInfo formatInfo = cultureInfo.NumberFormat;
+ string numberFormatType = (globalOptions.AdditionalOptions == null || string.IsNullOrEmpty(globalOptions.AdditionalOptions.Type)) ?
+ GlobalizationOptions.Decimal : globalOptions.AdditionalOptions.Type;
+ NumberPattern pattern = null;
+ string symbol;
+
+ // TODO find out how to get format pattern and the number of fraction digits
+ switch (numberFormatType)
+ {
+ case GlobalizationOptions.Percent:
+ {
+ symbol = formatInfo.PercentSymbol;
+ pattern = new NumberPattern("", symbol, 0, formatInfo.PercentPositivePattern.ToString(), formatInfo.PercentNegativePattern.ToString(), 0, formatInfo.PercentDecimalSeparator, formatInfo.PercentGroupSeparator);
+ break;
+ }
+ case GlobalizationOptions.Currency:
+ {
+ symbol = formatInfo.CurrencySymbol;
+ pattern = new NumberPattern("", symbol, 0, formatInfo.CurrencyPositivePattern.ToString(), formatInfo.CurrencyNegativePattern.ToString(), 0, formatInfo.CurrencyDecimalSeparator, formatInfo.CurrencyGroupSeparator);
+ break;
+ }
+ default:
+ {
+ symbol = formatInfo.NumberDecimalSeparator;
+ pattern = new NumberPattern("", symbol, 0, "", formatInfo.NumberNegativePattern.ToString(), 0, formatInfo.NumberDecimalSeparator, formatInfo.NumberGroupSeparator);
+ break;
+ }
+ }
+
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK, pattern));
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.PatternError)));
+ }
+ }
+
+ /// <summary>
+ /// Gets a pattern string for formatting and parsing currency values according to the client's user preferences and ISO 4217 currency code.
+ /// </summary>
+ /// <param name="options"></param>
+ public void getCurrencyPattern(string options)
+ {
+ GlobalizationOptions globalOptions;
+
+ try
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ globalOptions = JSON.JsonHelper.Deserialize<GlobalizationOptions>(args[0]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ try
+ {
+ if (string.IsNullOrEmpty(globalOptions.CurrencyCode))
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ return;
+ }
+
+ string currencyCode = globalOptions.CurrencyCode;
+
+ // temporary not supported via lack of api required
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.INVALID_ACTION, "Not supported"));
+ return;
+
+ // TODO find the way to get currency info from currency code
+ // http://stackoverflow.com/questions/12373800/3-digit-currency-code-to-currency-symbol
+ // http://stackoverflow.com/questions/6924067/how-to-get-specific-culture-currency-pattern
+ // CultureInfo cultureInfo = new CultureInfo(currencyCode);
+ // NumberFormatInfo numberFormat = cultureInfo.NumberFormat;
+ }
+ catch (Exception)
+ {
+ this.DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new GlobalizationError(ErrorCode.FormattingError)));
+ }
+ }
+
+ #endregion
+
+ #region private methods
+
+ /// <summary>
+ /// Wraps data into JSON format
+ /// </summary>
+ /// <param name="data">data</param>
+ /// <returns>data formatted as JSON object</returns>
+ private string WrapIntoJSON<T>(T data, string keyName = "value")
+ {
+ string param = "{0}";
+ string stringifiedData = data.ToString();
+
+ if (data.GetType() == typeof(string))
+ {
+ param = "\"" + param + "\"";
+ }
+
+ if (data.GetType() == typeof(bool))
+ {
+ stringifiedData = stringifiedData.ToLower();
+ }
+
+ if (data.GetType() == typeof(string[]))
+ {
+ stringifiedData = JSON.JsonHelper.Serialize(data);
+ }
+
+ var formattedData = string.Format("\"" + keyName + "\":" + param, stringifiedData);
+ formattedData = "{" + formattedData + "}";
+
+ return formattedData;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f59ddbbd/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs b/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs
new file mode 100644
index 0000000..68ddf87
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/ImageExifHelper.cs
@@ -0,0 +1,209 @@
+/*
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Windows.Media.Imaging;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ public class ImageExifOrientation
+ {
+ public const int Portrait = 1;
+ public const int PortraitUpsideDown = 3;
+ public const int LandscapeLeft = 6;
+ public const int LandscapeRight = 8;
+ }
+
+ public class ImageExifHelper
+ {
+
+ public static Stream RotateStream(Stream stream, int angle)
+ {
+ stream.Position = 0;
+ if (angle % 90 != 0 || angle < 0)
+ {
+ throw new ArgumentException();
+ }
+ if (angle % 360 == 0)
+ {
+ return stream;
+ }
+
+ angle = angle % 360;
+
+ BitmapImage bitmap = new BitmapImage();
+ bitmap.SetSource(stream);
+ WriteableBitmap wbSource = new WriteableBitmap(bitmap);
+
+ WriteableBitmap wbTarget = null;
+
+ int srcPixelWidth = wbSource.PixelWidth;
+ int srcPixelHeight = wbSource.PixelHeight;
+
+ if (angle % 180 == 0)
+ {
+ wbTarget = new WriteableBitmap(srcPixelWidth, srcPixelHeight);
+ }
+ else
+ {
+ wbTarget = new WriteableBitmap(srcPixelHeight, srcPixelWidth);
+ }
+
+ int destPixelWidth = wbTarget.PixelWidth;
+ int[] srcPxls = wbSource.Pixels;
+ int[] destPxls = wbTarget.Pixels;
+
+ // this ugly if/else is to avoid a conditional check for every pixel
+ if (angle == 90)
+ {
+ for (int x = 0; x < srcPixelWidth; x++)
+ {
+ for (int y = 0; y < srcPixelHeight; y++)
+ {
+ destPxls[(srcPixelHeight - y - 1) + (x * destPixelWidth)] = srcPxls[x + y * srcPixelWidth];
+ }
+ }
+ }
+ else if (angle == 180)
+ {
+ for (int x = 0; x < srcPixelWidth; x++)
+ {
+ for (int y = 0; y < srcPixelHeight; y++)
+ {
+ destPxls[(srcPixelWidth - x - 1) + (srcPixelHeight - y - 1) * srcPixelWidth] = srcPxls[x + y * srcPixelWidth];
+ }
+ }
+ }
+ else if (angle == 270)
+ {
+ for (int x = 0; x < srcPixelWidth; x++)
+ {
+ for (int y = 0; y < srcPixelHeight; y++)
+ {
+ destPxls[y + (srcPixelWidth - x - 1) * destPixelWidth] = srcPxls[x + y * srcPixelWidth];
+ }
+ }
+ }
+
+ MemoryStream targetStream = new MemoryStream();
+ wbTarget.SaveJpeg(targetStream, destPixelWidth, wbTarget.PixelHeight, 0, 100);
+ return targetStream;
+ }
+
+ public static int getImageOrientationFromStream(Stream imgStream)
+ {
+
+ // 0xFFD8 : jpgHeader
+ // 0xFFE1 :
+ // 0x???? : length of exif data
+ // 0x????, 0x???? : Chars 'E','x','i','f'
+ // 0x0000 : 2 empty bytes
+ // <== mark beginning of tags SIZE:ID:VALUE
+ // 0x???? : 'II' or 'MM' for Intel or Motorola ( always getting II on my WP7 devices ), determins littleEndian-ness
+ // 0x002A : marker value
+ // 0x???? : offset to the Image File Data
+
+ // XXXX possible space before actual tag data ... we skip to mark + offset
+
+ // 0x???? number of exif tags present
+
+ // make sure we are at the begining
+ imgStream.Seek(0, SeekOrigin.Begin);
+ BinaryReader reader = new BinaryReader(imgStream);
+
+ byte[] jpgHdr = reader.ReadBytes(2); // always (0xFFD8)
+
+ byte start = reader.ReadByte(); // 0xFF
+ byte index = reader.ReadByte(); // 0xE1
+
+ while (start == 0xFF && index != 0xE1) // This never seems to happen, todo: optimize
+ {
+ // Get the data length
+ ushort dLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+ // skip along
+ reader.ReadBytes(dLen - 2);
+ start = reader.ReadByte();
+ index = reader.ReadByte();
+ }
+
+ // It's only success if we found the 0xFFE1 marker
+ if (start != 0xFF || index != 0xE1)
+ {
+ // throw new Exception("Could not find Exif data block");
+ Debug.WriteLine("Did not find EXIF data");
+ return 0;
+ }
+
+ // read 2 byte length of EXIF data
+ ushort exifLen = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+ String exif = ""; // build the string
+ for (var n = 0; n < 4; n++)
+ {
+ exif += reader.ReadChar();
+ }
+ if (exif != "Exif")
+ {
+ // did not find exif data ...
+ Debug.WriteLine("Did not find EXIF data");
+ return 0;
+ }
+
+ // read 2 empty bytes
+ //ushort emptyBytes = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+ reader.ReadBytes(2);
+
+ long headerMark = reader.BaseStream.Position; // where are we now <==
+
+ //bool isLEndian = (reader.ReadChar() + "" + reader.ReadChar()) == "II";
+ reader.ReadBytes(2); // 'II' or 'MM', but we don't care
+
+ if (0x002A != BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+ {
+ Debug.WriteLine("Error in data != 0x002A");
+ return 0;
+ }
+
+ // Get the offset to the IFD (image file directory)
+ ushort imgOffset = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+
+ imgStream.Position = headerMark + imgOffset;
+ ushort tagCount = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+ for (ushort x = 0; x < tagCount; x++)
+ {
+ // Orientation = 0x112, aka 274
+ if (0x112 == BitConverter.ToUInt16(reader.ReadBytes(2), 0))
+ {
+ ushort dType = BitConverter.ToUInt16(reader.ReadBytes(2), 0);
+ // don't care ..
+ uint comps = reader.ReadUInt32();
+ byte[] tagData = reader.ReadBytes(4);
+ int orientation = (int)tagData[0];
+ Debug.WriteLine("orientation = " + orientation.ToString());
+ return orientation;
+ // 6 means rotate clockwise 90 deg
+ // 8 means rotate counter-clockwise 90 deg
+ // 1 means all is good
+ // 3 means flip vertical
+ }
+ // skip to the next item, 12 bytes each
+ reader.BaseStream.Seek(10, SeekOrigin.Current);
+ }
+ return 0;
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f59ddbbd/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs
----------------------------------------------------------------------
diff --git a/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs b/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs
new file mode 100644
index 0000000..2741355
--- /dev/null
+++ b/lib/cordova-wp8/templates/standalone/Plugins/InAppBrowser.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using Microsoft.Phone.Controls;
+using System.Diagnostics;
+using System.Runtime.Serialization;
+using WPCordovaClassLib.Cordova;
+using WPCordovaClassLib.Cordova.Commands;
+using WPCordovaClassLib.Cordova.JSON;
+using Microsoft.Phone.Shell;
+using Microsoft.Phone.Tasks;
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ [DataContract]
+ public class BrowserOptions
+ {
+ [DataMember]
+ public string url;
+
+ [DataMember]
+ public bool isGeolocationEnabled;
+ }
+
+ public class InAppBrowser : BaseCommand
+ {
+
+ private static WebBrowser browser;
+ private static ApplicationBarIconButton backButton;
+ private static ApplicationBarIconButton fwdButton;
+
+ public void open(string options)
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
+ //BrowserOptions opts = JSON.JsonHelper.Deserialize<BrowserOptions>(options);
+ string urlLoc = args[0];
+ string target = args[1];
+ /*
+ _self - opens in the Cordova WebView if url is in the white-list, else it opens in the InAppBrowser
+ _blank - always open in the InAppBrowser
+ _system - always open in the system web browser
+ */
+ switch (target)
+ {
+ case "_blank":
+ ShowInAppBrowser(urlLoc);
+ break;
+ case "_self":
+ ShowCordovaBrowser(urlLoc);
+ break;
+ case "_system":
+ ShowSystemBrowser(urlLoc);
+ break;
+ }
+
+
+ }
+
+ private void ShowCordovaBrowser(string url)
+ {
+ Uri loc = new Uri(url, UriKind.RelativeOrAbsolute);
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+ if (frame != null)
+ {
+ PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+ if (page != null)
+ {
+ CordovaView cView = page.FindName("CordovaView") as CordovaView;
+ if (cView != null)
+ {
+ WebBrowser br = cView.Browser;
+ br.Navigate(loc);
+ }
+ }
+
+ }
+ });
+ }
+
+ private void ShowSystemBrowser(string url)
+ {
+ WebBrowserTask webBrowserTask = new WebBrowserTask();
+ webBrowserTask.Uri = new Uri(url, UriKind.Absolute);
+ webBrowserTask.Show();
+ }
+
+
+ private void ShowInAppBrowser(string url)
+ {
+ Uri loc = new Uri(url);
+
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ if (browser != null)
+ {
+ //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
+ browser.Navigate(loc);
+ }
+ else
+ {
+ PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+ if (frame != null)
+ {
+ PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+
+ string baseImageUrl = "Images/";
+
+ if (page != null)
+ {
+ Grid grid = page.FindName("LayoutRoot") as Grid;
+ if (grid != null)
+ {
+ browser = new WebBrowser();
+ browser.IsScriptEnabled = true;
+ browser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted);
+
+ browser.Navigating += new EventHandler<NavigatingEventArgs>(browser_Navigating);
+ browser.NavigationFailed += new System.Windows.Navigation.NavigationFailedEventHandler(browser_NavigationFailed);
+ browser.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(browser_Navigated);
+ browser.Navigate(loc);
+ //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
+ grid.Children.Add(browser);
+ }
+
+ ApplicationBar bar = new ApplicationBar();
+ bar.BackgroundColor = Colors.Gray;
+ bar.IsMenuEnabled = false;
+
+ backButton = new ApplicationBarIconButton();
+ backButton.Text = "Back";
+
+ backButton.IconUri = new Uri(baseImageUrl + "appbar.back.rest.png", UriKind.Relative);
+ backButton.Click += new EventHandler(backButton_Click);
+ backButton.IsEnabled = false;
+ bar.Buttons.Add(backButton);
+
+
+ fwdButton = new ApplicationBarIconButton();
+ fwdButton.Text = "Forward";
+ fwdButton.IconUri = new Uri(baseImageUrl + "appbar.next.rest.png", UriKind.Relative);
+ fwdButton.Click += new EventHandler(fwdButton_Click);
+ fwdButton.IsEnabled = false;
+ bar.Buttons.Add(fwdButton);
+
+ ApplicationBarIconButton closeBtn = new ApplicationBarIconButton();
+ closeBtn.Text = "Close";
+ closeBtn.IconUri = new Uri(baseImageUrl + "appbar.close.rest.png", UriKind.Relative);
+ closeBtn.Click += new EventHandler(closeBtn_Click);
+ bar.Buttons.Add(closeBtn);
+
+ page.ApplicationBar = bar;
+ }
+
+ }
+ }
+ });
+ }
+
+ void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
+ {
+
+ }
+
+ void fwdButton_Click(object sender, EventArgs e)
+ {
+ if (browser != null)
+ {
+ try
+ {
+ browser.GoForward();
+ //browser.InvokeScript("execScript", "history.forward();");
+ }
+ catch (Exception)
+ {
+
+ }
+ }
+ }
+
+ void backButton_Click(object sender, EventArgs e)
+ {
+ if (browser != null)
+ {
+ try
+ {
+ browser.GoBack();
+ //browser.InvokeScript("execScript", "history.back();");
+ }
+ catch (Exception)
+ {
+
+ }
+ }
+ }
+
+ void closeBtn_Click(object sender, EventArgs e)
+ {
+ this.close();
+ }
+
+
+ public void close(string options = "")
+ {
+ if (browser != null)
+ {
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
+ if (frame != null)
+ {
+ PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
+ if (page != null)
+ {
+ Grid grid = page.FindName("LayoutRoot") as Grid;
+ if (grid != null)
+ {
+ grid.Children.Remove(browser);
+ }
+ page.ApplicationBar = null;
+ }
+ }
+ browser = null;
+ string message = "{\"type\":\"exit\"}";
+ PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+ result.KeepCallback = false;
+ this.DispatchCommandResult(result);
+ });
+ }
+ }
+
+ void browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
+ {
+ if (browser != null)
+ {
+ backButton.IsEnabled = browser.CanGoBack;
+ fwdButton.IsEnabled = browser.CanGoForward;
+
+ }
+ string message = "{\"type\":\"loadstop\", \"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+ PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+ result.KeepCallback = true;
+ this.DispatchCommandResult(result);
+ }
+
+ void browser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
+ {
+ string message = "{\"type\":\"error\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+ PluginResult result = new PluginResult(PluginResult.Status.ERROR, message);
+ result.KeepCallback = true;
+ this.DispatchCommandResult(result);
+ }
+
+ void browser_Navigating(object sender, NavigatingEventArgs e)
+ {
+ string message = "{\"type\":\"loadstart\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
+ PluginResult result = new PluginResult(PluginResult.Status.OK, message);
+ result.KeepCallback = true;
+ this.DispatchCommandResult(result);
+ }
+
+ }
+}