将 javascript 生成的 html 结构包装在具有多个端点实例的跨度中

Wrap a javascript generated html structure in a span with multiple instances of the end point

我一直在为 Velocity 的略微修改版本开发语法荧光笔。简而言之,我如何包装所有从 #inline() 开始并以相应的 #end 结束的内容,条件是可以有 0 无限的 if 语句,这些语句总是有一个 #end。如果您有任何问题,请告诉我。

可以在此 fiddle 上找到示例。请参阅下面的详细信息。

样本HTML

fiddle 显示 post javascript 下面 html 的修改版本。

<pre><code>
$javascript.push("/path/to/file")
#inline()
    $('.lazy.bg2').attr({'data-src':'/img_path2.jpg'}).css('background','red');
    #if($myVar == "hi")
       $('.someClass).hide()
    #elseif($myVar == "there")
       $('.lazy.bg1').attr({'data-src':'/img_path.jpg'})
    #else
       $('.lazy.bg3').attr({'data-src':'/img_path3.jpg'})
    #end
    $('.lazy.bg2 a').attr({'data-href':'/random-path2.htm'})
    $('.lazy.bg1 a').attr({'data-href':'/random-path.htm'})
#end

#if($test.method == "myVal")
  #set($foo = "swag")
#elseif($foo == "bar")
  #set($foo = "ballin")
#elseif($myObject.randomMethod().contains("myVal"))
  #set($foo = "weeee")
#else
  #set($foo = "sad days")
#end
#set($testVar = "Test value")
#parse("/path/to/file")</code></pre>

问题

因为 #end 有多个实例,所以我不确定如何让它与 #inline() 语句的结尾相匹配。主要问题是可能有无数个 if 语句,但 #inline() 总是有相应的 end 语句。所以我猜最好的方法是根据它是相同的 white-space 级别来匹配它。但是我不确定是否有更好的解决方案。我在 Gone Coding's post here 中找到了原来的 javascript。但是我稍微修改了它以更好地匹配我的实现。 请注意,我在之前的 jQuery 语句中将速度 class 添加到 <pre> 最终结果应该仅应用 <span>#inline().

里面的 jQuery

Javascript

$('pre.velocity code').each(function () {
    var open = false;
    var result = $();
    $(this).contents().each(function () {
        var $this = $(this);
        if ($this.text() == "#inline()" || $this.text() == "#end") {
            if (open) {
                result.wrapAll('<span class="velocity-inline-inner"></span>');
                open = false;
            } else {
                result = $();
                open = true;
            }
        } else {
            result = result.add($this)
        }
    });
    if (open) {
        result.wrapAll('<span class="velocity-inline-inner"></span>');
    }
});

已更新Javascript

我有以下可以计算白色 space 并选择与 #inline() 的白色 space 级别相匹配的 #end,但是我遇到了问题在 if 语句中仅将该子字符串转换为 html。

$('pre.velocity code').each(function(){
    var str = $(this).text();
  str = str.substring(str.indexOf("#inline()") + 10);
  textArray = str.split("\n");
  getInlineEnd(str,textArray);
});

function getInlineEnd(str,textArray) {
    for(var i = 0; i <= textArray.length; i++) {
      if(textArray[i].length === 4 && textArray[i] === "#end") {
        //convert textArray[i] to a html node and then wrap with a <span>
        break;
      }
    }
}

最终目标HTML

最终结果应如下所示。我已经在 #inline()#end 周围添加了一个跨度。

#inline()
<span class="velocity-inline-inner">
    $('.lazy.bg2').attr({'data-src':'/img_path2.jpg'}).css('background','red');
    #if($myVar == "hi")
       $('.someClass).hide()
    #elseif($myVar == "there")
       $('.lazy.bg1').attr({'data-src':'/img_path.jpg'})
    #else
       $('.lazy.bg3').attr({'data-src':'/img_path3.jpg'})
    #end
    $('.lazy.bg2 a').attr({'data-href':'/random-path2.htm'})
    $('.lazy.bg1 a').attr({'data-href':'/random-path.htm'})
</span>
#end

我相信一旦我让上面的代码正常工作,那么它应该适当地突出显示语法。不过,最终目标是让所有 jQuery 方法值突出显示。我将在不同的正则表达式下处理选择器。

我快速浏览了您的代码,但它有点像野兽。所以我会给出一个更一般的概述。首先,您需要能够(在某种程度上)解析输入的某些部分(为此,#inline、#if 和#end)。然后您需要计算将以#end 关闭的空缺数量。然后随着每个#end 出现,您检查列表中的最新开头。当你到达将关闭开头#inline 的#end 时,你将在那里关闭 </span>.

注意:我还没有测试代码。我不确切知道代码最初是如何工作的等等。我只是用它作为伪代码来展示你需要做什么。

$('pre.velocity code').each(function () {
    var open = [];
    var result = $();
    $(this).contents().each(function () {
        var $this = $(this);
        if ($this.text() == "#inline()" || $this.text().substr("#if(") === 0) {
            open.push($this);
        }
        else if ($this.text() == "#end") {
            var toClose = open.pop();
            if (toClose.text() === "#inline()") {
                // wrap from after #inline() to before #end
            }
        }
    });
});

我最后做了下面的 js 和 html 来包装 #inline() 和基于空白的相应 #end。这是 fiddle 呈现的 html。

    // Loop thorugh each of the velocity and see if there is an inline to syntax highlight.
    $('pre.velocity code').each(function(){
        var html = $(this).html();
        var str = html.toString(html); // convert html object to a string
        str = str.substring(str.indexOf("#inline()") + 17); // split the string so only take half of it.
        var textArray = str.split("\n"); // split the array on a new line
        var newHtml = getInlineEnd(textArray);
        regExVelocity($(this),str,newHtml); // replace the string
    });

    /**
     * Loops through the inline and finds the #end based on white-space
     * @param textArray
     * @type Array
     * @returns {string|*}
     */
    function getInlineEnd(textArray) {
        // loop through each line of the string.
        for(var i = 0; i <= textArray.length; i++) {
            if(i == 0) {
                // first line in #inline()
                textArray[i] = "<span class=\"velocity-inline-inner\">"+textArray[i]
            }
            if(undefined !== textArray[i] && textArray[i].length === 38 && textArray[i] === "<span class=\"velocity-end\">#end</span>") {
                // Found the end of the #inline() based on white-space. Update the text.
                textArray[i] = textArray[i].replace(textArray[i],'</span><span class="velocity-end">#end</span>');
                // break out of the for loop since we got the value
                break;
            }
        }
        // return the string and reformat it.
        return textArray.join('\n');
    }

    /**
     * Runs regex for velocity syntax highlighting uses .html() if you want to use the text then use regExText
     * @param selector
     * @param regex
     * @param output
     */
    function regExVelocity(selector, regex, output) {
        selector.html(function(i, html) {
            return( html.replace(regex, output));
        });
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre class="velocity"><code>
<span class="velocity-object">$javascript</span>.push(<span class="velocity-string">"/path/to/file"</span>)
<span class="velocity-inline">#inline()</span>
    $('.lazy.bg2').attr({'data-src':'/img_path2.jpg'}).css('background','red');
    <span class="velocity-if">#if(</span><span class="velocity-if-inner">$myVar == "hi"</span><span class="velocity-if">)</span>
    #elseif($myVar == "there")
          $('.lazy.bg1').attr({'data-src':'/img_path.jpg'})
    #else
          $('.lazy.bg3').attr({'data-src':'/img_path3.jpg'})
    <span class="velocity-end">#end</span>
    $('.lazy.bg1 a').attr({'data-href':'/random-path.htm'})
<span class="velocity-end">#end</span>
<span class="velocity-if">#if(</span><span class="velocity-if-inner"><span class="velocity-object">$test</span>.method == "myVal"</span><span class="velocity-if">)</span>
  <span class="velocity-set">#set(</span><span class="velocity-inner"><span class="velocity-variable">$foo </span><span class="equalSign">=</span> "swag"</span><span class="velocity-set">)</span>
#elseif($foo == "bar")
  <span class="velocity-set">#set(</span><span class="velocity-inner"><span class="velocity-variable">$foo </span><span class="equalSign">=</span> "ballin"</span><span class="velocity-set">)</span>
#elseif(<span class="velocity-object">$myObject</span>.randomMethod().contains(<span class="velocity-string">"myVal"</span>))
  <span class="velocity-set">#set(</span><span class="velocity-inner"><span class="velocity-variable">$foo </span><span class="equalSign">=</span> "weeee"</span><span class="velocity-set">)</span>
#else
  <span class="velocity-set">#set(</span><span class="velocity-inner"><span class="velocity-variable">$foo </span><span class="equalSign">=</span> "sad days"</span><span class="velocity-set">)</span>
<span class="velocity-end">#end</span>
<span class="velocity-set">#set(</span><span class="velocity-inner"><span class="velocity-variable">$testVar </span><span class="equalSign">=</span> "Test value"</span><span class="velocity-set">)</span>
<span class="velocity-parse">#parse(</span><span class="velocity-parse-path">"/path/to/file"</span><span class="velocity-parse">)</span></code></pre>