diff --git a/src/main/webapp/scripts/lib/spawn/spawn.js b/src/main/webapp/scripts/lib/spawn/spawn.js
index 818ea34a181ffb348f3d9458a821fad52f6d6072..45a86a44993c660bbcd7899f0b7990a1dc0d6016 100644
--- a/src/main/webapp/scripts/lib/spawn/spawn.js
+++ b/src/main/webapp/scripts/lib/spawn/spawn.js
@@ -164,7 +164,7 @@
         }
         
         // handle passing a config object as the only argument
-        if (!children && !opts) {
+        if (!children && !opts && typeof tag !== 'string') {
             opts = tag;
             tag = null;
         }
diff --git a/src/main/webapp/scripts/xnat/spawner.js b/src/main/webapp/scripts/xnat/spawner.js
index afae05a62ca7b230b6253734f8c7d4e64a8a327e..02f9631aa3c33c2252adcd997979221fe3d81614 100644
--- a/src/main/webapp/scripts/xnat/spawner.js
+++ b/src/main/webapp/scripts/xnat/spawner.js
@@ -36,7 +36,7 @@ var XNAT = getObject(XNAT);
     spawner.notSpawned = [];
 
     function setRoot(url){
-        url = url.replace(/^([*~.]\/+)/, '/');
+        url = url.replace(/^([*~.]\/*)/, '/');
         return XNAT.url.rootUrl(url)
     }
 
diff --git a/src/main/webapp/scripts/xnat/ui/panel.js b/src/main/webapp/scripts/xnat/ui/panel.js
index 6ef65f211c7f13beaed985ba7155821dd3614f1d..432c25c6f8c2b80404dbbe0d826b490c5f2ab597 100644
--- a/src/main/webapp/scripts/xnat/ui/panel.js
+++ b/src/main/webapp/scripts/xnat/ui/panel.js
@@ -842,10 +842,8 @@ var XNAT = getObject(XNAT || {});
     //////////////////////////////////////////////////
     // DATA PANELS - RETRIEVE/DISPLAY DATA
     //////////////////////////////////////////////////
-
-    panel.data = {};
-
-    panel.data.table = function(opts){
+    
+    panel.dataTable = function(opts){
 
         // initialize the table
         var dataTable = XNAT.table.dataTable(opts.data||[], opts);
@@ -860,7 +858,7 @@ var XNAT = getObject(XNAT || {});
         
     };
 
-    panel.data.list = function(opts){
+    panel.dataList = function(opts){
         // initialize the table
         opts = cloneObject(opts);
         opts.element = opts.element || {};
diff --git a/src/main/webapp/scripts/xnat/ui/table.js b/src/main/webapp/scripts/xnat/ui/table.js
index ec651f9445b3086c73c0b42e8f414aa715d4f0b4..e7070fc61557643c5584357477e5384e84afc03c 100755
--- a/src/main/webapp/scripts/xnat/ui/table.js
+++ b/src/main/webapp/scripts/xnat/ui/table.js
@@ -51,30 +51,36 @@ var XNAT = getObject(XNAT);
      */
     function Table(opts, config){
 
-        this.opts = opts || {};
-        this.config = config || null;
+        this.newTable = function(o, c){
+            o = o || opts || {};
+            c = c || config;
+            this.opts = cloneObject(o);
+            this.config = c ? cloneObject(c) : null;
+            this.table = element('table', this.opts);
+            this.$table = this.table$ = $(this.table);
 
-        this.table = element('table', this.opts);
-        this.table$ = $(this.table);
+            this.last = {};
 
-        this.last = {};
+            // 'parent' gets reset on return of chained methods
+            this.last.parent = this.table;
 
-        // 'parent' gets reset on return of chained methods
-        this.last.parent = this.table;
+            // get 'last' item wrapped in jQuery
+            this.last$ = function(el){
+                return $(this.last[el || 'parent']);
+            };
 
-        // get 'last' item wrapped in jQuery
-        this.last$ = function(el){
-            return $(this.last[el || 'parent']);
-        };
+            this.setLast = function(el){
+                this.last.parent =
+                    this.last[el.tagName.toLowerCase()] =
+                        el;
+            };
+
+            this._rows = [];
+            this._cols = 0; // how many columns?
 
-        this.setLast = function(el){
-            this.last.parent =
-                this.last[el.tagName.toLowerCase()] =
-                    el;
         };
 
-        this._rows = [];
-        this._cols = 0; // how many columns?
+        this.newTable();
 
     }
 
@@ -126,8 +132,9 @@ var XNAT = getObject(XNAT);
         //data = data || this.data || null;
         if (data) {
             this.last.tr = tr;
-            [].concat(data).forEach(function(item){
-                _this.td(item);                
+            [].concat(data).forEach(function(item, i){
+                if (_this._cols && _this._cols > i) return;
+                _this.td(item)._cols++;
             });
         }
         // only add <tr> elements to <table>, <thead>, <tbody>, and <tfoot>
@@ -141,22 +148,23 @@ var XNAT = getObject(XNAT);
         return this;
     };
 
-    // create a row with <tr> and <td> elements
+    // create a <tr> with optional <td> elements
     // in the <tbody>
-    Table.p.row = function(data, opts){
-        // var tr = element('tr', opts);
+    Table.p.row = Table.p.addRow = function(data, opts){
         data = data || [];
         this.tr(opts, data);
-        // (this.last.tbody || this.table).appendChild(tr);
-        // nullify last <th> and <td> elements since this is a new row
-        // this.last.th = this.last.td = null;
+        return this;
+    };
+
+    // add a <tr> to <tbody>
+    Table.p.bodyRow = function(data, opts){
+        this.toBody().row(data, opts);
         return this;
     };
 
     // create *multiple* <td> elements
     Table.p.tds = function(items, opts){
         var _this = this;
-        // var last_tr = this.last.tr;
         [].concat(items).forEach(function(item){
             if (stringable(item)) {
                 _this.td(opts, item);
@@ -206,34 +214,16 @@ var XNAT = getObject(XNAT);
     };
 
     // reset last.parent to <thead>
-    Table.p.toHead = Table.p.closestBody = function(){
+    Table.p.toHead = Table.p.closestHead = function(){
         this.setLast(this.last.thead || this.table);
         return this;
     };
 
-    Table.p.bodyRow = function(){
-        this.toBody();
-        this.tr();
-        return this;
-    };
-
-    // add a SINGLE row of data
-    Table.p.addRow = function(data){
-        var _this = this;
-        this.tr();
-        [].concat(data).forEach(function(item){
-            // could be an array of arrays
-            _this.td(item);
-        });
-        return this;
-    };
-
     // add multiple rows of data?
-    Table.p.appendBody = function(data){
+    Table.p.appendBody = Table.p.appendToBody = function(data){
         var _this = this;
         [].concat(data).forEach(function(row){
-            _this.toBody();
-            _this.addRow(row);
+            _this.toBody().addRow(row);
         });
         return this;
     };
@@ -242,11 +232,11 @@ var XNAT = getObject(XNAT);
         return this.table;
     };
 
-    Table.p.get$ = function(){
+    Table.p.$get = Table.p.get$ = function(){
         return $(this.table);
     };
 
-    Table.p.getHTML = function(){
+    Table.p.getHTML = Table.p.html = function(){
         return this.table.outerHTML;
     };
 
@@ -262,9 +252,13 @@ var XNAT = getObject(XNAT);
             header,
             cols  = 0;
 
-        // don't init twice
+        // don't init twice?
         if (this.inited) {
-            return this
+            // run .init() again to
+            // empty table and load new data
+            this.table$.empty();
+            //this.newTable();
+            //return this
         }
 
         data = data || [];
@@ -298,8 +292,7 @@ var XNAT = getObject(XNAT);
 
         // add the header
         if (header) {
-            this.thead();
-            this.tr();
+            this.thead().tr();
             [].concat(header).forEach(function(item){
                 _this.th(item);
             });
@@ -335,18 +328,14 @@ var XNAT = getObject(XNAT);
         }
         return this.table;
     };
-
-    Table.p.html = function(){
-        return this.table.outerHTML;
-    };
-
+    
     // 'opts' are options for the <table> element
     // 'config' is for other configurable stuff
     table = function(opts, config){
         return new Table(opts, config);
     };
 
-    // helper for future XNAT DataTable widget
+    // basic XNAT.dataTable widget
     table.dataTable = function(data, opts){
 
         var tableData = data;
@@ -443,6 +432,12 @@ var XNAT = getObject(XNAT);
             $$(opts.container).append(newTable.table);
         }
 
+        // add properties for Spawner compatibility
+        newTable.element = newTable.spawned = newTable.table;
+        newTable.get = function(){
+            return newTable.table;
+        };
+
         return newTable;
 
     };
diff --git a/src/main/webapp/scripts/xnat/ui/tabs.js b/src/main/webapp/scripts/xnat/ui/tabs.js
index 73eaf37aa06abe936f9f260c154a195e415d0bba..0f6c65a73e6f75fdc7b1b436dffdf9ba48051f46 100755
--- a/src/main/webapp/scripts/xnat/ui/tabs.js
+++ b/src/main/webapp/scripts/xnat/ui/tabs.js
@@ -81,7 +81,7 @@ var XNAT = getObject(XNAT || {});
         var $group, groupId, tabId, tabIdHash, _flipper, _pane;
 
         obj = cloneObject(obj);
-        obj.config = cloneObject(obj.config);
+        obj.element = getObject(obj.element || obj.config);
 
         tabId = toDashed(obj.id || obj.name || randomID('t', false));
 
@@ -108,13 +108,13 @@ var XNAT = getObject(XNAT || {});
         }
         tab.paneFooter = paneFooter;
 
-        obj.config.data =
-            extend(true, {}, obj.config.data, {
+        obj.element.data =
+            extend(true, {}, obj.element.data, {
                 name: obj.name||'',
                 tab: tabId
             });
 
-        _pane = spawn('div.tab-pane', obj.config);
+        _pane = spawn('div.tab-pane', obj.element);
 
         // if 'active' is explicitly set, use the tabId value
         obj.active = (obj.active) ? tabId : '';
@@ -130,16 +130,18 @@ var XNAT = getObject(XNAT || {});
             tabs.active = tab.active = tabId;
         }
 
-        groupId = toDashed(obj.group||'other');
-
-        // un-hide the group that this tab is in
-        // (groups are hidden until there is a tab for them)
-        $group = $('#' + groupId + '.tab-group');
+        if (tabs.hasGroups) {
+            groupId = toDashed(obj.group||'other');
+            // un-hide the group that this tab is in
+            // (groups are hidden until there is a tab for them)
+            $group = $('#' + groupId + '.tab-group');
+        }
+        else {
+            $group = $$(tabs.navTabs).find('ul.tab-group')
+        }
 
-        $group.show();
-        
         // add all the flippers
-        $group.append(_flipper);
+        $group.append(_flipper).show();
 
         function onRender(){
             console.log('tab: ' + tabId)
@@ -178,6 +180,11 @@ var XNAT = getObject(XNAT || {});
         navTabs = spawn('div.xnat-nav-tabs');
         tabContent = spawn('div.xnat-tab-content');
 
+        // copy values to XNAT.tabs object for use elsewhere
+        tabs.container = container;
+        tabs.layout = layout;
+        tabs.navTabs = navTabs;
+
         if (layout === 'left') {
             navTabs.className += ' side pull-left';
             tabContent.className += ' side pull-right';
@@ -188,8 +195,14 @@ var XNAT = getObject(XNAT || {});
         $container.append(navTabs);
         $container.append(tabContent);
 
-        // set up the group elements
-        $(navTabs).append(tab.groups(obj.meta.tabGroups));
+        // set up the group elements, if present
+        if (obj.meta && obj.meta.tabGroups){
+            tabs.hasGroups = true;
+            $(navTabs).append(tab.groups(obj.meta.tabGroups));
+        }
+        else {
+            $(navTabs).spawn('ul.tab-group');
+        }
 
         // bind tab click events
         $container.on('click', 'li.tab', function(e){