├── .gitignore ├── .npmignore ├── README.md ├── dist └── bundle.js ├── example ├── .env ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── EditorJsTool.js │ ├── index.css │ ├── index.js │ ├── reportWebVitals.js │ └── setupTests.js ├── package-lock.json ├── package.json ├── src ├── Hyperlink.css ├── Hyperlink.js └── SelectionUtils.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | npm-debug.log 3 | .idea/* 4 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | assets/ 3 | src/ 4 | webpack.config.js 5 | yarn.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://badgen.net/badge/Editor.js/v2.0/blue) 2 | 3 | # Hyperlink Tool 4 | 5 | A tool link with target & rel attribute for [Editor.js](https://editorjs.io). 6 | 7 | ![Screen Shot 2020-11-08 at 23 51 36](https://user-images.githubusercontent.com/22043198/98481955-acee3900-2230-11eb-8b9d-a76439dc258e.png) 8 | 9 | ![Screen Shot 2020-11-08 at 23 51 43](https://user-images.githubusercontent.com/22043198/98481956-afe92980-2230-11eb-9a84-f22149befbc0.png) 10 | 11 | ![Screen Shot 2020-11-08 at 23 52 04](https://user-images.githubusercontent.com/22043198/98481957-b11a5680-2230-11eb-9356-5e956f1f8d35.png) 12 | 13 | ## Installation 14 | 15 | ### Get the package via NPM 16 | 17 | ```shell 18 | npm i editorjs-hyperlink -D 19 | ``` 20 | ### or via Yarn 21 | 22 | ```shell 23 | yarn add editorjs-hyperlink -D 24 | ``` 25 | 26 | Include module at your application 27 | 28 | ```javascript 29 | const Hyperlink = require('editorjs-hyperlink'); 30 | ``` 31 | 32 | ## Usage 33 | Add a new Tool to the `tools` property of the Editor.js initial config. 34 | 35 | ```javascript 36 | var editor = EditorJS({ 37 | ... 38 | 39 | tools: { 40 | ... 41 | 42 | hyperlink: { 43 | class: Hyperlink, 44 | config: { 45 | shortcut: 'CMD+L', 46 | target: '_blank', 47 | rel: 'nofollow', 48 | availableTargets: ['_blank', '_self'], 49 | availableRels: ['author', 'noreferrer'], 50 | validate: false, 51 | } 52 | }, 53 | 54 | ... 55 | }, 56 | 57 | ... 58 | 59 | i18n: { 60 | toolNames: { 61 | Hyperlink: 'Link' 62 | }, 63 | tools: { 64 | hyperlink: { 65 | Save: 'Salvar', 66 | 'Select target': 'Seleziona destinazione', 67 | 'Select rel': 'Wählen rel' 68 | } 69 | } 70 | } 71 | 72 | ... 73 | }); 74 | ``` 75 | 76 | ## Config Params (optional) 77 | 78 | | Field | Type | Description | 79 | | ------ | -------- | ---------------- | 80 | | shortcut | `string` | Shortcut, defaults to 'CMD+L' | 81 | | target | `string` | Defines a default target, defaults to null | 82 | | rel | `string` | Defines a default rel, defaults to null | 83 | | availableTargets | `string[]` | Available link targets, defaults to all targets.
If empty array is provided, the control will be hidden and the default value applied. | 84 | | availableRels | `string[]` | Available link rels, defaults to all rels.
If empty array is provided, the control will be hidden and the default value applied. | 85 | | validate | `boolean` | Defines if an URL should be validated on saving | 86 | 87 | 88 | ## License 89 | [MIT](https://tamit.info) -------------------------------------------------------------------------------- /dist/bundle.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["Hyperlink"] = factory(); 8 | else 9 | root["Hyperlink"] = factory(); 10 | })(window, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) { 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ } 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ i: moduleId, 25 | /******/ l: false, 26 | /******/ exports: {} 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.l = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // define getter function for harmony exports 47 | /******/ __webpack_require__.d = function(exports, name, getter) { 48 | /******/ if(!__webpack_require__.o(exports, name)) { 49 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 50 | /******/ } 51 | /******/ }; 52 | /******/ 53 | /******/ // define __esModule on exports 54 | /******/ __webpack_require__.r = function(exports) { 55 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 56 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 57 | /******/ } 58 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 59 | /******/ }; 60 | /******/ 61 | /******/ // create a fake namespace object 62 | /******/ // mode & 1: value is a module id, require it 63 | /******/ // mode & 2: merge all properties of value into the ns 64 | /******/ // mode & 4: return value when already ns object 65 | /******/ // mode & 8|1: behave like require 66 | /******/ __webpack_require__.t = function(value, mode) { 67 | /******/ if(mode & 1) value = __webpack_require__(value); 68 | /******/ if(mode & 8) return value; 69 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 70 | /******/ var ns = Object.create(null); 71 | /******/ __webpack_require__.r(ns); 72 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 73 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 74 | /******/ return ns; 75 | /******/ }; 76 | /******/ 77 | /******/ // getDefaultExport function for compatibility with non-harmony modules 78 | /******/ __webpack_require__.n = function(module) { 79 | /******/ var getter = module && module.__esModule ? 80 | /******/ function getDefault() { return module['default']; } : 81 | /******/ function getModuleExports() { return module; }; 82 | /******/ __webpack_require__.d(getter, 'a', getter); 83 | /******/ return getter; 84 | /******/ }; 85 | /******/ 86 | /******/ // Object.prototype.hasOwnProperty.call 87 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 88 | /******/ 89 | /******/ // __webpack_public_path__ 90 | /******/ __webpack_require__.p = "/"; 91 | /******/ 92 | /******/ 93 | /******/ // Load entry module and return exports 94 | /******/ return __webpack_require__(__webpack_require__.s = "./src/Hyperlink.js"); 95 | /******/ }) 96 | /************************************************************************/ 97 | /******/ ({ 98 | 99 | /***/ "./node_modules/css-loader/dist/cjs.js!./src/Hyperlink.css": 100 | /*!*****************************************************************!*\ 101 | !*** ./node_modules/css-loader/dist/cjs.js!./src/Hyperlink.css ***! 102 | \*****************************************************************/ 103 | /*! no static exports found */ 104 | /***/ (function(module, exports, __webpack_require__) { 105 | 106 | eval("// Imports\nvar ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.i, \".ce-inline-tool-hyperlink-wrapper {\\r\\n outline: none;\\r\\n border: 0;\\r\\n border-radius: 0 0 4px 4px;\\r\\n margin: 0;\\r\\n font-size: 13px;\\r\\n padding: 10px;\\r\\n width: 100%;\\r\\n -webkit-box-sizing: border-box;\\r\\n box-sizing: border-box;\\r\\n display: none;\\r\\n font-weight: 500;\\r\\n border-top: 1px solid rgba(201,201,204,.48);\\r\\n}\\r\\n\\r\\n.ce-inline-tool-hyperlink-wrapper.ce-inline-tool-hyperlink-wrapper--showed {\\r\\n display: block;\\r\\n}\\r\\n\\r\\n.ce-inline-tool-hyperlink--input,\\r\\n.ce-inline-tool-hyperlink--select-target,\\r\\n.ce-inline-tool-hyperlink--select-rel {\\r\\n border: 1px solid rgba(201,201,204,.48);\\r\\n -webkit-box-shadow: inset 0 1px 2px 0 rgba(35,44,72,.06);\\r\\n box-shadow: inset 0 1px 2px 0 rgba(35,44,72,.06);\\r\\n border-radius: 5px;\\r\\n padding: 5px 8px;\\r\\n margin-bottom: 10px;\\r\\n outline: none;\\r\\n width: 100%;\\r\\n -webkit-box-sizing: border-box;\\r\\n box-sizing: border-box;\\r\\n}\\r\\n\\r\\n.ce-inline-tool-hyperlink--select-target,\\r\\n.ce-inline-tool-hyperlink--select-rel {\\r\\n width: 48%;\\r\\n display: inline-block;\\r\\n}\\r\\n.ce-inline-tool-hyperlink--select-target {\\r\\n margin-right: 2%;\\r\\n}\\r\\n.ce-inline-tool-hyperlink--select-rel {\\r\\n margin-left: 2%;\\r\\n}\\r\\n\\r\\n.ce-inline-tool-hyperlink--button {\\r\\n display: block;\\r\\n width: 100%;\\r\\n background-color: #34c38f;\\r\\n color: #fff;\\r\\n padding: 7px 0;\\r\\n border: none;\\r\\n text-align: center;\\r\\n text-decoration: none;\\r\\n font-size: 16px;\\r\\n border-radius: 5px;\\r\\n cursor: pointer;\\r\\n}\\r\\n\", \"\"]);\n// Exports\nmodule.exports = exports;\n\n\n//# sourceURL=webpack://Hyperlink/./src/Hyperlink.css?./node_modules/css-loader/dist/cjs.js"); 107 | 108 | /***/ }), 109 | 110 | /***/ "./node_modules/css-loader/dist/runtime/api.js": 111 | /*!*****************************************************!*\ 112 | !*** ./node_modules/css-loader/dist/runtime/api.js ***! 113 | \*****************************************************/ 114 | /*! no static exports found */ 115 | /***/ (function(module, exports, __webpack_require__) { 116 | 117 | "use strict"; 118 | eval("\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\n// eslint-disable-next-line func-names\nmodule.exports = function (useSourceMap) {\n var list = []; // return the list of modules as css string\n\n list.toString = function toString() {\n return this.map(function (item) {\n var content = cssWithMappingToString(item, useSourceMap);\n\n if (item[2]) {\n return \"@media \".concat(item[2], \" {\").concat(content, \"}\");\n }\n\n return content;\n }).join('');\n }; // import a list of modules into the list\n // eslint-disable-next-line func-names\n\n\n list.i = function (modules, mediaQuery, dedupe) {\n if (typeof modules === 'string') {\n // eslint-disable-next-line no-param-reassign\n modules = [[null, modules, '']];\n }\n\n var alreadyImportedModules = {};\n\n if (dedupe) {\n for (var i = 0; i < this.length; i++) {\n // eslint-disable-next-line prefer-destructuring\n var id = this[i][0];\n\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n }\n\n for (var _i = 0; _i < modules.length; _i++) {\n var item = [].concat(modules[_i]);\n\n if (dedupe && alreadyImportedModules[item[0]]) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n if (mediaQuery) {\n if (!item[2]) {\n item[2] = mediaQuery;\n } else {\n item[2] = \"\".concat(mediaQuery, \" and \").concat(item[2]);\n }\n }\n\n list.push(item);\n }\n };\n\n return list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n var content = item[1] || ''; // eslint-disable-next-line prefer-destructuring\n\n var cssMapping = item[3];\n\n if (!cssMapping) {\n return content;\n }\n\n if (useSourceMap && typeof btoa === 'function') {\n var sourceMapping = toComment(cssMapping);\n var sourceURLs = cssMapping.sources.map(function (source) {\n return \"/*# sourceURL=\".concat(cssMapping.sourceRoot || '').concat(source, \" */\");\n });\n return [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n }\n\n return [content].join('\\n');\n} // Adapted from convert-source-map (MIT)\n\n\nfunction toComment(sourceMap) {\n // eslint-disable-next-line no-undef\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n var data = \"sourceMappingURL=data:application/json;charset=utf-8;base64,\".concat(base64);\n return \"/*# \".concat(data, \" */\");\n}\n\n//# sourceURL=webpack://Hyperlink/./node_modules/css-loader/dist/runtime/api.js?"); 119 | 120 | /***/ }), 121 | 122 | /***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js": 123 | /*!****************************************************************************!*\ 124 | !*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***! 125 | \****************************************************************************/ 126 | /*! no static exports found */ 127 | /***/ (function(module, exports, __webpack_require__) { 128 | 129 | "use strict"; 130 | eval("\n\nvar isOldIE = function isOldIE() {\n var memo;\n return function memorize() {\n if (typeof memo === 'undefined') {\n // Test for IE <= 9 as proposed by Browserhacks\n // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805\n // Tests for existence of standard globals is to allow style-loader\n // to operate correctly into non-standard environments\n // @see https://github.com/webpack-contrib/style-loader/issues/177\n memo = Boolean(window && document && document.all && !window.atob);\n }\n\n return memo;\n };\n}();\n\nvar getTarget = function getTarget() {\n var memo = {};\n return function memorize(target) {\n if (typeof memo[target] === 'undefined') {\n var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself\n\n if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n try {\n // This will throw an exception if access to iframe is blocked\n // due to cross-origin restrictions\n styleTarget = styleTarget.contentDocument.head;\n } catch (e) {\n // istanbul ignore next\n styleTarget = null;\n }\n }\n\n memo[target] = styleTarget;\n }\n\n return memo[target];\n };\n}();\n\nvar stylesInDom = [];\n\nfunction getIndexByIdentifier(identifier) {\n var result = -1;\n\n for (var i = 0; i < stylesInDom.length; i++) {\n if (stylesInDom[i].identifier === identifier) {\n result = i;\n break;\n }\n }\n\n return result;\n}\n\nfunction modulesToDom(list, options) {\n var idCountMap = {};\n var identifiers = [];\n\n for (var i = 0; i < list.length; i++) {\n var item = list[i];\n var id = options.base ? item[0] + options.base : item[0];\n var count = idCountMap[id] || 0;\n var identifier = \"\".concat(id, \" \").concat(count);\n idCountMap[id] = count + 1;\n var index = getIndexByIdentifier(identifier);\n var obj = {\n css: item[1],\n media: item[2],\n sourceMap: item[3]\n };\n\n if (index !== -1) {\n stylesInDom[index].references++;\n stylesInDom[index].updater(obj);\n } else {\n stylesInDom.push({\n identifier: identifier,\n updater: addStyle(obj, options),\n references: 1\n });\n }\n\n identifiers.push(identifier);\n }\n\n return identifiers;\n}\n\nfunction insertStyleElement(options) {\n var style = document.createElement('style');\n var attributes = options.attributes || {};\n\n if (typeof attributes.nonce === 'undefined') {\n var nonce = true ? __webpack_require__.nc : undefined;\n\n if (nonce) {\n attributes.nonce = nonce;\n }\n }\n\n Object.keys(attributes).forEach(function (key) {\n style.setAttribute(key, attributes[key]);\n });\n\n if (typeof options.insert === 'function') {\n options.insert(style);\n } else {\n var target = getTarget(options.insert || 'head');\n\n if (!target) {\n throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");\n }\n\n target.appendChild(style);\n }\n\n return style;\n}\n\nfunction removeStyleElement(style) {\n // istanbul ignore if\n if (style.parentNode === null) {\n return false;\n }\n\n style.parentNode.removeChild(style);\n}\n/* istanbul ignore next */\n\n\nvar replaceText = function replaceText() {\n var textStore = [];\n return function replace(index, replacement) {\n textStore[index] = replacement;\n return textStore.filter(Boolean).join('\\n');\n };\n}();\n\nfunction applyToSingletonTag(style, index, remove, obj) {\n var css = remove ? '' : obj.media ? \"@media \".concat(obj.media, \" {\").concat(obj.css, \"}\") : obj.css; // For old IE\n\n /* istanbul ignore if */\n\n if (style.styleSheet) {\n style.styleSheet.cssText = replaceText(index, css);\n } else {\n var cssNode = document.createTextNode(css);\n var childNodes = style.childNodes;\n\n if (childNodes[index]) {\n style.removeChild(childNodes[index]);\n }\n\n if (childNodes.length) {\n style.insertBefore(cssNode, childNodes[index]);\n } else {\n style.appendChild(cssNode);\n }\n }\n}\n\nfunction applyToTag(style, options, obj) {\n var css = obj.css;\n var media = obj.media;\n var sourceMap = obj.sourceMap;\n\n if (media) {\n style.setAttribute('media', media);\n } else {\n style.removeAttribute('media');\n }\n\n if (sourceMap && typeof btoa !== 'undefined') {\n css += \"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), \" */\");\n } // For old IE\n\n /* istanbul ignore if */\n\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n while (style.firstChild) {\n style.removeChild(style.firstChild);\n }\n\n style.appendChild(document.createTextNode(css));\n }\n}\n\nvar singleton = null;\nvar singletonCounter = 0;\n\nfunction addStyle(obj, options) {\n var style;\n var update;\n var remove;\n\n if (options.singleton) {\n var styleIndex = singletonCounter++;\n style = singleton || (singleton = insertStyleElement(options));\n update = applyToSingletonTag.bind(null, style, styleIndex, false);\n remove = applyToSingletonTag.bind(null, style, styleIndex, true);\n } else {\n style = insertStyleElement(options);\n update = applyToTag.bind(null, style, options);\n\n remove = function remove() {\n removeStyleElement(style);\n };\n }\n\n update(obj);\n return function updateStyle(newObj) {\n if (newObj) {\n if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) {\n return;\n }\n\n update(obj = newObj);\n } else {\n remove();\n }\n };\n}\n\nmodule.exports = function (list, options) {\n options = options || {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of