1日1ツール作成、100本ノックをやっているとか、いないとか、という状態の下駄です。
本日の謎掛け
「markdown」とかけまして・・・
「買ったばかりの服」と、ときます。
そのココロは・・・
タグが付いていると、外したくなります。
text2markdownライブラリが欲しかった件
先日作成した、githubみたいなgitリポジトリ管理ツール
「gitpage」で、
やはり、本家と同じ用に、README.mdを、リポジトリトップ画面に表示したいと考えて、markdown形式のテキストファイルを表示してみたが、
htmlタグに変換しなければ、見栄えがなんとも悪い。
似たような事を考える人はいるだろうから、ネットに変換ライブラリがたくさん落ちていると考えたのだが、
探しても、なかなかいいのが見つからない。
ほとんどの人が、markdownのcssだけをアップしていて、肝心のタグ変換は、テキストエディタのプラグインぐらいしか見つからなかった・・・
探し方が緩かったのかもしれないが・・・
まあ、無いのであれば、自分で作ればいいかと考えて、サクッと1日かけて作ってみました。
ソースコード公開
;$$markdown_viewer = (function(){
var __options = {
target : "body",
style : "normal"
};
var LIB = function(){};
// 起動scriptタグを選択
LIB.prototype.currentScriptTag = (function(){
var scripts = document.getElementsByTagName("script");
return this.currentScriptTag = scripts[scripts.length-1].src;
})();
LIB.prototype.event = function(target, mode, func){
if (target.addEventListener){target.addEventListener(mode, func, false)}
else{target.attachEvent('on' + mode, function(){func.call(target , window.event)})}
};
LIB.prototype.urlinfo = function(uri){
uri = (uri) ? uri : location.href;
var data={};
var urls_hash = uri.split("#");
var urls_query = urls_hash[0].split("?");
var sp = urls_query[0].split("/");
var data = {
uri : uri
, url : sp.join("/")
, dir : sp.slice(0 , sp.length-1).join("/") +"/"
, file : sp.pop()
, domain : sp[2] ? sp[2] : ""
, protocol : sp[0] ? sp[0].replace(":","") : ""
, hash : (urls_hash[1]) ? urls_hash[1] : ""
, query : (urls_query[1])?(function(urls_query){
var data = {};
var sp = urls_query.split("#")[0].split("&");
for(var i=0;i<sp .length;i++){
var kv = sp[i].split("=");
if(!kv[0]){continue}
data[kv[0]]=kv[1];
}
return data;
})(urls_query[1]):[]
};
return data;
};
LIB.prototype.upperSelector = function(elm , selectors) {
selectors = (typeof selectors === "object") ? selectors : [selectors];
if(!elm || !selectors){return;}
var flg = null;
for(var i=0; i<selectors.length; i++){
for (var cur=elm; cur; cur=cur.parentElement) {
if (cur.matches(selectors[i])) {
flg = true;
break;
}
}
if(flg){
break;
}
}
return cur;
}
// ゼロパディング
LIB.prototype.zeroPadding = function(num , len){
var num_len = num.toString().length;
if(num_len > len){
return num;
}
var zero_len = len - num_len;
var zero_val = "";
for(var i=0; i<zero_len; i++){
zero_val += "0";
}
return (zero_val + num.toString()) . slice( -1 * len);
}
LIB.prototype.construct = function(){
var lib = new LIB();
switch(document.readyState){
case "complete" : new MAIN;break;
case "interactive" : lib.event(window , "DOMContentLoaded" , function(){new MAIN});break;
default : lib.event(window , "load" , function(){new MAIN});break;
}
};
// 基本起動処理
var MAIN = function(options){
this.options = this.setOptions(options);
this.setCSS();
this.set_markdown();
};
// options
MAIN.prototype.setOptions = function(options){
var new_options = JSON.parse(JSON.stringify(__options));
if(options){
for(var i in options){
new_options[i] = options[i];
}
}
return new_options;
};
// set-css
MAIN.prototype.setCSS = function(){
if(document.querySelector("link[data-markdown-viewer='1']")){return}
var myScript = new LIB().currentScriptTag;
var href = myScript.replace(".js",".css");
var link = document.createElement("link");
link.setAttribute("data-markdown-viewer","1");
link.rel = "stylesheet";
link.href = href;
var head = document.getElementsByTagName("head");
head[0].appendChild(link);
};
// MarkDown-view
MAIN.prototype.set_markdown = function(){
if(typeof this.options.target === "undefined" || !this.options.target){
this.options.target = "body";
}
var targets = document.querySelectorAll(this.options.target);
if(!targets || !targets.length){return;}
for(var i=0; i<targets.length; i++){
this.md_convert(targets[i]);
}
};
MAIN.prototype.md_convert = function(elm){
if(!elm){return;}
var text = elm.innerHTML;
if(!text){return;}
// console.log(text.replace(/\n/g,"---"));
// marking
elm.setAttribute("data-markdown" , "converted");
elm.setAttribute("data-markdown-style" , this.options.style);
// convert
var lines = text.split("\n");
for(var i=0; i<lines.length; i++){
// lines[i] = this.md_tag_escape(lines[i]);
lines[i] = this.md_tag_br(lines[i]);
lines[i] = this.md_tag_head(lines[i]);
lines[i] = this.md_tag_emphasis(lines[i]);
lines[i] = this.md_tag_link(lines[i]);
lines[i] = this.md_tag_horizon(lines[i]);
}
text = lines.join("\n");
text = this.md_tag_list(text);
text = this.md_tag_pre(text);
text = this.md_tag_h1(text);
text = this.md_tag_quote(text);
text = this.md_tag_table(text);
// view
elm.innerHTML = text;
};
// " $" -> <br>
MAIN.prototype.md_tag_br = function(text){
if(text.match(/^(.*?) {2}$/)){
text = RegExp.$1 +"<br>";
}
return text;
};
// #
MAIN.prototype.md_tag_head = function(text){
if(text.match(/^(.*?)#{6} (.*?)$/)){
text = RegExp.$1 +"<h6><a name='"+RegExp.$2+"'>"+ RegExp.$2 +"</a></h6>";
}
else if(text.match(/^(.*?)# {5}(.*?)$/)){
text = RegExp.$1 +"<h5><a name='"+RegExp.$2+"'>"+ RegExp.$2 +"</a></h5>";
}
else if(text.match(/^(.*?)# {4}(.*?)$/)){
text = RegExp.$1 +"<h4><a name='"+RegExp.$2+"'>"+ RegExp.$2 +"</a></h4>";
}
else if(text.match(/^(.*?)# {3}(.*?)$/)){
text = RegExp.$1 +"<h3><a name='"+RegExp.$2+"'>"+ RegExp.$2 +"</a></h3>";
}
else if(text.match(/^(.*?)# {2}(.*?)$/)){
text = RegExp.$1 +"<h2><a name='"+RegExp.$2+"'>"+ RegExp.$2 +"</a></h2>";
}
else if(text.match(/^(.*?)# (.*?)$/)){
text = RegExp.$1 +"<h1><a name='"+RegExp.$2+"'>"+ RegExp.$2 +"</a></h1>";
}
return text;
};
// -
MAIN.prototype.md_tag_list = function(text){
var lines = text.split("\n");
var flg = false;
for(var i=0; i<lines.length; i++){
if(lines[i].match(/^(.*?)- (.*?)$/)){
if(flg === false){
lines[i] = "<ul>"+ RegExp.$1 +"<li>"+ RegExp.$2 +"</li>";
flg = true;
}
else{
lines[i] = RegExp.$1 +"<li>"+ RegExp.$2 +"</li>";
}
}
else if(flg === true){
lines[i] = "</ul>\n" + lines[i];
flg = false;
}
}
return lines.join("\n");
}
// >
MAIN.prototype.md_tag_quote = function(text){
var lines = text.split("\n");
var flg = false;
var datas = [];
for(var i=0; i<lines.length; i++){
if(lines[i].match(/^([ \t]*?)((>){1,})(.*?)$/)){
var count = RegExp.$2.length / 4;
if(flg === false){
datas.push("<blockquote>");
flg = count;
}
else if(flg < count){
datas.push("<blockquote>");
flg = count;
}
else if(flg > count){
datas.push("</blockquote>");
flg = count;
}
lines[i] = RegExp.$1 + RegExp.$4;
}
else if(flg !== false){
datas.push("</blockquote>");
flg = false;
}
datas.push(lines[i]);
}
return datas.join("\n");
}
// *..* , -..-
MAIN.prototype.md_tag_emphasis = function(text){
if(text.match(/^(.*?) \*\*\*(.+?)\*\*\* (.*?)$/)
|| text.match(/^(.*?) \-\-\-(.+?)\-\-\- (.*?)$/)){
text = RegExp.$1 +"<strong><em>"+ RegExp.$2 +"</em></strong>" + RegExp.$3;
}
else if(text.match(/^(.*?) \*\*(.+?)\*\* (.*?)$/)
|| text.match(/^(.*?) \-\-(.+?)\-\- (.*?)$/)){
text = RegExp.$1 +"<strong>"+ RegExp.$2 +"</strong>" + RegExp.$3;
}
else if(text.match(/^(.*?) \*(.+?)\* (.*?)$/)
|| text.match(/^(.*?) \-(.+?)\- (.*?)$/)){
text = RegExp.$1 +"<em>"+ RegExp.$2 +"</em>" + RegExp.$3;
}
else if(text.match(/^(.*?) ~~(.+?)~~ (.*?)$/)){
text = RegExp.$1 +"<del>"+ RegExp.$2 +"</del>" + RegExp.$3;
}
return text;
}
// ``` || ~~~
MAIN.prototype.md_tag_pre = function(text){
var lines = text.split("\n");
var flg = false;
for(var i=0; i<lines.length; i++){
if(lines[i].match(/^(.*?)```(.*?)$/)){
if(flg === false){
lines[i] = lines[i].replace("```","<pre>");
flg = true;
}
else{
lines[i] = lines[i].replace("```","</pre>");
flg = false;
}
}
else if(lines[i].match(/^(.*?)~~~(.*?)$/)){
if(flg === false){
lines[i] = lines[i].replace("~~~","<pre>");
flg = true;
}
else{
lines[i] = lines[i].replace("~~~","</pre>");
flg = false;
}
}
}
return lines.join("\n");
}
// ![..](..)
MAIN.prototype.md_tag_link = function(text){
// image
if(text.match(/^([ \t]*?)\!\[(.+?)\]\((.+?)\)(.*?)$/)){
var url = "";
// global
if(RegExp.$3.match(/^[http|https]\:\/\//)){
url = RegExp.$3;
}
// local
else{
var urlinfo = new LIB().urlinfo();
var uid = typeof urlinfo.query.uid !== "undefined" ? urlinfo.query.uid : "";
var id = typeof urlinfo.query.id !== "undefined" ? urlinfo.query.id : "";
var sp = RegExp.$3.split(".");
var ext = sp.pop();
var branch = typeof urlinfo.query.branch !== "undefined" ? urlinfo.query.branch : "";
url = "image.php?mode=git&uid="+uid+"&id="+id+"&ext="+ext+"&branch="+branch+"&path="+ RegExp.$3
}
text = RegExp.$1 + "<img src='"+ url +"' alt='"+ RegExp.$2 +"' />";
}
// link
else if(text.match(/^([ \t]*?)\[(.+?)\]\((.+?)\)(.*?)$/)){
text = RegExp.$1 + "<a href='"+ RegExp.$3 +"' title='"+ RegExp.$2 +"' />"+ RegExp.$2 +"</a>";
}
return text;
}
// ***,---,___,===
MAIN.prototype.md_tag_horizon = function(text){
if(text.match(/^( \t*?)\*{3}( \t*?)$/)
|| text.match(/^( \t*?)\-{3}( \t*?)$/)
|| text.match(/^( \t*?)\_{3}( \t*?)$/)
|| text.match(/^( \t*?)\={3}( \t*?)$/)){
text = RegExp.$1 +"<hr>"+ RegExp.$2;
}
return text;
}
// ==
MAIN.prototype.md_tag_h1 = function(text){
var lines = text.split("\n");
var flg = false;
var newDatas = [];
for(var i=lines.length-1; i>=0; i--){
if(flg !== false){
if(lines[i].match(/^([ \t]*?)[\<](.*?)$/)
|| lines[i].match(/^[^ \ta-zA-Z0-9]/)){
newDatas.unshift("<h1 name='"+flg+"'>"+flg+"</h1>");
newDatas.unshift(lines[i]);
flg = false;
}
else{
flg = lines[i] + flg;
}
}
if(flg === false && lines[i].match(/^([ \t]*?)\=\=([ \t]*?)$/)){
flg = "";
}
else if(flg !== false && i==0){
newDatas.unshift("<h1 name='"+flg+"'>"+flg+"</h1>");
break;
}
else if(flg === false){
newDatas.unshift(lines[i]);
}
}
return newDatas.join("\n");
}
// table
MAIN.prototype.md_tag_table = function(text){
var lines = text.split("\n");
var flg = false;
var newDatas = [];
var normals = [];
for(var i=0; i<lines.length; i++){
if(lines[i].match(/^([ \t]*?)\|/)){
normals.push(lines[i]);
flg = true;
}
else if(flg !== false){
if(normals.length === 1){console.log(1);
newDatas.push(normals[0]);
}
else if(normals.length >= 2 && normals[1].indexOf("-") == -1){
newDatas.concat(normals);
}
else if(normals.length >= 2){
var settings = this.set_table_setting(normals[1]);
newDatas.push("<table>");
// header
newDatas.push(this.set_table_cell(normals[0] , settings , "th"));
// datas
for(var j=2; j<normals.length; j++){
if(!normals[j]){continue;}
newDatas.push(this.set_table_cell(normals[j] , settings , "td"));
}
newDatas.push("</table>");
}
normals = [];
flg = false;
}
else{
newDatas.push(lines[i]);
}
}
return newDatas.join("\n");
}
MAIN.prototype.set_table_setting = function(line){
var cells = line.split("|");
var data = [cells[0]];
for(var i=1; i<cells.length-1; i++){
data[i] = (function(cell){
switch(cell){
case ":--" : return "left";
case "--:" : return "right";
case ":--:" : return "center";
default : return "";
}
})(cells[i]);
}
return data;
};
MAIN.prototype.set_table_cell = function(line,settings,tag){
var cells = line.split("|");
var data = "";
for(var i=1; i<cells.length-1; i++){
data += "<"+tag+" class='"+settings[i]+"'>"+cells[i]+"</"+tag+">";
}
return "<tr>"+data+"</tr>";
};
return MAIN;
})();
/* Normal */
[data-markdown="converted"]{
/* white-space : pre-wrap;
word-break : break-all; */
background-color:white;
}
/* [data-markdown="converted"]:after,
[data-markdown="converted"]:before {
display: table;
content: "";
}
[data-markdown="converted"]:after {
clear: both;
} */
/* [data-markdown="converted"]>:first-child {
margin-top: 0;
}
[data-markdown="converted"]>:last-child {
margin-bottom: 0;
} */
[data-markdown="converted"] a {
background-color: initial;
color: #0366d6;
text-decoration: none;
font-size : 14px;
}
[data-markdown="converted"] a:active,
[data-markdown="converted"] a:hover {
outline-width: 0;
text-decoration: underline;
}
[data-markdown="converted"] a:not([href]) {
color: inherit;
text-decoration: none;
}
[data-markdown="converted"] strong {
font-weight: inherit;
font-weight: bolder;
font-weight: 600;
}
[data-markdown="converted"] h1,
[data-markdown="converted"] h1 > a {
/* font-size: 2em; */
margin: .67em 0;
font-size: 32px;
/* font-size: 2em; */
}
[data-markdown="converted"] h1,
[data-markdown="converted"] h2,
[data-markdown="converted"] h3,
[data-markdown="converted"] h4,
[data-markdown="converted"] h5,
[data-markdown="converted"] h6{
margin-top: 8px;
margin-bottom: 8px;
margin-top: 8px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
[data-markdown="converted"] h1,
[data-markdown="converted"] h2{
font-weight: 600;
padding-bottom: .3em;
border-bottom: 1px solid #eaecef;
}
[data-markdown="converted"] h2,
[data-markdown="converted"] h2 > a {
font-size: 24px;
font-size: 1.5em;
}
[data-markdown="converted"] h3,
[data-markdown="converted"] h3 > a {
font-size: 20px;
font-size: 1.25em;
}
[data-markdown="converted"] h3,
[data-markdown="converted"] h4,
[data-markdown="converted"] h3 > a,
[data-markdown="converted"] h4 > a {
font-weight: 600;
}
[data-markdown="converted"] h4,
[data-markdown="converted"] h4 > a {
font-size: 16px;
font-size: 1em;
}
[data-markdown="converted"] h5,
[data-markdown="converted"] h5 > a {
font-size: 14px;
font-size: .875em;
}
[data-markdown="converted"] h5,
[data-markdown="converted"] h6,
[data-markdown="converted"] h5 > a,
[data-markdown="converted"] h6 > a {
font-weight: 600;
}
[data-markdown="converted"] h6,
[data-markdown="converted"] h6 > a {
font-size: 12px;
font-size: .85em;
color: #6a737d;
}
[data-markdown="converted"] blockquote,
[data-markdown="converted"] details,
[data-markdown="converted"] dl,
[data-markdown="converted"] ol,
[data-markdown="converted"] p,
[data-markdown="converted"] pre,
[data-markdown="converted"] table,
[data-markdown="converted"] ul {
margin-top: 0;
margin-bottom: 16px;
}
[data-markdown="converted"] img {
border-style: none;
max-width: 100%;
box-sizing: initial;
background-color: #fff;
}
[data-markdown="converted"] img[align=right] {
padding-left: 20px;
}
[data-markdown="converted"] img[align=left] {
padding-right: 20px;
}
[data-markdown="converted"] code,
[data-markdown="converted"] kbd,
[data-markdown="converted"] pre {
font-family: monospace,monospace;
font-size: 1em;
}
[data-markdown="converted"] code {
padding: .2em .4em;
margin: 0;
font-size: 85%;
background-color: rgba(27,31,35,.05);
border-radius: 3px;
}
[data-markdown="converted"] pre {
word-wrap: normal;
}
[data-markdown="converted"] pre>code {
padding: 0;
margin: 0;
font-size: 100%;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
[data-markdown="converted"] pre code {
display: inline;
max-width: auto;
padding: 0;
margin: 0;
overflow: visible;
line-height: inherit;
word-wrap: normal;
background-color: initial;
border: 0;
}
[data-markdown="converted"] hr {
box-sizing: initial;
height: 0;
overflow: visible;
height: 0;
margin: 15px 0;
overflow: hidden;
background: transparent;
border: 0;
border-bottom: 1px solid #dfeaf5;
border-bottom-color: #eee;
height: .25em;
padding: 0;
margin: 24px 0;
background-color: #e1e4e8;
border: 0;
}
[data-markdown="converted"] hr:after,
[data-markdown="converted"] hr:before {
display: table;
content: "";
}
[data-markdown="converted"] hr:after {
clear: both;
}
[data-markdown="converted"] input {
font: inherit;
margin: 0;
overflow: visible;
}
[data-markdown="converted"] [type=checkbox] {
box-sizing: border-box;
padding: 0;
}
[data-markdown="converted"] * {
box-sizing: border-box;
}
[data-markdown="converted"] input {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
[data-markdown="converted"] input::-webkit-inner-spin-button,
[data-markdown="converted"] input::-webkit-outer-spin-button {
margin: 0;
-webkit-appearance: none;
appearance: none;
}
[data-markdown="converted"] :checked+.radio-label {
position: relative;
z-index: 1;
border-color: #0366d6;
}
[data-markdown="converted"] table {
border-spacing: 0;
border-collapse: collapse;
display: block;
width: 100%;
overflow: auto;
}
[data-markdown="converted"] table th {
font-weight: 600;
}
[data-markdown="converted"] td,
[data-markdown="converted"] th {
padding: 0;
font-size:14px;
}
[data-markdown="converted"] table td,
[data-markdown="converted"] table th {
padding: 6px 13px;
border: 1px solid #b6c9db;
}
[data-markdown="converted"] table td.left,
[data-markdown="converted"] table th.left {
text-align : left;
}
[data-markdown="converted"] table td.center,
[data-markdown="converted"] table th.center {
text-align : center;
}
[data-markdown="converted"] table td.right,
[data-markdown="converted"] table th.right {
text-align : right;
}
[data-markdown="converted"] table tr {
background-color: #fff;
border-top: 1px solid #c6cbd1;
}
[data-markdown="converted"] table tr:nth-child(2n) {
background-color: #dfeaf5;
}
[data-markdown="converted"] p {
margin-top: 0;
margin-bottom: 10px;
}
[data-markdown="converted"] blockquote {
margin: 0;
padding: 0 1em;
color: #6a737d;
border-left: .25em solid #dfeaf5;
font-size:14px;
}
[data-markdown="converted"] blockquote>:first-child {
margin-top: 0;
}
[data-markdown="converted"] blockquote>:last-child {
margin-bottom: 0;
}
[data-markdown="converted"] ol,
[data-markdown="converted"] ul {
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
padding-left: 2em;
}
[data-markdown="converted"] ol ol,
[data-markdown="converted"] ul ol {
list-style-type: lower-roman;
}
[data-markdown="converted"] ol ol ol,
[data-markdown="converted"] ol ul ol,
[data-markdown="converted"] ul ol ol,
[data-markdown="converted"] ul ul ol {
list-style-type: lower-alpha;
}
[data-markdown="converted"] ol ol,
[data-markdown="converted"] ol ul,
[data-markdown="converted"] ul ol,
[data-markdown="converted"] ul ul {
margin-top: 0;
margin-bottom: 0;
}
[data-markdown="converted"] li {
word-wrap: break-all;
font-size : 14px;
}
[data-markdown="converted"] li>p {
margin-top: 16px;
}
[data-markdown="converted"] li+li {
margin-top: .25em;
}
[data-markdown="converted"] dl {
padding: 0;
}
[data-markdown="converted"] dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: 600;
}
[data-markdown="converted"] dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
[data-markdown="converted"] dd {
margin-left: 0;
}
[data-markdown="converted"] code,
[data-markdown="converted"] pre {
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
font-size: 14px;
}
[data-markdown="converted"] pre {
margin-top: 0;
margin-bottom: 0;
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: #dfeaf5;
border-radius: 3px;
}
/* Style : Dark */
[data-markdown="converted"][data-markdown-style="dark"]{
/* white-space : pre-wrap;
word-break : break-all; */
background-color:#012340;
color:white;
}
[data-markdown="converted"][data-markdown-style="dark"] pre {
background-color: #033E8C;
}
[data-markdown="converted"][data-markdown-style="dark"] *{
color:white;
}
[data-markdown="converted"][data-markdown-style="dark"] table td,
[data-markdown="converted"][data-markdown-style="dark"] table th {
border: 1px solid white;
background-color:#033E8C;
}
[data-markdown="converted"][data-markdown-style="dark"] table tr:nth-child(2n) td,
[data-markdown="converted"][data-markdown-style="dark"] table tr:nth-child(2n) th{
background-color: #3271c2;
}
使い方
1. 上記ライブラリ、jsとcssを同じ階層に設置して、jsをscriptタグで読み込みます。
<script src="markdown_viewer/src/main.js"></script>
2. htmlの下の方に、下記のように、markdown表示させたいエレメントのselectorでオブジェクト起動します。
<script>
new $$markdown_viewer({
target : ".markdown",
style : "normal"
});
</script>
styleには、「normal」以外に「dark」モードを用意していますが、色味などが気に入らない人は、自分で任意のstyle値にして、cssファイルの下部にあるstyle色指定の箇所をコピーして作ってもらえれば、反映されます。
ライセンスと配布
基本的にMITです。
githubにおいているので、気になる人は、cloneしてお使いください。
https://github.com/yugeta/markdown_viewer
全ての機能を再現できているわけではないので、要望があれば、プルリクくださいませ。
0 件のコメント:
コメントを投稿