diff --git a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
old mode 100755
new mode 100644
index 2b5f0b381790ac3461beff36267bfac7bc806d4c..3bdd2557d1d0e24f0918e8a40385c5a18c8474de
--- a/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
+++ b/src/main/resources/META-INF/xnat/spawner/site-admin-elements.yaml
@@ -24,7 +24,7 @@ siteDescriptionPage:
         type: text
         id: siteDescriptionPage
         name: siteDescriptionPage
-        size: 50
+        size: 30
     after: "<p>Specify a velocity template file to display on the login page</p>"
 
 siteDescriptionText:
@@ -206,7 +206,7 @@ siteInfo:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         ${siteId}
@@ -224,7 +224,7 @@ adminInfo:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         ${adminEmail}
@@ -236,7 +236,7 @@ generalSecuritySettings:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         securityChannel:
@@ -245,7 +245,7 @@ generalSecuritySettings:
             name: security.channel
             label: Security Channel
             options:
-                any: 
+                any:
                     label: Any
                     value: any
                 http:
@@ -270,7 +270,7 @@ userLoginsSessionControls:
     label: User Logins / Session Controls
     method: POST
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contentType: json
     contents:
@@ -370,7 +370,7 @@ passwords:
     method: POST
     action: /xapi/siteConfig/batch
     contentType: json
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         passwordComplexity:
@@ -444,7 +444,7 @@ csrf:
     method: POST
     action: /xapi/siteConfig/batch
     contentType: json
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         enableCsrfToken:
@@ -467,7 +467,7 @@ securityServices:
     action: /xapi/siteConfig/batch
     method: POST
     contentType: json
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         securityServicesFeatureDefault:
@@ -490,13 +490,13 @@ securityServices:
             id: securityServicesRoleRepositoryDefault
             name: security.services.roleRepository.default
             label: Role Repository Default
-            
+
 emailServerSettings:
     kind: panel.form
     method: POST
     action: /xapi/notifications/batchMail
     contentType: json
-    load: ?? XNAT.data.notifications
+    load: XNAT.data.notifications
     refresh: /xapi/notifications
     name: emailServerSettings
     label: Mail Server Settings
@@ -561,7 +561,7 @@ notifications:
     action: /xapi/notifications/batch
     method: POST
     contentType: json
-    load: ?? XNAT.data.notifications
+    load: XNAT.data.notifications
     refresh: /xapi/notifications
     contents:
         helpContactInfo:
@@ -726,7 +726,7 @@ authenticationMethods:
     method: POST
     action: /xapi/siteConfig/batch
     contentType: json
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         xnatInternal:
@@ -751,7 +751,7 @@ genericAuthenticationProvider:
     method: POST
     action: /xapi/siteConfig/batch
     contentType: json
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         providerDbName:
@@ -777,7 +777,7 @@ ldapAuthentication:
     method: POST
     action: /xapi/siteConfig/batch
     contentType: json
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         ldapName:
@@ -840,7 +840,7 @@ registrationOptions:
     label: Registration Options
     method: POST
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     contentType: json
     contents:
         requireEmailVerificationToRegister:
@@ -898,7 +898,7 @@ manageDataTypes:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         displayNameForGenericImageSessionSingular:
@@ -919,7 +919,7 @@ anonymization:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         enableSitewideAnonymizationScript:
@@ -942,7 +942,7 @@ seriesImportFilter:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         enableSitewideSeriesImportFilter:
@@ -981,7 +981,7 @@ sessionUploadMethod:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         selectUploadMethod:
@@ -991,11 +991,11 @@ sessionUploadMethod:
             label: "Session Upload Method"
 #            options:                             # don't know where to populate this from
 #                http:
-#                    label: 
-#                    value: 
+#                    label:
+#                    value:
 #                https:
-#                    label: 
-#                    value: 
+#                    label:
+#                    value:
         showApplet:
             kind: panel.input.checkbox
             id: showApplet
@@ -1028,13 +1028,72 @@ sessionUploadMethod:
             label: Session Xml Rebuilder Interval
 
 dicomScpReceivers:
-    kind: panel.form
+    kind: panel
     name: dicomScpReceivers
+    label: Manage DICOM SCP Receivers
+    footer: false
+    contents:
+        message:
+            tag: div.message.bold
+            content: >
+                Caution: Changes to this setting will take effect immediately.
+                Before disabling the receiver, verify that there are no
+                transmissions currently in progress.
+        dicomScpEditorTemplate:
+            tag: "div#dicom-scp-editor-template.html-template"
+            contents:
+                dicomScpEditor:
+                    kind: panel.form
+                    name: dicomScpEditor
+                    id: dicom-scp-editor
+                    header: false
+                    footer: false
+                    method: PUT
+                    action: /xapi/dicomscp
+                    contentType: json
+                    contents:
+                        scpId:
+                            kind: panel.input.text
+                            id: scp-id
+                            name: scpId
+                            label: SCP ID
+                            validation: required
+                        aeTitle:
+                            kind: panel.input.text
+                            name: aeTitle
+                            label: AE Title
+                            validation: required
+                        port:
+                            kind: panel.input.number
+                            name: port
+                            label: Port
+                            validation: required naturalNoZero
+                        fileNamer:
+                            kind: panel.input.text
+                            name: fileNamer
+                            label: File Namer
+                        identifier:
+                            kind: panel.input.text
+                            name: identifier
+                            label: Identifier
+                        enabled:
+                            kind: panel.input.checkbox
+                            name: enabled
+        dicomScpManager:
+            tag: "div#dicom-scp-manager"
+        dicomScpManagerScript:
+            tag: script
+            element:
+                src: /scripts/xnat/admin/dicomScpManager.js
+
+dicomScpReceiversOld:
+    kind: panel.form
+    name: dicomScpReceiversOld
     label: DICOM SCP Receivers
     method: POST
     action: /xapi/dicomscp
     contentType: json
-    load: $? /xapi/dicomscp
+    load: /xapi/dicomscp
     contents:
         enableDicomReceiver:
             kind: panel.input.checkbox
@@ -1083,7 +1142,7 @@ dicomScpReceivers:
             name: receivedFileUser
             label: "Default DICOM Receiver: User"
             description: "User account for default DICOM receiver"
-            
+
 fileSystem:
     kind: panel.form
     name: fileSystem
@@ -1091,7 +1150,7 @@ fileSystem:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     contents:
         ${archivePath}
         ${cachePath}
@@ -1110,7 +1169,7 @@ misc:
     method: POST
     contentType: json
     action: /xapi/siteConfig/batch
-    load: ?? XNAT.data.siteConfig
+    load: XNAT.data.siteConfig
     refresh: /xapi/siteConfig
     contents:
         scanTypeMapping:
@@ -1130,7 +1189,7 @@ misc:
                     element:
                         href: ~/page/admin/spawner/
                         target: _blank
-                        html: Manage The Spawner
+                        html: Manage Spawner Elements
         swagger:
             kind: panel.element
             description: View the Swagger page.
diff --git a/src/main/webapp/page/admin/style.css b/src/main/webapp/page/admin/style.css
index 946b2f1f34bd6c48b04502031733291ffde74aea..28dc92c624b8c22fcfaad92d9c7770e06f90c851 100644
--- a/src/main/webapp/page/admin/style.css
+++ b/src/main/webapp/page/admin/style.css
@@ -72,11 +72,12 @@ body.xnat .panel { margin-bottom: 30px; background: /* #f8f8f8 */ inherit; borde
 body.xnat .panel-default { border: 1px solid #c8c8c8; }
 .panel-default .panel-heading {
     padding: 12px 16px;
-    color: #222; /background: /* #e9e9e9 */ inherit;
+    color: #fff; background: #1A75BB;
 }
-.panel-default .panel-body { padding: 12px 30px; }
+body.xnat .xmodal .panel { border: none; }
+.panel-default .panel-body { padding: 20px; }
 .panel-default .panel-footer  { padding: 12px; background: #f0f0f0; border-top: #c8c8c8; }
-.panel-title { font-weight: normal; font-size: 20px; line-height: inherit; }
+.panel-title { font-weight: normal; font-size: 18px; line-height: inherit; }
 
 /* PANEL ELEMENTS */
 .panel .panel-element { margin: 15px 0; clear: both; overflow: auto; }
@@ -244,4 +245,4 @@ embedded base-64 image fallbacks
 }
 .affix {
     position: fixed;
-}
\ No newline at end of file
+}
diff --git a/src/main/webapp/scripts/lib/spawn/spawn.js b/src/main/webapp/scripts/lib/spawn/spawn.js
index f5b425155a9c359128479b8f865122d0b4fc616a..51d020e2a95934f4d72f0730c29c3ff154dcfeef 100644
--- a/src/main/webapp/scripts/lib/spawn/spawn.js
+++ b/src/main/webapp/scripts/lib/spawn/spawn.js
@@ -148,7 +148,7 @@
         var el, $el, parts, id, classes, tagParts, attrs, isVoid,
             // property names to skip later
             skip = [
-                'innerHTML', 'html', 'attr', 'append', 'appendTo',
+                'innerHTML', 'html', 'attr', 'prepend', 'append', 'appendTo',
                 'classes', 'className', 'style', 'data', 'fn'
             ],
             errors = []; // collect errors
@@ -370,18 +370,18 @@
 
         var frag = document.createDocumentFragment();
 
-        if (opts.after){
-            frag.appendChild(el);
-            appendChildren(frag, opts.after, spawn);
-            el = frag;
-        }
-        
         if (opts.before){
             appendChildren(frag, opts.before, spawn);
             frag.appendChild(el);
             el = frag;
         }
 
+        if (opts.after){
+            frag.appendChild(el);
+            appendChildren(frag, opts.after, spawn);
+            el = frag;
+        }
+        
         if (errors.length){
             if (hasConsole) console.log(errors);
         }
diff --git a/src/main/webapp/scripts/xmodal-v1/xmodal.js b/src/main/webapp/scripts/xmodal-v1/xmodal.js
index b967f12a01bfb4554617974f15dd5da671c36bf0..84af5f4a47a5ad8da7396adbb2d2f45b2007f020 100644
--- a/src/main/webapp/scripts/xmodal-v1/xmodal.js
+++ b/src/main/webapp/scripts/xmodal-v1/xmodal.js
@@ -35,27 +35,37 @@ if (typeof jQuery == 'undefined') {
         // can't decide on a prefix for selection by id
         // use ONE of these:
         // id= | id: | @id= | @# | @= | @: | @ | #= | #: | #/
-        var ALL_PREFIX = /^\*!/, // $$('*!div.foo') --> return all 'div.foo' elements as an array
-            RAW_ID = /^#!/,      // $$('#!foo') --> return (one) element with id 'foo'
-            RAW_PREFIX = /^!/,   // $$('!div.foo') --> return FIRST 'div.foo' element
-            ID_PREFIX = /^(id=|id:|@id=|@#|@=|@:|@|#=|#:|#\/)/;
+        var ALL_PREFIX = /^!\*/,  // $$('!*div.foo') --> return raw 'div.foo' elements as an array
+            RAW_ID     = /^!#/,   // $$('!#foo')     --> return (one) raw element with id 'foo'
+            NAME_RAW   = /^!\?/,  // $$('!?foo')     --> return raw elements with [name="foo"]
+            NAME_$     = /^\?/,   // $$('?foo')      --> return wrapped elements with [name="foo"]
+            RAW_PREFIX = /^!/,    // $$('!div.foo')  --> return FIRST raw 'div.foo' element
+            ID_PREFIX  = /^(id=|id:|@id=|@#|@=|@:|@|#=|#:|#\/)/;
         if (!el || el.jquery){
             return el;
         }
-        if (el.search(ALL_PREFIX) === 0){
-            return document.querySelectorAll(el.replace(ALL_PREFIX, ''));
-        }
-        // pass empty string or null as the second argument
-        // to get the bare element by id (no jQuery)
-        if (id_prefix === '' || id_prefix === null || el.search(RAW_ID) === 0){
-            return document.getElementById(el.replace(RAW_ID,''));
-        }
-        if (el.search(RAW_PREFIX) === 0){
-            return document.querySelector(el.replace(RAW_PREFIX,''));
-        }
-        id_prefix = id_prefix || ID_PREFIX;
-        if (el.search(id_prefix) === 0){
-            return $(document.getElementById(el.replace(id_prefix,'')));
+        if (typeof el == 'string'){
+            if (el.search(ALL_PREFIX) === 0){
+                return document.querySelectorAll(el.replace(ALL_PREFIX, ''));
+            }
+            // pass empty string or null as the second argument
+            // to get the bare element by id (no jQuery)
+            if (id_prefix === '' || id_prefix === null || el.search(RAW_ID) === 0){
+                return document.getElementById(el.replace(RAW_ID,''));
+            }
+            if (el.search(NAME_RAW) === 0){
+                return document.getElementsByName(el.replace(NAME_RAW, ''));
+            }
+            if (el.search(NAME_$) === 0){
+                return $(document.getElementsByName(el.replace(NAME_$, '')));
+            }
+            if (el.search(RAW_PREFIX) === 0){
+                return document.querySelector(el.replace(RAW_PREFIX,''));
+            }
+            id_prefix = id_prefix || ID_PREFIX;
+            if (el.search(id_prefix) === 0){
+                return $(document.getElementById(el.replace(id_prefix,'')));
+            }
         }
         return $(el);
     }
diff --git a/src/main/webapp/scripts/xnat/admin/dicomScpManager.js b/src/main/webapp/scripts/xnat/admin/dicomScpManager.js
new file mode 100644
index 0000000000000000000000000000000000000000..29aee2bea8f28180c48d6c634e6a9a2f4bc51867
--- /dev/null
+++ b/src/main/webapp/scripts/xnat/admin/dicomScpManager.js
@@ -0,0 +1,283 @@
+/*!
+ * Manage DICOM SCP Receivers
+ */
+
+var XNAT = getObject(XNAT || {});
+
+(function(factory){
+    if (typeof define === 'function' && define.amd) {
+        define(factory);
+    }
+    else if (typeof exports === 'object') {
+        module.exports = factory();
+    }
+    else {
+        return factory();
+    }
+}(function(){
+
+    var dicomScpManager, undefined,
+        rootUrl = XNAT.url.rootUrl;
+
+    XNAT.admin = 
+        getObject(XNAT.admin || {});
+
+    XNAT.admin.dicomScpManager = dicomScpManager =
+        getObject(XNAT.admin.dicomScpManager || {});
+
+    dicomScpManager.samples = [
+        {
+            "aeTitle": "Bogus",
+            "enabled": true,
+            "fileNamer": "string",
+            "identifier": "string",
+            "port": 0,
+            "scpId": "BOGUS"
+        },
+        {
+            "enabled": true,
+            "fileNamer": "string",
+            "identifier": "string",
+            "port": 8104,
+            "scpId": "XNAT",
+            "aeTitle": "XNAT"
+        }
+    ];
+
+    function spacer(width){
+        return spawn('i.spacer', {
+            style: {
+                display: 'inline-block',
+                width: width + 'px'
+            }
+        })
+    }
+    
+    // keep track of used ports to help prevent port conflicts
+    dicomScpManager.usedPorts = [];
+
+    // get the list of DICOM SCP Receivers
+    dicomScpManager.getReceivers = dicomScpManager.getAll = function(callback){
+        callback = isFunction(callback) ? callback : function(){};
+        return XNAT.xhr.get({
+            url: rootUrl('/xapi/dicomscp'),
+            dataType: 'json',
+            success: function(data){
+                // refresh the 'usedPorts' array every time this function is called
+                dicomScpManager.usedPorts = data.map(function(item){
+                    return item.port;
+                });
+                callback.apply(this, arguments);
+            }
+        });
+    };
+
+    dicomScpManager.getReceiver = dicomScpManager.getOne = function(id, callback){
+        if (!id) return null;
+        callback = isFunction(callback) ? callback : function(){};
+        return XNAT.xhr.get({
+            url: rootUrl('/xapi/dicomscp/' + id),
+            dataType: 'json',
+            success: callback
+        });
+    };
+
+    dicomScpManager.get = function(id){
+        if (!id) {
+            return dicomScpManager.getAll();
+        }
+        return dicomScpManager.getOne(id);
+    };
+
+    // dialog to create/edit receivers
+    dicomScpManager.dialog = function(item, opts){
+        var tmpl = $('#dicom-scp-editor-template');
+        var doWhat = !item ? 'New' : 'Edit';
+        item = item || {};
+        xmodal.open({
+            title: doWhat + ' DICOM SCP Receiver',
+            template: tmpl.clone(),
+            height: 500,
+            padding: '0',
+            beforeShow: function(obj){
+                var $form = obj.$modal.find('#dicom-scp-editor-panel');
+                if (item && item.scpId) {
+                    forOwn(item, function(prop, val){
+                        $form.find('[name="'+prop+'"]').val(val);
+                    });
+                }
+            },
+            okClose: false,
+            okLabel: 'Save',
+            okAction: function(obj){
+                var $form = obj.$modal.find('#dicom-scp-editor-panel');
+                var id = $form.find('#scp-id').val();
+                XNAT.xhr.form($form, {
+                    method: 'PUT',
+                    url: '/xapi/dicomscp/' + id,
+                    contentType: 'application/json'
+                });
+            }
+        });
+    };
+
+    // create table for DICOM SCP receivers
+    dicomScpManager.table = function(container, callback){
+
+        // initialize the table - we'll add to it below
+        var scpTable = XNAT.table({
+            className: 'dicom-scp-receivers xnat-table',
+            style: {
+                width: '100%',
+                marginTop: '15px',
+                marginBottom: '15px'
+            }
+        });
+
+        // add table header row
+        scpTable.tr()
+                .th({ addClass: 'left', html: '<b>ID</b>' })
+                .th({ addClass: 'left', html: '<b>AE Title</b>' })
+                .th('<b>Port</b>')
+                .th('<b>Enabled</b>')
+                //.th('<b>Default?</b>')  // if this is enabled, enable the radio button(s) too (below)
+                .th('<b>Actions</b>');
+
+        // TODO: move event listeners to parent elements - events will bubble up
+        // ^-- this will reduce the number of event listeners
+        function enabledCheckbox(item){
+            return spawn('div.center', [
+                ['input.enabled', {
+                    type: 'checkbox',
+                    checked: !!item.enabled,
+                    onclick: function(){
+                        // save the status when clicked
+                        var enabled = this.checked;
+                        XNAT.xhr.put({
+                            url: rootUrl('/xapi/dicomscp/' + item.scpId + '/enabled/' + enabled),
+                            success: function(){
+                                console.log(item.scpId + (enabled ? ' enabled' : ' disabled'))
+                            }
+                        });
+                    }
+                }]
+            ]);
+        }
+
+        function editButton(item){
+            return spawn('button.btn.sm.edit', {
+                onclick: function(){
+                    dicomScpManager.dialog(item);
+                    //alert('(feature not yet enabled)')
+                }
+            }, 'Edit');
+        }
+
+        function deleteButton(item){
+            return spawn('button.btn.sm.delete', {
+                onclick: function(){
+                    xmodal.confirm({
+                        content: "" +
+                        "Are you sure you'd like to delete the '" + item.aeTitle + "' DICOM Receiver? " +
+                        "<b>This action cannot be undone.</b>",
+                        okAction: function(){
+                            XNAT.xhr.delete({
+                                url: rootUrl('/xapi/dicomscp/' + item.scpId),
+                                success: function(){
+                                    console.log('"'+ item.scpId + '" deleted')
+                                }
+                            });
+                        }
+                    })
+                }
+            }, 'Delete');
+        }
+        
+        dicomScpManager.getAll().done(function(data){
+            data.forEach(function(item){
+                scpTable.tr({title:item.scpId})
+                        .td(item.scpId)
+                        .td(item.aeTitle)
+                        .td([['div.mono.center', item.port]])
+                        .td([enabledCheckbox(item)])
+                        .td([['div.center', [editButton(item), spacer(10), deleteButton(item)]]]);
+                // scpTable.row([
+                //     item.aeTitle,
+                //     [['div.mono.center', item.port]],
+                //     [enabledCheckbox(item)],
+                //     //[['div.center', [['input|type=radio;name=defaultReceiver']] ]], // how do we know which one is 'default'
+                //     [['div.center', [editButton(item), spacer(10), deleteButton(item)]]]
+                // ]);
+            });
+
+            if (container){
+                $$(container).append(scpTable.table);
+            }
+
+            if (isFunction(callback)) {
+                callback();
+            }
+
+        });
+        
+        return scpTable.table;
+
+    };
+
+    dicomScpManager.init = function(container){
+        
+        var $manager = $$(container||'div#dicom-scp-manager');
+
+        $manager.append(dicomScpManager.table());
+        // dicomScpManager.table($manager);
+        
+        var newReceiver = spawn('button.new-dicomscp-receiver.btn.btn-sm.submit.pull-right', {
+            html: 'New DICOM SCP Receiver',
+            onclick: function(){
+                dicomScpManager.dialog();
+            }
+        });
+
+        var startAll = spawn('button.start-receivers.btn.btn-sm', {
+            html: 'Start All',
+            onclick: function(){
+                XNAT.xhr.put({
+                    url: XNAT.url.rootUrl('/xapi/dicomscp/start'),
+                    success: function(){
+                        console.log('DICOM SCP Receivers started')
+                    }
+                })
+            }
+        });
+
+        var stopAll = spawn('button.stop-receivers.btn.btn-sm', {
+            html: 'Stop All',
+            onclick: function(){
+                XNAT.xhr.put({
+                    url: XNAT.url.rootUrl('/xapi/dicomscp/stop'),
+                    success: function(){
+                        console.log('DICOM SCP Receivers stopped')
+                    }
+                })
+            }
+        });
+
+        // add the start, stop, and 'add new' buttons at the bottom
+        $manager.append(spawn('div', [
+            startAll, spacer(10), stopAll, newReceiver, ['div.clear.clearfix']
+        ]));
+        
+        return {
+            element: $manager[0],
+            spawned: $manager[0],
+            get: function(){
+                return $manager[0]
+            }
+        };
+    };
+
+    dicomScpManager.init();
+
+    return XNAT.admin.dicomScpManager = dicomScpManager;
+
+}));
diff --git a/src/main/webapp/scripts/xnat/app/siteSetup.js b/src/main/webapp/scripts/xnat/app/siteSetup.js
index 997b6fa9c50897208026669e99c67d932d8cc989..00262b57338167bf4de5774863d8518c3959a7a7 100644
--- a/src/main/webapp/scripts/xnat/app/siteSetup.js
+++ b/src/main/webapp/scripts/xnat/app/siteSetup.js
@@ -60,7 +60,7 @@ var XNAT = getObject(XNAT);
 
             resetBtn = spawn('button', {
                 type: 'button',
-                classes: 'btn btn-sm btn-default revert pull-right',
+                classes: 'btn revert pull-right',
                 html: 'Discard Changes',
                 onclick: function(e){
                     e.preventDefault();
diff --git a/src/main/webapp/scripts/xnat/element.js b/src/main/webapp/scripts/xnat/element.js
index e908f25f76efae07e88169ed91b17983d81e8bc0..6cddc1386c9f0adb6fbecf21fb68e6368f33cead 100644
--- a/src/main/webapp/scripts/xnat/element.js
+++ b/src/main/webapp/scripts/xnat/element.js
@@ -215,7 +215,7 @@ var XNAT = getObject(XNAT||{});
     's small sub sup u b i em strong pre ' +
     'form fieldset button input textarea ' +
     'label select option optgroup ' +
-    'img map area embed object script' +
+    'img map area embed object' +
     '').split(/\s+/);
 
     tagNames.forEach(function(tag, i){
@@ -257,10 +257,45 @@ var XNAT = getObject(XNAT||{});
         // -> <div>Foo</div>
         element[tag] = function(opts, content){
             var args = setOpts(opts, content);
-            return spawn.element(tag, args[0], args[1]);
+            return spawn(tag, args[0], args[1]);
         }
 
     });
+    
+    
+    // insert items into <head> element
+    element.head = {
+        append: function(opts){
+            opts = cloneObject(opts);
+            opts.tagName = opts.tagName || opts.tag;
+            if (!opts.tagName) return;
+            delete opts.tag;
+            var el = spawn(opts.tagName, opts);
+            document.head.appendChild(el);
+            return {
+                element: el,
+                spawned: el
+            }
+        }
+    };
+    element.head.insert = element.head.append;
+    
+
+    // special handling of <script> elements
+    // add them to the <head>
+    element.script = function(opts){
+        opts = cloneObject(opts);
+        var el;
+        if (opts.src) {
+            opts.html = '';
+        }
+        el = spawn('script', opts);
+        document.head.appendChild(el);
+        return {
+            element: el,
+            spawned: el
+        }
+    };
 
 
     //////////////////////////////////////////////////////////////////////
diff --git a/src/main/webapp/scripts/xnat/spawner.js b/src/main/webapp/scripts/xnat/spawner.js
index 7faa5a4f7a8a422c473e819dd389a9394156d879..ae1ebf0327ce28a4d6a96653c80eb12283376d09 100644
--- a/src/main/webapp/scripts/xnat/spawner.js
+++ b/src/main/webapp/scripts/xnat/spawner.js
@@ -221,7 +221,7 @@ var XNAT = getObject(XNAT);
 
         _spawn.done = function(callback){
             if (isFunction(callback)) {
-                callback()
+                callback(_spawn)
             }
             return _spawn;
         };
diff --git a/src/main/webapp/scripts/xnat/ui/panel.js b/src/main/webapp/scripts/xnat/ui/panel.js
index 89094323623a6b36c1b07aa4934015f8cd01296d..aeb0a31e66c4a44032936910fb85f949f26e9810 100644
--- a/src/main/webapp/scripts/xnat/ui/panel.js
+++ b/src/main/webapp/scripts/xnat/ui/panel.js
@@ -37,12 +37,12 @@ var XNAT = getObject(XNAT || {});
     }
     
     // string that indicates to look for a namespaced object value
-    var doLookupString = '??';
+    var lookupPrefix = '??';
 
-    function doLookup(input){
+    function lookupValue(input){
         if (!input) return '';
-        if (input.toString().indexOf(doLookupString) === 0){
-            input = input.split(doLookupString)[1].trim();
+        if (input.toString().indexOf(lookupPrefix) === 0){
+            input = input.split(lookupPrefix)[1].trim();
             return lookupObjectValue(window, input);
         }
         return input;
@@ -57,15 +57,19 @@ var XNAT = getObject(XNAT || {});
 
         opts = cloneObject(opts);
         opts.element = opts.element || opts.config || {};
+        opts.title = opts.title || opts.label || opts.header;
 
         var _target = spawn('div.panel-body', opts.element),
+                
+            hideHeader = (isDefined(opts.header) && (opts.header === false || /^-/.test(opts.title))),
 
             hideFooter = (isDefined(opts.footer) && (opts.footer === false || /^-/.test(opts.footer))),
 
             _panel  = spawn('div.panel.panel-default', [
-                ['div.panel-heading', [
-                    ['h3.panel-title', opts.title || opts.label]
-                ]],
+
+                (hideHeader ? ['div.hidden'] : ['div.panel-heading', [
+                    ['h3.panel-title', opts.title]
+                ]]),
 
                 // target is where the next spawned item will render
                 _target,
@@ -88,17 +92,23 @@ var XNAT = getObject(XNAT || {});
             }
         }
     };
+    
+    panel.formSampleConfig = {
+            
+    };
 
     // creates a panel that's a form that can be submitted
     panel.form = function panelForm(opts, callback){
 
         opts = cloneObject(opts);
         opts.element = opts.element || opts.config || {};
-
-        opts.name = opts.name || opts.element.name || opts.id || opts.element.id || randomID('form-', false)
+        opts.title = opts.title || opts.label || opts.header;
+        opts.name = opts.name || opts.element.name || opts.id || opts.element.id || randomID('form-', false);
 
         var _target = spawn('div.panel-body', opts.element),
 
+            hideHeader = (isDefined(opts.header) && (opts.header === false || /^-/.test(opts.title))),
+
             hideFooter = (isDefined(opts.footer) && (opts.footer === false || /^-/.test(opts.footer))),
 
             _resetBtn = spawn('button.btn.btn-sm.btn-default.revert.pull-right|type=button', 'Discard Changes'),
@@ -114,13 +124,14 @@ var XNAT = getObject(XNAT || {});
             _formPanel = spawn('form.validate.xnat-form-panel.panel.panel-default', {
                 name: opts.name,
                 method: opts.method || 'POST',
-                action: opts.action ? XNAT.url.rootUrl(opts.action) : '#!'
+                action: opts.action ? XNAT.url.rootUrl(opts.action) : '#!',
+                addClass: opts.classes || ''
             }, [
-                ['div.panel-heading', [
-                    ['h3.panel-title', opts.title || opts.label]
-                ]],
 
-                
+                (hideHeader ? ['div.hidden'] : ['div.panel-heading', [
+                    ['h3.panel-title', opts.title]
+                ]]),
+
                 // target is where this form's "contents" will be inserted
                 _target,
 
@@ -171,37 +182,57 @@ var XNAT = getObject(XNAT || {});
 
             obj = cloneObject(obj);
 
-            xmodal.loading.open('#load-data');
-
             // need a form to put the data into!
-            if (!form) {
+            // and a 'load' property too
+            if (!form || !obj.load) {
                 xmodal.loading.close('#load-data');
                 return;
             }
 
-            // if 'load' starts with ??, do lookup
-            var doLookup = '??';
+            xmodal.loading.open('#load-data');
 
-            if (obj.load && obj.load.toString().indexOf(doLookup) === 0) {
-                obj.load = (obj.load.split(doLookup)[1]||'').trim().split('|')[0];
-                obj.prop = obj.prop || obj.load.split('|')[1] || '';
-                setValues(form, lookupObjectValue(window, obj.load, obj.prop));
-                xmodal.loading.close('#load-data');
-                return form;
+            obj.load = obj.load.toString().trim();
+
+            // if 'load' starts with '$?', '~/', or just '/'
+            // then values need to load via REST
+            var ajaxPrefix = /^(\$\?|~\/|\/)/;
+            var doAjax = ajaxPrefix.test(obj.load);
 
-            }
-            
             // if 'load' starts with '!?' do an eval()
-            var doEval = '!?';
+            var evalPrefix = '!?';
+
+            // if 'load' starts with ?? (or NOT evalPrefix or ajaxPrefix), do lookup
+            var lookupPrefix = '??';
+
+            if (!doAjax) {
+
+                var doLookup = obj.load.indexOf(lookupPrefix) === 0;
+                if (doLookup) {
+                    obj.load = (obj.load.split(lookupPrefix)[1]||'').trim().split('|')[0];
+                    obj.prop = obj.prop || obj.load.split('|')[1] || '';
+                    setValues(form, lookupObjectValue(window, obj.load, obj.prop));
+                    xmodal.loading.close('#load-data');
+                    return form;
+                }
+
+                var doEval = obj.load.indexOf(evalPrefix) === 0;
+                if (doEval) {
+                    obj.load = (obj.load.split(evalPrefix)[1]||'').trim();
+                }
+
+                // lastly try to eval the 'load' value
+                try {
+                    setValues(form, eval(obj.load));
+                }
+                catch (e) {
+                    console.log(e);
+                }
 
-            if (obj.load && obj.load.toString().indexOf(doEval) === 0) {
-                obj.load = (obj.load.split(doEval)[1]||'').trim();
-                setValues(form, eval(obj.load));
                 xmodal.loading.close('#load-data');
                 return form;
-
+                
             }
-            
+
             //////////
             // REST
             //////////
@@ -209,12 +240,13 @@ var XNAT = getObject(XNAT || {});
             var ajaxUrl = obj.refresh || '';
 
             // if 'load' starts with $?, do ajax request
-            var ajaxPrefix = '$?';
+            //var ajaxPrefix = '$?';
             var ajaxProp = '';
 
             // value: $? /path/to/data | obj:prop:name
-            if (obj.load && obj.load.toString().indexOf(ajaxPrefix) === 0) {
-                ajaxUrl = (obj.load.split(ajaxPrefix)[1]||'').trim().split('|')[0];
+            // value: ~/path/to/data|obj.prop.name
+            if (doAjax) {
+                ajaxUrl = (obj.load.split(ajaxPrefix)[2]||'').trim().split('|')[0];
                 ajaxProp = ajaxUrl.split('|')[1] || '';
             }
 
@@ -268,6 +300,7 @@ var XNAT = getObject(XNAT || {});
 
         opts.onload = opts.onload || callback;
 
+        // custom event for reloading data (refresh)
         $formPanel.on('reload-data', function(){
             loadData(this, {
                 refresh: opts.refresh || opts.load || opts.url
@@ -712,7 +745,7 @@ var XNAT = getObject(XNAT || {});
             opts.text ||
             opts.html || '';
     
-        opts.element.html = doLookup(opts.element.html);
+        opts.element.html = lookupValue(opts.element.html);
 
         opts.element.rows = 6;
         
diff --git a/src/main/webapp/scripts/xnat/ui/table.js b/src/main/webapp/scripts/xnat/ui/table.js
index 5d78860113588cbace0f1d5ffe14415662a05411..28a841f02a36c8e490d5eaf170c9472154ec06c8 100755
--- a/src/main/webapp/scripts/xnat/ui/table.js
+++ b/src/main/webapp/scripts/xnat/ui/table.js
@@ -25,7 +25,7 @@ var XNAT = getObject(XNAT);
 }(function(XNAT, $){
 
     var table,
-        element = spawn.element,
+        element = window.spawn,
         undefined;
 
     // add new element class without destroying existing class