From 67ebbb2480eecae168dd625341e6c904ae522b05 Mon Sep 17 00:00:00 2001 From: Steven Wittens Date: Mon, 16 May 2011 01:10:26 -0700 Subject: [PATCH] Add front-end preferences (case sensitivity, usage logging). Add back-end config file. --- Cocoa/TermKit/English.lproj/MainMenu.xib | 44 +++- Cocoa/TermKit/English.lproj/Preferences.xib | 213 ++++++++++++++++-- .../TermKit/TermKit.xcodeproj/project.pbxproj | 22 +- Cocoa/TermKit/TermKitAppDelegate.h | 1 + Cocoa/TermKit/TermKitAppDelegate.m | 17 ++ Cocoa/TermKit/TermKitPrefsController.h | 17 ++ Cocoa/TermKit/TermKitPrefsController.m | 23 ++ Cocoa/TermKit/TermKitWebView.h | 9 +- Cocoa/TermKit/TermKitWebView.m | 66 +++++- Cocoa/TermKit/TermKitWindowController.m | 3 + HTML/client/client.js | 2 +- HTML/client/shell.js | 13 +- HTML/commandview/command.js | 1 + HTML/config.js | 7 + HTML/termkit.js | 2 +- Node/config.js | 152 +++++++++++++ Node/nodekit.js | 2 + Node/shell/autocomplete.js | 34 ++- Node/shell/processor.js | 15 +- Node/shell/shell.js | 18 +- Readme.md | 2 +- todo.txt | 7 +- 22 files changed, 617 insertions(+), 53 deletions(-) create mode 100644 Cocoa/TermKit/TermKitPrefsController.h create mode 100644 Cocoa/TermKit/TermKitPrefsController.m create mode 100644 HTML/config.js create mode 100644 Node/config.js diff --git a/Cocoa/TermKit/English.lproj/MainMenu.xib b/Cocoa/TermKit/English.lproj/MainMenu.xib index e070318..bf2db30 100644 --- a/Cocoa/TermKit/English.lproj/MainMenu.xib +++ b/Cocoa/TermKit/English.lproj/MainMenu.xib @@ -12,7 +12,6 @@ YES - YES @@ -1401,6 +1400,14 @@ 541 + + + openPreferences: + + + + 542 + @@ -2559,7 +2566,7 @@ - 541 + 542 @@ -2568,14 +2575,35 @@ TermKitAppDelegate NSObject - newWindow: - id + YES + + YES + newWindow: + openPreferences: + + + YES + id + id + - newWindow: - - newWindow: - id + YES + + YES + newWindow: + openPreferences: + + + YES + + newWindow: + id + + + openPreferences: + id + diff --git a/Cocoa/TermKit/English.lproj/Preferences.xib b/Cocoa/TermKit/English.lproj/Preferences.xib index 1253bf0..37148f1 100644 --- a/Cocoa/TermKit/English.lproj/Preferences.xib +++ b/Cocoa/TermKit/English.lproj/Preferences.xib @@ -12,7 +12,6 @@ YES - YES @@ -41,7 +40,7 @@ 15 2 - {{196, 223}, {522, 287}} + {{196, 322}, {578, 188}} 544735232 TermKit Preferences NSWindow @@ -55,8 +54,9 @@ 268 - {{119, 249}, {275, 18}} + {{158, 97}, {275, 18}} + YES -2080244224 @@ -70,11 +70,11 @@ 1211912703 2 - + NSImage NSSwitch - + NSSwitch @@ -86,8 +86,9 @@ 268 - {{17, 250}, {99, 17}} + {{56, 98}, {99, 17}} + YES 68288064 @@ -108,18 +109,59 @@ 6 System controlTextColor - + 3 MAA + + + 268 + {{158, 137}, {280, 18}} + + + YES + + -2080244224 + 0 + Ignore case when completing commands + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 268 + {{56, 138}, {99, 17}} + + + YES + + 68288064 + 272630784 + Autocomplete: + + + + + + 268 - {{137, 218}, {373, 28}} + {{176, 66}, {373, 28}} + YES 68288064 @@ -139,8 +181,13 @@ bGwgYXJndW1lbnQgdmFsdWVzIGFyZSByZW1vdmVkOg 268 - {{137, 182}, {179, 28}} + {{176, 30}, {179, 28}} + + 0.5 + + + YES 68288064 @@ -156,8 +203,10 @@ A 268 - {{323, 182}, {166, 28}} + {{362, 30}, {166, 28}} + + 0.5 YES 68288064 @@ -171,16 +220,52 @@ A - {522, 287} + {578, 188} + {{0, 0}, {1680, 1028}} {1.79769e+308, 1.79769e+308} + + YES + YES + + + value: values.ignoreCase + + + + + + value: values.ignoreCase + value + values.ignoreCase + 2 + + + 30 + + + + value: values.usageLogging + + + + + + value: values.usageLogging + value + values.usageLogging + 2 + + + 31 + @@ -223,11 +308,13 @@ A YES - - + + + + @@ -301,6 +388,39 @@ A + + 14 + + + YES + + + + + + 15 + + + + + 13 + + + YES + + + + + + 16 + + + + + 17 + + + @@ -319,7 +439,17 @@ A 10.IBPluginDependency 11.IBPluginDependency 11.IBViewBoundsToFrameTransform + 11.IBViewIntegration.shadowBlurRadius + 11.IBViewIntegration.shadowColor + 11.IBViewIntegration.shadowOffsetHeight + 11.IBViewIntegration.shadowOffsetWidth 12.IBPluginDependency + 13.IBPluginDependency + 13.IBViewBoundsToFrameTransform + 14.IBPluginDependency + 14.IBViewBoundsToFrameTransform + 15.IBPluginDependency + 16.IBPluginDependency 2.IBPluginDependency 3.IBPluginDependency 3.IBViewBoundsToFrameTransform @@ -332,44 +462,69 @@ A 8.IBPluginDependency 9.IBPluginDependency 9.IBViewBoundsToFrameTransform + 9.IBViewIntegration.shadowBlurRadius + 9.IBViewIntegration.shadowColor + 9.IBViewIntegration.shadowOffsetHeight + 9.IBViewIntegration.shadowOffsetWidth YES com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{28, 398}, {522, 287}} + {{123, 548}, {578, 188}} com.apple.InterfaceBuilder.CocoaPlugin - {{28, 398}, {522, 287}} + {{123, 548}, {578, 188}} {196, 240} {{357, 418}, {480, 270}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABCrgAAwuYAAA + P4AAAL+AAABDoYAAwrAAAA + + + + 1 + MCAwIDAAA + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABC7gAAwzkAAA + + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABBiAAAwzkAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABC7gAAw4SAAA + P4AAAL+AAABC7gAAwxEAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABBiAAAw4SAAA + P4AAAL+AAABBiAAAwxEAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABDCQAAw3QAAA + P4AAAL+AAABDMAAAwrgAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABDCQAAw1AAAA + P4AAAL+AAABDCQAAwrAAAA + + + + @@ -388,7 +543,7 @@ A - 12 + 31 @@ -476,6 +631,14 @@ A AppKit.framework/Headers/NSControl.h + + NSController + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSController.h + + NSFormatter NSObject @@ -837,6 +1000,14 @@ A AppKit.framework/Headers/NSTextFieldCell.h + + NSUserDefaultsController + NSController + + IBFrameworkSource + AppKit.framework/Headers/NSUserDefaultsController.h + + NSView diff --git a/Cocoa/TermKit/TermKit.xcodeproj/project.pbxproj b/Cocoa/TermKit/TermKit.xcodeproj/project.pbxproj index d96ba9a..501d749 100644 --- a/Cocoa/TermKit/TermKit.xcodeproj/project.pbxproj +++ b/Cocoa/TermKit/TermKit.xcodeproj/project.pbxproj @@ -12,9 +12,11 @@ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + B029D87A1380E4430033B680 /* TermKitPrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = B029D8791380E4430033B680 /* TermKitPrefsController.m */; }; B041C7F6127AA36F0098469B /* Window.xib in Resources */ = {isa = PBXBuildFile; fileRef = B041C7F4127AA36F0098469B /* Window.xib */; }; B041C8DD127AA9AD0098469B /* TermKitWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B041C8DC127AA9AD0098469B /* TermKitWindowController.m */; }; B090FFFC11E1CE7800AC604B /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B090FFFB11E1CE7800AC604B /* WebKit.framework */; }; + B092CBE6137BFAFE006B52CB /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = B092CBE4137BFAFE006B52CB /* Preferences.xib */; }; B0965B7D11DB375E00350D83 /* Terminal.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0965B7B11DB375E00350D83 /* Terminal.xib */; }; B0965B8211DB380600350D83 /* TermKitWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = B0965B8111DB380600350D83 /* TermKitWebView.m */; }; B0B02B2711E2CDA4004C960F /* TermKitTerminalController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B02B2611E2CDA4004C960F /* TermKitTerminalController.m */; }; @@ -40,11 +42,14 @@ 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 8D1107310486CEB800E47090 /* TermKit-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TermKit-Info.plist"; sourceTree = ""; }; 8D1107320486CEB800E47090 /* TermKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TermKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B029D8781380E4430033B680 /* TermKitPrefsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TermKitPrefsController.h; sourceTree = ""; }; + B029D8791380E4430033B680 /* TermKitPrefsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TermKitPrefsController.m; sourceTree = ""; }; B041C7F5127AA36F0098469B /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/Window.xib; sourceTree = ""; }; B041C8DB127AA9AD0098469B /* TermKitWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TermKitWindowController.h; sourceTree = ""; }; B041C8DC127AA9AD0098469B /* TermKitWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TermKitWindowController.m; sourceTree = ""; }; B061E2A0136AB12000F4632D /* WebInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebInspector.h; sourceTree = ""; }; B090FFFB11E1CE7800AC604B /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = /System/Library/Frameworks/WebKit.framework; sourceTree = ""; }; + B092CBE5137BFAFE006B52CB /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/Preferences.xib; sourceTree = ""; }; B0965B7C11DB375E00350D83 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/Terminal.xib; sourceTree = ""; }; B0965B8011DB380600350D83 /* TermKitWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TermKitWebView.h; sourceTree = ""; }; B0965B8111DB380600350D83 /* TermKitWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TermKitWebView.m; sourceTree = ""; }; @@ -83,6 +88,8 @@ B0CAB03A12685FE50085604B /* NSImage+QuickLook.m */, 256AC3D80F4B6AC300CF3369 /* TermKitAppDelegate.h */, 256AC3D90F4B6AC300CF3369 /* TermKitAppDelegate.m */, + B029D8781380E4430033B680 /* TermKitPrefsController.h */, + B029D8791380E4430033B680 /* TermKitPrefsController.m */, B0CAB03D12686AF70085604B /* TermKitIconProtocol.h */, B0CAB03E12686AF70085604B /* TermKitIconProtocol.m */, B0CAB04412686D220085604B /* TermKitIconLoadDelegate.h */, @@ -150,9 +157,10 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( - B0C532821352271300A92633 /* Shared */, B0B02B5D11E31B55004C960F /* HTML */, + B0C532821352271300A92633 /* Shared */, 1DDD58140DA1D0A300B32029 /* MainMenu.xib */, + B092CBE4137BFAFE006B52CB /* Preferences.xib */, B0965B7B11DB375E00350D83 /* Terminal.xib */, B041C7F4127AA36F0098469B /* Window.xib */, 8D1107310486CEB800E47090 /* TermKit-Info.plist */, @@ -224,6 +232,7 @@ files = ( 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */, + B092CBE6137BFAFE006B52CB /* Preferences.xib in Resources */, B0965B7D11DB375E00350D83 /* Terminal.xib in Resources */, B041C7F6127AA36F0098469B /* Window.xib in Resources */, B0B02B7111E31B56004C960F /* HTML in Resources */, @@ -263,6 +272,7 @@ B0CAB03F12686AF70085604B /* TermKitIconProtocol.m in Sources */, B0CAB04612686D220085604B /* TermKitIconLoadDelegate.m in Sources */, B041C8DD127AA9AD0098469B /* TermKitWindowController.m in Sources */, + B029D87A1380E4430033B680 /* TermKitPrefsController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -293,6 +303,14 @@ name = Window.xib; sourceTree = ""; }; + B092CBE4137BFAFE006B52CB /* Preferences.xib */ = { + isa = PBXVariantGroup; + children = ( + B092CBE5137BFAFE006B52CB /* English */, + ); + name = Preferences.xib; + sourceTree = ""; + }; B0965B7B11DB375E00350D83 /* Terminal.xib */ = { isa = PBXVariantGroup; children = ( @@ -315,9 +333,11 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = TermKit_Prefix.pch; + GENERATE_PROFILING_CODE = YES; INFOPLIST_FILE = "TermKit-Info.plist"; INSTALL_PATH = "$(HOME)/Applications"; PRODUCT_NAME = TermKit; + RUN_CLANG_STATIC_ANALYZER = YES; }; name = Debug; }; diff --git a/Cocoa/TermKit/TermKitAppDelegate.h b/Cocoa/TermKit/TermKitAppDelegate.h index 2681a6f..d220d01 100644 --- a/Cocoa/TermKit/TermKitAppDelegate.h +++ b/Cocoa/TermKit/TermKitAppDelegate.h @@ -11,6 +11,7 @@ @interface TermKitAppDelegate : NSObject { } +- (IBAction) openPreferences: sender; - (IBAction) newWindow: sender; @end diff --git a/Cocoa/TermKit/TermKitAppDelegate.m b/Cocoa/TermKit/TermKitAppDelegate.m index ded7595..82c6610 100644 --- a/Cocoa/TermKit/TermKitAppDelegate.m +++ b/Cocoa/TermKit/TermKitAppDelegate.m @@ -8,16 +8,33 @@ #import "TermKitAppDelegate.h" #import "TermKitWindowController.h" +#import "TermKitPrefsController.h" @implementation TermKitAppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + NSUserDefaults *defaults; + + defaults = [NSUserDefaults standardUserDefaults]; + [defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: + @"YES", @"ignoreCase", + @"YES", @"usageLogging", + @"YES", @"WebKitDeveloperExtras", + nil]]; + [self newWindow:self]; } +- (IBAction) openPreferences: sender { + id prefsController = [[TermKitPrefsController alloc] init]; + [prefsController showWindow:self]; + [prefsController retain]; +} + - (IBAction) newWindow: sender { id windowController = [[TermKitWindowController alloc] init]; [windowController showWindow:self]; + [windowController retain]; } @end diff --git a/Cocoa/TermKit/TermKitPrefsController.h b/Cocoa/TermKit/TermKitPrefsController.h new file mode 100644 index 0000000..00d1050 --- /dev/null +++ b/Cocoa/TermKit/TermKitPrefsController.h @@ -0,0 +1,17 @@ +// +// TermKitPrefsController.h +// TermKit +// +// Created by Steven Wittens on 29/10/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface TermKitPrefsController : NSWindowController { +} + +- (id) init; + +@end diff --git a/Cocoa/TermKit/TermKitPrefsController.m b/Cocoa/TermKit/TermKitPrefsController.m new file mode 100644 index 0000000..a4eb434 --- /dev/null +++ b/Cocoa/TermKit/TermKitPrefsController.m @@ -0,0 +1,23 @@ +// +// TermKitPrefsController.m +// TermKit +// +// Created by Steven Wittens on 29/10/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "TermKitPrefsController.h" + + +@implementation TermKitPrefsController { +} + +- (id) init { + self = [super initWithWindowNibName:@"Preferences"]; + if (self != nil) { + // Yay. + } + return self; +} + +@end diff --git a/Cocoa/TermKit/TermKitWebView.h b/Cocoa/TermKit/TermKitWebView.h index 1e1e8bc..d176d34 100644 --- a/Cocoa/TermKit/TermKitWebView.h +++ b/Cocoa/TermKit/TermKitWebView.h @@ -12,9 +12,16 @@ #import "TermKitIconLoadDelegate.h" @interface TermKitWebView : WebView { - + id delegate; + id config; } - (void)awakeFromNib; +- (void)webView:(WebView*)webView windowScriptObjectAvailable:(WebScriptObject*)windowScriptObject; +- (void)dealloc; ++ (NSString *)webScriptNameForSelector:(SEL)selector; ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector; +- (id)get:(NSString*)key; + @end diff --git a/Cocoa/TermKit/TermKitWebView.m b/Cocoa/TermKit/TermKitWebView.m index fcb6fd9..c6e7174 100644 --- a/Cocoa/TermKit/TermKitWebView.m +++ b/Cocoa/TermKit/TermKitWebView.m @@ -8,14 +8,74 @@ #import "TermKitWebView.h" - @implementation TermKitWebView - (void)awakeFromNib { + // Register icon protocol. [TermKitIconProtocol registerProtocol]; - id delegate = [[TermKitIconLoadDelegate alloc] init]; + // Register resource delegate. + delegate = [[TermKitIconLoadDelegate alloc] init]; [self setResourceLoadDelegate:delegate]; + + // Set self as frame load delegate. + [self setFrameLoadDelegate:self]; + + [delegate retain]; +} + +/** + * Hook into window/frame loading. + */ +- (void)webView:(WebView*)webView windowScriptObjectAvailable:(WebScriptObject*)windowScriptObject { + // Create window.preferences object. + [[self windowScriptObject] setValue:self forKey:@"preferences"]; + [config retain]; +} + +- (void)dealloc { + if (delegate) + [delegate release]; + if (config) + [config release]; + [super dealloc]; +} + +/** + * Export .get() method for getting preference values. + */ ++ (NSString *)webScriptNameForSelector:(SEL)selector { + if (selector == @selector(get:)) { + return @"get"; + } + return nil; +} + +/** + * Export .get: selector for getting preference values. + */ ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector { + if (selector == @selector(get:)) { + return NO; + } + return YES; +} + +/** + * Get user preferences directly from controller. + */ +- (id)get:(NSString*)key { + if (!key) return false; + + // Fetch user settings. + NSUserDefaults* defaults; + defaults = [NSUserDefaults standardUserDefaults]; + + id value = [defaults stringForKey:key]; + if (value == nil) { + return false; + } + return value; } -@end +@end \ No newline at end of file diff --git a/Cocoa/TermKit/TermKitWindowController.m b/Cocoa/TermKit/TermKitWindowController.m index bcae976..118053a 100644 --- a/Cocoa/TermKit/TermKitWindowController.m +++ b/Cocoa/TermKit/TermKitWindowController.m @@ -22,6 +22,7 @@ - (id) init { [[self window] setTitle:title]; [self newTerminal:self]; + [title autorelease]; } return self; } @@ -39,6 +40,8 @@ - (IBAction) newTerminal: sender { [terminalView setFrame:contentRect]; [contentView addSubview:terminalView]; + + [terminalController retain]; } @end diff --git a/HTML/client/client.js b/HTML/client/client.js index 3f6c6f3..174cf4d 100644 --- a/HTML/client/client.js +++ b/HTML/client/client.js @@ -68,5 +68,5 @@ tc.prototype = { }; -})(); +})(jQuery); diff --git a/HTML/client/shell.js b/HTML/client/shell.js index c8e4240..840a594 100644 --- a/HTML/client/shell.js +++ b/HTML/client/shell.js @@ -105,8 +105,15 @@ tc.shell.prototype = { }, callback); // Anonymized usage/command logging. HTTPS is used for privacy. - // Note the URL fragment is only sent to Google Analytics SSL through JS, not to usage.termkit.org. - $('#usage').attr('src', 'https://usage.termkit.org/#' + encodeURIComponent(this.anonymize(tokens))); + var url; + if (window.preferences && parseInt(window.preferences.get('usageLogging'))) { + // Note the URL fragment is only sent to Google Analytics SSL through JS, not to usage.termkit.org. + url = 'https://usage.termkit.org/#' + encodeURIComponent(this.anonymize(tokens)); + } + else { + url = 'about:blank'; + } + $('#usage').attr('src', url); }, /** @@ -164,5 +171,5 @@ tc.shell.prototype = { }, }; -})(); +})(jQuery); diff --git a/HTML/commandview/command.js b/HTML/commandview/command.js index 7e2b5a8..d5be371 100644 --- a/HTML/commandview/command.js +++ b/HTML/commandview/command.js @@ -222,6 +222,7 @@ cv.commandAutocomplete.handler = function (offset, event, tokens, callback) { cwd: shell.environment.cwd, tokens: command, offset: offset, + ignoreCase: !!(window.preferences && parseInt(window.preferences.get('ignoreCase'))), }, function (message) { if (message.args.matches) { callback(message.args.matches); diff --git a/HTML/config.js b/HTML/config.js new file mode 100644 index 0000000..69f0817 --- /dev/null +++ b/HTML/config.js @@ -0,0 +1,7 @@ +(function ($) { + +var cf = termkit.config = function () { + +}; + +})(jQuery); \ No newline at end of file diff --git a/HTML/termkit.js b/HTML/termkit.js index fb56a46..edc8b94 100644 --- a/HTML/termkit.js +++ b/HTML/termkit.js @@ -1,4 +1,4 @@ -var termkit = {}; +var termkit = window.termkit || {}; (function ($) { diff --git a/Node/config.js b/Node/config.js new file mode 100644 index 0000000..0cba500 --- /dev/null +++ b/Node/config.js @@ -0,0 +1,152 @@ +var EventEmitter = require('events').EventEmitter, + extend = require('misc').extend, + composePath = require('misc').composePath, + fs = require('fs'), + singleton; + +//var config = require('config').getConfig(); + +/** + * Static accessor. + */ +exports.getConfig = function (paths) { + if (!singleton) { + singleton = new exports.configStore(paths); + } + return singleton.config; +}; + +/** + * Config store, backs to .termkit.json. + */ +exports.configStore = function (paths) { + + // Default. + paths = paths || []; + this.path = process.env.HOME; + this.file = '.TermKit.json'; + + // Detect mac. + var mac; + try { + var appSupport = composePath('Library/Application Support', process.env.HOME), + termkitSupport = composePath('TermKit', appSupport); + + // Look for Application Support (detect mac). + fs.statSync(appSupport); + mac = true; + this.file = 'TermKit.json'; + + // Create TermKit folder if not exists. + try { + fs.statSync(termkitSupport); + } catch (e) { + fs.mkdir(termkitSupport, 0755); + }; + + // Add to search locations. + paths.push(termkitSupport); + this.path = termkitSupport; + } + catch (e) { }; + + // Look in user home directory. + paths.push(process.env.HOME); + + // Iterate over search paths. + var path, json; + for (i in paths) { + var file = composePath(this.file, paths[i]); + try { + json = fs.readFileSync(file, 'utf8'); + this.path = paths[i]; + } + catch (e) { + continue; + } + break; + } + + // Create config object. + var data = {}; + try { + if (json) { + data = JSON.parse(json); + } + } catch (e) { throw 'Unable to parse config file "' + composePath(this.file, this.path) + '"'; }; + + this.config = new exports.configValues(data, this); +}; + +exports.configStore.prototype = { + + update: function (values) { + + // Fire and forget save. + var json = JSON.stringify(values); + var file = composePath(this.file, this.path); + fs.writeFile(file, json, 'utf8'); + }, + +}; + +/** + * Configuration state. + */ +exports.configValues = function (values, store) { + this.values = values; + this.store = store; +}; + +exports.configValues.prototype = extend(new EventEmitter(), { + + // path = [ 'foo', 'bar' ] => values[foo][bar] + + replace: function (values) { + this.values = values; + + this.store.update(this.values); + this.emit('change', this.values); + }, + + "set": function (path, value) { + + var obj = this.values, + tail = path.pop(); + + // Descend path of keys. + for (var i in path) { + var child = obj[path[i]]; + if (!child) { + child = obj[path[i]] = {}; + } + obj = child; + } + obj[tail] = value; + + this.store.update(this.values); + this.emit('change', this.values); + }, + + "get": function (path) { + + if (typeof path == 'undefined' || path === null) { + return this.values; + } + + var obj = this.values, + tail = path.pop(); + + // Descend path of keys. + for (var i in path) { + var child = obj[path[i]]; + if (!child) { + return null; + } + obj = child; + } + return obj[tail]; + }, + +}); + diff --git a/Node/nodekit.js b/Node/nodekit.js index 54da86f..c853e2b 100644 --- a/Node/nodekit.js +++ b/Node/nodekit.js @@ -10,6 +10,8 @@ var http = require('http'), io = require('socket.io') router = require("router"); +var config = require('config').getConfig(); + // Set up http server. var server = http.createServer(function (request, result) { // result.writeHeader(200, {'Content-Type': 'text/html'}); diff --git a/Node/shell/autocomplete.js b/Node/shell/autocomplete.js index 11c7c5b..c1b00a3 100644 --- a/Node/shell/autocomplete.js +++ b/Node/shell/autocomplete.js @@ -41,7 +41,7 @@ exports.autocomplete.prototype = { /** * Return autocompletion for given command context. */ - process: function (cwd, paths, tokens, offset, callback) { + process: function (cwd, paths, tokens, offset, callback, ignoreCase) { var prefix = tokens[offset], matches = [], that = this; @@ -66,23 +66,23 @@ exports.autocomplete.prototype = { if (offset == 0) { // Match built-in commands. - var matches = this.builtin(prefix); + var matches = this.builtin(prefix, ignoreCase); // Scan current dir for executables. - this.filesystem(cwd, prefix, { executable: true }, track(function (files) { + this.filesystem(cwd, prefix, { ignoreCase: ignoreCase, executable: true }, track(function (files) { matches = matches.concat(files); })); // Scan search paths for executables. for (i in paths) (function (path) { - that.filesystem(path, prefix, { executable: true }, track(function (files) { + that.filesystem(path, prefix, { ignoreCase: ignoreCase, executable: true }, track(function (files) { matches = matches.concat(files); })); })(paths[i]); } else { // Scan current dir for files. - this.filesystem(cwd, prefix, { }, track(function (files) { + this.filesystem(cwd, prefix, { ignoreCase: ignoreCase }, track(function (files) { matches = files; })); } @@ -92,10 +92,17 @@ exports.autocomplete.prototype = { /** * Complete built-in commands. */ - builtin: function (prefix) { + builtin: function (prefix, ignoreCase) { var matches = []; + if (ignoreCase) { + prefix = prefix.toLowerCase(); + } for (i in builtin.commands) { - if (prefix == '' || i.indexOf(prefix) === 0) { + var key = i; + if (ignoreCase) { + key = key.toLowerCase(); + } + if (prefix == '' || key.indexOf(prefix) === 0) { matches.push(exports.autocomplete.match(i, i + ' ', 'command')); } } @@ -153,6 +160,7 @@ exports.autocomplete.prototype = { // Set options. options = extend({ type: '*', + ignoreCase: false, }, options || {}); // Completion callback. @@ -160,11 +168,23 @@ exports.autocomplete.prototype = { track = whenDone(function () { callback(matches); }); + + // Case handling. + if (options.ignoreCase) { + prefix = prefix.toLowerCase(); + } // Scan directory. fs.readdir(path, track(function (err, files) { if (!err) { for (i in files) (function (file) { + + // Case handling. + var key = file; + if (options.ignoreCase) { + key = key.toLowerCase(); + } + // Prefix match. if (prefix == '' || file.indexOf(prefix) === 0) { diff --git a/Node/shell/processor.js b/Node/shell/processor.js index 5989efc..3fd0300 100644 --- a/Node/shell/processor.js +++ b/Node/shell/processor.js @@ -16,6 +16,7 @@ var workerProcessor = exports.processor = function (inStream, outStream) { this.buffer = ''; this.views = {}; + this.config = {}; }; exports.processor.prototype = { @@ -112,6 +113,14 @@ exports.processor.prototype = { }; workerProcessor.handlers = { + + /** + * Set/update worker configuration. + */ + "shell.config": function (args, exit) { + process.stderr.write('config @' + JSON.stringify(args) + '@'); + this.config = args; + }, /** * Return environment state/variables. @@ -126,15 +135,17 @@ workerProcessor.handlers = { "shell.autocomplete": function (args, exit) { var tokens = args.tokens, offset = args.offset, - cwd = args.cwd; + cwd = args.cwd, + ignoreCase = !!args.ignoreCase; if (offset >= tokens.length) return exit(false); var auto = new autocomplete(), path = process.env.PATH.split(':'); + auto.process(cwd, path, tokens, offset, function (matches) { exit(true, { matches: matches }); - }); + }, ignoreCase); }, diff --git a/Node/shell/shell.js b/Node/shell/shell.js index dd15ea0..bdaffba 100644 --- a/Node/shell/shell.js +++ b/Node/shell/shell.js @@ -3,6 +3,8 @@ var fs = require('fs'), net = require('net'); var spawn = require('child_process').spawn, exec = require('child_process').exec; +var config = require('config').getConfig(); + exports.shell = function (args, router) { this.router = router; @@ -19,7 +21,7 @@ exports.shell = function (args, router) { // Determine user identity. if (user == process.env.USER) { // Spawn regular worker. - p = this.process = spawn('/usr/local/bin/node', [ path ], { + p = this.process = spawn('node', [ path ], { cwd: process.cwd(), }); } @@ -43,9 +45,17 @@ exports.shell = function (args, router) { else { throw "Error spawning worker.js."; } + + // Sync up configuration. + this.sync(); + config.on('change', function () { that.sync(); }); }; exports.shell.prototype = { + sync: function () { + this.send(null, 'shell.config', config.get()); + }, + dispatch: function (query, method, args, exit) { this.send(query, method, args); }, @@ -73,6 +83,12 @@ exports.shell.prototype = { // Parse message. var message = JSON.parse(chunk); + // Intercept config changes. + if (message.method == 'shell.config') { + config.replace(message.args); + return; + } + // Lock message to this session and forward. message.session = this.id; this.router.forward(message); diff --git a/Readme.md b/Readme.md index b6f6f02..784382e 100644 --- a/Readme.md +++ b/Readme.md @@ -39,7 +39,7 @@ Addresses following problems: 2. install node-mime: `npm install mime` 3. Clone the TermKit repository: `git clone git@github.com:unconed/TermKit.git --recursive` 4. Run the NodeKit daemon: `cd Node; node nodekit.js` -5. Unzip and run the Cocoa app in Build/TermKit.zip +5. Unzip and run the Mac app in Build/TermKit.zip *Tip:* Press ⌥⌘C to access the WebKit console. diff --git a/todo.txt b/todo.txt index ed8121b..c2c6219 100644 --- a/todo.txt +++ b/todo.txt @@ -29,7 +29,7 @@ Prototype: [X] fix backspacing around edges of tokens [X] add icon/spinner support to tokens [ ] caret positions around allowempty tokens and ^/$ - [ ] | and >> token support + [.] | and >> token support [ ] caret.remove() shouldn't need to touch tokenList when pruning empties -> let triggers do it [ ] mouseclick caret is off by ~8px, but only in first token [ ] switch between inline and block tokens -> input vs textarea. @@ -103,11 +103,12 @@ Prototype: [X] simplify message format to raw json [X] viewstream integration [X] data reader - [ ] arrange pipes for command/view for unix processes - [X] output formatter + [ ] arrange view pipes for command/view for unix processes + [X] output formatter [X] full mime header parser/generator [X] smart binary output [ ] handle encodings + [ ] preferences [ ] sudo support (askpass env?) 5) Command Suite