brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [06/16] brooklyn-ui git commit: Add auto-complete for provisioning properties
Date Fri, 18 Mar 2016 11:08:10 GMT
Add auto-complete for provisioning properties


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/commit/e011fe14
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/tree/e011fe14
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-ui/diff/e011fe14

Branch: refs/heads/master
Commit: e011fe149764c723746ccfc00291cc75bbce44e5
Parents: 90cfc6e
Author: Thomas Bouron <thomas.bouron@cloudsoftcorp.com>
Authored: Tue Mar 15 16:05:15 2016 +0000
Committer: Thomas Bouron <thomas.bouron@cloudsoftcorp.com>
Committed: Thu Mar 17 12:49:11 2016 +0000

----------------------------------------------------------------------
 src/main/webapp/assets/css/base.css             |   27 +-
 .../webapp/assets/css/easy-autocomplete.css     |  320 ++++
 src/main/webapp/assets/css/styles.css           |    1 +
 src/main/webapp/assets/js/config.js             |    2 +
 .../assets/js/libs/jquery.easy-autocomplete.js  | 1578 ++++++++++++++++++
 src/main/webapp/assets/js/view/catalog.js       |   11 +-
 src/main/webapp/assets/js/view/home.js          |    9 +-
 .../webapp/assets/js/view/location-wizard.js    |  158 +-
 src/main/webapp/assets/tpl/home/summaries.html  |    7 +-
 .../location-provisioning-entry.html            |    7 +
 .../location-wizard/location-provisioning.html  |   11 +-
 .../tpl/location-wizard/location-type.html      |   30 +-
 12 files changed, 2085 insertions(+), 76 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/css/base.css
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/css/base.css b/src/main/webapp/assets/css/base.css
index c6b17bb..6ccd08b 100644
--- a/src/main/webapp/assets/css/base.css
+++ b/src/main/webapp/assets/css/base.css
@@ -361,17 +361,16 @@ input[type="color"]:focus,.uneditable-input:focus {
     vertical-align: middle;
 }
 
-.addApplication {
+.addSummary {
     border: 1px solid #a1cb8c !important;
     color: #505050 !important;
     background: url(../images/addApplication-plus.png) no-repeat !important;
     padding: 10px 0px 0px 74px !important;
     width: 298px !important;
     height: 201px !important;
-    margin-right: 0;
 }
 
-.addApplication:hover {
+.addSummary:hover {
     border: 1px solid #58a82e !important;
     color: #58a82e !important;
     background: url(../images/addApplication-plus-hover.png) no-repeat
@@ -1488,6 +1487,9 @@ textarea.param-value {
 }
 
 /* Location wizard */
+.location-type-container {
+    overflow: hidden;
+}
 .location-type {
     width: 160px;
     height: 200px;
@@ -1517,16 +1519,22 @@ textarea.param-value {
 .location-type .location-type-title {
     font-size: 30px;
 }
+input.location-other {
+    margin-top: -10px;
+    margin-left: 10px;
+}
 #catalog-add-form .modal-header .close {
     display: none;
 }
 #catalog-add-form .modal-body {
     max-height: none;
+    overflow: visible;
 }
 #catalog-add-form .modal-body .tab-content {
     min-height: 0;
     background-color: transparent !important;
     padding: 0;
+    overflow: visible;
 }
 #catalog-add-form .location-type {
     border-color: #ddd;
@@ -1540,4 +1548,17 @@ textarea.param-value {
 }
 #catalog-add-form .modal-footer {
     background-color: transparent;
+}
+.easy-autocomplete {
+    display: inline;
+}
+.easy-autocomplete-container ul {
+    margin: 0
+}
+.easy-autocomplete-container ul .eac-category {
+    color: #fff;
+    background-color: rgba(120, 180, 70, 0.8);
+}
+button.remove-entry {
+    margin-top: -10px;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/css/easy-autocomplete.css
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/css/easy-autocomplete.css b/src/main/webapp/assets/css/easy-autocomplete.css
new file mode 100755
index 0000000..60a7348
--- /dev/null
+++ b/src/main/webapp/assets/css/easy-autocomplete.css
@@ -0,0 +1,320 @@
+/*
+ * easy-autocomplete
+ * jQuery plugin for autocompletion
+ * 
+ * @author Łukasz Pawełczak (http://github.com/pawelczak)
+ * @version 1.3.3
+ * Copyright MIT License: https://github.com/pawelczak/easy-autocomplete/blob/master/LICENSE.txt
+ */
+
+.easy-autocomplete {
+  position: relative;
+}
+/*.easy-autocomplete input {*/
+  /*border-color: #ccc;*/
+  /*border-radius: 4px;*/
+  /*border-style: solid;*/
+  /*border-width: 1px;*/
+  /*box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset;*/
+  /*color: #555;*/
+  /*float: none;*/
+  /*padding: 6px 12px;*/
+/*}*/
+.easy-autocomplete input:hover, .easy-autocomplete input:focus {
+  box-shadow: none;
+}
+.easy-autocomplete a {
+  display: block;
+}
+.easy-autocomplete.eac-blue-light input:hover, .easy-autocomplete.eac-blue-light input:focus {
+  border-color: #66afe9;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6);
+}
+.easy-autocomplete.eac-blue-light ul {
+  border-color: #66afe9;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6);
+}
+.easy-autocomplete.eac-blue-light ul li, .easy-autocomplete.eac-blue-light ul .eac-category {
+  border-color: #66afe9;
+}
+.easy-autocomplete.eac-blue-light ul li.selected, .easy-autocomplete.eac-blue-light ul .eac-category.selected {
+  background-color: #ecf5fc;
+}
+.easy-autocomplete.eac-green-light input:hover, .easy-autocomplete.eac-green-light input:focus {
+  border-color: #41DB00;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(146, 237, 107, 0.6);
+}
+.easy-autocomplete.eac-green-light ul {
+  border-color: #41DB00;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(146, 237, 107, 0.6);
+}
+.easy-autocomplete.eac-green-light ul li, .easy-autocomplete.eac-green-light ul .eac-category {
+  border-color: #41DB00;
+}
+.easy-autocomplete.eac-green-light ul li.selected, .easy-autocomplete.eac-green-light ul .eac-category.selected {
+  background-color: #9eff75;
+}
+.easy-autocomplete.eac-red-light input:hover, .easy-autocomplete.eac-red-light input:focus {
+  border-color: #ff5b5b;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(255, 90, 90, 0.6);
+}
+.easy-autocomplete.eac-red-light ul {
+  border-color: #ff5b5b;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(255, 90, 90, 0.6);
+}
+.easy-autocomplete.eac-red-light ul li, .easy-autocomplete.eac-red-light ul .eac-category {
+  border-color: #ff5b5b;
+}
+.easy-autocomplete.eac-red-light ul li.selected, .easy-autocomplete.eac-red-light ul .eac-category.selected {
+  background-color: #ff8e8e;
+}
+.easy-autocomplete.eac-yellow-light input:hover, .easy-autocomplete.eac-yellow-light input:focus {
+  border-color: #ffdb00;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(255, 231, 84, 0.6);
+}
+.easy-autocomplete.eac-yellow-light ul {
+  border-color: #ffdb00;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(255, 231, 84, 0.6);
+}
+.easy-autocomplete.eac-yellow-light ul li, .easy-autocomplete.eac-yellow-light ul .eac-category {
+  border-color: #ffdb00;
+}
+.easy-autocomplete.eac-yellow-light ul li.selected, .easy-autocomplete.eac-yellow-light ul .eac-category.selected {
+  background-color: #ffe233;
+}
+.easy-autocomplete.eac-dark-light input:hover, .easy-autocomplete.eac-dark-light input:focus {
+  border-color: #333;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(55, 55, 55, 0.6);
+}
+.easy-autocomplete.eac-dark-light ul {
+  border-color: #333;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(55, 55, 55, 0.6);
+}
+.easy-autocomplete.eac-dark-light ul li, .easy-autocomplete.eac-dark-light ul .eac-category {
+  border-color: #333;
+}
+.easy-autocomplete.eac-dark-light ul li.selected, .easy-autocomplete.eac-dark-light ul .eac-category.selected {
+  background-color: #4d4d4d;
+  color: #fff;
+}
+.easy-autocomplete.eac-dark {
+  color: #fff;
+}
+.easy-autocomplete.eac-dark input {
+  background-color: #404040;
+  border-radius: 4px;
+  box-shadow: 0;
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-dark input:hover, .easy-autocomplete.eac-dark input:focus {
+  border-color: #333;
+  box-shadow: 0;
+}
+.easy-autocomplete.eac-dark ul {
+  border-color: #333;
+}
+.easy-autocomplete.eac-dark ul li, .easy-autocomplete.eac-dark ul .eac-category {
+  background-color: #404040;
+  border-color: #333;
+}
+.easy-autocomplete.eac-dark ul li.selected, .easy-autocomplete.eac-dark ul .eac-category.selected {
+  background-color: #737373;
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-dark-glass {
+  color: #fff;
+}
+.easy-autocomplete.eac-dark-glass input {
+  background-color: rgba(0, 0, 0, 0.8);
+  border-radius: 4px;
+  box-shadow: 0;
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-dark-glass input:hover, .easy-autocomplete.eac-dark-glass input:focus {
+  border-color: rgba(0, 0, 0, 0.8);
+  box-shadow: 0;
+}
+.easy-autocomplete.eac-dark-glass ul {
+  border-color: rgba(0, 0, 0, 0.8);
+}
+.easy-autocomplete.eac-dark-glass ul li, .easy-autocomplete.eac-dark-glass ul .eac-category {
+  background-color: rgba(0, 0, 0, 0.8);
+  border-color: rgba(0, 0, 0, 0.8);
+}
+.easy-autocomplete.eac-dark-glass ul li.selected, .easy-autocomplete.eac-dark-glass ul .eac-category.selected {
+  background-color: rgba(64, 64, 64, 0.8);
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-dark-glass ul li:last-child, .easy-autocomplete.eac-dark-glass ul .eac-category:last-child {
+  border-radius: 0 0 4px 4px;
+}
+.easy-autocomplete.eac-blue {
+  color: #fff;
+}
+.easy-autocomplete.eac-blue input {
+  background-color: #6d9ed1;
+  border-radius: 4px;
+  box-shadow: 0;
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-blue input::-webkit-input-placeholder {
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-blue input:-moz-placeholder {
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-blue input::-moz-placeholder {
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-blue input:-ms-input-placeholder {
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-blue input:hover, .easy-autocomplete.eac-blue input:focus {
+  border-color: #5A91CB;
+  box-shadow: 0;
+}
+.easy-autocomplete.eac-blue ul {
+  border-color: #5A91CB;
+}
+.easy-autocomplete.eac-blue ul li, .easy-autocomplete.eac-blue ul .eac-category {
+  background-color: #6d9ed1;
+  border-color: #5A91CB;
+}
+.easy-autocomplete.eac-blue ul li.selected, .easy-autocomplete.eac-blue ul .eac-category.selected {
+  background-color: #94b8dd;
+  color: #f6f6f6;
+}
+.easy-autocomplete.eac-yellow {
+  color: #333;
+}
+.easy-autocomplete.eac-yellow input {
+  background-color: #ffdb7e;
+  border-color: #333;
+  border-radius: 4px;
+  box-shadow: 0;
+  color: #333;
+}
+.easy-autocomplete.eac-yellow input:hover, .easy-autocomplete.eac-yellow input:focus {
+  border-color: #333;
+  box-shadow: 0;
+}
+.easy-autocomplete.eac-yellow ul {
+  border-color: #333;
+}
+.easy-autocomplete.eac-yellow ul li, .easy-autocomplete.eac-yellow ul .eac-category {
+  background-color: #ffdb7e;
+  border-color: #333;
+}
+.easy-autocomplete.eac-yellow ul li.selected, .easy-autocomplete.eac-yellow ul .eac-category.selected {
+  background-color: #ffe9b1;
+  color: #333;
+}
+.easy-autocomplete.eac-purple {
+  color: #333;
+}
+.easy-autocomplete.eac-purple input {
+  background-color: #d6d1e7;
+  border-color: #b8afd5;
+  box-shadow: 0;
+  color: #333;
+}
+.easy-autocomplete.eac-purple input:hover, .easy-autocomplete.eac-purple input:focus {
+  border-color: #333;
+  box-shadow: 0;
+}
+.easy-autocomplete.eac-purple ul {
+  border-color: #333;
+}
+.easy-autocomplete.eac-purple ul li, .easy-autocomplete.eac-purple ul .eac-category {
+  background-color: #d6d1e7;
+  border-color: #333;
+}
+.easy-autocomplete.eac-purple ul li.selected, .easy-autocomplete.eac-purple ul .eac-category.selected {
+  background-color: #ebe8f3;
+  color: #333;
+}
+.easy-autocomplete.eac-bootstrap input {
+  border-color: #ccc;
+  border-radius: 4px;
+  border-style: solid;
+  border-width: 1px;
+  color: #555;
+  padding: 6px 12px;
+}
+
+.easy-autocomplete-container {
+  left: 0;
+  position: absolute;
+  width: 100%;
+  z-index: 2;
+}
+.easy-autocomplete-container ul {
+  background: none repeat scroll 0 0 #ffffff;
+  border-top: 1px dotted #ccc;
+  display: none;
+  margin-top: 0;
+  padding-bottom: 0;
+  padding-left: 0;
+  position: relative;
+  top: -1px;
+}
+.easy-autocomplete-container ul li, .easy-autocomplete-container ul .eac-category {
+  background: inherit;
+  border-color: #ccc;
+  border-image: none;
+  border-style: solid;
+  border-width: 0 1px;
+  display: block;
+  font-size: 14px;
+  font-weight: normal;
+  padding: 4px 12px;
+}
+.easy-autocomplete-container ul li:last-child {
+  border-radius: 0 0 2px 2px;
+  border-width: 0 1px 1px;
+}
+.easy-autocomplete-container ul li.selected {
+  background: none repeat scroll 0 0 #ebebeb;
+  cursor: pointer;
+}
+.easy-autocomplete-container ul li.selected div {
+  font-weight: normal;
+}
+.easy-autocomplete-container ul li div {
+  display: block;
+  font-weight: normal;
+  word-break: break-all;
+}
+.easy-autocomplete-container ul li b {
+  font-weight: bold;
+}
+.easy-autocomplete-container ul .eac-category {
+  font-color: #aaa;
+  font-style: italic;
+}
+
+.eac-description .eac-item span {
+  color: #aaa;
+  font-style: italic;
+  font-size: 0.9em;
+}
+
+.eac-icon-left .eac-item img {
+  margin-right: 4px;
+  max-height: 30px;
+}
+
+.eac-icon-right .eac-item {
+  margin-top: 8px;
+  min-height: 24px;
+  position: relative;
+}
+.eac-icon-right .eac-item img {
+  margin-left: 4px;
+  max-height: 30px;
+  position: absolute;
+  right: -4px;
+  top: -8px;
+}
+
+/*# sourceMappingURL=easy-autocomplete.css.map */

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/css/styles.css
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/css/styles.css b/src/main/webapp/assets/css/styles.css
index bfb5b40..75878c6 100644
--- a/src/main/webapp/assets/css/styles.css
+++ b/src/main/webapp/assets/css/styles.css
@@ -18,4 +18,5 @@
 */
 @import url('bootstrap.css');
 @import url('jquery.dataTables.css');
+@import url('easy-autocomplete.css');
 @import url('base.css');
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/js/config.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/config.js b/src/main/webapp/assets/js/config.js
index 94d6385..9ddb244 100644
--- a/src/main/webapp/assets/js/config.js
+++ b/src/main/webapp/assets/js/config.js
@@ -36,6 +36,7 @@ require.config({
         "jquery-slideto":"util/jquery.slideto",
         "jquery-wiggle":"libs/jquery.wiggle.min",
         "jquery-ba-bbq":"libs/jquery.ba-bbq.min",
+        "jquery-easy-autocomplete": "libs/jquery.easy-autocomplete",
         
         "moment":"libs/moment",
         "handlebars":"libs/handlebars-1.0.rc.1",
@@ -79,6 +80,7 @@ require.config({
         "jquery-slideto": { deps: [ "jquery" ] },
         "jquery-wiggle": { deps: [ "jquery" ] },
         "jquery-ba-bbq": { deps: [ "jquery" ] },
+        "jquery-easy-autocomplete": { deps: [ "jquery" ] },
         "handlebars": { deps: [ "jquery" ] },
         "codemirror":{
             exports:"CodeMirror"

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/js/libs/jquery.easy-autocomplete.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/libs/jquery.easy-autocomplete.js b/src/main/webapp/assets/js/libs/jquery.easy-autocomplete.js
new file mode 100755
index 0000000..ad30a5a
--- /dev/null
+++ b/src/main/webapp/assets/js/libs/jquery.easy-autocomplete.js
@@ -0,0 +1,1578 @@
+/*
+ * easy-autocomplete
+ * jQuery plugin for autocompletion
+ * 
+ * @author Łukasz Pawełczak (http://github.com/pawelczak)
+ * @version 1.3.3
+ * Copyright MIT License: https://github.com/pawelczak/easy-autocomplete/blob/master/LICENSE.txt
+ */
+
+/*
+ * EasyAutocomplete - Configuration 
+ */
+var EasyAutocomplete = (function(scope){
+
+	scope.Configuration = function Configuration(options) {
+		var defaults = {
+			data: "list-required",
+			url: "list-required",
+			dataType: "json",
+
+			listLocation: function(data) {
+				return data;
+			},
+
+			xmlElementName: "",
+
+			getValue: function(element) {
+				return element;
+			},
+
+			autocompleteOff: true,
+
+			placeholder: false,
+
+			ajaxCallback: function() {},
+
+			matchResponseProperty: false,
+
+			list: {
+				sort: {
+					enabled: false,
+					method: function(a, b) {
+						a = defaults.getValue(a);
+						b = defaults.getValue(b);
+						if (a < b) {
+							return -1;
+						}
+						if (a > b) {
+							return 1;
+						}
+						return 0;
+					}
+				},
+
+				maxNumberOfElements: 6,
+
+				hideOnEmptyPhrase: true,
+
+				match: {
+					enabled: false,
+					caseSensitive: false,
+					method: function(a, b) {
+						a = defaults.getValue(a);
+						b = defaults.getValue(b);
+
+						if (a === b){
+							return true;
+						}  
+						return false;
+					}
+				},
+
+				showAnimation: {
+					type: "normal", //normal|slide|fade
+					time: 400,
+					callback: function() {}
+				},
+
+				hideAnimation: {
+					type: "normal",
+					time: 400,
+					callback: function() {}
+				},
+
+				/* Events */
+				onClickEvent: function() {},
+				onSelectItemEvent: function() {},
+				onLoadEvent: function() {},
+				onChooseEvent: function() {},
+				onKeyEnterEvent: function() {},
+				onMouseOverEvent: function() {},
+				onMouseOutEvent: function() {},	
+				onShowListEvent: function() {},
+				onHideListEvent: function() {}
+			},
+
+			highlightPhrase: true,
+
+			theme: "",
+
+			cssClasses: "",
+
+			minCharNumber: 0,
+
+			requestDelay: 0,
+
+			adjustWidth: true,
+
+			ajaxSettings: {},
+
+			preparePostData: function(data, inputPhrase) {return data;},
+
+			loggerEnabled: true,
+
+			template: "",
+
+			categoriesAssigned: false,
+
+			categories: [{
+				maxNumberOfElements: 4
+			}]
+
+		};
+		
+
+		this.get = function(propertyName) {
+			return defaults[propertyName];
+		};
+
+		this.equals = function(name, value) {
+			if (isAssigned(name)) {
+				if (defaults[name] === value) {
+					return true;
+				}
+			} 
+			
+			return false;
+		};
+
+		this.checkDataUrlProperties = function() {
+			if (defaults.url === "list-required" && defaults.data === "list-required") {
+				return false;
+			}
+			return true;
+		};
+		this.checkRequiredProperties = function() {
+			for (var propertyName in defaults) {
+				if (defaults[propertyName] === "required") {
+					logger.error("Option " + propertyName + " must be defined");
+					return false;
+				}
+			}
+			return true;
+		};
+
+		this.printPropertiesThatDoesntExist = function(consol, optionsToCheck) {
+			printPropertiesThatDoesntExist(consol, optionsToCheck);
+		};
+
+
+		prepareDefaults();
+
+		mergeOptions();
+
+		if (defaults.loggerEnabled === true) {
+			printPropertiesThatDoesntExist(console, options);	
+		}
+
+		addAjaxSettings();
+
+		processAfterMerge();
+		function prepareDefaults() {
+
+			if (options.dataType === "xml") {
+				
+				if (!options.getValue) {
+
+					options.getValue = function(element) {
+						return $(element).text();
+					};
+				}
+
+				
+				if (!options.list) {
+
+					options.list = {};
+				} 
+
+				if (!options.list.sort) {
+					options.list.sort = {};
+				}
+
+
+				options.list.sort.method = function(a, b) {
+					a = options.getValue(a);
+					b = options.getValue(b);
+					if (a < b) {
+						return -1;
+					}
+					if (a > b) {
+						return 1;
+					}
+					return 0;
+				};
+
+				if (!options.list.match) {
+					options.list.match = {};
+				}
+
+				options.list.match.method = function(a, b) {
+					a = options.getValue(a);
+					b = options.getValue(b);
+
+					if (a === b){
+						return true;
+					}  
+					return false;
+				};
+
+			}
+			if (options.categories !== undefined && options.categories instanceof Array) {
+
+				var categories = [];
+
+				for (var i = 0, length = options.categories.length; i < length; i += 1) { 
+
+					var category = options.categories[i];
+
+					for (var property in defaults.categories[0]) {
+
+						if (category[property] === undefined) {
+							category[property] = defaults.categories[0][property];
+						}
+					}
+
+					categories.push(category);
+				}
+
+				options.categories = categories;
+			}
+		}
+
+		function mergeOptions() {
+
+			defaults = mergeObjects(defaults, options);
+
+			function mergeObjects(source, target) {
+				var mergedObject = source || {};
+
+				for (var propertyName in source) {
+					if (target[propertyName] !== undefined && target[propertyName] !== null) {
+
+						if (typeof target[propertyName] !== "object" || 
+								target[propertyName] instanceof Array) {
+							mergedObject[propertyName] = target[propertyName];
+						} else {
+							mergeObjects(source[propertyName], target[propertyName]);
+						}
+					}
+				}
+			
+				/* If data is an object */
+				if (target.data !== undefined && target.data !== null && typeof target.data === "object") {
+					mergedObject.data = target.data;
+				}
+
+				return mergedObject;
+			}
+		}	
+
+
+		function processAfterMerge() {
+			
+			if (defaults.url !== "list-required" && typeof defaults.url !== "function") {
+				var defaultUrl = defaults.url;
+				defaults.url = function() {
+					return defaultUrl;
+				};
+			}
+
+			if (defaults.ajaxSettings.url !== undefined && typeof defaults.ajaxSettings.url !== "function") {
+				var defaultUrl = defaults.ajaxSettings.url;
+				defaults.ajaxSettings.url = function() {
+					return defaultUrl;
+				};
+			}
+
+			if (typeof defaults.listLocation === "string") {
+				var defaultlistLocation = defaults.listLocation;
+
+				if (defaults.dataType.toUpperCase() === "XML") {
+					defaults.listLocation = function(data) {
+						return $(data).find(defaultlistLocation);
+					};
+				} else {
+					defaults.listLocation = function(data) {
+						return data[defaultlistLocation];
+					};	
+				}
+			}
+
+			if (typeof defaults.getValue === "string") {
+				var defaultsGetValue = defaults.getValue;
+				defaults.getValue = function(element) {
+					return element[defaultsGetValue];
+				};
+			}
+
+			if (options.categories !== undefined) {
+				defaults.categoriesAssigned = true;
+			}
+
+		}
+
+		function addAjaxSettings() {
+
+			if (options.ajaxSettings !== undefined && typeof options.ajaxSettings === "object") {
+				defaults.ajaxSettings = options.ajaxSettings;
+			} else {
+				defaults.ajaxSettings = {};	
+			}
+			
+		}
+
+		function isAssigned(name) {
+			if (defaults[name] !== undefined && defaults[name] !== null) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+		function printPropertiesThatDoesntExist(consol, optionsToCheck) {
+			
+
+			checkPropertiesIfExist(defaults, optionsToCheck);
+
+			function checkPropertiesIfExist(source, target) {
+				for(var property in target) {
+					if (source[property] === undefined) {
+						consol.log("Property '" + property + "' does not exist in EasyAutocomplete options API.");		
+					}
+
+					if (typeof source[property] === "object" && !externalObject(property)) {
+						checkPropertiesIfExist(source[property], target[property]);
+					}
+				}	
+			}
+
+			function externalObject(property) {
+				var notTestedObjects = ["ajaxSettings", "template"];
+
+				Array.prototype.contains = function(obj) {
+					var i = this.length;
+				    while (i--) {
+				        if (this[i] === obj) {
+				            return true;
+				        }
+				    }
+				    return false;
+				};
+
+				return notTestedObjects.contains(property);
+			}
+		}
+	};
+
+	return scope;
+
+})(EasyAutocomplete || {});
+
+
+/*
+ * EasyAutocomplete - Logger 
+ */
+var EasyAutocomplete = (function(scope){
+	
+	scope.Logger = function Logger() {
+
+		this.error = function(message) {
+			console.log("ERROR: " + message);
+		};
+
+		this.warning = function(message) {
+			console.log("WARNING: " + message);
+		};
+	};
+
+	return scope;
+
+})(EasyAutocomplete || {});
+	
+
+/*
+ * EasyAutocomplete - Constans
+ */
+var EasyAutocomplete = (function(scope){	
+	
+	scope.Constans = function Constans() {
+		var constants = {
+			CONTAINER_CLASS: "easy-autocomplete-container",
+			CONTAINER_ID: "eac-container-",
+
+			WRAPPER_CSS_CLASS: "easy-autocomplete"
+		};
+
+		this.getValue = function(propertyName) {
+			return constants[propertyName];
+		};
+
+	};
+
+	return scope;
+
+})(EasyAutocomplete || {});
+
+/*
+ * EasyAutocomplete - ListBuilderService 
+ *
+ * @author Łukasz Pawełczak 
+ *
+ */
+var EasyAutocomplete = (function(scope) {
+
+	scope.ListBuilderService = function ListBuilderService(configuration, proccessResponseData) {
+
+
+		this.init = function(data) {
+			var listBuilder = [],
+				builder = {};
+
+			builder.data = configuration.get("listLocation")(data);
+			builder.getValue = configuration.get("getValue");
+			builder.maxListSize = configuration.get("list").maxNumberOfElements;
+
+				
+			listBuilder.push(builder);
+
+			return listBuilder;
+		};
+
+		this.updateCategories = function(listBuilder, data) {
+			
+			if (configuration.get("categoriesAssigned")) {
+
+				listBuilder = [];
+
+				for(var i = 0; i < configuration.get("categories").length; i += 1) {
+
+					var builder = convertToListBuilder(configuration.get("categories")[i], data);
+
+					listBuilder.push(builder);
+				}
+
+			} 
+
+			return listBuilder;
+		};
+
+		this.convertXml = function(listBuilder) {
+			if(configuration.get("dataType").toUpperCase() === "XML") {
+
+				for(var i = 0; i < listBuilder.length; i += 1) {
+					listBuilder[i].data = convertXmlToList(listBuilder[i]);
+				}
+			}
+
+			return listBuilder;
+		};
+
+		this.processData = function(listBuilder, inputPhrase) {
+
+			for(var i = 0, length = listBuilder.length; i < length; i+=1) {
+				listBuilder[i].data = proccessResponseData(configuration, listBuilder[i], inputPhrase);
+			}
+
+			return listBuilder;
+		};
+
+		this.checkIfDataExists = function(listBuilders) {
+
+			for(var i = 0, length = listBuilders.length; i < length; i += 1) {
+
+				if (listBuilders[i].data !== undefined && listBuilders[i].data instanceof Array) {
+					if (listBuilders[i].data.length > 0) {
+						return true;
+					}
+				} 
+			}
+
+			return false;
+		};
+
+
+		function convertToListBuilder(category, data) {
+
+			var builder = {};
+
+			if(configuration.get("dataType").toUpperCase() === "XML") {
+
+				builder = convertXmlToListBuilder();
+			} else {
+
+				builder = convertDataToListBuilder();
+			}
+			
+
+			if (category.header !== undefined) {
+				builder.header = category.header;
+			}
+
+			if (category.maxNumberOfElements !== undefined) {
+				builder.maxNumberOfElements = category.maxNumberOfElements;
+			}
+
+			if (configuration.get("list").maxNumberOfElements !== undefined) {
+
+				builder.maxListSize = configuration.get("list").maxNumberOfElements;
+			}
+
+			if (category.getValue !== undefined) {
+
+				if (typeof category.getValue === "string") {
+					var defaultsGetValue = category.getValue;
+					builder.getValue = function(element) {
+						return element[defaultsGetValue];
+					};
+				} else if (typeof category.getValue === "function") {
+					builder.getValue = category.getValue;
+				}
+
+			} else {
+				builder.getValue = configuration.get("getValue");	
+			}
+			
+
+			return builder;
+
+
+			function convertXmlToListBuilder() {
+
+				var builder = {},
+					listLocation;
+
+				if (category.xmlElementName !== undefined) {
+					builder.xmlElementName = category.xmlElementName;
+				}
+
+				if (category.listLocation !== undefined) {
+
+					listLocation = category.listLocation;
+				} else if (configuration.get("listLocation") !== undefined) {
+
+					listLocation = configuration.get("listLocation");
+				}
+
+				if (listLocation !== undefined) {
+					if (typeof listLocation === "string") {
+						builder.data = $(data).find(listLocation);
+					} else if (typeof listLocation === "function") {
+
+						builder.data = listLocation(data);
+					}
+				} else {
+
+					builder.data = data;
+				}
+
+				return builder;
+			}
+
+
+			function convertDataToListBuilder() {
+
+				var builder = {};
+
+				if (category.listLocation !== undefined) {
+
+					if (typeof category.listLocation === "string") {
+						builder.data = data[category.listLocation];
+					} else if (typeof category.listLocation === "function") {
+						builder.data = category.listLocation(data);
+					}
+				} else {
+					builder.data = data;
+				}
+
+				return builder;
+			}
+		}
+
+		function convertXmlToList(builder) {
+			var simpleList = [];
+
+			if (builder.xmlElementName === undefined) {
+				builder.xmlElementName = configuration.get("xmlElementName");
+			}
+
+
+			$(builder.data).find(builder.xmlElementName).each(function() {
+				simpleList.push(this);
+			});
+
+			return simpleList;
+		}
+
+	};
+
+	return scope;
+
+})(EasyAutocomplete || {});
+
+
+/*
+ * EasyAutocomplete - Data proccess module
+ *
+ * Process list to display:
+ * - sort 
+ * - decrease number to specific number
+ * - show only matching list
+ *
+ */
+var EasyAutocomplete = (function(scope) {
+
+	scope.proccess = function proccessData(config, listBuilder, phrase) {
+
+		var list = listBuilder.data,
+			inputPhrase = phrase;//TODO REFACTOR
+
+		list = findMatch(list, inputPhrase);
+		list = reduceElementsInList(list);
+		list = sort(list);
+
+		return list;
+
+
+		function findMatch(list, phrase) {
+			var preparedList = [],
+				value = "";
+
+			if (config.get("list").match.enabled) {
+
+				for(var i = 0, length = list.length; i < length; i += 1) {
+
+					value = config.get("getValue")(list[i]);
+					
+					if (!config.get("list").match.caseSensitive) {
+
+						if (typeof value === "string") {
+							value = value.toLowerCase();	
+						}
+						
+						phrase = phrase.toLowerCase();
+					}
+					if (value.search(phrase) > -1) {
+						preparedList.push(list[i]);
+					}
+					
+				}
+
+			} else {
+				preparedList = list;
+			}
+
+			return preparedList;
+		}
+
+		function reduceElementsInList(list) {
+			if (listBuilder.maxNumberOfElements !== undefined && list.length > listBuilder.maxNumberOfElements) {
+				list = list.slice(0, listBuilder.maxNumberOfElements);
+			}
+
+			return list;
+		}
+
+		function sort(list) {
+			if (config.get("list").sort.enabled) {
+				list.sort(config.get("list").sort.method);
+			}
+
+			return list;
+		}
+		
+	};
+
+	return scope;
+
+
+
+})(EasyAutocomplete || {});
+
+
+/*
+ * EasyAutocomplete - Template 
+ *
+ * 
+ *
+ */
+var EasyAutocomplete = (function(scope){
+
+	scope.Template = function Template(options) {
+
+
+		var genericTemplates = {
+			basic: {
+				type: "basic",
+				method: function(element) { return element; },
+				cssClass: ""
+			},
+			description: {
+				type: "description",
+				fields: {
+					description: "description"
+				},
+				method: function(element) {	return element + " - description"; },
+				cssClass: "eac-description"
+			},
+			iconLeft: {
+				type: "iconLeft",
+				fields: {
+					icon: ""
+				},
+				method: function(element) {
+					return element;
+				},
+				cssClass: "eac-icon-left"
+			},
+			iconRight: {
+				type: "iconRight",
+				fields: {
+					iconSrc: ""
+				},
+				method: function(element) {
+					return element;
+				},
+				cssClass: "eac-icon-right"
+			},
+			links: {
+				type: "links",
+				fields: {
+					link: ""
+				},
+				method: function(element) {
+					return element;
+				},
+				cssClass: ""
+			},
+			custom: {
+				type: "custom",
+				method: function() {},
+				cssClass: ""
+			}
+		},
+
+
+
+		/*
+		 * Converts method with {{text}} to function
+		 */
+		convertTemplateToMethod = function(template) {
+
+
+			var _fields = template.fields,
+				buildMethod;
+
+			if (template.type === "description") {
+
+				buildMethod = genericTemplates.description.method; 
+
+				if (typeof _fields.description === "string") {
+					buildMethod = function(elementValue, element) {
+						return elementValue + " - <span>" + element[_fields.description] + "</span>";
+					};					
+				} else if (typeof _fields.description === "function") {
+					buildMethod = function(elementValue, element) {
+						return elementValue + " - <span>" + _fields.description(element) + "</span>";
+					};	
+				}
+
+				return buildMethod;
+			}
+
+			if (template.type === "iconRight") {
+
+				if (typeof _fields.iconSrc === "string") {
+					buildMethod = function(elementValue, element) {
+						return elementValue + "<img class='eac-icon' src='" + element[_fields.iconSrc] + "' />" ;
+					};					
+				} else if (typeof _fields.iconSrc === "function") {
+					buildMethod = function(elementValue, element) {
+						return elementValue + "<img class='eac-icon' src='" + _fields.iconSrc(element) + "' />" ;
+					};
+				}
+
+				return buildMethod;
+			}
+
+
+			if (template.type === "iconLeft") {
+
+				if (typeof _fields.iconSrc === "string") {
+					buildMethod = function(elementValue, element) {
+						return "<img class='eac-icon' src='" + element[_fields.iconSrc] + "' />" + elementValue;
+					};					
+				} else if (typeof _fields.iconSrc === "function") {
+					buildMethod = function(elementValue, element) {
+						return "<img class='eac-icon' src='" + _fields.iconSrc(element) + "' />" + elementValue;
+					};
+				}
+
+				return buildMethod;
+			}
+
+			if(template.type === "links") {
+
+				if (typeof _fields.link === "string") {
+					buildMethod = function(elementValue, element) {
+						return "<a href='" + element[_fields.link] + "' >" + elementValue + "</a>";
+					};					
+				} else if (typeof _fields.link === "function") {
+					buildMethod = function(elementValue, element) {
+						return "<a href='" + _fields.link(element) + "' >" + elementValue + "</a>";
+					};
+				}
+
+				return buildMethod;
+			}
+
+
+			if (template.type === "custom") {
+
+				return template.method;
+			}
+
+			return genericTemplates.basic.method;
+
+		},
+
+
+		prepareBuildMethod = function(options) {
+			if (!options || !options.type) {
+
+				return genericTemplates.basic.method;
+			}
+
+			if (options.type && genericTemplates[options.type]) {
+
+				return convertTemplateToMethod(options);
+			} else {
+
+				return genericTemplates.basic.method;
+			}
+
+		},
+
+		templateClass = function(options) {
+			var emptyStringFunction = function() {return "";};
+
+			if (!options || !options.type) {
+
+				return emptyStringFunction;
+			}
+
+			if (options.type && genericTemplates[options.type]) {
+				return (function () { 
+					var _cssClass = genericTemplates[options.type].cssClass;
+					return function() { return _cssClass;};
+				})();
+			} else {
+				return emptyStringFunction;
+			}
+		};
+
+
+		this.getTemplateClass = templateClass(options);
+
+		this.build = prepareBuildMethod(options);
+
+
+	};
+
+	return scope;
+
+})(EasyAutocomplete || {});
+
+
+/*
+ * EasyAutocomplete - jQuery plugin for autocompletion
+ *
+ */
+var EasyAutocomplete = (function(scope) {
+
+	
+	scope.main = function Core($input, options) {
+				
+		var module = {
+				name: "EasyAutocomplete",
+				shortcut: "eac"
+			};
+
+		var consts = new scope.Constans(),
+			config = new scope.Configuration(options),
+			logger = new scope.Logger(),
+			template = new scope.Template(options.template),
+			listBuilderService = new scope.ListBuilderService(config, scope.proccess),
+			checkParam = config.equals,
+
+			$field = $input, 
+			$container = "",
+			elementsList = [],
+			selectedElement = -1,
+			requestDelayTimeoutId;
+
+		scope.consts = consts;
+
+		this.getConstants = function() {
+			return consts;
+		};
+
+		this.getConfiguration = function() {
+			return config;
+		};
+
+		this.getContainer = function() {
+			return $container;
+		};
+
+		this.getSelectedItemIndex = function() {
+			return selectedElement;
+		};
+
+		this.getItemData = function(index) {
+
+			if (elementsList.length < index || elementsList[index] === undefined) {
+				return -1;
+			} else {
+				return elementsList[index];
+			}
+		};
+
+		this.getSelectedItemData = function() {
+			return this.getItemData(selectedElement);
+		};
+
+		this.build = function() {
+			prepareField();
+		};
+
+		this.init = function() {
+			init();
+		};
+		function init() {
+
+			if ($field.length === 0) {
+				logger.error("Input field doesn't exist.");
+				return;
+			}
+
+			if (!config.checkDataUrlProperties()) {
+				logger.error("One of options variables 'data' or 'url' must be defined.");
+				return;
+			}
+
+			if (!config.checkRequiredProperties()) {
+				logger.error("Will not work without mentioned properties.");
+				return;
+			}
+
+
+			prepareField();
+			bindEvents();	
+
+		}
+		function prepareField() {
+
+				
+			if ($field.parent().hasClass(consts.getValue("WRAPPER_CSS_CLASS"))) {
+				removeContainer();
+				removeWrapper();
+			} 
+			
+			createWrapper();
+			createContainer();	
+
+			$container = $("#" + getContainerId());
+			if (config.get("placeholder")) {
+				$field.attr("placeholder", config.get("placeholder"));
+			}
+
+
+			function createWrapper() {
+				var $wrapper = $("<div>"),
+					classes = consts.getValue("WRAPPER_CSS_CLASS");
+
+			
+				if (config.get("theme") && config.get("theme") !== "") {
+					classes += " eac-" + config.get("theme");
+				}
+
+				if (config.get("cssClasses") && config.get("cssClasses") !== "") {
+					classes += " " + config.get("cssClasses");
+				}
+
+				if (template.getTemplateClass() !== "") {
+					classes += " " + template.getTemplateClass();
+				}
+				
+
+				$wrapper
+					.addClass(classes);
+				$field.wrap($wrapper);
+
+
+				if (config.get("adjustWidth") === true) {
+					adjustWrapperWidth();	
+				}
+				
+
+			}
+
+			function adjustWrapperWidth() {
+				var fieldWidth = $field.outerWidth();
+
+				$field.parent().css("width", fieldWidth);				
+			}
+
+			function removeWrapper() {
+				$field.unwrap();
+			}
+
+			function createContainer() {
+				var $elements_container = $("<div>").addClass(consts.getValue("CONTAINER_CLASS"));
+
+				$elements_container
+						.attr("id", getContainerId())
+						.prepend($("<ul>"));
+
+
+				(function() {
+
+					$elements_container
+						/* List show animation */
+						.on("show.eac", function() {
+
+							switch(config.get("list").showAnimation.type) {
+
+								case "slide":
+									var animationTime = config.get("list").showAnimation.time,
+										callback = config.get("list").showAnimation.callback;
+
+									$elements_container.find("ul").slideDown(animationTime, callback);
+								break;
+
+								case "fade":
+									var animationTime = config.get("list").showAnimation.time,
+										callback = config.get("list").showAnimation.callback;
+
+									$elements_container.find("ul").fadeIn(animationTime), callback;
+								break;
+
+								default:
+									$elements_container.find("ul").show();
+								break;
+							}
+
+							config.get("list").onShowListEvent();
+							
+						})
+						/* List hide animation */
+						.on("hide.eac", function() {
+
+							switch(config.get("list").hideAnimation.type) {
+
+								case "slide":
+									var animationTime = config.get("list").hideAnimation.time,
+										callback = config.get("list").hideAnimation.callback;
+
+									$elements_container.find("ul").slideUp(animationTime, callback);
+								break;
+
+								case "fade":
+									var animationTime = config.get("list").hideAnimation.time,
+										callback = config.get("list").hideAnimation.callback;
+
+									$elements_container.find("ul").fadeOut(animationTime, callback);
+								break;
+
+								default:
+									$elements_container.find("ul").hide();
+								break;
+							}
+
+							config.get("list").onHideListEvent();
+
+						})
+						.on("selectElement.eac", function() {
+							$elements_container.find("ul li").removeClass("selected");
+							$elements_container.find("ul li").eq(selectedElement).addClass("selected");
+
+							config.get("list").onSelectItemEvent();
+						})
+						.on("loadElements.eac", function(event, listBuilders, phrase) {
+			
+
+							var $item = "",
+								$listContainer = $elements_container.find("ul");
+
+							$listContainer
+								.empty()
+								.detach();
+
+							elementsList = [];
+							var counter = 0;
+							for(var builderIndex = 0, listBuildersLength = listBuilders.length; builderIndex < listBuildersLength; builderIndex += 1) {
+
+								var listData = listBuilders[builderIndex].data;
+
+								if (listData.length === 0) {
+									continue;
+								}
+
+								if (listBuilders[builderIndex].header !== undefined && listBuilders[builderIndex].header.length > 0) {
+									$listContainer.append("<div class='eac-category' >" + listBuilders[builderIndex].header + "</div>");
+								}
+
+								for(var i = 0, listDataLength = listData.length; i < listDataLength && counter < listBuilders[builderIndex].maxListSize; i += 1) {
+									$item = $("<li><div class='eac-item'></div></li>");
+									
+
+									(function() {
+										var j = i,
+											itemCounter = counter,
+											elementsValue = listBuilders[builderIndex].getValue(listData[j]);
+
+										$item.find(" > div")
+											.on("click", function() {
+
+												$field.val(elementsValue).trigger("change");
+
+												selectedElement = itemCounter;
+												selectElement(itemCounter);
+
+												config.get("list").onClickEvent();
+												config.get("list").onChooseEvent();
+											})
+											.mouseover(function() {
+
+												selectedElement = itemCounter;
+												selectElement(itemCounter);	
+
+												config.get("list").onMouseOverEvent();
+											})
+											.mouseout(function() {
+												config.get("list").onMouseOutEvent();
+											})
+											.html(template.build(highlight(elementsValue, phrase), listData[j]));
+									})();
+
+									$listContainer.append($item);
+									elementsList.push(listData[i]);
+									counter += 1;
+								}
+							}
+
+							$elements_container.append($listContainer);
+
+							config.get("list").onLoadEvent();
+						});
+
+				})();
+
+				$field.after($elements_container);
+			}
+
+			function removeContainer() {
+				$field.next("." + consts.getValue("CONTAINER_CLASS")).remove();
+			}
+
+			function highlight(string, phrase) {
+
+				if(config.get("highlightPhrase") && phrase !== "") {
+					return highlightPhrase(string, phrase);	
+				} else {
+					return string;
+				}
+					
+			}
+
+			function escapeRegExp(str) {
+				return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+ 			}
+
+			function highlightPhrase(string, phrase) {
+				var escapedPhrase = escapeRegExp(phrase);
+				return (string + "").replace(new RegExp("(" + escapedPhrase + ")", "gi") , "<b>$1</b>");
+			}
+
+
+
+		}
+		function getContainerId() {
+			
+			var elementId = $field.attr("id");
+
+			elementId = consts.getValue("CONTAINER_ID") + elementId;
+
+			return elementId;
+		}
+		function bindEvents() {
+
+			bindAllEvents();
+			
+
+			function bindAllEvents() {
+				if (checkParam("autocompleteOff", true)) {
+					removeAutocomplete();
+				}
+
+				bindKeyup();
+				bindKeydown();
+				bindKeypress();
+				bindFocus();
+				bindBlur();
+			}
+
+			function bindKeyup() {
+				$field
+				.off("keyup")
+				.keyup(function(event) {
+
+					switch(event.keyCode) {
+
+						case 27:
+
+							hideContainer();
+							loseFieldFocus();
+						break;
+
+						case 38:
+
+							event.preventDefault();
+
+							if(elementsList.length > 0 && selectedElement > 0) {
+
+								selectedElement -= 1;
+
+								$field.val(config.get("getValue")(elementsList[selectedElement]));
+
+								selectElement(selectedElement);
+
+							}						
+						break;
+
+						case 40:
+
+							event.preventDefault();
+
+							if(elementsList.length > 0 && selectedElement < elementsList.length - 1) {
+
+								selectedElement += 1;
+
+								$field.val(config.get("getValue")(elementsList[selectedElement]));
+
+								selectElement(selectedElement);
+								
+							}
+
+						break;
+
+						default:
+
+							if (event.keyCode > 40 || event.keyCode === 8) {
+
+								var inputPhrase = $field.val();
+
+								if (!(config.get("list").hideOnEmptyPhrase === true && event.keyCode === 8 && inputPhrase === "")) {
+
+									if (config.get("requestDelay") > 0) {
+										if (requestDelayTimeoutId !== undefined) {
+											clearTimeout(requestDelayTimeoutId);
+										}
+
+										requestDelayTimeoutId = setTimeout(function () { loadData(inputPhrase);}, config.get("requestDelay"));
+									} else {
+										loadData(inputPhrase);
+									}
+
+								} else {
+									hideContainer();
+								}
+								
+							}
+							
+
+						break;
+					}
+				
+
+					function loadData(inputPhrase) {
+
+
+						if (inputPhrase.length < config.get("minCharNumber")) {
+							return;
+						}
+
+
+						if (config.get("data") !== "list-required") {
+
+							var data = config.get("data");
+
+							var listBuilders = listBuilderService.init(data);
+
+							listBuilders = listBuilderService.updateCategories(listBuilders, data);
+							
+							listBuilders = listBuilderService.processData(listBuilders, inputPhrase);
+
+							loadElements(listBuilders, inputPhrase);
+
+							if ($field.parent().find("li").length > 0) {
+								showContainer();	
+							} else {
+								hideContainer();
+							}
+
+						}
+
+						var settings = createAjaxSettings();
+
+						if (settings.url === undefined || settings.url === "") {
+							settings.url = config.get("url");
+						}
+
+						if (settings.dataType === undefined || settings.dataType === "") {
+							settings.dataType = config.get("dataType");
+						}
+
+
+						if (settings.url !== undefined && settings.url !== "list-required") {
+
+							settings.url = settings.url(inputPhrase);
+
+							settings.data = config.get("preparePostData")(settings.data, inputPhrase);
+
+							$.ajax(settings) 
+								.done(function(data) {
+
+									var listBuilders = listBuilderService.init(data);
+
+									listBuilders = listBuilderService.updateCategories(listBuilders, data);
+									
+									listBuilders = listBuilderService.convertXml(listBuilders);
+									if (checkInputPhraseMatchResponse(inputPhrase, data)) {
+
+										listBuilders = listBuilderService.processData(listBuilders, inputPhrase);
+
+										loadElements(listBuilders, inputPhrase);	
+																				
+									}
+
+									if (listBuilderService.checkIfDataExists(listBuilders) && $field.parent().find("li").length > 0) {
+										showContainer();	
+									} else {
+										hideContainer();
+									}
+
+									config.get("ajaxCallback")();
+
+								})
+								.fail(function() {
+									logger.warning("Fail to load response data");
+								})
+								.always(function() {
+
+								});
+						}
+
+						
+
+						function createAjaxSettings() {
+
+							var settings = {},
+								ajaxSettings = config.get("ajaxSettings") || {};
+
+							for (var set in ajaxSettings) {
+								settings[set] = ajaxSettings[set];
+							}
+
+							return settings;
+						}
+
+						function checkInputPhraseMatchResponse(inputPhrase, data) {
+
+							if (config.get("matchResponseProperty") !== false) {
+								if (typeof config.get("matchResponseProperty") === "string") {
+									return (data[config.get("matchResponseProperty")] === inputPhrase);
+								}
+
+								if (typeof config.get("matchResponseProperty") === "function") {
+									return (config.get("matchResponseProperty")(data) === inputPhrase);
+								}
+
+								return true;
+							} else {
+								return true;
+							}
+
+						}
+
+					}
+
+
+				});
+			}
+
+			function bindKeydown() {
+				$field
+					.on("keydown", function(evt) {
+	        		    evt = evt || window.event;
+	        		    var keyCode = evt.keyCode;
+	        		    if (keyCode === 38) {
+	        		        suppressKeypress = true; 
+	        		        return false;
+	        		    }
+		        	})
+					.keydown(function(event) {
+
+						if (event.keyCode === 13 && selectedElement > -1) {
+
+							$field.val(config.get("getValue")(elementsList[selectedElement]));
+
+							config.get("list").onKeyEnterEvent();
+							config.get("list").onChooseEvent();
+
+							selectedElement = -1;
+							hideContainer();
+
+							event.preventDefault();
+						}
+					});
+			}
+
+			function bindKeypress() {
+				$field
+				.off("keypress");
+			}
+
+			function bindFocus() {
+				$field.focus(function() {
+
+					if ($field.val() !== "" && elementsList.length > 0) {
+						
+						selectedElement = -1;
+						showContainer();	
+					}
+									
+				});
+			}
+
+			function bindBlur() {
+				$field.blur(function() {
+					setTimeout(function() { 
+						
+						selectedElement = -1;
+						hideContainer();
+					}, 250);
+				});
+			}
+
+			function removeAutocomplete() {
+				$field.attr("autocomplete","off");
+			}
+
+		}
+
+		function showContainer() {
+			$container.trigger("show.eac");
+		}
+
+		function hideContainer() {
+			$container.trigger("hide.eac");
+		}
+
+		function selectElement(index) {
+			
+			$container.trigger("selectElement.eac", index);
+		}
+
+		function loadElements(list, phrase) {
+			$container.trigger("loadElements.eac", [list, phrase]);
+		}
+
+		function loseFieldFocus() {
+			$field.trigger("blur");
+		}
+
+
+	};
+
+
+	scope.easyAutocompleteHandles = [];
+
+	scope.inputHasId = function(input) {
+
+		if($(input).attr("id") !== undefined && $(input).attr("id").length > 0) {
+			return true;
+		} else {
+			return false;
+		}
+
+	};
+
+	scope.assignRandomId = function(input) {
+
+		var fieldId = "";
+
+		do {
+			fieldId = "eac-" + Math.floor(Math.random() * 10000);		
+		} while ($("#" + fieldId).length !== 0);
+		
+		elementId = scope.consts.getValue("CONTAINER_ID") + fieldId;
+
+		$(input).attr("id", fieldId);
+ 
+	};
+
+	return scope;
+
+})(EasyAutocomplete || {});
+
+
+$.fn.easyAutocomplete = function(options) {
+
+	return this.each(function() {
+		var $this = $(this),
+			eacHandle = new EasyAutocomplete.main($this, options);
+
+		if (!EasyAutocomplete.inputHasId($this)) {
+			EasyAutocomplete.assignRandomId($this);
+		}
+
+		eacHandle.init();
+
+		EasyAutocomplete.easyAutocompleteHandles[$this.attr("id")] = eacHandle;
+
+	});	
+};
+
+$.fn.getSelectedItemIndex = function() {
+
+	var inputId = $(this).attr("id");
+
+	if (inputId !== undefined) {
+		return EasyAutocomplete.easyAutocompleteHandles[inputId].getSelectedItemIndex();
+	}
+
+	return -1;
+};
+
+$.fn.getItemData = function(index) {
+
+	var inputId = $(this).attr("id");
+
+	if (inputId !== undefined && index > -1) {
+		return EasyAutocomplete.easyAutocompleteHandles[inputId].getItemData(index);
+	}
+
+	return -1;
+};
+
+$.fn.getSelectedItemData = function() {
+
+	var inputId = $(this).attr("id");
+
+	if (inputId !== undefined) {
+		return EasyAutocomplete.easyAutocompleteHandles[inputId].getSelectedItemData();
+	}
+
+	return -1;
+};

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/js/view/catalog.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/view/catalog.js b/src/main/webapp/assets/js/view/catalog.js
index e10914b..0493355 100644
--- a/src/main/webapp/assets/js/view/catalog.js
+++ b/src/main/webapp/assets/js/view/catalog.js
@@ -188,12 +188,11 @@ define([
     function newLocationForm(addView, addViewParent) {
         return new LocationWizard({
             onLocationCreated: function(wizard, data) {
-                addViewParent.loadAccordionItem("locations", data.id);
+                addViewParent.loadAccordionItem("locations", data.id, true);
             },
             onFinish: function(wizard, data) {
                 addView.clearWithHtml( "Added: "+data.id+". Loading..." );
-            },
-            step: 1
+            }
         }).render();
     }
 
@@ -473,7 +472,7 @@ define([
             this.loadAccordionItem("locations", id);
         },
 
-        loadAccordionItem: function (kind, id) {
+        loadAccordionItem: function (kind, id, noRedirect) {
             if (!this.accordion[kind]) {
                 console.error("No accordion for: " + kind);
             } else {
@@ -499,8 +498,8 @@ define([
                             }
                         }
                         // TODO could look in collection for any starting with ID
-                        if (model) {
-                            Backbone.history.navigate("/v1/catalog/"+kind+"/"+id);
+                        if (model && !noRedirect) {
+                            Backbone.history.navigate("/v1/catalog/" + kind + "/" + id);
                             activeDetailsView = kind;
                             accordion.activeCid = model.cid;
                             accordion.options.onItemSelected(kind, model);

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/js/view/home.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/view/home.js b/src/main/webapp/assets/js/view/home.js
index 5c9db1d..dc8e084 100644
--- a/src/main/webapp/assets/js/view/home.js
+++ b/src/main/webapp/assets/js/view/home.js
@@ -163,20 +163,19 @@ define([
             if (this.options.offline || (this.options.cautionOverlay && this.options.cautionOverlay.warningActive)) {
                 // don't show wizard
             } else {
-                var wizard = new LocationWizard({
+                this._modal = new LocationWizard({
                     onLocationCreated: function(wizard, data) {
                         that.options.locations.fetch({reset:true});
                     },
                     onFinish: function(wizard) {
-                        that._modal.close();
+                        that.$(".add-app #modal-container .modal").modal('hide');
                     },
                     isModal: true
                 });
-                this._modal = wizard;
-                this.$(".add-app #modal-container").html(wizard.render().el);
+                this.$(".add-app #modal-container").html(this._modal.render().el);
                 this.$(".add-app #modal-container .modal")
                     .on("hidden",function () {
-                        wizard.close();
+                        that._modal.close();
                         that.options.locations.fetch({reset:true});
                     }).modal('show');
             }

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/js/view/location-wizard.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/view/location-wizard.js b/src/main/webapp/assets/js/view/location-wizard.js
index 8fac452..7cf516f 100644
--- a/src/main/webapp/assets/js/view/location-wizard.js
+++ b/src/main/webapp/assets/js/view/location-wizard.js
@@ -18,19 +18,21 @@
  */
 
 define([
-    'underscore', 'backbone', 'jquery', 'brooklyn-utils', 'model/location',
+    'underscore', 'backbone', 'jquery',
+    'brooklyn-utils', 'model/location',
     'text!tpl/location-wizard/modal.html',
     'text!tpl/location-wizard/location-type.html',
     'text!tpl/location-wizard/location-configuration.html',
     'text!tpl/location-wizard/location-provisioning.html',
-    'text!tpl/app-add-wizard/edit-config-entry.html',
-], function(_, Backbone, $, Util, Location, ModalHtml, LocationTypeHtml, LocationConfigurationHtml, LocationProvisioningHtml, EditConfigEntryHtml) {
+    'text!tpl/location-wizard/location-provisioning-entry.html',
+
+    'jquery-easy-autocomplete'
+], function(_, Backbone, $, Util, Location, ModalHtml, LocationTypeHtml, LocationConfigurationHtml, LocationProvisioningHtml, LocationProvisioningEntry) {
     var Wizard = Backbone.View.extend({
         template: _.template(ModalHtml),
         events: {
             'click .location-wizard-previous': 'previousStep',
             'click .location-wizard-next': 'nextStep',
-            'click .location-wizard-inject': 'inject',
             'click .location-wizard-save': 'save',
             'click .location-wizard-save-and-reset': 'saveAndReset'
         },
@@ -63,10 +65,6 @@ define([
 
             this.actions = [
                 {
-                    label: 'Inject in YAML',
-                    class: 'location-wizard-inject'
-                },
-                {
                     label: 'Save and add another',
                     class: 'location-wizard-save-and-reset'
                 },
@@ -155,16 +153,11 @@ define([
             return text && text.charAt(0).toUpperCase() + text.slice(1);
         },
 
-        inject: function() {
-            // TODO: Inject YAML location into the composer
-        },
-
         save: function(callback) {
             var that = this;
 
-            var view = this.steps[this.step].view;
-            if (view instanceof LocationProvisioning) {
-                view.setProvisioningProperties();
+            if (this.currentView instanceof LocationProvisioning) {
+                this.currentView.setProvisioningProperties();
             }
 
             this.location.save()
@@ -188,6 +181,8 @@ define([
             var that = this;
             this.save(function() {
                 that.step = 0;
+                that.type = '';
+                that.location = new Location.Model;
                 that.renderStep();
             });
         },
@@ -279,8 +274,9 @@ define([
                     type: 'select',
                     values: {
                         'jclouds:aws-ec2': 'Amazon',
-                        'jclouds:softlayer': 'Softlayer',
+                        'jclouds:google-compute-engine': 'Google',
                         'jclouds:openstack': 'Openstack',
+                        'jclouds:softlayer': 'Softlayer',
                         other: 'Other'
                     }
                 },
@@ -290,7 +286,7 @@ define([
                     type: 'text',
                     require: {
                         spec: [
-                            'jclouds:aws-ec2', 'jclouds:softlayer'
+                            'jclouds:aws-ec2', 'jclouds:google-compute-engine', 'jclouds:softlayer'
                         ]
                     },
                     disable: {
@@ -351,12 +347,12 @@ define([
                     type: 'password'
                 },
                 {
-                    id: 'publicKeyFile',
+                    id: 'privateKeyFile',
                     label: 'Private key',
                     type: 'textarea'
                 },
                 {
-                    id: 'publicKeyPassphrase',
+                    id: 'privateKeyPassphrase',
                     label: 'Private key passphrase',
                     type: 'test'
                 }
@@ -385,12 +381,12 @@ define([
                     type: 'password'
                 },
                 {
-                    id: 'publicKeyFile',
+                    id: 'privateKeyFile',
                     label: 'Private key',
                     type: 'textarea'
                 },
                 {
-                    id: 'publicKeyPassphrase',
+                    id: 'privateKeyPassphrase',
                     label: 'Private key passphrase',
                     type: 'test'
                 },
@@ -440,6 +436,7 @@ define([
                 _.each(field.values, function(value, key) {
                     input.append($('<option>').attr('value', key).html(value));
                 });
+                $('<input>').attr('name', field.id + '-other').insertAfter(input);
             }
 
             var value = '';
@@ -449,7 +446,7 @@ define([
                 value = this.wizard.location.get('config')[field.id];
             }
 
-            return $('<div>').addClass('control-group')
+            var $div =  $('<div>').addClass('control-group')
                 .append($('<label>')
                     .addClass('control-label deploy-label')
                     .attr('for', field.id)
@@ -464,6 +461,15 @@ define([
                         id: field.id,
                         name: field.id
                     }));
+
+            if (field.type === 'select' && _.has(field.values, 'other')) {
+                $div.append($('<input>')
+                    .attr('name', field.id + '-other')
+                    .addClass('location-other')
+                    .hide());
+            }
+
+            return $div;
         },
 
         onChange: function(event) {
@@ -471,13 +477,25 @@ define([
             var enable = true;
             var $elm = this.$(event.currentTarget);
 
+            if ($elm.attr('name') === 'spec') {
+                if ($elm.val() === 'other') {
+                    this.$('input[name=spec-other]').show();
+                } else {
+                    this.$('input[name=spec-other]').hide();
+                }
+            }
+
             // Update the location object based on the field values
-            if (_.contains(['name', 'spec'], $elm.attr('name'))) {
-                this.wizard.location.set($elm.attr('name'), $elm.val());
-            } else {
-                var config = {};
-                config[$elm.attr('name')] = $elm.data('list') ? $elm.val().split("\n") : $elm.val();
-                this.wizard.location.set('config', _.extend(this.wizard.location.get('config'), config));
+            if ($elm.val() !== '') {
+                if (_.contains(['name', 'spec'], $elm.attr('name'))) {
+                    this.wizard.location.set($elm.attr('name'), $elm.val());
+                } else if ($elm.attr('name') === 'spec-other') {
+                    this.wizard.location.set('spec', $elm.val());
+                } else {
+                    var config = {};
+                    config[$elm.attr('name')] = $elm.data('list') ? $elm.val().split("\n") : $elm.val();
+                    this.wizard.location.set('config', _.extend(this.wizard.location.get('config'), config));
+                }
             }
 
             this.$('input, select, textarea').each(function() {
@@ -517,11 +535,45 @@ define([
         className: 'location-wizard-body',
         template: _.template(LocationProvisioningHtml),
         events: {
-            'click #remove-config': 'removeProvisioningProperty',
-            'click #add-config': 'addProvisioningProperty'
+            'click .remove-entry': 'removeEntry',
+            'click .add-entry': 'addEntry'
         },
         wizard: null,
 
+        vmOptions: [
+            'minCore',
+            'minRam',
+            'osFamily',
+            'osVersionRegex',
+            'os64Bit',
+            'imageId',
+            'imageNameRegex',
+            'hardwareId',
+            'inboundPorts',
+            'securityGroups',
+            'domainName',
+            'userMetadata',
+            'machineCreateAttempts',
+            'destroyOnFailure'
+        ],
+        osOptions: [
+            'user',
+            'password',
+            'loginUser',
+            'privateKeyFile',
+            'privateKeyPassphrase',
+            'publicKeyFile',
+            'openIptables',
+            'installDevUrandom',
+            'useJcloudsSshInit'
+
+        ],
+        templateOptions: [
+            'subnetId',
+            'mapNewVolumeToDeviceName',
+            'securityGroupIds'
+        ],
+
         initialize: function() {
             this.wizard = this.options.wizard;
         },
@@ -534,19 +586,51 @@ define([
 
         setProvisioningProperties: function() {
             var config = {};
-            this.$('.app-add-wizard-config-entry').each(function() {
-                config[$(this).find('input[name=key]').val()] = $(this).find('input[name=value]').val();
+            this.$('.control-group').each(function() {
+                if ($(this).find('input.entry-key').val() !== '' && $(this).find('input.entry-value').val() !== '') {
+                    config[$(this).find('input.entry-key').val()] = $(this).find('input.entry-value').val();
+                }
             });
             this.wizard.location.set('config', _.extend(this.wizard.location.get('config'), config));
         },
 
-        addProvisioningProperty:function (event) {
-            var $row = _.template(EditConfigEntryHtml, {});
-            $(event.currentTarget).parent().prev().append($row);
+        addEntry:function (event) {
+            var that = this;
+            var $entry = $(_.template(LocationProvisioningEntry, {}));
+            $(event.currentTarget).prev().append($entry);
+
+            setTimeout(function() {
+                $entry.find('input.entry-key').easyAutocomplete({
+                    list: {
+                        match: {
+                            enabled: true
+                        }
+                    },
+                    categories: [
+                        {
+                            listLocation: 'vmOptions',
+                            header: 'VM Creation'
+                        },
+                        {
+                            listLocation: 'osOptions',
+                            header: 'OS Setup'
+                        },
+                        {
+                            listLocation: 'templateOptions',
+                            header: 'Template Options'
+                        }
+                    ],
+                    data: {
+                        vmOptions: that.vmOptions,
+                        osOptions: that.osOptions,
+                        templateOptions: that.templateOptions
+                    }
+                });
+            }, 100);
         },
 
-        removeProvisioningProperty:function (event) {
-            $(event.currentTarget).parent().parent().remove();
+        removeEntry:function (event) {
+            $(event.currentTarget).parent().remove();
         }
     });
 

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/tpl/home/summaries.html
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/tpl/home/summaries.html b/src/main/webapp/assets/tpl/home/summaries.html
index 6b02377..3e5d46a 100644
--- a/src/main/webapp/assets/tpl/home/summaries.html
+++ b/src/main/webapp/assets/tpl/home/summaries.html
@@ -30,10 +30,13 @@ under the License.
     templates available
   </div></div>
    -->
+  <% if (locations.size() > 0) { %>
   <div class="roundedSummary"><div class="roundedSummaryText">
     <span class="big"><%- locations.size() %></span> location<%- locations.size()==1 ? "" : "s" %> configured<br/>
     <!-- with <span class="big">14</span> active machines  -->
-    <button class="addLocation" type="button">add location</button>
   </div></div>
+  <% } else { %>
+  <button class="addSummary addLocation roundedSummary" type="button">add location</button>
+  <% } %>
 
-  <button class="roundedSummary addApplication addApplication" type="button">add application</button>
\ No newline at end of file
+  <button class="addSummary addApplication roundedSummary" type="button">add application</button>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/tpl/location-wizard/location-provisioning-entry.html
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/tpl/location-wizard/location-provisioning-entry.html b/src/main/webapp/assets/tpl/location-wizard/location-provisioning-entry.html
new file mode 100644
index 0000000..56eeb3e
--- /dev/null
+++ b/src/main/webapp/assets/tpl/location-wizard/location-provisioning-entry.html
@@ -0,0 +1,7 @@
+<div class="control-group">
+    <input class="entry-key input-medium" type="text" placeholder="Key" />
+    <input class="entry-value input-medium" type="text" placeholder="Value" />
+    <button class="btn btn-info btn-mini remove-entry" type="button">
+        <i class="icon-minus-sign"></i>
+    </button>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/tpl/location-wizard/location-provisioning.html
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/tpl/location-wizard/location-provisioning.html b/src/main/webapp/assets/tpl/location-wizard/location-provisioning.html
index c0d4834..3585b24 100644
--- a/src/main/webapp/assets/tpl/location-wizard/location-provisioning.html
+++ b/src/main/webapp/assets/tpl/location-wizard/location-provisioning.html
@@ -18,13 +18,6 @@ under the License.
 -->
 
 <div class="tab-content">
-    <div class="control-group">
-        <div class="app-add-wizard-create-entity-label-newline deploy-label">Configuration</div>
-        <table class="config-table controls" cellspacing="5">
-        </table>
-        <div>
-            <button id="add-config" class="btn btn-mini btn-info">
-                Add Configuration</button>
-        </div>
-    </div>
+    <div class="entries-group"></div>
+    <button class="btn btn-mini btn-info add-entry">Add Configuration</button>
 </div>

http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/e011fe14/src/main/webapp/assets/tpl/location-wizard/location-type.html
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/tpl/location-wizard/location-type.html b/src/main/webapp/assets/tpl/location-wizard/location-type.html
index b4a607f..ccfa2ed 100644
--- a/src/main/webapp/assets/tpl/location-wizard/location-type.html
+++ b/src/main/webapp/assets/tpl/location-wizard/location-type.html
@@ -18,23 +18,25 @@ under the License.
 -->
 
 <div class="tab-content">
-    <div class="location-type" data-type="cloud">
-        <div>
-            <i class="fa fa-cloud"></i>
-            <p class="location-type-title">Cloud</p>
+    <div class="location-type-container">
+        <div class="location-type" data-type="cloud">
+            <div>
+                <i class="fa fa-cloud"></i>
+                <p class="location-type-title">Cloud</p>
 
+            </div>
         </div>
-    </div>
-    <div class="location-type" data-type="localhost">
-        <div>
-            <i class="fa fa-laptop"></i>
-            <p class="location-type-title">Localhost</p>
+        <div class="location-type" data-type="localhost">
+            <div>
+                <i class="fa fa-laptop"></i>
+                <p class="location-type-title">Localhost</p>
+            </div>
         </div>
-    </div>
-    <div class="location-type" data-type="byon">
-        <div>
-            <i class="fa fa-file-o"></i>
-            <p class="location-type-title">BYON</p>
+        <div class="location-type" data-type="byon">
+            <div>
+                <i class="fa fa-file-o"></i>
+                <p class="location-type-title">BYON</p>
+            </div>
         </div>
     </div>
 </div>


Mime
View raw message