nota_red toolbar button
[redakcja.git] / redakcja / static / js / button_scripts.js
1 (function() {
2   var slice = Array.prototype.slice;
3
4   function update(array, args) {
5     var arrayLength = array.length, length = args.length;
6     while (length--) array[arrayLength + length] = args[length];
7     return array;
8   };
9
10   function merge(array, args) {
11     array = slice.call(array, 0);
12     return update(array, args);
13   };
14
15   Function.prototype.bind = function(context) {
16     if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
17       return this;
18     }
19     var __method = this;
20     var args = slice.call(arguments, 1);
21     return function() {
22       var a = merge(args, arguments);
23       return __method.apply(context, a);
24     }
25   }
26
27 })();
28
29 function nblck_each(array, body, after) {
30         $.each(array, function(i) {
31                 body(this, i);
32         });
33
34         after();
35 };
36
37 function nblck_map(array, func, after) {
38         var acc = [];
39
40         nblck_each(array, function(elem, index) {
41                 acc.push(func(elem, index));
42         }, function(){
43                 after(acc);
44         });
45 };
46
47 function ScriptletCenter()
48 {
49     this.scriptlets = {};
50
51     this.scriptlets['insert_tag'] = function(context, params, text, move_forward, move_up, done)
52     {
53         var padding_top = '';
54         for (var i=params.padding_top; i; i--)
55             padding_top += '\n';
56
57         var start_tag = '<'+params.tag;
58         var cursor_inside = false;
59
60         for (var attr in params.attrs) {
61             start_tag += ' '+attr+'="' + params.attrs[attr] + '"';
62         }
63
64         start_tag += '>';
65         var end_tag = '</'+params.tag+'>';
66
67         var padding_bottom = '';
68         for (var i=params.padding_bottom; i; i--)
69             padding_bottom += '\n';
70
71         if(text.length > 0) {
72             // tokenize
73             var output = '';
74             var token = '';
75             for(var index=0; index < text.length; index++)
76             {
77                 if (text[index].match(/\s/)) { // whitespace
78                     token += text[index];
79                 }
80                 else { // character
81                     output += token;
82                     if(output == token) output += padding_top + start_tag;
83                     token = '';
84                     output += text[index];
85                 }
86             }
87
88             if( output[output.length-1] == '\\' ) {
89                 output = output.substr(0, output.length-1) + end_tag + padding_bottom + '\\';
90             } else {
91                 output += end_tag + padding_bottom;
92             }
93             output += token;
94
95             // keep cursor inside tag if some previous scriptlet has already moved it
96             cursor_inside = move_forward != 0 || move_up != 0;
97         }
98         else {
99             if(params.nocontent) {
100                 output = padding_top + "<"+params.tag +" />" + padding_bottom;
101             }
102             else {
103                 output = padding_top + start_tag + end_tag + padding_bottom;
104                 cursor_inside = true;
105             }
106         }
107
108         if (cursor_inside) {
109             move_forward -= params.tag.length + 3;
110             move_up += params.padding_bottom || 0;
111         }
112
113         done(output, move_forward, move_up);
114     }.bind(this);
115
116     this.scriptlets['lineregexp'] = function(context, params, text, move_forward, move_up, done) {
117                 var self = this;
118
119         var exprs = $.map(params.exprs, function(expr) {
120             var opts = "g";
121             if(expr.length > 2) {
122                 opts = expr[2];
123             } return {
124                 rx: new RegExp(expr[0], opts),
125                 repl: expr[1]
126                 };
127         });
128
129         if(!text) done(text, move_forward, move_up);
130
131         var changed = 0;
132         var lines = text.split('\n');
133
134                 nblck_map(lines, function(line, index) {
135             var old_line = line;
136             $(exprs).each(function() {
137                 var expr = this;
138                 line = line.replace(expr.rx, expr.repl);
139             });
140
141                         $progress.html(index);
142
143             if(old_line != line) changed += 1;
144             return line;
145         }, function(newlines) {
146             if(changed > 0) {
147                 text = newlines.join('\n');
148             };
149
150             done(text, move_forward, move_up);
151                 });
152     }.bind(this);
153
154     this.scriptlets['fulltextregexp'] = function(context, params, text, move_forward, move_up, done) {
155                 var self = this;
156
157         var exprs = $.map(params.exprs, function(expr) {
158             var opts = "mg";
159             if(expr.length > 2) {
160                 opts = expr[2];
161             }
162             return {
163                 rx: new RegExp(expr[0], opts),
164                 repl: expr[1]
165                 };
166         });
167
168         if(!text) done(text, move_forward, move_up);
169
170                 nblck_each(exprs, function(expr, index) {
171                         $progress.html(600 + index);
172             text = text.replace(expr.rx, expr.repl);
173         }, function() {
174                         done(text, move_forward, move_up);
175                 });
176     }.bind(this);
177
178     this.scriptlets['macro'] = function(context, params, text, move_forward, move_up, done) {
179         var self = this;
180                 var i = 0;
181
182                 function next(text, move_forward, move_up) {
183                 if (i < params.length) {
184                                 var e = params[i];
185                                 i = i + 1;
186                                 self.scriptlets[e[0]](context, e[1], text, move_forward, move_up, next);
187                         }
188                         else {
189                                 done(text, move_forward, move_up);
190                         }
191         };
192
193                 next(text, move_forward, move_up);
194     }.bind(this);
195
196     this.scriptlets['lowercase'] = function(context, params, text, move_forward, move_up, done)
197     {
198         if(!text) done(text, move_forward, move_up);
199         done(text.toLowerCase(), move_forward, move_up);
200     }.bind(this);
201
202
203     this.scriptlets["insert_stanza"] = function(context, params, text, move_forward, move_up, done) {
204         var padding_top = '';
205         for (var i=params.padding_top; i; i--)
206             padding_top += '\n';
207
208         var padding_bottom = '';
209         for (var i=params.padding_bottom; i; i--)
210             padding_bottom += '\n';
211
212         if(text) {
213             var verses = text.split('\n');
214             text = ''; var buf = ''; var ebuf = '';
215             var first = true;
216
217             for(var i=0;  i < verses.length; i++) {
218                 var verse = verses[i].replace(/^\s+/, "").replace(/\s+$/, "");
219                 if(verse) {
220                     text += (buf ? buf + '/\n' : '') + ebuf;
221                     buf = (first ? '<strofa>\n' : '') + verses[i];
222                     ebuf = '';
223                     first = false;
224                 } else {
225                     ebuf += '\n' + verses[i];
226                 }
227             }
228             text = padding_top + text + buf + '\n</strofa>' + padding_bottom + ebuf;
229         }
230         else {
231             text = padding_top + "<strofa></strofa>" + padding_bottom;
232             move_forward -= "</strofa>".length;
233             move_up += params.padding_bottom || 0;
234         }
235
236         done(text, move_forward, move_up);
237     }.bind(this);
238
239
240     this.scriptlets['autotag'] = function(context, params, text, move_forward, move_up, done)
241     {
242         if(!text.match(/^\n+$/)) done(text, move_forward, move_up);
243
244         function insert_done(output, mf) {
245             text += output;
246         }
247
248         if (!params.split) params.split = 2;
249         if (!params.padding) params.padding = 3;
250
251         chunks = text.replace(/^\n+|\n+$/, '').split(new RegExp("\\n{"+params.split+",}"));
252         text = text.match(/^\n+/);
253         if (!text)
254             text = '';
255         padding = '';
256         for(; params.padding; params.padding--) {
257             padding += "\n";
258         }
259
260         if (params.tag == 'strofa')
261             tagger = this.scriptlets['insert_stanza'];
262         else
263             tagger = this.scriptlets['insert_tag'];
264
265         for (i in chunks) {
266             if (chunks[i]) {
267                 if (params.tag == 'akap' && chunks[i].match(/^---/))
268                     tag = 'akap_dialog';
269                 else tag = params.tag;
270                 tagger(context, {tag: tag}, chunks[i], 0, insert_done);
271                 text += padding;
272             }
273         }
274
275         done(text, move_forward, move_up);
276     }.bind(this);
277
278 }
279
280 ScriptletCenter.prototype.callInteractive = function(opts) {
281         $progress = $('<span>Executing script</span>');
282         var self = this;
283
284         /* This won't work, 'cause the JS below might be synchronous :( */
285         /* var timer = setTimeout(function() {
286             $.blockUI({message: $progress});
287             timer = null;
288         }, 1000); */
289
290         $.blockUI({message: $progress, showOverlay: false});
291
292     var input = self.XMLEditorSelectedText(opts.context);
293         self.scriptlets[opts.action](opts.context, opts.extra, input, 0, 0, function(output, move_forward, move_up){
294             /*if(timer)
295                 clearTimeout(timer);
296             else */
297         if (input != output) {
298             self.XMLEditorReplaceSelectedText(opts.context, output)
299         }
300         if (move_forward || move_up) {
301             try {
302                 self.XMLEditorMoveCursorForward(opts.context, move_forward, move_up)
303             }
304             catch(e) {}
305         }
306             $.unblockUI(); // done
307         });
308 }
309
310 ScriptletCenter.prototype.XMLEditorSelectedText = function(editor) {
311
312     return editor.selection();
313 };
314
315 ScriptletCenter.prototype.XMLEditorReplaceSelectedText = function(editor, replacement)
316 {
317         $progress.html("Replacing text");
318         editor.replaceSelection(replacement);
319 };
320
321 ScriptletCenter.prototype.XMLEditorMoveCursorForward = function(panel, right, up) {
322     var pos = panel.cursorPosition();
323     if (up) {
324         line = pos.line;
325         while (up < 0) {
326             line = panel.nextLine(line);
327             ++up;
328         }
329         while (up > 0) {
330             line = panel.prevLine(line);
331             --up;
332         }
333         len = panel.lineContent(line).length;
334         panel.selectLines(line, len + right);
335     }
336     else {
337         panel.selectLines(pos.line, pos.character + right);
338     }
339 };
340
341 var scriptletCenter;
342
343 $(function() {
344     scriptletCenter = new ScriptletCenter();
345 });