From: zuber Date: Thu, 15 Oct 2009 16:33:42 +0000 (+0200) Subject: Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/3193141f55df20910cf8ba35f9e669d79c90d3f4 Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma Conflicts: platforma/templates/explorer/edit_dc.html platforma/templates/explorer/edit_text.html platforma/templates/explorer/file_html.html platforma/templates/explorer/split.html platforma/templates/explorer/split_success.html --- 3193141f55df20910cf8ba35f9e669d79c90d3f4 diff --cc platforma/static/css/master.css index ae51e475,00000000..cebb0dc4 mode 100644,000000..100644 --- a/platforma/static/css/master.css +++ b/platforma/static/css/master.css @@@ -1,415 -1,0 +1,336 @@@ +body { + margin: 0; + + font: 12px Helvetica, Verdana, sans-serif; + overflow: hidden; + background: #AAA; +} + +/* fix icon buttons */ + +button img { + vertical-align: middle; + margin: 0px; +} + +/* default form style hacks */ +select { + border: none; - margin-left: 0.1em; ++ margin-left: 0.1em;f +} + +#body-wrap { + margin: 0px; - padding: 0px; ++ padding: 0px; ++ position: fixed; ++ left: 0px; ++ right: 0px; ++ bottom: 0px; ++ top: 0px; +} + +#header { + position: absolute; + padding: 2px 0.5em; + background-color: #CDCDCD; /* !unused */ + border-bottom: 2px solid black; + + font-size: 14px; + + line-height: 26px; + vertical-align: middle; + + /* height: 30px; */ + top: 0px; left: 0px; right: 0px; + z-index: 300; +} + +#content { + position: absolute; + top: 32px; left: 0px; right: 0px; bottom: 0px; + overflow: auto; + background-color: white; +} + +#header #breadcrumbs { +} + +#header-right-toolbar { + position: absolute; + right: 1em; +} + +#header button { + vertical-align: middle; +} + - /* ========== */ - /* = Panels = */ - /* ========== */ - - /* #panels { - height: 100%; - width: 100%; - } - - .panel-wrap { - overflow: hidden; - position: absolute; - top: 0px; - bottom: 0px; - } - - #left-panel-wrap { - left: 0px; - width: 8px; - } - - #right-panel-wrap { - right: 0px; - width: auto; - left: 8px; - } - - .panel-content { - position: absolute; - overflow: auto; - overflow-x: hidden; - top: 25px; left: 0px; bottom:0px; right: 0px; - } - - .panel-overlay { - position: absolute; - top: 0px; bottom: 0px; left: 0px; right: 0px; - z-index: 100; - background: gray; - opacity: 0.8; - text-align: center; - overflow: hidden; - display: none; - cursor: col-resize; - } - - .panel-content-overlay { - } - - .panel-wrap.last-panel .panel-content { - right: 0px; - } - - .panel-wrap.last-panel .panel-slider { - display: none; - } - - .panel-wrap .panel-toolbar { - position: absolute; - top: 0px; left:0px; right: 0px; height: 26px; - padding: 0px; - - border-bottom: 1px solid #AAA; - z-index: 80; - } - .panel-wrap .panel-slider { - position: absolute; - background-color: #DDD; - - top: 0px; bottom: 0px; right: 0px; width: 4px; - - border-left: 1px solid #999; - border-right: 1px solid #999; - border-top: none; - border-bottom: none; - - z-index: 90; - cursor: col-resize; - } - - .panel-wrap .panel-slider:hover { - background-color: #999; - } - - .panel-content-overlay.panel-wrap .panel-slider { - background-color: #DDD; - } - - */ - - /* OLD STUFF AGAIN */ - +/* Commit dialog */ +#commit-dialog-error-empty-message { + color: red; + display: none; + font-weight: bold; +} + +text#commit-dialog-message { + width: 80%; + margin: auto; +} + +#split-dialog .container-box form { + margin: 0.2em 1em; +} + +#split-dialog .container-box fieldset { + margin: 0.5em; +} + + + + +/* ======= */ +/* = New = */ +/* ======= */ +#splitview { + width: 100%; + height: 100%; + padding: 0; + margin: 0; +} + +.splitview-splitbar { + width: 5px; + border-left: 1px solid #999; + border-right: 1px solid #999; + height: 100%; + background-color: #CCC; + z-index: 100; +} + +.splitview-overlay { + z-index: 90; + background: #FFF; + opacity: 0.5; +} + +.panel-container { + height: 100%; + position: relative; +} + +.content-view { + position: absolute; + top: 25px; + right: 0; + bottom: 0; + left: 0; + overflow: hidden; +} + +.panel-main-toolbar { + z-index: 1100; + position: absolute; + + top: 0px; + right: 0px; + left: 0px; + height: 24px; + + border-bottom: 1px solid black; + background: gray; + + padding: 0px; +} + +.panel-main-toolbar p { + margin: 0px; + padding: 2px; + line-height: 20px; + font-size: 13px; +} + +.xmlview { + height: 100%; +} + +.view-overlay { + z-index: 1000; + background: #FFF; + opacity: 0.8; + text-align: center; - text-valign: center; ++ vertical-align: middle; ++ ++ position: absolute; ++ left: 0; ++ right: 0; ++ top: 0; ++ bottom: 0; ++ ++ user-select: 'none'; ++ -webkit-user-select: 'none'; ++ -khtml-user-select: 'none'; ++ -moz-user-select: 'none'; ++ overflow: 'hidden'; +} + - .view-overlay p { - display: block; - position: relative; - top: auto; - bottom: auto; - height: 40px; ++.view-overlay div { ++ position: absolute; +} + +/* .buttontoolbarview { + display: block; + background-color: #CCC; +} + +.buttontoolbarview a { + color: #000; + text-decoration: none; +} */ /* Similar classes already exist in toolbar.css */ + + +/* ================= */ +/* = Gallery panel = */ +/* ================= */ +.image-gallery-view-template { + position: absolute; + top: 0px; left: 0px; right: 0px; bottom: 0px; + overflow: hidden; +} + +.image-gallery-header { + position: absolute; + bottom: 0px; + left: 0px; + right: 0px; + height: 30px; + + background: gray; + border-top: 1px solid #780000; + z-index: 100; +} + +input.image-gallery-current-page { + text-align: center; +} + +.image-gallery-header p { + margin: 0px; + padding: 3px 1em; + height: 30px; + line-height: 24px; + text-align: center; + white-space: nowrap; +} + +.image-gallery-page-list { + position: absolute; + top: 0px; + left: 0px; + right: 0px; + bottom: 31px; + background: black; + z-index: 0; + + overflow: hidden; +} + +.image-gallery-page-container { + display: none; + border: none; + + position: absolute; + top: 0px; left: 0px; + + text-align: center; + padding: 0px; +} + +.htmlview { + position: absolute; + top: 25px; + right: 0; + bottom: 0; + left: 0; + overflow: auto; + margin: 0; +} + +.image-gallery-page-container img { + /* border: 2px solid green; */ + margin: 0px; +} + +body#base button { + background-color: #DDD; + border-width: 1px; + padding: 0px 0.5em; + font-family: Sans-Serif; + /* color: #000; */ + margin: 2px 4px; +} + +body#base button:hover { + background-color: #EEE; +} + +/* HTML editor interactive elements */ + +.html-editarea { + border: 2px solid black; + background-color: gray; + padding: 1px; + + z-index: 2000; +} + +.html-editarea textarea +{ + + border: 0px; + margin: 0px; + padding: 0px; + + width: 100%; + height: 100%; + + 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; +} + +/* ================= */ +/* = Message boxes = */ +/* ================= */ +.info { + background-color: gray; +} + +.success { + background-color: green; +} + +.error { + background-color: yellow; +} + +.critical { + background-color: red; +} + diff --cc platforma/static/js/app.js index fde15393,00000000..e8b439df mode 100644,000000..100644 --- a/platforma/static/js/app.js +++ b/platforma/static/js/app.js @@@ -1,185 -1,0 +1,207 @@@ +/*global Class*/ +var editor; +var panel_hooks; + + +// prevent a console.log from blowing things up if we are on a browser that +// does not support it +if (typeof console === 'undefined') { + window.console = {} ; + console.log = console.info = console.warn = console.error = function(){}; +} + + +(function(){ + // Classes + var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; + this.Class = function(){}; + Class.extend = function(prop) { + var _super = this.prototype; + initializing = true; + var prototype = new this(); + initializing = false; + for (var name in prop) { + prototype[name] = typeof prop[name] == "function" && + typeof _super[name] == "function" && fnTest.test(prop[name]) ? + (function(name, fn){ + return function() { + var tmp = this._super; + this._super = _super[name]; + var ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + }; + })(name, prop[name]) : + prop[name]; + } + function Class() { + if ( !initializing && this.init ) + this.init.apply(this, arguments); + } + Class.prototype = prototype; + Class.constructor = Class; + Class.extend = arguments.callee; + return Class; + }; + + // Templates + var cache = {}; + + this.render_template = function render_template(str, data){ + // Figure out if we're getting a template, or if we need to + // load the template - and be sure to cache the result. + var fn = !/^[\d\s-_]/.test(str) ? + cache[str] = cache[str] || + render_template(document.getElementById(str).innerHTML) : + + // Generate a reusable function that will serve as a template + // generator (and which will be cached). + + new Function("obj", + "var p=[],print=function(){p.push.apply(p,arguments);};" + + + // Introduce the data as local variables using with(){} + "with(obj){p.push('" + + + // Convert the template into pure JavaScript + str + .replace(/[\r\t\n]/g, " ") + .split("<%").join("\t") + .replace(/((^|%>)[^\t]*)'/g, "$1\r") + .replace(/\t=(.*?)%>/g, "',$1,'") + .split("\t").join("');") + .split("%>").join("p.push('") + .split("\r").join("\\'") + + "');}return p.join('');"); + + // Provide some basic currying to the user + return data ? fn( data ) : fn; + }; +})(); + + +(function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + }; + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + }; + + Function.prototype.bind = function(context) { + if (arguments.length < 2 && typeof arguments[0] === 'undefined') { + return this; + } + var __method = this; + var args = slice.call(arguments, 1); + return function() { + var a = merge(args, arguments); + return __method.apply(context, a); + } + } + +})(); + + +var Editor = Editor || {}; + +// Obiekt implementujący wzorzec KVC/KVO +Editor.Object = Class.extend({ + _className: 'Editor.Object', + _observers: {}, + _guid: null, + + init: function() { + this._observers = {}; + }, + + description: function() { + return this._className + '(guid = ' + this.guid() + ')'; + }, + + addObserver: function(observer, property, callback) { + // console.log('Add observer', observer.description(), 'to', this.description(), '[', property, ']'); + if (!this._observers[property]) { + this._observers[property] = {} + } + this._observers[property][observer.guid()] = callback; + return this; + }, + + removeObserver: function(observer, property) { + if (!property) { + for (var property in this._observers) { + this.removeObserver(observer, property) + } + } else { + // console.log('Remove observer', observer.description(), 'from', this.description(), '[', property, ']'); + delete this._observers[property][observer.guid()]; + } + return this; + }, + + notifyObservers: function(property) { + var currentValue = this[property]; + for (var guid in this._observers[property]) { + // console.log(this._observers[property][guid]); + // console.log('Notifying', guid, 'of', this.description(), '[', property, ']'); + this._observers[property][guid](property, currentValue, this); + } + return this; + }, + + guid: function() { + if (!this._guid) { + this._guid = ('editor-' + Editor.Object._lastGuid++); + } + return this._guid; + }, + + get: function(property) { + return this[property]; + }, + + set: function(property, value) { + if (this[property] != value) { + this[property] = value; + this.notifyObservers(property); + } + return this; + }, + + dispose: function() { + delete this._observers; + } +}); + ++// Handle JSON error responses in uniform way ++function parseXHRError(response) ++{ ++ var message = "" ++ try { ++ var json = $.evalJSON(response.responseText); ++ ++ if(json.reason == 'xml-parse-error') { ++ message = json_response.message.replace(/(line\s+)(\d+)(\s+)/i, ++ "$1$2$3"); ++ ++ message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i, ++ "$1$2$3$4"); ++ } ++ message = json_response.message || json_response.reason || "Nieznany błąd :(("; ++ } catch(e) { ++ // not a valid JSON response ++ message = response.statusText; ++ } ++ return message; ++} ++ +Editor.Object._lastGuid = 0; + - var panels = []; ++var panels = []; diff --cc platforma/static/js/messages.js index 51a457dd,00000000..eddb46e4 mode 100644,000000..100644 --- a/platforma/static/js/messages.js +++ b/platforma/static/js/messages.js @@@ -1,35 -1,0 +1,71 @@@ +/*global Editor*/ +Editor.MessageCenter = Editor.Object.extend({ - init: function() { - this.messages = []; - this.flashMessages = []; - this.firstFlashMessage = null; - }, ++ init: function() { ++ this.messages = []; ++ this.flashMessages = []; ++ this.firstFlashMessage = null; ++ this.timeout = null; ++ console.log("MSC-init:", Date(), this); ++ }, + - addMessage: function(type, text, flash) { - if (!flash) { - flash = text; - } - this.messages.push({type: type, text: text}); - this.flashMessages.push({type: type, text: flash}); - if (this.flashMessages.length == 1) { - this.set('firstFlashMessage', this.flashMessages[0]); - setTimeout(this.changeFlashMessage.bind(this), 1000 * 10); - } - }, ++ addMessage: function(type, tag, text, flash) ++ { ++ if (!tag) tag = '#default' ++ ++ if (!flash) { ++ flash = text; ++ } ++ ++ this.messages.push({ ++ type: type, ++ text: text ++ }); ++ ++ this.flashMessages.push({ ++ type: type, ++ text: flash, ++ tag: tag ++ }); ++ ++ if(this.timeout) { ++ if(this.flashMessages[0] && (this.flashMessages[0].tag == tag)) ++ { ++ clearTimeout(this.timeout); ++ this.timeout = null; ++ this.changeFlashMessage(); ++ } ++ } ++ ++ else { ++ /* queue was empty at the start */ ++ if (this.flashMessages.length == 1) { ++ console.log("MSC-added-fisrt", Date(), this); ++ this.set('firstFlashMessage', this.flashMessages[0]); ++ this.timeout = setTimeout(this.changeFlashMessage.bind(this), 3000); ++ } ++ ++ } ++ ++ }, + - changeFlashMessage: function() { - this.flashMessages.splice(0, 1); - if (this.flashMessages.length > 0) { - this.set('firstFlashMessage', this.flashMessages[0]); - setTimeout(this.changeFlashMessage.bind(this), 1000 * 3); // 3 seconds - } else { - this.set('firstFlashMessage', null); ++ changeFlashMessage: function() ++ { ++ console.log("MSC-change", Date(), this); ++ var previous = this.flashMessages.splice(0, 1); ++ ++ if (this.flashMessages.length > 0) ++ { ++ console.log("MSC-chaning-first", Date(), this); ++ this.set('firstFlashMessage', this.flashMessages[0]); ++ this.timeout = setTimeout(this.changeFlashMessage.bind(this), 3000); ++ } else { ++ console.log("MSC-emptying", Date(), this); ++ this.set('firstFlashMessage', null); ++ } + } - } + +}); + + +var messageCenter = new Editor.MessageCenter(); + diff --cc platforma/static/js/models.js index ab021f51,00000000..6399c037 mode 100644,000000..100644 --- a/platforma/static/js/models.js +++ b/platforma/static/js/models.js @@@ -1,593 -1,0 +1,604 @@@ +/*globals Editor fileId SplitView PanelContainerView EditorView FlashView messageCenter*/ +Editor.Model = Editor.Object.extend({ + synced: false, + data: null +}); + - +Editor.ToolbarButtonsModel = Editor.Model.extend({ + className: 'Editor.ToolbarButtonsModel', + buttons: {}, + + init: function() { + this._super(); + }, + + load: function() { + if (!this.get('buttons').length) { + $.ajax({ - url: toolbarUrl, ++ url: documentInfo.toolbarURL, + dataType: 'json', + success: this.loadSucceeded.bind(this) + }); + } + }, + + loadSucceeded: function(data) + { + // do some escaping + $.each(data, function() { + $.each(this.buttons, function() { + //do some lame escapes + this.tooltip = this.tooltip.replace(/"/g, """); + }); + }); + this.set('buttons', data); + } +}); + + +// 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(serverURL, revision) { ++ init: function(document, serverURL) { + this._super(); + this.set('state', 'empty'); - this.set('revision', revision); ++ this.set('revision', document.get('revision')); ++ this.document = document; + 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'); - messageCenter.addMessage('info', 'Wczytuję XML...'); ++ messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...'); + $.ajax({ + url: this.serverURL, + dataType: 'text', + data: { - revision: this.get('revision') ++ revision: this.get('revision'), ++ user: this.document.get('user') + }, + success: this.loadingSucceeded.bind(this), + error: this.loadingFailed.bind(this) + }); + return true; + } + return false; + }, + + loadingSucceeded: function(data) { + if (this.get('state') != 'loading') { + alert('erroneous state:', this.get('state')); + } + this.set('data', data); + this.set('state', 'synced'); - messageCenter.addMessage('success', 'Wczytałem XML :-)'); ++ messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)'); + }, + + loadingFailed: function() { + if (this.get('state') != 'loading') { + alert('erroneous state:', this.get('state')); + } - this.set('error', 'Nie udało się załadować panelu'); ++ var message = parseXHRError(response); ++ ++ this.set('error', '

Błąd przy ładowaniu XML

'+message+'

'); + this.set('state', 'error'); - messageCenter.addMessage('error', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-('); ++ messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-('); + }, + - update: function(message) { ++ save: function(message) { + if (this.get('state') == 'dirty') { + this.set('state', 'updating'); - messageCenter.addMessage('info', 'Zapisuję XML...'); ++ messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...'); + + var payload = { + contents: this.get('data'), - revision: this.get('revision') ++ revision: this.get('revision'), ++ user: this.document.get('user') + }; + if (message) { + payload.message = message; + } + + $.ajax({ + url: this.serverURL, + type: 'post', + dataType: 'json', + data: payload, - success: this.updatingSucceeded.bind(this), - error: this.updatingFailed.bind(this) ++ success: this.saveSucceeded.bind(this), ++ error: this.saveFailed.bind(this) + }); + return true; + } + return false; + }, + - updatingSucceeded: function(data) { ++ saveSucceeded: function(data) { + if (this.get('state') != 'updating') { + alert('erroneous state:', this.get('state')); + } + this.set('revision', data.revision); + this.set('state', 'updated'); - messageCenter.addMessage('success', 'Zapisałem XML :-)'); ++ messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)'); + }, + - updatingFailed: function() { ++ saveFailed: function() { + if (this.get('state') != 'updating') { + alert('erroneous state:', this.get('state')); + } - messageCenter.addMessage('error', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-('); ++ 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.HTMLModel = Editor.Model.extend({ + _className: 'Editor.HTMLModel', + dataURL: null, + htmlURL: null, + renderURL: null, + displaData: '', + xmlParts: {}, + state: 'empty', + - init: function(htmlURL, revision, dataURL) { ++ init: function(document, dataURL, htmlURL) { + this._super(); + this.set('state', 'empty'); - this.set('revision', revision); ++ this.set('revision', document.get('revision')); ++ ++ this.document = document; + this.htmlURL = htmlURL; + this.dataURL = dataURL; - this.renderURL = "http://localhost:8000/api/render"; ++ this.renderURL = documentInfo.renderURL; + this.xmlParts = {}; + }, + + load: function(force) { + if (force || this.get('state') == 'empty') { + this.set('state', 'loading'); + + // load the transformed data + // messageCenter.addMessage('info', 'Wczytuję HTML...'); + + $.ajax({ + url: this.htmlURL, + dataType: 'text', + data: { - revision: this.get('revision') ++ revision: this.get('revision'), ++ user: this.document.get('user') + }, + success: this.loadingSucceeded.bind(this), + error: this.loadingFailed.bind(this) + }); + } - }, ++ }, + + loadingSucceeded: function(data) { + if (this.get('state') != 'loading') { + alert('erroneous state:', this.get('state')); + } + this.set('data', data); + this.set('state', 'synced'); - // messageCenter.addMessage('success', 'Wczytałem HTML :-)'); + }, + + loadingFailed: function(response) { + if (this.get('state') != 'loading') { + alert('erroneous state:', this.get('state')); + } - - var json_response = null; - var message = ""; - - try { - json_response = $.evalJSON(response.responseText); - - if(json_response.reason == 'xml-parse-error') { - - message = json_response.message.replace(/(line\s+)(\d+)(\s+)/i, - "$1$2$3"); - - message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i, - "$1$2$3$4"); - - - } - else { - message = json_response.message || json_response.reason || "nieznany błąd."; - } - } - catch (e) { - message = response.statusText; - } - ++ ++ var message = parseXHRError(response); ++ + this.set('error', '

Nie udało się wczytać widoku HTML:

' + message); - - this.set('state', 'error'); - // messageCenter.addMessage('error', 'Nie udało mi się wczytać HTML. Spróbuj ponownie :-('); ++ 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'); + } + }); + }, + - update: function(message) { ++ save: function(message) { + if (this.get('state') == 'dirty') { + this.set('state', 'updating'); + + var payload = { + chunks: $.toJSON(this.xmlParts), - revision: this.get('revision') ++ revision: this.get('revision'), ++ user: this.document.get('user') + }; + + if (message) { + payload.message = message; + } + + console.log(payload) + + $.ajax({ + url: this.dataURL, + type: 'post', + dataType: 'json', + data: payload, - success: this.updatingSucceeded.bind(this), - error: this.updatingFailed.bind(this) ++ success: this.saveSucceeded.bind(this), ++ error: this.saveFailed.bind(this) + }); + return true; + } + return false; + + }, + - updatingSucceeded: function(data) { ++ 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'); + }, + - updatingFailed: function() { ++ saveFailed: function() { + if (this.get('state') != 'updating') { + alert('erroneous state:', this.get('state')); - } - messageCenter.addMessage('error', 'Uaktualnienie nie powiodło się', 'Uaktualnienie nie powiodło się'); ++ } + this.set('state', 'dirty'); + }, + + // For debbuging + set: function(property, value) { + if (property == 'state') { + console.log(this.description(), ':', property, '=', value); + } + return this._super(property, value); + } +}); + + +Editor.ImageGalleryModel = Editor.Model.extend({ + _className: 'Editor.ImageGalleryModel', + serverURL: null, + data: [], + state: 'empty', + + init: function(serverURL) { + this._super(); + this.set('state', 'empty'); + this.serverURL = serverURL; + // olewać data + this.pages = []; + }, + + load: function(force) { + if (force || this.get('state') == 'empty') { + this.set('state', 'loading'); + $.ajax({ + url: this.serverURL, + dataType: 'json', + success: this.loadingSucceeded.bind(this) + }); + } + }, + + loadingSucceeded: function(data) { + if (this.get('state') != 'loading') { + alert('erroneous state:', this.get('state')); + } + + console.log('galleries:', data); + + if (data.length === 0) { + this.set('data', []); - } else { - console.log('dupa'); ++ } else { + this.set('data', data[0].pages); + } + + this.set('state', 'synced'); + }, + + set: function(property, value) { + if (property == 'state') { + console.log(this.description(), ':', property, '=', value); + } + return this._super(property, value); + } +}); + + +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 + contentModels: {}, + state: 'empty', ++ errors: '', ++ revision: '', ++ user: '', + + init: function() { + this._super(); - this.set('state', 'empty'); - this.load(); ++ this.set('state', 'empty'); + }, + + load: function() { + if (this.get('state') == 'empty') { + this.set('state', 'loading'); - messageCenter.addMessage('info', 'Ładuję dane dokumentu...'); ++ messageCenter.addMessage('info', 'docload', 'Ładuję dane dokumentu...'); + $.ajax({ + cache: false, - url: documentsUrl + fileId, ++ url: documentInfo.docURL, + dataType: 'json', - success: this.successfulLoad.bind(this) ++ success: this.successfulLoad.bind(this), ++ error: this.failedLoad.bind(this) + }); + } + }, + + successfulLoad: function(data) { + this.set('data', data); + this.set('state', 'synced'); ++ ++ this.set('revision', data.user_revision); ++ this.set('user', data.user); ++ + this.contentModels = { - 'xml': new Editor.XMLModel(data.text_url, data.user_revision), - 'html': new Editor.HTMLModel(data.html_url, data.user_revision, data.text_url), - 'gallery': new Editor.ImageGalleryModel(data.gallery_url) - }; ++ 'xml': new Editor.XMLModel(this, data.text_url), ++ 'html': new Editor.HTMLModel(this, data.text_url, data.html_url), ++ 'gallery': new Editor.ImageGalleryModel(this, data.gallery_url) ++ }; ++ + for (var key in this.contentModels) { + this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this)); + } - messageCenter.addMessage('success', 'Dane dokumentu zostały załadowane :-)'); ++ ++ this.error = ''; ++ ++ messageCenter.addMessage('success', 'docload', 'Dokument załadowany poprawnie :-)'); ++ }, ++ ++ failedLoad: 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ć dokumentu

'+message+"

"); ++ this.set('state', 'error'); + }, + + contentModelStateChanged: function(property, value, contentModel) { + if (value == 'dirty') { + this.set('state', 'dirty'); + for (var key in this.contentModels) { + if (this.contentModels[key].guid() != contentModel.guid()) { + this.contentModels[key].set('state', 'unsynced'); + } + } + } else if (value == 'updated') { + this.set('state', 'synced'); + 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'); + } + } + 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('state', 'empty'); + } + } + } + }, + + saveDirtyContentModel: function(message) { + for (var key in this.contentModels) { + if (this.contentModels[key].get('state') == 'dirty') { - this.contentModels[key].update(message); ++ this.contentModels[key].save(message); + break; + } + } + }, + + update: function() { + this.set('state', 'loading'); + messageCenter.addMessage('info', 'Uaktualniam dokument...'); + $.ajax({ + url: this.data.merge_url, + dataType: 'json', + type: 'post', + data: { + type: 'update', - target_revision: this.data.user_revision ++ revision: this.revision, ++ user: this.user + }, + complete: this.updateCompleted.bind(this), + success: function(data) { + this.set('updateData', data); + }.bind(this) + }); + }, + + updateCompleted: function(xhr, textStatus) { + console.log(xhr.status, textStatus); + if (xhr.status == 200) { // Sukces - this.data.user_revision = this.get('updateData').revision; - messageCenter.addMessage('info', 'Uaktualnienie dokumentu do wersji ' + this.get('updateData').revision, ++ 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); + for (var key in this.contentModels) { + this.contentModels[key].set('revision', this.data.user_revision); + this.contentModels[key].set('state', 'empty'); + } - messageCenter.addMessage('success', 'Uaktualniłem dokument do najnowszej wersji :-)'); ++ 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', 'Nic się nie zmieniło od ostatniej aktualizacji. Po co mam uaktualniać?'); ++ 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', 'Wystąpił konflikt podczas aktualizacji. Pędź po programistów! :-('); ++ messageCenter.addMessage('error', null, 'Wystąpił konflikt podczas aktualizacji. Pędź po programistów! :-('); + } else if (xhr.status == 500) { - messageCenter.addMessage('critical', 'Błąd serwera. Pędź po programistów! :-('); ++ messageCenter.addMessage('critical', null, 'Błąd serwera. Pędź po programistów! :-('); + } + this.set('state', 'synced'); + this.set('updateData', null); + }, + + merge: function(message) { + this.set('state', 'loading'); - messageCenter.addMessage('info', 'Scalam dokument z głównym repozytorium...'); ++ messageCenter.addMessage('info', null, 'Scalam dokument z głównym repozytorium...'); + $.ajax({ + url: this.data.merge_url, + type: 'post', + dataType: 'json', + data: { + type: 'share', - target_revision: this.data.user_revision, ++ revision: this.revision, ++ user: this.user, + message: message + }, + complete: this.mergeCompleted.bind(this), + success: function(data) { + this.set('mergeData', data); + }.bind(this) + }); + }, + + mergeCompleted: function(xhr, textStatus) { + console.log(xhr.status, textStatus); + if (xhr.status == 200) { // Sukces - this.data.user_revision = this.get('mergeData').revision; ++ 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.data.user_revision); ++ this.contentModels[key].set('revision', this.revision); + this.contentModels[key].set('state', 'empty'); + } - messageCenter.addMessage('success', 'Scaliłem dokument z głównym repozytorium :-)'); ++ ++ messageCenter.addMessage('success', null, 'Scaliłem dokument z głównym repozytorium :-)'); + } else if (xhr.status == 202) { // Wygenerowano PullRequest - messageCenter.addMessage('success', 'Wysłałem prośbę o scalenie dokumentu z głównym repozytorium.'); ++ 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', 'Nic się nie zmieniło od ostatniego scalenia. Po co mam scalać?'); ++ 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', 'Wystąpił konflikt podczas scalania. Pędź po programistów! :-('); ++ messageCenter.addMessage('error', null, 'Wystąpił konflikt podczas scalania. Pędź po programistów! :-('); + } else if (xhr.status == 500) { - messageCenter.addMessage('critical', 'Błąd serwera. Pędź po programistów! :-('); ++ messageCenter.addMessage('critical', null, 'Błąd serwera. Pędź po programistów! :-('); + } + this.set('state', 'synced'); + this.set('mergeData', null); + }, + + // For debbuging + set: function(property, value) { + if (property == 'state') { + console.log(this.description(), ':', property, '=', value); + } + return this._super(property, value); + } +}); + + +var leftPanelView, rightPanelContainer, doc; + +$(function() +{ - documentsUrl = $('#api-base-url').text() + '/'; - toolbarUrl = $('#api-toolbar-url').text(); - ++ var flashView = new FlashView('#flashview', messageCenter); ++ + doc = new Editor.DocumentModel(); + + EditorView = new EditorView('#body-wrap', doc); - EditorView.freeze(); ++ EditorView.freeze("

Wczytuję dokument...

"); + + leftPanelView = new PanelContainerView('#left-panel-container', doc); + rightPanelContainer = new PanelContainerView('#right-panel-container', doc); + - var flashView = new FlashView('#flashview', messageCenter); ++ +}); diff --cc platforma/static/js/views/editor.js index 2793141f,00000000..d43caaaa mode 100644,000000..100644 --- a/platforma/static/js/views/editor.js +++ b/platforma/static/js/views/editor.js @@@ -1,142 -1,0 +1,151 @@@ +/*global View render_template panels */ +var EditorView = View.extend({ + _className: 'EditorView', + element: null, + model: null, + template: null, + + init: function(element, model, template) { + this._super(element, model, template); + + this.quickSaveButton = $('#action-quick-save', this.element).bind('click.editorview', this.quickSave.bind(this)); + this.commitButton = $('#action-commit', this.element).bind('click.editorview', this.commit.bind(this)); + this.updateButton = $('#action-update', this.element).bind('click.editorview', this.update.bind(this)); + this.mergeButton = $('#action-merge', this.element).bind('click.editorview', this.merge.bind(this)); + + this.model.addObserver(this, 'state', this.modelStateChanged.bind(this)); + this.modelStateChanged('state', this.model.get('state')); + + this.splitView = new SplitView('#splitview', doc); + + // Inicjalizacja okien jQuery Modal + $('#commit-dialog', this.element). + jqm({ + modal: true, + onShow: this.loadRelatedIssues.bind(this) + }); + + $('#commit-dialog-cancel-button', this.element).click(function() { + $('#commit-dialog-error-empty-message').hide(); + $('#commit-dialog').jqmHide(); + }); ++ + + // $('#split-dialog').jqm({ + // modal: true, + // onShow: $.fbind(self, self.loadSplitDialog) + // }). + // jqmAddClose('button.dialog-close-button'); + + this.model.load(); + }, + + quickSave: function(event) { + this.model.saveDirtyContentModel(); + }, + + commit: function(event) { + $('#commit-dialog', this.element).jqmShow({ + callback: this.doCommit.bind(this) + }); + }, + + doCommit: function(message) { + this.model.saveDirtyContentModel(message); + }, + + update: function(event) { + this.model.update(); + }, + + merge: function(event) { + $('#commit-dialog', this.element).jqmShow({ + callback: this.doMerge.bind(this) + }); + }, + + doMerge: function(message) { + this.model.merge(message); + }, + + loadRelatedIssues: function(hash) { + var self = this; + var c = $('#commit-dialog-related-issues'); + + $('#commit-dialog-save-button').click(function(event, data) + { + if ($('#commit-dialog-message').val().match(/^\s*$/)) { + $('#commit-dialog-error-empty-message').fadeIn(); + } else { + $('#commit-dialog-error-empty-message').hide(); + $('#commit-dialog').jqmHide(); + + var message = $('#commit-dialog-message').val(); + $('#commit-dialog-related-issues input:checked') + .each(function() { + message += ' refs #' + $(this).val(); + }); + console.log("COMMIT APROVED", hash.t); + hash.t.callback(message); + } + return false; + }); + + $("div.loading-box", c).show(); + $("div.fatal-error-box", c).hide(); + $("div.container-box", c).hide(); + + $.getJSON(c.attr('ui:ajax-src') + '?callback=?', + function(data, status) + { + var fmt = ''; + $(data).each( function() { + fmt += '\n'; + }); + $("div.container-box", c).html(fmt); + $("div.loading-box", c).hide(); + $("div.container-box", c).show(); + }); + + hash.w.show(); + }, + + modelStateChanged: function(property, value) { + // Uaktualnia stan przycisków + if (value == 'dirty') { + this.quickSaveButton.attr('disabled', null); + this.commitButton.attr('disabled', null); + this.updateButton.attr('disabled', 'disabled'); + this.mergeButton.attr('disabled', 'disabled'); - } else if (value == 'synced') { ++ } else if (value == 'synced') { + this.quickSaveButton.attr('disabled', 'disabled'); + this.commitButton.attr('disabled', 'disabled'); + this.updateButton.attr('disabled', null); + this.mergeButton.attr('disabled', null); ++ this.unfreeze(); + } else if (value == 'empty') { + this.quickSaveButton.attr('disabled', 'disabled'); + this.commitButton.attr('disabled', 'disabled'); + this.updateButton.attr('disabled', 'disabled'); + this.mergeButton.attr('disabled', 'disabled'); ++ } else if (value == 'error') { ++ this.freeze(this.model.get('error')); ++ this.quickSaveButton.attr('disabled', 'disabled'); ++ this.commitButton.attr('disabled', 'disabled'); ++ this.updateButton.attr('disabled', 'disabled'); ++ this.mergeButton.attr('disabled', 'disabled'); ++ + } + }, + + dispose: function() { + $('#action-quick-save', this.element).unbind('click.editorview'); + $('#action-commit', this.element).unbind('click.editorview'); + $('#action-update', this.element).unbind('click.editorview'); + $('#action-merge', this.element).unbind('click.editorview'); + + this.model.removeObserver(this); + this._super(); + } +}); diff --cc platforma/static/js/views/flash.js index b2240e43,00000000..e67b0463 mode 100644,000000..100644 --- a/platforma/static/js/views/flash.js +++ b/platforma/static/js/views/flash.js @@@ -1,40 -1,0 +1,42 @@@ +/*globals View render_template*/ +var FlashView = View.extend({ + template: 'flash-view-template', + + init: function(element, model, template) { + this.shownMessage = null; + this._super(element, model, template); + this.setModel(model); + }, + + setModel: function(model) { + if (this.model) { + this.model.removeObserver(this); + } + this.model = model; + this.shownMessage = null; + if (this.model) { + this.shownMessage = this.model.get('firstFlashMessage'); + this.model.addObserver(this, 'firstFlashMessage', this.modelFirstFlashMessageChanged.bind(this)); + } + this.render(); + }, + + render: function() { + this.element.html(render_template(this.template, this)); ++ setTimeout(function() {}, 0); + }, + - modelFirstFlashMessageChanged: function(property, value) { - this.element.fadeOut('slow', function() { - this.element.css({'z-index': 0}); - this.shownMessage = value; - this.render(); ++ modelFirstFlashMessageChanged: function(property, value) { ++ this.element.fadeOut(200, (function() { ++ ++ this.element.css({'z-index': 0}); ++ this.shownMessage = value; ++ this.render(); + - if(this.shownMessage) { ++ if(this.shownMessage) { + this.element.css({'z-index': 1000}); - this.element.fadeIn('slow'); - } - }.bind(this)); ++ this.element.fadeIn(); ++ }; ++ }).bind(this)); + } +}); diff --cc platforma/static/js/views/html.js index a3db1d28,00000000..fa52bd0f mode 100644,000000..100644 --- a/platforma/static/js/views/html.js +++ b/platforma/static/js/views/html.js @@@ -1,152 -1,0 +1,153 @@@ +/*global View render_template panels */ +var HTMLView = View.extend({ + _className: 'HTMLView', + element: null, + model: null, + template: 'html-view-template', + + init: function(element, model, parent, template) { + this._super(element, model, template); + this.parent = parent; + + this.model + .addObserver(this, 'data', this.modelDataChanged.bind(this)) + .addObserver(this, 'state', this.modelStateChanged.bind(this)); + + $('.htmlview', this.element).html(this.model.get('data')); + this.modelStateChanged('state', this.model.get('state')); + this.model.load(); + }, + + modelDataChanged: function(property, value) { + $('.htmlview', this.element).html(value); ++ this.updatePrintLink(); ++ }, + ++ updatePrintLink: function() { + var base = this.$printLink.attr('ui:baseref'); - this.$printLink.attr('href', base + "?revision=" + this.model.get('revision')); ++ this.$printLink.attr('href', base + "?user="+this.model.document.get('user')+"&revision=" + this.model.get('revision')); + }, + + modelStateChanged: function(property, value) + { + var self = $(this); + + if (value == 'synced' || value == 'dirty') { + this.unfreeze(); + } else if (value == 'unsynced') { + this.freeze('Niezsynchronizowany...'); + } else if (value == 'loading') { + this.freeze('Ładowanie...'); + } else if (value == 'saving') { + this.freeze('Zapisywanie...'); + } 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]); + + $(document).trigger('xml-scroll-request', {line:line, column:column}); + } catch(e) { + console.log(e); + } + + return false; + }); + } + }, + + render: function() { + this.element.unbind('click'); + + if(this.$printLink) this.$printLink.unbind(); + this._super(); + this.$printLink = $('.html-print-link', this.element); - - var base = this.$printLink.attr('ui:baseref'); - this.$printLink.attr('href', base + "?revision=" + this.model.get('revision')); ++ this.updatePrintLink(); + + this.element.bind('click', this.itemClicked.bind(this)); + }, + + reload: function() { + this.model.load(true); + }, + + dispose: function() { + this.model.removeObserver(this); + this._super(); + }, + + itemClicked: function(event) + { + var self = this; + + console.log('click:', event, event.ctrlKey, event.target); + var editableContent = null; + var $e = $(event.target); + + var n = 0; + + while( ($e[0] != this.element[0]) && !($e.attr('wl2o:editable')) + && n < 50) + { + // console.log($e, $e.parent(), this.element); + $e = $e.parent(); + n += 1; + } + + if(!$e.attr('wl2o:editable')) + return true; + + // start edition on this node + + + var $overlay = $( + '
\n\ +

\n\ + \n\ + \n\ +

\n\ + \n\ +
'); + + var x = $e[0].offsetLeft; + var y = $e[0].offsetTop; + var w = $e.outerWidth(); + var h = $e.innerHeight(); + $overlay.css({position: 'absolute', height: h, left: "5%", top: y, width: "90%"}); + $e.offsetParent().append($overlay); + + // load the original XML content + console.log($e, $e.offsetParent(), $overlay); + + $('.html-editarea-cancel-button', $overlay).click(function() { + $overlay.remove(); + }); + + $('.html-editarea-save-button', $overlay).click(function() { + $overlay.remove(); + + // put the part back to the model + self.model.putXMLPart($e, $('textarea', $overlay).val()); + }); + + $('textarea', $overlay).focus(function() { + $overlay.css('z-index', 3000); + }).blur(function() { + $overlay.css('z-index', 2000); + }); + + this.model.getXMLPart($e, function(path, data) { + $('textarea', $overlay).val(data); + }); + + return false; + } + +}); + +// Register view +panels['html'] = HTMLView; diff --cc platforma/static/js/views/view.js index e9ff938e,00000000..d0c6d170 mode 100644,000000..100644 --- a/platforma/static/js/views/view.js +++ b/platforma/static/js/views/view.js @@@ -1,90 -1,0 +1,82 @@@ +/*globals Editor render_template*/ +var View = Editor.Object.extend({ + _className: 'View', + element: null, + model: null, + template: null, + overlayClass: 'view-overlay', + overlay: null, + + init: function(element, model, template) + { + console.log("init for view"); + this.element = $(element); + this.model = model; + this.template = template || this.template; + + if (this.template) this.render(); + + this._resizeHandler = this.resized.bind(this); + $(window).bind('resize', this._resizeHandler); + $(this.element).bind('resize', this._resizeHandler); + }, + + render: function() { + console.log('rendering:', this._className); + this.element.html(render_template(this.template, this)); + }, + + frozen: function() { + return !!this.overlay; + }, + + freeze: function(message) { + if (this.frozen()) { + this.unfreeze(); + } - this.overlay = this.overlay - || $('
' + message + '
') - .addClass(this.overlayClass) ++ this.overlay = this.overlay || $('
' + message + '
'); ++ ++ this.overlay.addClass(this.overlayClass) + .css({ - position: 'absolute', - width: this.element.width(), - height: this.element.height(), - top: this.element.position().top, - left: this.element.position().left, - 'user-select': 'none', - '-webkit-user-select': 'none', - '-khtml-user-select': 'none', - '-moz-user-select': 'none', - overflow: 'hidden' - }) - .attr('unselectable', 'on') - .appendTo(this.element.parent()); + ++ }).attr('unselectable', 'on') ++ ++ this.overlay.appendTo(this.element); ++ ++ var ovc = this.overlay.children('div'); ++ var padV = (this.overlay.height() - ovc.outerHeight())/2; ++ var padH = (this.overlay.width() - ovc.outerWidth())/2; ++ + this.overlay.children('div').css({ - position: 'relative', - top: this.overlay.height() / 2 - 20 - }); ++ top: padV, left: padH ++ }); + }, + + unfreeze: function() { + if (this.frozen()) { + this.overlay.remove(); + this.overlay = null; + } + }, + + resized: function(event) { - if (this.frozen()) { - this.overlay.css({ - position: 'absolute', - width: this.element.width(), - height: this.element.height(), - top: this.element.position().top, - left: this.element.position().left - }).children('div').css({ - position: 'relative', - top: this.overlay.height() / 2 - 20 ++ if(this.overlay) { ++ var ovc = this.overlay.children('div'); ++ var padV = (this.overlay.height() - ovc.outerHeight())/2; ++ var padH = (this.overlay.width() - ovc.outerWidth())/2; ++ ++ this.overlay.children('div').css({ ++ top: padV, ++ left: padH + }); + } + }, + + dispose: function() { + console.log('disposing:', this._className); + $(window).unbind('resize', this._resizeHandler); + $(this.element).unbind('resize', this._resizeHandler); + this.unfreeze(); + this.element.html(''); + } - }); ++}); diff --cc platforma/static/js/views/xml.js index 1681caee,00000000..5c184321 mode 100644,000000..100644 --- a/platforma/static/js/views/xml.js +++ b/platforma/static/js/views/xml.js @@@ -1,181 -1,0 +1,186 @@@ +/*global View CodeMirror ToolbarView render_template panels */ +var XMLView = View.extend({ + _className: 'XMLView', + element: null, + model: null, + template: 'xml-view-template', + editor: null, + buttonToolbar: null, + + init: function(element, model, parent, template) { + this._super(element, model, template); + this.parent = parent; + this.buttonToolbar = new ButtonToolbarView( + $('.xmlview-toolbar', this.element), + this.model.toolbarButtonsModel, parent); + + this.hotkeys = []; + var self = this; + + $('.xmlview-toolbar', this.element).bind('resize.xmlview', this.resized.bind(this)); + + // scroll to the given position (if availble) + this.scrollCallback = this.scrollOnRequest.bind(this); + $(document).bind('xml-scroll-request', this.scrollCallback); + + this.parent.freeze('Ładowanie edytora...'); ++ ++ setTimeout((function(){ ++ + this.editor = new CodeMirror($('.xmlview', this.element).get(0), { + parserfile: 'parsexml.js', + path: "/static/js/lib/codemirror/", + stylesheet: "/static/css/xmlcolors.css", + parserConfig: { + useHTMLKludges: false + }, + textWrapping: true, + tabMode: 'spaces', + indentUnit: 0, + onChange: this.editorDataChanged.bind(this), + initCallback: this.editorDidLoad.bind(this) + }); ++ ++ }).bind(this), 0); + }, + + resized: function(event) { + var height = this.element.height() - $('.xmlview-toolbar', this.element).outerHeight(); + $('.xmlview', this.element).height(height); + }, + + reload: function() { + this.model.load(true); + }, + + editorDidLoad: function(editor) { + $(editor.frame).css({ + width: '100%', + height: '100%' + }); + this.model + .addObserver(this, 'data', this.modelDataChanged.bind(this)) + .addObserver(this, 'state', this.modelStateChanged.bind(this)) - .load(); - - this.parent.unfreeze(); ++ .load(); + + this.editor.setCode(this.model.get('data')); + this.modelStateChanged('state', this.model.get('state')); + + editor.grabKeys( + this.hotkeyPressed.bind(this), + this.isHotkey.bind(this) - ); ++ ); ++ ++ this.parent.unfreeze(); + }, + + editorDataChanged: function() { + this.model.set('data', this.editor.getCode()); + }, + + modelDataChanged: function(property, value) { + if (this.editor.getCode() != value) { + this.editor.setCode(value); + } + }, + + modelStateChanged: function(property, value) { + if (value == 'synced' || value == 'dirty') { + this.unfreeze(); + } else if (value == 'unsynced') { + this.freeze('Niezsynchronizowany...'); + } else if (value == 'loading') { - this.freeze('Ładowanie...'); ++ this.freeze('Ładowanie danych...'); + } else if (value == 'saving') { + this.freeze('Zapisywanie...'); + } else if (value == 'error') { + this.freeze(this.model.get('error')); + } + }, + + dispose: function() { + $(document).unbind('xml-scroll-request', this.scrollCallback); + + this.model.removeObserver(this); + $(this.editor.frame).remove(); + this._super(); + }, + + getHotkey: function(event) { + var code = event.keyCode; + if(!((code >= 97 && code <= 122) + || (code >= 65 && code <= 90)) ) return null; + + var ch = String.fromCharCode(code & 0xff).toLowerCase(); + /* # console.log(ch.charCodeAt(0), '#', buttons); */ + + var buttons = $('.buttontoolbarview-button[hotkey='+ch+']', this.element); + var mod = 0; + + if(event.altKey) mod |= 0x01; + if(event.ctrlKey) mod |= 0x02; + if(event.shiftKey) mod |= 0x04; + + if(buttons.length) { + var match = null; + + buttons.each(function() { + if( parseInt($(this).attr('ui:hotkey_mod')) == mod ) { + match = this; + return; + } + }) + + return match; + } + else { + return null; + } + }, + + isHotkey: function() { + /* console.log(arguments); */ + if(this.getHotkey.apply(this, arguments)) + return true; + else + return false; + }, + + hotkeyPressed: function() { + var button = this.getHotkey.apply(this, arguments); + this.buttonToolbar.buttonPressed({ + target: button + }); + }, + + scrollOnRequest: function(event, data) + { + try { + var line = this.editor.nthLine(data.line); + this.editor.selectLines(line, (data.column-1)); + } catch(e) { + console.log('Exception in scrollOnRequest:', e); + } + } + +}); + +function Hotkey(code) { + this.code = code; + this.has_alt = ((code & 0x01 << 8) !== 0); + this.has_ctrl = ((code & 0x01 << 9) !== 0); + this.has_shift = ((code & 0x01 << 10) !== 0); + this.character = String.fromCharCode(code & 0xff); +} + +Hotkey.prototype.toString = function() { + var mods = []; + if(this.has_alt) mods.push('Alt'); + if(this.has_ctrl) mods.push('Ctrl'); + if(this.has_shift) mods.push('Shift'); + mods.push('"'+this.character+'"'); + return mods.join('+'); +}; + +// Register view +panels['xml'] = XMLView; diff --cc platforma/templates/explorer/editor.html index 3c775d4e,00000000..576a7939 mode 100644,000000..100644 --- a/platforma/templates/explorer/editor.html +++ b/platforma/templates/explorer/editor.html @@@ -1,195 -1,0 +1,202 @@@ +{% extends "base.html" %} + +{% block extrahead %} + + + + + + + + {# Libraries #} - ++ + + ++ + {# Scriptlets #} + + + {# App and views #} + + + + + + + + + + + + + + + {# JavaScript templates #} + + + + + + + + + + + +{% endblock extrahead %} + - {% block breadcrumbs %}Platforma Redakcyjna > {{ fileid }}{% endblock breadcrumbs %} ++{% block breadcrumbs %}Platforma > {{euser}} > {{ fileid }}{% endblock breadcrumbs %} + +{% block header-toolbar %} + Historia + + + + +{% endblock %} + - {% block maincontent %} - - ++{% block maincontent %} ++ + +
+
+
+
+ + + + +{% endblock maincontent %} + +{% block extrabody %} +
+{% endblock %} diff --cc platforma/templates/manager/pull_request.html index c2964c22,00000000..3b19c3bd mode 100644,000000..100644 --- a/platforma/templates/manager/pull_request.html +++ b/platforma/templates/manager/pull_request.html @@@ -1,75 -1,0 +1,79 @@@ +{% extends 'base.html' %} + +{% block extrahead %} + + +{% endblock %} + +{% block maincontent %} + + + - ++ + +{% if objects %} + {% for pullreq in objects %} + + + + + - ++ ++ + + + {% endfor %} +{% else %} - ++ +{% endif %} +
UtwórUżytkownikKomentarzStanAkcjeZgłoszonoAkcje
{{ pullreq.document }}{{ pullreq.comitter }}{{ pullreq.comment }} {{ pullreq.status }}{{ pullreq.timestamp }} ++ ++ Zobacz ++
Brak żądań
Brak żądań
+ +{% endblock %}