|
|
@@ -1,3 +1,5 @@
|
|
|
+"use strict";
|
|
|
+
|
|
|
/**
|
|
|
* replace all :emoji: codes with corresponding image
|
|
|
* @param {string} inputString
|
|
|
@@ -19,339 +21,332 @@ function formatEmojis(inputString) {
|
|
|
/** @type {function(string):string} */
|
|
|
var formatText = (function() {
|
|
|
/**
|
|
|
- * @constructor
|
|
|
- * @param {string} fullText
|
|
|
- */
|
|
|
- function MessagePart(fullText) {
|
|
|
- /** @type {Array.<MessagePart>} */
|
|
|
- this.subParts = [];
|
|
|
+ * @param {string} c
|
|
|
+ * @return {boolean}
|
|
|
+ **/
|
|
|
+ function isAlphadec(c) {
|
|
|
+ return ((c >= 'A' && c <= 'Z') ||
|
|
|
+ (c >= 'a' && c <= 'z') ||
|
|
|
+ (c >= '0' && c <= '9') ||
|
|
|
+ "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇߨøÅ寿œ".indexOf(c) !== -1);
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
+ /**
|
|
|
+ * @constructor
|
|
|
+ * @param {MsgBranch!} _parent
|
|
|
+ **/
|
|
|
+ function MsgTextLeaf(_parent) {
|
|
|
/** @type {string} */
|
|
|
this.text = "";
|
|
|
|
|
|
- /** @type {string} */
|
|
|
- this.fullText = fullText;
|
|
|
+ /** @const @type {MsgBranch} */
|
|
|
+ this._parent = _parent;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @constructor
|
|
|
+ * @param {MsgBranch|MsgTree} _parent
|
|
|
+ * @param {number} triggerIndex
|
|
|
+ * @param {string=} trigger
|
|
|
+ */
|
|
|
+ function MsgBranch(_parent, triggerIndex, trigger) {
|
|
|
+ /** @const @type {number} */
|
|
|
+ this.triggerIndex = triggerIndex;
|
|
|
+
|
|
|
+ /** @type {MsgBranch|MsgTextLeaf} */
|
|
|
+ this.lastNode = new MsgTextLeaf(this);
|
|
|
+
|
|
|
+ /** @type {Array<MsgBranch|MsgTextLeaf>} */
|
|
|
+ this.subNodes = [ this.lastNode ];
|
|
|
+
|
|
|
+ /** @const @type {string} */
|
|
|
+ this.trigger = trigger || '';
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
- this.italic = false;
|
|
|
+ this.isLink = this.trigger === '<';
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
- this.bold = false;
|
|
|
+ this.isBold = this.trigger === '*';
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
- this.strike = false;
|
|
|
+ this.isItalic = this.trigger === '_';
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
- this.code = false;
|
|
|
+ this.isStrike = this.trigger === '~' || this.trigger === '-';
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
- this.longCode = false;
|
|
|
+ this.isQuote = this.trigger === '>';
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
- this.quote = false;
|
|
|
- }
|
|
|
+ this.isEmoji = this.trigger === ':';
|
|
|
|
|
|
- var isAlphadec = function(c) {
|
|
|
- return ((c >= 'A' && c <= 'Z') ||
|
|
|
- (c >= 'a' && c <= 'z') ||
|
|
|
- (c >= '0' && c <= '9') ||
|
|
|
- "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇߨøÅ寿œ".indexOf(c) !== -1);
|
|
|
- }
|
|
|
- ,checkEnd = function(str, pos, c) {
|
|
|
- while (str[pos]) {
|
|
|
- if (isAlphadec(str[pos]) && str[pos] != c && str[pos +1] == c) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- pos++;
|
|
|
- }
|
|
|
- return false;
|
|
|
+ /** @type {boolean} */
|
|
|
+ this.isCode = this.trigger === '`';
|
|
|
+
|
|
|
+ /** @type {boolean} */
|
|
|
+ this.isCodeBlock = this.trigger === '```';
|
|
|
+
|
|
|
+ /** @type {boolean} */
|
|
|
+ this.isEol = this.trigger === '\n';
|
|
|
+
|
|
|
+ /** @const @type {MsgBranch|MsgTree} */
|
|
|
+ this._parent = _parent;
|
|
|
+
|
|
|
+ /** @type {boolean} */
|
|
|
+ this.terminated = false;
|
|
|
}
|
|
|
|
|
|
- MessagePart.prototype.isFinished = function() {
|
|
|
- var result = false;
|
|
|
- if (this.longCode)
|
|
|
- return this.text.substr(this.text.length -3) === '```';
|
|
|
- if (this.code)
|
|
|
- return this.text[this.text.length -1] === '`' && this.text.length > 1;
|
|
|
- if (this.bold)
|
|
|
- result |= this.text[this.text.length -1] === '*' && this.text.length > 1;
|
|
|
- if (this.strike)
|
|
|
- result |= this.text[this.text.length -1] === '~' && this.text.length > 1;
|
|
|
- if (this.italic)
|
|
|
- result |= this.text[this.text.length -1] === '_' && this.text.length > 1;
|
|
|
- if (this.quote)
|
|
|
- result |= this.text[this.text.length -1] === '\n';
|
|
|
- return result || this.isOnlyText();
|
|
|
+ /** @return {boolean} */
|
|
|
+ MsgBranch.prototype.checkIsBold = function() {
|
|
|
+ return (this.isBold && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsBold());
|
|
|
+ };
|
|
|
+
|
|
|
+ /** @return {boolean} */
|
|
|
+ MsgBranch.prototype.checkIsItalic = function() {
|
|
|
+ return (this.isItalic && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsItalic());
|
|
|
+ };
|
|
|
+
|
|
|
+ /** @return {boolean} */
|
|
|
+ MsgBranch.prototype.checkIsStrike = function() {
|
|
|
+ return (this.isStrike && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsStrike());
|
|
|
+ };
|
|
|
+
|
|
|
+ /** @return {boolean} */
|
|
|
+ MsgBranch.prototype.checkIsQuote = function() {
|
|
|
+ return (this.isQuote && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsQuote());
|
|
|
+ };
|
|
|
+
|
|
|
+ /** @return {boolean} */
|
|
|
+ MsgBranch.prototype.checkIsEmoji = function() {
|
|
|
+ return (this.isEmoji && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsEmoji());
|
|
|
};
|
|
|
|
|
|
- MessagePart.prototype.addChar = function(c) {
|
|
|
- this.text += c;
|
|
|
- return this;
|
|
|
+ /** @return {boolean} */
|
|
|
+ MsgBranch.prototype.checkIsCode = function() {
|
|
|
+ return (this.isCode && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsCode());
|
|
|
};
|
|
|
|
|
|
- MessagePart.prototype.isOnlyText = function() {
|
|
|
- return !this.italic &&
|
|
|
- !this.bold &&
|
|
|
- !this.strike &&
|
|
|
- !this.code &&
|
|
|
- !this.longCode &&
|
|
|
- !this.quote;
|
|
|
+ /** @return {boolean} */
|
|
|
+ MsgBranch.prototype.checkIsCodeBlock = function() {
|
|
|
+ return (this.isCodeBlock && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsCodeBlock());
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * @return {MessagePart}
|
|
|
+ * Check if this token closes the branch
|
|
|
+ * FIXME it is possible that an inner branch still lives
|
|
|
+ * In this case, finishWith should return the new leave
|
|
|
+ * Exemple: `_text *bold_ bold continue*` should be
|
|
|
+ * + root
|
|
|
+ * | italic
|
|
|
+ * | "text"
|
|
|
+ * | bold
|
|
|
+ * | "bold"
|
|
|
+ * | bold
|
|
|
+ * | "bold continue"
|
|
|
+ * and this function will return a "bold" branch
|
|
|
+ *
|
|
|
+ * @param {string} str
|
|
|
+ * @param {number} i
|
|
|
+ * @return {MsgTextLeaf|MsgBranch|null}
|
|
|
**/
|
|
|
- MessagePart.prototype.finalize = function() {
|
|
|
- if (this.longCode)
|
|
|
- this.text = this.text.substr(0, this.text.length -3);
|
|
|
- else if (this.code)
|
|
|
- this.text = this.text.substr(0, this.text.length -1);
|
|
|
- else if (this.bold)
|
|
|
- this.text = this.text.substr(0, this.text.length -1);
|
|
|
- else if (this.strike)
|
|
|
- this.text = this.text.substr(0, this.text.length -1);
|
|
|
- else if (this.italic)
|
|
|
- this.text = this.text.substr(0, this.text.length -1);
|
|
|
- else if (this.quote)
|
|
|
- this.text = this.text.substr(0, this.text.length -1);
|
|
|
- return this;
|
|
|
+ MsgBranch.prototype.finishWith = function(str, i) {
|
|
|
+ if (this.trigger === '<' && str[i] === '>') {
|
|
|
+ return new MsgTextLeaf(/** @type {MsgBranch!} */ (this._parent));
|
|
|
+ }
|
|
|
+ if (str.substr(i, this.trigger.length) === this.trigger) {
|
|
|
+ return new MsgTextLeaf(/** @type {MsgBranch!} */ (this._parent));
|
|
|
+ }
|
|
|
+ // FIXME
|
|
|
+ // check if branch terminates here
|
|
|
+ // If yes return a branche containing all unfinished children
|
|
|
+ // If yes but no children, return a new MsgTextLeaf
|
|
|
+ // If not return null
|
|
|
+ return null;
|
|
|
};
|
|
|
|
|
|
- MessagePart.prototype.cloneType = function() {
|
|
|
- var clone = new MessagePart(this.fullText);
|
|
|
- clone.italic = this.italic;
|
|
|
- clone.bold = this.bold;
|
|
|
- clone.strike = this.strike;
|
|
|
- clone.code = this.code;
|
|
|
- clone.longCode = this.longCode;
|
|
|
- clone.quote = this.quote;
|
|
|
- return clone;
|
|
|
+ /**
|
|
|
+ * Check if this token is compatible with this branch
|
|
|
+ * @param {string} str
|
|
|
+ * @param {number} i
|
|
|
+ * @return {boolean}
|
|
|
+ **/
|
|
|
+ MsgBranch.prototype.isAcceptable = function(str, i) {
|
|
|
+ if (this.isEmoji && (str[i] === ' ' || str[i] === '\t'))
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * @param {boolean=} skipFirst
|
|
|
+ * Check if str[i] is a trigger for a new node
|
|
|
+ * if true, return the trigger
|
|
|
+ * @param {string} str
|
|
|
+ * @param {number} i
|
|
|
+ * @return {string|null}
|
|
|
**/
|
|
|
- MessagePart.prototype.formatText = function(skipFirst) {
|
|
|
- var lastChild = new MessagePart(this.fullText);
|
|
|
-
|
|
|
- for (var i =skipFirst === true ? 1 : 0, textLength =this.fullText.length; i < textLength; i++) {
|
|
|
- //First, check if we have a ``` code block
|
|
|
- if (!lastChild.longCode && this.fullText.substr(i, 3) === '```') {
|
|
|
- this.subParts.push(lastChild.finalize());
|
|
|
- lastChild = new MessagePart(this.fullText.substr(i));
|
|
|
- lastChild.longCode = true;
|
|
|
- i += 2;
|
|
|
- } else if (!lastChild.longCode && !lastChild.code && this.fullText[i] === '`') {
|
|
|
- this.subParts.push(lastChild.finalize());
|
|
|
- lastChild = new MessagePart(this.fullText.substr(i));
|
|
|
- lastChild.code = true;
|
|
|
- } else if (!lastChild.longCode && !lastChild.italic && this.fullText[i] === '_') {
|
|
|
- this.subParts.push(lastChild.finalize());
|
|
|
- lastChild = new MessagePart(this.fullText.substr(i));
|
|
|
- lastChild.italic = true;
|
|
|
- } else if (!lastChild.longCode && !lastChild.bold && this.fullText[i] === '*') {
|
|
|
- this.subParts.push(lastChild.finalize());
|
|
|
- lastChild = new MessagePart(this.fullText.substr(i));
|
|
|
- lastChild.bold = true;
|
|
|
- } else if (!lastChild.longCode && !lastChild.strike && this.fullText[i] === '~') {
|
|
|
- this.subParts.push(lastChild.finalize());
|
|
|
- lastChild = new MessagePart(this.fullText.substr(i));
|
|
|
- lastChild.strike = true;
|
|
|
- } else {
|
|
|
- lastChild.addChar(this.fullText[i]);
|
|
|
- if (!lastChild.isOnlyText() && lastChild.isFinished()) {
|
|
|
- this.subParts.push(lastChild.finalize());
|
|
|
- lastChild = new MessagePart(this.fullText.substr(i +1));
|
|
|
- }
|
|
|
- }
|
|
|
+ MsgBranch.prototype.isNewToken = function(str, i) {
|
|
|
+ if (this.lastNode instanceof MsgTextLeaf) {
|
|
|
+ if (str.substr(i, 3) === '```')
|
|
|
+ return '```';
|
|
|
+ if ('`' === str[i])
|
|
|
+ return str[i];
|
|
|
+ if (['*', '~', '-', '_' ].indexOf(str[i]) !== -1 && (isAlphadec(str[i +1]) || ['*', '`', '~', '-', '_', ':', '<'].indexOf(str[i+1]) !== -1))
|
|
|
+ return str[i];
|
|
|
+ if ([':', '<'].indexOf(str[i]) !== -1 && isAlphadec(str[i +1]))
|
|
|
+ return str[i];
|
|
|
}
|
|
|
- if (lastChild.isFinished()) {
|
|
|
- this.subParts.push(lastChild.finalize());
|
|
|
+ return null;
|
|
|
+ };
|
|
|
+
|
|
|
+ /** @return {number} */
|
|
|
+ MsgTextLeaf.prototype.addChar = function(str, i) {
|
|
|
+ this.text += str[i];
|
|
|
+ return 1;
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Parse next char
|
|
|
+ * @param {string} str
|
|
|
+ * @param {number} i
|
|
|
+ * @return {number}
|
|
|
+ **/
|
|
|
+ MsgBranch.prototype.addChar = function(str, i) {
|
|
|
+ var isFinished = this.lastNode.finishWith ? this.lastNode.finishWith(str, i) : null;
|
|
|
+
|
|
|
+ if (isFinished) {
|
|
|
+ // terminated token, move pointer to a new leaf
|
|
|
+ this.lastNode.terminated = true;
|
|
|
+ this.lastNode = isFinished;
|
|
|
+ this.subNodes.push(isFinished);
|
|
|
+ return 1;
|
|
|
} else {
|
|
|
- // Unterminated sequence
|
|
|
- var textChild = new MessagePart(lastChild.fullText[0]);
|
|
|
- textChild.text = lastChild.fullText[0];
|
|
|
- this.subParts.push(textChild.finalize());
|
|
|
- lastChild.formatText(true);
|
|
|
- this.subParts.push(lastChild);
|
|
|
- }
|
|
|
- /*
|
|
|
- var _msgContent = ""
|
|
|
- ,currentMods = {}
|
|
|
- ,quote = false
|
|
|
- ,i =0
|
|
|
-
|
|
|
- var msgContent = this.text.replace(new RegExp('<([@#]?)([^>]*)>', 'g'),
|
|
|
- function(matched, type, entity) {
|
|
|
- var sub = entity.split('|');
|
|
|
-
|
|
|
- if (type === '@') {
|
|
|
- if (!sub[1]) {
|
|
|
- var user = SLACK.context.users[sub[0]];
|
|
|
- sub[1] = user ? ('@' +user.name) : locale.unknownMember;
|
|
|
- } else if ('@' !== sub[1][0]) {
|
|
|
- sub[1] = '@' +sub[1];
|
|
|
- }
|
|
|
- sub[0] = '#' +sub[0];
|
|
|
- sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkuser;
|
|
|
- } else if (type === '#') {
|
|
|
- if (!sub[1]) {
|
|
|
- var chan = SLACK.context.channels[sub[0]];
|
|
|
- sub[1] = chan ? ('#' +chan.name) : locale.unknownChannel;
|
|
|
- } else if ('#' !== sub[1][0]) {
|
|
|
- sub[1] = '#' +sub[1];
|
|
|
- }
|
|
|
- sub[0] = '#' +sub[0];
|
|
|
- sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkchan;
|
|
|
- } else if (sub[0].indexOf("://") !== -1) {
|
|
|
- if (!sub[1])
|
|
|
- sub[1] = sub[0];
|
|
|
- sub[2] = R.klass.msg.link;
|
|
|
+ if (this.lastNode instanceof MsgTextLeaf || this.lastNode.isAcceptable(str, i)) {
|
|
|
+ var isNewToken = this.isNewToken(str, i);
|
|
|
+ if (isNewToken) {
|
|
|
+ this.lastNode = new MsgBranch(this, i, isNewToken);
|
|
|
+ this.subNodes.push(this.lastNode);
|
|
|
+ return this.lastNode.trigger.length;
|
|
|
} else {
|
|
|
- return matched;
|
|
|
+ return this.lastNode.addChar(str, i);
|
|
|
}
|
|
|
- return '<a href="' +sub[0] +'" class="' +sub[2] +'"' +(!type ? ' target="_blank"' : '') +'>' +sub[1] +'</a>';
|
|
|
- });
|
|
|
- msgContent = formatEmojis(msgContent);
|
|
|
- var msgLength = msgContent.length,
|
|
|
- appendMod = function(mods) {
|
|
|
- if (!Object.keys(currentMods).length)
|
|
|
- return "";
|
|
|
- return '<span class="' +Object.keys(mods).join(' ') +'">';
|
|
|
- };
|
|
|
-
|
|
|
- // Skip trailing
|
|
|
- while (i < msgLength && (msgContent[i] === ' ' || msgContent[i] === '\t'))
|
|
|
- i++;
|
|
|
- if (msgContent.substr(i, 4) === '>') {
|
|
|
- quote = true;
|
|
|
- i += 4;
|
|
|
- }
|
|
|
- for (; i < msgLength; i++) {
|
|
|
- var c = msgContent[i];
|
|
|
-
|
|
|
- if (c === '<') {
|
|
|
- do {
|
|
|
- _msgContent += msgContent[i++];
|
|
|
- } while (msgContent[i -1] !== '>' && msgContent[i]);
|
|
|
- i--;
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (!(currentMods[R.klass.msg.style.bold]) && c === '*' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
|
|
|
- if (Object.keys(currentMods).length)
|
|
|
- _msgContent += '</span>';
|
|
|
- currentMods[R.klass.msg.style.bold] = true;
|
|
|
- _msgContent += appendMod(currentMods);
|
|
|
- } else if (!(currentMods[R.klass.msg.style.strike]) && c === '~' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
|
|
|
- if (Object.keys(currentMods).length)
|
|
|
- _msgContent += '</span>';
|
|
|
- currentMods[R.klass.msg.style.strike] = true;
|
|
|
- _msgContent += appendMod(currentMods);
|
|
|
- } else if (!(currentMods[R.klass.msg.style.code]) && c === '`' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
|
|
|
- if (Object.keys(currentMods).length)
|
|
|
- _msgContent += '</span>';
|
|
|
- currentMods[R.klass.msg.style.code] = true;
|
|
|
- _msgContent += appendMod(currentMods);
|
|
|
- } else if (!(currentMods[R.klass.msg.style.italic]) && c === '_' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
|
|
|
- if (Object.keys(currentMods).length)
|
|
|
- _msgContent += '</span>';
|
|
|
- currentMods[R.klass.msg.style.italic] = true;
|
|
|
- _msgContent += appendMod(currentMods);
|
|
|
} else {
|
|
|
- var finalFound = false;
|
|
|
- _msgContent += c;
|
|
|
-
|
|
|
- do {
|
|
|
- if ((currentMods[R.klass.msg.style.bold]) && c !== '*' && msgContent[i +1] === '*') {
|
|
|
- delete currentMods[R.klass.msg.style.bold];
|
|
|
- finalFound = true;
|
|
|
- } else if ((currentMods[R.klass.msg.style.strike]) && c !== '~' && msgContent[i +1] === '~') {
|
|
|
- delete currentMods[R.klass.msg.style.strike];
|
|
|
- finalFound = true;
|
|
|
- } else if ((currentMods[R.klass.msg.style.code]) && c !== '`' && msgContent[i +1] === '`') {
|
|
|
- delete currentMods[R.klass.msg.style.code];
|
|
|
- finalFound = true;
|
|
|
- } else if ((currentMods[R.klass.msg.style.italic]) && c !== '_' && msgContent[i +1] === '_') {
|
|
|
- delete currentMods[R.klass.msg.style.italic];
|
|
|
- finalFound = true;
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
- c = msgContent[++i];
|
|
|
- } while (i < msgLength);
|
|
|
- if (finalFound)
|
|
|
- _msgContent += '</span>' +appendMod(currentMods);
|
|
|
+ // last branch child is not compatible with this token.
|
|
|
+ // So, lastBranch is not a branch
|
|
|
+ // Add a new "escaped" node to replace trigger
|
|
|
+ var textNode = new MsgTextLeaf(this);
|
|
|
+ textNode.addChar(str, this.lastNode.trigger);
|
|
|
+ this.subNodes.pop();
|
|
|
+ this.subNodes.push(textNode);
|
|
|
+
|
|
|
+ // new root
|
|
|
+ var newBranch = new MsgBranch(this, this.lastNode.triggerIndex +1);
|
|
|
+ for (var charIndex = this.lastNode.triggerIndex +1; charIndex <= i;)
|
|
|
+ charIndex += newBranch.addChar(str, charIndex);
|
|
|
+ this.lastNode = this.subNodes[this.subNodes.length -1];
|
|
|
+ return 1;
|
|
|
}
|
|
|
}
|
|
|
- if (!isObjectEmpty(currentMods)) {
|
|
|
- // Should not append
|
|
|
- console.warn("formatter warning");
|
|
|
- _msgContent += '</span>';
|
|
|
- }
|
|
|
- /*
|
|
|
- if (quote)
|
|
|
- msgContents[msgContentIndex] = '<span class="' +R.klass.msg.style.quote +'">' +_msgContent +'</span>';
|
|
|
- else
|
|
|
- msgContents[msgContentIndex] = _msgContent;
|
|
|
- */
|
|
|
};
|
|
|
|
|
|
- MessagePart.prototype.getOuterClass = function() {
|
|
|
- var classList = '';
|
|
|
-
|
|
|
- if (this.longCode) {
|
|
|
- classList += ' ' +R.klass.msg.style.longCode;
|
|
|
- }
|
|
|
- if (this.bold) {
|
|
|
- classList += ' ' +R.klass.msg.style.bold;
|
|
|
- }
|
|
|
- if (this.code) {
|
|
|
- classList += ' ' +R.klass.msg.style.code;
|
|
|
- }
|
|
|
- if (this.italic) {
|
|
|
- classList += ' ' +R.klass.msg.style.italic;
|
|
|
- }
|
|
|
- if (this.strike) {
|
|
|
- classList += ' ' +R.klass.msg.style.strike;
|
|
|
- }
|
|
|
- if (this.quote) {
|
|
|
- classList += ' ' +R.klass.msg.style.quote;
|
|
|
- }
|
|
|
- return classList;
|
|
|
+ /**
|
|
|
+ * @return {boolean} true if still contains stuff
|
|
|
+ **/
|
|
|
+ MsgBranch.prototype.prune = function() {
|
|
|
+ var branches = [];
|
|
|
+ this.subNodes.forEach(function(i) {
|
|
|
+ if (i instanceof MsgTextLeaf) {
|
|
|
+ if (i.text !== '')
|
|
|
+ branches.push(i);
|
|
|
+ } else if (i.prune()) {
|
|
|
+ branches.push(i);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.subNodes = branches;
|
|
|
+ this.lastNode = branches[branches.length -1];
|
|
|
+ return !!this.subNodes.length;
|
|
|
};
|
|
|
|
|
|
- /** @return {string} */
|
|
|
- MessagePart.prototype.getInnerHTML = function() {
|
|
|
- return this.text
|
|
|
- .replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ' ') // replace trailing spaces par non-secable spaces
|
|
|
- .replace(/\n/g, '<br/>');
|
|
|
+ /**
|
|
|
+ * @return {string}
|
|
|
+ **/
|
|
|
+ MsgTextLeaf.prototype.innerHTML = function() {
|
|
|
+ return this.text;
|
|
|
};
|
|
|
|
|
|
- /** @return {string} */
|
|
|
- MessagePart.prototype.toHTML = function() {
|
|
|
- if (this.subParts.length) {
|
|
|
- var result = "";
|
|
|
- this.subParts.forEach(function(part) {
|
|
|
- result += part.toHTML();
|
|
|
- });
|
|
|
- return result;
|
|
|
+ /**
|
|
|
+ * @return {string}
|
|
|
+ **/
|
|
|
+ MsgTextLeaf.prototype.outerHTML = function() {
|
|
|
+ var tagName = 'span'
|
|
|
+ ,classList = [];
|
|
|
+
|
|
|
+ if (this._parent.checkIsCodeBlock()) {
|
|
|
+ classList.push('codeblock');
|
|
|
+ } else if (this._parent.checkIsCode()) {
|
|
|
+ classList.push('code');
|
|
|
} else {
|
|
|
- if (this.isOnlyText() && !this.text.length)
|
|
|
- return "";
|
|
|
- return '<span class="' +this.getOuterClass() +'">' +this.getInnerHTML() +'</span>';
|
|
|
+ if (this.isLink)
|
|
|
+ tagName = 'a';
|
|
|
+ if (this._parent.checkIsBold())
|
|
|
+ classList.push('bold');
|
|
|
+ if (this._parent.checkIsItalic())
|
|
|
+ classList.push('italic');
|
|
|
+ if (this._parent.checkIsStrike())
|
|
|
+ classList.push('strike');
|
|
|
+ if (this._parent.checkIsQuote()) // FIXME nope.
|
|
|
+ classList.push('quote');
|
|
|
+ if (this._parent.checkIsEmoji())
|
|
|
+ classList.push('emoji');
|
|
|
}
|
|
|
+ return '<' +tagName +(classList.length ? ' class="' +classList.join(' ') +'"' : '') +'>' +this.innerHTML() +'</' +tagName +'>';
|
|
|
+ };
|
|
|
+
|
|
|
+ MsgBranch.prototype.outerHTML = function() {
|
|
|
+ var html = "";
|
|
|
+
|
|
|
+ this.subNodes.forEach(function(node) {
|
|
|
+ html += node.outerHTML();
|
|
|
+ });
|
|
|
+ return html;
|
|
|
+ };
|
|
|
+
|
|
|
+ MsgBranch.prototype.eof = function() {
|
|
|
+ // FIXME check for any unterminated branches to reinterpret them
|
|
|
};
|
|
|
|
|
|
- return function (fullText) {
|
|
|
- // trivial empty string
|
|
|
- var text = (fullText || "").trim();
|
|
|
- if (text == "")
|
|
|
- return "";
|
|
|
+ /**
|
|
|
+ * @constructor
|
|
|
+ * @param {string} text
|
|
|
+ */
|
|
|
+ function MsgTree(text) {
|
|
|
+ /** @const @type {string} */
|
|
|
+ this.text = text;
|
|
|
|
|
|
- var msgContent = new MessagePart(text);
|
|
|
- msgContent.formatText();
|
|
|
- console.log(msgContent);
|
|
|
- return msgContent.toHTML();
|
|
|
+ /** @type {MsgBranch|null} */
|
|
|
+ this.root = null;
|
|
|
}
|
|
|
+
|
|
|
+ MsgTree.prototype.parse = function() {
|
|
|
+ this.root = new MsgBranch(this, 0);
|
|
|
+ for (var i =0, textLen = this.text.length; i < textLen;)
|
|
|
+ i += this.root.addChar(this.text, i);
|
|
|
+ this.root.eof();
|
|
|
+ if (!this.root.prune()) {
|
|
|
+ this.root = null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return {string}
|
|
|
+ **/
|
|
|
+ MsgTree.prototype.toHTML = function() {
|
|
|
+ return this.root ? this.root.outerHTML() : "";
|
|
|
+ };
|
|
|
+
|
|
|
+ return function(text) {
|
|
|
+ var root = new MsgTree(text);
|
|
|
+ root.parse();
|
|
|
+ return (root.toHTML());
|
|
|
+ };
|
|
|
})();
|
|
|
|