1/** 2 * DokuWiki Plugin copycode (Action Component) 3 * 4 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 5 * @author Nicolas Prigent <mail.nicolasprigent@gmail.com> 6 * 7 * Adds a click event on all code blocks that copy the content of the block to clipboard 8 * 9 */ 10 11 12class SourceCodeProvider { 13 14 constructor(source) { 15 if (this.constructor === SourceCodeProvider) { 16 throw new TypeError("Abstract class must be subclassed."); 17 } 18 this._source = source; 19 } 20 21 get_source_code() { 22 throw new TypeError("Not implemented."); 23 } 24} 25 26 27class SelectionProvider extends SourceCodeProvider { 28 29 get_source_code() { 30 return this._source.getSelection().toString(); 31 } 32} 33 34class BlockProvider extends SourceCodeProvider { 35 36 constructor(source, inline=false) { 37 super(source); 38 this._inline = inline; 39 } 40 41 get_source_code() { 42 let result = this._source.textContent.split("_||copycode||_").join("\n"); 43 if (this._inline) { 44 result = result.split("\n").join(" "); 45 } 46 return result; 47 } 48} 49 50 51class CopyCodeStrategy { 52 53 constructor() { 54 if (this.constructor === CopyCodeStrategy) { 55 throw new TypeError("Abstract class must be subclassed."); 56 } 57 } 58 59 get_message() { 60 throw new TypeError("Not implemented."); 61 } 62 63 copy() { 64 throw new TypeError("Not implemented."); 65 } 66} 67 68 69class DummyCopyStrategy { 70 71 get_message() { 72 return ""; 73 } 74 75 copy() { 76 // dummy doens't da anything. 77 } 78} 79 80 81class CopyCodeStrategyBase { 82 83 constructor(source) { 84 if (this.constructor === CopyCodeStrategyBase) { 85 throw new TypeError("Abstract class must be subclassed."); 86 } 87 this._source = source; 88 this._provider = null; 89 } 90 91 get_message() { 92 return '<div class="' + this._alert_class + ' alert-copycode">' + this._message + "</div>"; 93 } 94 95 copy() { 96 if (this._provider === null) { 97 throw new TypeError("No source-code provider available."); 98 } 99 let inputValue = this._provider.get_source_code(); 100 // replacing problematic white space character that appears for no obvious reason :/ 101 inputValue = inputValue.split(/\u00A0/).join(""); 102 if (inputValue !== "") { 103 // check if clipboard is available in navigator 104 if (navigator.clipboard != undefined) { 105 //Copy raw text to clipboard 106 navigator.clipboard.writeText(inputValue); 107 } else { 108 // if for any reason the clipboard is unavalaible, uses the fake textarea hack to copy the content 109 let textarea = document.createElement("textarea"); 110 textarea.value = inputValue; 111 textarea.style = "height: 1px; width : 1px"; 112 document.body.appendChild(textarea); 113 textarea.select(); 114 document.execCommand("copy"); 115 textarea.remove(); 116 } 117 } 118 } 119} 120 121 122class CopyHighlighted extends CopyCodeStrategyBase { 123 124 constructor(source) { 125 super(source); 126 this._message = LANG.plugins.copycode["highlightedcopied"]; 127 this._alert_class = "orange"; 128 this._provider = new SelectionProvider(this._source) 129 } 130} 131 132 133class CopyBlock extends CopyCodeStrategyBase { 134 135 constructor(source) { 136 super(source); 137 this._message = LANG.plugins.copycode["copied"]; 138 this._alert_class = "green"; 139 this._provider = new BlockProvider(this._source) 140 } 141} 142 143 144class CopyBlockInline extends CopyBlock { 145 146 constructor(source) { 147 super(source); 148 this._message = LANG.plugins.copycode["copiedinline"]; 149 this._alert_class = "blue"; 150 this._provider = new BlockProvider(this._source, true) 151 } 152} 153 154 155jQuery(document).ready(function ($) { 156 157 //detects mouseup after scroll 158 var scrolling = false; 159 function preventClickOnScroll () { 160 $(window).mouseup(function(){ 161 scrolling = false; 162 }); 163 } 164 165 function alertMessage(message) { 166 $("body").append(message); 167 168 window.setTimeout(function () { 169 $(".alert-copycode") 170 .fadeTo(500, 0) 171 .slideUp(500, function () { 172 $(this).remove(); 173 }); 174 }, 1000); 175 } 176 177 //enabled <code> and <file> blocks on all wiki pages and hooks(sidebar,header,mainpage,dropdownpages etc.) 178 var blocs = $(".dokuwiki pre.code, .dokuwiki pre.file"); 179 //enabled inlinecodes ''like this'' 180 if (JSINFO.plugins.copycode.EnableForInline) 181 blocs = blocs.add(".dokuwiki code"); 182 blocs.addClass("enabled-copycode"); 183 184 185 for (i = 0; i < blocs.length; i++) { 186 if (JSINFO.plugins.copycode.EnableBlockInline) { 187 //deactivate context menu on right click 188 $(blocs[i]).on("contextmenu", function (evt) { 189 if (window.getSelection().toString() == "") { 190 evt.preventDefault(); 191 } 192 }); 193 } 194 195 // detects scrolling on element 196 $(blocs[i]).scroll(function() { 197 scrolling = true; 198 preventClickOnScroll(); 199 }); 200 201 $(blocs[i]).mouseup(function (event) { 202 203 if (!scrolling){ 204 let strategy = new DummyCopyStrategy(); 205 if (JSINFO.plugins.copycode.EnableForHighlighted) { 206 strategy = new CopyHighlighted(window); 207 } 208 if (window.getSelection().toString() === "") { 209 strategy = new CopyBlock(this); 210 if (event.which === 3) { 211 strategy = new DummyCopyStrategy(); 212 if (JSINFO.plugins.copycode.EnableBlockInline) { 213 strategy = new CopyBlockInline(this); 214 } 215 } 216 } 217 strategy.copy(); 218 alertMessage(strategy.get_message()); 219 } 220 }); 221 222 line = $(blocs[i]) 223 .find("ol > li") 224 .append('<span class="copycode_line">_||copycode||_</span>'); 225 } 226}); 227