[
  {
    "path": ".gitignore",
    "content": ".lock-wscript\nnode_modules\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2011 Nathan Rajlich\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "node-iOS\n========\n### Native [node][Node] bindings to iOS functionality (vibrate, acceleromoter, geoservices, etc.)\n\n\nThis module offers native node binding to Apple iOS functionality, meant for `node`\nrunning on an Apple iDevice. It exposes low-level functionality of the device, like\nvibrate, sending SMS messages, displaying pop-up user notifications, simulating the\nhome and lock buttons, and a lot more planned!\n\nConsidering the fact that most jailbroken devices won't have a GCC toolchain set up on their\ndevice, this repo includes a pre-built version of the module that hopefully will work on the\nmajority of devices.\n\nI'm always looking for ideas for additional APIs to add. So if you can think of any\nfunctionality that you want exposed to JavaScript that's missing from the API below,\nthen please don't hesitate to open an [Issue](https://github.com/TooTallNate/node-iOS/issues)\nrequesting it!\n\n\nAPI\n---\n\n### createNotification(options[, callback]) -> undefined\n\nCreates and displays a pop-up \"notification\" onto the iDevice. The optional `callback`\nfunction will be called after the notification has been dismissed (through user interaction\nor otherwise cancelled).\n\nThe most recent call to this function will be the active notification. That is, if\nthere's already an active notification, and this function is called, then the newly\ncreated notification will take precedence over any existing notifications. So if you\nwant to display a series of notifications, it's better invoke the next one in the\nprevious notification's callback.\n\n``` javascript\niOS.createNotification({\n  header: \"Title\",\n  message: \"Enter your name...\"\n}, function(err, response) {\n  if (err) throw err;\n\n  console.log(response);  \n});\n```\n\nThe `options` Object accepts the following parameters:\n\n * `header` - A String that will be used as the header of the notification. Defaults to `null`.\n * `message` - A String that will be used as the message body of the notification. Defaults to `null`.\n * `defaultButton` - The text of the default (primary) button. Defaults to `'OK'`. To disable the\n                     default button (notification without any buttons), explicity pass `null` here.\n * `alternateButton` - The text of the secondary (alternate) button. Defaults to `null` (no second button).\n * `otherButton` - The text of the third (other) button. Defaults to `null` (no third button).\n * `timeout` - The timeout in seconds of the notification. Defaults to `0` for no timeout.\n\n\n### AddressBook\n\nThere's one global `AddressBook` instance that you may use to interact with the\ncontents of your iDevice's central Address Book database. It's also possible to\nmodify the Address Book by adding, editing or removing contacts or groups.\n\n``` javascript\n// The singleton AddressBook namespace:\niOS.AddressBook;\n```\n\n#### AddressBook.getContacts([filter,] callback)\n\nAsynchronously retrieves an Array of \"Contact\" instances from the Address Book\ndatabase that match the given _filter_. If no _filter_ is given, then **ALL**\ncontacts that are currently in the Address Book will be retrieved.\n\n``` javascript\niOS.AddressBook.getContacts(function(err, contacts) {\n  if (err) // Something went wrong\n\n  console.log(contacts);\n  // [ {\n  //  firstName: 'John',\n  //  lastName: 'Doe',\n  //  numbers: { Mobile: '(555) 555-5555' } },\n  //  ...\n  // ]\n});\n```\n\n\n### vibrate() -> undefined\n\nVibrates the iDevice shortly. This is the same as when a text message or email arrives, etc.\nOn devices that don't vibrate, this function does nothing (no error is thrown).\n\n\n### device() -> Object\n\nReturns an Object containing properties from the current [UIDevice][]. An example:\n\n``` javascript\n{ model: 'iPhone',\n  localizedModel: 'iPhone',\n  name: 'Nathan Rajlich’s iPhone',\n  systemName: 'iPhone OS',\n  systemVersion: '4.3.1',\n  uniqueIdentifier: 'f1dfb3fa9f73fc9ffef4fcf3f61fff6f05ff1afb' }\n```\n\n\n### sendSMS(number, message) -> Boolean\n\nSends an [SMS][] with the specified `message` String to the specified `number`. Examples:\n\n``` javascript\niOS.sendSMS('5555555555', 'this is a text message!');\niOS.sendSMS('555-555-5555', 'another text message!');\niOS.sendSMS('(555) 555-5555', 'and one more?!');\niOS.sendSMS(5555555555, 'you may just use a Number as well');\n```\n\n\n### lockScreen() -> undefined\n\nLocks the screen of the iDevice. Same effect as pressing the top \"Lock\" button.\n\n\n### quitTopApplication() -> undefined\n\nQuits the currently visible application, going straight to the Home screen. On a\ndevice with multitasking support, the app will still be running in the process list.\nSame effect as pressing the \"Home\" button.\n\n\n[Node]: http://nodejs.org\n[SMS]: http://en.wikipedia.org/wiki/SMS\n[UIDevice]: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html\n"
  },
  {
    "path": "build/.gitignore",
    "content": ".wafpickle-7\nc4che\nconfig.log\n"
  },
  {
    "path": "build/default/.gitignore",
    "content": "src\n"
  },
  {
    "path": "index.js",
    "content": "// We can require 'autoreleasepool' and the NSAutoreleasePool for the main\n// (node's) thread will be automatically taken care of until the process exits\nrequire('autoreleasepool');\n\n// Ok, so the 'AddressBook' APIs look in \"$HOME/Library/AddressBook\" of the current\n// user. The problem is that \"/var/mobile\" contains the REAL address book, and\n// \"/var/root\" contains no address book, so when 'node' is run as the 'root' user,\n// the address book bindings fail.... well that is unless we symlink to the real\n// address book before any JS calls are made... dirty hack but I can't find a\n// better way to do it (explicity setting the \"HOME\" var doesn't do it)...\nvar abPath = '/var/'+process.env.USER+'/Library/AddressBook';\nvar mobilePath = '/var/mobile/Library/AddressBook';\nif (abPath !== mobilePath) {\n  var fs = require('fs');\n  if (!fs.lstatSync(abPath).isSymbolicLink()) {\n    // Backup the original 'AddressBook' dir, just in case...\n    fs.renameSync(abPath, abPath+'.bak');\n    fs.symlinkSync(mobilePath, abPath);\n  }\n  fs = null;\n}\nabPath = mobilePath = null;\n\nmodule.exports = require('./build/default/binding.node');\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"author\": \"Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)\",\n  \"name\": \"iOS\",\n  \"description\": \"Native node bindings to iOS functionality (vibrate, acceleromoter, geoservices, etc.)\",\n  \"version\": \"0.0.0\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/TooTallNate/node-iOS.git\"\n  },\n  \"main\": \"./index.js\",\n  \"directories\": {\n    \"lib\": \"./\"\n  },\n  \"engines\": {\n    \"node\": \">= 0.4.0\"\n  },\n  \"dependencies\": {\n    \"autoreleasepool\": \"0.0.x\"\n  },\n  \"devDependencies\": {}\n}\n"
  },
  {
    "path": "src/addressBook-Contact.h",
    "content": "#import \"addressBook-Record.h\"\n\nclass Contact : public Record {\n  public:\n    // Person\n    const char *firstName;\n    const char *middleName;\n    const char *lastName;\n    // Organization\n    const char *organization;\n    const char *jobTitle;\n    const char *department;\n    // Phone Numbers\n    int numNumbers;\n    const char **numbersNames;\n    const char **numbersValues;\n}; // class Contact\n"
  },
  {
    "path": "src/addressBook-Record.h",
    "content": "#import <node.h>\n#import <AddressBook/AddressBook.h>\n\nclass Record : public node::ObjectWrap {\n  public:\n    ABRecordID recordId;\n}; // class Record\n"
  },
  {
    "path": "src/addressBook.cc",
    "content": "// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABAddressBookRef_iPhoneOS/Reference/reference.html\n// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABRecordRef_iPhoneOS/Reference/reference.html\n// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABPersonRef_iPhoneOS/Reference/reference.html\n// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABGroupRef_iPhoneOS/Reference/reference.html\n\n#import \"addressBook.h\"\n#include <stdio.h>\n\nusing namespace node;\nusing namespace v8;\n\n\n// Set up the exports for AddressBook\nvoid AddressBook::Init(v8::Handle<Object> target) {\n  HandleScope scope;\n  Local<Object> ab = Object::New();\n  NODE_SET_METHOD(ab, \"getContacts\", AddressBook::GetContacts);\n  NODE_SET_METHOD(ab, \"getGroups\", AddressBook::GetGroups);\n  target->Set(String::NewSymbol(\"AddressBook\"), ab);\n}\n\n\n// 'createNotification' begins the async notification process. The user should pass\n// an \"options\" Object and an optional callback function (to examine the results of\n// the notification).\nv8::Handle<Value> AddressBook::GetContacts(const Arguments& args) {\n  HandleScope scope;\n\n  // This is the struct that gets passed around EIO\n  struct async_request* ar = (struct async_request*) malloc(sizeof(struct async_request));\n\n  // TODO: Add support for the search predicate\n  //Local<Object> options = args[0]->ToObject();\n\n  // A callback function is optional (though why would you call\n  // this if you didn't want the results?)\n  ar->hasCb = false;\n  int argsLen = args.Length();\n  if (argsLen >= 1) {\n    Local<Function> cb = Local<Function>::Cast(args[argsLen-1]);\n    ar->cb = Persistent<Function>::New(cb);\n    ar->hasCb = true;\n  }\n\n  eio_custom(GetContacts_DoRequest, EIO_PRI_DEFAULT, GetContacts_AfterResponse, ar);\n  ev_ref(EV_DEFAULT_UC);\n\n  return Undefined();\n}\n\nint GetContacts_DoRequest (eio_req * req) {\n  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\n  struct async_request* ar = (struct async_request*)req->data;\n\n  ABAddressBookRef addressBook = ABAddressBookCreate();\n  CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);\n  // TODO: Sort by the user's current sort preference by default, or a configurable sort\n  CFIndex count = CFArrayGetCount(people);\n  ar->resultsCount = count;\n  ar->results = new Record *[count];\n\n  for (CFIndex i=0; i<count; i++) {\n    Contact *p = new Contact;\n    ABRecordRef pRef = CFArrayGetValueAtIndex(people, i);\n\n    // RecordID\n    p->recordId = ABRecordGetRecordID(pRef);\n\n    // FirstName\n    NSString* firstNameStr = (NSString *)ABRecordCopyValue(pRef, kABPersonFirstNameProperty);\n    p->firstName = firstNameStr != NULL ? [firstNameStr UTF8String] : NULL;\n    // MiddleName\n    NSString* middleNameStr = (NSString *)ABRecordCopyValue(pRef, kABPersonMiddleNameProperty);\n    p->middleName = middleNameStr != NULL ? [middleNameStr UTF8String] : NULL;\n    // LastName\n    NSString *lastNameStr = (NSString *)ABRecordCopyValue(pRef, kABPersonLastNameProperty);\n    p->lastName = lastNameStr != NULL ? [lastNameStr UTF8String] : NULL;\n    // Organization\n    NSString *organizationStr = (NSString *)ABRecordCopyValue(pRef, kABPersonOrganizationProperty);\n    p->organization = organizationStr != NULL ? [organizationStr UTF8String] : NULL;\n    // JobTitle\n    NSString *jobTitleStr = (NSString *)ABRecordCopyValue(pRef, kABPersonJobTitleProperty);\n    p->jobTitle = jobTitleStr != NULL ? [jobTitleStr UTF8String] : NULL;\n    // Department\n    NSString *departmentStr = (NSString *)ABRecordCopyValue(pRef, kABPersonDepartmentProperty);\n    p->department = departmentStr != NULL ? [departmentStr UTF8String] : NULL;\n\n    // PhoneNumbers\n    ABMultiValueRef numbers = ABRecordCopyValue(pRef, kABPersonPhoneProperty);\n    p->numNumbers = ABMultiValueGetCount(numbers);\n    p->numbersNames = new const char *[p->numNumbers];\n    p->numbersValues = new const char *[p->numNumbers];\n    for (CFIndex j=0; j < p->numNumbers; j++) {\n      NSString *numberName = (NSString *)ABMultiValueCopyLabelAtIndex(numbers, j);\n      NSString *numberValue = (NSString *)ABMultiValueCopyValueAtIndex(numbers, j);\n      if ([numberName hasPrefix:@\"_$!<\" ])\n        numberName = [numberName substringFromIndex:4 ];\n      if ([numberName hasSuffix:@\">!$_\" ])\n        numberName = [numberName substringToIndex: [numberName length] - 4];\n      p->numbersNames[j] = [numberName UTF8String];\n      p->numbersValues[j] = [numberValue UTF8String];\n    }\n\n    ar->results[i] = p;\n  }\n\n  CFRelease(people);\n  CFRelease(addressBook);\n  [pool drain];\n  return 0;\n}\n\nint GetContacts_AfterResponse (eio_req * req) {\n  HandleScope scope;\n  ev_unref(EV_DEFAULT_UC);\n\n  struct async_request* ar = (struct async_request*)req->data;\n\n  if (ar->hasCb) {\n    // Prepare the callback arguments\n    Local<Value> argv[2];\n    argv[0] = Local<Value>::New(Null());\n\n    Local<Array> resultsArray = Array::New(ar->resultsCount);\n    for (CFIndex i=0; i < ar->resultsCount; i++) {\n      Contact *p = (Contact *)ar->results[i];\n      // TODO: Instead of Object::New(), replace this with a JavaScript\n      //       \"Contact\" constructor.\n      Local<Object> curPerson = Object::New();\n      //curPerson->Set(String::NewSymbol(\"_id\"), Integer::New(p->recordId));\n      if (p->firstName != NULL)\n        curPerson->Set(String::NewSymbol(\"firstName\"), String::NewSymbol( p->firstName ));\n      if (p->middleName != NULL)\n        curPerson->Set(String::NewSymbol(\"middleName\"), String::NewSymbol( p->middleName ));\n      if (p->lastName != NULL)\n        curPerson->Set(String::NewSymbol(\"lastName\"), String::NewSymbol( p->lastName ));\n      if (p->organization!= NULL)\n        curPerson->Set(String::NewSymbol(\"organization\"), String::NewSymbol( p->organization ));\n      if (p->jobTitle != NULL)\n        curPerson->Set(String::NewSymbol(\"jobTitle\"), String::NewSymbol( p->jobTitle ));\n      if (p->department != NULL)\n        curPerson->Set(String::NewSymbol(\"department\"), String::NewSymbol( p->department ));\n\n      // PhoneNumbers\n      Local<Object> phoneNumbersObj = Object::New();\n      for (int j=0; j < p->numNumbers; j++) {\n        phoneNumbersObj->Set(String::NewSymbol(p->numbersNames[j]), String::NewSymbol(p->numbersValues[j]));\n      }\n      curPerson->Set(String::NewSymbol(\"numbers\"), phoneNumbersObj);\n      resultsArray->Set(Integer::New(i), curPerson);\n      delete [] p->numbersNames;\n      delete [] p->numbersValues;\n      delete p;\n    }\n    argv[1] = resultsArray;\n\n    // Invoke 'le callback\n    TryCatch try_catch;\n    ar->cb->Call(Context::GetCurrent()->Global(), 2, argv);\n    if (try_catch.HasCaught()) {\n      FatalException(try_catch);\n    }\n    ar->cb.Dispose();\n  }\n\n  delete [] ar->results;\n  free(ar);\n  return 0;\n}\n\nv8::Handle<Value> AddressBook::GetGroups(const Arguments& args) {\n  HandleScope scope;\n\n  return Undefined();\n}\n"
  },
  {
    "path": "src/addressBook.h",
    "content": "#import <node.h>\n#import <v8.h>\n#import <CoreFoundation/CoreFoundation.h>\n#import <Foundation/Foundation.h>\n#import <AddressBook/AddressBook.h>\n#import \"addressBook-Record.h\"\n#import \"addressBook-Contact.h\"\n\nint GetContacts_DoRequest (eio_req *);\nint GetContacts_AfterResponse (eio_req *);\n\nstruct async_request {\n  v8::Persistent<v8::Function> cb;\n  bool hasCb;\n  CFIndex resultsCount;\n  // 'results' is an array of pointers to \"Record\" instances\n  Record **results;\n};\n\nclass AddressBook {\n  public:\n    static void Init(v8::Handle<v8::Object> target);\n    static v8::Handle<v8::Value> GetContacts(const v8::Arguments& args);\n    static v8::Handle<v8::Value> GetGroups(const v8::Arguments& args);\n    static v8::Handle<v8::Value> Save(const v8::Arguments& args);\n};\n"
  },
  {
    "path": "src/binding.cc",
    "content": "#import <v8.h>\n#import <node.h>\n#import <AudioToolbox/AudioToolbox.h>\n#import <UIKit/UIKit.h>\n#import \"addressBook.h\"\n#import \"graphicServices.h\"\n#import \"notifications.h\"\n#import \"telephony.h\"\n#import \"compatibility.h\" // ...meh\n\nusing namespace node;\nusing namespace v8;\n\nclass Binding {\n  public:\n\n  static void Init(v8::Handle<Object> target) {\n    HandleScope scope;\n\n    UIDevice *aDevice = [UIDevice currentDevice];\n    [aDevice beginGeneratingDeviceOrientationNotifications];\n    [aDevice setBatteryMonitoringEnabled:YES];\n\n    NODE_SET_METHOD(target, \"vibrate\", Vibrate);\n    NODE_SET_METHOD(target, \"device\", Device);\n    NODE_SET_METHOD(target, \"sendSMS\", Telephony::SendSMS);\n  }\n\n  static v8::Handle<Value> Vibrate(const Arguments& args) {\n    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);\n    return Undefined();\n  }\n\n  static v8::Handle<Value> Device(const Arguments& args) {\n    HandleScope scope;\n    Local<Object> result = Object::New();\n\n    UIDevice *aDevice = [UIDevice currentDevice];\n\n    result->Set(String::NewSymbol(\"batteryLevel\"), Number::New([aDevice batteryLevel]));\n    result->Set(String::NewSymbol(\"batteryState\"), Integer::New([aDevice batteryState]));\n    result->Set(String::NewSymbol(\"model\"), String::NewSymbol([[aDevice model] UTF8String]));\n    result->Set(String::NewSymbol(\"localizedModel\"), String::NewSymbol([[aDevice localizedModel] UTF8String]));\n    result->Set(String::NewSymbol(\"orientation\"), Integer::New([aDevice orientation]));\n    result->Set(String::NewSymbol(\"name\"), String::NewSymbol([[aDevice name] UTF8String]));\n    result->Set(String::NewSymbol(\"systemName\"), String::NewSymbol([[aDevice systemName] UTF8String]));\n    result->Set(String::NewSymbol(\"systemVersion\"), String::NewSymbol([[aDevice systemVersion] UTF8String]));\n    result->Set(String::NewSymbol(\"uniqueIdentifier\"), String::NewSymbol([[aDevice uniqueIdentifier] UTF8String]));\n\n    return scope.Close(result);\n  }\n\n};\n\nextern \"C\" {\n  static void init (v8::Handle<Object> target) {\n    Binding::Init(target);\n    AddressBook::Init(target);\n    GraphicServices::Init(target);\n    Notifications::Init(target);\n  }\n\n  NODE_MODULE(binding, init);\n}\n"
  },
  {
    "path": "src/compatibility.h",
    "content": "#import <UIKit/UIKit.h>\n\n// Add newer API stuff. This shouldn't be needed, unfortunately\n// it seems as though the 'gcc' from Cydia for iOS (my version at least)\n// doesn't support either the 'IPHONEOS_DEPLOYMENT_TARGET' env var nor\n// the '-miphoneos-version-min' compiler flag... lame...\ntypedef enum {\n    UIDeviceBatteryStateUnknown,\n    UIDeviceBatteryStateUnplugged,   // on battery, discharging\n    UIDeviceBatteryStateCharging,    // plugged in, less than 100%\n    UIDeviceBatteryStateFull,        // plugged in, at 100%\n} UIDeviceBatteryState;              // available in iPhone 3.0\n\n@interface UIDevice ()\n  - (void) setOrientation:(UIInterfaceOrientation)orientation;\n  @property(getter=isBatteryMonitoringEnabled) BOOL batteryMonitoringEnabled;\n  @property(readonly) UIDeviceBatteryState          batteryState;\n  @property(readonly) float                         batteryLevel;\n@end\n"
  },
  {
    "path": "src/graphicServices.cc",
    "content": "#import \"graphicServices.h\"\n\nusing namespace node;\nusing namespace v8;\n\nvoid GraphicServices::Init(v8::Handle<Object> target) {\n  HandleScope scope;\n\n  NODE_SET_METHOD(target, \"lockScreen\", LockScreen);\n  NODE_SET_METHOD(target, \"quitTopApplication\", QuitTopApplication);\n}\n\n/* Same effect as pressing the top \"Lock\" button on your iDevice. No arguments required. */\nv8::Handle<Value> GraphicServices::LockScreen(const Arguments& args) {\n  HandleScope scope;\n  GSEventLockDevice();\n  GSEventLockDevice();\n  return scope.Close(Undefined());\n}\n\n/* Quits the currently visible App. No arguments required. */\nv8::Handle<Value> GraphicServices::QuitTopApplication(const Arguments& args) {\n  HandleScope scope;\n  GSEventQuitTopApplication();\n  return scope.Close(Undefined());\n}\n"
  },
  {
    "path": "src/graphicServices.h",
    "content": "#import <v8.h>\n#import <node.h>\n#import <GraphicsServices/GraphicsServices.h>\n\n/* Private, undocumented APIs */\n//void GSEventLockDevice();\n//void GSEventQuitTopApplication();\n\nclass GraphicServices {\n  public:\n    static void Init(v8::Handle<v8::Object> target);\n    static v8::Handle<v8::Value> LockScreen(const v8::Arguments& args);\n    static v8::Handle<v8::Value> QuitTopApplication(const v8::Arguments& args);\n};\n"
  },
  {
    "path": "src/notifications.cc",
    "content": "#import \"notifications.h\"\n\nusing namespace node;\nusing namespace v8;\n\n\n// Initialize the notification-related exports\nvoid Notifications::Init(v8::Handle<Object> target) {\n  HandleScope scope;\n  NODE_SET_METHOD(target, \"createNotification\", Notifications::createNotification);\n}\n\n// From v8's 'shell.cc'\nconst char* ToCString(const v8::String::Utf8Value& value) {\n  return *value ? *value : \"<string conversion failed>\";\n}\n\n// 'createNotification' begins the async notification process. The user should pass\n// an \"options\" Object and an optional callback function (to examine the results of\n// the notification).\nv8::Handle<Value> Notifications::createNotification(const Arguments& args) {\n  HandleScope scope;\n\n  if (args.Length() < 1) {\n    return ThrowException(Exception::TypeError(String::New(\"An 'options' Object is required\")));\n  }\n\n  Local<Object> options = args[0]->ToObject();\n\n  // This is the struct that gets passed around EIO\n  notification_request* nr = (notification_request*) malloc(sizeof(struct notification_request));\n\n  CFOptionFlags flags = 0;\n  flags |= kCFUserNotificationPlainAlertLevel;\n\n  // 'dict' will contain the processed 'options', so the alert message is formatted the way the user asked\n  CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);\n\n  // This is always set. It appears to be needed for daemon processes on iOS...\n  CFDictionaryAddValue(dict, kCFUserNotificationAlertTopMostKey, kCFBooleanTrue);\n\n  if (options->Has(String::NewSymbol(\"header\"))) {\n    String::Utf8Value headerStr(options->Get(String::NewSymbol(\"header\")));\n    CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFStringCreateWithCString(NULL, ToCString(headerStr), kCFStringEncodingUTF8) );\n  }\n  if (options->Has(String::NewSymbol(\"message\"))) {\n    String::Utf8Value messageStr(options->Get(String::NewSymbol(\"message\")));\n    CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, CFStringCreateWithCString(NULL, ToCString(messageStr), kCFStringEncodingUTF8) );\n  }\n  if (options->Has(String::NewSymbol(\"defaultButton\"))) {\n    Local<Value> defaultButtonVal = options->Get(String::NewSymbol(\"defaultButton\"));\n    if (defaultButtonVal->IsNull()) {\n      flags |= kCFUserNotificationNoDefaultButtonFlag;\n    } else {\n      String::Utf8Value firstButtonStr(defaultButtonVal);\n      CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFStringCreateWithCString(NULL, ToCString(firstButtonStr), kCFStringEncodingUTF8) );\n    }\n  }\n  if (options->Has(String::NewSymbol(\"alternateButton\"))) {\n    String::Utf8Value secondButtonStr(options->Get(String::NewSymbol(\"alternateButton\")));\n    CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFStringCreateWithCString(NULL, ToCString(secondButtonStr), kCFStringEncodingUTF8) );\n  }\n  if (options->Has(String::NewSymbol(\"otherButton\"))) {\n    String::Utf8Value thirdButtonStr(options->Get(String::NewSymbol(\"otherButton\")));\n    CFDictionaryAddValue(dict, kCFUserNotificationOtherButtonTitleKey, CFStringCreateWithCString(NULL, ToCString(thirdButtonStr), kCFStringEncodingUTF8) );\n  }\n\n  CFTimeInterval timeout = 0; // 0 = disabled, by default\n  if (options->Has(String::NewSymbol(\"timeout\"))) {\n    timeout = options->Get(String::NewSymbol(\"timeout\"))->Int32Value();\n  }\n\n  CFUserNotificationRef notif = CFUserNotificationCreate(NULL, timeout, flags, &nr->error, dict);\n  nr->notif = notif;\n\n  nr->hasCb = false;\n  if (args.Length() >= 2) {\n    Local<Function> cb = Local<Function>::Cast(args[1]);\n    nr->cb = Persistent<Function>::New(cb);\n    nr->hasCb = true;\n  }\n\n  eio_custom(CreateNotification_WaitForResponse, EIO_PRI_DEFAULT, CreateNotification_AfterResponse, nr);\n  ev_ref(EV_DEFAULT_UC);\n\n  return Undefined();\n}\n\n// This is the function that gets called on the thread pool. It blocks for as long as\n// the notification is active on the iDevice.\nint CreateNotification_WaitForResponse (eio_req * req) {\n  struct notification_request * nr = (struct notification_request *)req->data;\n  req->result = CFUserNotificationReceiveResponse(nr->notif, 0, &nr->options);\n  return 0;\n}\n\n// This function gets called on node's main thread after the notification has been\n// dismissed. This function collects the results of the dismissal into a 'results'\n// Object that gets passed to the JS callback function.\nint CreateNotification_AfterResponse (eio_req * req) {\n  HandleScope scope;\n  ev_unref(EV_DEFAULT_UC);\n\n  struct notification_request * nr = (struct notification_request *)req->data;\n\n  if (nr->hasCb) {\n    // Prepare the callback arguments\n    Local<Value> argv[2];\n    argv[0] = Local<Value>::New(Null());\n\n    Local<Object> results = Object::New();\n    results->Set(String::NewSymbol(\"result\"), Integer::New( req->result ));\n    results->Set(String::NewSymbol(\"error\"), Integer::New( nr->error ));\n    results->Set(String::NewSymbol(\"options\"), Integer::New( nr->options ));\n    argv[1] = results;\n\n    // Invoke 'le callback\n    TryCatch try_catch;\n    nr->cb->Call(Context::GetCurrent()->Global(), 2, argv);\n    if (try_catch.HasCaught()) {\n      FatalException(try_catch);\n    }\n    nr->cb.Dispose();\n  }\n\n  free(nr);\n  return 0;\n}\n"
  },
  {
    "path": "src/notifications.h",
    "content": "#import <node.h>\n#import <v8.h>\n#import <unistd.h>\n#import <Foundation/Foundation.h>\n#import <CoreFoundation/CoreFoundation.h>\n#import <CoreFoundation/CFUserNotification.h>\n\nint CreateNotification_WaitForResponse (eio_req *);\nint CreateNotification_AfterResponse (eio_req *);\n\nstruct notification_request {\n  CFOptionFlags options;\n  CFUserNotificationRef notif;\n  SInt32 error;\n  v8::Persistent<v8::Function> cb;\n  bool hasCb;\n};\n\nclass Notifications {\n  public:\n    static void Init(v8::Handle<v8::Object> target);\n    static v8::Handle<v8::Value> createNotification(const v8::Arguments& args);\n};\n"
  },
  {
    "path": "src/telephony.cc",
    "content": "#import \"telephony.h\"\n\nusing namespace node;\nusing namespace v8;\n\n//extern id CTTelephonyCenterGetDefault();\n//extern void CTTelephonyCenterAddObserver(void*,id,CFNotificationCallback,NSString*,void*,int);\n\n//static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {\n//  NSLog(@\"Name: %@\", name);\n//}\n\n//v8::Handle<Value> Telephony::Init(const Arguments& args) {\n//  id ct = CTTelephonyCenterGetDefault();\n//  CTTelephonyCenterAddObserver(ct, NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold);\n//  return Undefined();\n//}\n\n// Sends an SMS. First argument should be the number, second arg should be the message.\n// TODO: All support for a 'sent' callback or something... I'm not sure if this is possible...\nv8::Handle<Value> Telephony::SendSMS(const Arguments& args) {\n  HandleScope scope;\n  if (args.Length() != 2) {\n    return ThrowException(Exception::TypeError(\n                  String::New(\"\\\"sendSMS\\\" requires two String arguments, a number and the message\")));\n  }\n  NSString* number = [NSString stringWithUTF8String: *String::Utf8Value(args[0]->ToString()) ];\n  NSString* message = [NSString stringWithUTF8String: *String::Utf8Value(args[1]->ToString()) ];\n  BOOL success = [[CTMessageCenter sharedMessageCenter] sendSMSWithText:message serviceCenter:nil toAddress:number];\n  return scope.Close(v8::Boolean::New(success));\n}\n"
  },
  {
    "path": "src/telephony.h",
    "content": "#import <node.h>\n#import <v8.h>\n#import <Foundation/Foundation.h>\n#import <CoreFoundation/CoreFoundation.h>\n#import <CoreTelephony/CoreTelephony.h>\n\nclass Telephony {\n  public:\n    static v8::Handle<v8::Value> Init(const v8::Arguments& args);\n    static v8::Handle<v8::Value> SendSMS(const v8::Arguments& args);\n};\n"
  },
  {
    "path": "tests/addressBook-test.js",
    "content": "var iOS = require('../');\n\n// Get an Array of all the Contacts in the address book\niOS.AddressBook.getContacts(function(err, results) {\n  if (err) throw err;\n  console.log(results);\n  console.log(results.length);\n});\n"
  },
  {
    "path": "tests/notification-test.js",
    "content": "var iOS = require('../');\n\niOS.createNotification({\n  message: 'This message will have NO buttons!',\n  defaultButton: null,\n  timeout: 3\n}, function(err, res) {\n  console.log(arguments);\n\n  iOS.createNotification({\n    header: 'test header',\n    message: 'This is a message!!',\n    defaultButton: 'first',\n    alternateButton: 'second'\n  }, console.log);\n});\n\n"
  },
  {
    "path": "wscript",
    "content": "def set_options(opt):\n  opt.tool_options(\"compiler_cxx\")\n\ndef configure(conf):\n  conf.check_tool(\"compiler_cxx\")\n  conf.check_tool(\"node_addon\")\n\ndef build(bld):\n  bld.env[\"IPHONEOS_DEPLOYMENT_TARGET\"] = \"3.0\"\n  obj = bld.new_task_gen(\"cxx\", \"shlib\", \"node_addon\")\n  obj.cxxflags = [\"-g\", \"-D_FILE_OFFSET_BITS=64\", \"-D_LARGEFILE_SOURCE\", \"-Wall\", \"-ObjC++\", \"-F/System/Library/PrivateFrameworks\"]\n  obj.ldflags = [\"-F/System/Library/PrivateFrameworks\", \"-mmacosx-version-min=10.5\"]\n  obj.framework = ['Foundation', 'AddressBook', 'AudioToolbox', 'UIKit', 'CoreFoundation', 'CoreTelephony', 'GraphicsServices']\n  obj.target = \"binding\"\n  obj.source = [\"src/binding.cc\", \"src/addressBook.cc\", \"src/graphicServices.cc\", \"src/notifications.cc\", \"src/telephony.cc\"]\n"
  }
]