alexstaubo / keywurl

Keywurl is a small extension for Safari that adds keyword search, similar to Firefox's "Quick Search" functionality.

This URL has Read+Write access

alexstaubo (author)
Fri Aug 14 02:54:04 -0700 2009
commit  261a49720d285fcc56b77de6e7d436774d186f8c
tree    2bdf208ce95283c2e075913298a730316d24aa4e
parent  93d3f4d73316c4031323bb5c5ce6e39518df2f6d
keywurl / KeywordSaveController.m
100644 203 lines (182 sloc) 7.582 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#import "KeywordSaveController.h"
#import "KeywordMapper.h"
#import "KeywordMapping.h"
#import "KeywurlPlugin.h"
 
@interface KeywordSaveController (Internal)
 
- (void) collectFields: (NSMutableArray*) fields
    fromElement: (DOMElement*) element;
- (void) collectButtons: (NSMutableDictionary*) buttons
    fromElement: (DOMElement*) element;
 
@end
 
@interface FormField : NSObject {
    NSString* name;
    NSString* value;
}
 
- initWithName: (NSString*) theName value: (NSString*) theValue;
- (NSString*) name;
- (NSString*) value;
 
@end
 
@implementation FormField
 
- initWithName: (NSString*) theName value: (NSString*) theValue {
    name = [theName copy];
    value = [theValue copy];
    return self;
}
 
- (NSString*) name {
    return name;
}
 
- (NSString*) value {
    return value;
}
 
@end
 
@implementation KeywordSaveController
 
- (id) initWithUrl: (NSString*) theUrl
    inputElement: (DOMElement*) theInputElement
    formElement: (DOMElement*) theFormElement {
    url = [theUrl copy];
    inputElement = [theInputElement retain];
    formElement = [theFormElement retain];
    if (![NSBundle loadNibNamed: @"SaveKeywordWindow" owner: self]) {
        NSLog(@"Could not load resource.");
        return nil;
    }
    {
        [buttonPopupButton removeAllItems];
        NSMutableDictionary* buttons = [NSMutableDictionary new];
        [self collectButtons: buttons fromElement: formElement];
        if ([buttons count] <= 1) {
            [buttonPopupButton setHidden: YES];
            [buttonPopupLabel setHidden: YES];
        } else {
            NSEnumerator* keyEumerator = [buttons keyEnumerator];
            NSString* name;
            int index = 0;
            while (name = [keyEumerator nextObject]) {
                NSString* value = (NSString*) [buttons objectForKey: name];
                if ([value length] == 0) {
                    value = @"Default button";
                }
                [buttonPopupButton insertItemWithTitle: value atIndex: index];
                NSMenuItem* item = [buttonPopupButton itemAtIndex: index];
                [item setRepresentedObject: name];
                index++;
            }
            [buttonPopupButton selectItemAtIndex: 0];
            [buttonPopupButton synchronizeTitleAndSelectedItem];
        }
    }
    [NSApp beginSheet: window
        modalForWindow: [NSApp keyWindow]
        modalDelegate: self
        didEndSelector: @selector(didEndSheet:returnCode:contextInfo:)
        contextInfo: nil];
    return self;
}
- (void)dealloc
{
    [url release];
    [inputElement release];
    [formElement release];
    [super dealloc];
}
 
- (void) saveKeyword: (id) sender {
    NSMutableArray* fields = [NSMutableArray new];
    [self collectFields: fields fromElement: formElement];
    NSURL* absoluteUrl = [NSURL URLWithString: url];
    NSString* action = [formElement action];
    NSURL* actionUrl = [NSURL URLWithString: action relativeToURL: absoluteUrl];
    NSMutableString* expansion = [NSMutableString new];
    [expansion appendString: [actionUrl absoluteString]];
    [expansion appendString: @"?"];
    for (int i = 0; i < [fields count]; i++) {
        FormField* field = (FormField*) [fields objectAtIndex: i];
        [expansion appendString: [[field name] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
        [expansion appendString: @"="];
        [expansion appendString: [[field value] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
        [expansion appendString: @"&"];
    }
    NSMenuItem* buttonItem = [buttonPopupButton selectedItem];
    if (buttonItem) {
        [expansion appendString: [[buttonItem representedObject] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
        [expansion appendString: @"="];
        [expansion appendString: [[buttonItem title] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
        [expansion appendString: @"&"];
    }
    [expansion appendString: [inputElement name]];
    [expansion appendString: @"={query}"];
    NSString* keyword = [nameTextField stringValue];
    KeywordMapping* mapping = [[KeywordMapping alloc] initWithKeyword: keyword
        expansion: expansion];
    KeywordMapper* mapper = [[KeywurlPlugin sharedInstance] keywordMapper];
    [mapper addKeyword: mapping];
    [mapper saveMappings];
    [NSApp endSheet: window];
 
    NSAlert *alert = [[NSAlert alloc] init];
    [alert addButtonWithTitle: @"OK"];
    [alert setMessageText: @"Keyword added"];
    [alert setInformativeText: [NSString stringWithFormat:
        @"You can now type '%@ something' in Safari's address field to do a search for 'something'", keyword]];
    [alert setAlertStyle: NSInformationalAlertStyle];
    [alert beginSheetModalForWindow: [NSApp keyWindow]
        modalDelegate: nil
        didEndSelector: nil
        contextInfo: nil];
}
 
- (void) cancel: (id) sender {
    [NSApp endSheet: window];
}
 
- (void) didEndSheet: (NSWindow*) sheet returnCode: (int) returnCode
    contextInfo: (void*) contextInfo
{
    [window orderOut: self];
}
 
- (void) collectFields: (NSMutableArray*) fields
    fromElement: (DOMElement*) element {
    DOMNodeList* children = [element childNodes];
    for (int i = 0; i < [children length]; i++) {
        DOMNode* node = [children item: i];
        if ([node isKindOfClass: [DOMElement class]]) {
            [self collectFields: fields fromElement: (DOMElement*) node];
        }
    }
    if (element != inputElement && [element respondsToSelector: @selector(disabled)] &&
        ![element disabled]) {
        NSString* tag = [[element tagName] lowercaseString];
        if ([tag isEqualToString: @"textarea"] || (
            [tag isEqualToString: @"input"] &&
            ([[element getAttribute: @"type"] isEqualToString: @"hidden"] ||
            ([[element getAttribute: @"type"] isEqualToString: @"radio"] && [element checked]) ||
            ([[element getAttribute: @"type"] isEqualToString: @"checkbox"] && [element checked])))) {
            [fields addObject: [[FormField alloc] initWithName: [element name] value: [element value]]];
        } else if ([tag isEqualToString: @"select"] &&
            ![element multiple] && [element selectedIndex] != -1) {
            [fields addObject: [[FormField alloc] initWithName: [element name] value: [element value]]];
        } else if ([tag isEqualToString: @"select"] && [element multiple]) {
            DOMHTMLOptionsCollection* options = [element options];
            for (int i = 0; i < [options length]; i++) {
                DOMHTMLOptionElement* option = [options item: i];
                if ([option selected]) {
                    [fields addObject: [[FormField alloc] initWithName: [element name]
                        value: [option value]]];
                }
            }
        }
    }
}
 
- (void) collectButtons: (NSMutableDictionary*) buttons
    fromElement: (DOMElement*) element {
    DOMNodeList* children = [element childNodes];
    for (int i = 0; i < [children length]; i++) {
        DOMNode* node = [children item: i];
        if ([node isKindOfClass: [DOMElement class]]) {
            [self collectButtons: buttons fromElement: (DOMElement*) node];
        }
    }
    NSString* tag = [[element tagName] lowercaseString];
    if ([tag isEqualToString: @"input"] &&
        ([[element getAttribute: @"type"] isEqualToString: @"submit"] ||
        [[element getAttribute: @"type"] isEqualToString: @"image"])) {
        [buttons setObject: [element value] forKey: [element name]];
    }
}
 
@end