diff --git a/src/css/meditor.scss b/src/css/meditor.scss index 49be92d..d93ce8a 100644 --- a/src/css/meditor.scss +++ b/src/css/meditor.scss @@ -86,6 +86,14 @@ } &:active {background:nth($cp, 2)} } +.do-meditor__input {width:100%;height:40px;padding:0 10px;background:nth($cp, 1);border:2px solid transparent;border-radius:5px;font-size:13px;@include ts();color: nth($cd, 2); + + &.area {height:120px;padding:5px 10px;resize:none;outline:none;} + &:focus {background:#fff;border-color:nth($cd, 2);} + + &::-webkit-input-placeholder {color:nth($cp, 3);} + +} /* 关于编辑器模块*/ @@ -128,7 +136,6 @@ section {width:100%;height: 40px;margin:10px 0;line-height:40px; - .txt {width:100%;height:40px;padding:0 10px;border:0;border-radius:5px;background:nth($cp, 1);color:nth($cd, 2);font-size:14px;} .label {float: left;width:50%;} .submit {float:right;width:30%;} } @@ -137,16 +144,18 @@ .do-meditor-codeblock {width:480px;height:auto; section {display:block;width:100%;height:auto;margin:10px 0;line-height:35px; - .label {float: left;width:80px;} - select {float:left;width:200px;height:35px;padding:0 30px 0 10px;border:0;border-radius:0;border-bottom:1px solid nth($cp, 3);background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAMAAABV0m3JAAAADFBMVEUAAAD///+Pj4+JiYkxcGihAAAABHRSTlMAABBwqVQF9wAAADNJREFUeNqlzjEOACAMw8DQ/v/PSE5FFhaEx5usdekBuzRVH0RtCqJYELUFrVjQigX/5jdvzgDh9izlMQAAAABJRU5ErkJggg==) no-repeat right 12px;color:nth($cd, 2);outline:none;appearance: none;@include ts; - - &::-ms-expand {display:none;} + .select {position:relative;width:200px;height:35px;color:nth($cgr, 1); - &:focus {box-shadow:0 0 5px nth($ct, 1)} - } - textarea {width:100%;height:120px;padding:5px 10px;border:0;border-radius:5px;background:nth($cp, 1);font-size:14px;resize:none;outline:none;color:nth($cd, 2); - - &:focus {box-shadow:0 0 5px nth($ct, 1)} + select {width:100%;height:100%;padding:5px 13px;line-height:1;background:nth($cp, 1);border-radius:5px;appearance:none;border:2px solid transparent;outline:none;color: nth($cd, 2);font-size:13px; + + &:focus {background:#fff;border-color:nth($cd, 2);} + &::-ms-expand {display:none;} + &:disabled {border-color:transparent;background:#fff8ed;color:nth($cp, 3)} + } + .trigon {position:absolute;right:7px;top:0;width:15px;height:35px;padding:7px 0;font-size:12px;text-align:center; + i {float:left;width:15px;height:12px;line-height:12px;} + i:nth-child(2) {margin-top:-6px;} + } } .submit {float:right;width:80px;} } diff --git a/src/css/meditor__attach.scss b/src/css/meditor__attach.scss index c52821d..8e043f6 100644 --- a/src/css/meditor__attach.scss +++ b/src/css/meditor__attach.scss @@ -34,7 +34,6 @@ .section {display:block;width:100%;height:auto;margin:15px 0;line-height:35px; - .txt {width:100%;height:45px;padding:0 10px;border:0;border-radius:5px;background:nth($cp, 1);color:nth($cd, 2);font-size:14px;} .submit {float:right;width:30%;height:45px;line-height:45px;} } @@ -52,9 +51,8 @@ .upload-box {width:100%;height:auto;min-height:255px;padding-top:10px; .thead {width:100%;height:35px;line-height:35px;background:nth($cp, 1);} - .thead .col {text-align:center;} - .col {float:left;height:30px;padding:0 5px;} + .col {overflow:hidden;float:left;height:30px;padding:0 5px;text-align:center;} .col:nth-child(1) {width:50%} .col:nth-child(2) {width:35%} .col:nth-child(3) {width:15%} diff --git a/src/layer/index.js b/src/layer/index.js index c6dbeb8..7cf0490 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -12,6 +12,31 @@ import 'css/layer-normal.scss' Anot.ui.layer = '1.0.0-normal' +const LANGUAGES = { + en: { + TITLE: 'Dialog', + YES_BTN: 'OK', + NO_BTN: 'Cancel', + ERROR: 'The layer instance is not exists', + NEED_CONTAINER: 'layer "tips" require a DOM object as container' + }, + zh: { + TITLE: '提示', + YES_BTN: '确定', + NO_BTN: '取消', + ERROR: '要关闭的layer实例不存在', + NEED_CONTAINER: 'tips类型需要指定一个元素节点作为容器' + }, + 'zh-TW': { + TITLE: '提示', + YES_BTN: '確定', + NO_BTN: '取消', + ERROR: '要關閉的layer實例不存在', + NEED_CONTAINER: 'tips类型需要指定一個元素節點作爲容器' + } +} +LANGUAGES['zh-CN'] = LANGUAGES.zh +const lang = LANGUAGES[Anot.language || navigator.language || 'en'] let layerDom = {} let layerObj = {} let unique = null // 储存当前打开的1/2/3类型的弹窗 @@ -24,13 +49,13 @@ let defconf = { maskColor: null, // 遮罩背景色 radius: '0px', // 弹窗圆角半径 area: ['auto', 'auto'], - title: '提示', // 弹窗主标题(在工具栏上的) + title: lang.TITLE, // 弹窗主标题(在工具栏上的) menubar: true, // 是否显示菜单栏 content: '', // 弹窗的内容 fixed: false, // 是否固定不可拖拽 shift: 'cc', // 弹窗出来的初始位置,用于出场动画 offset: [], // 弹窗出来后的坐标, 为数组,可有4个值,依次是 上右下左 - btns: ['确定', '取消'] // 弹窗的2个按钮的文字 + btns: [lang.YES_BTN, lang.NO_BTN] // 弹窗的2个按钮的文字 } const $doc = Anot(document) const uuid = function() { @@ -38,7 +63,7 @@ const uuid = function() { } const close = function(id) { if (typeof id !== 'string' && typeof id !== 'number') { - return Anot.error('要关闭的layer实例不存在') + return Anot.error(lang.ERROR) } if (/^layerwrap\-/.test(id) || layerObj['layerwrap-' + id]) { try { @@ -626,7 +651,7 @@ const _layer = { }, tips(content, container, opt = {}) { if (!(container instanceof HTMLElement)) { - return Anot.error('layer "tips" require a DOM object') + return Anot.error(lang.NEED_CONTAINER) } if (!opt.background) { @@ -669,7 +694,7 @@ const _layer = { if (typeof opt === 'string') { opt = 'layerwrap-' + opt if (!layerObj[opt]) { - throw new Error('layer实例不存在') + throw new Error(lang.ERROR) } else { //只能显示一个实例 if (layerObj[opt].show) { @@ -813,7 +838,6 @@ Anot.directive('layer', { tips.style.visibility = 'hidden' }, 100) }) - // _layer.tips(val, this.element) } } }) diff --git a/src/marked/index.js b/src/marked/index.js index 6d53d92..cb37a37 100644 --- a/src/marked/index.js +++ b/src/marked/index.js @@ -726,7 +726,9 @@ InlineLexer.prototype.output = function(src) { // br if ((cap = this.rules.br.exec(src))) { src = src.substring(cap[0].length) - out += this.renderer.br() + if (!/<[\/]?([a-z0-9\-])+[^>]*>/.test(src)) { + out += this.renderer.br() + } continue } @@ -921,25 +923,24 @@ Renderer.prototype.listitem = function(text) { } Renderer.prototype.paragraph = function(text) { - text = text.replace(/
/g, '').replace(/

<\/p>/g, '') return '

' + text + '

' } Renderer.prototype.table = function(header, body) { return ( - '\n' + - '\n' + + '
' + + '' + header + - '\n' + - '\n' + + '' + + '' + body + - '\n' + - '
\n' + '' + + '' ) } Renderer.prototype.tablerow = function(content) { - return '\n' + content + '\n' + return '' + content + '' } Renderer.prototype.tablecell = function(content, flags) { @@ -947,7 +948,7 @@ Renderer.prototype.tablecell = function(content, flags) { var tag = flags.align ? '<' + type + ' style="text-align:' + flags.align + '">' : '<' + type + '>' - return tag + content + '\n' + return tag + content + '' } // span level renderer @@ -1016,7 +1017,7 @@ Renderer.prototype.image = function(href, title, text) { } Renderer.prototype.text = function(text) { - return text.trim() + return text } /** diff --git a/src/meditor/addon/attach-native.js b/src/meditor/addon/attach-native.js new file mode 100644 index 0000000..d941901 --- /dev/null +++ b/src/meditor/addon/attach-native.js @@ -0,0 +1,371 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2017-04-19 21:17:26 + * + */ + +'use strict' + +import '../../layer/index' +import 'css/meditor__attach.scss' + +const $doc = Anot(document) +const LANGUAGES = { + zh: { + IMAGE: { + REMOTE: '远程图片', + LOCAL: '本地上传', + MANAGE: '图片管理', + ALT: '图片描述', + ADDRESS: '图片地址' + }, + FILE: { + REMOTE: '远程附件', + LOCAL: '本地上传', + MANAGE: '附件管理', + ALT: '附件描述', + ADDRESS: '附件地址' + }, + BTN: '确定', + INSERT: '插入', + CHOOSE: '选择文件', + LIMIT: '上传大小限制:单文件最大 ', + SCREENSHOT: '截图', + COMPRESS: '截图处理中...', + TABLE: { + NAME: '文件名', + PROGRESS: '上传进度', + HANDLE: '操作' + }, + ERROR: { + TYPE: '文件类型错误', + SIZE: '文件体积过大', + EMPTY: '描述和地址不能为空', + UNDEFINED: '在node-webkit中saveAttach回调必须定义' + } + }, + en: { + IMAGE: { + REMOTE: 'Remote image', + LOCAL: 'Local image', + MANAGE: 'Manage', + ALT: 'Image alt text', + ADDRESS: 'Image address' + }, + FILE: { + REMOTE: 'Remote file', + LOCAL: 'Local file', + MANAGE: 'Manage', + ALT: 'File alt text', + ADDRESS: 'File address' + }, + BTN: 'OK', + INSERT: 'insert', + CHOOSE: 'Choose file', + LIMIT: 'Size of upload file limit to ', + SCREENSHOT: 'screenshot', + COMPRESS: 'Screenshot compressing...', + TABLE: { + NAME: 'name', + PROGRESS: 'progress', + HANDLE: 'handle' + }, + ERROR: { + TYPE: 'Forbidden type', + SIZE: 'Too large', + EMPTY: 'Alt text and address can not be null', + UNDEFINED: 'Function saveAttach is not defined' + } + } +} +LANGUAGES['zh-CN'] = LANGUAGES.zh +LANGUAGES['zh-TW'] = LANGUAGES.zh +const lang = LANGUAGES[Anot.language || navigator.language || 'en'] + +const fixCont = function(vm, tool) { + let limit = false + if (vm.props.uploadSizeLimit) { + limit = (vm.props.uploadSizeLimit / (1024 * 1024)).toFixed(2) + } + return ` +
+
+ + ${lang[tool].REMOTE} + + + ${lang[tool].LOCAL} + + + ${lang[tool].MANAGE} + +
+
+
+
+ +
+
+ +
+
+ ${lang.BTN} +
+
+
+
+ + ${lang.CHOOSE} + ${limit ? `(${lang.LIMIT + limit} MB)` : ''} +
+
    +
  • + ${lang.TABLE.NAME} + ${lang.TABLE.PROGRESS} + ${lang.TABLE.HANDLE} +
  • +
  • +

    + + + ${ + lang.INSERT + } +

    +
  • +
+
+
+
    +
  • + + +

    +
  • +
+ +
+
+
` +} + +/** + * [uploadFile 文件上传] + * @param {[type]} vm [vm对象] + * @param {[type]} tool [image/file] + */ +function uploadFile(vm, tool) { + for (let it of this.files) { + let ext = it.name.slice(it.name.lastIndexOf('.')) + if (tool === 'IMAGE' && !/^\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(ext)) { + this.uploadQueue.push({ + name: it.name, + progress: '0%(' + lang.ERROR.TYPE + ')', + url: '' + }) + continue + } + if (vm.props.uploadSizeLimit && it.size > vm.props.uploadSizeLimit) { + this.uploadQueue.push({ + name: it.name, + progress: '0%(' + lang.ERROR.SIZE + ')', + url: '' + }) + continue + } + let fixName = new Date().format('YmdHis') + ext + let attach = { name: it.name, fixName, progress: '100%', url: '' } + + if (vm.props.saveAttach) { + vm.props + .saveAttach(attach, it) + .then(url => { + attach.url = url + this.uploadQueue.push(attach) + }) + .catch(err => { + Anot.error(err) + }) + } else { + layer.toast(lang.ERROR.UNDEFINED, 'error') + } + } +} + +function uploadScreenshot(vm, blob) { + let name = new Date().format('YmdHis') + '.jpg' + let attach = { name, url: '' } + + if (vm.props.saveAttach) { + vm.props + .saveAttach(attach, blob) + .then(url => { + vm.insert(`![${lang.SCREENSHOT}](${url})`) + }) + .catch(err => { + Anot.error(err) + }) + } else { + layer.toast(lang.ERROR.UNDEFINED, 'error') + } +} + +function showDialog(elem, vm, tool) { + let offset = Anot(elem).offset() + + layer.open({ + type: 7, + menubar: false, + fixed: true, + maskClose: true, + offset: [offset.top + 35 - $doc.scrollTop()], + shift: { + top: offset.top - $doc.scrollTop() + }, + tab: 2, + attach: '', + attachAlt: '', + uploadQueue: [], //当前上传的列表 + attachList: [], //附件管理列表 + switchTab(id) { + this.tab = id + if (id === 3) { + this.attachList.clear() + if (vm.props.getAttachList) { + vm.props + .getAttachList(tool) + .then(list => { + list.forEach(it => { + let ext = it.name.slice(it.name.lastIndexOf('.')) + it.isImage = /^\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(ext) + it.thumb = it.isImage + ? `` + : `` + }) + return list + }) + .then(list => { + list = list.filter(it => { + if (tool === 'IMAGE') { + return it.isImage + } + return true + }) + this.attachList = list + }) + } + } + }, + select() { + let ev = document.createEvent('MouseEvent') + ev.initEvent('click', false, false) + this.$refs.attach.dispatchEvent(ev) + }, + change(ev) { + this.files = ev.target.files + uploadFile.call(this, vm, tool) + }, + insert: function(it) { + if (!it.url) { + return + } + let val = `\n${tool === 'IMAGE' ? '!' : ''}[${it.name}](${it.url})` + vm.insert(val) + }, + confirm: function() { + if (!this.attach || !this.attachAlt) { + return layer.toast(lang.ERROR.EMPTY, 'error') + } + let val = `\n${tool === 'IMAGE' ? '!' : ''}[${this.attachAlt}](${ + this.attach + })` + + vm.insert(val) + this.close() + }, + content: fixCont(vm, tool) + }) +} + +const plugin = { + __init__(ME) { + Object.assign(ME.vm.addon, { + attach(elem) { + showDialog(elem, this, 'FILE') + }, + image(elem) { + showDialog(elem, this, 'IMAGE') + } + }) + + ME.vm.$refs.editor.addEventListener('paste', function(ev) { + ev.preventDefault() + let txt = ev.clipboardData.getData('text/plain') + + //文本类型直接默认处理 + if (txt) { + return + } + + if (ev.clipboardData.items) { + let items = ev.clipboardData.items + let len = items.length + let blob = null + + for (let it of items) { + if (it.type.indexOf('image') > -1) { + blob = it.getAsFile() + } + } + + if (blob !== null) { + layer.toast(lang.COMPRESS) + // 压缩截图,避免文件过大 + let reader = new FileReader() + reader.onload = function() { + let img = document.createElement('img') + let canvas = document.createElement('canvas') + + img.onload = function() { + canvas.width = img.width + canvas.height = img.height + + let ctx = canvas.getContext('2d') + ctx.clearRect(0, 0, canvas.width, canvas.height) + ctx.drawImage(this, 0, 0, canvas.width, canvas.height) + + canvas.toBlob( + obj => { + uploadScreenshot(ME.vm, obj) + }, + 'image/jpeg', + 0.8 + ) + } + img.src = this.result + } + reader.readAsDataURL(blob) + } + } + }) + } +} + +export default plugin diff --git a/src/meditor/addon/attach.js b/src/meditor/addon/attach.js index 2b67ba6..48af897 100644 --- a/src/meditor/addon/attach.js +++ b/src/meditor/addon/attach.js @@ -11,10 +11,75 @@ import '../../layer/index' import 'css/meditor__attach.scss' const $doc = Anot(document) -const LANG = { - image: ['远程图片', '图片管理', '图片描述', '图片地址'], - file: ['远程附件', '附件管理', '附件描述', '附件地址'] +const LANGUAGES = { + zh: { + IMAGE: { + REMOTE: '远程图片', + LOCAL: '本地上传', + MANAGE: '图片管理', + ALT: '图片描述', + ADDRESS: '图片地址' + }, + FILE: { + REMOTE: '远程附件', + LOCAL: '本地上传', + MANAGE: '附件管理', + ALT: '附件描述', + ADDRESS: '附件地址' + }, + BTN: '确定', + INSERT: '插入', + CHOOSE: '选择文件', + LIMIT: '上传大小限制:单文件最大 ', + SCREENSHOT: '截图', + COMPRESS: '截图处理中...', + TABLE: { + NAME: '文件名', + PROGRESS: '上传进度', + HANDLE: '操作' + }, + ERROR: { + TYPE: '文件类型错误', + SIZE: '文件体积过大', + EMPTY: '描述和地址不能为空' + } + }, + en: { + IMAGE: { + REMOTE: 'Remote image', + LOCAL: 'Local image', + MANAGE: 'Manage', + ALT: 'Image alt text', + ADDRESS: 'Image address' + }, + FILE: { + REMOTE: 'Remote file', + LOCAL: 'Local file', + MANAGE: 'Manage', + ALT: 'File alt text', + ADDRESS: 'File address' + }, + BTN: 'OK', + INSERT: 'insert', + CHOOSE: 'Choose file', + LIMIT: 'Size of upload file limit to ', + SCREENSHOT: 'screenshot', + COMPRESS: 'Screenshot compressing...', + TABLE: { + NAME: 'name', + PROGRESS: 'progress', + HANDLE: 'handle' + }, + ERROR: { + TYPE: 'Forbidden type', + SIZE: 'Too large', + EMPTY: 'Alt text and address can not be null' + } + } } +LANGUAGES['zh-CN'] = LANGUAGES.zh +LANGUAGES['zh-TW'] = LANGUAGES.zh +const lang = LANGUAGES[Anot.language || navigator.language || 'en'] class Uploader { constructor(url) { @@ -77,49 +142,47 @@ const fixCont = function(vm, tool) {
- ${LANG[tool][0]} + ${lang[tool].REMOTE} + + + ${lang[tool].LOCAL} - 本地上传 - ${LANG[tool][1]} + ${lang[tool].MANAGE}
+ placeholder="${lang[tool].ALT}" />
+ placeholder="${lang[tool].ADDRESS}" />
确定 + :click="confirm">${lang.BTN}
- 选择文件 - ${ - limit - ? `(上传大小限制:单文件最大${limit} MB)` - : '' - } + ${lang.CHOOSE} + ${limit ? `(${lang.LIMIT + limit} MB)` : ''}
  • - 文件名 - 上传进度 - 操作 + ${lang.TABLE.NAME} + ${lang.TABLE.PROGRESS} + ${lang.TABLE.HANDLE}
  • @@ -128,7 +191,9 @@ const fixCont = function(vm, tool) { :text="el.name" :layer-tips="el.name"> - 插入 + ${ + lang.INSERT + }

@@ -159,10 +224,10 @@ const fixCont = function(vm, tool) { function uploadFile(vm, tool) { for (let it of this.files) { let ext = it.name.slice(it.name.lastIndexOf('.')) - if (tool === 'image' && !/^\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(ext)) { + if (tool === 'IMAGE' && !/^\.(jpg|jpeg|png|gif|bmp|webp|ico)$/.test(ext)) { this.uploadQueue.push({ name: it.name, - progress: '0%(文件类型错误)', + progress: '0%(' + lang.ERROR.TYPE + ')', url: '' }) continue @@ -170,7 +235,7 @@ function uploadFile(vm, tool) { if (vm.props.uploadSizeLimit && it.size > vm.props.uploadSizeLimit) { this.uploadQueue.push({ name: it.name, - progress: '0%(文件体积过大)', + progress: '0%(' + lang.ERROR.SIZE + ')', url: '' }) continue @@ -250,7 +315,7 @@ function uploadScreenshot(vm, blob) { }) }) .then(url => { - vm.insert(`![截图](${url})`) + vm.insert(`![${lang.SCREENSHOT}](${url})`) }) .catch(err => { Anot.error(err) @@ -265,7 +330,7 @@ function uploadScreenshot(vm, blob) { } }) .then(url => { - vm.insert(`![截图](${url})`) + vm.insert(`![${lang.SCREENSHOT}](${url})`) }) } } @@ -306,7 +371,7 @@ function showDialog(elem, vm, tool) { }) .then(list => { list = list.filter(it => { - if (tool === 'image') { + if (tool === 'IMAGE') { return it.isImage } return true @@ -329,14 +394,14 @@ function showDialog(elem, vm, tool) { if (!it.url) { return } - let val = `\n${tool === 'image' ? '!' : ''}[${it.name}](${it.url})` + let val = `\n${tool === 'IMAGE' ? '!' : ''}[${it.name}](${it.url})` vm.insert(val) }, confirm: function() { if (!this.attach || !this.attachAlt) { - return layer.toast('描述和地址不能为空', 'error') + return layer.toast(lang.ERROR.EMPTY, 'error') } - let val = `\n${tool === 'image' ? '!' : ''}[${this.attachAlt}](${ + let val = `\n${tool === 'IMAGE' ? '!' : ''}[${this.attachAlt}](${ this.attach })` @@ -351,10 +416,10 @@ const plugin = { __init__(ME) { Object.assign(ME.vm.addon, { attach(elem) { - showDialog(elem, this, 'file') + showDialog(elem, this, 'FILE') }, image(elem) { - showDialog(elem, this, 'image') + showDialog(elem, this, 'IMAGE') } }) @@ -379,7 +444,7 @@ const plugin = { } if (blob !== null) { - layer.toast('截图处理中...') + layer.toast(lang.COMPRESS) // 压缩截图,避免文件过大 let reader = new FileReader() reader.onload = function() { @@ -394,7 +459,7 @@ const plugin = { ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.drawImage(this, 0, 0, canvas.width, canvas.height) - // 目前 IE和Safari的toBlob方法还不支持图片质量的设定 + // chrome, Firefox, 以及支持toBlob 设置图片质量 if (canvas.toBlob && (window.chrome || window.sidebar)) { canvas.toBlob( function(obj) { @@ -404,6 +469,8 @@ const plugin = { 0.8 ) } else { + // IE和Safari的toBlob方法还不支持图片质量的设定 + // 需要先转base64再转回Blob let base64 = canvas.toDataURL('image/jpeg', 0.8) let buf = atob(base64.split(',')[1]) let intArr = new Uint8Array(buf.length) diff --git a/src/meditor/addon/base.js b/src/meditor/addon/base.js index 0530f9b..0e45d7f 100644 --- a/src/meditor/addon/base.js +++ b/src/meditor/addon/base.js @@ -21,12 +21,11 @@ function trim(str, sign) { } const $doc = Anot(document) - const addon = { h1: function(elem) { let that = this let offset = Anot(elem).offset() - let wrap = this.selection(true) || '在此输入文本' + let wrap = this.selection(true) || Anot.ui.meditor.lang.PLACEHOLDER layer.open({ type: 7, menubar: false, @@ -49,23 +48,35 @@ const addon = { }, content: `
    -
  • 一级标题
  • -
  • 二级标题
  • -
  • 三级标题
  • -
  • 四级标题
  • -
  • 五级标题
  • -
  • 六级标题
  • +
  • ${ + Anot.ui.meditor.lang.HEADERS.H1 + }
  • +
  • ${ + Anot.ui.meditor.lang.HEADERS.H2 + }
  • +
  • ${ + Anot.ui.meditor.lang.HEADERS.H3 + }
  • +
  • ${ + Anot.ui.meditor.lang.HEADERS.H4 + }
  • +
  • ${ + Anot.ui.meditor.lang.HEADERS.H5 + }
  • +
  • ${ + Anot.ui.meditor.lang.HEADERS.H6 + }
` }) }, quote: function(elem) { - let wrap = this.selection() || '在此输入文本' + let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER wrap = '> ' + wrap this.insert(wrap, true) }, bold: function(elem) { - let wrap = this.selection() || '在此输入文本' + let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER let wraped = trim(wrap, '\\*\\*') wrap = wrap === wraped ? '**' + wrap + '**' : wraped @@ -73,7 +84,7 @@ const addon = { this.insert(wrap, true) }, italic: function(elem) { - let wrap = this.selection() || '在此输入文本' + let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER let wraped = trim(wrap, '_') wrap = wrap === wraped ? '_' + wrap + '_' : wraped @@ -81,7 +92,7 @@ const addon = { this.insert(wrap, true) }, through: function(elem) { - let wrap = this.selection() || '在此输入文本' + let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER let wraped = trim(wrap, '~~') wrap = wrap === wraped ? '~~' + wrap + '~~' : wraped @@ -89,13 +100,13 @@ const addon = { this.insert(wrap, true) }, unordered: function(elem) { - let wrap = this.selection() || '在此输入文本' + let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER wrap = '* ' + wrap this.insert(wrap, false) }, ordered: function(elem) { - let wrap = this.selection() || '在此输入文本' + let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER wrap = '1. ' + wrap this.insert(wrap, false) @@ -118,7 +129,7 @@ const addon = { linkTarget: 1, insert: function() { if (!this.link || !this.linkName) { - return layer.toast('链接文字和地址不能为空', 'error') + return layer.toast(Anot.ui.meditor.lang.LINK.ERROR, 'error') } let val = `[${this.linkName}](${this.link} ${ this.linkTarget === 1 ? ' "target=_blank"' : '' @@ -140,10 +151,14 @@ const addon = { content: `
- +
- +
确定 + :click="insert">${Anot.ui.meditor.lang.BTN.YES}
` }) @@ -183,7 +198,7 @@ const addon = { layer.open({ type: 7, - title: '插入表情', + title: Anot.ui.meditor.lang.LAYER.FACE_TITLE, fixed: true, maskClose: true, arr: [ @@ -251,7 +266,9 @@ const addon = { layer.open({ type: 7, - title: '0行 x 0列', + title: `0 ${Anot.ui.meditor.lang.TABLE.ROW} x 0 ${ + Anot.ui.meditor.lang.TABLE.COLUMN + }`, fixed: true, maskClose: true, offset: [ @@ -279,6 +296,7 @@ const addon = { success: function() { let tb = this.$refs.table let lastx, lasty + let { lang } = Anot.ui.meditor Anot(tb).bind('mousemove', ev => { if (ev.target.nodeName === 'SPAN') { @@ -289,7 +307,9 @@ const addon = { } lastx = x lasty = y - this.title = y + 1 + '行 x ' + (x + 1) + '列' + this.title = `${y + 1} ${lang.TABLE.ROW} x ${x + 1} ${ + lang.TABLE.COLUMN + }` for (let i = 0; i <= 9; i++) { for (let j = 0; j <= 9; j++) { this.matrix[i][j].v = i <= y && j <= x ? 1 : 0 @@ -300,7 +320,7 @@ const addon = { Anot(tb).bind('mouseleave', ev => { lastx = -1 lasty = -1 - this.title = '0行 x 0列' + this.title = `0 ${lang.TABLE.ROW} x 0 ${lang.TABLE.COLUMN}` for (let i = 0; i <= 9; i++) { for (let j = 0; j <= 9; j++) { this.matrix[i][j].v = 0 @@ -312,7 +332,7 @@ const addon = { let x = ev.target.dataset.x - 0 + 1 let y = ev.target.dataset.y - 0 + 1 - let thead = `\n\n${'| 表头 '.repeat(x)}|\n` + let thead = `\n\n${('| ' + lang.TABLE.THEAD + ' ').repeat(x)}|\n` let pipe = `${'| -- '.repeat(x)}|\n` let tbody = ('| '.repeat(x) + '|\n').repeat(y) @@ -337,7 +357,7 @@ const addon = { imgAlt: wrap, insert: function() { if (!this.img || !this.imgAlt) { - return layer.toast('链接文字和地址不能为空', 'error') + return layer.toast(Anot.ui.meditor.lang.LINK.ERROR, 'error') } let val = `![${this.imgAlt}](${this.img})` @@ -357,16 +377,20 @@ const addon = { content: `
- +
- +
确定 + :click="insert">${Anot.ui.meditor.lang.BTN.YES}
` @@ -376,7 +400,7 @@ const addon = { this.addon.link.call(this, elem) }, inlinecode: function(elem) { - let wrap = this.selection() || '在此输入文本' + let wrap = this.selection() || Anot.ui.meditor.lang.PLACEHOLDER let wraped = trim(wrap, '`') wrap = wrap === wraped ? '`' + wrap + '`' : wraped @@ -387,7 +411,8 @@ const addon = { let offset = Anot(elem).offset() layer.open({ type: 7, - title: '添加代码块', + menubar: false, + fixed: true, __lang__: [ { id: 'asp' }, { id: 'actionscript', name: 'ActionScript(3.0)/Flash/Flex' }, @@ -422,7 +447,7 @@ const addon = { { id: 'typescript' }, { id: 'xml' }, { id: 'yaml' }, - { id: 'other', name: '其他语言' } + { id: 'other', name: Anot.ui.meditor.lang.CODE.OTHER } ], lang: 'javascript', code: '', @@ -431,26 +456,33 @@ const addon = { shift: { top: offset.top - $doc.scrollTop() }, insert: function() { let val = `\n\`\`\`${this.lang}\n${this.code || - '// 在此输入代码'}\n\`\`\`\n` + '// ' + Anot.ui.meditor.lang.PLACEHOLDER}\n\`\`\`\n` that.insert(val, false) this.close() }, content: `
- 语言类型 - +
+ + + + + +
- +
确定 + :click="insert">${Anot.ui.meditor.lang.BTN.YES}
` @@ -477,25 +509,21 @@ const addon = { let offset = Anot(elem).offset() layer.open({ type: 7, - title: '关于编辑器', + title: Anot.ui.meditor.lang.LAYER.ABOUT_TITLE, maskClose: true, offset: [offset.top + 35 - $doc.scrollTop()], shift: { top: offset.top - $doc.scrollTop() }, - content: - '
' + - '
' +
-        ' __  __ _____    _ _ _\n' +
-        '|  \\/  | ____|__| (_) |_ ___  _ __\n' +
-        "| |\\/| |  _| / _` | | __/ _ \\| '__|\n" +
-        '| |  | | |__| (_| | | || (_) | |\n' +
-        '|_|  |_|_____\\__,_|_|\\__\\___/|_|    ' +
-        'v' +
-        Anot.ui.meditor +
-        '
' + - '

开源在线Markdown编辑器

' + - '

https://doui.cc/product/meditor

' + - '

Copyright © 2017 Yutent, The MIT License.

' + - '
' + content: `
+
+ __  __ _____    _ _ _
+|  \\/  | ____|__| (_) |_ ___  _ __
+| |\\/| |  _| / _\` | | __/ _ \\| '__|
+| |  | | |__| (_| | | || (_) | |
+|_|  |_|_____\\__,_|_|\\__\\___/|_|    v${Anot.ui.meditor.version}
+

${Anot.ui.meditor.lang.NAME}

+

https://doui.cc/product/meditor

+

Copyright © 2017 Yutent, The MIT License.

+
` }) } } diff --git a/src/meditor/index.js b/src/meditor/index.js index 4ac51d7..269645e 100644 --- a/src/meditor/index.js +++ b/src/meditor/index.js @@ -12,6 +12,136 @@ import '../marked/index' import addon from './addon/base' import 'css/meditor.scss' +const LANGUAGES = { + en: { + NAME: 'Open Source Markdown Editor', + TOOLBAR: { + PIPE: '', + H1: 'Header', + QUOTE: 'Quote text', + BOLD: 'Font-bold', + ITALIC: 'Font-italic', + THROUGH: 'Font-through', + UNORDERED: 'Unordered list', + ORDERED: 'Ordered list', + LINK: 'Hyperlink', + HR: 'Line', + TIME: 'Insert current time', + FACE: 'Face', + TABLE: 'Insert table', + IMAGE: 'Upload Pictures', + FILE: 'Upload Files', + INLINECODE: 'Inline code', + BLOCKCODE: 'Block code', + PREVIEW: 'Preview', + FULLSCREEN: 'Fullscreen', + ABOUT: 'About MEditor' + }, + HEADERS: { + H1: '#{1}', + H2: '#{2}', + H3: '#{3}', + H4: '#{4}', + H5: '#{5}', + H6: '#{6}' + }, + PLACEHOLDER: 'Type here', + LINK: { + ALT: 'Link text', + URL: 'Link address', + ERROR: 'Link address and text can not be null' + }, + IMAGE: { + ALT: 'Image alt text', + URL: 'Image address' + }, + TARGET: { + BLANK: 'New window open', + SELF: 'Current window open' + }, + BTN: { + YES: 'OK' + }, + TABLE: { + ROW: 'row', + COLUMN: 'column', + THEAD: 'thead' + }, + LAYER: { + FACE_TITLE: 'Insert Face', + ABOUT_TITLE: 'About MEditor' + }, + CODE: { + OTHER: 'Other language' + } + }, + zh: { + NAME: '开源在线Markdown编辑器', + TOOLBAR: { + PIPE: '', + H1: '标题', + QUOTE: '引用文本', + BOLD: '粗体', + ITALIC: '斜体', + THROUGH: '删除线', + UNORDERED: '无序列表', + ORDERED: '有序列表', + LINK: '超链接', + HR: '横线', + TIME: '插入当前时间', + FACE: '表情', + TABLE: '插入表格', + IMAGE: '插入图片', + FILE: '插入附件', + INLINECODE: '行内代码', + BLOCKCODE: '代码块', + PREVIEW: '预览', + FULLSCREEN: '全屏', + ABOUT: '关于编辑器' + }, + HEADERS: { + H1: '一级标题', + H2: '二级标题', + H3: '三级标题', + H4: '四级标题', + H5: '五级标题', + H6: '六级标题' + }, + PLACEHOLDER: '在此输入文本', + LINK: { + ALT: '链接文字', + URL: '链接地址', + ERROR: '链接文字和地址不能为空' + }, + IMAGE: { + ALT: '图片描述', + URL: '图片地址' + }, + TARGET: { + BLANK: '新窗口打开', + SELF: '本窗口打开' + }, + BTN: { + YES: '确定' + }, + TABLE: { + ROW: '行', + COLUMN: '列', + THEAD: '表头' + }, + LAYER: { + FACE_TITLE: '插入表情', + ABOUT_TITLE: '关于编辑器' + }, + CODE: { + OTHER: '其他语言' + } + } +} +LANGUAGES['zh-CN'] = LANGUAGES.zh +LANGUAGES['zh-TW'] = LANGUAGES.zh +const lang = LANGUAGES[Anot.language || navigator.language || 'en'] + marked.setOptions({ highlight: function(code, lang) { return Prism.highlight(code, Prism.languages[lang]) @@ -28,31 +158,9 @@ if (!String.prototype.repeat) { } } -Anot.ui.meditor = '1.0.0' +Anot.ui.meditor = { version: '1.0.0', author: 'yutent', lang } const log = console.log -// 工具栏title -const TOOLBAR = { - pipe: '', - h1: '标题', - quote: '引用文本', - bold: '粗体', - italic: '斜体', - through: '删除线', - unordered: '无序列表', - ordered: '有序列表', - link: '超链接', - hr: '横线', - time: '插入当前时间', - face: '表情', - table: '插入表格', - image: '插入图片', - file: '插入附件', - inlinecode: '行内代码', - blockcode: '代码块', - preview: '预览', - fullscreen: '全屏', - about: '关于编辑器' -} + const DEFAULT_TOOLBAR = [ 'h1', 'quote', @@ -234,7 +342,7 @@ function tool(name) { name = (name + '').trim().toLowerCase() name = '|' === name ? 'pipe' : name - let title = TOOLBAR[name] + let title = lang.TOOLBAR[name.toUpperCase()] let extra = '' switch (name) { case 'preview':