From 36e45d04abf6012eee0b07a69120cd87daf4e4ac Mon Sep 17 00:00:00 2001 From: jeena Date: Mon, 8 Jul 2013 17:03:53 +0200 Subject: [PATCH] more textarea fixes --- Qt/Bungloo.py | 23 ++-- Qt/Windows.py | 100 +++++--------- WebKit/css/default.css | 16 ++- WebKit/scripts/controller/NewPost.js | 197 +++++++++++++++++++++++++++ WebKit/scripts/controller/Oauth.js | 33 +++-- WebKit/scripts/controller/Profile.js | 27 ++-- WebKit/scripts/helper/Core.js | 13 +- WebKit/scripts/main.js | 25 +++- 8 files changed, 317 insertions(+), 117 deletions(-) create mode 100644 WebKit/scripts/controller/NewPost.js diff --git a/Qt/Bungloo.py b/Qt/Bungloo.py index 6a065e7..e5eaf3e 100755 --- a/Qt/Bungloo.py +++ b/Qt/Bungloo.py @@ -107,15 +107,12 @@ class Controller(QtCore.QObject): QtCore.QObject.__init__(self) self.app = app - oldpath = os.path.expanduser('~/.bungloo/') - if os.path.isdir(oldpath): - shutil.copytree(oldpath, os.path.expanduser('~/.config/bungloo/')) - shutil.rmtree(os.path.expanduser('~/.bungloo/')) + name = "bungloo2" - if not os.path.exists(os.path.expanduser("~/.config/bungloo/")): - os.makedirs(os.path.expanduser("~/.config/bungloo/")) + if not os.path.exists(os.path.expanduser("~/.config/" + name + "/")): + 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): with open(self.config_path, 'r') as f: @@ -180,15 +177,12 @@ class Controller(QtCore.QObject): @QtCore.pyqtSlot(str, str, str, bool) def openNewMessageWindowInReplyTostatusIdwithStringIsPrivate(self, entity, status_id, string, is_private): - new_message_window = Windows.NewPost(self.app) - new_message_window.inReplyToStatusIdWithString(entity, status_id, string) - new_message_window.setIsPrivate(is_private) + new_message_window = Windows.NewPost(self.app, string, "[]", is_private) new_message_window.show() new_message_window.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.app.new_message_windows.append(new_message_window) new_message_window.activateWindow() new_message_window.setFocus() - new_message_window.textInput.setFocus() new_message_window.show() new_message_window.raise_() @@ -268,6 +262,11 @@ class Controller(QtCore.QObject): msgBox.setInformativeText(message) msgBox.exec_() + @QtCore.pyqtSlot(result=str) + def getCachedEntities(self): + entities = self.app.timeline.evaluateJavaScript("JSON.stringify(bungloo.cache.entities);") + return entities.toString() + def logout(self, sender): print "logout is not implemented yet" @@ -297,7 +296,7 @@ class Console(QtCore.QObject): if __name__ == "__main__": - key = 'BUNGLOO' + key = 'BUNGLOO2' if len(sys.argv) > 1 and sys.argv[1] == "--help": print """ diff --git a/Qt/Windows.py b/Qt/Windows.py index b47d8ae..7317f56 100644 --- a/Qt/Windows.py +++ b/Qt/Windows.py @@ -242,7 +242,7 @@ class Oauth: new_manager.sslErrors.connect(lambda reply, errors: self.handleSslErrors(reply, errors)) self.auth_view.page().setNetworkAccessManager(new_manager) self.auth_view.show() - + print url self.auth_view.load_url(url) return False @@ -329,25 +329,29 @@ class FindEntity(QtGui.QDialog): class NewPost(Helper.RestorableWindow): - def __init__(self, app): + def __init__(self, app, string, mentions, is_private): self.app = app + self.string = string + self.mentions = mentions + self.is_private = is_private + Helper.RestorableWindow.__init__(self, "newpost", self.app) + self.activateWindow() + self.raise_() self.setWindowIcon(QtGui.QIcon(self.app.resources_path() + "/images/Icon.png")) - self.textInput = QtGui.QPlainTextEdit(self) - self.setCentralWidget(self.textInput) - self.textInput.textChanged.connect(self.onChanged) + self.webView = Helper.WebViewCreator(self.app, True, self) + self.webView.load_local(self.load_finished) + self.setCentralWidget(self.webView) + + self.initUI() + + self.webView.triggerPageAction(QtWebKit.QWebPage.InspectElement) self.setWindowTitle("New Post") self.resize(300, 150) self.setMinimumSize(100, 100) - self.initUI() - - self.setIsPrivate(False) - self.status_id = None - self.reply_to_entity = None - self.imageFilePath = None def initUI(self): newPostAction = QtGui.QAction("&New Post", self) @@ -396,69 +400,29 @@ class NewPost(Helper.RestorableWindow): aboutAction.setStatusTip("Open about page in Webbrowser") 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.addAction(aboutAction) + helpMenu.addAction(developerExtrasAction) - self.statusBar().showMessage('256') + def load_finished(self, widget): + is_private = "false" + if self.is_private: + is_private = "true" - self.addButton = QtGui.QToolButton() - self.addButton.setToolTip("Add photo") - 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) + callback = "function() { bungloo.newpost.setString('%s'); bungloo.newpost.setIsPrivate(%s); bungloo.newpost.setMentions(%s);}" % (self.string, is_private, self.mentions) - 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) + script = "function HostAppGo() { start('newpost', " + callback + "); }" + self.webView.page().mainFrame().evaluateJavaScript(script) + self.webView.setFocus() def toggleIsPrivate(self): - self.setIsPrivate(not self.isPrivate) - - 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)) + script = "bungloo.newpost.toggleIsPrivate();" + self.webView.page().mainFrame().evaluateJavaScript(script) def sendMessage(self): count = len(self.textInput.toPlainText()) @@ -482,3 +446,7 @@ class NewPost(Helper.RestorableWindow): else: self.imageFilePath = None + def developer_extras(self, widget): + QtWebKit.QWebSettings.globalSettings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True) + + diff --git a/WebKit/css/default.css b/WebKit/css/default.css index ff8bbfd..63bd2e8 100644 --- a/WebKit/css/default.css +++ b/WebKit/css/default.css @@ -446,4 +446,18 @@ form.search input { p.noresult { padding : 10px; text-align : center; -} \ No newline at end of file +} + +.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 { position: absolute; left: 0; bottom: 0; } +#suggestions .active { color: red; } +#status_bar { height: 1em; } +#status_bar p { float: right; } \ No newline at end of file diff --git a/WebKit/scripts/controller/NewPost.js b/WebKit/scripts/controller/NewPost.js new file mode 100644 index 0000000..113c58c --- /dev/null +++ b/WebKit/scripts/controller/NewPost.js @@ -0,0 +1,197 @@ +define([ +], + +function() { + + function NewPost() { + + this.entities = JSON.parse(controller.getCachedEntities()); + this.mentions = []; + this.is_private = false; + document.body.className = "new_post"; + + // Textarea + + this.container = $("
"); + 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 = $(""); + + $(document.body).append(this.suggestions); + + // Status bar + this.counter = $("256"); + var buttons = $( + "

" + + "" + + "" + + "" + + "

"); + + 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.togglePrivate.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() + } + + NewPost.prototype.setString = function(string) { + this.textarea.val(string); + } + + NewPost.prototype.setMentions = function(mentions) { + + if(mentions && mentions.length > 0) { + var mentions_string = " "; + for (var i = 0; i < mentions.length; i++) { + mentions_string += mentions[i].name + " "; + } + + this.textarea.val(this.textarea.val() + " " + mentions_string); + this.mentions = mentions; + } + this.keyup(); + } + + NewPost.prototype.setIsPrivate = function(is_private) { + this.is_private = is_private; + } + + NewPost.prototype.toggleIsPrivate = function() { + this.is_private = !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.entities.length) { + var item = this.entities[key]; + if(item.name.toLowerCase().indexOf(name.toLowerCase()) != -1 || item.entity.toLowerCase().indexOf(name.toLowerCase()) != -1) { + var li = $("
  • " + item.name + " " + item.entity + "
  • ") + li.get(0).item = item; + this.suggestions.append(li) + } + } + } + + // parse the text: + // replace all the line braks by
    , and all the double spaces by the html version   + text = this.replaceAll(text,'\n','
    '); + text = this.replaceAll(text,' ','  '); + + // replace the words by a highlighted version of the words + + var remove = []; + + for (var i=0;i' + name + ''); + } 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) + + return true; + } + + NewPost.prototype.send = function() { + debug("Send not implemented yet"); + $("textarea").focus(); + } + + + return NewPost; +}) \ No newline at end of file diff --git a/WebKit/scripts/controller/Oauth.js b/WebKit/scripts/controller/Oauth.js index f020b5e..1adf8a9 100644 --- a/WebKit/scripts/controller/Oauth.js +++ b/WebKit/scripts/controller/Oauth.js @@ -14,7 +14,7 @@ function(HostApp, APICalls, Hmac) { "url": "http://jabs.nu/bungloo/", "description": "A desktop Tent client.", "redirect_uri": "bungloo://oauthtoken", - "post_types": { + "types": { "read": [ "https://tent.io/types/meta/v0", "https://tent.io/types/relationship/v0", @@ -87,12 +87,12 @@ function(HostApp, APICalls, Hmac) { Oauth.prototype.register = function (url) { var those = this; - debug(url) + 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.content.entity; HostApp.setStringForKey(those.entity, "entity") HostApp.setServerUrls(those.profile.content.servers[0].urls); @@ -101,19 +101,19 @@ function(HostApp, APICalls, Hmac) { 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 app_id = JSON.parse(resp.responseText).id; - var header_string = resp.getAllResponseHeaders(); - var regexp = /https:\/\/tent.io\/rels\/credentials/i - var url = APICalls.parseHeaderForLink(header_string, regexp); - - APICalls.get(url, { - content_type: "https://tent.io/types/app/v0#", - no_auth: true, - callback: function(resp) { - var data = JSON.parse(resp.responseText); - those.authRequest(data, app_id); - }}); + APICalls.get(url, { + content_type: "https://tent.io/types/app/v0#", + no_auth: true, + callback: function(resp) { + var data = JSON.parse(resp.responseText); + those.authRequest(data.post, app_id); + } + }); }}); }}); @@ -150,8 +150,7 @@ function(HostApp, APICalls, Hmac) { url, "POST", HostApp.stringForKey("app_hawk_id"), - HostApp.stringForKey("app_hawk_key"), - requestBody + HostApp.stringForKey("app_hawk_key") ); APICalls.post(url, requestBody, { diff --git a/WebKit/scripts/controller/Profile.js b/WebKit/scripts/controller/Profile.js index 6058784..94dcb7a 100644 --- a/WebKit/scripts/controller/Profile.js +++ b/WebKit/scripts/controller/Profile.js @@ -24,7 +24,7 @@ function(HostApp, Core, APICalls, URI) { this.hide(); var _this = this; - setTimeout(function() { _this.showProfileForEntity() }, 5000); // Load users profile on start + setTimeout(function() { _this.showProfileForEntity() }, 500); // Load users profile on start } Profile.prototype = Object.create(Core.prototype); @@ -239,20 +239,14 @@ function(HostApp, Core, APICalls, URI) { this.profile = profile; } else { - APICalls.findProfileURL(this.entity, function(profile_url) { - - if (profile_url) { - - APICalls.http_call(profile_url, "GET", function(resp) { - - profile = JSON.parse(resp.responseText); - _this.showProfile(profile); - _this.profile = profile; - - }, null, false); // do not send auth-headers - } - }); - + var url = HostApp.serverUrl("posts_feed") + "?types=" + encodeURIComponent("https://tent.io/types/meta/v0") + "&entities=" + encodeURIComponent(this.entity) + debug(url) + APICalls.get(url, { + callback: function(resp) { + profile = JSON.parse(resp.responseText); + _this.showProfile(profile); + _this.profile = profile; + }}); } } @@ -278,6 +272,9 @@ function(HostApp, Core, APICalls, URI) { Profile.prototype.showProfile = function(profile) { + debug(profile) + return + var basic = profile["https://tent.io/types/info/basic/v0.1.0"]; if (profile && basic) { diff --git a/WebKit/scripts/helper/Core.js b/WebKit/scripts/helper/Core.js index 802ca7a..77ea169 100644 --- a/WebKit/scripts/helper/Core.js +++ b/WebKit/scripts/helper/Core.js @@ -212,7 +212,8 @@ function(jQuery, APICalls, URI, HostApp, Cache) { template.reply_to.onclick = function() { 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") { status_mentions.push({entity:status.__repost.entity}); @@ -507,10 +508,13 @@ function(jQuery, APICalls, URI, HostApp, Cache) { var url = URI(HostApp.serverUrl("new_post")); - var http_method = "POST"; + var type = in_reply_to_status_id.length == 0 ? "https://tent.io/types/status/v0#" : "https://tent.io/types/status/v0#reply"; + debug(typeof in_reply_to_status_id) + debug(in_reply_to_status_id.length) + debug(type) var data = { - "type": in_reply_to_status_id ? "https://tent.io/types/status/v0#" : "https://tent.io/types/status/v0#reply", + "type": type, "published_at": parseInt(new Date().getTime(), 10), "permissions": { "public": !is_private @@ -521,7 +525,7 @@ function(jQuery, APICalls, URI, HostApp, Cache) { }; if (location) { - data["content"]["location"] = { "type": "Point", "coordinates": location } + //data["content"]["location"] = { "type": "Point", "coordinates": location } } var mentions = this.parseMentions(content, in_reply_to_status_id, in_reply_to_entity); @@ -1041,7 +1045,6 @@ function(jQuery, APICalls, URI, HostApp, Cache) { } - return Core; }); \ No newline at end of file diff --git a/WebKit/scripts/main.js b/WebKit/scripts/main.js index 7e30518..f823148 100644 --- a/WebKit/scripts/main.js +++ b/WebKit/scripts/main.js @@ -7,7 +7,8 @@ var bungloo = { entityProfile: null, conversation: null, search: null, - cache: {} + cache: {}, + newpost: null }; 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 { @@ -55,6 +65,19 @@ function start(view, callback) { bungloo.sidebar.showContentForTimeline(); + bungloo.cache.entities = { + "https://jeena.net" : { + name: "Jeena", + entity: "https://jeena.net", + avatar: "https://jeena.net/avatar.png" + }, + "https://ck.kennt-wayne.de": { + name: "Christian", + entity: "http://ck.kennt-wayne.de", + avatar: "http://ck.kennt-wayne.de/pavatar.png" + } + }; + }); }