editor.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. function bx_ex_editor_init(oEditor, oParams)
  2. {
  3. $(oParams.selector).after("<div id='" + oParams.name + "' class='" + oParams.name + " bx-def-font-inputs bx-form-input-textarea bx-form-input-html bx-form-input-html-editor mt-px text-gray-700 dark:text-gray-300 w-full p-4 ring-1 ring-gray-300 dark:ring-gray-700 dark:focus:placeholder-700 bg-gray-50 dark:bg-gray-900/50 placeholder-gray-500 focus:outline-none focus:bg-white dark:focus:bg-gray-900 focus:placeholder-gray-300 dark:focus:placeholder-gray-700 focus:text-gray-900 dark:focus:text-gray-100 text-base border-0 flex-wrap" + oParams.css_class + "'></div>" );
  4. $(oParams.selector).hide();
  5. var oImage = {
  6. class: ImageTool,
  7. config: {
  8. field: 'file',
  9. uploader: {
  10. uploadByFile(file){
  11. return new Promise((resolve, reject) => {
  12. const formData = new FormData();
  13. formData.append("file", file);
  14. fetch(oParams.root_url + "storage.php?o=sys_images_editor&t=sys_images_editor&a=upload", {
  15. method: "POST",
  16. body: formData
  17. }
  18. )
  19. .then(response => response.json())
  20. .then(result => {
  21. if ('undefined' != typeof(result.link))
  22. resolve({
  23. success: 1,
  24. file: {
  25. url: result.link,
  26. }
  27. });
  28. else
  29. reject("Upload failed");
  30. })
  31. .catch(error => {
  32. reject("Upload failed");
  33. });
  34. });
  35. },
  36. uploadByUrl(url){
  37. return new Promise((resolve, reject) => {
  38. resolve({
  39. success: 1,
  40. file: {
  41. url: url,
  42. }
  43. });
  44. });
  45. }
  46. }
  47. }
  48. };
  49. var oTools = {
  50. paragraph: {
  51. class: Paragraph,
  52. inlineToolbar: true,
  53. },
  54. mention: BxMention,
  55. marker: Marker,
  56. inlineCode: InlineCode,
  57. };
  58. if (oParams.toolbar){
  59. oParams.toolbar.forEach(function(item){
  60. switch(item) {
  61. case 'header':
  62. oTools.header = Header;
  63. break;
  64. case 'list':
  65. oTools.list = List;
  66. break;
  67. case 'image':
  68. oTools.image = oImage;
  69. break;
  70. case 'embed':
  71. oTools.embedblock = BxEmbedBlock;
  72. break;
  73. case 'code':
  74. oTools.code = CodeTool;
  75. break;
  76. case 'delimiter':
  77. oTools.delimiter = Delimiter;
  78. break;
  79. }
  80. })
  81. }
  82. oEditor = new EditorJS({
  83. holder : document.getElementById(oParams.name),
  84. inlineToolbar: oParams.toolbar_inline.concat(['mention']),
  85. tools: oTools,
  86. onReady: () => {
  87. if ($(oParams.selector).val() != ''){
  88. const doc = new DOMParser().parseFromString($(oParams.selector).val(), "text/html");
  89. var a = doc.getElementsByClassName('bx-embed-link');
  90. Array.prototype.forEach.call(a, function(i) {
  91. i.innerHTML = i.getAttribute('source');
  92. });
  93. oEditor.blocks.renderFromHTML(doc.documentElement.getElementsByTagName('body')[0].innerHTML);
  94. }
  95. },
  96. onChange:() =>{
  97. oEditor.save().then((savedData) =>{
  98. const edjsParser = edjsHTML({embedblock: bx_ex_editor_custom_parser_embedblock});
  99. oData = edjsParser.parse(savedData);
  100. var s ='';
  101. oData.forEach(function(item){
  102. s += item;
  103. })
  104. $(oParams.selector).val(s);
  105. bx_editor_on_space_enter (s, oParams.selector);
  106. }).catch((error) =>{
  107. console.log("error", error)
  108. })
  109. }
  110. });
  111. tribute = new Tribute({
  112. collection: [
  113. {
  114. selectTemplate: function(item) {
  115. if (this.range.isContentEditable(this.current.element)) {
  116. return ('<a class="bx-mention-link" title="' + item.original.label + '" dchar="@" data-profile-id="' + item.original.value + '" href="' + item.original.url + '">@' + item.original.label + '</a>');
  117. }
  118. return "@" + item.original.value;
  119. },
  120. values: function (text, cb) {
  121. $.getJSON(oParams.root_url + "/searchExtended.php?action=get_mention&symbol=%40&", {term: text}, function(data) {
  122. cb(data);
  123. });
  124. },
  125. lookup: "label",
  126. fillAttr: "label"
  127. },
  128. {
  129. trigger: "#",
  130. selectTemplate: function(item) {
  131. if (this.range.isContentEditable(this.current.element)) {
  132. return ('<a class="bx-mention-link" title="' + item.original.label + '" dchar="#" data-profile-id="' + item.original.value + '" href="' + item.original.url + '">#' + item.original.label + '</a>');
  133. }
  134. return "#" + item.original.value;
  135. },
  136. values: function (text, cb) {
  137. $.getJSON(oParams.root_url + "/searchExtended.php?action=get_mention&symbol=%23&", {term: text}, function(data) {
  138. cb(data);
  139. });
  140. },
  141. lookup: "label",
  142. fillAttr: "label"
  143. }
  144. ]
  145. })
  146. tribute.attach(document.getElementById(oParams.name));
  147. document.getElementById(oParams.name).removeAttribute("contenteditable");
  148. }
  149. function bx_ex_editor_custom_parser_embedblock(block)
  150. {
  151. if (block.data && block.data.source)
  152. return '<div class="bx-embed-link" source="' + block.data.source + '"></div>';
  153. }
  154. var oLink ='';
  155. class BxEmbedBlock {
  156. constructor({ data, block }){
  157. this.blockAPI = block
  158. this.nodes = {
  159. linkContent: null,
  160. };
  161. if (this.data && this.data.source)
  162. this.source = data.source;
  163. this.wrapper = undefined;
  164. this.data = data;
  165. }
  166. static get toolbox() {
  167. return {
  168. title: 'Embed',
  169. icon: '<svg width="19" height="13" viewBox="0 0 19 13"><path d="M18.004 5.794c.24.422.18.968-.18 1.328l-4.943 4.943a1.105 1.105 0 1 1-1.562-1.562l4.162-4.162-4.103-4.103A1.125 1.125 0 1 1 12.97.648l4.796 4.796c.104.104.184.223.239.35zm-15.142.547l4.162 4.162a1.105 1.105 0 1 1-1.562 1.562L.519 7.122c-.36-.36-.42-.906-.18-1.328a1.13 1.13 0 0 1 .239-.35L5.374.647a1.125 1.125 0 0 1 1.591 1.591L2.862 6.341z"></path></svg>'
  170. };
  171. }
  172. render() {
  173. this.wrapper = document.createElement('p');
  174. if (this.data && this.data.source){
  175. this._createEmbed(this.data.source);
  176. return this.wrapper;
  177. }
  178. else{
  179. this._createInput();
  180. }
  181. return this.wrapper;
  182. }
  183. _createEmbed(sLink){
  184. var oObj = document.createElement('div');
  185. oObj.setAttribute('source', sLink)
  186. oObj.className = 'bx-embed-link';
  187. this.wrapper.removeChild(this.wrapper.querySelector('input'));
  188. this.wrapper.appendChild(oObj);
  189. this.blockAPI.dispatchChange();
  190. }
  191. _createInput(){
  192. var oObj = document.createElement('input');
  193. oObj.className = 'bx-def-font-inputs bx-form-input-text';
  194. oObj.setAttribute('placeholder', _t('_bx_editor_embed_popup_header'));
  195. oObj.addEventListener('blur', (event) => {
  196. if (oObj.value != ''){
  197. this._createEmbed(oObj.value);
  198. }
  199. });
  200. this.wrapper.appendChild(oObj);
  201. }
  202. save(blockContent){
  203. const div = blockContent.querySelector('div');
  204. if (div){
  205. return {
  206. source: div.getAttribute('source')
  207. }
  208. }
  209. }
  210. static get pasteConfig() {
  211. return {
  212. tags: ['DIV'],
  213. }
  214. }
  215. onPaste(event) {
  216. if (event.detail.data.attributes.source){
  217. var sSource = event.detail.data.attributes.source.nodeValue;
  218. this._createEmbed(sSource);
  219. this.data.source = sSource;
  220. }
  221. }
  222. static get sanitize(){
  223. return {
  224. url: true, // disallow HTML
  225. }
  226. }
  227. validate(savedData) {
  228. return true;
  229. }
  230. }
  231. class BxMention {
  232. static get CSS() {
  233. return 'bx-mention-link';
  234. };
  235. constructor({api}) {
  236. this.api = api;
  237. this.button = null;
  238. this.tag = 'a';
  239. this.iconClasses = {
  240. base: this.api.styles.inlineToolButton,
  241. active: this.api.styles.inlineToolButtonActive
  242. };
  243. }
  244. static get isInline() {
  245. return true;
  246. }
  247. render() {
  248. return;
  249. }
  250. surround(range) {
  251. if (!range) {
  252. return;
  253. }
  254. let termWrapper = this.api.selection.findParentTag(this.tag, BxMention.CSS);
  255. if (termWrapper) {
  256. this.unwrap(termWrapper);
  257. } else {
  258. this.wrap(range);
  259. }
  260. }
  261. wrap(range) {
  262. let marker = document.createElement(this.tag);
  263. marker.classList.add(BxMention.CSS);
  264. marker.appendChild(range.extractContents());
  265. range.insertNode(marker);
  266. this.api.selection.expandToTag(marker);
  267. }
  268. unwrap(termWrapper) {
  269. this.api.selection.expandToTag(termWrapper);
  270. let sel = window.getSelection();
  271. let range = sel.getRangeAt(0);
  272. let unwrappedContent = range.extractContents();
  273. termWrapper.parentNode.removeChild(termWrapper);
  274. range.insertNode(unwrappedContent);
  275. sel.removeAllRanges();
  276. sel.addRange(range);
  277. }
  278. checkState() {
  279. const termTag = this.api.selection.findParentTag(this.tag, BxMention.CSS);
  280. this.button.classList.toggle(this.iconClasses.active, !!termTag);
  281. }
  282. get toolboxIcon() {
  283. return require('./../assets/icon.svg').default;
  284. }
  285. static get sanitize() {
  286. return {
  287. a: function(el) {
  288. return {
  289. class: BxMention.CSS,
  290. href: el.getAttribute('href'),
  291. 'data-profile-id': el.getAttribute('data-profile-id'),
  292. title: el.getAttribute('title'),
  293. dchar: el.getAttribute('dchar')
  294. }
  295. }
  296. };
  297. }
  298. }
  299. function bx_editor_on_space_enter (sCode, sEditorId, bSpace = true)
  300. {
  301. if (typeof glBxEditorOnSpaceEnterTimer !== 'undefined')
  302. clearTimeout(glBxEditorOnSpaceEnterTimer);
  303. if (bSpace)
  304. glBxEditorOnSpaceEnterTimer = setTimeout(bx_editor_on_space_enter_in, 500, sCode, sEditorId);
  305. else
  306. bx_editor_on_space_enter_in(sCode, sEditorId);
  307. }
  308. function bx_editor_on_space_enter_in(sCode, sEditorId) {
  309. glBxEditorOnSpaceEnterTimer = undefined;
  310. if (typeof glOnSpaceEnterInEditor !== 'undefined' && glOnSpaceEnterInEditor instanceof Array) {
  311. for (var i = 0; i < glOnSpaceEnterInEditor.length; i++) {
  312. if (typeof glOnSpaceEnterInEditor[i] === "function") {;
  313. glOnSpaceEnterInEditor[i](sCode, sEditorId);
  314. }
  315. }
  316. }
  317. }
  318. function bx_editor_remove_img (aEditorIds, aMarkers)
  319. {
  320. for (var i = 0; i < aEditorIds.length; i++) {
  321. var eEditor = $('#' + $('#' + aEditorIds[i]).attr('object_editor'));
  322. // delete images in html editor
  323. for (var k = 0; k < aMarkers.length; k++) {
  324. var jFiles = eEditor.find(aMarkers[k]);
  325. jFiles.each(function () {
  326. $(this).remove();
  327. });
  328. }
  329. }
  330. }