You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by an...@apache.org on 2013/07/25 00:39:47 UTC
[07/18] [CB-4341] Adding a fix to make subdirectories work within a
local plugin dependency - Includes the integration of integration specs which
test installation of plugins with dependencies
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/src/ios/CDVContacts.m
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/src/ios/CDVContacts.m b/spec/plugins/Contacts/src/ios/CDVContacts.m
new file mode 100644
index 0000000..3ca3e81
--- /dev/null
+++ b/spec/plugins/Contacts/src/ios/CDVContacts.m
@@ -0,0 +1,593 @@
+/*
+ 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.
+ */
+
+#import "CDVContacts.h"
+#import <UIKit/UIKit.h>
+#import <Cordova/NSArray+Comparisons.h>
+#import <Cordova/NSDictionary+Extensions.h>
+//#import "CDVNotification.h"
+
+@implementation CDVContactsPicker
+
+@synthesize allowsEditing;
+@synthesize callbackId;
+@synthesize options;
+@synthesize pickedContactDictionary;
+
+@end
+@implementation CDVNewContactsController
+
+@synthesize callbackId;
+
+@end
+
+@implementation CDVContacts
+
+// no longer used since code gets AddressBook for each operation.
+// If address book changes during save or remove operation, may get error but not much we can do about it
+// If address book changes during UI creation, display or edit, we don't control any saves so no need for callback
+
+/*void addressBookChanged(ABAddressBookRef addressBook, CFDictionaryRef info, void* context)
+{
+ // note that this function is only called when another AddressBook instance modifies
+ // the address book, not the current one. For example, through an OTA MobileMe sync
+ Contacts* contacts = (Contacts*)context;
+ [contacts addressBookDirty];
+ }*/
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView
+{
+ self = (CDVContacts*)[super initWithWebView:(UIWebView*)theWebView];
+
+ /*if (self) {
+ addressBook = ABAddressBookCreate();
+ ABAddressBookRegisterExternalChangeCallback(addressBook, addressBookChanged, self);
+ }*/
+
+ return self;
+}
+
+// overridden to clean up Contact statics
+- (void)onAppTerminate
+{
+ // NSLog(@"Contacts::onAppTerminate");
+}
+
+// iPhone only method to create a new contact through the GUI
+- (void)newContact:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+
+ CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
+ CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
+
+ [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) {
+ if (addrBook == NULL) {
+ // permission was denied or other error just return (no error callback)
+ return;
+ }
+ CDVNewContactsController* npController = [[CDVNewContactsController alloc] init];
+ npController.addressBook = addrBook; // a CF retaining assign
+ CFRelease(addrBook);
+
+ npController.newPersonViewDelegate = self;
+ npController.callbackId = callbackId;
+
+ UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:npController];
+
+ if ([weakSelf.viewController respondsToSelector:@selector(presentViewController:::)]) {
+ [weakSelf.viewController presentViewController:navController animated:YES completion:nil];
+ } else {
+ [weakSelf.viewController presentModalViewController:navController animated:YES];
+ }
+ }];
+}
+
+- (void)newPersonViewController:(ABNewPersonViewController*)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person
+{
+ ABRecordID recordId = kABRecordInvalidID;
+ CDVNewContactsController* newCP = (CDVNewContactsController*)newPersonViewController;
+ NSString* callbackId = newCP.callbackId;
+
+ if (person != NULL) {
+ // return the contact id
+ recordId = ABRecordGetRecordID(person);
+ }
+
+ if ([newPersonViewController respondsToSelector:@selector(presentingViewController)]) {
+ [[newPersonViewController presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+ } else {
+ [[newPersonViewController parentViewController] dismissModalViewControllerAnimated:YES];
+ }
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:recordId];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+}
+
+- (void)displayContact:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ ABRecordID recordID = [[command.arguments objectAtIndex:0] intValue];
+ NSDictionary* options = [command.arguments objectAtIndex:1 withDefault:[NSNull null]];
+ bool bEdit = [options isKindOfClass:[NSNull class]] ? false : [options existsValue:@"true" forKey:@"allowsEditing"];
+
+ CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
+ CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
+
+ [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) {
+ if (addrBook == NULL) {
+ // permission was denied or other error - return error
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:errCode ? errCode.errorCode:UNKNOWN_ERROR];
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ return;
+ }
+ ABRecordRef rec = ABAddressBookGetPersonWithRecordID(addrBook, recordID);
+
+ if (rec) {
+ CDVDisplayContactViewController* personController = [[CDVDisplayContactViewController alloc] init];
+ personController.displayedPerson = rec;
+ personController.personViewDelegate = self;
+ personController.allowsEditing = NO;
+
+ // create this so DisplayContactViewController will have a "back" button.
+ UIViewController* parentController = [[UIViewController alloc] init];
+ UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:parentController];
+
+ [navController pushViewController:personController animated:YES];
+
+ if ([self.viewController respondsToSelector:@selector(presentViewController:::)]) {
+ [self.viewController presentViewController:navController animated:YES completion:nil];
+ } else {
+ [self.viewController presentModalViewController:navController animated:YES];
+ }
+
+ if (bEdit) {
+ // create the editing controller and push it onto the stack
+ ABPersonViewController* editPersonController = [[ABPersonViewController alloc] init];
+ editPersonController.displayedPerson = rec;
+ editPersonController.personViewDelegate = self;
+ editPersonController.allowsEditing = YES;
+ [navController pushViewController:editPersonController animated:YES];
+ }
+ } else {
+ // no record, return error
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:UNKNOWN_ERROR];
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+ CFRelease(addrBook);
+ }];
+}
+
+- (BOOL)personViewController:(ABPersonViewController*)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person
+ property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue
+{
+ return YES;
+}
+
+- (void)chooseContact:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:[NSNull null]];
+
+ CDVContactsPicker* pickerController = [[CDVContactsPicker alloc] init];
+
+ pickerController.peoplePickerDelegate = self;
+ pickerController.callbackId = callbackId;
+ pickerController.options = options;
+ pickerController.pickedContactDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kABRecordInvalidID], kW3ContactId, nil];
+ pickerController.allowsEditing = (BOOL)[options existsValue : @"true" forKey : @"allowsEditing"];
+
+ if ([self.viewController respondsToSelector:@selector(presentViewController:::)]) {
+ [self.viewController presentViewController:pickerController animated:YES completion:nil];
+ } else {
+ [self.viewController presentModalViewController:pickerController animated:YES];
+ }
+}
+
+- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker
+ shouldContinueAfterSelectingPerson:(ABRecordRef)person
+{
+ CDVContactsPicker* picker = (CDVContactsPicker*)peoplePicker;
+ NSNumber* pickedId = [NSNumber numberWithInt:ABRecordGetRecordID(person)];
+
+ if (picker.allowsEditing) {
+ ABPersonViewController* personController = [[ABPersonViewController alloc] init];
+ personController.displayedPerson = person;
+ personController.personViewDelegate = self;
+ personController.allowsEditing = picker.allowsEditing;
+ // store id so can get info in peoplePickerNavigationControllerDidCancel
+ picker.pickedContactDictionary = [NSDictionary dictionaryWithObjectsAndKeys:pickedId, kW3ContactId, nil];
+
+ [peoplePicker pushViewController:personController animated:YES];
+ } else {
+ // Retrieve and return pickedContact information
+ CDVContact* pickedContact = [[CDVContact alloc] initFromABRecord:(ABRecordRef)person];
+ NSArray* fields = [picker.options objectForKey:@"fields"];
+ NSDictionary* returnFields = [[CDVContact class] calcReturnFields:fields];
+ picker.pickedContactDictionary = [pickedContact toDictionary:returnFields];
+
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:picker.pickedContactDictionary];
+ [self.commandDelegate sendPluginResult:result callbackId:picker.callbackId];
+
+ if ([picker respondsToSelector:@selector(presentingViewController)]) {
+ [[picker presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+ } else {
+ [[picker parentViewController] dismissModalViewControllerAnimated:YES];
+ }
+ }
+ return NO;
+}
+
+- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker
+ shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
+{
+ return YES;
+}
+
+- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController*)peoplePicker
+{
+ // return contactId or invalid if none picked
+ CDVContactsPicker* picker = (CDVContactsPicker*)peoplePicker;
+
+ if (picker.allowsEditing) {
+ // get the info after possible edit
+ // if we got this far, user has already approved/ disapproved addressBook access
+ ABAddressBookRef addrBook = nil;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
+ if (&ABAddressBookCreateWithOptions != NULL) {
+ addrBook = ABAddressBookCreateWithOptions(NULL, NULL);
+ } else
+#endif
+ {
+ // iOS 4 & 5
+ addrBook = ABAddressBookCreate();
+ }
+ ABRecordRef person = ABAddressBookGetPersonWithRecordID(addrBook, [[picker.pickedContactDictionary objectForKey:kW3ContactId] integerValue]);
+ if (person) {
+ CDVContact* pickedContact = [[CDVContact alloc] initFromABRecord:(ABRecordRef)person];
+ NSArray* fields = [picker.options objectForKey:@"fields"];
+ NSDictionary* returnFields = [[CDVContact class] calcReturnFields:fields];
+ picker.pickedContactDictionary = [pickedContact toDictionary:returnFields];
+ }
+ CFRelease(addrBook);
+ }
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:picker.pickedContactDictionary];
+ [self.commandDelegate sendPluginResult:result callbackId:picker.callbackId];
+
+ if ([peoplePicker respondsToSelector:@selector(presentingViewController)]) {
+ [[peoplePicker presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+ } else {
+ [[peoplePicker parentViewController] dismissModalViewControllerAnimated:YES];
+ }
+}
+
+- (void)search:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSArray* fields = [command.arguments objectAtIndex:0];
+ NSDictionary* findOptions = [command.arguments objectAtIndex:1 withDefault:[NSNull null]];
+
+ [self.commandDelegate runInBackground:^{
+ // from Apple: Important You must ensure that an instance of ABAddressBookRef is used by only one thread.
+ // which is why address book is created within the dispatch queue.
+ // more details here: http: //blog.byadrian.net/2012/05/05/ios-addressbook-framework-and-gcd/
+ CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
+ CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
+ // it gets uglier, block within block.....
+ [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) {
+ if (addrBook == NULL) {
+ // permission was denied or other error - return error
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:errCode ? errCode.errorCode:UNKNOWN_ERROR];
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ return;
+ }
+
+ NSArray* foundRecords = nil;
+ // get the findOptions values
+ BOOL multiple = NO; // default is false
+ NSString* filter = nil;
+ if (![findOptions isKindOfClass:[NSNull class]]) {
+ id value = nil;
+ filter = (NSString*)[findOptions objectForKey:@"filter"];
+ value = [findOptions objectForKey:@"multiple"];
+ if ([value isKindOfClass:[NSNumber class]]) {
+ // multiple is a boolean that will come through as an NSNumber
+ multiple = [(NSNumber*)value boolValue];
+ // NSLog(@"multiple is: %d", multiple);
+ }
+ }
+
+ NSDictionary* returnFields = [[CDVContact class] calcReturnFields:fields];
+
+ NSMutableArray* matches = nil;
+ if (!filter || [filter isEqualToString:@""]) {
+ // get all records
+ foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
+ if (foundRecords && ([foundRecords count] > 0)) {
+ // create Contacts and put into matches array
+ // doesn't make sense to ask for all records when multiple == NO but better check
+ int xferCount = multiple == YES ? [foundRecords count] : 1;
+ matches = [NSMutableArray arrayWithCapacity:xferCount];
+
+ for (int k = 0; k < xferCount; k++) {
+ CDVContact* xferContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:k]];
+ [matches addObject:xferContact];
+ xferContact = nil;
+ }
+ }
+ } else {
+ foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
+ matches = [NSMutableArray arrayWithCapacity:1];
+ BOOL bFound = NO;
+ int testCount = [foundRecords count];
+
+ for (int j = 0; j < testCount; j++) {
+ CDVContact* testContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:j]];
+ if (testContact) {
+ bFound = [testContact foundValue:filter inFields:returnFields];
+ if (bFound) {
+ [matches addObject:testContact];
+ }
+ testContact = nil;
+ }
+ }
+ }
+ NSMutableArray* returnContacts = [NSMutableArray arrayWithCapacity:1];
+
+ if ((matches != nil) && ([matches count] > 0)) {
+ // convert to JS Contacts format and return in callback
+ // - returnFields determines what properties to return
+ @autoreleasepool {
+ int count = multiple == YES ? [matches count] : 1;
+
+ for (int i = 0; i < count; i++) {
+ CDVContact* newContact = [matches objectAtIndex:i];
+ NSDictionary* aContact = [newContact toDictionary:returnFields];
+ [returnContacts addObject:aContact];
+ }
+ }
+ }
+ // return found contacts (array is empty if no contacts found)
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:returnContacts];
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ // NSLog(@"findCallback string: %@", jsString);
+
+ if (addrBook) {
+ CFRelease(addrBook);
+ }
+ }];
+ }]; // end of workQueue block
+
+ return;
+}
+
+- (void)save:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSDictionary* contactDict = [command.arguments objectAtIndex:0];
+
+ [self.commandDelegate runInBackground:^{
+ CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
+ CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
+
+ [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errorCode) {
+ CDVPluginResult* result = nil;
+ if (addrBook == NULL) {
+ // permission was denied or other error - return error
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode ? errorCode.errorCode:UNKNOWN_ERROR];
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ return;
+ }
+
+ bool bIsError = FALSE, bSuccess = FALSE;
+ BOOL bUpdate = NO;
+ CDVContactError errCode = UNKNOWN_ERROR;
+ CFErrorRef error;
+ NSNumber* cId = [contactDict valueForKey:kW3ContactId];
+ CDVContact* aContact = nil;
+ ABRecordRef rec = nil;
+ if (cId && ![cId isKindOfClass:[NSNull class]]) {
+ rec = ABAddressBookGetPersonWithRecordID(addrBook, [cId intValue]);
+ if (rec) {
+ aContact = [[CDVContact alloc] initFromABRecord:rec];
+ bUpdate = YES;
+ }
+ }
+ if (!aContact) {
+ aContact = [[CDVContact alloc] init];
+ }
+
+ bSuccess = [aContact setFromContactDict:contactDict asUpdate:bUpdate];
+ if (bSuccess) {
+ if (!bUpdate) {
+ bSuccess = ABAddressBookAddRecord(addrBook, [aContact record], &error);
+ }
+ if (bSuccess) {
+ bSuccess = ABAddressBookSave(addrBook, &error);
+ }
+ if (!bSuccess) { // need to provide error codes
+ bIsError = TRUE;
+ errCode = IO_ERROR;
+ } else {
+ // give original dictionary back? If generate dictionary from saved contact, have no returnFields specified
+ // so would give back all fields (which W3C spec. indicates is not desired)
+ // for now (while testing) give back saved, full contact
+ NSDictionary* newContact = [aContact toDictionary:[CDVContact defaultFields]];
+ // NSString* contactStr = [newContact JSONRepresentation];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newContact];
+ }
+ } else {
+ bIsError = TRUE;
+ errCode = IO_ERROR;
+ }
+ CFRelease(addrBook);
+
+ if (bIsError) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+ }
+
+ if (result) {
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+ }];
+ }]; // end of queue
+}
+
+- (void)remove:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSNumber* cId = [command.arguments objectAtIndex:0];
+
+ CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
+ CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
+
+ [abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errorCode) {
+ CDVPluginResult* result = nil;
+ if (addrBook == NULL) {
+ // permission was denied or other error - return error
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode ? errorCode.errorCode:UNKNOWN_ERROR];
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ return;
+ }
+
+ bool bIsError = FALSE, bSuccess = FALSE;
+ CDVContactError errCode = UNKNOWN_ERROR;
+ CFErrorRef error;
+ ABRecordRef rec = nil;
+ if (cId && ![cId isKindOfClass:[NSNull class]] && ([cId intValue] != kABRecordInvalidID)) {
+ rec = ABAddressBookGetPersonWithRecordID(addrBook, [cId intValue]);
+ if (rec) {
+ bSuccess = ABAddressBookRemoveRecord(addrBook, rec, &error);
+ if (!bSuccess) {
+ bIsError = TRUE;
+ errCode = IO_ERROR;
+ } else {
+ bSuccess = ABAddressBookSave(addrBook, &error);
+ if (!bSuccess) {
+ bIsError = TRUE;
+ errCode = IO_ERROR;
+ } else {
+ // set id to null
+ // [contactDict setObject:[NSNull null] forKey:kW3ContactId];
+ // result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary: contactDict];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ // NSString* contactStr = [contactDict JSONRepresentation];
+ }
+ }
+ } else {
+ // no record found return error
+ bIsError = TRUE;
+ errCode = UNKNOWN_ERROR;
+ }
+ } else {
+ // invalid contact id provided
+ bIsError = TRUE;
+ errCode = INVALID_ARGUMENT_ERROR;
+ }
+
+ if (addrBook) {
+ CFRelease(addrBook);
+ }
+ if (bIsError) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+ }
+ if (result) {
+ [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+ }];
+ return;
+}
+
+@end
+
+/* ABPersonViewController does not have any UI to dismiss. Adding navigationItems to it does not work properly
+ * The navigationItems are lost when the app goes into the background. The solution was to create an empty
+ * NavController in front of the ABPersonViewController. This will cause the ABPersonViewController to have a back button. By subclassing the ABPersonViewController, we can override viewDidDisappear and take down the entire NavigationController.
+ */
+@implementation CDVDisplayContactViewController
+@synthesize contactsPlugin;
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+ [super viewWillDisappear:animated];
+
+ if ([self respondsToSelector:@selector(presentingViewController)]) {
+ [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+ } else {
+ [[self parentViewController] dismissModalViewControllerAnimated:YES];
+ }
+}
+
+@end
+@implementation CDVAddressBookAccessError
+
+@synthesize errorCode;
+
+- (CDVAddressBookAccessError*)initWithCode:(CDVContactError)code
+{
+ self = [super init];
+ if (self) {
+ self.errorCode = code;
+ }
+ return self;
+}
+
+@end
+
+@implementation CDVAddressBookHelper
+
+/**
+ * NOTE: workerBlock is responsible for releasing the addressBook that is passed to it
+ */
+- (void)createAddressBook:(CDVAddressBookWorkerBlock)workerBlock
+{
+ // TODO: this probably should be reworked - seems like the workerBlock can just create and release its own AddressBook,
+ // and also this important warning from (http://developer.apple.com/library/ios/#documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Chapters/BasicObjects.html):
+ // "Important: Instances of ABAddressBookRef cannot be used by multiple threads. Each thread must make its own instance."
+ ABAddressBookRef addressBook;
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
+ if (&ABAddressBookCreateWithOptions != NULL) {
+ CFErrorRef error = nil;
+ // CFIndex status = ABAddressBookGetAuthorizationStatus();
+ addressBook = ABAddressBookCreateWithOptions(NULL, &error);
+ // NSLog(@"addressBook access: %lu", status);
+ ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
+ // callback can occur in background, address book must be accessed on thread it was created on
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ if (error) {
+ workerBlock(NULL, [[CDVAddressBookAccessError alloc] initWithCode:UNKNOWN_ERROR]);
+ } else if (!granted) {
+ workerBlock(NULL, [[CDVAddressBookAccessError alloc] initWithCode:PERMISSION_DENIED_ERROR]);
+ } else {
+ // access granted
+ workerBlock(addressBook, [[CDVAddressBookAccessError alloc] initWithCode:UNKNOWN_ERROR]);
+ }
+ });
+ });
+ } else
+#endif
+ {
+ // iOS 4 or 5 no checks needed
+ addressBook = ABAddressBookCreate();
+ workerBlock(addressBook, NULL);
+ }
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/src/wp/Contacts.cs
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/src/wp/Contacts.cs b/spec/plugins/Contacts/src/wp/Contacts.cs
new file mode 100644
index 0000000..af78942
--- /dev/null
+++ b/spec/plugins/Contacts/src/wp/Contacts.cs
@@ -0,0 +1,664 @@
+/*
+ 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 Microsoft.Phone.Tasks;
+using Microsoft.Phone.UserData;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Windows;
+using DeviceContacts = Microsoft.Phone.UserData.Contacts;
+
+
+namespace WPCordovaClassLib.Cordova.Commands
+{
+ [DataContract]
+ public class SearchOptions
+ {
+ [DataMember]
+ public string filter { get; set; }
+ [DataMember]
+ public bool multiple { get; set; }
+ }
+
+ [DataContract]
+ public class ContactSearchParams
+ {
+ [DataMember]
+ public string[] fields { get; set; }
+ [DataMember]
+ public SearchOptions options { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactAddress
+ {
+ [DataMember]
+ public string formatted { get; set; }
+ [DataMember]
+ public string type { get; set; }
+ [DataMember]
+ public string streetAddress { get; set; }
+ [DataMember]
+ public string locality { get; set; }
+ [DataMember]
+ public string region { get; set; }
+ [DataMember]
+ public string postalCode { get; set; }
+ [DataMember]
+ public string country { get; set; }
+ [DataMember]
+ public bool pref { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactName
+ {
+ [DataMember]
+ public string formatted { get; set; }
+ [DataMember]
+ public string familyName { get; set; }
+ [DataMember]
+ public string givenName { get; set; }
+ [DataMember]
+ public string middleName { get; set; }
+ [DataMember]
+ public string honorificPrefix { get; set; }
+ [DataMember]
+ public string honorificSuffix { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactField
+ {
+ [DataMember]
+ public string type { get; set; }
+ [DataMember]
+ public string value { get; set; }
+ [DataMember]
+ public bool pref { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContactOrganization
+ {
+ [DataMember]
+ public string type { get; set; }
+ [DataMember]
+ public string name { get; set; }
+ [DataMember]
+ public bool pref { get; set; }
+ [DataMember]
+ public string department { get; set; }
+ [DataMember]
+ public string title { get; set; }
+ }
+
+ [DataContract]
+ public class JSONContact
+ {
+ [DataMember]
+ public string id { get; set; }
+ [DataMember]
+ public string rawId { get; set; }
+ [DataMember]
+ public string displayName { get; set; }
+ [DataMember]
+ public string nickname { get; set; }
+ [DataMember]
+ public string note { get; set; }
+
+ [DataMember]
+ public JSONContactName name { get; set; }
+
+ [DataMember]
+ public JSONContactField[] emails { get; set; }
+
+ [DataMember]
+ public JSONContactField[] phoneNumbers { get; set; }
+
+ [DataMember]
+ public JSONContactField[] ims { get; set; }
+
+ [DataMember]
+ public JSONContactField[] photos { get; set; }
+
+ [DataMember]
+ public JSONContactField[] categories { get; set; }
+
+ [DataMember]
+ public JSONContactField[] urls { get; set; }
+
+ [DataMember]
+ public JSONContactOrganization[] organizations { get; set; }
+
+ [DataMember]
+ public JSONContactAddress[] addresses { get; set; }
+ }
+
+
+ public class Contacts : BaseCommand
+ {
+
+ public const int UNKNOWN_ERROR = 0;
+ public const int INVALID_ARGUMENT_ERROR = 1;
+ public const int TIMEOUT_ERROR = 2;
+ public const int PENDING_OPERATION_ERROR = 3;
+ public const int IO_ERROR = 4;
+ public const int NOT_SUPPORTED_ERROR = 5;
+ public const int PERMISSION_DENIED_ERROR = 20;
+ public const int SYNTAX_ERR = 8;
+
+ public Contacts()
+ {
+
+ }
+
+ // refer here for contact properties we can access: http://msdn.microsoft.com/en-us/library/microsoft.phone.tasks.savecontacttask_members%28v=VS.92%29.aspx
+ public void save(string jsonContact)
+ {
+
+ // jsonContact is actually an array of 1 {contact}
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(jsonContact);
+
+
+ JSONContact contact = JSON.JsonHelper.Deserialize<JSONContact>(args[0]);
+
+ SaveContactTask contactTask = new SaveContactTask();
+
+ if (contact.nickname != null)
+ {
+ contactTask.Nickname = contact.nickname;
+ }
+ if (contact.urls != null && contact.urls.Length > 0)
+ {
+ contactTask.Website = contact.urls[0].value;
+ }
+ if (contact.note != null)
+ {
+ contactTask.Notes = contact.note;
+ }
+
+ #region contact.name
+ if (contact.name != null)
+ {
+ if (contact.name.givenName != null)
+ contactTask.FirstName = contact.name.givenName;
+ if (contact.name.familyName != null)
+ contactTask.LastName = contact.name.familyName;
+ if (contact.name.middleName != null)
+ contactTask.MiddleName = contact.name.middleName;
+ if (contact.name.honorificSuffix != null)
+ contactTask.Suffix = contact.name.honorificSuffix;
+ if (contact.name.honorificPrefix != null)
+ contactTask.Title = contact.name.honorificPrefix;
+ }
+ #endregion
+
+ #region contact.org
+ if (contact.organizations != null && contact.organizations.Count() > 0)
+ {
+ contactTask.Company = contact.organizations[0].name;
+ contactTask.JobTitle = contact.organizations[0].title;
+ }
+ #endregion
+
+ #region contact.phoneNumbers
+ if (contact.phoneNumbers != null && contact.phoneNumbers.Length > 0)
+ {
+ foreach (JSONContactField field in contact.phoneNumbers)
+ {
+ string fieldType = field.type.ToLower();
+ if (fieldType == "work")
+ {
+ contactTask.WorkPhone = field.value;
+ }
+ else if (fieldType == "home")
+ {
+ contactTask.HomePhone = field.value;
+ }
+ else if (fieldType == "mobile")
+ {
+ contactTask.MobilePhone = field.value;
+ }
+ }
+ }
+ #endregion
+
+ #region contact.emails
+
+ if (contact.emails != null && contact.emails.Length > 0)
+ {
+
+ // set up different email types if they are not explicitly defined
+ foreach (string type in new string[] { "personal", "work", "other" })
+ {
+ foreach (JSONContactField field in contact.emails)
+ {
+ if (field != null && String.IsNullOrEmpty(field.type))
+ {
+ field.type = type;
+ break;
+ }
+ }
+ }
+
+ foreach (JSONContactField field in contact.emails)
+ {
+ if (field != null)
+ {
+ if (field.type != null && field.type != "other")
+ {
+ string fieldType = field.type.ToLower();
+ if (fieldType == "work")
+ {
+ contactTask.WorkEmail = field.value;
+ }
+ else if (fieldType == "home" || fieldType == "personal")
+ {
+ contactTask.PersonalEmail = field.value;
+ }
+ }
+ else
+ {
+ contactTask.OtherEmail = field.value;
+ }
+ }
+
+ }
+ }
+ #endregion
+
+ if (contact.note != null && contact.note.Length > 0)
+ {
+ contactTask.Notes = contact.note;
+ }
+
+ #region contact.addresses
+ if (contact.addresses != null && contact.addresses.Length > 0)
+ {
+ foreach (JSONContactAddress address in contact.addresses)
+ {
+ if (address.type == null)
+ {
+ address.type = "home"; // set a default
+ }
+ string fieldType = address.type.ToLower();
+ if (fieldType == "work")
+ {
+ contactTask.WorkAddressCity = address.locality;
+ contactTask.WorkAddressCountry = address.country;
+ contactTask.WorkAddressState = address.region;
+ contactTask.WorkAddressStreet = address.streetAddress;
+ contactTask.WorkAddressZipCode = address.postalCode;
+ }
+ else if (fieldType == "home" || fieldType == "personal")
+ {
+ contactTask.HomeAddressCity = address.locality;
+ contactTask.HomeAddressCountry = address.country;
+ contactTask.HomeAddressState = address.region;
+ contactTask.HomeAddressStreet = address.streetAddress;
+ contactTask.HomeAddressZipCode = address.postalCode;
+ }
+ else
+ {
+ // no other address fields available ...
+ Debug.WriteLine("Creating contact with unsupported address type :: " + address.type);
+ }
+ }
+ }
+ #endregion
+
+
+ contactTask.Completed += new EventHandler<SaveContactResult>(ContactSaveTaskCompleted);
+ contactTask.Show();
+ }
+
+ void ContactSaveTaskCompleted(object sender, SaveContactResult e)
+ {
+ SaveContactTask task = sender as SaveContactTask;
+
+ if (e.TaskResult == TaskResult.OK)
+ {
+
+ Deployment.Current.Dispatcher.BeginInvoke(() =>
+ {
+ DeviceContacts deviceContacts = new DeviceContacts();
+ deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(postAdd_SearchCompleted);
+
+ string displayName = String.Format("{0}{2}{1}", task.FirstName, task.LastName, String.IsNullOrEmpty(task.FirstName) ? "" : " ");
+
+ deviceContacts.SearchAsync(displayName, FilterKind.DisplayName, task);
+ });
+
+
+ }
+ else if (e.TaskResult == TaskResult.Cancel)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Operation cancelled."));
+ }
+ }
+
+ void postAdd_SearchCompleted(object sender, ContactsSearchEventArgs e)
+ {
+ if (e.Results.Count() > 0)
+ {
+ List<Contact> foundContacts = new List<Contact>();
+
+ int n = (from Contact contact in e.Results select contact.GetHashCode()).Max();
+ Contact newContact = (from Contact contact in e.Results
+ where contact.GetHashCode() == n
+ select contact).First();
+
+ DispatchCommandResult(new PluginResult(PluginResult.Status.OK, FormatJSONContact(newContact, null)));
+ }
+ else
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
+ }
+ }
+
+
+
+ public void remove(string id)
+ {
+ // note id is wrapped in [] and always has exactly one string ...
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "{\"code\":" + NOT_SUPPORTED_ERROR + "}"));
+ }
+
+ public void search(string searchCriteria)
+ {
+ string[] args = JSON.JsonHelper.Deserialize<string[]>(searchCriteria);
+
+ ContactSearchParams searchParams = new ContactSearchParams();
+ try
+ {
+ searchParams.fields = JSON.JsonHelper.Deserialize<string[]>(args[0]);
+ searchParams.options = JSON.JsonHelper.Deserialize<SearchOptions>(args[1]);
+ }
+ catch (Exception)
+ {
+ DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, INVALID_ARGUMENT_ERROR));
+ return;
+ }
+
+ if (searchParams.options == null)
+ {
+ searchParams.options = new SearchOptions();
+ searchParams.options.filter = "";
+ searchParams.options.multiple = true;
+ }
+
+ DeviceContacts deviceContacts = new DeviceContacts();
+ deviceContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(contacts_SearchCompleted);
+
+ // default is to search all fields
+ FilterKind filterKind = FilterKind.None;
+ // if only one field is specified, we will try the 3 available DeviceContact search filters
+ if (searchParams.fields.Count() == 1)
+ {
+ if (searchParams.fields.Contains("name"))
+ {
+ filterKind = FilterKind.DisplayName;
+ }
+ else if (searchParams.fields.Contains("emails"))
+ {
+ filterKind = FilterKind.EmailAddress;
+ }
+ else if (searchParams.fields.Contains("phoneNumbers"))
+ {
+ filterKind = FilterKind.PhoneNumber;
+ }
+ }
+
+ try
+ {
+
+ deviceContacts.SearchAsync(searchParams.options.filter, filterKind, searchParams);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("search contacts exception :: " + ex.Message);
+ }
+ }
+
+ private void contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
+ {
+ ContactSearchParams searchParams = (ContactSearchParams)e.State;
+
+ List<Contact> foundContacts = null;
+
+ // if we have multiple search fields
+ if (searchParams.options.filter.Length > 0 && searchParams.fields.Count() > 1)
+ {
+ foundContacts = new List<Contact>();
+ if (searchParams.fields.Contains("emails"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ from ContactEmailAddress a in con.EmailAddresses
+ where a.EmailAddress.Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("displayName"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ where con.DisplayName.Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("name"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ where con.CompleteName != null && con.CompleteName.ToString().Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("phoneNumbers"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ from ContactPhoneNumber a in con.PhoneNumbers
+ where a.PhoneNumber.Contains(searchParams.options.filter)
+ select con);
+ }
+ if (searchParams.fields.Contains("urls"))
+ {
+ foundContacts.AddRange(from Contact con in e.Results
+ from string a in con.Websites
+ where a.Contains(searchParams.options.filter)
+ select con);
+ }
+ }
+ else
+ {
+ foundContacts = new List<Contact>(e.Results);
+ }
+
+ //List<string> contactList = new List<string>();
+
+ string strResult = "";
+
+ IEnumerable<Contact> distinctContacts = foundContacts.Distinct();
+
+ foreach (Contact contact in distinctContacts)
+ {
+ strResult += FormatJSONContact(contact, null) + ",";
+ //contactList.Add(FormatJSONContact(contact, null));
+ if (!searchParams.options.multiple)
+ {
+ break; // just return the first item
+ }
+ }
+ PluginResult result = new PluginResult(PluginResult.Status.OK);
+ result.Message = "[" + strResult.TrimEnd(',') + "]";
+ DispatchCommandResult(result);
+
+ }
+
+ private string FormatJSONPhoneNumbers(Contact con)
+ {
+ string retVal = "";
+ 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(',');
+ }
+
+ private string FormatJSONEmails(Contact con)
+ {
+ string retVal = "";
+ string contactFieldFormat = "\"type\":\"{0}\",\"value\":\"{1}\",\"pref\":\"false\"";
+ foreach (ContactEmailAddress address in con.EmailAddresses)
+ {
+ string contactField = string.Format(contactFieldFormat,
+ address.Kind.ToString(),
+ address.EmailAddress);
+
+ retVal += "{" + contactField + "},";
+ }
+ return retVal.TrimEnd(',');
+ }
+
+ private string getFormattedJSONAddress(ContactAddress address, bool isPrefered)
+ {
+
+ string addressFormatString = "\"pref\":{0}," + // bool
+ "\"type\":\"{1}\"," +
+ "\"formatted\":\"{2}\"," +
+ "\"streetAddress\":\"{3}\"," +
+ "\"locality\":\"{4}\"," +
+ "\"region\":\"{5}\"," +
+ "\"postalCode\":\"{6}\"," +
+ "\"country\":\"{7}\"";
+
+ string formattedAddress = 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,
+ address.PhysicalAddress.AddressLine1 + " " + address.PhysicalAddress.AddressLine2,
+ address.PhysicalAddress.City,
+ address.PhysicalAddress.StateProvince,
+ address.PhysicalAddress.PostalCode,
+ address.PhysicalAddress.CountryRegion);
+
+ //Debug.WriteLine("getFormattedJSONAddress returning :: " + jsonAddress);
+
+ return "{" + jsonAddress + "}";
+ }
+
+ private string FormatJSONAddresses(Contact con)
+ {
+ string retVal = "";
+ foreach (ContactAddress address in con.Addresses)
+ {
+ retVal += this.getFormattedJSONAddress(address, false) + ",";
+ }
+
+ //Debug.WriteLine("FormatJSONAddresses returning :: " + retVal);
+ return retVal.TrimEnd(',');
+ }
+
+ private string FormatJSONWebsites(Contact con)
+ {
+ string retVal = "";
+ foreach (string website in con.Websites)
+ {
+ retVal += "\"" + website + "\",";
+ }
+ 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)
+ */
+ private string FormatJSONName(Contact con)
+ {
+ string retVal = "";
+ string formatStr = "\"formatted\":\"{0}\"," +
+ "\"familyName\":\"{1}\"," +
+ "\"givenName\":\"{2}\"," +
+ "\"middleName\":\"{3}\"," +
+ "\"honorificPrefix\":\"{4}\"," +
+ "\"honorificSuffix\":\"{5}\"";
+
+ if (con.CompleteName != null)
+ {
+ retVal = string.Format(formatStr,
+ con.CompleteName.FirstName + " " + con.CompleteName.LastName, // TODO: does this need suffix? middlename?
+ con.CompleteName.LastName,
+ con.CompleteName.FirstName,
+ con.CompleteName.MiddleName,
+ con.CompleteName.Title,
+ con.CompleteName.Suffix);
+ }
+ else
+ {
+ retVal = string.Format(formatStr,"","","","","","");
+ }
+
+ return "{" + retVal + "}";
+ }
+
+ private string FormatJSONContact(Contact con, string[] fields)
+ {
+
+ string contactFormatStr = "\"id\":\"{0}\"," +
+ "\"displayName\":\"{1}\"," +
+ "\"nickname\":\"{2}\"," +
+ "\"phoneNumbers\":[{3}]," +
+ "\"emails\":[{4}]," +
+ "\"addresses\":[{5}]," +
+ "\"urls\":[{6}]," +
+ "\"name\":{7}," +
+ "\"note\":\"{8}\"," +
+ "\"birthday\":\"{9}\"";
+
+
+ string jsonContact = String.Format(contactFormatStr,
+ con.GetHashCode(),
+ con.DisplayName,
+ con.CompleteName != null ? con.CompleteName.Nickname : "",
+ FormatJSONPhoneNumbers(con),
+ FormatJSONEmails(con),
+ FormatJSONAddresses(con),
+ FormatJSONWebsites(con),
+ FormatJSONName(con),
+ con.Notes.FirstOrDefault(),
+ con.Birthdays.FirstOrDefault());
+
+ //Debug.WriteLine("jsonContact = " + jsonContact);
+ // JSON requires new line characters be escaped
+ return "{" + jsonContact.Replace("\n", "\\n") + "}";
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/Contact.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/Contact.js b/spec/plugins/Contacts/www/Contact.js
new file mode 100644
index 0000000..9c46a0c
--- /dev/null
+++ b/spec/plugins/Contacts/www/Contact.js
@@ -0,0 +1,177 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+var argscheck = require('cordova/argscheck'),
+ exec = require('cordova/exec'),
+ ContactError = require('./ContactError'),
+ utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+ var value = contact.birthday;
+ try {
+ contact.birthday = new Date(parseFloat(value));
+ } catch (exception){
+ console.log("Cordova Contact convertIn error: exception creating date.");
+ }
+ return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+ var value = contact.birthday;
+ if (value !== null) {
+ // try to make it a Date object if it is not already
+ if (!utils.isDate(value)){
+ try {
+ value = new Date(value);
+ } catch(exception){
+ value = null;
+ }
+ }
+ if (utils.isDate(value)){
+ value = value.valueOf(); // convert to milliseconds
+ }
+ contact.birthday = value;
+ }
+ return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.<ContactField>} phoneNumbers array of phone numbers
+* @param {Array.<ContactField>} emails array of email addresses
+* @param {Array.<ContactAddress>} addresses array of addresses
+* @param {Array.<ContactField>} ims instant messaging user ids
+* @param {Array.<ContactOrganization>} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.<ContactField>} photos
+* @param {Array.<ContactField>} categories
+* @param {Array.<ContactField>} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+ ims, organizations, birthday, note, photos, categories, urls) {
+ this.id = id || null;
+ this.rawId = null;
+ this.displayName = displayName || null;
+ this.name = name || null; // ContactName
+ this.nickname = nickname || null;
+ this.phoneNumbers = phoneNumbers || null; // ContactField[]
+ this.emails = emails || null; // ContactField[]
+ this.addresses = addresses || null; // ContactAddress[]
+ this.ims = ims || null; // ContactField[]
+ this.organizations = organizations || null; // ContactOrganization[]
+ this.birthday = birthday || null;
+ this.note = note || null;
+ this.photos = photos || null; // ContactField[]
+ this.categories = categories || null; // ContactField[]
+ this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+ argscheck.checkArgs('FF', 'Contact.remove', arguments);
+ var fail = errorCB && function(code) {
+ errorCB(new ContactError(code));
+ };
+ if (this.id === null) {
+ fail(ContactError.UNKNOWN_ERROR);
+ }
+ else {
+ exec(successCB, fail, "Contacts", "remove", [this.id]);
+ }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+ var clonedContact = utils.clone(this);
+ clonedContact.id = null;
+ clonedContact.rawId = null;
+
+ function nullIds(arr) {
+ if (arr) {
+ for (var i = 0; i < arr.length; ++i) {
+ arr[i].id = null;
+ }
+ }
+ }
+
+ // Loop through and clear out any id's in phones, emails, etc.
+ nullIds(clonedContact.phoneNumbers);
+ nullIds(clonedContact.emails);
+ nullIds(clonedContact.addresses);
+ nullIds(clonedContact.ims);
+ nullIds(clonedContact.organizations);
+ nullIds(clonedContact.categories);
+ nullIds(clonedContact.photos);
+ nullIds(clonedContact.urls);
+ return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+ argscheck.checkArgs('FFO', 'Contact.save', arguments);
+ var fail = errorCB && function(code) {
+ errorCB(new ContactError(code));
+ };
+ var success = function(result) {
+ if (result) {
+ if (successCB) {
+ var fullContact = require('./contacts').create(result);
+ successCB(convertIn(fullContact));
+ }
+ }
+ else {
+ // no Entry object returned
+ fail(ContactError.UNKNOWN_ERROR);
+ }
+ };
+ var dupContact = convertOut(utils.clone(this));
+ exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ContactAddress.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ContactAddress.js b/spec/plugins/Contacts/www/ContactAddress.js
new file mode 100644
index 0000000..3d39086
--- /dev/null
+++ b/spec/plugins/Contacts/www/ContactAddress.js
@@ -0,0 +1,46 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+ this.id = null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+ this.type = type || null;
+ this.formatted = formatted || null;
+ this.streetAddress = streetAddress || null;
+ this.locality = locality || null;
+ this.region = region || null;
+ this.postalCode = postalCode || null;
+ this.country = country || null;
+};
+
+module.exports = ContactAddress;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ContactError.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ContactError.js b/spec/plugins/Contacts/www/ContactError.js
new file mode 100644
index 0000000..01b229a
--- /dev/null
+++ b/spec/plugins/Contacts/www/ContactError.js
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * ContactError.
+ * An error code assigned by an implementation when an error has occurred
+ * @constructor
+ */
+var ContactError = function(err) {
+ this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ContactField.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ContactField.js b/spec/plugins/Contacts/www/ContactField.js
new file mode 100644
index 0000000..e84107a
--- /dev/null
+++ b/spec/plugins/Contacts/www/ContactField.js
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+ this.id = null;
+ this.type = (type && type.toString()) || null;
+ this.value = (value && value.toString()) || null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ContactFindOptions.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ContactFindOptions.js b/spec/plugins/Contacts/www/ContactFindOptions.js
new file mode 100644
index 0000000..bd8bf35
--- /dev/null
+++ b/spec/plugins/Contacts/www/ContactFindOptions.js
@@ -0,0 +1,34 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+ this.filter = filter || '';
+ this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ContactName.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ContactName.js b/spec/plugins/Contacts/www/ContactName.js
new file mode 100644
index 0000000..15cf60b
--- /dev/null
+++ b/spec/plugins/Contacts/www/ContactName.js
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+ this.formatted = formatted || null;
+ this.familyName = familyName || null;
+ this.givenName = givenName || null;
+ this.middleName = middle || null;
+ this.honorificPrefix = prefix || null;
+ this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ContactOrganization.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ContactOrganization.js b/spec/plugins/Contacts/www/ContactOrganization.js
new file mode 100644
index 0000000..5dd242b
--- /dev/null
+++ b/spec/plugins/Contacts/www/ContactOrganization.js
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+ this.id = null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+ this.type = type || null;
+ this.name = name || null;
+ this.department = dept || null;
+ this.title = title || null;
+};
+
+module.exports = ContactOrganization;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/contacts.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/contacts.js b/spec/plugins/Contacts/www/contacts.js
new file mode 100644
index 0000000..5e6b4db
--- /dev/null
+++ b/spec/plugins/Contacts/www/contacts.js
@@ -0,0 +1,76 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+var argscheck = require('cordova/argscheck'),
+ exec = require('cordova/exec'),
+ ContactError = require('./ContactError'),
+ utils = require('cordova/utils'),
+ Contact = require('./Contact');
+
+/**
+* Represents a group of Contacts.
+* @constructor
+*/
+var contacts = {
+ /**
+ * Returns an array of Contacts matching the search criteria.
+ * @param fields that should be searched
+ * @param successCB success callback
+ * @param errorCB error callback
+ * @param {ContactFindOptions} options that can be applied to contact searching
+ * @return array of Contacts matching search criteria
+ */
+ find:function(fields, successCB, errorCB, options) {
+ argscheck.checkArgs('afFO', 'contacts.find', arguments);
+ if (!fields.length) {
+ errorCB && errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR));
+ } else {
+ var win = function(result) {
+ var cs = [];
+ for (var i = 0, l = result.length; i < l; i++) {
+ cs.push(contacts.create(result[i]));
+ }
+ successCB(cs);
+ };
+ exec(win, errorCB, "Contacts", "search", [fields, options]);
+ }
+ },
+
+ /**
+ * This function creates a new contact, but it does not persist the contact
+ * to device storage. To persist the contact to device storage, invoke
+ * contact.save().
+ * @param properties an object whose properties will be examined to create a new Contact
+ * @returns new Contact object
+ */
+ create:function(properties) {
+ argscheck.checkArgs('O', 'contacts.create', arguments);
+ var contact = new Contact();
+ for (var i in properties) {
+ if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) {
+ contact[i] = properties[i];
+ }
+ }
+ return contact;
+ }
+};
+
+module.exports = contacts;
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ios/Contact.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ios/Contact.js b/spec/plugins/Contacts/www/ios/Contact.js
new file mode 100644
index 0000000..b40c41a
--- /dev/null
+++ b/spec/plugins/Contacts/www/ios/Contact.js
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+var exec = require('cordova/exec'),
+ ContactError = require('./ContactError');
+
+/**
+ * Provides iOS Contact.display API.
+ */
+module.exports = {
+ display : function(errorCB, options) {
+ /*
+ * Display a contact using the iOS Contact Picker UI
+ * NOT part of W3C spec so no official documentation
+ *
+ * @param errorCB error callback
+ * @param options object
+ * allowsEditing: boolean AS STRING
+ * "true" to allow editing the contact
+ * "false" (default) display contact
+ */
+
+ if (this.id === null) {
+ if (typeof errorCB === "function") {
+ var errorObj = new ContactError(ContactError.UNKNOWN_ERROR);
+ errorCB(errorObj);
+ }
+ }
+ else {
+ exec(null, errorCB, "Contacts","displayContact", [this.id, options]);
+ }
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/Contacts/www/ios/contacts.js
----------------------------------------------------------------------
diff --git a/spec/plugins/Contacts/www/ios/contacts.js b/spec/plugins/Contacts/www/ios/contacts.js
new file mode 100644
index 0000000..67cf421
--- /dev/null
+++ b/spec/plugins/Contacts/www/ios/contacts.js
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+var exec = require('cordova/exec');
+
+/**
+ * Provides iOS enhanced contacts API.
+ */
+module.exports = {
+ newContactUI : function(successCallback) {
+ /*
+ * Create a contact using the iOS Contact Picker UI
+ * NOT part of W3C spec so no official documentation
+ *
+ * returns: the id of the created contact as param to successCallback
+ */
+ exec(successCallback, null, "Contacts","newContact", []);
+ },
+ chooseContact : function(successCallback, options) {
+ /*
+ * Select a contact using the iOS Contact Picker UI
+ * NOT part of W3C spec so no official documentation
+ *
+ * @param errorCB error callback
+ * @param options object
+ * allowsEditing: boolean AS STRING
+ * "true" to allow editing the contact
+ * "false" (default) display contact
+ * fields: array of fields to return in contact object (see ContactOptions.fields)
+ *
+ * @returns
+ * id of contact selected
+ * ContactObject
+ * if no fields provided contact contains just id information
+ * if fields provided contact object contains information for the specified fields
+ *
+ */
+ var win = function(result) {
+ var fullContact = require('./contacts').create(result);
+ successCallback(fullContact.id, fullContact);
+ };
+ exec(win, null, "Contacts","chooseContact", [options]);
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/B/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/B/plugin.xml b/spec/plugins/dependencies/B/plugin.xml
index 4258857..16847a5 100644
--- a/spec/plugins/dependencies/B/plugin.xml
+++ b/spec/plugins/dependencies/B/plugin.xml
@@ -25,15 +25,15 @@
<name>Plugin B</name>
- <dependency id="D" />
- <dependency id="E" />
+ <dependency id="D" url="." subdir="spec/plugins/dependencies/D"/>
+ <dependency id="E" url="." subdir="spec/plugins/dependencies/subdir/E"/>
<asset src="www/plugin-b.js" target="plugin-b.js" />
<config-file target="config.xml" parent="/*">
<access origin="build.phonegap.com" />
</config-file>
-
+
<!-- android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="plugins">
@@ -45,7 +45,7 @@
target-dir="src/com/phonegap/B" />
</platform>
-
+
<!-- ios -->
<platform name="ios">
<!-- CDV 2.5+ -->
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/E/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/E/plugin.xml b/spec/plugins/dependencies/E/plugin.xml
deleted file mode 100644
index c7a2098..0000000
--- a/spec/plugins/dependencies/E/plugin.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
- Copyright 2013 Anis Kadri
-
- 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.
-
--->
-
-<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
- xmlns:android="http://schemas.android.com/apk/res/android"
- id="E"
- version="0.6.0">
-
- <name>Plugin E</name>
-
- <asset src="www/plugin-e.js" target="plugin-e.js" />
-
- <config-file target="config.xml" parent="/*">
- <access origin="build.phonegap.com" />
- </config-file>
-
- <!-- android -->
- <platform name="android">
- <config-file target="res/xml/config.xml" parent="plugins">
- <plugin name="E"
- value="com.phonegap.E.E"/>
- </config-file>
-
- <source-file src="src/android/E.java"
- target-dir="src/com/phonegap/E" />
- </platform>
-
-
- <!-- ios -->
- <platform name="ios">
- <!-- CDV 2.5+ -->
- <config-file target="config.xml" parent="plugins">
- <plugin name="E"
- value="EPluginCommand"/>
- </config-file>
-
- <header-file src="src/ios/EPluginCommand.h" />
- <source-file src="src/ios/EPluginCommand.m"/>
- </platform>
-</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/E/src/android/E.java
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/E/src/android/E.java b/spec/plugins/dependencies/E/src/android/E.java
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/E/src/ios/EPluginCommand.h
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/E/src/ios/EPluginCommand.h b/spec/plugins/dependencies/E/src/ios/EPluginCommand.h
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/E/src/ios/EPluginCommand.m
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/E/src/ios/EPluginCommand.m b/spec/plugins/dependencies/E/src/ios/EPluginCommand.m
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/E/www/plugin-e.js
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/E/www/plugin-e.js b/spec/plugins/dependencies/E/www/plugin-e.js
deleted file mode 100644
index e69de29..0000000
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/subdir/E/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/subdir/E/plugin.xml b/spec/plugins/dependencies/subdir/E/plugin.xml
new file mode 100644
index 0000000..c7a2098
--- /dev/null
+++ b/spec/plugins/dependencies/subdir/E/plugin.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ id="E"
+ version="0.6.0">
+
+ <name>Plugin E</name>
+
+ <asset src="www/plugin-e.js" target="plugin-e.js" />
+
+ <config-file target="config.xml" parent="/*">
+ <access origin="build.phonegap.com" />
+ </config-file>
+
+ <!-- android -->
+ <platform name="android">
+ <config-file target="res/xml/config.xml" parent="plugins">
+ <plugin name="E"
+ value="com.phonegap.E.E"/>
+ </config-file>
+
+ <source-file src="src/android/E.java"
+ target-dir="src/com/phonegap/E" />
+ </platform>
+
+
+ <!-- ios -->
+ <platform name="ios">
+ <!-- CDV 2.5+ -->
+ <config-file target="config.xml" parent="plugins">
+ <plugin name="E"
+ value="EPluginCommand"/>
+ </config-file>
+
+ <header-file src="src/ios/EPluginCommand.h" />
+ <source-file src="src/ios/EPluginCommand.m"/>
+ </platform>
+</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/subdir/E/src/android/E.java
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/subdir/E/src/android/E.java b/spec/plugins/dependencies/subdir/E/src/android/E.java
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/subdir/E/src/ios/EPluginCommand.h
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/subdir/E/src/ios/EPluginCommand.h b/spec/plugins/dependencies/subdir/E/src/ios/EPluginCommand.h
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/subdir/E/src/ios/EPluginCommand.m
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/subdir/E/src/ios/EPluginCommand.m b/spec/plugins/dependencies/subdir/E/src/ios/EPluginCommand.m
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/plugins/dependencies/subdir/E/www/plugin-e.js
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/subdir/E/www/plugin-e.js b/spec/plugins/dependencies/subdir/E/www/plugin-e.js
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/projects/blackberry10/native/device/chrome/.gitkeep
----------------------------------------------------------------------
diff --git a/spec/projects/blackberry10/native/device/chrome/.gitkeep b/spec/projects/blackberry10/native/device/chrome/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/projects/blackberry10/native/device/plugins/jnext/auth.txt
----------------------------------------------------------------------
diff --git a/spec/projects/blackberry10/native/device/plugins/jnext/auth.txt b/spec/projects/blackberry10/native/device/plugins/jnext/auth.txt
new file mode 100644
index 0000000..0983f4f
--- /dev/null
+++ b/spec/projects/blackberry10/native/device/plugins/jnext/auth.txt
@@ -0,0 +1,3 @@
+local:/// *
+file:// *
+http:// *
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/projects/blackberry10/native/simulator/chrome/.gitkeep
----------------------------------------------------------------------
diff --git a/spec/projects/blackberry10/native/simulator/chrome/.gitkeep b/spec/projects/blackberry10/native/simulator/chrome/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/21b6d79b/spec/projects/blackberry10/native/simulator/plugins/jnext/auth.txt
----------------------------------------------------------------------
diff --git a/spec/projects/blackberry10/native/simulator/plugins/jnext/auth.txt b/spec/projects/blackberry10/native/simulator/plugins/jnext/auth.txt
new file mode 100644
index 0000000..0983f4f
--- /dev/null
+++ b/spec/projects/blackberry10/native/simulator/plugins/jnext/auth.txt
@@ -0,0 +1,3 @@
+local:/// *
+file:// *
+http:// *
\ No newline at end of file