Działający widok SplitView.
authorzuber <marek@stepniowski.com>
Wed, 23 Sep 2009 14:09:53 +0000 (16:09 +0200)
committerzuber <marek@stepniowski.com>
Wed, 23 Sep 2009 14:09:53 +0000 (16:09 +0200)
project/static/css/master.css
project/static/js/app.js [new file with mode: 0644]
project/static/js/editor.ui.js
project/static/js/views/split.js

index de79989..256c09b 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 {
@@ -341,4 +340,15 @@ text#commit-dialog-message {
 
 #split-dialog .container-box fieldset {
     margin: 0.5em;
-}
\ No newline at end of file
+}
+
+/* ======= */
+/* = New = */
+/* ======= */
+.splitview-splitbar {
+    width: 5px;
+    border-left: 1px solid #999;
+    border-right: 1px solid #999;
+    height: 100%;
+    background-color: #CCC;
+}
diff --git a/project/static/js/app.js b/project/static/js/app.js
new file mode 100644 (file)
index 0000000..702fb0c
--- /dev/null
@@ -0,0 +1,70 @@
+/*global Class*/
+var editor;
+var panel_hooks;
+
+
+(function(){
+  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;
+  };
+})();
+
+(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);
+    }
+  }
+  
+})();
+
+$(function() {
+  var splitView = new SplitView('#panels');
+});
index 8932b4e..de2d0bf 100755 (executable)
  * UI related Editor methods\r
  */\r
 Editor.prototype.setupUI = function() {\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
-     * Connect panel actions\r
-     */\r
-    $('#panels > *.panel-wrap').each(function() {\r
-        var panelWrap = $(this);\r
-        // $.log('wrap: ', panelWrap);\r
-        var panel = new Panel(panelWrap);\r
-        panelWrap.data('ctrl', panel); // attach controllers to wraps\r
-        panel.load($('.panel-toolbar select', panelWrap).val());\r
-\r
-        $('.panel-toolbar select', panelWrap).change(function() {\r
-            var url = $(this).val();\r
-            panelWrap.data('ctrl').load(url);\r
-            self.savePanelOptions();\r
-        });\r
-\r
-        $('.panel-toolbar button.refresh-button', panelWrap).click(\r
-            function() {\r
-                panel.refresh();\r
-            } );\r
-\r
-        self.rootDiv.bind('stopResize', function() {\r
-            panel.callHook('toolbarResized');\r
-        });\r
-    });\r
-\r
-    $(document).bind('panel:contentChanged', function() {\r
-        self.onContentChanged.apply(self, arguments)\r
-    });  \r
-\r
-    /*\r
-     * Connect various buttons\r
-     */\r
-\r
-    $('#toolbar-button-quick-save').click( function (event, data) {\r
-        self.saveToBranch();\r
-    } );\r
-\r
-    $('#toolbar-button-save').click( function (event, data) {\r
-        $('#commit-dialog').jqmShow( {callback: $.fbind(self, self.saveToBranch)} );\r
-    } );\r
-\r
-    $('#toolbar-button-update').click( function (event, data) {\r
-        if (self.updateUserBranch()) {\r
-            // commit/update can be called only after proper, save\r
-            // this means all panels are clean, and will get refreshed\r
-            // do this only, when there are any changes to local branch\r
-            self.refreshPanels();\r
-        }\r
-    } );\r
-\r
-    /* COMMIT DIALOG */\r
-    $('#commit-dialog').\r
-    jqm({\r
-        modal: true,\r
-        onShow: $.fbind(self, self.loadRelatedIssues)        \r
-    });\r
-\r
-    $('#toolbar-button-commit').click( function (event, data) {\r
-        $('#commit-dialog').jqmShow( {callback: $.fbind(self, self.sendMergeRequest)} );\r
-    } );\r
-    \r
-    /* STATIC BINDS */\r
-    $('#commit-dialog-cancel-button').click(function() {\r
-        $('#commit-dialog-error-empty-message').hide();\r
-        $('#commit-dialog').jqmHide();\r
-    });   \r
-    \r
-\r
-    /* SPLIT DIALOG */\r
-    $('#split-dialog').jqm({\r
-        modal: true,\r
-        onShow: $.fbind(self, self.loadSplitDialog)\r
-    }).\r
-    jqmAddClose('button.dialog-close-button');\r
-\r
-// $('#split-dialog').   \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
+//      * Connect panel actions\r
+//      */\r
+//     $('#panels > *.panel-wrap').each(function() {\r
+//         var panelWrap = $(this);\r
+//         // $.log('wrap: ', panelWrap);\r
+//         var panel = new Panel(panelWrap);\r
+//         panelWrap.data('ctrl', panel); // attach controllers to wraps\r
+//         panel.load($('.panel-toolbar select', panelWrap).val());\r
+// \r
+//         $('.panel-toolbar select', panelWrap).change(function() {\r
+//             var url = $(this).val();\r
+//             panelWrap.data('ctrl').load(url);\r
+//             self.savePanelOptions();\r
+//         });\r
+// \r
+//         $('.panel-toolbar button.refresh-button', panelWrap).click(\r
+//             function() {\r
+//                 panel.refresh();\r
+//             } );\r
+// \r
+//         self.rootDiv.bind('stopResize', function() {\r
+//             panel.callHook('toolbarResized');\r
+//         });\r
+//     });\r
+// \r
+//     $(document).bind('panel:contentChanged', function() {\r
+//         self.onContentChanged.apply(self, arguments)\r
+//     });  \r
+// \r
+//     /*\r
+//      * Connect various buttons\r
+//      */\r
+// \r
+//     $('#toolbar-button-quick-save').click( function (event, data) {\r
+//         self.saveToBranch();\r
+//     } );\r
+// \r
+//     $('#toolbar-button-save').click( function (event, data) {\r
+//         $('#commit-dialog').jqmShow( {callback: $.fbind(self, self.saveToBranch)} );\r
+//     } );\r
+// \r
+//     $('#toolbar-button-update').click( function (event, data) {\r
+//         if (self.updateUserBranch()) {\r
+//             // commit/update can be called only after proper, save\r
+//             // this means all panels are clean, and will get refreshed\r
+//             // do this only, when there are any changes to local branch\r
+//             self.refreshPanels();\r
+//         }\r
+//     } );\r
+// \r
+//     /* COMMIT DIALOG */\r
+//     $('#commit-dialog').\r
+//     jqm({\r
+//         modal: true,\r
+//         onShow: $.fbind(self, self.loadRelatedIssues)        \r
+//     });\r
+// \r
+//     $('#toolbar-button-commit').click( function (event, data) {\r
+//         $('#commit-dialog').jqmShow( {callback: $.fbind(self, self.sendMergeRequest)} );\r
+//     } );\r
+//     \r
+//     /* STATIC BINDS */\r
+//     $('#commit-dialog-cancel-button').click(function() {\r
+//         $('#commit-dialog-error-empty-message').hide();\r
+//         $('#commit-dialog').jqmHide();\r
+//     });   \r
+//     \r
+// \r
+//     /* SPLIT DIALOG */\r
+//     $('#split-dialog').jqm({\r
+//         modal: true,\r
+//         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
index 84f48ea..d8c5e6b 100644 (file)
@@ -1,82 +1,89 @@
 /*globals Class*/
 
+// Split view inspired by jQuery Splitter Plugin http://methvin.com/splitter/
 var SplitView = Class.extend({
-  leftPanelClass: 'splitview-left-panel',
-  rightPanelClass: 'splitview-right-panel',
-  splitterClass: 'splitview-splitter',
-  overlayClass: 'splitview-overlay',
+  splitbarClass: 'splitview-splitbar',
+  activeClass: 'splitview-active',
   element: null,
+  zombie: null,
+  leftViewOffset: 0,
+  
+  // Cache
+  _splitbarWidth: 0,
   
   init: function(element) {
-    this.element = $(element);
-    this.leftPanel = $(this.leftPanelClass, element);
-    this.rightPanel = $(this.rightPanelClass, element);
-    this.splitter = $(this.splitterClass, element);
-    this.overlay = this._createOverlay(this.overlayClass);
+    this.element = $(element).css('position', 'relative');
+    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.splitter.bind('mousedown.splitview', this.beginResize.bind(this));
-  },
-  
-  _createOverlay: function(cssClass) {
-    var pos = this.root.position();
-    return $('<div />')
-      .addClass(cssClass)
+    this.leftView = $(this.views[0]);
+    this.rightView = $(this.views[1]);
+    this.splitbar = $(this.views[2] || '<div></div>')
+      .insertAfter(this.leftView)
       .css({
         position: 'absolute',
-        left: pos.left,
-        top: pos.top,
-        width: this.root.width(),
-        height: this.root.height()
+        'user-select': 'none',
+        '-webkit-user-select': 'none',
+        '-khtml-user-select': 'none',
+        '-moz-user-select': 'none',
+        'z-index': 100
       })
-      .hide()
-      .appendTo(this.element);
+      .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.hotspotX = event.pageX - this.splitter.position().left;
+    this.zombie = this.zombie || this.splitbar.clone(false).insertAfter(this.leftView);
+    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));
-
-    this.overlay.show();
-    return false;
   },
   
   resizeChanged: function(event) {
-    var old_width = this.overlay.width();
-    var delta = event.pageX + this.hotspotX - old_width;
-
-    if(old_width + delta < 12) delta = 12 - old_width;
-    if(old_width + delta > $(window).width()) {
-      delta = $(window).width() - old_width;
-    }
-    
-    this.overlay.css({'width': old_width + delta});
-    
-    if(this.overlay.next) {
-        var left = parseInt(this.overlay.next.css('left'), 10);
-        this.overlay.next.css('left', left+delta);
-    }
-    return false;
+    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.resplit(newPosition);
+
     $(document)
       .unbind('mousemove.splitview')
       .unbind('mouseup.splitview');
-    
-    this.leftPanel.css({
+  },
+
+  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,
-      right: this.hotspotX
+      width: newPosition
     });
-
-    this.rightPanel.css({
-      left: this.hotspotX,
-      right: 0
+    this.rightView.css({
+      left: newPosition + this._splitbarWidth,
+      width: this.element.width() - newPosition - this._splitbarWidth
     });
-
-    this.overlay.hide();
+    if (!$.browser.msie) {
+                 this.views.trigger("resize");
+               }
   },
   
   dispose: function() {