github.com/bitcubate/cryptojournal@v1.2.5-0.20171102134152-f578b3d788ab/src/lib/editable/assets/scripts/editable.js (about) 1 // Package Editable provides an active toolbar for content-editable textareas 2 // Version 1.0 3 4 // TODO - show formatting selected on toolbar when selection changes in our contenteditable 5 // TODO - perhaps intercept return key to be sure we get a para, and to be sure we insert newline within code sections, not new code tags + br or similar 6 // TODO - Clean out more HTML cruft from programs like word/textedit 7 8 // On document ready, scan for and activate toolbars associated with contenteditable 9 DOM.Ready(function() { 10 if (!DOM.Exists('.content-editable-toolbar')) { 11 return 12 } 13 14 // Activate editable content 15 Editable.Activate('.content-editable-toolbar'); 16 }); 17 18 var Editable = (function() { 19 return { 20 // Activate editable elements with selector s 21 Activate: function(s) { 22 if (!DOM.Exists(s)) { 23 return; 24 } 25 26 DOM.Each('.content-editable-toolbar', function(toolbar) { 27 // Store associated elements for access later 28 toolbar.buttons = toolbar.querySelectorAll('a'); 29 var dataEditable = toolbar.getAttribute('data-editable'); 30 toolbar.editable = DOM.First(DOM.Format("#{0}-editable", dataEditable)); 31 toolbar.textarea = DOM.First(DOM.Format("#{0}-textarea", dataEditable)); 32 33 if (toolbar.editable === undefined) { 34 toolbar.editable = DOM.Nearest(toolbar, '.content-editable')[0]; 35 } 36 if (toolbar.textarea === undefined) { 37 toolbar.textarea = DOM.Nearest(toolbar, '.content-textarea')[0]; 38 } 39 40 // Set textarea to hidden initially 41 toolbar.textarea.style.display = 'none'; 42 43 // Listen to a form submit, and call updateContent to make sure 44 // our textarea in the form is up to date with the latest content 45 toolbar.textarea.form.addEventListener('submit', function(e) { 46 Editable.updateContent(toolbar.editable, toolbar.textarea, false); 47 return false; 48 }); 49 50 // Intercept paste on editable and remove complex html before it is pasted in 51 toolbar.editable.addEventListener('input', function(e) { 52 Editable.cleanHTMLElements(this); 53 }); 54 55 56 // Listen to button clicks within the toolbar 57 DOM.ForEach(toolbar.buttons, function(el, i) { 58 el.addEventListener('click', function(e) { 59 var cmd = this.id; 60 var insert = ""; 61 62 switch (cmd) { 63 case "showCode": 64 Editable.updateContent(toolbar.editable, toolbar.textarea, true); 65 break; 66 case "createLink": 67 insert = prompt("Supply the web URL to link to"); 68 // Prefix url with http:// if no scheme supplied 69 if (insert.indexOf('http') !== 0) { 70 insert = "http://" + insert; 71 } 72 break; 73 case "formatblock": 74 insert = this.getAttribute('data-format'); 75 break; 76 default: 77 break; 78 } 79 80 if (cmd.length > 0) { 81 document.execCommand(cmd, false, insert); 82 } 83 84 // Find and remove evil html created by browsers 85 var sel = Editable.getSelectionParentElement(); 86 87 if (sel !== null) { 88 // Clean align stuff 89 Editable.cleanAlign(cmd, sel); 90 Editable.cleanHTMLElements(sel); 91 sel.removeAttribute('style'); 92 } 93 94 e.preventDefault(); 95 return false; 96 }); 97 }); 98 }); 99 }, // End activate 100 101 // cleanAlign 102 cleanAlign: function(cmd, el) { 103 104 switch (cmd) { 105 case "justifyCenter": 106 107 if (sel.hasClass('align-center')) { 108 sel.removeClass('align-center'); 109 } else { 110 sel.addClass('align-center'); 111 } 112 113 sel.removeClass('align-left').removeClass('align-right'); 114 sel.removeAttr('style'); 115 break; 116 case "justifyLeft": 117 if (sel.hasClass('align-left')) { 118 sel.removeClass('align-left'); 119 } else { 120 sel.addClass('align-left'); 121 } 122 123 sel.removeClass('align-center').removeClass('align-right'); 124 sel.removeAttribute('style'); 125 126 127 break; 128 case "justifyRight": 129 130 if (sel.hasClass('align-right')) { 131 sel.removeClass('align-right'); 132 } else { 133 sel.addClass('align-right'); 134 } 135 136 sel.removeClass('align-center').removeClass('align-left'); 137 sel.removeAttribute('style'); 138 break; 139 } 140 }, 141 142 143 144 // CleanHTML is used to clean the html content from the contenteditable before it is assigned to the textarea 145 cleanHTML: function(html) { 146 html = html.replace(/<\/?span>/gi, ''); // Remove all empty span tags 147 html = html.replace(/<\/?font [^>]*>/gi, ''); // Remove ALL font tags 148 html = html.replace(/ </gi, ' <'); // this is sometimes required, be careful 149 html = html.replace(/<p><\/p>/gi, '\n'); // pretty format but remove empty paras 150 html = html.replace(/<br><\/li>/gi, '<\/li>'); 151 152 // Remove comments and other MS cruft 153 html = html.replace(/<!--[\w\d\[\]\s<\/>:.!="*{};-]*-->/gi, ''); 154 html = html.replace(/ class\=\"MsoNormal\"/gi, ''); 155 html = html.replace(/<p><o:p> <\/o:p><\/p>/gi, ''); 156 157 // Pretty printing elements which follow on from one another 158 html = html.replace(/><(li|ul|ol|p|h\d|\/ul|\/ol)>/gi, '>\n<$1>'); 159 160 return html; 161 }, 162 163 // cleanHTMLElements removes certain attributes which are usually full of junk (style, color etc) 164 cleanHTMLElements: function(el) { 165 // Browsers tend to use style attributes to add all sorts of awful stuff to the html 166 // No inline styles allowed 167 DOM.ForEach(el.querySelectorAll('p, div, b, i, h1, h2, h3, h4, h5, h6'), function(e) { e.removeAttribute('style'); }); 168 DOM.ForEach(el.querySelectorAll('span'), function(e) { 169 e.removeAttribute('style'); 170 e.removeAttribute('lang'); 171 }); 172 DOM.ForEach(el.querySelectorAll('font'), function(e) { e.removeAttribute('color'); }); 173 }, 174 175 // If textarea visible update the content editable with new html 176 // else update the textarea with new content editable html 177 updateContent: function(editable, textarea, toggle) { 178 var html = ''; 179 if (textarea.style.display !== 'none') { 180 html = textarea.value; 181 editable.innerHTML = html; 182 if (toggle) { 183 editable.style.display = ''; 184 textarea.style.display = 'none'; 185 } 186 } else { 187 html = editable.innerHTML; 188 // Cleanup the html by removing plain spans 189 html = Editable.cleanHTML(html); 190 textarea.value = html; 191 if (toggle) { 192 editable.style.display = 'none'; 193 textarea.style.display = ''; 194 } 195 } 196 197 }, 198 199 // The closest parent element which encloses the entire text selection 200 getSelectionParentElement: function() { 201 var p = null, 202 sel; 203 if (window.getSelection) { 204 sel = window.getSelection(); 205 if (sel.rangeCount) { 206 p = sel.getRangeAt(0).commonAncestorContainer; 207 if (p.nodeType != 1) { 208 p = p.parentNode; 209 } 210 } 211 } else if ((sel = document.selection) && sel.type != "Control") { 212 p = sel.createRange().parentElement(); 213 } 214 return p; 215 } 216 217 }; 218 }());