]> gitweb @ CieloNegro.org - Rakka.git/blob - js/editPage.js
implemented validator on page editor
[Rakka.git] / js / editPage.js
1 (function () {
2
3     var $previewHeader = null;
4     var $previewArea   = null;
5
6     var isDirty = null;
7
8     var rePageName = /^[^ a-z.|#\[\]][^ .|#\[\]]*$/;
9
10     Rakka.editPage = function (pageName) {
11         var $area = Rakka.switchScreen();
12
13         Rakka.displayWaitingMessage("Loading... please wait.");
14     
15         // XML 版のページを取得する。
16         $.ajax({
17             url    : Rakka.baseURI + pageName + ".xml",
18             success: function (pageXml) {
19                 Rakka.hideWaitingMessage();
20
21                 if (pageXml.documentElement.tagName == "page") {
22                     var $page       = $(pageXml).find("page");
23                     var oldRevision = $page.attr("revision");
24                     var defaultType
25                         = $page.attr("isBinary") == "yes"          ? "binary"
26                         : $page.attr("type")     == "text/x-rakka" ? "rakka"
27                         : $page.attr("type")     == "text/css"     ? "css"
28                         : $page.attr("redirect") != null           ? "redirect"
29                         :                                            "unknown"
30                         ;
31                     var lang        = $page.attr("lang");
32                     var isLocked    = $page.attr("isLocked") == "yes";
33                     var otherLangs  = (function () {
34                         var obj = {};
35                         $page.find("otherLang > link").each(function () {
36                             obj[this.getAttribute("lang")] = this.getAttribute("page");
37                         });
38                         return obj;
39                     })();
40                     var source
41                         = $page.attr("redirect") != null ? $page.attr("redirect")
42                         : $page.find("textData").text()
43                         ;
44                     var summary     = $page.find("summary").text();
45                 
46                     displayPageEditor(pageName, oldRevision, defaultType, lang, isLocked, otherLangs, source, summary);
47                 }
48                 else {
49                     displayPageEditor(pageName, null, "rakka", null, false, {}, null, "");
50                 }
51             },
52             error  : function (req) {
53                 Rakka.hideWaitingMessage();
54                 
55                 if (req.status == 404) {
56                     displayPageEditor(pageName, null, "rakka", null, false, {}, null, "");
57                 }
58                 else {
59                     $area.text("Error: " + req.status + " " + req.statusText);
60                 }
61             }
62         });
63     };
64
65     Rakka.newPage = function () {
66         displayPageEditor("", null, "rakka", null, false, {}, null, "");
67     };
68
69     var displayPageEditor = function (pageName, oldRevision, defaultType, lang, isLocked, otherLangs, source, summary) {
70         var $area = Rakka.switchScreen();
71
72         $previewHeader = $( $.H1({}, "Preview") );
73         $area.append($previewHeader);
74         $previewHeader.hide();
75
76         $previewArea = $( $.DIV({className: "preview"}) );
77         $area.append($previewArea);
78         $previewArea.hide();
79
80         $area.append($.H1({}, pageName == "" ? "Create page" : "Edit page"));
81
82         var makeDirty = function () {
83             isDirty = true;
84         };
85     
86         var fldPageName
87             = $.INPUT({type : "text", value: pageName});
88
89         $(fldPageName).change(makeDirty);
90
91         var chkIsLocked
92             = $.INPUT({type    : "checkbox",
93                        checked : (isLocked ? "checked" : "")});
94
95         $(chkIsLocked).change(makeDirty);
96
97         var trIsLocked
98             = $.TR({},
99                    $.TH({}, "Page lock"),
100                    $.TD({},
101                         $.LABEL({},
102                                 chkIsLocked,
103                                 "Disallow anonymous users to edit or delete this page")));
104         
105         var btnTypeRakka
106             = $.INPUT({type   : "radio",
107                        name   : "type",
108                        checked: (defaultType == "rakka"    ? "checked" : "")});
109
110         $(btnTypeRakka).change(makeDirty);
111
112         var btnTypeCSS
113             = $.INPUT({type   : "radio",
114                        name   : "type",
115                        checked: (defaultType == "css"      ? "checked" : "")});
116
117         $(btnTypeCSS).change(makeDirty);
118
119         var btnTypeBinary
120             = $.INPUT({type   : "radio",
121                        name   : "type",
122                        checked: (defaultType == "binary"   ? "checked" : "")});
123
124         $(btnTypeBinary).change(makeDirty);
125
126         var btnTypeRedirect
127             = $.INPUT({type   : "radio",
128                        name   : "type",
129                        checked: (defaultType == "redirect" ? "checked" : "")});
130
131         $(btnTypeRedirect).change(makeDirty);
132
133         var selPageLang
134             = $.SELECT({},
135                        $.OPTION({value: ""}, "(unspecified)"),
136                        (function () {
137                            var options = [];
138                            
139                            $.each(Rakka.getSystemConfig().languages, function (tag, name) {
140                                options.push(
141                                    $.OPTION({value: tag}, name));
142                            });
143
144                            return options;
145                        })());
146
147         $(selPageLang).change(makeDirty);
148
149         if (lang == null || lang == "") {
150             $(selPageLang).val($("html").attr("xml:lang"));
151         }
152         else {
153             $(selPageLang).val(lang);
154         }
155
156         var trPageLang
157             = $.TR({},
158                    $.TH({}, "Page language"),
159                    $.TD({}, selPageLang));
160
161         var trOtherLangs = (function () {
162             var options = [];
163
164             $.each(Rakka.getSystemConfig().languages, function (tag, name) {
165                 options.push(
166                     $.OPTION({value: tag}, name));
167             });
168
169             var selLang = $.SELECT({}, options);
170             var fldLink = $.INPUT({type: "text", className: "smallField"});
171
172             $(selLang).change(function () {
173                 var pageName = otherLangs[$(selLang).val()];
174                 $(fldLink).val(
175                     pageName == null ? "" : pageName
176                 );
177             }).trigger("change");
178
179             var onLinkChanged = function () {
180                 isDirty = true;
181
182                 var lang     = $(selLang).val();
183                 var pageName = $(this).val();
184                 
185                 if (pageName == "") {
186                     delete otherLangs[lang];
187                 }
188                 else {
189                     otherLangs[lang] = pageName;
190                 }
191             };
192             $(fldLink).change(onLinkChanged).keyup(onLinkChanged);
193
194             return $.TR({},
195                         $.TH({}, "Language links"),
196                         $.TD({}, selLang, fldLink));
197         })();
198
199         var fldSummary
200             = $.TEXTAREA({className: "summary"}, summary);
201
202         $(fldSummary).change(makeDirty);
203
204         var trSummary
205             = $.TR({},
206                    $.TH({}, "Summary"),
207                    $.TD({}, fldSummary));
208
209         var fldRakkaSource
210             = $.TEXTAREA({className: "source"},
211                          (defaultType == "rakka" && source != null ? source : ""));
212
213         $(fldRakkaSource).change(makeDirty);
214
215         var fldCSSSource
216             = $.TEXTAREA({className: "source"},
217                          (defaultType == "css"   && source != null ? source : ""));
218
219         $(fldCSSSource).change(makeDirty);
220
221         var fldUploadFile
222             = $.INPUT({type: "file"});
223
224         $(fldUploadFile).change(makeDirty);
225
226         var fldRedirect
227             = $.INPUT({type: "text", value: (defaultType == "redirect" ? source : "")});
228
229         $(fldRedirect).change(makeDirty);
230
231         var trContent
232             = $.TR({}, 
233                    $.TH({}),
234                    $.TD({})
235                   );
236
237         var btnPreview
238             = $.INPUT({type: "button", value: "Preview page"});
239         
240         $(btnPreview).click(function () {
241             if (btnTypeRakka.checked) {
242                 previewRakkaPage(
243                     fldPageName.value, fldRakkaSource.value);
244             }
245             else if (btnTypeBinary.checked) {
246                 if (fldUploadFile.value != "") {
247                     previewBinaryPage(
248                         fldPageName.value, fldUploadFile.value);
249                 }
250             }
251         });
252
253         var btnSubmit
254             = $.INPUT({type: "button", value: "Submit page"});
255         
256         $(btnSubmit).click(function () {
257             if (btnTypeRakka.checked) {
258                 submitTextPage(
259                     pageName,
260                     oldRevision,
261                     fldPageName.value,
262                     chkIsLocked.checked,
263                     "text/x-rakka",
264                     $(selPageLang).val(),
265                     otherLangs,
266                     fldSummary.value,
267                     fldRakkaSource.value);
268             }
269             else if (btnTypeCSS.checked) {
270                 submitTextPage(
271                     pageName,
272                     oldRevision,
273                     fldPageName.value,
274                     chkIsLocked.checked,
275                     "text/css",
276                     $(selPageLang).val(),
277                     otherLangs,
278                     fldSummary.value,
279                     fldCSSSource.value);
280             }
281             else if (btnTypeBinary.checked) {
282                 if (fldUploadFile.value != "") {
283                     submitBinaryPage(
284                         pageName,
285                         oldRevision,
286                         fldPageName.value,
287                         chkIsLocked.checked,
288                         $(selPageLang).val(),
289                         otherLangs,
290                         fldSummary.value,
291                         fldUploadFile.value);
292                 }
293             }
294             else if (btnTypeRedirect.checked) {
295                 submitRedirection(
296                     pageName,
297                     oldRevision,
298                     fldPageName.value,
299                     chkIsLocked.checked,
300                     fldRedirect.value);
301             }
302         });
303
304         var btnDelete
305             = $.INPUT({type: "button", value: "Delete this page"});
306         
307         $(btnDelete).click(function () {
308             if (window.confirm("Do you really want to delete this page?")) {
309                 deletePage(pageName);
310             }
311         });
312
313         var btnCancel
314             = $.INPUT({type: "button", value: "Cancel editing"});
315
316         $(btnCancel).click(function () {
317             if (isDirty) {
318                 if (window.confirm("Do you really want to discard changes?")) {
319                     Rakka.restoreScreen();
320                 }
321             }
322             else {
323                 Rakka.restoreScreen();
324             }
325         });
326
327         var updateTRContent = function () {
328             if (btnTypeRakka.checked) {
329                 $(trPageLang).show();
330                 $(trOtherLangs).show();
331                 $(trSummary).show();
332                 $(trContent).find("th").text("Wiki source");
333                 $(trContent).find("td").empty().append(fldRakkaSource);
334                 $(btnPreview).show();
335             }
336             else if (btnTypeCSS.checked) {
337                 $(trPageLang).show();
338                 $(trOtherLangs).show();
339                 $(trSummary).show();
340                 $(trContent).find("th").text("CSS source");
341                 $(trContent).find("td").empty().append(fldCSSSource);
342                 $(btnPreview).hide();
343             }
344             else if (btnTypeBinary.checked) {
345                 $(trPageLang).show();
346                 $(trOtherLangs).show();
347                 $(trSummary).show();
348                 $(trContent).find("th").text("File");
349                 $(trContent).find("td").empty().append(fldUploadFile);
350                 $(btnPreview).show();
351             }
352             else if (btnTypeRedirect.checked) {
353                 $(trPageLang).hide();
354                 $(trOtherLangs).hide();
355                 $(trSummary).hide();
356                 $(trContent).find("th").text("Destination Page");
357                 $(trContent).find("td").empty().append(fldRedirect);
358                 $(btnPreview).hide();
359             }
360         };
361         $(btnTypeRakka   ).change(updateTRContent);
362         $(btnTypeCSS     ).change(updateTRContent);
363         $(btnTypeBinary  ).change(updateTRContent);
364         $(btnTypeRedirect).change(updateTRContent);
365         updateTRContent();
366
367         var pageEditor
368             = $.TABLE({className: "pageEditor"},
369                       $.TBODY({},
370                               $.TR({},
371                                    $.TH({}, "Page name"),
372                                    $.TD({}, fldPageName)
373                                   ),
374                               trIsLocked,
375                               $.TR({},
376                                    $.TH({}, "Page type"),
377                                    $.TD({},
378                                         $.UL({},
379                                              $.LI({},
380                                                   $.LABEL({},
381                                                           btnTypeRakka,
382                                                           "Wiki page"
383                                                          )
384                                                  ),
385                                              $.LI({},
386                                                   $.LABEL({},
387                                                           btnTypeCSS,
388                                                           "Style sheet"
389                                                          )
390                                                  ),
391                                              $.LI({},
392                                                   $.LABEL({},
393                                                           btnTypeBinary,
394                                                           "Binary file"
395                                                          )
396                                                  ),
397                                              $.LI({},
398                                                   $.LABEL({},
399                                                           btnTypeRedirect,
400                                                           "Redirection"
401                                                          )
402                                                  )
403                                             )
404                                        )
405                                   ),
406                               trPageLang,
407                               trOtherLangs,
408                               trSummary,
409                               trContent,
410                               $.TR({},
411                                    $.TH({}),
412                                    $.TD({}, btnPreview, btnSubmit, btnDelete, btnCancel)
413                                   )
414                              )
415                      );
416
417         var validate = function () {
418             var isValid = (function () {
419                 if (fldPageName.value.match(rePageName) == null) {
420                     return false;
421                 }
422
423                 if (btnTypeRedirect.checked) {
424                     if (fldRedirect.value.match(rePageName) == null) {
425                         return false;
426                     }
427                 }
428                 else {
429                     for (var tag in otherLangs) {
430                         if (otherLangs[tag].match(rePageName) == null) {
431                             return false;
432                         }
433                     }
434
435                     if (btnTypeBinary.checked) {
436                         if (fldUploadFile.value == "") {
437                             return false;
438                         }
439                     }
440                 }
441
442                 return true;
443             })();
444
445             $(btnSubmit).attr({disabled: (isValid ? "" : "disabled")});
446         };
447         $(fldPageName)
448             .add(btnTypeRakka)
449             .add(btnTypeCSS)
450             .add(btnTypeBinary)
451             .add(btnTypeRedirect)
452             .add($(trOtherLangs).find("input"))
453             .add(fldUploadFile)
454             .add(fldRedirect)
455             .change(validate)
456             .keyup(validate);
457         validate();
458
459         if (oldRevision == null || oldRevision == 0) {
460             // 削除不可
461             $(btnDelete).hide();
462         }
463
464         $area.append(pageEditor);
465
466         if (!Rakka.isLoggedIn() || Rakka.isGlobalLocked) {
467             $(trIsLocked).hide();
468         }
469
470         isDirty = false;
471     };
472
473     var previewRakkaPage = function (pageName, source) {
474         Rakka.displayWaitingMessage("Loading... please wait.");
475         
476         var url = Rakka.baseURI + "render/" + encodeURI(pageName);
477         $.ajax({
478             type       : "POST",
479             url        : url,
480             contentType: "text/x-rakka",
481             data       : source,
482             processData: false,
483             success    : function (resultDoc) {
484                 Rakka.hideWaitingMessage();
485                 showPreview(resultDoc);
486             },
487             error      : function (req) {
488                 Rakka.hideWaitingMessage();
489                 alert("Error: " + req.status + " " + req.statusText);
490             }
491         });
492     };
493
494     var previewBinaryPage = function (pageName, path) {
495         Rakka.displayWaitingMessage("Loading... please wait.");
496
497         /* Firefox でバイナリを送らうとすると 0x00 の位置で切れてしまふ。*/
498         var bin = Rakka.loadLocalBinaryFile(path);
499         var url = Rakka.baseURI + "render/" + encodeURI(pageName);
500         $.ajax({
501             type       : "POST",
502             url        : url,
503             contentType: "application/x-rakka-base64-stream",
504             data       : Rakka.encodeBase64(bin),
505             processData: false,
506             success    : function (resultDoc) {
507                 Rakka.hideWaitingMessage();
508                 showPreview(resultDoc);
509             },
510             error      : function (req) {
511                 Rakka.hideWaitingMessage();
512                 alert("Error: " + req.status + " " + req.statusText);
513             }
514         });
515     };
516
517     var showPreview = function (doc) {
518         $previewArea.empty();
519         
520         $previewHeader.show();
521         $previewArea.show();
522         
523         var root  = doc.documentElement;
524         var child = root.firstChild;
525         do {
526             if (child.nodeType == 1) {
527                 // 要素だったので複製
528                 $previewArea.append(child.cloneNode(true));
529             }
530         } while (child = child.nextSibling);
531
532         Rakka.scrollToTopLeft();
533     };
534
535     var submitTextPage = function (pageName, oldRevision, givenPageName, isLocked, mimeType, lang, otherLangs, summary, text) {
536         var doc = document.implementation.createDocument(
537             "http://cielonegro.org/schema/Rakka/Page/1.0", "page", null);
538
539         var page = doc.documentElement;
540
541         if (oldRevision != null) {
542             // ページ書換時
543             var updateInfo = doc.createElement("updateInfo");
544             updateInfo.setAttribute("oldRevision", oldRevision);
545
546             if (pageName != givenPageName) {
547                 var move = doc.createElement("move");
548                 move.setAttribute("from", pageName);
549                 updateInfo.appendChild(move);
550             }
551
552             page.appendChild(updateInfo);
553         }
554
555         page.setAttribute("isLocked", isLocked ? "yes" : "no");
556         page.setAttribute("type", mimeType);
557
558         if (lang != null && lang != "") {
559             page.setAttribute("lang", lang);
560         }
561
562         if (summary != null && summary != "") {
563             var s = doc.createElement("summary");
564             s.appendChild(
565                 doc.createTextNode(summary));
566             page.appendChild(s);
567         }
568
569         var oLang = doc.createElement("otherLang");
570         for (var tag in otherLangs) {
571             var link = doc.createElement("link");
572             link.setAttribute("lang", tag);
573             link.setAttribute("page", otherLangs[tag]);
574             oLang.appendChild(link);
575         }
576         page.appendChild(oLang);
577
578         var textData = doc.createElement("textData");
579         textData.appendChild(
580             doc.createTextNode(text));
581
582         page.appendChild(textData);
583
584         Rakka.displayWaitingMessage("Submitting... please wait.");
585
586         var url = Rakka.baseURI + encodeURI(givenPageName);
587         $.ajax({
588             type       : "PUT",
589             url        : url,
590             contentType: "text/xml",
591             data       : doc,
592             processData: false,
593             beforeSend : function (req) {
594                 Rakka.setAuthorization(req);
595             },
596             success    : function () {
597                 window.location.replace(url);
598             },
599             error      : function (req) {
600                 Rakka.hideWaitingMessage();
601                 
602                 var $area = Rakka.switchScreen();
603                 $area.text("Error: " + req.status + " " + req.statusText);
604             }
605         });
606     };
607
608     var submitBinaryPage = function (pageName, oldRevision, givenPageName, isLocked, lang, otherLangs, summary, path) {
609         var doc = document.implementation.createDocument(
610             "http://cielonegro.org/schema/Rakka/Page/1.0", "page", null);
611
612         var page = doc.documentElement;
613
614         if (oldRevision != null) {
615             // ページ書換時
616             var updateInfo = doc.createElement("updateInfo");
617             updateInfo.setAttribute("oldRevision", oldRevision);
618
619             if (pageName != givenPageName) {
620                 var move = doc.createElement("move");
621                 move.setAttribute("from", pageName);
622                 updateInfo.appendChild(move);
623             }
624
625             page.appendChild(updateInfo);
626         }
627
628         page.setAttribute("isLocked", isLocked ? "yes" : "no");
629         page.setAttribute("type", "");
630
631         if (lang != null && lang != "") {
632             page.setAttribute("lang", lang);
633         }
634
635         if (summary != null) {
636             var s = doc.createElement("summary");
637             s.appendChild(
638                 doc.createTextNode(summary));
639             page.appendChild(s);
640         }
641
642         var oLang = doc.createElement("otherLang");
643         for (var tag in otherLangs) {
644             var link = doc.createElement("link");
645             link.setAttribute("lang", tag);
646             link.setAttribute("page", otherLangs[tag]);
647             oLang.appendChild(link);
648         }
649         page.appendChild(oLang);
650
651         var bin = Rakka.loadLocalBinaryFile(path);
652         var b64 = Rakka.encodeBase64(bin);
653
654         var binaryData = doc.createElement("binaryData");
655         binaryData.appendChild(
656             doc.createTextNode(b64));
657
658         page.appendChild(binaryData);
659
660         Rakka.displayWaitingMessage("Submitting... please wait.");
661
662         var url = Rakka.baseURI + encodeURI(givenPageName);
663         $.ajax({
664             type       : "PUT",
665             url        : url,
666             contentType: "text/xml",
667             data       : doc,
668             processData: false,
669             beforeSend : function (req) {
670                 Rakka.setAuthorization(req);
671             },
672             success    : function () {
673                 window.location.replace(url);
674             },
675             error      : function (req) {
676                 Rakka.hideWaitingMessage();
677                 
678                 var $area = Rakka.switchScreen();
679                 $area.text("Error: " + req.status + " " + req.statusText);
680             }
681         });
682     };
683
684     var submitRedirection = function (pageName, oldRevision, givenPageName, isLocked, destination) {
685         var doc = document.implementation.createDocument(
686             "http://cielonegro.org/schema/Rakka/Page/1.0", "page", null);
687
688         var page = doc.documentElement;
689
690         if (oldRevision != null) {
691             // ページ書換時
692             var updateInfo = doc.createElement("updateInfo");
693             updateInfo.setAttribute("oldRevision", oldRevision);
694
695             if (pageName != givenPageName) {
696                 var move = doc.createElement("move");
697                 move.setAttribute("from", pageName);
698                 updateInfo.appendChild(move);
699             }
700
701             page.appendChild(updateInfo);
702         }
703
704         page.setAttribute("isLocked", isLocked ? "yes" : "no");
705         page.setAttribute("redirect", destination);
706
707         Rakka.displayWaitingMessage("Submitting... please wait.");
708
709         var url = Rakka.baseURI + encodeURI(givenPageName);
710         $.ajax({
711             type       : "PUT",
712             url        : url,
713             contentType: "text/xml",
714             data       : doc,
715             processData: false,
716             beforeSend : function (req) {
717                 Rakka.setAuthorization(req);
718             },
719             success    : function () {
720                 window.location.replace(url);
721             },
722             error      : function (req) {
723                 Rakka.hideWaitingMessage();
724                 
725                 var $area = Rakka.switchScreen();
726                 $area.text("Error: " + req.status + " " + req.statusText);
727             }
728         });
729     };
730
731     var deletePage = function (pageName) {
732         var url = Rakka.baseURI + encodeURI(pageName);
733         $.ajax({
734             type       : "DELETE",
735             url        : url,
736             beforeSend : function (req) {
737                 Rakka.setAuthorization(req);
738             },
739             success    : function () {
740                 window.location.replace(url);
741             },
742             error      : function (req) {
743                 Rakka.hideWaitingMessage();
744                 
745                 var $area = Rakka.switchScreen();
746                 $area.text("Error: " + req.status + " " + req.statusText);
747             }
748         });
749     };
750
751 })();