Going to have to continue to use the old MAC web-container-based UI on Macs prior to 10.10 even when the new UI comes out, and the new UI is not ready yet anyway, so resurrect this.

This commit is contained in:
Adam Ierymenko
2016-06-29 12:00:16 -07:00
parent 19735e7050
commit df00d3b046
74 changed files with 7397 additions and 0 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