Browse Source

post content is now parsed to generate links to urls and profiles

master
Julian Steinwachs 10 years ago
parent
commit
9afd7f871b
  1. 1
      .gitignore
  2. 791
      build/app-bundle.js
  3. 162
      build/twister-lib.js
  4. 16
      css/main.css
  5. 2
      jsx/App.js
  6. 3
      jsx/common/Post.js
  7. 205
      jsx/common/PostContent.js
  8. 5
      tests/PostContentTest.js

1
.gitignore vendored

@ -0,0 +1 @@
/build-buffer

791
build/app-bundle.js

File diff suppressed because it is too large Load Diff

162
build/twister-lib.js

@ -31151,6 +31151,44 @@ Twister.trimCache = function (timestamp) {
} }
Twister._activeQueryIds = {};
Twister.raiseQueryId = function (id) {
if (id) {
if(!Twister._activeQueryIds[id]){
Twister._activeQueryIds[id]={func:null,count:1};
}else{
Twister._activeQueryIds[id].count++;
}
}
}
Twister.bumpQueryId = function (id) {
if (id) {
Twister._activeQueryIds[id].count--;
if (Twister._activeQueryIds[id].count==0) {
if (Twister._activeQueryIds[id].func) {
Twister._activeQueryIds[id].func();
}
delete Twister._activeQueryIds[id];
}
}
}
Twister.onQueryComplete = function (id, cbfunc){
if(!Twister._activeQueryIds[id]){
Twister._activeQueryIds[id]={func:cbfunc,count:0};
}else{
Twister._activeQueryIds[id].func=cbfunc;
}
}
module.exports = Twister; module.exports = Twister;
},{"./ServerWallet/TwisterAccount.js":139,"./TwisterHashtag.js":145,"./TwisterPromotedPosts.js":149,"./TwisterResource.js":152,"./TwisterUser.js":155}],143:[function(require,module,exports){ },{"./ServerWallet/TwisterAccount.js":139,"./TwisterHashtag.js":145,"./TwisterPromotedPosts.js":149,"./TwisterResource.js":152,"./TwisterUser.js":155}],143:[function(require,module,exports){
@ -32594,6 +32632,36 @@ TwisterResource.prototype.inCache = function () {
return (this._lastUpdate>0); return (this._lastUpdate>0);
} }
TwisterResource.prototype._wrapPromise = function (context,handler,cbfunc,querySettings) {
if ( typeof cbfunc != "function" ) {
querySettings = cbfunc;
cbfunc = null;
}
if (!querySettings){ querySettings = {}; }
if (querySettings["errorfunc"]) {
var errorfuncFromQuerySettings = querySettings["errorfunc"];
} else {
var errorfuncFromQuerySettings = null;
}
delete querySettings["errorfunc"];
return new Promise ( function ( resolve, reject ) {
querySettings["errorfunc"]=reject;
handler.call(context,resolve,querySettings);
} ).then(cbfunc,errorfuncFromQuerySettings);
}
/** /**
* Checks whether cached resource is outdated and invokes an update if needed. Calls cbfunc on the resource when done. * Checks whether cached resource is outdated and invokes an update if needed. Calls cbfunc on the resource when done.
* @function * @function
@ -32602,6 +32670,8 @@ TwisterResource.prototype.inCache = function () {
*/ */
TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) { TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
if (querySettings===undefined) {querySettings={};} if (querySettings===undefined) {querySettings={};}
//else {console.log(querySettings)} //else {console.log(querySettings)}
@ -32611,8 +32681,9 @@ TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
if (!thisResource._updateInProgress) { if (!thisResource._updateInProgress) {
thisResource._activeQuerySettings = querySettings; thisResource._activeQuerySettings = JSON.parse(JSON.stringify(querySettings));
thisResource._updateInProgress = true; thisResource._updateInProgress = true;
Twister.raiseQueryId(thisResource._activeQuerySettings["queryId"]);
var outdatedTimestamp = 0; var outdatedTimestamp = 0;
@ -32624,17 +32695,19 @@ TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
thisResource._log("resource present in cache"); thisResource._log("resource present in cache");
Twister.bumpQueryId(thisResource._activeQuerySettings["queryId"]);
thisResource._activeQuerySettings = {}; thisResource._activeQuerySettings = {};
thisResource._updateInProgress = false; thisResource._updateInProgress = false;
} else { } else {
thisResource._log("resource not in cache. querying");
thisResource._queryAndDo(function(newresource){ thisResource._queryAndDo(function(newresource){
thisResource._do(cbfunc); thisResource._do(cbfunc);
thisResource._log("resource not in cache. querying"); Twister.bumpQueryId(thisResource._activeQuerySettings["queryId"]);
thisResource._activeQuerySettings = {}; thisResource._activeQuerySettings = {};
thisResource._updateInProgress = false; thisResource._updateInProgress = false;
@ -32654,6 +32727,7 @@ TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
} }
} }
/** /**
@ -32709,8 +32783,9 @@ TwisterResource.prototype.setQuerySettings = function (settings) {
TwisterResource.prototype._handleError = function (error) { TwisterResource.prototype._handleError = function (error) {
this._updateInProgress = false; this._updateInProgress = false;
this.getQuerySetting("errorfunc").call(this,error); this.getQuerySetting("errorfunc").call(this,error);
Twister.bumpQueryId(this._activeQuerySettings["queryId"]);
this._activeQuerySettings={};
} }
@ -32764,18 +32839,45 @@ TwisterResource.prototype.RPC = function (method, params, resultFunc, errorFunc)
if (error) { if (error) {
error.message = "Host not reachable (http error)."; thisResource._handleError({
message: "Host not reachable.",
data: error.code,
code: 32090
})
thisResource._handleError(error) } else {
if (response.statusCode<200 || response.statusCode>299) {
thisResource._handleError({
message: "Request was not processed successfully (http error: "+response.statusCode+").",
data: response.statusCode,
code: 32091
})
} else { } else {
try {
var res = JSON.parse(body); var res = JSON.parse(body);
if (res.error) { if (res.error) {
thisResource._handleError(res.error); thisResource._handleError(res.error);
} else { } else {
resultFunc(res.result); resultFunc(res.result);
} }
} catch (err) {
thisResource._handleError({
message: "An error occurred while parsing the JSON response body.",
code: 32092
})
}
}
} }
}); });
@ -33405,6 +33507,8 @@ var TwisterFollowings = require('./TwisterFollowings.js');
var TwisterPubKey = require('./TwisterPubKey.js'); var TwisterPubKey = require('./TwisterPubKey.js');
var TwisterStream = require('./TwisterStream.js'); var TwisterStream = require('./TwisterStream.js');
var TwisterMentions = require('./TwisterMentions.js'); var TwisterMentions = require('./TwisterMentions.js');
var TwisterResource = require('./TwisterResource.js');
var inherits = require('inherits');
/** /**
* Describes a user in {@ Twister}. Allows for accessing all public onformation about this user. * Describes a user in {@ Twister}. Allows for accessing all public onformation about this user.
@ -33428,6 +33532,8 @@ function TwisterUser(name,scope) {
} }
inherits(TwisterUser,TwisterResource);
module.exports = TwisterUser; module.exports = TwisterUser;
TwisterUser.prototype.trim = function () { TwisterUser.prototype.trim = function () {
@ -33505,7 +33611,11 @@ TwisterUser.prototype._doPubKey = function (cbfunc, querySettings) {
} }
TwisterUser.prototype.doProfile = function (cbfunc, querySettings) { TwisterUser.prototype.doProfile = function (cbfunc, querySettings) {
this._profile._checkQueryAndDo(cbfunc, querySettings); return this._wrapPromise(
this._profile,
this._profile._checkQueryAndDo,
cbfunc,
querySettings);
}; };
TwisterUser.prototype.getProfile = function () { TwisterUser.prototype.getProfile = function () {
@ -33513,7 +33623,11 @@ TwisterUser.prototype.getProfile = function () {
}; };
TwisterUser.prototype.doAvatar = function (cbfunc, querySettings) { TwisterUser.prototype.doAvatar = function (cbfunc, querySettings) {
this._avatar._checkQueryAndDo(cbfunc, querySettings); return this._wrapPromise(
this._avatar,
this._avatar._checkQueryAndDo,
cbfunc,
querySettings);
}; };
TwisterUser.prototype.getAvatar = function () { TwisterUser.prototype.getAvatar = function () {
@ -33521,7 +33635,11 @@ TwisterUser.prototype.getAvatar = function () {
}; };
TwisterUser.prototype.doFollowings = function (cbfunc, querySettings) { TwisterUser.prototype.doFollowings = function (cbfunc, querySettings) {
this._followings._checkQueryAndDo(cbfunc, querySettings); return this._wrapPromise(
this._followings,
this._followings._checkQueryAndDo,
cbfunc,
querySettings);
}; };
TwisterUser.prototype.getFollowings = function () { TwisterUser.prototype.getFollowings = function () {
@ -33529,11 +33647,25 @@ TwisterUser.prototype.getFollowings = function () {
}; };
TwisterUser.prototype.doStatus = function (cbfunc, querySettings) { TwisterUser.prototype.doStatus = function (cbfunc, querySettings) {
this._stream._checkQueryAndDo(cbfunc, querySettings); return this._wrapPromise(
this._stream,
this._stream._checkQueryAndDo,
cbfunc,
querySettings);
}; };
TwisterUser.prototype.doPost = function (id, cbfunc, querySettings) { TwisterUser.prototype.doPost = function (id, cbfunc, querySettings) {
this._stream._doPost(id, cbfunc, querySettings);
var thisStream = this._stream;
return this._wrapPromise(
thisStream,
function(cb,qs){
thisStream._doPost(id, cb, qs);
},
cbfunc,
querySettings);
} }
@ -33547,7 +33679,11 @@ TwisterUser.prototype.getPost = function (id) {
TwisterUser.prototype.doMentions = function (cbfunc, querySettings) { TwisterUser.prototype.doMentions = function (cbfunc, querySettings) {
this._mentions._checkQueryAndDo(cbfunc); return this._wrapPromise(
this._mentions,
this._mentions._checkQueryAndDo,
cbfunc,
querySettings);
} }
@ -33560,7 +33696,7 @@ TwisterUser.prototype.doLatestPostsUntil = function (cbfunc, querySettings) {
this._stream._doUntil(cbfunc, querySettings); this._stream._doUntil(cbfunc, querySettings);
} }
},{"./TwisterAvatar.js":143,"./TwisterFollowings.js":144,"./TwisterMentions.js":146,"./TwisterProfile.js":148,"./TwisterPubKey.js":150,"./TwisterStream.js":154}],156:[function(require,module,exports){ },{"./TwisterAvatar.js":143,"./TwisterFollowings.js":144,"./TwisterMentions.js":146,"./TwisterProfile.js":148,"./TwisterPubKey.js":150,"./TwisterResource.js":152,"./TwisterStream.js":154,"inherits":52}],156:[function(require,module,exports){
},{}],157:[function(require,module,exports){ },{}],157:[function(require,module,exports){
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 // http://wiki.commonjs.org/wiki/Unit_Testing/1.0

16
css/main.css

@ -64,3 +64,19 @@ body.modal-open {
opacity: 0.01; opacity: 0.01;
} }
.mention {
color: red;
}
.hashtag {
color: green;
}
.url {
color: blue;
}
.email {
color: coral;
}

2
jsx/App.js

@ -197,7 +197,7 @@ initializeApp = function () {
Twister.deserializeCache(JSON.parse(localStorage.getItem("twister-cache"))); Twister.deserializeCache(JSON.parse(localStorage.getItem("twister-cache")));
Twister.setup({logfunc: function(log){console.log(log)}}) //Twister.setup({logfunc: function(log){console.log(log)}})
var accounts = Twister.getAccounts(); var accounts = Twister.getAccounts();

3
jsx/common/Post.js

@ -12,6 +12,7 @@ var React = require('react');
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); var SetIntervalMixin = require("../common/SetIntervalMixin.js");
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js');
var PostContent = require('../common/PostContent.js');
module.exports = Post = React.createClass({ module.exports = Post = React.createClass({
mixins: [SetIntervalMixin,SafeStateChangeMixin], mixins: [SetIntervalMixin,SafeStateChangeMixin],
@ -104,7 +105,7 @@ module.exports = Post = React.createClass({
</Col> </Col>
<Col xs={9} md={9}> <Col xs={9} md={9}>
<strong>{this.state.fullname}</strong>&nbsp; <strong>{this.state.fullname}</strong>&nbsp;
{post.getContent()} <PostContent content={post.getContent()}/>
</Col> </Col>
<Col xs={1} md={1} className="fullytight text-align-right">{this.state.timeAgo}</Col> <Col xs={1} md={1} className="fullytight text-align-right">{this.state.timeAgo}</Col>
</Row> </Row>

205
jsx/common/PostContent.js

@ -0,0 +1,205 @@
var React = require('react');
module.exports = Post = React.createClass({
extractUsername: function(s) {
var username = "";
for( var i = 0; i < s.length; i++ ) {
var c = s.charCodeAt(i);
if( (c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) ||
(c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) ||
(c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) ||
c == '_'.charCodeAt(0) ) {
username += s[i];
} else {
break;
}
}
return username;
},
extractHashtag: function(s) {
var hashtag = "";
s = this.reverseHtmlEntities(s);
for( var i = 0; i < s.length; i++ ) {
if( " \n\t.,:/?!;'\"()[]{}*#".indexOf(s[i]) < 0 ) {
hashtag += s[i];
} else {
break;
}
}
return hashtag;
},
escapeHtmlEntities: function(str) {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
},
reverseHtmlEntities: function(str) {
return str
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&amp;/g, '&');
},
parseContent: function( msg ) {
var output = [];
var tmp;
var match = null;
var index;
var strUrlRegexp = "http[s]?://";
var strEmailRegexp = "\\S+@\\S+\\.\\S+";
var strSplitCounterR = "\\(\\d{1,2}\\/\\d{1,2}\\)$";
var reAll = new RegExp("(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + "|" + strSplitCounterR + ")");
var reHttp = new RegExp(strUrlRegexp);
var reEmail = new RegExp(strEmailRegexp);
var reSplitCounter = new RegExp(strSplitCounterR);
msg = this.escapeHtmlEntities(msg);
while( msg != undefined && msg.length ) {
match = reAll.exec(msg);
if( match ) {
index = (match[0] === match[1]) ? match.index : match.index + 1;
if( match[1] == "@" ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substr(index+1);
var username = this.extractUsername(tmp);
if( username.length ) {
output.push({type:"mention",raw:"@"+username});
}
msg = tmp.substr(String(username).length);
continue;
}
if( reHttp.exec(match[1]) ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substring(index);
var space = tmp.search(/[ \n\t]/);
var url;
if( space != -1 ) url = tmp.substring(0,space); else url = tmp;
if( url.length ) {
output.push({type:"url",raw:url});
}
msg = tmp.substr(String(url).length);
continue;
}
if( reEmail.exec(match[1]) ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substring(index);
var space = tmp.search(/[ \n\t]/);
var email;
if( space != -1 ) email = tmp.substring(0,space); else email = tmp;
if( email.length ) {
output.push({type:"email",raw:email});
}
msg = tmp.substr(String(email).length);
continue;
}
if( match[1] == "#" ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substr(index+1);
var hashtag = this.extractHashtag(tmp);
if( hashtag.length ) {
// var hashtag_lc='';
// for( var i = 0; i < hashtag.length; i++ ) {
// var c = hashtag[i];
// hashtag_lc += (c >= 'A' && c <= 'Z') ? c.toLowerCase() : c;
// }
output.push({type:"hashtag",raw:"#"+hashtag});
}
msg = tmp.substr(String(hashtag).length);
continue;
}
/*if (reSplitCounter.exec(match[1])) {
output.append({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substring(index);
if( tmp.length ) {
var splitCounter = $('<span class="splited-post-counter"></span>');
splitCounter.text(tmp);
output.append(splitCounter);
msg = "";
continue;
}
msg = tmp.substr(String(hashtag).length);
continue;
}*/
}
output.push({type:"text",raw:(msg.substr(0, index))});
msg = "";
}
return output;
},
render: function() {
var parsedContent = this.parseContent(this.props.content);
//console.log(parsedContent)
var ret = parsedContent.map(function(item,index){
//console.log(item.raw)
switch(item.type) {
case "mention":
return (
<a key={index} className="text-muted" href={"#/profile/"+item.raw.substr(1)}>{item.raw}</a>
)
case "hashtag":
return (
<span key={index} className="text-muted">{item.raw}</span>
)
case "url":
return (
<a key={index} className="text-primary" href={item.raw} target="_blank">{item.raw}</a>
)
case "email":
return (
<span key={index} className="text-primary">{item.raw}</span>
)
default:
return (
<span key={index}>{item.raw}</span>
)
}
});
//console.log(ret);
return (
<div>
{ret}
</div>
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

5
tests/PostContentTest.js

@ -0,0 +1,5 @@
var PostContent = require("../jsx/common/PostContent.js");
var sut = new PostContent();
console.log(sut.parseContent("asdas julian.steinwachs@fau.de fgh"));
Loading…
Cancel
Save