X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/3193141f55df20910cf8ba35f9e669d79c90d3f4..b02d71628de3e39c79abd257096281bf7fa1291d:/platforma/static/js/models.js
diff --git a/platforma/static/js/models.js b/platforma/static/js/models.js
old mode 100644
new mode 100755
index 6399c037..dae64d8a
--- a/platforma/static/js/models.js
+++ b/platforma/static/js/models.js
@@ -36,85 +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+'');
+ return this.updateWithWLML($element, s);
+ },
+
+ updateWithWLML: function($element, text)
+ {
+ // filter the string
+ text = text.replace(/\/\s+/g, '
');
+ try {
+ var chunk = this.parser.parseFromString("
Ź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:'+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, @@ -125,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') { @@ -150,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(); @@ -164,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'), @@ -202,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') { @@ -211,95 +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 message = parseXHRError(response); - this.set('error', 'Nie udaÅo siÄ wczytaÄ widoku HTML:
' + message); - this.set('state', 'error'); - }, - - getXMLPart: function(elem, callback) - { - var path = elem.attr('wl2o:path'); - if(!this.xmlParts[path]) - this.loadXMLPart(elem, callback); - else - callback(path, this.xmlParts[path]); - }, - - loadXMLPart: function(elem, callback) - { - var path = elem.attr('wl2o:path'); - var self = this; - - $.ajax({ - url: this.dataURL, - dataType: 'text', - data: { - revision: this.get('revision'), - user: this.document.get('user'), - part: path - }, - success: function(data) { - self.xmlParts[path] = 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('wl2o:path'); - this.xmlParts[path] = data; - - this.set('state', 'unsynced'); - - /* re-render the changed fragment */ - $.ajax({ - url: this.renderURL, - type: "POST", - dataType: 'text; charset=utf-8', - data: { - fragment: data, - part: path - }, - success: function(htmldata) { - elem.replaceWith(htmldata); - self.set('state', 'dirty'); - } - }); + this.set('error', ''+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, @@ -309,45 +416,52 @@ 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, data: [], state: 'empty', - init: function(serverURL) { + init: function(document, serverURL) { this._super(); this.set('state', 'empty'); this.serverURL = serverURL; @@ -355,18 +469,40 @@ 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"); this.set('state', 'loading'); + console.log("going ajax"); $.ajax({ url: this.serverURL, dataType: 'json', - success: this.loadingSucceeded.bind(this) + success: this.loadingSucceeded.bind(this), + error: this.loadingFailed.bind(this) }); } }, - loadingSucceeded: function(data) { + loadingSucceeded: function(data) + { + console.log("success"); + if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } @@ -382,6 +518,16 @@ Editor.ImageGalleryModel = Editor.Model.extend({ this.set('state', 'synced'); }, + loadingFailed: function(data) { + console.log("failed"); + + if (this.get('state') != 'loading') { + alert('erroneous state:', this.get('state')); + } + + this.set('state', 'error'); + }, + set: function(property, value) { if (property == 'state') { console.log(this.description(), ':', property, '=', value); @@ -393,7 +539,7 @@ Editor.ImageGalleryModel = Editor.Model.extend({ Editor.DocumentModel = Editor.Model.extend({ _className: 'Editor.DocumentModel', - data: null, // name, text_url, user_revision, latest_shared_rev, parts_url, dc_url, size, merge_url + data: null, // name, text_url, revision, latest_shared_rev, parts_url, dc_url, size, merge_url contentModels: {}, state: 'empty', errors: '', @@ -423,12 +569,12 @@ Editor.DocumentModel = Editor.Model.extend({ this.set('data', data); this.set('state', 'synced'); - this.set('revision', data.user_revision); + this.set('revision', data.revision); this.set('user', data.user); 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) }; @@ -446,8 +592,8 @@ Editor.DocumentModel = Editor.Model.extend({ alert('erroneous state:', this.get('state')); } - var message = parseXHRError(response); - this.set('error', ''+message+"
"); + var err = parseXHRError(response); + this.set('error', ''+err.error_message+"
"); this.set('state', 'error'); }, @@ -464,12 +610,13 @@ Editor.DocumentModel = Editor.Model.extend({ for (key in this.contentModels) { if (this.contentModels[key].guid() == contentModel.guid()) { this.contentModels[key].set('state', 'synced'); - this.data.user_revision = this.contentModels[key].get('revision'); + this.revision = this.contentModels[key].get('revision'); + } } for (key in this.contentModels) { if (this.contentModels[key].guid() != contentModel.guid()) { - this.contentModels[key].set('revision', this.data.user_revision); + this.contentModels[key].set('revision', this.revision); this.contentModels[key].set('state', 'empty'); } } @@ -487,60 +634,81 @@ Editor.DocumentModel = Editor.Model.extend({ update: function() { this.set('state', 'loading'); - messageCenter.addMessage('info', 'Uaktualniam dokument...'); + + messageCenter.addMessage('info', 'doc_update', + 'Uaktualniam dokument...'); + $.ajax({ url: this.data.merge_url, dataType: 'json', type: 'post', data: { type: 'update', - revision: this.revision, - user: this.user + revision: this.get('revision'), + user: this.get('user') }, - complete: this.updateCompleted.bind(this), - success: function(data) { - this.set('updateData', data); - }.bind(this) + complete: this.updateCompleted.bind(this) }); }, - updateCompleted: function(xhr, textStatus) { - console.log(xhr.status, textStatus); - if (xhr.status == 200) { // Sukces - this.data = this.get('updateData'); - this.revision = this.data.user_revision; - this.user = this.data.user; - - messageCenter.addMessage('info', null, 'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision, - 'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision); + updateCompleted: function(xhr, textStatus) + { + console.log(xhr.status, xhr.responseText); + var response = parseXHRResponse(xhr); + if(response.success) + { + if( (response.data.result == 'no-op') + || (response.data.timestamp == response.data.parent_timestamp)) + { + if( (response.data.revision) && (response.data.revision != this.get('revision')) ) + { + // we're out of sync + this.set('state', 'unsynced'); + return; + } + + messageCenter.addMessage('info', 'doc_update', + 'Już posiadasz najbardziej aktualnÄ wersjÄ.'); + this.set('state', 'synced'); + return; + } + + // result: success + this.set('revision', response.data.revision); + this.set('user', response.data.user); + + messageCenter.addMessage('info', 'doc_update', + 'Uaktualnienie dokumentu do wersji ' + response.data.revision); + for (var key in this.contentModels) { - this.contentModels[key].set('revision', this.data.user_revision); + this.contentModels[key].set('revision', this.get('revision') ); this.contentModels[key].set('state', 'empty'); } - messageCenter.addMessage('success', null, 'UaktualniÅem dokument do najnowszej wersji :-)'); - } else if (xhr.status == 202) { // Wygenerowano PullRequest (tutaj?) - } else if (xhr.status == 204) { // Nic nie zmieniono - messageCenter.addMessage('info', null, 'Nic siÄ nie zmieniÅo od ostatniej aktualizacji. Po co mam uaktualniaÄ?'); - } else if (xhr.status == 409) { // Konflikt podczas operacji - messageCenter.addMessage('error', null, 'WystÄ piÅ konflikt podczas aktualizacji. PÄdź po programistów! :-('); - } else if (xhr.status == 500) { - messageCenter.addMessage('critical', null, 'BÅÄ d serwera. PÄdź po programistów! :-('); + + this.set('state', 'synced'); + return; } - this.set('state', 'synced'); - this.set('updateData', null); + + // no success means trouble + messageCenter.addMessage(response.error_level, 'doc_update', + response.error_message); + + this.set('state', 'unsynced'); }, merge: function(message) { this.set('state', 'loading'); - messageCenter.addMessage('info', null, 'Scalam dokument z gÅównym repozytorium...'); + messageCenter.addMessage('info', 'doc_merge', + 'Scalam dokument z gÅównym repozytorium...'); + $.ajax({ url: this.data.merge_url, type: 'post', dataType: 'json', data: { type: 'share', - revision: this.revision, - user: this.user, + revision: this.get('revision'), + user: this.get('user'), message: message }, complete: this.mergeCompleted.bind(this), @@ -551,29 +719,53 @@ Editor.DocumentModel = Editor.Model.extend({ }, mergeCompleted: function(xhr, textStatus) { - console.log(xhr.status, textStatus); - if (xhr.status == 200) { // Sukces - this.data = this.get('updateData'); - this.revision = this.data.user_revision; - this.user = this.data.user; - - for (var key in this.contentModels) { - this.contentModels[key].set('revision', this.revision); - this.contentModels[key].set('state', 'empty'); + console.log(xhr.status, xhr.responseText); + var response = parseXHRResponse(xhr); + + 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')) ) + { + // we're out of sync + this.set('state', 'unsynced'); + return; + } + + messageCenter.addMessage('info', 'doc_merge', + 'Twoja aktualna wersja nie różni siÄ od ostatnio zatwierdzonej.'); + this.set('state', 'synced'); + return; + } + + if( response.data.result == 'accepted') + { + messageCenter.addMessage('info', 'doc_merge', + 'ProÅba o zatwierdzenie zostaÅa przyjÄta i oczekujÄ na przyjÄcie.'); + this.set('state', 'synced'); + return; } - messageCenter.addMessage('success', null, 'ScaliÅem dokument z gÅównym repozytorium :-)'); - } else if (xhr.status == 202) { // Wygenerowano PullRequest - messageCenter.addMessage('success', null, 'WysÅaÅem proÅbÄ o scalenie dokumentu z gÅównym repozytorium.'); - } else if (xhr.status == 204) { // Nic nie zmieniono - messageCenter.addMessage('info', null, 'Nic siÄ nie zmieniÅo od ostatniego scalenia. Po co mam scalaÄ?'); - } else if (xhr.status == 409) { // Konflikt podczas operacji - messageCenter.addMessage('error', null, 'WystÄ piÅ konflikt podczas scalania. PÄdź po programistów! :-('); - } else if (xhr.status == 500) { - messageCenter.addMessage('critical', null, 'BÅÄ d serwera. PÄdź po programistów! :-('); + // result: success + this.set('revision', response.data.revision); + this.set('user', response.data.user); + + messageCenter.addMessage('info', 'doc_merge', + 'Twoja wersja dokumentu zostaÅa zatwierdzona.'); + + this.set('state', 'synced'); + return; } - this.set('state', 'synced'); - this.set('mergeData', null); + + // no success means trouble + messageCenter.addMessage(response.error_level, 'doc_merge', + response.error_message); + + this.set('state', 'unsynced'); }, // For debbuging