diff --git a/Mac/Controller.h b/Mac/Controller.h index 1fa902c..1ee60ff 100644 --- a/Mac/Controller.h +++ b/Mac/Controller.h @@ -23,6 +23,8 @@ IBOutlet NSWindow *mentionsViewWindow; IBOutlet WebView *conversationView; IBOutlet NSWindow *conversationViewWindow; + WebView *profileView; + NSWindow *profileViewWindow; NSWindow *loginViewWindow; NSTextField *loginEntityTextField; NSProgressIndicator *loginActivityIndicator; @@ -34,12 +36,15 @@ } -@property (retain, nonatomic) IBOutlet WebView *timelineView; -@property (retain, nonatomic) IBOutlet NSWindow *timelineViewWindow; -@property (retain, nonatomic) IBOutlet WebView *mentionsView; -@property (retain, nonatomic) IBOutlet NSWindow *mentionsViewWindow; -@property (retain, nonatomic) IBOutlet WebView *conversationView; -@property (retain, nonatomic) IBOutlet NSWindow *conversationViewWindow; +@property (assign) IBOutlet WebView *timelineView; +@property (assign) IBOutlet NSWindow *timelineViewWindow; +@property (assign) IBOutlet WebView *mentionsView; +@property (assign) IBOutlet NSWindow *mentionsViewWindow; +@property (assign) IBOutlet WebView *conversationView; +@property (assign) IBOutlet NSWindow *conversationViewWindow; +@property (assign) IBOutlet WebView *profileView; +@property (assign) IBOutlet NSWindow *profileViewWindow; + @property (assign) IBOutlet NSWindow *loginViewWindow; @property (assign) IBOutlet NSTextField *loginEntityTextField; @property (assign) IBOutlet NSProgressIndicator *loginActivityIndicator; diff --git a/Mac/Controller.m b/Mac/Controller.m index a7d85fd..a3c267a 100644 --- a/Mac/Controller.m +++ b/Mac/Controller.m @@ -15,8 +15,7 @@ @synthesize loginViewWindow; @synthesize loginEntityTextField; @synthesize loginActivityIndicator; - -@synthesize timelineView, timelineViewWindow, mentionsView, mentionsViewWindow, conversationView, conversationViewWindow; +@synthesize timelineView, timelineViewWindow, mentionsView, mentionsViewWindow, conversationView, conversationViewWindow, profileView, profileViewWindow; @synthesize globalHotkeyMenuItem, viewDelegate; @synthesize logoLayer; @synthesize oauthView, accessToken; @@ -136,12 +135,20 @@ [conversationView setPolicyDelegate:viewDelegate]; [conversationView setUIDelegate:viewDelegate]; [[conversationView windowScriptObject] setValue:self forKey:@"controller"]; + + viewDelegate.profileView = profileView; + [[profileView mainFrame] loadHTMLString:index_string baseURL:url]; + [profileView setFrameLoadDelegate:viewDelegate]; + [profileView setPolicyDelegate:viewDelegate]; + [profileView setUIDelegate:viewDelegate]; + [[profileView windowScriptObject] setValue:self forKey:@"controller"]; } else { [timelineView stringByEvaluatingJavaScriptFromString:@"start('timeline')"]; [mentionsView stringByEvaluatingJavaScriptFromString:@"start('mentions')"]; [conversationView stringByEvaluatingJavaScriptFromString:@"start('conversation')"]; + [profileView stringByEvaluatingJavaScriptFromString:@"start('profile')"]; } } @@ -445,6 +452,13 @@ [conversationViewWindow makeKeyAndOrderFront:self]; } +- (IBAction)showProfileForEntity:(NSString *)entity +{ + NSString *js = [NSString stringWithFormat:@"tentia_instance.showProfileForEntity('%@');", entity]; + [profileView stringByEvaluatingJavaScriptFromString:js]; + [profileViewWindow makeKeyAndOrderFront:self]; +} + - (void)growlNotificationWasClicked:(id)clickContext { NSDictionary *userInfo = (NSDictionary *)clickContext; diff --git a/Mac/English.lproj/MainMenu.xib b/Mac/English.lproj/MainMenu.xib index 93a6f20..98e454d 100644 --- a/Mac/English.lproj/MainMenu.xib +++ b/Mac/English.lproj/MainMenu.xib @@ -958,7 +958,7 @@ - + 256 YES @@ -988,6 +988,7 @@ {376, 581} + @@ -997,11 +998,72 @@ {376, 581} + + {{0, 0}, {2560, 1418}} {10000000000000, 10000000000000} - conversations + conversation + YES + + + 15 + 2 + {{1292, 328}, {376, 581}} + 1685586944 + Profile + NSWindow + + + + + 256 + + YES + + + 274 + + YES + + YES + Apple HTML pasteboard type + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple URL pasteboard type + Apple Web Archive pasteboard type + NSColor pasteboard type + NSFilenamesPboardType + NSStringPboardType + NeXT RTFD pasteboard type + NeXT Rich Text Format v1.0 pasteboard type + NeXT TIFF v4.0 pasteboard type + WebURLsWithTitlesPboardType + public.png + public.url + public.url-name + + + {376, 581} + + + + + + + NO + YES + + + {376, 581} + + + + + {{0, 0}, {2560, 1418}} + {10000000000000, 10000000000000} + profile YES @@ -1035,7 +1097,6 @@ {{20, 20}, {146, 146}} - YES @@ -1058,7 +1119,6 @@ 268 {{194, 82}, {266, 22}} - _NS:9 YES @@ -1105,7 +1165,6 @@ 268 {{191, 112}, {163, 17}} - _NS:1535 YES @@ -1139,8 +1198,6 @@ 268 {{391, 46}, {75, 32}} - - _NS:9 YES @@ -1164,7 +1221,6 @@ 268 {{373, 55}, {16, 16}} - _NS:945 28938 @@ -1173,7 +1229,6 @@ {480, 186} - _NS:20 @@ -1645,6 +1700,22 @@ 643 + + + profileView + + + + 662 + + + + profileViewWindow + + + + 663 + makeKeyAndOrderFront: @@ -1709,6 +1780,14 @@ 631 + + + delegate + + + + 661 + @@ -2413,7 +2492,7 @@ - Mentions + Conversation 629 @@ -2488,6 +2567,30 @@ + + 658 + + + YES + + + + Profile + + + 659 + + + YES + + + + + + 660 + + + @@ -2606,6 +2709,11 @@ 653.IBPluginDependency 654.IBPluginDependency 657.IBPluginDependency + 658.IBPluginDependency + 658.IBWindowTemplateEditedContentRect + 658.NSWindowTemplate.visibleAtLaunch + 659.IBPluginDependency + 660.IBPluginDependency 72.IBPluginDependency 73.IBPluginDependency 79.IBPluginDependency @@ -2728,6 +2836,11 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + {{344, 175}, {376, 581}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.WebKitIBPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2747,7 +2860,7 @@ - 657 + 663 @@ -2800,6 +2913,8 @@ logoLayer mentionsView mentionsViewWindow + profileView + profileViewWindow timelineView timelineViewWindow viewDelegate @@ -2817,6 +2932,8 @@ NSWindow WebView NSWindow + WebView + NSWindow ViewDelegate @@ -2833,6 +2950,8 @@ logoLayer mentionsView mentionsViewWindow + profileView + profileViewWindow timelineView timelineViewWindow viewDelegate @@ -2875,6 +2994,14 @@ mentionsViewWindow NSWindow + + profileView + WebView + + + profileViewWindow + NSWindow + timelineView WebView diff --git a/Mac/ViewDelegate.h b/Mac/ViewDelegate.h index 5001c65..0054ce3 100644 --- a/Mac/ViewDelegate.h +++ b/Mac/ViewDelegate.h @@ -14,12 +14,14 @@ WebView *timelineView; WebView *mentionsView; WebView *conversationView; + WebView *profileView; WebView *oauthView; } @property (nonatomic, assign) WebView *timelineView; @property (nonatomic, assign) WebView *mentionsView; @property (nonatomic, assign) WebView *conversationView; +@property (nonatomic, assign) WebView *profileView; @property (nonatomic, assign) WebView *oauthView; @end diff --git a/Mac/ViewDelegate.m b/Mac/ViewDelegate.m index 80bc517..0209416 100644 --- a/Mac/ViewDelegate.m +++ b/Mac/ViewDelegate.m @@ -11,7 +11,7 @@ @implementation ViewDelegate -@synthesize timelineView, mentionsView, conversationView, oauthView; +@synthesize timelineView, mentionsView, conversationView, profileView, oauthView; - (void)webView:(WebView *)sender addMessageToConsole:(NSDictionary *)message;{ @@ -21,6 +21,7 @@ if (sender == mentionsView) viewName = @"MentionsView"; if (sender == conversationView) viewName = @"ConversationView"; if (sender == oauthView) viewName = @"OauthView"; + if (sender == profileView) viewName = @"ProfileView"; NSLog(@"js<%@>: %@:%@: %@", viewName, @@ -79,7 +80,11 @@ } else if(sender == conversationView) { [conversationView stringByEvaluatingJavaScriptFromString:@"function HostAppGo() { start('conversation') }"]; - + + } else if(sender == profileView) { + + [profileView stringByEvaluatingJavaScriptFromString:@"function HostAppGo() { start('profile') }"]; + } else { NSString *action = @"timeline"; diff --git a/WebKit/css/default.css b/WebKit/css/default.css index ddd6c62..95cac43 100644 --- a/WebKit/css/default.css +++ b/WebKit/css/default.css @@ -20,7 +20,7 @@ ol { padding: 0; } -li, .error { +ol li, .error, header.profile { clear: both; padding: 8px; background: #eee; @@ -37,23 +37,23 @@ li, .error { color: red; } -li:first-child { +ol li:first-child { border-top: 0; } -li:nth-child(odd), .error { +ol li:nth-child(odd), .error, header.profile { background: url(../img/odd-bg.png) repeat-x bottom #fafafa; } -li:nth-child(even) { +ol li:nth-child(even) { background: url(../img/even-bg.png) repeat-x bottom #f2f2f2; } -li:hover { +ol li:hover { background: #dedede; } -li:after { +ol li:after, header.profile:after { content: "."; display: block; clear: both; @@ -62,6 +62,28 @@ li:after { height: 0; } +header.profile img { + float: left; + margin: 0 10px 10px 0; + max-width: 100px; + border-radius: 10px; +} + +header.profile table { + border-collapse: collapse; + margin-top: 10px; +} + +header.profile th { + text-align: left; + padding-right: 1em; +} + +header.profile h1 + p { + color: #aaa; + margin-bottom: 10px; +} + .highlight { border-right: 5px solid #f17779; } diff --git a/WebKit/scripts/controller/Conversation.js b/WebKit/scripts/controller/Conversation.js index 7441e9d..16953a2 100644 --- a/WebKit/scripts/controller/Conversation.js +++ b/WebKit/scripts/controller/Conversation.js @@ -5,7 +5,7 @@ define([ "lib/URI" ], -function(HostApp, Core, Paths, URI, Followings) { +function(HostApp, Core, Paths, URI) { function Conversation() { diff --git a/WebKit/scripts/controller/Profile.js b/WebKit/scripts/controller/Profile.js new file mode 100644 index 0000000..46e76c0 --- /dev/null +++ b/WebKit/scripts/controller/Profile.js @@ -0,0 +1,270 @@ +define([ + "helper/HostApp", + "helper/Core", + "helper/Paths", + "lib/URI" +], + +function(HostApp, Core, Paths, URI) { + + + function Profile() { + + Core.call(this); + + this.action = "profile"; + + this.initProfileTemplate(); + } + + Profile.prototype = Object.create(Core.prototype); + + Profile.prototype.showProfileForEntity = function(entity) { + + this.clear(); + this.entity = entity; + this.profile_template.entity.innerHTML = entity; + this.profile_template.entity.href = entity; + this.getProfile(); + } + + Profile.prototype.initProfileTemplate = function() { + + var header = document.createElement("header"); + header.className = "profile" + document.body.appendChild(header); + + this.profile_template = { + avatar: document.createElement("img"), + name: document.createElement("h1"), + entity: document.createElement("a"), + bio: document.createElement("p"), + posts: document.createElement("td"), + following: document.createElement("td"), + followed: document.createElement("td"), + birthdate: document.createElement("td"), + location: document.createElement("td"), + gender: document.createElement("td"), + url: document.createElement("a") + }; + + header.appendChild(this.profile_template.avatar); + this.profile_template.avatar.src = "img/default-avatar.png"; + var div = document.createElement("div"); + header.appendChild(div); + + div.appendChild(this.profile_template.name); + + var p = document.createElement("p"); + p.appendChild(this.profile_template.entity); + div.appendChild(p); + + div.appendChild(this.profile_template.bio); + + var table = document.createElement("table"); + div.appendChild(table); + + function mkLi(name, template) { + var tr = document.createElement("tr"); + var th = document.createElement("th"); + tr.style.display = "none"; + th.innerText = name + ": "; + tr.appendChild(th); + tr.appendChild(template); + table.appendChild(tr); + } + + mkLi("Birth date", this.profile_template.birthdate); + mkLi("Location", this.profile_template.location); + mkLi("Gender", this.profile_template.gender); + + var td = document.createElement("td"); + td.appendChild(this.profile_template.url); + mkLi("Homepage", td); + + mkLi("Posts", this.profile_template.posts); + mkLi("Following", this.profile_template.following); + mkLi("Followed by", this.profile_template.followed); + + + this.body = document.createElement("ol"); + this.body.className = this.action; + document.body.appendChild(this.body); + } + + Profile.prototype.clear = function() { + + this.profile_template.avatar.src = "img/default-avatar.png"; + + this.profile_template.name.innerText = ""; + this.profile_template.entity.innerText = ""; + this.profile_template.bio.innerText = ""; + this.profile_template.posts.innerText = ""; + this.profile_template.following.innerText = ""; + this.profile_template.followed.innerText = ""; + this.profile_template.birthdate.innerText = ""; + this.profile_template.location.innerText = ""; + this.profile_template.gender.innerText = ""; + this.profile_template.url.innerText = ""; + this.profile_template.url.href = ""; + + this.profile_template.posts.parentNode.style.display = "none"; + this.profile_template.following.parentNode.style.display = "none"; + this.profile_template.followed.parentNode.style.display = "none"; + this.profile_template.birthdate.parentNode.style.display = "none"; + this.profile_template.location.parentNode.style.display = "none"; + this.profile_template.gender.parentNode.style.display = "none"; + this.profile_template.url.parentNode.parentNode.style.display = "none"; + + this.body.innerHTML = ""; + }; + + Profile.prototype.getProfile = function() { + + var _this = this; + + Paths.findProfileURL(this.entity, function(profile_url) { + + if (profile_url) { + + Paths.getURL(profile_url, "GET", function(resp) { + + _this.showProfile(JSON.parse(resp.responseText)); + + }, null, false); // do not send auth-headers + } + }); + } + + Profile.prototype.showProfile = function(profile) { + + var basic = profile["https://tent.io/types/info/basic/v0.1.0"]; + + if (profile && basic) { + + if(basic.avatar_url) { + this.profile_template.avatar.onerror = function() { this.profile_template.avatar.src = 'img/default-avatar.png' }; + this.profile_template.avatar.src = basic.avatar_url; + } + + this.populate(this.profile_template.name, basic.name); + this.populate(this.profile_template.birthdate, basic.birthdate); + this.populate(this.profile_template.location, basic.location); + this.populate(this.profile_template.gender, basic.gender); + this.populate(this.profile_template.bio, basic.bio); + + if(basic.url) { + + var url = basic.url; + this.profile_template.url.innerText = url; + this.profile_template.url.parentNode.parentNode.style.display = ""; + + if (!url.startsWith("http")) { + url = "http://" + url; + } + + this.profile_template.url.href = url; + } + } + + if (profile) { + var server = profile["https://tent.io/types/info/core/v0.1.0"]["servers"][0]; + this.getMeta(server); + this.getStatuses(server); + } + } + + Profile.prototype.populate = function(t, v) { + if (v) { + t.innerText = v; + t.parentNode.style.display = ""; + } + } + + Profile.prototype.getMeta = function(root_url) { + + var _this = this; + Paths.getURL(URI(root_url + "/followings/count").toString(), "GET", function(resp) { + + _this.populate(_this.profile_template.following, resp.responseText); + }, null, false); + + Paths.getURL(URI(root_url + "/followers/count").toString(), "GET", function(resp) { + + _this.populate(_this.profile_template.followed, resp.responseText); + }, null, false); + + Paths.getURL(URI(root_url + "/posts/count").toString(), "GET", function(resp) { + + _this.populate(_this.profile_template.posts, resp.responseText); + }, null, false); + } + + + Profile.prototype.getStatuses = function(root_url) { + var _this = this; + + var url = URI(root_url + "/posts"); + url.addSearch("limit", 20); + + var post_types = [ + "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(",")); + + Paths.getURL(url.toString(), "GET", function(resp) { + + var statuses = JSON.parse(resp.responseText); + + _this.newStatus(statuses); + + }, null, false); + } + + Profile.prototype.newStatus = function(statuses) { + if(statuses != null && statuses.length > 0) { + for(var i = statuses.length-1, c=0; i>=c; --i) { + + var status = statuses[i]; + this.since_id = status.id; + 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") { + + var new_node = this.getStatusDOMElement(status); + + if(this.body.childNodes.length > 0) { + + if(this.body.childNodes.length > this.max_length) { + + this.body.removeChild(this.body.lastChild); + } + + this.body.insertBefore(new_node, this.body.firstChild); + + } else { + + this.body.appendChild(new_node); + } + + } else if (status.type == "https://tent.io/types/post/delete/v0.1.0") { + + var li = document.getElementById("post-" + status.content.id); + if (li) { + this.body.removeChild(li); + } + } else if (status.type == "https://tent.io/types/post/repost/v0.1.0") { + + this.getRepost(status, this.body.firstChild); + } + + } + } + } + + return Profile; + +}); \ No newline at end of file diff --git a/WebKit/scripts/controller/Timeline.js b/WebKit/scripts/controller/Timeline.js index 4f9c734..6038ef8 100644 --- a/WebKit/scripts/controller/Timeline.js +++ b/WebKit/scripts/controller/Timeline.js @@ -86,7 +86,7 @@ function(Core, Paths, HostApp, URI) { "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" + //"https://tent.io/types/post/photo/v0.1.0" ]; url.addSearch("post_types", post_types.join(",")); @@ -125,30 +125,6 @@ function(Core, Paths, HostApp, URI) { } } - Timeline.prototype.getRepost = function(repost, before_node) { - - var _this = this; - var callback = function(resp) { - var status = JSON.parse(resp.responseText); - status.__repost = repost; - var li = _this.getStatusDOMElement(status); - before_node.parentNode.insertBefore(li, before_node); - } - - Paths.findProfileURL(repost.content.entity, function(profile_url) { - if (profile_url) { - - Paths.getURL(profile_url, "GET", function(resp) { - - var profile = JSON.parse(resp.responseText); - 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); - - }, null, false); // do not send auth-headers - } - }) - } - Timeline.prototype.sendNewMessage = function(content, in_reply_to_status_id, in_reply_to_entity, location, image_data_uri) { var _this = this; var callback = function(data) { _this.getNewData(); } diff --git a/WebKit/scripts/helper/Core.js b/WebKit/scripts/helper/Core.js index 6f2f9af..47d21b9 100644 --- a/WebKit/scripts/helper/Core.js +++ b/WebKit/scripts/helper/Core.js @@ -177,7 +177,6 @@ function(jQuery, Paths, URI, HostApp, Followings) { return false; } - //template.retweet.onclick = function() { template.retweet.className = "hidden"; _this.retweet(status.id_str, template.item); return false; } template.repost.onclick = function() { template.repost.className = "hidden"; _this.repost(status.id, status.entity); @@ -186,6 +185,10 @@ function(jQuery, Paths, URI, HostApp, Followings) { template.username.innerText = status.entity; template.username.href = status.entity; // FIXME open profile + template.username.onclick = function() { + HostApp.showProfileForEntity(status.entity); + return false; + } var profile = function(profile) { @@ -258,7 +261,9 @@ function(jQuery, Paths, URI, HostApp, Followings) { if (status.type == "https://tent.io/types/post/photo/v0.1.0") { text = status.content.caption; } else { - text = status.content.text; + if (status.content && status.content.text) { + text = status.content.text; + } } text = text.escapeHTML().replace(/\n/g, "
"); @@ -302,7 +307,7 @@ function(jQuery, Paths, URI, HostApp, Followings) { break; } } - + var published_at = typeof status.__repost == "undefined" ? status.published_at : status.__repost.published_at; var time = document.createElement("abbr"); time.innerText = this.ISODateString(new Date(published_at * 1000)); @@ -336,6 +341,31 @@ function(jQuery, Paths, URI, HostApp, Followings) { return template.item; } + + Core.prototype.getRepost = function(repost, before_node) { + + var _this = this; + var callback = function(resp) { + var status = JSON.parse(resp.responseText); + status.__repost = repost; + var li = _this.getStatusDOMElement(status); + before_node.parentNode.insertBefore(li, before_node); + } + + Paths.findProfileURL(repost.content.entity, function(profile_url) { + if (profile_url) { + + Paths.getURL(profile_url, "GET", function(resp) { + + var profile = JSON.parse(resp.responseText); + 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); + + }, null, false); // do not send auth-headers + } + }) + } + Core.prototype.sendNewMessage = function(content, in_reply_to_status_id, in_reply_to_entity, location, image_file_path, callback) { if (image_file_path) { @@ -528,11 +558,16 @@ function(jQuery, Paths, URI, HostApp, Followings) { if(basic.name) { var new_text = node.innerHTML.replace( mention.text, - "" + "" + basic.name - + "" + + "" ); + node.innerHTML = new_text; + $(node).find("a.name").click(function(e) { + HostApp.showProfileForEntity(e.target.href); + return false; + }); } } } diff --git a/WebKit/scripts/helper/HostApp.js b/WebKit/scripts/helper/HostApp.js index 686f190..37c870e 100644 --- a/WebKit/scripts/helper/HostApp.js +++ b/WebKit/scripts/helper/HostApp.js @@ -87,6 +87,15 @@ define(function() { } } + HostApp.showProfileForEntity = function(entity) { + + if (OS_TYPE == "mac") { + controller.showProfileForEntity_(entity); + } else { + controller.showProfileForEntity(entity); + } + } + HostApp.notificateUserAboutMention = function(text, name, post_id, entity) { if (OS_TYPE == "mac") { controller.notificateUserAboutMention_fromName_withPostId_andEntity_(text, name, post_id, entity); diff --git a/WebKit/scripts/main.js b/WebKit/scripts/main.js index 8fd074e..0c981fd 100644 --- a/WebKit/scripts/main.js +++ b/WebKit/scripts/main.js @@ -33,6 +33,12 @@ function start(view) { } else if (view == "profile") { + require(["controller/Profile"], function(Profile) { + + tentia_instance = new Profile(); + + }); + } else if (view == "follow") { } else if (view == "conversation") { @@ -55,17 +61,17 @@ String.prototype.endsWith = function(suffix) { return this.match(suffix+"$") == suffix; }; - var __entityMap = { - "&": "&", - "<": "<", - ">": ">" - }; +var __entityMap = { + "&": "&", + "<": "<", + ">": ">" +}; - String.prototype.escapeHTML = function() { - return String(this).replace(/[&<>]/g, function (s) { - return __entityMap[s]; - }); - } +String.prototype.escapeHTML = function() { + return String(this).replace(/[&<>]/g, function (s) { + return __entityMap[s]; + }); +} var console = { log: function(s) {