You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:23:54 UTC

[sling-org-apache-sling-resource-editor] 38/50: SLING-4736 Resource Editor :: properties page :: add CRUD for String and Path and prepare for other types: o Added crud for String and Path properties o Added path-editor, path-viewer and string-editor tag files o Added e2e tests for the new crud o Added shortcuts and a cheat sheet for properties to explain them o Added bootstrap-notify to notify after saving and deleting properties o Added Glyphicons-halflings icon font for property icons o Manually tested on IE9 o Styles adapted for IE9 [...]

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resource-editor.git

commit d8866ccf02638031c37d4cc4b7295223cc8925de
Author: Sandro Boehme <sb...@apache.org>
AuthorDate: Fri Jun 26 10:58:10 2015 +0000

    SLING-4736 Resource Editor :: properties page :: add CRUD for String and Path and prepare for other types:
    o Added crud for String and Path properties
    o Added path-editor, path-viewer and string-editor tag files
    o Added e2e tests for the new crud
    o Added shortcuts and a cheat sheet for properties to explain them
    o Added bootstrap-notify to notify after saving and deleting properties
    o Added Glyphicons-halflings icon font for property icons
    o Manually tested on IE9
    o Styles adapted for IE9
    o Added toolbar with dropdown for the properties page
    o Replaced some css colors by variables in less files
    o Added vertical scrolling to the properties page
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1687749 13f79535-47bb-0310-9956-ffa450edef68
---
 frontend/Gruntfile.js                              |  58 ++-
 frontend/package.json                              |   3 +-
 pom.xml                                            |   8 +-
 src/main/img/save-bigger_white.xcf                 | Bin 0 -> 1467 bytes
 src/main/less/forms.less                           |   5 +
 src/main/less/glyphicons.less                      |  15 +
 src/main/less/navs.less                            |  22 +
 src/main/less/reseditor.less                       |  64 ++-
 src/main/less/scaffolding.less                     |   4 +
 src/main/less/variables.less                       |   2 +
 src/main/resources/META-INF/resource-editor.tld    |  17 +
 src/main/resources/META-INF/tags/path-editor.tag   |  13 +
 src/main/resources/META-INF/tags/path-viewer.tag   |  24 +
 src/main/resources/META-INF/tags/string-editor.tag |  17 +
 .../resource-editor-static-content/css/ie.css}     |  17 +-
 .../js/properties/PropertyController.js            | 196 +++++++++
 .../js/tree/AddNodeController.js                   |   6 +-
 .../SLING-INF/libs/sling/resource-editor/html.jsp  |  86 +---
 .../libs/sling/resource-editor/nodes.json.incl.jsp |   4 +-
 .../libs/sling/resource-editor/nodes.json.jsp      |   2 +-
 .../libs/sling/resource-editor/properties.jsp      | 180 ++++++++
 .../libs/sling/resource-editor/rootnodes.json.jsp  |   2 +-
 src/test/javascript/e2e/spec/e2e_spec.js           | 485 +++++++++++++--------
 23 files changed, 923 insertions(+), 307 deletions(-)

diff --git a/frontend/Gruntfile.js b/frontend/Gruntfile.js
index 49559d1..e5c1718 100644
--- a/frontend/Gruntfile.js
+++ b/frontend/Gruntfile.js
@@ -71,33 +71,49 @@ module.exports = function(grunt) {
 		                  'jquery/dist/jquery.min.js',
 		                  'jquery/dist/jquery.min.map',
 		                  'bootbox/bootbox.min.js',
-		                  'jstree/dist/jstree.min.js'
+		                  'jstree/dist/jstree.min.js',
+		                  'bootstrap-notify/dist/bootstrap-notify.min.js'
 		                 ], // Actual pattern(s) to match.
 		            dest: staticContentFolder+'/generated/3rd_party/js',   // Destination path prefix.
 		            flatten: true
 		          },
 		        ],
 		      },
-    	css_dependencies: {
-	        files: [
-	          {
-	            expand: true,     // Enable dynamic expansion.
-	            cwd: 'node_modules/',      // Src matches are relative to this path.
-	            src: [
-	                  'select2/select2.css',
-	                  'select2/select2.png',
-	                  'select2/select2-spinner.gif',
-	                  'animate.css/animate.min.css',
-	                  'jstree/dist/themes/default/style.min.css',
-	                  'jstree/dist/themes/default/32px.png',
-	                  'jstree/dist/themes/default/40px.png',
-	                  'jstree/dist/themes/default/throbber.gif',
-	                 ], // Actual pattern(s) to match.
-	            dest: staticContentFolder+'/generated/3rd_party/css',   // Destination path prefix.
-	            flatten: true
-	          },
-	        ],
-	      }
+	    	css_dependencies: {
+		        files: [
+		          {
+		            expand: true,     // Enable dynamic expansion.
+		            cwd: 'node_modules/',      // Src matches are relative to this path.
+		            src: [
+		                  'select2/select2.css',
+		                  'select2/select2.png',
+		                  'select2/select2-spinner.gif',
+		                  'animate.css/animate.min.css',
+		                  'jstree/dist/themes/default/style.min.css',
+		                  'jstree/dist/themes/default/32px.png',
+		                  'jstree/dist/themes/default/40px.png',
+		                  'jstree/dist/themes/default/throbber.gif',
+		                 ], // Actual pattern(s) to match.
+		            dest: staticContentFolder+'/generated/3rd_party/css',   // Destination path prefix.
+		            flatten: true
+		          },
+		        ],
+		      },
+		    font_dependencies: {
+		        files: [
+		          {
+		            expand: true,     // Enable dynamic expansion.
+		            cwd: 'node_modules/',      // Src matches are relative to this path.
+		            src: [
+		                  'bootstrap/fonts/glyphicons-halflings-regular.ttf',
+		                  'bootstrap/fonts/glyphicons-halflings-regular.woff2',
+		                  'bootstrap/fonts/glyphicons-halflings-regular.woff'
+		                 ], // Actual pattern(s) to match.
+		            dest: staticContentFolder+'/generated/3rd_party/fonts',   // Destination path prefix.
+		            flatten: true
+		          },
+		        ],
+		      }
 	    },
 	    karma: {
 	    	options: {
diff --git a/frontend/package.json b/frontend/package.json
index 7fe4321..6966d1e 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,7 +11,8 @@
   	"select2": "3.5.2-browserify",
   	"jstree": "3.0.9",
   	"animate.css": "3.1.1",
-  	"jquery-scrollto": "1.4.4"
+  	"jquery-scrollto": "1.4.4",
+  	"bootstrap-notify": "3.0.0"
   },
   "devDependencies": {
     "grunt": "0.4.5",
diff --git a/pom.xml b/pom.xml
index 08ad5fa..e75dd64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,7 +58,11 @@
 				<extensions>true</extensions>
 				<configuration>
 					<instructions>
-						<Sling-Initial-Content>SLING-INF/libs/sling/resource-editor-static-content;overwrite:=true;path:=/libs/sling/resource-editor-static-content,SLING-INF/libs/sling/resource-editor;overwrite:=true;path:=/libs/sling/resource-editor</Sling-Initial-Content>
+						<Sling-Initial-Content>
+							META-INF/tags;overwrite:=true;path:=/libs/sling/resource-editor-tags/property-editor,
+							SLING-INF/libs/sling/resource-editor-static-content;overwrite:=true;path:=/libs/sling/resource-editor-static-content,
+							SLING-INF/libs/sling/resource-editor;overwrite:=true;path:=/libs/sling/resource-editor
+						</Sling-Initial-Content>
 						<Import-Package>!org.slf4j.impl,*,
 							org.apache.sling.commons.js.nodetypes.javascript,
 							javax.el,
@@ -80,6 +84,8 @@
 							org.xml.sax.helpers
 						</Import-Package>
 						<Export-Package>!org.apache.sling.reseditor</Export-Package>
+			            <Include-Resource>META-INF/resource-editor.tld=${project.build.outputDirectory}/META-INF/resource-editor.tld,{maven-resources}</Include-Resource>
+			            <Sling-Bundle-Resources>/META-INF/tags</Sling-Bundle-Resources>
 					</instructions>
 					<remoteOBR>www.jcrbrowser.org</remoteOBR>
 				</configuration>
diff --git a/src/main/img/save-bigger_white.xcf b/src/main/img/save-bigger_white.xcf
new file mode 100644
index 0000000..bf58185
Binary files /dev/null and b/src/main/img/save-bigger_white.xcf differ
diff --git a/src/main/less/forms.less b/src/main/less/forms.less
index ea3ae31..3b6aa85 100644
--- a/src/main/less/forms.less
+++ b/src/main/less/forms.less
@@ -53,4 +53,9 @@
 // vertical-form is a custom class
 .modal-body.vertical-form label {
 	display: block;
+}
+
+
+#properties fieldset {
+	margin-bottom: 5px;
 }
\ No newline at end of file
diff --git a/src/main/less/glyphicons.less b/src/main/less/glyphicons.less
new file mode 100644
index 0000000..4650880
--- /dev/null
+++ b/src/main/less/glyphicons.less
@@ -0,0 +1,15 @@
+//
+// Glyphicons for Bootstrap
+//
+// Since icons are fonts, they can be placed anywhere text is placed and are
+// thus automatically sized to match the surrounding child. To use, create an
+// inline element with the appropriate classes, like so:
+//
+// <a href="#"><span class="glyphicon glyphicon-star"></span> Star</a>
+
+
+#properties .glyphicon {
+	font-size: 16px;
+	color: @textColor;
+}
+
diff --git a/src/main/less/navs.less b/src/main/less/navs.less
index ca7862c..0d880a8 100644
--- a/src/main/less/navs.less
+++ b/src/main/less/navs.less
@@ -79,3 +79,25 @@
   padding: 5px 10px 5px 10px;
   
 }
+
+.nav {
+	> li > a {
+	      &:hover,
+	      &:focus {
+	        background-color: @selectedBackground;
+	        border: 1px solid black;
+	      }
+	    border: 1px solid transparent;
+		color: @textColor;
+	}
+  // Open dropdowns
+  .open {
+	text-shadow: none;
+  }
+  .dropdown-menu > li > a {
+	      &:hover,
+	      &:focus {
+	        background-color: @textColor;
+	      }
+   }
+}
diff --git a/src/main/less/reseditor.less b/src/main/less/reseditor.less
index bf16646..18942d4 100755
--- a/src/main/less/reseditor.less
+++ b/src/main/less/reseditor.less
@@ -31,6 +31,7 @@
 @import "forms.less";
 @import "buttons.less";
 @import "modals.less";
+@import "glyphicons.less";
  
 
 /* ################################################################# */
@@ -120,7 +121,7 @@ body
 	font-family: 'Michroma', sans-serif;
 	font-weight: bold;
 	font-size: 24px;
-	color: #2D373D;
+	color: @brand-primary;
 	text-shadow: 0 2px 2px #dddddd;
 	display: inline;
 	margin: 10px;
@@ -134,16 +135,18 @@ body
 	margin-left: 4px;
 }
 
-.logout a {
+#login .logout a {
 	.rounded;
-	border: 1px solid #2D373D;
-	color: #2D373D;
+	border: 1px solid @brand-primary;
+	color: @brand-primary;
 	text-shadow: 0 1px 1px #EEE;
 	font-size: 15px;
 	margin: 10px 10px 0 0;
-	padding: 0 10px;
 	box-shadow: inset 0px 0px 1px black, 1px 1px 0px white;
 }
+#login .logout a:hover {
+	background-color: @textColor;
+}
 
 #login_error {
 	width: 200px;
@@ -171,28 +174,12 @@ body
     padding: 5px;
 }
 
-
-fieldset
-{
-	border: none;
-	margin-top: 5px;
-	padding: 0;
-}
 .proplabel
 {
-	width: 25%;
-	float: left;
-	display: inline;
 	line-height: 24px;
-	color: #C0C0C0;
+	color: @textColor;
 	font-weight: normal;
 }
-.propinput
-{
-	float: left;
-	display: inline;
-	width: 65%;
-}
 .propinputmultival
 {
 	float: left;
@@ -202,7 +189,6 @@ fieldset
 .propmultival_fieldset
 {
 	float: left;
-	width: 65%;
 	margin: 0 5px 0 0
 }
 
@@ -247,6 +233,23 @@ fieldset
 	background-image: url(../../img/remove.png);
 }
 
+.property-icon {
+  	margin-top: 5px;
+}
+
+.icon {
+	cursor: pointer;
+	border: 1px solid transparent; 
+	padding: 3px;
+	-moz-border-radius: 3px;
+    border-radius: 3px;
+}
+
+.property-icon:hover {
+	border: 1px solid black;
+	background-color: @selectedBackground;
+}
+
 .jstree-default .jstree-icon.add-icon{
 	background-image: url(../../img/add.png);
     margin-right: 3px;
@@ -282,7 +285,7 @@ fieldset
 
 #tree .jstree-clicked 
 {
-	background:#303030; 
+	background: @selectedBackground; 
 	border:1px solid black; 
 	padding:0px 1px;
     -moz-border-radius: 5px;
@@ -349,4 +352,17 @@ input.jstree-rename-input{
 }
 #addNodeDialog .form-group label a{
 	font-weight: normal;
+}
+
+.propertyTypeMenu {
+	text-shadow: none;
+}
+
+#properties .ie9filter-plate-div {
+	overflow-y: auto;
+	overflow-x: hidden;
+}
+
+#properties-info-icon {
+	margin-top: -4px;
 }
\ No newline at end of file
diff --git a/src/main/less/scaffolding.less b/src/main/less/scaffolding.less
index 75b563c..1c90e95 100644
--- a/src/main/less/scaffolding.less
+++ b/src/main/less/scaffolding.less
@@ -27,4 +27,8 @@
 
 body {
   .plate-text-shadow;
+}
+
+.property-row a {
+	color: @textColor;
 }
\ No newline at end of file
diff --git a/src/main/less/variables.less b/src/main/less/variables.less
index 9d692a2..25f14f5 100644
--- a/src/main/less/variables.less
+++ b/src/main/less/variables.less
@@ -26,6 +26,7 @@
 // -------------------------
 @bodyBackground:	transparent;
 @textColor: #c0c0c0;
+@selectedBackground: #303030;
 
 // Links
 // -------------------------
@@ -34,3 +35,4 @@
 // Forms
 // -------------------------
 @inputBorderRadius:             3px;
+@icon-font-path:          "../3rd_party/fonts/";
\ No newline at end of file
diff --git a/src/main/resources/META-INF/resource-editor.tld b/src/main/resources/META-INF/resource-editor.tld
new file mode 100644
index 0000000..b4a0288
--- /dev/null
+++ b/src/main/resources/META-INF/resource-editor.tld
@@ -0,0 +1,17 @@
+<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee">
+ <tlib-version>1.0-SNAPSHOT</tlib-version>
+ <short-name>test</short-name>
+ <uri>http://sling.apache.org/resource-editor</uri>
+ <tag-file>
+  <name>string-editor</name>
+  <path>/META-INF/tags/string-editor.tag</path>
+ </tag-file>
+ <tag-file>
+  <name>path-editor</name>
+  <path>/META-INF/tags/path-editor.tag</path>
+ </tag-file>
+ <tag-file>
+  <name>path-viewer</name>
+  <path>/META-INF/tags/path-viewer.tag</path>
+ </tag-file>
+</taglib>
\ No newline at end of file
diff --git a/src/main/resources/META-INF/tags/path-editor.tag b/src/main/resources/META-INF/tags/path-editor.tag
new file mode 100644
index 0000000..90df44b
--- /dev/null
+++ b/src/main/resources/META-INF/tags/path-editor.tag
@@ -0,0 +1,13 @@
+<%@ tag body-content="empty" isELIgnored="false" display-name="Path-Editor" %>
+
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+
+<%@attribute name="component_id" %>
+<%@attribute name="value"  %>
+
+<c:if test="${not empty value}" >
+	<c:set var="escapedValue" value="${fn:escapeXml(value)}" />
+</c:if>
+
+<input id="${component_id}" class="propinput form-control property-value path-editor" name="new-property-value" value="${escapedValue}"/>
diff --git a/src/main/resources/META-INF/tags/path-viewer.tag b/src/main/resources/META-INF/tags/path-viewer.tag
new file mode 100644
index 0000000..6a1fa84
--- /dev/null
+++ b/src/main/resources/META-INF/tags/path-viewer.tag
@@ -0,0 +1,24 @@
+<%@ tag body-content="empty" isELIgnored="false" display-name="Path-Editor" %>
+
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+
+<%@attribute name="component_id" %>
+<%@attribute name="value" %>
+<%@attribute name="editor_component_id" %>
+
+<c:if test="${not empty value}" >
+	<c:set var="escapedValue" value="${fn:escapeXml(value)}" />
+</c:if>
+
+<script type="text/javascript">
+$(document).ready(function() {
+	
+	$('\#${editor_component_id}').on("keyup", function(){
+		$('\#${component_id}').attr('href', '/reseditor'+$(this).val()+'.html');
+		$('\#${component_id}').text($(this).val()+'.html');
+	});
+});
+</script>
+
+<a id="${component_id}" class="path-viewer" href="/reseditor${value}.html">${escapedValue}.html</a>
\ No newline at end of file
diff --git a/src/main/resources/META-INF/tags/string-editor.tag b/src/main/resources/META-INF/tags/string-editor.tag
new file mode 100644
index 0000000..07615de
--- /dev/null
+++ b/src/main/resources/META-INF/tags/string-editor.tag
@@ -0,0 +1,17 @@
+<%@ tag body-content="empty" isELIgnored="false" display-name="String-Editor" %>
+
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+
+<%@attribute name="property_name"  %>
+<%@attribute name="value"  %>
+<%@attribute name="rows"  %>
+
+<c:choose>
+  <c:when test="${empty rows}">
+	<textarea data-property-name="${fn:escapeXml(property_name)}" name="new-property-value" class="propinput form-control property-value">${value}</textarea>
+  </c:when>
+  <c:otherwise>
+	<textarea data-property-name="${fn:escapeXml(property_name)}" name="new-property-value" class="propinput form-control property-value" rows="${rows}">${value}</textarea>
+  </c:otherwise>
+</c:choose>
diff --git a/src/main/less/scaffolding.less b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/css/ie.css
similarity index 77%
copy from src/main/less/scaffolding.less
copy to src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/css/ie.css
index 75b563c..287decd 100644
--- a/src/main/less/scaffolding.less
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/css/ie.css
@@ -17,14 +17,13 @@
  * under the License.
  */
  
-//
-// Scaffolding
-// --------------------------------------------------
+#login .nav-tabs > li > a, #login .tabbable .tab-content {
+	background-color: #202020;
+}
 
-
-// Body reset
-// -------------------------
-
-body {
-  .plate-text-shadow;
+#login_tab_content {
+	border-top-right-radius: 7px;
+	border-top-left-radius: 7px;
+	border-bottom-right-radius: 7px;
+	border-bottom-left-radius: 7px;
 }
\ No newline at end of file
diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/properties/PropertyController.js b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/properties/PropertyController.js
new file mode 100644
index 0000000..ebddeab
--- /dev/null
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/properties/PropertyController.js
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+// creating the namespace
+var org = org || {};
+org.apache = org.apache || {};
+org.apache.sling = org.apache.sling || {};
+org.apache.sling.reseditor = org.apache.sling.reseditor || {};
+
+/*
+ * The PropertyController is responsible for the property page functionality.
+ */
+
+//defining the module
+org.apache.sling.reseditor.PropertyController = (function() {
+
+	function PropertyController(settings, mainController){
+		this.mainController = mainController;
+		this.settings = settings;
+		
+		var thisPropertyController = this;
+		
+		$(document).ready(function() {
+			$.notifyDefaults({
+				delay: 500,
+				template: '<div data-notify="container" class="col-xs-11 col-sm-3 growl-notify alert alert-{0}" role="alert">' +
+				'<button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button>' +
+				'<span data-notify="icon"></span> ' +
+				'<span data-notify="title">{1}</span> ' +
+				'<span data-notify="message">{2}</span>' +
+				'<a href="{3}" target="{4}" data-notify="url"></a>' +
+			'</div>'
+			});
+
+			$( "#properties" ).on( "click", ".dropdown-menu.add-property-menu li a", function() {
+				var dataType = $(this).attr('data-property-type');
+				thisPropertyController.openAddPropertyDialog(dataType);
+			});
+			$('#addPropertyDialog .submit').click(function(){
+				thisPropertyController.addProperty();
+			});
+			$( "#properties" ).on( "click", ".property-icon.glyphicon-remove", function() {
+				var parentRow = $(this).parents(".row:first");
+				var propertyKey = parentRow.find(".proplabel").attr("for");
+				thisPropertyController.removeProperty(propertyKey, parentRow);
+			});
+			$( "#properties" ).on( "click", ".property-icon.glyphicon-save", function() {
+				var parentRow = $(this).parents(".row:first");
+				var key, value;
+				if (parentRow.hasClass('new-property')){
+					key = $('#newStringPropertyKey').val();
+				} else {
+					key = parentRow.find(".proplabel").attr("for");
+				}
+				value = parentRow.find(".form-control").val();
+				
+				thisPropertyController.saveProperty(key, value);
+			});
+
+			$("#properties-info-icon").on("click", function(e, data) {
+				$('#properties .info-content-container').slideToggle();
+			});
+			$("#properties .info-content-container .close").on("click", function(e, data) {
+				$('#properties .info-content-container').slideToggle();
+			});
+			$( "#properties" ).on( "keydown", function(event, data) {
+		    	// see http://www.javascripter.net/faq/keycodes.htm
+				if (event.ctrlKey || event.metaKey) {
+					var pressedKey = String.fromCharCode(event.which).toLowerCase();
+					var n = 78;
+					var s = 83;
+					var del = 46;
+					switch (event.which){
+					    case s:
+					    	event.preventDefault();
+					    	var parentRow = $( document.activeElement ).parents(".row:first");
+					    	var key = parentRow.find(".proplabel").attr("for");
+							var value = parentRow.find(".form-control").val();
+							thisPropertyController.saveProperty(key, value);
+					        break;
+					    case del:
+					    	event.preventDefault();
+					    	var parentRow = $( document.activeElement ).parents(".row:first");
+					    	var key = parentRow.find(".proplabel").attr("for");
+							thisPropertyController.removeProperty(key, parentRow);
+					        break;
+					    case n:
+					    	event.preventDefault();
+					    	$('#properties .add-property-menu-item.dropdown-toggle').dropdown('toggle');
+					        break;
+					}
+				}
+			});
+		});
+	};
+	
+	PropertyController.prototype.openAddPropertyDialog = function(dataType){
+		$('#addPropertyDialog').modal('show');
+		$('#addPropertyDialog .property-editor').hide();
+		$("#addPropertyDialog .property-editor[data-property-type='"+dataType+"']").show();
+	};
+	
+	PropertyController.prototype.removeProperty = function(key, row){
+		var thisPropertyController = this;
+		var confirmationMsg = "You are about to delete the property with the key '"+key+"'. Are you sure?";
+		bootbox.confirm(confirmationMsg, function(result) {
+			if (result){
+				var data = {};
+				data[key+"@Delete"] = "";
+				$.ajax({
+			  	  type: 'POST',
+				  url: location.href,
+				  dataType: "json",
+			  	  data: data
+			  	})
+				.done(function() {
+					row.remove();
+					$.notify({
+						message: 'Property \''+key+'\' deleted.' 
+					},{
+						type: 'success'
+					});
+				})
+				.fail(function(errorJson) {
+					thisPropertyController.mainController.displayAlert(errorJson);
+				});
+			}
+		});
+	};
+	
+	PropertyController.prototype.saveProperty = function(key, value){
+		var thisPropertyController = this;
+		var data = {};
+		data[key] = value;
+		data["_charset_"] = "utf-8";
+		$.ajax({
+	  	  type: 'POST',
+		  url: location.href,
+		  dataType: "json",
+	  	  data: data
+	  	})
+		.done(function() {
+			$.notify({
+				message: 'Property \''+key+'\' saved.' 
+			},{
+				type: 'success'
+			});
+		})
+		.fail(function(errorJson) {
+			thisPropertyController.mainController.displayAlert(errorJson);
+		});
+	};
+
+	PropertyController.prototype.addProperty = function(){
+		var thisPropertyController = this;
+		var key = $('#new-property-key').val();
+		var value = $("#addPropertyDialog .property-editor:visible .property-value").val();
+		var data = {};
+		var propertyType = $("#addPropertyDialog .property-editor:visible").attr("data-property-type");
+		data[key] = value;
+		data[key+"@TypeHint"] = propertyType;
+		data["_charset_"] = "utf-8";
+		$.ajax({
+	  	  type: 'POST',
+	  	  "_charset_": "utf-8",
+		  url: location.href,
+		  dataType: "json",
+	  	  data: data
+	  	})
+		.done(function() {
+			 location.reload();
+		})
+		.fail(function(errorJson) {
+			$('#addPropertyDialog').modal('hide');
+			thisPropertyController.mainController.displayAlert(errorJson);
+		});
+	}
+	
+	return PropertyController;
+}());
diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/AddNodeController.js b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/AddNodeController.js
index 3c2571e..e5f0231 100644
--- a/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/AddNodeController.js
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor-static-content/js/tree/AddNodeController.js
@@ -244,8 +244,6 @@ org.apache.sling.reseditor.AddNodeController = (function() {
 		    }
 		});
 		
-		$('#addNodeDialog').modal('show');
-		
 		var contextPath = this.mainController.getContextPath() == "/" && resourcePath.length>0 && resourcePath.charAt(0)=="/" ? "" : this.mainController.getContextPath(); 
 		this.lastAddNodeURL = contextPath+resourcePath;
 
@@ -270,8 +268,10 @@ org.apache.sling.reseditor.AddNodeController = (function() {
 					return {id:searchTerm, text:searchTerm};
 				}
 			}).data("select2");
+			
+			$('#addNodeDialog').modal('show');
+			
 			$('#addNodeDialog').addClass('add-node-finished');
-//			$('#addNodeDialog').append('<div id="add-node-finished"></div>');
 		});
 	}
 	
diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp
index 170e0e0..86c0d50 100644
--- a/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/html.jsp
@@ -22,7 +22,8 @@
 <%@ page import="javax.jcr.*,org.apache.sling.api.resource.Resource, org.apache.sling.api.resource.ValueMap"%>
 <%@ page import="java.security.Principal"%>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
-<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling"%>
 
 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 
@@ -36,6 +37,10 @@
 <link href='<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/css/font.css' rel='stylesheet' type='text/css'>
  <!--[if lt IE 9]>
 <link href='<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/css/font_ie.css' rel='stylesheet' type='text/css'>
+  <![endif]--> 
+ 
+ <!--[if IE]>
+<link href='<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/css/ie.css' rel='stylesheet' type='text/css'>
   <![endif]-->
   
 <!-- 
@@ -50,10 +55,12 @@ original
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/generated/3rd_party/js/bootbox.min.js"></script>
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/generated/3rd_party/js/jstree.min.js"></script>
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/generated/3rd_party/js/select2.min.js"></script>
+<script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/generated/3rd_party/js/bootstrap-notify.min.js"></script>
 
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/js/tree/JSTreeAdapter.js"></script>
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/js/tree/TreeController.js"></script>
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/js/tree/AddNodeController.js"></script>
+<script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/js/properties/PropertyController.js"></script>
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/js/LoginController.js"></script>
 <script type="text/javascript" src="<%= request.getContextPath() %>/libs/sling/resource-editor-static-content/js/MainController.js"></script>
 
@@ -109,6 +116,8 @@ var jsTreeAdapterSettings = {
 };
 new org.apache.sling.reseditor.JSTreeAdapter(jsTreeAdapterSettings, treeController, mainController);
 
+new org.apache.sling.reseditor.PropertyController({}, mainController);
+
 </script>	
 
 </head>
@@ -157,6 +166,7 @@ new org.apache.sling.reseditor.JSTreeAdapter(jsTreeAdapterSettings, treeControll
 						</div>
 				    </div>
 				  </div>
+				  
 				  <ul class="nav nav-tabs">
 				    <li class="active">
 				    	<a id="login_tab" href="#login_tab_content" data-toggle="tab">Login</a>
@@ -203,78 +213,8 @@ new org.apache.sling.reseditor.JSTreeAdapter(jsTreeAdapterSettings, treeControll
 					</div>
 				</div>
 			</div>
-			<div class="col-sm-8">
-				<div id="outer_content" class="plate">
-					<div class="ie9filter-plate-div">
-						<div id="inner_content_margin">
-							<form action="not_configured_yet.change.properties" method="post">
-								<c:set var="resourceIsNode" scope="request" value="<%=resource.adaptTo(Node.class) !=null %>"/>
-								<c:choose>
-								     <c:when test="${resourceIsNode}" >
-										<%--
-										For some reason I get the following exception when using the JSTL expression '${currentNode.properties}'
-										instead of the scriptlet code 'currentNode.getProperties()':
-										org.apache.sling.scripting.jsp.jasper.JasperException: Unable to compile class for JSP: 
-										org.apache.sling.scripting.jsp.jasper.el.JspValueExpression cannot be resolved to a type
-										see https://issues.apache.org/jira/browse/SLING-2455
-										 --%>
-										<c:forEach var="property" items="<%=currentNode.getProperties()%>">
-									<%  Property property = (Property) pageContext.getAttribute("property");%>
-											<fieldset>
-												<label class="proplabel" for='${property.name}'>${property.name} [<%=PropertyType.nameFromValue(property.getType())%>${property.multiple ? ' multiple' : ''}]</label>
-												<c:choose>
-												     <c:when test="${property.multiple}" >
-												     	<fieldset class="propmultival_fieldset">
-												     		<div>&nbsp;</div>
-												     	<c:forEach var="value" items="<%=property.getValues()%>">
-												     		<c:choose>
-												     		<c:when test="${property.type == PropertyType.BINARY}" >
-														     	<p>I'm a binary property</p>
-														     </c:when>
-														     <c:otherwise>
-													     		<input class="propinputmultival form-control" value="${value.string}"/>
-														     </c:otherwise>
-														     </c:choose>
-												     	</c:forEach>
-						     							</fieldset>
-												     </c:when>
-												     <c:when test="${false}" >
-												     </c:when>
-												     <c:otherwise>
-													     <c:choose>
-													     <c:when test="<%=property.getType() == PropertyType.BINARY%>" >
-													     	<c:choose>
-														     	<c:when test='<%=currentNode.getParent().isNodeType("nt:file") %>'>
-														     		<a class="propinput" href="<%= request.getContextPath() %>${resource.parent.path}">Download</a>
-														     	</c:when>
-														     	<c:otherwise>
-														     		<a class="propinput" href="<%= request.getContextPath() %>${resource.path}.property.download?property=${property.name}">View (choose "Save as..." to download)</a>
-														     	</c:otherwise>
-													     	</c:choose>
-													     </c:when>
-													     <c:otherwise>
-															<input class="propinput form-control" id="${property.name}" name="${property.name}" value="${property.string}"/>							
-													     </c:otherwise>
-													     </c:choose>
-												     </c:otherwise>
-												 </c:choose>
-											</fieldset>
-										</c:forEach>
-								     </c:when>
-								     <c:otherwise>
-										<c:forEach var="property" items="<%=resource.adaptTo(ValueMap.class)%>">	
-											<fieldset>										
-												<label class="proplabel" for='${property.key}'>${property.key}</label>
-												<input class="propinput form-control" id="${property.key}" name="${property.key}" value="${property.value}"/>
-											</fieldset>							
-										</c:forEach>
-								     </c:otherwise>
-								 </c:choose>
-							</form>
-						</div>
-					</div>
-			    </div>
-			</div>
+			
+			<%@ include file="properties.jsp" %>
 	    </div>
 		<div class="row" style="visibility:hidden; display:none;">
 			<div class="col-sm-12">
diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp
index c6a44bc..72cb589 100644
--- a/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.incl.jsp
@@ -17,8 +17,8 @@
   under the License.
 --%>
 [
-	<c:forEach var="theResource" items="<%=resource.listChildren()%>" varStatus="status">
-		<c:set var="resourceIsNode" scope="request" value="<%=resource.adaptTo(Node.class) !=null %>"/>
+	<c:forEach var="theResource" items="${sling:listChildren(resource)}" varStatus="status">
+		<c:set var="resourceIsNode" scope="request" value="${sling:adaptTo(resource,'javax.jcr.Node') != null}"/>
 		<%--Hiding the resource provider root. --%>
 		<c:if test="${theResource.path != '/reseditor'}">
 			<% Resource theResource = (Resource) pageContext.getAttribute("theResource");
diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.jsp
index faa6f80..09f90d9 100644
--- a/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.jsp
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/nodes.json.jsp
@@ -26,7 +26,7 @@
 
 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
-<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%>
+<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling"%>
 <sling:defineObjects />
 <% response.setContentType("application/json"); %>
 
diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/properties.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/properties.jsp
new file mode 100644
index 0000000..6b75ad5
--- /dev/null
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/properties.jsp
@@ -0,0 +1,180 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ taglib prefix="re" uri="http://sling.apache.org/resource-editor"%>
+
+<sling:defineObjects />
+
+			<div id="properties" class="col-sm-8">
+				<div id="outer_content" class="plate">
+					<div class="ie9filter-plate-div">
+						<div id="inner_content_margin">
+							<div class="row">
+								<div class="col-sm-12">
+									<div style="display: none;" class="info-content-container" >
+										<div class="well well-sm info-content">
+											<button type="button" class="close"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
+										  	<h4>Cheat Sheet</h4>
+									  		<p>You can use</p>
+									  		<ul>
+									  			<li><kbd><kbd>ctrl</kbd> + <kbd>s</kbd></kbd> or <kbd><kbd>cmd</kbd> + <kbd>s</kbd></kbd> (for Mac) for saving a property.</li>
+									  			<li><kbd><kbd>ctrl</kbd> + <kbd>del</kbd></kbd> or <kbd><kbd>cmd</kbd> + <kbd>del</kbd></kbd> (for Mac) for removing a property.</li>
+									  			<li><kbd><kbd>ctrl</kbd> + <kbd>n</kbd></kbd> or <kbd><kbd>cmd</kbd> + <kbd>n</kbd></kbd> (for Mac) for opening the add property menu.</li>
+									  		</ul>
+										</div>
+								  	</div>	
+								  	<span id="properties-info-icon" class="info-icon info-icon-lightgray pull-right clearfix" ></span>
+									<ul class="nav nav-pills">
+									  <li class="dropdown" role="presentation">
+									  	<a class="add-property-menu-item dropdown-toggle" data-toggle="dropdown"  href="#"><span class=""><span class="glyphicon glyphicon-plus"></span><span class="caret"></span></span>	
+									  	</a>
+										    <ul class="dropdown-menu add-property-menu" role="menu" aria-labelledby="propertyTypeMenu">
+										        <li><a tabindex="-1" href="#" data-property-type="String">String</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Date">Date</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Boolean">Boolean</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Long">Long</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Double">Double</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Decimal">Decimal</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Binary">Binary</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Name">Name</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Path">Path</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Reference">Reference</a></li>
+										        <li><a tabindex="-1" href="#" data-property-type="Uri">URI</a></li>
+										    </ul>
+									  </li>
+									</ul>
+								</div>
+							</div>
+							<form action="not_configured_yet.change.properties" method="post">
+								<c:set var="resourceIsNode" scope="request" value="<%=resource.adaptTo(Node.class) !=null %>"/>
+								<c:choose>
+								     <c:when test="${resourceIsNode}" >
+										<%--
+										For some reason I get the following exception when using the JSTL expression '${currentNode.properties}'
+										instead of the scriptlet code 'currentNode.getProperties()':
+										org.apache.sling.scripting.jsp.jasper.JasperException: Unable to compile class for JSP: 
+										org.apache.sling.scripting.jsp.jasper.el.JspValueExpression cannot be resolved to a type
+										see https://issues.apache.org/jira/browse/SLING-2455
+										 --%>
+										<c:forEach var="property" items="<%=currentNode.getProperties()%>" varStatus="propertyLoopStatus">
+									<%  Property property = (Property) pageContext.getAttribute("property");%>
+											<div id="property-${propertyLoopStatus.index}" class="row property-row" data-property-name="${fn:escapeXml(property.name)}" >
+												<fieldset>
+													<div class="col-sm-3">
+														<label class="proplabel" for='${property.name}'>${property.name} [<%=PropertyType.nameFromValue(property.getType())%>${property.multiple ? ' multiple' : ''}]</label>
+													</div>
+													<div class="col-sm-7 property-col">
+														<c:choose>
+														     <c:when test="${property.multiple}" >
+														     	<fieldset class="propmultival_fieldset">
+														     		<div>&nbsp;</div>
+														     	<c:forEach var="value" items="<%=property.getValues()%>">
+														     		<c:choose>
+															     		<c:when test="${property.type == PropertyType.BINARY}" >
+																	     	<p>I'm a binary property</p>
+																	     </c:when>
+															     		<c:when test="${property.type == PropertyType.STRING}" >
+																	     	<re:string-editor></re:string-editor>
+																	     </c:when>
+																	     <c:otherwise>
+																     		<input class="propinputmultival form-control" value="${value.string}"/>
+																	     </c:otherwise>
+																     </c:choose>
+														     	</c:forEach>
+								     							</fieldset>
+														     </c:when>
+														     <c:otherwise>
+															     <c:choose>
+																     <c:when test="<%=property.getType() == PropertyType.BINARY%>" >
+																     	<c:choose>
+																	     	<c:when test='<%=currentNode.getParent().isNodeType("nt:file") %>'>
+																	     		<a class="propinput" href="<%= request.getContextPath() %>${resource.parent.path}">Download</a>
+																	     	</c:when>
+																	     	<c:otherwise>
+																	     		<a class="propinput" href="<%= request.getContextPath() %>${resource.path}.property.download?property=${property.name}">View (choose "Save as..." to download)</a>
+																	     	</c:otherwise>
+																     	</c:choose>
+																     </c:when>
+																     <c:when test="<%=property.getType() == PropertyType.STRING%>" >
+																     	<re:string-editor property_name="${fn:escapeXml(property.name)}-editor" value="${property.string}"></re:string-editor>
+																     </c:when>
+																     <c:when test="<%=property.getType() == PropertyType.PATH%>" >
+																     	<re:path-editor value="${property.string}" component_id="property-${propertyLoopStatus.index}-path-editor"></re:path-editor>
+																     	<re:path-viewer value="${property.string}" component_id="property-${propertyLoopStatus.index}-path-viewer" editor_component_id="property-${propertyLoopStatus.index}-path-editor"></re:path-viewer>
+																     </c:when>
+																     <c:otherwise>
+																		<input class="propinput form-control" id="${property.name}" name="${property.name}" value="${property.string}"/>							
+																     </c:otherwise>
+															     </c:choose>
+														     </c:otherwise>
+														 </c:choose>
+													</div>
+												 	<div class="col-sm-2">
+												 		<span class="icon property-icon glyphicon glyphicon-save" aria-hidden="true"></span>
+												 		<span class="icon property-icon glyphicon glyphicon-remove" aria-hidden="true"></span>
+												 	</div>
+												</fieldset>
+											</div>
+										</c:forEach>
+								     </c:when>
+								     <c:otherwise>
+										<c:forEach var="property" items="<%=resource.adaptTo(ValueMap.class)%>">	
+											<div class="row">
+												<fieldset>			
+													<div class="col-sm-3">							
+														<label class="proplabel" for='${property.key}'>${property.key}</label>
+													</div>
+													<div class="col-sm-7">
+														<input class="propinput form-control" id="${property.key}" name="${property.key}" value="${property.value}"/>
+													</div>
+												</fieldset>
+											</div>							
+										</c:forEach>
+								     </c:otherwise>
+								 </c:choose>
+							</form>
+						</div>
+					</div>
+			    </div>
+			</div>
+	<div class="modal fade" id="addPropertyDialog" tabindex="-1" role="dialog" aria-labelledby="addPropertyDialogLabel" aria-hidden="true">
+	  <div class="modal-dialog modal-lg">
+	    <div class="modal-content">
+		      <div class="modal-header">
+		        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
+		        <h4 class="modal-title" id="addPropertyDialogLabel">Add Property</h4>
+		      </div>
+		      <div class="modal-body">
+			      	<div class="container-fluid">
+									 <div class="row new-property">
+										<fieldset>								
+									 		<div class="col-sm-1">
+									 			<label class="proplabel" for='new-property-key'>Key</label>
+											</div>
+										 	<div class="col-sm-3">
+												<input id="new-property-key" class="propinput form-control" name="newPropertyKey" />
+											</div>						
+									 		<div class="col-sm-1">
+									 			<label class="proplabel" for='new-property-value'>Value</label>
+											</div>
+										 	<div class="col-sm-7">
+												<fieldset>								
+													<label class="sr-only" for='new-property-value'>Value</label>	
+													<div class="property-editor" data-property-type="Path">
+<%-- 														<sling:include resource="${resource}" resourceType="sling/resource-editor/property-editor" addSelectors="path-editor"/> --%>
+														<re:path-editor value="" component_id="property-path-editor-new"></re:path-editor>
+													</div>		
+													<div class="property-editor" data-property-type="String">
+														<re:string-editor rows="4"></re:string-editor>
+													</div>
+												</fieldset>		
+											</div>	
+											</fieldset>		
+									</div>	
+					</div>
+		      </div>
+		      <div class="modal-footer">
+		        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+		        <button type="button" class="btn btn-primary submit">Save changes</button>
+		      </div>
+	    </div>
+	  </div>
+	</div>
\ No newline at end of file
diff --git a/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp b/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp
index 1457dbe..b2821b7 100644
--- a/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp
+++ b/src/main/resources/SLING-INF/libs/sling/resource-editor/rootnodes.json.jsp
@@ -27,7 +27,7 @@
 
 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
-<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%>
+<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling"%>
 <sling:defineObjects />
 <% response.setContentType("application/json"); %>
 [{
diff --git a/src/test/javascript/e2e/spec/e2e_spec.js b/src/test/javascript/e2e/spec/e2e_spec.js
index 9143602..984cf83 100644
--- a/src/test/javascript/e2e/spec/e2e_spec.js
+++ b/src/test/javascript/e2e/spec/e2e_spec.js
@@ -29,28 +29,7 @@ describe('A user of the Apache Sling Resource Editor', function() {
 	client.timeouts("script", 1000);
 	client.timeouts("implicit", 1000);
 	client.timeouts("page load", 1000);
-
-	describe('can open the add node dialog with', function() {
-		  it('the icon', function(done) {
-			  client.url(homeURL).waitForExist('#last-element').
-			  click('#root_anchor i.add-icon').waitForVisible('#addNodeDialog', function(err, visible) {
-	    		  assert(typeof err === "undefined" || err === null);
-	    		  assert(true === visible);
-		      })
-		      .call(done);
-		  });
-
-		  it('the shortcut', function(done) {
-			   client.url(homeURL).waitForExist('#last-element')
-			  .click('#root_anchor i.jstree-themeicon')
-			  .keys("c").waitForVisible('#addNodeDialog', function(err, visible) {
-	    		  assert(typeof err === "undefined" || err === null);
-	    		  assert(true === visible);
-		      })
-		      .call(done);
-		  });
-	});
-
+	
 	  it('can login as admin', function(done) {
 		  client.url(homeURL).waitForExist('#last-element')
 		  .click('#login_tab').waitForVisible('#login_submit')
@@ -58,170 +37,334 @@ describe('A user of the Apache Sling Resource Editor', function() {
 		  .setValue('#login_form input[name="j_password"]', 'admin')
 		  .click('#login_submit').waitForExist('#login .logout')
 		  .getText('#login_tab', function(err, text) {
-    		  assert(typeof err === "undefined" || err === null);
+		  assert(typeof err === "undefined" || err === null);
 			  assert("Logout admin" === text);
 	      })
 	      .call(done);
 	  });
-	  
-	  it('can add an unstructured node to the root node', function(done) {
-		  client.url(homeURL).waitForExist('#last-element')
-		  .click("#root i.add-icon").waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container input')
-		  .setValue('.node_name_dd_container input', "aTestNode")
-		  .addValue('.node_name_dd_container input', 'Return') // presses the 'return' key
-		  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist("#root li[nodename=\"aTestNode\"]", function(err, existed) {
-			  assert(typeof err === "undefined" || err === null);
-			  assert(existed === true);
-	      })
-	      .call(done);
-	  });
-	  
-	 it('can add a node with an encoded name on the second level ', function(done) {
-		  client.url(homeURL).waitForExist('#last-element')
-		  .click("#root li[nodename=\"aTestNode\"] i.add-icon").waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container input')
-		  .setValue('.node_name_dd_container input', "täßt ?<>")
-		  .addValue('.node_name_dd_container input', 'Return') // presses the 'return' key
-		  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist("#root  li[nodename=\"aTestNode\"].opened li[nodename=\"täßt ?&lt;&gt;\"]", function(err, existed) {
-			  assert(typeof err === "undefined" || err === null);
-			  assert(existed === true);
-	      })
-	      .call(done); 
-	  });
-	 
-	  it('can link to a node with an encoded name', function(done) {
-		  var encodedNodeNameSelector = '#root li[nodename="aTestNode"].opened li[nodename="täßt ?&lt;&gt;"]';
-		  var encodedNodeNameOpenSelector = encodedNodeNameSelector +' i.open-icon';
-		  client.url(homeURL).waitForExist('#last-element')
-		  .click("#root li[nodename=\"aTestNode\"] i.jstree-ocl").waitForExist(encodedNodeNameOpenSelector)
-		  .click(encodedNodeNameOpenSelector).waitForExist(encodedNodeNameSelector+' a.jstree-clicked', function(err, existed) {
-			  assert(typeof err === "undefined" || err === null);
-			  assert(existed === true);
-	      })
-	      .call(done);
-	  });
-	  
-
-	  it('can add a node with a specific node type ', function(done) {
-		  client.url(homeURL);
-		  client.waitForExist('#last-element').click("#root li[nodename=\"aTestNode\"] i.add-icon").waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container', 1000)
-		  .setValue('.node_name_dd_container input', "aFolder")
-		  .addValue('.node_name_dd_container input', 'Return')
-		  .click(".form-group.node-type .select2-chosen").waitForExist('.node_type_dd_container input')
-		  .setValue('.node_type_dd_container input', "sling:Folder")
-		  .addValue('.node_type_dd_container input', 'Return')
-		  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist('#root  li[nodename="aTestNode"] li[nodename="aFolder"][nodetype="sling:Folder"]', function(err, existed) {
-			  assert(typeof err === "undefined" || err === null);
-			  assert(existed === true);
-		  })
-	      .call(done);
-	  });
-	  
-	  it('can add a node with a resource type on the second level ', function(done) {
-		  var nodeName =  "a node with a resource type";
-		  var resourceType =  "test/resource-editor/resource-type";
-		  var resourceTypeSelector = '#root  li[nodename="aTestNode"].opened li[nodename="a node with a resource type"] span.node-type';
-		  client.url(homeURL);
-		  client.waitForExist('#last-element').click("#root li[nodename=\"aTestNode\"] i.add-icon")
-		  .waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container', 1000)
-		  .setValue('.node_name_dd_container input', nodeName)
-		  .addValue('.node_name_dd_container input', 'Return') // presses the 'return' key
-		  .click(".form-group.resource-type .select2-chosen").waitForExist('.resource_type_dd_container input', 1000)
-		  .setValue('.resource_type_dd_container input', resourceType)
-		  .addValue('.resource_type_dd_container input', 'Return') // presses the 'return' key
-		  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist(resourceTypeSelector, 1000)
-		  .getText(resourceTypeSelector, function(err, text) {
-			  assert(typeof err === "undefined" || err === null);
-			  assert(text === resourceType);
-		  })
-	      .call(done);
-	  });
-	  
-	  it('can rename a node with an encoded name', function(done) {
-		  client = client.url(homeURL);
-		  client.waitForExist('#last-element', function(err) {
-			  client.click("#root li[nodename=\"aTestNode\"] i.jstree-ocl", function(err, res) {
-				  var encodedNodeNameSelector = '#root li[nodename="aTestNode"].opened li[nodename="täßt ?&lt;&gt;"]';
-				  var encodedNodeNameAnchorSelector = encodedNodeNameSelector +' a .node-type';
-// 				  The open node animation will take longer than 500ms thus setting 2000ms as max.
-				  client.waitForExist(encodedNodeNameAnchorSelector, 2000, function(err, existed) {
-					  client.doubleClick(encodedNodeNameAnchorSelector, function(err) {
-						  var encodedNodeNameInputSelector = encodedNodeNameSelector+' input.jstree-rename-input';
-						  client.waitForExist(encodedNodeNameInputSelector, function(err, existed) {
-							  client.setValue(encodedNodeNameInputSelector, 'täßt2& <>');
-							  client.execute(function() {
-								  $('#root li[nodename="aTestNode"] li[nodename="täßt ?&lt;&gt;"] input.jstree-rename-input').blur();
-								  $('#root li[nodename="aTestNode"] li[nodename="täßt ?&lt;&gt;"] input.jstree-rename-input').blur();
-								});
-							  client.waitForExist('#root li[nodename="aTestNode"].opened li[nodename="täßt2&amp; &lt;&gt;"]', 2000, function(err, existed) {
-								  assert(typeof err === "undefined" || err === null);
-								  assert(true === existed);
-								  
+	
+	describe('can manage nodes as he', function() {
+		  
+		describe('can open the add node dialog with', function() {
+			  it('the icon', function(done) {
+				  client.url(homeURL).waitForExist('#last-element').
+				  click('#root_anchor i.add-icon').waitForVisible('#addNodeDialog', function(err, visible) {
+		    		  assert(typeof err === "undefined" || err === null);
+		    		  assert(true === visible);
+			      })
+			      .call(done);
+			  });
+	
+			  it('the shortcut', function(done) {
+				   client.url(homeURL).waitForExist('#last-element')
+				  .click('#root_anchor i.jstree-themeicon')
+				  .keys("c").waitForVisible('#addNodeDialog', function(err, visible) {
+		    		  assert(typeof err === "undefined" || err === null);
+		    		  assert(true === visible);
+			      })
+			      .call(done);
+			  });
+		});
+	
+		  
+		  it('can add an unstructured node to the root node', function(done) {
+			  client.url(homeURL).waitForExist('#last-element')
+			  .click("#root i.add-icon").waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container input', 1000)
+			  .setValue('.node_name_dd_container input', "aTestNode")
+			  .addValue('.node_name_dd_container input', 'Return') // presses the 'return' key
+			  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist("#root li[nodename=\"aTestNode\"]", function(err, existed) {
+				  assert(typeof err === "undefined" || err === null);
+				  assert(existed === true);
+		      })
+		      .call(done);
+		  });
+		  
+		 it('can add a node with an encoded name on the second level ', function(done) {
+			  client.url(homeURL).waitForExist('#last-element')
+			  .click("#root li[nodename=\"aTestNode\"] i.add-icon").waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container input', 1000)
+			  .setValue('.node_name_dd_container input', "täßt ?<>")
+			  .addValue('.node_name_dd_container input', 'Return') // presses the 'return' key
+			  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist("#root  li[nodename=\"aTestNode\"].opened li[nodename=\"täßt ?&lt;&gt;\"]", function(err, existed) {
+				  assert(typeof err === "undefined" || err === null);
+				  assert(existed === true);
+		      })
+		      .call(done); 
+		  });
+		 
+		  it('can link to a node with an encoded name', function(done) {
+			  var encodedNodeNameSelector = '#root li[nodename="aTestNode"].opened li[nodename="täßt ?&lt;&gt;"]';
+			  var encodedNodeNameOpenSelector = encodedNodeNameSelector +' i.open-icon';
+			  client.url(homeURL).waitForExist('#last-element')
+			  .click("#root li[nodename=\"aTestNode\"] i.jstree-ocl").waitForExist(encodedNodeNameOpenSelector)
+			  .click(encodedNodeNameOpenSelector).waitForExist(encodedNodeNameSelector+' a.jstree-clicked', function(err, existed) {
+				  assert(typeof err === "undefined" || err === null);
+				  assert(existed === true);
+		      })
+		      .call(done);
+		  });
+		  
+	
+		  it('can add a node with a specific node type ', function(done) {
+			  client.url(homeURL);
+			  client.waitForExist('#last-element').click("#root li[nodename=\"aTestNode\"] i.add-icon").waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container', 1000)
+			  .setValue('.node_name_dd_container input', "aFolder")
+			  .addValue('.node_name_dd_container input', 'Return')
+			  .click(".form-group.node-type .select2-chosen").waitForExist('.node_type_dd_container input')
+			  .setValue('.node_type_dd_container input', "sling:Folder")
+			  .addValue('.node_type_dd_container input', 'Return')
+			  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist('#root  li[nodename="aTestNode"] li[nodename="aFolder"][nodetype="sling:Folder"]', function(err, existed) {
+				  assert(typeof err === "undefined" || err === null);
+				  assert(existed === true);
+			  })
+		      .call(done);
+		  });
+		  
+		  it('can add a node with a resource type on the second level ', function(done) {
+			  var nodeName =  "a node with a resource type";
+			  var resourceType =  "test/resource-editor/resource-type";
+			  var resourceTypeSelector = '#root  li[nodename="aTestNode"].opened li[nodename="a node with a resource type"] span.node-type';
+			  client.url(homeURL);
+			  client.waitForExist('#last-element').click("#root li[nodename=\"aTestNode\"] i.add-icon")
+			  .waitForVisible('#addNodeDialog.add-node-finished .node_name_dd_container', 1000)
+			  .setValue('.node_name_dd_container input', nodeName)
+			  .addValue('.node_name_dd_container input', 'Return') // presses the 'return' key
+			  .click(".form-group.resource-type .select2-chosen").waitForExist('.resource_type_dd_container input', 1000)
+			  .setValue('.resource_type_dd_container input', resourceType)
+			  .addValue('.resource_type_dd_container input', 'Return') // presses the 'return' key
+			  .click('#addNodeDialog .btn.btn-primary.submit').waitForExist(resourceTypeSelector, 1000)
+			  .getText(resourceTypeSelector, function(err, text) {
+				  assert(typeof err === "undefined" || err === null);
+				  assert(text === resourceType);
+			  })
+		      .call(done);
+		  });
+		  
+		  it('can rename a node with an encoded name', function(done) {
+			  client = client.url(homeURL);
+			  client.waitForExist('#last-element', function(err) {
+				  client.click("#root li[nodename=\"aTestNode\"] i.jstree-ocl", function(err, res) {
+					  var encodedNodeNameSelector = '#root li[nodename="aTestNode"].opened li[nodename="täßt ?&lt;&gt;"]';
+					  var encodedNodeNameAnchorSelector = encodedNodeNameSelector +' a .node-type';
+	// 				  The open node animation will take longer than 500ms thus setting 2000ms as max.
+					  client.waitForExist(encodedNodeNameAnchorSelector, 2000, function(err, existed) {
+						  client.doubleClick(encodedNodeNameAnchorSelector, function(err) {
+							  var encodedNodeNameInputSelector = encodedNodeNameSelector+' input.jstree-rename-input';
+							  client.waitForExist(encodedNodeNameInputSelector, function(err, existed) {
+								  client.setValue(encodedNodeNameInputSelector, 'täßt2& <>');
+								  client.execute(function() {
+									  $('#root li[nodename="aTestNode"] li[nodename="täßt ?&lt;&gt;"] input.jstree-rename-input').blur();
+									  $('#root li[nodename="aTestNode"] li[nodename="täßt ?&lt;&gt;"] input.jstree-rename-input').blur();
+									});
+								  client.waitForExist('#root li[nodename="aTestNode"].opened li[nodename="täßt2&amp; &lt;&gt;"]', 2000, function(err, existed) {
+									  assert(typeof err === "undefined" || err === null);
+									  assert(true === existed);
+									  
+								  });
 							  });
 						  });
-					  });
+				      })  
 			      })  
-		      })  
-	      })
-	      .call(done);
+		      })
+		      .call(done);
+		  });
+		  it('can delete nodes via multi selection and shortcut', function(done) {
+			  client = client.url(homeURL);
+			  client
+			  .waitForExist('#last-element').click("#root li[nodename=\"aTestNode\"] i.add-icon")
+			  	.waitForVisible('#addNodeDialog.add-node-finished', 1000).click('#addNodeDialog .btn.btn-primary.submit')
+			  	// The open node animation will take longer than 500ms thus setting 2000ms as max.
+			  	.waitForExist('#root li[nodename="aTestNode"].opened', 2000).elements('#root li[nodename="aTestNode"].opened li a .jstree-themeicon', function(err, res) {
+				    client
+			         .moveTo(res.value[0].ELEMENT, 0, 0)
+			         .buttonPress('left')
+			         .keys('Shift')
+			         .moveTo(res.value[1].ELEMENT, 0, 0)
+			         .buttonPress('left');
+	
+				    client.keys('Shift'); // release the Shift key
+				    
+				    // On Mac the ctrl key opens the context menu in the browser
+				    // this is why the Cmd key should be used in this case.
+				    if ('darwin' === process.platform){
+				    	client.keys('Command');
+				    } else {
+				    	client.keys('Control');
+				    }
+			        
+				    client.moveTo(res.value[3].ELEMENT, 0, 0)
+			        .buttonPress('left');
+				    client.keys('NULL'); // release all keys
+			  });
+			  var confirmationOkBtn = 'div.bootbox-confirm div.modal-footer button[data-bb-handler="confirm"]';
+			  var openTestNodeIcon = '#root li[nodename=\"aTestNode\"] i.open-icon';
+			  client.keys('Delete')
+			  .waitForVisible(confirmationOkBtn)
+			  .click(confirmationOkBtn)
+			  .waitForVisible(openTestNodeIcon)
+			  .click(openTestNodeIcon)
+			  .waitForExist('#last-element').elements('#root li[nodename="aTestNode"] li a .jstree-themeicon', function(err, res) {
+	    		  assert(typeof err === "undefined" || err === null);
+				  assert(1 === res.value.length);
+			  });
+		      client.call(done);
+			  
+		  });
+	  });
+
+	  describe('can add a', function(done){
+		  it('String property', function(done) {
+			  addProperty("String", "textarea");
+			  client.call(done);
+		  });
+
+		  it('Path property', function(done) {
+			  addProperty("Path", "input");
+			  client.call(done);
+		  });
+		  
+		  function addProperty(type, editorTagName){
+			  var encodedNodeNameSelector = '#root li[nodename="aTestNode"].opened li[nodename="a node with a resource type"]';
+			  var encodedNodeNameOpenSelector = encodedNodeNameSelector +' i.open-icon';
+			  var addPropertyMenuItemSelector = "#properties .add-property-menu [data-property-type='"+type+"']";
+			  var key="a"+type+"Key";
+			  var value="a "+type+" value";
+			  var propValueEditorSelector = "#addPropertyDialog div[data-property-type='"+type+"'] "+editorTagName;
+			  client.url(homeURL).waitForExist('#last-element')
+			  .click("#root li[nodename=\"aTestNode\"] i.open-icon").waitForExist("#properties .add-property-menu-item", 1000)
+			  .click("#properties .add-property-menu-item").waitForExist(addPropertyMenuItemSelector, 1000)
+			  .click(addPropertyMenuItemSelector).waitForVisible('#new-property-key', 1000)
+			  /* 
+			   * The value is not always set completely the first time for some strange reason so I set it twice.
+			   * ToDo: Find a way to reproduce that and file a bug report. 
+			   * A simple test that fills some input fields on the facebook registration page did not show the problem.
+			   * See: https://groups.google.com/forum/#!topic/webdriverio/b7ohm7tkG7Q
+			   */
+			  .setValue('#new-property-key ', key)
+			  .setValue('#new-property-key ', key)
+			  .setValue(propValueEditorSelector, value)
+			  .click("#addPropertyDialog .btn-primary.submit").waitForExist("label.proplabel[for='"+key+"']", 1000, function(err, existed) {
+				  assert(typeof err === "undefined" || err === null);
+				  assert(existed === true);
+				  client.getValue("#properties div[data-property-name='"+key+"'].row .propinput.property-value", function(err, resultingValue) {
+					  assert(typeof err === "undefined" || err === null);
+					  assert(value === resultingValue);
+				  });
+				  
+		      });
+		  }
 	  });
-	  it('can delete nodes via multi selection and shortcut', function(done) {
-		  client = client.url(homeURL);
-		  client
-		  .waitForExist('#last-element').click("#root li[nodename=\"aTestNode\"] i.add-icon")
-		  	.waitForVisible('#addNodeDialog.add-node-finished', 1000).click('#addNodeDialog .btn.btn-primary.submit')
-		  	// The open node animation will take longer than 500ms thus setting 2000ms as max.
-		  	.waitForExist('#root li[nodename="aTestNode"].opened', 2000).elements('#root li[nodename="aTestNode"].opened li a .jstree-themeicon', function(err, res) {
-			    client
-		         .moveTo(res.value[0].ELEMENT, 0, 0)
-		         .buttonPress('left')
-		         .keys('Shift')
-		         .moveTo(res.value[1].ELEMENT, 0, 0)
-		         .buttonPress('left');
 
-			    client.keys('Shift'); // release the Shift key
-			    
-			    // On Mac the ctrl key opens the context menu in the browser
-			    // this is why the Cmd key should be used in this case.
-			    if ('darwin' === process.platform){
-			    	client.keys('Command');
-			    } else {
-			    	client.keys('Control');
-			    }
-		        
-			    client.moveTo(res.value[3].ELEMENT, 0, 0)
-		        .buttonPress('left');
-			    client.keys('NULL'); // release all keys
+	  describe('can save a String property', function(){
+		  var inputElementSelector = "#properties div[data-property-name='aStringKey'].row .propinput.property-value";
+		  
+		  it('with the icon', function(done) {
+			  var stringValue = "new String value";
+			  setStringFieldValue(client, stringValue);
+			  client.click("#properties div[data-property-name='aStringKey'].row .property-icon.glyphicon-save").waitForExist("div.alert-success.growl-notify", 1000).refresh();
+			  testStringFieldValue(client, stringValue);
+			  client.call(done);
 		  });
-		  var confirmationOkBtn = 'div.bootbox-confirm div.modal-footer button[data-bb-handler="confirm"]';
-		  var openTestNodeIcon = '#root li[nodename=\"aTestNode\"] i.open-icon';
-		  client.keys('Delete')
-		  .waitForVisible(confirmationOkBtn)
-		  .click(confirmationOkBtn)
-		  .waitForVisible(openTestNodeIcon)
-		  .click(openTestNodeIcon)
-		  .waitForExist('#last-element').elements('#root li[nodename="aTestNode"] li a .jstree-themeicon', function(err, res) {
-    		  assert(typeof err === "undefined" || err === null);
-			  assert(1 === res.value.length);
+		  
+		  it('with the shortcut', function(done) {
+			  var stringValue = "3rd String value";
+			  setStringFieldValue(client, stringValue);
+			  // On Mac the ctrl key opens the context menu in the browser
+			  // this is why the Cmd key should be used in this case.
+			  if ('darwin' === process.platform){
+				client.keys('Command');
+				client.keys("s")
+				client.keys('Command'); // releases the key
+			  } else {
+				client.keys('Control');
+				client.keys("s")
+				client.keys('Control'); // releases the key
+			  };
+			  testStringFieldValue(client, stringValue);
+			  client.call(done);
 		  });
-	      client.call(done);
 		  
+		  function setStringFieldValue(client, value){
+			  client.url(homeURL).waitForExist('#last-element')
+			  .click("#root li[nodename=\"aTestNode\"] i.open-icon").waitForExist("#properties label.proplabel[for='aStringKey']", 1000)
+			  .setValue("#properties div[data-property-name='aStringKey'].row .propinput.property-value", value);
+		  }
+		  
+		  function testStringFieldValue(client, value){
+			  client
+			  .waitForExist("div.alert-success.growl-notify", 1000).refresh()
+			  .waitForExist(inputElementSelector, 1000).getValue(inputElementSelector, function(err, resultingValue){
+				  assert(typeof err === "undefined" || err === null);
+				  assert(value === resultingValue);
+			  })
+		  }
 	  });
+	  
+	  describe('can delete a property', function(){
+		  it('with the icon', function(done) {
+			  var value= "aStringKey";
+			  focusInputField(client, value);
+			  client
+			  .click("#properties div[data-property-name='"+value+"'].row .property-icon.glyphicon-remove");
+			  testRemoval(client, value);
+			  client.call(done);
+		  });
+
+		  it('with the shortcut', function(done) {
+			  var value= "aPathKey";
+			  focusInputField(client, value);
 
-	  it('can delete a node with the icon', function(done) {
-		  client = client.url(homeURL);
-		  client.waitForExist('#last-element', function(err) {
-			  client.click('li[nodetype="rep:root"] li[nodename="aTestNode"] a i.remove-icon', function(err, res) {
-				  client.waitForText('div.bootbox-confirm div.bootbox-body', function(err, result, third, fourth){
-					  client.click('div.bootbox-confirm div.modal-footer button[data-bb-handler="confirm"]', function(err, res) {
-						  client.waitForExist('li[nodetype="rep:root"] li[nodename="aTestNode"]', true/*reverse*/, function(err, existed) {
-							  assert(typeof err === "undefined" || err === null);
-							  assert(existed === false);
+			  // On Mac the ctrl key opens the context menu in the browser
+			  // this is why the Cmd key should be used in this case.
+			  if ('darwin' === process.platform){
+				client.keys('Command');
+				client.keys("Delete")
+				client.keys('Command'); // releases the key
+			  } else {
+				client.keys('Control');
+				client.keys("Delete")
+				client.keys('Control'); // releases the key
+			  };
+			  testRemoval(client, value);
+			  client.call(done);
+		  });
+		  
+		  function focusInputField(client, value){
+			  var stringPropertyInputFieldSelector = "#properties div[data-property-name='"+value+"'].row .propinput.property-value";
+			  client.url(homeURL).waitForExist('#last-element')
+			  .click("#root li[nodename=\"aTestNode\"] i.open-icon").waitForExist(stringPropertyInputFieldSelector, 1000)
+			  .click(stringPropertyInputFieldSelector);
+		  }
+		  
+		  function testRemoval(client, value){
+			  client
+			  .waitForVisible("div.bootbox-confirm .btn-primary", 1000)
+			  .click("div.bootbox-confirm .btn-primary").waitForExist("div.alert-success.growl-notify", 1000).refresh()
+			  .waitForExist("#properties div[data-property-name='"+value+"'].row", true/*reverse*/, function(err, existed) {
+				  assert(typeof err === "undefined" || err === null);
+				  assert(existed === false);
+			  })
+		  }
+	  });
+
+	  // specs ("it" functions) are always executed before describe blocks
+	  // that's why this spec is wrapped within a describe block as
+	  // it has to be executed last
+	  describe('can delete the (test) node', function(done){
+		  it('with the icon', function(done) {
+			  client = client.url(homeURL);
+			  client.waitForExist('#last-element', function(err) {
+				  client.click('li[nodetype="rep:root"] li[nodename="aTestNode"] a i.remove-icon', function(err, res) {
+					  client.waitForText('div.bootbox-confirm div.bootbox-body', function(err, result, third, fourth){
+						  client.click('div.bootbox-confirm div.modal-footer button[data-bb-handler="confirm"]', function(err, res) {
+							  client.waitForExist('li[nodetype="rep:root"] li[nodename="aTestNode"]', true/*reverse*/, function(err, existed) {
+								  assert(typeof err === "undefined" || err === null);
+								  assert(existed === false);
+							  });
 						  });
 					  });
-				  });
-		      })  
-	      })
-	      .call(done);
+			      })  
+		      })
+		      .call(done);
+		  });
 	  });
-	
 });
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.