Merge branch 't0.3' because this will be the new master anyway.

This commit is contained in:
jeena 2013-09-03 13:07:17 +02:00
commit 8e614555bd
25 changed files with 1836 additions and 1229 deletions

View file

@ -107,15 +107,12 @@ class Controller(QtCore.QObject):
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
self.app = app self.app = app
oldpath = os.path.expanduser('~/.bungloo/') name = "bungloo2"
if os.path.isdir(oldpath):
shutil.copytree(oldpath, os.path.expanduser('~/.config/bungloo/'))
shutil.rmtree(os.path.expanduser('~/.bungloo/'))
if not os.path.exists(os.path.expanduser("~/.config/bungloo/")): if not os.path.exists(os.path.expanduser("~/.config/" + name + "/")):
os.makedirs(os.path.expanduser("~/.config/bungloo/")) os.makedirs(os.path.expanduser("~/.config/" + name + "/"))
self.config_path = os.path.expanduser('~/.config/bungloo/bungloo.cfg') self.config_path = os.path.expanduser('~/.config/' + name + '/bungloo.cfg')
if os.access(self.config_path, os.R_OK): if os.access(self.config_path, os.R_OK):
with open(self.config_path, 'r') as f: with open(self.config_path, 'r') as f:
@ -173,22 +170,18 @@ class Controller(QtCore.QObject):
except OSError: except OSError:
pass pass
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot()
def openNewMessageWidow(self, is_private=False, string=""): def openNewMessageWidow(self):
string = str(string) self.openNewMessageWindowInReplyToStatus("")
self.openNewMessageWindowInReplyTostatusIdwithStringIsPrivate(None, None, string, is_private)
@QtCore.pyqtSlot(str, str, str, bool) @QtCore.pyqtSlot(str)
def openNewMessageWindowInReplyTostatusIdwithStringIsPrivate(self, entity, status_id, string, is_private): def openNewMessageWindowInReplyToStatus(self, status_string):
new_message_window = Windows.NewPost(self.app) new_message_window = Windows.NewPost(self.app, status_string)
new_message_window.inReplyToStatusIdWithString(entity, status_id, string)
new_message_window.setIsPrivate(is_private)
new_message_window.show() new_message_window.show()
new_message_window.setAttribute(QtCore.Qt.WA_DeleteOnClose) new_message_window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.app.new_message_windows.append(new_message_window) self.app.new_message_windows.append(new_message_window)
new_message_window.activateWindow() new_message_window.activateWindow()
new_message_window.setFocus() new_message_window.setFocus()
new_message_window.textInput.setFocus()
new_message_window.show() new_message_window.show()
new_message_window.raise_() new_message_window.raise_()
@ -268,6 +261,16 @@ class Controller(QtCore.QObject):
msgBox.setInformativeText(message) msgBox.setInformativeText(message)
msgBox.exec_() msgBox.exec_()
@QtCore.pyqtSlot(result=str)
def getCachedProfiles(self):
entities = self.app.timeline.evaluateJavaScript("JSON.stringify(bungloo.cache.profiles);")
return entities.toString()
@QtCore.pyqtSlot()
def getNewData(self):
func = "bungloo.timeline.getNewData()"
self.app.timeline.evaluateJavaScript(func)
def logout(self, sender): def logout(self, sender):
print "logout is not implemented yet" print "logout is not implemented yet"
@ -297,7 +300,7 @@ class Console(QtCore.QObject):
if __name__ == "__main__": if __name__ == "__main__":
key = 'BUNGLOO' key = 'BUNGLOO2'
if len(sys.argv) > 1 and sys.argv[1] == "--help": if len(sys.argv) > 1 and sys.argv[1] == "--help":
print """ print """

View file

@ -9,7 +9,7 @@ class Preferences:
# window # window
self.window = QtGui.QMainWindow() self.window = QtGui.QMainWindow()
self.window.setWindowTitle("Preferences") self.window.setWindowTitle("Login")
self.window.resize(480, 186) self.window.resize(480, 186)
self.window.setMinimumSize(480, 186) self.window.setMinimumSize(480, 186)
self.window.setMaximumSize(480, 186) self.window.setMaximumSize(480, 186)
@ -242,7 +242,7 @@ class Oauth:
new_manager.sslErrors.connect(lambda reply, errors: self.handleSslErrors(reply, errors)) new_manager.sslErrors.connect(lambda reply, errors: self.handleSslErrors(reply, errors))
self.auth_view.page().setNetworkAccessManager(new_manager) self.auth_view.page().setNetworkAccessManager(new_manager)
self.auth_view.show() self.auth_view.show()
print url
self.auth_view.load_url(url) self.auth_view.load_url(url)
return False return False
@ -329,25 +329,29 @@ class FindEntity(QtGui.QDialog):
class NewPost(Helper.RestorableWindow): class NewPost(Helper.RestorableWindow):
def __init__(self, app): def __init__(self, app, status_string):
self.app = app self.app = app
self.status_string = status_string
Helper.RestorableWindow.__init__(self, "newpost", self.app) Helper.RestorableWindow.__init__(self, "newpost", self.app)
self.activateWindow()
self.raise_()
self.setWindowIcon(QtGui.QIcon(self.app.resources_path() + "/images/Icon.png")) self.setWindowIcon(QtGui.QIcon(self.app.resources_path() + "/images/Icon.png"))
self.textInput = QtGui.QPlainTextEdit(self) self.webView = Helper.WebViewCreator(self.app, True, self)
self.setCentralWidget(self.textInput) self.webView.load_local(self.load_finished)
self.textInput.textChanged.connect(self.onChanged) self.setCentralWidget(self.webView)
self.initUI()
self.webView.triggerPageAction(QtWebKit.QWebPage.InspectElement)
frame = self.webView.page().mainFrame()
frame.addToJavaScriptWindowObject("new_post_window", self)
self.setWindowTitle("New Post") self.setWindowTitle("New Post")
self.resize(300, 150) self.resize(300, 150)
self.setMinimumSize(100, 100) self.setMinimumSize(100, 100)
self.initUI()
self.setIsPrivate(False)
self.status_id = None
self.reply_to_entity = None
self.imageFilePath = None
def initUI(self): def initUI(self):
newPostAction = QtGui.QAction("&New Post", self) newPostAction = QtGui.QAction("&New Post", self)
@ -396,89 +400,38 @@ class NewPost(Helper.RestorableWindow):
aboutAction.setStatusTip("Open about page in Webbrowser") aboutAction.setStatusTip("Open about page in Webbrowser")
aboutAction.triggered.connect(self.app.open_about) aboutAction.triggered.connect(self.app.open_about)
developerExtrasAction = QtGui.QAction("&Developer Extras", self)
developerExtrasAction.setStatusTip("Activate webkit inspector")
developerExtrasAction.triggered.connect(self.developer_extras)
helpMenu = menubar.addMenu("&Help") helpMenu = menubar.addMenu("&Help")
helpMenu.addAction(aboutAction) helpMenu.addAction(aboutAction)
helpMenu.addAction(developerExtrasAction)
def load_finished(self, widget):
self.statusBar().showMessage('256') callback = "function() { bungloo.newpost.setStatus('%s'); }" % (self.status_string)
script = "function HostAppGo() { start('newpost', " + callback + "); }"
self.addButton = QtGui.QToolButton() self.webView.page().mainFrame().evaluateJavaScript(script)
self.addButton.setToolTip("Add photo") self.webView.setFocus()
self.addButton.clicked.connect(self.openFileDialog)
self.addButton.setAutoRaise(True)
#addIcon = QtGui.QIcon.fromTheme("insert-image", QtGui.QIcon(self.app.resources_path() + "/images/Actions-insert-image-icon.png"))
addIcon = QtGui.QIcon(self.app.resources_path() + "/images/glyphicons_138_picture.png")
self.addButton.setIcon(addIcon)
self.statusBar().addPermanentWidget(self.addButton)
self.isPrivateButton = QtGui.QToolButton()
self.isPrivateButton.setToolTip("Make private")
self.isPrivateButton.clicked.connect(self.toggleIsPrivate)
self.isPrivateButton.setAutoRaise(True)
#self.isPrivateIcon = QtGui.QIcon(self.app.resources_path() + "/images/Lock-Lock-icon.png")
self.isPrivateIcon = QtGui.QIcon(self.app.resources_path() + "/images/glyphicons_203_lock.png")
#self.isNotPrivateIcon = QtGui.QIcon(self.app.resources_path() + "/images/Lock-Unlock-icon.png")
self.isNotPrivateIcon = QtGui.QIcon(self.app.resources_path() + "/images/glyphicons_204_unlock.png")
self.isPrivateButton.setIcon(self.isNotPrivateIcon)
self.statusBar().addPermanentWidget(self.isPrivateButton)
self.sendButton = QtGui.QToolButton()
self.sendButton.setToolTip("Send")
self.sendButton.clicked.connect(self.sendMessage)
self.sendButton.setAutoRaise(True)
#sendIcon = QtGui.QIcon.fromTheme("mail-send", QtGui.QIcon(self.app.resources_path() + "/images/send-icon.png"))
sendIcon = QtGui.QIcon(self.app.resources_path() + "/images/glyphicons_123_message_out.png")
self.sendButton.setIcon(sendIcon)
self.statusBar().addPermanentWidget(self.sendButton)
def setIsPrivate(self, is_private):
self.isPrivate = is_private
icon = self.isNotPrivateIcon
if self.isPrivate:
icon = self.isPrivateIcon
self.isPrivateButton.setIcon(icon)
def toggleIsPrivate(self): def toggleIsPrivate(self):
self.setIsPrivate(not self.isPrivate) script = "bungloo.newpost.toggleIsPrivate();"
self.webView.page().mainFrame().evaluateJavaScript(script)
def setString(self, string):
self.inReplyToStatusIdWithString(None, None, string)
def inReplyToStatusIdWithString(self, reply_to, status_id, string):
self.reply_to_entity = reply_to
self.status_id = status_id
self.textInput.setPlainText(string)
cursor = self.textInput.textCursor()
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
cursor.movePosition(QtGui.QTextCursor.Start, QtGui.QTextCursor.KeepAnchor)
cursor.movePosition(QtGui.QTextCursor.EndOfLine, QtGui.QTextCursor.KeepAnchor)
self.textInput.setTextCursor(cursor)
def onChanged(self):
count = 256 - len(self.textInput.toPlainText())
self.statusBar().showMessage(str(count))
def sendMessage(self): def sendMessage(self):
count = len(self.textInput.toPlainText()) script = "bungloo.newpost.send()"
if count > 0 and count <= 256: self.webView.page().mainFrame().evaluateJavaScript(script)
message = Helper.PostModel()
message.text = unicode(self.textInput.toPlainText().toUtf8(), "utf-8") def developer_extras(self, widget):
message.inReplyTostatusId = self.status_id QtWebKit.QWebSettings.globalSettings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
message.inReplyToEntity = self.reply_to_entity
message.location = None
message.imageFilePath = self.imageFilePath
message.isPrivate = self.isPrivate
self.app.controller.sendMessage(message)
self.close()
else:
QtGui.qApp.beep()
def openFileDialog(self): def openFileDialog(self):
fileNamePath = QtGui.QFileDialog.getOpenFileName(self, "Choose a image", "", "Images (*.png *.gif *.jpg *.jpeg)") print "openFileDialog Not implemented yet"
if len(fileNamePath) > 0:
self.imageFilePath = str(fileNamePath)
else:
self.imageFilePath = None
@QtCore.pyqtSlot()
def closeWindow(self):
self.close()
@QtCore.pyqtSlot()
def beep(self):
QtGui.qApp.beep()

View file

@ -18,6 +18,15 @@ a {
text-decoration: none; text-decoration: none;
color: #00317a; color: #00317a;
outline: 0; outline: 0;
outline : none;
}
button {
background: transparent;
border: 0;
margin: 0;
padding: 4px 5px 0 5px;
outline : none;
} }
#sidebar { #sidebar {
@ -446,4 +455,23 @@ form.search input {
p.noresult { p.noresult {
padding : 10px; padding : 10px;
text-align : center; text-align : center;
} }
.new_post #sidebar, .new_post #content { display: none; }
.new_post { height: 100%; }
#new_post_container { position: absolute; border-collapse: collapse; height: 100%; width: 100%; }
#new_post_container td { position: relative; height: 90%; }
#new_post_container .text td { background: white; }
#new_post_container textarea { resize: none; box-sizing: border-box; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0; background: transparent; outline: none; }
#new_post_container div { box-sizing: border-box; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0; background: white; color: white; padding: 2px; }
#new_post_container div span { background: #D8DFEA; }
#suggestions { width: 100%; position: absolute; left: 0; bottom: 0; background: #efefef; list-style-type: none; padding: 0; margin: 0; border-top: 1px solid #ccc; }
#suggestions li { border-top: 1px solid #fefefe; border-bottom: #c9c9c9; padding: 0 0.5em; }
#suggestions strong { font-weight: normal; color: #555; }
#suggestions .active { background: #dedede; }
#suggestions .active strong { color: black; }
#status_bar { height: 1em; border-top: 1px solid #ccc; }
#status_bar p { float: right; margin: 0; padding: 0; }
#status_bar span { display: inline-block; margin: 4px 5px 0 5px; }

View file

Before

Width:  |  Height:  |  Size: 338 B

After

Width:  |  Height:  |  Size: 338 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 325 B

After

Width:  |  Height:  |  Size: 325 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 231 B

After

Width:  |  Height:  |  Size: 231 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 320 B

Before After
Before After

View file

@ -1,11 +1,11 @@
define([ define([
"helper/HostApp", "helper/HostApp",
"helper/Core", "helper/Core",
"helper/Paths", "helper/APICalls",
"lib/URI" "lib/URI"
], ],
function(HostApp, Core, Paths, URI) { function(HostApp, Core, APICalls, URI) {
function Conversation(standalone) { function Conversation(standalone) {
@ -86,15 +86,15 @@ function(HostApp, Core, Paths, URI) {
function getRemoteStatus(profile) { function getRemoteStatus(profile) {
var server = profile["https://tent.io/types/info/core/v0.1.0"].servers[0]; var server = profile["https://tent.io/types/info/core/v0.1.0"].servers[0];
Paths.getURL(URI(server + "/posts/" + id).toString(), "GET", callback, null, false); APICalls.http_call(URI(server + "/posts/" + id).toString(), "GET", callback, null, false);
} }
var profile = this.cache.profiles.getItem(entity); var profile = this.cache.profiles.getItem(entity);
if (entity == HostApp.stringForKey("entity")) { if (entity == HostApp.stringForKey("entity")) {
var url = URI(Paths.mkApiRootPath("/posts/" + id)); var url = URI(APICalls.mkApiRootPath("/posts/" + id));
Paths.getURL(url.toString(), "GET", callback, null); APICalls.http_call(url.toString(), "GET", callback, null);
} else if(profile) { } else if(profile) {
@ -102,7 +102,7 @@ function(HostApp, Core, Paths, URI) {
} else { } else {
Paths.findProfileURL(entity, function(profile_url) { APICalls.findProfileURL(entity, function(profile_url) {
if (profile_url) { if (profile_url) {
@ -113,7 +113,7 @@ function(HostApp, Core, Paths, URI) {
} else { } else {
Paths.getURL(profile_url, "GET", function(resp) { APICalls.http_call(profile_url, "GET", function(resp) {
var profile = JSON.parse(resp.responseText) var profile = JSON.parse(resp.responseText)
this.cache.profiles.setItem(entity, profile); this.cache.profiles.setItem(entity, profile);
@ -128,7 +128,7 @@ function(HostApp, Core, Paths, URI) {
Conversation.prototype.appendMentioned = function(id, entity, node) { Conversation.prototype.appendMentioned = function(id, entity, node) {
var url = URI(Paths.mkApiRootPath("/posts")); var url = URI(APICalls.mkApiRootPath("/posts"));
url.addSearch("mentioned_post", id); url.addSearch("mentioned_post", id);
url.addSearch("post_types", "https%3A%2F%2Ftent.io%2Ftypes%2Fpost%2Fstatus%2Fv0.1.0"); url.addSearch("post_types", "https%3A%2F%2Ftent.io%2Ftypes%2Fpost%2Fstatus%2Fv0.1.0");
@ -147,7 +147,7 @@ function(HostApp, Core, Paths, URI) {
} }
} }
Paths.getURL(url.toString(), "GET", callback); APICalls.http_call(url.toString(), "GET", callback);
} }

View file

@ -2,11 +2,11 @@ define([
"helper/HostApp", "helper/HostApp",
"controller/Timeline", "controller/Timeline",
"lib/URI", "lib/URI",
"helper/Paths", "helper/APICalls",
"helper/Core" "helper/Core"
], ],
function(HostApp, Timeline, URI, Paths, Core) { function(HostApp, Timeline, URI, APICalls, Core) {
function Mentions() { function Mentions() {
@ -36,7 +36,7 @@ function(HostApp, Timeline, URI, Paths, Core) {
Mentions.prototype.newStatus = function(statuses, append) { Mentions.prototype.newStatus = function(statuses, append) {
Timeline.prototype.newStatus.call(this, statuses, append); Timeline.prototype.newStatus.call(this, statuses, append);
/*
if(this.is_not_init) { if(this.is_not_init) {
for (var i = 0; i < statuses.length; i++) { for (var i = 0; i < statuses.length; i++) {
@ -51,21 +51,21 @@ function(HostApp, Timeline, URI, Paths, Core) {
if(!append) HostApp.notificateUserAboutMention(status.content.text, name || status.entity, status.id, status.entity); if(!append) HostApp.notificateUserAboutMention(status.content.text, name || status.entity, status.id, status.entity);
} }
} }
*/
this.is_not_init = true; this.is_not_init = true;
} }
Mentions.prototype.getNewData = function(add_to_search, append) { Mentions.prototype.getNewData = function(add_to_search, append, query) {
add_to_search = add_to_search || {}; add_to_search = add_to_search || {};
if (!add_to_search["mentioned_entity"]) { if (!add_to_search["mentions"]) {
add_to_search["mentioned_entity"] = HostApp.stringForKey("entity"); add_to_search["mentions"] = HostApp.stringForKey("entity");
} }
Timeline.prototype.getNewData.call(this, add_to_search, append); Timeline.prototype.getNewData.call(this, add_to_search, append, query);
this.getLatestMentionRead(); //this.getLatestMentionRead();
} }
Mentions.prototype.mentionRead = function(id, entity) { Mentions.prototype.mentionRead = function(id, entity) {
@ -90,7 +90,7 @@ function(HostApp, Timeline, URI, Paths, Core) {
if (!status.__repost) { if (!status.__repost) {
if (status && status.type == "https://tent.io/types/post/status/v0.1.0") { if (status && status.type == "https://tent.io/types/post/status/v0.1.0") {
var url = URI(Paths.mkApiRootPath("/profile/" + encodeURIComponent("https://tent.io/types/info/cursor/v0.1.0"))); var url = URI(APICalls.mkApiRootPath("/profile/" + encodeURIComponent("https://tent.io/types/info/cursor/v0.1.0")));
var body = { var body = {
"mentions": { "mentions": {
"https://tent.io/types/post/status/v0.1.0": { "https://tent.io/types/post/status/v0.1.0": {
@ -104,7 +104,7 @@ function(HostApp, Timeline, URI, Paths, Core) {
} }
Paths.getURL(url.toString(), "PUT", callback, JSON.stringify(body)); APICalls.http_call(url.toString(), "PUT", callback, JSON.stringify(body));
} }
break; break;
@ -115,11 +115,11 @@ function(HostApp, Timeline, URI, Paths, Core) {
Mentions.prototype.getLatestMentionRead = function() { Mentions.prototype.getLatestMentionRead = function() {
var cursor_url = URI(Paths.mkApiRootPath("/profile/" + encodeURIComponent("https://tent.io/types/info/cursor/v0.1.0"))); var cursor_url = URI(APICalls.mkApiRootPath("/profile/" + encodeURIComponent("https://tent.io/types/info/cursor/v0.1.0")));
Paths.getURL(cursor_url.toString(), "GET", function(resp) { APICalls.http_call(cursor_url.toString(), "GET", function(resp) {
var url = URI(Paths.mkApiRootPath("/posts/count")); var url = URI(APICalls.mkApiRootPath("/posts/count"));
var post_types = [ var post_types = [
"https://tent.io/types/post/status/v0.1.0", "https://tent.io/types/post/status/v0.1.0",
]; ];
@ -139,7 +139,7 @@ function(HostApp, Timeline, URI, Paths, Core) {
HostApp.unreadMentions(this.unread_mentions); HostApp.unreadMentions(this.unread_mentions);
} }
Paths.getURL(url.toString(), "GET", callback); // FIXME: error callback APICalls.http_call(url.toString(), "GET", callback); // FIXME: error callback
}); });
} }

View file

@ -0,0 +1,395 @@
define([
"helper/APICalls",
"helper/HostApp"
],
function(APICalls, HostApp) {
function NewPost() {
this.profiles = JSON.parse(controller.getCachedProfiles());
for (var key in this.profiles) {
var item = this.profiles[key];
if(!item.entity) item.entity = key;
if(!item.name) item.name = key;
}
this.mentions = [];
document.body.className = "new_post";
this.is_private = false;
// Textarea
this.container = $("<table id='new_post_container'><tr class='text'><td><div></div><textarea></textarea></td></tr><tr><td id='status_bar'></td></tr></table>");
this.textarea = this.container.find("textarea");
this.highlighter = this.container.find("div");
$(document.body).append(this.container);
this.textarea.keyup(this.keyup.bind(this));
this.textarea.keydown(this.keydown.bind(this));
this.suggestions = $("<ul id='suggestions'></ul>");
$(document.body).append(this.suggestions);
// Status bar
this.counter = $("<span>256</span>");
var buttons = $(
"<p>" +
//"<button id='images'><img src='images/images.png'></button>" +
"<button id='private'><img src='img/public.png'></button>" +
"<button id='send'><img src='img/send.png'></button>" +
"</p>");
this.buttons = {
images: buttons.find("#images"),
is_private: buttons.find("#private"),
send: buttons.find("#send")
}
//this.buttons.images.bind("click", this.addImage.bind(this));
this.buttons.is_private.bind("click", this.toggleIsPrivate.bind(this));
this.buttons.send.bind("click", this.send.bind(this));
this.container.find("#status_bar").append(this.counter);
this.container.find("#status_bar").append(buttons);
this.textarea.focus();
this.setIsPrivate(false);
}
NewPost.prototype.setStatus = function(status_string) {
if (status_string && status_string.length > 0) {
this.status = JSON.parse(status_string);
this.setIsPrivate(this.status.permissions && !this.status.permissions.public);
this.setMentions(this.status);
} else {
this.status = null;
}
// FIXME set string, private, mentions, etc.
};
NewPost.prototype.setString = function(string) {
this.textarea.val(string);
}
NewPost.prototype.setMentions = function(status) {
var mentions = [this.profiles[status.entity]];
var text = this.profiles[status.entity].name + " ";
var start = text.length;
if(status.mentions && status.mentions.length > 0) {
var mentions_text = ""
for (var i = 0; i < status.mentions.length; i++) {
var entity = status.mentions[i].entity;
// Sometimes there are mentions without entity, don't know why
if(entity) {
// fix broken profiles
var profile = this.profiles[entity];
if(!profile) {
profile = {};
this.profiles[entity] = profile;
}
if(!profile.entity) profile.entity = entity;
if(!profile.name) profile.name = entity;
// add profile to mentions and textarea
mentions.push(profile);
mentions_text += profile.name;
// add space after mention
if(i < status.mentions.length) {
mentions_text += " ";
}
}
}
if (mentions_text.length > 0) {
text += "\n\n/cc " + mentions_text;
};
}
this.mentions = mentions;
this.textarea.val(text);
this.parseText(text);
// Select other mentions so user can start writing and removing them
var end = text.length;
this.textarea.get(0).setSelectionRange(start, end);
}
NewPost.prototype.setIsPrivate = function(is_private) {
this.is_private = is_private;
if (this.is_private) {
this.buttons.is_private.find("img").attr("src", "img/private.png");
} else {
this.buttons.is_private.find("img").attr("src", "img/public.png");
}
}
NewPost.prototype.toggleIsPrivate = function() {
this.setIsPrivate(!this.is_private);
}
NewPost.prototype.keyup = function(e) {
if(!e) return;
var key = e.which;
if(key != 38 && key != 40 && key != 13) {
this.applyText($(this.textarea).val());
} else {
var lis = this.suggestions.find("li");
if (lis.length > 0) {
e.preventDefault();
var active = this.suggestions.find(".active");
if(key == 38) { // up
var prev = active.prev();
if(active.lentgh == 0) {
lis.last().addClass("active");
} else if(prev) {
active.removeClass("active");
prev.addClass("active");
}
} else if(key == 40) { // down
var next = active.next();
if(active.length == 0) {
lis.first().addClass("active");
} else if(next) {
active.removeClass("active");
next.addClass("active");
}
} else if(key == 13) { // enter
if(active.length > 0) {
this.replaceWithName(this.textarea.val(), this.suggestions.find("li.active").get(0).item);
}
}
}
}
}
NewPost.prototype.keydown = function(e) {
var key = e.which;
var lis = this.suggestions.find("li");
if(lis.length > 0 && (key == 38 || key == 40 || key == 13)) {
e.preventDefault();
}
}
NewPost.prototype.replaceAll = function(txt, replace, with_this) {
return txt.replace(new RegExp(replace, 'g'),with_this);
}
NewPost.prototype.replaceWithName = function(txt, with_item) {
var words = txt.match(/(^|\s)\^([^\s]+)/);
var replace = words[2];
var original = txt.replace("^" + replace, with_item.name + " ");
this.textarea.val(original);
this.mentions.push(with_item);
this.applyText(original);
}
NewPost.prototype.applyText = function (text) {
var words = text.match(/(^|\s)\^([^\s]+)/);
this.suggestions.html("");
if(words) {
var name = words[2];
for (var key in this.profiles) {
var item = this.profiles[key];
if((item.name.toLowerCase().indexOf(name.toLowerCase()) != -1) || item.entity.toLowerCase().indexOf(name.toLowerCase()) != -1) {
var li = $("<li><strong title='" + item.entity + "'>" + item.name + "</strong></li>")
li.get(0).item = item;
this.suggestions.append(li);
}
}
this.suggestions.find("li:first-child").addClass("active");
}
this.parseText(text);
}
NewPost.prototype.parseText = function(text) {
// parse the text:
// replace all the line braks by <br/>, and all the double spaces by the html version &nbsp;
text = this.replaceAll(text,'\n','<br/>');
text = this.replaceAll(text,' ','&nbsp;&nbsp;');
// replace the words by a highlighted version of the words
var remove = [];
for (var i=0;i<this.mentions.length; i++) {
var name = this.mentions[i].name;
if(text.match(new RegExp(name))) {
text = this.replaceAll(text, name, '<span>' + name + '</span>');
} else {
remove.push(this.mentions[i]);
}
}
for (var i = 0; i < remove.length; i++) {
this.mentions.splice(this.mentions.indexOf(remove[i]), 1);
}
// re-inject the processed text into the div
this.highlighter.html(text);
var count = 256 - this.textarea.val().length + (this.mentions.length * 6);
this.counter.html(count);
}
NewPost.prototype.send = function() {
var count = 256 - this.textarea.val().length + (this.mentions.length * 6);
if(count >= 0 && count <= 256) {
this.sendNewMessage();
return true;
} else {
debug("BEEP");
return false;
}
}
NewPost.prototype.sendNewMessage = function() {
var content = this.textarea.val();
var type = "https://tent.io/types/status/v0#";
var data = {
type: type,
content: {
text: content
},
permissions: {
public: !this.is_private
}
};
var mentions = [];
for (var i = 0; i < this.mentions.length; i++) {
var mention = this.mentions[i];
if(this.status && this.status.entity == mention.entity) {
mentions.push({
entity: this.status.entity,
post: this.status.id,
type: this.status.type
});
} else {
mentions.push({
entity: mention.entity
});
}
}
data.mentions = mentions;
// Make tent flavored markdown mentions
for (var i = 0; i < this.mentions.length; i++) {
var mention = this.mentions[i];
data.content.text = this.replaceAll(data.content.text, mention.name, "^[" + mention.name + "](" + i + ")")
}
APICalls.post(HostApp.serverUrl("new_post"), JSON.stringify(data), {
content_type: data.type,
accept: 'application/vnd.tent.post.v0+json; type="https://tent.io/types/status/v0#"',
callback: function(resp) {
if (resp.status >= 200 < 300) {
new_post_window.closeWindow();
controller.getNewData();
} else {
new_post_window.beep();
}
}
});
}
/*
NewPost.prototype.sendNewMessageWithImage = function(content, in_reply_to_status_id, in_reply_to_entity, location, image_data_uri, is_private, callback) {
var url = URI(APICalls.mkApiRootPath("/posts"));
var data = {
"type": "https://tent.io/types/post/photo/v0.1.0",
"published_at": parseInt(new Date().getTime() / 1000, 10),
"permissions": {
"public": !is_private
},
"content": {
"caption": content,
},
};
if (location) {
data["content"]["location"] = { "type": "Point", "coordinates": location }
}
var mentions = this.parseMentions(content, in_reply_to_status_id, in_reply_to_entity);
if (mentions.length > 0) {
data["mentions"] = mentions;
if (is_private) {
var entities = {};
for (var i = 0; i < mentions.length; i++) {
var entity = mentions[i]["entity"]
entities[entity] = true;
};
data["permissions"]["entities"] = entities;
}
}
var data_string = JSON.stringify(data);
var boundary = "TentAttachment----------TentAttachment";
var post = "--" + boundary + "\r\n";
post += 'Content-Disposition: form-data; name="post"; filename="post.json"\r\n';
post += 'Content-Length: ' + data_string.length + '\r\n';
post += 'Content-Type: application/vnd.tent.v0+json\r\n';
post += 'Content-Transfer-Encoding: binary\r\n\r\n';
post += data_string;
post += "\r\n--" + boundary + "\r\n";
var blob_string = image_data_uri.split(',')[1];
var mime_type = image_data_uri.split(',')[0].split(':')[1].split(';')[0];
var ext = "png";
if (mime_type == "image/jpeg") {
ext = "jpeg";
} else if (mime_type == "image/gif") {
ext = "gif";
}
post += 'Content-Disposition: form-data; name="photos[0]"; filename="photo.' + ext + '"\r\n';
post += 'Content-Length: ' + blob_string.length + "\r\n";
post += 'Content-Type: ' + mime_type + "\r\n";
post += 'Content-Transfer-Encoding: base64\r\n\r\n';
post += blob_string;
post += "\r\n--" + boundary + "--\r\n";
var newCallback = function(resp) {
if (resp.status == 403) {
var err = JSON.parse(resp.responseText);
HostApp.alertTitleWithMessage(resp.statusText, err.error);
}
callback(resp);
}
APICalls.postMultipart(url.toString(), newCallback, post, boundary);
}
*/
return NewPost;
})

View file

@ -1,38 +1,46 @@
define([ define([
"helper/HostApp", "helper/HostApp",
"helper/Paths", "helper/APICalls",
"helper/Hmac" "helper/Hmac"
], ],
function(HostApp, Paths, Hmac) { function(HostApp, APICalls, Hmac) {
function Oauth() { function Oauth() {
this.app_info = { this.app_info = {
"id": null, "type": "https://tent.io/types/app/v0#",
"name": "Bungloo on " + HostApp.osType(), "content": {
"description": "A small TentStatus client.", "name": "Bungloo on " + HostApp.osType(),
"url": "http://jabs.nu/bungloo/", "url": "http://jabs.nu/bungloo/",
"icon": "http://jabs.nu/bungloo/icon.png", "description": "A desktop Tent client.",
"redirect_uris": [ "redirect_uri": "bungloo://oauthtoken",
"bungloo://oauthtoken" "types": {
], "read": [
"scopes": { "https://tent.io/types/meta/v0",
"read_posts": "Uses posts to show them in a list", "https://tent.io/types/relationship/v0",
"write_posts": "Posts on users behalf", "https://tent.io/types/subscription/v0",
"read_profile": "Displays your own profile", "https://tent.io/types/delete/v0",
"write_profile": "Updating profile and mentions pointer", "https://tent.io/types/status/v0",
"read_followers": "Display a list of people who follow you", "https://tent.io/types/repost/v0",
"write_followers": "Be able to block people who follow you", "https://tent.io/types/photo/v0",
"read_followings": "Display following list and their older posts in conversations", "https://tent.io/types/cursor/v0",
"write_followings": "Follow ne entities" "https://tent.io/types/basic-profile/v0"
],
"write": [
"https://tent.io/types/relationship/v0",
"https://tent.io/types/subscription/v0",
"https://tent.io/types/delete/v0",
"https://tent.io/types/status/v0",
"https://tent.io/types/repost/v0",
"https://tent.io/types/photo/v0",
"https://tent.io/types/cursor/v0"
]
},
"scopes": ["permissions"]
}, },
"tent_profile_info_types": [ "all" ], "permissions": {
"tent_post_types": [ "public": false
"https://tent.io/types/post/status/v0.1.0", }
"https://tent.io/types/post/photo/v0.1.0",
"https://tent.io/types/post/repost/v0.1.0",
"https://tent.io/types/post/delete/v0.1.0"
]
}; };
this.register_data = null; this.register_data = null;
this.profile = null; this.profile = null;
@ -61,13 +69,9 @@ function(HostApp, Paths, Hmac) {
} }
} }
Oauth.prototype.apiRoot = function() {
return this.profile["https://tent.io/types/info/core/v0.1.0"]["servers"][0];
}
Oauth.prototype.requestProfileURL = function (entity) { Oauth.prototype.requestProfileURL = function (entity) {
var those = this; var those = this;
Paths.findProfileURL(entity, APICalls.findProfileURL(entity,
function(profile_url) { function(profile_url) {
if (profile_url && (profile_url.startsWith("http://") || profile_url.startsWith("https://"))) { if (profile_url && (profile_url.startsWith("http://") || profile_url.startsWith("https://"))) {
those.register(profile_url); those.register(profile_url);
@ -76,6 +80,7 @@ function(HostApp, Paths, Hmac) {
} }
}, },
function(errorMessage) { // error callback function(errorMessage) { // error callback
HostApp.authentificationDidNotSucceed(errorMessage);
HostApp.authentificationDidNotSucceed("Could not find profile for: " + entity); HostApp.authentificationDidNotSucceed("Could not find profile for: " + entity);
} }
); );
@ -84,72 +89,76 @@ function(HostApp, Paths, Hmac) {
Oauth.prototype.register = function (url) { Oauth.prototype.register = function (url) {
var those = this; var those = this;
Paths.getURL(url, "GET", function(resp) { APICalls.get(url, {
no_auth: true,
callback: function(resp) {
those.profile = JSON.parse(resp.responseText); those.profile = JSON.parse(resp.responseText).post;
those.entity = those.profile["https://tent.io/types/info/core/v0.1.0"].entity; those.entity = those.profile.content.entity;
HostApp.setStringForKey(those.entity, "entity") HostApp.setStringForKey(those.entity, "entity")
HostApp.setStringForKey(those.apiRoot(), "api_root"); HostApp.setServerUrls(those.profile.content.servers[0].urls);
APICalls.post(HostApp.serverUrl("new_post"), JSON.stringify(those.app_info), {
content_type: "https://tent.io/types/app/v0#",
no_auth: true,
callback: function(resp) {
var app_id = JSON.parse(resp.responseText).post.id;
var header_string = resp.getAllResponseHeaders();
var regexp = /https:\/\/tent.io\/rels\/credentials/i
var url = APICalls.parseHeaderForLink(header_string, regexp);
var callback = function(resp) { APICalls.get(url, {
var data = JSON.parse(resp.responseText); content_type: "https://tent.io/types/app/v0#",
those.authRequest(data); no_auth: true,
} callback: function(resp) {
Paths.getURL(Paths.mkApiRootPath("/apps"), "POST", callback, JSON.stringify(those.app_info), false); var data = JSON.parse(resp.responseText);
}, null, false); those.authRequest(data.post, app_id);
}
});
}});
}});
} }
Oauth.prototype.authRequest = function(register_data) { Oauth.prototype.authRequest = function(credentials, app_id) {
// id
// mac_key_id HostApp.setStringForKey(app_id, "app_id");
// mac_key HostApp.setStringForKey(credentials.id, "app_hawk_id");
// mac_algorithm HostApp.setStringForKey(credentials.content.hawk_key, "app_hawk_key");
this.register_data = register_data; HostApp.setStringForKey(credentials.content.hawk_algorithm, "app_hawk_algorithm");
// Needed for later App Registration Modification
HostApp.setStringForKey(register_data["mac_key"], "app_mac_key");
HostApp.setStringForKey(register_data["mac_key_id"], "app_mac_key_id");
HostApp.setStringForKey(register_data["id"], "app_id");
HostApp.setStringForKey(register_data["mac_algorithm"], "app_mac_algorithm");
this.state = Hmac.makeid(19); this.state = Hmac.makeid(19);
var auth = "/oauth/authorize?client_id=" + register_data["id"] var url = HostApp.serverUrl("oauth_auth") + "?client_id=" + app_id + "&state=" + this.state;
+ "&redirect_uri=" + this.app_info["redirect_uris"][0] HostApp.openAuthorizationURL(url);
+ "&scope=" + Object.keys(this.app_info["scopes"]).join(",")
+ "&state=" + this.state
+ "&tent_post_types=" + this.app_info["tent_post_types"].join(",")
+ "&tent_profile_info_types=" + this.app_info["tent_profile_info_types"].join(",");
HostApp.openAuthorizationURL(this.apiRoot() + auth);
} }
Oauth.prototype.requestAccessToken = function(responseBody) { Oauth.prototype.requestAccessToken = function(responseBody) {
// /oauthtoken?code=51d0115b04d1ed94001dde751c5b360f&state=aQfH1VEohYsQr86qqyv // /oauthtoken?code=51d0115b04d1ed94001dde751c5b360f&state=aQfH1VEohYsQr86qqyv
// https://app.example.com/oauth?code=K4m2J2bGI9rcICBqmUCYuQ&state=d173d2bb868a
var urlVars = Paths.getUrlVars(responseBody); var urlVars = APICalls.getUrlVars(responseBody);
if(this.state && this.state != "" && urlVars["state"] == this.state) { if(this.state && this.state != "" && urlVars["state"] == this.state) {
var url = Paths.mkApiRootPath("/apps/") + this.register_data["id"] + "/authorizations"; var url = HostApp.serverUrl("oauth_token");
var requestBody = JSON.stringify({ var requestBody = JSON.stringify({
'code' : urlVars["code"], 'code' : urlVars["code"],
'token_type' : "mac" 'token_type': "https://tent.io/oauth/hawk-token"
}); });
var those = this; var those = this;
var http_method = "POST"; var auth_header = Hmac.makeHawkAuthHeader(
var callback = function(resp) {
those.requestAccessTokenTicketFinished(resp.responseText);
};
var auth_header = Hmac.makeAuthHeader(
url, url,
http_method, "POST",
HostApp.stringForKey("app_mac_key"), HostApp.stringForKey("app_hawk_id"),
HostApp.stringForKey("app_mac_key_id") HostApp.stringForKey("app_hawk_key")
); );
Paths.getURL(url, http_method, callback, requestBody, auth_header); APICalls.post(url, requestBody, {
content_type: "application/json",
auth_header: auth_header,
callback: function(resp) {
those.requestAccessTokenTicketFinished(resp.responseText);
}});
} else { } else {
console.error("State is not the same: {" + this.state + "} vs {" + urlVars["state"] + "}") console.error("State is not the same: {" + this.state + "} vs {" + urlVars["state"] + "}")
@ -163,16 +172,16 @@ function(HostApp, Paths, Hmac) {
var access = JSON.parse(responseBody); var access = JSON.parse(responseBody);
HostApp.setStringForKey(access["access_token"], "user_access_token"); HostApp.setStringForKey(access["access_token"], "user_access_token");
HostApp.setSecret(access["mac_key"]); HostApp.setSecret(access["hawk_key"]);
HostApp.setStringForKey(access["mac_algorithm"], "user_mac_algorithm"); HostApp.setStringForKey(access["hawk_algorithm"], "user_hawk_algorithm");
HostApp.setStringForKey(access["token_type"], "user_token_type"); HostApp.setStringForKey(access["token_type"], "user_token_type");
HostApp.loggedIn(); HostApp.loggedIn();
} }
Oauth.prototype.logout = function() { Oauth.prototype.logout = function() { // FIXME
var url = Paths.mkApiRootPath("/apps/" + HostApp.stringForKey("app_id")); var url = APICalls.mkApiRootPath("/apps/" + HostApp.stringForKey("app_id"));
var http_method = "DELETE"; var http_method = "DELETE";
var auth_header = Hmac.makeAuthHeader( var auth_header = Hmac.makeAuthHeader(
url, url,
@ -181,7 +190,7 @@ function(HostApp, Paths, Hmac) {
HostApp.stringForKey("app_mac_key_id") HostApp.stringForKey("app_mac_key_id")
); );
Paths.getURL(url, http_method, function(resp) { APICalls.http_call(url, http_method, function(resp) {
HostApp.setStringForKey(null, "app_mac_key"); HostApp.setStringForKey(null, "app_mac_key");
HostApp.setStringForKey(null, "app_mac_key_id"); HostApp.setStringForKey(null, "app_mac_key_id");
HostApp.setStringForKey(null, "app_id"); HostApp.setStringForKey(null, "app_id");

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
define([ define([
"helper/HostApp", "helper/HostApp",
"helper/Core", "helper/Core",
"helper/Paths", "helper/APICalls",
"lib/URI" "lib/URI"
], ],
function(HostApp, Core, Paths, URI) { function(HostApp, Core, APICalls, URI) {
function Search() { function Search() {
@ -79,7 +79,7 @@ function(HostApp, Core, Paths, URI) {
var _this = this; var _this = this;
Paths.getURL(url.toString(), "GET", function(resp) { APICalls.http_call(url.toString(), "GET", function(resp) {
var results = JSON.parse(resp.responseText).results; var results = JSON.parse(resp.responseText).results;
if (results && results.length > 0) { if (results && results.length > 0) {

View file

@ -1,16 +1,13 @@
define([ define([
"helper/HostApp", "helper/HostApp",
"helper/Paths", "helper/APICalls",
"helper/Cache"
], ],
function(HostApp, Paths, Cache) { function(HostApp, APICalls) {
function Sidebar() { function Sidebar() {
this.cache = new Cache();
this.body = document.createElement("ul"); this.body = document.createElement("ul");
this.body.class = "sidebar"; this.body.class = "sidebar";
@ -84,57 +81,39 @@ function(HostApp, Paths, Cache) {
var entity = HostApp.stringForKey("entity"); var entity = HostApp.stringForKey("entity");
this.menu.user.title = entity; this.menu.user.title = entity;
var img = this.menu.user.getElementsByTagName("img")[0]; var avatar = this.menu.user.getElementsByTagName("img")[0];
var _this = this; var _this = this;
var profile_callback = function(p) { var url = HostApp.serverUrl("posts_feed") + "?types=" + encodeURIComponent("https://tent.io/types/meta/v0") + "&entities=" + encodeURIComponent(entity);
APICalls.get(url, { callback: function(resp) {
var profiles = JSON.parse(resp.responseText);
var basic = p["https://tent.io/types/info/basic/v0.1.0"]; if(profiles.posts.length < 1) return;
var profile = profiles.posts[0];
bungloo.cache.profiles[entity] = profile;
if (p && basic) { // Find and apply avatar
if(basic.name) { if(profile.attachments) {
_this.menu.user.title = basic.name;
}
if(basic.avatar_url) {
img.onerror = function() { var digest = null;
img.src = "img/sidebar/user.png"; for (var i = 0; i < profile.attachments.length; i++) {
img.src_inactive = img.src; var attachment = profile.attachments[i];
img.src_active = img.src; if(attachment.category == "avatar") {
digest = attachment.digest;
break;
} }
}
img.src = basic.avatar_url; if(digest) {
img.src_inactive = basic.avatar_url; var _this = this;
img.src_active = basic.avatar_url; avatar.onerror = function() { avatar.src = 'img/default-avatar.png' };
var avatar_url = profile.content.servers[0].urls.attachment.replace(/\{entity\}/, encodeURIComponent(profile.entity));
avatar.src = avatar_url.replace(/\{digest\}/, digest);
avatar.src_inactive = avatar.src;
avatar.src_active = avatar.src;
} }
} }
}});
}
var p = this.cache.profiles.getItem(entity);
if (p && p != "null") {
profile_callback(p);
} else {
Paths.findProfileURL(entity, function(profile_url) {
if (profile_url) {
Paths.getURL(profile_url, "GET", function(resp) {
var p = JSON.parse(resp.responseText);
if (p && p != "null") {
_this.cache.profiles.setItem(entity, p);
profile_callback(p);
}
}, null, false); // do not send auth-headers
}
});
}
} }
Sidebar.prototype.removeEntityAvatar = function() { Sidebar.prototype.removeEntityAvatar = function() {

View file

@ -1,11 +1,11 @@
define([ define([
"helper/Core", "helper/Core",
"helper/Paths", "helper/APICalls",
"helper/HostApp", "helper/HostApp",
"lib/URI" "lib/URI"
], ],
function(Core, Paths, HostApp, URI) { function(Core, APICalls, HostApp, URI) {
function Timeline() { function Timeline() {
@ -21,6 +21,8 @@ function(Core, Paths, HostApp, URI) {
this.since_id_entity = null; this.since_id_entity = null;
this.since_time = 0; this.since_time = 0;
this.pages = {};
this.before = {id: null, entity: null, loading: false}; this.before = {id: null, entity: null, loading: false};
this.container = document.createElement("div"); this.container = document.createElement("div");
@ -46,8 +48,19 @@ function(Core, Paths, HostApp, URI) {
} }
Timeline.prototype.newStatus = function(statuses, append) { Timeline.prototype.newStatus = function(_statuses, append) {
for (var entity in _statuses.profiles) {
if (_statuses.profiles[entity] != null) {
bungloo.cache.profiles[entity] = _statuses.profiles[entity];
} else {
bungloo.cache.profiles[entity] = {};
}
}
this.pages = _statuses.pages;
statuses = _statuses.posts;
if(statuses != null && statuses.length > 0) { if(statuses != null && statuses.length > 0) {
this.before.loading = false; this.before.loading = false;
@ -62,7 +75,7 @@ function(Core, Paths, HostApp, URI) {
this.since_id_entity = status.entity; this.since_id_entity = status.entity;
} }
if (status.type == "https://tent.io/types/post/status/v0.1.0" || status.type == "https://tent.io/types/post/photo/v0.1.0") { if (status.type == "https://tent.io/types/status/v0#") {
var new_node = this.getStatusDOMElement(status); var new_node = this.getStatusDOMElement(status);
@ -95,68 +108,69 @@ function(Core, Paths, HostApp, URI) {
} }
} }
Timeline.prototype.getNewData = function(add_to_search, append) { Timeline.prototype.getNewData = function(add_to_search, append, query) {
add_to_search = add_to_search || {}; add_to_search = add_to_search || {};
var those = this; var those = this;
var url = URI(Paths.mkApiRootPath("/posts")); var url = HostApp.serverUrl("posts_feed");
var post_types = [ if(!query) {
"https://tent.io/types/post/repost/v0.1.0",
"https://tent.io/types/post/status/v0.1.0",
"https://tent.io/types/post/delete/v0.1.0",
"https://tent.io/types/post/photo/v0.1.0"
];
url.addSearch("post_types", post_types.join(","));
//url.addSearch("sort_by", "published_at");
url.addSearch("limit", this.posts_limit);
if(this.since_id && !append) { var uri = URI(url);
url.addSearch("since_id", this.since_id);
url.addSearch("since_id_entity", this.since_id_entity);
}
for (key in add_to_search) { var post_types = [
url.addSearch(key, add_to_search[key]); "https://tent.io/types/status/v0#",
} "https://tent.io/types/status/v0#reply",
"https://tent.io/types/repost/v0#",
"https://tent.io/types/delete/v0#",
//"https://tent.io/types/post/photo/v0.1.0"
];
uri.addSearch("types", post_types.join(","));
//uri.addSearch("sort_by", "published_at");
uri.addSearch("limit", this.posts_limit);
uri.addSearch("max_refs", 20);
uri.addSearch("profiles", "entity");
var http_method = "GET"; for (key in add_to_search) {
var callback = function(resp) { uri.addSearch(key, add_to_search[key]);
those.reload_blocked = false;
try {
var json = JSON.parse(resp.responseText);
those.newStatus(json, append);
} catch (e) {
console.error(url + " JSON parse error");
throw e;
} }
}
var data = null; url = uri.toString();
} else {
url += query;
}
if (HostApp.stringForKey("user_access_token")) { if (HostApp.stringForKey("user_access_token")) {
if (!this.reload_blocked) { if (!this.reload_blocked) {
this.reload_blocked = true; this.reload_blocked = true;
Paths.getURL(url.toString(), http_method, callback, data); // FIXME: error callback
APICalls.get(url, { callback: function(resp) {
// FIXME this is getting data when it shouldn't debug(resp.responseText)
those.reload_blocked = false;
try {
var json = JSON.parse(resp.responseText);
those.newStatus(json, append);
} catch (e) {
console.error(url + " JSON parse error");
throw e;
}
} });
} }
} }
} }
Timeline.prototype.getMoreStatusPosts = function() { Timeline.prototype.getMoreStatusPosts = function() {
if (!this.before.loading) { if (!this.before.loading) {
this.before.loading = true; if (this.pages.next) {
var add_search = { this.before.loading = true;
"before_id": this.body.lastChild.status.id, this.getNewData({}, true, this.pages.next);
"before_id_entity": this.body.lastChild.status.entity
} }
this.getNewData(add_search, true);
} }
} }

View file

@ -0,0 +1,319 @@
define([
"jquery",
"helper/HostApp",
"helper/Hmac",
"helper/Cache"
],
function(jQuery, HostApp, Hmac, Cache) {
var APICalls = {};
APICalls.cache = new Cache();
APICalls.getUrlVars = function(url) {
var vars = [], hash;
if(url.indexOf("#") > -1) url = url.slice(0, url.indexOf("#"));
var hashes = url.slice(url.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
APICalls.http_call = function(options) {
if (typeof options === "string") {
console.error(options + " not implemented yet")
return;
}
var content_type = null;
if((options.http_method == "POST" || options.http_method == "PUT") && !options.content_type) {
console.error("No content type for " + options.url);
return;
} else if(options.content_type != "AAA") {
if(options.content_type == "application/json") {
content_type = "application/json";
} else if(options.content_type) {
content_type = "application/vnd.tent.post.v0+json; charset=UTF-8; type=\"" + options.content_type + "\"";
}
} else {
content_type = 'application/vnd.tent.post.v0+json; charset=UTF-8; type="https://tent.io/types/status/v0#"';
}
var settings = {
beforeSend: function(xhr) {
if (options.data) xhr.setRequestHeader("Content-Length", options.data.length);
if (options.accept) xhr.setRequestHeader("Accept", options.accept);
else xhr.setRequestHeader("Accept", "application/vnd.tent.post.v0+json");
var user_access_token = HostApp.stringForKey("user_access_token");
if (!options.auth_header && !options.no_auth && user_access_token) {
var auth_header = Hmac.makeHawkAuthHeader(
options.url,
options.http_method,
user_access_token,
HostApp.secret()//,
//HostApp.stringForKey("app_id")
);
xhr.setRequestHeader("Authorization", auth_header);
} else if(options.auth_header) {
xhr.setRequestHeader("Authorization", options.auth_header);
} else if(!options.no_auth) {
console.error("No user_access_token yet - " + options.url);
}
xhr.setRequestHeader("Cache-Control", "no-proxy");
},
url: options.url,
contentType: content_type,
type: options.http_method,
complete: options.callback,
data: options.data,
processData: false,
error: function(xhr, ajaxOptions, thrownError) {
console.error("HTTP CALL (" + xhr.status + ") " + xhr.statusText + " " + options.http_method + " URL(" + options.url + "): '" + xhr.responseText + "'");
}
};
jQuery.ajax(settings);
}
APICalls.head = function(url, options) {
var settings = {
url: url,
http_method: "HEAD",
};
for (var key in options) {
settings[key] = options[key];
}
APICalls.http_call(settings);
}
APICalls.get = function(url, options) {
var settings = {
url: url,
http_method: "GET",
};
for (var key in options) {
settings[key] = options[key];
}
APICalls.http_call(settings);
}
APICalls.post = function(url, data, options) {
var settings = {
url: url,
http_method: "POST",
data: data
};
for (var key in options) {
settings[key] = options[key];
}
APICalls.http_call(settings);
}
APICalls.delete = function(url, options) {
var settings = {
url: url,
http_method: "DELETE"
};
for (var key in options) {
settings[key] = options[key];
}
APICalls.http_call(settings);
}
APICalls.put = function(url, data, options) {
var settings = {
url: url,
http_method: "PUT",
data: data
};
for (var key in options) {
settings[key] = options[key];
}
APICalls.http_call(settings);
}
APICalls.postMultipart = function(url, callback, data, boundary, accepts) {
accepts = accepts || "application/vnd.tent.v0+json";
jQuery.ajax({
beforeSend: function(xhr) {
xhr.setRequestHeader("Accept", accepts);
if (data) xhr.setRequestHeader("Content-Length", data.length);
var user_access_token = HostApp.stringForKey("user_access_token");
if (user_access_token) {
auth_header = Hmac.makeAuthHeader(
url,
"POST",
HostApp.secret(),
user_access_token
);
xhr.setRequestHeader("Authorization", auth_header);
}
},
url: url,
contentType: "multipart/form-data;boundary=" + boundary,
type: "POST",
complete: callback,
data: data,
processData: false,
error: function(xhr, ajaxOptions, thrownError) {
console.error("postMultipart (" + xhr.status + ")" + xhr.statusText + " (" + url + "): '" + xhr.responseText + "'");
}
});
}
APICalls.findProfileURL = function(entity, callback, errorCallback) {
var profile_url = APICalls.cache.profile_urls.getItem(entity);
if (profile_url && profile_url != "null") {
callback(profile_url);
} else {
jQuery.ajax({
url: entity,
type: "HEAD",
complete: function(resp) {
if(resp) {
var headers = resp.getAllResponseHeaders();
var profile_urls = APICalls.parseHeaderForProfiles(headers);
var profile_url = null;
if(profile_urls.length > 0) {
var profile_url = profile_urls[0];
if (!profile_url.startsWith("http")) {
profile_url = entity + profile_url;
}
}
if (profile_url) {
APICalls.cache.profile_urls.setItem(entity, profile_url);
callback(profile_url);
} else {
APICalls.http_call(entity, "GET", function(resp) {
if (resp.status >= 200 && resp.status < 300) {
var doc = document.implementation.createHTMLDocument("");
doc.documentElement.innerHTML = resp.responseText;
var links = $(doc).find("link[rel='https://tent.io/rels/meta-post']");
if (links.length > 0) {
var href = links.get(0).href;
APICalls.cache.profile_urls.setItem(entity, href);
if (!href.startsWith("http")) {
href = entity + href;
}
callback(href);
} else {
if(errorCallback) errorCallback(entity + " has no profile URL");
}
} else {
if(errorCallback) errorCallback(entity + " has no profile URL");
}
}, null, false, false);
//if(errorCallback) errorCallback(entity + " has no profile URL");
}
}
},
error: function(xhr, ajaxOptions, thrownError) {
console.error("findProfileURL " + xhr.statusText + " (" + entity + "): " + xhr.responseText);
if (errorCallback) errorCallback(xhr.statusText + " - " + xhr.responseText)
}
});
}
}
APICalls.mkApiRootPath = function(path) {
var api_root = HostApp.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;
}
APICalls.parseHeaderForProfiles = function(header_string) {
var regexp = /https:\/\/tent.io\/rels\/meta-post/i;
return APICalls.parseHeaderForLink(header_string, regexp);
}
APICalls.parseHeader = function(header_string) {
var header_strings = header_string.split(/\n/);
var headers = {};
for (var i = 0; i < header_strings.length; i++) {
var hs = header_strings[i].split(/:(.+)?/);
headers[hs[0]] = hs[1];
}
return headers;
}
APICalls.getCount = function(resp) {
var count = 0;
var headers = APICalls.parseHeader(resp.getAllResponseHeaders());
if(headers["Count"]) count = parseInt(headers["Count"], 10);
return count;
}
APICalls.parseHeaderForLink = function(header_string, match) {
var headers = header_string.split(/\n/);
var links = [];
for (var i = 0; i < headers.length; i++) {
var header = headers[i];
if (header.match(/^Link:(.*)/i)) {
links.push(header.replace(/\r/, "").substr(5).trim());
}
}
var items = [];
for (var i = 0; i < links.length; i++) {
items = items.concat(links[i].split(","));
}
var things = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.match(match)) {
var n = item.match(/<([^>]*)>/);
if (n) {
things.push(n[1]);
}
}
}
return things;
}
return APICalls;
});

View file

@ -40,12 +40,12 @@ function(URI, CacheStorage, require) {
} }
} }
var url = URI(require("helper/Paths").mkApiRootPath("/followings")); var url = URI(require("helper/APICalls").mkApiRootPath("/followings"));
if (this.followings_before_id) { if (this.followings_before_id) {
url.addSearch("before_id", this.followings_before_id); url.addSearch("before_id", this.followings_before_id);
} }
require("helper/Paths").getURL(url, "GET", callback); require("helper/APICalls").getURL(url, "GET", callback);
} }
Cache.prototype.periodicallyGetFollowings = function() { Cache.prototype.periodicallyGetFollowings = function() {

View file

@ -1,17 +1,15 @@
define([ define([
"jquery", "jquery",
"helper/Paths", "helper/APICalls",
"lib/URI", "lib/URI",
"helper/HostApp", "helper/HostApp",
"helper/Cache",
"lib/Timeago", "lib/Timeago",
"lib/SingleDoubleClick" "lib/SingleDoubleClick"
], ],
function(jQuery, Paths, URI, HostApp, Cache) { function(jQuery, APICalls, URI, HostApp) {
function Core() { function Core() {
this.cache = new Cache();
this.saveScrollTop = 0; this.saveScrollTop = 0;
} }
@ -69,6 +67,7 @@ function(jQuery, Paths, URI, HostApp, Cache) {
image.className = "image"; image.className = "image";
image.src = "img/default-avatar.png"; image.src = "img/default-avatar.png";
image.onmousedown = function(e) { e.preventDefault(); }; image.onmousedown = function(e) { e.preventDefault(); };
image.onerror = function() { this.src = 'img/default-avatar.png' };
item.appendChild(image); item.appendChild(image);
var image_username = a.cloneNode(); var image_username = a.cloneNode();
@ -162,7 +161,28 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} }
Core.prototype.getStatusDOMElement = function(status) { Core.prototype.getStatusDOMElement = function(status) {
/*
{
"app": {
"id": "P8FJjaiRv0AKXfjUMd_4YQ",
"name": "Bungloo on Linux",
"url": "http:\/\/jabs.nu\/bungloo\/"
},
"content": {
"text": "jeena test"
},
"entity": "http:\/\/155969d81672.alpha.attic.is",
"id": "HlSXe8MREzU4h2fGLGSnCA",
"published_at": 1369566009,
"received_at": 1369566008799,
"type": "https:\/\/tent.io\/types\/status\/v0#",
"version": {
"id": "a2f702b4615c7d7dd0f98c73d7b55749880bf6e437a77349454ff10745d134c6",
"published_at": 1369566009,
"received_at": 1369566008799
}
}
*/
var _this = this; var _this = this;
var template = this.getTemplate(); var template = this.getTemplate();
@ -191,7 +211,8 @@ function(jQuery, Paths, URI, HostApp, Cache) {
template.reply_to.onclick = function() { template.reply_to.onclick = function() {
var mentions = []; var mentions = [];
var status_mentions = status.mentions.slice(0); var status_mentions = [];
if(status.mentions) status_mentions = status.mentions.slice(0);
if (typeof status.__repost != "undefined") { if (typeof status.__repost != "undefined") {
status_mentions.push({entity:status.__repost.entity}); status_mentions.push({entity:status.__repost.entity});
@ -202,7 +223,7 @@ function(jQuery, Paths, URI, HostApp, Cache) {
mentions.push(mention); mentions.push(mention);
} }
_this.replyTo(status.entity, status.id, mentions, (status && status.permissions && !status.permissions.public)); _this.replyTo(status);
return false; return false;
} }
@ -212,7 +233,8 @@ function(jQuery, Paths, URI, HostApp, Cache) {
return false; return false;
} }
template.username.innerText = status.entity; if(bungloo.cache.profiles[status.entity].name) template.username.innerText = bungloo.cache.profiles[status.entity].name;
else template.username.innerText = status.entity;
template.username.href = status.entity; template.username.href = status.entity;
template.username.title = status.entity; template.username.title = status.entity;
template.username.onclick = function() { template.username.onclick = function() {
@ -220,47 +242,12 @@ function(jQuery, Paths, URI, HostApp, Cache) {
return false; return false;
} }
if(bungloo.cache.profiles[status.entity].avatar_digest) {
template.image.src = HostApp.serverUrl("attachment").replace(/\{entity\}/, encodeURIComponent(status.entity)).replace(/\{digest\}/, bungloo.cache.profiles[status.entity].avatar_digest);
}
template.image.onclick = template.username.onclick; template.image.onclick = template.username.onclick;
var profile_callback = function(p) {
var basic = p["https://tent.io/types/info/basic/v0.1.0"];
if (p && basic) {
if(basic.name) {
template.username.title = template.username.innerText;
template.username.innerText = basic.name;
}
if(basic.avatar_url) {
template.image.onerror = function() { template.image.src = 'img/default-avatar.png' };
template.image.src = basic.avatar_url;
}
}
}
var p = this.cache.profiles.getItem(status.entity);
if (p && p != "null") {
profile_callback(p);
} else {
Paths.findProfileURL(status.entity, function(profile_url) {
if (profile_url) {
Paths.getURL(profile_url, "GET", function(resp) {
var p = JSON.parse(resp.responseText);
if (p && p != "null") {
_this.cache.profiles.setItem(status.entity, p);
profile_callback(p);
}
}, null, false); // do not send auth-headers
}
});
}
if (status && status.permissions && !status.permissions.public) { if (status && status.permissions && !status.permissions.public) {
template.is_private.style.display = ''; template.is_private.style.display = '';
@ -290,6 +277,7 @@ function(jQuery, Paths, URI, HostApp, Cache) {
template.message.innerHTML = this.replaceURLWithHTMLLinks(text, entities, template.message); template.message.innerHTML = this.replaceURLWithHTMLLinks(text, entities, template.message);
this.afterChangingTextinMessageHTML(template.message) this.afterChangingTextinMessageHTML(template.message)
/*
if (status.type == "https://tent.io/types/post/photo/v0.1.0") { if (status.type == "https://tent.io/types/post/photo/v0.1.0") {
for (var i = 0; i < status.attachments.length; i++) { for (var i = 0; i < status.attachments.length; i++) {
@ -308,18 +296,19 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} }
if (status.entity == HostApp.stringForKey("entity")) { if (status.entity == HostApp.stringForKey("entity")) {
var url = Paths.mkApiRootPath("/posts/" + status.id + "/attachments/" + attachment.name); var url = APICalls.mkApiRootPath("/posts/" + status.id + "/attachments/" + attachment.name);
Paths.getURL(url, "GET", callback, null, null, attachment.type); APICalls.http_call(url, "GET", callback, null, null, attachment.type);
} else { } else {
var url = Paths.mkApiRootPath("/posts/" + encodeURIComponent(status.entity) + "/" + status.id + "/attachments/" + attachment.name); var url = APICalls.mkApiRootPath("/posts/" + encodeURIComponent(status.entity) + "/" + status.id + "/attachments/" + attachment.name);
Paths.getURL(url, "GET", callback, null, null, attachment.type); APICalls.http_call(url, "GET", callback, null, null, attachment.type);
} }
})(); })();
} }
} }
*/
this.findMentions(template.message, status.mentions); this.findMentions(template.message, status.mentions);
/*
for (var i = 0; i < status.mentions.length; i++) { for (var i = 0; i < status.mentions.length; i++) {
var mention = status.mentions[i]; var mention = status.mentions[i];
if (mention.entity == HostApp.stringForKey("entity")) { if (mention.entity == HostApp.stringForKey("entity")) {
@ -327,10 +316,10 @@ function(jQuery, Paths, URI, HostApp, Cache) {
break; break;
} }
} }
*/
var published_at = typeof status.__repost == "undefined" ? status.published_at : status.__repost.published_at; var published_at = typeof status.__repost == "undefined" ? status.version.published_at : status.__repost.published_at;
var time = document.createElement("abbr"); var time = document.createElement("abbr");
time.innerText = this.ISODateString(new Date(published_at * 1000)); time.innerText = this.ISODateString(new Date(published_at));
time.title = time.innerText; time.title = time.innerText;
time.className = "timeago"; time.className = "timeago";
jQuery(time).timeago(); jQuery(time).timeago();
@ -366,9 +355,11 @@ function(jQuery, Paths, URI, HostApp, Cache) {
template.source.innerHTML = status.__repost.app.name; template.source.innerHTML = status.__repost.app.name;
template.source.title = status.__repost.app.url; template.source.title = status.__repost.app.url;
} else { } else {
template.source.href = status.app.url; if(status.app) {
template.source.innerHTML = status.app.name; template.source.href = status.app.url;
template.source.title = status.app.url; template.source.innerHTML = status.app.name;
template.source.title = status.app.url;
}
} }
return template.item; return template.item;
@ -431,9 +422,9 @@ function(jQuery, Paths, URI, HostApp, Cache) {
}); });
var _this = this; var _this = this;
Paths.findProfileURL(repost.entity, function(profile_url) { APICalls.findProfileURL(repost.entity, function(profile_url) {
if (profile_url) { if (profile_url) {
Paths.getURL(profile_url, "GET", function(resp) { APICalls.http_call(profile_url, "GET", function(resp) {
if (resp.status >= 200 && resp.status < 400) { if (resp.status >= 200 && resp.status < 400) {
var _p = JSON.parse(resp.responseText); var _p = JSON.parse(resp.responseText);
_this.cache.profiles.setItem(repost.entity, _p); _this.cache.profiles.setItem(repost.entity, _p);
@ -460,14 +451,14 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} }
} }
Paths.findProfileURL(repost.content.entity, function(profile_url) { APICalls.findProfileURL(repost.content.entity, function(profile_url) {
if (profile_url) { if (profile_url) {
Paths.getURL(profile_url, "GET", function(resp) { APICalls.http_call(profile_url, "GET", function(resp) {
var profile = JSON.parse(resp.responseText); var profile = JSON.parse(resp.responseText);
var server = profile["https://tent.io/types/info/core/v0.1.0"].servers[0]; var server = profile["https://tent.io/types/info/core/v0.1.0"].servers[0];
Paths.getURL(URI(server + "/posts/" + repost.content.id).toString(), "GET", callback, null, false); APICalls.http_call(URI(server + "/posts/" + repost.content.id).toString(), "GET", callback, null, false);
}, null, false); // do not send auth-headers }, null, false); // do not send auth-headers
} }
@ -475,55 +466,8 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} }
} }
Core.prototype.sendNewMessage = function(content, in_reply_to_status_id, in_reply_to_entity, location, image_data_uri, is_private, callback) {
if (image_data_uri) {
this.sendNewMessageWithImage(content, in_reply_to_status_id, in_reply_to_entity, location, image_data_uri, is_private, callback);
} else {
var url = URI(Paths.mkApiRootPath("/posts"));
var http_method = "POST";
var data = {
"type": "https://tent.io/types/post/status/v0.1.0",
"published_at": parseInt(new Date().getTime() / 1000, 10),
"permissions": {
"public": !is_private
},
"content": {
"text": content,
},
};
if (location) {
data["content"]["location"] = { "type": "Point", "coordinates": location }
}
var mentions = this.parseMentions(content, in_reply_to_status_id, in_reply_to_entity);
if (mentions.length > 0) {
data["mentions"] = mentions;
if (is_private) {
var entities = {};
for (var i = 0; i < mentions.length; i++) {
var entity = mentions[i]["entity"]
entities[entity] = true;
};
data["permissions"]["entities"] = entities;
}
}
Paths.getURL(url.toString(), http_method, callback, JSON.stringify(data));
}
}
Core.prototype.repost = function(id, entity, callback) { Core.prototype.repost = function(id, entity, callback) {
var url = URI(Paths.mkApiRootPath("/posts")); var url = URI(APICalls.mkApiRootPath("/posts"));
var data = { var data = {
"type": "https://tent.io/types/post/repost/v0.1.0", "type": "https://tent.io/types/post/repost/v0.1.0",
@ -549,88 +493,14 @@ function(jQuery, Paths, URI, HostApp, Cache) {
_this.highlight(id); _this.highlight(id);
} }
Paths.getURL(url.toString(), "POST", new_callback, JSON.stringify(data)); APICalls.http_call(url.toString(), "POST", new_callback, JSON.stringify(data));
}
Core.prototype.sendNewMessageWithImage = function(content, in_reply_to_status_id, in_reply_to_entity, location, image_data_uri, is_private, callback) {
var url = URI(Paths.mkApiRootPath("/posts"));
var data = {
"type": "https://tent.io/types/post/photo/v0.1.0",
"published_at": parseInt(new Date().getTime() / 1000, 10),
"permissions": {
"public": !is_private
},
"content": {
"caption": content,
},
};
if (location) {
data["content"]["location"] = { "type": "Point", "coordinates": location }
}
var mentions = this.parseMentions(content, in_reply_to_status_id, in_reply_to_entity);
if (mentions.length > 0) {
data["mentions"] = mentions;
if (is_private) {
var entities = {};
for (var i = 0; i < mentions.length; i++) {
var entity = mentions[i]["entity"]
entities[entity] = true;
};
data["permissions"]["entities"] = entities;
}
}
var data_string = JSON.stringify(data);
var boundary = "TentAttachment----------TentAttachment";
var post = "--" + boundary + "\r\n";
post += 'Content-Disposition: form-data; name="post"; filename="post.json"\r\n';
post += 'Content-Length: ' + data_string.length + '\r\n';
post += 'Content-Type: application/vnd.tent.v0+json\r\n';
post += 'Content-Transfer-Encoding: binary\r\n\r\n';
post += data_string;
post += "\r\n--" + boundary + "\r\n";
var blob_string = image_data_uri.split(',')[1];
var mime_type = image_data_uri.split(',')[0].split(':')[1].split(';')[0];
var ext = "png";
if (mime_type == "image/jpeg") {
ext = "jpeg";
} else if (mime_type == "image/gif") {
ext = "gif";
}
post += 'Content-Disposition: form-data; name="photos[0]"; filename="photo.' + ext + '"\r\n';
post += 'Content-Length: ' + blob_string.length + "\r\n";
post += 'Content-Type: ' + mime_type + "\r\n";
post += 'Content-Transfer-Encoding: base64\r\n\r\n';
post += blob_string;
post += "\r\n--" + boundary + "--\r\n";
var newCallback = function(resp) {
if (resp.status == 403) {
var err = JSON.parse(resp.responseText);
HostApp.alertTitleWithMessage(resp.statusText, err.error);
}
callback(resp);
}
Paths.postMultipart(url.toString(), newCallback, post, boundary);
} }
Core.prototype.remove = function(id, callback, type) { Core.prototype.remove = function(id, callback, type) {
type = type || "post"; type = type || "post";
if (confirm("Really delete this " + type + "?")) { if (confirm("Really delete this " + type + "?")) {
var url = URI(Paths.mkApiRootPath("/posts/" + id)); var url = URI(APICalls.mkApiRootPath("/posts/" + id));
Paths.getURL(url.toString(), "DELETE", callback); APICalls.http_call(url.toString(), "DELETE", callback);
} }
} }
@ -738,9 +608,9 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} else { } else {
Paths.findProfileURL(mention.entity, function(profile_url) { APICalls.findProfileURL(mention.entity, function(profile_url) {
if (profile_url) { if (profile_url) {
Paths.getURL(profile_url, "GET", function(resp) { APICalls.http_call(profile_url, "GET", function(resp) {
if (resp.status >= 200 && resp.status < 400) { if (resp.status >= 200 && resp.status < 400) {
var p = JSON.parse(resp.responseText); var p = JSON.parse(resp.responseText);
_this.cache.profiles.setItem(mention.entity, p); _this.cache.profiles.setItem(mention.entity, p);
@ -796,27 +666,14 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} }
Core.prototype.replaceURLWithHTMLLinks = function(text, entities, message_node) { Core.prototype.replaceURLWithHTMLLinks = function(text, entities, message_node) {
// FIXME: this has to be done better so one can nest that stuff and escape with \
var callback = function(url) { return text.replace(/_([^_]+)_/g, "<em>$1</em>")
.replace(/\*([^\*]+)\*/g, "<strong>$1</strong>")
var result; .replace(/`([^`]+)`/g, "<code>$1</code>")
.replace(/~([^~]+)~/g, "<del>$1</del>")
if (entities && entities.some(function(x) { return x == url })) { .replace(/\#([^\s]+)/g, "<a class='hash' href=\"javascript:bungloo.search.searchFor('$1')\">#$1</a>")
result = url; .replace(/(^|[^\^])\[([^\]]+)\]\(([^\)]+)\)/g, "<a class='link' href='javascript:controller.openURL(\"$3\");'>$2</a>")
} else { .replace(/\^\[([^\]]+)\]\((\d+)\)/g, "<a class='name' href='#' onclick='bungloo.entityProfile.showEntity(this, $2); return false;'>$1</a>");
result = url;
if (url.startsWith("http://") || url.startsWith("https://")) {
result = '<a href="' + url + '">' + url + '</a>';
}
}
return result;
}
var hash = /(^|\s)(#)(\w+)/ig;
return URI.withinString(text, callback).replace(hash, "$1<a class='hash' href='https://skate.io/search?q=%23$3'>$2$3</a>");
} }
Core.prototype.parseForMedia = function(text, images) { Core.prototype.parseForMedia = function(text, images) {
@ -838,7 +695,7 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} else if(word.startsWith("http://youtube.com/") || word.startsWith("http://www.youtube.com/") || word.startsWith("https://youtube.com/") || word.startsWith("https://www.youtube.com/")) { } else if(word.startsWith("http://youtube.com/") || word.startsWith("http://www.youtube.com/") || word.startsWith("https://youtube.com/") || word.startsWith("https://www.youtube.com/")) {
var v = Paths.getUrlVars(word)["v"]; var v = APICalls.getUrlVars(word)["v"];
this.addYouTube(v, images); this.addYouTube(v, images);
} else if (word.startsWith("http://youtu.be/") || word.startsWith("https://youtu.be/")) { } else if (word.startsWith("http://youtu.be/") || word.startsWith("https://youtu.be/")) {
@ -890,19 +747,8 @@ function(jQuery, Paths, URI, HostApp, Cache) {
} }
} }
Core.prototype.replyTo = function(entity, status_id, mentions, is_private) { Core.prototype.replyTo = function(status) {
HostApp.openNewMessageWidow(status);
var string = "^" + entity.replace("https://", "") + " ";
var ms = "";
for (var i = 0; i < mentions.length; i++) {
var e = mentions[i].entity.replace("https://", "");
if(string.indexOf(e) == -1) ms += " ^" + e;
}
if(ms.length > 0) string += "\n\n/cc" + ms;
HostApp.openNewMessageWidow(entity, status_id, string, is_private);
} }
Core.prototype.postDeleted = function(post_id, entity) { Core.prototype.postDeleted = function(post_id, entity) {
@ -1001,21 +847,22 @@ function(jQuery, Paths, URI, HostApp, Cache) {
Core.prototype.afterChangingTextinMessageHTML = function(message_node) { Core.prototype.afterChangingTextinMessageHTML = function(message_node) {
// adding show search on click hash // adding show search on click hash
/*
$(message_node).find("a.hash").click(function(e) { $(message_node).find("a.hash").click(function(e) {
if(bungloo.search) bungloo.search.searchFor(e.target.innerHTML); if(bungloo.search) bungloo.search.searchFor(e.target.innerHTML);
return false; return false;
}); });
*/
// adding show profile on click // adding show profile on click
/*
$(message_node).find("a.name").click(function(e) { $(message_node).find("a.name").click(function(e) {
HostApp.showProfileForEntity(e.target.title); HostApp.showProfileForEntity(e.target.title);
return false; return false;
}); });*/
} }
return Core; return Core;
}); });

View file

@ -7,7 +7,7 @@ function(URI, CryptoJS) {
var Hmac = {}; var Hmac = {};
Hmac.makeAuthHeader = function(url, http_method, mac_key, mac_key_id) { Hmac.makeHawkAuthHeader = function(url, http_method, hawk_id, key, app_id) {
url = URI(url); url = URI(url);
var nonce = Hmac.makeid(8); var nonce = Hmac.makeid(8);
@ -18,26 +18,47 @@ function(URI, CryptoJS) {
port = url.protocol() == "https" ? "443" : "80"; port = url.protocol() == "https" ? "443" : "80";
} }
var normalizedRequestString = "" var normalizedRequestString = "hawk.1.header\n" // header
+ time_stamp + '\n' + time_stamp + '\n' // ts
+ nonce + '\n' + nonce + '\n' // nonce
+ http_method + '\n' + http_method.toUpperCase() + '\n' // method
+ url.path() + url.search() + url.hash() + '\n' + url.path() + url.search() + url.hash() + '\n' // request uri
+ url.hostname() + '\n' + url.hostname().toLowerCase() + '\n' // host
+ port + '\n' + port + '\n' // port
+ '\n' ; + '\n' // Hmac.calculatePayloadHash(payload) + '\n' // hash // FIXME implement payload validation
+ '\n' // ext (we don't use it)
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, mac_key); var app = "";
if(app_id) {
app = ', app="' + app_id + "'";
normalizedRequestString += app_id + "\n" + // app
'\n'; // dlg should be empty
}
var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
hmac.update(normalizedRequestString); hmac.update(normalizedRequestString);
var hash = hmac.finalize(); var hash = hmac.finalize();
var mac = hash.toString(CryptoJS.enc.Base64); var mac = hash.toString(CryptoJS.enc.Base64);
return 'MAC id="' + mac_key_id + return 'Hawk id="' + hawk_id +
'", mac="' + mac +
'", ts="' + time_stamp + '", ts="' + time_stamp +
'", nonce="' + nonce + '", nonce="' + nonce + '"' +
'", mac="' + mac + '"'; app
} }
Hmac.calculatePayloadHash = function (payload) {
if (!payload) return "";
var hash = CryptoJS.algo.SHA256.create();
hash.update('hawk.1.payload\n');
hash.update('application/vnd.tent.post.v0+json\n');
hash.update(payload || '');
hash.update('\n');
return hash.finalize().toString(CryptoJS.enc.Base64);
},
Hmac.makeid = function(len) { Hmac.makeid = function(len) {
var text = ""; var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

View file

@ -37,6 +37,14 @@ define(function() {
} }
} }
HostApp.setServerUrls = function(server_urls) {
HostApp.setStringForKey(JSON.stringify(server_urls), "server_urls");
}
HostApp.serverUrl = function(key) {
return JSON.parse(HostApp.stringForKey("server_urls"))[key];
}
HostApp.openURL = function(url) { HostApp.openURL = function(url) {
if (OS_TYPE == "mac") { if (OS_TYPE == "mac") {
@ -77,13 +85,12 @@ define(function() {
} }
} }
HostApp.openNewMessageWidow = function(entity, status_id, string, is_private) { HostApp.openNewMessageWidow = function(status) {
if (OS_TYPE == "mac") { if (OS_TYPE == "mac") {
controller.openNewMessageWindowInReplyTo_statusId_withString_isPrivate_(entity, status_id, string, is_private); controller.openNewMessageWindowInReplyToStatus_(JSON.stringify(status));
} else { } else {
is_private = is_private == true controller.openNewMessageWindowInReplyToStatus(JSON.stringify(status).escapeSpecialChars());
controller.openNewMessageWindowInReplyTostatusIdwithStringIsPrivate(entity, status_id, string, is_private);
} }
} }

View file

@ -25,7 +25,7 @@ function(jQuery, HostApp, Hmac, Cache) {
Paths.getURL = function(url, http_method, callback, data, auth_header, accepts) { Paths.getURL = function(url, http_method, callback, data, auth_header, accepts) {
if(accepts !== false) accepts = accepts || "application/vnd.tent.v0+json; charset=utf-8"; if(accepts !== false) accepts = accepts || "application/vnd.tent.post.v0+json";
var options = { var options = {
@ -56,7 +56,7 @@ function(jQuery, HostApp, Hmac, Cache) {
} }
}, },
url: url, url: url,
contentType: "application/vnd.tent.v0+json", contentType: 'application/vnd.tent.post.v0+json; type="https://tent.io/types/app/v0#"',
type: http_method, type: http_method,
complete: callback, complete: callback,
data: data, data: data,
@ -65,7 +65,7 @@ function(jQuery, HostApp, Hmac, Cache) {
console.error("getURL (" + xhr.status + ")" + xhr.statusText + " " + http_method + " (" + url + "): '" + xhr.responseText + "'"); console.error("getURL (" + xhr.status + ")" + xhr.statusText + " " + http_method + " (" + url + "): '" + xhr.responseText + "'");
} }
} }
jQuery.ajax(options); jQuery.ajax(options);
} }
@ -127,7 +127,7 @@ function(jQuery, HostApp, Hmac, Cache) {
if(profile_urls.length > 0) { if(profile_urls.length > 0) {
var profile_url = profile_urls[0]; var profile_url = profile_urls[0];
if (!profile_url.startsWith("http")) { if (!profile_url.startsWith("http")) {
profile_url = entity + "/profile"; profile_url = entity + profile_url;
} }
} }
@ -140,13 +140,13 @@ function(jQuery, HostApp, Hmac, Cache) {
if (resp.status >= 200 && resp.status < 300) { if (resp.status >= 200 && resp.status < 300) {
var doc = document.implementation.createHTMLDocument(""); var doc = document.implementation.createHTMLDocument("");
doc.documentElement.innerHTML = resp.responseText; doc.documentElement.innerHTML = resp.responseText;
var links = $(doc).find("link[rel='https://tent.io/rels/profile']"); var links = $(doc).find("link[rel='https://tent.io/rels/meta-post']");
if (links.length > 0) { if (links.length > 0) {
var href = links.get(0).href; var href = links.get(0).href;
Paths.cache.profile_urls.setItem(entity, href); Paths.cache.profile_urls.setItem(entity, href);
if (!href.startsWith("http")) { if (!href.startsWith("http")) {
href = entity + "/profile"; href = entity + href;
} }
callback(href); callback(href);
@ -184,6 +184,11 @@ function(jQuery, HostApp, Hmac, Cache) {
} }
Paths.parseHeaderForProfiles = function(header_string) { Paths.parseHeaderForProfiles = function(header_string) {
var regexp = /https:\/\/tent.io\/rels\/meta-post/i;
return Paths.parseHeaderForLink(header_string, regexp);
}
Paths.parseHeaderForLink = function(header_string, match) {
var headers = header_string.split(/\n/); var headers = header_string.split(/\n/);
var links = []; var links = [];
for (var i = 0; i < headers.length; i++) { for (var i = 0; i < headers.length; i++) {
@ -197,18 +202,18 @@ function(jQuery, HostApp, Hmac, Cache) {
for (var i = 0; i < links.length; i++) { for (var i = 0; i < links.length; i++) {
items = items.concat(links[i].split(",")); items = items.concat(links[i].split(","));
} }
var profiles = []; var things = [];
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
var item = items[i]; var item = items[i];
if (item.match(/https:\/\/tent.io\/rels\/profile/i)) { if (item.match(match)) {
var n = item.match(/<([^>]*)>/); var n = item.match(/<([^>]*)>/);
if (n) { if (n) {
profiles.push(n[1]); things.push(n[1]);
} }
} }
} }
return profiles; return things;
} }
return Paths; return Paths;

View file

@ -7,7 +7,8 @@ var bungloo = {
entityProfile: null, entityProfile: null,
conversation: null, conversation: null,
search: null, search: null,
cache: {} cache: { profiles: {}},
newpost: null
}; };
requirejs.config({ requirejs.config({
@ -33,6 +34,15 @@ function start(view, callback) {
}); });
} else if (view == "newpost") {
require(["controller/NewPost"], function(NewPost) {
bungloo.newpost = new NewPost();
if(callback) callback();
});
} else { } else {
@ -54,33 +64,12 @@ function start(view, callback) {
bungloo.search = new Search(); bungloo.search = new Search();
bungloo.sidebar.showContentForTimeline(); bungloo.sidebar.showContentForTimeline();
}); });
} }
} }
String.prototype.startsWith = function(prefix) {
return this.indexOf(prefix) === 0;
}
String.prototype.endsWith = function(suffix) {
return this.match(suffix+"$") == suffix;
};
var __entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;"
};
String.prototype.escapeHTML = function() {
return String(this).replace(/[&<>]/g, function (s) {
return __entityMap[s];
});
}
var console = { var console = {
log: function(s) { log: function(s) {
if (OS_TYPE == "mac") { if (OS_TYPE == "mac") {
@ -172,4 +161,42 @@ function go() { // wait untill everything is loaded
}, 500); }, 500);
} }
go(); go();
// String stuff
String.prototype.startsWith = function(prefix) {
return this.indexOf(prefix) === 0;
}
String.prototype.endsWith = function(suffix) {
return this.match(suffix+"$") == suffix;
};
var __entityMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;"
};
String.prototype.escapeHTML = function() {
return String(this).replace(/[&<>]/g, function (s) {
return __entityMap[s];
});
}
String.prototype.hasArabicCharacter = function() {
var arregex = /[\u0600-\u06FF]/;
return arregex.test(this);
}
String.prototype.escapeSpecialChars = function() {
return this.replace(/[\\]/g, '\\\\')
.replace(/[\"]/g, '\\\"')
.replace(/[\/]/g, '\\/')
.replace(/[\b]/g, '\\b')
.replace(/[\f]/g, '\\f')
.replace(/[\n]/g, '\\n')
.replace(/[\r]/g, '\\r')
.replace(/[\t]/g, '\\t');
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 516 B