From 0703381cc93f21a3d03a622e131ce732da094c7a Mon Sep 17 00:00:00 2001
From: "Mark M. Florida" <markflorida@wustl.edu>
Date: Fri, 15 Jul 2016 18:37:20 -0500
Subject: [PATCH] XNAT-4142: script editor now works in all browsers; added
 "Script Label" column to listing and set label to copy id if left blank in
 the editor;

---
 src/main/webapp/scripts/lib/spawn/spawn.js    |  32 +-
 src/main/webapp/scripts/utils.js              |  16 +-
 .../webapp/scripts/xnat/app/scriptEditor.js   | 342 ++++++++----------
 .../webapp/xnat-templates/screens/Scripts.vm  |  23 +-
 4 files changed, 210 insertions(+), 203 deletions(-)

diff --git a/src/main/webapp/scripts/lib/spawn/spawn.js b/src/main/webapp/scripts/lib/spawn/spawn.js
index 7b2f64c0..818ea34a 100644
--- a/src/main/webapp/scripts/lib/spawn/spawn.js
+++ b/src/main/webapp/scripts/lib/spawn/spawn.js
@@ -148,17 +148,28 @@
         var el, $el, parts, id, classes, tagParts, attrs, isVoid,
             // property names to skip later
             skip = [
-                'innerHTML', 'html', 'attr', 'config', 'kind', 'prepend', 'append', 'appendTo',
-                'classes', 'className', 'addClass', 'style', 'data', 'fn', 'label'
+                'innerHTML', 'html', 'attr', 'config', 
+                'kind', 'tag', 'tagName',
+                'prepend', 'append', 'appendTo',
+                'classes', 'className', 'addClass',
+                'style', 'data', 'fn', 'label', 'done'
             ],
             errors = []; // collect errors
 
-        // deal with passing an array as the only argument
+        // handle passing an array with the arguments
         if (Array.isArray(tag)){
             children = tag[2];
             opts = tag[1];
             tag = tag[0];
         }
+        
+        // handle passing a config object as the only argument
+        if (!children && !opts) {
+            opts = tag;
+            tag = null;
+        }
+        
+        tag = tag || opts.tag || opts.tagName || 'span';
 
         if (tag === '!'){
             el = doc.createDocumentFragment();
@@ -380,7 +391,11 @@
         }
 
         if (opts.after){
-            frag.appendChild(el);
+            // don't append element twice if 
+            // there's 'before' AND 'after'
+            if (!opts.before) {
+                frag.appendChild(el);
+            }
             appendChildren(frag, opts.after, spawn);
             el = frag;
         }
@@ -394,10 +409,17 @@
 
         el.element = el;
 
-        el.get = function(){
+        el.get = function(callback){
+            if (typeof callback == 'function') {
+                callback.call(el);
+            }
             return el;
         };
 
+        if (typeof opts.done == 'function') {
+            opts.done.call(el);    
+        }
+
         return el;
 
     }
diff --git a/src/main/webapp/scripts/utils.js b/src/main/webapp/scripts/utils.js
index afd53303..f65aa438 100755
--- a/src/main/webapp/scripts/utils.js
+++ b/src/main/webapp/scripts/utils.js
@@ -299,9 +299,9 @@ jQuery.fn.tableSort = function(){
         if ($tr.find('> th, > td').first().hasClass('index')) return;
         $tr.prepend('<td class="index hidden" style="display:none;">' + i + '</td>');
     });
-    $table.find('th').not('.sort').filter(function(){
-        return this.innerHTML.trim() > '';
-    }).addClass('sort');
+    // $table.find('th').not('.sort').filter(function(){
+    //     return this.innerHTML.trim() > '';
+    // }).addClass('sort');
     $table.find('th.sort')
           .append('<i>&nbsp;</i>')
           // wrapInner('<a href="#" class="nolink" title="click to sort on this column"/>').
@@ -309,14 +309,14 @@ jQuery.fn.tableSort = function(){
               // don't overwrite existing title
               this.title += ' (click to sort) ';
               $(this).on('click.sort', function(){
-                  var $this = $(this),
-                      thIndex = $this.index(),
-                      sorted = $this.hasAnyClass('asc desc'),
+                  var $th = $(this),
+                      thIndex = $th.index(),
+                      sorted = $th.hasAnyClass('asc desc'),
                       sortOrder = 1,
                       sortClass = 'asc';
                   if (sorted) {
                       // if already sorted, switch to descending order
-                      if ($this.hasClass('asc')) {
+                      if ($th.hasClass('asc')) {
                           sortClass = 'desc';
                       }
                       else {
@@ -325,7 +325,7 @@ jQuery.fn.tableSort = function(){
                       }
                   }
                   $table.find('th.sort').removeClass('asc desc');
-                  $this.addClass(sortClass);
+                  $th.addClass(sortClass);
                   sortOrder = (sortClass === 'desc') ? -1 : 1;
                   sorted = !!sortClass;
                   $table.find('td').filter(function(){
diff --git a/src/main/webapp/scripts/xnat/app/scriptEditor.js b/src/main/webapp/scripts/xnat/app/scriptEditor.js
index 4d82de62..123cdf73 100644
--- a/src/main/webapp/scripts/xnat/app/scriptEditor.js
+++ b/src/main/webapp/scripts/xnat/app/scriptEditor.js
@@ -3,18 +3,22 @@
  * xnat-templates/screens/Scripts.vm
  */
 
-var XNAT = getObject(XNAT||{});
+var XNAT = getObject(XNAT || {});
 
 (function(XNAT){
 
     var app, scriptEditor,
         xhr = XNAT.xhr;
 
+    var csrfParam = {
+        XNAT_CSRF: csrfToken
+    };
+    
     XNAT.app =
-        app = getObject(XNAT.app||{});
+        app = getObject(XNAT.app || {});
 
     XNAT.app.scriptEditor =
-        scriptEditor = getObject(XNAT.app.scriptEditor||{});
+        scriptEditor = getObject(XNAT.app.scriptEditor || {});
 
     scriptEditor.scriptIds = [];
     scriptEditor.languages = scriptEditor.runners = [];
@@ -41,12 +45,13 @@ var XNAT = getObject(XNAT||{});
     };
 
     // return script url with common parts pre-defined
-    function scriptURL( scriptId, params ){
-        return XNAT.url.restUrl('/data/automation/scripts/' + scriptId, params);
+    function scriptURL(scriptId, params){
+        scriptId = (scriptId) ? '/' + scriptId : '';
+        return XNAT.url.restUrl('/data/automation/scripts' + scriptId, params);
     }
 
     // return script url with common parts pre-defined
-    function scriptVersionsURL( scriptId, params ){
+    function scriptVersionsURL(scriptId, params){
         return XNAT.url.restUrl('/data/automation/scriptVersions/' + scriptId, params);
     }
 
@@ -56,7 +61,7 @@ var XNAT = getObject(XNAT||{});
 
         var langOptions = '<option value="!">Select a Language</option>';
 
-        if (langs.length){
+        if (langs.length) {
             forEach(langs, function(lang){
                 langOptions +=
                     '<option value="' + lang + '">' + lang + '</option>';
@@ -69,25 +74,25 @@ var XNAT = getObject(XNAT||{});
 
     scriptEditor.loadScripts = function(){
 
-        var rowTemplate = $('#script-row-template').find('> tbody').html(),
+        var rowTemplate  = $('#script-row-template').find('> tbody').html(),
             scriptsTable = $('#scripts-table'),
-            noScripts = $('#no-scripts-installed');
+            noScripts    = $('#no-scripts-installed');
 
-        xhr.getJSON(XNAT.url.restUrl('/data/automation/scripts'), function(json){
+        xhr.getJSON(scriptURL(), function(json){
 
             var scripts = json.ResultSet.Result,
-                list = '';
+                list    = '';
 
             // reset id array
             scriptEditor.scriptIds = [];
 
-            if (scripts.length){
+            if (scripts.length) {
                 $.each(scripts, function(i, script){
                     scriptEditor.scriptIds.push(script['Script ID']);
                     list +=
-                        rowTemplate.
-                        replace(/__SCRIPT_ID__/g, script['Script ID']).
-                        replace(/__SCRIPT_DESCRIPTION__/g, XNAT.utils.escapeXML(script['Description']));
+                        rowTemplate.replace(/__SCRIPT_ID__/g, script['Script ID'])
+                                   .replace(/__SCRIPT_LABEL__/g, script['Script Label'])
+                                   .replace(/__SCRIPT_DESCRIPTION__/g, XNAT.utils.escapeXML(script['Description']));
                 });
                 scriptsTable.find('> tbody').html(list);
                 scriptsTable.show();
@@ -102,27 +107,27 @@ var XNAT = getObject(XNAT||{});
 
     };
 
-    function saveScript( scriptId, $dialog, editor_id ){
+    function saveScript(scriptId, $dialog, editor_id){
 
         var $scriptIdInput = $dialog.find('.script-id-input');
 
-        if ( !scriptId ) {
+        if (!scriptId) {
             xmodal.message('Error: Empty Script ID', 'Please give your script an ID before saving.');
         }
         // check for valid script ID
-        else if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(scriptId)){
+        else if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(scriptId)) {
             xmodal.message({
                 title: 'Error: Invalid Characters in Script ID',
                 message: 'Script ID must start with a letter and can only consist of letters, numbers, and the underscore "_" character.',
                 action: function(){
                     setTimeout(function(){
                         $scriptIdInput.focus().select();
-                    },10);
+                    }, 10);
                 }
             });
         }
         // check to see if script already exists
-        else if ( scriptEditor.scriptIds.indexOf($scriptIdInput.val()) > -1 ) {
+        else if (scriptEditor.scriptIds.indexOf($scriptIdInput.val()) > -1) {
             // TODO: (maybe) get list via REST instead of when the page loads
             xmodal.message({
                 title: 'Error: Script ID Already Exists',
@@ -130,7 +135,7 @@ var XNAT = getObject(XNAT||{});
                 action: function(){
                     setTimeout(function(){
                         $scriptIdInput.focus().select();
-                    },10);
+                    }, 10);
                 }
             });
         }
@@ -141,31 +146,30 @@ var XNAT = getObject(XNAT||{});
                 $dialog.find('.editor-content').attr('id');
 
             var data = {
-                scriptLabel: $dialog.find('.scriptLabel').val(),
+                // copy id to label if empty
+                scriptLabel: $dialog.find('.scriptLabel').val() || scriptId,
                 content: ace.edit(editor_id).getSession().getValue(),
                 description: $dialog.find('.script-description').val(),
                 scriptVersion: $dialog.find('.script-version').val(),
                 language: $dialog.find('input.language').val() || ''
             };
 
-            if (!data.content || /^\s+$/.test(data.content)){
+            if (!data.content || /^\s+$/.test(data.content)) {
                 xmodal.message('No Content', 'Please add script content and try again.');
             }
             else {
-                var csrfParam = {
-                    XNAT_CSRF: csrfToken
-                };
                 xhr.put(scriptURL(scriptId, csrfParam), data, {
                     success: function(){
+                        xmodal.close($dialog);
+                        scriptEditor.loadScripts();
                         xmodal.message('Success', 'Your script was successfully saved.', {
                             action: function(){
-                                scriptEditor.loadScripts();
                                 xmodal.closeAll();
                             }
                         });
                         //xmodal.close($dialog);
                     },
-                    error: function( request, status, error ){
+                    error: function(request, status, error){
                         xmodal.message('Error', 'An error occurred: [' + status + '] ' + error);
                     }
                 });
@@ -174,26 +178,119 @@ var XNAT = getObject(XNAT||{});
     }
 
     var counter = 0;
+
+    // load script into the editor
+    function loadEditor($dialog, json){
+
+        json = json || {};
+
+        console.log(json);
+
+        var scriptId = json.scriptId || '';
+        var lang = json.language || 'groovy';
+
+        $dialog.find('.id').val(json.id || '');
+
+        // version menu
+        var $versionMenu = $dialog.find('select.script-version');
+
+        if (scriptId) {
+            
+            // if editing existing script, show id as text
+            $dialog.find('.script-id-text').html(scriptId);
+            $dialog.find('.script-id-input').remove();
+            
+            // and load the versions menu
+            xhr.getJSON({
+                url: scriptVersionsURL(scriptId),
+                success: function(versions){
+                    $versionMenu.empty().append(versions.map(function(vers, i){
+                        return spawn('option', {
+                            value: vers,
+                            html: i + 1
+                        });  
+                    }));
+                    // make sure latest version is selected
+                    $versionMenu.find('option').last().prop('selected', true);
+                    $versionMenu.change();
+                }
+            });
+        }
+        else {
+            $versionMenu.append('<option value="1" selected>1</option>')
+        }
+
+        var $wrapper = $dialog.find('.editor-wrapper');
+        
+        function initEditor(data){
+
+            var timestamp = data.timestamp || '',
+                modifiedDate = '';
+
+            // update top fields
+            $dialog.find('.scriptId').val(data.scriptId);
+            $dialog.find('.scriptLabel').val(data.scriptLabel || '');
+            $dialog.find('.timestamp').val(timestamp);
+            $dialog.find('.script-description').val(data.description || '');
+
+            // update date/time in foter
+            if (timestamp) {
+                modifiedDate = ('last modified: ' + (new Date(timestamp)).toString());
+                $dialog.find('.modified-date').html(modifiedDate);
+            }
+
+            var editor = spawn('div', {
+                id: 'script-' + (scriptId || (data.id || ++counter)) + '-content',
+                className: 'editor-content',
+                html: XNAT.utils.escapeXML(data.content || '') || '',
+                style: {
+                    position: 'absolute',
+                    top: 0, right: 0, bottom: 0, left: 0,
+                    border: '1px solid #ccc'
+                },
+                done: function(){
+                    var aceEditor = ace.edit(this);
+                    aceEditor.setTheme("ace/theme/eclipse");
+                    aceEditor.getSession().setMode("ace/mode/" + stringLower(lang));
+                }
+            });
+
+            // put the new editor div in the wrapper
+            $wrapper.empty().append(editor);
+
+            // save the id to outer scope for other functions
+            scriptEditor.editor_id = editor.id;
+
+            return editor;
+        
+        }
+
+        // init the editor on load
+        initEditor(json);
+        
+        // init the editor again when changing versions
+        $versionMenu.on('change', function(){
+            var selectedVersion = this.value;
+            xhr.getJSON({
+                url: scriptURL(scriptId + '/' + selectedVersion),
+                success: function(json){
+                    initEditor(json);
+                }
+            })
+        })
+
+    }
+
+
     // open xmodal dialog for script editing
-    function renderEditor( json ){
-        //var fullJson = json || {};
-        //json = {};
-        //var largestVersion = -1;
-        //var arrayLength = fullJson.length;
-        //for (var i = 0; i < arrayLength; i++) {
-        //    if(fullJson[i].scriptVersion>largestVersion){
-        //        largestVersion = fullJson[i].scriptVersion;
-        //        json = fullJson[i];
-        //    }
-        //}
+    function renderEditor(json){
 
         var scriptId = json.scriptId || '';
         var lang = json.language || 'groovy';
-        var time = json.timestamp || '';
 
         var opts = {};
         opts.width = 880;
-        opts.height = 695;
+        opts.height = 720;
         //opts.top = 100;
         opts.scroll = false;
         opts.template = $('#script-editor-template');
@@ -201,16 +298,11 @@ var XNAT = getObject(XNAT||{});
         opts.title += (lang) ? ' - ' + lang : '';
         opts.esc = false; // prevent closing on 'esc'
         opts.enter = false; // prevents modal closing on 'enter' keypress
-        opts.footerContent = '<span style="color:#555;">';
-        if (time){
-            opts.footerContent +=
-                'last modified: ' + (new Date(time)).toString();
-        }
-        opts.footerContent += '</span>';
+        opts.footerContent = '<span class="modified-date" style="color:#555;"></span>';
         opts.buttons = {
             save: {
                 label: 'Save and Close',
-                action: function( obj ){
+                action: function(obj){
                     scriptId =
                         obj.$modal.find('.scriptId').val() ||
                         obj.$modal.find('.script-id-input').val();
@@ -223,129 +315,18 @@ var XNAT = getObject(XNAT||{});
                 label: 'Cancel'
             }
         };
-        opts.beforeShow = function( obj ){
-
-            var $dialog = obj.$modal;
-
-            $dialog.find('.id').val(json.id || '');
-            $dialog.find('.scriptId').val(scriptId);
-            $dialog.find('.scriptLabel').val(json.scriptLabel || '');
-            $dialog.find('.language').val(lang);
-            $dialog.find('.timestamp').val(time);
-            $dialog.find('.script-description').val(json.description || '');
-
-            var currScriptVersion = 1;
-            if(scriptId) {
-                //xhr.getJSON(XNAT.url.restUrl('/data/automation/scriptVersions/' + scriptId), function (jsonVersions) {
-                //    for(var key in jsonVersions){
-                //        if(key!="contains") {
-                //            var currScript = jsonVersions[key];
-                //            currScriptVersion = currScript.scriptVersion;
-                //            $('#script-version')
-                //                .append($("<option></option>")
-                //                    .attr("value", currScriptVersion)
-                //                    .text(currScriptVersion));
-                //        }
-                //    }
-                //    $('#script-version').val(currScriptVersion);
-                //});
-                xhr.getJSON(XNAT.url.restUrl('/data/automation/scriptVersions/' + scriptId), function (versionsList) {
-                    var versCounter = 1;
-                    for(var vers in versionsList){
-                        if(vers!="contains") {
-                            currScriptVersion = versionsList[vers];
-                            $('#script-version')
-                                .append($("<option></option>")
-                                    .attr("value", currScriptVersion)
-                                    .text(versCounter));
-                            versCounter = versCounter+1;
-                        }
-                    }
-                    $('#script-version').val(currScriptVersion);
-                });
-            }
-            else{
-                $('#script-version')
-                    .append($("<option></option>")
-                        .attr("value", "1")
-                        .attr("selected","selected")
-                        .text("1"));
-            }
-            //$("div.script-version select").val(currScriptVersion);
-            $('#script-version').change(function(){
-                var selectedVersion = $('#script-version')[0].value;
-                xhr.getJSON(XNAT.url.restUrl('/data/automation/scripts/' + scriptId + '/'+ selectedVersion), function (versionObj) {
-                    json = versionObj;
-                    var $dialog = obj.$modal;
-
-                    //$dialog.find('.id').val(json.id || '');
-                    $dialog.find('.scriptId').val(scriptId || '');
-                    $dialog.find('.scriptLabel').val(json.scriptLabel || '');
-                    $dialog.find('.language').val(json.language || '');
-                    $dialog.find('.timestamp').val(json.timestamp || '');
-                    $dialog.find('.script-description').val(json.description || '');
-                    $dialog.find('.script-version').val(selectedVersion || '');
-
-                    var $wrapper = $dialog.find('.editor-wrapper');
-
-                    // make sure the editor wrapper is empty
-                    $wrapper.empty();
-
-                    // create an entirely new editor div
-                    var _editor = document.createElement('div');
-                    _editor.id = 'script-' + (scriptId || (json.id||++counter)) + '-content';
-                    _editor.className = 'editor-content';
-                    _editor.innerHTML = XNAT.utils.escapeXML(json.content) || '';
-                    _editor.style = 'position:absolute;top:0;right:0;bottom:0;left:0;border: 1px solid #ccc';
-
-                    // put the new editor div in the wrapper
-                    $wrapper.append(_editor);
-
-                    // save the id to outer scope for other functions
-                    scriptEditor.editor_id = _editor.id;
-
-                    var aceEditor = ace.edit(_editor);
-                    aceEditor.setTheme("ace/theme/eclipse");
-                    aceEditor.getSession().setMode("ace/mode/" + stringLower(lang));
-                });
-
-            });
-
-            if (scriptId){
-                $dialog.find('.script-id-text').html(scriptId);
-                $dialog.find('.script-id-input').remove();
-                //$dialog.find('.script-id-input').val(scriptId);
-            }
-
-            var $wrapper = $dialog.find('.editor-wrapper');
-
-            // make sure the editor wrapper is empty
-            $wrapper.empty();
-
-            // create an entirely new editor div
-            var _editor = document.createElement('div');
-            _editor.id = 'script-' + (scriptId || (json.id||++counter)) + '-content';
-            _editor.className = 'editor-content';
-            _editor.innerHTML = XNAT.utils.escapeXML(json.content) || '';
-            _editor.style = 'position:absolute;top:0;right:0;bottom:0;left:0;border: 1px solid #ccc';
-
-            // put the new editor div in the wrapper
-            $wrapper.append(_editor);
-
-            // save the id to outer scope for other functions
-            scriptEditor.editor_id = _editor.id;
-
-            var aceEditor = ace.edit(_editor);
-            aceEditor.setTheme("ace/theme/eclipse");
-            aceEditor.getSession().setMode("ace/mode/" + stringLower(lang));
-
+        opts.beforeShow = function(obj){
+            loadEditor(obj.$modal, json);
         };
         opts.afterShow = function(obj){
-            if (!scriptId){
+            if (!scriptId) {
                 obj.$modal.find('.script-id-input').focus().select();
             }
+            // TODO: prompt user to save if they could lose unsaved changes
         };
+
         xmodal.open(opts);
+
     }
 
     // open dialog to choose language
@@ -357,7 +338,6 @@ var XNAT = getObject(XNAT||{});
                 title: 'Select a Language',
                 content: '<p>Please select a language:</p>' +
                 '<select class="language">' + langMenuOptions(langs) + '</select>',
-                //top: 120,
                 beforeShow: function(obj){
                     //console.log(obj.content);
                 },
@@ -365,9 +345,9 @@ var XNAT = getObject(XNAT||{});
                 okClose: false,
                 okAction: function(obj){
                     var $langSelect = obj.$modal.find('select.language'),
-                        lang = $langSelect.val();
-                    if (lang === '!'){
-                        xmodal.message('Please select a language before proceeding.',{
+                        lang        = $langSelect.val();
+                    if (lang === '!') {
+                        xmodal.message('Please select a language before proceeding.', {
                             //top: 140,
                             width: 350,
                             height: 150
@@ -388,7 +368,7 @@ var XNAT = getObject(XNAT||{});
 
         // can we assume the language choices will be
         // up-to-date when the dialog opens?
-        if (scriptEditor.languages.length){
+        if (scriptEditor.languages.length) {
             langDialog(scriptEditor.languages);
         }
         // otherwise get the runners (again?)
@@ -400,14 +380,14 @@ var XNAT = getObject(XNAT||{});
 
     }
 
-    scriptEditor.editScript = function( scriptId ){
-        xhr.getJSON(scriptURL(scriptId), function( json ){
+    scriptEditor.editScript = function(scriptId){
+        xhr.getJSON(scriptURL(scriptId), function(json){
             renderEditor(json);
         });
     };
 
-    scriptEditor.duplicateScript = function( scriptId ){
-        xhr.getJSON(scriptURL(scriptId), function( json ){
+    scriptEditor.duplicateScript = function(scriptId){
+        xhr.getJSON(scriptURL(scriptId), function(json){
             var data = {
                 //scriptId: json.scriptId + '_copy',
                 scriptLabel: json.scriptLabel,
@@ -423,8 +403,8 @@ var XNAT = getObject(XNAT||{});
     scriptEditor.addScript = function(){
         //window.location = url;
         var $langMenu = $('#add-script-language'),
-            _lang = $langMenu.val();
-        if (_lang === '!'){
+            _lang     = $langMenu.val();
+        if (_lang === '!') {
             selectLanguage();
         }
         else {
@@ -434,7 +414,7 @@ var XNAT = getObject(XNAT||{});
         }
     };
 
-    function doDelete( scriptId ){
+    function doDelete(scriptId){
 
         var successDialog = {
             title: 'Success',
@@ -445,21 +425,18 @@ var XNAT = getObject(XNAT||{});
                 xmodal.closeAll();
             }
         };
-        var csrfParam = {
-            XNAT_CSRF: csrfToken
-        };
 
         xhr.delete(scriptURL(scriptId, csrfParam), {
             success: function(){
                 xmodal.message(successDialog);
             },
-            error: function( request, status, error ){
+            error: function(request, status, error){
                 xmodal.message('Error', 'An error occurred: [' + status + '] ' + error);
             }
         });
     }
 
-    scriptEditor.deleteScript = function( scriptId ){
+    scriptEditor.deleteScript = function(scriptId){
         xmodal.confirm({
             width: 450,
             title: 'Delete Script?',
@@ -498,3 +475,4 @@ var XNAT = getObject(XNAT||{});
     });
 
 })(XNAT);
+
diff --git a/src/main/webapp/xnat-templates/screens/Scripts.vm b/src/main/webapp/xnat-templates/screens/Scripts.vm
index decd810e..318fb4d5 100644
--- a/src/main/webapp/xnat-templates/screens/Scripts.vm
+++ b/src/main/webapp/xnat-templates/screens/Scripts.vm
@@ -208,6 +208,7 @@
                 <td class="edit" data-action="editScript">
                     <a href="#!" class="edit"><b>__SCRIPT_ID__</b></a>
                 </td>
+                <td class="edit" data-action="editScript">__SCRIPT_LABEL__</td>
                 <td class="edit" data-action="editScript">__SCRIPT_DESCRIPTION__</td>
                 <td class="actions" style="text-align:center;white-space:nowrap;">
                     <a href="#!" data-action="editScript" title="edit existing script">edit</a>
@@ -298,14 +299,17 @@
 
                             <p id="no-scripts-installed" style="display:none;">No scripts are currently installed on this system.</p>
 
-                            <table id="scripts-table" class="xnat-table" style="display:none;width:100%;">
+                            <table id="scripts-table" class="sortable xnat-table" style="display:none;width:100%;">
                                 <thead>
-                                <th>Script ID</th>
+                                <tr>
+                                <th class="sort index">Script ID</th>
+                                <th class="sort">Script Label</th>
                                 <th width="50%">Description</th>
                                 ##<th>Version</th>
                                 <th>&nbsp;</th>
                                 ##                <th>&nbsp;</th>
                                 ##                <th>&nbsp;</th>
+                                </tr>
                                 </thead>
                                 <tbody>
 
@@ -412,7 +416,6 @@
         </div>
 
 
-
         <!-- SCRIPT EDITOR TEMPLATE -->
         <div id="script-editor-template" class="html-template">
             <input type="hidden" name="id" class="id" value="">
@@ -440,21 +443,24 @@
                 </tr>
                 <tr>
                     <td><b>Script Version: </b>&nbsp;</td>
-                    <td><select id="script-version" class="script-version">
-                        <option value="!">Select a Version</option>
-                    </select></td>
+                    <td>
+                        <select id="script-version" class="script-version">
+##                          <option value="!">Select a Version</option>
+                        </select>
+                    </td>
                 </tr>
             </table>
             <br>
-            <div class="editor-wrapper" style="width:840px;height:482px;position:relative;">
+            <div class="editor-wrapper" style="width:840px;height:440px;position:relative;">
                 <!-- the '.editor-content' div gets replaced when the script content is loaded -->
                 <div class="editor-content" style="position:absolute;top:0;right:0;bottom:0;left:0;border:1px solid #ccc;"></div>
             </div>
         </div><!-- /#script-editor-template -->
 
+
         <script type="text/javascript" src="$content.getURI("scripts/lib/ace/ace.js")"></script>
         <script type="text/javascript" src="$content.getURI("scripts/xnat/app/automation.js")"></script>
-        <script type="text/javascript" src="$content.getURI("scripts/xnat/app/scriptEditor.js")"></script>
+    <script type="text/javascript" src="$content.getURI("scripts/xnat/app/scriptEditor.js")"></script>
 
     #else
         <div id="congratsContainer" class="container" style="width:98%;margin:5px;background-color:#797979;">
@@ -466,3 +472,4 @@
     #end
 
     <!-- END plugin-resources/webapp/xnat-templates/screens/Scripts.vm -->
+
-- 
GitLab