You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2015/01/27 21:20:18 UTC

incubator-nifi git commit: NIFI-250: - Using the property table plugin in the read only processor details dialog. - Clean up.

Repository: incubator-nifi
Updated Branches:
  refs/heads/NIFI-250 2d7c700d1 -> 526e18d00


NIFI-250:
- Using the property table plugin in the read only processor details dialog.
- Clean up.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/526e18d0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/526e18d0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/526e18d0

Branch: refs/heads/NIFI-250
Commit: 526e18d00a1bdf8a63ebc70fd257ea23bf678425
Parents: 2d7c700
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Jan 27 15:20:01 2015 -0500
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Jan 27 15:20:01 2015 -0500

----------------------------------------------------------------------
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |   3 -
 .../main/resources/filters/canvas.properties    |   3 -
 .../src/main/webapp/WEB-INF/pages/summary.jsp   |   2 +
 .../WEB-INF/partials/processor-details.jsp      |   4 -
 .../main/webapp/css/processor-configuration.css |  69 --
 .../src/main/webapp/css/processor-details.css   |  26 -
 .../propertytable/jquery.propertytable.css      |  69 ++
 .../propertytable/jquery.propertytable.js       | 700 ++++++++++++++++++-
 .../nf-processor-property-combo-editor.js       | 174 -----
 .../canvas/nf-processor-property-nfel-editor.js | 209 ------
 .../canvas/nf-processor-property-text-editor.js | 214 ------
 .../main/webapp/js/nf/nf-processor-details.js   | 335 +--------
 12 files changed, 765 insertions(+), 1043 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index cd4152f..4a54a48 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -267,9 +267,6 @@
                                                 <include>${staging.dir}/js/nf/canvas/nf-canvas-toolbox.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-custom-processor-ui.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-registration.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-processor-property-text-editor.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-processor-property-nfel-editor.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-processor-property-combo-editor.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-controller-service-configuration.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-processor-configuration.js</include>
                                                 <include>${staging.dir}/js/nf/nf-processor-details.js</include>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index eba3a0e..44841ef 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -24,9 +24,6 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?
 <script type="text/javascript" src="js/nf/canvas/nf-canvas-toolbox.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-custom-processor-ui.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-registration.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-processor-property-text-editor.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-processor-property-nfel-editor.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-processor-property-combo-editor.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-controller-service-configuration.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-processor-configuration.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-processor-details.js?${project.version}"></script>\n\

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
index fdfc806..032509b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
@@ -25,6 +25,7 @@
         ${nf.summary.style.tags}
         <link rel="stylesheet" href="js/jquery/tabbs/jquery.tabbs.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/combo/jquery.combo.css?${project.version}" type="text/css" />
+        <link rel="stylesheet" href="js/jquery/propertytable/jquery.propertytable.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/modal/jquery.modal.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/codemirror/lib/codemirror.css" type="text/css" />
         <link rel="stylesheet" href="js/codemirror/addon/hint/show-hint.css" type="text/css" />
@@ -40,6 +41,7 @@
         <script type="text/javascript" src="js/jquery/jquery.center.js"></script>
         <script type="text/javascript" src="js/jquery/tabbs/jquery.tabbs.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></script>
+        <script type="text/javascript" src="js/jquery/propertytable/jquery.propertytable.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/jquery.ellipsis.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
index abbd4c4..31df7c9 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
@@ -138,10 +138,6 @@
                 </div>
             </div>
             <div id="details-processor-properties-tab-content" class="details-tab">
-                <div id="read-only-processor-properties-header">
-                    <div id="read-only-required-property-note">Required field</div>
-                    <div class="clear"></div>
-                </div>
                 <div id="read-only-processor-properties"></div>
             </div>
             <div id="details-processor-comments-tab-content" class="details-tab">

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
index 6d04834..ed53ac5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
@@ -226,75 +226,6 @@ div.relationship-description {
 }
 
 /*
-    Styles for the processor property editor.
-*/
-
-div.slickgrid-nfel-editor .nfel-editor {
-    margin-bottom: 30px;
-}
-
-div.edit-property-value {
-    height: 20px;
-    padding-right: 60px;
-}
-
-textarea.value-field {
-    float: left;
-    width: 95%;
-    height: 26px;
-    font-size: 11px !important;
-    resize: vertical;
-    overflow-y: auto;
-}
-
-div.string-check-container {
-    float: left;
-    margin-top: 1px;
-    height: 20px;
-    line-height: 20px;
-}
-
-div.string-check {
-    width: 12px;
-    height: 12px;
-    float: left;
-    margin-top: 4px;
-    margin-left: 4px;
-}
-
-span.string-check-label {
-    font-size: 9px;
-    font-weight: normal;
-}
-
-div.value pre {
-    font-family: Verdana, Arial, Helvetica, sans-serif;
-    overflow: hidden;
-}
-
-/*
-    Styles for the property value combo
-*/
-
-div.edit-property-value-combo {
-    height: 22px;
-}
-
-div.value-combo {
-    height: 18px;
-    margin-top: 0px;
-    float: left;
-}
-
-/*
-    Table styles
-*/
-
-td.right-border {
-    border-right: 1px solid #aaa;
-}
-
-/*
     Comments
 */
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
index ef770cb..47849eb 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
@@ -90,30 +90,4 @@ div.concurrently-schedulable-tasks-setting, div.scheduling-period-setting, div.p
 
 div.scheduling-period-setting, div.yield-duration-setting {
     margin-left: 36px;
-}
-
-/* properties */
-
-#read-only-processor-properties-header {
-    margin-bottom: 10px;
-    height: 19px;
-}
-
-#read-only-required-property-note {
-    font-weight: bold;
-    float: left;
-    height: 19px;
-    line-height: 19px;
-    margin-left: 6px;
-}
-
-#read-only-processor-properties {
-    border-bottom: 1px solid #666;
-    width: 758px;
-    height: 290px;
-    background-color: #fff;
-}
-
-div.processor-property-detail .nfel-editor {
-    margin-bottom: 30px;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
index 632ad06..64f5638 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
@@ -105,4 +105,73 @@ div.new-property-button-container {
     bottom: 0;
     right: 0;
     padding: 0 8px 10px;
+}
+
+/*
+    Styles for the property editor.
+*/
+
+div.slickgrid-nfel-editor .nfel-editor, div.property-detail .nfel-editor {
+    margin-bottom: 30px;
+}
+
+div.edit-property-value {
+    height: 20px;
+    padding-right: 60px;
+}
+
+textarea.value-field {
+    float: left;
+    width: 95%;
+    height: 26px;
+    font-size: 11px !important;
+    resize: vertical;
+    overflow-y: auto;
+}
+
+div.string-check-container {
+    float: left;
+    margin-top: 1px;
+    height: 20px;
+    line-height: 20px;
+}
+
+div.string-check {
+    width: 12px;
+    height: 12px;
+    float: left;
+    margin-top: 4px;
+    margin-left: 4px;
+}
+
+span.string-check-label {
+    font-size: 9px;
+    font-weight: normal;
+}
+
+div.value pre {
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+    overflow: hidden;
+}
+
+/*
+    Styles for the property value combo
+*/
+
+div.edit-property-value-combo {
+    height: 22px;
+}
+
+div.value-combo {
+    height: 18px;
+    margin-top: 0px;
+    float: left;
+}
+
+/*
+    Table styles
+*/
+
+td.right-border {
+    border-right: 1px solid #aaa;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index 479499d..fe50e9a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -35,20 +35,671 @@
     var languageId = 'nfel';
     var editorClass = languageId + '-editor';
 
-    var isUndefined = function (obj) {
-        return typeof obj === 'undefined';
+    // text editor
+    var textEditor = function (args) {
+        var scope = this;
+        var initialValue = '';
+        var previousValue;
+        var propertyDescriptor;
+        var wrapper;
+        var isEmpty;
+        var input;
+
+
+        this.init = function () {
+            var container = $('body');
+
+            // get the property descriptor
+            var gridContainer = $(args.grid.getContainerNode());
+            var descriptors = gridContainer.data('descriptors');
+            propertyDescriptor = descriptors[args.item.property];
+
+            // record the previous value
+            previousValue = args.item[args.column.field];
+
+            // create the wrapper
+            wrapper = $('<div></div>').css({
+                'z-index': 100000,
+                'position': 'absolute',
+                'background': 'white',
+                'padding': '5px',
+                'overflow': 'hidden',
+                'border': '3px solid #365C6A',
+                'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                'cursor': 'move'
+            }).draggable({
+                cancel: '.button, textarea, .nf-checkbox',
+                containment: 'parent'
+            }).appendTo(container);
+
+            // create the input field
+            input = $('<textarea hidefocus rows="5"/>').css({
+                'background': 'white',
+                'width': args.position.width + 'px',
+                'min-width': '150px',
+                'height': '80px',
+                'border-width': '0',
+                'outline': '0',
+                'overflow-y': 'auto',
+                'resize': 'both',
+                'margin-bottom': '28px'
+            }).tab().on('keydown', scope.handleKeyDown).appendTo(wrapper);
+
+            // create the button panel
+            var stringCheckPanel = $('<div class="string-check-container">');
+
+            // build the custom checkbox
+            isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
+            $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
+
+            var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
+            var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
+            $('<div></div>').css({
+                'position': 'absolute',
+                'bottom': '0',
+                'left': '0',
+                'right': '0',
+                'padding': '0 3px 5px'
+            }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
+
+            // position and focus
+            scope.position(args.position);
+            input.focus().select();
+        };
+
+        this.handleKeyDown = function (e) {
+            if (e.which === $.ui.keyCode.ENTER && !e.shiftKey) {
+                scope.save();
+            } else if (e.which === $.ui.keyCode.ESCAPE) {
+                e.preventDefault();
+                scope.cancel();
+            }
+        };
+
+        this.save = function () {
+            args.commitChanges();
+        };
+
+        this.cancel = function () {
+            input.val(initialValue);
+            args.cancelChanges();
+        };
+
+        this.hide = function () {
+            wrapper.hide();
+        };
+
+        this.show = function () {
+            wrapper.show();
+        };
+
+        this.position = function (position) {
+            wrapper.css({
+                'top': position.top - 5,
+                'left': position.left - 5
+            });
+        };
+
+        this.destroy = function () {
+            wrapper.remove();
+        };
+
+        this.focus = function () {
+            input.focus();
+        };
+
+        this.loadValue = function (item) {
+            // determine if this is a sensitive property
+            var isEmptyChecked = false;
+            var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
+
+            // determine the value to use when populating the text field
+            if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
+                if (sensitive) {
+                    initialValue = nf.Common.config.sensitiveText;
+                } else {
+                    initialValue = item[args.column.field];
+                    isEmptyChecked = initialValue === '';
+                }
+            }
+
+            // determine if its an empty string
+            var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
+            isEmpty.addClass(checkboxStyle);
+
+            // style sensitive properties differently
+            if (sensitive) {
+                input.addClass('sensitive').keydown(function () {
+                    var sensitiveInput = $(this);
+                    if (sensitiveInput.hasClass('sensitive')) {
+                        sensitiveInput.removeClass('sensitive');
+                        if (sensitiveInput.val() === nf.Common.config.sensitiveText) {
+                            sensitiveInput.val('');
+                        }
+                    }
+                });
+            }
+
+            input.val(initialValue);
+            input.select();
+        };
+
+        this.serializeValue = function () {
+            // if the field has been cleared, set the value accordingly
+            if (input.val() === '') {
+                // if the user has checked the empty string checkbox, use emtpy string
+                if (isEmpty.hasClass('checkbox-checked')) {
+                    return '';
+                } else {
+                    // otherwise if the property is required
+                    if (nf.Common.isRequiredProperty(propertyDescriptor)) {
+                        if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
+                            return previousValue;
+                        } else {
+                            return propertyDescriptor.defaultValue;
+                        }
+                    } else {
+                        // if the property is not required, clear the value
+                        return null;
+                    }
+                }
+            } else {
+                // if the field still has the sensitive class it means a property
+                // was edited but never modified so we should restore the previous
+                // value instead of setting it to the 'sensitive value set' string
+                if (input.hasClass('sensitive')) {
+                    return previousValue;
+                } else {
+                    // if there is text specified, use that value
+                    return input.val();
+                }
+            }
+        };
+
+        this.applyValue = function (item, state) {
+            item[args.column.field] = state;
+        };
+
+        this.isValueChanged = function () {
+            return scope.serializeValue() !== previousValue;
+        };
+
+        this.validate = function () {
+            return {
+                valid: true,
+                msg: null
+            };
+        };
+
+        // initialize the custom long text editor
+        this.init();
+    };
+    
+    // nfel editor
+    var nfelEditor = function (args) {
+        var scope = this;
+        var initialValue = '';
+        var previousValue;
+        var propertyDescriptor;
+        var isEmpty;
+        var wrapper;
+        var editor;
+
+        this.init = function () {
+            var container = $('body');
+
+            // get the property descriptor
+            var gridContainer = $(args.grid.getContainerNode());
+            var descriptors = gridContainer.data('descriptors');
+            propertyDescriptor = descriptors[args.item.property];
+
+            // determine if this is a sensitive property
+            var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
+
+            // record the previous value
+            previousValue = args.item[args.column.field];
+
+            var languageId = 'nfel';
+            var editorClass = languageId + '-editor';
+
+            // create the wrapper
+            wrapper = $('<div></div>').addClass('slickgrid-nfel-editor').css({
+                'z-index': 14000,
+                'position': 'absolute',
+                'background': 'white',
+                'padding': '5px',
+                'overflow': 'hidden',
+                'border': '3px solid #365C6A',
+                'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                'cursor': 'move'
+            }).draggable({
+                cancel: 'input, textarea, pre, .nf-checkbox, .button, .' + editorClass,
+                containment: 'parent'
+            }).appendTo(container);
+
+            // create the editor
+            editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
+                languageId: languageId,
+                width: args.position.width,
+                minWidth: 175,
+                minHeight: 100,
+                resizable: true,
+                sensitive: sensitive,
+                escape: function () {
+                    scope.cancel();
+                },
+                enter: function () {
+                    scope.save();
+                }
+            });
+
+            // create the button panel
+            var stringCheckPanel = $('<div class="string-check-container">');
+
+            // build the custom checkbox
+            isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
+            $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
+
+            var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
+            var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
+            $('<div></div>').css({
+                'position': 'absolute',
+                'bottom': '0',
+                'left': '0',
+                'right': '0',
+                'padding': '0 3px 5px 1px'
+            }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
+
+            // position and focus
+            scope.position(args.position);
+            editor.nfeditor('focus').nfeditor('selectAll');
+        };
+
+        this.save = function () {
+            args.commitChanges();
+        };
+
+        this.cancel = function () {
+            editor.nfeditor('setValue', initialValue);
+            args.cancelChanges();
+        };
+
+        this.hide = function () {
+            wrapper.hide();
+        };
+
+        this.show = function () {
+            wrapper.show();
+            editor.nfeditor('setSize', args.position.width, null).nfeditor('refresh');
+        };
+
+        this.position = function (position) {
+            wrapper.css({
+                'top': position.top - 5,
+                'left': position.left - 5
+            });
+        };
+
+        this.destroy = function () {
+            editor.nfeditor('destroy');
+            wrapper.remove();
+        };
+
+        this.focus = function () {
+            editor.nfeditor('focus');
+        };
+
+        this.loadValue = function (item) {
+            // determine if this is a sensitive property
+            var isEmptyChecked = false;
+            var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
+
+            // determine the value to use when populating the text field
+            if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
+                if (sensitive) {
+                    initialValue = nf.Common.config.sensitiveText;
+                } else {
+                    initialValue = item[args.column.field];
+                    isEmptyChecked = initialValue === '';
+                }
+            }
+
+            // determine if its an empty string
+            var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
+            isEmpty.addClass(checkboxStyle);
+
+            editor.nfeditor('setValue', initialValue).nfeditor('selectAll');
+        };
+
+        this.serializeValue = function () {
+            var value = editor.nfeditor('getValue');
+
+            // if the field has been cleared, set the value accordingly
+            if (value === '') {
+                // if the user has checked the empty string checkbox, use emtpy string
+                if (isEmpty.hasClass('checkbox-checked')) {
+                    return '';
+                } else {
+                    // otherwise if the property is required
+                    if (nf.Common.isRequiredProperty(propertyDescriptor)) {
+                        if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
+                            return previousValue;
+                        } else {
+                            return propertyDescriptor.defaultValue;
+                        }
+                    } else {
+                        // if the property is not required, clear the value
+                        return null;
+                    }
+                }
+            } else {
+                // if the field still has the sensitive class it means a property
+                // was edited but never modified so we should restore the previous
+                // value instead of setting it to the 'sensitive value set' string
+
+                // if the field hasn't been modified return the previous value... this
+                // is important because sensitive properties contain the text 'sensitive
+                // value set' which is cleared when the value is edited. we do not 
+                // want to actually use this value
+                if (editor.nfeditor('isModified') === false) {
+                    return previousValue;
+                } else {
+                    // if there is text specified, use that value
+                    return value;
+                }
+            }
+        };
+
+        this.applyValue = function (item, state) {
+            item[args.column.field] = state;
+        };
+
+        this.isValueChanged = function () {
+            return scope.serializeValue() !== previousValue;
+        };
+
+        this.validate = function () {
+            return {
+                valid: true,
+                msg: null
+            };
+        };
+
+        // initialize the custom long nfel editor
+        this.init();
     };
+    
+    // combo editor
+    var comboEditor = function (args) {
+        var scope = this;
+        var initialValue = null;
+        var wrapper;
+        var combo;
+        var propertyDescriptor;
+
+        this.init = function () {
+            var container = $('body');
+
+            // get the property descriptor
+            var gridContainer = $(args.grid.getContainerNode());
+            var descriptors = gridContainer.data('descriptors');
+            propertyDescriptor = descriptors[args.item.property];
 
-    var isNull = function (obj) {
-        return obj === null;
+            // create the wrapper
+            wrapper = $('<div></div>').css({
+                'z-index': 1999,
+                'position': 'absolute',
+                'background': 'white',
+                'padding': '5px',
+                'overflow': 'hidden',
+                'border': '3px solid #365C6A',
+                'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                'cursor': 'move'
+            }).draggable({
+                cancel: '.button, .combo',
+                containment: 'parent'
+            }).appendTo(container);
+
+            // check for allowable values which will drive which editor to use
+            var allowableValues = nf.Common.getAllowableValues(propertyDescriptor);
+
+            // show the output port options
+            var options = [];
+            if (propertyDescriptor.required === false) {
+                options.push({
+                    text: 'No value',
+                    value: null,
+                    optionClass: 'unset'
+                });
+            }
+            if ($.isArray(allowableValues)) {
+                $.each(allowableValues, function (i, allowableValue) {
+                    options.push({
+                        text: allowableValue.displayName,
+                        value: allowableValue.value,
+                        description: nf.Common.escapeHtml(allowableValue.description)
+                    });
+                });
+            }
+
+            // ensure the options there is at least one option
+            if (options.length === 0) {
+                options.push({
+                    text: 'No value',
+                    value: null,
+                    optionClass: 'unset',
+                    disabled: true
+                });
+            }
+
+            // determine the max height
+            var position = args.position;
+            var windowHeight = $(window).height();
+            var maxHeight = windowHeight - position.bottom - 16;
+
+            // build the combo field
+            combo = $('<div class="value-combo combo"></div>').combo({
+                options: options,
+                maxHeight: maxHeight
+            }).width(position.width - 16).appendTo(wrapper);
+
+            // add buttons for handling user input
+            $('<div class="button button-normal">Cancel</div>').css({
+                'margin': '0 0 0 5px',
+                'float': 'left'
+            }).on('click', scope.cancel).appendTo(wrapper);
+            $('<div class="button button-normal">Ok</div>').css({
+                'margin': '0 0 0 5px',
+                'float': 'left'
+            }).on('click', scope.save).appendTo(wrapper);
+
+            // position and focus
+            scope.position(position);
+        };
+
+        this.save = function () {
+            args.commitChanges();
+        };
+
+        this.cancel = function () {
+            args.cancelChanges();
+        };
+
+        this.hide = function () {
+            wrapper.hide();
+        };
+
+        this.show = function () {
+            wrapper.show();
+        };
+
+        this.position = function (position) {
+            wrapper.css({
+                'top': position.top - 5,
+                'left': position.left - 5
+            });
+        };
+
+        this.destroy = function () {
+            wrapper.remove();
+        };
+
+        this.focus = function () {
+        };
+
+        this.loadValue = function (item) {
+            // select as appropriate
+            if (!nf.Common.isUndefined(item.value)) {
+                initialValue = item.value;
+
+                combo.combo('setSelectedOption', {
+                    value: item.value
+                });
+            } else if (nf.Common.isDefinedAndNotNull(propertyDescriptor.defaultValue)) {
+                initialValue = propertyDescriptor.defaultValue;
+
+                combo.combo('setSelectedOption', {
+                    value: propertyDescriptor.defaultValue
+                });
+            }
+        };
+
+        this.serializeValue = function () {
+            var selectedOption = combo.combo('getSelectedOption');
+            return selectedOption.value;
+        };
+
+        this.applyValue = function (item, state) {
+            item[args.column.field] = state;
+        };
+
+        this.isValueChanged = function () {
+            var selectedOption = combo.combo('getSelectedOption');
+            return (!(selectedOption.value === "" && initialValue === null)) && (selectedOption.value !== initialValue);
+        };
+
+        this.validate = function () {
+            return {
+                valid: true,
+                msg: null
+            };
+        };
+
+        // initialize the custom long text editor
+        this.init();
     };
 
-    var isDefinedAndNotNull = function (obj) {
-        return !isUndefined(obj) && !isNull(obj);
+    /**
+     * Shows the property value for the specified row and cell.
+     * 
+     * @param {type} propertyGrid
+     * @param {type} descriptors 
+     * @param {type} row
+     * @param {type} cell
+     */
+    var showPropertyValue = function (propertyGrid, descriptors, row, cell) {
+        // remove any currently open detail dialogs
+        removeAllPropertyDetailDialogs();
+
+        // get the property in question
+        var propertyData = propertyGrid.getData();
+        var property = propertyData.getItem(row);
+
+        // ensure there is a value
+        if (nf.Common.isDefinedAndNotNull(property.value)) {
+
+            // get the descriptor to insert the description tooltip
+            var propertyDescriptor = descriptors[property.property];
+
+            // ensure we're not dealing with a sensitive property
+            if (!nf.Common.isSensitiveProperty(propertyDescriptor)) {
+
+                // get details about the location of the cell
+                var cellNode = $(propertyGrid.getCellNode(row, cell));
+                var offset = cellNode.offset();
+
+                // create the wrapper
+                var wrapper = $('<div class="property-detail"></div>').css({
+                    'z-index': 100000,
+                    'position': 'absolute',
+                    'background': 'white',
+                    'padding': '5px',
+                    'overflow': 'hidden',
+                    'border': '3px solid #365C6A',
+                    'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                    'cursor': 'move',
+                    'top': offset.top - 5,
+                    'left': offset.left - 5
+                }).appendTo('body');
+
+                var editor = null;
+
+                // so the nfel editor is appropriate
+                if (nf.Common.supportsEl(propertyDescriptor)) {
+                    var languageId = 'nfel';
+                    var editorClass = languageId + '-editor';
+
+                    // prevent dragging over the nf editor
+                    wrapper.draggable({
+                        cancel: 'input, textarea, pre, .button, .' + editorClass,
+                        containment: 'parent'
+                    });
+
+                    // create the editor
+                    editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
+                        languageId: languageId,
+                        width: cellNode.width(),
+                        content: property.value,
+                        minWidth: 175,
+                        minHeight: 100,
+                        readOnly: true,
+                        resizable: true
+                    });
+                } else {
+                    // prevent dragging over standard components
+                    wrapper.draggable({
+                        containment: 'parent'
+                    });
+
+                    // create the input field
+                    $('<textarea hidefocus rows="5" readonly="readonly"/>').css({
+                        'background': 'white',
+                        'width': cellNode.width() + 'px',
+                        'height': '80px',
+                        'border-width': '0',
+                        'outline': '0',
+                        'overflow-y': 'auto',
+                        'resize': 'both',
+                        'margin-bottom': '28px'
+                    }).text(property.value).appendTo(wrapper);
+                }
+
+                // add an ok button that will remove the entire pop up
+                var ok = $('<div class="button button-normal">Ok</div>').on('click', function () {
+                    // clean up the editor
+                    if (editor !== null) {
+                        editor.nfeditor('destroy');
+                    }
+                    
+                    // clean up the rest
+                    wrapper.hide().remove();
+                });
+                $('<div></div>').css({
+                    'position': 'absolute',
+                    'bottom': '0',
+                    'left': '0',
+                    'right': '0',
+                    'padding': '0 3px 5px'
+                }).append(ok).append('<div class="clear"></div>').appendTo(wrapper);
+            }
+        }
     };
 
-    var isBlank = function (str) {
-        return isUndefined(str) || isNull(str) || str === '';
+    /**
+     * Removes all currently open property detail dialogs.
+     */
+    var removeAllPropertyDetailDialogs = function () {
+        $('body').children('div.property-detail').hide().remove();
     };
 
     var initPropertiesTable = function (table, options) {
@@ -127,7 +778,7 @@
             return $('<div/>').append(content).html();
         };
 
-        var processorConfigurationColumns = [
+        var propertyColumns = [
             {id: 'property', field: 'displayName', name: 'Property', sortable: false, resizable: true, rerenderOnResize: true, formatter: nameFormatter},
             {id: 'value', field: 'value', name: 'Value', sortable: false, resizable: true, cssClass: 'pointer', rerenderOnResize: true, formatter: valueFormatter}
         ];
@@ -144,7 +795,7 @@
 
                 return markup;
             };
-            processorConfigurationColumns.push({id: "actions", name: "&nbsp;", minWidth: 20, width: 20, formatter: actionFormatter});
+            propertyColumns.push({id: "actions", name: "&nbsp;", minWidth: 20, width: 20, formatter: actionFormatter});
         }
         
         var propertyConfigurationOptions = {
@@ -179,7 +830,7 @@
                 return {
                     columns: {
                         value: {
-                            editor: nf.ProcessorPropertyNfelEditor
+                            editor: nfelEditor
                         }
                     }
                 };
@@ -190,7 +841,7 @@
                     return {
                         columns: {
                             value: {
-                                editor: nf.ProcessorPropertyComboEditor
+                                editor: comboEditor
                             }
                         }
                     };
@@ -198,7 +849,7 @@
                     return {
                         columns: {
                             value: {
-                                editor: nf.ProcessorPropertyTextEditor
+                                editor: textEditor
                             }
                         }
                     };
@@ -207,12 +858,17 @@
         };
 
         // initialize the grid
-        var propertyGrid = new Slick.Grid(table, propertyData, processorConfigurationColumns, propertyConfigurationOptions);
+        var propertyGrid = new Slick.Grid(table, propertyData, propertyColumns, propertyConfigurationOptions);
         propertyGrid.setSelectionModel(new Slick.RowSelectionModel());
         propertyGrid.onClick.subscribe(function (e, args) {
             if (propertyGrid.getColumns()[args.cell].id === 'value') {
-                // edits the clicked cell
-                propertyGrid.gotoCell(args.row, args.cell, true);
+                if (options.readOnly === true) {
+                    var descriptors = table.data('descriptors');
+                    showPropertyValue(propertyGrid, descriptors, args.row, args.cell);
+                } else {
+                    // edits the clicked cell
+                    propertyGrid.gotoCell(args.row, args.cell, true);
+                }
 
                 // prevents standard edit logic
                 e.stopImmediatePropagation();
@@ -252,7 +908,7 @@
                 var descriptors = table.data('descriptors');
                 var propertyDescriptor = descriptors[property];
 
-                // get the processor history
+                // get the history
                 var history = table.data('history');
                 var propertyHistory = history[property];
 
@@ -363,7 +1019,7 @@
         init: function (options) {
             return this.each(function () {
                 // ensure the options have been properly specified
-                if (isDefinedAndNotNull(options)) {
+                if (nf.Common.isDefinedAndNotNull(options)) {
                     // get the tag cloud
                     var propertyTableContainer = $(this);
                     
@@ -553,7 +1209,13 @@
          */
         clear: function () {
             return this.each(function () {
+                var options = $(this).data('options');
+                if (options.readOnly === true) {
+                    removeAllPropertyDetailDialogs();
+                }
+                
                 var table = $(this).find('div.property-table');
+                table.removeData('descriptors history');
             
                 // clean up any tooltips that may have been generated
                 nf.Common.cleanUpTooltips(table, 'img.icon-info');
@@ -577,7 +1239,7 @@
                 var propertyGrid = table.data('gridInstance');
                 var propertyData = propertyGrid.getData();
 
-                // determine if any of the processor properties have changed
+                // determine if any of the properties have changed
                 $.each(propertyData.getItems(), function () {
                     if (this.value !== this.previousValue) {
                         isSaveRequired = true;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js
deleted file mode 100644
index 590550a..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-nf.ProcessorPropertyComboEditor = function (args) {
-    var scope = this;
-    var initialValue = null;
-    var wrapper;
-    var combo;
-    var propertyDescriptor;
-    
-    this.init = function () {
-        var container = $('body');
-
-        // get the property descriptor
-        var gridContainer = $(args.grid.getContainerNode());
-        var descriptors = gridContainer.data('descriptors');
-        propertyDescriptor = descriptors[args.item.property];
-
-        // create the wrapper
-        wrapper = $('<div></div>').css({
-            'z-index': 1999,
-            'position': 'absolute',
-            'background': 'white',
-            'padding': '5px',
-            'overflow': 'hidden',
-            'border': '3px solid #365C6A',
-            'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-            'cursor': 'move'
-        }).draggable({
-            cancel: '.button, .combo',
-            containment: 'parent'
-        }).appendTo(container);
-
-        // check for allowable values which will drive which editor to use
-        var allowableValues = nf.Common.getAllowableValues(propertyDescriptor);
-
-        // show the output port options
-        var options = [];
-        if (propertyDescriptor.required === false) {
-            options.push({
-                text: 'No value',
-                value: null,
-                optionClass: 'unset'
-            });
-        }
-        if ($.isArray(allowableValues)) {
-            $.each(allowableValues, function (i, allowableValue) {
-                options.push({
-                    text: allowableValue.displayName,
-                    value: allowableValue.value,
-                    description: nf.Common.escapeHtml(allowableValue.description)
-                });
-            });
-        }
-
-        // ensure the options there is at least one option
-        if (options.length === 0) {
-            options.push({
-                text: 'No value',
-                value: null,
-                optionClass: 'unset',
-                disabled: true
-            });
-        }
-
-        // determine the max height
-        var position = args.position;
-        var windowHeight = $(window).height();
-        var maxHeight = windowHeight - position.bottom - 16;
-
-        // build the combo field
-        combo = $('<div class="value-combo combo"></div>').combo({
-            options: options,
-            maxHeight: maxHeight
-        }).width(position.width - 16).appendTo(wrapper);
-
-        // add buttons for handling user input
-        $('<div class="button button-normal">Cancel</div>').css({
-            'margin': '0 0 0 5px',
-            'float': 'left'
-        }).on('click', scope.cancel).appendTo(wrapper);
-        $('<div class="button button-normal">Ok</div>').css({
-            'margin': '0 0 0 5px',
-            'float': 'left'
-        }).on('click', scope.save).appendTo(wrapper);
-
-        // position and focus
-        scope.position(position);
-    };
-
-    this.save = function () {
-        args.commitChanges();
-    };
-
-    this.cancel = function () {
-        args.cancelChanges();
-    };
-
-    this.hide = function () {
-        wrapper.hide();
-    };
-
-    this.show = function () {
-        wrapper.show();
-    };
-
-    this.position = function (position) {
-        wrapper.css({
-            'top': position.top - 5,
-            'left': position.left - 5
-        });
-    };
-
-    this.destroy = function () {
-        wrapper.remove();
-    };
-
-    this.focus = function () {
-    };
-
-    this.loadValue = function (item) {
-        // select as appropriate
-        if (!nf.Common.isUndefined(item.value)) {
-            initialValue = item.value;
-
-            combo.combo('setSelectedOption', {
-                value: item.value
-            });
-        } else if (nf.Common.isDefinedAndNotNull(propertyDescriptor.defaultValue)) {
-            initialValue = propertyDescriptor.defaultValue;
-
-            combo.combo('setSelectedOption', {
-                value: propertyDescriptor.defaultValue
-            });
-        }
-    };
-
-    this.serializeValue = function () {
-        var selectedOption = combo.combo('getSelectedOption');
-        return selectedOption.value;
-    };
-
-    this.applyValue = function (item, state) {
-        item[args.column.field] = state;
-    };
-
-    this.isValueChanged = function () {
-        var selectedOption = combo.combo('getSelectedOption');
-        return (!(selectedOption.value === "" && initialValue === null)) && (selectedOption.value !== initialValue);
-    };
-
-    this.validate = function () {
-        return {
-            valid: true,
-            msg: null
-        };
-    };
-
-    // initialize the custom long text editor
-    this.init();
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js
deleted file mode 100644
index 91498c3..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-nf.ProcessorPropertyNfelEditor = function (args) {
-    var scope = this;
-    var initialValue = '';
-    var previousValue;
-    var propertyDescriptor;
-    var isEmpty;
-    var wrapper;
-    var editor;
-
-
-    this.init = function () {
-        var container = $('body');
-        
-        // get the property descriptor
-        var gridContainer = $(args.grid.getContainerNode());
-        var descriptors = gridContainer.data('descriptors');
-        propertyDescriptor = descriptors[args.item.property];
-
-        // determine if this is a sensitive property
-        var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
-
-        // record the previous value
-        previousValue = args.item[args.column.field];
-
-        var languageId = 'nfel';
-        var editorClass = languageId + '-editor';
-
-        // create the wrapper
-        wrapper = $('<div></div>').addClass('slickgrid-nfel-editor').css({
-            'z-index': 14000,
-            'position': 'absolute',
-            'background': 'white',
-            'padding': '5px',
-            'overflow': 'hidden',
-            'border': '3px solid #365C6A',
-            'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-            'cursor': 'move'
-        }).draggable({
-            cancel: 'input, textarea, pre, .nf-checkbox, .button, .' + editorClass,
-            containment: 'parent'
-        }).appendTo(container);
-
-        // create the editor
-        editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
-            languageId: languageId,
-            width: args.position.width,
-            minWidth: 175,
-            minHeight: 100,
-            resizable: true,
-            sensitive: sensitive,
-            escape: function () {
-                scope.cancel();
-            },
-            enter: function () {
-                scope.save();
-            }
-        });
-
-        // create the button panel
-        var stringCheckPanel = $('<div class="string-check-container">');
-
-        // build the custom checkbox
-        isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
-        $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
-
-        var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
-        var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
-        $('<div></div>').css({
-            'position': 'absolute',
-            'bottom': '0',
-            'left': '0',
-            'right': '0',
-            'padding': '0 3px 5px 1px'
-        }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
-
-        // position and focus
-        scope.position(args.position);
-        editor.nfeditor('focus').nfeditor('selectAll');
-    };
-
-    this.save = function () {
-        args.commitChanges();
-    };
-
-    this.cancel = function () {
-        editor.nfeditor('setValue', initialValue);
-        args.cancelChanges();
-    };
-
-    this.hide = function () {
-        wrapper.hide();
-    };
-
-    this.show = function () {
-        wrapper.show();
-        editor.nfeditor('setSize', args.position.width, null).nfeditor('refresh');
-    };
-
-    this.position = function (position) {
-        wrapper.css({
-            'top': position.top - 5,
-            'left': position.left - 5
-        });
-    };
-
-    this.destroy = function () {
-        editor.nfeditor('destroy');
-        wrapper.remove();
-    };
-
-    this.focus = function () {
-        editor.nfeditor('focus');
-    };
-
-    this.loadValue = function (item) {
-        // determine if this is a sensitive property
-        var isEmptyChecked = false;
-        var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
-
-        // determine the value to use when populating the text field
-        if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
-            if (sensitive) {
-                initialValue = nf.Common.config.sensitiveText;
-            } else {
-                initialValue = item[args.column.field];
-                isEmptyChecked = initialValue === '';
-            }
-        }
-
-        // determine if its an empty string
-        var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
-        isEmpty.addClass(checkboxStyle);
-
-        editor.nfeditor('setValue', initialValue).nfeditor('selectAll');
-    };
-
-    this.serializeValue = function () {
-        var value = editor.nfeditor('getValue');
-
-        // if the field has been cleared, set the value accordingly
-        if (value === '') {
-            // if the user has checked the empty string checkbox, use emtpy string
-            if (isEmpty.hasClass('checkbox-checked')) {
-                return '';
-            } else {
-                // otherwise if the property is required
-                if (nf.Common.isRequiredProperty(propertyDescriptor)) {
-                    if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
-                        return previousValue;
-                    } else {
-                        return propertyDescriptor.defaultValue;
-                    }
-                } else {
-                    // if the property is not required, clear the value
-                    return null;
-                }
-            }
-        } else {
-            // if the field still has the sensitive class it means a property
-            // was edited but never modified so we should restore the previous
-            // value instead of setting it to the 'sensitive value set' string
-
-            // if the field hasn't been modified return the previous value... this
-            // is important because sensitive properties contain the text 'sensitive
-            // value set' which is cleared when the value is edited. we do not 
-            // want to actually use this value
-            if (editor.nfeditor('isModified') === false) {
-                return previousValue;
-            } else {
-                // if there is text specified, use that value
-                return value;
-            }
-        }
-    };
-
-    this.applyValue = function (item, state) {
-        item[args.column.field] = state;
-    };
-
-    this.isValueChanged = function () {
-        return scope.serializeValue() !== previousValue;
-    };
-
-    this.validate = function () {
-        return {
-            valid: true,
-            msg: null
-        };
-    };
-
-    // initialize the custom long nfel editor
-    this.init();
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js
deleted file mode 100644
index 6f84a7a..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-nf.ProcessorPropertyTextEditor = function (args) {
-    var scope = this;
-    var initialValue = '';
-    var previousValue;
-    var propertyDescriptor;
-    var wrapper;
-    var isEmpty;
-    var input;
-    
-
-    this.init = function () {
-        var container = $('body');
-
-        // get the property descriptor
-        var gridContainer = $(args.grid.getContainerNode());
-        var descriptors = gridContainer.data('descriptors');
-        propertyDescriptor = descriptors[args.item.property];
-
-        // record the previous value
-        previousValue = args.item[args.column.field];
-
-        // create the wrapper
-        wrapper = $('<div></div>').css({
-            'z-index': 100000,
-            'position': 'absolute',
-            'background': 'white',
-            'padding': '5px',
-            'overflow': 'hidden',
-            'border': '3px solid #365C6A',
-            'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-            'cursor': 'move'
-        }).draggable({
-            cancel: '.button, textarea, .nf-checkbox',
-            containment: 'parent'
-        }).appendTo(container);
-
-        // create the input field
-        input = $('<textarea hidefocus rows="5"/>').css({
-            'background': 'white',
-            'width': args.position.width + 'px',
-            'min-width': '150px',
-            'height': '80px',
-            'border-width': '0',
-            'outline': '0',
-            'overflow-y': 'auto',
-            'resize': 'both',
-            'margin-bottom': '28px'
-        }).tab().on('keydown', scope.handleKeyDown).appendTo(wrapper);
-
-        // create the button panel
-        var stringCheckPanel = $('<div class="string-check-container">');
-
-        // build the custom checkbox
-        isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
-        $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
-
-        var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
-        var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
-        $('<div></div>').css({
-            'position': 'absolute',
-            'bottom': '0',
-            'left': '0',
-            'right': '0',
-            'padding': '0 3px 5px'
-        }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
-
-        // position and focus
-        scope.position(args.position);
-        input.focus().select();
-    };
-
-    this.handleKeyDown = function (e) {
-        if (e.which === $.ui.keyCode.ENTER && !e.shiftKey) {
-            scope.save();
-        } else if (e.which === $.ui.keyCode.ESCAPE) {
-            e.preventDefault();
-            scope.cancel();
-        }
-    };
-
-    this.save = function () {
-        args.commitChanges();
-    };
-
-    this.cancel = function () {
-        input.val(initialValue);
-        args.cancelChanges();
-    };
-
-    this.hide = function () {
-        wrapper.hide();
-    };
-
-    this.show = function () {
-        wrapper.show();
-    };
-
-    this.position = function (position) {
-        wrapper.css({
-            'top': position.top - 5,
-            'left': position.left - 5
-        });
-    };
-
-    this.destroy = function () {
-        wrapper.remove();
-    };
-
-    this.focus = function () {
-        input.focus();
-    };
-
-    this.loadValue = function (item) {
-        // determine if this is a sensitive property
-        var isEmptyChecked = false;
-        var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
-
-        // determine the value to use when populating the text field
-        if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
-            if (sensitive) {
-                initialValue = nf.Common.config.sensitiveText;
-            } else {
-                initialValue = item[args.column.field];
-                isEmptyChecked = initialValue === '';
-            }
-        }
-
-        // determine if its an empty string
-        var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
-        isEmpty.addClass(checkboxStyle);
-
-        // style sensitive properties differently
-        if (sensitive) {
-            input.addClass('sensitive').keydown(function () {
-                var sensitiveInput = $(this);
-                if (sensitiveInput.hasClass('sensitive')) {
-                    sensitiveInput.removeClass('sensitive');
-                    if (sensitiveInput.val() === nf.Common.config.sensitiveText) {
-                        sensitiveInput.val('');
-                    }
-                }
-            });
-        }
-
-        input.val(initialValue);
-        input.select();
-    };
-
-    this.serializeValue = function () {
-        // if the field has been cleared, set the value accordingly
-        if (input.val() === '') {
-            // if the user has checked the empty string checkbox, use emtpy string
-            if (isEmpty.hasClass('checkbox-checked')) {
-                return '';
-            } else {
-                // otherwise if the property is required
-                if (nf.Common.isRequiredProperty(propertyDescriptor)) {
-                    if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
-                        return previousValue;
-                    } else {
-                        return propertyDescriptor.defaultValue;
-                    }
-                } else {
-                    // if the property is not required, clear the value
-                    return null;
-                }
-            }
-        } else {
-            // if the field still has the sensitive class it means a property
-            // was edited but never modified so we should restore the previous
-            // value instead of setting it to the 'sensitive value set' string
-            if (input.hasClass('sensitive')) {
-                return previousValue;
-            } else {
-                // if there is text specified, use that value
-                return input.val();
-            }
-        }
-    };
-
-    this.applyValue = function (item, state) {
-        item[args.column.field] = state;
-    };
-
-    this.isValueChanged = function () {
-        return scope.serializeValue() !== previousValue;
-    };
-
-    this.validate = function () {
-        return {
-            valid: true,
-            msg: null
-        };
-    };
-
-    // initialize the custom long text editor
-    this.init();
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/526e18d0/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
index 16dd95a..b7bf448 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
@@ -17,132 +17,6 @@
 nf.ProcessorDetails = (function () {
 
     /**
-     * Shows the property value for the specified row and cell.
-     * 
-     * @param {type} row
-     * @param {type} cell
-     */
-    var showPropertyValue = function (row, cell) {
-        // remove any currently open detail dialogs
-        removeAllPropertyDetailDialogs();
-
-        // get the property in question
-        var propertyGrid = $('#read-only-processor-properties').data('gridInstance');
-        var propertyData = propertyGrid.getData();
-        var property = propertyData.getItem(row);
-
-        // ensure there is a value
-        if (nf.Common.isDefinedAndNotNull(property.value)) {
-
-            // get the processor details to insert the description tooltip
-            var details = $('#processor-details').data('processorDetails');
-            var propertyDescriptor = details.config.descriptors[property.property];
-
-            // ensure we're not dealing with a sensitive property
-            if (!nf.Common.isSensitiveProperty(propertyDescriptor)) {
-
-                // get details about the location of the cell
-                var cellNode = $(propertyGrid.getCellNode(row, cell));
-                var offset = cellNode.offset();
-
-                // create the wrapper
-                var wrapper = $('<div class="processor-property-detail"></div>').css({
-                    'z-index': 100000,
-                    'position': 'absolute',
-                    'background': 'white',
-                    'padding': '5px',
-                    'overflow': 'hidden',
-                    'border': '3px solid #365C6A',
-                    'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-                    'cursor': 'move',
-                    'top': offset.top - 5,
-                    'left': offset.left - 5
-                }).appendTo('body');
-
-                var editor = null;
-
-                // so the nfel editor is appropriate
-                if (supportsEl(propertyDescriptor)) {
-                    var languageId = 'nfel';
-                    var editorClass = languageId + '-editor';
-
-                    // prevent dragging over the nf editor
-                    wrapper.draggable({
-                        cancel: 'input, textarea, pre, .button, .' + editorClass,
-                        containment: 'parent'
-                    });
-
-                    // create the editor
-                    editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
-                        languageId: languageId,
-                        width: cellNode.width(),
-                        content: property.value,
-                        minWidth: 175,
-                        minHeight: 100,
-                        readOnly: true,
-                        resizable: true
-                    });
-                } else {
-                    // prevent dragging over standard components
-                    wrapper.draggable({
-                        containment: 'parent'
-                    });
-
-                    // create the input field
-                    $('<textarea hidefocus rows="5" readonly="readonly"/>').css({
-                        'background': 'white',
-                        'width': cellNode.width() + 'px',
-                        'height': '80px',
-                        'border-width': '0',
-                        'outline': '0',
-                        'overflow-y': 'auto',
-                        'resize': 'both',
-                        'margin-bottom': '28px'
-                    }).text(property.value).appendTo(wrapper);
-                }
-
-                // add an ok button that will remove the entire pop up
-                var ok = $('<div class="button button-normal">Ok</div>').on('click', function () {
-                    // clean up the editor
-                    if (editor !== null) {
-                        editor.nfeditor('destroy');
-                    }
-                    
-                    // clean up the rest
-                    wrapper.hide().remove();
-                });
-                $('<div></div>').css({
-                    'position': 'absolute',
-                    'bottom': '0',
-                    'left': '0',
-                    'right': '0',
-                    'padding': '0 3px 5px'
-                }).append(ok).append('<div class="clear"></div>').appendTo(wrapper);
-            }
-        }
-    };
-
-    /**
-     * Removes all currently open process property detail dialogs.
-     */
-    var removeAllPropertyDetailDialogs = function () {
-        $('body').children('div.processor-property-detail').hide().remove();
-    };
-
-    /**
-     * Returns whether the specified property supports EL.
-     * 
-     * @param {object} propertyDescriptor           The property descriptor
-     */
-    var supportsEl = function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.supportsEl === true;
-        } else {
-            return false;
-        }
-    };
-
-    /**
      * Creates an option for the specified relationship name.
      * 
      * @argument {object} relationship      The relationship
@@ -194,10 +68,7 @@ nf.ProcessorDetails = (function () {
                 select: function () {
                     // resize the property grid in case this is the first time its rendered
                     if ($(this).text() === 'Properties') {
-                        var propertyGrid = $('#read-only-processor-properties').data('gridInstance');
-                        if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
-                            propertyGrid.resizeCanvas();
-                        }
+                        $('#read-only-processor-properties').propertytable('resetTableSize');
                     }
 
                     // show the border if processor relationship names if necessary
@@ -217,17 +88,9 @@ nf.ProcessorDetails = (function () {
                         // empty the relationship list
                         $('#read-only-auto-terminate-relationship-names').css('border-width', '0').empty();
 
-                        // get the property grid element
-                        var propertyGridElement = $('#read-only-processor-properties');
+                        // clear the property grid
+                        $('#read-only-processor-properties').propertytable('clear');
             
-                        // clean up any tooltips that may have been generated
-                        nf.Common.cleanUpTooltips(propertyGridElement, 'img.icon-info');
-
-                        // clear the data in the grid
-                        var propertyGrid = propertyGridElement.data('gridInstance');
-                        var propertyData = propertyGrid.getData();
-                        propertyData.setItems([]);
-
                         // clear the processor details
                         nf.Common.clearField('read-only-processor-id');
                         nf.Common.clearField('read-only-processor-type');
@@ -244,9 +107,6 @@ nf.ProcessorDetails = (function () {
                         // removed the cached processor details
                         $('#processor-details').removeData('processorDetails');
                         $('#processor-details').removeData('processorHistory');
-
-                        // remove any currently open detail dialogs
-                        removeAllPropertyDetailDialogs();
                     }
                 }
             });
@@ -258,135 +118,9 @@ nf.ProcessorDetails = (function () {
                 });
             }
 
-            // function for formatting the property name
-            var nameFormatter = function (row, cell, value, columnDef, dataContext) {
-                var nameWidthOffset = 10;
-                var cellContent = $('<div></div>');
-
-                // format the contents
-                var formattedValue = $('<span/>').addClass('table-cell').text(value).appendTo(cellContent);
-                if (dataContext.type === 'required') {
-                    formattedValue.addClass('required');
-                }
-
-                // get the processor details to insert the description tooltip
-                var details = $('#processor-details').data('processorDetails');
-                var propertyDescriptor = details.config.descriptors[dataContext.property];
-
-                // show the property description if applicable
-                if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                    if (!nf.Common.isBlank(propertyDescriptor.description) || !nf.Common.isBlank(propertyDescriptor.defaultValue) || !nf.Common.isBlank(propertyDescriptor.supportsEl)) {
-                        $('<img class="icon-info" src="images/iconInfo.png" alt="Info" title="" style="float: right; margin-right: 6px; margin-top: 4px;" />').appendTo(cellContent);
-                        nameWidthOffset = 26; // 10 + icon width (10) + icon margin (6)
-                    }
-                }
-
-                // adjust the width accordingly
-                formattedValue.width(columnDef.width - nameWidthOffset).ellipsis();
-
-                // return the cell content
-                return cellContent.html();
-            };
-
-            // function for formatting the property value
-            var valueFormatter = function (row, cell, value, columnDef, dataContext) {
-                var valueMarkup;
-                if (nf.Common.isDefinedAndNotNull(value)) {
-                    // identify the property descriptor
-                    var processorDetails = $('#processor-details').data('processorDetails');
-                    var propertyDescriptor = processorDetails.config.descriptors[dataContext.property];
-
-                    // determine if the property is sensitive
-                    if (nf.Common.isSensitiveProperty(propertyDescriptor)) {
-                        valueMarkup = '<span class="table-cell sensitive">Sensitive value set</span>';
-                    } else {
-                        if (value === '') {
-                            valueMarkup = '<span class="table-cell blank">Empty string set</span>';
-                        } else {
-                            valueMarkup = '<div class="table-cell value"><pre class="ellipsis">' + nf.Common.escapeHtml(value) + '</pre></div>';
-                        }
-                    }
-                } else {
-                    valueMarkup = '<span class="unset">No value set</span>';
-                }
-
-                // format the contents
-                var content = $(valueMarkup);
-                if (dataContext.type === 'required') {
-                    content.addClass('required');
-                }
-                content.find('.ellipsis').width(columnDef.width - 10).ellipsis();
-
-                // return the appropriate markup
-                return $('<div/>').append(content).html();
-            };
-
-            // initialize the processor type table
-            var processorDetailsColumns = [
-                {id: 'property', field: 'property', name: 'Property', sortable: false, resizable: true, rerenderOnResize: true, formatter: nameFormatter},
-                {id: 'value', field: 'value', name: 'Value', sortable: false, resizable: true, cssClass: 'pointer', rerenderOnResize: true, formatter: valueFormatter}
-            ];
-            var processorDetailsOptions = {
-                forceFitColumns: true,
-                enableTextSelectionOnCells: true,
-                enableCellNavigation: false,
-                enableColumnReorder: false,
-                autoEdit: false
-            };
-
-            // initialize the dataview
-            var processorDetailsData = new Slick.Data.DataView({
-                inlineFilters: false
-            });
-            processorDetailsData.setItems([]);
-
-            // initialize the grid
-            var processorDetailsGrid = new Slick.Grid('#read-only-processor-properties', processorDetailsData, processorDetailsColumns, processorDetailsOptions);
-            processorDetailsGrid.setSelectionModel(new Slick.RowSelectionModel());
-            processorDetailsGrid.onClick.subscribe(function (e, args) {
-                // only consider property values
-                if (args.cell === 1) {
-                    // show the property value in a resizable dialog
-                    showPropertyValue(args.row, args.cell);
-
-                    // prevents standard edit logic
-                    e.stopImmediatePropagation();
-                }
-            });
-
-            // wire up the dataview to the grid
-            processorDetailsData.onRowCountChanged.subscribe(function (e, args) {
-                processorDetailsGrid.updateRowCount();
-                processorDetailsGrid.render();
-            });
-            processorDetailsData.onRowsChanged.subscribe(function (e, args) {
-                processorDetailsGrid.invalidateRows(args.rows);
-                processorDetailsGrid.render();
-            });
-
-            // hold onto an instance of the grid and listen for mouse events to add tooltips where appropriate
-            $('#read-only-processor-properties').data('gridInstance', processorDetailsGrid).on('mouseenter', 'div.slick-cell', function (e) {
-                var infoIcon = $(this).find('img.icon-info');
-                if (infoIcon.length && !infoIcon.data('qtip')) {
-                    var property = $(this).find('span.table-cell').text();
-
-                    // get the processor details to insert the description tooltip
-                    var details = $('#processor-details').data('processorDetails');
-                    var propertyDescriptor = details.config.descriptors[property];
-
-                    // get the processor history
-                    var processorHistory = $('#processor-details').data('processorHistory');
-                    var propertyHistory = processorHistory.propertyHistory[property];
-
-                    // format the tooltip
-                    var tooltip = nf.Common.formatPropertyTooltip(propertyDescriptor, propertyHistory);
-
-                    if (nf.Common.isDefinedAndNotNull(tooltip)) {
-                        infoIcon.qtip($.extend({
-                            content: tooltip
-                        }, nf.Common.config.tooltipConfig));
-                    }
-                }
+            // initialize the properties
+            $('#read-only-processor-properties').propertytable({
+                readOnly: true
             });
         },
         
@@ -453,54 +187,6 @@ nf.ProcessorDetails = (function () {
                     } else {
                         $('#read-only-auto-terminate-relationship-names').append('<div class="unset">This processor has no relationships.</div>');
                     }
-
-                    // get the property grid
-                    var propertyGrid = $('#read-only-processor-properties').data('gridInstance');
-                    var propertyData = propertyGrid.getData();
-                    var properties = details.config.properties;
-                    var descriptors = details.config.descriptors;
-
-                    // generate the processor properties
-                    if (nf.Common.isDefinedAndNotNull(properties)) {
-                        propertyData.beginUpdate();
-
-                        var i = 0;
-                        $.each(properties, function (name, value) {
-                            // get the property descriptor
-                            var descriptor = descriptors[name];
-
-                            // determine the property type
-                            var type = 'userDefined';
-                            var displayName = name;
-                            if (nf.Common.isDefinedAndNotNull(descriptor)) {
-                                if (descriptor.required === true) {
-                                    type = 'required';
-                                } else if (descriptor.dynamic === true) {
-                                    type = 'userDefined';
-                                } else {
-                                    type = 'optional';
-                                }
-                                
-                                // use the display name if possible
-                                displayName = descriptor.displayName;
-                                
-                                // determine the value
-                                if (nf.Common.isNull(value) && nf.Common.isDefinedAndNotNull(descriptor.defaultValue)) {
-                                    value = descriptor.defaultValue;
-                                }
-                            }
-
-                            // add the row
-                            propertyData.addItem({
-                                id: i++,
-                                property: displayName,
-                                value: value,
-                                type: type
-                            });
-                        });
-
-                        propertyData.endUpdate();
-                    }
                 }
             });
 
@@ -517,9 +203,14 @@ nf.ProcessorDetails = (function () {
             });
 
             // show the dialog once we have the processor and its history
-            $.when(getProcessor, getProcessorHistory).done(function (response) {
-                var processorResponse = response[0];
+            $.when(getProcessor, getProcessorHistory).done(function (processorResponse, historyResponse) {
+                var processorResponse = processorResponse[0];
                 var processor = processorResponse.processor;
+                var historyResponse = historyResponse[0];
+                var history = historyResponse.processorHistory;
+
+                // load the properties
+                $('#read-only-processor-properties').propertytable('loadProperties', processor.config.properties, processor.config.descriptors, history.propertyHistory);
 
                 var buttons = [{
                         buttonText: 'Ok',