将 Minecraft 颜色代码翻译成 HTML

Translating Minecraft Color Codes into HTML

最近,我正在尝试将 Minecraft MOTD 转换为 Plain HTML。 Minecraft MOTD 是基本文本,用 Minecraft 颜色代码表示颜色变化。

示例:“§d§l枢纽§8:§6§oO_O §a§l[变异星期一]”

翻译:<span style='color:pink'><b>The Nexus</b></span><span style='color:gray'>:</span>...

等等...所以基本上,有颜色代码,可以改变文本的颜色,还有格式代码,可以改变文本的格式。

现在,因为 Minecraft 颜色代码没有说明颜色代码在哪里结束,所以我不知道如何将其转换为 HTML。

我做到了一半,将所有颜色代码替换为 </span><span style='color:newcolor'> 并将所有格式代码替换为 </var><var style='text-decoration:blah'>

但是这没有用,因为当你在格式代码之前有一个颜色代码时,它会变成 <span></var><var></span>

所以问题是,我该怎么做?查找颜色代码的结束位置等等。

Minecraft 的颜色代码可在此处获得:http://ess.khhq.net/mc/

由于我的代码太大,我把它发布在 JSFiddle 上。

到目前为止我的代码:https://jsfiddle.net/thwe0yek/1/(第一个很好,第二个搞砸了..)

这是一种方法。

这是如何工作的简要说明:

  • 它在字符串中查找所有代码,并使用与其匹配的正则表达式(参见 parseStyle 函数的第一行)
  • 它将这些代码在字符串中的位置的索引推入一个数组,并用双 \x00 (空字符)替换每个代码,以处理后面部分的代码重复字符串(参见第一个 for 循环)。
  • 它根据一个代码的开头和另一个代码的开头将字符串分成多个部分,这样每个部分都可以根据自己的规则单独设置样式。
  • 同样在第二个循环中,字符串被发送到 applyCode 函数,该函数在 styleMap 的帮助下应用给它的代码(通过数组)。此函数为给定的每个字符串创建一个 span 元素。如果找到 k 规则,它还会应用 obfuscate 函数。
  • obfuscate 函数接受一个字符串,获取它的长度,并为字符串中的每个字符生成一个随机字符。它在一个时间间隔内逐个字符执行此操作。它选择的字符范围可以通过更改 replaceRand 函数中的 randInt 值来修改。
  • 最后,脚本 returns 一个具有两个属性的对象:一个是 parsed 其中 returns 一个 DOM 元素与 'obfuscators' 运行,另一个是raw,其中returns跨度元素为HTML字符串(只是parsed的内部HTML)。

调用mineParse(string)即可使用,例如:

var result = mineParse('§6Hello');
document.body.appendChild(result.parsed);

现在支持将换行符转换为 <br> 标记、§r 重置代码以及以任意顺序应用多个代码。

不过,我确信脚本可以进一步简化。另外,我确定它有错误,你可以随意修改它。

http://jsfiddle.net/fku9gsax/12/

(function () {

    'use strict';

    var currId = 0,
        obfuscators = {},
        alreadyParsed = [],
        styleMap = {
            '§0': 'color:#000000',
            '§1': 'color:#0000AA',
            '§2': 'color:#00AA00',
            '§3': 'color:#00AAAA',
            '§4': 'color:#AA0000',
            '§5': 'color:#AA00AA',
            '§6': 'color:#FFAA00',
            '§7': 'color:#AAAAAA',
            '§8': 'color:#555555',
            '§9': 'color:#5555FF',
            '§a': 'color:#55FF55',
            '§b': 'color:#55FFFF',
            '§c': 'color:#FF5555',
            '§d': 'color:#FF55FF',
            '§e': 'color:#FFFF55',
            '§f': 'color:#FFFFFF',
            '§l': 'font-weight:bold',
            '§m': 'text-decoration:line-through',
            '§n': 'text-decoration:underline',
            '§o': 'font-style:italic'
        };

    function obfuscate(elem, string) {
        var multiMagic,
            currNode,
            listLen,
            nodeI;

        function randInt(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }

        function replaceRand(string, i) {
            var randChar = String.fromCharCode(randInt(64, 95));
            return string.substr(0, i) + randChar + string.substr(i + 1, string.length);
        }

        function initMagic(el, str) {
            var i = 0,
                obsStr = str || el.innerHTML,
                strLen = obsStr.length;
            if(!strLen) return;
            obfuscators[currId].push(
                window.setInterval(function () {
                    if (i >= strLen) i = 0;
                    obsStr = replaceRand(obsStr, i);
                    el.innerHTML = obsStr;
                    i++;
                }, 0)
            );
        }

        if (string.indexOf('<br>') > -1) {
            elem.innerHTML = string;
            listLen = elem.childNodes.length;
            for (nodeI = 0; nodeI < listLen; nodeI++) {
                currNode = elem.childNodes[nodeI];
                if (currNode.nodeType === 3) {
                    multiMagic = document.createElement('span');
                    multiMagic.innerHTML = currNode.nodeValue;
                    elem.replaceChild(multiMagic, currNode);
                    initMagic(multiMagic);
                }
            }
        } else {
            initMagic(elem, string);
        }
    }

    function applyCode(string, codes) {
        var elem = document.createElement('span'),
            obfuscated = false;

        string = string.replace(/\x00/g, '');

        codes.forEach(function (code) {
            elem.style.cssText += styleMap[code] + ';';
            if (code === '§k') {
                obfuscate(elem, string);
                obfuscated = true;
            }
        });

        if (!obfuscated) elem.innerHTML = string;

        return elem;
    }

    function parseStyle(string) {
        var finalPre = document.createElement('pre'),
            codes = string.match(/§.{1}/g) || [],
            codesLen = codes.length,
            indexes = [],
            indexDelta,
            apply = [],
            strSlice,
            i;

        if (!obfuscators[currId]) obfuscators[currId] = [];

        string = string.replace(/\n|\n/g, '<br>');

        for (i = 0; i < codesLen; i++) {
            indexes.push(string.indexOf(codes[i]));
            string = string.replace(codes[i], '\x00\x00');
        }

        if (indexes[0] !== 0) {
            finalPre.appendChild(applyCode(string.substring(0, indexes[0]), []));
        }

        for (i = 0; i < codesLen; i++) {
            indexDelta = indexes[i + 1] - indexes[i];
            if (indexDelta === 2) {
                while (indexDelta === 2) {
                    apply.push(codes[i]);
                    i++;
                    indexDelta = indexes[i + 1] - indexes[i];
                }
                apply.push(codes[i]);
            } else {
                apply.push(codes[i]);
            }
            if (apply.lastIndexOf('§r') > -1) {
                apply = apply.slice(apply.lastIndexOf('§r') + 1);
            }
            strSlice = string.substring(indexes[i], indexes[i + 1]);
            finalPre.appendChild(applyCode(strSlice, apply));
        }

        return finalPre;
    }

    function clearObfuscators(id) {
        obfuscators[id].forEach(function (interval) {
            clearInterval(interval);
        });
        alreadyParsed[id] = [];
        obfuscators[id] = [];
    }

    window.mineParse = function initParser(input) {
        var parsed,
            i = currId;
        if (i > 0) {
            while (i--) {
                if (alreadyParsed[i].nodeType) {
                    if (!document.contains(alreadyParsed[i])) {
                        clearObfuscators(i);
                    }
                }
            }
        }
        parsed = parseStyle(input);
        alreadyParsed.push(parsed);
        currId++;
        return {
            parsed: parsed,
            raw: parsed.innerHTML
        };
    };

}());
@media (max-width: 850px) {
   
   div#inBetween {
        width: 100%;
        box-sizing: border-box;
        padding: 0.05em;
    }
    #inBetween:before {
        display: inline-block;
        font-family:'FontAwesome';
        content:"\f107";
        animation: bounceDown 2s infinite;
    }
}
@media (min-width: 850px) {
    #inBetween:before {
        display: inline-block;
        font-family:'FontAwesome';
        content:"\f105";
        animation: bounceRight 2s infinite;
    }
}
html, body {
    height: 100%;
    width:  100%;
    margin: 0;
    padding: 0;
}
button::-moz-focus-inner {
    border: 0;
}
body {
    text-align: center;
    background: #050A0E;
    font-family:'Open Sans', sans-serif;
    overflow-x: hidden;
}
#mainContainer {
  padding:  5em 0 5em 0;
}
h1 {
    font-weight: bold;
    color: #5EB4E7;
    text-shadow: 0 0 50px #5EB4E7;
    padding: 1em;
    margin:  0;
    animation: fadeIn 2s ease-in-out;
}
#inputContainer, #outputContainer {
    display: inline-block;
    padding: 1em;
    width: 20em;
    height: 21em;
    background: #152E3D;
    box-shadow: 0 0 4px #5EB4E7;
    vertical-align: middle;
    animation: scaleIn 1s ease-in-out;
}
#inBetween {
    display: inline-block;
    vertical-align: middle;
    color: #5EB4E7;
    font-size: 4em;
    padding: 0.5em;
    animation: scaleIn 1s ease-in-out;
}
#outputContainer {
    display: inline-block;
    padding: 1em;
    vertical-align: top;
    border-radius: 0 5px 5px 0;
}
#inputContainer {
    border-radius: 5px 0 0 5px;
}
#input {
    box-sizing: border-box;
    padding: 1em;
    width: 20em;
    max-width: 20em;
    height: 20em;
    max-height: 20em;
}
#output {
    background: rgba(255, 255, 255, 0.97);
    text-align: left;
    animation: bounceInRight 1s;
}
#output pre {
    margin-top: 0;
    padding: 1em;
    border: 4px solid #050A0E;
    overflow: auto;
    max-height: 20em;
    animation: bounceInRight 0.5s;
}
#input, #parse, #parse:active, #parse:focus {
    padding: 1em;
    width: 20em;
    background: #050A0E;
    color: #5EB4E7;
    outline: 0;
    outline-style: none;
    outline-width: 0;
    box-shadow: none;
    border: none;
}
#parse {
    transition: all 225ms ease-in-out;
    border-radius: 0 0 5px 5px;
}
#parse:hover {
    color: rgba(255, 255, 255, 0.8);
    background: #5EB4E7;
    cursor: pointer;
    box-shadow: 0 0 25px #5EB4E7;
}
.containerLabel {
    position: relative;
    top: -0.5em;
    left: -1.5em;
    color: #5EB4E7;
    text-align: left;
    margin-left: 1.5em;
    font-size: 80%;
    font-weight: bold;
    opacity: 0.85;
    margin-bottom: 0.25em;
    text-shadow: 0 0 40px #5EB4E7;
}
.bounceInRight {
    animation: bounceInRight 1s;
}
@keyframes bounceRight {
    0%, 20%, 50%, 80%, 100% {
        transform: translateX(0);
    }
    40% {
        transform: translateX(-10px);
    }
    60% {
        transform: translateX(-5px);
    }
}
@keyframes bounceDown {
    0%, 20%, 50%, 80%, 100% {
        transform: translateY(0);
    }
    40% {
        transform: translateY(-10px);
    }
    60% {
        transform: translateY(-5px);
    }
}
@keyframes bounceInRight {
    0% {
        opacity: 0;
        transform: translateX(2000px);
    }
    60% {
        opacity: 1;
        transform: translateX(-30px);
    }
    80% {
        transform: translateX(10px);
    }
    100% {
        transform: translateX(0);
    }
}
@keyframes flipInY {
    0% {
        transform: perspective(400px) rotateY(90deg);
        opacity: 0;
    }
    40% {
        transform: perspective(400px) rotateY(-10deg);
    }
    70% {
        transform: perspective(400px) rotateY(10deg);
    }
    100% {
        transform: perspective(400px) rotateY(0deg);
        opacity: 1;
    }
}
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1
    }
}
@keyframes scaleIn {
    0% {
        transform: scale(0);
    }
    80% {
        transform: scale(1.05);
    }
    100% {
        transform: scale(1);
    }
}
<link rel="stylesheet" href="style/style.css">
<script src="js/mine-parse.js"></script>
<link href='http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div id="mainContainer">
  <h1> <i class="fa fa-code"></i> MineParse </h1>
  <div id="inputContainer">
      <div class="containerLabel">
          <i class="fa fa-pencil-square"></i>
          Input
      </div>
      <textarea id="input">
§nMinecraft Formatting

§r§00 §11 §22 §33
§44 §55 §66 §77
§88 §99 §aa §bb
§cc §dd §ee §ff
    
§r§0k §kMinecraft
§rl §lMinecraft
§rm §mMinecraft
§rn §nMinecraft
§ro §oMinecraft
§rr §rMinecraft
      </textarea>
      <button id="parse">
          <i class="fa fa-angle-right"></i>
          Parse
      </button>
  </div>
  <div id="inBetween"></div>
  <div id="outputContainer">
      <div class="containerLabel">
          <i class="fa fa-eye"></i>
          Output <span id="parseTime"></span>
      </div>
      <div id="output"></div>
  </div>
  <script>
      (function () {
          'use strict';
          var input = document.getElementById('input'),
              output = document.getElementById('output'),
              parseBtn = document.getElementById('parse'),
              parseTime = document.getElementById('parseTime'),
              outputContainer = document.getElementById('outputContainer'),
              scrollInterval;
              
          parseBtn.onclick = function () {
              var scrollY = window.pageYOffset,
                  outputY = outputContainer.offsetTop,
                  parseStart = Date.now(),
                  result = mineParse(input.value);
              parseTime.innerHTML = '~ ' + ((Date.now() - parseStart) / 1000) + ' seconds';
              clearInterval(scrollInterval);
              output.innerHTML = '';
              if (window.innerWidth < 850) {
                  scrollInterval = setInterval(function () {
                      scrollY += 5;
                      window.scrollTo(0, scrollY);
                      if (scrollY >= outputY) {
                          window.scrollTo(0, outputY);
                          clearInterval(scrollInterval);
                      }
                  }, 0);
              }
              output.appendChild(result.parsed);
          };
      }());
  </script>