Merge branch 'master' into view-refactor
authorzuber <marek@stepniowski.com>
Sat, 26 Sep 2009 18:28:35 +0000 (20:28 +0200)
committerzuber <marek@stepniowski.com>
Sat, 26 Sep 2009 18:28:35 +0000 (20:28 +0200)
project/static/css/master.css
project/static/js/app.js [new file with mode: 0644]
project/static/js/editor.js
project/static/js/editor.ui.js
project/static/js/views/html.js [new file with mode: 0644]
project/static/js/views/panel_container.js [new file with mode: 0644]
project/static/js/views/split.js [new file with mode: 0644]
project/static/js/views/view.js [new file with mode: 0644]
project/static/js/views/xml.js [new file with mode: 0644]
project/templates/explorer/editor.html

index b413b2c..6ba367b 100644 (file)
@@ -35,7 +35,6 @@ body {
     top: 2.4em; left: 0px; right: 0px; bottom: 0px;
     overflow: auto;    
     background-color: white;
-    padding: 0.2em 1em;
 }
 
 ul {
@@ -107,8 +106,8 @@ label {
 /* ========== */
 
 #panels {
-    position: absolute;
-    bottom: 0px; left: 0px; right: 0px; top: 0px;
+    height: 100%;
+    width: 100%;
 }
 
 .panel-wrap {
@@ -343,13 +342,60 @@ text#commit-dialog-message {
     margin: 0.5em;
 }
 
-.CodeMirror-line-numbers
-{
-    text-align: right;
-    padding-top: 0.4em;
-    padding-right: 2px;
-    width: 28px;
-    font-size: 10pt;
-    background: black;
-    color: white;
-}
\ No newline at end of file
+/* ======= */
+/* = 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: 20px;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    overflow: none;
+}
+
+.xmlview {
+    height: 100%;
+}
+
+.view-overlay {
+    z-index: 1000;
+    background: #FFF;
+    opacity: 0.8;
+    text-align: center;
+    text-valign: center;
+}
+
+.view-overlay p {
+    display: block;
+    position: relative;
+    top: auto;
+    bottom: auto;
+    height: 40px;
diff --git a/project/static/js/app.js b/project/static/js/app.js
new file mode 100644 (file)
index 0000000..6e6f0f8
--- /dev/null
@@ -0,0 +1,285 @@
+/*global Class*/
+var editor;
+var panel_hooks;
+
+
+(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 panels = [];
+
+var documentsUrl = '/api/documents/';
+
+
+var Model = Class.extend({
+  observers: {},
+  
+  init: function() {},
+  
+  signal: function(event, data) {
+    console.log('signal', this, event, data);
+    if (this.observers[event]) {
+      for (key in this.observers[event]) {
+        this.observers[event][key](event, data);
+      }
+    };
+    return this;
+  },
+  
+  addObserver: function(observer, event, callback) {
+    if (!this.observers[event]) {
+      this.observers[event] = {};
+    }
+    this.observers[event][observer.id] = callback;
+    return this;
+  },
+  
+  removeObserver: function(observer, event) {
+    if (!event) {
+      for (e in this.observers) {
+        this.removeObserver(observer, e);
+      }
+    } else {
+      delete this.observers[event][observer.id];
+    }
+    return this;
+  }
+});
+
+
+var XMLModel = Model.extend({
+  parent: null,
+  data: '',
+  serverURL: null,
+  needsReload: false,
+  
+  init: function(parent, serverURL) {
+    this.parent = parent;
+    this.serverURL = serverURL;
+  },
+  
+  getData: function() {
+    if (!this.data) {
+      this.reload();
+    }
+    return this.data;
+  },
+  
+  setData: function(data) {
+    this.data = data;
+    this.dataChanged();
+  },
+  
+  reload: function() {
+    $.ajax({
+      url: this.serverURL,
+      dataType: 'text',
+      success: this.reloadSucceeded.bind(this)
+    });
+  },
+  
+  reloadSucceeded: function(data) {
+    this.data = data;
+    this.signal('reloaded');
+  },
+  
+  dataChanged: function() {
+    this.parent.modelChanged('xml');
+    this.signal('dataChanged');
+  },
+  
+  needsReload: function() {
+    this.needsReload = true;
+    this.signal('needsReload');
+  }
+})
+
+
+
+var HTMLModel = Model.extend({
+  parent: null,
+  data: '',
+  serverURL: null,
+  needsReload: false,
+  
+  init: function(parent, serverURL) {
+    this.parent = parent;
+    this.serverURL = serverURL;
+  },
+  
+  getData: function() {
+    if (!this.data) {
+      this.reload();
+    }
+    return this.data;
+  },
+  
+  setData: function(data) {
+    console.log('setData');
+    if (this.data != data) {
+      this.data = data;
+      this.dataChanged();
+    }
+  },
+  
+  reload: function() {
+    $.ajax({
+      url: this.serverURL,
+      dataType: 'text',
+      success: this.reloadSucceeded.bind(this)
+    });
+  },
+  
+  reloadSucceeded: function(data) {
+    this.data = data;
+    this.signal('reloaded');
+  },
+  
+  dataChanged: function() {
+    this.parent.modelChanged('html');
+  },
+  
+  needsReload: function() {
+    this.needsReload = true;
+    this.signal('needsReload');
+  }
+})
+
+
+var DocumentModel = Model.extend({
+  data: null, // name, text_url, latest_rev, latest_shared_rev, parts_url, dc_url, size
+  xml: null,
+  html: null,
+  contentModels: {},
+  
+  init: function() {
+    this.getData();
+  },
+  
+  getData: function() {
+    console.log('DocumentModel#getData');
+    $.ajax({
+      cache: false,
+      url: documentsUrl + fileId,
+      dataType: 'json',
+      success: this.successfulGetData.bind(this)
+    });
+  },
+  
+  successfulGetData: function(data) {
+    console.log('DocumentModel#successfulGetData:', data);
+    this.data = data;
+    this.contentModels = {
+      'xml': new XMLModel(this, data.text_url)
+    };
+  },
+  
+  modelChanged: function(contentModelName) {
+    for (modelName in this.contentModels) {
+      if (!(modelName == contentModelName)) {
+        this.contentModels[modelName].needsReload();
+      }
+    }
+  }
+});
+
+var leftPanelView, rightPanelContainer, doc;
+
+$(function() {
+  doc = new DocumentModel();
+  var splitView = new SplitView('#splitview', doc);
+  leftPanelView = new PanelContainerView('#left-panel-container', doc);
+  rightPanelContainer = new PanelContainerView('#right-panel-container', doc);
+});
index 79b7fa9..6918f9e 100644 (file)
@@ -320,30 +320,30 @@ Editor.prototype.loadConfig = function() {
 };
 
 Editor.prototype.loadPanelOptions = function() {
-    var self = this;
-    var totalWidth = 0;
-    
-    $('.panel-wrap', self.rootDiv).each(function(index) {
-        var panelWidth = self.fileOptions.panels[index].ratio * self.rootDiv.width();
-        if ($(this).hasClass('last-panel')) {
-            $(this).css({
-                left: totalWidth,
-                right: 0
-            });
-        } else {
-            $(this).css({
-                left: totalWidth,
-                width: panelWidth
-            });
-            totalWidth += panelWidth;               
-        }
-        $.log('panel:', this, $(this).css('left'));
-        $('.panel-toolbar option', this).each(function() {
-            if ($(this).attr('p:panel-name') == self.fileOptions.panels[index].name) {
-                $(this).parent('select').val($(this).attr('value'));
-            }
-        });
-    });   
+    // var self = this;
+    // var totalWidth = 0;
+    // 
+    // $('.panel-wrap', self.rootDiv).each(function(index) {
+    //     var panelWidth = self.fileOptions.panels[index].ratio * self.rootDiv.width();
+    //     if ($(this).hasClass('last-panel')) {
+    //         $(this).css({
+    //             left: totalWidth,
+    //             right: 0
+    //         });
+    //     } else {
+    //         $(this).css({
+    //             left: totalWidth,
+    //             width: panelWidth
+    //         });
+    //         totalWidth += panelWidth;               
+    //     }
+    //     $.log('panel:', this, $(this).css('left'));
+    //     $('.panel-toolbar option', this).each(function() {
+    //         if ($(this).attr('p:panel-name') == self.fileOptions.panels[index].name) {
+    //             $(this).parent('select').val($(this).attr('value'));
+    //         }
+    //     });
+    // });   
 };
 
 Editor.prototype.savePanelOptions = function() {
index f66d977..c8d47f6 100755 (executable)
  * UI related Editor methods\r
  */\r
 Editor.prototype.setupUI = function() {\r
-    // set up the UI visually and attach callbacks\r
+//     // set up the UI visually and attach callbacks\r
     var self = this;\r
-\r
-    var resize_start = function(event, mydata) {\r
-        $(document).bind('mousemove', mydata, resize_changed).\r
-        bind('mouseup', mydata, resize_stop);\r
-\r
-        $('.panel-overlay', mydata.root).css('display', 'block');\r
-        return false;\r
-    }\r
-    var resize_changed =  function(event) {\r
-        var old_width = parseInt(event.data.overlay.css('width'));\r
-        var delta = event.pageX + event.data.hotspot_x - old_width;\r
-\r
-        if(old_width + delta < 12) delta = 12 - old_width;\r
-        if(old_width + delta > $(window).width()) \r
-            delta = $(window).width() - old_width;\r
-        \r
-        event.data.overlay.css({\r
-            'width': old_width + delta\r
-        });\r
-\r
-        if(event.data.overlay.next) {\r
-            var left = parseInt(event.data.overlay.next.css('left'));\r
-            event.data.overlay.next.css('left', left+delta);\r
-        }\r
-\r
-        return false;\r
-    };\r
-\r
-    var resize_stop = function(event) {\r
-        $(document).unbind('mousemove', resize_changed).unbind('mouseup', resize_stop);\r
-        // $('.panel-content', event.data.root).css('display', 'block');\r
-        var overlays = $('.panel-content-overlay', event.data.root);\r
-        $('.panel-content-overlay', event.data.root).each(function(i) {\r
-            if( $(this).data('panel').hasClass('last-panel') )\r
-                $(this).data('panel').css({\r
-                    'left': $(this).css('left'),\r
-                    'right': $(this).css('right')\r
-                });\r
-            else\r
-                $(this).data('panel').css({\r
-                    'left': $(this).css('left'),\r
-                    'width': $(this).css('width')\r
-                });\r
-        });\r
-        $('.panel-overlay', event.data.root).css('display', 'none');\r
-        $(event.data.root).trigger('stopResize');\r
-    };\r
-\r
-    /*\r
-     * Prepare panels (overlays & stuff)\r
-     */\r
-    /* create an overlay */\r
-    var panel_root = self.rootDiv;\r
-    var overlay_root = $("<div class='panel-overlay'></div>");\r
-    panel_root.append(overlay_root);\r
-\r
-    var prev = null;\r
-\r
-    $('*.panel-wrap', panel_root).each( function()\r
-    {\r
-        var panel = $(this);\r
-        var handle = $('.panel-slider', panel);\r
-        var overlay = $("<div class='panel-content-overlay panel-wrap'>&nbsp;</div>");\r
-        overlay_root.append(overlay);\r
-        overlay.data('panel', panel);\r
-        overlay.data('next', null);\r
-\r
-        if (prev) prev.next = overlay;\r
-\r
-        if( panel.hasClass('last-panel') )\r
-        {\r
-            overlay.css({\r
-                'left': panel.css('left'),\r
-                'right': panel.css('right')\r
-            });\r
-        }\r
-        else {\r
-            overlay.css({\r
-                'left': panel.css('left'),\r
-                'width': panel.css('width')\r
-            });\r
-            // $.log('Has handle: ' + panel.attr('id'));\r
-            overlay.append(handle.clone());\r
-            /* attach the trigger */\r
-            handle.mousedown(function(event) {\r
-                var touch_data = {\r
-                    root: panel_root,\r
-                    overlay: overlay,\r
-                    hotspot_x: event.pageX - handle.position().left\r
-                };\r
-\r
-                $(this).trigger('hpanel:panel-resize-start', touch_data);\r
-                return false;\r
-            });\r
-            $('.panel-content', panel).css('right',\r
-                (handle.outerWidth() || 10) + 'px');\r
-            $('.panel-content-overlay', panel).css('right',\r
-                (handle.outerWidth() || 10) + 'px');\r
-        };\r
-\r
-        prev = overlay;\r
-    });\r
-\r
-    panel_root.bind('hpanel:panel-resize-start', resize_start);\r
-    self.rootDiv.bind('stopResize', function() {\r
-        self.savePanelOptions();      \r
-    });\r
-    \r
+// \r
+//     var resize_start = function(event, mydata) {\r
+//         $(document).bind('mousemove', mydata, resize_changed).\r
+//         bind('mouseup', mydata, resize_stop);\r
+// \r
+//         $('.panel-overlay', mydata.root).css('display', 'block');\r
+//         return false;\r
+//     }\r
+//     var resize_changed =  function(event) {\r
+//         var old_width = parseInt(event.data.overlay.css('width'));\r
+//         var delta = event.pageX + event.data.hotspot_x - old_width;\r
+// \r
+//         if(old_width + delta < 12) delta = 12 - old_width;\r
+//         if(old_width + delta > $(window).width()) \r
+//             delta = $(window).width() - old_width;\r
+//         \r
+//         event.data.overlay.css({\r
+//             'width': old_width + delta\r
+//         });\r
+// \r
+//         if(event.data.overlay.next) {\r
+//             var left = parseInt(event.data.overlay.next.css('left'));\r
+//             event.data.overlay.next.css('left', left+delta);\r
+//         }\r
+// \r
+//         return false;\r
+//     };\r
+// \r
+//     var resize_stop = function(event) {\r
+//         $(document).unbind('mousemove', resize_changed).unbind('mouseup', resize_stop);\r
+//         // $('.panel-content', event.data.root).css('display', 'block');\r
+//         var overlays = $('.panel-content-overlay', event.data.root);\r
+//         $('.panel-content-overlay', event.data.root).each(function(i) {\r
+//             if( $(this).data('panel').hasClass('last-panel') )\r
+//                 $(this).data('panel').css({\r
+//                     'left': $(this).css('left'),\r
+//                     'right': $(this).css('right')\r
+//                 });\r
+//             else\r
+//                 $(this).data('panel').css({\r
+//                     'left': $(this).css('left'),\r
+//                     'width': $(this).css('width')\r
+//                 });\r
+//         });\r
+//         $('.panel-overlay', event.data.root).css('display', 'none');\r
+//         $(event.data.root).trigger('stopResize');\r
+//     };\r
+// \r
+//     /*\r
+//      * Prepare panels (overlays & stuff)\r
+//      */\r
+//     /* create an overlay */\r
+//     var panel_root = self.rootDiv;\r
+//     var overlay_root = $("<div class='panel-overlay'></div>");\r
+//     panel_root.append(overlay_root);\r
+// \r
+//     var prev = null;\r
+// \r
+//     $('*.panel-wrap', panel_root).each( function()\r
+//     {\r
+//         var panel = $(this);\r
+//         var handle = $('.panel-slider', panel);\r
+//         var overlay = $("<div class='panel-content-overlay panel-wrap'>&nbsp;</div>");\r
+//         overlay_root.append(overlay);\r
+//         overlay.data('panel', panel);\r
+//         overlay.data('next', null);\r
+// \r
+//         if (prev) prev.next = overlay;\r
+// \r
+//         if( panel.hasClass('last-panel') )\r
+//         {\r
+//             overlay.css({\r
+//                 'left': panel.css('left'),\r
+//                 'right': panel.css('right')\r
+//             });\r
+//         }\r
+//         else {\r
+//             overlay.css({\r
+//                 'left': panel.css('left'),\r
+//                 'width': panel.css('width')\r
+//             });\r
+//             // $.log('Has handle: ' + panel.attr('id'));\r
+//             overlay.append(handle.clone());\r
+//             /* attach the trigger */\r
+//             handle.mousedown(function(event) {\r
+//                 var touch_data = {\r
+//                     root: panel_root,\r
+//                     overlay: overlay,\r
+//                     hotspot_x: event.pageX - handle.position().left\r
+//                 };\r
+// \r
+//                 $(this).trigger('hpanel:panel-resize-start', touch_data);\r
+//                 return false;\r
+//             });\r
+//             $('.panel-content', panel).css('right',\r
+//                 (handle.outerWidth() || 10) + 'px');\r
+//             $('.panel-content-overlay', panel).css('right',\r
+//                 (handle.outerWidth() || 10) + 'px');\r
+//         };\r
+// \r
+//         prev = overlay;\r
+//     });\r
+// \r
+//     panel_root.bind('hpanel:panel-resize-start', resize_start);\r
+//     self.rootDiv.bind('stopResize', function() {\r
+//         self.savePanelOptions();      \r
+//     });\r
+//     \r
     /*\r
      * Connect panel actions\r
      */\r
@@ -187,8 +187,6 @@ Editor.prototype.setupUI = function() {
         onShow: $.fbind(self, self.loadSplitDialog)\r
     }).\r
     jqmAddClose('button.dialog-close-button');\r
-\r
-// $('#split-dialog').   \r
 }\r
 \r
 Editor.prototype.loadRelatedIssues = function(hash)\r
diff --git a/project/static/js/views/html.js b/project/static/js/views/html.js
new file mode 100644 (file)
index 0000000..87f2ab7
--- /dev/null
@@ -0,0 +1,17 @@
+/*global View render_template panels */
+var HTMLView = View.extend({
+  element: null,
+  model: null,
+  template: 'html-view-template',
+  
+  init: function(element, model, template) {
+    this._super(element, model, template);
+  },
+  
+  dispose: function() {
+    this._super();
+  }
+});
+
+// Register view
+panels['html'] = HTMLView;
\ No newline at end of file
diff --git a/project/static/js/views/panel_container.js b/project/static/js/views/panel_container.js
new file mode 100644 (file)
index 0000000..e4950ba
--- /dev/null
@@ -0,0 +1,34 @@
+/*globals View render_template panels*/
+
+var PanelContainerView = View.extend({
+  element: null,
+  model: null,
+  template: 'panel-container-view-template',
+  contentView: null,
+  
+  init: function(element, model, template) {
+    this.element = $(element);
+    this.model = model;
+    this.template = template || this.template;
+    
+    this.element.html(render_template(this.template, {panels: panels}));
+    $('select', this.element.get(0)).bind('change.panel-container-view', this.selectChanged.bind(this));
+  },
+  
+  selectChanged: function(event) {
+    var value = $('select', this.element.get(0)).val();
+    var klass = panels[value];
+    if (this.contentView) {
+      this.contentView.dispose();
+      this.contentView = null;
+    }
+    this.contentView = new klass($('.content-view', 
+      this.element.get(0)), this.model.contentModels[value]);
+  },
+  
+  dispose: function() {
+    $('select', this.element.get(0)).unbind('change.panel-container-view');
+    this._super();
+  }
+});
+
diff --git a/project/static/js/views/split.js b/project/static/js/views/split.js
new file mode 100644 (file)
index 0000000..cc0d361
--- /dev/null
@@ -0,0 +1,106 @@
+/*globals View*/
+
+// Split view inspired by jQuery Splitter Plugin http://methvin.com/splitter/
+var SplitView = View.extend({
+  splitbarClass: 'splitview-splitbar',
+  activeClass: 'splitview-active',
+  overlayClass: 'splitview-overlay',
+  element: null,
+  model: null,
+  zombie: null,
+  leftViewOffset: 0,
+  
+  // Cache
+  _splitbarWidth: 0,
+  
+  init: function(element, model) {
+    this.element = $(element).css('position', 'relative');
+    this.model = model;
+    this.views = $(">*", this.element[0]).css({
+       position: 'absolute',                     // positioned inside splitter container
+       'z-index': 1,                                           // splitbar is positioned above
+       '-moz-outline-style': 'none',   // don't show dotted outline
+      overflow: 'auto'
+    });
+    
+    this.leftView = $(this.views[0]);
+    this.rightView = $(this.views[1]);
+    this.splitbar = $(this.views[2] || '<div></div>')
+      .insertAfter(this.leftView)
+      .css({
+        position: 'absolute',
+        'user-select': 'none',
+        '-webkit-user-select': 'none',
+        '-khtml-user-select': 'none',
+        '-moz-user-select': 'none',
+        'z-index': 100
+      })
+      .attr('unselectable', 'on')
+      .addClass(this.splitbarClass)
+      .bind('mousedown.splitview', this.beginResize.bind(this));
+    
+    this._splitbarWidth = this.splitbar.outerWidth();
+    
+    // Solomon's algorithm ;-)
+    this.resplit(this.element.width() / 2);
+  },
+    
+  beginResize: function(event) {
+    this.zombie = this.zombie || this.splitbar.clone(false).insertAfter(this.leftView);
+    this.overlay = this.overlay || $('<div></div>').addClass(this.overlayClass).css({
+        position: 'absolute',
+        width: this.element.width(),
+        height: this.element.height(),
+        top: this.element.position().top,
+        left: this.element.position().left
+      }).appendTo(this.element);
+    this.views.css("-webkit-user-select", "none"); // Safari selects A/B text on a move
+    this.splitbar.addClass(this.activeClass);
+    this.leftViewOffset = this.leftView[0].offsetWidth - event.pageX;
+    
+    $(document)
+      .bind('mousemove.splitview', this.resizeChanged.bind(this))
+      .bind('mouseup.splitview', this.endResize.bind(this));
+  },
+  
+  resizeChanged: function(event) {
+    var newPosition = event.pageX + this.leftViewOffset;
+    newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
+    this.splitbar.css('left', newPosition);
+  },
+
+  endResize: function(event) {
+    var newPosition = event.pageX + this.leftViewOffset;
+    this.zombie.remove();
+    this.zombie = null;
+    this.overlay.remove();
+    this.overlay = null;
+    this.resplit(newPosition);
+
+    $(document)
+      .unbind('mousemove.splitview')
+      .unbind('mouseup.splitview');
+  },
+
+  resplit: function(newPosition) {
+    newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
+    this.splitbar.css('left', newPosition);
+    this.leftView.css({
+      left: 0,
+      width: newPosition
+    });
+    this.rightView.css({
+      left: newPosition + this._splitbarWidth,
+      width: this.element.width() - newPosition - this._splitbarWidth
+    });
+    if (!$.browser.msie) {
+                 this.views.trigger("resize");
+               }
+  },
+  
+  dispose: function() {
+    this.splitter.unbind('mousedown.splitview');
+    this._super();
+  }
+});
+
diff --git a/project/static/js/views/view.js b/project/static/js/views/view.js
new file mode 100644 (file)
index 0000000..498341e
--- /dev/null
@@ -0,0 +1,65 @@
+/*globals Class render_template*/
+var View = Class.extend({
+  element: null,
+  model: null,
+  template: null,
+  overlayClass: 'view-overlay',
+  overlay: null,
+  id: null,
+  
+  init: function(element, model, template) {
+    this.element = $(element);
+    this.model = model;
+    this.template = template || this.template;
+    
+    if (this.template) {
+      this.element.html(render_template(this.template, {}));
+    }
+    
+    View.lastId = View.lastId + 1;
+    this.id = 'view-' + View.lastId;
+  },
+  
+  // Identyczność
+  hash: function() {
+    
+  },
+  
+  frozen: function() {
+    return !!this.overlay;
+  },
+  
+  freeze: function(message) {
+    this.overlay = this.overlay 
+      || $('<div><div>' + message + '</div></div>')
+            .addClass(this.overlayClass)
+            .css({
+              position: 'absolute',
+              width: this.element.width(),
+              height: this.element.height(),
+              top: this.element.position().top,
+              left: this.element.position().left
+            })
+            .appendTo(this.element.parent());
+            
+    this.overlay.children('div').css({
+      position: 'relative',
+      top: this.overlay.height() / 2 - 20
+    });
+  },
+  
+  unfreeze: function() {
+    if (this.frozen()) {
+      this.overlay.remove();
+      this.overlay = null;      
+    }
+  },
+
+  dispose: function() {
+    this.unfreeze();
+    this.element.contents().remove();
+  }
+});
+
+
+View.lastId = 0;
diff --git a/project/static/js/views/xml.js b/project/static/js/views/xml.js
new file mode 100644 (file)
index 0000000..54abf16
--- /dev/null
@@ -0,0 +1,62 @@
+/*global View CodeMirror render_template panels */
+var XMLView = View.extend({
+  element: null,
+  model: null,
+  template: 'xml-view-template',
+  editor: null,
+  
+  init: function(element, model, template) {
+    this._super(element, model, template);
+    
+    this.freeze('Ładowanie edytora...');
+       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: false,
+      tabMode: 'spaces',
+      indentUnit: 0,
+      onChange: this.changed.bind(this),
+      initCallback: this.editorDidLoad.bind(this)
+    });
+  },
+  
+  changed: function() {
+    this.model.setData(this.editor.getCode());
+  },
+  
+  editorDidLoad: function(editor) {
+    editor.setCode('Ładowanie edytora...');
+    $(editor.frame).css({width: '100%', height: '100%'});
+    this.editor.setCode(this.model.getData());
+    this.unfreeze();
+    this.model
+      .addObserver(this, 'reloaded', function() {
+        this.editor.setCode(this.model.getData()); this.unfreeze(); }.bind(this))
+      .addObserver(this, 'needsReload', function() { 
+        this.freeze('Niezsynchronizowany'); }.bind(this))
+      .addObserver(this, 'dataChanged', this.textDidChange.bind(this));
+
+    // editor.grabKeys(
+    //   $.fbind(self, self.hotkeyPressed),
+    //   $.fbind(self, self.isHotkey)
+    // );
+  },
+  
+  textDidChange: function(event) {
+    console.log('textDidChange!');
+    if (this.editor.getCode() != this.model.getData()) {
+      this.editor.setCode(this.model.getData());
+    }
+  },
+  
+  dispose: function() {
+    this.model.removeObserver(this);
+    $(this.editor.frame).remove();
+    this._super();
+  }
+});
+
+// Register view
+panels['xml'] = XMLView;
index 22a60f6..d3c5196 100644 (file)
     <script src="{{STATIC_URL}}js/lib/jquery.json.js" type="text/javascript" charset="utf-8"></script>
     <script src="{{STATIC_URL}}js/lib/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
     <script src="{{STATIC_URL}}js/lib/jquery.modal.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/views/view.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/panel_container.js" type="text/javascript" charset="utf-8"></script>
+       
     <script src="{{STATIC_URL}}js/editor.js" type="text/javascript" charset="utf-8"></script>
     <script src="{{STATIC_URL}}js/editor.ui.js" type="text/javascript" charset="utf-8"></script>
+
+       {# JavaScript templates #}
+       <script type="text/html" charset="utf-8" id="panel-container-view-template">
+               <select>
+                       <% for (panel in panels) { %>
+                       <option value="<%= panel %>"><%= panel %></option>
+                       <% }; %>
+               </select>
+               <div class="content-view"></div>
+       </script>
+       
+       <script type="text/html" charset="utf-8" id="xml-view-template">
+               <div class="xmlview">
+               </div>
+       </script>
+       
+       <script type="text/html" charset="utf-8" id="html-view-template">
+               <p>Hej! Jestem widokiem HTML!</p>
+       </script>
 {% endblock extrahead %}
 
 {% block extrabody %}
 {% endblock %}
 
 {% block maincontent %}
-        <div id="panels">
-            {% for n in panel_list %}
-            <div class="panel-wrap{% if forloop.last %} last-panel{% endif %}" id="panel-{{n}}">
-                <div class="panel-toolbar">
-                    <p><label>{{n|capfirst}} panel:
-                    <select name="select-{{n}}-panel" id="panel-{{n}}-select">
-                        {% for panel_type in availble_panels %}
-                        <option value="{% url panel_view path=fileid,name=panel_type.id  %}" p:panel-name="{{ panel_type.id }}">{{panel_type.display_name}}</option>
-                        {% endfor %}
-                    </select>
-                    </label>
+       <div id="splitview">
+               <div id="left-panel-container" class='panel-container'></div>
+           <div id="right-panel-container" class='panel-container'></div>
+    </div>
 
-                    <span
-                    <span class="toolbar-buttons-container panel-toolbar-extra">
-                        </span>
-                    </span>
-                    <!-- rethink the refresh button - it doesn't work very well -->
-                    <!-- <button type="button" class="refresh-button">Odśwież</button> -->
-                    {# <a href="{% url print_xml fileid %}" target="_new">Wydruk</a> #}
-                    <strong class="change-notification" style="display: none">Widok nieaktualny!</strong>
-                    </p>
-               </div>
-               <div id="panel-{{n}}-content" class="panel-content"></div>
-               <button type="button" class="panel-slider"></button>
-            </div>
-            {% endfor %}
-        </div>
 
-        <div id="commit-dialog" class="jqmWindow">
-            <form action="{% url file_commit fileid %}" 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="http://localhost:3000/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="commit-dialog" class="jqmWindow">
+        <form action="{% url file_commit fileid %}" 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="http://localhost:3000/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">
-            <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 id="split-dialog" class="jqmWindow">
+        <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 %}