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:
- 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;
Thêm vào thuộc tính mới:
- Code:
max-height:250px;
Tìm style của .pun .controlCode input.findLine, thêm vào thuộc tính
- 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>
[spoiler=Source 34660.js]
- 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