/* * Copyright 2013 Research In Motion Limited. * * 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. */ /* global PluginResult, JNEXT, escape */ var pimContacts, ContactError = require("./ContactError"), ContactName = require("./ContactName"), ContactFindOptions = require("./ContactFindOptions"); function populateSearchFields(fields) { var i, l, key, searchFieldsObject = {}, searchFields = []; for (i = 0, l = fields.length; i < l; i++) { if (fields[i] === "*") { searchFieldsObject[ContactFindOptions.SEARCH_FIELD_GIVEN_NAME] = true; searchFieldsObject[ContactFindOptions.SEARCH_FIELD_FAMILY_NAME] = true; searchFieldsObject[ContactFindOptions.SEARCH_FIELD_PHONE] = true; searchFieldsObject[ContactFindOptions.SEARCH_FIELD_EMAIL] = true; searchFieldsObject[ContactFindOptions.SEARCH_FIELD_ORGANIZATION_NAME] = true; } else if (fields[i] === "displayName" || fields[i] === "name") { searchFieldsObject[ContactFindOptions.SEARCH_FIELD_GIVEN_NAME] = true; searchFieldsObject[ContactFindOptions.SEARCH_FIELD_FAMILY_NAME] = true; } else if (fields[i] === "nickname") { // not supported by Cascades } else if (fields[i] === "phoneNumbers") { searchFieldsObject[ContactFindOptions.SEARCH_FIELD_PHONE] = true; } else if (fields[i] === "emails") { searchFieldsObject[ContactFindOptions.SEARCH_FIELD_EMAIL] = true; } else if (fields[i] === "addresses") { // not supported by Cascades } else if (fields[i] === "ims") { // not supported by Cascades } else if (fields[i] === "organizations") { searchFieldsObject[ContactFindOptions.SEARCH_FIELD_ORGANIZATION_NAME] = true; } else if (fields[i] === "birthday") { // not supported by Cascades } else if (fields[i] === "note") { // not supported by Cascades } else if (fields[i] === "photos") { // not supported by Cascades } else if (fields[i] === "categories") { // not supported by Cascades } else if (fields[i] === "urls") { // not supported by Cascades } } for (key in searchFieldsObject) { if (searchFieldsObject.hasOwnProperty(key)) { searchFields.push(window.parseInt(key)); } } return searchFields; } function convertBirthday(birthday) { //Convert date string from native to milliseconds since epoch for cordova-js var birthdayInfo; if (birthday) { birthdayInfo = birthday.split("-"); return new Date(birthdayInfo[0], birthdayInfo[1] - 1, birthdayInfo[2]).getTime(); } else { return null; } } function processJnextSaveData(result, JnextData) { var data = JnextData; if (data._success === true) { data.birthday = convertBirthday(data.birthday); result.callbackOk(data, false); } else { result.callbackError(data.code, false); } } function processJnextRemoveData(result, JnextData) { var data = JnextData; if (data._success === true) { result.callbackOk(data); } else { result.callbackError(ContactError.UNKNOWN_ERROR, false); } } function processJnextFindData(eventId, eventHandler, JnextData) { var data = JnextData, i, l, more = false, resultsObject = {}; if (data.contacts) { for (i = 0, l = data.contacts.length; i < l; i++) { data.contacts[i].birthday = convertBirthday(data.contacts[i].birthday); data.contacts[i].name = new ContactName(data.contacts[i].name); } } else { data.contacts = []; // if JnextData.contacts return null, return an empty array } if (data._success === true) { eventHandler.error = false; } if (eventHandler.multiple) { // Concatenate results; do not add the same contacts for (i = 0, l = eventHandler.searchResult.length; i < l; i++) { resultsObject[eventHandler.searchResult[i].id] = true; } for (i = 0, l = data.contacts.length; i < l; i++) { if (resultsObject[data.contacts[i].id]) { // Already existing } else { eventHandler.searchResult.push(data.contacts[i]); } } // check if more search is required eventHandler.searchFieldIndex++; if (eventHandler.searchFieldIndex < eventHandler.searchFields.length) { more = true; } } else { eventHandler.searchResult = data.contacts; } if (more) { pimContacts.getInstance().invokeJnextSearch(eventId); } else { if (eventHandler.error) { eventHandler.result.callbackError(data.code, false); } else { eventHandler.result.callbackOk(eventHandler.searchResult, false); } } } module.exports = { search: function (successCb, failCb, args, env) { var cordovaFindOptions = {}, result = new PluginResult(args, env), key; for (key in args) { if (args.hasOwnProperty(key)) { cordovaFindOptions[key] = JSON.parse(decodeURIComponent(args[key])); } } pimContacts.getInstance().find(cordovaFindOptions, result, processJnextFindData); result.noResult(true); }, save: function (successCb, failCb, args, env) { var attributes = {}, result = new PluginResult(args, env), nativeEmails = []; attributes = JSON.parse(decodeURIComponent(args[0])); //convert birthday format for our native .so file if (attributes.birthday) { attributes.birthday = new Date(attributes.birthday).toDateString(); } if (attributes.emails) { attributes.emails.forEach(function (email) { if (email.value) { if (email.type) { nativeEmails.push({ "type" : email.type, "value" : email.value }); } else { nativeEmails.push({ "type" : "home", "value" : email.value }); } } }); attributes.emails = nativeEmails; } if (attributes.id !== null) { attributes.id = window.parseInt(attributes.id); } attributes._eventId = result.callbackId; pimContacts.getInstance().save(attributes, result, processJnextSaveData); result.noResult(true); }, remove: function (successCb, failCb, args, env) { var result = new PluginResult(args, env), attributes = { "contactId": window.parseInt(JSON.parse(decodeURIComponent(args[0]))), "_eventId": result.callbackId }; if (!window.isNaN(attributes.contactId)) { pimContacts.getInstance().remove(attributes, result, processJnextRemoveData); result.noResult(true); } else { result.error(ContactError.UNKNOWN_ERROR); result.noResult(false); } } }; /////////////////////////////////////////////////////////////////// // JavaScript wrapper for JNEXT plugin /////////////////////////////////////////////////////////////////// JNEXT.PimContacts = function () { var self = this, hasInstance = false; self.find = function (cordovaFindOptions, pluginResult, handler) { //register find eventHandler for when JNEXT onEvent fires self.eventHandlers[cordovaFindOptions.callbackId] = { "result" : pluginResult, "action" : "find", "multiple" : cordovaFindOptions[1].filter ? true : false, "fields" : cordovaFindOptions[0], "searchFilter" : cordovaFindOptions[1].filter, "searchFields" : cordovaFindOptions[1].filter ? populateSearchFields(cordovaFindOptions[0]) : null, "searchFieldIndex" : 0, "searchResult" : [], "handler" : handler, "error" : true }; self.invokeJnextSearch(cordovaFindOptions.callbackId); return ""; }; self.invokeJnextSearch = function(eventId) { var jnextArgs = {}, findHandler = self.eventHandlers[eventId]; jnextArgs._eventId = eventId; jnextArgs.fields = findHandler.fields; jnextArgs.options = {}; jnextArgs.options.filter = []; if (findHandler.multiple) { jnextArgs.options.filter.push({ "fieldName" : findHandler.searchFields[findHandler.searchFieldIndex], "fieldValue" : findHandler.searchFilter }); //findHandler.searchFieldIndex++; } JNEXT.invoke(self.m_id, "find " + JSON.stringify(jnextArgs)); }; self.getContact = function (args) { return JSON.parse(JNEXT.invoke(self.m_id, "getContact " + JSON.stringify(args))); }; self.save = function (args, pluginResult, handler) { //register save eventHandler for when JNEXT onEvent fires self.eventHandlers[args._eventId] = { "result" : pluginResult, "action" : "save", "handler" : handler }; JNEXT.invoke(self.m_id, "save " + JSON.stringify(args)); return ""; }; self.remove = function (args, pluginResult, handler) { //register remove eventHandler for when JNEXT onEvent fires self.eventHandlers[args._eventId] = { "result" : pluginResult, "action" : "remove", "handler" : handler }; JNEXT.invoke(self.m_id, "remove " + JSON.stringify(args)); return ""; }; self.getId = function () { return self.m_id; }; self.getContactAccounts = function () { var value = JNEXT.invoke(self.m_id, "getContactAccounts"); return JSON.parse(value); }; self.init = function () { if (!JNEXT.require("libpimcontacts")) { return false; } self.m_id = JNEXT.createObject("libpimcontacts.PimContacts"); if (self.m_id === "") { return false; } JNEXT.registerEvents(self); }; // Handle data coming back from JNEXT native layer. Each async function registers a handler and a PluginResult object. // When JNEXT fires onEvent we parse the result string back into JSON and trigger the appropriate handler (eventHandlers map // uses callbackId as key), along with the actual data coming back from the native layer. Each function may have its own way of // processing native data so we do not do any processing here. self.onEvent = function (strData) { var arData = strData.split(" "), strEventDesc = arData[0], eventHandler, args = {}; if (strEventDesc === "result") { args.result = escape(strData.split(" ").slice(2).join(" ")); eventHandler = self.eventHandlers[arData[1]]; if (eventHandler.action === "save" || eventHandler.action === "remove") { eventHandler.handler(eventHandler.result, JSON.parse(decodeURIComponent(args.result))); } else if (eventHandler.action === "find") { eventHandler.handler(arData[1], eventHandler, JSON.parse(decodeURIComponent(args.result))); } } }; self.m_id = ""; self.eventHandlers = {}; self.getInstance = function () { if (!hasInstance) { self.init(); hasInstance = true; } return self; }; }; pimContacts = new JNEXT.PimContacts();