From: Lukasz Rekucki Date: Sun, 8 Nov 2009 13:49:11 +0000 (+0100) Subject: Merge branch 'master' into lqc-trunk X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/061f517181630ee982c33aee63965b18965fd5aa?hp=8d490fc7810e0052bad3414b445b24276bbfca63 Merge branch 'master' into lqc-trunk --- diff --git a/apps/api/handlers/library_handlers.py b/apps/api/handlers/library_handlers.py index 7e90e313..e2d986ee 100755 --- a/apps/api/handlers/library_handlers.py +++ b/apps/api/handlers/library_handlers.py @@ -15,8 +15,8 @@ from django.db import IntegrityError import librarian import librarian.html import difflib +import wlrepo -from wlrepo import * from explorer.models import GalleryForDocument # internal imports @@ -151,7 +151,7 @@ class LibraryHandler(BaseHandler): import traceback # rollback branch creation lib._rollback() - raise LibraryException(traceback.format_exc()) + raise wlrepo.LibraryException(traceback.format_exc()) url = reverse('document_view', args=[doc.id]) @@ -163,12 +163,12 @@ class LibraryHandler(BaseHandler): url = url ) finally: lock.release() - except LibraryException, e: + except wlrepo.LibraryException, e: import traceback return response.InternalError().django_response({ "reason": traceback.format_exc() }) - except DocumentAlreadyExists: + except wlrepo.DocumentAlreadyExists: # Document is already there return response.EntityConflict().django_response({ "reason": "already-exists", @@ -178,6 +178,7 @@ class LibraryHandler(BaseHandler): # # Document Handlers # + class DiffHandler(BaseHandler): allowed_methods = ('GET',) @@ -222,7 +223,7 @@ class DocumentHandler(BaseHandler): try: doc = lib.document(docid, user, rev=rev) - except RevisionMismatch, e: + except wlrepo.RevisionMismatch, e: # the document exists, but the revision is bad return response.EntityNotFound().django_response({ 'reason': 'revision-mismatch', @@ -230,7 +231,7 @@ class DocumentHandler(BaseHandler): 'docid': docid, 'user': user, }) - except RevisionNotFound, e: + except wlrepo.RevisionNotFound, e: # the user doesn't have this document checked out # or some other weird error occured # try to do the checkout @@ -250,7 +251,7 @@ class DocumentHandler(BaseHandler): 'docid': docid, 'user': user, }) - except RevisionNotFound, e: + except wlrepo.RevisionNotFound, e: return response.EntityNotFound().django_response({ 'reason': 'document-not-found', 'message': e.message, @@ -313,7 +314,7 @@ class DocumentHTMLHandler(BaseHandler): "with-paths": 'boolean(1)', }) - except (EntryNotFound, RevisionNotFound), e: + except (wlrepo.EntryNotFound, wlrepo.RevisionNotFound), e: return response.EntityNotFound().django_response({ 'reason': 'not-found', 'message': e.message}) except librarian.ValidationError, e: diff --git a/apps/explorer/views.py b/apps/explorer/views.py old mode 100644 new mode 100755 index 5ec452ae..a67d27ca --- a/apps/explorer/views.py +++ b/apps/explorer/views.py @@ -11,6 +11,8 @@ from django.core.urlresolvers import reverse from django.http import HttpResponse from django.utils import simplejson as json from django.views.generic.simple import direct_to_template +from django.shortcuts import render_to_response +from django.template import RequestContext from django.contrib.auth.decorators import login_required from explorer import forms @@ -32,12 +34,14 @@ def display_editor(request, path): user = request.GET.get('user', request.user.username) gallery_form = forms.GalleryChoiceForm() - return direct_to_template(request, 'explorer/editor.html', extra_context={ + return render_to_response('explorer/editor.html', + # mimetype="text/html", + dictionary = { 'fileid': path, 'euser': user, - 'gallery_form': gallery_form, - }) - + 'gallery_from': gallery_form, + }, context_instance=RequestContext(request)) + # # View all files # @@ -119,3 +123,11 @@ def pull_requests(request): return direct_to_template(request, 'manager/pull_request.html', extra_context = {'objects': objects} ) + + +# +# Testing +# +def renderer_test(request): + return direct_to_template(request, 'renderer.html', mimetype="application/xhtml+xml", + extra_context = {} ) diff --git a/platforma/static/css/html.css b/platforma/static/css/html.css index 9db90bed..acbeac2c 100755 --- a/platforma/static/css/html.css +++ b/platforma/static/css/html.css @@ -5,7 +5,8 @@ font-size: 16px; font-family: "Georgia", "Times New Roman", serif; line-height: 1.5em; - padding: 3em; + padding: 3em; + padding-left: 55px; } .htmlview * { @@ -25,12 +26,18 @@ text-decoration: none; } +.htmlview a:hover { + text-decoration: none; +} + .htmlview h1 { font-size: 3em; margin: 1.5em 0; text-align: center; line-height: 1.5em; font-weight: bold; + + text-transform: capitalize; } .htmlview h2 { @@ -57,25 +64,6 @@ margin: 0; } -/* ======================== */ -/* = Footnotes and themes = */ -/* ======================== */ -.htmlview .theme-begin { - border-left: 0.1em solid #DDDDDD; - color: #777; - padding: 0 0.5em; - width: 7.5em; - font-style: normal; - font-weight: normal; - font-size: 16px; - float: right; - margin-right: -9.5em; - clear: both; - left: 40em; - line-height: 1.5em; - text-align: left; -} - .htmlview #footnotes div { margin: 1.5em 0 0 0; } @@ -226,22 +214,57 @@ margin: 1em; } -/* Przypisy */ -.htmlview .annotation { - vertical-align: super; - text-decoration: none; - font-size: 0.66em; + .parse-warning .message { + color: purple; + font-weight: bold; } -.htmlview a:hover { + +/* Motywy */ +/* ======================== */ +/* = Footnotes and themes = */ +/* ======================== */ + +.htmlview .begin, .htmlview .end { + background: green; +} + +.htmlview .motyw { + position: absolute; + right: -8em; + + min-width: 5em; + max-width: 8em; + + font-weight: normal; + font-size: 10pt; + font-variant: normal; text-decoration: none; + + padding: 0.2em 0.2em 0.2em 0.5em; + + border: 1px solid gray; + border-left: 2px dotted gray; + border-right: none; + + z-index: 1; + -moz-user-select: -moz-none; + -webkit-user-select: none; + user-select: none; } -/* .htmlview .annotation:before { - content: "[\2217]"; -} */ +/* + * Przypisy + */ -.htmlview span.annotation:before { +/* Znaczniki w tekście */ +.htmlview .annotation { + vertical-align: super; + text-decoration: none; + font-size: 0.66em; +} + +.htmlview .annotation:before { content: "[" counter(main) "]"; counter-increment: main; } @@ -250,94 +273,177 @@ background-color: #dfdfdf; } -.parse-warning .message { - color: purple; - font-weight: bold; +*.htmlview *.annotation-inline-box { + position: static; +} + +/* + * Przypisy w tekście + */ + .htmlview .annotation-inline-box > span[x-annotation-box] + { + display: none; + position: absolute; + + max-width: 36em; + + font-size: 8pt; + line-height: 12pt; + font-weight: normal; + font-style: normal; + + background: white; + border-color: gray; + border-width: 2px; + border-style: outset; + padding: 3px 5px; + + text-decoration: none; + z-index: 11; + } + + +/* + * Przypisy na końcu utworu (aktualnie nieuzywane) + */ +.htmlview .annotations-block { + counter-reset: secondary; +} + +.htmlview .annotations-block .annotation-body { + position: relative; + padding-left: 2.5em; + padding-top: 0.2em; +} + +.htmlview .annotations-block .annotation-backref { + position: absolute; + top: 0.4em; + left: -0.4em; + width: 2.5em; + text-align: right; + } +.htmlview .annotations-block .annotation-backref:before { + content: "[" counter(secondary) "]"; + counter-increment: secondary; +} /* * EDITABLE ELEMENTS */ -.htmlview *[x-editable] { - border: 2px solid white; - padding: 5px; +.htmlview *[x-editable] { + padding-left: 3px; } /* focused editable element */ -.htmlview *[x-editable]:hover -{ - background-color: #dfdfdf; - border: 2px solid black; - z-index: 100; -} +/* .htmlview *[x-editable]:hover +{ + +} */ .htmlview *[x-editable][x-open] { visibility: hidden; } -.htmlview *[x-editable] .context-menu { +.context-menu { position: absolute; - top: -28px; - left: -2px; - height: 24px; + top: 0px; + left: -50px; + width: 50px; text-align: center; - - font-size: 14px; - line-height: 24px; + font-size: 11px; + line-height: 15px; + font-weight: normal; font-style: normal; background-color: #dfdfdf; + margin: 0px; padding: 0px; + visibility: inherit; + display: block; - border-top: 2px solid black; - border-left: 2px solid black; - border-right: 2px solid black; + border-top: 1px solid black; + border-left: 1px solid black; + border-bottom: 1px solid black; - display: none; - visibility: visible; overflow: hidden; - -moz-border-radius-topright: 5px; - -moz-border-radius-topleft: 5px; + -moz-border-radius-topleft: 2px; + -moz-border-radius-bottomleft: 2px; - -webkit-border-top-right-radius: 5px; - -webkit-border-top-left-radius: 5px; + -webkit-border-top-left-radius: 2px; + -webkit-border-bottom-left-radius: 2px; z-index: 3000; } -.htmlview *[x-editable] *.context-menu * { - padding: 5px; +.context-menu * { + margin: 0px; + display: block; + + -moz-user-select: -moz-none; + -webkit-user-select: none; + + padding: 2px; + cursor: pointer; + border-bottom: 1px solid black; } -.htmlview *[x-editable] *.context-menu *:hover { - background-color: yellow; - z-index: 3100; +.context-menu *:last-child { + cursor: pointer; + border-bottom: none; +} + +.context-menu *:hover { + background-color: orange; } /* * VISIBILITY RULES */ -.htmlview *[x-editable]:hover *.default-menu { - display: block; +.default-menu { + visibility: inherit; + opacity: 0.2; } -.htmlview *[x-editable][x-open] *.default-menu { - display: none; +.default-menu:hover { + opacity: 1; } -.htmlview *[x-editable][x-open] *.edit-menu { - display: block; +.htmlview *[x-annotation-box] > .default-menu { + opacity: 1; +} + +.htmlview *[x-editable][x-open] > .default-menu { + visibility: hidden; +} +.htmlview *[x-editable][x-open] *[x-annotation-box] > .default-menu { + visibility: hidden; +} + +.htmlview *[x-editable] > .edit-menu { + visibility: hidden; +} +.htmlview *[x-editable] *[x-annotation-box] > .edit-menu { + visibility: hidden; +} + +.htmlview *[x-editable][x-open] > .edit-menu { + visibility: visible; +} +.htmlview *[x-editable][x-open] *[x-annotation-box] > .edit-menu { + visibility: visible; } .html-editarea { - border: 2px solid black; + border: 0px; background-color: gray; padding: 1px; @@ -346,7 +452,8 @@ .html-editarea textarea { - border: 0px; + border: 2px solid black; + margin: 0px; padding: 0px; @@ -356,24 +463,4 @@ z-index: 0; font-size: 10pt; background-color: ivory; -} - -.html-editarea p.html-editarea-toolbar { - position: absolute; - background: gray; - - bottom: -26px; - height: 24px; - - left: 0px; - right: 0px; - - border: 2px solid black; - - margin: 0px; - padding: 0px; - - z-index: 100; -} - - +} \ No newline at end of file diff --git a/platforma/static/css/master.css b/platforma/static/css/master.css index 8af8ad48..8200037c 100755 --- a/platforma/static/css/master.css +++ b/platforma/static/css/master.css @@ -153,9 +153,9 @@ text#commit-dialog-message { } .view-overlay { - z-index: 1000; - background: #FFF; - opacity: 0.8; + display: none; + z-index: 0; + background: #FFF; text-align: center; vertical-align: middle; @@ -165,10 +165,11 @@ text#commit-dialog-message { top: 0; bottom: 0; - user-select: 'none'; + /* user-select: 'none'; -webkit-user-select: 'none'; -khtml-user-select: 'none'; - -moz-user-select: 'none'; + -moz-user-select: 'none'; */ + overflow: 'hidden'; } diff --git a/platforma/static/js/button_scripts.js b/platforma/static/js/button_scripts.js old mode 100644 new mode 100755 index 40f45f6f..4af9d72d --- a/platforma/static/js/button_scripts.js +++ b/platforma/static/js/button_scripts.js @@ -6,6 +6,7 @@ function ScriptletCenter() { var text = this.XMLEditorSelectedText(context); var start_tag = '<'+params.tag; + var move_cursor = false; for (var attr in params.attrs) { start_tag += ' '+attr+'="' + params.attrs[attr] + '"'; @@ -39,14 +40,23 @@ function ScriptletCenter() output += token; } else { - output = start_tag + end_tag; + if(params.nocontent) { + output = "<"+params.tag +" />"; + } + else { + output = start_tag + end_tag; + move_cursor = true; + } } this.XMLEditorReplaceSelectedText(context, output); - if (text.length == 0) { - this.XMLEditorMoveCursorForward(context, -params.tag.length-3); - } + try { + if (move_cursor) { + this.XMLEditorMoveCursorForward(context, params.tag.length+2); + } + } catch(e) {} + }.bind(this); this.scriptlets['lineregexp'] = function(context, params) { diff --git a/platforma/static/js/lib/codemirror/codemirror.js b/platforma/static/js/lib/codemirror/codemirror.js old mode 100644 new mode 100755 index 97e2657b..4eb5fb73 --- a/platforma/static/js/lib/codemirror/codemirror.js +++ b/platforma/static/js/lib/codemirror/codemirror.js @@ -55,9 +55,9 @@ var CodeMirror = (function(){ function wrapLineNumberDiv(place) { return function(node) { - var container = document.createElement("DIV"), - nums = document.createElement("DIV"), - scroller = document.createElement("DIV"); + var container = document.createElement("div"), + nums = document.createElement("div"), + scroller = document.createElement("div"); container.style.position = "relative"; nums.style.position = "absolute"; nums.style.height = "100%"; @@ -98,7 +98,7 @@ var CodeMirror = (function(){ function update() { var diff = 20 + Math.max(doc.body.offsetHeight, frame.offsetHeight) - scroller.offsetHeight; for (var n = Math.ceil(diff / 10); n > 0; n--) { - var div = document.createElement("DIV"); + var div = document.createElement("div"); div.appendChild(document.createTextNode(nextNum++)); scroller.appendChild(div); } @@ -120,7 +120,7 @@ var CodeMirror = (function(){ this.options = options = options || {}; setDefaults(options, CodeMirrorConfig); - var frame = this.frame = document.createElement("IFRAME"); + var frame = this.frame = document.createElement("iframe"); if (options.iframeClass) frame.className = options.iframeClass; frame.frameBorder = 0; frame.src = "javascript:false;"; diff --git a/platforma/static/js/models.js b/platforma/static/js/models.js index 2c36e2bb..59a285a1 100755 --- a/platforma/static/js/models.js +++ b/platforma/static/js/models.js @@ -36,87 +36,192 @@ 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) + { + 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); + return this.serializer.serializeToString(result.documentElement); + }, + + updateWithWLML: function($element, text) + { + // filter the string + text = text.replace(/\/\s+/g, '
'); + var chunk = this.parser.parseFromString(""+text+"", "text/xml"); + + var errors = $('parsererror', chunk); + + // check if chunk is parsable + if(errors.length > 0) + throw {text: errors.text(), html: errors.html()}; + + var result = this.htmlXSL.transformToFragment(chunk, document); + + console.log("RESULT", this.serializer.serializeToString(result)); + + if(!result) + throw "WLML->HTML transformation failed."; + + $element.replaceWith(result); + this.set('state', 'dirty'); + }, + + 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) { + this.loadingFailed(); + } + }, + + wlmlXSLLoadSuccess: function(data) + { + try { + this.wlmlXSL = this.createXSLT(data); + + if(this.wlmlXSL && this.htmlXSL && this.rawText) + this.loadSuccess(); + } catch(e) { + this.loadingFailed(); + } + }, + + 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 +232,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 +257,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 +271,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 +309,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 +320,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, callback) { - 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) { - callback(elem, 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 +361,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, @@ -465,7 +519,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) }; @@ -538,7 +592,7 @@ Editor.DocumentModel = Editor.Model.extend({ revision: this.get('revision'), user: this.get('user') }, - complete: this.updateCompleted.bind(this), + complete: this.updateCompleted.bind(this) }); }, diff --git a/platforma/static/js/views/gallery.js b/platforma/static/js/views/gallery.js old mode 100644 new mode 100755 index a403c9ef..d6844c62 --- a/platforma/static/js/views/gallery.js +++ b/platforma/static/js/views/gallery.js @@ -10,7 +10,8 @@ var ImageGalleryView = View.extend({ init: function(element, model, parent, template) { console.log("init for gallery"); - this._super(element, model, template); + var submodel = model.contentModels['gallery']; + this._super(element, submodel, template); this.parent = parent; console.log("gallery model", this.model); diff --git a/platforma/static/js/views/html.js b/platforma/static/js/views/html.js index 211b33bc..0b1234ce 100755 --- a/platforma/static/js/views/html.js +++ b/platforma/static/js/views/html.js @@ -6,31 +6,47 @@ var HTMLView = View.extend({ template: 'html-view-template', init: function(element, model, parent, template) { - this._super(element, model, template); - this.parent = parent; + var submodel = model.contentModels['html']; + this._super(element, submodel, template); + this.parent = parent; this.model - .addObserver(this, 'data', this.modelDataChanged.bind(this)) + .addObserver(this, 'data', this.modelDataChanged.bind(this)) .addObserver(this, 'state', this.modelStateChanged.bind(this)); - - $('.htmlview', this.element).html(this.model.get('data')); - - this.$menuTemplate = $(render_template('html-view-frag-menu-template', this)); this.modelStateChanged('state', this.model.get('state')); + this.modelDataChanged('data', this.model.get('data')); + this.model.load(); this.currentOpen = null; + this.currentFocused = null; + this.themeBoxes = []; }, - modelDataChanged: function(property, value) { - $('.htmlview', this.element).html(value); + modelDataChanged: function(property, value) + { + if(!value) return; + + // the xml model changed + var container = $('.htmlview', this.element); + container.empty(); + container.append(value); + this.updatePrintLink(); - var self = this; - $("*[x-editable]").each(function() { - $(this).append( self.$menuTemplate.clone() ); - }); + /* mark themes */ + /* $(".theme-ref", this.$docbase).each(function() { + var id = $(this).attr('x-theme-class'); + + var end = $("span.theme-end[x-theme-class = " + id+"]"); + var begin = $("span.theme-begin[x-theme-class = " + id+"]"); + + var h = $(this).outerHeight(); + + h = Math.max(h, end.offset().top - begin.offset().top); + $(this).css('height', h); + }); */ }, updatePrintLink: function() { @@ -54,33 +70,77 @@ var HTMLView = View.extend({ } else if (value == 'error') { this.freeze(this.model.get('error')); $('.xml-editor-ref', this.overlay).click( - function(event) { - console.log("Sending scroll rq.", this); - try { - var href = $(this).attr('href').split('-'); - var line = parseInt(href[1]); - var column = parseInt(href[2]); + function(event) { + console.log("Sending scroll rq.", this); + try { + var href = $(this).attr('href').split('-'); + var line = parseInt(href[1]); + var column = parseInt(href[2]); - $(document).trigger('xml-scroll-request', {line:line, column:column}); - } catch(e) { - console.log(e); - } + $(document).trigger('xml-scroll-request', { + line:line, + column:column + }); + } catch(e) { + console.log(e); + } - return false; - }); + return false; + }); } }, render: function() { - this.element.unbind('click'); + if(this.$docbase) + this.$docbase.unbind('click'); + + if(this.$printLink) + this.$printLink.unbind(); + + if(this.$addThemeButton) + this.$addThemeButton.unbind(); - if(this.$printLink) this.$printLink.unbind(); this._super(); - this.$printLink = $('.html-print-link', this.element); + + this.$printLink = $('.htmlview-toolbar .html-print-link', this.element); + this.$docbase = $('.htmlview', this.element); + this.$addThemeButton = $('.htmlview-toolbar .html-add-motive', this.element); + + this.$debugButton = $('.htmlview-toolbar .html-serialize', this.element); + this.updatePrintLink(); + this.$docbase.bind('click', this.itemClicked.bind(this)); + this.$addThemeButton.click( this.addTheme.bind(this) ); + this.$debugButton.click( this.serialized.bind(this) ); + }, + + serialized: function() { + this.model.set('state', 'dirty'); + console.log( this.model.serializer.serializeToString(this.model.get('data')) ); + }, + + renderPart: function($e, html) { + // exceptions aren't good, but I don't have a better idea right now + if($e.attr('x-annotation-box')) { + // replace the whole annotation + var $p = $e.parent(); + $p.html(html); + var $box = $('*[x-annotation-box]', $p); + $box.append( this.$menuTemplate.clone() ); + + if(this.currentFocused && $p[0] == this.currentFocused[0]) + { + this.currentFocused = $p; + $box.css({ + 'display': 'block' + }); + } + + return; + } - this.element.bind('click', this.itemClicked.bind(this)); - // this.element.bind('mouseover', this.itemHover.bind(this)); + $e.html(html); + $e.append( this.$menuTemplate.clone() ); }, reload: function() { @@ -99,26 +159,99 @@ var HTMLView = View.extend({ console.log('click:', event, event.ctrlKey, event.target); var $e = $(event.target); - if($e.hasClass('edit-button')) - this.openForEdit( this.editableFor($e) ); + if($e.hasClass('annotation')) + { + if(this.currentOpen) return false; + + var $p = $e.parent(); + if(this.currentFocused) + { + console.log(this.currentFocused, $p); + if($p[0] == this.currentFocused[0]) { + console.log('unfocus of current'); + this.unfocusAnnotation(); + return false; + } + + console.log('switch unfocus'); + this.unfocusAnnotation(); + } + + this.focusAnnotation($p); + return false; + } + + /* + * Clicking outside of focused area doesn't unfocus by default + * - this greatly simplifies the whole click check + */ + + if( $e.hasClass('theme-ref') ) + { + console.log($e); + this.selectTheme($e.attr('x-theme-class')); + return false; + } + + /* other buttons */ + try { + if($e.hasClass('edit-button')) + this.openForEdit( this.editableFor($e) ); + + if($e.hasClass('accept-button')) + this.closeWithSave( this.editableFor($e) ); + + if($e.hasClass('reject-button')) + this.closeWithoutSave( this.editableFor($e) ); + } catch(e) { + messageCenter.addMessage('error', "wlsave", 'Błąd:' + e.text); + } + + return false; + }, + + unfocusAnnotation: function() + { + if(!this.currentFocused) + { + console.log('Redundant unfocus'); + return false; + } + + if(this.currentOpen + && this.currentOpen.is("*[x-annotation-box]") + && this.currentOpen.parent()[0] == this.currentFocused[0]) + { + console.log("Can't unfocus open box"); + return false; + } - if($e.hasClass('accept-button')) - this.closeWithSave( this.editableFor($e) ); + var $box = $("*[x-annotation-box]", this.currentFocused); + $box.css({ + 'display': 'none' + }); + // this.currentFocused.removeAttr('x-focused'); + // this.currentFocused.hide(); + this.currentFocused = null; + }, - if($e.hasClass('reject-button')) - this.closeWithoutSave( this.editableFor($e) ); + focusAnnotation: function($e) { + this.currentFocused = $e; + var $box = $("*[x-annotation-box]", $e); + $box.css({ + 'display': 'block' + }); + + // $e.attr('x-focused', 'focused'); }, closeWithSave: function($e) { var $edit = $e.data('edit-overlay'); var newText = $('textarea', $edit).val(); - this.model.putXMLPart($e, newText, function($e, html) { - this.renderPart($e, html); - $edit.remove(); - $e.removeAttr('x-open'); - }.bind(this) ); - this.currentOpen = null; + this.model.updateWithWLML($e, newText); + $edit.remove(); + this.currentOpen = null; }, closeWithoutSave: function($e) { @@ -128,11 +261,6 @@ var HTMLView = View.extend({ this.currentOpen = null; }, - renderPart: function($e, html) { - $e.html(html); - $e.append( this.$menuTemplate.clone() ); - }, - editableFor: function($button) { var $e = $button; @@ -148,40 +276,148 @@ var HTMLView = View.extend({ if(!$e.attr('x-editable')) throw Exception("Click outside of editable") + console.log("Trigger", $button, " yields editable: ", $e); return $e; }, openForEdit: function($origin) { if(this.currentOpen && this.currentOpen != $origin) { - this.closeWithSave(this.currentOpen); - + this.closeWithSave(this.currentOpen); } - - // start edition on this node - var $overlay = $('
'); + + var $box = null - var x = $origin[0].offsetLeft; - var y = $origin[0].offsetTop; - var w = $origin.outerWidth(); - var h = $origin.innerHeight(); + // annotations overlay their sub box - not their own box // + if($origin.is(".annotation-inline-box")) + $box = $("*[x-annotation-box]", $origin); + else + $box = $origin; - $overlay.css({position: 'absolute', height: h, left: x, top: y, width: '95%'}); + var x = $box[0].offsetLeft; + var y = $box[0].offsetTop; + var w = $box.outerWidth(); + var h = $box.innerHeight(); + + console.log("Edit origin:", $origin, " box:", $box); + console.log("offsetParent:", $box[0].offsetParent); + console.log("Dimensions: ", x, y, w , h); + + // start edition on this node + var $overlay = $('
'); - $origin.offsetParent().append($overlay); - $origin.data('edit-overlay', $overlay); - - this.model.getXMLPart($origin, function(path, data) { - $('textarea', $overlay).val(data); - }); + $overlay.css({ + position: 'absolute', + height: h, + left: x, + top: y, + width: '95%' + }); + + try { + $('textarea', $overlay).val( this.model.asWLML($origin[0]) ); + + if($origin.is(".annotation-inline-box")) + { + if(this.currentFocused) { + // if some other is focused + if($origin[0] != this.currentFocused[0]) { + this.unfocusAnnotation(); + this.focusAnnotation($origin); + } + // already focues + } + else { // nothing was focused + this.focusAnnotation($origin); + } + } + else { // this item is not focusable + if(this.currentFocused) this.unfocusAnnotation(); + } + $($box[0].offsetParent).append($overlay); + $origin.data('edit-overlay', $overlay); + this.currentOpen = $origin; $origin.attr('x-open', 'open'); + } + catch(e) { + console.log("Can't open", e); + } + + return false; + }, + + addTheme: function() + { + var selection = window.getSelection(); + var n = selection.rangeCount; + + console.log("Range count:", n); + if(n == 0) + window.alert("Nie zaznaczono żadnego obszaru"); + + // for now allow only 1 range + if(n > 1) + window.alert("Zaznacz jeden obszar"); + + + // from this point, we will assume that the ranges are disjoint + for(var i=0; i < n; i++) + { + var range = selection.getRangeAt(i); + console.log(i, range.startContainer, range.endContainer); + var date = (new Date()).getTime(); + var random = Math.floor(4000000000*Math.random()); + var id = (''+date) + '-' + (''+random); + + var spoint = document.createRange(); + var epoint = document.createRange(); + + spoint.setStart(range.startContainer, range.startOffset); + epoint.setStart(range.endContainer, range.endOffset); + + // insert theme-ref + + var elem = $('Nowy motyw'); + elem.attr('x-attr-qname-'+id, 'id'); + elem.attr('x-attr-value-'+id, 'm'+id); + spoint.insertNode(elem[0]); + + // insert theme-begin + elem = $(''); + elem.attr('x-attr-qname-'+id, 'id'); + elem.attr('x-attr-value-'+id, 'b'+id); + spoint.insertNode(elem[0]); + + elem = $(''); + elem.attr('x-attr-qname-'+id, 'id'); + elem.attr('x-attr-value-'+id, 'e'+id); + epoint.insertNode(elem[0]); + } + + //selection.removeAllRanges(); + }, + + selectTheme: function(themeId) + { + var selection = document.getSelection(); - return false; + // remove current selection + selection.removeAllRanges(); + + var range = document.createRange(); + var s = $('#m'+themeId)[0]; + var e = $('#e'+themeId)[0]; + console.log('Selecting range:', themeId, range, s, e); + + if(s && e) { + range.setStartAfter(s); + range.setEndBefore(e); + selection.addRange(range); + } } - }); // Register view diff --git a/platforma/static/js/views/panel_container.js b/platforma/static/js/views/panel_container.js old mode 100644 new mode 100755 index 6dbddd98..f4bdc89f --- a/platforma/static/js/views/panel_container.js +++ b/platforma/static/js/views/panel_container.js @@ -26,9 +26,8 @@ var PanelContainerView = View.extend({ } if( value != 'empty') { - this.contentView = new klass($('.content-view', - this.element.get(0)), this.model.contentModels[value], this); - $('.panel-main-toolbar .refresh', this.element.get(0)).attr('disabled', null); + this.contentView = new klass($('.content-view', this.element.get(0)), this.model, this); + $('.panel-main-toolbar .refresh', this.element.get(0)).attr('disabled', null); } }, diff --git a/platforma/static/js/views/split.js b/platforma/static/js/views/split.js old mode 100644 new mode 100755 index 30eda4ab..a91cab60 --- a/platforma/static/js/views/split.js +++ b/platforma/static/js/views/split.js @@ -59,6 +59,7 @@ var SplitView = View.extend({ left: this.element.position().left }).appendTo(this.element); this.views.css("-webkit-user-select", "none"); // Safari selects A/B text on a move + this.splitbar.addClass(this.activeClass); this.leftViewOffset = this.leftView[0].offsetWidth - event.pageX; @@ -84,6 +85,11 @@ var SplitView = View.extend({ $(document) .unbind('mousemove.splitview') .unbind('mouseup.splitview'); + + //from beginResize: + // this.views.css("-webkit-user-select", "none"); // Safari selects A/B text on a move + // Restore it! + this.views.css("-webkit-user-select", "auto"); }, resized: function(event) { diff --git a/platforma/static/js/views/xml.js b/platforma/static/js/views/xml.js old mode 100644 new mode 100755 index 6f0b6fa6..0dd5453f --- a/platforma/static/js/views/xml.js +++ b/platforma/static/js/views/xml.js @@ -8,8 +8,11 @@ var XMLView = View.extend({ buttonToolbar: null, init: function(element, model, parent, template) { - this._super(element, model, template); + var submodel = model.contentModels['xml']; + this._super(element, submodel, template); + this.parent = parent; + this.buttonToolbar = new ButtonToolbarView( $('.xmlview-toolbar', this.element), this.model.toolbarButtonsModel, parent); diff --git a/platforma/static/xsl/html2wl_client.xsl b/platforma/static/xsl/html2wl_client.xsl new file mode 100755 index 00000000..726a404a --- /dev/null +++ b/platforma/static/xsl/html2wl_client.xsl @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + + + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + + + / + + + + + + \ No newline at end of file diff --git a/platforma/static/xsl/wl2html_client.xsl b/platforma/static/xsl/wl2html_client.xsl new file mode 100755 index 00000000..234ea5cf --- /dev/null +++ b/platforma/static/xsl/wl2html_client.xsl @@ -0,0 +1,754 @@ + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + + + + + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + + + + +

+ + + + + +

+
+ + + + +

+ + + + + +

+
+ + + + +

+ + + + + +

+
+ + + + +

+ + + + + +

+
+ + + + + +
+ + + + +
+
+ + + + +
+ + + + +
+
+ + + + +
+ + + + +
+
+ + + +

+ + + + + +

+
+ + + + +
+ + + + +
+
+ + + +

+ + + + + +

+
+ + + +

+ + + + + +

+
+ + + + +

+ + + + + +

+
+ + + + + + + +

+ + + + + +

+
+ + + + +

+ + + + + +

+
+ + + + +

+ + + + + +

+
+ + + + + +

+ + + + + +

+
+ + + +

+ + + + + +

+
+ + + + + +

+ + + + + +

+
+ + + +

+ + + + + +

+
+ + + + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + +
+ + + + +
+
+ + + + + +

+ + + + + +

+
+ + + +

+ + + + + +

+
+ + + +

+ + + + + +

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +

+ + + +

+
+ + + + + + +
+
+ + + +

+ + + + +

+
+ + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + + + Unmatched tag + + + + + + Edit + + + OK + Cancel + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/platforma/templates/base.html b/platforma/templates/base.html old mode 100644 new mode 100755 index 590825a1..597a4320 --- a/platforma/templates/base.html +++ b/platforma/templates/base.html @@ -1,6 +1,6 @@ - + {% block title %}Platforma Redakcyjna{% block subtitle %}{% endblock subtitle %}{% endblock title%} @@ -21,9 +21,9 @@
{% block message-box %} {% endblock %}
-
{% block maincontent %} {% endblock %}
- +
{% block maincontent %} {% endblock %}
+ {% block extrabody %}{% endblock %} - + http://192.168.56.2:8000/editor/mickiewicz__pan_tadeusz__ksi%C4%99ga_1/ diff --git a/platforma/templates/explorer/editor.html b/platforma/templates/explorer/editor.html index 8930c944..2e8a6b4b 100755 --- a/platforma/templates/explorer/editor.html +++ b/platforma/templates/explorer/editor.html @@ -12,9 +12,8 @@ docID: '{{ fileid }}', userID: '{{ euser }}', docURL: '{% url document_view fileid %}{% if euser %}?user={{ euser|urlencode }}{% endif %}', - toolbarURL: '{% url toolbar_buttons %}', - renderURL: '{% url api.views.render %}', - staticURL: '{{ STATIC_URL }}' + toolbarURL: '{% url toolbar_buttons %}', + staticURL: '{{ STATIC_URL }}' } @@ -65,23 +64,13 @@ - - - - +{% endblock %} + +{% block maincontent %} + + +
+{% endblock %} + +{% block extrabody %} + + + +{% comment %} + +{% endcomment %} +{% endblock %} \ No newline at end of file diff --git a/platforma/urls.py b/platforma/urls.py old mode 100644 new mode 100755 index 498b5b2e..1f7372a3 --- a/platforma/urls.py +++ b/platforma/urls.py @@ -13,6 +13,8 @@ urlpatterns = patterns('', url(r'^$', 'explorer.views.file_list', name='file_list'), url(r'^file/upload', 'explorer.views.file_upload', name='file_upload'), + url(r'^renderer$', 'explorer.views.renderer_test'), + url(r'^management/pull-requests$', 'explorer.views.pull_requests'), # Editor panels @@ -26,6 +28,8 @@ urlpatterns = patterns('', url(r'^admin/doc/', include('django.contrib.admindocs.urls')), url(r'^admin/(.*)', admin.site.root), + + # Our über-restful api url(r'^api/', include('api.urls')),