diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..f9f2a02 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [ "es2015" ] +} \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..f0165fa --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +*.less +*.sass +*.pcss +*.css +*.html \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..ca45ce4 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,74 @@ +{ + "env": { + "es6": true, + "node": true, + "browser": true + }, + "parserOptions": { + "ecmaVersion": 6, + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + }, + "sourceType": "module" + }, + "globals": { + "VERSION": false + }, + "plugins": [ + "import" + ], + "rules": { + "indent": "off", + "semi": [ + "error", + "never" + ], + "comma-dangle": [ + "error", + "never" + ], + "comma-spacing": "off", + "import/no-extraneous-dependencies": "off", + "no-param-reassign": [ + "error", + { + "props": false + } + ], + "no-plusplus": "off", + "eol-last": "off", + "class-methods-use-this": "off", + "object-curly-spacing": "off", + "new-cap": [ + "error", + { + "newIsCap": true, + "capIsNew": false, + "properties": true + } + ], + "space-before-blocks": "off", + "radix": [ + "error", + "as-needed" + ], + "import/no-unresolved": "off", + "import/extensions": [ + "error", + { + "js": "never", + "json": "always" + } + ], + "no-console": [ + "error", + { + "allow": [ + "warn", + "error" + ] + } + ] + }, + "extends": "eslint-config-airbnb" +} \ No newline at end of file diff --git a/README.md b/README.md index 3b3355f..50a79fc 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ -# Intro +# 简介 Intro -Vue-html5-editor is an html5 wysiwyg editor for vue,easy and flexible,compatible with Vue.js 1.0+. +Vue-html5-editor是一个Vue的富文本编辑器插件,简洁灵活可扩展,适用于vue2.0以上版本,支持IE11. + +Vue-html5-editor is an html5 wysiwyg editor for vue,easy and flexible,compatible with Vue.js 2.0+,support IE11. ![screenshot](http://tai.coding.me/vue-html5-editor/editor.png?v=20160912) -[demo is here](http://tai.coding.me/vue-html5-editor) +[点击查看演示效果 Demo is here](http://tai.coding.me/vue-html5-editor) -# Installation +# 安装 Installation ### Npm @@ -15,29 +17,51 @@ Vue-html5-editor is an html5 wysiwyg editor for vue,easy and flexible,compatible npm install vue-html5-editor --save-dev ``` +引入并安装作为全局组件 + import and install as global component ```js -var Vue = require("vue") -var editor = require("vue-html5-editor") -Vue.use(editor,options); +import Vue from 'vue' +import VueHtml5Editor from 'vue-html5-editor' +Vue.use(VueHtml5Editor,options); ``` -### browser +同样你也可以作为局部组件使用,方便在不同的场景里使用不同的配置. + +```js +const editor1 = new VueHtml5Editor(options1) +const app1 = new Vue({ + components:{editor1 + } +}) +const editor2 = new VueHtml5Editor(options2) +const app2 = new Vue({ + components:{editor2 + } +}) +``` + + +### 浏览器直接使用 browser ```html ``` -Install using global variable `VueHtml5Editor` +通过全局变量`VueHtml5Editor`来安装. + +Install using global variable `VueHtml5Editor`. ```js Vue.use(VueHtml5Editor, options) ``` -# Usage +# 使用说明 Usage + +模板代码如下: -template code as follows +template code as follows: ```html @@ -47,9 +71,11 @@ template code as follows ```js Vue.use(VueHtml5Editor, { - //global component name + // 全局组件名称,使用new VueHtml5Editor(options)时该选项无效 + // global component name name: "vue-html5-editor", - //custom icon class of built-in modules,default using font-awesome + // 自定义各个图标的class,默认使用的是font-awesome提供的图标 + // custom icon class of built-in modules,default using font-awesome icons: { text: "fa fa-pencil", color: "fa fa-paint-brush", @@ -66,21 +92,27 @@ Vue.use(VueHtml5Editor, { "full-screen": "fa fa-arrows-alt", info: "fa fa-info", }, - //config image module + // 配置图片模块 + // config image module image: { - //Url of the server-side,default null and convert image to base64 + // 后端图片上传的地址,如果为空,默认转图片为base64 + // Url of the server-side,default null and convert image to base64 server: null, - //the name for file field in multipart request + // 请求时表单参数名 + // the name for file field in multipart request fieldName: "image", - //max file size + // 文件最大体积,单位字节 max file size sizeLimit: 512 * 1024, + // 是否压缩,默认true,设置为true时会使用localResizeIMG进行压缩 // default true,if set to true,the image will resize by localResizeIMG (https://github.com/think2011/localResizeIMG) compress: true, - //follows are options of localResizeIMG + // 图片压缩选项 + // follows are options of localResizeIMG width: 1600, height: 1600, quality: 80, - //handle response data,return image url + // 响应数据处理 + // handle response data,return image url uploadHandler(responseText){ //default accept json data like {ok:false,msg:"unexpected"} or {ok:true,data:"image url"} var json = JSON.parse(responseText) @@ -91,8 +123,10 @@ Vue.use(VueHtml5Editor, { } } }, + // 语言,内建的有英文(en-us)和中文(zh-cn) //default en-us, en-us and zh-cn are built-in language: "zh-cn", + // 自定义语言 i18n: { //specify your language here "zh-cn": { @@ -138,10 +172,12 @@ Vue.use(VueHtml5Editor, { "reset": "重置" } }, - //the modules you don't want + // 隐藏不想要显示出来的模块 + // the modules you don't want hiddenModules: [], - //keep only the modules you want and customize the order. - //can be used with hiddenModules together + // 自定义要显示的模块,并控制顺序 + // keep only the modules you want and customize the order. + // can be used with hiddenModules together visibleModules: [ "text", "color", @@ -158,14 +194,15 @@ Vue.use(VueHtml5Editor, { "full-screen", "info", ], - //extended modules + // 扩展模块,具体可以参考examples或查看源码 + // extended modules modules: { //omit,reference to source code of build-in modules } }) ``` -# attributes of component +# 组件属性 attributes of component ```html @@ -173,18 +210,26 @@ Vue.use(VueHtml5Editor, { ### content -Content to edit,need two-way binding +编辑内容 + +The html content to edit ### height -The height or min-height ( if auto-height is true ) of editor +编辑器高度,如果设置了`auto-height`为true,将设置为编辑器的最小高度. + +The height or min-height ( if auto-height is true ) of editor. ### z-index +层级,将会设置编辑器容量的`z-index`样式属性. + Sets z-index style property of editor ### auto-height +是否自动根据内容控制编辑器高度. + Resize editor height Automatically # License diff --git a/dist/vue-html5-editor.js b/dist/vue-html5-editor.js index ade533c..d268b4e 100644 --- a/dist/vue-html5-editor.js +++ b/dist/vue-html5-editor.js @@ -1,4242 +1,1421 @@ -/*! - * Vue-html5-editor 0.5.1 - * https://github.com/PeakTai/vue-html5-editor +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.VueHtml5Editor = factory()); +}(this, (function () { 'use strict'; + +function __$styleInject(css, returnValue) { + if (typeof document === 'undefined') { + return returnValue; + } + css = css || ''; + var head = document.head || document.getElementsByTagName('head')[0]; + var style = document.createElement('style'); + style.type = 'text/css'; + if (style.styleSheet){ + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + head.appendChild(style); + return returnValue; +} + +var template = "
"; + +/** + * Created by peak on 2017/2/10. */ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define([], factory); - else if(typeof exports === 'object') - exports["VueHtml5Editor"] = factory(); - else - root["VueHtml5Editor"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; +var dashboard = { + template: template +}; -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - var _keys = __webpack_require__(1); - - var _keys2 = _interopRequireDefault(_keys); - - var _editor = __webpack_require__(36); - - var _editor2 = _interopRequireDefault(_editor); - - var _index = __webpack_require__(43); - - var _index2 = _interopRequireDefault(_index); - - var _index3 = __webpack_require__(46); - - var _index4 = _interopRequireDefault(_index3); - - var _index5 = __webpack_require__(52); - - var _index6 = _interopRequireDefault(_index5); - - var _index7 = __webpack_require__(58); - - var _index8 = _interopRequireDefault(_index7); - - var _index9 = __webpack_require__(61); - - var _index10 = _interopRequireDefault(_index9); - - var _index11 = __webpack_require__(64); - - var _index12 = _interopRequireDefault(_index11); - - var _index13 = __webpack_require__(68); - - var _index14 = _interopRequireDefault(_index13); - - var _index15 = __webpack_require__(69); - - var _index16 = _interopRequireDefault(_index15); - - var _index17 = __webpack_require__(73); - - var _index18 = _interopRequireDefault(_index17); - - var _hr = __webpack_require__(116); - - var _hr2 = _interopRequireDefault(_hr); - - var _index19 = __webpack_require__(117); - - var _index20 = _interopRequireDefault(_index19); - - var _index21 = __webpack_require__(118); - - var _index22 = _interopRequireDefault(_index21); - - var _index23 = __webpack_require__(119); - - var _index24 = _interopRequireDefault(_index23); - - var _index25 = __webpack_require__(120); - - var _index26 = _interopRequireDefault(_index25); - - var _zhCn = __webpack_require__(124); - - var _zhCn2 = _interopRequireDefault(_zhCn); - - var _enUs = __webpack_require__(125); - - var _enUs2 = _interopRequireDefault(_enUs); - - var _arrayPolyfill = __webpack_require__(126); - - var _arrayPolyfill2 = _interopRequireDefault(_arrayPolyfill); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - /** - * install - * @param Vue {Vue} - * @param options {Object} - */ - exports.install = function (Vue, options) { - - (0, _arrayPolyfill2.default)(); - - options = options || {}; - - //modules - var modules = [_index2.default, _index6.default, _index4.default, _index8.default, _index10.default, _index12.default, _index14.default, _index16.default, _index18.default, _hr2.default, _index20.default, _index22.default, _index24.default, _index26.default]; - //extended modules - if (Array.isArray(options.modules)) { - (function () { - var arr = []; - options.modules.forEach(function (module) { - if (module.name) { - arr.push(module); - } - }); - modules = modules.concat(arr); - })(); - } - //hidden modules - if (Array.isArray(options.hiddenModules)) { - modules = function () { - var arr = []; - modules.forEach(function (m) { - if (!options.hiddenModules.includes(m.name)) { - arr.push(m); - } - }); - return arr; - }(); - } - //visible modules - if (Array.isArray(options.visibleModules)) { - modules = function () { - var arr = []; - options.visibleModules.forEach(function (name) { - modules.forEach(function (module) { - if (module.name == name) { - arr.push(module); - } - }); - }); - return arr; - }(); - } - - var components = {}; - modules.forEach(function (module) { - - //specify the config for each module in options by name - var config = options[module.name]; - module.config = Vue.util.extend(module.config || {}, config || {}); - - if (module.dashboard) { - //$options.module - module.dashboard.module = module; - components['dashboard-' + module.name] = module.dashboard; - } - if (options.icons && options.icons[module.name]) { - module.icon = options.icons[module.name]; - } - - module.hasDashboard = !!module.dashboard; - //prevent vue sync - module.dashboard = null; - }); - - //i18n - var i18n = { "zh-cn": _zhCn2.default, "en-us": _enUs2.default }; - var customI18n = options.i18n || {}; - (0, _keys2.default)(customI18n).forEach(function (key) { - i18n[key] = i18n[key] ? Vue.util.extend(i18n[key], customI18n[key]) : customI18n[key]; - }); - var language = options.language || "en-us"; - var locale = i18n[language] || i18n["en-us"]; - - var component = Vue.extend(_editor2.default).extend({ - data: function data() { - return { modules: modules, locale: locale }; - }, - - components: components - }); - - Vue.component(options.name || "vue-html5-editor", component); - }; - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = { "default": __webpack_require__(2), __esModule: true }; - -/***/ }, -/* 2 */ -/***/ function(module, exports, __webpack_require__) { - - __webpack_require__(3); - module.exports = __webpack_require__(23).Object.keys; - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - // 19.1.2.14 Object.keys(O) - var toObject = __webpack_require__(4) - , $keys = __webpack_require__(6); - - __webpack_require__(21)('keys', function(){ - return function keys(it){ - return $keys(toObject(it)); - }; - }); - -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - - // 7.1.13 ToObject(argument) - var defined = __webpack_require__(5); - module.exports = function(it){ - return Object(defined(it)); - }; - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - // 7.2.1 RequireObjectCoercible(argument) - module.exports = function(it){ - if(it == undefined)throw TypeError("Can't call method on " + it); - return it; - }; - -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { - - // 19.1.2.14 / 15.2.3.14 Object.keys(O) - var $keys = __webpack_require__(7) - , enumBugKeys = __webpack_require__(20); - - module.exports = Object.keys || function keys(O){ - return $keys(O, enumBugKeys); - }; - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - var has = __webpack_require__(8) - , toIObject = __webpack_require__(9) - , arrayIndexOf = __webpack_require__(12)(false) - , IE_PROTO = __webpack_require__(16)('IE_PROTO'); - - module.exports = function(object, names){ - var O = toIObject(object) - , i = 0 - , result = [] - , key; - for(key in O)if(key != IE_PROTO)has(O, key) && result.push(key); - // Don't enum bug & hidden keys - while(names.length > i)if(has(O, key = names[i++])){ - ~arrayIndexOf(result, key) || result.push(key); - } - return result; - }; - -/***/ }, -/* 8 */ -/***/ function(module, exports) { - - var hasOwnProperty = {}.hasOwnProperty; - module.exports = function(it, key){ - return hasOwnProperty.call(it, key); - }; - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - // to indexed object, toObject with fallback for non-array-like ES3 strings - var IObject = __webpack_require__(10) - , defined = __webpack_require__(5); - module.exports = function(it){ - return IObject(defined(it)); - }; - -/***/ }, -/* 10 */ -/***/ function(module, exports, __webpack_require__) { - - // fallback for non-array-like ES3 and non-enumerable old V8 strings - var cof = __webpack_require__(11); - module.exports = Object('z').propertyIsEnumerable(0) ? Object : function(it){ - return cof(it) == 'String' ? it.split('') : Object(it); - }; - -/***/ }, -/* 11 */ -/***/ function(module, exports) { - - var toString = {}.toString; - - module.exports = function(it){ - return toString.call(it).slice(8, -1); - }; - -/***/ }, -/* 12 */ -/***/ function(module, exports, __webpack_require__) { - - // false -> Array#indexOf - // true -> Array#includes - var toIObject = __webpack_require__(9) - , toLength = __webpack_require__(13) - , toIndex = __webpack_require__(15); - module.exports = function(IS_INCLUDES){ - return function($this, el, fromIndex){ - var O = toIObject($this) - , length = toLength(O.length) - , index = toIndex(fromIndex, length) - , value; - // Array#includes uses SameValueZero equality algorithm - if(IS_INCLUDES && el != el)while(length > index){ - value = O[index++]; - if(value != value)return true; - // Array#toIndex ignores holes, Array#includes - not - } else for(;length > index; index++)if(IS_INCLUDES || index in O){ - if(O[index] === el)return IS_INCLUDES || index || 0; - } return !IS_INCLUDES && -1; - }; - }; - -/***/ }, -/* 13 */ -/***/ function(module, exports, __webpack_require__) { - - // 7.1.15 ToLength - var toInteger = __webpack_require__(14) - , min = Math.min; - module.exports = function(it){ - return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 - }; - -/***/ }, -/* 14 */ -/***/ function(module, exports) { - - // 7.1.4 ToInteger - var ceil = Math.ceil - , floor = Math.floor; - module.exports = function(it){ - return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); - }; - -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { - - var toInteger = __webpack_require__(14) - , max = Math.max - , min = Math.min; - module.exports = function(index, length){ - index = toInteger(index); - return index < 0 ? max(index + length, 0) : min(index, length); - }; - -/***/ }, -/* 16 */ -/***/ function(module, exports, __webpack_require__) { - - var shared = __webpack_require__(17)('keys') - , uid = __webpack_require__(19); - module.exports = function(key){ - return shared[key] || (shared[key] = uid(key)); - }; - -/***/ }, -/* 17 */ -/***/ function(module, exports, __webpack_require__) { - - var global = __webpack_require__(18) - , SHARED = '__core-js_shared__' - , store = global[SHARED] || (global[SHARED] = {}); - module.exports = function(key){ - return store[key] || (store[key] = {}); - }; - -/***/ }, -/* 18 */ -/***/ function(module, exports) { - - // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 - var global = module.exports = typeof window != 'undefined' && window.Math == Math - ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); - if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef - -/***/ }, -/* 19 */ -/***/ function(module, exports) { - - var id = 0 - , px = Math.random(); - module.exports = function(key){ - return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); - }; - -/***/ }, -/* 20 */ -/***/ function(module, exports) { - - // IE 8- don't enum bug keys - module.exports = ( - 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' - ).split(','); - -/***/ }, -/* 21 */ -/***/ function(module, exports, __webpack_require__) { - - // most Object methods by ES6 should accept primitives - var $export = __webpack_require__(22) - , core = __webpack_require__(23) - , fails = __webpack_require__(32); - module.exports = function(KEY, exec){ - var fn = (core.Object || {})[KEY] || Object[KEY] - , exp = {}; - exp[KEY] = exec(fn); - $export($export.S + $export.F * fails(function(){ fn(1); }), 'Object', exp); - }; - -/***/ }, -/* 22 */ -/***/ function(module, exports, __webpack_require__) { - - var global = __webpack_require__(18) - , core = __webpack_require__(23) - , ctx = __webpack_require__(24) - , hide = __webpack_require__(26) - , PROTOTYPE = 'prototype'; - - var $export = function(type, name, source){ - var IS_FORCED = type & $export.F - , IS_GLOBAL = type & $export.G - , IS_STATIC = type & $export.S - , IS_PROTO = type & $export.P - , IS_BIND = type & $export.B - , IS_WRAP = type & $export.W - , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) - , expProto = exports[PROTOTYPE] - , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] - , key, own, out; - if(IS_GLOBAL)source = name; - for(key in source){ - // contains in native - own = !IS_FORCED && target && target[key] !== undefined; - if(own && key in exports)continue; - // export native or passed - out = own ? target[key] : source[key]; - // prevent global pollution for namespaces - exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] - // bind timers to global for call from export context - : IS_BIND && own ? ctx(out, global) - // wrap global constructors for prevent change them in library - : IS_WRAP && target[key] == out ? (function(C){ - var F = function(a, b, c){ - if(this instanceof C){ - switch(arguments.length){ - case 0: return new C; - case 1: return new C(a); - case 2: return new C(a, b); - } return new C(a, b, c); - } return C.apply(this, arguments); - }; - F[PROTOTYPE] = C[PROTOTYPE]; - return F; - // make static versions for prototype methods - })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; - // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% - if(IS_PROTO){ - (exports.virtual || (exports.virtual = {}))[key] = out; - // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% - if(type & $export.R && expProto && !expProto[key])hide(expProto, key, out); - } - } - }; - // type bitmap - $export.F = 1; // forced - $export.G = 2; // global - $export.S = 4; // static - $export.P = 8; // proto - $export.B = 16; // bind - $export.W = 32; // wrap - $export.U = 64; // safe - $export.R = 128; // real proto method for `library` - module.exports = $export; - -/***/ }, -/* 23 */ -/***/ function(module, exports) { - - var core = module.exports = {version: '2.4.0'}; - if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef - -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { - - // optional / simple context binding - var aFunction = __webpack_require__(25); - module.exports = function(fn, that, length){ - aFunction(fn); - if(that === undefined)return fn; - switch(length){ - case 1: return function(a){ - return fn.call(that, a); - }; - case 2: return function(a, b){ - return fn.call(that, a, b); - }; - case 3: return function(a, b, c){ - return fn.call(that, a, b, c); - }; - } - return function(/* ...args */){ - return fn.apply(that, arguments); - }; - }; - -/***/ }, -/* 25 */ -/***/ function(module, exports) { - - module.exports = function(it){ - if(typeof it != 'function')throw TypeError(it + ' is not a function!'); - return it; - }; - -/***/ }, -/* 26 */ -/***/ function(module, exports, __webpack_require__) { - - var dP = __webpack_require__(27) - , createDesc = __webpack_require__(35); - module.exports = __webpack_require__(31) ? function(object, key, value){ - return dP.f(object, key, createDesc(1, value)); - } : function(object, key, value){ - object[key] = value; - return object; - }; - -/***/ }, -/* 27 */ -/***/ function(module, exports, __webpack_require__) { - - var anObject = __webpack_require__(28) - , IE8_DOM_DEFINE = __webpack_require__(30) - , toPrimitive = __webpack_require__(34) - , dP = Object.defineProperty; - - exports.f = __webpack_require__(31) ? Object.defineProperty : function defineProperty(O, P, Attributes){ - anObject(O); - P = toPrimitive(P, true); - anObject(Attributes); - if(IE8_DOM_DEFINE)try { - return dP(O, P, Attributes); - } catch(e){ /* empty */ } - if('get' in Attributes || 'set' in Attributes)throw TypeError('Accessors not supported!'); - if('value' in Attributes)O[P] = Attributes.value; - return O; - }; - -/***/ }, -/* 28 */ -/***/ function(module, exports, __webpack_require__) { - - var isObject = __webpack_require__(29); - module.exports = function(it){ - if(!isObject(it))throw TypeError(it + ' is not an object!'); - return it; - }; - -/***/ }, -/* 29 */ -/***/ function(module, exports) { - - module.exports = function(it){ - return typeof it === 'object' ? it !== null : typeof it === 'function'; - }; - -/***/ }, -/* 30 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = !__webpack_require__(31) && !__webpack_require__(32)(function(){ - return Object.defineProperty(__webpack_require__(33)('div'), 'a', {get: function(){ return 7; }}).a != 7; - }); - -/***/ }, -/* 31 */ -/***/ function(module, exports, __webpack_require__) { - - // Thank's IE8 for his funny defineProperty - module.exports = !__webpack_require__(32)(function(){ - return Object.defineProperty({}, 'a', {get: function(){ return 7; }}).a != 7; - }); - -/***/ }, -/* 32 */ -/***/ function(module, exports) { - - module.exports = function(exec){ - try { - return !!exec(); - } catch(e){ - return true; - } - }; - -/***/ }, -/* 33 */ -/***/ function(module, exports, __webpack_require__) { - - var isObject = __webpack_require__(29) - , document = __webpack_require__(18).document - // in old IE typeof document.createElement is 'object' - , is = isObject(document) && isObject(document.createElement); - module.exports = function(it){ - return is ? document.createElement(it) : {}; - }; - -/***/ }, -/* 34 */ -/***/ function(module, exports, __webpack_require__) { - - // 7.1.1 ToPrimitive(input [, PreferredType]) - var isObject = __webpack_require__(29); - // instead of the ES6 spec version, we didn't implement @@toPrimitive case - // and the second argument - flag - preferred type is a string - module.exports = function(it, S){ - if(!isObject(it))return it; - var fn, val; - if(S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; - if(typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it)))return val; - if(!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; - throw TypeError("Can't convert object to primitive value"); - }; - -/***/ }, -/* 35 */ -/***/ function(module, exports) { - - module.exports = function(bitmap, value){ - return { - enumerable : !(bitmap & 1), - configurable: !(bitmap & 2), - writable : !(bitmap & 4), - value : value - }; - }; - -/***/ }, -/* 36 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __webpack_require__(37) - __vue_script__ = __webpack_require__(41) - if (__vue_script__ && - __vue_script__.__esModule && - Object.keys(__vue_script__).length > 1) { - console.warn("[vue-loader] src/editor.vue: named exports in *.vue files are ignored.")} - __vue_template__ = __webpack_require__(42) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/editor.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 37 */ -/***/ function(module, exports, __webpack_require__) { - - // style-loader: Adds some css to the DOM by adding a - // - // - -/***/ }, -/* 42 */ -/***/ function(module, exports) { - - module.exports = "\n
\n
\n \n
\n
\n
\n
\n
\n
\n
\n"; - -/***/ }, -/* 43 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(44); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - exports.default = { - name: "text", - icon: "fa fa-pencil", - i18n: "text", - show: true, - dashboard: _dashboard2.default - }; /** - * text,set the text bold or italic or underline or with strike through or subscript or superscript - * Created by peak on 16/8/18. - */ - -/***/ }, -/* 44 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __vue_template__ = __webpack_require__(45) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/text/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 45 */ -/***/ function(module, exports) { - - module.exports = "\n\n\n\n\n\n\n"; - -/***/ }, -/* 46 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(47); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - /** - * font name and font size - * Created by peak on 16/8/18. - */ - exports.default = { - name: "font", - icon: "fa fa-font", - i18n: "font", - show: true, - dashboard: _dashboard2.default - }; - -/***/ }, -/* 47 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __webpack_require__(48) - __vue_script__ = __webpack_require__(50) - if (__vue_script__ && - __vue_script__.__esModule && - Object.keys(__vue_script__).length > 1) { - console.warn("[vue-loader] src/modules/font/dashboard.vue: named exports in *.vue files are ignored.")} - __vue_template__ = __webpack_require__(51) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/font/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 48 */ -/***/ function(module, exports, __webpack_require__) { - - // style-loader: Adds some css to the DOM by adding a - // - // - -/***/ }, -/* 51 */ -/***/ function(module, exports) { - - module.exports = "\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n"; - -/***/ }, -/* 52 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(53); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - /** - * fore color and back color - * Created by peak on 16/8/18. - */ - exports.default = { - name: "color", - icon: "fa fa-paint-brush", - i18n: "color", - show: true, - dashboard: _dashboard2.default - }; - -/***/ }, -/* 53 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __webpack_require__(54) - __vue_script__ = __webpack_require__(56) - if (__vue_script__ && - __vue_script__.__esModule && - Object.keys(__vue_script__).length > 1) { - console.warn("[vue-loader] src/modules/color/dashboard.vue: named exports in *.vue files are ignored.")} - __vue_template__ = __webpack_require__(57) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/color/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 54 */ -/***/ function(module, exports, __webpack_require__) { - - // style-loader: Adds some css to the DOM by adding a - // - // - -/***/ }, -/* 57 */ -/***/ function(module, exports) { - - module.exports = "\n
\n \n \n
\n
\n
\n
\n
\n
\n"; - -/***/ }, -/* 58 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(59); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - exports.default = { - name: "align", - icon: "fa fa-align-center", - i18n: "align", - show: true, - dashboard: _dashboard2.default - }; /** - * text align - * Created by peak on 16/8/18. - */ - -/***/ }, -/* 59 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __vue_template__ = __webpack_require__(60) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/align/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 60 */ -/***/ function(module, exports) { - - module.exports = "\n\n\n\n"; - -/***/ }, -/* 61 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(62); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - exports.default = { - name: "list", - icon: "fa fa-list", - show: true, - i18n: "list", - dashboard: _dashboard2.default - }; /** - * list,ul,ol - * Created by peak on 16/8/18. - */ - -/***/ }, -/* 62 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __vue_template__ = __webpack_require__(63) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/list/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 63 */ -/***/ function(module, exports) { - - module.exports = "\n\n\n"; - -/***/ }, -/* 64 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(65); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - exports.default = { - name: "link", - icon: "fa fa-chain", - show: true, - i18n: "link", - dashboard: _dashboard2.default - }; /** - * create link - * Created by peak on 16/8/18. - */ - -/***/ }, -/* 65 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __vue_script__ = __webpack_require__(66) - if (__vue_script__ && - __vue_script__.__esModule && - Object.keys(__vue_script__).length > 1) { - console.warn("[vue-loader] src/modules/link/dashboard.vue: named exports in *.vue files are ignored.")} - __vue_template__ = __webpack_require__(67) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/link/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 66 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - // - // - -/***/ }, -/* 67 */ -/***/ function(module, exports) { - - module.exports = "\n
\n \n \n
\n"; - -/***/ }, -/* 68 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - /** - * unlink - * Created by peak on 16/8/18. - */ - exports.default = { - name: "unlink", - icon: "fa fa-chain-broken", - show: true, - i18n: "unlink", - handler: function handler(editor) { - editor.execCommand("unlink"); - } - }; - -/***/ }, -/* 69 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(70); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - /** - * insert table - * Created by peak on 16/8/18. - */ - exports.default = { - //can not named table - //dashboard will add to editor as a child component and named as module name - //Do not use built-in or reserved HTML elements as component id - name: "tabulation", - icon: "fa fa-table", - i18n: "table", - show: true, - dashboard: _dashboard2.default - }; - -/***/ }, -/* 70 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __vue_script__ = __webpack_require__(71) - if (__vue_script__ && - __vue_script__.__esModule && - Object.keys(__vue_script__).length > 1) { - console.warn("[vue-loader] src/modules/table/dashboard.vue: named exports in *.vue files are ignored.")} - __vue_template__ = __webpack_require__(72) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/table/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 71 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - // - // - // - -/***/ }, -/* 72 */ -/***/ function(module, exports) { - - module.exports = "\n
\n \n \n\n \n
\n"; - -/***/ }, -/* 73 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _dashboard = __webpack_require__(74); - - var _dashboard2 = _interopRequireDefault(_dashboard); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - /** - * insert image - * Created by peak on 16/8/18. - */ - exports.default = { - name: "image", - icon: "fa fa-file-image-o", - i18n: "image", - show: true, - config: { - server: null, - fieldName: "image", - sizeLimit: 512 * 1024, //512k - compress: true, - width: 1600, - height: 1600, - quality: 80, - uploadHandler: function uploadHandler(responseText) { - var json = JSON.parse(responseText); - if (!json.ok) { - alert(json.msg); - } else { - return json.data; - } - } - }, - dashboard: _dashboard2.default - }; - -/***/ }, -/* 74 */ -/***/ function(module, exports, __webpack_require__) { - - var __vue_script__, __vue_template__ - __vue_script__ = __webpack_require__(75) - if (__vue_script__ && - __vue_script__.__esModule && - Object.keys(__vue_script__).length > 1) { - console.warn("[vue-loader] src/modules/image/dashboard.vue: named exports in *.vue files are ignored.")} - __vue_template__ = __webpack_require__(115) - module.exports = __vue_script__ || {} - if (module.exports.__esModule) module.exports = module.exports.default - if (__vue_template__) { - (typeof module.exports === "function" ? (module.exports.options || (module.exports.options = {})) : module.exports).template = __vue_template__ - } - if (false) {(function () { module.hot.accept() - var hotAPI = require("vue-hot-reload-api") - hotAPI.install(require("vue"), true) - if (!hotAPI.compatible) return - var id = "/Users/peak/workspace/vue-html5-editor/src/modules/image/dashboard.vue" - if (!module.hot.data) { - hotAPI.createRecord(id, module.exports) - } else { - hotAPI.update(id, module.exports, __vue_template__) - } - })()} - -/***/ }, -/* 75 */ -/***/ function(module, exports, __webpack_require__) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - - var _lrzAll = __webpack_require__(76); - - var _lrzAll2 = _interopRequireDefault(_lrzAll); - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - - exports.default = { - data: function data() { - return { - url: "", - upload: { - status: "ready", //progress,success,error,abort - progressComputable: false, - complete: 0 - } - - }; - }, - - methods: { - reset: function reset() { - this.upload.status = "ready"; - }, - pick: function pick() { - this.$els.file.click(); - }, - insertImage: function insertImage(e) { - e.preventDefault(); - if (!this.url) { - return; - } - this.$parent.execCommand("insertImage", this.url); - this.url = null; - }, - selectFile: function selectFile(e) { - var component = this; - var config = component.$options.module.config; - - var file = this.$els.file.files[0]; - if (file.size > config.size_limit) { - var prompt = component.$parent.locale["exceed size limit"]; - alert(prompt); - return; - } - component.$els.file.value = null; - //需要压缩 - if (config.compress) { - (0, _lrzAll2.default)(file, { - width: config.width, - height: config.height, - quality: config.quality, - fieldName: config.fieldName - }).then(function (rst) { - config.server ? component.uploadFile(rst.file) : component.insertBase64(rst.base64); - }).catch(function (err) { - component.upload.status = "error"; - console.log("upload error", err); - }); - return; - } - //不需要压缩 - //base64 - if (!config.server) { - var reader = new FileReader(); - reader.onload = function (e) { - component.insertBase64(e.target.result); - }; - reader.readAsDataURL(file); - return; - } - //上传服务器 - component.uploadFile(file); - }, - - insertBase64: function insertBase64(data) { - this.$parent.execCommand("insertimage", data); - }, - uploadFile: function uploadFile(file) { - var component = this; - var config = component.$options.module.config; - var formData = new FormData(); - formData.append(config.fieldName, file); - var xhr = new XMLHttpRequest(); - xhr.onprogress = function (e) { - component.upload.status = "progress"; - if (e.lengthComputable) { - component.upload.progressComputable = true; - var percentComplete = e.loaded / e.total; - component.upload.complete = (percentComplete * 100).toFixed(2); - } else { - component.upload.progressComputable = false; - } - }; - xhr.onload = function (e) { - if (xhr.status != 200) { - component.upload.status = "error"; - console.log("upload error", e); - return; - } - component.upload.status = "success"; - try { - var url = config.uploadHandler(xhr.responseText); - if (url) { - component.$parent.execCommand("insertImage", url); - } - } catch (e) { - console.error(e); - } finally { - component.upload.status = "ready"; - } - }; - xhr.onerror = function (e) { - component.upload.status = "error"; - console.log("upload error", e); - }; - xhr.onabort = function (e) { - component.upload.status = "abort"; - console.log("upload abort", e); - }; - xhr.open("POST", config.server); - xhr.send(formData); - } - } - }; - - // - // - // - // - -/***/ }, -/* 123 */ -/***/ function(module, exports) { - - module.exports = "\n

Vue-html5-editor {{version}}

\n

\n GitHub:\n \n https://github.com/PeakTai/vue-html5-editor\n \n

\n"; - -/***/ }, -/* 124 */ -/***/ function(module, exports) { - - "use strict"; - - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.default = { - "align": "对齐方式", - "image": "图片", - "list": "列表", - "link": "链接", - "unlink": "去除链接", - "table": "表格", - "font": "文字", - "full screen": "全屏", - "text": "排版", - "eraser": "格式清除", - "info": "关于", - "color": "颜色", - "please enter a url": "请输入地址", - "create link": "创建链接", - "bold": "加粗", - "italic": "倾斜", - "underline": "下划线", - "strike through": "删除线", - "subscript": "上标", - "superscript": "下标", - "heading": "标题", - "font name": "字体", - "font size": "文字大小", - "left justify": "左对齐", - "center justify": "居中", - "right justify": "右对齐", - "ordered list": "有序列表", - "unordered list": "无序列表", - "fore color": "前景色", - "background color": "背景色", - "row count": "行数", - "column count": "列数", - "save": "确定", - "upload": "上传", - "progress": "进度", - "unknown": "未知", - "please wait": "请稍等", - "error": "错误", - "abort": "中断", - "reset": "重置", - "hr": "分隔线", - "undo": "撤消", - "line height": "行高", - "exceed size limit": "超出大小限制" - }; - -/***/ }, -/* 125 */ -/***/ function(module, exports) { +/** + * text align + * Created by peak on 16/8/18. + */ +var align = { + name: 'align', + icon: 'fa fa-align-center', + i18n: 'align', + show: true, + dashboard: dashboard +}; - "use strict"; +var template$1 = "
"; - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.default = { - "align": "align", - "image": "image", - "list": "list", - "link": "link", - "unlink": "unlink", - "table": "table", - "font": "font", - "full screen": "full screen", - "text": "text", - "eraser": "remove format", - "info": "info", - "color": "color", - "please enter a url": "please enter a url", - "create link": "create link", - "bold": "bold", - "italic": "italic", - "underline": "underline", - "strike through": "strike through", - "subscript": "subscript", - "superscript": "superscript", - "heading": "heading", - "font name": "font name", - "font size": "font size", - "left justify": "left justify", - "center justify": "center justify", - "right justify": "right justify", - "ordered list": "ordered list", - "unordered list": "unordered list", - "fore color": "fore color", - "background color": "background color", - "row count": "row count", - "column count": "column count", - "save": "save", - "upload": "upload", - "progress": "progress", - "unknown": "unknown", - "please wait": "please wait", - "error": "error", - "abort": "abort", - "reset": "reset", - "hr": "horizontal rule", - "undo": "undo", - "line height": "line height", - "exceed size limit": "exceed size limit" - }; +__$styleInject(".vue-html5-editor .color-card{margin:2px;width:30px;height:30px;float:left;cursor:pointer}",undefined); -/***/ }, -/* 126 */ -/***/ function(module, exports) { +/** + * Created by peak on 2017/2/10. + */ +var dashboard$1 = { + template: template$1, + data: function data(){ + return { + // foreColor,backColor + command: 'foreColor', + colors: [ + '#000000', '#000033', '#000066', '#000099', '#003300', '#003333', '#003366', + '#003399', '#006600', '#006633', '#009900', '#330000', '#330033', '#330066', + '#333300', '#333366', '#660000', '#660033', '#663300', '#666600', '#666633', + '#666666', '#666699', '#990000', '#990033', '#9900CC', '#996600', '#FFCC00', + '#FFCCCC', '#FFCC99', '#FFFF00', '#FF9900', '#CCFFCC', '#CCFFFF', '#CCFF99' + ] + } + }, + methods: { + changeColor: function changeColor(color){ + this.$parent.execCommand(this.command, color); + } + } +}; + +/** + * fore color and back color + * Created by peak on 16/8/18. + */ +var color = { + name: 'color', + icon: 'fa fa-paint-brush', + i18n: 'color', + show: true, + dashboard: dashboard$1 +}; + +/** + * remove format of selection + * Created by peak on 16/8/18. + */ +var eraser = { + name: 'eraser', + icon: 'fa fa-eraser', + i18n: 'eraser', + show: true, + handler: function handler(editor) { + editor.execCommand('removeFormat'); + } +}; + +var template$2 = "
"; + +/** + * Created by peak on 2017/2/14. + */ +var Command = { + JUSTIFY_LEFT: 'justifyLeft', + JUSTIFY_CENTER: 'justifyCenter', + JUSTIFY_RIGHT: 'justifyRight', + FORE_COLOR: 'foreColor', + BACK_COLOR: 'backColor', + REMOVE_FORMAT: 'removeFormat', + FONT_NAME: 'fontName', + FONT_SIZE: 'fontSize', + FORMAT_BLOCK: 'formatBlock', + LINE_HEIGHT: 'lineHeight', + INSERT_HORIZONTAL_RULE: 'insertHorizontalRule', + INSERT_IMAGE: 'insertImage', + CREATE_LINK: 'createLink', + INSERT_ORDERED_LIST: 'insertOrderedList', + INSERT_UNORDERED_LIST: 'insertUnorderedList', + INSERT_HTML: 'insertHTML', + BOLD: 'bold', + ITALIC: 'italic', + UNDERLINE: 'underline', + STRIKE_THROUGH: 'strikeThrough', + SUBSCRIPT: 'subscript', + SUPERSCRIPT: 'superscript', + UNDO: 'undo', + UNLINK: 'unlink' +}; + +/** + * Created by peak on 2017/2/10. + */ +var dashboard$2 = { + template: template$2, + data: function data(){ + return { + nameList: [ + 'Microsoft YaHei', + 'Helvetica Neue', + 'Helvetica', + 'Arial', + 'sans-serif', + 'Verdana', + 'Georgia', + 'Times New Roman', + 'Trebuchet MS', + 'Microsoft JhengHei', + 'Courier New', + 'Impact', + 'Comic Sans MS', + 'Consolas' + ], + lineHeightList: [ + '1.0', '1.2', '1.5', '1.8', '2.0', '2.5', '3.0' + ], + fontSizeList: [ + '12px', '14px', '16px', '18px', '20px', '22px', '24px' + ] + } + }, + methods: { + setFontName: function setFontName(name){ + this.$parent.execCommand('fontName', name); + }, + setFontSize: function setFontSize(size){ + this.$parent.execCommand('fontSize', size); + }, + setHeading: function setHeading(heading){ + this.$parent.execCommand('formatBlock', ("h" + heading)); + }, + _contains: function _contains(arr, el){ + for (var i = 0; i < arr.length; i++) { + if (arr[i] === el) { + return true + } + } + return false + }, + setLineHeight: function setLineHeight(lh){ + this.$parent.execCommand(Command.LINE_HEIGHT, lh); + } + } +}; + +/** + * font name and font size + * Created by peak on 16/8/18. + */ +var font = { + name: 'font', + icon: 'fa fa-font', + i18n: 'font', + show: true, + dashboard: dashboard$2 +}; + +/** + * toggle full screen mode + * Created by peak on 16/8/18. + */ +var fullScreen$1 = { + name: 'full-screen', + icon: 'fa fa-arrows-alt', + i18n: 'full screen', + show: true, + handler: function handler(editor) { + editor.toggleFullScreen(); + } +}; + +/** + * hr + * Created by peak on 16/8/20. + */ +var hr = { + name: 'hr', + icon: 'fa fa-minus', + show: true, + i18n: 'hr', + handler: function handler(editor) { + editor.execCommand('insertHorizontalRule'); + } + // init (editor) { + // + // }, + // destroyed(editor){ + // + // }, +}; + +var template$3 = "
{{$parent.locale.progress}}:{{progressComputable ? $parent.locale.unknown : upload.complete}}
{{$parent.locale[\"please wait\"]}}...
{{$parent.locale.upload}} {{$parent.locale.error}},
{{$parent.locale.upload}} {{$parent.locale.abort}},
"; + +// import lrz from 'lrz' +/** + * Created by peak on 2017/2/10. + */ +var dashboard$3 = { + template: template$3 + // data() { + // return { + // url: '', + // upload: { + // status: 'ready', // progress,success,error,abort + // errmsg: null, + // progressComputable: false, + // complete: 0 + // } + // + // } + // }, + // methods: { + // reset(){ + // this.upload.status = 'ready' + // }, + // pick() { + // this.$els.file.click() + // }, + // insertImage(e) { + // e.preventDefault() + // if (!this.url) { + // return + // } + // this.$parent.execCommand('insertImage', this.url) + // this.url = null + // }, + // selectFile() { + // const component = this + // const config = component.$options.module.config + // + // const file = this.$els.file.files[0] + // if (file.size > config.size_limit) { + // const prompt = component.$parent.locale['exceed size limit'] + // component.upload.status = 'error' + // component.errmsg = prompt + // return + // } + // component.$els.file.value = null + // // 需要压缩 + // if (config.compress) { + // lrz(file, { + // width: config.width, + // height: config.height, + // quality: config.quality, + // fieldName: config.fieldName + // }).then((rst) => { + // if (config.server) { + // component.uploadFile(rst.file) + // } else { + // component.insertBase64(rst.base64) + // } + // }).catch((err) => { + // component.upload.status = 'error' + // console.log('upload error', err) + // }) + // return + // } + // // 不需要压缩 + // // base64 + // if (!config.server) { + // const reader = new FileReader() + // reader.onload = function (e) { + // component.insertBase64(e.target.result) + // } + // reader.readAsDataURL(file) + // return + // } + // // 上传服务器 + // component.uploadFile(file) + // }, + // insertBase64(data) { + // this.$parent.execCommand('insertimage', data) + // }, + // uploadFile(file) { + // const component = this + // const config = component.$options.module.config + // const formData = new FormData() + // formData.append(config.fieldName, file) + // const xhr = new XMLHttpRequest() + // xhr.onprogress = function (e) { + // component.upload.status = 'progress' + // if (e.lengthComputable) { + // component.upload.progressComputable = true + // const percentComplete = e.loaded / e.total + // component.upload.complete = (percentComplete * 100).toFixed(2) + // } else { + // component.upload.progressComputable = false + // } + // } + // xhr.onload = function (e) { + // if (xhr.status != 200) { + // component.upload.status = 'error' + // console.log('upload error', e) + // return + // } + // component.upload.status = 'success' + // try { + // const url = config.uploadHandler(xhr.responseText) + // if (url) { + // component.$parent.execCommand('insertImage', url) + // } + // } catch (e) { + // console.error(e) + // } finally { + // component.upload.status = 'ready' + // } + // } + // xhr.onerror = function (e) { + // component.upload.status = 'error' + // console.log('upload error', e) + // } + // xhr.onabort = function (e) { + // component.upload.status = 'abort' + // console.log('upload abort', e) + // } + // xhr.open('POST', config.server) + // xhr.send(formData) + // } + // } +}; + +/** + * insert image + * Created by peak on 16/8/18. + */ +var image = { + name: 'image', + icon: 'fa fa-file-image-o', + i18n: 'image', + show: true, + config: { + server: null, + fieldName: 'image', + sizeLimit: 512 * 1024,// 512k + compress: true, + width: 1600, + height: 1600, + quality: 80, + uploadHandler: function uploadHandler(responseText){ + var json = JSON.parse(responseText); + return json.ok ? json.data : null + } + }, + dashboard: dashboard$3 +}; + +var template$4 = "

Vue-html5-editor {{version}}

repository: https://github.com/PeakTai/vue-html5-editor

"; + +/** + * Created by peak on 2017/2/10. + */ +var dashboard$4 = { + template: template$4, + data: function data(){ + return { + version: "1.0.0" + } + } +}; + +/** + * editor info + * Created by peak on 16/8/18. + */ +var info = { + name: 'info', + icon: 'fa fa-info', + show: true, + i18n: 'info', + // handler () { + // + // }, + // init (editor) { + // + // }, + // destroyed(editor){ + // + // }, + dashboard: dashboard$4 +}; + +var template$5 = "
"; + +var dashboard$5 = { + template: template$5, + data: function data(){ + return {url: null} + }, + methods: { + createLink: function createLink(){ + if (!this.url) { + return + } + this.$parent.execCommand('createLink', this.url); + this.url = null; + } + } +}; + +/** + * create link + * Created by peak on 16/8/18. + */ +var link = { + name: 'link', + icon: 'fa fa-chain', + show: true, + i18n: 'link', + dashboard: dashboard$5 +}; + +var template$6 = "
"; + +/** + * Created by peak on 2017/2/10. + */ +var dashboard$6 = { + template: template$6 +}; - 'use strict'; +/** + * list,ul,ol + * Created by peak on 16/8/18. + */ +var list = { + name: 'list', + icon: 'fa fa-list', + show: true, + i18n: 'list', + dashboard: dashboard$6 +}; + +var template$7 = "
"; + +/** + * Created by peak on 2017/2/10. + */ +var dashboard$7 = { + template: template$7, + data: function data(){ + return { + rows: 2, + cols: 2, + hasHead: false, + striped: false, + hover: false + } + }, + methods: { + insertTable: function insertTable(){ + if (this.rows < 2 || this.rows > 10) { + return + } + if (this.cols < 2 || this.cols > 10) { + return + } + var table = ''; + for (var i = 0; i < this.rows; i++) { + table += ''; + for (var j = 0; j < this.cols; j++) { + table += ''; + } + table += ''; + } + table += '
 
'; + this.$parent.execCommand('insertHTML', table); + } + } +}; + +/** + * insert table + * Created by peak on 16/8/18. + */ +var table = { + // can not named table + // dashboard.html will add to editor as a child component and named as module name + // Do not use built-in or reserved HTML elements as component id + name: 'tabulation', + icon: 'fa fa-table', + i18n: 'table', + show: true, + dashboard: dashboard$7 +}; + +var template$8 = "
"; + +var dashboard$8 = { + template: template$8 +}; + +/** + * text,set the text bold or italic or underline or with strike through or subscript or superscript + * Created by peak on 16/8/18. + */ +var text = { + name: 'text', + icon: 'fa fa-pencil', + i18n: 'text', + show: true, + dashboard: dashboard$8 +}; + +/** + * undo + * Created by peak on 16/8/20. + */ +var undo = { + name: 'undo', + icon: 'fa-undo fa', + show: true, + i18n: 'undo', + handler: function handler(editor) { + editor.execCommand('undo'); + } +}; + +/** + * unlink + * Created by peak on 16/8/18. + */ +var unlink = { + name: 'unlink', + icon: 'fa fa-chain-broken', + show: true, + i18n: 'unlink', + handler: function handler(editor) { + editor.execCommand('unlink'); + } +}; + +/** + * build-in moduls + * Created by peak on 2016/11/1. + */ +var buildInModules = [ + text, + color, + font, + align, + list, + link, + unlink, + table, + image, + hr, + eraser, + undo, + fullScreen$1, + info +]; + +/** + * Created by peak on 2017/2/15. + */ +/** + * add every elements of extArr to sourceArr. + * @param sourceArr + * @param extArr + */ +var mergeArray = function (sourceArr, extArr) { + // note: Array.prototype.push.apply(arr1,arr2) is unreliable + extArr.forEach(function (el) { + sourceArr.push(el); + }); +}; + +/** + * find all the descendant text nodes of a element + * @param ancestor + */ +var getDescendantTextNodes = function (ancestor) { + if (ancestor.nodeType === Node.TEXT_NODE) { + return [ancestor] + } + var textNodes = []; + if (!ancestor.hasChildNodes()) { + return textNodes + } + var childNodes = ancestor.childNodes; + for (var i = 0; i < childNodes.length; i++) { + var node = childNodes[i]; + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node); + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getDescendantTextNodes(node)); + } + } + return textNodes +}; +/** + * find all the descendant text nodes of an ancestor element that before the specify end element, + * the ancestor element must contains the end element. + * @param ancestor + * @param endEl + */ +var getBeforeEndDescendantTextNodes = function (ancestor, endEl) { + var textNodes = []; + var endIndex = 0; + for (var i = 0; i < ancestor.childNodes.length; i++) { + if (ancestor.childNodes[i].contains(endEl)) { + endIndex = i; + break + } + } + + for (var i$1 = 0; i$1 <= endIndex; i$1++) { + var node = ancestor.childNodes[i$1]; + if (node === endEl) { + mergeArray(textNodes, getDescendantTextNodes(node)); + } else if (i$1 === endIndex) { + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node); + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getBeforeEndDescendantTextNodes(node, endEl)); + } + } else if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node); + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getDescendantTextNodes(node)); + } + } + return textNodes +}; +/** + * find all the descendant text nodes of an ancestor element that after the specify start element, + * the ancestor element must contains the start element. + * @param ancestor + * @param startEl + */ +var getAfterStartDescendantTextNodes = function (ancestor, startEl) { + var textNodes = []; + var startIndex = 0; + for (var i = 0; i < ancestor.childNodes.length; i++) { + if (ancestor.childNodes[i].contains(startEl)) { + startIndex = i; + break + } + } + + for (var i$1 = startIndex; i$1 < ancestor.childNodes.length; i$1++) { + var node = ancestor.childNodes[i$1]; + if (node === startEl) { + mergeArray(textNodes, getDescendantTextNodes(node)); + } else if (i$1 === startIndex) { + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node); + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, + getAfterStartDescendantTextNodes(node, startEl)); + } + } else if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node); + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, + getDescendantTextNodes(node)); + } + } + return textNodes +}; + + +/** + * get the closest parent block node of a text node. + * @param node + * @return {Node} + */ +var getParentBlockNode = function (node) { + var blockNodeNames = ['DIV', 'P', 'SECTION', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', + 'OL', 'UL', 'LI', 'TR', 'TD', 'TH', 'TBODY', 'THEAD', 'TABLE', 'ARTICLE', 'HEADER', 'FOOTER']; + var container = node.parentNode; + while (container) { + if (blockNodeNames.includes(container.nodeName)) { + break + } + } + return container +}; + +var isInlineElement = function (node) { + var inlineNodeNames = ['A', 'ABBR', 'ACRONYM', 'B', 'CITE', 'CODE', 'EM', 'I', + 'FONT', 'IMG', 'S', 'SMALL', 'SPAN', 'STRIKE', 'STRONG', 'U', 'SUB', 'SUP']; + return inlineNodeNames.includes(node.nodeName) +}; + +/** + * Created by peak on 2017/2/14. + */ +var RangeHandler = function RangeHandler(range) { + if (!range || !(range instanceof Range)) { + throw new TypeError('cant\'t resolve range') + } + this.range = range; +}; - Object.defineProperty(exports, "__esModule", { - value: true - }); - exports.default = function () { - if (!Array.prototype.includes) { - Array.prototype.includes = function (searchElement /*, fromIndex*/) { - 'use strict'; +/** + * find all the text nodes in range + */ +RangeHandler.prototype.getAllTextNodesInRange = function getAllTextNodesInRange () { + var startContainer = this.range.startContainer; + var endContainer = this.range.endContainer; + var rootEl = this.range.commonAncestorContainer; + var textNodes = []; + + if (startContainer === endContainer) { + if (startContainer.nodeType === Node.TEXT_NODE) { + return [startContainer] + } + var childNodes = startContainer.childNodes; + for (var i = this.range.startOffset; i < this.range.endOffset; i++) { + mergeArray(textNodes, getDescendantTextNodes(childNodes[i])); + } + return textNodes + } + + var startIndex = 0; + var endIndex = 0; + for (var i$1 = 0; i$1 < rootEl.childNodes.length; i$1++) { + var node = rootEl.childNodes[i$1]; + if (node.contains(startContainer)) { + startIndex = i$1; + } + if (node.contains(endContainer)) { + endIndex = i$1; + } + } + + for (var i$2 = startIndex; i$2 <= endIndex; i$2++) { + var node$1 = rootEl.childNodes[i$2]; + if (i$2 === startIndex) { + if (node$1.nodeType === Node.TEXT_NODE) { + textNodes.push(node$1); + } else if (node$1.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getAfterStartDescendantTextNodes(node$1, startContainer)); + } + } else if (i$2 === endIndex) { + if (node$1.nodeType === Node.TEXT_NODE) { + textNodes.push(node$1); + } else if (node$1.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getBeforeEndDescendantTextNodes(node$1, endContainer)); + } + } else if (node$1.nodeType === Node.TEXT_NODE) { + textNodes.push(node$1); + } else if (node$1.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getDescendantTextNodes(node$1)); + } + } + return textNodes +}; + +/** + * execute edit command + * @param {String} command + * @param arg + */ +RangeHandler.prototype.execCommand = function execCommand (command, arg) { + var this$1 = this; + + switch (command) { + + case Command.JUSTIFY_LEFT: { + document.execCommand(Command.JUSTIFY_LEFT, false, arg); + break + } + + case Command.JUSTIFY_RIGHT: { + document.execCommand(Command.JUSTIFY_RIGHT, false, arg); + break + } + + case Command.JUSTIFY_CENTER: { + document.execCommand(Command.JUSTIFY_CENTER, false, arg); + break + } + + case Command.FORE_COLOR: { + document.execCommand(Command.FORE_COLOR, false, arg); + break + } + case Command.BACK_COLOR: { + document.execCommand(Command.BACK_COLOR, false, arg); + break + } + case Command.REMOVE_FORMAT: { + document.execCommand(Command.REMOVE_FORMAT, false, arg); + break + } + case Command.FONT_NAME: { + document.execCommand(Command.FONT_NAME, false, arg); + break + } + case Command.FONT_SIZE: { + // 重新实现,改为直接修改样式 + var textNodes = this.getAllTextNodesInRange(); + if (!textNodes.length) { + break + } + if (textNodes.length === 1 && textNodes[0] === this.range.startContainer + && textNodes[0] === this.range.endContainer) { + var textNode = textNodes[0]; + if (this.range.startOffset === 0 + && this.range.endOffset === textNode.textContent.length) { + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg; + break + } + var span = document.createElement('span'); + span.style.fontSize = arg; + textNode.parentNode.insertBefore(span, textNode); + span.appendChild(textNode); + break + } + var span$1 = document.createElement('span'); + span$1.innerText = textNode.textContent.substring( + this.range.startOffset, this.range.endOffset); + span$1.style.fontSize = arg; + var frontPart = document.createTextNode( + textNode.textContent.substring(0, this.range.startOffset + 1)); + textNode.parentNode.insertBefore(frontPart, textNode); + textNode.parentNode.insertBefore(span$1, textNode); + textNode.textContent = textNode.textContent.substring(this.range.endOffset); + this.range.setStart(span$1, 0); + this.range.setEnd(span$1, 1); + break + } + + textNodes.forEach(function (textNode) { + if (textNode === this$1.range.startContainer) { + if (this$1.range.startOffset === 0) { + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg; + } else { + var span$1 = document.createElement('span'); + span$1.style.fontSize = arg; + textNode.parentNode.insertBefore(span$1, textNode); + span$1.appendChild(textNode); + } + return + } + var span$2 = document.createElement('span'); + textNode.textContent = textNode.textContent.substring( + 0, this$1.range.startOffset); + span$2.style.fontSize = arg; + textNode.parentNode.insertBefore(span$2, textNode); + this$1.range.setStart(textNode, 0); + return + } + if (textNode === this$1.range.endContainer) { + if (this$1.range.endOffset === textNode.textContent.length) { + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg; + } else { + var span$3 = document.createElement('span'); + span$3.style.fontSize = arg; + textNode.parentNode.insertBefore(span$3, textNode); + span$3.appendChild(textNode); + } + return + } + var span$4 = document.createElement('span'); + textNode.textContent = textNode.textContent.substring(this$1.range.endOffset); + span$4.style.fontSize = arg; + textNode.parentNode.insertBefore(span$4, textNode); + span$4.appendChild(textNode); + this$1.range.setStart(textNode, textNode.textContent.length); + return + } + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg; + return + } + + var span = document.createElement('span'); + span.style.fontSize = arg; + textNode.parentNode.insertBefore(span, textNode); + span.appendChild(textNode); + }); + break + } + case Command.FORMAT_BLOCK: { + if (document.execCommand(Command.FORMAT_BLOCK, false, arg)) { + break + } + // hack + var element = document.createElement(arg); + this.range.surroundContents(element); + break + } + case Command.LINE_HEIGHT: { + var textNodes$1 = this.getAllTextNodesInRange(); + textNodes$1.forEach(function (textNode) { + var parentBlock = getParentBlockNode(textNode); + if (parentBlock) { + parentBlock.style.lineHeight = arg; + } + }); + break + } + case Command.INSERT_HORIZONTAL_RULE: { + document.execCommand(Command.INSERT_HORIZONTAL_RULE, false); + break + } + case Command.INSERT_IMAGE: { + document.execCommand(Command.INSERT_IMAGE, false, arg); + break + } + case Command.CREATE_LINK: { + document.execCommand(Command.CREATE_LINK, false, arg); + break + } + case Command.INSERT_ORDERED_LIST: { + document.execCommand(Command.INSERT_ORDERED_LIST, false, arg); + break + } + case Command.INSERT_UNORDERED_LIST: { + document.execCommand(Command.INSERT_UNORDERED_LIST, false, arg); + break + } + case Command.INSERT_HTML: { + if (document.execCommand(Command.INSERT_HTML, false, arg)) { + break + } + // hack + var fragment = document.createDocumentFragment(); + var div = document.createElement('div'); + div.innerHTML = arg; + if (div.hasChildNodes()) { + for (var i = 0; i < div.childNodes.length; i++) { + fragment.appendChild(div.childNodes[i].cloneNode(true)); + } + } + this.range.deleteContents(); + this.range.insertNode(fragment); + break + } + case Command.BOLD: { + console.warn('文本结点获取测试'); + console.warn(this.range); + var textNodes$2 = this.getAllTextNodesInRange(); + window.textNodes = textNodes$2; + console.warn(textNodes$2); + // todo + document.execCommand(Command.BOLD, false, arg); + break + } + case Command.ITALIC: { + document.execCommand(Command.ITALIC, false); + break + } + case Command.UNDERLINE: { + document.execCommand(Command.UNDERLINE, false); + break + } + case Command.STRIKE_THROUGH: { + document.execCommand(Command.STRIKE_THROUGH, false); + break + } + case Command.SUBSCRIPT: { + document.execCommand(Command.SUBSCRIPT, false); + break + } + case Command.SUPERSCRIPT: { + document.execCommand(Command.SUPERSCRIPT, false); + break + } + case Command.UNDO: { + document.execCommand(Command.UNDO, false); + break + } + case Command.UNLINK: { + document.execCommand(Command.UNLINK, false); + break + } + default: { + break + } + } +}; + +__$styleInject(".vue-html5-editor{font-size:14px;line-height:1.5;background-color:#fff;color:#333;border:1px solid #ddd;text-align:left;border-radius:5px;overflow:hidden;box-sizing:border-box}.vue-html5-editor.full-screen{position:fixed!important;top:0!important;left:0!important;bottom:0!important;right:0!important;border-radius:0}.vue-html5-editor>.toolbar{position:relative;background-color:inherit}.vue-html5-editor>.toolbar>ul{list-style:none;padding:0;margin:0;border-bottom:1px solid #ddd}.vue-html5-editor>.toolbar>ul>li{display:inline-block;cursor:pointer;width:50px;text-align:center;line-height:36px}.vue-html5-editor>.toolbar>ul>li .icon{height:16px;width:16px;display:inline-block}.vue-html5-editor>.toolbar>.dashboard{background-color:inherit;border-bottom:1px solid #ddd;padding:10px;position:absolute;top:100%;left:0;right:0;overflow:auto}.vue-html5-editor>.toolbar>.dashboard input[type=text],.vue-html5-editor>.toolbar>.dashboard input[type=number],.vue-html5-editor>.toolbar>.dashboard select{padding:6px 12px;color:inherit;background-color:transparent;border:1px solid #ddd;border-radius:5px}.vue-html5-editor>.toolbar>.dashboard input[type=text]:hover,.vue-html5-editor>.toolbar>.dashboard input[type=number]:hover,.vue-html5-editor>.toolbar>.dashboard select:hover{border-color:#bebebe}.vue-html5-editor>.toolbar>.dashboard input[type=text][disabled],.vue-html5-editor>.toolbar>.dashboard input[type=text][readonly],.vue-html5-editor>.toolbar>.dashboard input[type=number][disabled],.vue-html5-editor>.toolbar>.dashboard input[type=number][readonly],.vue-html5-editor>.toolbar>.dashboard select[disabled],.vue-html5-editor>.toolbar>.dashboard select[readonly]{background-color:#eee;opacity:1}.vue-html5-editor>.toolbar>.dashboard input[type=text][disabled],.vue-html5-editor>.toolbar>.dashboard input[type=number][disabled],.vue-html5-editor>.toolbar>.dashboard select[disabled]{cursor:not-allowed}.vue-html5-editor>.toolbar>.dashboard button{color:inherit;background-color:inherit;padding:6px 12px;white-space:nowrap;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid #ddd;border-radius:5px}.vue-html5-editor>.toolbar>.dashboard button:hover{border-color:#bebebe}.vue-html5-editor>.toolbar>.dashboard button[disabled]{cursor:not-allowed;opacity:.68}.vue-html5-editor>.toolbar>.dashboard label{font-weight:bolder}.vue-html5-editor>.content{overflow:auto;padding:10px}.vue-html5-editor>.content:focus{outline:0}",undefined); + +var template$9 = "
"; + +/** + * Created by peak on 2017/2/9. + */ +var editor = { + template: template$9, + props: { + content: { + type: String, + required: true, + default: '' + }, + height: { + type: Number, + default: 300, + validator: function validator(val){ + return val >= 100 + } + }, + zIndex: { + type: Number, + default: 1000 + }, + autoHeight: { + type: Boolean, + default: true + } + }, + data: function data(){ + return { + // locale: {}, + fullScreen: false, + dashboard: null, + dashboardStyle: {} + } + }, + watch: { + content: function content(val) { + var content = this.$refs.content.innerHTML; + if (val !== content) { + this.$res.content.innerHTML = val; + } + }, + fullScreen: function fullScreen(val){ + var component = this; + if (val) { + component.parentEl = component.$el.parentNode; + component.nextEl = component.$el.nextSibling; + document.body.appendChild(component.$el); + return + } + if (component.nextEl) { + component.parentEl.insertBefore(component.$el, component.nextEl); + return + } + component.parentEl.appendChild(component.$el); + } + }, + computed: { + contentStyle: function contentStyle(){ + var style = {}; + if (this.fullScreen) { + style.height = (window.innerHeight - this.$refs.toolbar.clientHeight - 1) + "px"; + return style + } + if (!this.autoHeight) { + style.height = (this.height) + "px"; + return style + } + style['min-height'] = (this.height) + "px"; + return style + } + }, + methods: { + toggleFullScreen: function toggleFullScreen(){ + this.fullScreen = !this.fullScreen; + }, + toggleDashboard: function toggleDashboard(dashboard){ + this.dashboard = this.dashboard === dashboard ? null : dashboard; + }, + execCommand: function execCommand(command, arg){ + this.restoreSelection(); + if (this.range) { + new RangeHandler(this.range).execCommand(command, arg); + } + this.toggleDashboard(); + this.$emit('change', this.$refs.content.innerHTML); + }, + getCurrentRange: function getCurrentRange(){ + return this.range + }, + saveCurrentRange: function saveCurrentRange(){ + var selection = window.getSelection ? window.getSelection() : document.getSelection(); + var range = selection.rangeCount ? selection.getRangeAt(0) : null; + if (!range) { + return + } + if (this.$refs.content.contains(range.startContainer) && + this.$refs.content.contains(range.endContainer)) { + this.range = range; + } + }, + restoreSelection: function restoreSelection(){ + var selection = window.getSelection ? window.getSelection() : document.getSelection(); + selection.removeAllRanges(); + if (this.range) { + selection.addRange(this.range); + } else { + var content = this.$refs.content; + var div = document.createElement('div'); + var range = document.createRange(); + content.appendChild(div); + range.setStart(div, 0); + range.setEnd(div, 0); + selection.addRange(range); + this.range = range; + } + }, + activeModule: function activeModule(module){ + if (typeof module.handler === 'function') { + module.handler(this); + return + } + if (module.hasDashboard) { + this.toggleDashboard(("dashboard-" + (module.name))); + } + } + }, + created: function created(){ + var this$1 = this; + + this.modules.forEach(function (module) { + if (typeof module.init === 'function') { + module.init(this$1); + } + }); + }, + mounted: function mounted(){ + var this$1 = this; + + var content = this.$refs.content; + content.innerHTML = this.content; + content.addEventListener('mouseup', this.saveCurrentRange, false); + content.addEventListener('keyup', this.saveCurrentRange, false); + content.addEventListener('mouseout', this.saveCurrentRange, false); + content.addEventListener('keyup', function () { + this$1.$emit('change', content.innerHTML); + }, false); + + this.touchHandler = function (e) { + if (content.contains(e.target)) { + this$1.saveCurrentRange(); + } + }; + + window.addEventListener('touchend', this.touchHandler, false); + }, + updated: function updated(){ + this.dashboardStyle = {'max-height': ((this.$refs.content.clientHeight) + "px")}; + }, + beforeDestroy: function beforeDestroy(){ + var this$1 = this; + + window.removeEventListener('touchend', this.touchHandler); + this.modules.forEach(function (module) { + if (typeof module.destroyed === 'function') { + module.destroyed(this$1); + } + }); + } +}; + +var i18nZhCn = { + align: '对齐方式', + image: '图片', + list: '列表', + link: '链接', + unlink: '去除链接', + table: '表格', + font: '文字', + 'full screen': '全屏', + text: '排版', + eraser: '格式清除', + info: '关于', + color: '颜色', + 'please enter a url': '请输入地址', + 'create link': '创建链接', + bold: '加粗', + italic: '倾斜', + underline: '下划线', + 'strike through': '删除线', + subscript: '上标', + superscript: '下标', + heading: '标题', + 'font name': '字体', + 'font size': '文字大小', + 'left justify': '左对齐', + 'center justify': '居中', + 'right justify': '右对齐', + 'ordered list': '有序列表', + 'unordered list': '无序列表', + 'fore color': '前景色', + 'background color': '背景色', + 'row count': '行数', + 'column count': '列数', + save: '确定', + upload: '上传', + progress: '进度', + unknown: '未知', + 'please wait': '请稍等', + error: '错误', + abort: '中断', + reset: '重置', + hr: '分隔线', + undo: '撤消', + 'line height': '行高', + 'exceed size limit': '超出大小限制' +}; + +var i18nEnUs = { + align: 'align', + image: 'image', + list: 'list', + link: 'link', + unlink: 'unlink', + table: 'table', + font: 'font', + 'full screen': 'full screen', + text: 'text', + eraser: 'remove format', + info: 'info', + color: 'color', + 'please enter a url': 'please enter a url', + 'create link': 'create link', + bold: 'bold', + italic: 'italic', + underline: 'underline', + 'strike through': 'strike through', + subscript: 'subscript', + superscript: 'superscript', + heading: 'heading', + 'font name': 'font name', + 'font size': 'font size', + 'left justify': 'left justify', + 'center justify': 'center justify', + 'right justify': 'right justify', + 'ordered list': 'ordered list', + 'unordered list': 'unordered list', + 'fore color': 'fore color', + 'background color': 'background color', + 'row count': 'row count', + 'column count': 'column count', + save: 'save', + upload: 'upload', + progress: 'progress', + unknown: 'unknown', + 'please wait': 'please wait', + error: 'error', + abort: 'abort', + reset: 'reset', + hr: 'horizontal rule', + undo: 'undo', + 'line height': 'line height', + 'exceed size limit': 'exceed size limit' +}; + +/** + * shadow clone + * + * @param source source object + * @param ext extended object + */ +var mixin = function (source, ext) { + if ( source === void 0 ) source = {}; + if ( ext === void 0 ) ext = {}; + + Object.keys(ext).forEach(function (k) { + // for data function + if (k === 'data') { + var dataSrc = source[k]; + var dataDesc = ext[k]; + if (typeof dataDesc === 'function') { + if (typeof dataSrc !== 'function') { + source[k] = dataDesc; + } else { + source[k] = function () { return mixin(dataSrc(), dataDesc()); }; + } + } + } else { + source[k] = ext[k]; + } + }); + return source +}; + +/** + * Vue html5 Editor + * @param Vue {Vue} + * @param options {Object} + */ +var VueHtml5Editor = function VueHtml5Editor(options) { + if ( options === void 0 ) options = {}; + + var modules = [].concat( buildInModules ); + var components = {}; + + // extended modules + if (Array.isArray(options.modules)) { + options.modules.forEach(function (module) { + if (module.name) { + modules.push(module); + } + }); + } + // hidden modules + if (Array.isArray(options.hiddenModules)) { + modules = (function () { + var arr = []; + modules.forEach(function (m) { + if (!options.hiddenModules.includes(m.name)) { + arr.push(m); + } + }); + return arr + })(); + } + // visible modules + if (Array.isArray(options.visibleModules)) { + modules = (function () { + var arr = []; + modules.forEach(function (module) { + if (options.visibleModules.includes(module.name)) { + arr.push(module); + } + }); + return arr + })(); + } + + + modules.forEach(function (module) { + // specify the config for each module in options by name + var config = options[module.name]; + module.config = mixin(module.config, config); + + if (module.dashboard) { + // $options.module + module.dashboard.module = module; + components[("dashboard-" + (module.name))] = module.dashboard; + } + if (options.icons && options.icons[module.name]) { + module.icon = options.icons[module.name]; + } + + module.hasDashboard = !!module.dashboard; + // prevent vue sync + module.dashboard = null; + }); + + // i18n + var i18n = {'zh-cn': i18nZhCn, 'en-us': i18nEnUs}; + var customI18n = options.i18n || {}; + Object.keys(customI18n).forEach(function (key) { + i18n[key] = i18n[key] ? mixin(i18n[key], customI18n[key]) : customI18n[key]; + }); + var language = options.language || 'en-us'; + var locale = i18n[language]; + + // ###################################### + var compo = mixin(editor, { + data: function data() { + return {modules: modules, locale: locale} + }, + components: components + }); + mixin(this, compo); +}; + +/** + * global install + * + * @param Vue + * @param options + */ +VueHtml5Editor.install = function install (Vue, options) { + if ( options === void 0 ) options = {}; - if (this == null) { - throw new TypeError('Array.prototype.includes called on null or undefined'); - } + Vue.component(options.name || 'vue-html5-editor', new VueHtml5Editor(options)); +}; - var O = Object(this); - var len = parseInt(O.length, 10) || 0; - if (len === 0) { - return false; - } - var n = parseInt(arguments[1], 10) || 0; - var k; - if (n >= 0) { - k = n; - } else { - k = len + n; - if (k < 0) { - k = 0; - } - } - var currentElement; - while (k < len) { - currentElement = O[k]; - if (searchElement === currentElement || searchElement !== searchElement && currentElement !== currentElement) { - return true; - } - k++; - } - return false; - }; - } - }; +return VueHtml5Editor; -/***/ } -/******/ ]) -}); -; \ No newline at end of file +}))); diff --git a/example/basic.html b/example/basic.html index fea1f3d..7bdd92e 100644 --- a/example/basic.html +++ b/example/basic.html @@ -5,7 +5,7 @@ vue html5 editor demo - + + + +
+ +
+ + + + \ No newline at end of file diff --git a/example/custom-icon.html b/example/custom-icon.html index 87525fc..1600c24 100644 --- a/example/custom-icon.html +++ b/example/custom-icon.html @@ -5,7 +5,7 @@ vue html5 editor demo - + - - diff --git a/src/i18n/en-us.js b/src/i18n/en-us.js index 7c109f2..88569db 100644 --- a/src/i18n/en-us.js +++ b/src/i18n/en-us.js @@ -1,46 +1,46 @@ -export default { - "align": "align", - "image": "image", - "list": "list", - "link": "link", - "unlink": "unlink", - "table": "table", - "font": "font", - "full screen": "full screen", - "text": "text", - "eraser": "remove format", - "info": "info", - "color": "color", - "please enter a url": "please enter a url", - "create link": "create link", - "bold": "bold", - "italic": "italic", - "underline": "underline", - "strike through": "strike through", - "subscript": "subscript", - "superscript": "superscript", - "heading": "heading", - "font name": "font name", - "font size": "font size", - "left justify": "left justify", - "center justify": "center justify", - "right justify": "right justify", - "ordered list": "ordered list", - "unordered list": "unordered list", - "fore color": "fore color", - "background color": "background color", - "row count": "row count", - "column count": "column count", - "save": "save", - "upload": "upload", - "progress": "progress", - "unknown": "unknown", - "please wait": "please wait", - "error": "error", - "abort": "abort", - "reset": "reset", - "hr": "horizontal rule", - "undo": "undo", - "line height": "line height", - "exceed size limit": "exceed size limit" +export default { + align: 'align', + image: 'image', + list: 'list', + link: 'link', + unlink: 'unlink', + table: 'table', + font: 'font', + 'full screen': 'full screen', + text: 'text', + eraser: 'remove format', + info: 'info', + color: 'color', + 'please enter a url': 'please enter a url', + 'create link': 'create link', + bold: 'bold', + italic: 'italic', + underline: 'underline', + 'strike through': 'strike through', + subscript: 'subscript', + superscript: 'superscript', + heading: 'heading', + 'font name': 'font name', + 'font size': 'font size', + 'left justify': 'left justify', + 'center justify': 'center justify', + 'right justify': 'right justify', + 'ordered list': 'ordered list', + 'unordered list': 'unordered list', + 'fore color': 'fore color', + 'background color': 'background color', + 'row count': 'row count', + 'column count': 'column count', + save: 'save', + upload: 'upload', + progress: 'progress', + unknown: 'unknown', + 'please wait': 'please wait', + error: 'error', + abort: 'abort', + reset: 'reset', + hr: 'horizontal rule', + undo: 'undo', + 'line height': 'line height', + 'exceed size limit': 'exceed size limit' } \ No newline at end of file diff --git a/src/i18n/zh-cn.js b/src/i18n/zh-cn.js index b76f1c7..8aad55f 100644 --- a/src/i18n/zh-cn.js +++ b/src/i18n/zh-cn.js @@ -1,46 +1,46 @@ -export default { - "align": "对齐方式", - "image": "图片", - "list": "列表", - "link": "链接", - "unlink": "去除链接", - "table": "表格", - "font": "文字", - "full screen": "全屏", - "text": "排版", - "eraser": "格式清除", - "info": "关于", - "color": "颜色", - "please enter a url": "请输入地址", - "create link": "创建链接", - "bold": "加粗", - "italic": "倾斜", - "underline": "下划线", - "strike through": "删除线", - "subscript": "上标", - "superscript": "下标", - "heading": "标题", - "font name": "字体", - "font size": "文字大小", - "left justify": "左对齐", - "center justify": "居中", - "right justify": "右对齐", - "ordered list": "有序列表", - "unordered list": "无序列表", - "fore color": "前景色", - "background color": "背景色", - "row count": "行数", - "column count": "列数", - "save": "确定", - "upload": "上传", - "progress": "进度", - "unknown": "未知", - "please wait": "请稍等", - "error": "错误", - "abort": "中断", - "reset": "重置", - "hr": "分隔线", - "undo": "撤消", - "line height": "行高", - "exceed size limit": "超出大小限制" +export default { + align: '对齐方式', + image: '图片', + list: '列表', + link: '链接', + unlink: '去除链接', + table: '表格', + font: '文字', + 'full screen': '全屏', + text: '排版', + eraser: '格式清除', + info: '关于', + color: '颜色', + 'please enter a url': '请输入地址', + 'create link': '创建链接', + bold: '加粗', + italic: '倾斜', + underline: '下划线', + 'strike through': '删除线', + subscript: '上标', + superscript: '下标', + heading: '标题', + 'font name': '字体', + 'font size': '文字大小', + 'left justify': '左对齐', + 'center justify': '居中', + 'right justify': '右对齐', + 'ordered list': '有序列表', + 'unordered list': '无序列表', + 'fore color': '前景色', + 'background color': '背景色', + 'row count': '行数', + 'column count': '列数', + save: '确定', + upload: '上传', + progress: '进度', + unknown: '未知', + 'please wait': '请稍等', + error: '错误', + abort: '中断', + reset: '重置', + hr: '分隔线', + undo: '撤消', + 'line height': '行高', + 'exceed size limit': '超出大小限制' } \ No newline at end of file diff --git a/src/index.js b/src/index.js index e59c31a..b826cfa 100644 --- a/src/index.js +++ b/src/index.js @@ -1,125 +1,129 @@ -import editor from "./editor.vue"; -import moduleText from "./modules/text/index"; -import moduleFont from "./modules/font/index"; -import moduleColor from "./modules/color/index"; -import moduleAlign from "./modules/align/index"; -import moduleList from "./modules/list/index"; -import moduleLink from "./modules/link/index"; -import moduleUnlink from "./modules/unlink/index"; -import moduleTable from "./modules/table/index"; -import moduleImage from "./modules/image/index"; -import moduleHr from "./modules/hr"; -import moduleEraser from "./modules/eraser/index"; -import moduleUndo from "./modules/undo/index"; -import moduleFullScreen from "./modules/full-screen/index"; -import moduleInfo from "./modules/info/index"; -import i18nZhCn from "./i18n/zh-cn"; -import i18nEnUs from "./i18n/en-us"; -import arrayFill from "./array-polyfill"; +import buildInModules from './modules/index' +import editor from './editor' +import i18nZhCn from './i18n/zh-cn' +import i18nEnUs from './i18n/en-us' /** - * install + * shadow clone + * + * @param source source object + * @param ext extended object + */ +const mixin = (source = {}, ext = {}) => { + Object.keys(ext).forEach((k) => { + // for data function + if (k === 'data') { + const dataSrc = source[k] + const dataDesc = ext[k] + if (typeof dataDesc === 'function') { + if (typeof dataSrc !== 'function') { + source[k] = dataDesc + } else { + source[k] = () => mixin(dataSrc(), dataDesc()) + } + } + } else { + source[k] = ext[k] + } + }) + return source +} + +/** + * Vue html5 Editor * @param Vue {Vue} * @param options {Object} */ -exports.install = (Vue, options) => { - - arrayFill() +class VueHtml5Editor { - options = options || {} + /** + * build an editor component + */ + constructor(options = {}) { + let modules = [...buildInModules] + const components = {} - //modules - let modules = [ - moduleText, - moduleColor, - moduleFont, - moduleAlign, - moduleList, - moduleLink, - moduleUnlink, - moduleTable, - moduleImage, - moduleHr, - moduleEraser, - moduleUndo, - moduleFullScreen, - moduleInfo - ] - //extended modules - if (Array.isArray(options.modules)) { - let arr = [] - options.modules.forEach(function (module) { - if (module.name) { - arr.push(module) - } - }) - modules = modules.concat(arr) - } - //hidden modules - if (Array.isArray(options.hiddenModules)) { - modules = (()=> { - let arr = [] - modules.forEach(function (m) { - if (!options.hiddenModules.includes(m.name)) { - arr.push(m) + // extended modules + if (Array.isArray(options.modules)) { + options.modules.forEach((module) => { + if (module.name) { + modules.push(module) } }) - return arr - })() - } - //visible modules - if (Array.isArray(options.visibleModules)) { - modules = (()=> { - let arr = [] - options.visibleModules.forEach(function (name) { - modules.forEach(function (module) { - if (module.name == name) { + } + // hidden modules + if (Array.isArray(options.hiddenModules)) { + modules = (() => { + const arr = [] + modules.forEach((m) => { + if (!options.hiddenModules.includes(m.name)) { + arr.push(m) + } + }) + return arr + })() + } + // visible modules + if (Array.isArray(options.visibleModules)) { + modules = (() => { + const arr = [] + modules.forEach((module) => { + if (options.visibleModules.includes(module.name)) { arr.push(module) } }) - }) - return arr - })() - } - + return arr + })() + } - let components = {} - modules.forEach((module)=> { - //specify the config for each module in options by name - let config = options[module.name] - module.config = Vue.util.extend(module.config || {}, config || {}) + modules.forEach((module) => { + // specify the config for each module in options by name + const config = options[module.name] + module.config = mixin(module.config, config) - if (module.dashboard) { - //$options.module - module.dashboard.module = module - components['dashboard-' + module.name] = module.dashboard - } - if (options.icons && options.icons[module.name]) { - module.icon = options.icons[module.name] - } + if (module.dashboard) { + // $options.module + module.dashboard.module = module + components[`dashboard-${module.name}`] = module.dashboard + } + if (options.icons && options.icons[module.name]) { + module.icon = options.icons[module.name] + } - module.hasDashboard = !!module.dashboard - //prevent vue sync - module.dashboard = null - }) + module.hasDashboard = !!module.dashboard + // prevent vue sync + module.dashboard = null + }) - //i18n - let i18n = {"zh-cn": i18nZhCn, "en-us": i18nEnUs} - let customI18n = options.i18n || {} - Object.keys(customI18n).forEach((key)=> { - i18n[key] = i18n[key] ? Vue.util.extend(i18n[key], customI18n[key]) : customI18n[key] - }) - let language = options.language || "en-us" - let locale = i18n[language] || i18n["en-us"] + // i18n + const i18n = {'zh-cn': i18nZhCn, 'en-us': i18nEnUs} + const customI18n = options.i18n || {} + Object.keys(customI18n).forEach((key) => { + i18n[key] = i18n[key] ? mixin(i18n[key], customI18n[key]) : customI18n[key] + }) + const language = options.language || 'en-us' + const locale = i18n[language] + // ###################################### + const compo = mixin(editor, { + data() { + return {modules, locale} + }, + components + }) + mixin(this, compo) + } - let component = Vue.extend(editor).extend({ - data () { - return {modules, locale} - }, - components - }) + /** + * global install + * + * @param Vue + * @param options + */ + static install(Vue, options = {}) { + Vue.component(options.name || 'vue-html5-editor', new VueHtml5Editor(options)) + } +} - Vue.component(options.name || "vue-html5-editor", component) -} \ No newline at end of file +export default VueHtml5Editor \ No newline at end of file diff --git a/src/modules/align/dashboard.vue b/src/modules/align/dashboard.html similarity index 94% rename from src/modules/align/dashboard.vue rename to src/modules/align/dashboard.html index 19ccab5..7125d8e 100644 --- a/src/modules/align/dashboard.vue +++ b/src/modules/align/dashboard.html @@ -1,4 +1,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/modules/align/dashboard.js b/src/modules/align/dashboard.js new file mode 100644 index 0000000..0674d13 --- /dev/null +++ b/src/modules/align/dashboard.js @@ -0,0 +1,7 @@ +import template from './dashboard.html' +/** + * Created by peak on 2017/2/10. + */ +export default { + template +} \ No newline at end of file diff --git a/src/modules/align/index.js b/src/modules/align/index.js index de7cad3..55999f8 100644 --- a/src/modules/align/index.js +++ b/src/modules/align/index.js @@ -2,11 +2,12 @@ * text align * Created by peak on 16/8/18. */ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' + export default { - name: "align", - icon: "fa fa-align-center", - i18n: "align", + name: 'align', + icon: 'fa fa-align-center', + i18n: 'align', show: true, dashboard } diff --git a/src/modules/color/dashboard.html b/src/modules/color/dashboard.html new file mode 100644 index 0000000..1e3117d --- /dev/null +++ b/src/modules/color/dashboard.html @@ -0,0 +1,18 @@ +
+
+ + +
+
+
+
+
+
+
diff --git a/src/modules/color/dashboard.js b/src/modules/color/dashboard.js new file mode 100644 index 0000000..9e55fc5 --- /dev/null +++ b/src/modules/color/dashboard.js @@ -0,0 +1,26 @@ +import template from './dashboard.html' +import './style.css' +/** + * Created by peak on 2017/2/10. + */ +export default { + template, + data(){ + return { + // foreColor,backColor + command: 'foreColor', + colors: [ + '#000000', '#000033', '#000066', '#000099', '#003300', '#003333', '#003366', + '#003399', '#006600', '#006633', '#009900', '#330000', '#330033', '#330066', + '#333300', '#333366', '#660000', '#660033', '#663300', '#666600', '#666633', + '#666666', '#666699', '#990000', '#990033', '#9900CC', '#996600', '#FFCC00', + '#FFCCCC', '#FFCC99', '#FFFF00', '#FF9900', '#CCFFCC', '#CCFFFF', '#CCFF99' + ] + } + }, + methods: { + changeColor(color){ + this.$parent.execCommand(this.command, color) + } + } +} diff --git a/src/modules/color/dashboard.vue b/src/modules/color/dashboard.vue deleted file mode 100644 index 9bf71e8..0000000 --- a/src/modules/color/dashboard.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - \ No newline at end of file diff --git a/src/modules/color/index.js b/src/modules/color/index.js index bae4802..98fbb56 100644 --- a/src/modules/color/index.js +++ b/src/modules/color/index.js @@ -1,12 +1,12 @@ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' /** * fore color and back color * Created by peak on 16/8/18. */ export default { - name: "color", - icon: "fa fa-paint-brush", - i18n: "color", + name: 'color', + icon: 'fa fa-paint-brush', + i18n: 'color', show: true, dashboard } diff --git a/src/modules/color/style.css b/src/modules/color/style.css new file mode 100644 index 0000000..af3f99b --- /dev/null +++ b/src/modules/color/style.css @@ -0,0 +1,7 @@ +.vue-html5-editor .color-card { + margin: 2px; + width: 30px; + height: 30px; + float: left; + cursor: pointer; +} \ No newline at end of file diff --git a/src/modules/eraser/index.js b/src/modules/eraser/index.js index c926ab2..d6a4b39 100644 --- a/src/modules/eraser/index.js +++ b/src/modules/eraser/index.js @@ -3,11 +3,11 @@ * Created by peak on 16/8/18. */ export default { - name: "eraser", - icon: "fa fa-eraser", - i18n: "eraser", + name: 'eraser', + icon: 'fa fa-eraser', + i18n: 'eraser', show: true, - handler (editor) { - editor.execCommand("removeFormat") + handler(editor) { + editor.execCommand('removeFormat') } } diff --git a/src/modules/font/dashboard.html b/src/modules/font/dashboard.html new file mode 100644 index 0000000..518beb6 --- /dev/null +++ b/src/modules/font/dashboard.html @@ -0,0 +1,26 @@ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
\ No newline at end of file diff --git a/src/modules/font/dashboard.js b/src/modules/font/dashboard.js new file mode 100644 index 0000000..beb5ea8 --- /dev/null +++ b/src/modules/font/dashboard.js @@ -0,0 +1,56 @@ +import template from './dashboard.html' +import Command from '../../range/command' +/** + * Created by peak on 2017/2/10. + */ +export default { + template, + data(){ + return { + nameList: [ + 'Microsoft YaHei', + 'Helvetica Neue', + 'Helvetica', + 'Arial', + 'sans-serif', + 'Verdana', + 'Georgia', + 'Times New Roman', + 'Trebuchet MS', + 'Microsoft JhengHei', + 'Courier New', + 'Impact', + 'Comic Sans MS', + 'Consolas' + ], + lineHeightList: [ + '1.0', '1.2', '1.5', '1.8', '2.0', '2.5', '3.0' + ], + fontSizeList: [ + '12px', '14px', '16px', '18px', '20px', '22px', '24px' + ] + } + }, + methods: { + setFontName(name){ + this.$parent.execCommand('fontName', name) + }, + setFontSize(size){ + this.$parent.execCommand('fontSize', size) + }, + setHeading(heading){ + this.$parent.execCommand('formatBlock', `h${heading}`) + }, + _contains(arr, el){ + for (let i = 0; i < arr.length; i++) { + if (arr[i] === el) { + return true + } + } + return false + }, + setLineHeight(lh){ + this.$parent.execCommand(Command.LINE_HEIGHT, lh) + } + } +} diff --git a/src/modules/font/dashboard.vue b/src/modules/font/dashboard.vue deleted file mode 100644 index c1ca146..0000000 --- a/src/modules/font/dashboard.vue +++ /dev/null @@ -1,113 +0,0 @@ - - - \ No newline at end of file diff --git a/src/modules/font/index.js b/src/modules/font/index.js index 7546e18..4db2094 100644 --- a/src/modules/font/index.js +++ b/src/modules/font/index.js @@ -1,12 +1,12 @@ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' /** * font name and font size * Created by peak on 16/8/18. */ export default { - name: "font", - icon: "fa fa-font", - i18n: "font", + name: 'font', + icon: 'fa fa-font', + i18n: 'font', show: true, dashboard } \ No newline at end of file diff --git a/src/modules/full-screen/index.js b/src/modules/full-screen/index.js index 52f6ea5..949e6eb 100644 --- a/src/modules/full-screen/index.js +++ b/src/modules/full-screen/index.js @@ -2,12 +2,12 @@ * toggle full screen mode * Created by peak on 16/8/18. */ -export default { - name: "full-screen", - icon: "fa fa-arrows-alt", - i18n: "full screen", +export default { + name: 'full-screen', + icon: 'fa fa-arrows-alt', + i18n: 'full screen', show: true, - handler (editor) { + handler(editor) { editor.toggleFullScreen() } } diff --git a/src/modules/hr/index.js b/src/modules/hr/index.js index 01c5072..ba9183f 100644 --- a/src/modules/hr/index.js +++ b/src/modules/hr/index.js @@ -3,13 +3,13 @@ * Created by peak on 16/8/20. */ export default { - name: "hr", - icon: "fa fa-minus", + name: 'hr', + icon: 'fa fa-minus', show: true, - i18n: "hr", - handler (editor) { - editor.execCommand("insertHorizontalRule") - }, + i18n: 'hr', + handler(editor) { + editor.execCommand('insertHorizontalRule') + } // init (editor) { // // }, diff --git a/src/modules/image/dashboard.html b/src/modules/image/dashboard.html new file mode 100644 index 0000000..c4f17c9 --- /dev/null +++ b/src/modules/image/dashboard.html @@ -0,0 +1,23 @@ +
+
+ + + + +
+
+ {{$parent.locale.progress}}:{{progressComputable ? $parent.locale.unknown : upload.complete}} +
+
+ {{$parent.locale["please wait"]}}... +
+
+ {{$parent.locale.upload}} {{$parent.locale.error}}, + +
+
+ {{$parent.locale.upload}} {{$parent.locale.abort}}, + +
+
diff --git a/src/modules/image/dashboard.js b/src/modules/image/dashboard.js new file mode 100644 index 0000000..36ef8f8 --- /dev/null +++ b/src/modules/image/dashboard.js @@ -0,0 +1,129 @@ +// import lrz from 'lrz' +import template from './dashboard.html' + +/** + * Created by peak on 2017/2/10. + */ +export default { + template + // data() { + // return { + // url: '', + // upload: { + // status: 'ready', // progress,success,error,abort + // errmsg: null, + // progressComputable: false, + // complete: 0 + // } + // + // } + // }, + // methods: { + // reset(){ + // this.upload.status = 'ready' + // }, + // pick() { + // this.$els.file.click() + // }, + // insertImage(e) { + // e.preventDefault() + // if (!this.url) { + // return + // } + // this.$parent.execCommand('insertImage', this.url) + // this.url = null + // }, + // selectFile() { + // const component = this + // const config = component.$options.module.config + // + // const file = this.$els.file.files[0] + // if (file.size > config.size_limit) { + // const prompt = component.$parent.locale['exceed size limit'] + // component.upload.status = 'error' + // component.errmsg = prompt + // return + // } + // component.$els.file.value = null + // // 需要压缩 + // if (config.compress) { + // lrz(file, { + // width: config.width, + // height: config.height, + // quality: config.quality, + // fieldName: config.fieldName + // }).then((rst) => { + // if (config.server) { + // component.uploadFile(rst.file) + // } else { + // component.insertBase64(rst.base64) + // } + // }).catch((err) => { + // component.upload.status = 'error' + // console.log('upload error', err) + // }) + // return + // } + // // 不需要压缩 + // // base64 + // if (!config.server) { + // const reader = new FileReader() + // reader.onload = function (e) { + // component.insertBase64(e.target.result) + // } + // reader.readAsDataURL(file) + // return + // } + // // 上传服务器 + // component.uploadFile(file) + // }, + // insertBase64(data) { + // this.$parent.execCommand('insertimage', data) + // }, + // uploadFile(file) { + // const component = this + // const config = component.$options.module.config + // const formData = new FormData() + // formData.append(config.fieldName, file) + // const xhr = new XMLHttpRequest() + // xhr.onprogress = function (e) { + // component.upload.status = 'progress' + // if (e.lengthComputable) { + // component.upload.progressComputable = true + // const percentComplete = e.loaded / e.total + // component.upload.complete = (percentComplete * 100).toFixed(2) + // } else { + // component.upload.progressComputable = false + // } + // } + // xhr.onload = function (e) { + // if (xhr.status != 200) { + // component.upload.status = 'error' + // console.log('upload error', e) + // return + // } + // component.upload.status = 'success' + // try { + // const url = config.uploadHandler(xhr.responseText) + // if (url) { + // component.$parent.execCommand('insertImage', url) + // } + // } catch (e) { + // console.error(e) + // } finally { + // component.upload.status = 'ready' + // } + // } + // xhr.onerror = function (e) { + // component.upload.status = 'error' + // console.log('upload error', e) + // } + // xhr.onabort = function (e) { + // component.upload.status = 'abort' + // console.log('upload abort', e) + // } + // xhr.open('POST', config.server) + // xhr.send(formData) + // } + // } +} \ No newline at end of file diff --git a/src/modules/image/dashboard.vue b/src/modules/image/dashboard.vue deleted file mode 100644 index 6d121e9..0000000 --- a/src/modules/image/dashboard.vue +++ /dev/null @@ -1,151 +0,0 @@ - - - diff --git a/src/modules/image/index.js b/src/modules/image/index.js index 3b9342d..872ab09 100644 --- a/src/modules/image/index.js +++ b/src/modules/image/index.js @@ -1,29 +1,25 @@ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' /** * insert image * Created by peak on 16/8/18. */ export default { - name: "image", - icon: "fa fa-file-image-o", - i18n: "image", + name: 'image', + icon: 'fa fa-file-image-o', + i18n: 'image', show: true, config: { server: null, - fieldName: "image", - sizeLimit: 512 * 1024,//512k + fieldName: 'image', + sizeLimit: 512 * 1024,// 512k compress: true, width: 1600, height: 1600, quality: 80, uploadHandler(responseText){ - var json = JSON.parse(responseText) - if (!json.ok) { - alert(json.msg) - } else { - return json.data - } + const json = JSON.parse(responseText) + return json.ok ? json.data : null } }, dashboard diff --git a/src/modules/index.js b/src/modules/index.js new file mode 100644 index 0000000..784c205 --- /dev/null +++ b/src/modules/index.js @@ -0,0 +1,35 @@ +import align from './align/index' +import color from './color/index' +import eraser from './eraser/index' +import font from './font/index' +import fullScreen from './full-screen/index' +import hr from './hr/index' +import image from './image/index' +import info from './info/index' +import link from './link/index' +import list from './list/index' +import table from './table/index' +import text from './text/index' +import undo from './undo/index' +import unlink from './unlink/index' + +/** + * build-in moduls + * Created by peak on 2016/11/1. + */ +export default [ + text, + color, + font, + align, + list, + link, + unlink, + table, + image, + hr, + eraser, + undo, + fullScreen, + info +] diff --git a/src/modules/info/dashboard.vue b/src/modules/info/dashboard.html similarity index 59% rename from src/modules/info/dashboard.vue rename to src/modules/info/dashboard.html index 1930656..0ee82ae 100644 --- a/src/modules/info/dashboard.vue +++ b/src/modules/info/dashboard.html @@ -1,18 +1,9 @@ - - \ No newline at end of file + diff --git a/src/modules/info/dashboard.js b/src/modules/info/dashboard.js new file mode 100644 index 0000000..f60b2d9 --- /dev/null +++ b/src/modules/info/dashboard.js @@ -0,0 +1,12 @@ +import template from './dashboard.html' +/** + * Created by peak on 2017/2/10. + */ +export default { + template, + data(){ + return { + version: VERSION + } + } +} \ No newline at end of file diff --git a/src/modules/info/index.js b/src/modules/info/index.js index 8f0b9f7..344c745 100644 --- a/src/modules/info/index.js +++ b/src/modules/info/index.js @@ -2,12 +2,13 @@ * editor info * Created by peak on 16/8/18. */ -import dashboard from "./dashboard.vue"; -export default { - name: "info", - icon: "fa fa-info", +import dashboard from './dashboard' + +export default { + name: 'info', + icon: 'fa fa-info', show: true, - i18n: "info", + i18n: 'info', // handler () { // // }, diff --git a/src/modules/link/dashboard.html b/src/modules/link/dashboard.html new file mode 100644 index 0000000..1468d27 --- /dev/null +++ b/src/modules/link/dashboard.html @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/src/modules/link/dashboard.js b/src/modules/link/dashboard.js new file mode 100644 index 0000000..601df2e --- /dev/null +++ b/src/modules/link/dashboard.js @@ -0,0 +1,17 @@ +import template from './dashboard.html' + +export default { + template, + data(){ + return {url: null} + }, + methods: { + createLink(){ + if (!this.url) { + return + } + this.$parent.execCommand('createLink', this.url) + this.url = null + } + } +} diff --git a/src/modules/link/dashboard.vue b/src/modules/link/dashboard.vue deleted file mode 100644 index 24b4b85..0000000 --- a/src/modules/link/dashboard.vue +++ /dev/null @@ -1,22 +0,0 @@ - - \ No newline at end of file diff --git a/src/modules/link/index.js b/src/modules/link/index.js index 83bacad..576a37c 100644 --- a/src/modules/link/index.js +++ b/src/modules/link/index.js @@ -2,11 +2,12 @@ * create link * Created by peak on 16/8/18. */ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' + export default { - name: "link", - icon: "fa fa-chain", + name: 'link', + icon: 'fa fa-chain', show: true, - i18n: "link", + i18n: 'link', dashboard } diff --git a/src/modules/list/dashboard.vue b/src/modules/list/dashboard.html similarity index 92% rename from src/modules/list/dashboard.vue rename to src/modules/list/dashboard.html index 4081673..a5e3a75 100644 --- a/src/modules/list/dashboard.vue +++ b/src/modules/list/dashboard.html @@ -1,8 +1,8 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/modules/list/dashboard.js b/src/modules/list/dashboard.js new file mode 100644 index 0000000..0674d13 --- /dev/null +++ b/src/modules/list/dashboard.js @@ -0,0 +1,7 @@ +import template from './dashboard.html' +/** + * Created by peak on 2017/2/10. + */ +export default { + template +} \ No newline at end of file diff --git a/src/modules/list/index.js b/src/modules/list/index.js index 7c706cb..383d11e 100644 --- a/src/modules/list/index.js +++ b/src/modules/list/index.js @@ -2,11 +2,12 @@ * list,ul,ol * Created by peak on 16/8/18. */ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' + export default { - name: "list", - icon: "fa fa-list", + name: 'list', + icon: 'fa fa-list', show: true, - i18n: "list", + i18n: 'list', dashboard } \ No newline at end of file diff --git a/src/modules/table/dashboard.html b/src/modules/table/dashboard.html new file mode 100644 index 0000000..cbd8d10 --- /dev/null +++ b/src/modules/table/dashboard.html @@ -0,0 +1,12 @@ +
+ + + + +
\ No newline at end of file diff --git a/src/modules/table/dashboard.js b/src/modules/table/dashboard.js new file mode 100644 index 0000000..aa421d2 --- /dev/null +++ b/src/modules/table/dashboard.js @@ -0,0 +1,37 @@ +import template from './dashboard.html' + +/** + * Created by peak on 2017/2/10. + */ +export default { + template, + data(){ + return { + rows: 2, + cols: 2, + hasHead: false, + striped: false, + hover: false + } + }, + methods: { + insertTable(){ + if (this.rows < 2 || this.rows > 10) { + return + } + if (this.cols < 2 || this.cols > 10) { + return + } + let table = '' + for (let i = 0; i < this.rows; i++) { + table += '' + for (let j = 0; j < this.cols; j++) { + table += '' + } + table += '' + } + table += '
 
' + this.$parent.execCommand('insertHTML', table) + } + } +} \ No newline at end of file diff --git a/src/modules/table/dashboard.vue b/src/modules/table/dashboard.vue deleted file mode 100644 index be5dad4..0000000 --- a/src/modules/table/dashboard.vue +++ /dev/null @@ -1,49 +0,0 @@ - - - diff --git a/src/modules/table/index.js b/src/modules/table/index.js index e39a58a..18bcf99 100644 --- a/src/modules/table/index.js +++ b/src/modules/table/index.js @@ -1,15 +1,15 @@ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' /** * insert table * Created by peak on 16/8/18. */ export default { - //can not named table - //dashboard will add to editor as a child component and named as module name - //Do not use built-in or reserved HTML elements as component id - name: "tabulation", - icon: "fa fa-table", - i18n: "table", + // can not named table + // dashboard.html will add to editor as a child component and named as module name + // Do not use built-in or reserved HTML elements as component id + name: 'tabulation', + icon: 'fa fa-table', + i18n: 'table', show: true, dashboard } diff --git a/src/modules/text/dashboard.vue b/src/modules/text/dashboard.html similarity index 88% rename from src/modules/text/dashboard.vue rename to src/modules/text/dashboard.html index 2b8eb97..b1fe8bf 100644 --- a/src/modules/text/dashboard.vue +++ b/src/modules/text/dashboard.html @@ -1,8 +1,9 @@ - \ No newline at end of file + diff --git a/src/modules/text/dashboard.js b/src/modules/text/dashboard.js new file mode 100644 index 0000000..ccfcd5a --- /dev/null +++ b/src/modules/text/dashboard.js @@ -0,0 +1,5 @@ +import template from './dashboard.html' + +export default { + template +} \ No newline at end of file diff --git a/src/modules/text/index.js b/src/modules/text/index.js index 746f8ed..47d7138 100644 --- a/src/modules/text/index.js +++ b/src/modules/text/index.js @@ -2,11 +2,12 @@ * text,set the text bold or italic or underline or with strike through or subscript or superscript * Created by peak on 16/8/18. */ -import dashboard from "./dashboard.vue"; +import dashboard from './dashboard' + export default { - name: "text", - icon: "fa fa-pencil", - i18n: "text", + name: 'text', + icon: 'fa fa-pencil', + i18n: 'text', show: true, dashboard } diff --git a/src/modules/undo/index.js b/src/modules/undo/index.js index cf4a86e..74955b9 100644 --- a/src/modules/undo/index.js +++ b/src/modules/undo/index.js @@ -3,12 +3,12 @@ * Created by peak on 16/8/20. */ export default { - name: "undo", - icon: "fa-undo fa", + name: 'undo', + icon: 'fa-undo fa', show: true, - i18n: "undo", - handler (editor) { - editor.execCommand("undo") - }, + i18n: 'undo', + handler(editor) { + editor.execCommand('undo') + } } diff --git a/src/modules/unlink/index.js b/src/modules/unlink/index.js index dcffdcb..b101c59 100644 --- a/src/modules/unlink/index.js +++ b/src/modules/unlink/index.js @@ -2,12 +2,12 @@ * unlink * Created by peak on 16/8/18. */ -export default{ - name: "unlink", - icon: "fa fa-chain-broken", +export default { + name: 'unlink', + icon: 'fa fa-chain-broken', show: true, - i18n: "unlink", - handler (editor) { - editor.execCommand("unlink") + i18n: 'unlink', + handler(editor) { + editor.execCommand('unlink') } } \ No newline at end of file diff --git a/src/range/README.md b/src/range/README.md new file mode 100644 index 0000000..355a3ba --- /dev/null +++ b/src/range/README.md @@ -0,0 +1,29 @@ +### document.execCommand()指令测试结果 + +command | IE11 | chrome | firefox +----: | :---: | :---: | :----- +justifyLeft | Y | Y | Y +justifyCenter | Y | Y | Y +justifyRight | Y | Y | Y +foreColor | Y | Y | Y +backColor | Y | Y | Y +removeFormat | Y | Y | Y +fontName | Y | Y | Y +fontSize | Y | Y | Y +formatBlock | Y | N | N +insertHorizontalRule | Y | Y | Y +insertImage | Y | Y | Y +createLink | Y | Y | Y +insertOrderedList | Y | Y | Y +insertUnorderedList | Y | Y | Y +insertHTML | N | Y | Y +bold | Y | Y | Y +italic | Y | Y | Y +underline | Y | Y | Y +strikeThrough | Y | Y | Y +subscript | Y | Y | Y +superscript | Y | Y | Y +undo | Y | Y | Y +unlink | Y | Y | Y + + diff --git a/src/range/command.js b/src/range/command.js new file mode 100644 index 0000000..02db803 --- /dev/null +++ b/src/range/command.js @@ -0,0 +1,29 @@ +/** + * Created by peak on 2017/2/14. + */ +export default { + JUSTIFY_LEFT: 'justifyLeft', + JUSTIFY_CENTER: 'justifyCenter', + JUSTIFY_RIGHT: 'justifyRight', + FORE_COLOR: 'foreColor', + BACK_COLOR: 'backColor', + REMOVE_FORMAT: 'removeFormat', + FONT_NAME: 'fontName', + FONT_SIZE: 'fontSize', + FORMAT_BLOCK: 'formatBlock', + LINE_HEIGHT: 'lineHeight', + INSERT_HORIZONTAL_RULE: 'insertHorizontalRule', + INSERT_IMAGE: 'insertImage', + CREATE_LINK: 'createLink', + INSERT_ORDERED_LIST: 'insertOrderedList', + INSERT_UNORDERED_LIST: 'insertUnorderedList', + INSERT_HTML: 'insertHTML', + BOLD: 'bold', + ITALIC: 'italic', + UNDERLINE: 'underline', + STRIKE_THROUGH: 'strikeThrough', + SUBSCRIPT: 'subscript', + SUPERSCRIPT: 'superscript', + UNDO: 'undo', + UNLINK: 'unlink', +} \ No newline at end of file diff --git a/src/range/handler.js b/src/range/handler.js new file mode 100644 index 0000000..8c4fd40 --- /dev/null +++ b/src/range/handler.js @@ -0,0 +1,306 @@ +import Command from './command' +import { + mergeArray, + getDescendantTextNodes, + getAfterStartDescendantTextNodes, + getBeforeEndDescendantTextNodes, + getParentBlockNode, + isInlineElement +} from './util' + +/** + * Created by peak on 2017/2/14. + */ +export default class RangeHandler { + /** + * build range handler + * @param {Range} range + */ + constructor(range) { + if (!range || !(range instanceof Range)) { + throw new TypeError('cant\'t resolve range') + } + this.range = range + } + + + /** + * find all the text nodes in range + */ + getAllTextNodesInRange() { + const startContainer = this.range.startContainer + const endContainer = this.range.endContainer + const rootEl = this.range.commonAncestorContainer + const textNodes = [] + + if (startContainer === endContainer) { + if (startContainer.nodeType === Node.TEXT_NODE) { + return [startContainer] + } + const childNodes = startContainer.childNodes + for (let i = this.range.startOffset; i < this.range.endOffset; i++) { + mergeArray(textNodes, getDescendantTextNodes(childNodes[i])) + } + return textNodes + } + + let startIndex = 0 + let endIndex = 0 + for (let i = 0; i < rootEl.childNodes.length; i++) { + const node = rootEl.childNodes[i] + if (node.contains(startContainer)) { + startIndex = i + } + if (node.contains(endContainer)) { + endIndex = i + } + } + + for (let i = startIndex; i <= endIndex; i++) { + const node = rootEl.childNodes[i] + if (i === startIndex) { + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getAfterStartDescendantTextNodes(node, startContainer)) + } + } else if (i === endIndex) { + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getBeforeEndDescendantTextNodes(node, endContainer)) + } + } else if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getDescendantTextNodes(node)) + } + } + return textNodes + } + + /** + * execute edit command + * @param {String} command + * @param arg + */ + execCommand(command, arg) { + switch (command) { + + case Command.JUSTIFY_LEFT: { + document.execCommand(Command.JUSTIFY_LEFT, false, arg) + break + } + + case Command.JUSTIFY_RIGHT: { + document.execCommand(Command.JUSTIFY_RIGHT, false, arg) + break + } + + case Command.JUSTIFY_CENTER: { + document.execCommand(Command.JUSTIFY_CENTER, false, arg) + break + } + + case Command.FORE_COLOR: { + document.execCommand(Command.FORE_COLOR, false, arg) + break + } + case Command.BACK_COLOR: { + document.execCommand(Command.BACK_COLOR, false, arg) + break + } + case Command.REMOVE_FORMAT: { + document.execCommand(Command.REMOVE_FORMAT, false, arg) + break + } + case Command.FONT_NAME: { + document.execCommand(Command.FONT_NAME, false, arg) + break + } + case Command.FONT_SIZE: { + // 重新实现,改为直接修改样式 + const textNodes = this.getAllTextNodesInRange() + if (!textNodes.length) { + break + } + if (textNodes.length === 1 && textNodes[0] === this.range.startContainer + && textNodes[0] === this.range.endContainer) { + const textNode = textNodes[0] + if (this.range.startOffset === 0 + && this.range.endOffset === textNode.textContent.length) { + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg + break + } + const span = document.createElement('span') + span.style.fontSize = arg + textNode.parentNode.insertBefore(span, textNode) + span.appendChild(textNode) + break + } + const span = document.createElement('span') + span.innerText = textNode.textContent.substring( + this.range.startOffset, this.range.endOffset) + span.style.fontSize = arg + const frontPart = document.createTextNode( + textNode.textContent.substring(0, this.range.startOffset + 1)) + textNode.parentNode.insertBefore(frontPart, textNode) + textNode.parentNode.insertBefore(span, textNode) + textNode.textContent = textNode.textContent.substring(this.range.endOffset) + this.range.setStart(span, 0) + this.range.setEnd(span, 1) + break + } + + textNodes.forEach((textNode) => { + if (textNode === this.range.startContainer) { + if (this.range.startOffset === 0) { + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg + } else { + const span = document.createElement('span') + span.style.fontSize = arg + textNode.parentNode.insertBefore(span, textNode) + span.appendChild(textNode) + } + return + } + const span = document.createElement('span') + textNode.textContent = textNode.textContent.substring( + 0, this.range.startOffset) + span.style.fontSize = arg + textNode.parentNode.insertBefore(span, textNode) + this.range.setStart(textNode, 0) + return + } + if (textNode === this.range.endContainer) { + if (this.range.endOffset === textNode.textContent.length) { + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg + } else { + const span = document.createElement('span') + span.style.fontSize = arg + textNode.parentNode.insertBefore(span, textNode) + span.appendChild(textNode) + } + return + } + const span = document.createElement('span') + textNode.textContent = textNode.textContent.substring(this.range.endOffset) + span.style.fontSize = arg + textNode.parentNode.insertBefore(span, textNode) + span.appendChild(textNode) + this.range.setStart(textNode, textNode.textContent.length) + return + } + if (textNode.parentNode.childNodes.length === 1 + && isInlineElement(textNode.parentNode)) { + textNode.parentNode.style.fontSize = arg + return + } + + const span = document.createElement('span') + span.style.fontSize = arg + textNode.parentNode.insertBefore(span, textNode) + span.appendChild(textNode) + }) + break + } + case Command.FORMAT_BLOCK: { + if (document.execCommand(Command.FORMAT_BLOCK, false, arg)) { + break + } + // hack + const element = document.createElement(arg) + this.range.surroundContents(element) + break + } + case Command.LINE_HEIGHT: { + const textNodes = this.getAllTextNodesInRange() + textNodes.forEach((textNode) => { + const parentBlock = getParentBlockNode(textNode) + if (parentBlock) { + parentBlock.style.lineHeight = arg + } + }) + break + } + case Command.INSERT_HORIZONTAL_RULE: { + document.execCommand(Command.INSERT_HORIZONTAL_RULE, false) + break + } + case Command.INSERT_IMAGE: { + document.execCommand(Command.INSERT_IMAGE, false, arg) + break + } + case Command.CREATE_LINK: { + document.execCommand(Command.CREATE_LINK, false, arg) + break + } + case Command.INSERT_ORDERED_LIST: { + document.execCommand(Command.INSERT_ORDERED_LIST, false, arg) + break + } + case Command.INSERT_UNORDERED_LIST: { + document.execCommand(Command.INSERT_UNORDERED_LIST, false, arg) + break + } + case Command.INSERT_HTML: { + if (document.execCommand(Command.INSERT_HTML, false, arg)) { + break + } + // hack + const fragment = document.createDocumentFragment() + const div = document.createElement('div') + div.innerHTML = arg + if (div.hasChildNodes()) { + for (let i = 0; i < div.childNodes.length; i++) { + fragment.appendChild(div.childNodes[i].cloneNode(true)) + } + } + this.range.deleteContents() + this.range.insertNode(fragment) + break + } + case Command.BOLD: { + document.execCommand(Command.BOLD, false, arg) + break + } + case Command.ITALIC: { + document.execCommand(Command.ITALIC, false) + break + } + case Command.UNDERLINE: { + document.execCommand(Command.UNDERLINE, false) + break + } + case Command.STRIKE_THROUGH: { + document.execCommand(Command.STRIKE_THROUGH, false) + break + } + case Command.SUBSCRIPT: { + document.execCommand(Command.SUBSCRIPT, false) + break + } + case Command.SUPERSCRIPT: { + document.execCommand(Command.SUPERSCRIPT, false) + break + } + case Command.UNDO: { + document.execCommand(Command.UNDO, false) + break + } + case Command.UNLINK: { + document.execCommand(Command.UNLINK, false) + break + } + default: { + break + } + } + } +} \ No newline at end of file diff --git a/src/range/util.js b/src/range/util.js new file mode 100644 index 0000000..a66e34c --- /dev/null +++ b/src/range/util.js @@ -0,0 +1,132 @@ +/** + * Created by peak on 2017/2/15. + */ +/** + * add every elements of extArr to sourceArr. + * @param sourceArr + * @param extArr + */ +export const mergeArray = (sourceArr, extArr) => { + // note: Array.prototype.push.apply(arr1,arr2) is unreliable + extArr.forEach((el) => { + sourceArr.push(el) + }) +} + +/** + * find all the descendant text nodes of a element + * @param ancestor + */ +export const getDescendantTextNodes = (ancestor) => { + if (ancestor.nodeType === Node.TEXT_NODE) { + return [ancestor] + } + const textNodes = [] + if (!ancestor.hasChildNodes()) { + return textNodes + } + const childNodes = ancestor.childNodes + for (let i = 0; i < childNodes.length; i++) { + const node = childNodes[i] + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getDescendantTextNodes(node)) + } + } + return textNodes +} +/** + * find all the descendant text nodes of an ancestor element that before the specify end element, + * the ancestor element must contains the end element. + * @param ancestor + * @param endEl + */ +export const getBeforeEndDescendantTextNodes = (ancestor, endEl) => { + const textNodes = [] + let endIndex = 0 + for (let i = 0; i < ancestor.childNodes.length; i++) { + if (ancestor.childNodes[i].contains(endEl)) { + endIndex = i + break + } + } + + for (let i = 0; i <= endIndex; i++) { + const node = ancestor.childNodes[i] + if (node === endEl) { + mergeArray(textNodes, getDescendantTextNodes(node)) + } else if (i === endIndex) { + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getBeforeEndDescendantTextNodes(node, endEl)) + } + } else if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, getDescendantTextNodes(node)) + } + } + return textNodes +} +/** + * find all the descendant text nodes of an ancestor element that after the specify start element, + * the ancestor element must contains the start element. + * @param ancestor + * @param startEl + */ +export const getAfterStartDescendantTextNodes = (ancestor, startEl) => { + const textNodes = [] + let startIndex = 0 + for (let i = 0; i < ancestor.childNodes.length; i++) { + if (ancestor.childNodes[i].contains(startEl)) { + startIndex = i + break + } + } + + for (let i = startIndex; i < ancestor.childNodes.length; i++) { + const node = ancestor.childNodes[i] + if (node === startEl) { + mergeArray(textNodes, getDescendantTextNodes(node)) + } else if (i === startIndex) { + if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, + getAfterStartDescendantTextNodes(node, startEl)) + } + } else if (node.nodeType === Node.TEXT_NODE) { + textNodes.push(node) + } else if (node.nodeType === Node.ELEMENT_NODE) { + mergeArray(textNodes, + getDescendantTextNodes(node)) + } + } + return textNodes +} + + +/** + * get the closest parent block node of a text node. + * @param node + * @return {Node} + */ +export const getParentBlockNode = (node) => { + const blockNodeNames = ['DIV', 'P', 'SECTION', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', + 'OL', 'UL', 'LI', 'TR', 'TD', 'TH', 'TBODY', 'THEAD', 'TABLE', 'ARTICLE', 'HEADER', 'FOOTER'] + const container = node.parentNode + while (container) { + if (blockNodeNames.includes(container.nodeName)) { + break + } + } + return container +} + +export const isInlineElement = (node) => { + const inlineNodeNames = ['A', 'ABBR', 'ACRONYM', 'B', 'CITE', 'CODE', 'EM', 'I', + 'FONT', 'IMG', 'S', 'SMALL', 'SPAN', 'STRIKE', 'STRONG', 'U', 'SUB', 'SUP'] + return inlineNodeNames.includes(node.nodeName) +} \ No newline at end of file diff --git a/src/style.less b/src/style.css similarity index 57% rename from src/style.less rename to src/style.css index ec981c6..593f472 100644 --- a/src/style.less +++ b/src/style.css @@ -1,27 +1,27 @@ -@border-color: #ddd; -@border-radius: 4px; -@background-color: white; +:root { + --border-color: #ddd; + --border-radius: 5px; + --color: #333; +} /** .vue-html5-editor ├──.toolbar | ├── ul (menu) - | └── .dashboard + | └── .dashboard.html └──.content */ .vue-html5-editor { font-size: 14px; line-height: 1.5; - border: 1px solid @border-color; + background-color: white; + color: var(--color); + border: 1px solid var(--border-color); text-align: left; - background-color: @background-color; - border-radius: .4em; + border-radius: var(--border-radius); overflow: hidden; - - * { - box-sizing: border-box; - } + box-sizing: border-box; &.full-screen { position: fixed !important; @@ -33,14 +33,14 @@ } & > .toolbar { - background-color: @background-color; position: relative; + background-color: inherit; & > ul { list-style: none; padding: 0; margin: 0; - border-bottom: 1px solid @border-color; + border-bottom: 1px solid var(--border-color); & > li { display: inline-block; @@ -48,7 +48,7 @@ width: 50px; text-align: center; line-height: 36px; - .icon { + & .icon { height: 16px; width: 16px; display: inline-block; @@ -56,82 +56,87 @@ } } - .dashboard { - border-bottom: 1px solid @border-color; + & > .dashboard { + background-color: inherit; + border-bottom: 1px solid var(--border-color); padding: 10px; position: absolute; top: 100%; left: 0; right: 0; - background-color: @background-color; overflow: auto; - input[type='text'], input[type='number'], select { + & input[type='text'], & input[type='number'], & select { padding: 6px 12px; - color: #555; - background-color: #fff; - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - &:focus { - border-color: #66afe9; - outline: 0; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); + color: inherit; + background-color: transparent; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + + &:hover { + border-color: color(var(--border-color) blackness(30%)); } + &[disabled], &[readonly] { background-color: #eee; opacity: 1; } + &[disabled] { cursor: not-allowed; } } - button { + & button { + color: inherit; + background-color: inherit; padding: 6px 12px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; - border: 1px solid transparent; - border-radius: 4px; - color: #333; - background-color: #fff; - border-color: #ccc; - - &.active, &:active, &:focus, &:hover { - color: #333; - background-color: #e6e6e6; - } + border: 1px solid var(--border-color); + border-radius: var(--border-radius); - &.active, &:active { - border-color: #adadad; - outline: 0; - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - } - &:focus { - border-color: #8c8c8c; - text-decoration: none; - } &:hover { - border-color: #adadad; - text-decoration: none; + border-color: color(var(--border-color) blackness(30%)); + } + + &[disabled] { + cursor: not-allowed; + opacity: .68; } + + /*&.active, &:active, &:focus, &:hover {*/ + /*opacity: .8;*/ + /*}*/ + + /*&.active, &:active {*/ + /*outline: 0;*/ + /*box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);*/ + /*}*/ + /*&:focus {*/ + /*border-color: #8c8c8c;*/ + /*text-decoration: none;*/ + /*}*/ + /*&:hover {*/ + /*border-color: #adadad;*/ + /*text-decoration: none;*/ + /*}*/ } - input, button, select { + & input, button, select { line-height: normal; } - label { + & label { font-weight: bolder; } } } - .content { + & > .content { overflow: auto; padding: 10px; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 3003409..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,40 +0,0 @@ -var path = require("path") -var package = require("./package.json") -var webpack = require("webpack") -module.exports = { - context: __dirname + "/src", - entry: "./index.js", - output: { - path: __dirname + "/dist", - filename: "vue-html5-editor.js", - libraryTarget: "umd", - library: "VueHtml5Editor" - }, - module: { - loaders: [ - {test: /\.css$/, loader: "style-loader!css-loader"}, - {test: /(\.html)$/, loader: "html-loader"}, - {test: /\.(jpg)|(png)|(gif)$/, loader: "url-loader"}, - {test: /\.vue$/, loader: "vue-loader"}, - { - test: /\.js$/, - exclude: /node_modules/, - loader: 'babel' - } - ] - }, - plugins: [ - new webpack.BannerPlugin("Vue-html5-editor " + package.version + "\nhttps://github.com/PeakTai/vue-html5-editor"), - new webpack.DefinePlugin({ - ROOT: JSON.stringify(path.normalize(__dirname)), - }), - new webpack.DefinePlugin({ - VERSION: JSON.stringify(package.version), - }) - ], - debug: false, - babel: { - presets: ['es2015'], - plugins: ['transform-runtime'] - } -}