Fixed bug when opening xinha editor and changed editor template.
[booki.git] / site_media / js / editor.js
blob7a9c2a80522b21f64fb9a030c7c9aa034e664478
1 $(function() {
2 /*
3 * todo:
4 * - should send projectid and bookid and not their full names
7 * */
10 /* booki.chat */
12 jQuery.namespace('jQuery.booki.chat');
14 jQuery.booki.chat = function() {
15 var element = null;
16 var element2 = null;
18 function showJoined(notice) {
19 $('.content', element).append('<p><span class="icon">JOINED</span> '+notice+'</p>');
20 $('.content', element2).append('<p><span class="icon">JOINED</span> '+notice+'</p>');
23 function formatMessage(from, message) {
24 return $("<p><b>"+from+"</b>: "+message+"</p>");
27 function showMessage(from, message) {
28 $(".content", element).append(formatMessage(from, message));
29 $(".content", element2).append(formatMessage(from, message));
32 $(".content", element).attr({ scrollTop: $(".content", element).attr("scrollHeight") });
33 $(".content", element2).attr({ scrollTop: $(".content", element2).attr("scrollHeight") });
37 function initUI() {
38 element2.html($('<form onsubmit="javascript: return false;"><div class="content" style="margin-bottom: 5px; width: 500px; height: 400px; border: 1px solid gray; padding: 5px"></div><input type="text" style="width: 500px;"/></form>').submit(function() { var s = $("INPUT", element2).val(); $("INPUT", element2).attr("value", "");
39 showMessage($.booki.username, s);
40 $.booki.sendToChannel("/chat/"+$.booki.currentProjectID+"/"+$.booki.currentBookID+"/", {"command": "message_send", "message": s}, function() {} );
42 }));
44 element.html($('<form onsubmit="javascript: return false;"><div class="content" style="margin-bottom: 5px; width: 200px; height: 400px; border: 1px solid black; padding: 5px"></div><input type="text" style="width: 200px;"/></form>').submit(function() { var s = $("INPUT", element).val(); $("INPUT", element).attr("value", "");
45 showMessage($.booki.username, s);
46 $.booki.sendToChannel("/chat/"+$.booki.currentProjectID+"/"+$.booki.currentBookID+"/", {"command": "message_send", "message": s}, function() {} );
48 }));
52 return {
53 'initChat': function(elem, elem2) {
54 element = elem;
55 element2 = elem2;
56 initUI();
58 jQuery.booki.subscribeToChannel("/chat/"+$.booki.currentProjectID+"/"+$.booki.currentBookID+"/", function(message) {
59 if(message.command == "user_joined") {
60 showJoined(message.user_joined);
63 if(message.command == "message_received") {
64 showMessage(message.from, message.message);
66 });
70 }();
71 /* booki.editor */
73 jQuery.namespace('jQuery.booki.editor');
75 jQuery.booki.editor = function() {
77 /* status */
78 var statuses = null;
79 var attachments = null;
81 function getStatusDescription(statusID) {
82 var r = $.grep(statuses, function(v, i) {
84 return v[0] == statusID;
85 });
87 if(r && r.length > 0)
88 return r[0][1];
90 return null;
94 /* TOC */
96 function createChapter(vals) {
97 var options = {
98 id: null,
99 title: '',
100 isChapter: true,
101 isLocked: false,
102 status: null
105 $.extend(options, vals);
107 return options;
110 function TOC(containerName) {
111 this.containerName = containerName;
112 this.items = new Array();
116 $.extend(TOC.prototype, {
117 'addItem': function(item) {
118 this.items.push(item);
121 'delItemById': function(id) {
122 for(var i = 0; i < this.items.length; i++) {
123 if(this.items[i].id == id) {
124 return this.items.splice(i, 1);
129 'getItemById': function(id) {
130 for(var i = 0; i < this.items.length; i++) {
131 if(this.items[i].id == id) {
132 return this.items[i];
136 return null;
139 'update': function(order) {
140 var newOrder = new Array();
142 for(var i = 0; i < order.length; i++) {
143 var item = this.getItemById(order[i])
144 newOrder.push(item);
146 this.items = newOrder;
149 'draw': function() {
150 var $this = this;
152 $($this.containerName).empty();
153 $.each(this.items, function(i, v) {
154 if(v.isChapter)
155 makeChapterLine(v.id, v.title, getStatusDescription(v.status)).appendTo($this.containerName);
156 else
157 makeSectionLine(v.id, v.title).appendTo($this.containerName);
162 'redraw': function() {
163 var $this = this;
164 var chldrn = $($this.containerName).contents().clone(true);
166 $($this.containerName).empty();
168 $.each(this.items, function(i, v) {
169 for(var n = 0; n < chldrn.length; n++) {
170 if( $(chldrn[n]).attr("id") == "item_"+v.id) {
171 $(chldrn[n]).appendTo($this.containerName);
172 break;
178 'refresh': function() {
179 // should update status and other things also
180 $.each(this.items, function(i, v) {
181 $("#item_"+v.id+" .title").html(v.title);
182 $("#item_"+v.id+" .status").html(getStatusDescription(v.status));
190 var toc = new TOC("#chapterslist");
191 var holdChapters = new TOC("#holdchapterslist");
194 function getChapter(chapterID) {
195 var chap = toc.getItemById(chapterID);
196 if(!chap)
197 chap = holdChapters.getItemById(chapterID);
198 return chap;
201 var _isEditingSmall = false;
204 function makeSectionLine(chapterID, name) {
205 return $('<li class="ui-state-default" id="item_'+chapterID+'" style="background-color: #a0a0a0; color: white; background-image: none"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span><div class="cont"><table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td width="70%"><div class="title" style="float: left">'+name+'</div></td><td width="10%"><td width="20%"><div class="extra" style="float: right; font-size: 6pt; clear: right"></div></td></tr></table></div></li>');
208 function makeChapterLine(chapterID, name, status) {
209 return $('<li class="ui-state-default" id="item_'+chapterID+'"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span><div class="cont"><table border="0" cellspacing="0" cellpadding="0" width="100%"><tr><td width="70%"><div class="title" style="float: left">'+name+'</div></td><td width="10%"><a href="javascript:void(0)" onclick="$.booki.editor.editChapter('+chapterID+')" style="font-size: 12px">EDIT</a><td width="20%"><div class="status" style="float:right; font-size: 6pt"><a href="javascript:void(0)" onclick="$.booki.editor.editStatusForChapter('+chapterID+')">'+status+'</a></div><div class="extra" style="float: right; font-size: 6pt; clear: right"></div></td></tr></table></div></li>').dblclick(function() {
210 if(_isEditingSmall) return;
211 _isEditingSmall = true;
213 $.booki.ui.notify("Sending data...");
214 $.booki.sendToCurrentBook({"command": "chapter_status", "status": "rename", "chapterID": chapterID}, function() {$.booki.ui.notify("")} );
216 var s = $(".title", $(this)).text();
217 var $this = $(this);
219 $(".cont", $(this)).html('<form style="font-size:12px; white-space: nowrap"></form>');
221 // Make it look nicer and look on the width of input box
222 // also use white-space: nowrap
225 $("FORM", $(this)).append($('<input type="text" style="width: 70%" value="'+s+'" >'));
226 $("FORM", $(this)).append($('<a href="#">SAVE</a>').click(function() {
228 var newName = $("INPUT", $this).val();
229 _isEditingSmall = false; $.booki.ui.notify("Sending data...");
230 $.booki.sendToCurrentBook({"command": "chapter_rename", "chapterID": chapterID, "chapter": newName}, function() {
231 $.booki.ui.notify("");
233 $("#item_"+chapterID).replaceWith(makeChapterLine(chapterID, newName, status));
235 }));
236 $("FORM", $(this)).append($('<span> </span>').html());
237 $("FORM", $(this)).append($('<a href="#">CANCEL</a>').click(function() {
238 _isEditingSmall = false; $.booki.ui.notify("Sending data...");
239 $.booki.sendToCurrentBook({"command": "chapter_status", "status": "normal", "chapterID": chapterID}, function() {$.booki.ui.notify("")} );
241 // this is not god. should get info from toc
242 var ch = getChapter(chapterID);
243 $("#item_"+chapterID).replaceWith(makeChapterLine(chapterID, ch.title, getStatusDescription(ch.status))); }));
247 function closeEditor() {
248 $("#editor").fadeOut("slow",
249 function() {
250 $("#editor").css("display", "none");
251 $("#container").hide().css("display", "block");
255 return {
256 editStatusForChapter: function(chapterID) {
257 var selopts = '<select><option value="-1" style="font-weight: bold; color: black">Cancel</option><option value="-1" style="font-weight: bold; color: black">--------</option>';
258 var chap = getChapter(chapterID);
260 $.each(statuses, function(i, v) {
261 selopts += '<option value="'+v[0]+'"';
262 if(v[0] == chap.status)
263 selopts += ' selected="selected" ';
264 selopts += '">'+v[1]+'</option>';
266 selopts += '</select>';
268 var s = $(selopts);
270 $("#item_"+chapterID+" .status").html(s.change(function() {
271 var chap = getChapter(chapterID);
272 if(parseInt($(this).val()) != -1)
273 chap.status = parseInt($(this).val());
275 $("#item_"+chapterID+" .status").html('<a href="javascript:void(0)" onclick="$.booki.editor.editStatusForChapter('+chapterID+')">'+getStatusDescription(chap.status)+'</a>');
277 }).wrap("<form></form>"));
280 editChapter: function(chapterID) {
281 $.booki.ui.notify("Loading chapter data...");
282 $.booki.sendToChannel("/booki/book/"+$.booki.currentProjectID+"/"+$.booki.currentBookID+"/",
283 {"command": "get_chapter", "chapterID": chapterID}, function(data) {
284 $.booki.ui.notify();
285 $("#container").fadeOut("slow", function() {
287 $("#container").css("display", "none");
288 $("#editor").css("display", "block").fadeIn("slow");
290 /* xinha */
291 xinha_init();
293 function _tryAgain() {
294 var edi = xinha_editors.myTextArea;
295 if(edi) {
296 edi.whenDocReady(function() {
297 edi.setEditorContent(data.content);
299 } else {
300 setTimeout(_tryAgain, 500);
304 if(!xinha_editors.myTextArea) {
305 setTimeout(_tryAgain, 500);
306 } else {
307 _tryAgain();
312 var edi = xinha_editors.myTextArea;
313 if(edi)
314 edi.setEditorContent(data.content);
316 /* $("#editor INPUT[name=title]").attr("value", data.title); */
318 $("#editor INPUT[name=chapter_id]").attr("value", chapterID);
319 $("#editor INPUT[name=save]").unbind('click').click(function() {
320 var edi = xinha_editors["myTextArea"];
321 var content = edi.getEditorContent();
323 var r = new RegExp("<h1>([^<]+)</h1>", "ig");
324 var chapters_n = 0;
326 var c = content.substring(0);
328 while(true) {
329 m = r.exec(c);
330 if(m) {
331 chapters_n += 1;
332 c = c.substring(r.lastIndex-m[0].length);
333 } else {
334 break;
338 if(chapters_n > 1) {
339 $("#spalatodialog").dialog("open");
340 } else {
341 $.booki.ui.notify("Sending data...");
343 $.booki.sendToCurrentBook({"command": "chapter_save", "chapterID": chapterID, "content": content}, function() {$.booki.ui.notify(); closeEditor(); } );
348 $("#editor INPUT[class=cancel]").unbind('click').click(function() {
349 $.booki.sendToCurrentBook({"command": "chapter_status", "status": "normal", "chapterID": chapterID});
350 closeEditor();
351 });
359 _initUI: function() {
360 $("#tabs").tabs();
361 $('#tabs').bind('tabsselect', function(event, ui) {});
363 /* $("#accordion").accordion({ header: "h3" }); */
364 $("#chapterslist, #holdchapterslist").sortable({'connectWith': ['.connectedSortable'], 'dropOnEmpty': true, 'stop': function(event, ui) {
366 var result = $('#chapterslist').sortable('toArray');
367 var holdResult = $('#holdchapterslist').sortable('toArray');
369 // too much copy+paste here. should organise it in better way.
371 if(toc.items.length > result.length) {
372 for(var i = 0; i < toc.items.length; i++) {
373 var wasFound = false;
374 for(var n = 0; n < result.length; n++) {
375 if(toc.items[i].id == result[n].substr(5)) {
376 wasFound = true;
380 if(!wasFound) {
381 var itm = toc.getItemById(toc.items[i].id);
382 if((""+itm.id).substring(0,1) != 's') {
383 holdChapters.addItem(itm);
384 } else {
385 $("#item_"+toc.items[i].id).remove();
387 $.booki.debug.debug(itm.id);
388 toc.delItemById(toc.items[i].id);
390 $.booki.ui.info("#container .middleinfo", "Removing chapter from Table of Contents.");
391 $.booki.ui.notify("Sending data...");
392 $.booki.sendToCurrentBook({"command": "chapters_changed",
393 "chapters": result,
394 "hold": holdResult,
395 "kind": "remove",
396 "chapter_id": itm.id},
397 function() {$.booki.ui.notify()} );
399 break;
402 } else if(toc.items.length < result.length) {
403 for(var i = 0; i < holdChapters.items.length; i++) {
404 var wasFound = false;
405 for(var n = 0; n < holdResult.length; n++) {
406 if(holdChapters.items[i].id == holdResult[n].substr(5)) {
407 wasFound = true;
411 if(!wasFound) {
412 var itm = holdChapters.getItemById(holdChapters.items[i].id);
413 toc.addItem(itm);
414 holdChapters.delItemById(itm.id);
416 $.booki.ui.info("#container .middleinfo", "Adding chapter to Table of Contents.");
417 $.booki.ui.notify("Sending data...");
418 $.booki.sendToCurrentBook({"command": "chapters_changed",
419 "chapters": result,
420 "hold": holdResult,
421 "kind": "add",
422 "chapter_id": itm.id},
423 function() {$.booki.ui.notify()} );
424 break;
428 } else if (toc.items.length == result.length) {
429 $.booki.ui.info("#container .middleinfo", "Reordering the chapters...");
430 $.booki.ui.notify("Sending data...");
431 $.booki.sendToCurrentBook({"command": "chapters_changed",
432 "chapters": result,
433 "hold": holdResult,
434 "kind": "order",
435 "chapter_id": null
437 function() {$.booki.ui.notify()} );
440 $.booki.sendToCurrentBook({"command": "chapters_changed", "chapters": result}, function() {$.booki.ui.notify()} );
443 }, 'placeholder': 'ui-state-highlight', 'scroll': true}).disableSelection();
446 $.booki.chat.initChat($("#chat"), $("#chat2"));
448 $("#tabpublish BUTTON").click(function() {
449 $.booki.sendToCurrentBook({"command": "publish_book"},
450 function() {
451 $("#tabpublish .info").html("<p>Book is being send to objavi.flossmanuals.net. Soon it will be converted to .epub.</p>");
453 $.booki.ui.notify();
454 } );
458 // initialize dialogs
459 $("#insertattachment").dialog({
460 bgiframe: true,
461 autoOpen: false,
462 height: 400,
463 width: 700,
464 modal: true,
465 buttons: {
466 'Insert image': function() {
468 $(this).dialog('close');
470 'Cancel': function() {
471 $(this).dialog('close');
474 open: function(event,ui) {
475 $("#insertattachment .files").empty();
476 $("#insertattachment .files").append('<tr><td><b>name</b></td><td><b>size</b></td></tr>');
478 $.each(attachments, function(i, att) {
479 $("#insertattachment .files").append('<tr><td><a class="file" href="javascript:void(0)" alt="'+att.name+'">'+att.name+'</a></td><td>'+att.size+'</td></tr>');
482 $("#insertattachment A.file").click(function() {
483 var fileName = $(this).attr("alt");
484 $("#insertattachment .previewattachment").html('<img src="../_utils/thumbnail/'+fileName+'">');
488 close: function() {
494 // spalato dialog
496 $("#spalatodialog").dialog({
497 bgiframe: true,
498 autoOpen: false,
499 height: 400,
500 width: 700,
501 modal: true,
502 buttons: {
503 'Split into chapters and save changes': function() {
505 $(this).dialog('close');
508 /* 'Cancel': function() {
509 $(this).dialog('close');
510 }, */
511 'Continue editing': function() {
512 $(this).dialog('close');
516 open: function(event,ui) {
518 var edi = xinha_editors["myTextArea"];
519 var content = edi.getEditorContent();
521 var endSplitting = false;
523 var n = 0;
525 $("#spalatodialog .chapters").empty();
526 $("#spalatodialog .content").empty();
528 while(!endSplitting) {
529 var r = new RegExp("<h1>([^<]+)</h1>", "ig");
530 var m = r.exec(content);
532 if(m != null) {
533 if(n == 0) {
534 if(r.lastIndex-m[0].length > 1) {
535 $("#spalatodialog .chapters").append('<li><a class="chapter" href="javascript:void(0)" title="0">Unknown chapter</a></li>');
536 var chap = content.substring(0, r.lastIndex-m[0].length);
537 $("#spalatodialog .content").append('<div style="display: none" class="chapter0">'+chap+'</div>');
539 n += 1;
541 $("#spalatodialog .chapters").append('<li><a class="chapter" href="javascript:void(0)" title="'+n+'">'+m[1]+'</a></li>');
543 } else {
544 $("#spalatodialog .chapters").append('<li><a class="chapter" href="javascript:void(0)" title="'+n+'">'+m[1]+'</a></li>');
546 if(n > 0) {
547 var chap = content.substring(0, r.lastIndex-m[0].length);
548 $("#spalatodialog .content").append('<div style="display: none" class="chapter'+(n-1)+'">'+chap+'</div>');
552 n += 1;
553 content = content.substring(r.lastIndex);
554 } else {
555 endSplitting = true;
559 $("#spalatodialog .content").append('<div style="display: none" class="chapter'+(n-1)+'">'+content+'</div>');
562 $("#spalatodialog A.chapter").click(function() {
563 var chap_n = $(this).attr("title");
564 $("#spalatodialog .content > DIV").css("display", "none");
565 $("#spalatodialog DIV.chapter"+chap_n).css("display", "block");
570 close: function() {
579 _loadInitialData : function() {
580 $.booki.ui.notify("Loading...");
582 jQuery.booki.sendToCurrentBook({"command": "init_editor"},
584 function(data) {
585 $.booki.ui.notify("");
587 statuses = data.statuses;
589 $.each(data.metadata, function(i, elem) {
590 $("#tabinfo .metadata").append('<tr><td valign="top"><b>'+elem.name+':</b></td><td valign="top"> '+elem.value+'</td></tr>');
593 $.each(data.chapters, function(i, elem) {
594 toc.addItem(createChapter({id: elem[0], title: elem[1], isChapter: elem[3] == 1, status: elem[4]}));
597 $.each(data.hold, function(i, elem) {
598 holdChapters.addItem(createChapter({id: elem[0], title: elem[1], isChapter: elem[3] == 1, status: elem[4]}));
602 toc.draw();
603 holdChapters.draw();
606 attachments = data.attachments;
608 /* this should not be here */
610 function _getDimension(dim) {
611 if(dim) {
612 return dim[0]+'x'+dim[1];
615 return '';
618 function _getSize(size) {
619 return (size/1024).toFixed(2)+' Kb';
622 $.each(data.attachments, function(i, elem) {
623 $("#tabattachments .files").append('<tr class="line"><td><input type="checkbox"></td><td><a class="file" href="javascript:void(0)" alt="'+elem["name"]+'">'+elem["name"]+'</a></td><td>'+_getDimension(elem["dimension"])+'</td><td align="right"><nobr>'+_getSize(elem.size)+'</nobr></td></tr>');
625 /* $("#tabattachments .files").append('<tr><td><input type="checkbox"></td><td><a class="file" href="../static/'+elem["name"]+'" target="_new">'+elem["name"]+'</a></td><td align="right"> '+elem.size+'</td></tr>'); */
630 $("#tabattachments .line").hover(function() {
631 $(this).css("background-color", "#f0f0f0");
633 function() {
634 $(this).css("background-color", "white");
638 $("#tabattachments .file").click(function() {
639 var imageName = $(this).attr("alt");
640 if(imageName.match(/.+\.jpg$/gi)) {
641 $("#attachmentpreview").html('<img src="../_utils/thumbnail/'+imageName+'"><br/><br/><a style="font-size: 10px" href="../static/'+imageName+'" target="_new">Open in new window</a>');
647 $.each(data.users, function(i, elem) {
648 $("#users").append(elem+"<br/>");
655 /* initialize editor */
657 initEditor: function() {
659 jQuery.booki.subscribeToChannel("/booki/book/"+$.booki.currentProjectID+"/"+$.booki.currentBookID+"/", function(message) {
661 // ERROR
662 // this does not work when you change chapter status very fast
663 if(message.command == "chapter_status") {
664 if(message.status == "rename" || message.status == "edit") {
665 // $("#item_"+message.chapterID).css("color", "red");
666 $(".extra", $("#item_"+message.chapterID)).html('<div style="padding: 3px; background-color: red; color: white">'+message.username+'</div>');
669 if(message.status == "normal") {
670 //$("#item_"+message.chapterID).css("color", "gray");
671 $(".extra", $("#item_"+message.chapterID)).html("");
675 if(message.command == "chapters_changed") {
676 if(message.kind == "remove") {
678 var itm = toc.getItemById(message.chapter_id);
680 if((""+message.chapter_id).substring(0,1) != 's')
681 holdChapters.addItem(itm);
683 toc.delItemById(message.chapter_id);
685 toc.update(message.ids);
686 holdChapters.update(message.hold_ids);
688 toc.draw();
689 holdChapters.draw();
691 $.booki.ui.info("#container .middleinfo", "Removing chapter from Table of Contents.");
692 } else {
693 if(message.kind == "add") {
694 var itm = holdChapters.getItemById(message.chapter_id);
695 toc.addItem(itm);
696 holdChapters.delItemById(message.chapter_id);
698 toc.update(message.ids);
699 holdChapters.update(message.hold_ids);
701 toc.draw();
702 holdChapters.draw();
704 $.booki.ui.info("#container .middleinfo", "Adding chapter to Table of Contents.");
705 } else {
706 $.booki.ui.info("#container .middleinfo", "Reordering the chapters...");
708 toc.update(message.ids);
709 holdChapters.update(message.hold_ids);
710 toc.redraw();
711 holdChapters.redraw();
716 if(message.command == "chapter_create") {
717 // this also only works for the TOC
718 if(message.chapter[3] == 1) {
719 holdChapters.addItem(createChapter({id: message.chapter[0], title: message.chapter[1], isChapter: true, status: message.chapter[4]}));
720 var v = holdChapters.getItemById(message.chapter[0]);
721 makeChapterLine(v.id, v.title, getStatusDescription(v.status)).appendTo("#holdchapterslist");
722 } else {
723 toc.addItem(createChapter({id: message.chapter[0], title: message.chapter[1], isChapter: false}));
724 var v = toc.getItemById(message.chapter[0]);
725 makeSectionLine(v.id, v.title).appendTo("#chapterslist");
729 if(message.command == "chapter_rename") {
730 $.booki.debug.debug("[chapter_rename]");
731 var item = toc.getItemById(message.chapterID);
732 if(!item)
733 item = holdChapters.getItemById(message.chapterID);
734 item.title = message.chapter;
735 toc.refresh();
736 holdChapters.refresh();
739 if(message.command == "chapters_list") {
740 $("#chapterslist").empty();
741 $.each(message.chapters, function(i, elem) {
742 // should be makeSectionLine also
743 makeChapterLine(elem[0], elem[1], 0).prependTo("#chapterslist");
750 $.booki.editor._initUI();
751 $.booki.editor._loadInitialData();
754 }();