Trước đây, để làm nổi bật code, mình dùng Google Code Prettify, sau một thời gian, mình dần phát hiện ra các nhược điểm của nó, nhất là với các khung code dài.
Vì thế, mình đã tìm và thay thế nó bằng plugin Prismjs.
Ưu điểm:
Nhược điểm:
Giao diện zzPrismjs
Bước 1: Thêm vào CSS
ACP >> Display >> Pictures and Colors >> Colors >> CSS Stylesheet
Bước 2: Thêm code vào cuối 2 temp viewtopic_body và posting_preview
ACP >> Display >> Templates >> QLTT >> viewtopic_body
ACP >> Display >> Templates >> Post & Private Messages >> posting_preview
Tags: #code
Vì thế, mình đã tìm và thay thế nó bằng plugin Prismjs.
Ưu điểm:
- Gọn nhẹ, mượt mà, dễ dàng tùy chọn giao diện và ngôn ngữ lập trình.
- Không bị lỗi treo trình duyệt(firefox) ở những khung code dài.
- Không bị cách đầu dòng khi sao chép code.
- Kế thừa các tính năng bổ sung từ zzPrettify.
- Có thể tạo plugin dựa trên các API được cung cấp.
Nhược điểm:
- Gây lỗi trình duyệt Chrome khi gặp script có chuỗi không phân tích được (hạn chế do mã regexp).
Giao diện zzPrismjs
Hướng dẫn
Bước 1: Thêm vào CSS
ACP >> Display >> Pictures and Colors >> Colors >> CSS Stylesheet
- Code:
/* zzPrismjs v1 by devs forumvi */
.codebox,.codebox *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
.codebox{background:transparent;border:0 none;margin:0 0 10px}
.codebox dd{background:transparent;margin:0;padding:0}
.codebox > dt{position:relative;background:url(//i83.servimg.com/u/f83/16/58/89/73/page_w10.png) no-repeat scroll 10px center #777;color:#FFF;border:1px solid #e2e2e2;height:30px!important;border-bottom-width:0;line-height:26px!important;padding:2px 10px 0 30px!important}
.codebox dd.cont_code{background:#FFF;max-height:100%!important;overflow:visible!important;position:relative;border:1px solid #e2e2e2}
.cont_code > code{overflow:auto;white-space:pre;display:block;line-height:17px;padding:10px;word-break:break-word}
.pun .controlCode,.pun .controlCode *{transition:all .3s;-webkit-transition:all .3s;-moz-transition:all .3s;-o-transition:all .3s;-ms-transition:all .3s}
.pun .controlCode{position:absolute;right:0;top:-30px;opacity:0}
.pun .codebox:hover .controlCode{opacity:1}
.pun .controlCode *{width:20px;height:20px!important;display:block;float:right;border:1px solid #999;background:url(http://i.imgur.com/ccn5y7c.png) no-repeat center #FFF;margin:4px 4px 0 0 !important}
.pun .controlCode input.findLine{background:url(http://i.imgur.com/8fxbcQy.png) no-repeat center #FFF!important;padding:0!important}
.pun .controlCode input.findLine:focus{background:#FFF!important;width:40px;text-align:center;color:#333;font-size:11px}
.pun .controlCode img.textCode{background-image:url(http://i.imgur.com/gXbPqhr.png)}
.pun .controlCode img.textCode.pretty{background-image:url(http://i.imgur.com/FJOia1z.png)}
.pun .controlCode :hover{cursor:pointer;border-color:#FFF}
/* prism.js Coy theme for JavaScript, CSS and HTML by Tim Shedor */
code[class*="language-"],pre[class*="language-"]{color:#000;text-shadow:0 1px #FFF;white-space:pre}
pre.line-numbers{background-color:#fdfdfd;background-image:-webkit-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);background-image:-moz-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);background-size:3em 2.6em;background-origin:content-box;position:relative;counter-reset:linenumber;overflow-y:hidden;padding:.1em 0 .1em 3.8em}
.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#7D8B99}
.token.punctuation{color:#5F6364}
.token.property,.token.tag,.token.boolean,.token.number,.token.function-name,.token.constant,.token.symbol{color:#c92c2c}
.token.selector,.token.attr-name,.token.string,.token.function,.token.builtin{color:#2f9c0a}
.token.atrule,.token.attr-value,.token.keyword,.token.class-name{color:#1990b8}
.token.regex,.token.important{color:#e90}
.token.operator,.token.entity,.token.url,.token.variable,.language-css .token.string,.style .token.string{color:#a67f59;background:rgba(255,255,255,0.5)}
.namespace{opacity:.7}
.token.important{font-weight:700}
.token.entity{cursor:help}
pre.line-numbers > code{padding:.1em 0;position:relative}
.line-numbers .line-numbers-rows{padding:.1em 0;background:#F5F2F0;position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #DDD;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.line-numbers-rows > span{pointer-events:none;display:block;counter-increment:linenumber}
.line-numbers-rows > span:before{content:counter(linenumber);color:rgba(0,0,0,0.3);display:block;padding-right:.7em;text-align:right}
@media print {code[class*="language-"],pre[class*="language-"]{text-shadow:none}}
- Cách giới hạn chiều cao khung code 250px:
- Tìm style của pre.line-numbers, xóa thuộc tính:
- Code:
overflow-y:hidden;
- Code:
max-height:250px;
- Code:
display:none;
Bước 2: Thêm code vào cuối 2 temp viewtopic_body và posting_preview
ACP >> Display >> Templates >> QLTT >> viewtopic_body
ACP >> Display >> Templates >> Post & Private Messages >> posting_preview
- Code:
<script src="http://devs.forumvi.com/34660.js" type="text/javascript"></script>
- Code:
/* Language test css - html - js by baivong <http://devs.forumvi.com> */
$("br", "code").replaceWith("\n");
$("code").wrap('<pre class="line-numbers"></pre>').parent().addClass(function () {
var a = $(this).text();
return "language-" + (/^[\s\n]*<[\s\S]+>[\s\n]*$/.test(a) ? "markup" : /\bif\s?\(.+\)\{?[\s\S]+\}?|\bif\s?\(.+\)\{?[\s\S]+\}?\s?else\s?\{?|;?\s?while\s?\(|\bfor\s?\(.+\)|\bfunction:\s?\w+|\w+\sfunction\s\(|\(function\s?\(.*?\)\s?\{|\b(var\s)?\w+\s?=\s?('|")?[\w\d]+('|")?(;|,)?|\}\);?|\}\n*(,|;)|('|")?\w+('|")?\s?:\s?('|")[\w\d]+('|")(,|\})|\b(true|false)\b|(^|[^\/])\/(?!\/)[^\*].+[^\*]\/[gimy]{0,4}(?=\s*($|[\r\n,.})]))|\(('|").+('|")\)|\((window|document)\)/g.test(a.replace(/\/\*.*\*\//g, "")) && !/DXImageTransform\.Microsoft/g.test(a) ? "javascript" : "css")
});
/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript&plugins=line-numbers */
(function () {
var l = "undefined" != typeof window ? window : {},
c = function () {
var c = /\blang(?:uage)?-(?!\*)(\w+)\b/i,
d = l.Prism = {
util: {
type: function (a) {
return Object.prototype.toString.call(a).match(/\[object (\w+)\]/)[1]
},
clone: function (a) {
switch (d.util.type(a)) {
case "Object":
var e = {},
b;
for (b in a) a.hasOwnProperty(b) && (e[b] = d.util.clone(a[b]));
return e;
case "Array":
return a.slice()
}
return a
}
},
languages: {
extend: function (a, e) {
var b = d.util.clone(d.languages[a]),
h;
for (h in e) b[h] = e[h];
return b
},
insertBefore: function (a, e, b, h) {
h = h || d.languages;
var f = h[a],
g = {},
k;
for (k in f)
if (f.hasOwnProperty(k)) {
if (k == e)
for (var c in b) b.hasOwnProperty(c) && (g[c] = b[c]);
g[k] = f[k]
}
return h[a] = g
},
DFS: function (a, e) {
for (var b in a) e.call(a, b, a[b]), "Object" === d.util.type(a) && d.languages.DFS(a[b], e)
}
},
highlightAll: function (a, e) {
for (var b = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'), h = 0, f; f = b[h++];) d.highlightElement(f, !0 === a, e)
},
highlightElement: function (a, e, b) {
for (var h, f, g = a; g && !c.test(g.className);) g = g.parentNode;
g && (h = (g.className.match(c) || [, ""])[1], f = d.languages[h]);
if (f && (a.className = a.className.replace(c, "").replace(/\s+/g, " ") + " language-" + h, g = a.parentNode, /pre/i.test(g.nodeName) && (g.className = g.className.replace(c, "").replace(/\s+/g, " ") + " language-" + h), g = a.textContent)) {
var g = g.replace(/&/g, "&").replace(/</g, "<").replace(/\u00a0/g, " "),
k = {
element: a,
language: h,
grammar: f,
code: g
};
d.hooks.run("before-highlight", k);
e && l.Worker ? (a = new Worker(d.filename), a.onmessage = function (a) {
k.highlightedCode = p.stringify(JSON.parse(a.data), h);
d.hooks.run("before-insert", k);
k.element.innerHTML = k.highlightedCode;
b && b.call(k.element);
d.hooks.run("after-highlight", k)
}, a.postMessage(JSON.stringify({
language: k.language,
code: k.code
}))) : (k.highlightedCode = d.highlight(k.code, k.grammar, k.language), d.hooks.run("before-insert", k), k.element.innerHTML = k.highlightedCode, b && b.call(a), d.hooks.run("after-highlight", k))
}
},
highlight: function (a, e, b) {
return p.stringify(d.tokenize(a, e), b)
},
tokenize: function (a, e, b) {
b = d.Token;
var h = [a],
f = e.rest;
if (f) {
for (var g in f) e[g] = f[g];
delete e.rest
}
a: for (g in e)
if (e.hasOwnProperty(g) && e[g])
for (var f = e[g], c = f.inside, p = !!f.lookbehind, l = 0, f = f.pattern || f, s = 0; s < h.length; s++) {
var q = h[s];
if (h.length > a.length) break a;
if (!(q instanceof b)) {
f.lastIndex = 0;
var n = f.exec(q);
if (n) {
p && (l = n[1].length);
var m = n.index - 1 + l,
n = n[0].slice(l),
r = m + n.length,
m = q.slice(0, m + 1),
q = q.slice(r + 1),
r = [s, 1];
m && r.push(m);
n = new b(g, c ? d.tokenize(n, c) : n);
r.push(n);
q && r.push(q);
Array.prototype.splice.apply(h, r)
}
}
}
return h
},
hooks: {
all: {},
add: function (a, e) {
var b = d.hooks.all;
b[a] = b[a] || [];
b[a].push(e)
},
run: function (a, e) {
var b = d.hooks.all[a];
if (b && b.length)
for (var c = 0, f; f = b[c++];) f(e)
}
}
},
p = d.Token = function (a, d) {
this.type = a;
this.content = d
};
p.stringify = function (a, e, b) {
if ("string" == typeof a) return a;
if ("[object Array]" == Object.prototype.toString.call(a)) return a.map(function (b) {
return p.stringify(b, e, a)
}).join("");
b = {
type: a.type,
content: p.stringify(a.content, e, b),
tag: "span",
classes: ["token", a.type],
attributes: {},
language: e,
parent: b
};
"comment" == b.type && (b.attributes.spellcheck = "true");
d.hooks.run("wrap", b);
var c = "",
f;
for (f in b.attributes) c += f + '="' + (b.attributes[f] || "") + '"';
return "<" + b.tag + ' class="' + b.classes.join(" ") + '" ' + c + ">" + b.content + "</" + b.tag + ">"
};
if (!l.document) {
if (!l.addEventListener) return l.Prism;
l.addEventListener("message", function (a) {
a = JSON.parse(a.data);
l.postMessage(JSON.stringify(d.tokenize(a.code, d.languages[a.language])));
l.close()
}, !1);
return l.Prism
}
var m = document.getElementsByTagName("script");
if (m = m[m.length - 1]) d.filename = m.src, document.addEventListener && !m.hasAttribute("data-manual") && document.addEventListener("DOMContentLoaded", d.highlightAll);
return l.Prism
}();
"undefined" != typeof module && module.exports && (module.exports = c);
c.languages.markup = {
comment: /<!--[\w\W]*?--\x3e/g,
prolog: /<\?.+?\?>/,
doctype: /<!DOCTYPE.+?>/,
cdata: /<!\[CDATA\[[\w\W]*?]]\x3e/i,
tag: {
pattern: /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi,
inside: {
tag: {
pattern: /^<\/?[\w:-]+/i,
inside: {
punctuation: /^<\/?/,
namespace: /^[\w-]+?:/
}
},
"attr-value": {
pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,
inside: {
punctuation: /=|>|"/g
}
},
punctuation: /\/?>/g,
"attr-name": {
pattern: /[\w:-]+/g,
inside: {
namespace: /^[\w-]+?:/
}
}
}
},
entity: /&#?[\da-z]{1,8};/gi
};
c.hooks.add("wrap", function (c) {
"entity" === c.type && (c.attributes.title = c.content.replace(/&/, "&"))
});
c.languages.css = {
comment: /\/\*[\w\W]*?\*\//g,
atrule: {
pattern: /@[\w-]+?.*?(;|(?=\s*{))/gi,
inside: {
punctuation: /[;:]/g
}
},
url: /url\((["']?).*?\1\)/gi,
selector: /[^\{\}\s][^\{\};]*(?=\s*\{)/g,
property: /(\b|\B)[\w-]+(?=\s*:)/ig,
string: /("|')(\\?.)*?\1/g,
important: /\B!important\b/gi,
ignore: /&(lt|gt|amp);/gi,
punctuation: /[\{\};:]/g
};
c.languages.markup && c.languages.insertBefore("markup", "tag", {
style: {
pattern: /(<|<)style[\w\W]*?(>|>)[\w\W]*?(<|<)\/style(>|>)/ig,
inside: {
tag: {
pattern: /(<|<)style[\w\W]*?(>|>)|(<|<)\/style(>|>)/ig,
inside: c.languages.markup.tag.inside
},
rest: c.languages.css
}
}
});
c.languages.clike = {
comment: {
pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,
lookbehind: !0
},
string: /("|')(\\?.)*?\1/g,
"class-name": {
pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,
lookbehind: !0,
inside: {
punctuation: /(\.|\\)/
}
},
keyword: /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,
"boolean": /\b(true|false)\b/g,
"function": {
pattern: /[a-z0-9_]+\(/ig,
inside: {
punctuation: /\(/
}
},
number: /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,
operator: /[-+]{1,2}|!|<=?|>=?|={1,3}|(&){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,
ignore: /&(lt|gt|amp);/gi,
punctuation: /[{}[\];(),.:]/g
};
c.languages.javascript = c.languages.extend("clike", {
keyword: /\b(var|let|if|else|while|do|for|return|in|instanceof|function|get|set|new|with|typeof|try|throw|catch|finally|null|break|continue|this)\b/g,
number: /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g
});
c.languages.insertBefore("javascript", "keyword", {
regex: {
pattern: /(^|[^\/])\/(?!\/)[^\*].+[^\*]\/[gimy]{0,4}(?=\s*($|[\r\n,.})]))/g,
lookbehind: !0
}
});
c.languages.markup && c.languages.insertBefore("markup", "tag", {
script: {
pattern: /(<|<)script[\w\W]*?(>|>)[\w\W]*?(<|<)\/script(>|>)/ig,
inside: {
tag: {
pattern: /(<|<)script[\w\W]*?(>|>)|(<|<)\/script(>|>)/ig,
inside: c.languages.markup.tag.inside
},
rest: c.languages.javascript
}
}
});
c.hooks.add("after-highlight", function (c) {
var d = c.element.parentNode;
if (d && /pre/i.test(d.nodeName) && -1 !== d.className.indexOf("line-numbers")) {
var l = 1 + c.code.split("\n").length;
lines = Array(l);
lines = lines.join("<span></span>");
l = document.createElement("span");
l.className = "line-numbers-rows";
l.innerHTML = lines;
d.hasAttribute("data-start") && (d.style.counterReset = "linenumber " + (parseInt(d.getAttribute("data-start"), 10) - 1));
c.element.appendChild(l)
}
})
})();
/* zzPrismjs v1 by baivong <http://devs.forumvi.com> */
function selectCode(a) {
a = $(a).parent().next()[0];
if (window.getSelection) {
var b = window.getSelection();
if (b.setBaseAndExtent) b.setBaseAndExtent(a, 0, a, a.innerText.length - 1);
else {
window.opera && "<BR>" == a.innerHTML.substring(a.innerHTML.length - 4) && (a.innerHTML += " ");
var c = document.createRange();
c.selectNodeContents(a);
b.removeAllRanges();
b.addRange(c)
}
} else document.getSelection ? (b = document.getSelection(), c = document.createRange(), c.selectNodeContents(a), b.removeAllRanges(), b.addRange(c)) : document.selection && (c = document.body.createTextRange(), c.moveToElementText(a), c.select())
}
$(function () {
$("dd.cont_code").prepend('<div class="controlCode"><img class="selectCode" title="Click để chọn toàn bộ code" onclick="selectCode(this)" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" /><img class="textCode" title="Ẩn số dòng" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" /><input class="findLine" title="Nhập số dòng muốn chuyển đến" size="4" maxlength="4" type="text" /></div>');
$(".findLine").on("input", function () {
this.value = /\d+/g.exec(this.value)
}).keydown(function (a) {
var b = $(this);
13 == a.keyCode ? (a = b.parent().next().find(".line-numbers-rows").find("span:eq(" + (parseInt(this.value, 10) - 1) + ")"), a.length && $("body, html").animate({
scrollTop: a.offset().top
}), this.value = "") : $("body, html").animate({
scrollTop: b.offset().top - 50
})
}).focus(function () {
$(this).parent().css("opacity", 1)
}).blur(function () {
this.value = "";
$(this).parent().removeAttr("style")
});
$(".textCode").click(function () {
var a = $(this),
b = a.parent().next();
a.toggleClass("pretty");
a.hasClass("pretty") ? (b.animate({
"padding-left": "0.8em"
}), a.attr("title", "Hiện số dòng")) : (b.animate({
"padding-left": "3.8em"
}), a.attr("title", "Ẩn số dòng"))
})
});
Tags: #code