diff --git a/Controller.m b/Controller.m
index 9649b2b..b04af1b 100644
--- a/Controller.m
+++ b/Controller.m
@@ -233,7 +233,7 @@
- (void)unreadMentions:(NSInteger)count {
if (![mentionsViewWindow isVisible] && count > 0) {
- [timelineViewWindow setTitle:[NSString stringWithFormat:@"Tentia (@%i)", count]];
+ [timelineViewWindow setTitle:[NSString stringWithFormat:@"Tentia (^%i)", count]];
[[[NSApplication sharedApplication] dockTile] setBadgeLabel:[NSString stringWithFormat:@"%i", count]];
} else {
[timelineViewWindow setTitle:[NSString stringWithFormat:@"Tentia"]];
diff --git a/Core.js b/Core.js
index de905f4..f308964 100644
--- a/Core.js
+++ b/Core.js
@@ -22,16 +22,11 @@ function Core(action) {
this.cache = {};
this.is_not_init = false;
-/*
- if (action == "home_timeline") {
- this.usernames = [];
- this.getUsernames("friends");
- this.getUsernames("followers");
- }
-*/
+ var _this = this;
+ setInterval(function() { _this.getNewData() }, this.timeout);
}
-Core.prototype.newStatus = function(status, supress_new_with_timeout) {
+Core.prototype.newStatus = function(status) {
if(status != null && status.length > 0) {
this.since_id = status[0]["id"];
for(var i = status.length-1, c=0; i>=c; --i) {
@@ -46,10 +41,6 @@ Core.prototype.newStatus = function(status, supress_new_with_timeout) {
}
}
- if(!supress_new_with_timeout) {
- var _this = this;
- setTimeout(function() { _this.getNewData() }, this.timeout);
- }
if(this.action == "mentions" && this.is_not_init) {
this.unread_mentions += status.length;
controller.unreadMentions_(this.unread_mentions);
@@ -231,11 +222,10 @@ Core.prototype.getTemplate = function() {
return jQuery.extend(true, {}, this.template);
}
-Core.prototype.getNewData = function(supress_new_with_timeout) {
+Core.prototype.getNewData = function() {
var those = this;
- var url = URI(controller.stringForKey_("api_root"));
- url.path(url.directory() + "posts");
+ var url = URI(mkApiRootPath("/posts"));
url.addSearch("post_types", "https://tent.io/types/post/status/v0.1.0");
url.addSearch("limit", this.max_length);
if(this.since_id) {
@@ -258,7 +248,7 @@ Core.prototype.getNewData = function(supress_new_with_timeout) {
throw e;
}
- those.newStatus(json, supress_new_with_timeout);
+ those.newStatus(json);
}
var data = null;
@@ -266,24 +256,6 @@ Core.prototype.getNewData = function(supress_new_with_timeout) {
if (controller.stringForKey_("user_access_token")) {
getURL(url.toString(), http_method, callback, data); // FIXME: error callback
}
-
- /*
- $.ajax(
- { beforeSend: function(xhr) {
- xhr.setRequestHeader("Authorization", );
- },
- url: url + url2,
- dataType: 'json',
- success: function(data) {
- _this.newStatus(data, supress_new_with_timeout);
- },
- error:function (xhr, ajaxOptions, thrownError){
- alert(xhr.status);
- alert(thrownError);
- setTimeout(function() { _this.getNewData(supress_new_with_timeout) }, this.timeout);
- }
- }
- );*/
}
@@ -291,7 +263,7 @@ Core.prototype.sendNewMessage = function(content, in_reply_to_status_id, in_repl
var _this = this;
- var url = URI(controller.stringForKey_("api_root") + "/posts");
+ var url = URI(mkApiRootPath("/posts"));
var http_method = "POST";
var callback = function(data) { _this.getNewData(true); }
@@ -315,185 +287,11 @@ Core.prototype.sendNewMessage = function(content, in_reply_to_status_id, in_repl
getURL(url.toString(), http_method, callback, JSON.stringify(data)); // FIXME: error callback
}
-/*
-
-Core.prototype.retweet = function(status_id, item) {
- var url = API_PATH + "statuses/retweet/" + status_id + ".json";
- var _this = this;
-
- var message = { method:"POST" , action:url };
-
- OAuth.completeRequest(message,
- { consumerKey : OAUTH_CONSUMER_KEY
- , consumerSecret: OAUTH_CONSUMER_SECRET
- , token : controller.accessToken.accessToken()
- , tokenSecret : controller.accessToken.secret()
- });
-
- $.ajax({
- beforeSend: function(xhr) {
- xhr.setRequestHeader("Authorization", OAuth.getAuthorizationHeader("", message.parameters));
- },
- url: url,
- type: 'POST',
- dataType: 'json',
- success: function(data) {
- item.parentNode.replaceChild(_this.getItem(data), item);
- },
- error:function (xhr, ajaxOptions, thrownError) {
- alert(xhr.status);
- alert(thrownError);
- }
- });
-}
-
-Core.prototype.getUsernames = function(type, cursor) {
- cursor = typeof cursor == "undefined" ? -1 : cursor;
-
- var url = API_PATH + type + "/ids.json";
- var _this = this;
- var parameters = { stringify_ids: "true", cursor:cursor };
-
- var message = { method:"GET" , action:url, parameters:parameters };
-
- OAuth.completeRequest(message,
- { consumerKey : OAUTH_CONSUMER_KEY
- , consumerSecret: OAUTH_CONSUMER_SECRET
- , token : controller.accessToken.accessToken()
- , tokenSecret : controller.accessToken.secret()
- });
-
- $.ajax({
- beforeSend: function(xhr) {
- xhr.setRequestHeader("Authorization", OAuth.getAuthorizationHeader("", message.parameters));
- },
- url: url + "?stringify_ids=true&cursor=" + cursor ,
- type: 'GET',
- dataType: 'json',
- success: function(data) {
- for (var i=0; i < data.ids.length; i = i + 100) {
- _this.getUsernamesFromIds(data.ids.slice(i, i + 100));
- }
- if (data.next_cursor > 0) {
- _this.getUsernames(type, data.next_cursor);
- }
- },
- error:function (xhr, ajaxOptions, thrownError) {
- alert(xhr.status);
- alert(thrownError);
- }
- });
-}
-
-Core.prototype.getUsernamesFromIds = function(ids) {
-
- var url = API_PATH + "users/lookup.json";
- var _this = this;
- var parameters = { user_id:ids.join(",") };
- var message = { method:"GET" , action:url, parameters:parameters };
-
- OAuth.completeRequest(message,
- { consumerKey : OAUTH_CONSUMER_KEY
- , consumerSecret: OAUTH_CONSUMER_SECRET
- , token : controller.accessToken.accessToken()
- , tokenSecret : controller.accessToken.secret()
- });
-
- $.ajax({
- beforeSend: function(xhr) {
- xhr.setRequestHeader("Authorization", OAuth.getAuthorizationHeader("", message.parameters));
- },
- url: url + "?user_id=" + ids.join(","),
- type: 'GET',
- dataType: 'json',
- success: function(data) {
- for (var i=0; i < data.length; i++) {
- _this.usernames.push(data[i].screen_name);
- }
- },
- error:function (xhr, ajaxOptions, thrownError) {
- alert(xhr.status);
- alert(thrownError);
- }
- });
-}
-
-Core.prototype.findUsernamesFor = function(query) {
- var ret = [];
- for (var i=0; i < this.usernames.length; i++) {
- if(this.usernames[i].startsWith(query)) {
- ret.push(this.usernames[i]);
- }
- }
- return ret;
-}
-*/
/* Helper functions */
function replaceURLWithHTMLLinks(text, entities, message_node) {
var exp = /(([^\^]https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_()|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
return text.replace(exp, "$1");
-
- /*
- var urls = entities.urls;
-
- for(var i = 0; i" + replace + "");
-
- var media = null;
-
- // add thumbnail
- if(replace.startsWith("http://youtube.com/") || replace.startsWith("http://www.youtube.com/")) {
- var v = getUrlVars(replace)["v"];
- if (v) {
- media = {
- type: "tentia_youtube",
- url: original,
- media_url: "http://img.youtube.com/vi/" + v + "/1.jpg"
- }
- }
-
- } else if (replace.startsWith("http://twitpic.com/")) {
- media = {
- type: "tentia_photo",
- url: original,
- media_url: "http://twitpic.com/show/mini/" + replace.substring("http://twitpic.com/".length)
- }
-
- } else if (replace.startsWith("http://yfrog")) {
- media = {
- type: "tentia_photo",
- url: original,
- media_url: replace + ":small"
- }
-
- } else if (replace.startsWith("http://instagr.am/p/") || replace.startsWith("http://instagram.com/p/")) {
- media = {
- type: "tentia_photo",
- url: original,
- media_url: replace + "media?size=t"
- }
- }
-
- if(media) {
- if(entities.media) {
- entities.media.push(media);
- } else {
- entities.media = [media];
- }
- }
-
- }
-
- return text;*/
}
function replaceUsernamesWithLinks(text, mentions) {
@@ -505,9 +303,10 @@ function replaceUsernamesWithLinks(text, mentions) {
}
function replyTo(entity, status_id, mentions) {
- var string = "^" + entity + " ";
+ var string = "^" + entity.replace("https://", "") + " ";
for (var i = 0; i < mentions.length; i++) {
- string += "^" + mentions[i].entity + " ";
+ var e = mentions[i].entity.replace("https://", "");
+ if(string.indexOf(e) == -1) string += "^" + e + " ";
}
controller.openNewMessageWindowInReplyTo_statusId_withString_(entity, status_id, string);
}
@@ -575,11 +374,14 @@ function parseMentions(text, post_id, entity) {
})
}
- var res = text.match(/((\^https?):\/\/\S+)/ig);
+ var res = text.match(/(\^\S+)/ig);
if (res) {
for (var i = 0; i < res.length; i++) {
var e = res[i].substring(1);
+ if (e.substring(0,7) != "http://" && e.substring(0,8) != "https://") {
+ e = "https://" + e;
+ }
if (e != entity) {
mentions.push({entity:e});
}
diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib
index 747e36f..930c402 100644
--- a/English.lproj/MainMenu.xib
+++ b/English.lproj/MainMenu.xib
@@ -886,7 +886,7 @@
{376, 581}
+
+
{{0, 0}, {2560, 1418}}
{10000000000000, 10000000000000}
@@ -1070,7 +1073,6 @@
{{391, 46}, {75, 32}}
-
_NS:9
YES
@@ -1719,11 +1721,6 @@
-
- 73
-
-
-
79
@@ -2227,16 +2224,6 @@
-
- 544
-
-
-
-
- 551
-
-
-
559
@@ -2364,6 +2351,21 @@
+
+ 73
+
+
+
+
+ 551
+
+
+
+
+ 544
+
+
+
@@ -2611,7 +2613,7 @@
- 622
+ 627
@@ -2625,15 +2627,11 @@
YES
login:
logout:
- openNewMessageWindow:
- sendTweet:
YES
id
id
- id
- id
@@ -2642,8 +2640,6 @@
YES
login:
logout:
- openNewMessageWindow:
- sendTweet:
YES
@@ -2655,14 +2651,6 @@
logout:
id
-
- openNewMessageWindow:
- id
-
-
- sendTweet:
- id
-
diff --git a/English.lproj/NewMessageWindow.xib b/English.lproj/NewMessageWindow.xib
index b8a4397..273c84f 100644
--- a/English.lproj/NewMessageWindow.xib
+++ b/English.lproj/NewMessageWindow.xib
@@ -95,7 +95,6 @@
{{257, 2}, {38, 17}}
-
YES
68157504
diff --git a/NewMessageWindow.h b/NewMessageWindow.h
index 2513461..7d993e6 100644
--- a/NewMessageWindow.h
+++ b/NewMessageWindow.h
@@ -10,7 +10,7 @@
#import
-@interface NewMessageWindow : NSDocument
+@interface NewMessageWindow : NSDocument
{
IBOutlet NSTextField *textField;
IBOutlet NSTextField *counter;
diff --git a/NewMessageWindow.m b/NewMessageWindow.m
index c387ba2..0fa4fa8 100644
--- a/NewMessageWindow.m
+++ b/NewMessageWindow.m
@@ -10,6 +10,10 @@
#import "Constants.h"
#import "TweetModel.h"
+@interface NewMessageWindow (private)
+- (BOOL)isCommandEnterEvent:(NSEvent *)e;
+@end
+
@implementation NewMessageWindow
@synthesize textField, counter;
@@ -43,6 +47,7 @@
{
[super windowControllerDidLoadNib:aController];
// Add any code here that needs to be executed once the windowController has loaded the document's window.
+
}
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
@@ -85,12 +90,16 @@
[inReplyToEntity release];
inReplyToEntity = entity;
[inReplyToEntity retain];
+
+ [self controlTextDidChange:nil];
}
- (void)withString:(NSString *)aString {
[textField setStringValue:aString];
NSRange range = {[[textField stringValue] length] , 0};
- [[textField currentEditor] setSelectedRange:range];
+ [[textField currentEditor] setSelectedRange:range];
+
+ [self controlTextDidChange:nil];
}
-(void)controlTextDidChange:(NSNotification *)aNotification {
@@ -120,4 +129,27 @@
}
+- (BOOL)isCommandEnterEvent:(NSEvent *)e {
+ NSUInteger flags = (e.modifierFlags & NSDeviceIndependentModifierFlagsMask);
+ BOOL isCommand = (flags & NSCommandKeyMask) == NSCommandKeyMask;
+ BOOL isEnter = (e.keyCode == 0x24); // VK_RETURN
+ return (isCommand && isEnter);
+}
+
+- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
+{
+ BOOL retval = NO;
+
+ if (commandSelector == @selector(insertNewline:)) {
+ retval = YES; // causes Apple to NOT fire the default enter action
+ textField.stringValue = [NSString stringWithFormat:@"%@\n", textField.stringValue];
+ }
+
+ if (commandSelector == @selector(noop:)) {
+ retval = YES;
+ [self sendTweet:control];
+ }
+
+ return retval;
+}
@end
diff --git a/OauthImplementation.js b/OauthImplementation.js
index 06e6880..09e40bb 100644
--- a/OauthImplementation.js
+++ b/OauthImplementation.js
@@ -66,7 +66,7 @@ OauthImplementation.prototype.register = function (url) {
var data = JSON.parse(resp.responseText);
those.authRequest(data);
}
- getURL(those.apiRoot() + "/apps", "POST", callback, JSON.stringify(those.app_info));
+ getURL(mkApiRootPath("/apps"), "POST", callback, JSON.stringify(those.app_info));
});
}
@@ -98,7 +98,7 @@ OauthImplementation.prototype.requestAccessToken = function(responseBody) {
var urlVars = getUrlVars(responseBody);
if(this.state && this.state != "" && urlVars["state"] == this.state) {
- var url = this.apiRoot() + "/apps/" + this.register_data["id"] + "/authorizations";
+ var url = mkApiRootPath("/apps/") + this.register_data["id"] + "/authorizations";
var requestBody = JSON.stringify({
'code' : urlVars["code"],
diff --git a/Tentia.xcodeproj/project.pbxproj b/Tentia.xcodeproj/project.pbxproj
index e1af4ea..2c2dff3 100644
--- a/Tentia.xcodeproj/project.pbxproj
+++ b/Tentia.xcodeproj/project.pbxproj
@@ -11,7 +11,6 @@
1DDD582D0DA1D0D100B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD582A0DA1D0D100B32029 /* MainMenu.xib */; };
1F122D49118E1DE100E83B77 /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 1F122D48118E1DE100E83B77 /* Icon.icns */; };
1F1990C6117BCA960049BEA7 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F1990C5117BCA960049BEA7 /* ApplicationServices.framework */; };
- 1F245D6F1632AEFE00E4469A /* jso.js in Sources */ = {isa = PBXBuildFile; fileRef = 1F245D6E1632AEFE00E4469A /* jso.js */; };
1F2746FC12D9057600339B4F /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 1FE2FCA6117A8952000504B0 /* dsa_pub.pem */; };
1F4673FE1180F7EA006CC37C /* Core.js in Resources */ = {isa = PBXBuildFile; fileRef = 1F4673E61180F654006CC37C /* Core.js */; };
1F4674081180F7EE006CC37C /* jQuery.js in Resources */ = {isa = PBXBuildFile; fileRef = 1F4673E21180F519006CC37C /* jQuery.js */; };
@@ -75,7 +74,6 @@
1F1990C5117BCA960049BEA7 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; };
1F1990DF117BD2250049BEA7 /* Appcast.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Appcast.xml; sourceTree = ""; };
1F1990E1117BD2650049BEA7 /* ReleaseNotes.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; lineEnding = 0; path = ReleaseNotes.html; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.html; };
- 1F245D6E1632AEFE00E4469A /* jso.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jso.js; sourceTree = ""; };
1F36440E118CC173008198EF /* OAuthConsumer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OAuthConsumer.framework; sourceTree = ""; };
1F4673E21180F519006CC37C /* jQuery.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jQuery.js; sourceTree = ""; };
1F4673E41180F590006CC37C /* jQuery-Plugins.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "jQuery-Plugins.js"; sourceTree = ""; };
@@ -174,7 +172,6 @@
1F9D655D163B63E300B7282B /* URI.min.js */,
1F9816C6163915A100AFD4EE /* enc-base64-min.js */,
1F9816C7163915A100AFD4EE /* hmac-sha256.js */,
- 1F245D6E1632AEFE00E4469A /* jso.js */,
1F4673E61180F654006CC37C /* Core.js */,
1FC254911427ADF90035D84B /* OauthImplementation.js */,
1FC2549D1427DC2B0035D84B /* Constants.js */,
@@ -355,7 +352,6 @@
1FFA36D81177D879006C8562 /* ViewDelegate.m in Sources */,
1F77DB47118C5F1C007C7F1E /* Constants.m in Sources */,
1F618ECA12DB5E6100E500D9 /* TweetModel.m in Sources */,
- 1F245D6F1632AEFE00E4469A /* jso.js in Sources */,
1F9816C8163915A100AFD4EE /* enc-base64-min.js in Sources */,
1F9816C9163915A100AFD4EE /* hmac-sha256.js in Sources */,
1F9D655E163B63E300B7282B /* URI.min.js in Sources */,
diff --git a/ViewDelegate.m b/ViewDelegate.m
index f3802f6..ad041fb 100644
--- a/ViewDelegate.m
+++ b/ViewDelegate.m
@@ -65,4 +65,20 @@
}
}
+- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems {
+ for (NSMenuItem*item in defaultMenuItems) {
+ if ([[item title] isEqualToString:@"Reload"]) {
+ [item setAction:@selector(reload:)];
+ [item setTarget:self];
+ }
+ }
+
+ return defaultMenuItems;
+}
+
+- (void)reload:(id)sender {
+ [timelineView stringByEvaluatingJavaScriptFromString:@"tentia_instance.getNewData();"];
+ [mentionsView stringByEvaluatingJavaScriptFromString:@"tentia_instance.getNewData();"];
+}
+
@end
diff --git a/hmac-helper.js b/hmac-helper.js
index 53c4f1d..f3ad041 100644
--- a/hmac-helper.js
+++ b/hmac-helper.js
@@ -104,6 +104,16 @@ function findProfileURL(entity, callback) {
});
}
+function mkApiRootPath(path) {
+ var api_root = controller.stringForKey_("api_root");
+ if((api_root.substring(api_root.length - 1, api_root.length) != "/") && (path.substring(0, 1) != "/")) {
+ api_root += "/";
+ } else if((api_root.substring(api_root.length - 1, api_root.length) == "/") && (path.substring(0, 1) == "/")) {
+ api_root = api_root.substring(0, api_root.length -1);
+ }
+ return api_root + path;
+}
+
function debug(string) {
if (typeof string == "Object") {
string = JSON.stirngify(string);