├── .editorconfig ├── .gitignore ├── bower.json ├── dist ├── css │ ├── demo.css │ ├── mirrormark.css │ ├── mirrormark.min.css │ ├── mirrormark.package.css │ └── mirrormark.package.min.css ├── index.html └── js │ ├── mirrormark.js │ ├── mirrormark.min.js │ ├── mirrormark.package.js │ └── mirrormark.package.min.js ├── gulpfile.js ├── package.json ├── readme.md └── src ├── css ├── demo.css ├── highlighting.less ├── mirrormark.less ├── preview.less ├── toolbar-icons.less └── toolbar.less ├── index.html └── js ├── mirrormark.js └── preview.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | bower_components 3 | node_modules 4 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MirrorMark", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/themusicbed/MirrorMark", 5 | "authors": [ 6 | "Andrew Del Prete " 7 | ], 8 | "description": "Simple, yet extensible.", 9 | "keywords": [ 10 | "markdown", 11 | "codemirror", 12 | "editor" 13 | ], 14 | "license": "MIT", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "tests" 21 | ], 22 | "dependencies": { 23 | "font-awesome": "~4.3.0" 24 | }, 25 | "devDependencies": { 26 | "codemirror": "~5.0.0", 27 | "pagedown-extra": "*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /dist/css/demo.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ul, ol, li 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | border: 0; 20 | font-size: 100%; 21 | vertical-align: baseline; 22 | } 23 | 24 | html, body, div, span, applet, object, iframe, 25 | h1, h2, h3, h4, h5, h6 { 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | /* HTML5 display-role reset for older browsers */ 31 | article, aside, details, figcaption, figure, 32 | footer, header, hgroup, menu, nav, section { 33 | display: block; 34 | } 35 | body { 36 | line-height: 1; 37 | } 38 | blockquote, q { 39 | quotes: none; 40 | } 41 | blockquote:before, blockquote:after, 42 | q:before, q:after { 43 | content: ''; 44 | content: none; 45 | } 46 | table { 47 | border-collapse: collapse; 48 | border-spacing: 0; 49 | } 50 | 51 | 52 | /* Elements */ 53 | a { 54 | color: gray; 55 | text-decoration: none; 56 | } 57 | a:hover { 58 | color: black; 59 | } 60 | 61 | body { 62 | font-family: 'Merriweather', serif; 63 | } 64 | 65 | h1 { 66 | font-size: 3rem; 67 | } 68 | 69 | h2 { 70 | font-size: 2rem; 71 | } 72 | 73 | /* Classes */ 74 | .bgg05 { 75 | background: rgba(0,0,0,0.05); 76 | } 77 | 78 | .bgg1 { 79 | background: rgba(0,0,0,0.1); 80 | } 81 | 82 | .btn { 83 | border-radius: 3px; 84 | text-transform: lowercase; 85 | font-weight: bold; 86 | padding: .5rem .75rem; 87 | background: #222222; 88 | color: white; 89 | transition: all .2s linear; 90 | } 91 | 92 | .btn:hover { 93 | padding: .5rem 1rem; 94 | background: #555555; 95 | } 96 | 97 | .tac { 98 | text-align: center; 99 | } 100 | 101 | .wrap { 102 | padding: 3%; 103 | } 104 | 105 | .editor { 106 | max-width: 66%; 107 | margin: auto; 108 | } 109 | -------------------------------------------------------------------------------- /dist/css/mirrormark.css: -------------------------------------------------------------------------------- 1 | .cm-s-mirrormark .cm-m-markdown { 2 | color: #000; 3 | font-family: sans-serif; 4 | } 5 | .cm-s-mirrormark .cm-m-markdown.cm-header { 6 | font-family: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 7 | font-weight: bold; 8 | } 9 | .cm-s-mirrormark .cm-m-markdown.cm-header-1 { 10 | font-size: 28px; 11 | } 12 | .cm-s-mirrormark .cm-m-markdown.cm-header-2 { 13 | font-size: 22px; 14 | } 15 | .cm-s-mirrormark .cm-m-markdown.cm-header-2 { 16 | font-size: 18px; 17 | } 18 | .cm-s-mirrormark .cm-m-markdown.cm-quote { 19 | color: #888; 20 | background-color: rgba(128, 128, 128, 0.05); 21 | padding: 5px; 22 | } 23 | .cm-s-mirrormark .cm-m-markdown.cm-quote, 24 | .cm-s-mirrormark .cm-m-markdown.cm-string { 25 | color: #888; 26 | } 27 | .cm-s-mirrormark .cm-m-markdown.cm-tag, 28 | .cm-s-mirrormark .cm-m-markdown.cm-link { 29 | color: #444; 30 | } 31 | .cm-s-mirrormark .cm-m-markdown.cm-comment { 32 | color: #a50; 33 | font-family: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 34 | } 35 | .cm-s-mirrormark .cm-m-markdown.cm-m-xml, 36 | .cm-s-mirrormark .cm-m-markdown.cm-bracket { 37 | color: #170; 38 | font-family: inherit; 39 | } 40 | .cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-scroll { 41 | display: none; 42 | } 43 | .cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-preview { 44 | display: block; 45 | } 46 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview { 47 | display: none; 48 | height: auto !important; 49 | overflow: visible !important; 50 | padding: 30px 4%; 51 | box-sizing: border-box; 52 | font-family: sans-serif; 53 | } 54 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview h1, 55 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview h2, 56 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview h3 { 57 | font-fami: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 58 | } 59 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview pre, 60 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview code { 61 | font-family: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 62 | } 63 | .cm-s-mirrormark.CodeMirror .CodeMirror-fullscreen .CodeMirror-preview { 64 | height: 100% !important; 65 | overflow: scroll !important; 66 | } 67 | .mirrormark-toolbar li > a { 68 | display: inline-block; 69 | font: normal normal normal 14px/1 FontAwesome; 70 | font-size: inherit; 71 | text-rendering: auto; 72 | -webkit-font-smoothing: antialiased; 73 | -moz-osx-font-smoothing: grayscale; 74 | transform: translate(0, 0); 75 | } 76 | .mirrormark-toolbar li > a.pull-left { 77 | margin-right: .3em; 78 | } 79 | .mirrormark-toolbar li > a.pull-right { 80 | margin-left: .3em; 81 | } 82 | .mirrormark-toolbar .bold a:before { 83 | content: "\f032"; 84 | } 85 | .mirrormark-toolbar .italicize a:before { 86 | content: "\f033"; 87 | } 88 | .mirrormark-toolbar .blockquote a:before { 89 | content: "\f10d"; 90 | } 91 | .mirrormark-toolbar .strikethrough a:before { 92 | content: "\f0cc"; 93 | } 94 | .mirrormark-toolbar .link a:before { 95 | content: "\f0c1"; 96 | } 97 | .mirrormark-toolbar .image a:before { 98 | content: "\f03e"; 99 | } 100 | .mirrormark-toolbar .unorderedList a:before { 101 | content: "\f03a"; 102 | } 103 | .mirrormark-toolbar .orderedList a:before { 104 | content: "\f0cb"; 105 | } 106 | .mirrormark-toolbar .fullScreen a:before { 107 | content: "\f065"; 108 | } 109 | .mirrormark-toolbar .preview a:before { 110 | content: "\f15b"; 111 | } 112 | .cm-s-mirrormark.CodeMirror-fullscreen .mirrormark-toolbar .fullScreen a:before { 113 | content: "\f066"; 114 | } 115 | .cm-s-mirrormark.CodeMirror-has-preview .mirrormark-toolbar .preview a:before { 116 | content: "\f016"; 117 | } 118 | /* Toolbar Theme */ 119 | .mirrormark-toolbar { 120 | background: #fff; 121 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 122 | display: block; 123 | width: 100%; 124 | margin-top: 0; 125 | margin-bottom: 0; 126 | padding-left: 0; 127 | padding-right: 0; 128 | /* First level */ 129 | } 130 | .mirrormark-toolbar li { 131 | display: inline-block; 132 | position: relative; 133 | z-index: 1; 134 | } 135 | .mirrormark-toolbar li.fullScreen, 136 | .mirrormark-toolbar li.preview { 137 | float: right; 138 | } 139 | .mirrormark-toolbar li.has-nested:after { 140 | content: '\2304'; 141 | color: #888; 142 | position: absolute; 143 | bottom: 3px; 144 | right: 3px; 145 | font-size: 11px; 146 | transform: scale(1.3, 1); 147 | } 148 | .mirrormark-toolbar li a { 149 | background-color: #fff; 150 | color: #888; 151 | cursor: pointer; 152 | display: block; 153 | font-size: 16px; 154 | height: 40px; 155 | line-height: 40px; 156 | text-align: center; 157 | transition: color .2s linear; 158 | width: 40px; 159 | } 160 | .mirrormark-toolbar li:hover a, 161 | .mirrormark-toolbar li:hover.has-nested:after { 162 | color: #000; 163 | } 164 | .mirrormark-toolbar li:hover ul { 165 | display: block; 166 | } 167 | .mirrormark-toolbar li ul { 168 | background: #e5e5e5; 169 | display: none; 170 | position: absolute; 171 | top: 41px; 172 | width: 150px; 173 | z-index: 1000; 174 | } 175 | .mirrormark-toolbar li ul li { 176 | float: none; 177 | width: 100%; 178 | } 179 | .mirrormark-toolbar li ul li a { 180 | background: transparent; 181 | color: #888 !important; 182 | font-family: "Helvetica Neue", sans-serif; 183 | font-size: 12px; 184 | height: auto; 185 | line-height: normal; 186 | padding: .25rem .75rem; 187 | text-align: left; 188 | width: auto; 189 | } 190 | .mirrormark-toolbar li ul li:hover a { 191 | color: black !important; 192 | } 193 | .cm-s-mirrormark { 194 | font: 16px/1.7 Menlo, Consolas, Monaco, 'Andale Mono', monospace; 195 | box-sizing: border-box; 196 | height: auto; 197 | margin: auto; 198 | position: relative; 199 | z-index: 0; 200 | } 201 | .cm-s-mirrormark .CodeMirror-scroll { 202 | height: auto !important; 203 | overflow: visible !important; 204 | padding: 30px 4%; 205 | box-sizing: border-box; 206 | } 207 | .cm-s-mirrormark.CodeMirror-fullscreen .CodeMirror-scroll { 208 | height: 100% !important; 209 | overflow: scroll !important; 210 | } 211 | .cm-s-mirrormark pre.CodeMirror-placeholder { 212 | color: #999; 213 | } 214 | -------------------------------------------------------------------------------- /dist/css/mirrormark.min.css: -------------------------------------------------------------------------------- 1 | .cm-s-mirrormark .cm-m-markdown{color:#000;font-family:sans-serif}.cm-s-mirrormark .cm-m-markdown.cm-header{font-family:Menlo,Consolas,Monaco,'Andale Mono',monospace;font-weight:700}.cm-s-mirrormark .cm-m-markdown.cm-header-1{font-size:28px}.cm-s-mirrormark .cm-m-markdown.cm-header-2{font-size:18px}.cm-s-mirrormark .cm-m-markdown.cm-quote{background-color:rgba(128,128,128,.05);padding:5px}.cm-s-mirrormark .cm-m-markdown.cm-quote,.cm-s-mirrormark .cm-m-markdown.cm-string{color:#888}.cm-s-mirrormark .cm-m-markdown.cm-link,.cm-s-mirrormark .cm-m-markdown.cm-tag{color:#444}.cm-s-mirrormark .cm-m-markdown.cm-comment{color:#a50;font-family:Menlo,Consolas,Monaco,'Andale Mono',monospace}.cm-s-mirrormark .cm-m-markdown.cm-bracket,.cm-s-mirrormark .cm-m-markdown.cm-m-xml{color:#170;font-family:inherit}.cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-scroll{display:none}.cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-preview{display:block}.cm-s-mirrormark.CodeMirror .CodeMirror-preview{display:none;height:auto!important;overflow:visible!important;padding:30px 4%;box-sizing:border-box;font-family:sans-serif}.cm-s-mirrormark.CodeMirror .CodeMirror-preview h1,.cm-s-mirrormark.CodeMirror .CodeMirror-preview h2,.cm-s-mirrormark.CodeMirror .CodeMirror-preview h3{font-fami:Menlo,Consolas,Monaco,'Andale Mono',monospace}.cm-s-mirrormark.CodeMirror .CodeMirror-preview code,.cm-s-mirrormark.CodeMirror .CodeMirror-preview pre{font-family:Menlo,Consolas,Monaco,'Andale Mono',monospace}.cm-s-mirrormark.CodeMirror .CodeMirror-fullscreen .CodeMirror-preview{height:100%!important;overflow:scroll!important}.mirrormark-toolbar li>a{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)}.mirrormark-toolbar li>a.pull-left{margin-right:.3em}.mirrormark-toolbar li>a.pull-right{margin-left:.3em}.mirrormark-toolbar .bold a:before{content:"\f032"}.mirrormark-toolbar .italicize a:before{content:"\f033"}.mirrormark-toolbar .blockquote a:before{content:"\f10d"}.mirrormark-toolbar .strikethrough a:before{content:"\f0cc"}.mirrormark-toolbar .link a:before{content:"\f0c1"}.mirrormark-toolbar .image a:before{content:"\f03e"}.mirrormark-toolbar .unorderedList a:before{content:"\f03a"}.mirrormark-toolbar .orderedList a:before{content:"\f0cb"}.mirrormark-toolbar .fullScreen a:before{content:"\f065"}.mirrormark-toolbar .preview a:before{content:"\f15b"}.cm-s-mirrormark.CodeMirror-fullscreen .mirrormark-toolbar .fullScreen a:before{content:"\f066"}.cm-s-mirrormark.CodeMirror-has-preview .mirrormark-toolbar .preview a:before{content:"\f016"}.mirrormark-toolbar{background:#fff;border-bottom:1px solid rgba(0,0,0,.1);display:block;width:100%;margin-top:0;margin-bottom:0;padding-left:0;padding-right:0}.mirrormark-toolbar li{display:inline-block;position:relative;z-index:1}.mirrormark-toolbar li.fullScreen,.mirrormark-toolbar li.preview{float:right}.mirrormark-toolbar li.has-nested:after{content:'\2304';color:#888;position:absolute;bottom:3px;right:3px;font-size:11px;transform:scale(1.3,1)}.mirrormark-toolbar li a{background-color:#fff;color:#888;cursor:pointer;display:block;font-size:16px;height:40px;line-height:40px;text-align:center;transition:color .2s linear;width:40px}.mirrormark-toolbar li:hover a,.mirrormark-toolbar li:hover.has-nested:after{color:#000}.mirrormark-toolbar li:hover ul{display:block}.mirrormark-toolbar li ul{background:#e5e5e5;display:none;position:absolute;top:41px;width:150px;z-index:1000}.mirrormark-toolbar li ul li{float:none;width:100%}.mirrormark-toolbar li ul li a{background:0 0;color:#888!important;font-family:"Helvetica Neue",sans-serif;font-size:12px;height:auto;line-height:normal;padding:.25rem .75rem;text-align:left;width:auto}.mirrormark-toolbar li ul li:hover a{color:#000!important}.cm-s-mirrormark{font:16px/1.7 Menlo,Consolas,Monaco,'Andale Mono',monospace;box-sizing:border-box;height:auto;margin:auto;position:relative;z-index:0}.cm-s-mirrormark .CodeMirror-scroll{height:auto!important;overflow:visible!important;padding:30px 4%;box-sizing:border-box}.cm-s-mirrormark.CodeMirror-fullscreen .CodeMirror-scroll{height:100%!important;overflow:scroll!important}.cm-s-mirrormark pre.CodeMirror-placeholder{color:#999} -------------------------------------------------------------------------------- /dist/css/mirrormark.package.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | } 9 | 10 | /* PADDING */ 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0; /* Vertical padding around content */ 14 | } 15 | .CodeMirror pre { 16 | padding: 0 4px; /* Horizontal padding of content */ 17 | } 18 | 19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 | background-color: white; /* The little square between H and V scrollbars */ 21 | } 22 | 23 | /* GUTTER */ 24 | 25 | .CodeMirror-gutters { 26 | border-right: 1px solid #ddd; 27 | background-color: #f7f7f7; 28 | white-space: nowrap; 29 | } 30 | .CodeMirror-linenumbers {} 31 | .CodeMirror-linenumber { 32 | padding: 0 3px 0 5px; 33 | min-width: 20px; 34 | text-align: right; 35 | color: #999; 36 | -moz-box-sizing: content-box; 37 | box-sizing: content-box; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror div.CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | } 48 | /* Shown when moving in bi-directional text */ 49 | .CodeMirror div.CodeMirror-secondarycursor { 50 | border-left: 1px solid silver; 51 | } 52 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { 53 | width: auto; 54 | border: 0; 55 | background: #7e7; 56 | } 57 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { 58 | z-index: 1; 59 | } 60 | 61 | .cm-animate-fat-cursor { 62 | width: auto; 63 | border: 0; 64 | -webkit-animation: blink 1.06s steps(1) infinite; 65 | -moz-animation: blink 1.06s steps(1) infinite; 66 | animation: blink 1.06s steps(1) infinite; 67 | } 68 | @-moz-keyframes blink { 69 | 0% { background: #7e7; } 70 | 50% { background: none; } 71 | 100% { background: #7e7; } 72 | } 73 | @-webkit-keyframes blink { 74 | 0% { background: #7e7; } 75 | 50% { background: none; } 76 | 100% { background: #7e7; } 77 | } 78 | @keyframes blink { 79 | 0% { background: #7e7; } 80 | 50% { background: none; } 81 | 100% { background: #7e7; } 82 | } 83 | 84 | /* Can style cursor different in overwrite (non-insert) mode */ 85 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 86 | 87 | .cm-tab { display: inline-block; text-decoration: inherit; } 88 | 89 | .CodeMirror-ruler { 90 | border-left: 1px solid #ccc; 91 | position: absolute; 92 | } 93 | 94 | /* DEFAULT THEME */ 95 | 96 | .cm-s-default .cm-keyword {color: #708;} 97 | .cm-s-default .cm-atom {color: #219;} 98 | .cm-s-default .cm-number {color: #164;} 99 | .cm-s-default .cm-def {color: #00f;} 100 | .cm-s-default .cm-variable, 101 | .cm-s-default .cm-punctuation, 102 | .cm-s-default .cm-property, 103 | .cm-s-default .cm-operator {} 104 | .cm-s-default .cm-variable-2 {color: #05a;} 105 | .cm-s-default .cm-variable-3 {color: #085;} 106 | .cm-s-default .cm-comment {color: #a50;} 107 | .cm-s-default .cm-string {color: #a11;} 108 | .cm-s-default .cm-string-2 {color: #f50;} 109 | .cm-s-default .cm-meta {color: #555;} 110 | .cm-s-default .cm-qualifier {color: #555;} 111 | .cm-s-default .cm-builtin {color: #30a;} 112 | .cm-s-default .cm-bracket {color: #997;} 113 | .cm-s-default .cm-tag {color: #170;} 114 | .cm-s-default .cm-attribute {color: #00c;} 115 | .cm-s-default .cm-header {color: blue;} 116 | .cm-s-default .cm-quote {color: #090;} 117 | .cm-s-default .cm-hr {color: #999;} 118 | .cm-s-default .cm-link {color: #00c;} 119 | 120 | .cm-negative {color: #d44;} 121 | .cm-positive {color: #292;} 122 | .cm-header, .cm-strong {font-weight: bold;} 123 | .cm-em {font-style: italic;} 124 | .cm-link {text-decoration: underline;} 125 | .cm-strikethrough {text-decoration: line-through;} 126 | 127 | .cm-s-default .cm-error {color: #f00;} 128 | .cm-invalidchar {color: #f00;} 129 | 130 | /* Default styles for common addons */ 131 | 132 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 133 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 134 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 135 | .CodeMirror-activeline-background {background: #e8f2ff;} 136 | 137 | /* STOP */ 138 | 139 | /* The rest of this file contains styles related to the mechanics of 140 | the editor. You probably shouldn't touch them. */ 141 | 142 | .CodeMirror { 143 | position: relative; 144 | overflow: hidden; 145 | background: white; 146 | } 147 | 148 | .CodeMirror-scroll { 149 | overflow: scroll !important; /* Things will break if this is overridden */ 150 | /* 30px is the magic margin used to hide the element's real scrollbars */ 151 | /* See overflow: hidden in .CodeMirror */ 152 | margin-bottom: -30px; margin-right: -30px; 153 | padding-bottom: 30px; 154 | height: 100%; 155 | outline: none; /* Prevent dragging from highlighting the element */ 156 | position: relative; 157 | -moz-box-sizing: content-box; 158 | box-sizing: content-box; 159 | } 160 | .CodeMirror-sizer { 161 | position: relative; 162 | border-right: 30px solid transparent; 163 | -moz-box-sizing: content-box; 164 | box-sizing: content-box; 165 | } 166 | 167 | /* The fake, visible scrollbars. Used to force redraw during scrolling 168 | before actuall scrolling happens, thus preventing shaking and 169 | flickering artifacts. */ 170 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 171 | position: absolute; 172 | z-index: 6; 173 | display: none; 174 | } 175 | .CodeMirror-vscrollbar { 176 | right: 0; top: 0; 177 | overflow-x: hidden; 178 | overflow-y: scroll; 179 | } 180 | .CodeMirror-hscrollbar { 181 | bottom: 0; left: 0; 182 | overflow-y: hidden; 183 | overflow-x: scroll; 184 | } 185 | .CodeMirror-scrollbar-filler { 186 | right: 0; bottom: 0; 187 | } 188 | .CodeMirror-gutter-filler { 189 | left: 0; bottom: 0; 190 | } 191 | 192 | .CodeMirror-gutters { 193 | position: absolute; left: 0; top: 0; 194 | z-index: 3; 195 | } 196 | .CodeMirror-gutter { 197 | white-space: normal; 198 | height: 100%; 199 | -moz-box-sizing: content-box; 200 | box-sizing: content-box; 201 | display: inline-block; 202 | margin-bottom: -30px; 203 | /* Hack to make IE7 behave */ 204 | *zoom:1; 205 | *display:inline; 206 | } 207 | .CodeMirror-gutter-wrapper { 208 | position: absolute; 209 | z-index: 4; 210 | height: 100%; 211 | } 212 | .CodeMirror-gutter-elt { 213 | position: absolute; 214 | cursor: default; 215 | z-index: 4; 216 | } 217 | .CodeMirror-gutter-wrapper { 218 | -webkit-user-select: none; 219 | -moz-user-select: none; 220 | user-select: none; 221 | } 222 | 223 | .CodeMirror-lines { 224 | cursor: text; 225 | min-height: 1px; /* prevents collapsing before first draw */ 226 | } 227 | .CodeMirror pre { 228 | /* Reset some styles that the rest of the page might have set */ 229 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 230 | border-width: 0; 231 | background: transparent; 232 | font-family: inherit; 233 | font-size: inherit; 234 | margin: 0; 235 | white-space: pre; 236 | word-wrap: normal; 237 | line-height: inherit; 238 | color: inherit; 239 | z-index: 2; 240 | position: relative; 241 | overflow: visible; 242 | -webkit-tap-highlight-color: transparent; 243 | } 244 | .CodeMirror-wrap pre { 245 | word-wrap: break-word; 246 | white-space: pre-wrap; 247 | word-break: normal; 248 | } 249 | 250 | .CodeMirror-linebackground { 251 | position: absolute; 252 | left: 0; right: 0; top: 0; bottom: 0; 253 | z-index: 0; 254 | } 255 | 256 | .CodeMirror-linewidget { 257 | position: relative; 258 | z-index: 2; 259 | overflow: auto; 260 | } 261 | 262 | .CodeMirror-widget {} 263 | 264 | .CodeMirror-code { 265 | outline: none; 266 | } 267 | 268 | .CodeMirror-measure { 269 | position: absolute; 270 | width: 100%; 271 | height: 0; 272 | overflow: hidden; 273 | visibility: hidden; 274 | } 275 | .CodeMirror-measure pre { position: static; } 276 | 277 | .CodeMirror div.CodeMirror-cursor { 278 | position: absolute; 279 | border-right: none; 280 | width: 0; 281 | } 282 | 283 | div.CodeMirror-cursors { 284 | visibility: hidden; 285 | position: relative; 286 | z-index: 3; 287 | } 288 | .CodeMirror-focused div.CodeMirror-cursors { 289 | visibility: visible; 290 | } 291 | 292 | .CodeMirror-selected { background: #d9d9d9; } 293 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 294 | .CodeMirror-crosshair { cursor: crosshair; } 295 | .CodeMirror ::selection { background: #d7d4f0; } 296 | .CodeMirror ::-moz-selection { background: #d7d4f0; } 297 | 298 | .cm-searching { 299 | background: #ffa; 300 | background: rgba(255, 255, 0, .4); 301 | } 302 | 303 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 304 | .CodeMirror span { *vertical-align: text-bottom; } 305 | 306 | /* Used to force a border model for a node */ 307 | .cm-force-border { padding-right: .1px; } 308 | 309 | @media print { 310 | /* Hide the cursor when printing */ 311 | .CodeMirror div.CodeMirror-cursors { 312 | visibility: hidden; 313 | } 314 | } 315 | 316 | /* See issue #2901 */ 317 | .cm-tab-wrap-hack:after { content: ''; } 318 | 319 | /* Help users use markselection to safely style text background */ 320 | span.CodeMirror-selectedtext { background: none; } 321 | 322 | .cm-s-mirrormark .cm-m-markdown { 323 | color: #000; 324 | font-family: sans-serif; 325 | } 326 | .cm-s-mirrormark .cm-m-markdown.cm-header { 327 | font-family: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 328 | font-weight: bold; 329 | } 330 | .cm-s-mirrormark .cm-m-markdown.cm-header-1 { 331 | font-size: 28px; 332 | } 333 | .cm-s-mirrormark .cm-m-markdown.cm-header-2 { 334 | font-size: 22px; 335 | } 336 | .cm-s-mirrormark .cm-m-markdown.cm-header-2 { 337 | font-size: 18px; 338 | } 339 | .cm-s-mirrormark .cm-m-markdown.cm-quote { 340 | color: #888; 341 | background-color: rgba(128, 128, 128, 0.05); 342 | padding: 5px; 343 | } 344 | .cm-s-mirrormark .cm-m-markdown.cm-quote, 345 | .cm-s-mirrormark .cm-m-markdown.cm-string { 346 | color: #888; 347 | } 348 | .cm-s-mirrormark .cm-m-markdown.cm-tag, 349 | .cm-s-mirrormark .cm-m-markdown.cm-link { 350 | color: #444; 351 | } 352 | .cm-s-mirrormark .cm-m-markdown.cm-comment { 353 | color: #a50; 354 | font-family: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 355 | } 356 | .cm-s-mirrormark .cm-m-markdown.cm-m-xml, 357 | .cm-s-mirrormark .cm-m-markdown.cm-bracket { 358 | color: #170; 359 | font-family: inherit; 360 | } 361 | .cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-scroll { 362 | display: none; 363 | } 364 | .cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-preview { 365 | display: block; 366 | } 367 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview { 368 | display: none; 369 | height: auto !important; 370 | overflow: visible !important; 371 | padding: 30px 4%; 372 | box-sizing: border-box; 373 | font-family: sans-serif; 374 | } 375 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview h1, 376 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview h2, 377 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview h3 { 378 | font-fami: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 379 | } 380 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview pre, 381 | .cm-s-mirrormark.CodeMirror .CodeMirror-preview code { 382 | font-family: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 383 | } 384 | .cm-s-mirrormark.CodeMirror .CodeMirror-fullscreen .CodeMirror-preview { 385 | height: 100% !important; 386 | overflow: scroll !important; 387 | } 388 | .mirrormark-toolbar li > a { 389 | display: inline-block; 390 | font: normal normal normal 14px/1 FontAwesome; 391 | font-size: inherit; 392 | text-rendering: auto; 393 | -webkit-font-smoothing: antialiased; 394 | -moz-osx-font-smoothing: grayscale; 395 | transform: translate(0, 0); 396 | } 397 | .mirrormark-toolbar li > a.pull-left { 398 | margin-right: .3em; 399 | } 400 | .mirrormark-toolbar li > a.pull-right { 401 | margin-left: .3em; 402 | } 403 | .mirrormark-toolbar .bold a:before { 404 | content: "\f032"; 405 | } 406 | .mirrormark-toolbar .italicize a:before { 407 | content: "\f033"; 408 | } 409 | .mirrormark-toolbar .blockquote a:before { 410 | content: "\f10d"; 411 | } 412 | .mirrormark-toolbar .strikethrough a:before { 413 | content: "\f0cc"; 414 | } 415 | .mirrormark-toolbar .link a:before { 416 | content: "\f0c1"; 417 | } 418 | .mirrormark-toolbar .image a:before { 419 | content: "\f03e"; 420 | } 421 | .mirrormark-toolbar .unorderedList a:before { 422 | content: "\f03a"; 423 | } 424 | .mirrormark-toolbar .orderedList a:before { 425 | content: "\f0cb"; 426 | } 427 | .mirrormark-toolbar .fullScreen a:before { 428 | content: "\f065"; 429 | } 430 | .mirrormark-toolbar .preview a:before { 431 | content: "\f15b"; 432 | } 433 | .cm-s-mirrormark.CodeMirror-fullscreen .mirrormark-toolbar .fullScreen a:before { 434 | content: "\f066"; 435 | } 436 | .cm-s-mirrormark.CodeMirror-has-preview .mirrormark-toolbar .preview a:before { 437 | content: "\f016"; 438 | } 439 | /* Toolbar Theme */ 440 | .mirrormark-toolbar { 441 | background: #fff; 442 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 443 | display: block; 444 | width: 100%; 445 | margin-top: 0; 446 | margin-bottom: 0; 447 | padding-left: 0; 448 | padding-right: 0; 449 | /* First level */ 450 | } 451 | .mirrormark-toolbar li { 452 | display: inline-block; 453 | position: relative; 454 | z-index: 1; 455 | } 456 | .mirrormark-toolbar li.fullScreen, 457 | .mirrormark-toolbar li.preview { 458 | float: right; 459 | } 460 | .mirrormark-toolbar li.has-nested:after { 461 | content: '\2304'; 462 | color: #888; 463 | position: absolute; 464 | bottom: 3px; 465 | right: 3px; 466 | font-size: 11px; 467 | transform: scale(1.3, 1); 468 | } 469 | .mirrormark-toolbar li a { 470 | background-color: #fff; 471 | color: #888; 472 | cursor: pointer; 473 | display: block; 474 | font-size: 16px; 475 | height: 40px; 476 | line-height: 40px; 477 | text-align: center; 478 | transition: color .2s linear; 479 | width: 40px; 480 | } 481 | .mirrormark-toolbar li:hover a, 482 | .mirrormark-toolbar li:hover.has-nested:after { 483 | color: #000; 484 | } 485 | .mirrormark-toolbar li:hover ul { 486 | display: block; 487 | } 488 | .mirrormark-toolbar li ul { 489 | background: #e5e5e5; 490 | display: none; 491 | position: absolute; 492 | top: 41px; 493 | width: 150px; 494 | z-index: 1000; 495 | } 496 | .mirrormark-toolbar li ul li { 497 | float: none; 498 | width: 100%; 499 | } 500 | .mirrormark-toolbar li ul li a { 501 | background: transparent; 502 | color: #888 !important; 503 | font-family: "Helvetica Neue", sans-serif; 504 | font-size: 12px; 505 | height: auto; 506 | line-height: normal; 507 | padding: .25rem .75rem; 508 | text-align: left; 509 | width: auto; 510 | } 511 | .mirrormark-toolbar li ul li:hover a { 512 | color: black !important; 513 | } 514 | .cm-s-mirrormark { 515 | font: 16px/1.7 Menlo, Consolas, Monaco, 'Andale Mono', monospace; 516 | box-sizing: border-box; 517 | height: auto; 518 | margin: auto; 519 | position: relative; 520 | z-index: 0; 521 | } 522 | .cm-s-mirrormark .CodeMirror-scroll { 523 | height: auto !important; 524 | overflow: visible !important; 525 | padding: 30px 4%; 526 | box-sizing: border-box; 527 | } 528 | .cm-s-mirrormark.CodeMirror-fullscreen .CodeMirror-scroll { 529 | height: 100% !important; 530 | overflow: scroll !important; 531 | } 532 | .cm-s-mirrormark pre.CodeMirror-placeholder { 533 | color: #999; 534 | } 535 | 536 | .CodeMirror-fullscreen { 537 | position: fixed; 538 | top: 0; left: 0; right: 0; bottom: 0; 539 | height: auto; 540 | z-index: 9; 541 | } 542 | 543 | .CodeMirror-dialog { 544 | position: absolute; 545 | left: 0; right: 0; 546 | background: white; 547 | z-index: 15; 548 | padding: .1em .8em; 549 | overflow: hidden; 550 | color: #333; 551 | } 552 | 553 | .CodeMirror-dialog-top { 554 | border-bottom: 1px solid #eee; 555 | top: 0; 556 | } 557 | 558 | .CodeMirror-dialog-bottom { 559 | border-top: 1px solid #eee; 560 | bottom: 0; 561 | } 562 | 563 | .CodeMirror-dialog input { 564 | border: none; 565 | outline: none; 566 | background: transparent; 567 | width: 20em; 568 | color: inherit; 569 | font-family: monospace; 570 | } 571 | 572 | .CodeMirror-dialog button { 573 | font-size: 70%; 574 | } 575 | 576 | .CodeMirror-search-match { 577 | background: gold; 578 | border-top: 1px solid orange; 579 | border-bottom: 1px solid orange; 580 | -moz-box-sizing: border-box; 581 | box-sizing: border-box; 582 | opacity: .5; 583 | } 584 | -------------------------------------------------------------------------------- /dist/css/mirrormark.package.min.css: -------------------------------------------------------------------------------- 1 | .CodeMirror{font-family:monospace;height:300px;color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror div.CodeMirror-cursor{border-left:1px solid #000}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.CodeMirror.cm-fat-cursor div.CodeMirror-cursor{width:auto;border:0;background:#7e7}.CodeMirror.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1)infinite;-moz-animation:blink 1.06s steps(1)infinite;animation:blink 1.06s steps(1)infinite}@-moz-keyframes blink{0%,100%{background:#7e7}50%{background:0 0}}@-webkit-keyframes blink{0%,100%{background:#7e7}50%{background:0 0}}@keyframes blink{0%,100%{background:#7e7}50%{background:0 0}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-invalidchar,.cm-s-default .cm-error{color:red}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative;-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-sizer{position:relative;border-right:30px solid transparent;-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;-moz-box-sizing:content-box;box-sizing:content-box;display:inline-block;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-code{outline:0}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-measure pre{position:static}.CodeMirror div.CodeMirror-cursor{position:absolute;border-right:none;width:0}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror ::selection,.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror ::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.cm-s-mirrormark .cm-m-markdown{color:#000;font-family:sans-serif}.cm-s-mirrormark .cm-m-markdown.cm-header{font-family:Menlo,Consolas,Monaco,'Andale Mono',monospace;font-weight:700}.cm-s-mirrormark .cm-m-markdown.cm-header-1{font-size:28px}.cm-s-mirrormark .cm-m-markdown.cm-header-2{font-size:18px}.cm-s-mirrormark .cm-m-markdown.cm-quote{background-color:rgba(128,128,128,.05);padding:5px}.cm-s-mirrormark .cm-m-markdown.cm-quote,.cm-s-mirrormark .cm-m-markdown.cm-string{color:#888}.cm-s-mirrormark .cm-m-markdown.cm-link,.cm-s-mirrormark .cm-m-markdown.cm-tag{color:#444}.cm-s-mirrormark .cm-m-markdown.cm-comment{color:#a50;font-family:Menlo,Consolas,Monaco,'Andale Mono',monospace}.cm-s-mirrormark .cm-m-markdown.cm-bracket,.cm-s-mirrormark .cm-m-markdown.cm-m-xml{color:#170;font-family:inherit}.cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-scroll{display:none}.cm-s-mirrormark.CodeMirror.CodeMirror-has-preview .CodeMirror-preview{display:block}.cm-s-mirrormark.CodeMirror .CodeMirror-preview{display:none;height:auto!important;overflow:visible!important;padding:30px 4%;box-sizing:border-box;font-family:sans-serif}.cm-s-mirrormark.CodeMirror .CodeMirror-preview h1,.cm-s-mirrormark.CodeMirror .CodeMirror-preview h2,.cm-s-mirrormark.CodeMirror .CodeMirror-preview h3{font-fami:Menlo,Consolas,Monaco,'Andale Mono',monospace}.cm-s-mirrormark.CodeMirror .CodeMirror-preview code,.cm-s-mirrormark.CodeMirror .CodeMirror-preview pre{font-family:Menlo,Consolas,Monaco,'Andale Mono',monospace}.cm-s-mirrormark.CodeMirror .CodeMirror-fullscreen .CodeMirror-preview{height:100%!important;overflow:scroll!important}.mirrormark-toolbar li>a{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)}.mirrormark-toolbar li>a.pull-left{margin-right:.3em}.mirrormark-toolbar li>a.pull-right{margin-left:.3em}.mirrormark-toolbar .bold a:before{content:"\f032"}.mirrormark-toolbar .italicize a:before{content:"\f033"}.mirrormark-toolbar .blockquote a:before{content:"\f10d"}.mirrormark-toolbar .strikethrough a:before{content:"\f0cc"}.mirrormark-toolbar .link a:before{content:"\f0c1"}.mirrormark-toolbar .image a:before{content:"\f03e"}.mirrormark-toolbar .unorderedList a:before{content:"\f03a"}.mirrormark-toolbar .orderedList a:before{content:"\f0cb"}.mirrormark-toolbar .fullScreen a:before{content:"\f065"}.mirrormark-toolbar .preview a:before{content:"\f15b"}.cm-s-mirrormark.CodeMirror-fullscreen .mirrormark-toolbar .fullScreen a:before{content:"\f066"}.cm-s-mirrormark.CodeMirror-has-preview .mirrormark-toolbar .preview a:before{content:"\f016"}.mirrormark-toolbar{background:#fff;border-bottom:1px solid rgba(0,0,0,.1);display:block;width:100%;margin-top:0;margin-bottom:0;padding-left:0;padding-right:0}.mirrormark-toolbar li{display:inline-block;position:relative;z-index:1}.mirrormark-toolbar li.fullScreen,.mirrormark-toolbar li.preview{float:right}.mirrormark-toolbar li.has-nested:after{content:'\2304';color:#888;position:absolute;bottom:3px;right:3px;font-size:11px;transform:scale(1.3,1)}.mirrormark-toolbar li a{background-color:#fff;color:#888;cursor:pointer;display:block;font-size:16px;height:40px;line-height:40px;text-align:center;transition:color .2s linear;width:40px}.mirrormark-toolbar li:hover a,.mirrormark-toolbar li:hover.has-nested:after{color:#000}.mirrormark-toolbar li:hover ul{display:block}.mirrormark-toolbar li ul{background:#e5e5e5;display:none;position:absolute;top:41px;width:150px;z-index:1000}.mirrormark-toolbar li ul li{float:none;width:100%}.mirrormark-toolbar li ul li a{background:0 0;color:#888!important;font-family:"Helvetica Neue",sans-serif;font-size:12px;height:auto;line-height:normal;padding:.25rem .75rem;text-align:left;width:auto}.mirrormark-toolbar li ul li:hover a{color:#000!important}.cm-s-mirrormark{font:16px/1.7 Menlo,Consolas,Monaco,'Andale Mono',monospace;box-sizing:border-box;height:auto;margin:auto;position:relative;z-index:0}.cm-s-mirrormark .CodeMirror-scroll{height:auto!important;overflow:visible!important;padding:30px 4%;box-sizing:border-box}.cm-s-mirrormark.CodeMirror-fullscreen .CodeMirror-scroll{height:100%!important;overflow:scroll!important}.cm-s-mirrormark pre.CodeMirror-placeholder{color:#999}.CodeMirror-fullscreen{position:fixed;top:0;left:0;right:0;bottom:0;height:auto;z-index:9}.CodeMirror-dialog{position:absolute;left:0;right:0;background:#fff;z-index:15;padding:.1em .8em;overflow:hidden;color:#333}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:none;outline:0;background:0 0;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}.CodeMirror-search-match{background:gold;border-top:1px solid orange;border-bottom:1px solid orange;-moz-box-sizing:border-box;box-sizing:border-box;opacity:.5} -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MirrorMark - Simple, yet extensible. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

MirrorMark - Simple, yet extensible.

20 |

Built on the sheer power of the amazing CodeMirror

21 |
22 |
23 |
24 | 68 |
69 |
70 |
71 |

Another Textarea

72 |
73 |
74 |
75 | 120 |
121 |
122 | 123 |
124 | MirrorMark built by Andrew Del Prete at Musicbed 125 |
126 | 127 | 128 | 129 | 130 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /dist/js/mirrormark.js: -------------------------------------------------------------------------------- 1 | (function(CodeMirror, Markdown) { 2 | "use strict"; 3 | 4 | var converter = new Markdown.Converter(); 5 | Markdown.Extra.init(converter); 6 | 7 | CodeMirror.defineOption("preview", false, function(cm, val, old) { 8 | if (old == CodeMirror.Init) old = false; 9 | if (!old == !val) return; 10 | if (val) { 11 | setPreview(cm); 12 | } else { 13 | setNormal(cm); 14 | } 15 | }); 16 | 17 | function setPreview(cm) { 18 | var wrap = cm.getWrapperElement(); 19 | wrap.className += " CodeMirror-has-preview"; 20 | 21 | refreshPreview(wrap, cm); 22 | } 23 | 24 | function refreshPreview(wrap, cm) { 25 | var previewNodes = wrap.getElementsByClassName("CodeMirror-preview"); 26 | var previewNode; 27 | if(previewNodes.length == 0) { 28 | var previewNode = document.createElement('div'); 29 | previewNode.className = "CodeMirror-preview"; 30 | wrap.appendChild(previewNode); 31 | } else { 32 | previewNode = previewNodes[0]; 33 | } 34 | previewNode.innerHTML = converter.makeHtml(cm.getValue()); 35 | } 36 | 37 | function setNormal(cm) { 38 | var wrap = cm.getWrapperElement(); 39 | wrap.className = wrap.className.replace(/\s*CodeMirror-has-preview\b/, ""); 40 | cm.refresh(); 41 | } 42 | })(CodeMirror, Markdown); 43 | 44 | /** 45 | * @license MirrorMark v0.1 46 | * (c) 2015 Musicbed http://www.musicbed.com 47 | * License: MIT 48 | */ 49 | (function(CodeMirror) { 'use strict'; 50 | /** 51 | * Bootstrap our module 52 | */ 53 | (function(fn) { 54 | if (typeof exports == "object" && typeof module == "object") { // CommonJS 55 | module.exports = fn; 56 | } else if (typeof define == "function" && define.amd) { // AMD 57 | return define([], fn); 58 | } 59 | 60 | if (window) window.mirrorMark = fn 61 | })(mirrorMark); 62 | 63 | /** 64 | * Merge 65 | * 66 | * 67 | * @param {Object} object The object to merge into 68 | * @param {Object/Array} source The object or array of objects to merge 69 | * @return {Object} The original object 70 | */ 71 | function merge(object, source) { 72 | if(Array.isArray(source)) { 73 | for(var i = sources.length - 1; i >= 0; i--) { 74 | merge(object, source[i]); 75 | } 76 | } else { 77 | for (var attrname in source) { 78 | object[attrname] = source[attrname]; 79 | } 80 | } 81 | 82 | return object; 83 | } 84 | 85 | /** 86 | * Our delegate prototype used by our factory 87 | * @type {Object} 88 | */ 89 | var mirrorMarkProto = { 90 | 91 | /** 92 | * Render the component 93 | */ 94 | render: function render() { 95 | this.registerKeyMaps(this.keyMaps); 96 | this.cm = CodeMirror.fromTextArea(this.element, this.options); 97 | 98 | if (this.options.showToolbar) { 99 | this.setToolbar(this.tools); 100 | } 101 | }, 102 | 103 | /** 104 | * Setup the toolbar 105 | */ 106 | setToolbar: function setToolbar(tools) { 107 | 108 | var toolbar = document.createElement('ul'); 109 | toolbar.className = this.options.theme + '-' + 'toolbar'; 110 | 111 | var tools = this.generateToolList(tools); 112 | 113 | tools.forEach(function(tool) { 114 | toolbar.appendChild(tool) 115 | }); 116 | 117 | var cmWrapper = this.cm.getWrapperElement(); 118 | cmWrapper.insertBefore(toolbar, cmWrapper.firstChild); 119 | }, 120 | 121 | /** 122 | * Register Keymaps by extending the extraKeys object 123 | * @param {Object} keyMaps 124 | */ 125 | registerKeyMaps: function registerKeyMaps(keyMaps) { 126 | for (var name in keyMaps) { 127 | if (typeof(this.actions[keyMaps[name]]) !== 'function') throw "MirrorMark - '" + keyMaps[name] + "' is not a registered action"; 128 | 129 | var realName = name.replace("Cmd-", (CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault) ? "Cmd-" : "Ctrl-"); 130 | this.options.extraKeys[realName] = this.actions[keyMaps[name]].bind(this) 131 | } 132 | }, 133 | 134 | 135 | /** 136 | * Register actions by extending the default actions 137 | * @param {Object} actions [description] 138 | */ 139 | registerActions: function registerActions(actions) { 140 | return merge(this.actions, actions); 141 | }, 142 | 143 | 144 | /** 145 | * Register tools by extending and overwriting the default tools 146 | * @param {Array} tools 147 | * @param {Bool} replace - replace the default tools with the ones provided. Defaults to false. 148 | */ 149 | registerTools: function registerTools(tools, replace) { 150 | for (var action in tools) { 151 | if (this.actions[tools[action].action] && typeof(this.actions[tools[action].action]) !== 'function') throw "MirrorMark - '" + tools[action].action + "' is not a registered action"; 152 | } 153 | 154 | if (replace) { 155 | this.tools = tools; 156 | return; 157 | } 158 | 159 | this.tools = this.tools.concat(tools) 160 | }, 161 | 162 | /** 163 | * A recursive function to generate and return an unordered list of tools 164 | * @param {Object} 165 | */ 166 | generateToolList: function generateToolList(tools) { 167 | return tools.map(function(tool) { 168 | var item = document.createElement("li"), 169 | anchor = document.createElement("a"); 170 | 171 | item.className = tool.name; 172 | 173 | if (tool.className) { 174 | anchor.className = tool.className; 175 | } 176 | 177 | if (tool.showName) { 178 | var text = document.createTextNode(tool.name); 179 | anchor.appendChild(text); 180 | } 181 | 182 | if (tool.action) { 183 | anchor.onclick = function(e) { 184 | this.cm.focus(); 185 | this.actions[tool.action].call(this); 186 | }.bind(this); 187 | } 188 | 189 | item.appendChild(anchor); 190 | 191 | if (tool.nested) { 192 | item.className += " has-nested"; 193 | var ul = document.createElement('ul'); 194 | ul.className = this.options.theme + "-toolbar-list" 195 | var nested = generateToolList.call(this, tool.nested); 196 | nested.forEach(function(nestedItem) { 197 | ul.appendChild(nestedItem); 198 | }); 199 | 200 | item.appendChild(ul); 201 | } 202 | 203 | return item 204 | 205 | }.bind(this)); 206 | }, 207 | 208 | /** 209 | * Default Tools in Toolbar 210 | */ 211 | tools: [ 212 | { name: "bold", action: "bold" }, 213 | { name: "italicize", action: "italicize" }, 214 | { name: "blockquote", action: "blockquote" }, 215 | { name: "strikethrough", action: "strikethrough" }, 216 | { name: "link", action: "link" }, 217 | { name: "image", action: "image" }, 218 | { name: "unorderedList", action: "unorderedList" }, 219 | { name: "orderedList", action: "orderedList" }, 220 | { name: "fullScreen", action: "fullScreen" }, 221 | { name: "preview", action: "preview" }, 222 | ], 223 | 224 | /** 225 | * Default Keymaps 226 | * @type {Object} 227 | */ 228 | keyMaps: { 229 | "Cmd-B": 'bold', 230 | "Cmd-I": 'italicize', 231 | "Cmd-'": 'blockquote', 232 | "Cmd-Alt-L": 'orderedList', 233 | "Cmd-L": 'unorderedList', 234 | "Cmd-Alt-I": 'image', 235 | "Cmd-H": 'hr', 236 | "Cmd-K": 'link', 237 | "F11": "fullScreen", 238 | "Esc": "exitFullScreen", 239 | }, 240 | 241 | /** 242 | * Default Actions 243 | * @type {Object} 244 | */ 245 | actions: { 246 | bold: function () { 247 | this.toggleAround('**', '**') 248 | }, 249 | italicize: function () { 250 | this.toggleAround('*', '*') 251 | }, 252 | strikethrough: function () { 253 | this.toggleAround('~~', '~~') 254 | }, 255 | "code": function () { 256 | this.toggleAround('```\r\n', '\r\n```') 257 | }, 258 | "blockquote": function () { 259 | this.toggleBefore('> '); 260 | }, 261 | "orderedList": function () { 262 | this.toggleBefore('1. '); 263 | }, 264 | "unorderedList": function () { 265 | this.toggleBefore('* '); 266 | }, 267 | "image": function () { 268 | this.toggleAround('![', '](http://)'); 269 | }, 270 | "link": function () { 271 | this.toggleAround('[', '](http://)'); 272 | }, 273 | "hr": function () { 274 | this.insert('---'); 275 | }, 276 | "fullScreen": function () { 277 | var fullScreen = !this.cm.getOption("fullScreen"); 278 | 279 | // You must turn off scrollPastEnd on after going full screen 280 | // and before exiting it 281 | if(!fullScreen) this.cm.setOption("scrollPastEnd", fullScreen) 282 | this.cm.setOption("fullScreen", fullScreen); 283 | if(fullScreen) this.cm.setOption("scrollPastEnd", fullScreen) 284 | }, 285 | "exitFullScreen": function() { 286 | if (this.cm.getOption("fullScreen")) { 287 | this.cm.setOption("scrollPastEnd", fullScreen) 288 | this.cm.setOption("fullScreen", false); 289 | } 290 | }, 291 | "preview": function() { 292 | this.cm.setOption("preview", !this.cm.getOption("preview")); 293 | } 294 | }, 295 | 296 | /** 297 | * Insert a string at cursor position 298 | * @param {String} insertion 299 | */ 300 | insert: function insert(insertion) { 301 | var doc = this.cm.getDoc(); 302 | var cursor = doc.getCursor(); 303 | 304 | doc.replaceRange(insertion, { line: cursor.line, ch: cursor.ch }); 305 | }, 306 | 307 | /** 308 | * Toggle a string at the start and end of a selection 309 | * @param {String} start Start string to wrap 310 | * @param {String} end End string to wrap 311 | */ 312 | toggleAround: function toggleAround(start, end) { 313 | var doc = this.cm.getDoc(); 314 | var cursor = doc.getCursor(); 315 | 316 | if (doc.somethingSelected()) { 317 | var selection = doc.getSelection(); 318 | if(selection.startsWith(start) && selection.endsWith(end)) { 319 | doc.replaceSelection(selection.substring(start.length, selection.length - end.length), "around"); 320 | } else { 321 | doc.replaceSelection(start + selection + end, "around"); 322 | } 323 | } else { 324 | // If no selection then insert start and end args and set cursor position between the two. 325 | doc.replaceRange(start + end, { line: cursor.line, ch: cursor.ch }); 326 | doc.setCursor({ line: cursor.line, ch: cursor.ch + start.length }) 327 | } 328 | }, 329 | 330 | /** 331 | * Toggle a string before a selection 332 | * @param {String} insertion String to insert 333 | */ 334 | toggleBefore: function toggleBefore(insertion) { 335 | var doc = this.cm.getDoc(); 336 | var cursor = doc.getCursor(); 337 | 338 | if (doc.somethingSelected()) { 339 | var selections = doc.listSelections(); 340 | var remove = null; 341 | this.cm.operation(function() { 342 | selections.forEach(function(selection) { 343 | var pos = [selection.head.line, selection.anchor.line].sort(); 344 | 345 | // Remove if the first text starts with it 346 | if(remove == null) { 347 | remove = doc.getLine(pos[0]).startsWith(insertion); 348 | } 349 | 350 | for (var i = pos[0]; i <= pos[1]; i++) { 351 | if(remove) { 352 | // Don't remove if we don't start with it 353 | if(doc.getLine(i).startsWith(insertion)) { 354 | doc.replaceRange("", { line: i, ch: 0 }, {line: i, ch: insertion.length}); 355 | } 356 | } else { 357 | doc.replaceRange(insertion, { line: i, ch: 0 }); 358 | } 359 | } 360 | }); 361 | }); 362 | } else { 363 | var line = cursor.line; 364 | if(doc.getLine(line).startsWith(insertion)) { 365 | doc.replaceRange("", { line: line, ch: 0 }, {line: line, ch: insertion.length}); 366 | } else { 367 | doc.replaceRange(insertion, { line: line, ch: 0 }); 368 | } 369 | 370 | } 371 | } 372 | } 373 | 374 | /** 375 | * Our Factory 376 | * @param {Object} element 377 | * @param {Object} options 378 | * @return {Object} 379 | */ 380 | function mirrorMark(element, options) { 381 | 382 | // Defaults 383 | var defaults = { 384 | mode: 'gfm', 385 | theme: 'default mirrormark', 386 | tabSize: '2', 387 | indentWithTabs: true, 388 | lineWrapping: true, 389 | autoCloseBrackets: true, 390 | autoCloseTags: true, 391 | addModeClass: true, 392 | showToolbar: true, 393 | extraKeys: { 394 | "Enter": 'newlineAndIndentContinueMarkdownList', 395 | }, 396 | } 397 | 398 | // Extend our defaults with the options provided 399 | merge(defaults, options); 400 | 401 | return merge(Object.create(mirrorMarkProto), { element: element, options: defaults }); 402 | } 403 | 404 | })(window.CodeMirror); 405 | -------------------------------------------------------------------------------- /dist/js/mirrormark.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"use strict";function n(e){var t=e.getWrapperElement();t.className+=" CodeMirror-has-preview",i(t,e)}function i(e,t){var n,i=e.getElementsByClassName("CodeMirror-preview");if(0==i.length){var n=document.createElement("div");n.className="CodeMirror-preview",e.appendChild(n)}else n=i[0];n.innerHTML=r.makeHtml(t.getValue())}function o(e){var t=e.getWrapperElement();t.className=t.className.replace(/\s*CodeMirror-has-preview\b/,""),e.refresh()}var r=new t.Converter;t.Extra.init(r),e.defineOption("preview",!1,function(t,i,r){r==e.Init&&(r=!1),!r!=!i&&(i?n(t):o(t))})}(CodeMirror,Markdown),function(e){"use strict";function t(e,n){if(Array.isArray(n))for(var i=sources.length-1;i>=0;i--)t(e,n[i]);else for(var o in n)e[o]=n[o];return e}function n(e,n){var o={mode:"gfm",theme:"default mirrormark",tabSize:"2",indentWithTabs:!0,lineWrapping:!0,autoCloseBrackets:!0,autoCloseTags:!0,addModeClass:!0,showToolbar:!0,extraKeys:{Enter:"newlineAndIndentContinueMarkdownList"}};return t(o,n),t(Object.create(i),{element:e,options:o})}!function(e){if("object"==typeof exports&&"object"==typeof module)module.exports=e;else if("function"==typeof define&&define.amd)return define([],e);window&&(window.mirrorMark=e)}(n);var i={render:function(){this.registerKeyMaps(this.keyMaps),this.cm=e.fromTextArea(this.element,this.options),this.options.showToolbar&&this.setToolbar(this.tools)},setToolbar:function(e){var t=document.createElement("ul");t.className=this.options.theme+"-toolbar";var e=this.generateToolList(e);e.forEach(function(e){t.appendChild(e)});var n=this.cm.getWrapperElement();n.insertBefore(t,n.firstChild)},registerKeyMaps:function(t){for(var n in t){if("function"!=typeof this.actions[t[n]])throw"MirrorMark - '"+t[n]+"' is not a registered action";var i=n.replace("Cmd-",e.keyMap["default"]==e.keyMap.macDefault?"Cmd-":"Ctrl-");this.options.extraKeys[i]=this.actions[t[n]].bind(this)}},registerActions:function(e){return t(this.actions,e)},registerTools:function(e,t){for(var n in e)if(this.actions[e[n].action]&&"function"!=typeof this.actions[e[n].action])throw"MirrorMark - '"+e[n].action+"' is not a registered action";return t?void(this.tools=e):void(this.tools=this.tools.concat(e))},generateToolList:function o(e){return e.map(function(e){var t=document.createElement("li"),n=document.createElement("a");if(t.className=e.name,e.className&&(n.className=e.className),e.showName){var i=document.createTextNode(e.name);n.appendChild(i)}if(e.action&&(n.onclick=function(t){this.cm.focus(),this.actions[e.action].call(this)}.bind(this)),t.appendChild(n),e.nested){t.className+=" has-nested";var r=document.createElement("ul");r.className=this.options.theme+"-toolbar-list";var s=o.call(this,e.nested);s.forEach(function(e){r.appendChild(e)}),t.appendChild(r)}return t}.bind(this))},tools:[{name:"bold",action:"bold"},{name:"italicize",action:"italicize"},{name:"blockquote",action:"blockquote"},{name:"strikethrough",action:"strikethrough"},{name:"link",action:"link"},{name:"image",action:"image"},{name:"unorderedList",action:"unorderedList"},{name:"orderedList",action:"orderedList"},{name:"fullScreen",action:"fullScreen"},{name:"preview",action:"preview"}],keyMaps:{"Cmd-B":"bold","Cmd-I":"italicize","Cmd-'":"blockquote","Cmd-Alt-L":"orderedList","Cmd-L":"unorderedList","Cmd-Alt-I":"image","Cmd-H":"hr","Cmd-K":"link",F11:"fullScreen",Esc:"exitFullScreen"},actions:{bold:function(){this.toggleAround("**","**")},italicize:function(){this.toggleAround("*","*")},strikethrough:function(){this.toggleAround("~~","~~")},code:function(){this.toggleAround("```\r\n","\r\n```")},blockquote:function(){this.toggleBefore("> ")},orderedList:function(){this.toggleBefore("1. ")},unorderedList:function(){this.toggleBefore("* ")},image:function(){this.toggleAround("![","](http://)")},link:function(){this.toggleAround("[","](http://)")},hr:function(){this.insert("---")},fullScreen:function(){var e=!this.cm.getOption("fullScreen");e||this.cm.setOption("scrollPastEnd",e),this.cm.setOption("fullScreen",e),e&&this.cm.setOption("scrollPastEnd",e)},exitFullScreen:function(){this.cm.getOption("fullScreen")&&(this.cm.setOption("scrollPastEnd",fullScreen),this.cm.setOption("fullScreen",!1))},preview:function(){this.cm.setOption("preview",!this.cm.getOption("preview"))}},insert:function(e){var t=this.cm.getDoc(),n=t.getCursor();t.replaceRange(e,{line:n.line,ch:n.ch})},toggleAround:function(e,t){var n=this.cm.getDoc(),i=n.getCursor();if(n.somethingSelected()){var o=n.getSelection();o.startsWith(e)&&o.endsWith(t)?n.replaceSelection(o.substring(e.length,o.length-t.length),"around"):n.replaceSelection(e+o+t,"around")}else n.replaceRange(e+t,{line:i.line,ch:i.ch}),n.setCursor({line:i.line,ch:i.ch+e.length})},toggleBefore:function(e){var t=this.cm.getDoc(),n=t.getCursor();if(t.somethingSelected()){var i=t.listSelections(),o=null;this.cm.operation(function(){i.forEach(function(n){var i=[n.head.line,n.anchor.line].sort();null==o&&(o=t.getLine(i[0]).startsWith(e));for(var r=i[0];r<=i[1];r++)o?t.getLine(r).startsWith(e)&&t.replaceRange("",{line:r,ch:0},{line:r,ch:e.length}):t.replaceRange(e,{line:r,ch:0})})})}else{var r=n.line;t.getLine(r).startsWith(e)?t.replaceRange("",{line:r,ch:0},{line:r,ch:e.length}):t.replaceRange(e,{line:r,ch:0})}}}}(window.CodeMirror); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | del = require('del'), // Used to clean files 3 | rename = require('gulp-rename'), // Used to rename *.* to *.min.* 4 | runSequence = require('run-sequence'), // Don't release files before cleaning 5 | subtree = require('gulp-subtree'), // Used to commit to gh-pages 6 | shell = require('gulp-shell'), 7 | 8 | // Javascript 9 | concat = require('gulp-concat'), 10 | uglify = require('gulp-uglify'), 11 | 12 | // Styles 13 | less = require('gulp-less'), 14 | minifycss = require('gulp-minify-css'); 15 | 16 | 17 | // Directories 18 | var SRC = './src/', 19 | BUILD = './build/', 20 | DIST = './dist/', 21 | BOWER = './bower_components/'; 22 | 23 | var languages = [ 24 | 'markdown', 25 | 'gfm', 26 | 27 | // Syntax highlighting 28 | 'clike', 29 | 'css', 30 | 'htmlmixed', 31 | 'javascript', 32 | 'lua', 33 | 'xml', 34 | ]; 35 | 36 | var addons = [ 37 | "display/fullscreen", // Fullscreen support 38 | 'edit/continuelist', // Nicer lists 39 | 'mode/overlay', // Required for GFM 40 | "comment/comment", 41 | 42 | "display/placeholder", // Pretty placeholder 43 | 44 | // Useful for code blocks 45 | "edit/closebrackets", 46 | "edit/closetag", 47 | 48 | // Find and replace 49 | "dialog/dialog", 50 | "scroll/annotatescrollbar", 51 | "scroll/scrollpastend", 52 | "search/matchesonscrollbar", 53 | "search/search", 54 | "search/searchcursor", 55 | ]; 56 | 57 | gulp.task('clean', function (cb) { 58 | del(DIST + "**/*", cb); 59 | }); 60 | 61 | gulp.task('copy', function() { 62 | return gulp.src([ 63 | SRC + '**/*.css', 64 | SRC + 'index.html', 65 | ]).pipe(gulp.dest(DIST)); 66 | }); 67 | 68 | gulp.task('css:less', function() { 69 | return gulp.src(SRC + 'css/mirrormark.less') 70 | .pipe(less({ 71 | paths: [ BOWER ] 72 | })) 73 | .pipe(gulp.dest(DIST + 'css')); 74 | }) 75 | 76 | gulp.task('css:package', ['css:less'], function() { 77 | var files = [ 78 | BOWER + 'codemirror/lib/codemirror.css', 79 | DIST + 'css/mirrormark.css', 80 | ]; 81 | [].push.apply(files, addons.map(function(path) { 82 | return BOWER + "codemirror/addon/" + path + ".css"; 83 | })); 84 | 85 | return gulp.src(files) 86 | .pipe(concat('mirrormark.package.css')) 87 | .pipe(gulp.dest(DIST + 'css')); 88 | }); 89 | 90 | gulp.task('css:minify', ['css:package'], function() { 91 | return gulp.src([ 92 | DIST + 'css/mirrormark.css', 93 | DIST + 'css/mirrormark.package.css' 94 | ]) 95 | .pipe(minifycss()) 96 | .pipe(rename({ suffix: '.min' })) 97 | .pipe(gulp.dest(DIST + 'css')); 98 | }); 99 | 100 | /** 101 | * Combine to produce the MirrorMark sources 102 | */ 103 | gulp.task('js:combine', function() { 104 | return gulp.src( [ 105 | SRC + 'js/preview.js', 106 | SRC + 'js/mirrormark.js', 107 | ]) 108 | .pipe(concat('mirrormark.js')) 109 | .pipe(gulp.dest(DIST + 'js')); 110 | }); 111 | 112 | /** 113 | * Combine all files together 114 | */ 115 | gulp.task('js:package', function() { 116 | var files = [ 117 | BOWER + 'codemirror/lib/codemirror.js', 118 | BOWER + 'codemirror/mode/meta.js', 119 | 120 | // Preview 121 | BOWER + 'pagedown-extra/pagedown/Markdown.Converter.js', 122 | BOWER + 'pagedown-extra/Markdown.Extra.js', 123 | SRC + 'js/preview.js', 124 | 125 | SRC + 'js/mirrormark.js', 126 | ]; 127 | [].push.apply(files, languages.map(function(path) { 128 | return BOWER + "codemirror/mode/" + path + "/" + path + ".js"; 129 | })); 130 | [].push.apply(files, addons.map(function(path) { 131 | return BOWER + "codemirror/addon/" + path + ".js"; 132 | })); 133 | 134 | return gulp.src(files) 135 | .pipe(concat('mirrormark.package.js')) 136 | .pipe(gulp.dest(DIST + 'js')); 137 | }); 138 | 139 | /** 140 | * Minify everything 141 | */ 142 | gulp.task('js:minify', ['js:package', 'js:combine'], function() { 143 | return gulp.src([ 144 | DIST + 'js/mirrormark.js', 145 | DIST + 'js/mirrormark.package.js' 146 | ]) 147 | .pipe(uglify()) 148 | .pipe(rename({ 149 | suffix: '.min' 150 | })) 151 | .pipe(gulp.dest(DIST + 'js')); 152 | }); 153 | 154 | 155 | gulp.task('js', ['js:combine', 'js:package', 'js:minify']); 156 | gulp.task('css', ['css:package', 'css:minify']); 157 | 158 | gulp.task('build', function(callback) { 159 | // We should clean before anything else, everything else doesn't matter 160 | return runSequence('clean', 'copy', 'js', 'css', callback); 161 | }); 162 | 163 | // Just build it 164 | gulp.task('default', ['build']); 165 | 166 | // Commit dist 167 | gulp.task('release', ['build'], shell.task([ 168 | 'git ls-files -z dist | xargs -0 git update-index --no-assume-unchanged', 169 | 'git add -A dist && git commit -m "Distribution"', 170 | 'git ls-files -z dist | xargs -0 git update-index --assume-unchanged', 171 | ])); 172 | 173 | gulp.task('deploy', ['build'], function() { 174 | return gulp.src(DIST).pipe(subtree({ 175 | remote: 'origin', 176 | branch: 'gh-pages', 177 | message: 'Distribution', 178 | })); 179 | }); 180 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MirrorMark", 3 | "version": "1.0.0", 4 | "description": "Simple, yet extensible.", 5 | "main": "gulpfile.js", 6 | "devDependencies": { 7 | "del": "^1.1.1", 8 | "gulp": "^3.8.11", 9 | "gulp-concat": "^2.5.2", 10 | "gulp-less": "^3.0.3", 11 | "gulp-minify-css": "^1.0.0", 12 | "gulp-rename": "^1.2.0", 13 | "gulp-shell": "^0.4.1", 14 | "gulp-subtree": "^0.1.0", 15 | "gulp-uglify": "^1.1.0", 16 | "run-sequence": "^1.0.2" 17 | }, 18 | "scripts": { 19 | "test": "echo \"Error: no test specified\" && exit 1" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/themusicbed/MirrorMark.git" 24 | }, 25 | "keywords": [ 26 | "markdown", 27 | "editor", 28 | "codemirror" 29 | ], 30 | "author": "Andrew Del Prete ", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/themusicbed/MirrorMark/issues" 34 | }, 35 | "homepage": "https://github.com/themusicbed/MirrorMark" 36 | } 37 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # MirrorMark 2 | [Demo](http://squiddev.github.io/MirrorMark/). 3 | MirrorMark is a simple, yet extensible Markdown editor created with [Codemirror](http://www.codemirror.net). 4 | 5 | ## Features 6 | - Preview mode 7 | - Fullscreen 8 | - Syntax highlighting, including in fenced code blocks 9 | 10 | ## Install 11 | ``` 12 | bower install --save squiddev/mirrormark 13 | ``` 14 | 15 | ### Dependencies 16 | * [Codemirror](http://codemirror.net) (packaged with `mirrormark.package.js/css`) 17 | * [Pagedown Extra](https://github.com/jmcmanus/pagedown-extra) (packaged with `mirrormark.package.js`) 18 | * [Font Awesome](http://fortawesome.github.io/Font-Awesome/) (for toolbar icons - though this is optional) 19 | 20 | ### CSS 21 | 22 | ```html 23 | 24 | 25 | ``` 26 | You can load always load each components' CSS individually. `mirrormark.css` contains the theme for the editor, toolbar and preview. 27 | 28 | ### Javascript 29 | 30 | ```html 31 | 32 | ``` 33 | If you are so inclined, you can just load `mirrormark.js` and CodeMirror separately 34 | 35 | ## Basic Usage 36 | ### HTML 37 | ```html 38 | 39 | ``` 40 | 41 | ### Javascript 42 | ```javascript 43 | textarea1 = mirrorMark(document.getElementById("textarea1"), { showToolbar: false }); 44 | textarea1.render(); 45 | ``` 46 | 47 | or if you are feeling groovy: 48 | 49 | ```javascript 50 | var mirrors = document.querySelectorAll('[mirrormark]') 51 | for(var i = 0; i < mirrors.length; i++) { 52 | mirrorMark(mirrors[i]).render(); 53 | } 54 | ``` 55 | 56 | ## Options 57 | When instantiating the editor you can pass the various Codemirror options available. 58 | There is also an option to opt to show toolbar `showToolbar: false` (Default: true) and the option to change the theme `theme: name`. 59 | 60 | 61 | ## Custom Actions, Keymaps, and Toolbar. 62 | MirrorMark allows you to add custom actions, keymaps, and toolbar options. Included in the [demo](http://squiddev.github.io/MirrorMark/) is an example of how to add custom options. 63 | 64 | ### Custom Methods 65 | * `registerActions(actions)` - Accepts an object with an Action name as the key and an anonymous function for the value. 66 | * `registerKeymaps(keymaps)` - Accepts an object with Keymap name as the key and an anonymous function for the value. 67 | * `registerTools(tools, replace)` - Accepts an array of custom tool objects with the option of replacing (default: false) the existing toolbar. 68 | 69 | ### Insertion Methods 70 | * `this.insert(string)` - Inserts a string at the current cursor position 71 | * `this.toggleBefore(string)` - Toggles a string at the beginning of a line 72 | * `this.toggleAround(start, end)` - Toggles a string at the beginning and end of a selection 73 | -------------------------------------------------------------------------------- /src/css/demo.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ul, ol, li 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | border: 0; 20 | font-size: 100%; 21 | vertical-align: baseline; 22 | } 23 | 24 | html, body, div, span, applet, object, iframe, 25 | h1, h2, h3, h4, h5, h6 { 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | /* HTML5 display-role reset for older browsers */ 31 | article, aside, details, figcaption, figure, 32 | footer, header, hgroup, menu, nav, section { 33 | display: block; 34 | } 35 | body { 36 | line-height: 1; 37 | } 38 | blockquote, q { 39 | quotes: none; 40 | } 41 | blockquote:before, blockquote:after, 42 | q:before, q:after { 43 | content: ''; 44 | content: none; 45 | } 46 | table { 47 | border-collapse: collapse; 48 | border-spacing: 0; 49 | } 50 | 51 | 52 | /* Elements */ 53 | a { 54 | color: gray; 55 | text-decoration: none; 56 | } 57 | a:hover { 58 | color: black; 59 | } 60 | 61 | body { 62 | font-family: 'Merriweather', serif; 63 | } 64 | 65 | h1 { 66 | font-size: 3rem; 67 | } 68 | 69 | h2 { 70 | font-size: 2rem; 71 | } 72 | 73 | /* Classes */ 74 | .bgg05 { 75 | background: rgba(0,0,0,0.05); 76 | } 77 | 78 | .bgg1 { 79 | background: rgba(0,0,0,0.1); 80 | } 81 | 82 | .btn { 83 | border-radius: 3px; 84 | text-transform: lowercase; 85 | font-weight: bold; 86 | padding: .5rem .75rem; 87 | background: #222222; 88 | color: white; 89 | transition: all .2s linear; 90 | } 91 | 92 | .btn:hover { 93 | padding: .5rem 1rem; 94 | background: #555555; 95 | } 96 | 97 | .tac { 98 | text-align: center; 99 | } 100 | 101 | .wrap { 102 | padding: 3%; 103 | } 104 | 105 | .editor { 106 | max-width: 66%; 107 | margin: auto; 108 | } 109 | -------------------------------------------------------------------------------- /src/css/highlighting.less: -------------------------------------------------------------------------------- 1 | .cm-s-mirrormark .cm-m-markdown { 2 | color: #000; 3 | font-family: @mirror-mark-font; 4 | 5 | &.cm-header { 6 | font-family: @mirror-mark-font-header; 7 | font-weight: bold; 8 | } 9 | &.cm-header-1 { 10 | font-size: 28px; 11 | } 12 | 13 | &.cm-header-2 { 14 | font-size: 22px; 15 | } 16 | 17 | &.cm-header-2 { 18 | font-size: 18px; 19 | } 20 | 21 | &.cm-quote { 22 | color: #888; 23 | background-color: rgba(128, 128, 128, 0.05); 24 | padding: 5px; 25 | } 26 | 27 | &.cm-quote, 28 | &.cm-string { 29 | color: #888; 30 | } 31 | 32 | &.cm-tag, 33 | &.cm-link { 34 | color: #444; 35 | } 36 | 37 | &.cm-comment { 38 | color: #a50; 39 | font-family: @mirror-mark-font-mono; 40 | } 41 | 42 | // There are issues where the first < of a HTML tag are classified 43 | // with .cm-m-markdown. 44 | &.cm-m-xml, 45 | &.cm-bracket { 46 | color: #170; 47 | font-family: inherit; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/css/mirrormark.less: -------------------------------------------------------------------------------- 1 | @mirror-mark-font: sans-serif; 2 | @mirror-mark-font-mono: Menlo, Consolas, Monaco, 'Andale Mono', monospace; 3 | @mirror-mark-font-header: @mirror-mark-font-mono; 4 | 5 | @import "highlighting.less"; 6 | @import "preview.less"; 7 | @import "toolbar-icons.less"; 8 | @import "toolbar.less"; 9 | 10 | .cm-s-mirrormark { 11 | font: 16px/1.7 @mirror-mark-font-mono; 12 | box-sizing: border-box; 13 | height: auto; 14 | margin: auto; 15 | position: relative; 16 | z-index: 0; 17 | 18 | .CodeMirror-scroll { 19 | height: auto !important; 20 | overflow: visible !important; 21 | padding: 30px 4%; 22 | box-sizing: border-box; 23 | } 24 | 25 | &.CodeMirror-fullscreen .CodeMirror-scroll { 26 | height: 100% !important; 27 | overflow: scroll !important; 28 | } 29 | 30 | pre.CodeMirror-placeholder { color: #999; } 31 | } 32 | -------------------------------------------------------------------------------- /src/css/preview.less: -------------------------------------------------------------------------------- 1 | .cm-s-mirrormark.CodeMirror { 2 | &.CodeMirror-has-preview { 3 | .CodeMirror-scroll { 4 | display: none; 5 | } 6 | 7 | .CodeMirror-preview { 8 | display: block; 9 | } 10 | } 11 | 12 | .CodeMirror-preview { 13 | display: none; 14 | height: auto !important; 15 | overflow: visible !important; 16 | padding: 30px 4%; 17 | box-sizing: border-box; 18 | 19 | font-family: @mirror-mark-font; 20 | 21 | h1, h2, h3 { 22 | font-fami: @mirror-mark-font-header; 23 | } 24 | 25 | pre, code { 26 | font-family: @mirror-mark-font-mono; 27 | } 28 | } 29 | 30 | .CodeMirror-fullscreen .CodeMirror-preview{ 31 | height: 100% !important; 32 | overflow: scroll !important; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/css/toolbar-icons.less: -------------------------------------------------------------------------------- 1 | @import (reference) "font-awesome/less/font-awesome.less"; 2 | 3 | .mirrormark-toolbar { 4 | li > a { 5 | .fa; 6 | } 7 | .bold a:before{ content: @fa-var-bold; } 8 | .italicize a:before{ content: @fa-var-italic; } 9 | .blockquote a:before{ content: @fa-var-quote-left; } 10 | .strikethrough a:before{ content: @fa-var-strikethrough; } 11 | .link a:before{ content: @fa-var-link; } 12 | .image a:before{ content: @fa-var-image; } 13 | .unorderedList a:before{ content: @fa-var-list; } 14 | .orderedList a:before{ content: @fa-var-list-ol; } 15 | .fullScreen a:before{ content: @fa-var-expand; } 16 | .preview a:before{ content: @fa-var-file; } 17 | } 18 | 19 | .cm-s-mirrormark.CodeMirror-fullscreen .mirrormark-toolbar { 20 | .fullScreen a:before{ content: @fa-var-compress; } 21 | } 22 | 23 | .cm-s-mirrormark.CodeMirror-has-preview .mirrormark-toolbar { 24 | .preview a:before{ content: @fa-var-file-o; } 25 | } 26 | -------------------------------------------------------------------------------- /src/css/toolbar.less: -------------------------------------------------------------------------------- 1 | /* Toolbar Theme */ 2 | .mirrormark-toolbar { 3 | background: #fff; 4 | border-bottom: 1px solid rgba(0,0,0,0.1); 5 | display: block; 6 | width: 100%; 7 | margin-top: 0; 8 | margin-bottom: 0; 9 | padding-left: 0; 10 | padding-right: 0; 11 | 12 | /* First level */ 13 | li { 14 | display: inline-block; 15 | position: relative; 16 | z-index: 1; 17 | 18 | &.fullScreen, 19 | &.preview { 20 | float: right; 21 | } 22 | 23 | &.has-nested:after { 24 | content: '\2304'; 25 | color: #888; 26 | position: absolute; 27 | bottom: 3px; 28 | right: 3px; 29 | font-size: 11px; 30 | transform:scale(1.3, 1) 31 | } 32 | 33 | a { 34 | background-color: #fff; 35 | color: #888; 36 | cursor: pointer; 37 | display: block; 38 | font-size: 16px; 39 | height: 40px; 40 | line-height: 40px; 41 | text-align: center; 42 | transition: color .2s linear; 43 | width: 40px; 44 | } 45 | 46 | &:hover { 47 | a, 48 | &.has-nested:after { 49 | color: #000; 50 | } 51 | 52 | ul { 53 | display: block; 54 | } 55 | } 56 | 57 | ul { 58 | background: #e5e5e5; 59 | display: none; 60 | position: absolute; 61 | top: 41px; 62 | width: 150px; 63 | z-index: 1000; 64 | 65 | li { 66 | float: none; 67 | width: 100%; 68 | 69 | a { 70 | background: transparent; 71 | color: #888 !important; 72 | font-family: "Helvetica Neue", sans-serif; 73 | font-size: 12px; 74 | height: auto; 75 | line-height: normal; 76 | padding: .25rem .75rem; 77 | text-align: left; 78 | width: auto; 79 | } 80 | 81 | &:hover a { 82 | color: black !important; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MirrorMark - Simple, yet extensible. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

MirrorMark - Simple, yet extensible.

20 |

Built on the sheer power of the amazing CodeMirror

21 |
22 |
23 |
24 | 68 |
69 |
70 |
71 |

Another Textarea

72 |
73 |
74 |
75 | 120 |
121 |
122 | 123 |
124 | MirrorMark built by Andrew Del Prete at Musicbed 125 |
126 | 127 | 128 | 129 | 130 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/js/mirrormark.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license MirrorMark v0.1 3 | * (c) 2015 Musicbed http://www.musicbed.com 4 | * License: MIT 5 | */ 6 | (function(CodeMirror) { 'use strict'; 7 | /** 8 | * Bootstrap our module 9 | */ 10 | (function(fn) { 11 | if (typeof exports == "object" && typeof module == "object") { // CommonJS 12 | module.exports = fn; 13 | } else if (typeof define == "function" && define.amd) { // AMD 14 | return define([], fn); 15 | } 16 | 17 | if (window) window.mirrorMark = fn 18 | })(mirrorMark); 19 | 20 | /** 21 | * Merge 22 | * 23 | * 24 | * @param {Object} object The object to merge into 25 | * @param {Object/Array} source The object or array of objects to merge 26 | * @return {Object} The original object 27 | */ 28 | function merge(object, source) { 29 | if(Array.isArray(source)) { 30 | for(var i = sources.length - 1; i >= 0; i--) { 31 | merge(object, source[i]); 32 | } 33 | } else { 34 | for (var attrname in source) { 35 | object[attrname] = source[attrname]; 36 | } 37 | } 38 | 39 | return object; 40 | } 41 | 42 | /** 43 | * Our delegate prototype used by our factory 44 | * @type {Object} 45 | */ 46 | var mirrorMarkProto = { 47 | 48 | /** 49 | * Render the component 50 | */ 51 | render: function render() { 52 | this.registerKeyMaps(this.keyMaps); 53 | this.cm = CodeMirror.fromTextArea(this.element, this.options); 54 | 55 | if (this.options.showToolbar) { 56 | this.setToolbar(this.tools); 57 | } 58 | }, 59 | 60 | /** 61 | * Setup the toolbar 62 | */ 63 | setToolbar: function setToolbar(tools) { 64 | 65 | var toolbar = document.createElement('ul'); 66 | toolbar.className = this.options.theme + '-' + 'toolbar'; 67 | 68 | var tools = this.generateToolList(tools); 69 | 70 | tools.forEach(function(tool) { 71 | toolbar.appendChild(tool) 72 | }); 73 | 74 | var cmWrapper = this.cm.getWrapperElement(); 75 | cmWrapper.insertBefore(toolbar, cmWrapper.firstChild); 76 | }, 77 | 78 | /** 79 | * Register Keymaps by extending the extraKeys object 80 | * @param {Object} keyMaps 81 | */ 82 | registerKeyMaps: function registerKeyMaps(keyMaps) { 83 | for (var name in keyMaps) { 84 | if (typeof(this.actions[keyMaps[name]]) !== 'function') throw "MirrorMark - '" + keyMaps[name] + "' is not a registered action"; 85 | 86 | var realName = name.replace("Cmd-", (CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault) ? "Cmd-" : "Ctrl-"); 87 | this.options.extraKeys[realName] = this.actions[keyMaps[name]].bind(this) 88 | } 89 | }, 90 | 91 | 92 | /** 93 | * Register actions by extending the default actions 94 | * @param {Object} actions [description] 95 | */ 96 | registerActions: function registerActions(actions) { 97 | return merge(this.actions, actions); 98 | }, 99 | 100 | 101 | /** 102 | * Register tools by extending and overwriting the default tools 103 | * @param {Array} tools 104 | * @param {Bool} replace - replace the default tools with the ones provided. Defaults to false. 105 | */ 106 | registerTools: function registerTools(tools, replace) { 107 | for (var action in tools) { 108 | if (this.actions[tools[action].action] && typeof(this.actions[tools[action].action]) !== 'function') throw "MirrorMark - '" + tools[action].action + "' is not a registered action"; 109 | } 110 | 111 | if (replace) { 112 | this.tools = tools; 113 | return; 114 | } 115 | 116 | this.tools = this.tools.concat(tools) 117 | }, 118 | 119 | /** 120 | * A recursive function to generate and return an unordered list of tools 121 | * @param {Object} 122 | */ 123 | generateToolList: function generateToolList(tools) { 124 | return tools.map(function(tool) { 125 | var item = document.createElement("li"), 126 | anchor = document.createElement("a"); 127 | 128 | item.className = tool.name; 129 | 130 | if (tool.className) { 131 | anchor.className = tool.className; 132 | } 133 | 134 | if (tool.showName) { 135 | var text = document.createTextNode(tool.name); 136 | anchor.appendChild(text); 137 | } 138 | 139 | if (tool.action) { 140 | anchor.onclick = function(e) { 141 | this.cm.focus(); 142 | this.actions[tool.action].call(this); 143 | }.bind(this); 144 | } 145 | 146 | item.appendChild(anchor); 147 | 148 | if (tool.nested) { 149 | item.className += " has-nested"; 150 | var ul = document.createElement('ul'); 151 | ul.className = this.options.theme + "-toolbar-list" 152 | var nested = generateToolList.call(this, tool.nested); 153 | nested.forEach(function(nestedItem) { 154 | ul.appendChild(nestedItem); 155 | }); 156 | 157 | item.appendChild(ul); 158 | } 159 | 160 | return item 161 | 162 | }.bind(this)); 163 | }, 164 | 165 | /** 166 | * Default Tools in Toolbar 167 | */ 168 | tools: [ 169 | { name: "bold", action: "bold" }, 170 | { name: "italicize", action: "italicize" }, 171 | { name: "blockquote", action: "blockquote" }, 172 | { name: "strikethrough", action: "strikethrough" }, 173 | { name: "link", action: "link" }, 174 | { name: "image", action: "image" }, 175 | { name: "unorderedList", action: "unorderedList" }, 176 | { name: "orderedList", action: "orderedList" }, 177 | { name: "fullScreen", action: "fullScreen" }, 178 | { name: "preview", action: "preview" }, 179 | ], 180 | 181 | /** 182 | * Default Keymaps 183 | * @type {Object} 184 | */ 185 | keyMaps: { 186 | "Cmd-B": 'bold', 187 | "Cmd-I": 'italicize', 188 | "Cmd-'": 'blockquote', 189 | "Cmd-Alt-L": 'orderedList', 190 | "Cmd-L": 'unorderedList', 191 | "Cmd-Alt-I": 'image', 192 | "Cmd-H": 'hr', 193 | "Cmd-K": 'link', 194 | "F11": "fullScreen", 195 | "Esc": "exitFullScreen", 196 | }, 197 | 198 | /** 199 | * Default Actions 200 | * @type {Object} 201 | */ 202 | actions: { 203 | bold: function () { 204 | this.toggleAround('**', '**') 205 | }, 206 | italicize: function () { 207 | this.toggleAround('*', '*') 208 | }, 209 | strikethrough: function () { 210 | this.toggleAround('~~', '~~') 211 | }, 212 | "code": function () { 213 | this.toggleAround('```\r\n', '\r\n```') 214 | }, 215 | "blockquote": function () { 216 | this.toggleBefore('> '); 217 | }, 218 | "orderedList": function () { 219 | this.toggleBefore('1. '); 220 | }, 221 | "unorderedList": function () { 222 | this.toggleBefore('* '); 223 | }, 224 | "image": function () { 225 | this.toggleAround('![', '](http://)'); 226 | }, 227 | "link": function () { 228 | this.toggleAround('[', '](http://)'); 229 | }, 230 | "hr": function () { 231 | this.insert('---'); 232 | }, 233 | "fullScreen": function () { 234 | var fullScreen = !this.cm.getOption("fullScreen"); 235 | 236 | // You must turn off scrollPastEnd on after going full screen 237 | // and before exiting it 238 | if(!fullScreen) this.cm.setOption("scrollPastEnd", fullScreen) 239 | this.cm.setOption("fullScreen", fullScreen); 240 | if(fullScreen) this.cm.setOption("scrollPastEnd", fullScreen) 241 | }, 242 | "exitFullScreen": function() { 243 | if (this.cm.getOption("fullScreen")) { 244 | this.cm.setOption("scrollPastEnd", fullScreen) 245 | this.cm.setOption("fullScreen", false); 246 | } 247 | }, 248 | "preview": function() { 249 | this.cm.setOption("preview", !this.cm.getOption("preview")); 250 | } 251 | }, 252 | 253 | /** 254 | * Insert a string at cursor position 255 | * @param {String} insertion 256 | */ 257 | insert: function insert(insertion) { 258 | var doc = this.cm.getDoc(); 259 | var cursor = doc.getCursor(); 260 | 261 | doc.replaceRange(insertion, { line: cursor.line, ch: cursor.ch }); 262 | }, 263 | 264 | /** 265 | * Toggle a string at the start and end of a selection 266 | * @param {String} start Start string to wrap 267 | * @param {String} end End string to wrap 268 | */ 269 | toggleAround: function toggleAround(start, end) { 270 | var doc = this.cm.getDoc(); 271 | var cursor = doc.getCursor(); 272 | 273 | if (doc.somethingSelected()) { 274 | var selection = doc.getSelection(); 275 | if(selection.startsWith(start) && selection.endsWith(end)) { 276 | doc.replaceSelection(selection.substring(start.length, selection.length - end.length), "around"); 277 | } else { 278 | doc.replaceSelection(start + selection + end, "around"); 279 | } 280 | } else { 281 | // If no selection then insert start and end args and set cursor position between the two. 282 | doc.replaceRange(start + end, { line: cursor.line, ch: cursor.ch }); 283 | doc.setCursor({ line: cursor.line, ch: cursor.ch + start.length }) 284 | } 285 | }, 286 | 287 | /** 288 | * Toggle a string before a selection 289 | * @param {String} insertion String to insert 290 | */ 291 | toggleBefore: function toggleBefore(insertion) { 292 | var doc = this.cm.getDoc(); 293 | var cursor = doc.getCursor(); 294 | 295 | if (doc.somethingSelected()) { 296 | var selections = doc.listSelections(); 297 | var remove = null; 298 | this.cm.operation(function() { 299 | selections.forEach(function(selection) { 300 | var pos = [selection.head.line, selection.anchor.line].sort(); 301 | 302 | // Remove if the first text starts with it 303 | if(remove == null) { 304 | remove = doc.getLine(pos[0]).startsWith(insertion); 305 | } 306 | 307 | for (var i = pos[0]; i <= pos[1]; i++) { 308 | if(remove) { 309 | // Don't remove if we don't start with it 310 | if(doc.getLine(i).startsWith(insertion)) { 311 | doc.replaceRange("", { line: i, ch: 0 }, {line: i, ch: insertion.length}); 312 | } 313 | } else { 314 | doc.replaceRange(insertion, { line: i, ch: 0 }); 315 | } 316 | } 317 | }); 318 | }); 319 | } else { 320 | var line = cursor.line; 321 | if(doc.getLine(line).startsWith(insertion)) { 322 | doc.replaceRange("", { line: line, ch: 0 }, {line: line, ch: insertion.length}); 323 | } else { 324 | doc.replaceRange(insertion, { line: line, ch: 0 }); 325 | } 326 | 327 | } 328 | } 329 | } 330 | 331 | /** 332 | * Our Factory 333 | * @param {Object} element 334 | * @param {Object} options 335 | * @return {Object} 336 | */ 337 | function mirrorMark(element, options) { 338 | 339 | // Defaults 340 | var defaults = { 341 | mode: 'gfm', 342 | theme: 'default mirrormark', 343 | tabSize: '2', 344 | indentWithTabs: true, 345 | lineWrapping: true, 346 | autoCloseBrackets: true, 347 | autoCloseTags: true, 348 | addModeClass: true, 349 | showToolbar: true, 350 | extraKeys: { 351 | "Enter": 'newlineAndIndentContinueMarkdownList', 352 | }, 353 | } 354 | 355 | // Extend our defaults with the options provided 356 | merge(defaults, options); 357 | 358 | return merge(Object.create(mirrorMarkProto), { element: element, options: defaults }); 359 | } 360 | 361 | })(window.CodeMirror); 362 | -------------------------------------------------------------------------------- /src/js/preview.js: -------------------------------------------------------------------------------- 1 | (function(CodeMirror, Markdown) { 2 | "use strict"; 3 | 4 | var converter = new Markdown.Converter(); 5 | Markdown.Extra.init(converter); 6 | 7 | CodeMirror.defineOption("preview", false, function(cm, val, old) { 8 | if (old == CodeMirror.Init) old = false; 9 | if (!old == !val) return; 10 | if (val) { 11 | setPreview(cm); 12 | } else { 13 | setNormal(cm); 14 | } 15 | }); 16 | 17 | function setPreview(cm) { 18 | var wrap = cm.getWrapperElement(); 19 | wrap.className += " CodeMirror-has-preview"; 20 | 21 | refreshPreview(wrap, cm); 22 | } 23 | 24 | function refreshPreview(wrap, cm) { 25 | var previewNodes = wrap.getElementsByClassName("CodeMirror-preview"); 26 | var previewNode; 27 | if(previewNodes.length == 0) { 28 | var previewNode = document.createElement('div'); 29 | previewNode.className = "CodeMirror-preview"; 30 | wrap.appendChild(previewNode); 31 | } else { 32 | previewNode = previewNodes[0]; 33 | } 34 | previewNode.innerHTML = converter.makeHtml(cm.getValue()); 35 | } 36 | 37 | function setNormal(cm) { 38 | var wrap = cm.getWrapperElement(); 39 | wrap.className = wrap.className.replace(/\s*CodeMirror-has-preview\b/, ""); 40 | cm.refresh(); 41 | } 42 | })(CodeMirror, Markdown); 43 | --------------------------------------------------------------------------------