'
775 | + (escaped ? code : escape(code, true))
776 | + '\n
';
777 | }
778 |
779 | return ''
783 | + (escaped ? code : escape(code, true))
784 | + '\n
\n';
785 | };
786 |
787 | Renderer.prototype.blockquote = function(quote) {
788 | return '\n' + quote + '\n'; 789 | }; 790 | 791 | Renderer.prototype.html = function(html) { 792 | return html; 793 | }; 794 | 795 | Renderer.prototype.heading = function(text, level, raw) { 796 | return '
' + text + '
\n'; 823 | }; 824 | 825 | Renderer.prototype.table = function(header, body) { 826 | return '' + text + '
';
859 | };
860 |
861 | Renderer.prototype.br = function() {
862 | return this.options.xhtml ? 'An error occured:
' 1223 | + escape(e.message + '', true) 1224 | + ''; 1225 | } 1226 | throw e; 1227 | } 1228 | } 1229 | 1230 | /** 1231 | * Options 1232 | */ 1233 | 1234 | marked.options = 1235 | marked.setOptions = function(opt) { 1236 | merge(marked.defaults, opt); 1237 | return marked; 1238 | }; 1239 | 1240 | marked.defaults = { 1241 | gfm: true, 1242 | tables: true, 1243 | breaks: false, 1244 | pedantic: false, 1245 | sanitize: false, 1246 | sanitizer: null, 1247 | mangle: true, 1248 | smartLists: false, 1249 | silent: false, 1250 | highlight: null, 1251 | langPrefix: 'lang-', 1252 | smartypants: false, 1253 | headerPrefix: '', 1254 | renderer: new Renderer, 1255 | xhtml: false 1256 | }; 1257 | 1258 | /** 1259 | * Expose 1260 | */ 1261 | 1262 | marked.Parser = Parser; 1263 | marked.parser = Parser.parse; 1264 | 1265 | marked.Renderer = Renderer; 1266 | 1267 | marked.Lexer = Lexer; 1268 | marked.lexer = Lexer.lex; 1269 | 1270 | marked.InlineLexer = InlineLexer; 1271 | marked.inlineLexer = InlineLexer.output; 1272 | 1273 | marked.parse = marked; 1274 | 1275 | if (typeof module !== 'undefined' && typeof exports === 'object') { 1276 | module.exports = marked; 1277 | } else if (typeof define === 'function' && define.amd) { 1278 | define(function() { return marked; }); 1279 | } else { 1280 | this.marked = marked; 1281 | } 1282 | 1283 | }).call(function() { 1284 | return this || (typeof window !== 'undefined' ? window : global); 1285 | }()); 1286 | -------------------------------------------------------------------------------- /markedview/src/main/assets/html/js/md_preview.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | var rend = new marked.Renderer(); 4 | 5 | marked.setOptions({ 6 | langPrefix: '', 7 | highlight: function(code){ 8 | return hljs.highlightAuto(code).value; 9 | } 10 | }); 11 | 12 | rend.code = function(code, lang, escaped){ 13 | var lineArray = code.split(/\r\n|\r|\n/); 14 | var len = 0; 15 | if(lineArray == null){ 16 | len = code.length; 17 | 18 | }else{ 19 | $.each(lineArray, function(index, val){ 20 | if(len < val.length){ 21 | len = val.length; 22 | } 23 | }); 24 | } 25 | 26 | var code = code.replace(/&/g, '&') 27 | .replace(//g, '>') 29 | .replace(/"/g, '"') 30 | .replace(/'/g, '''); 31 | 32 | if(!lang){ 33 | return '
'
38 | + code
39 | + '\n
';
40 | }
41 |
42 | return ''
49 | + code
50 | + '\n
';
51 | };
52 |
53 | function escSub(text){
54 | var result = text.match(/~+.*?~+/g);
55 | if(result == null){
56 | return text;
57 | }
58 |
59 | $.each(result, function(index, val){
60 | if(val.lastIndexOf('~~', 0) === 0){
61 | return true;
62 | }
63 | var escapedText = val.replace(/~/, '');
64 | escapedText = escapedText.replace(/~/, '');
65 | var reg = new RegExp(val, 'g');
66 | text = text.replace(reg, escapedText);
67 | });
68 |
69 | return text;
70 | }
71 |
72 | function escSup(text){
73 | var result = text.match(/\^.*?\^/g);
74 | if(result == null){
75 | return text;
76 | }
77 |
78 | $.each(result, function(index, val){
79 | var escapedText = val.replace(/\^/, '');
80 | escapedText = escapedText.replace(/\^/, '');
81 | val = val.replace(/\^/g, '\\^');
82 | var reg = new RegExp(val, 'g');
83 | text = text.replace(reg, escapedText);
84 | });
85 |
86 | return text;
87 | }
88 |
89 | preview = function setMarkdown(md_text, codeScrollDisable){
90 | if(md_text == ""){
91 | return false;
92 | }
93 |
94 | md_text = md_text.replace(/\\n/g, "\n");
95 | md_text = escSub(md_text);
96 | md_text = escSup(md_text);
97 |
98 | // markdown html
99 | var md_html;
100 | if(codeScrollDisable){
101 | md_html = marked(md_text);
102 | }else{
103 | md_html = marked(md_text, {renderer: rend});
104 | }
105 |
106 | $('#preview').html(md_html);
107 |
108 | $('pre code').each(function(i, block){
109 | hljs.highlightBlock(block);
110 | });
111 | };
112 | });
--------------------------------------------------------------------------------
/markedview/src/main/assets/html/md_preview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/markedview/src/main/java/com/mittsu/markedview/MarkedView.java:
--------------------------------------------------------------------------------
1 | package com.mittsu.markedview;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.annotation.TargetApi;
5 | import android.content.Context;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.util.Base64;
9 | import android.util.Log;
10 | import android.webkit.WebSettings;
11 | import android.webkit.WebView;
12 | import android.webkit.WebViewClient;
13 |
14 | import java.io.BufferedInputStream;
15 | import java.io.BufferedReader;
16 | import java.io.File;
17 | import java.io.FileInputStream;
18 | import java.io.FileNotFoundException;
19 | import java.io.IOException;
20 | import java.io.InputStreamReader;
21 | import java.util.regex.Matcher;
22 | import java.util.regex.Pattern;
23 |
24 | /**
25 | * The MarkedView is the Markdown viewer.
26 | *
27 | * Created by mittsu on 2016/04/25.
28 | */
29 | public final class MarkedView extends WebView {
30 |
31 | private static final String TAG = MarkedView.class.getSimpleName();
32 | private static final String IMAGE_PATTERN = "!\\[(.*)\\]\\((.*)\\)";
33 |
34 | private String previewText;
35 | private boolean codeScrollDisable;
36 |
37 | public MarkedView(Context context) {
38 | this(context, null);
39 | }
40 |
41 | public MarkedView(Context context, AttributeSet attrs) {
42 | this(context, attrs, 0);
43 | }
44 |
45 | public MarkedView(Context context, AttributeSet attrs, int defStyleAttr) {
46 | super(context, attrs, defStyleAttr);
47 | init();
48 | }
49 |
50 | @TargetApi(11)
51 | @SuppressLint("SetJavaScriptEnabled")
52 | private void init(){
53 | // default browser is not called.
54 | setWebViewClient(new WebViewClient(){
55 | public void onPageFinished(WebView view, String url){
56 | sendScriptAction();
57 | }
58 | });
59 |
60 | loadUrl("file:///android_asset/html/md_preview.html");
61 |
62 | getSettings().setJavaScriptEnabled(true);
63 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
64 | getSettings().setAllowUniversalAccessFromFileURLs(true);
65 | }
66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
67 | getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
68 | }
69 | }
70 |
71 | private void sendScriptAction() {
72 | if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
73 | loadUrl(previewText);
74 | } else {
75 | evaluateJavascript(previewText, null);
76 | }
77 | }
78 |
79 | /** load Markdown text from file path. **/
80 | public void loadMDFilePath(String filePath){
81 | loadMDFile(new File(filePath));
82 | }
83 |
84 | /** load Markdown text from file. **/
85 | public void loadMDFile(File file){
86 | String mdText = "";
87 | try {
88 | FileInputStream fileInputStream = new FileInputStream(file);
89 |
90 | InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
91 | BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
92 |
93 | String readText = "";
94 | StringBuilder stringBuilder = new StringBuilder();
95 | while ((readText = bufferedReader.readLine()) != null) {
96 | stringBuilder.append(readText);
97 | stringBuilder.append("\n");
98 | }
99 | fileInputStream.close();
100 | mdText = stringBuilder.toString();
101 |
102 | } catch(FileNotFoundException e) {
103 | Log.e(TAG, "FileNotFoundException:" + e);
104 | } catch(IOException e) {
105 | Log.e(TAG, "IOException:" + e);
106 | }
107 | setMDText(mdText);
108 | }
109 |
110 | /** set show the Markdown text. **/
111 | public void setMDText(String text){
112 | text2Mark(text);
113 | }
114 |
115 | private void text2Mark(String mdText){
116 |
117 | String bs64MdText = imgToBase64(mdText);
118 | String escMdText = escapeForText(bs64MdText);
119 |
120 | if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
121 | previewText = String.format("javascript:preview('%s', %b)", escMdText, isCodeScrollDisable());
122 |
123 | } else {
124 | previewText = String.format("preview('%s', %b)", escMdText, isCodeScrollDisable());
125 | }
126 | sendScriptAction();
127 | }
128 |
129 | private String escapeForText(String mdText){
130 | String escText = mdText.replace("\n", "\\\\n");
131 | escText = escText.replace("'", "\\\'");
132 | //in some cases the string may have "\r" and our view will show nothing,so replace it
133 | escText = escText.replace("\r","");
134 | return escText;
135 | }
136 |
137 | private String imgToBase64(String mdText){
138 | Pattern ptn = Pattern.compile(IMAGE_PATTERN);
139 | Matcher matcher = ptn.matcher(mdText);
140 | if(!matcher.find()){
141 | return mdText;
142 | }
143 |
144 | String imgPath = matcher.group(2);
145 | if(isUrlPrefix(imgPath) || !isPathExChack(imgPath)) {
146 | return mdText;
147 | }
148 | String baseType = imgEx2BaseType(imgPath);
149 | if(baseType.equals("")){
150 | // image load error.
151 | return mdText;
152 | }
153 |
154 | File file = new File(imgPath);
155 | byte[] bytes = new byte[(int) file.length()];
156 | try {
157 | BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
158 | buf.read(bytes, 0, bytes.length);
159 | buf.close();
160 | } catch (FileNotFoundException e) {
161 | Log.e(TAG, "FileNotFoundException:" + e);
162 | } catch (IOException e) {
163 | Log.e(TAG, "IOException:" + e);
164 | }
165 | String base64Img = baseType + Base64.encodeToString(bytes, Base64.NO_WRAP);
166 |
167 | return mdText.replace(imgPath, base64Img);
168 | }
169 |
170 | private boolean isUrlPrefix(String text){
171 | return text.startsWith("http://") || text.startsWith("https://");
172 | }
173 |
174 | private boolean isPathExChack(String text){
175 | return text.endsWith(".png")
176 | || text.endsWith(".jpg")
177 | || text.endsWith(".jpeg")
178 | || text.endsWith(".gif");
179 | }
180 |
181 | private String imgEx2BaseType(String text){
182 | if(text.endsWith(".png")){
183 | return "data:image/png;base64,";
184 | }else if(text.endsWith(".jpg") || text.endsWith(".jpeg")){
185 | return "data:image/jpg;base64,";
186 | }else if(text.endsWith(".gif")){
187 | return "data:image/gif;base64,";
188 | }else{
189 | return "";
190 | }
191 | }
192 |
193 |
194 | /* options */
195 |
196 | public void setCodeScrollDisable(){
197 | codeScrollDisable = true;
198 | }
199 |
200 | private boolean isCodeScrollDisable(){
201 | return codeScrollDisable;
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/markedview/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |