Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma
authorzuber <marek@stepniowski.com>
Thu, 15 Oct 2009 16:33:42 +0000 (18:33 +0200)
committerzuber <marek@stepniowski.com>
Thu, 15 Oct 2009 16:33:42 +0000 (18:33 +0200)
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

1  2 
platforma/static/css/master.css
platforma/static/js/app.js
platforma/static/js/messages.js
platforma/static/js/models.js
platforma/static/js/views/editor.js
platforma/static/js/views/flash.js
platforma/static/js/views/html.js
platforma/static/js/views/view.js
platforma/static/js/views/xml.js
platforma/templates/explorer/editor.html
platforma/templates/manager/pull_request.html

index ae51e47,0000000..cebb0dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,415 -1,0 +1,336 @@@
-     margin-left: 0.1em;    
 +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;
-     padding: 0px;    
++    margin-left: 0.1em;f
 +}
 +
 +#body-wrap {
 +    margin: 0px;
- /* ========== */
- /* = 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 */
++    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;
 +}
 +
-     text-valign: center;
 +/* 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;
- .view-overlay p {
-     display: block;
-     position: relative;
-     top: auto;
-     bottom: auto;
-     height: 40px;
++    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 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;
 +}
 +
index fde1539,0000000..e8b439d
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,207 @@@
- var panels = [];
 +/*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,
++            "<a class='xml-editor-ref' href='#xml-$2-1'>$1$2$3</a>");
++
++          message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i,
++            "<a class='xml-editor-ref' href='#xml-$2-$4'>$1$2$3$4</a>");
++      }
++      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 = [];
index 51a457d,0000000..eddb46e
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,71 @@@
-   init: function() {
-     this.messages = [];
-     this.flashMessages = [];
-     this.firstFlashMessage = null;
-   },
 +/*global Editor*/
 +Editor.MessageCenter = Editor.Object.extend({
-   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);
-     }
-   },
++    init: function() {
++        this.messages = [];
++        this.flashMessages = [];
++        this.firstFlashMessage = null;
++        this.timeout = null;
++        console.log("MSC-init:", Date(), this);
++    },
 +  
-   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);
++    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() 
++    {
++        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();
 +
index ab021f5,0000000..6399c03
mode 100644,000000..100644
--- /dev/null
@@@ -1,593 -1,0 +1,604 @@@
 +/*globals Editor fileId SplitView PanelContainerView EditorView FlashView messageCenter*/
 +Editor.Model = Editor.Object.extend({
 +    synced: false,
 +    data: null
 +});
 +
-                 url: toolbarUrl,
 +Editor.ToolbarButtonsModel = Editor.Model.extend({
 +    className: 'Editor.ToolbarButtonsModel',
 +    buttons: {},
 +  
 +    init: function() {
 +        this._super();
 +    },
 +  
 +    load: function() {
 +        if (!this.get('buttons').length) {
 +            $.ajax({
-     init: function(serverURL, revision) {
++                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, "&#34;");
 +            });
 +        });
 +        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',
 +  
-         this.set('revision', revision);
++    init: function(document, serverURL) {
 +        this._super();
 +        this.set('state', 'empty');
-             messageCenter.addMessage('info', 'Wczytuję XML...');
++        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');
-                     revision: this.get('revision')
++            messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...');
 +            $.ajax({
 +                url: this.serverURL,
 +                dataType: 'text',
 +                data: {
-         messageCenter.addMessage('success', 'Wczytałem XML :-)');
++                    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');
-         this.set('error', 'Nie udało się załadować panelu');
++        messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)');
 +    },
 +  
 +    loadingFailed: function() {
 +        if (this.get('state') != 'loading') {
 +            alert('erroneous state:', this.get('state'));
 +        }
-         messageCenter.addMessage('error', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-(');
++        var message = parseXHRError(response);
++        
++        this.set('error', '<h2>Błąd przy ładowaniu XML</h2><p>'+message+'</p>');
 +        this.set('state', 'error');
-     update: function(message) {
++        messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-(');
 +    },
 +  
-             messageCenter.addMessage('info', 'Zapisuję XML...');
++    save: function(message) {
 +        if (this.get('state') == 'dirty') {
 +            this.set('state', 'updating');
-                 revision: this.get('revision')
++            messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...');
 +      
 +            var payload = {
 +                contents: this.get('data'),
-                 success: this.updatingSucceeded.bind(this),
-                 error: this.updatingFailed.bind(this)
++                revision: this.get('revision'),
++                user: this.document.get('user')
 +            };
 +            if (message) {
 +                payload.message = message;
 +            }
 +      
 +            $.ajax({
 +                url: this.serverURL,
 +                type: 'post',
 +                dataType: 'json',
 +                data: payload,
-     updatingSucceeded: function(data) {
++                success: this.saveSucceeded.bind(this),
++                error: this.saveFailed.bind(this)
 +            });
 +            return true;
 +        }
 +        return false;
 +    },
 +  
-         messageCenter.addMessage('success', 'Zapisałem XML :-)');
++    saveSucceeded: function(data) {
 +        if (this.get('state') != 'updating') {
 +            alert('erroneous state:', this.get('state'));
 +        }
 +        this.set('revision', data.revision);
 +        this.set('state', 'updated');
-     updatingFailed: function() {
++        messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)');
 +    },
 +  
-         messageCenter.addMessage('error', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-(');
++    saveFailed: function() {
 +        if (this.get('state') != 'updating') {
 +            alert('erroneous state:', this.get('state'));
 +        }
-     init: function(htmlURL, revision, dataURL) {
++        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',
 +  
-         this.set('revision', revision);
++    init: function(document, dataURL, htmlURL) {
 +        this._super();
 +        this.set('state', 'empty');
-         this.renderURL = "http://localhost:8000/api/render";
++        this.set('revision', document.get('revision'));        
++        
++        this.document = document;
 +        this.htmlURL = htmlURL;
 +        this.dataURL = dataURL;
-                     revision: this.get('revision')
++        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'),
++                    user: this.document.get('user')
 +                    },
 +                success: this.loadingSucceeded.bind(this),
 +                error: this.loadingFailed.bind(this)
 +            });
 +        }
-         // messageCenter.addMessage('success', 'Wczytałem HTML :-)');
++    },    
 +  
 +    loadingSucceeded: function(data) {
 +        if (this.get('state') != 'loading') {
 +            alert('erroneous state:', this.get('state'));
 +        }
 +        this.set('data', data);
 +        this.set('state', 'synced');
-         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,
-                     "<a class='xml-editor-ref' href='#xml-$2-1'>$1$2$3</a>");
-                 message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i,
-                     "<a class='xml-editor-ref' href='#xml-$2-$4'>$1$2$3$4</a>");
-                 
-             }
-             else {
-                 message = json_response.message || json_response.reason || "nieznany błąd.";
-             }
-         }
-         catch (e) {
-             message = response.statusText;
-         }
 +    },
 +  
 +    loadingFailed: function(response) {
 +        if (this.get('state') != 'loading') {
 +            alert('erroneous state:', this.get('state'));
 +        }
-         this.set('state', 'error');
-         // messageCenter.addMessage('error', 'Nie udało mi się wczytać HTML. Spróbuj ponownie :-(');
++        
++        var message = parseXHRError(response);
++        
 +        this.set('error', '<p>Nie udało się wczytać widoku HTML: </p>' + message);
-     update: function(message) {
++        this.set('state', 'error');        
 +    },
 +
 +    getXMLPart: function(elem, callback)
 +    {
 +        var path = elem.attr('wl2o:path');
 +        if(!this.xmlParts[path])
 +            this.loadXMLPart(elem, callback);
 +        else
 +            callback(path, this.xmlParts[path]);
 +    },
 +
 +    loadXMLPart: function(elem, callback)
 +    {
 +        var path = elem.attr('wl2o:path');
 +        var self = this;
 +
 +        $.ajax({
 +            url: this.dataURL,
 +            dataType: 'text',
 +            data: {
 +                revision: this.get('revision'),
++                user: this.document.get('user'),
 +                part: path
 +            },
 +            success: function(data) {
 +                self.xmlParts[path] = data;
 +                callback(path, data);
 +            },
 +            // TODO: error handling
 +            error: function(data) {
 +                console.log('Failed to load fragment');
 +                callback(undefined, undefined);
 +            }
 +        });
 +    },
 +
 +    putXMLPart: function(elem, data) {
 +        var self = this;
 +      
 +        var path = elem.attr('wl2o:path');
 +        this.xmlParts[path] = data;
 +
 +        this.set('state', 'unsynced');
 +
 +        /* re-render the changed fragment */
 +        $.ajax({
 +            url: this.renderURL,
 +            type: "POST",
 +            dataType: 'text; charset=utf-8',
 +            data: {
 +                fragment: data,
 +                part: path
 +            },
 +            success: function(htmldata) {
 +                elem.replaceWith(htmldata);
 +                self.set('state', 'dirty');
 +            }
 +        });
 +    },
 +
-                 revision: this.get('revision')
++    save: function(message) {
 +        if (this.get('state') == 'dirty') {
 +            this.set('state', 'updating');
 +
 +            var payload = {
 +                chunks: $.toJSON(this.xmlParts),
-                 success: this.updatingSucceeded.bind(this),
-                 error: this.updatingFailed.bind(this)
++                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,
-     updatingSucceeded: function(data) {
++                success: this.saveSucceeded.bind(this),
++                error: this.saveFailed.bind(this)
 +            });
 +            return true;
 +        }
 +        return false;
 +      
 +    },
 +
-     updatingFailed: function() {
++    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('error', 'Uaktualnienie nie powiodło się', 'Uaktualnienie nie powiodło się');
++    saveFailed: function() {
 +        if (this.get('state') != 'updating') {
 +            alert('erroneous state:', this.get('state'));
-         } else {
-             console.log('dupa');
++        }        
 +        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', []);
-         this.set('state', 'empty');
-         this.load();
++        } 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();
-             messageCenter.addMessage('info', 'Ładuję dane dokumentu...');
++        this.set('state', 'empty');        
 +    },
 +  
 +    load: function() {
 +        if (this.get('state') == 'empty') {
 +            this.set('state', 'loading');
-                 url: documentsUrl + fileId,
++            messageCenter.addMessage('info', 'docload', 'Ładuję dane dokumentu...');
 +            $.ajax({
 +                cache: false,
-                 success: this.successfulLoad.bind(this)
++                url: documentInfo.docURL,
 +                dataType: 'json',
-             '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)
-         };
++                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 = {
-         messageCenter.addMessage('success', 'Dane dokumentu zostały załadowane :-)');
++            '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));
 +        }
-                 this.contentModels[key].update(message);
++
++        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', '<h2>Nie udało się wczytać dokumentu</h2><p>'+message+"</p>");
++        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') {
-                 target_revision: this.data.user_revision
++                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',
-             this.data.user_revision = this.get('updateData').revision;
-             messageCenter.addMessage('info', 'Uaktualnienie dokumentu do wersji ' + this.get('updateData').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
-             messageCenter.addMessage('success', 'Uaktualniłem dokument do najnowszej wersji :-)');
++            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('info', 'Nic się nie zmieniło od ostatniej aktualizacji. Po co mam uaktualniać?');
++            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('error', 'Wystąpił konflikt podczas aktualizacji. Pędź po programistów! :-(');
++            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('critical', 'Błąd serwera. 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('info', 'Scalam dokument z głównym repozytorium...');
++            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');
-                 target_revision: this.data.user_revision,
++        messageCenter.addMessage('info', null, 'Scalam dokument z głównym repozytorium...');
 +        $.ajax({
 +            url: this.data.merge_url,
 +            type: 'post',
 +            dataType: 'json',
 +            data: {
 +                type: 'share',
-             this.data.user_revision = this.get('mergeData').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.contentModels[key].set('revision', this.data.user_revision);
++            this.data = this.get('updateData');
++            this.revision = this.data.user_revision;
++            this.user = this.data.user;
++            
 +            for (var key in this.contentModels) {
-             messageCenter.addMessage('success', 'Scaliłem dokument z głównym repozytorium :-)');
++                this.contentModels[key].set('revision', this.revision);
 +                this.contentModels[key].set('state', 'empty');
 +            }
-             messageCenter.addMessage('success', 'Wysłałem prośbę o scalenie dokumentu z głównym repozytorium.');
++
++            messageCenter.addMessage('success', null, 'Scaliłem dokument z głównym repozytorium :-)');
 +        } else if (xhr.status == 202) { // Wygenerowano PullRequest
-             messageCenter.addMessage('info', 'Nic się nie zmieniło od ostatniego scalenia. Po co mam scalać?');
++            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('error', 'Wystąpił konflikt podczas scalania. Pędź po programistów! :-(');
++            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('critical', 'Błąd serwera. Pędź po programistów! :-(');
++            messageCenter.addMessage('error', null, 'Wystąpił konflikt podczas scalania. Pędź po programistów! :-(');
 +        } else if (xhr.status == 500) {
-     documentsUrl = $('#api-base-url').text() + '/';
-     toolbarUrl = $('#api-toolbar-url').text();
++            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()
 +{
-     EditorView.freeze();
++    var flashView = new FlashView('#flashview', messageCenter);
++    
 +    doc = new Editor.DocumentModel();
 +
 +    EditorView = new EditorView('#body-wrap', doc);
-     var flashView = new FlashView('#flashview', messageCenter);   
++    EditorView.freeze("<h1>Wczytuję dokument...</h1>");
 +
 +    leftPanelView = new PanelContainerView('#left-panel-container', doc);
 +    rightPanelContainer = new PanelContainerView('#right-panel-container', doc);
 +
++    
 +});
index 2793141,0000000..d43caaa
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,151 @@@
-         } else if (value == 'synced') {
 +/*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 += '<label><input type="checkbox" checked="checked"';
 +                    fmt += ' value="' + this.id + '" />' + this.subject +'</label>\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') {            
 +            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();
 +    }    
 +});
index b2240e4,0000000..e67b046
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,42 @@@
-   modelFirstFlashMessageChanged: function(property, value) {
-     this.element.fadeOut('slow', function() {
-       this.element.css({'z-index': 0});
-       this.shownMessage = value;
-       this.render();
 +/*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);
 +  },
 +  
-       if(this.shownMessage) {
++  modelFirstFlashMessageChanged: function(property, value) {    
++    this.element.fadeOut(200, (function() {
++    
++    this.element.css({'z-index': 0});
++    this.shownMessage = value;
++    this.render();
 +
-         this.element.fadeIn('slow');
-       }
-     }.bind(this));
++    if(this.shownMessage) {
 +        this.element.css({'z-index': 1000});
++        this.element.fadeIn();
++    };
++    }).bind(this));
 +  }
 +});
index a3db1d2,0000000..fa52bd0
mode 100644,000000..100644
--- /dev/null
@@@ -1,152 -1,0 +1,153 @@@
-         this.$printLink.attr('href', base + "?revision=" + this.model.get('revision'));
 +/*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');
-         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);
++        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 = $(
 +        '<div class="html-editarea">\n\
 +            <p class="html-editarea-toolbar">\n\
 +                <button class="html-editarea-save-button" type="button">Zapisz</button>\n\
 +                <button class="html-editarea-cancel-button" type="button">Anuluj</button>\n\
 +            </p>\n\
 +            <textarea></textarea>\n\
 +        </div>');
 +
 +        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;
index e9ff938,0000000..d0c6d17
mode 100644,000000..100644
--- /dev/null
@@@ -1,90 -1,0 +1,82 @@@
-         this.overlay = this.overlay
-         || $('<div><div>' + message + '</div></div>')
-         .addClass(this.overlayClass)
 +/*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();
 +        }
-             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());
++        this.overlay = this.overlay || $('<div><div>' + message + '</div></div>');
++
++        this.overlay.addClass(this.overlayClass)
 +        .css({
-             position: 'relative',
-             top: this.overlay.height() / 2 - 20
-         });
 +            
++        }).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({
-         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
++            top: padV, left: padH
++        });    
 +    },
 +  
 +    unfreeze: function() {
 +        if (this.frozen()) {
 +            this.overlay.remove();
 +            this.overlay = null;
 +        }
 +    },
 +
 +    resized: function(event) {
- });
++        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('');
 +    }
++});
index 1681cae,0000000..5c18432
mode 100644,000000..100644
--- /dev/null
@@@ -1,181 -1,0 +1,186 @@@
-         .load();
-     
-         this.parent.unfreeze();
 +/*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.editor.setCode(this.model.get('data'));
 +        this.modelStateChanged('state', this.model.get('state'));
 +        
 +        editor.grabKeys(
 +            this.hotkeyPressed.bind(this),
 +            this.isHotkey.bind(this)
-             this.freeze('Ładowanie...');
++        );
++
++        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 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;
index 3c775d4,0000000..576a793
mode 100644,000000..100644
--- /dev/null
@@@ -1,195 -1,0 +1,202 @@@
-               var fileId = '{{ fileid }}';
 +{% extends "base.html" %}
 +
 +{% block extrahead %}
 +      <link rel="stylesheet" href="{{STATIC_URL}}css/jquery.modal.css" type="text/css" media="screen" charset="utf-8">
 +      <link rel="stylesheet" href="{{STATIC_URL}}css/html.css" type="text/css" charset="utf-8">
 +              <link rel="stylesheet" href="{{STATIC_URL}}css/toolbar.css" type="text/css" charset="utf-8">
 +        <link rel="stylesheet" href="{{STATIC_URL}}css/autumn.css" type="text/css" media="screen" title="Autumn colors" charset="utf-8">
 +      
 +      <script type="text/javascript" charset="utf-8">
-     <script src="{{STATIC_URL}}js/lib/codemirror/codemirror.js" type="text/javascript" charset="utf-8"></script>
++            var documentInfo = {
++                docID: '{{ fileid }}',
++                userID: '{{ euser }}',
++                docURL: '{% url document_view fileid %}{% if euser %}?user={{ euser|urlencode }}{% endif %}',
++                toolbarURL: '{% url toolbar_buttons %}',
++                renderURL: '{% url api.views.render %}'
++            }         
++                
 +      </script>
 +      
 +      {# Libraries #}
- {% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a> &gt; {{ fileid }}{% endblock breadcrumbs %}
++        <script src="{{STATIC_URL}}js/lib/codemirror/codemirror.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/lib/jquery.modal.js" type="text/javascript" charset="utf-8"></script>
 +        <script src="{{STATIC_URL}}js/lib/jquery.json.js" type="text/javascript" charset="utf-8"></script>
++        
 +      {# Scriptlets #}
 +      <script src="{{STATIC_URL}}js/button_scripts.js" type="text/javascript" charset="utf-8"></script>
 +      
 +      {# App and views #}
 +      <script src="{{STATIC_URL}}js/app.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/messages.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/view.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/flash.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/editor.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/button_toolbar.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/split.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/xml.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/html.js" type="text/javascript" charset="utf-8"></script>
 +        <script src="{{STATIC_URL}}js/views/gallery.js" type="text/javascript" charset="utf-8"></script>
 +      <script src="{{STATIC_URL}}js/views/panel_container.js" type="text/javascript" charset="utf-8"></script>
 +      
 +      <script src="{{STATIC_URL}}js/models.js" type="text/javascript" charset="utf-8"></script>
 +      
 +      {# JavaScript templates #}
 +      <script type="text/html" charset="utf-8" id="panel-container-view-template">
 +              <div class="panel-main-toolbar">
 +                <p><select>
 +                        <option value="empty" selected="selected"> ---- </option>
 +                      <% for (panel in panels) { %>
 +                      <option value="<%= panel %>"><%= panel %></option>
 +                      <% }; %>
 +              </select> <button class="refresh">Odśwież panel</button></p>
 +                </div>
 +              <div class="content-view"></div>
 +      </script>
 +      
 +      <script type="text/html" charset="utf-8" id="xml-view-template">
 +              <div class="xmlview-toolbar"></div>
 +              <div class="xmlview">
 +                      
 +              </div>
 +      </script>
 +      
 +      <script type="text/html" charset="utf-8" id="html-view-template">
 +                <div class="htmlview-toolbar">
 +                    <a class="html-print-link" href="print" ui:baseref="{% url file_print fileid %}" target="_new">Wersja do druku</a>
 +                </div>
 +                
 +              <div class="htmlview">
 +              </div>
 +      </script>       
 +
 +      <script type="text/html" charset="utf-8" id="flash-view-template">
 +              <div class="flashview">
 +              <% if (shownMessage) { %>
 +                      <p style="margin: 0; padding: 0.2em 0.5em; line-height: 1.8em" class="<%= shownMessage.type %>"><%= shownMessage.text %></p>
 +              <% } %>
 +              </div>
 +      </script>
 +      
 +    <script type="text/html" charset="utf-8" id="image-gallery-view-template">
 +      <div class="image-gallery-view-template">
 +
 +        <div class="image-gallery-header">
 +        <p>
 +        <button type="button" class="image-gallery-prev-button">
 +        <img alt="PPrevious" src="{{STATIC_URL}}/icons/go-previous.png" width="16" height="16" />
 +        </button>
 +
 +        <input type="input" class="image-gallery-current-page"
 +            size="5" value="<%= (currentPage + 1) %>" />
 +
 +
 +        <button type="button" class="image-gallery-next-button">
 +        <img alt="Next" src="{{STATIC_URL}}/icons/go-next.png" width="16" height="16" />
 +        </button>
 +
 +        <button type="button" class="image-gallery-zoom-in">
 +            <img alt="Zoom in" src="{{STATIC_URL}}/icons/zoom_in.png" width="16" height="16" />
 +        </button>
 +        <button type="button" class="image-gallery-zoom-out">
 +            <img alt="Zoom out" src="{{STATIC_URL}}/icons/zoom_out.png" width="16" height="16" />
 +        </button>
 +        <button type="button" class="image-gallery-zoom-reset">
 +            <img alt="Zoom reset" src="{{STATIC_URL}}/icons/zoom.png" width="16" height="16" />
 +        </button>
 +        </p>
 +        </div>
 +
 +        <div class="image-gallery-page-list">
 +            <% for(var i=0; i < model.data.length; i++) { %>
 +                <div class="image-gallery-page-container"
 +                    ui:model="<%= model.data[i] %>"
 +                    ui:width="480"
 +                    ui:height="752"
 +                ></div>
 +            <% }; %>
 +      </div>
 +        
 +        </div>
 +      </script>
 +      
 +      <script type="text/html" charset="utf-8" id="button-toolbar-view-template">
 +              <div class="buttontoolbarview panel-toolbar">
 +                      <div class="buttontoolbarview-tabs toolbar-tabs-container toolbar-buttons-container">
 +                      <% for (var i=0; i < buttons.length; i++) { %>
 +                              <button type="button" class="buttontoolbarview-tab" ui:groupindex="<%= i %>"><%= buttons[i].name %></button>
 +                      <% }; %>
 +                      </div>
 +                      <div class="buttontoolbarview-groups ">
 +                      <% for (var i=0; i < buttons.length; i++) { %>
 +                              <div class="buttontoolbarview-group toolbar-buttons-container" ui:groupIndex="<%= i %>" style="display: none">
 +                                      <% for (var j=0; j < buttons[i].buttons.length; j++) { %>
 +                                              <% if (buttons[i].buttons[j].scriptlet_id) { %>
 +                                              <button type="button" class="buttontoolbarview-button"
 +                                                    title="<%= buttons[i].buttons[j].tooltip %>"
 +                                                    hotkey="<%= buttons[i].buttons[j].key %>"
 +                                                    ui:hotkey_mod="<%= buttons[i].buttons[j].key_mod %>"
 +                                                    ui:groupindex="<%= i %>" ui:buttonindex="<%= j %>">
 +                                                      <%= buttons[i].buttons[j].label %>
 +                                              </button>
 +                                              <% } %>
 +                                      <% } %>
 +                              </div>
 +                      <% }; %>
 +                      </div>
 +              </div>
 +      </script>
 +{% endblock extrahead %}
 +
- {% block maincontent %}
-     <p style="display: none;" id="api-base-url">{% url document_list_view %}</p>
-     <p style="display: none;" id="api-toolbar-url">{% url toolbar_buttons %}</p>
++{% block breadcrumbs %}<a href="{% url file_list %}">Platforma</a> &gt; {{euser}} &gt; {{ fileid }}{% endblock breadcrumbs %}
 +
 +{% block header-toolbar %}
 +    <a href="http://stigma.nowoczesnapolska.org.pl/platforma-hg/ksiazki/log/tip/{{ fileid }}.xml" target="_new" >Historia</a>
 +      <button id="action-merge">Merge</button>
 +        <button id="action-update">Update</button>
 +        <button id="action-commit">Commit</button>
 +        <button id="action-quick-save">Quick Save</button>
 +{% endblock %}
 +
++{% block maincontent %}    
++    
 +
 +    <div id="splitview">
 +        <div id="left-panel-container" class='panel-container'></div>
 +      <div id="right-panel-container" class='panel-container'></div>
 +    </div>
 +
 +    <div id="commit-dialog" class="jqmWindow" style="display:none">
 +        <form action="" method="POST">
 +            <label for="message">Commit message:</label>
 +            <textarea cols="60" rows="10" name="message" id="commit-dialog-message"></textarea>
 +            <p id="commit-dialog-error-empty-message">Wiadomość nie może być pusta.</p>                
 +            <fieldset id="commit-dialog-related-issues" ui:ajax-src="{{REDMINE_URL}}/publications/issues/{{fileid}}">
 +                <legend>Related issues</legend>
 +                <div class="loading-box" style="display: none;">
 +                    <p>Loading related issues...</p>
 +                </div>
 +                <div class="container-box">No related issues.</div>
 +            </fieldset>
 +            <p>
 +               <input type="button" value="Save" id="commit-dialog-save-button" />
 +               <input type="reset" value="Cancel" id="commit-dialog-cancel-button" />
 +            </p>
 +        </form>
 +    </div>
 +    
 +    <div id="split-dialog" class="jqmWindow" style="display:none">
 +        <div class="container-box"> </div>
 +        <div class="loading-box" style="display: none;">
 +            <p>Loading dialog contents...</p>
 +            <!-- <p><button type="button" class="dialog-close-button">Close</button></p> -->
 +        </div>
 +        <div class="fatal-error-box" style="display: none;">
 +            <p>Server error, while loading dialog :(</p>
 +            <p><button type="button" class="dialog-close-button">Close</button></p>
 +        </div>
 +    </div>
 +{% endblock maincontent %}
 +
 +{% block extrabody %}
 +      <div style="position: absolute; left: 35%; right: 35%; top: 0; height: 20px; z-index: 0" id="flashview"></div>
 +{% endblock %}
index c2964c2,0000000..3b19c3b
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,79 @@@
-         <th>Akcje</th>
 +{% extends 'base.html' %}
 +
 +{% block extrahead %}
 +<link rel="stylesheet" href="{{ STATIC_URL }}css/managment.css" type="text/css" />
 +<script type="text/javascript">
 +
 +    
 +    $(function() {
 +
 +        function refreshRow(id) {
 +            var row = $('#request-' +id);
 +
 +            $.ajax({
 +                url: '{% url pullrequest_list %}/'+id,
 +                dataType: 'json',
 +                type: 'GET',
 +                success: function(data) {
 +                    row.removeClass('status-N');
 +                    row.removeClass('status-R');
 +                    row.removeClass('status-A');
 +                    row.addClass('status-'+ data.status);
 +
 +                    $('.column-doc', row).html(data.document);
 +                    $('.column-status', row).html(data.status);
 +
 +                    alert('Merge accepted.');
 +                }
 +            });            
 +            
 +        }
 +        
 +        $('.accept-button').click(function()
 +        {
 +            var id = parseInt($(this).attr('title'));
 +            
 +
 +            $.ajax({
 +                url: '{% url pullrequest_list %}/'+id,
 +                data: {action: 'accept'},
 +                dataType: 'json',
 +                type: 'PUT',
 +                success: function(data) {
 +                    refreshRow(id);
 +                }
 +            });
 +            
 +        });
 +        
 +    });
 +</script>
 +{% endblock %}
 +
 +{% block maincontent %}
 +<table class="request-report" cellspacing="0">
 +    <tr>
 +        <th>Utwór</th><th>Użytkownik</th><th>Komentarz</th><th>Stan</th>
-         <td><button type="button" class="accept-button" title="{{pullreq.id}}">Akceptuj</button></td>
++        <th>Zgłoszono</th><th>Akcje</th>
 +    </tr>
 +{% if objects %}
 +    {% for pullreq in objects %}
 +    <tr class="status-{{pullreq.status}}" id="request-{{pullreq.id}}">
 +        <td class="column-doc">{{ pullreq.document }}</td>
 +        <td class="column-user">{{ pullreq.comitter }}</td>
 +        <td class="column-comment">{{ pullreq.comment }}</td>
 +        <td class="column-status"> {{ pullreq.status }}</td>
-     <tr><td colspan="*">Brak żądań</td></tr>
++        <td class="column-data">{{ pullreq.timestamp }}</td>
++        <td>
++            <button type="button" class="accept-button" title="{{pullreq.id}}">Akceptuj</button>
++            <a href="{% url editor_view pullreq.document %}?user=$prq-{{pullreq.id}}">Zobacz</a>
++        </td>
 +    </tr>
 +    
 +    {% endfor %}
 +{% else %}
++    <tr><td colspan="6">Brak żądań</td></tr>
 +{% endif %}
 +</table>
 +
 +{% endblock %}