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