├── demo.jpeg ├── demo.mp3 ├── demo.webp ├── screenshot.en.png ├── demo.han.drool ├── demo.en.drool ├── README.md ├── demo.html ├── demo.css └── drool.js /demo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dou4cc/drool/HEAD/demo.jpeg -------------------------------------------------------------------------------- /demo.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dou4cc/drool/HEAD/demo.mp3 -------------------------------------------------------------------------------- /demo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dou4cc/drool/HEAD/demo.webp -------------------------------------------------------------------------------- /screenshot.en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dou4cc/drool/HEAD/screenshot.en.png -------------------------------------------------------------------------------- /demo.han.drool: -------------------------------------------------------------------------------- 1 | * 本demo也是教程,请对照本页源码阅读。 2 | 3 | 插入图片 drool源码 @@ ./demo.jpeg 4 | 效果 @@./demo.jpeg 5 | 插入音视频 drool源码 @@ ./demo.mp3 6 | @@ https://videos.cdn.mozilla.net/uploads/mozillaorg/Mozilla_2014_i_am.webm 7 | 效果 @@./demo.mp3 8 | @@https://videos.cdn.mozilla.net/uploads/mozillaorg/Mozilla_2014_i_am.webm 9 | 插入链接 drool源码 There is @@ a link to @@ ./demo.webp https://www.google.com/ncr. 10 | 效果 There is @@a link to @@./demo.webp https://www.google.com/ncr. 11 | 右对齐 drool源码 --- hello 12 | —— 你好 13 | 效果 ---hello 14 | ——你好 15 | “智能”缩进 #include 16 | 17 | int main(){ 18 | std::cout << "Hello World!" << std::endl; 19 | return 0; 20 | } -------------------------------------------------------------------------------- /demo.en.drool: -------------------------------------------------------------------------------- 1 | * This demo is also a guide when you view the source of this page at the same time. 2 | 3 | to insert a picture drool code @@ ./demo.jpeg 4 | result @@./demo.jpeg 5 | to insert an audio and a video drool code @@ ./demo.mp3 6 | @@ https://videos.cdn.mozilla.net/uploads/mozillaorg/Mozilla_2014_i_am.webm 7 | result @@./demo.mp3 8 | @@https://videos.cdn.mozilla.net/uploads/mozillaorg/Mozilla_2014_i_am.webm 9 | to insert a link drool code There is @@ a link to @@ ./demo.webp https://www.google.com/ncr. 10 | result There is @@a link to @@./demo.webp https://www.google.com/ncr. 11 | to align right drool code --- hello 12 | —— 你好 13 | result ---hello 14 | ——你好 15 | “smart” retract #include 16 | 17 | int main(){ 18 | std::cout << "Hello World!" << std::endl; 19 | return 0; 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drool 2 | 3 | - Since it is said that the source of a markdown is as readable as visual documents, why do we compile `*something*` to *something*? Based on this consideration, I designed *drool*. You cannot make something bold or italic with it (because sources like `*something*` is readable enough), which is why *drool* is different from markdown. You can retract (The restract of drool can be used as table. I made a table in `./demo.en.drool`. When viewing a *drool* file which contains a table with a common text editor, I recommand you **set tab-size to at least 31ch**.) and right align something to express the structure of a document and can still insert media elements and links. 4 | - Compared with markdown, *drool* is minimal without signals like `[` or `!`. *drool* will not make a literary article look like programming codes. 5 | - You can use links like `https://example.org/).html` in *drool* but cannot in markdown. 6 | 7 | For details, please visit [a demo](https://dou4cc.github.io/drool/demo.html?./demo.en.drool) **which requires your browser supporting `Element.prototype.append`, async (generator) functions, WebP, WebM, fetch API, CSS Variables, CSS sticky position, and full of ECMAScript 6**. I recommend you use [Chromium](https://download-chromium.appspot.com) or [Chrome Canary](https://www.google.com/chrome/browser/canary.html) with two flags enabled before 2018: 8 | - *chrome://flags/#enable-javascript-harmony* 9 | - *chrome://flags/#enable-experimental-web-platform-features* 10 | 11 | I am sorry that I used a few unstable features even [Babel](https://babeljs.io) does not support which. If you are unable to open the demo, please refer to [the screenshot](https://dou4cc.github.io/drool/screenshot.en.png). 12 | 13 | There is a UTF-8 BOM in the head of every text file of this repository and I use `\r\n` to break lines. (You need not add a BOM to your *drool* file.) 14 | 15 | ———————— 16 | 17 | - 既然markdown自称源码的可读性堪比可视化编辑,为什么我们还要把`*something*`编译成*something*?*drool*没有加粗、斜体之类的功能(`*something*`的可读性就很好了,不是吗?),但可用缩进(*drool*的缩进可用作表格,比如`./demo.han.drool`就包含一个表格,使用普通文本编辑器打开时为了不使表格混乱,应**调宽tab-size,例如调成12ch**)和右对齐表达文档结构,图片(音视频)、链接仍可插入。 18 | - 相比markdown,*drool*是极简的,不使用`[`、`!`之类的标识。*drool*不会像markdown那样让一篇文学文章看上去布满代码。 19 | - markdown要求链接和图片的URL要么不含括号,要么成对出现,而*drool*不要求,毕竟不成对出现括号的URL也是合法的。 20 | - *drool*对中文友好,中文标点不会使用英文字体。 21 | 22 | 详见[demo](https://dou4cc.github.io/drool/demo.html?./demo.han.drool)。**你的浏览器得支持`Element.prototype.append`、async (generator) functions、WebP、WebM、fetch API、CSS变量、CSS sticky position和完整的ECMAScript 6**,我推荐使用启用了如下实验选项的[Chromium](https://download-chromium.appspot.com)或[Chrome Canary](https://www.google.com/chrome/browser/canary.html): 23 | - *chrome://flags/#enable-javascript-harmony* 24 | - *chrome://flags/#enable-experimental-web-platform-features* 25 | 26 | 抱歉使用了一些草案级别的、连[Babel](https://babeljs.io)都不支持的特性,如果你打不开demo,请参考[其英语版的截图](https://dou4cc.github.io/drool/screenshot.en.png)。 27 | 28 | 因为使用了Windows和微软的编辑器,本仓库的每个文本文件头都有UTF-8的BOM,使用`\r\n`换行。 29 | 30 | ## License 31 | MIT 32 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | drool demo 7 | 8 | 9 | 10 |
11 | 89 | 90 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /demo.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | article{ 4 | display: inline-table; 5 | text-align: left; 6 | color: rgb(var(--color)); 7 | background-color: rgba(255, 255, 255, var(--background-color-alpha)); 8 | line-height: var(--line-height); 9 | } 10 | 11 | article > div{ 12 | display: table; 13 | width: 100%; 14 | min-height: var(--line-height); 15 | } 16 | 17 | article > div > a{ 18 | display: block; 19 | float: left; 20 | opacity: 0; 21 | } 22 | 23 | article > div > a::before{ 24 | content: "#"; 25 | } 26 | 27 | article > div:hover > a{ 28 | opacity: 0.5; 29 | } 30 | 31 | article > div > a:focus, 32 | html[blur] article > div > a:hover{ 33 | opacity: 1; 34 | } 35 | 36 | article > div > div{ 37 | display: table-cell; 38 | vertical-align: top; 39 | } 40 | 41 | article > div > div:not(:first-of-type){ 42 | padding-left: calc(var(--tab-size) - 1px); 43 | border-left: 1px dotted rgba(var(--color), 0.5); 44 | } 45 | 46 | article > div > div:last-of-type{ 47 | width: 100%; 48 | white-space: pre-wrap; 49 | word-break: break-word; 50 | } 51 | 52 | article > div > div:not(:last-of-type) > div{ 53 | white-space: pre; 54 | } 55 | 56 | article > div > div:not(:last-of-type) > div:not(:last-of-type){ 57 | overflow-y: hidden; 58 | height: 0; 59 | } 60 | 61 | article > div > a, 62 | article > div > div:not(:last-of-type) > div:last-of-type{ 63 | position: sticky; 64 | top: 0; 65 | } 66 | 67 | article > div > div:last-of-type:not(:first-of-type), 68 | article > div > div:not(:last-of-type):not(:first-of-type) > div{ 69 | tab-size: calc(var(--tab-size) - 1px); 70 | text-indent: calc(1px - var(--tab-size)); 71 | } 72 | 73 | article > div > div:last-of-type:not(:first-of-type) *, 74 | article > div > div:not(:last-of-type):not(:first-of-type) > div *{ 75 | text-indent: initial; 76 | } 77 | 78 | article > div > a::after, 79 | article > div > div:last-of-type::before, 80 | article > div > div:not(:last-of-type) > div::before, 81 | article > div > div a::before, 82 | article > div > div a::after{ 83 | content: ""; 84 | } 85 | 86 | article > div > div a:not([hidden]){ 87 | display: inline-block; 88 | position: relative; 89 | } 90 | 91 | article > div > div a::before, 92 | article > div > div a::after{ 93 | display: block; 94 | position: absolute; 95 | width: 100%; 96 | height: 0; 97 | bottom: 0; 98 | border-bottom-width: 1px; 99 | border-bottom-color: rgb(var(--color)); 100 | } 101 | 102 | article > div > div a::before{ 103 | border-bottom-style: solid; 104 | } 105 | 106 | article > div > div a:visited::before{ 107 | border-bottom-color: rgb(255, 255, 255); 108 | } 109 | 110 | article > div > div a::after{ 111 | border-bottom-style: dashed; 112 | } 113 | 114 | article > div > div a:focus, 115 | html[blur] article > div > div a:hover{ 116 | background-color: rgba(var(--color), 0.17); 117 | } 118 | 119 | article span, 120 | article > div > a::after, 121 | article > div > div:last-of-type::before, 122 | article > div > div:not(:last-of-type) > div::before{ 123 | font-family: var(--font-family); 124 | } 125 | 126 | article a{ 127 | text-decoration: none; 128 | } 129 | 130 | article a:link, 131 | article a:active, 132 | article a:visited{ 133 | color: rgb(var(--color)); 134 | } 135 | 136 | article a:focus{ 137 | outline: none; 138 | } 139 | 140 | article img, 141 | article video{ 142 | max-width: 100%; 143 | margin-bottom: calc((1em - var(--line-height)) / 2); 144 | } 145 | 146 | article video[mini]{ 147 | width: 100%; 148 | height: 28px; 149 | } 150 | 151 | article [right]{ 152 | text-align: right; 153 | } 154 | 155 | article [center]{ 156 | text-align: center; 157 | } 158 | -------------------------------------------------------------------------------- /drool.js: -------------------------------------------------------------------------------- 1 | return async ( 2 | source, 3 | hash_iter, 4 | new_span = ( 5 | node, 6 | span = document.createElement("span") 7 | ) => ( 8 | span.append(node), 9 | span 10 | ), 11 | new_link = ( 12 | uri, 13 | nodes, 14 | link = document.createElement("a") 15 | ) => ( 16 | link.href = uri, 17 | link.append(...nodes), 18 | link.addEventListener("click", () => void link.blur()), 19 | link.addEventListener("mouseleave", () => void link.blur()), 20 | link.addEventListener("mousemove", () => void link.focus()), 21 | link 22 | ), 23 | new_cell = ( 24 | source, 25 | plain = ( 26 | source, 27 | match = source.match(/[^\0-\u{ff}]+/u) 28 | ) => 29 | match 30 | ? (( 31 | {index} = match, 32 | [$] = match 33 | ) => [ 34 | source.slice(0, index), 35 | new_span($), 36 | ...plain(source.slice(index + $.length)), 37 | ])() 38 | : [source], 39 | media = ( 40 | uri, 41 | link = new_link(uri, plain((() => { 42 | try{ 43 | const t = decodeURI(uri); 44 | return /\n|\r|\t/u.test(t) ? uri : t; 45 | }catch(error){ 46 | return uri; 47 | } 48 | })())), 49 | image = document.createElement("img"), 50 | video = document.createElement("video"), 51 | image_off = () => void ( 52 | image.removeEventListener("abort", image_onabort), 53 | image.removeEventListener("load", image_onload), 54 | image.removeEventListener("error", image_onerror) 55 | ), 56 | image_onabort = () => void ( 57 | image.src = "", 58 | image.src = uri 59 | ), 60 | image_onload = () => void ( 61 | image_off(), 62 | link.hidden = true, 63 | image.hidden = false 64 | ), 65 | image_onerror = () => void ( 66 | image_off(), 67 | image.src = "", 68 | video.addEventListener("abort", video_onabort), 69 | video.addEventListener("canplay", video_oncanplay), 70 | video.addEventListener("error", video_onerror), 71 | video.src = uri 72 | ), 73 | video_off = () => void ( 74 | video.removeEventListener("abort", video_onabort), 75 | video.removeEventListener("canplay", video_oncanplay), 76 | video.removeEventListener("error", video_onerror) 77 | ), 78 | video_onabort = () => void ( 79 | video.src = "", 80 | video.src = uri 81 | ), 82 | video_oncanplay = () => void ( 83 | video_off(), 84 | link.hidden = true, 85 | video.hidden = false, 86 | video.videoWidth || video.videoHeight || video.setAttribute("mini", "") 87 | ), 88 | video_onerror = () => void ( 89 | video_off(), 90 | video.src = "", 91 | requestAnimationFrame(() => setTimeout(start, 2000)) 92 | ), 93 | start = () => void ( 94 | image.addEventListener("abort", image_onabort), 95 | image.addEventListener("load", image_onload), 96 | image.addEventListener("error", image_onerror), 97 | image.src = uri 98 | ) 99 | ) => ( 100 | image.hidden = video.hidden = video.controls = true, 101 | start(), 102 | [link, image, video] 103 | ), 104 | inline_media = ( 105 | uri, 106 | image = document.createElement("img"), 107 | video = document.createElement("video"), 108 | image_off = () => void ( 109 | image.removeEventListener("abort", image_onabort), 110 | image.removeEventListener("load", image_onload), 111 | image.removeEventListener("error", image_onerror) 112 | ), 113 | image_onabort = () => void ( 114 | image.src = "", 115 | image.src = uri 116 | ), 117 | image_onload = () => void ( 118 | image_off(), 119 | image.hidden = false 120 | ), 121 | image_onerror = () => void ( 122 | image_off(), 123 | image.src = "", 124 | video.addEventListener("abort", video_onabort), 125 | video.addEventListener("canplaythrough", video_oncanplaythrough), 126 | video.addEventListener("error", video_onerror), 127 | video.src = uri 128 | ), 129 | video_off = () => void ( 130 | video.removeEventListener("abort", video_onabort), 131 | video.removeEventListener("canplaythrough", video_oncanplaythrough), 132 | video.removeEventListener("error", video_onerror) 133 | ), 134 | video_onabort = () => void ( 135 | video.src = "", 136 | video.src = uri 137 | ), 138 | video_oncanplaythrough = () => void ( 139 | video_off(), 140 | video.hidden = false, 141 | video.play() 142 | ), 143 | video_onerror = () => void ( 144 | video_off(), 145 | video.src = "", 146 | requestAnimationFrame(() => setTimeout(start, 2000)) 147 | ), 148 | start = () => void ( 149 | image.addEventListener("abort", image_onabort), 150 | image.addEventListener("load", image_onload), 151 | image.addEventListener("error", image_onerror), 152 | image.src = uri 153 | ) 154 | ) => ( 155 | video.volume = 0, 156 | image.hidden = video.hidden = video.loop = true, 157 | start(), 158 | [image, video] 159 | ), 160 | $1 = "\\w'():,;.?!+\\-*\\/$%#@&~=", 161 | $2 = "[" + $1 + "]", 162 | $3 = "[^" + $1 + "]", 163 | $4 = "(?:[:,;.?!]|\\.{3})", 164 | $5 = "\\)*" + $4 + "@*(?=@" + $3 + ")", 165 | $6 = "\\)+@*(?=@\\)*(?:" + $4 + "?(?:\\s|$))|" + $3 + ")", 166 | $7 = "(?=\\)*(?:" + $4 + "?(?:\\s|$))|" + $3 + ")", 167 | $8 = "(?:[a-z0-9\-]+:\\/\\/|\\/\\/[a-z0-9\\-.:]+\\/|data:|\\/(?!\\/)|\\.\\.?\\/)" + $2 + "+?(?:" + $5 + "|" + $6 + "|" + $7 + ")", 168 | $9 = "(?:(?:[a-z0-9\-]+:\\/\\/|\\/\\/|data:|javascript:|mailto:)" + $2 + "|\\/(?!\\/)|\\.\\.?\\/)" + $2 + "*?(?:" + $5 + "|" + $6 + "|" + $7 + ")", 169 | $10 = "(?=\\S)(?!" + $8 + ")(.*?(?:@@" + $8 + "@?.*?)+?|.+?)(?:\\s+|(?<=\\)|" + $3 + "))(" + $9 + ")@?", 170 | f1 = source => plain(source.replace(RegExp("(?<=@@) (?= *" + $8 + ")", "ug"), "")), 171 | f2 = ( 172 | source, 173 | match = source.match(RegExp("@@(" + $8 + ")@?", "u")) 174 | ) => 175 | match 176 | ? (( 177 | {index} = match, 178 | [{length}, $1] = match 179 | ) => [ 180 | ...f1(source.slice(0, index)), 181 | ...media($1), 182 | ...f2(source.slice(index + length)), 183 | ])() 184 | : f1(source), 185 | f3 = source => f2(source.replace(RegExp("(?<=@@) (?= *" + $10 + ")", "ug"), "")), 186 | f4 = ( 187 | source, 188 | match = source.match(RegExp("@@" + $10, "u")) 189 | ) => 190 | match 191 | ? (( 192 | {index} = match, 193 | [{length}, $1, $2] = match, 194 | f = ( 195 | source, 196 | match = source.match(RegExp("@@(" + $8 + ")@?", "u")) 197 | ) => 198 | match 199 | ? (( 200 | {index} = match, 201 | [{length}, $1] = match 202 | ) => [ 203 | ...plain(source.slice(0, index)), 204 | ...inline_media($1), 205 | ...f(source.slice(index + length)), 206 | ])() 207 | : plain(source) 208 | ) => [ 209 | ...f3(source.slice(0, index)), 210 | new_link($2, f($1)), 211 | ...f4(source.slice(index + length)), 212 | ])() 213 | : f3(source), 214 | cell = document.createElement("div") 215 | ) => ( 216 | /^\t?(?:-{3,}(?!-)|—{2,}(?!—))[^ ]/u.test(source) 217 | ? ( 218 | cell.setAttribute("right", ""), 219 | cell.append(...f4(source.replace(/(?<=^\t?)(?:-{3,}|—{2,})/u, ""))) 220 | ) 221 | : ( 222 | RegExp("^\\t?(?:(?:@@" + $8 + "@?)+|-+|—+)$", "u").test(source) && cell.setAttribute("center", ""), 223 | cell.append(...f4(source.replace(/(?<=^\t?-{3,}) (?=[^\-])|(?<=^\t?—{2,}) (?=[^—])/u, ""))) 224 | ), 225 | cell 226 | ), 227 | new_column = ( 228 | x, 229 | y, 230 | f1 = n => 231 | matrix[n - 1] && typeof matrix[n - 1][y + 1] === "string" 232 | ? (( 233 | source = matrix[n - 1][y] 234 | ) => [...(/^\t?$/u.test(source) ? [] : [new_cell(source)]), ...f1(n - 1)])() 235 | : [], 236 | f2 = n => 237 | matrix[n + 1] && typeof matrix[n + 1][y + 1] === "string" 238 | ? (( 239 | source = matrix[n + 1][y] 240 | ) => [...(/^\t?$/u.test(source) ? [] : [new_cell(source)]), ...f2(n + 1)])() 241 | : [], 242 | column = document.createElement("div") 243 | ) => ( 244 | column.append(...f1(x), ...f2(x), new_cell(matrix[x][y])), 245 | column 246 | ), 247 | new_row = async ( 248 | x, 249 | hash, 250 | link, 251 | list = matrix[x], 252 | {length} = list, 253 | row = document.createElement("div") 254 | ) => ( 255 | hash_iter && (hash = hash_iter.next()), 256 | row.append( 257 | ...list.slice(0, -1).map((n, i) => new_column(x, i)), 258 | new_cell(list[length - 1]) 259 | ), 260 | hash_iter && ( 261 | hash = (await hash).value, 262 | link = new_link("#" + hash, []), 263 | link.id = hash, 264 | row.insertBefore(link, row.firstChild) 265 | ), 266 | row 267 | ), 268 | f = n => n >= 0 ? [...f(n - 1), new_row(n)] : [], 269 | matrix = source.split(/\n|\r\n|\r/ug).map(source => [ 270 | ...(source.startsWith("\t") ? [""] : []), 271 | ...source.split(/(?=\t)/ug), 272 | ]) 273 | ) => await Promise.all(f(matrix.length - 1)); 274 | --------------------------------------------------------------------------------