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