Full Code of TooTallNate/node-iOS for AI

master 955e2d23675d cached
23 files
28.3 KB
7.7k tokens
22 symbols
1 requests
Download .txt
Repository: TooTallNate/node-iOS
Branch: master
Commit: 955e2d23675d
Files: 23
Total size: 28.3 KB

Directory structure:
gitextract_sem97i7t/

├── .gitignore
├── LICENSE
├── README.md
├── build/
│   ├── .gitignore
│   └── default/
│       ├── .gitignore
│       └── binding.node
├── index.js
├── package.json
├── src/
│   ├── addressBook-Contact.h
│   ├── addressBook-Record.h
│   ├── addressBook.cc
│   ├── addressBook.h
│   ├── binding.cc
│   ├── compatibility.h
│   ├── graphicServices.cc
│   ├── graphicServices.h
│   ├── notifications.cc
│   ├── notifications.h
│   ├── telephony.cc
│   └── telephony.h
├── tests/
│   ├── addressBook-test.js
│   └── notification-test.js
└── wscript

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.lock-wscript
node_modules


================================================
FILE: LICENSE
================================================
Copyright (c) 2011 Nathan Rajlich

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
node-iOS
========
### Native [node][Node] bindings to iOS functionality (vibrate, acceleromoter, geoservices, etc.)


This module offers native node binding to Apple iOS functionality, meant for `node`
running on an Apple iDevice. It exposes low-level functionality of the device, like
vibrate, sending SMS messages, displaying pop-up user notifications, simulating the
home and lock buttons, and a lot more planned!

Considering the fact that most jailbroken devices won't have a GCC toolchain set up on their
device, this repo includes a pre-built version of the module that hopefully will work on the
majority of devices.

I'm always looking for ideas for additional APIs to add. So if you can think of any
functionality that you want exposed to JavaScript that's missing from the API below,
then please don't hesitate to open an [Issue](https://github.com/TooTallNate/node-iOS/issues)
requesting it!


API
---

### createNotification(options[, callback]) -> undefined

Creates and displays a pop-up "notification" onto the iDevice. The optional `callback`
function will be called after the notification has been dismissed (through user interaction
or otherwise cancelled).

The most recent call to this function will be the active notification. That is, if
there's already an active notification, and this function is called, then the newly
created notification will take precedence over any existing notifications. So if you
want to display a series of notifications, it's better invoke the next one in the
previous notification's callback.

``` javascript
iOS.createNotification({
  header: "Title",
  message: "Enter your name..."
}, function(err, response) {
  if (err) throw err;

  console.log(response);  
});
```

The `options` Object accepts the following parameters:

 * `header` - A String that will be used as the header of the notification. Defaults to `null`.
 * `message` - A String that will be used as the message body of the notification. Defaults to `null`.
 * `defaultButton` - The text of the default (primary) button. Defaults to `'OK'`. To disable the
                     default button (notification without any buttons), explicity pass `null` here.
 * `alternateButton` - The text of the secondary (alternate) button. Defaults to `null` (no second button).
 * `otherButton` - The text of the third (other) button. Defaults to `null` (no third button).
 * `timeout` - The timeout in seconds of the notification. Defaults to `0` for no timeout.


### AddressBook

There's one global `AddressBook` instance that you may use to interact with the
contents of your iDevice's central Address Book database. It's also possible to
modify the Address Book by adding, editing or removing contacts or groups.

``` javascript
// The singleton AddressBook namespace:
iOS.AddressBook;
```

#### AddressBook.getContacts([filter,] callback)

Asynchronously retrieves an Array of "Contact" instances from the Address Book
database that match the given _filter_. If no _filter_ is given, then **ALL**
contacts that are currently in the Address Book will be retrieved.

``` javascript
iOS.AddressBook.getContacts(function(err, contacts) {
  if (err) // Something went wrong

  console.log(contacts);
  // [ {
  //  firstName: 'John',
  //  lastName: 'Doe',
  //  numbers: { Mobile: '(555) 555-5555' } },
  //  ...
  // ]
});
```


### vibrate() -> undefined

Vibrates the iDevice shortly. This is the same as when a text message or email arrives, etc.
On devices that don't vibrate, this function does nothing (no error is thrown).


### device() -> Object

Returns an Object containing properties from the current [UIDevice][]. An example:

``` javascript
{ model: 'iPhone',
  localizedModel: 'iPhone',
  name: 'Nathan Rajlich’s iPhone',
  systemName: 'iPhone OS',
  systemVersion: '4.3.1',
  uniqueIdentifier: 'f1dfb3fa9f73fc9ffef4fcf3f61fff6f05ff1afb' }
```


### sendSMS(number, message) -> Boolean

Sends an [SMS][] with the specified `message` String to the specified `number`. Examples:

``` javascript
iOS.sendSMS('5555555555', 'this is a text message!');
iOS.sendSMS('555-555-5555', 'another text message!');
iOS.sendSMS('(555) 555-5555', 'and one more?!');
iOS.sendSMS(5555555555, 'you may just use a Number as well');
```


### lockScreen() -> undefined

Locks the screen of the iDevice. Same effect as pressing the top "Lock" button.


### quitTopApplication() -> undefined

Quits the currently visible application, going straight to the Home screen. On a
device with multitasking support, the app will still be running in the process list.
Same effect as pressing the "Home" button.


[Node]: http://nodejs.org
[SMS]: http://en.wikipedia.org/wiki/SMS
[UIDevice]: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html


================================================
FILE: build/.gitignore
================================================
.wafpickle-7
c4che
config.log


================================================
FILE: build/default/.gitignore
================================================
src


================================================
FILE: index.js
================================================
// We can require 'autoreleasepool' and the NSAutoreleasePool for the main
// (node's) thread will be automatically taken care of until the process exits
require('autoreleasepool');

// Ok, so the 'AddressBook' APIs look in "$HOME/Library/AddressBook" of the current
// user. The problem is that "/var/mobile" contains the REAL address book, and
// "/var/root" contains no address book, so when 'node' is run as the 'root' user,
// the address book bindings fail.... well that is unless we symlink to the real
// address book before any JS calls are made... dirty hack but I can't find a
// better way to do it (explicity setting the "HOME" var doesn't do it)...
var abPath = '/var/'+process.env.USER+'/Library/AddressBook';
var mobilePath = '/var/mobile/Library/AddressBook';
if (abPath !== mobilePath) {
  var fs = require('fs');
  if (!fs.lstatSync(abPath).isSymbolicLink()) {
    // Backup the original 'AddressBook' dir, just in case...
    fs.renameSync(abPath, abPath+'.bak');
    fs.symlinkSync(mobilePath, abPath);
  }
  fs = null;
}
abPath = mobilePath = null;

module.exports = require('./build/default/binding.node');


================================================
FILE: package.json
================================================
{
  "author": "Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)",
  "name": "iOS",
  "description": "Native node bindings to iOS functionality (vibrate, acceleromoter, geoservices, etc.)",
  "version": "0.0.0",
  "repository": {
    "type": "git",
    "url": "git://github.com/TooTallNate/node-iOS.git"
  },
  "main": "./index.js",
  "directories": {
    "lib": "./"
  },
  "engines": {
    "node": ">= 0.4.0"
  },
  "dependencies": {
    "autoreleasepool": "0.0.x"
  },
  "devDependencies": {}
}


================================================
FILE: src/addressBook-Contact.h
================================================
#import "addressBook-Record.h"

class Contact : public Record {
  public:
    // Person
    const char *firstName;
    const char *middleName;
    const char *lastName;
    // Organization
    const char *organization;
    const char *jobTitle;
    const char *department;
    // Phone Numbers
    int numNumbers;
    const char **numbersNames;
    const char **numbersValues;
}; // class Contact


================================================
FILE: src/addressBook-Record.h
================================================
#import <node.h>
#import <AddressBook/AddressBook.h>

class Record : public node::ObjectWrap {
  public:
    ABRecordID recordId;
}; // class Record


================================================
FILE: src/addressBook.cc
================================================
// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABAddressBookRef_iPhoneOS/Reference/reference.html
// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABRecordRef_iPhoneOS/Reference/reference.html
// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABPersonRef_iPhoneOS/Reference/reference.html
// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABGroupRef_iPhoneOS/Reference/reference.html

#import "addressBook.h"
#include <stdio.h>

using namespace node;
using namespace v8;


// Set up the exports for AddressBook
void AddressBook::Init(v8::Handle<Object> target) {
  HandleScope scope;
  Local<Object> ab = Object::New();
  NODE_SET_METHOD(ab, "getContacts", AddressBook::GetContacts);
  NODE_SET_METHOD(ab, "getGroups", AddressBook::GetGroups);
  target->Set(String::NewSymbol("AddressBook"), ab);
}


// 'createNotification' begins the async notification process. The user should pass
// an "options" Object and an optional callback function (to examine the results of
// the notification).
v8::Handle<Value> AddressBook::GetContacts(const Arguments& args) {
  HandleScope scope;

  // This is the struct that gets passed around EIO
  struct async_request* ar = (struct async_request*) malloc(sizeof(struct async_request));

  // TODO: Add support for the search predicate
  //Local<Object> options = args[0]->ToObject();

  // A callback function is optional (though why would you call
  // this if you didn't want the results?)
  ar->hasCb = false;
  int argsLen = args.Length();
  if (argsLen >= 1) {
    Local<Function> cb = Local<Function>::Cast(args[argsLen-1]);
    ar->cb = Persistent<Function>::New(cb);
    ar->hasCb = true;
  }

  eio_custom(GetContacts_DoRequest, EIO_PRI_DEFAULT, GetContacts_AfterResponse, ar);
  ev_ref(EV_DEFAULT_UC);

  return Undefined();
}

int GetContacts_DoRequest (eio_req * req) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  struct async_request* ar = (struct async_request*)req->data;

  ABAddressBookRef addressBook = ABAddressBookCreate();
  CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);
  // TODO: Sort by the user's current sort preference by default, or a configurable sort
  CFIndex count = CFArrayGetCount(people);
  ar->resultsCount = count;
  ar->results = new Record *[count];

  for (CFIndex i=0; i<count; i++) {
    Contact *p = new Contact;
    ABRecordRef pRef = CFArrayGetValueAtIndex(people, i);

    // RecordID
    p->recordId = ABRecordGetRecordID(pRef);

    // FirstName
    NSString* firstNameStr = (NSString *)ABRecordCopyValue(pRef, kABPersonFirstNameProperty);
    p->firstName = firstNameStr != NULL ? [firstNameStr UTF8String] : NULL;
    // MiddleName
    NSString* middleNameStr = (NSString *)ABRecordCopyValue(pRef, kABPersonMiddleNameProperty);
    p->middleName = middleNameStr != NULL ? [middleNameStr UTF8String] : NULL;
    // LastName
    NSString *lastNameStr = (NSString *)ABRecordCopyValue(pRef, kABPersonLastNameProperty);
    p->lastName = lastNameStr != NULL ? [lastNameStr UTF8String] : NULL;
    // Organization
    NSString *organizationStr = (NSString *)ABRecordCopyValue(pRef, kABPersonOrganizationProperty);
    p->organization = organizationStr != NULL ? [organizationStr UTF8String] : NULL;
    // JobTitle
    NSString *jobTitleStr = (NSString *)ABRecordCopyValue(pRef, kABPersonJobTitleProperty);
    p->jobTitle = jobTitleStr != NULL ? [jobTitleStr UTF8String] : NULL;
    // Department
    NSString *departmentStr = (NSString *)ABRecordCopyValue(pRef, kABPersonDepartmentProperty);
    p->department = departmentStr != NULL ? [departmentStr UTF8String] : NULL;

    // PhoneNumbers
    ABMultiValueRef numbers = ABRecordCopyValue(pRef, kABPersonPhoneProperty);
    p->numNumbers = ABMultiValueGetCount(numbers);
    p->numbersNames = new const char *[p->numNumbers];
    p->numbersValues = new const char *[p->numNumbers];
    for (CFIndex j=0; j < p->numNumbers; j++) {
      NSString *numberName = (NSString *)ABMultiValueCopyLabelAtIndex(numbers, j);
      NSString *numberValue = (NSString *)ABMultiValueCopyValueAtIndex(numbers, j);
      if ([numberName hasPrefix:@"_$!<" ])
        numberName = [numberName substringFromIndex:4 ];
      if ([numberName hasSuffix:@">!$_" ])
        numberName = [numberName substringToIndex: [numberName length] - 4];
      p->numbersNames[j] = [numberName UTF8String];
      p->numbersValues[j] = [numberValue UTF8String];
    }

    ar->results[i] = p;
  }

  CFRelease(people);
  CFRelease(addressBook);
  [pool drain];
  return 0;
}

int GetContacts_AfterResponse (eio_req * req) {
  HandleScope scope;
  ev_unref(EV_DEFAULT_UC);

  struct async_request* ar = (struct async_request*)req->data;

  if (ar->hasCb) {
    // Prepare the callback arguments
    Local<Value> argv[2];
    argv[0] = Local<Value>::New(Null());

    Local<Array> resultsArray = Array::New(ar->resultsCount);
    for (CFIndex i=0; i < ar->resultsCount; i++) {
      Contact *p = (Contact *)ar->results[i];
      // TODO: Instead of Object::New(), replace this with a JavaScript
      //       "Contact" constructor.
      Local<Object> curPerson = Object::New();
      //curPerson->Set(String::NewSymbol("_id"), Integer::New(p->recordId));
      if (p->firstName != NULL)
        curPerson->Set(String::NewSymbol("firstName"), String::NewSymbol( p->firstName ));
      if (p->middleName != NULL)
        curPerson->Set(String::NewSymbol("middleName"), String::NewSymbol( p->middleName ));
      if (p->lastName != NULL)
        curPerson->Set(String::NewSymbol("lastName"), String::NewSymbol( p->lastName ));
      if (p->organization!= NULL)
        curPerson->Set(String::NewSymbol("organization"), String::NewSymbol( p->organization ));
      if (p->jobTitle != NULL)
        curPerson->Set(String::NewSymbol("jobTitle"), String::NewSymbol( p->jobTitle ));
      if (p->department != NULL)
        curPerson->Set(String::NewSymbol("department"), String::NewSymbol( p->department ));

      // PhoneNumbers
      Local<Object> phoneNumbersObj = Object::New();
      for (int j=0; j < p->numNumbers; j++) {
        phoneNumbersObj->Set(String::NewSymbol(p->numbersNames[j]), String::NewSymbol(p->numbersValues[j]));
      }
      curPerson->Set(String::NewSymbol("numbers"), phoneNumbersObj);
      resultsArray->Set(Integer::New(i), curPerson);
      delete [] p->numbersNames;
      delete [] p->numbersValues;
      delete p;
    }
    argv[1] = resultsArray;

    // Invoke 'le callback
    TryCatch try_catch;
    ar->cb->Call(Context::GetCurrent()->Global(), 2, argv);
    if (try_catch.HasCaught()) {
      FatalException(try_catch);
    }
    ar->cb.Dispose();
  }

  delete [] ar->results;
  free(ar);
  return 0;
}

v8::Handle<Value> AddressBook::GetGroups(const Arguments& args) {
  HandleScope scope;

  return Undefined();
}


================================================
FILE: src/addressBook.h
================================================
#import <node.h>
#import <v8.h>
#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <AddressBook/AddressBook.h>
#import "addressBook-Record.h"
#import "addressBook-Contact.h"

int GetContacts_DoRequest (eio_req *);
int GetContacts_AfterResponse (eio_req *);

struct async_request {
  v8::Persistent<v8::Function> cb;
  bool hasCb;
  CFIndex resultsCount;
  // 'results' is an array of pointers to "Record" instances
  Record **results;
};

class AddressBook {
  public:
    static void Init(v8::Handle<v8::Object> target);
    static v8::Handle<v8::Value> GetContacts(const v8::Arguments& args);
    static v8::Handle<v8::Value> GetGroups(const v8::Arguments& args);
    static v8::Handle<v8::Value> Save(const v8::Arguments& args);
};


================================================
FILE: src/binding.cc
================================================
#import <v8.h>
#import <node.h>
#import <AudioToolbox/AudioToolbox.h>
#import <UIKit/UIKit.h>
#import "addressBook.h"
#import "graphicServices.h"
#import "notifications.h"
#import "telephony.h"
#import "compatibility.h" // ...meh

using namespace node;
using namespace v8;

class Binding {
  public:

  static void Init(v8::Handle<Object> target) {
    HandleScope scope;

    UIDevice *aDevice = [UIDevice currentDevice];
    [aDevice beginGeneratingDeviceOrientationNotifications];
    [aDevice setBatteryMonitoringEnabled:YES];

    NODE_SET_METHOD(target, "vibrate", Vibrate);
    NODE_SET_METHOD(target, "device", Device);
    NODE_SET_METHOD(target, "sendSMS", Telephony::SendSMS);
  }

  static v8::Handle<Value> Vibrate(const Arguments& args) {
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
    return Undefined();
  }

  static v8::Handle<Value> Device(const Arguments& args) {
    HandleScope scope;
    Local<Object> result = Object::New();

    UIDevice *aDevice = [UIDevice currentDevice];

    result->Set(String::NewSymbol("batteryLevel"), Number::New([aDevice batteryLevel]));
    result->Set(String::NewSymbol("batteryState"), Integer::New([aDevice batteryState]));
    result->Set(String::NewSymbol("model"), String::NewSymbol([[aDevice model] UTF8String]));
    result->Set(String::NewSymbol("localizedModel"), String::NewSymbol([[aDevice localizedModel] UTF8String]));
    result->Set(String::NewSymbol("orientation"), Integer::New([aDevice orientation]));
    result->Set(String::NewSymbol("name"), String::NewSymbol([[aDevice name] UTF8String]));
    result->Set(String::NewSymbol("systemName"), String::NewSymbol([[aDevice systemName] UTF8String]));
    result->Set(String::NewSymbol("systemVersion"), String::NewSymbol([[aDevice systemVersion] UTF8String]));
    result->Set(String::NewSymbol("uniqueIdentifier"), String::NewSymbol([[aDevice uniqueIdentifier] UTF8String]));

    return scope.Close(result);
  }

};

extern "C" {
  static void init (v8::Handle<Object> target) {
    Binding::Init(target);
    AddressBook::Init(target);
    GraphicServices::Init(target);
    Notifications::Init(target);
  }

  NODE_MODULE(binding, init);
}


================================================
FILE: src/compatibility.h
================================================
#import <UIKit/UIKit.h>

// Add newer API stuff. This shouldn't be needed, unfortunately
// it seems as though the 'gcc' from Cydia for iOS (my version at least)
// doesn't support either the 'IPHONEOS_DEPLOYMENT_TARGET' env var nor
// the '-miphoneos-version-min' compiler flag... lame...
typedef enum {
    UIDeviceBatteryStateUnknown,
    UIDeviceBatteryStateUnplugged,   // on battery, discharging
    UIDeviceBatteryStateCharging,    // plugged in, less than 100%
    UIDeviceBatteryStateFull,        // plugged in, at 100%
} UIDeviceBatteryState;              // available in iPhone 3.0

@interface UIDevice ()
  - (void) setOrientation:(UIInterfaceOrientation)orientation;
  @property(getter=isBatteryMonitoringEnabled) BOOL batteryMonitoringEnabled;
  @property(readonly) UIDeviceBatteryState          batteryState;
  @property(readonly) float                         batteryLevel;
@end


================================================
FILE: src/graphicServices.cc
================================================
#import "graphicServices.h"

using namespace node;
using namespace v8;

void GraphicServices::Init(v8::Handle<Object> target) {
  HandleScope scope;

  NODE_SET_METHOD(target, "lockScreen", LockScreen);
  NODE_SET_METHOD(target, "quitTopApplication", QuitTopApplication);
}

/* Same effect as pressing the top "Lock" button on your iDevice. No arguments required. */
v8::Handle<Value> GraphicServices::LockScreen(const Arguments& args) {
  HandleScope scope;
  GSEventLockDevice();
  GSEventLockDevice();
  return scope.Close(Undefined());
}

/* Quits the currently visible App. No arguments required. */
v8::Handle<Value> GraphicServices::QuitTopApplication(const Arguments& args) {
  HandleScope scope;
  GSEventQuitTopApplication();
  return scope.Close(Undefined());
}


================================================
FILE: src/graphicServices.h
================================================
#import <v8.h>
#import <node.h>
#import <GraphicsServices/GraphicsServices.h>

/* Private, undocumented APIs */
//void GSEventLockDevice();
//void GSEventQuitTopApplication();

class GraphicServices {
  public:
    static void Init(v8::Handle<v8::Object> target);
    static v8::Handle<v8::Value> LockScreen(const v8::Arguments& args);
    static v8::Handle<v8::Value> QuitTopApplication(const v8::Arguments& args);
};


================================================
FILE: src/notifications.cc
================================================
#import "notifications.h"

using namespace node;
using namespace v8;


// Initialize the notification-related exports
void Notifications::Init(v8::Handle<Object> target) {
  HandleScope scope;
  NODE_SET_METHOD(target, "createNotification", Notifications::createNotification);
}

// From v8's 'shell.cc'
const char* ToCString(const v8::String::Utf8Value& value) {
  return *value ? *value : "<string conversion failed>";
}

// 'createNotification' begins the async notification process. The user should pass
// an "options" Object and an optional callback function (to examine the results of
// the notification).
v8::Handle<Value> Notifications::createNotification(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 1) {
    return ThrowException(Exception::TypeError(String::New("An 'options' Object is required")));
  }

  Local<Object> options = args[0]->ToObject();

  // This is the struct that gets passed around EIO
  notification_request* nr = (notification_request*) malloc(sizeof(struct notification_request));

  CFOptionFlags flags = 0;
  flags |= kCFUserNotificationPlainAlertLevel;

  // 'dict' will contain the processed 'options', so the alert message is formatted the way the user asked
  CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

  // This is always set. It appears to be needed for daemon processes on iOS...
  CFDictionaryAddValue(dict, kCFUserNotificationAlertTopMostKey, kCFBooleanTrue);

  if (options->Has(String::NewSymbol("header"))) {
    String::Utf8Value headerStr(options->Get(String::NewSymbol("header")));
    CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFStringCreateWithCString(NULL, ToCString(headerStr), kCFStringEncodingUTF8) );
  }
  if (options->Has(String::NewSymbol("message"))) {
    String::Utf8Value messageStr(options->Get(String::NewSymbol("message")));
    CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, CFStringCreateWithCString(NULL, ToCString(messageStr), kCFStringEncodingUTF8) );
  }
  if (options->Has(String::NewSymbol("defaultButton"))) {
    Local<Value> defaultButtonVal = options->Get(String::NewSymbol("defaultButton"));
    if (defaultButtonVal->IsNull()) {
      flags |= kCFUserNotificationNoDefaultButtonFlag;
    } else {
      String::Utf8Value firstButtonStr(defaultButtonVal);
      CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFStringCreateWithCString(NULL, ToCString(firstButtonStr), kCFStringEncodingUTF8) );
    }
  }
  if (options->Has(String::NewSymbol("alternateButton"))) {
    String::Utf8Value secondButtonStr(options->Get(String::NewSymbol("alternateButton")));
    CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFStringCreateWithCString(NULL, ToCString(secondButtonStr), kCFStringEncodingUTF8) );
  }
  if (options->Has(String::NewSymbol("otherButton"))) {
    String::Utf8Value thirdButtonStr(options->Get(String::NewSymbol("otherButton")));
    CFDictionaryAddValue(dict, kCFUserNotificationOtherButtonTitleKey, CFStringCreateWithCString(NULL, ToCString(thirdButtonStr), kCFStringEncodingUTF8) );
  }

  CFTimeInterval timeout = 0; // 0 = disabled, by default
  if (options->Has(String::NewSymbol("timeout"))) {
    timeout = options->Get(String::NewSymbol("timeout"))->Int32Value();
  }

  CFUserNotificationRef notif = CFUserNotificationCreate(NULL, timeout, flags, &nr->error, dict);
  nr->notif = notif;

  nr->hasCb = false;
  if (args.Length() >= 2) {
    Local<Function> cb = Local<Function>::Cast(args[1]);
    nr->cb = Persistent<Function>::New(cb);
    nr->hasCb = true;
  }

  eio_custom(CreateNotification_WaitForResponse, EIO_PRI_DEFAULT, CreateNotification_AfterResponse, nr);
  ev_ref(EV_DEFAULT_UC);

  return Undefined();
}

// This is the function that gets called on the thread pool. It blocks for as long as
// the notification is active on the iDevice.
int CreateNotification_WaitForResponse (eio_req * req) {
  struct notification_request * nr = (struct notification_request *)req->data;
  req->result = CFUserNotificationReceiveResponse(nr->notif, 0, &nr->options);
  return 0;
}

// This function gets called on node's main thread after the notification has been
// dismissed. This function collects the results of the dismissal into a 'results'
// Object that gets passed to the JS callback function.
int CreateNotification_AfterResponse (eio_req * req) {
  HandleScope scope;
  ev_unref(EV_DEFAULT_UC);

  struct notification_request * nr = (struct notification_request *)req->data;

  if (nr->hasCb) {
    // Prepare the callback arguments
    Local<Value> argv[2];
    argv[0] = Local<Value>::New(Null());

    Local<Object> results = Object::New();
    results->Set(String::NewSymbol("result"), Integer::New( req->result ));
    results->Set(String::NewSymbol("error"), Integer::New( nr->error ));
    results->Set(String::NewSymbol("options"), Integer::New( nr->options ));
    argv[1] = results;

    // Invoke 'le callback
    TryCatch try_catch;
    nr->cb->Call(Context::GetCurrent()->Global(), 2, argv);
    if (try_catch.HasCaught()) {
      FatalException(try_catch);
    }
    nr->cb.Dispose();
  }

  free(nr);
  return 0;
}


================================================
FILE: src/notifications.h
================================================
#import <node.h>
#import <v8.h>
#import <unistd.h>
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <CoreFoundation/CFUserNotification.h>

int CreateNotification_WaitForResponse (eio_req *);
int CreateNotification_AfterResponse (eio_req *);

struct notification_request {
  CFOptionFlags options;
  CFUserNotificationRef notif;
  SInt32 error;
  v8::Persistent<v8::Function> cb;
  bool hasCb;
};

class Notifications {
  public:
    static void Init(v8::Handle<v8::Object> target);
    static v8::Handle<v8::Value> createNotification(const v8::Arguments& args);
};


================================================
FILE: src/telephony.cc
================================================
#import "telephony.h"

using namespace node;
using namespace v8;

//extern id CTTelephonyCenterGetDefault();
//extern void CTTelephonyCenterAddObserver(void*,id,CFNotificationCallback,NSString*,void*,int);

//static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
//  NSLog(@"Name: %@", name);
//}

//v8::Handle<Value> Telephony::Init(const Arguments& args) {
//  id ct = CTTelephonyCenterGetDefault();
//  CTTelephonyCenterAddObserver(ct, NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold);
//  return Undefined();
//}

// Sends an SMS. First argument should be the number, second arg should be the message.
// TODO: All support for a 'sent' callback or something... I'm not sure if this is possible...
v8::Handle<Value> Telephony::SendSMS(const Arguments& args) {
  HandleScope scope;
  if (args.Length() != 2) {
    return ThrowException(Exception::TypeError(
                  String::New("\"sendSMS\" requires two String arguments, a number and the message")));
  }
  NSString* number = [NSString stringWithUTF8String: *String::Utf8Value(args[0]->ToString()) ];
  NSString* message = [NSString stringWithUTF8String: *String::Utf8Value(args[1]->ToString()) ];
  BOOL success = [[CTMessageCenter sharedMessageCenter] sendSMSWithText:message serviceCenter:nil toAddress:number];
  return scope.Close(v8::Boolean::New(success));
}


================================================
FILE: src/telephony.h
================================================
#import <node.h>
#import <v8.h>
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <CoreTelephony/CoreTelephony.h>

class Telephony {
  public:
    static v8::Handle<v8::Value> Init(const v8::Arguments& args);
    static v8::Handle<v8::Value> SendSMS(const v8::Arguments& args);
};


================================================
FILE: tests/addressBook-test.js
================================================
var iOS = require('../');

// Get an Array of all the Contacts in the address book
iOS.AddressBook.getContacts(function(err, results) {
  if (err) throw err;
  console.log(results);
  console.log(results.length);
});


================================================
FILE: tests/notification-test.js
================================================
var iOS = require('../');

iOS.createNotification({
  message: 'This message will have NO buttons!',
  defaultButton: null,
  timeout: 3
}, function(err, res) {
  console.log(arguments);

  iOS.createNotification({
    header: 'test header',
    message: 'This is a message!!',
    defaultButton: 'first',
    alternateButton: 'second'
  }, console.log);
});



================================================
FILE: wscript
================================================
def set_options(opt):
  opt.tool_options("compiler_cxx")

def configure(conf):
  conf.check_tool("compiler_cxx")
  conf.check_tool("node_addon")

def build(bld):
  bld.env["IPHONEOS_DEPLOYMENT_TARGET"] = "3.0"
  obj = bld.new_task_gen("cxx", "shlib", "node_addon")
  obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall", "-ObjC++", "-F/System/Library/PrivateFrameworks"]
  obj.ldflags = ["-F/System/Library/PrivateFrameworks", "-mmacosx-version-min=10.5"]
  obj.framework = ['Foundation', 'AddressBook', 'AudioToolbox', 'UIKit', 'CoreFoundation', 'CoreTelephony', 'GraphicsServices']
  obj.target = "binding"
  obj.source = ["src/binding.cc", "src/addressBook.cc", "src/graphicServices.cc", "src/notifications.cc", "src/telephony.cc"]
Download .txt
gitextract_sem97i7t/

├── .gitignore
├── LICENSE
├── README.md
├── build/
│   ├── .gitignore
│   └── default/
│       ├── .gitignore
│       └── binding.node
├── index.js
├── package.json
├── src/
│   ├── addressBook-Contact.h
│   ├── addressBook-Record.h
│   ├── addressBook.cc
│   ├── addressBook.h
│   ├── binding.cc
│   ├── compatibility.h
│   ├── graphicServices.cc
│   ├── graphicServices.h
│   ├── notifications.cc
│   ├── notifications.h
│   ├── telephony.cc
│   └── telephony.h
├── tests/
│   ├── addressBook-test.js
│   └── notification-test.js
└── wscript
Download .txt
SYMBOL INDEX (22 symbols across 10 files)

FILE: src/addressBook-Contact.h
  function class (line 3) | class Contact : public Record {

FILE: src/addressBook-Record.h
  function class (line 4) | class Record : public node::ObjectWrap {

FILE: src/addressBook.cc
  type async_request (line 30) | struct async_request
  type async_request (line 30) | struct async_request
  type async_request (line 30) | struct async_request
  function GetContacts_DoRequest (line 51) | int GetContacts_DoRequest (eio_req * req) {
  function GetContacts_AfterResponse (line 113) | int GetContacts_AfterResponse (eio_req * req) {

FILE: src/addressBook.h
  type async_request (line 12) | struct async_request {
  function class (line 20) | class AddressBook {

FILE: src/binding.cc
  class Binding (line 14) | class Binding {
    method Init (line 17) | static void Init(v8::Handle<Object> target) {
    method Vibrate (line 29) | static v8::Handle<Value> Vibrate(const Arguments& args) {
    method Device (line 34) | static v8::Handle<Value> Device(const Arguments& args) {
  function init (line 56) | static void init (v8::Handle<Object> target) {

FILE: src/compatibility.h
  type UIDeviceBatteryState (line 7) | typedef enum {

FILE: src/graphicServices.h
  function class (line 9) | class GraphicServices {

FILE: src/notifications.cc
  type notification_request (line 31) | struct notification_request
  function CreateNotification_WaitForResponse (line 91) | int CreateNotification_WaitForResponse (eio_req * req) {
  function CreateNotification_AfterResponse (line 100) | int CreateNotification_AfterResponse (eio_req * req) {

FILE: src/notifications.h
  type notification_request (line 11) | struct notification_request {
  function class (line 19) | class Notifications {

FILE: src/telephony.h
  function class (line 7) | class Telephony {
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (31K chars).
[
  {
    "path": ".gitignore",
    "chars": 27,
    "preview": ".lock-wscript\nnode_modules\n"
  },
  {
    "path": "LICENSE",
    "chars": 1058,
    "preview": "Copyright (c) 2011 Nathan Rajlich\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this "
  },
  {
    "path": "README.md",
    "chars": 4795,
    "preview": "node-iOS\n========\n### Native [node][Node] bindings to iOS functionality (vibrate, acceleromoter, geoservices, etc.)\n\n\nTh"
  },
  {
    "path": "build/.gitignore",
    "chars": 30,
    "preview": ".wafpickle-7\nc4che\nconfig.log\n"
  },
  {
    "path": "build/default/.gitignore",
    "chars": 4,
    "preview": "src\n"
  },
  {
    "path": "index.js",
    "chars": 1130,
    "preview": "// We can require 'autoreleasepool' and the NSAutoreleasePool for the main\n// (node's) thread will be automatically take"
  },
  {
    "path": "package.json",
    "chars": 513,
    "preview": "{\n  \"author\": \"Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)\",\n  \"name\": \"iOS\",\n  \"description\": \"Nat"
  },
  {
    "path": "src/addressBook-Contact.h",
    "chars": 397,
    "preview": "#import \"addressBook-Record.h\"\n\nclass Contact : public Record {\n  public:\n    // Person\n    const char *firstName;\n    c"
  },
  {
    "path": "src/addressBook-Record.h",
    "chars": 149,
    "preview": "#import <node.h>\n#import <AddressBook/AddressBook.h>\n\nclass Record : public node::ObjectWrap {\n  public:\n    ABRecordID "
  },
  {
    "path": "src/addressBook.cc",
    "chars": 6908,
    "preview": "// http://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABAddressBookRef_iPhoneOS/Reference/refere"
  },
  {
    "path": "src/addressBook.h",
    "chars": 768,
    "preview": "#import <node.h>\n#import <v8.h>\n#import <CoreFoundation/CoreFoundation.h>\n#import <Foundation/Foundation.h>\n#import <Add"
  },
  {
    "path": "src/binding.cc",
    "chars": 2178,
    "preview": "#import <v8.h>\n#import <node.h>\n#import <AudioToolbox/AudioToolbox.h>\n#import <UIKit/UIKit.h>\n#import \"addressBook.h\"\n#i"
  },
  {
    "path": "src/compatibility.h",
    "chars": 895,
    "preview": "#import <UIKit/UIKit.h>\n\n// Add newer API stuff. This shouldn't be needed, unfortunately\n// it seems as though the 'gcc'"
  },
  {
    "path": "src/graphicServices.cc",
    "chars": 773,
    "preview": "#import \"graphicServices.h\"\n\nusing namespace node;\nusing namespace v8;\n\nvoid GraphicServices::Init(v8::Handle<Object> ta"
  },
  {
    "path": "src/graphicServices.h",
    "chars": 419,
    "preview": "#import <v8.h>\n#import <node.h>\n#import <GraphicsServices/GraphicsServices.h>\n\n/* Private, undocumented APIs */\n//void G"
  },
  {
    "path": "src/notifications.cc",
    "chars": 5239,
    "preview": "#import \"notifications.h\"\n\nusing namespace node;\nusing namespace v8;\n\n\n// Initialize the notification-related exports\nvo"
  },
  {
    "path": "src/notifications.h",
    "chars": 600,
    "preview": "#import <node.h>\n#import <v8.h>\n#import <unistd.h>\n#import <Foundation/Foundation.h>\n#import <CoreFoundation/CoreFoundat"
  },
  {
    "path": "src/telephony.cc",
    "chars": 1427,
    "preview": "#import \"telephony.h\"\n\nusing namespace node;\nusing namespace v8;\n\n//extern id CTTelephonyCenterGetDefault();\n//extern vo"
  },
  {
    "path": "src/telephony.h",
    "chars": 315,
    "preview": "#import <node.h>\n#import <v8.h>\n#import <Foundation/Foundation.h>\n#import <CoreFoundation/CoreFoundation.h>\n#import <Cor"
  },
  {
    "path": "tests/addressBook-test.js",
    "chars": 217,
    "preview": "var iOS = require('../');\n\n// Get an Array of all the Contacts in the address book\niOS.AddressBook.getContacts(function("
  },
  {
    "path": "tests/notification-test.js",
    "chars": 360,
    "preview": "var iOS = require('../');\n\niOS.createNotification({\n  message: 'This message will have NO buttons!',\n  defaultButton: nu"
  },
  {
    "path": "wscript",
    "chars": 761,
    "preview": "def set_options(opt):\n  opt.tool_options(\"compiler_cxx\")\n\ndef configure(conf):\n  conf.check_tool(\"compiler_cxx\")\n  conf."
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the TooTallNate/node-iOS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (28.3 KB), approximately 7.7k tokens, and a symbol index with 22 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!