diff --git a/js/ui/cordova/plugins/cordova-plugin-contacts/src/wp/ContactsHelper.cs b/js/ui/cordova/plugins/cordova-plugin-contacts/src/wp/ContactsHelper.cs
new file mode 100644
index 0000000..80166fa
--- /dev/null
+++ b/js/ui/cordova/plugins/cordova-plugin-contacts/src/wp/ContactsHelper.cs
@@ -0,0 +1,335 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.Phone.UserData;
+ using System.IO;
+
+ ///
+ /// Implements helper functionality to serialize contact to JSON string.
+ ///
+ internal static class ContactsHelper
+ {
+ ///
+ /// Converts Contact object to string representation
+ ///
+ /// Contact object
+ /// array of fields names
+ /// JSON string
+ public static string ToJson(this Contact contact, string[] desiredFields)
+ {
+ var contactFieldsWithJsonVals = contact.PopulateContactDictionary();
+
+ // if desiredFields are not defined, use all avilable fields
+ if (desiredFields == null || desiredFields.Length == 0)
+ {
+ desiredFields = contactFieldsWithJsonVals.Keys.ToArray();
+ }
+
+ return FillResultWithFields(desiredFields, contactFieldsWithJsonVals);
+ }
+
+ ///
+ /// Returns JSON string with desired fields only.
+ ///
+ /// The desired fields.
+ /// The contact fields with JSON values.
+ /// JSON string
+ private static string FillResultWithFields(string[] desiredFields, Dictionary> contactFieldsWithJsonVals)
+ {
+ var result = new StringBuilder();
+ for (int i = 0; i < desiredFields.Count(); i++)
+ {
+ if (!contactFieldsWithJsonVals.ContainsKey(desiredFields[i]))
+ {
+ continue;
+ }
+
+ result.Append(contactFieldsWithJsonVals[desiredFields[i]]());
+ if (i != desiredFields.Count() - 1)
+ {
+ result.Append(",");
+ }
+ }
+
+ return "{" + result + "}";
+ }
+
+ ///
+ /// Populates the contact dictionary.
+ ///
+ /// Contact, that converted to JSON
+ /// JSON string with populated data
+ private static Dictionary> PopulateContactDictionary(this Contact contact)
+ {
+ var contactFieldsJsonValsDictionary = new Dictionary>(StringComparer.InvariantCultureIgnoreCase)
+ {
+ { "id", () => string.Format("\"id\":\"{0}\"", contact.GetHashCode()) },
+ { "displayName", () => string.Format("\"displayName\":\"{0}\"", EscapeJson(contact.DisplayName)) },
+ {
+ "nickname",
+ () =>
+ string.Format(
+ "\"nickname\":\"{0}\"",
+ EscapeJson(contact.CompleteName != null ? contact.CompleteName.Nickname : string.Empty))
+ },
+ { "phoneNumbers", () => string.Format("\"phoneNumbers\":[{0}]", FormatJsonPhoneNumbers(contact)) },
+ { "emails", () => string.Format("\"emails\":[{0}]", FormatJsonEmails(contact)) },
+ { "addresses", () => string.Format("\"addresses\":[{0}]", FormatJsonAddresses(contact)) },
+ { "urls", () => string.Format("\"urls\":[{0}]", FormatJsonWebsites(contact)) },
+ { "photos", () => string.Format("\"photos\":[{0}]", FormatJsonPhotos(contact)) },
+ { "name", () => string.Format("\"name\":{0}", FormatJsonName(contact)) },
+ { "note", () => string.Format("\"note\":\"{0}\"", EscapeJson(contact.Notes.FirstOrDefault())) },
+ {
+ "birthday",
+ () =>
+ string.Format(
+ "\"birthday\":\"{0}\"",
+ EscapeJson(Convert.ToString(contact.Birthdays.FirstOrDefault())))
+ }
+ };
+ return contactFieldsJsonValsDictionary;
+ }
+
+ ///
+ /// Add escape characters to the JSON string.
+ ///
+ /// Input JSON formatted string
+ /// Escaped JSON string
+ private static string EscapeJson(string str)
+ {
+ if (string.IsNullOrEmpty(str))
+ {
+ return str;
+ }
+
+ return str.Replace("\n", "\\n")
+ .Replace("\r", "\\r")
+ .Replace("\t", "\\t")
+ .Replace("\"", "\\\"")
+ .Replace("&", "\\&");
+ }
+
+ ///
+ /// Formats phone numbers to JSON string.
+ ///
+ /// Contact object
+ /// JSON string
+ private static string FormatJsonPhoneNumbers(Contact con)
+ {
+ string retVal = string.Empty;
+ const string ContactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+ foreach (ContactPhoneNumber number in con.PhoneNumbers)
+ {
+ string contactField = string.Format(ContactFieldFormat, number.Kind.ToString(), number.PhoneNumber);
+ retVal += "{" + contactField + "},";
+ }
+
+ return retVal.TrimEnd(',');
+ }
+
+ /*
+ * formatted: The complete name of the contact. (DOMString)
+ familyName: The contacts family name. (DOMString)
+ givenName: The contacts given name. (DOMString)
+ middleName: The contacts middle name. (DOMString)
+ honorificPrefix: The contacts prefix (example Mr. or Dr.) (DOMString)
+ honorificSuffix: The contacts suffix (example Esq.). (DOMString)
+ */
+
+ ///
+ /// Formats the name to JSON string.
+ ///
+ /// Contact object
+ /// JSON string
+ private static string FormatJsonName(Contact con)
+ {
+ string retVal;
+ const string FormatStr = "\"formatted\":\"{0}\"," +
+ "\"familyName\":\"{1}\"," +
+ "\"givenName\":\"{2}\"," +
+ "\"middleName\":\"{3}\"," +
+ "\"honorificPrefix\":\"{4}\"," +
+ "\"honorificSuffix\":\"{5}\"";
+
+ if (con.CompleteName != null)
+ {
+ retVal = string.Format(
+ FormatStr,
+ EscapeJson(con.CompleteName.FirstName + " " + con.CompleteName.LastName),
+ //// TODO: does this need suffix? middlename?
+ EscapeJson(con.CompleteName.LastName),
+ EscapeJson(con.CompleteName.FirstName),
+ EscapeJson(con.CompleteName.MiddleName),
+ EscapeJson(con.CompleteName.Title),
+ EscapeJson(con.CompleteName.Suffix));
+ }
+ else
+ {
+ retVal = string.Format(FormatStr, "", "", "", "", "", "");
+ }
+
+ return "{" + retVal + "}";
+ }
+
+ ///
+ /// Format Emails to JSON string.
+ ///
+ /// Contact object
+ /// JSON string
+ private static string FormatJsonEmails(Contact con)
+ {
+ string retVal = string.Empty;
+ const string ContactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+ foreach (ContactEmailAddress address in con.EmailAddresses)
+ {
+ string contactField = string.Format(
+ ContactFieldFormat,
+ address.Kind.ToString(),
+ EscapeJson(address.EmailAddress));
+
+ retVal += "{" + contactField + "},";
+ }
+
+ return retVal.TrimEnd(',');
+ }
+
+ ///
+ /// Format Addresses to JSON string.
+ ///
+ /// Contact object
+ /// JSON string
+ private static string FormatJsonAddresses(Contact con)
+ {
+ string retVal = string.Empty;
+ foreach (ContactAddress address in con.Addresses)
+ {
+ retVal += GetFormattedJsonAddress(address, false) + ",";
+ }
+
+ return retVal.TrimEnd(',');
+ }
+
+ ///
+ /// Format Websites to JSON string.
+ ///
+ /// Contact object
+ /// JSON string
+ private static string FormatJsonWebsites(Contact con)
+ {
+ string retVal = string.Empty;
+ foreach (string website in con.Websites)
+ {
+ retVal += "\"" + EscapeJson(website) + "\",";
+ }
+
+ return retVal.TrimEnd(',');
+ }
+
+ ///
+ /// Format single address to JSON string.
+ ///
+ ///
+ /// Contact address.
+ ///
+ ///
+ /// Contact is preferred?
+ ///
+ ///
+ /// JSON string
+ ///
+ private static string GetFormattedJsonAddress(ContactAddress address, bool isPrefered)
+ {
+ const string AddressFormatString = "\"pref\":{0}," + // bool
+ "\"type\":\"{1}\"," +
+ "\"formatted\":\"{2}\"," +
+ "\"streetAddress\":\"{3}\"," +
+ "\"locality\":\"{4}\"," +
+ "\"region\":\"{5}\"," +
+ "\"postalCode\":\"{6}\"," +
+ "\"country\":\"{7}\"";
+
+ string formattedAddress = EscapeJson(address.PhysicalAddress.AddressLine1 + " "
+ + address.PhysicalAddress.AddressLine2 + " "
+ + address.PhysicalAddress.City + " "
+ + address.PhysicalAddress.StateProvince + " "
+ + address.PhysicalAddress.CountryRegion + " "
+ + address.PhysicalAddress.PostalCode);
+
+ string jsonAddress = string.Format(
+ AddressFormatString,
+ isPrefered ? "\"true\"" : "\"false\"",
+ address.Kind.ToString(),
+ formattedAddress,
+ EscapeJson(address.PhysicalAddress.AddressLine1 + " " + address.PhysicalAddress.AddressLine2),
+ address.PhysicalAddress.City,
+ address.PhysicalAddress.StateProvince,
+ address.PhysicalAddress.PostalCode,
+ address.PhysicalAddress.CountryRegion);
+
+ return "{" + jsonAddress + "}";
+ }
+
+ ///
+ /// Formats contact photos to JSON string.
+ ///
+ /// Contact object
+ /// JSON string
+ private static string FormatJsonPhotos(Contact con) {
+
+ // we return single photo since contact object instance contains single picture only
+ var photoStream = con.GetPicture();
+
+ if (photoStream == null) {
+ return "";
+ }
+
+ return String.Format("{{value:\"{0}\", type: \"data\", pref: false}}", GetImageContent(photoStream));
+ }
+
+ ///
+ /// Returns image content in a form of base64 string
+ ///
+ /// Image stream
+ /// Base64 representation of the image
+ private static string GetImageContent(Stream stream)
+ {
+ byte[] imageContent = null;
+
+ try
+ {
+ int streamLength = (int)stream.Length;
+ imageContent = new byte[streamLength + 1];
+ stream.Read(imageContent, 0, streamLength);
+
+ }
+ finally
+ {
+ stream.Dispose();
+ }
+
+ return Convert.ToBase64String(imageContent);
+ }
+ }
+}
\ No newline at end of file