X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/7a12b550f4a8366c8ace2f9c55706aba7c4b17f7..b02d71628de3e39c79abd257096281bf7fa1291d:/platforma/static/js/models.js diff --git a/platforma/static/js/models.js b/platforma/static/js/models.js index ffaf999a..dae64d8a 100755 --- a/platforma/static/js/models.js +++ b/platforma/static/js/models.js @@ -36,87 +36,247 @@ Editor.ToolbarButtonsModel = Editor.Model.extend({ }); -// Stany modelu: // -// -> error -> loading -// / -// empty -> loading -> synced -> unsynced -> loading -// \ -// -> dirty -> updating -> updated -> synced +// HTML Document Model // -Editor.XMLModel = Editor.Model.extend({ - _className: 'Editor.XMLModel', - serverURL: null, - data: '', +Editor.HTMLModel = Editor.Model.extend({ + _className: 'Editor.HTMLModel', + textURL: null, state: 'empty', - - init: function(document, serverURL) { + + init: function(document, textURL) { this._super(); this.set('state', 'empty'); this.set('revision', document.get('revision')); this.document = document; - this.serverURL = serverURL; - this.toolbarButtonsModel = new Editor.ToolbarButtonsModel(); + + this.textURL = textURL; + + this.htmlXSL = null; + this.wlmlXSL = null; + this.rawText = null; + + // create a parser and a serializer + this.parser = new DOMParser(); + this.serializer = new XMLSerializer(); + this.addObserver(this, 'data', this.dataChanged.bind(this)); }, - + load: function(force) { if (force || this.get('state') == 'empty') { this.set('state', 'loading'); - messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...'); + messageCenter.addMessage('info', 'xmlload', 'Wczytuję HTML...'); + + // request all stylesheets $.ajax({ - url: this.serverURL, + url: documentInfo.staticURL + 'xsl/wl2html_client.xsl', + dataType: 'xml', + success: this.htmlXSLLoadSuccess.bind(this), + error: this.loadingFailed.bind(this) + }); + + $.ajax({ + url: documentInfo.staticURL + 'xsl/html2wl_client.xsl', + dataType: 'xml', + success: this.wlmlXSLLoadSuccess.bind(this), + error: this.loadingFailed.bind(this) + }); + + $.ajax({ + url: this.textURL, dataType: 'text', data: { revision: this.get('revision'), user: this.document.get('user') }, - success: this.loadingSucceeded.bind(this), + success: this.textLoadSuccess.bind(this), error: this.loadingFailed.bind(this) }); return true; } return false; }, - - loadingSucceeded: function(data) { + + asWLML: function(element, inner) + { + console.log("Source", element); + var doc = this.parser.parseFromString(this.serializer.serializeToString(element), 'text/xml'); + + var result = this.wlmlXSL.transformToDocument(doc); + + if(!result) { + console.log("Failed", this.wlmlXSL, doc); + throw "Failed to transform fragment"; + } + + console.log("Transformed", doc, " to: ", result.documentElement); + if(inner) { + var children = result.documentElement.childNodes; + var buf = ''; + + for(var i=0; i < children.length; i++) + buf += this.serializer.serializeToString(children.item(i)); + + return buf; + } + + return this.serializer.serializeToString(result.documentElement); + }, + + innerAsWLML: function(elem) + { + return this.asWLML(elem, true); + }, + + updateInnerWithWLML: function($element, innerML) + { + var e = $element.clone().html('')[0]; + var s = this.asWLML(e); + // hurray for dirty hacks :P + s = s.replace(/>%<\//, '>'+innerML+''); + try { + var chunk = this.parser.parseFromString(""+text+"", "text/xml"); + } catch(e) { + console.log('Caught parse exception.'); + return "

Źle sformatowana zawartość:" + e.toString() + "

"; + } + + var parseError = chunk.getElementsByTagName('parsererror'); + console.log("Errors:", parseError); + + if(parseError.length > 0) + { + console.log("Parse errors.") + return this.serializer.serializeToString(parseError.item(0)); + } + + console.log("Transforming to HTML"); + var result = this.htmlXSL.transformToFragment(chunk, $element[0].ownerDocument).firstChild; + + if(!result) { + return "Błąd aplikacji - nie udało się wygenerować nowego widoku HTML."; + } + + var errors = result.getElementsByTagName('error'); + if(errors.length > 0) + { + var errorMessage = 'Wystąpiły błędy:"; + return errorMessage; + } + + try { + $element.replaceWith(result); + this.set('state', 'dirty'); + return false; + } catch(e) { + return "Błąd podczas wstawiania tekstu: '" + e.toString() + "'"; + } + }, + + createXSLT: function(xslt_doc) { + var p = new XSLTProcessor(); + p.importStylesheet(xslt_doc); + return p; + }, + + htmlXSLLoadSuccess: function(data) + { + try { + this.htmlXSL = this.createXSLT(data); + + if(this.wlmlXSL && this.htmlXSL && this.rawText) + this.loadSuccess(); + } catch(e) { + console.log(e); + this.set('error', e.toString() ); + this.set('state', 'error'); + } + }, + + wlmlXSLLoadSuccess: function(data) + { + try { + this.wlmlXSL = this.createXSLT(data); + + if(this.wlmlXSL && this.htmlXSL && this.rawText) + this.loadSuccess(); + } catch(e) { + console.log(e); + this.set('error', e.toString() ); + this.set('state', 'error'); + } + }, + + textLoadSuccess: function(data) { + this.rawText = data; + + if(this.wlmlXSL && this.htmlXSL && this.rawText) + this.loadSuccess(); + }, + + loadSuccess: function() { if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } - this.set('data', data); + + // prepare text + var doc = null; + doc = this.rawText.replace(/\/\s+/g, '
'); + doc = this.parser.parseFromString(doc, 'text/xml'); + doc = this.htmlXSL.transformToFragment(doc, document).firstChild; + + this.set('data', doc); this.set('state', 'synced'); - messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)'); + messageCenter.addMessage('success', 'xmlload', 'Wczytałem HTML :-)'); }, - + loadingFailed: function(response) { if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } - + var message = parseXHRError(response); - + this.set('error', '

Błąd przy ładowaniu XML

'+message+'

'); this.set('state', 'error'); - messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-('); + messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać HTML. Spróbuj ponownie :-('); }, - + save: function(message) { if (this.get('state') == 'dirty') { - this.set('state', 'updating'); - messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...'); - + this.set('state', 'saving'); + + messageCenter.addMessage('info', 'htmlsave', 'Zapisuję HTML...'); + var wlml = this.asWLML(this.get('data')); + var payload = { - contents: this.get('data'), + contents: wlml, revision: this.get('revision'), user: this.document.get('user') }; + if (message) { payload.message = message; } - + $.ajax({ - url: this.serverURL, + url: this.textURL, type: 'post', dataType: 'json', data: payload, @@ -127,24 +287,24 @@ Editor.XMLModel = Editor.Model.extend({ } return false; }, - + saveSucceeded: function(data) { - if (this.get('state') != 'updating') { + if (this.get('state') != 'saving') { alert('erroneous state:', this.get('state')); } this.set('revision', data.revision); this.set('state', 'updated'); - messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)'); + messageCenter.addMessage('success', 'htmlsave', 'Zapisałem :-)'); }, - + saveFailed: function() { - if (this.get('state') != 'updating') { + if (this.get('state') != 'saving') { alert('erroneous state:', this.get('state')); } - messageCenter.addMessage('error', 'xmlsave', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-('); + messageCenter.addMessage('error', 'htmlsave', 'Nie udało mi się zapisać.'); this.set('state', 'dirty'); }, - + // For debbuging set: function(property, value) { if (property == 'state') { @@ -152,13 +312,13 @@ Editor.XMLModel = Editor.Model.extend({ } return this._super(property, value); }, - + dataChanged: function(property, value) { if (this.get('state') == 'synced') { this.set('state', 'dirty'); } }, - + dispose: function() { this.removeObserver(this); this._super(); @@ -166,36 +326,36 @@ Editor.XMLModel = Editor.Model.extend({ }); -Editor.HTMLModel = Editor.Model.extend({ - _className: 'Editor.HTMLModel', - dataURL: null, - htmlURL: null, - renderURL: null, - displaData: '', - xmlParts: {}, +// Stany modelu: +// +// -> error -> loading +// / +// empty -> loading -> synced -> unsynced -> loading +// \ +// -> dirty -> updating -> updated -> synced +// +Editor.XMLModel = Editor.Model.extend({ + _className: 'Editor.XMLModel', + serverURL: null, + data: '', state: 'empty', - init: function(document, dataURL, htmlURL) { + init: function(document, serverURL) { this._super(); this.set('state', 'empty'); - this.set('revision', document.get('revision')); - + this.set('revision', document.get('revision')); this.document = document; - this.htmlURL = htmlURL; - this.dataURL = dataURL; - this.renderURL = documentInfo.renderURL; - this.xmlParts = {}; + this.serverURL = serverURL; + this.toolbarButtonsModel = new Editor.ToolbarButtonsModel(); + this.addObserver(this, 'data', this.dataChanged.bind(this)); }, load: function(force) { if (force || this.get('state') == 'empty') { this.set('state', 'loading'); - - // load the transformed data - // messageCenter.addMessage('info', 'Wczytuję HTML...'); - + messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...'); $.ajax({ - url: this.htmlURL, + url: this.serverURL, dataType: 'text', data: { revision: this.get('revision'), @@ -204,8 +364,10 @@ Editor.HTMLModel = Editor.Model.extend({ success: this.loadingSucceeded.bind(this), error: this.loadingFailed.bind(this) }); + return true; } - }, + return false; + }, loadingSucceeded: function(data) { if (this.get('state') != 'loading') { @@ -213,98 +375,38 @@ Editor.HTMLModel = Editor.Model.extend({ } this.set('data', data); this.set('state', 'synced'); + messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)'); }, - loadingFailed: function(response) { + loadingFailed: function(response) + { if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } - var err = parseXHRError(response); + var message = parseXHRError(response); - this.set('error', '

Nie udało się wczytać widoku HTML:

' + err.error_message); - this.set('state', 'error'); - }, - - getXMLPart: function(elem, callback) - { - var path = elem.attr('x-pointer'); - if(!this.xmlParts[path]) - this.loadXMLPart(elem, callback); - else - callback(path, this.xmlParts[path]); - }, - - loadXMLPart: function(elem, callback) - { - var path = elem.attr('x-pointer'); - var self = this; - - $.ajax({ - url: this.dataURL, - dataType: 'text', - data: { - revision: this.get('revision'), - user: this.document.get('user'), - chunk: path - // format: 'nl' - }, - success: function(data) { - self.xmlParts[path] = data; - console.log(data); - callback(path, data); - }, - // TODO: error handling - error: function(data) { - console.log('Failed to load fragment'); - callback(undefined, undefined); - } - }); - }, - - putXMLPart: function(elem, data) { - var self = this; - - var path = elem.attr('x-pointer'); - this.xmlParts[path] = data; - - this.set('state', 'dirty'); - - /* re-render the changed fragment */ - $.ajax({ - url: this.renderURL, - type: "POST", - dataType: 'text; charset=utf-8', - data: { - fragment: data, - chunk: path - // format: 'nl' - }, - success: function(htmldata) { - elem.html(htmldata); - self.set('state', 'dirty'); - } - }); + this.set('error', '

Błąd przy ładowaniu XML

'+message+'

'); + this.set('state', 'error'); + messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-('); }, - + save: function(message) { if (this.get('state') == 'dirty') { this.set('state', 'updating'); - + messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...'); + var payload = { - chunks: $.toJSON(this.xmlParts), + contents: this.get('data'), revision: this.get('revision'), user: this.document.get('user') }; - if (message) { payload.message = message; } - - console.log(payload) - + $.ajax({ - url: this.dataURL, + url: this.serverURL, type: 'post', dataType: 'json', data: payload, @@ -314,38 +416,45 @@ Editor.HTMLModel = Editor.Model.extend({ return true; } return false; - }, - + saveSucceeded: function(data) { if (this.get('state') != 'updating') { alert('erroneous state:', this.get('state')); } - - // flush the cache - this.xmlParts = {}; - this.set('revision', data.revision); this.set('state', 'updated'); + messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)'); }, - + saveFailed: function() { if (this.get('state') != 'updating') { alert('erroneous state:', this.get('state')); - } + } + messageCenter.addMessage('error', 'xmlsave', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-('); this.set('state', 'dirty'); }, - + // For debbuging set: function(property, value) { if (property == 'state') { console.log(this.description(), ':', property, '=', value); } return this._super(property, value); + }, + + dataChanged: function(property, value) { + if (this.get('state') == 'synced') { + this.set('state', 'dirty'); + } + }, + + dispose: function() { + this.removeObserver(this); + this._super(); } }); - Editor.ImageGalleryModel = Editor.Model.extend({ _className: 'Editor.ImageGalleryModel', serverURL: null, @@ -360,6 +469,22 @@ Editor.ImageGalleryModel = Editor.Model.extend({ this.pages = []; }, + setGallery: function(path) { + $.ajax({ + url: this.serverURL, + type: 'post', + data: { + path: path, + }, + success: this.settingGallerySucceeded.bind(this) + }); + }, + + settingGallerySucceeded: function(data) { + console.log('settingGallerySucceeded'); + this.load(true); + }, + load: function(force) { if (force || this.get('state') == 'empty') { console.log("setting state"); @@ -449,7 +574,7 @@ Editor.DocumentModel = Editor.Model.extend({ this.contentModels = { 'xml': new Editor.XMLModel(this, data.text_url), - 'html': new Editor.HTMLModel(this, data.text_url, data.html_url), + 'html': new Editor.HTMLModel(this, data.text_url), 'gallery': new Editor.ImageGalleryModel(this, data.gallery_url) }; @@ -522,7 +647,7 @@ Editor.DocumentModel = Editor.Model.extend({ revision: this.get('revision'), user: this.get('user') }, - complete: this.updateCompleted.bind(this), + complete: this.updateCompleted.bind(this) }); }, @@ -596,10 +721,13 @@ Editor.DocumentModel = Editor.Model.extend({ mergeCompleted: function(xhr, textStatus) { console.log(xhr.status, xhr.responseText); var response = parseXHRResponse(xhr); - if(response.success) - { - if( (response.data.result == 'no-op') - || (response.data.shared_timestamp == response.data.shared_parent_timestamp)) + + if(response.success) { + + if( (response.data.result == 'no-op') || + ( response.data.shared_parent_timestamp + && response.data.shared_timestamp + && (response.data.shared_timestamp == response.data.shared_parent_timestamp)) ) { if( (response.data.revision) && (response.data.revision != this.get('revision')) ) {