1 /*globals Editor fileId SplitView PanelContainerView EditorView FlashView messageCenter*/
 
   2 Editor.Model = Editor.Object.extend({
 
   7 Editor.ToolbarButtonsModel = Editor.Model.extend({
 
   8     className: 'Editor.ToolbarButtonsModel',
 
  16         if (!this.get('buttons').length) {
 
  18                 url: documentInfo.toolbarURL,
 
  20                 success: this.loadSucceeded.bind(this)
 
  25     loadSucceeded: function(data)
 
  28         $.each(data, function() {
 
  29             $.each(this.buttons, function() {
 
  30                 //do some lame escapes
 
  31                 this.tooltip = this.tooltip.replace(/"/g, """);
 
  34         this.set('buttons', data);
 
  40 // HTML Document Model
 
  42 Editor.HTMLModel = Editor.Model.extend({
 
  43     _className: 'Editor.HTMLModel',
 
  47     init: function(document, textURL) {
 
  49         this.set('state', 'empty');
 
  50         this.set('revision', document.get('revision'));
 
  51         this.document = document;
 
  53         this.textURL = textURL;        
 
  59         // create a parser and a serializer
 
  60         this.parser = new DOMParser();
 
  61         this.serializer = new XMLSerializer();
 
  63         this.addObserver(this, 'data', this.dataChanged.bind(this));
 
  66     load: function(force) {
 
  67         if (force || this.get('state') == 'empty') {
 
  68             this.set('state', 'loading');
 
  69             messageCenter.addMessage('info', 'xmlload', 'Wczytuję HTML...');
 
  71             // request all stylesheets
 
  73                 url: documentInfo.staticURL + 'xsl/wl2html_client.xsl',
 
  75                 success: this.htmlXSLLoadSuccess.bind(this),
 
  76                 error: this.loadingFailed.bind(this)
 
  80                 url: documentInfo.staticURL + 'xsl/html2wl_client.xsl',
 
  82                 success: this.wlmlXSLLoadSuccess.bind(this),
 
  83                 error: this.loadingFailed.bind(this)
 
  90                     revision: this.get('revision'),
 
  91                     user: this.document.get('user')
 
  93                 success: this.textLoadSuccess.bind(this),
 
  94                 error: this.loadingFailed.bind(this)
 
 101     asWLML: function(element, inner)
 
 103         console.log("Source", element);
 
 104         var doc = this.parser.parseFromString(this.serializer.serializeToString(element), 'text/xml');
 
 106         var result = this.wlmlXSL.transformToDocument(doc);
 
 109             console.log("Failed", this.wlmlXSL, doc);
 
 110             throw "Failed to transform fragment";
 
 113         console.log("Transformed", doc, " to: ", result.documentElement);
 
 115             var children = result.documentElement.childNodes;
 
 118             for(var i=0; i < children.length; i++)
 
 119                 buf += this.serializer.serializeToString(children.item(i));
 
 124          return this.serializer.serializeToString(result.documentElement);
 
 127     innerAsWLML: function(elem)
 
 129         return this.asWLML(elem, true);
 
 132     updateInnerWithWLML: function($element, innerML)
 
 134         var e = $element.clone().html('<span x-node="out-of-flow-text" x-content="%"></span>')[0];
 
 135         var s = this.asWLML(e);
 
 136         // hurray for dirty hacks :P
 
 137         s = s.replace(/>%<\//, '>'+innerML+'</');
 
 138         return this.updateWithWLML($element, s);
 
 141     updateWithWLML: function($element, text)
 
 144         text = text.replace(/\/\s+/g, '<br />');
 
 146             var chunk = this.parser.parseFromString("<chunk>"+text+"</chunk>", "text/xml");
 
 148             console.log('Caught parse exception.');
 
 149             return "<p>Źle sformatowana zawartość:" + e.toString() + "</p>";
 
 152         var parseError = chunk.getElementsByTagName('parsererror');
 
 153         console.log("Errors:", parseError);
 
 155         if(parseError.length > 0)
 
 157             console.log("Parse errors.")
 
 158             return this.serializer.serializeToString(parseError.item(0));
 
 161         console.log("Transforming to HTML");        
 
 162         var result = this.htmlXSL.transformToFragment(chunk, $element[0].ownerDocument).firstChild;
 
 165             return "Błąd aplikacji - nie udało się wygenerować nowego widoku HTML.";
 
 168         var errors = result.getElementsByTagName('error');
 
 169         if(errors.length > 0)
 
 171             var errorMessage = 'Wystąpiły błędy:<ul>';
 
 172             for(var i=0; i < errors.length; i++)
 
 174                 var estr = this.serializer.serializeToString(errors.item(i));
 
 175                 console.log("XFRM error:", estr);
 
 176                 errorMessage += "<li>"+estr+"</li>";
 
 178             errorMessage += "</ul>";
 
 183             $element.replaceWith(result);
 
 184             this.set('state', 'dirty');
 
 187             return "Błąd podczas wstawiania tekstu: '" + e.toString() + "'";
 
 191     createXSLT: function(xslt_doc) {
 
 192         var p = new XSLTProcessor();
 
 193         p.importStylesheet(xslt_doc);
 
 197     htmlXSLLoadSuccess: function(data) 
 
 200             this.htmlXSL = this.createXSLT(data);
 
 202             if(this.wlmlXSL && this.htmlXSL && this.rawText)
 
 206             this.set('error', e.toString() );
 
 207             this.set('state', 'error');
 
 211     wlmlXSLLoadSuccess: function(data)
 
 214             this.wlmlXSL = this.createXSLT(data);
 
 216             if(this.wlmlXSL && this.htmlXSL && this.rawText)
 
 220             this.set('error', e.toString() );
 
 221             this.set('state', 'error');
 
 225     textLoadSuccess: function(data) {
 
 228         if(this.wlmlXSL && this.htmlXSL && this.rawText)
 
 232     loadSuccess: function() {
 
 233         if (this.get('state') != 'loading') {
 
 234             alert('erroneous state:', this.get('state'));
 
 239         doc = this.rawText.replace(/\/\s+/g, '<br />');
 
 240         doc = this.parser.parseFromString(doc, 'text/xml');
 
 241         doc = this.htmlXSL.transformToFragment(doc, document).firstChild;
 
 243         this.set('data', doc);
 
 244         this.set('state', 'synced');
 
 245         messageCenter.addMessage('success', 'xmlload', 'Wczytałem HTML :-)');
 
 248     loadingFailed: function(response)
 
 250         if (this.get('state') != 'loading') {
 
 251             alert('erroneous state:', this.get('state'));
 
 254         var message = parseXHRError(response);
 
 256         this.set('error', '<h2>Błąd przy ładowaniu XML</h2><p>'+message+'</p>');
 
 257         this.set('state', 'error');
 
 258         messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać HTML. Spróbuj ponownie :-(');
 
 261     save: function(message) {
 
 262         if (this.get('state') == 'dirty') {
 
 263             this.set('state', 'saving');
 
 265             messageCenter.addMessage('info', 'htmlsave', 'Zapisuję HTML...');
 
 266             var wlml = this.asWLML(this.get('data'));
 
 270                 revision: this.get('revision'),
 
 271                 user: this.document.get('user')
 
 275                 payload.message = message;
 
 283                 success: this.saveSucceeded.bind(this),
 
 284                 error: this.saveFailed.bind(this)
 
 291     saveSucceeded: function(data) {
 
 292         if (this.get('state') != 'saving') {
 
 293             alert('erroneous state:', this.get('state'));
 
 295         this.set('revision', data.revision);
 
 296         this.set('state', 'updated');
 
 297         messageCenter.addMessage('success', 'htmlsave', 'Zapisałem :-)');
 
 300     saveFailed: function() {
 
 301         if (this.get('state') != 'saving') {
 
 302             alert('erroneous state:', this.get('state'));
 
 304         messageCenter.addMessage('error', 'htmlsave', 'Nie udało mi się zapisać.');
 
 305         this.set('state', 'dirty');
 
 309     set: function(property, value) {
 
 310         if (property == 'state') {
 
 311             console.log(this.description(), ':', property, '=', value);
 
 313         return this._super(property, value);
 
 316     dataChanged: function(property, value) {
 
 317         if (this.get('state') == 'synced') {
 
 318             this.set('state', 'dirty');
 
 322     dispose: function() {
 
 323         this.removeObserver(this);
 
 331 //                  -> error -> loading
 
 333 // empty -> loading -> synced -> unsynced -> loading
 
 335 //                            -> dirty -> updating -> updated -> synced
 
 337 Editor.XMLModel = Editor.Model.extend({
 
 338     _className: 'Editor.XMLModel',
 
 343     init: function(document, serverURL) {
 
 345         this.set('state', 'empty');
 
 346         this.set('revision', document.get('revision'));
 
 347         this.document = document;
 
 348         this.serverURL = serverURL;
 
 349         this.toolbarButtonsModel = new Editor.ToolbarButtonsModel();
 
 350         this.addObserver(this, 'data', this.dataChanged.bind(this));
 
 353     load: function(force) {
 
 354         if (force || this.get('state') == 'empty') {
 
 355             this.set('state', 'loading');
 
 356             messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...');
 
 361                     revision: this.get('revision'),
 
 362                     user: this.document.get('user')
 
 364                 success: this.loadingSucceeded.bind(this),
 
 365                 error: this.loadingFailed.bind(this)
 
 372     loadingSucceeded: function(data) {
 
 373         if (this.get('state') != 'loading') {
 
 374             alert('erroneous state:', this.get('state'));
 
 376         this.set('data', data);
 
 377         this.set('state', 'synced');
 
 378         messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)');
 
 381     loadingFailed: function(response)
 
 383         if (this.get('state') != 'loading') {
 
 384             alert('erroneous state:', this.get('state'));
 
 387         var message = parseXHRError(response);
 
 389         this.set('error', '<h2>Błąd przy ładowaniu XML</h2><p>'+message+'</p>');
 
 390         this.set('state', 'error');
 
 391         messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-(');
 
 394     save: function(message) {
 
 395         if (this.get('state') == 'dirty') {
 
 396             this.set('state', 'updating');
 
 397             messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...');
 
 400                 contents: this.get('data'),
 
 401                 revision: this.get('revision'),
 
 402                 user: this.document.get('user')
 
 405                 payload.message = message;
 
 413                 success: this.saveSucceeded.bind(this),
 
 414                 error: this.saveFailed.bind(this)
 
 421     saveSucceeded: function(data) {
 
 422         if (this.get('state') != 'updating') {
 
 423             alert('erroneous state:', this.get('state'));
 
 425         this.set('revision', data.revision);
 
 426         this.set('state', 'updated');
 
 427         messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)');
 
 430     saveFailed: function() {
 
 431         if (this.get('state') != 'updating') {
 
 432             alert('erroneous state:', this.get('state'));
 
 434         messageCenter.addMessage('error', 'xmlsave', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-(');
 
 435         this.set('state', 'dirty');
 
 439     set: function(property, value) {
 
 440         if (property == 'state') {
 
 441             console.log(this.description(), ':', property, '=', value);
 
 443         return this._super(property, value);
 
 446     dataChanged: function(property, value) {
 
 447         if (this.get('state') == 'synced') {
 
 448             this.set('state', 'dirty');
 
 452     dispose: function() {
 
 453         this.removeObserver(this);
 
 458 Editor.ImageGalleryModel = Editor.Model.extend({
 
 459     _className: 'Editor.ImageGalleryModel',
 
 464     init: function(document, serverURL) {
 
 466         this.set('state', 'empty');
 
 467         this.serverURL = serverURL;
 
 472     setGallery: function(path) {
 
 479           success: this.settingGallerySucceeded.bind(this)           
 
 483     settingGallerySucceeded: function(data) {
 
 484       console.log('settingGallerySucceeded');
 
 488     load: function(force) {
 
 489         if (force || this.get('state') == 'empty') {
 
 490             console.log("setting state");
 
 491             this.set('state', 'loading');
 
 492             console.log("going ajax");
 
 496                 success: this.loadingSucceeded.bind(this),
 
 497                 error: this.loadingFailed.bind(this)
 
 502     loadingSucceeded: function(data) 
 
 504         console.log("success");        
 
 506         if (this.get('state') != 'loading') {
 
 507             alert('erroneous state:', this.get('state'));
 
 510         console.log('galleries:', data);
 
 512         if (data.length === 0) {
 
 513             this.set('data', []);
 
 515             this.set('data', data[0].pages);
 
 518         this.set('state', 'synced');
 
 521     loadingFailed: function(data) {
 
 522         console.log("failed");
 
 524         if (this.get('state') != 'loading') {
 
 525             alert('erroneous state:', this.get('state'));
 
 528         this.set('state', 'error');
 
 531     set: function(property, value) {
 
 532         if (property == 'state') {
 
 533             console.log(this.description(), ':', property, '=', value);
 
 535         return this._super(property, value);
 
 540 Editor.DocumentModel = Editor.Model.extend({
 
 541     _className: 'Editor.DocumentModel',
 
 542     data: null, // name, text_url, revision, latest_shared_rev, parts_url, dc_url, size, merge_url
 
 551         this.set('state', 'empty');        
 
 555         if (this.get('state') == 'empty') {
 
 556             this.set('state', 'loading');
 
 557             messageCenter.addMessage('info', 'docload', 'Ładuję dane dokumentu...');
 
 560                 url: documentInfo.docURL,
 
 562                 success: this.successfulLoad.bind(this),
 
 563                 error: this.failedLoad.bind(this)
 
 568     successfulLoad: function(data) {
 
 569         this.set('data', data);
 
 570         this.set('state', 'synced');
 
 572         this.set('revision', data.revision);
 
 573         this.set('user', data.user);
 
 575         this.contentModels = {
 
 576             'xml': new Editor.XMLModel(this, data.text_url),
 
 577             'html': new Editor.HTMLModel(this, data.text_url),
 
 578             'gallery': new Editor.ImageGalleryModel(this, data.gallery_url)
 
 581         for (var key in this.contentModels) {
 
 582             this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this));
 
 587         messageCenter.addMessage('success', 'docload', 'Dokument załadowany poprawnie :-)');
 
 590     failedLoad: function(response) {
 
 591         if (this.get('state') != 'loading') {
 
 592             alert('erroneous state:', this.get('state'));
 
 595         var err = parseXHRError(response);
 
 596         this.set('error', '<h2>Nie udało się wczytać dokumentu</h2><p>'+err.error_message+"</p>");
 
 597         this.set('state', 'error');
 
 600     contentModelStateChanged: function(property, value, contentModel) {
 
 601         if (value == 'dirty') {
 
 602             this.set('state', 'dirty');
 
 603             for (var key in this.contentModels) {
 
 604                 if (this.contentModels[key].guid() != contentModel.guid()) {
 
 605                     this.contentModels[key].set('state', 'unsynced');
 
 608         } else if (value == 'updated') {
 
 609             this.set('state', 'synced');
 
 610             for (key in this.contentModels) {
 
 611                 if (this.contentModels[key].guid() == contentModel.guid()) {
 
 612                     this.contentModels[key].set('state', 'synced');
 
 613                     this.revision = this.contentModels[key].get('revision');
 
 617             for (key in this.contentModels) {
 
 618                 if (this.contentModels[key].guid() != contentModel.guid()) {
 
 619                     this.contentModels[key].set('revision', this.revision);
 
 620                     this.contentModels[key].set('state', 'empty');
 
 626     saveDirtyContentModel: function(message) {
 
 627         for (var key in this.contentModels) {
 
 628             if (this.contentModels[key].get('state') == 'dirty') {
 
 629                 this.contentModels[key].save(message);
 
 636         this.set('state', 'loading');
 
 638         messageCenter.addMessage('info', 'doc_update',
 
 639             'Uaktualniam dokument...');
 
 642             url: this.data.merge_url,
 
 647                 revision: this.get('revision'),
 
 648                 user: this.get('user')
 
 650             complete: this.updateCompleted.bind(this)           
 
 654     updateCompleted: function(xhr, textStatus)
 
 656         console.log(xhr.status, xhr.responseText);
 
 657         var response = parseXHRResponse(xhr);
 
 660             if( (response.data.result == 'no-op')
 
 661              || (response.data.timestamp == response.data.parent_timestamp))
 
 663                 if( (response.data.revision) && (response.data.revision != this.get('revision')) )
 
 666                     this.set('state', 'unsynced');
 
 670                 messageCenter.addMessage('info', 'doc_update',
 
 671                     'Już posiadasz najbardziej aktualną wersję.');
 
 672                     this.set('state', 'synced');
 
 677             this.set('revision', response.data.revision);
 
 678             this.set('user', response.data.user);
 
 680             messageCenter.addMessage('info', 'doc_update',
 
 681                 'Uaktualnienie dokumentu do wersji ' + response.data.revision);
 
 683             for (var key in this.contentModels) {
 
 684                 this.contentModels[key].set('revision', this.get('revision') );
 
 685                 this.contentModels[key].set('state', 'empty');
 
 688             this.set('state', 'synced');
 
 692         // no success means trouble
 
 693         messageCenter.addMessage(response.error_level, 'doc_update', 
 
 694             response.error_message);       
 
 696         this.set('state', 'unsynced');
 
 699     merge: function(message) {
 
 700         this.set('state', 'loading');
 
 701         messageCenter.addMessage('info', 'doc_merge',
 
 702             'Scalam dokument z głównym repozytorium...');
 
 705             url: this.data.merge_url,
 
 710                 revision: this.get('revision'),
 
 711                 user: this.get('user'),
 
 714             complete: this.mergeCompleted.bind(this),
 
 715             success: function(data) {
 
 716                 this.set('mergeData', data);
 
 721     mergeCompleted: function(xhr, textStatus) {
 
 722         console.log(xhr.status, xhr.responseText);
 
 723         var response = parseXHRResponse(xhr);
 
 725         if(response.success) {
 
 727             if( (response.data.result == 'no-op') ||             
 
 728              ( response.data.shared_parent_timestamp
 
 729                && response.data.shared_timestamp
 
 730                && (response.data.shared_timestamp == response.data.shared_parent_timestamp)) )
 
 732                 if( (response.data.revision) && (response.data.revision != this.get('revision')) )
 
 735                     this.set('state', 'unsynced');
 
 739                 messageCenter.addMessage('info', 'doc_merge',
 
 740                     'Twoja aktualna wersja nie różni się od ostatnio zatwierdzonej.');
 
 741                 this.set('state', 'synced');
 
 745             if( response.data.result == 'accepted')
 
 747                 messageCenter.addMessage('info', 'doc_merge',
 
 748                     'Prośba o zatwierdzenie została przyjęta i oczekuję na przyjęcie.');
 
 749                 this.set('state', 'synced');
 
 754             this.set('revision', response.data.revision);
 
 755             this.set('user', response.data.user);
 
 757             messageCenter.addMessage('info', 'doc_merge',
 
 758                 'Twoja wersja dokumentu została zatwierdzona.');
 
 760             this.set('state', 'synced');
 
 764         // no success means trouble
 
 765         messageCenter.addMessage(response.error_level, 'doc_merge',
 
 766             response.error_message);
 
 768         this.set('state', 'unsynced');
 
 772     set: function(property, value) {
 
 773         if (property == 'state') {
 
 774             console.log(this.description(), ':', property, '=', value);
 
 776         return this._super(property, value);
 
 781 var leftPanelView, rightPanelContainer, doc;
 
 785     var flashView = new FlashView('#flashview', messageCenter);
 
 787     doc = new Editor.DocumentModel();
 
 789     EditorView = new EditorView('#body-wrap', doc);
 
 790     EditorView.freeze("<h1>Wczytuję dokument...</h1>");
 
 792     leftPanelView = new PanelContainerView('#left-panel-container', doc);
 
 793     rightPanelContainer = new PanelContainerView('#right-panel-container', doc);