Pass authtoken in from user, and add a wrapper for web UI on Mac.

This commit is contained in:
Adam Ierymenko 2015-05-06 20:41:51 -07:00
commit 5b54612d91
68 changed files with 7537 additions and 1 deletions

View file

@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
#import "WindowController.h"
@interface App : NSObject {
}
@property (nonatomic, retain) WebView *webView;
- (id) initWithWebView:(WebView *)view;
- (void) terminate;
- (void) activate;
- (void) hide;
- (void) unhide;
- (void) beep;
- (void) bounce;
- (void) setCustomUserAgent:(NSString *)userAgentString;
- (NSNumber*) systemIdleTime;
@end

View file

@ -0,0 +1,128 @@
#import "App.h"
#import "JSEventHelper.h"
@implementation App
@synthesize webView;
- (id) initWithWebView:(WebView *) view{
self = [super init];
if (self) {
self.webView = view;
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: @selector(receiveSleepNotification:)
name: NSWorkspaceWillSleepNotification object: NULL];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: @selector(receiveWakeNotification:)
name: NSWorkspaceDidWakeNotification object: NULL];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: @selector(receiveActivateNotification:)
name: NSWorkspaceDidActivateApplicationNotification object: NULL];
}
return self;
}
- (void) terminate {
[NSApp terminate:nil];
}
- (void) activate {
[NSApp activateIgnoringOtherApps:YES];
}
- (void) hide {
[NSApp hide:nil];
}
- (void) unhide {
[NSApp unhide:nil];
}
- (void)beep {
NSBeep();
}
- (void) bounce {
[NSApp requestUserAttention:NSInformationalRequest];
}
- (void)setCustomUserAgent:(NSString *)userAgentString {
[self.webView setCustomUserAgent: userAgentString];
}
- (void) open:(NSString*)url {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
}
- (void) launch:(NSString *)name {
[[NSWorkspace sharedWorkspace] launchApplication:name];
}
- (void)receiveSleepNotification:(NSNotification*)note{
[JSEventHelper triggerEvent:@"sleep" forWebView:self.webView];
}
- (void) receiveWakeNotification:(NSNotification*)note{
[JSEventHelper triggerEvent:@"wake" forWebView:self.webView];
}
- (void) receiveActivateNotification:(NSNotification*)notification{
NSDictionary* userInfo = [notification userInfo];
NSRunningApplication* runningApplication = [userInfo objectForKey:NSWorkspaceApplicationKey];
if (runningApplication) {
NSMutableDictionary* applicationDidGetFocusDict = [[NSMutableDictionary alloc] initWithCapacity:2];
[applicationDidGetFocusDict setObject:runningApplication.localizedName
forKey:@"localizedName"];
[applicationDidGetFocusDict setObject:[runningApplication.bundleURL absoluteString]
forKey:@"bundleURL"];
[JSEventHelper triggerEvent:@"appActivated" withArgs:applicationDidGetFocusDict forWebView:self.webView];
}
}
/*
To get the elapsed time since the previous input eventkeyboard, mouse, or tabletspecify kCGAnyInputEventType.
*/
- (NSNumber*)systemIdleTime {
CFTimeInterval timeSinceLastEvent = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGAnyInputEventType);
return [NSNumber numberWithDouble:timeSinceLastEvent];
}
+ (NSString*) webScriptNameForSelector:(SEL)selector
{
id result = nil;
if (selector == @selector(open:)) {
result = @"open";
} else if (selector == @selector(launch:)) {
result = @"launch";
} else if (selector == @selector(setCustomUserAgent:)) {
result = @"setCustomUserAgent";
} else if (selector == @selector(systemIdleTime)) {
result = @"systemIdleTime";
}
return result;
}
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
{
return NO;
}
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
{
return YES;
}
@end

View file

@ -0,0 +1,18 @@
//
// Command.h
// MacGap
//
// Created by Joe Hildebrand on 1/10/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <Webkit/WebScriptObject.h>
@interface Command : NSObject {
JSContextRef context;
}
- (id) initWithContext:(JSContextRef)aContext;
@end

View file

@ -0,0 +1,28 @@
//
// Command.m
// MacGap
//
// Created by Joe Hildebrand on 1/10/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import "Command.h"
#import <JavaScriptCore/JSContextRef.h>
@implementation Command
- (id) initWithContext:(JSContextRef)aContext {
self = [super init];
if (!self)
return nil;
context = aContext;
JSGlobalContextRetain((JSGlobalContextRef)context);
return self;
}
- (void)dealloc
{
if (context)
JSGlobalContextRelease((JSGlobalContextRef)context);
}
@end

View file

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface Dock : NSObject {
}
- (void) setBadge:(NSString*)value;
- (NSString *) badge;
@property (readwrite, copy) NSString *badge;
@end

View file

@ -0,0 +1,31 @@
#import "Dock.h"
@implementation Dock
@synthesize badge;
- (void) setBadge:(NSString *)value
{
NSDockTile *tile = [[NSApplication sharedApplication] dockTile];
[tile setBadgeLabel:value];
}
- (NSString *) badge
{
NSDockTile *tile = [[NSApplication sharedApplication] dockTile];
return [tile badgeLabel];
}
#pragma mark WebScripting Protocol
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
{
return NO;
}
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
{
return NO;
}
@end

View file

@ -0,0 +1,31 @@
//
// MenuItemProxy.h
// MacGap
//
// Created by Joe Hildebrand on 1/15/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import "Command.h"
#import "CallbackDelegate.h"
@class MenuProxy;
@interface MenuItemProxy : Command {
NSMenuItem *item;
CallbackDelegate *callback;
}
+ (MenuItemProxy*) proxyWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem;
- (MenuProxy*)addSubmenu;
- (void) remove;
- (void) setCallback:(WebScriptObject*)aCallback;
- (void) setKey:(NSString*)keyCommand;
- (void) setTitle:(NSString*)title;
- (void) enable;
- (void) disable;
- (MenuProxy*)submenu;
@end

View file

@ -0,0 +1,150 @@
//
// MenuItemProxy.m
// MacGap
//
// Created by Joe Hildebrand on 1/15/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import "MenuItemProxy.h"
#import "MenuProxy.h"
@implementation MenuItemProxy
- (id) initWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem
{
NSAssert(anItem, @"anItem required");
self = [super initWithContext:aContext];
if (!self)
return nil;
item = anItem;
item.representedObject = self;
return self;
}
+ (MenuItemProxy*) proxyWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem
{
MenuItemProxy *proxy = [anItem representedObject];
if (proxy)
{
NSLog(@"MIP Cache hit");
NSAssert([proxy class] == [MenuItemProxy class], @"Bad proxy");
return proxy;
}
return [[MenuItemProxy alloc] initWithContext:aContext andMenuItem:anItem];
}
- (NSString*) description
{
return [item description];
}
- (MenuProxy*)addSubmenu
{
NSMenu *s = [item submenu];
if (!s)
{
s = [[NSMenu alloc] initWithTitle:@"FFFFFFOOOOO"];
[item setSubmenu:s];
}
return [MenuProxy proxyWithContext:context andMenu:s];
}
- (void) remove
{
NSMenu *menu = [item menu];
[menu removeItem:item];
}
- (void)callCallback:(id)sender
{
[callback callWithParams:[sender title], nil];
}
- (void) setCallback:(WebScriptObject*)aCallback
{
NSAssert(item, @"item required");
callback = [[CallbackDelegate alloc] initWithContext:context forCallback:aCallback];
[item setAction:@selector(callCallback:)];
[item setTarget:self];
}
- (void)setKey:(NSString*)keyCommand
{
NSString *aKey = [MenuProxy getKeyFromString:keyCommand];
[item setKeyEquivalent:aKey];
NSUInteger modifiers = [MenuProxy getModifiersFromString:keyCommand];
[item setKeyEquivalentModifierMask:modifiers];
}
- (void) setTitle:(NSString*)title
{
[item setTitle:title];
}
- (MenuProxy*)submenu;
{
// TODO: make this work as a property
NSMenu *s = [item submenu];
if (!s)
return nil;
return [MenuProxy proxyWithContext:context andMenu:s];
}
- (void) enable
{
[item setEnabled:YES];
}
- (void) disable
{
[item setEnabled:NO];
}
#pragma mark WebScripting protocol
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
{
return [self webScriptNameForSelector:selector] == nil;
}
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
{
return YES;
}
+ (NSString*) webScriptNameForSelector:(SEL)selector
{
id result = nil;
if (selector == @selector(addSubmenu)) {
result = @"addSubmenu";
}
else if (selector == @selector(remove)) {
result = @"remove";
}
else if (selector == @selector(setCallback:)) {
result = @"setCallback";
}
else if (selector == @selector(setKey:)) {
result = @"setKey";
}
else if (selector == @selector(setTitle:)) {
result = @"setTitle";
}
else if (selector == @selector(submenu)) {
result = @"submenu";
}
else if (selector == @selector(enable)) {
result = @"enable";
}
else if (selector == @selector(disable)) {
result = @"disable";
}
return result;
}
@end

View file

@ -0,0 +1,31 @@
//
// MenuProxy.h
// MacGap
//
// Created by Joe Hildebrand on 1/14/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import "Command.h"
@class MenuItemProxy;
@interface MenuProxy : Command {
NSMenu *menu;
}
+ (MenuProxy*)proxyWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu;
- (MenuItemProxy*)addItemWithTitle:(NSString*)title
keyEquivalent:(NSString*)aKey
callback:(WebScriptObject*)aCallback
atIndex:(NSInteger)index;
- (MenuItemProxy*)addSeparator;
- (MenuItemProxy*)itemForKey:(id)key;
- (MenuProxy*)removeItem:(id)key;
+ (NSString*)getKeyFromString:(NSString*)keyCommand;
+ (NSUInteger*)getModifiersFromString:(NSString*)keyCommand;
@end

View file

@ -0,0 +1,233 @@
//
// MenuProxy.m
// MacGap
//
// Created by Joe Hildebrand on 1/14/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import <objc/runtime.h>
#import <JavaScriptCore/JavaScript.h>
#import "MenuProxy.h"
#import "MenuItemProxy.h"
static char REPRESENTED_OBJECT;
@interface NSMenu (represented)
@property (strong) id representedObject;
@end
@implementation NSMenu (represented)
- (id) representedObject
{
return objc_getAssociatedObject(self, &REPRESENTED_OBJECT);
}
- (void) setRepresentedObject:(id)representedObject
{
objc_setAssociatedObject(self,
&REPRESENTED_OBJECT,
representedObject,
OBJC_ASSOCIATION_RETAIN);
}
@end
@implementation MenuProxy
- (id) initWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu
{
self = [super initWithContext:aContext];
if (!self)
return nil;
menu = aMenu;
menu.representedObject = self;
return self;
}
+ (MenuProxy*)proxyWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu
{
// singleton-ish.
MenuProxy *ret = [aMenu representedObject];
if (ret)
{
NSLog(@"MP cache hit");
return ret;
}
return [[MenuProxy alloc] initWithContext:aContext andMenu:aMenu];
}
- (void) dealloc
{
menu.representedObject = nil;
}
- (NSString*) description
{
return [menu description];
}
static BOOL isNullish(id o)
{
if (!o)
return YES;
if ([o isKindOfClass:[WebUndefined class]])
return YES;
return NO;
}
- (MenuItemProxy*)addItemWithTitle:(NSString*)title
keyEquivalent:(NSString*)keyCommand
callback:(WebScriptObject*)aCallback
atIndex:(NSInteger)index
{
if (isNullish(title))
title = @"";
NSString *aKey = [MenuProxy getKeyFromString:keyCommand];
NSMenuItem *item = nil;
if(index) {
item = [menu insertItemWithTitle:title action:nil keyEquivalent:aKey atIndex:index ];
} else {
item = [menu addItemWithTitle:title action:nil keyEquivalent:aKey ];
}
// Set the modifiers.
NSUInteger modifiers = [MenuProxy getModifiersFromString:keyCommand];
[item setKeyEquivalentModifierMask:modifiers];
if(!menu.supermenu) {
NSMenu *s = [[NSMenu alloc] initWithTitle:title];
[item setSubmenu:s];
}
MenuItemProxy *mip = [MenuItemProxy proxyWithContext:context andMenuItem:item];
if (!isNullish(aCallback))
[mip setCallback:aCallback];
return mip;
}
+ (NSString*)getKeyFromString:(NSString*)keyCommand {
if (isNullish(keyCommand))
keyCommand = @"";
// Obtain the key (if there are modifiers, it will be the last character).
NSString *aKey = @"";
if ([keyCommand length] > 0) {
aKey = [keyCommand substringFromIndex:[keyCommand length] - 1];
}
return aKey;
}
+ (NSUInteger*)getModifiersFromString:(NSString*)keyCommand {
// aKeys may optionally specify one or more modifiers.
NSUInteger modifiers = 0;
if ([keyCommand rangeOfString:@"caps"].location != NSNotFound) modifiers += NSAlphaShiftKeyMask;
if ([keyCommand rangeOfString:@"shift"].location != NSNotFound) modifiers += NSShiftKeyMask;
if ([keyCommand rangeOfString:@"cmd"].location != NSNotFound) modifiers += NSCommandKeyMask;
if ([keyCommand rangeOfString:@"ctrl"].location != NSNotFound) modifiers += NSControlKeyMask;
if ([keyCommand rangeOfString:@"opt"].location != NSNotFound) modifiers += NSAlternateKeyMask;
if ([keyCommand rangeOfString:@"alt"].location != NSNotFound) modifiers += NSAlternateKeyMask;
return modifiers;
}
- (MenuItemProxy*)addSeparator
{
NSMenuItem *sep = [NSMenuItem separatorItem];
[menu addItem:sep];
return [MenuItemProxy proxyWithContext:context andMenuItem:sep];
}
- (MenuItemProxy*)itemForKey:(id)key
{
if (isNullish(key))
return nil;
NSMenuItem *item = nil;
if ([key isKindOfClass:[NSNumber class]])
{
item = [menu itemAtIndex:[key intValue]];
}
else if ([key isKindOfClass:[NSString class]])
{
item = [menu itemWithTitle:key];
if (!item)
{
// Try again, with ... appended. e.g. "Save..."
item = [menu itemWithTitle:
[key stringByAppendingString:@"\u2026"]];
}
}
if (!item)
return nil;
return [MenuItemProxy proxyWithContext:context andMenuItem:item];
}
- (MenuProxy*)removeItem:(id)key
{
if (isNullish(key))
return nil;
NSMenuItem *item = nil;
if ([key isKindOfClass:[NSNumber class]])
{
item = [menu itemAtIndex:[key intValue]];
}
else if ([key isKindOfClass:[NSString class]])
{
item = [menu itemWithTitle:key];
if (!item)
{
// Try again, with ... appended. e.g. "Save..."
item = [menu itemWithTitle:
[key stringByAppendingString:@"\u2026"]];
}
}
if (!item)
return nil;
[menu removeItem:item];
return [MenuProxy proxyWithContext:context andMenu:menu];
}
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
{
return [self webScriptNameForSelector:selector] == nil;
}
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
{
return YES;
}
+ (NSString*) webScriptNameForSelector:(SEL)selector
{
id result = nil;
if (selector == @selector(addItemWithTitle:keyEquivalent:callback:atIndex:)) {
result = @"addItem";
}
else if (selector == @selector(addSeparator)) {
result = @"addSeparator";
}
else if (selector == @selector(itemForKey:)) {
result = @"getItem";
}
else if (selector == @selector(removeItem:)) {
result = @"removeMenu";
}
return result;
}
@end

View file

@ -0,0 +1,26 @@
//
// Notice.h
// MacGap
//
// Created by Christian Sullivan on 7/26/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "WindowController.h"
#define APP_NOTICE_NOTIFICATION @"Notice"
@interface Notice : NSObject <NSUserNotificationCenterDelegate> {
}
@property (nonatomic, retain) WebView *webView;
- (id) initWithWebView:(WebView *)view;
- (void) notify:(NSDictionary*)message;
- (void) close:(NSString*)notificationId;
+ (BOOL) available;
@end

View file

@ -0,0 +1,108 @@
//
// Notice.m
// MacGap
//
// Created by Christian Sullivan on 7/26/12.
// Copyright (c) 2012 Twitter. All rights reserved.
//
#import "Notice.h"
#import "JSEventHelper.h"
@implementation Notice
- (id) initWithWebView:(WebView*)view
{
if(self = [super init]) {
self.webView = view;
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
}
return self;
}
- (void) notify:(NSDictionary *)message {
NSUserNotification *notification = [[NSUserNotification alloc] init];
[notification setTitle:[message valueForKey:@"title"]];
[notification setInformativeText:[message valueForKey:@"content"]];
[notification setDeliveryDate:[NSDate dateWithTimeInterval:0 sinceDate:[NSDate date]]];
BOOL playSound = true; // optional parameter, false only when {sound: false}
@try {
NSNumber *s = [message valueForKey:@"sound"];
if ([[s className] isEqual: @"__NSCFBoolean"]) {
playSound = [s boolValue];
}
}
@catch (NSException *exception) {
}
if (playSound) {
[notification setSoundName:NSUserNotificationDefaultSoundName];
}
NSString *id = @""; // optional, needed for close
@try {
id = [message valueForKey:@"id"];
}
@catch (NSException *exception) {
}
[notification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:id, @"id", nil]];
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
[center scheduleNotification:notification];
}
// close all notifications with id == notificationId or close all notifications if notificationId == "*"
- (void) close:(NSString*)notificationId {
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
for(NSUserNotification * deliveredNote in center.deliveredNotifications) {
if ([notificationId isEqualToString:@"*"] || [deliveredNote.userInfo[@"id"] isEqualToString:notificationId]) {
[center removeDeliveredNotification: deliveredNote];
}
}
}
+ (BOOL) available {
if ([NSUserNotificationCenter respondsToSelector:@selector(defaultUserNotificationCenter)])
return YES;
return NO;
}
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
{
NSString *notificationId = [notification.userInfo valueForKey:@"id"];
[JSEventHelper triggerEvent:@"macgap.notify.activated" forDetail:notificationId forWebView:self.webView];
}
#pragma mark WebScripting Protocol
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
{
BOOL result = YES;
if (selector == @selector(notify:))
result = NO;
if (selector == @selector(close:))
result = NO;
return result;
}
+ (NSString*) webScriptNameForSelector:(SEL)selector
{
id result = nil;
if (selector == @selector(notify:)) {
result = @"notify";
}
if (selector == @selector(close:)) {
result = @"close";
}
return result;
}
// right now exclude all properties (eg keys)
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
{
return YES;
}
@end

View file

@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
@interface Path : NSObject {
}
- (NSString *) application;
- (NSString *) resource;
- (NSString *) documents;
- (NSString *) library;
- (NSString *) home;
- (NSString *) temp;
@property (readonly,copy) NSString* application;
@property (readonly,copy) NSString* resource;
@property (readonly,copy) NSString* documents;
@property (readonly,copy) NSString* library;
@property (readonly,copy) NSString* home;
@property (readonly,copy) NSString* temp;
@end

View file

@ -0,0 +1,53 @@
#import "Path.h"
@implementation Path
@synthesize application;
@synthesize resource;
@synthesize documents;
@synthesize library;
@synthesize home;
@synthesize temp;
- (NSString *)application {
return [[NSBundle mainBundle] bundlePath];
}
- (NSString *)resource {
return [[NSBundle mainBundle] resourcePath];
}
- (NSString *)documents {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [paths objectAtIndex:0];
}
- (NSString *)library {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSLog( @"%@", paths );
return [paths objectAtIndex:0];
}
- (NSString *)home {
return NSHomeDirectory();
}
- (NSString *)temp {
return NSTemporaryDirectory();
}
#pragma mark WebScripting Protocol
/* checks whether a selector is acceptable to be called from JavaScript */
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
{
return NO;
}
// right now exclude all properties (eg keys)
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
{
return NO;
}
@end

View file

@ -0,0 +1,17 @@
#import <Cocoa/Cocoa.h>
#import "Command.h"
#import "CallbackDelegate.h"
@interface Sound : Command {
}
// pending callbacks for sounds being played, to keep
// ARC from freeing them too early
@property (nonatomic, strong) NSMutableSet *pending;
- (void) play:(NSString*)file onComplete:(WebScriptObject*)callback;
- (void) playSystem:(NSString*)name onComplete:(WebScriptObject*)callback;
@end

View file

@ -0,0 +1,97 @@
#import "Sound.h"
@interface PlayDelegate : CallbackDelegate <NSSoundDelegate> {
}
@property (nonatomic, weak) Sound *sound;
- (id) initWithContext:(JSContextRef)aContext
forCallback:(WebScriptObject*)aCallback
withSound:(Sound*)aSound;
@end
@implementation PlayDelegate
@synthesize sound;
- (id) initWithContext:(JSContextRef)aContext
forCallback:(WebScriptObject*)aCallback
withSound:(Sound*)aSound
{
self = [super initWithContext:aContext forCallback:aCallback];
if (!self)
return nil;
sound = aSound;
return self;
}
- (void)sound:(NSSound *)aSound didFinishPlaying:(BOOL)finishedPlaying {
[self callWithParams:[aSound name], nil];
[sound.pending removeObject:self];
}
@end
@implementation Sound
@synthesize pending;
- (id) initWithContext:(JSContextRef)aContext {
self = [super initWithContext:aContext];
if (!self) {
return nil;
}
pending = [NSMutableSet new];
return self;
}
- (void) playSound:(NSSound*)sound onComplete:(WebScriptObject*)callback {
if (callback != (id)[WebUndefined undefined]) {
PlayDelegate *d = [[PlayDelegate alloc] initWithContext:context
forCallback:callback
withSound:self];
[pending addObject:d];
[sound setDelegate:d];
}
[sound play];
}
- (void) play:(NSString*)file onComplete:(WebScriptObject*)callback {
NSURL* fileUrl = [NSURL fileURLWithPath:[[Utils sharedInstance] pathForResource:file]];
DebugNSLog(@"Sound file:%@", [fileUrl description]);
NSSound* sound = [[NSSound alloc] initWithContentsOfURL:fileUrl byReference:YES];
[self playSound:sound onComplete:callback];
}
- (void) playSystem:(NSString*)name onComplete:(WebScriptObject*)callback {
NSSound *systemSound = [NSSound soundNamed:name];
[self playSound:systemSound onComplete:callback];
}
#pragma mark WebScripting Protocol
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
return [self webScriptNameForSelector:selector] == nil;
}
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name {
return YES;
}
+ (NSString*) webScriptNameForSelector:(SEL)selector {
id result = nil;
if (selector == @selector(play:onComplete:)) {
result = @"play";
}
else if (selector == @selector(playSystem:onComplete:)) {
result = @"playSystem";
}
return result;
}
@end

View file

@ -0,0 +1,43 @@
//
// UserDefaults.h
// MacGap
//
// Created by Jeff Hanbury on 16/04/2014.
// Copyright (c) 2014 Twitter. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "WindowController.h"
@interface UserDefaults : NSObject
@property (nonatomic, retain) WebView *webView;
- (id) initWithWebView:(WebView *)view;
- (NSString*) getMyDefaults;
- (NSDictionary*) myDefaultsDictionary;
- (void) removeObjectForKey:(NSString*)key;
- (NSArray*) getUserDefaultsKeys;
- (NSString*) addPrefix:(NSString*)key;
- (void) setString:(NSString*)key withValue:(NSString*)value;
- (NSString*) getString:(NSString*)key;
- (void) setInteger:(NSString*)key withValue:(NSString*)value;
- (NSNumber*) getInteger:(NSString*)key;
- (void) setBool:(NSString*)key withValue:(NSString*)value;
- (NSNumber*) getBool:(NSString*)key;
- (void) setFloat:(NSString*)key withValue:(NSString*)value;
- (NSNumber*) getFloat:(NSString*)key;
// Could also be implemented:
// setObject:forKey:
// setDouble:forKey:
// setURL:forKey:
@end

View file

@ -0,0 +1,211 @@
//
// UserDefaults.m
// MacGap
//
// Created by Jeff Hanbury on 16/04/2014.
// Copyright (c) 2014 Twitter. All rights reserved.
//
#import "UserDefaults.h"
#import "JSEventHelper.h"
@interface UserDefaults() {
}
-(void) setupNotificationCenter;
@end
@implementation UserDefaults
- (id) initWithWebView:(WebView *) view{
self = [super init];
if (self) {
self.webView = view;
[self setupNotificationCenter];
}
return self;
}
-(void) setupNotificationCenter{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(defaultsChanged:)
name:NSUserDefaultsDidChangeNotification
object:nil];
}
- (void)defaultsChanged:(NSNotification *)notification {
NSDictionary* returnDict = [self myDefaultsDictionary];
[JSEventHelper triggerEvent:@"userDefaultsChanged" withArgs:returnDict forWebView:self.webView];
}
- (NSString*) getMyDefaults {
NSDictionary* myDefaults = [self myDefaultsDictionary];
return [[Utils sharedInstance] convertDictionaryToJSON:myDefaults];
}
- (NSDictionary*) myDefaultsDictionary {
NSString* prefix = [kWebScriptNamespace stringByAppendingString:@"_"];
NSMutableDictionary* returnDict = [[NSMutableDictionary alloc] init];
// Get the user defaults.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// Build up a dictionary containing just the items beginning with our
// prefix.
for (NSString* key in [self getUserDefaultsKeys]) {
if ([key hasPrefix:prefix]) {
id val = [defaults valueForKey:key];
[returnDict setObject:val forKey:key];
}
}
return returnDict;
}
- (NSArray*) getUserDefaultsKeys {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
return [[prefs dictionaryRepresentation] allKeys];
}
- (void) removeObjectForKey:(NSString*)key {
NSString* prefixedKey;
prefixedKey = [self addPrefix:key];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:prefixedKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
// Check we have a standard prefix for JS-modified keys, for security purposes.
// If not, add it. This stops JavaScript from ever being able to modify keys
// it did not create.
- (NSString*) addPrefix:(NSString*)key {
NSString* prefix;
prefix = [kWebScriptNamespace stringByAppendingString:@"_"];
if (![key hasPrefix:prefix]) {
key = [prefix stringByAppendingString:key];
}
return key;
}
// String
- (void) setString:(NSString*)key withValue:(NSString*)value {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString* prefixedKey;
prefixedKey = [self addPrefix:key];
[prefs setObject:value forKey:prefixedKey];
}
- (NSString*) getString:(NSString *)key {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
return [prefs stringForKey:key];
}
// All the following must convert their type to NSNumber for JavaScript.
// Integer
- (void) setInteger:(NSString*)key withValue:(NSString*)value {
NSString* prefixedKey;
prefixedKey = [self addPrefix:key];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSInteger myInt = [value intValue];
[prefs setInteger:myInt forKey:prefixedKey];
}
- (NSNumber*) getInteger:(NSString *)key {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
return [NSNumber numberWithInteger:[prefs integerForKey:key]];
}
// Boolean
- (void) setBool:(NSString*)key withValue:(NSString*)value {
NSString* prefixedKey;
prefixedKey = [self addPrefix:key];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
BOOL myBool = [value boolValue];
[prefs setBool:myBool forKey:prefixedKey];
}
- (NSNumber*) getBool:(NSString *)key {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
return [NSNumber numberWithBool:[prefs boolForKey:key]];
}
// Float
- (void) setFloat:(NSString*)key withValue:(NSString*)value {
NSString* prefixedKey;
prefixedKey = [self addPrefix:key];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
float myFloat = [value floatValue];
[prefs setFloat:myFloat forKey:prefixedKey];
}
- (NSNumber*) getFloat:(NSString *)key {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
return [NSNumber numberWithFloat:[prefs floatForKey:key]];
}
#pragma mark WebScripting Protocol
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
return NO;
}
+ (NSString*) webScriptNameForSelector:(SEL)selector {
id result = nil;
if (selector == @selector(getMyDefaults)) {
result = @"getMyDefaults";
}
if (selector == @selector(removeObjectForKey:)) {
result = @"removeObjectForKey";
}
else if (selector == @selector(setString:withValue:)) {
result = @"setString";
} else if (selector == @selector(getString:)) {
result = @"getString";
}
else if (selector == @selector(setInteger:withValue:)) {
result = @"setInteger";
} else if (selector == @selector(getInteger:)) {
result = @"getInteger";
}
else if (selector == @selector(setBool:withValue:)) {
result = @"setBool";
} else if (selector == @selector(getBool:)) {
result = @"getBool";
}
else if (selector == @selector(setFloat:withValue:)) {
result = @"setFloat";
} else if (selector == @selector(getFloat:)) {
result = @"getFloat";
}
return result;
}
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name {
return NO;
}
@end

View file

@ -0,0 +1,9 @@
@interface Fonts : NSObject {
}
- (NSArray*) availableFonts;
- (NSArray*) availableFontFamilies;
- (NSArray*) availableMembersOfFontFamily:(NSString*)fontFamily;
- (CGFloat) defaultLineHeightForFont:(NSString *)theFontName ofSize:(CGFloat)theFontSize;
@end

View file

@ -0,0 +1,48 @@
#import "fonts.h"
@implementation Fonts
- (NSArray*) availableFonts {
return [[NSFontManager sharedFontManager] availableFonts];
}
- (NSArray*) availableFontFamilies {
return [[NSFontManager sharedFontManager] availableFontFamilies];
}
- (NSArray*) availableMembersOfFontFamily:(NSString *)fontFamily {
return [[NSFontManager sharedFontManager] availableMembersOfFontFamily:fontFamily];
}
- (CGFloat) defaultLineHeightForFont:(NSString*)theFontName ofSize:(CGFloat)theFontSize {
NSFont *theFont = [NSFont fontWithName:theFontName size:theFontSize];
NSLayoutManager *lm = [[NSLayoutManager alloc] init];
return [lm defaultLineHeightForFont:theFont];
}
#pragma mark WebScripting Protocol
+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
return NO;
}
+ (NSString*) webScriptNameForSelector:(SEL)selector {
id result = nil;
if (selector == @selector(availableMembersOfFontFamily:)) {
result = @"availableMembersOfFontFamily";
} else if (selector == @selector(defaultLineHeightForFont:ofSize:)) {
result = @"defaultLineHeightForFont";
}
return result;
}
+ (BOOL) isKeyExcludedFromWebScript:(const char*)name {
return NO;
}
@end