Poprawienie serializacji
[redakcja.git] / platforma / static / js / main.js
1 var MARGIN = {
2     dramat_wierszowany_l: 4,
3     dramat_wierszowany_lp: 4,
4     dramat_wspolczesny: 4,
5     wywiad: 4,
6     opowiadanie: 4,
7     powiesc: 4,
8     liryka_l: 4,
9     liryka_lp: 4,
10     naglowek_czesc: 4,
11     naglowek_akt: 4,
12     naglowek_rozdzial: 4,
13     naglowek_osoba: 4,
14     lista_osob: 4,
15     
16     akap: 3,
17     akap_cd: 3,
18     akap_dialog: 3,
19     strofa: 3,
20     motto: 3, 
21     miejsce_czas: 3,
22         
23     autor_utworu: 2,
24     nazwa_utworu: 2,
25     dzielo_nadrzedne: 2,
26     didaskalia: 2,
27     motto_podpis: 2,
28     naglowek_listy: 2,
29     
30     kwestia: 1,
31     lista_osoba: 1
32 }
33
34 MARGIN['rdf:RDF'] = 3;
35 MARGIN['rdf:Description'] = 2;
36
37 function elementType(element) {
38     if ($.inArray(element.tagName, ['akap', 'akap_cd', 'akap_dialog', 'strofa', 'didaskalia', 'wers', 'wers_cd', 'wers_akap', 'wers_wciety', 'autor_utworu', 'nazwa_utworu', 'dzielo_nadrzedne', 'podpis'])) {
39         return 'inline';
40     } else {
41         return 'block';
42     }
43 }
44 // Serializuje XML, wstawiając odpowiednie ilości białych znaków między elementami
45 function serialize(element, mode) {
46     if (!mode) {
47         mode = 'block';
48     }
49     
50     if (element.nodeType == 3) { // tekst
51         if (mode == 'block') {
52             return [$.trim(element.nodeValue)];
53         } else {
54             return [element.nodeValue];
55         }
56     } else if (element.nodeType != 1) { // pomijamy węzły nie będące elementami XML ani tekstem
57         return [];
58     }
59     
60     var result = [];
61     var hasContent = false;
62     
63     if (MARGIN[element.tagName]) {
64         for (var i=0; i < MARGIN[element.tagName]; i++) {
65             result.push('\n');
66         };
67     } else if (element.tagName.indexOf('dc:') != -1) {
68         result.push('\n');
69     }
70     
71     result.push('<');
72     result.push(element.tagName);
73     
74     // Mozilla nie uważa deklaracji namespace za atrybuty
75     var ns = element.tagName.indexOf(':');
76     if (ns != -1 && $.browser.mozilla) {
77         result.push(' xmlns:');
78         result.push(element.tagName.substring(0, ns));
79         result.push('="');
80         result.push(element.namespaceURI);
81         result.push('"');
82     }
83     
84     if (element.attributes) {
85         for (var i=0; i < element.attributes.length; i++) {
86             var attr = element.attributes[i];
87             result.push(' ');
88             result.push(attr.name);
89             result.push('="');
90             result.push(attr.value);
91             result.push('"');
92             hasContent = true;
93         }
94     }
95     
96     if (element.childNodes.length == 0) {
97         result.push(' />');
98     } else {
99         result.push('>');
100
101         for (var i=0; i < element.childNodes.length; i++) {
102             result = result.concat(serialize(element.childNodes[i], 
103                 mode == 'inline' ? 'inline' : elementType(element.childNodes[i])));
104         }
105
106         result.push('</');
107         result.push(element.tagName);
108         result.push('>');
109     }
110     
111     return result;
112 };
113
114
115 // Teraz nieużywane
116 function highlight(colour) {
117     var range, sel;
118     if (window.getSelection) {
119         // Non-IE case
120         sel = window.getSelection();
121         if (sel.getRangeAt) {
122             range = sel.getRangeAt(0);
123         }
124         document.designMode = "on";
125         if (range) {
126             sel.removeAllRanges();
127             sel.addRange(range);
128         }
129         // Use HiliteColor since some browsers apply BackColor to the whole block
130         if ( !document.execCommand("HiliteColor", false, colour) ) {
131             document.execCommand("BackColor", false, colour);
132         }
133         document.designMode = "off";
134     } else if (document.selection && document.selection.createRange) {
135         // IE case
136         range = document.selection.createRange();
137         range.execCommand("BackColor", false, colour);
138     }
139 }
140
141 // function unselectThemes(themeId) {
142 //     $('.Apple-style-span').each(function() {
143 //         $(this).after($(this).html());
144 //         $(this).remove();
145 //     });
146 // }
147
148 function gallery(element) {
149     var element = $(element);
150     var imageDimensions = {};
151     
152     function changePage(pageNumber) {        
153         $('img', element).attr('src', element.data('images')[pageNumber - 1]);
154     }
155     
156     function normalizeNumber(pageNumber) {
157         // Numer strony musi być pomiędzy 1 a najwyższym numerem
158         var pageCount = element.data('images').length;
159         pageNumber = parseInt(pageNumber, 10);
160         
161         if (!pageNumber || pageNumber == NaN || pageNumber == Infinity || pageNumber == -Infinity) {
162             return 1;
163         } else if (pageNumber < 1) {
164             return 1;
165         } else if (pageNumber > pageCount) {
166             return pageCount;
167         } else {
168             return pageNumber;
169         }
170     }
171     
172     $.ajax({
173         url: '/gallery/sample',
174         type: 'GET',
175         dataType: 'json',
176     
177         success: function(data) {
178             element.data('images', data);
179             var pn = $('.page-number', element);
180             pn.change(function(event) {
181                 console.log('change!', $(this).val());
182                 event.preventDefault();
183                 var n = normalizeNumber(pn.val());
184                 pn.val(n);
185                 changePage(n);
186             });
187             $('.previous-page', element).click(function() {
188                 pn.val(normalizeNumber(pn.val()) - 1);
189                 pn.change();
190             });
191             $('.next-page', element).click(function() {
192                 pn.val(normalizeNumber(pn.val()) + 1);
193                 pn.change();
194             });
195             
196             
197             var image = $('img', element).attr('unselectable', 'on');
198             var origin = {};
199             var imageOrigin = {};
200             var zoomFactor = 1;
201             
202             $('.zoom-in', element).click(function() {
203                 zoomFactor = Math.min(2, zoomFactor + 0.2);
204                 zoom();
205             });
206             $('.zoom-out', element).click(function() {
207                 zoomFactor = Math.max(0.2, zoomFactor - 0.2);
208                 zoom();
209             });
210             
211             $('img', element).load(function() {
212                 image.css({width: null, height: null});
213                 imageDimensions = {
214                     width: $(this).width() * zoomFactor,
215                     height: $(this).height() * zoomFactor,
216                     originWidth: $(this).width(),
217                     originHeight: $(this).height(),
218                     galleryWidth: $(this).parent().width(),
219                     galleryHeight: $(this).parent().height()
220                 };
221                 console.log('load', imageDimensions)
222                 var position = normalizePosition(
223                     image.position().left,
224                     image.position().top, 
225                     imageDimensions.galleryWidth,
226                     imageDimensions.galleryHeight,
227                     imageDimensions.width,
228                     imageDimensions.height
229                 );
230                 image.css({left: position.x, top: position.y, width: $(this).width() * zoomFactor, height: $(this).height() * zoomFactor});
231             });
232
233             $(window).resize(function() {
234                 imageDimensions.galleryWidth = image.parent().width();
235                 imageDimensions.galleryHeight = image.parent().height();
236             });
237             
238             function bounds(galleryWidth, galleryHeight, imageWidth, imageHeight) {
239                 return {
240                     maxX: 0,
241                     maxY: 0,
242                     minX: galleryWidth - imageWidth,
243                     minY: galleryHeight - imageHeight
244                 }
245             }
246             
247             function normalizePosition(x, y, galleryWidth, galleryHeight, imageWidth, imageHeight) {
248                 var b = bounds(galleryWidth, galleryHeight, imageWidth, imageHeight);
249                 return {
250                     x: Math.min(b.maxX, Math.max(b.minX, x)),
251                     y: Math.min(b.maxY, Math.max(b.minY, y))
252                 }
253             }
254             
255             function onMouseMove(event) {
256                 var position = normalizePosition(
257                     event.clientX - origin.x + imageOrigin.left,
258                     event.clientY - origin.y + imageOrigin.top, 
259                     imageDimensions.galleryWidth,
260                     imageDimensions.galleryHeight,
261                     imageDimensions.width,
262                     imageDimensions.height
263                 );
264                 image.css({position: 'absolute', top: position.y, left: position.x});
265                 return false;
266             }
267             
268             function setZoom(factor) {
269                 zoomFactor = factor;
270             }
271             
272             function zoom() {
273                 imageDimensions.width = imageDimensions.originWidth * zoomFactor;
274                 imageDimensions.height = imageDimensions.originHeight * zoomFactor;
275                 var position = normalizePosition(
276                     image.position().left,
277                     image.position().top, 
278                     imageDimensions.galleryWidth,
279                     imageDimensions.galleryHeight,
280                     imageDimensions.width,
281                     imageDimensions.height
282                 );
283                 console.log(image.position(), imageDimensions, position);
284                 image.css({width: imageDimensions.width, height: imageDimensions.height,
285                     left: position.x, top: position.y});
286
287             }
288             
289             window.setZoom = setZoom;
290             
291             function onMouseUp(event) {
292                 $(document)
293                     .unbind('mousemove.gallery')
294                     .unbind('mouseup.gallery');
295                 return false;
296             }
297             
298             image.bind('mousedown', function(event) {
299                 origin = {
300                     x: event.clientX,
301                     y: event.clientY
302                 };
303                 imageOrigin = image.position();
304                 $(document)
305                     .bind('mousemove.gallery', onMouseMove)
306                     .bind('mouseup.gallery', onMouseUp);
307                 return false;
308             });
309         }
310     });
311 }
312
313
314 function html(element) {
315     var element = $(element);
316     
317     function selectTheme(themeId)
318     {
319         var selection = window.getSelection();
320
321         // remove current selection
322         selection.removeAllRanges();
323
324         var range = document.createRange();
325         var s = $(".motyw[theme-class='"+themeId+"']")[0];
326         var e = $(".end[theme-class='"+themeId+"']")[0];
327
328         if(s && e) {
329             range.setStartAfter(s);
330             range.setEndBefore(e);
331             selection.addRange(range);
332         }
333     };
334     
335     // function openForEdit($origin)
336     // {       
337     //     // if(this.currentOpen && this.currentOpen != $origin) {
338     //     //     this.closeWithSave(this.currentOpen);
339     //     // }
340     //     
341     //     var $box = null
342     // 
343     //     // annotations overlay their sub box - not their own box //
344     //     if($origin.is(".annotation-inline-box"))
345     //         $box = $("*[x-annotation-box]", $origin);
346     //     else
347     //         $box = $origin;
348     //     
349     //     var x = $box[0].offsetLeft;
350     //     var y = $box[0].offsetTop;
351     //     var w = $box.outerWidth();
352     //     var h = $box.innerHeight();
353     // 
354     //     console.log("Edit origin:", $origin, " box:", $box);
355     //     console.log("offsetParent:", $box[0].offsetParent);
356     //     console.log("Dimensions: ", x, y, w , h);
357     // 
358     //     // start edition on this node
359     //     var $overlay = $('<div class="html-editarea"><textarea></textarea></div>');
360     // 
361     //     h = Math.max(h - 20, 2*parseInt($box.css('line-height')));
362     //     
363     //     console.log(h);
364     //     
365     //     $overlay.css({
366     //         position: 'absolute',
367     //         height: h,
368     //         left: x,
369     //         top: y,
370     //         right: 0
371     //     });
372     //     
373     //     $($box[0].offsetParent).append($overlay);
374     //     console.log($overlay);
375     // }
376     // 
377     // $('.edit-button').live('click', function() {
378     //     openForEdit($(this).parent());
379     // });
380     // 
381     var button = $('<button class="edit-button">Edytuj</button>');
382     $(element).bind('mousemove', function(event) {
383         var editable = $(event.target).closest('*[x-editable]');
384         $('.active[x-editable]', element).not(editable).removeClass('active').children('.edit-button').remove();
385         if (!editable.hasClass('active')) {
386             editable.addClass('active').append(button);
387         }
388     });
389
390     $('.motyw').live('click', function() {
391         selectTheme($(this).attr('theme-class'));
392     });
393 }
394
395
396 $(function() {
397     gallery('#sidebar');
398     html('#html-view');
399     
400     CodeMirror.fromTextArea('id_text', {
401         parserfile: 'parsexml.js',
402         path: "/static/js/lib/codemirror/",
403         stylesheet: "/static/css/xmlcolors.css",
404         parserConfig: {
405             useHTMLKludges: false
406         },
407         iframeClass: 'xml-iframe',
408         textWrapping: true,
409         tabMode: 'spaces',
410         indentUnit: 0,
411         initCallback: function(editor) {
412             
413             function createXSLT(xsl) {
414                 var p = new XSLTProcessor();
415                 p.importStylesheet(xsl);
416                 return p;
417             }
418
419             function transform() {
420                 $('#simple-editor').block({message: 'Ładowanie...'});
421                 setTimeout(function() {
422                     $.ajax({
423                         url: '/static/xsl/wl2html_client.xsl',
424                         dataType: 'xml',
425                         success: function(data) {
426                             var doc = null;
427                             var parser = new DOMParser();
428                             var serializer = new XMLSerializer();
429                             var htmlXSL = createXSLT(data);
430
431                             doc = editor.getCode().replace(/\/\s+/g, '<br />');
432                             doc = parser.parseFromString(doc, 'text/xml');
433                             var error = $('parsererror', doc);
434                             console.log(error);
435                             if (error.length == 0) {
436                                 doc = htmlXSL.transformToFragment(doc, document);
437                                 error = $('parsererror', doc);
438                             }
439                             console.log('xml', doc);
440                             if (error.length > 0) {
441                                 console.log(error);
442                                 $('#html-view').html('<p class="error">Wystąpił błąd:</p><pre>' + error.text() + '</pre>');
443                             } else {
444                                 console.log('after transform', doc);
445                                 $('#html-view').html(doc.firstChild);                          
446                             }
447
448                             $('#simple-editor').unblock();
449                         },
450                         error: function() {alert('Error loading XSL!')}
451                     });
452                 }, 200);
453             };
454
455             function reverseTransform () {
456                 $('#source-editor').block({message: 'Ładowanie...'});
457                 setTimeout(function() {
458                     $.ajax({
459                         url: '/static/xsl/html2wl_client.xsl',
460                         dataType: 'xml',
461                         success: function(data) {
462                             var doc = null;
463                             var parser = new DOMParser();
464                             var serializer = new XMLSerializer();
465                             var xsl = createXSLT(data);
466
467                             if ($('#html-view .error').length > 0) {
468                                 $('#source-editor').unblock();
469                                 return;
470                             }
471                             doc = serializer.serializeToString($('#html-view div').get(0))
472                             doc = parser.parseFromString(doc, 'text/xml');
473                             console.log('xml',doc, doc.documentElement);
474                             // TODO: Sprawdzenie błędów
475                             var error = $('parsererror', doc.documentElement);
476                             console.log(error);
477                             if (error.length == 0) {
478                                 doc = xsl.transformToDocument(doc, document);
479                                 error = $('parsererror', doc.documentElement);
480                             }
481                             
482                             if (error.length > 0) {
483                                 console.log(error);
484                                 $('#source-editor').html('<p>Wystąpił błąd:</p>' + error.text());
485                             } else {
486                                 doc = serialize(doc.documentElement).join('');
487                                 editor.setCode(doc);                                
488                             }
489                             
490                             console.log('after transform', doc, doc.documentElement);
491                             $('#source-editor').unblock();
492                         },
493                         error: function() {alert('Error loading XSL!')}
494                     });                    
495                 }, 200);
496             };
497
498             $('#save-button').click(function(event) {
499                 event.preventDefault();
500                 $.blockUI({message: $('#save-dialog')});
501             });
502             
503             $('#save-ok').click(function() {
504                 $.blockUI({message: 'Zapisywanie...'});
505                 
506                 var data = {
507                     name: $('#document-name').html(),
508                     text: editor.getCode(),
509                     revision: $('#document-revision').html(),
510                     author: 'annonymous',
511                     comment: $('#komentarz').val()
512                 };
513                 
514                 console.log(data);
515                 
516                 $.ajax({
517                     url: document.location.href,
518                     type: "POST",
519                     dataType: "json",
520                     data: data,                
521                     success: function(data) {
522                         if (data.text) {
523                             editor.setCode(data.text);
524                             $('#document-revision').html(data.revision);
525                         } else {
526                             console.log(data.errors);
527                             alert(data.errors);
528                         }
529                         $.unblockUI();
530                     },
531                     error: function(xhr, textStatus, errorThrown) {
532                         alert('error: ' + textStatus + ' ' + errorThrown);
533                     },
534                 })
535             });
536             
537             $('#save-cancel').click(function() {
538                 $.unblockUI();
539             });
540
541             $('#simple-view-tab').click(function() {
542                 if ($(this).hasClass('active')) {
543                     return;
544                 }
545                 $(this).addClass('active');
546                 $('#source-view-tab').removeClass('active');
547                 $('#source-editor').hide();
548                 $('#simple-editor').show();
549                 transform();
550             });
551
552             $('#source-view-tab').click(function() {
553                 if ($(this).hasClass('active')) {
554                     return;
555                 }
556                 $(this).addClass('active');
557                 $('#simple-view-tab').removeClass('active');
558                 $('#simple-editor').hide();
559                 $('#source-editor').show();
560                 reverseTransform();
561             });
562
563             $('.toolbar button').click(function(event) {
564                 event.preventDefault();
565                 var params = eval("(" + $(this).attr('ui:action-params') + ")");
566                 scriptletCenter.scriptlets[$(this).attr('ui:action')](editor, params);
567             });
568
569             $('.toolbar select').change(function() {
570                 var slug = $(this).val();
571
572                 $('.toolbar-buttons-container').hide().filter('[data-group=' + slug + ']').show();
573                 $(window).resize();
574             });
575
576             $('.toolbar-buttons-container').hide();
577             $('.toolbar select').change();
578
579             $('#simple-view-tab').click();
580         }
581     });
582     
583     $(window).resize(function() {
584         $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
585     });
586     
587     $(window).resize();
588     
589     $('.vsplitbar').click(function() {
590         if ($('#sidebar').width() == 0) {
591             $('#sidebar').width(480).css({right: 0}).show();
592             $('#source-editor, #simple-editor').css({right: 495});
593             $('.vsplitbar').css({right: 480}).addClass('active');
594             // $('#splitter').trigger('resize', [$(window).width() - 480]);
595         } else {
596             $('#sidebar').width(0).hide();
597             $('#source-editor, #simple-editor').css({right: 15});
598             $('.vsplitbar').css({right: 0}).removeClass('active');
599             // $('#splitter').trigger('resize', [$(window).width()]);
600         }
601         $(window).resize();
602     });
603                 
604
605 });