4

我的任务是实现自己的标签,使文本加粗、下划线或删除线以及任何嵌套。像一个

*bold text* _underlinetext_ -strikethrough-

我还需要制作自己的超链接,例如

[link | http://stackoverflow.com]

出现的第一个想法 - 它应用正则表达式。编码:

View.prototype.parseText = function(text) {

text = text.replace(/\*([^\*]+)\*/g, '<b>$1</b>');
text = text.replace(/\_([^\_]+)\_/g, '<u>$1</u>');
text = text.replace(/\-([^\-]+)\-/g, '<s>$1</s>');
text = text.replace(/\[([^\|].+)\|(.+)\]/g, '<a href="$2">$1</a>');

return text;};

它正在工作,但我需要可扩展性。正则表达式不是一个好主意,因为它是硬编码的。如何使用有限状态机(或任何 jQuery 插件)实现该任务?如果有任何帮助,我将不胜感激。

4

3 回答 3

3

无论您做什么,要扩展您的标记系统,您都需要: 1. 定义标记,以及 2. 将其替换为等效的 HTML。

即使您用 js 编写自己的解析器,归根结底,您仍然需要执行上述 2 个步骤,因此它并不比您现在拥有的更具可扩展性。

正则表达式是这项工作的工具,除非您有其他要求(即仅在这样的元素内替换,但在另一个元素中执行其他操作,这需要解析)。

您可以将您的正则表达式调用包装在一个函数中,并在需要扩展该功能时简单地将正则表达式替换添加到该函数中。如果需要在多个页面中,将其添加到外部 js 文件中。

function formatUserContent(text)
{
  text = text.replace(/\*([^\*]+)\*/g, '<b>$1</b>');
  text = text.replace(/\_([^\_]+)\_/g, '<u>$1</u>');
  text = text.replace(/\-([^\-]+)\-/g, '<s>$1</s>');
  text = text.replace(/\[([^\|].+)\|(.+)\]/g, '<a href="$2">$1</a>');
  return text;
}

完成后,扩展功能就像添加一样简单

text = text.replace(/\+([^\-]+)\+/g, '<em>$1</em>');

在函数体中。我怀疑推出自己的有限状态机会更容易扩展,恰恰相反。

花几个小时在有限状态机上,希望它可以在未来某个未知的时间节省几分钟,这不是一个好的投资……当然,除非你想找个借口来写一个有限状态机,在这种情况下, 前进。

作为旁注,我建议让您的正则表达式更加简单。

text = text.replace(/\[([^\|].+)\|\s*(http://.+)\]/g, '<a href="$2">$1</a>');

(除非您有可以为用户完成工作的 UI 元素)

于 2012-12-22T16:07:48.360 回答
3

我可以建议你以下实现http://jsfiddle.net/NwRCm/5/

它使用状态设计模式(由于 JavaScript 和目的而稍作修改)。从表面上看,所有状态都是用正则表达式实现的,但在我看来,这是最有效的方式。

/* View definition */

function View(container) {
    this.container = container;
    this._parsers = [];
    this._currentState = 0;
};

View.prototype.parse = function(text) {

    var self = this;
    this._parsers.forEach(function (e) {
        self._parse(e);
    });

    return this.container.innerHTML;

};

View.prototype._parse = function (parser) {
    var text = parser.parse(this.container.innerHTML);
    this.container.innerHTML = text;
    return text;
};

View.prototype.nextState = function () {
    if (this._currentState < this._parsers.length) {
        return this._parse(this._parsers[this._currentState++]);
    }
    return null;
};

View.prototype.addParser = function (parser) {
    if (parser instanceof Parser) {
        return this._parsers.push(parser);
    } else {
        throw 'The parser you\'re trying to add is not an instance of Parser';
    }
};
/* end of the View definition */

/* Simulation of interface */
function Parser() {};

Parser.prototype.parse = function () {
    throw 'Not implemented!';
};

/* Implementation of bold parser */
function BoldParser() {};

BoldParser.prototype = new Parser();

BoldParser.prototype.parse = function (text) {
    text = text.replace(/\*([^\*]+)\*/g, '<b>$1</b>');
    return text;
};

/* Implementation of underline parser */
function UnderlineParser() {};

UnderlineParser.prototype = new Parser();

UnderlineParser.prototype.parse = function (text) {
    text = text.replace(/\_([^\_]+)\_/g, '<u>$1</u>');
    return text;
};

/* Link parser */
function LinkParser() {};

LinkParser.prototype = new Parser();

LinkParser.prototype.parse = function (text) {
    text = text.replace(/\[([^\|].+)\|(.+)\]/g, '<a href="$2">$1</a>');
    return text;
};


var v = new View(document.getElementById('container'));
v.addParser(new UnderlineParser());
v.addParser(new BoldParser());
v.addParser(new LinkParser());
v.nextState();
v.nextState();
v.nextState();

​让我更深入地了解一下实现。首先我们有一个基“类”(构造函数)视图。每个视图都有它的基础container和解析器列表,它还记得接下来应该应用哪个解析器。

之后,我们有了“抽象类”(原型中带有引发异常的方法的构造函数)Parser,它定义了一个parse必须由每个解析器实现的方法。

之后,我们只需定义不同的具体解析器并将它们添加到视图中。我们可以一个一个地传递状态(View's nextState)或在单个方法调用(View's parse)中传递所有状态。我们可以动态添加新的解析器。

可以批准的事情是包括用于管理解析器的享元工厂。

在实现不同的模式(例如模板方法)时,使用“抽象”构造函数的方法也非常有用。

  • 由于所有这些构造函数和对象的定义,编辑可能会有一些开销。一切都可以通过回调来完成,即每个状态都是不同的函数。我使用这种方法是因为我一直在寻找最容易理解、从语言特定功能中清晰的答案。我希望我做到了。
于 2012-12-22T16:16:03.380 回答
1

也许您想使用现有的库,例如http://www.showdown.im/上的 Markdown 库

如果您更喜欢自己编写,那么我建议您查看源代码以了解它的解析方式(可能还有其他语言的 Markdown 处理器的源代码)。给你的一些建议:

  • 使用 jQuery 操作标记
  • 不要使用正则表达式来解析语言。当标记元素混合在一起时,您会遇到问题。
于 2012-12-22T15:36:37.107 回答