Commits

gfouquet committed 278e00b

[Issue 1793] Added the missing handler on delete Cust Field button
Removed "value" attr on a div because of which Backbone did not
correctly received events.
* * *
[Mystery Issue] Could not rename Cust Field option since rev c64dcd2f9022

Comments (0)

Files changed (14)

tm/tm.web/src/main/java/org/squashtest/csp/tm/web/internal/controller/administration/CustomFieldAdministrationController.java

 	
 	@RequestMapping(value = "/{customFieldId}", method = RequestMethod.DELETE)
 	@ResponseBody
-	public void deleteCustomField( @PathVariable Long customFieldId) {
+	public void deleteCustomField( @PathVariable long customFieldId) {
 		customFieldManagerService.deleteCustomField(customFieldId);
 	}
 }

tm/tm.web/src/main/java/org/squashtest/csp/tm/web/internal/controller/customfield/CustomFieldController.java

 	@RequestMapping(value = "/{customFieldId}", method = RequestMethod.GET)
 	public String showCustomFieldModificationPage(@PathVariable Long customFieldId, Model model) {
 		CustomField customField = customFieldManager.findById(customFieldId);
+		
 		if (customField.getInputType().equals(InputType.DROPDOWN_LIST)) {
 			SingleSelectField cuf = customFieldManager.findSingleSelectFieldById(customFieldId);
 			model.addAttribute(CUSTOM_FIELD, cuf);
 	 */
 	@RequestMapping(value = "/{customFieldId}/defaultValue", method = RequestMethod.POST, params = { "value" })
 	@ResponseBody
+	@ResponseStatus(value = HttpStatus.NO_CONTENT)
 	public void changeDefaultValue(@PathVariable long customFieldId, @RequestParam("value") String defaultValue) {
 		customFieldManager.changeDefaultValue(customFieldId, defaultValue);
 	}
 	 */
 	@RequestMapping(value = "/{customFieldId}/options/{optionLabel}/label", method = RequestMethod.POST, params = { "value" })
 	@ResponseBody
+	@ResponseStatus(value = HttpStatus.NO_CONTENT)
 	public void changeOptionLabel(@PathVariable long customFieldId, @PathVariable String optionLabel,
 			@RequestParam("value") String newLabel) {
-		try{
-		customFieldManager.changeOptionLabel(customFieldId, optionLabel, newLabel);
-		}catch(DomainException e){
+		try {
+			customFieldManager.changeOptionLabel(customFieldId, optionLabel, newLabel);
+		} catch (DomainException e) {
 			e.setObjectName("rename-cuf-option");
 			throw e;
 		}
 	}
-	
+
 	/**
 	 * Changes the code of the concerned custom-field's option
 	 * 
 	 */
 	@RequestMapping(value = "/{customFieldId}/options/{optionLabel}/code", method = RequestMethod.POST, params = { "value" })
 	@ResponseBody
+	@ResponseStatus(value = HttpStatus.NO_CONTENT)
 	public void changeOptionCode(@PathVariable long customFieldId, @PathVariable String optionLabel,
 			@RequestParam("value") String newCode) {
-		try{
-		customFieldManager.changeOptionCode(customFieldId, optionLabel, newCode);
-		}catch(DomainException e){
+		try {
+			customFieldManager.changeOptionCode(customFieldId, optionLabel, newCode);
+		} catch (DomainException e) {
 			e.setObjectName("change-cuf-option");
 			throw e;
 		}
 	 * 
 	 * @param customFieldId
 	 *            : the id of the concerned custom-field
-	 * @param option : the new option
+	 * @param option
+	 *            : the new option
 	 */
 	@RequestMapping(value = "/{customFieldId}/options/new", method = RequestMethod.POST)
 	@ResponseBody
-	public void addOption(@PathVariable long customFieldId, @Valid @ModelAttribute("new-cuf-option") CustomFieldOption option) {
-		try{customFieldManager.addOption(customFieldId, option);}
-		catch(DomainException e){
+	@ResponseStatus(value = HttpStatus.NO_CONTENT)
+	public void addOption(@PathVariable long customFieldId,
+			@Valid @ModelAttribute("new-cuf-option") CustomFieldOption option) {
+		try {
+			customFieldManager.addOption(customFieldId, option);
+		} catch (DomainException e) {
 			e.setObjectName("new-cuf-option");
 			throw e;
 		}
 	 */
 	@RequestMapping(value = "/{customFieldId}/options/{optionLabel}", method = RequestMethod.DELETE)
 	@ResponseBody
+	@ResponseStatus(value = HttpStatus.NO_CONTENT)
 	public void removeOption(@PathVariable long customFieldId, @PathVariable String optionLabel) {
 		customFieldManager.removeOption(customFieldId, optionLabel);
 	}
 	@RequestMapping(value = "/{customFieldId}/options/positions", method = RequestMethod.POST, params = { "itemIds[]",
 			"newIndex" })
 	@ResponseBody
+	@ResponseStatus(value = HttpStatus.NO_CONTENT)
 	public void changeOptionsPositions(@PathVariable long customFieldId, @RequestParam int newIndex,
 			@RequestParam("itemIds[]") List<String> optionsLabels) {
 		customFieldManager.changeOptionsPositions(customFieldId, newIndex, optionsLabels);
 	}
+
+	@RequestMapping(method = RequestMethod.DELETE)
+	@ResponseStatus(value = HttpStatus.NO_CONTENT)
+	public @ResponseBody void deleteCustomField(@PathVariable long customFieldId) {
+		customFieldManager.deleteCustomField(customFieldId);
+	}
 }

tm/tm.web/src/main/webapp/WEB-INF/jsp/page/ieo/ieo-execute-execution.jsp

 				parent.squashtm.ieomanager.fillRightPane(url);
 				return false;				
 			});
-
 			
 			$("#bugtracker-section-div a").live('click', function(){
 				$(this).attr('target', "${bugLinkTarget}");

tm/tm.web/src/main/webapp/WEB-INF/templates/custom-field-modification.html

 <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-transitional-thymeleaf-spring3-3.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
 <head>
-  <title th:text="#{title.customField}">Espace detail champ personnalisé</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-  <script th:remove="all" type="text/javascript" src="../../scripts/lib/jquery/jquery-1.8.2.js"></script>
-  <script th:remove="all" type="text/javascript" src="../../js/thymol.js"></script>
-  <script type="text/javascript" th:inline="javascript">
-  	var squashtm = {};
-  	squashtm.app = {
-  		contextRoot : /*[[ @{/} ]]*/"http://localhost:8080/squash",
-  		cfMod : {
-  			customFieldUrl : /*[[ @{'/custom-fields/'+${customField.id}} ]]*/"http://localhost:8080/custom-fields/1",
-  			richEditPlaceHolder: /*[[#{rich-edit.placeholder}]]*/ '(Click to edit ...)',
-  			confirmMandatoryTitle : /*[[#{title.confirmMandatory}]]*/ 'Confirm mandatory',
-  			confirmMandatoryMessage : /*[[#{message.confirmMandatory}]]*/ 'Do you confirm the change to mandatory ? This change has bad consequences on your health !',
-  			confirmLabel : /*[[#{label.Confirm}]]*/ 'Confirm',
-  			okLabel: /*[[ #{label.Ok} ]]*/ 'Ok',
-			cancelLabel: /*[[ #{label.Cancel} ]]*/ 'Cancel',
-			informationPanelLabel: /*[[#{label.CUFInformation}]]*/ 'Custom-Field informations',
-			popupErrorTitle : /*[[#{popup.title.error}]]*/ 'Error',
-			mandatoryNeedsDefaultMessage : /*[[#{message.mandatoryNeedsDefault}]]*/ 'The custom field cannot become mandatory without a default value.\n Please set a default value before you uncheck "optional".',
-			optionsPanelLabel: /*[[#{title.optionsDefinition}]]*/ 'Options definition',
-			ckeditorLang: /*[[#{rich-edit.language.value}]]*/ 'en',
-			renameLabel: /*[[#{label.Rename}]]*/ 'Rename',
-			renameCufTitle: /*[[#{title.renameCuf}]]*/ 'Rename custom-field',
-			checkboxJsonDefaultValues: {'false': /*[[#{label.false}]]*/ "false", 
-										'true': /*[[#{label.true}]]*/ "true",
-										},
-			defaultOptionMandatoryMessage: /*[[#{message.defaultOptionMandatory}]]*/ 'Default option is mandatory when custom field is not optional',
-			defaultValueMandatoryMessage: /*[[#{message.defaultValueMandatory}]]*/ 'Default value is mandatory when custom field is not optional',
+<title th:text="#{title.customField}">Espace detail champ personnalisé</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<script th:remove="all" type="text/javascript" src="../../scripts/lib/jquery/jquery-1.8.2.js"></script>
+<script th:remove="all" type="text/javascript" src="../../js/thymol.js"></script>
+<script type="text/javascript" th:inline="javascript">
+	var squashtm = {};
+	squashtm.app = {
+		contextRoot : /*[[ @{/} ]]*/"http://localhost:8080/squash",
+		cfMod : {
+			customFieldUrl : /*[[ @{'/custom-fields/'+${customField.id}} ]]*/"http://localhost:8080/custom-fields/1",
+			richEditPlaceHolder : /*[[#{rich-edit.placeholder}]]*/'(Click to edit ...)',
+			confirmMandatoryTitle : /*[[#{title.confirmMandatory}]]*/'Confirm mandatory',
+			confirmMandatoryMessage : /*[[#{message.confirmMandatory}]]*/'Do you confirm the change to mandatory ? This change has bad consequences on your health !',
+			confirmLabel : /*[[#{label.Confirm}]]*/'Confirm',
+			okLabel : /*[[ #{label.Ok} ]]*/'Ok',
+			cancelLabel : /*[[ #{label.Cancel} ]]*/'Cancel',
+			informationPanelLabel : /*[[#{label.CUFInformation}]]*/'Custom-Field informations',
+			popupErrorTitle : /*[[#{popup.title.error}]]*/'Error',
+			mandatoryNeedsDefaultMessage : /*[[#{message.mandatoryNeedsDefault}]]*/'The custom field cannot become mandatory without a default value.\n Please set a default value before you uncheck "optional".',
+			optionsPanelLabel : /*[[#{title.optionsDefinition}]]*/'Options definition',
+			ckeditorLang : /*[[#{rich-edit.language.value}]]*/'en',
+			renameLabel : /*[[#{label.Rename}]]*/'Rename',
+			renameCufTitle : /*[[#{title.renameCuf}]]*/'Rename custom-field',
+			checkboxJsonDefaultValues : {
+				'false' : /*[[#{label.false}]]*/"false",
+				'true' : /*[[#{label.true}]]*/"true",
+			},
+			defaultOptionMandatoryMessage : /*[[#{message.defaultOptionMandatory}]]*/'Default option is mandatory when custom field is not optional',
+			defaultValueMandatoryMessage : /*[[#{message.defaultValueMandatory}]]*/'Default value is mandatory when custom field is not optional',
 			optionsTable : {
 				languageUrl : /*[[ @{/datatables/messages} ]]*/"http://localhost:8080/squash/datatables/messages",
-				newOptionUrl : /*[[@{'/custom-fields/'+${customField.id}+'/options/new'}]]*/ "http://localhost:8080/squash/custom-fields/1/options/new",
-				ajaxSource:  /*[[ @{'/custom-fields/'+${customField.id}+'/options'} ]]*/"http://localhost:8080/squash/custom-fields/1/options",
-				displayLength : /*[[ ${customFieldOptionsPageSize} ]]*/50,				
-				deleteTooltip: /*[[ #{label.Remove} ]]*/ 'Remove',
-				deleteConfirmMessage : /*[[ #{message.customFieldOption.remove} ]]*/ 'Remove ?',
-				renameCufOptionTitle : /*[[#{title.renameCufOption}]]*/ 'Rename custom field option',
-				changeOptionCodeTitle:/*[[#{title.changeCufOptionCode}]]*/ 'Change custom field option code',
-				addCufOptionTitle : /*[[#{title.addCufOption}]]*/ 'Add custom field option',
-				renameOptionLabel: /*[[#{label.Rename}]]*/ 'Rename',
-				changeOptionCodeLabel: /*[[#{label.changeCode}]]*/ 'Change code',
-				addOptionLabel : /*[[#{label.addOption}]]*/ 'Add option',
+				newOptionUrl : /*[[@{'/custom-fields/'+${customField.id}+'/options/new'}]]*/"http://localhost:8080/squash/custom-fields/1/options/new",
+				ajaxSource : /*[[ @{'/custom-fields/'+${customField.id}+'/options'} ]]*/"http://localhost:8080/squash/custom-fields/1/options",
+				displayLength : /*[[ ${customFieldOptionsPageSize} ]]*/50,
+				deleteTooltip : /*[[ #{label.Remove} ]]*/'Remove',
+				deleteConfirmMessage : /*[[ #{message.customFieldOption.remove} ]]*/'Remove ?',
+				renameCufOptionTitle : /*[[#{title.renameCufOption}]]*/'Rename custom field option',
+				changeOptionCodeTitle : /*[[#{title.changeCufOptionCode}]]*/'Change custom field option code',
+				addCufOptionTitle : /*[[#{title.addCufOption}]]*/'Add custom field option',
+				renameOptionLabel : /*[[#{label.Rename}]]*/'Rename',
+				changeOptionCodeLabel : /*[[#{label.changeCode}]]*/'Change code',
+				addOptionLabel : /*[[#{label.addOption}]]*/'Add option',
 			}
-  		},
-  		projectFilterConf: {
-			url: /*[[ @{/global-filter/filter} ]]*/ '/global-filter/filter',
-			title: /*[[ #{dialog.settings.filter.title} ]]*/ 'Filtre de projets',
-			confirmLabel: /*[[ #{label.Confirm} ]]*/ 'Confirmer',
-			cancelLabel: /*[[ #{label.Cancel} ]]*/ 'Cancel',
-		}, 
-		menuBarConf: {
-    		boxSelector: "#menu-toggle-filter-ckbox",
-    		url: /*[[ @{/global-filter/filter-status} ]]*/ '/global-filter/filter-status',
-    		linkSelector: "#menu-project-filter-link",
-    		enabledTxt: /*[[ #{workspace.menubar.filter.enabled.label} ]]*/ 'Actif',
-    		disabledTxt: /*[[ #{workspace.menubar.filter.disabled.label} ]]*/ 'Inactif',
-    		enabledCallbacks: [ function(){ $("div.tree-filter-reminder-div > span").removeClass("not-displayed");} ],
-    	}, 
-    	notificationConf: {
-  			infoTitle: /*[[ #{popup.title.info} ]]*/ 'Info', 
-  			errorTitle: /*[[ #{popup.title.error} ]]*/ 'Erreur'
-  		},
-  	};
-  </script>
-  <script data-main="../../scripts/custom-field-modification.js" th:attr="data-main=@{/scripts/custom-field-modification.js}"
-    src="../../scripts/require.js" th:src="@{/scripts/require-min.js}">
-  </script>
-  <!-- common head -->
-  <link rel="stylesheet" type="text/css" media="all"
-    href="../../../../../../../core/core.web/src/main/webapp/styles/master.css" th:href="@{/styles/master.css}" />
-  <link rel="shortcut icon" type="image/x-icon" media="all"
-    href="../../../../../../../core/core.web/src/main/webapp/images/favicon.ico" th:href="@{/images/favicon.ico}" />
-  <!-- /common head -->
-  <!-- common script import -->
-  <link rel="stylesheet" type="text/css" media="all"
-    href="../../../../../../../core/core.web/src/main/webapp/styles/squashtm.fg.menu.css"
-    th:href="@{/styles/squashtm.fg.menu.css}" />
-  <!-- /common script import -->
-  <link rel="stylesheet" type="text/css" media="all"
-    href="../../../../../../../core/core.web/src/main/webapp/styles/master.grey.css" th:href="@{/styles/master.grey.css}" />
-    <link rel="stylesheet" type="text/css" media="all"
-  href="../../../../../../../core/core.web/src/main/webapp/styles/structure.override.css" th:href="@{/styles/structure.override.css}" />
- 
-  <link rel="stylesheet" type="text/css" media="all"
-  href="../../../../../../../core/core.web/src/main/webapp/styles/structure.subpageoverride.css" th:href="@{/styles/structure.subpageoverride.css}" />
- <!-- rich jeditable header --> 
+		},
+		projectFilterConf : {
+			url : /*[[ @{/global-filter/filter} ]]*/'/global-filter/filter',
+			title : /*[[ #{dialog.settings.filter.title} ]]*/'Filtre de projets',
+			confirmLabel : /*[[ #{label.Confirm} ]]*/'Confirmer',
+			cancelLabel : /*[[ #{label.Cancel} ]]*/'Cancel',
+		},
+		menuBarConf : {
+			boxSelector : "#menu-toggle-filter-ckbox",
+			url : /*[[ @{/global-filter/filter-status} ]]*/'/global-filter/filter-status',
+			linkSelector : "#menu-project-filter-link",
+			enabledTxt : /*[[ #{workspace.menubar.filter.enabled.label} ]]*/'Actif',
+			disabledTxt : /*[[ #{workspace.menubar.filter.disabled.label} ]]*/'Inactif',
+			enabledCallbacks : [ function() {
+				$("div.tree-filter-reminder-div > span").removeClass("not-displayed");
+			} ],
+		},
+		notificationConf : {
+			infoTitle : /*[[ #{popup.title.info} ]]*/'Info',
+			errorTitle : /*[[ #{popup.title.error} ]]*/'Erreur'
+		},
+	};
+</script>
+<script data-main="../../scripts/custom-field-modification.js"
+  th:attr="data-main=@{/scripts/custom-field-modification.js}" src="../../scripts/require.js"
+  th:src="@{/scripts/require-min.js}">
+	
+</script>
+<!-- common head -->
+<link rel="stylesheet" type="text/css" media="all"
+  href="../../../../../../../core/core.web/src/main/webapp/styles/master.css" th:href="@{/styles/master.css}" />
+<link rel="shortcut icon" type="image/x-icon" media="all"
+  href="../../../../../../../core/core.web/src/main/webapp/images/favicon.ico" th:href="@{/images/favicon.ico}" />
+<!-- /common head -->
+<!-- common script import -->
+<link rel="stylesheet" type="text/css" media="all"
+  href="../../../../../../../core/core.web/src/main/webapp/styles/squashtm.fg.menu.css"
+  th:href="@{/styles/squashtm.fg.menu.css}" />
+<!-- /common script import -->
+<link rel="stylesheet" type="text/css" media="all"
+  href="../../../../../../../core/core.web/src/main/webapp/styles/master.grey.css" th:href="@{/styles/master.grey.css}" />
+<link rel="stylesheet" type="text/css" media="all"
+  href="../../../../../../../core/core.web/src/main/webapp/styles/structure.override.css"
+  th:href="@{/styles/structure.override.css}" />
+
+<link rel="stylesheet" type="text/css" media="all"
+  href="../../../../../../../core/core.web/src/main/webapp/styles/structure.subpageoverride.css"
+  th:href="@{/styles/structure.subpageoverride.css}" />
+<!-- rich jeditable header -->
 <!--   <link rel="stylesheet" type="text/css" media="all" -->
 <!--     href="../../../../../../../core/core.web/src/main/webapp/styles/ckeditor.override.css" -->
 <!--     th:href="@{/styles/ckeditor.override.css}" /> -->
 </head>
 <body>
-  <div id="navigation" th:include="navbar.frag :: navbar">
-    NAVBAR
-  </div>
+  <div id="navigation" th:include="navbar.frag :: navbar">NAVBAR</div>
   <div id="workspace">
     <div id="workspace-title">
       <div class="snap-left">
-        <h2 th:text="#{label.administration}"  class="admin">Administration</h2> 
+        <h2 th:text="#{label.administration}" class="admin">Administration</h2>
       </div>
       <div class="snap-right">
-        <div class="main-menubar " th:include="menubar.frag :: menubar">
-          MENUBAR
+        <div class="main-menubar " th:include="menubar.frag :: menubar">MENUBAR</div>
+        <div class="unstyled-notification-pane" th:include="menubar.frag :: notification">NOTIFICATION</div>
+      </div>
+    </div>
+    <div id="sub-page" class="sub-page">
+
+      <div id="sub-page-header" class="sub-page-header shadow ui-corner-all">
+
+        <div id="sub-page-title" class="sub-page-title">
+          <h2 th:text="#{title.customField}">Espace detail champ personnalisé de test</h2>
         </div>
-        <div class="unstyled-notification-pane" th:include="menubar.frag :: notification">
-          NOTIFICATION
+
+        <div id="sub-page-buttons" class="sub-page-buttons">
+
+          <input id="back" type="button" th:value="#{ label.Back }" value="Back" />
+
+        </div>
+
+        <div class="unsnap"></div>
+      </div>
+
+      <div id="sub-page-content" class="sub-page-content shadow ui-corner-all">
+
+        <div id="information-content">
+          <div id="cuf-name-div" class="ui-widget-header ui-corner-all ui-state-default fragment-header">
+            <div style="float: left; height: 3em">
+              <h2>
+                <label for="cuf-name-header" th:text="#{label.customField}">Custom Field</label> <a id="cuf-name-header"
+                  href="#" th:text="${customField.name}">Test Case description</a>
+              </h2>
+            </div>
+            <div class="snap-right"></div>
+            <div class="unsnap"></div>
+          </div>
+
+          <div class="fragment-body unstyled">
+            <div id="cuf-toolbar" class="toolbar-class ui-corner-all ">
+              <div class="toolbar-button-panel">
+                <input type="button" th:value="#{ label.Rename }" value="Rename" id="rename-cuf-button" class="button" />
+                <input type="button" th:value="#{ label.Remove }" value="Remove" id="delete-cuf-button" class="button" />
+              </div>
+            </div>
+            <br /> <br />
+            <div class="toggle-panel ">
+              <span class="not-displayed toogle-panel-buttons"></span>
+              <div id="cuf-info-panel" class="information-panel toggle-panel-main">
+                <div class="display-table" id="cuf-infos-table">
+                  <div class="display-table-row">
+                    <label for="cuf-label" th:text="#{label.Label}">Label</label>
+                    <div id="cuf-label" class="display-table-cell" th:text="${customField.label}">Description</div>
+
+                  </div>
+                  <div class="display-table-row">
+                    <label for="cuf-code" th:text="#{label.code}">Code</label>
+                    <div id="cuf-code" class="display-table-cell" th:text="${customField.code}">code</div>
+
+                  </div>
+                  <div class="display-table-row">
+                    <label for="inputType" th:text="#{label.inputType}">Input Type</label>
+                    <div id="cuf-inputType" class="display-table-cell"
+                      th:attr="data-type=${customField.inputType.name()}" data-type="DROPDOWN_LIST"
+                      th:text="#{${customField.inputType.i18nKey}}">Dropdown list</div>
+                  </div>
+                  <div th:unless="${customField.inputType.name() == 'CHECKBOX'}" class="display-table-row">
+                    <label for="cf-optional" th:text="#{label.Optional}">Optional</label>
+                    <div class="table-cell">
+                      <input id="cf-optional" name="optional" value="optional" type="checkbox"
+                        th:checked="${customField.optional}" />
+                    </div>
+                    <span class="error-message  optional-error"></span>
+                  </div>
+                  <div th:unless="${customField.inputType.name() == 'DROPDOWN_LIST'}" class="display-table-row">
+                    <label for="cuf-default-value" th:text="#{label.defaultValue}">Default value</label>
+                    <div th:unless="${customField.inputType.name() == 'CHECKBOX'}" id="cuf-default-value"
+                      class="display-table-cell" th:text="${customField.defaultValue}">false</div>
+                    <div th:if="${customField.inputType.name() == 'CHECKBOX'}" id="cuf-default-value"
+                      class="display-table-cell" th:text="#{'label.'+${customField.defaultValue}}">false</div>
+                    <span class="error-message  defaultValue-error"></span>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div class="toggle-panel" th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}">
+              <span class="not-displayed toggle-panel-buttons"> <input id="add-cuf-option-button"
+                class="add-option button" type="button" th:value="#{label.addOption}" value="Add an option" />
+              </span>
+              <div id="cuf-options-panel" class="toggle-panel-main">
+                <table id="options-table">
+                  <thead>
+                    <tr>
+                      <th>#</th>
+                      <th th:text="#{label.option}">Option</th>
+                      <th th:text="#{label.code}">Code</th>
+                      <th th:text="#{label.default}">Default</th>
+                      <th>&nbsp;</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <tr th:each="cfOpt , iterStat: ${customField.options}" th:object="${cfOpt}">
+                      <td th:text="${iterStat.count}">1</td>
+                      <td th:text="*{label}">label1</td>
+                      <td th:text="*{code}">code1</td>
+                      <td>
+                        <input type="checkbox" name="default" th:value="*{label}" value="label1"
+                          th:checked="${customField.defaultValue == cfOpt.label}" title="Add cuf option" />
+                      </td>
+                      <td></td>
+                    </tr>
+                    <tr th:remove="all">
+                      <td>2</td>
+                      <td class="opt-label">label2</td>
+                      <td class="opt-code">code2</td>
+                      <td class="is-default">
+                        <input type="checkbox" name="default" value="label2" />
+                      </td>
+                      <td class="delete-button"></td>
+                    </tr>
+                    <tr th:remove="all">
+                      <td>3</td>
+                      <td class="opt-label">label3</td>
+                      <td class="opt-code">code3</td>
+                      <td class="is-default">
+                        <input type="checkbox" name="default" value="label3" checked="checked" />
+                      </td>
+                      <td class="delete-button"></td>
+                    </tr>
+                  </tbody>
+                </table>
+              </div>
+            </div>
+          </div>
+
+          <div id="delete-warning-pane" class="not-displayed popup-dialog" title="New Custom field"
+            th:title="#{title.newCustomField}">
+            <div class="alert">
+              <span th:text="#{message.customField.remove}">Do you wanna remove custom field ?</span>
+            </div>
+            <div class="popup-dialog-buttonpane">
+              <input class="confirm" type="button" value="Proceed" th:value="#{label.Confirm}" />
+              <input class="cancel" type="button" value="Cancel" th:value="#{label.Cancel}" />
+            </div>
+          </div>
         </div>
       </div>
     </div>
-     <div id="sub-page" class="sub-page" >
-			
-			<div id="sub-page-header" class="sub-page-header shadow ui-corner-all">
-			
-				<div id="sub-page-title" class="sub-page-title">
-					  <h2 th:text="#{title.customField}">Espace detail champ personnalisé de test</h2> 
-				</div>
-				
-				<div id="sub-page-buttons" class="sub-page-buttons">
-					
-				<input id="back" type="button" th:value="#{ label.Back }" value="Back"/>
-	
-				</div>
-				
-				<div class="unsnap"></div>
-			</div>
-			
-			<div id="sub-page-content" class="sub-page-content shadow ui-corner-all">
-			
-    <div id="information-content ">
-	   <div id="cuf-name-div"
-			class="ui-widget-header ui-corner-all ui-state-default fragment-header">
-			<div style="float: left; height: 3em">
-				<h2>
-					<label for="cuf-name-header" th:text="#{label.customField}">Custom Field</label>
-					<a id="cuf-name-header" href="#" th:text="${customField.name}">Test Case description</a>
-				</h2>
-			</div>
-			<div style="float: right;">
-			</div>
-			<div style="clear: both;"></div>
-		</div>
-		
-		<div class="fragment-body unstyled" >
-			<div id="cuf-toolbar" class="toolbar-class ui-corner-all ">
-				<div class="toolbar-button-panel">
-					<input type="button" th:value="#{ label.Rename }" value="Rename" id="rename-cuf-button"
-								class="button"/>
-					<input type="button" th:value="#{ label.Remove }" value="Remove" id="delete-cuf-button"
-								class="button"/>
-				</div>
-			</div>
-			<br />
-			<br />
-			<div class="toggle-panel ">
-				<span class="not-displayed toogle-panel-buttons"></span>
-				<div id="cuf-info-panel"  class="information-panel toggle-panel-main">
-					<div class="display-table" id="cuf-infos-table">
-						<div class="display-table-row">
-							<label for="cuf-label" th:text="#{label.Label}">Label</label>
-							<div id="cuf-label" class="display-table-cell" th:text="${customField.label}">Description</div>
-							
-						</div>
-						<div class="display-table-row">
-							<label for="cuf-code" th:text="#{label.code}">Code</label>
-							<div id="cuf-code" class="display-table-cell" th:text="${customField.code}">code</div>
-							
-						</div>
-						 <div class="display-table-row">
-						 	<label for="inputType" th:text="#{label.inputType}">Input Type</label>
-						 	<div id="cuf-inputType" class="display-table-cell" th:value="${customField.inputType.name()}" value="DROPDOWN_LIST" th:text="#{${customField.inputType.i18nKey}}">Dropdown list</div>
-      					</div>      					
-				      	<div th:unless="${customField.inputType.name() == 'CHECKBOX'}" class="display-table-row">
-				        	<label for="cf-optional" th:text="#{label.Optional}">Optional</label>
-				        	<div class="table-cell"><input id="cf-optional" name="optional" value="optional" type="checkbox" th:checked="${customField.optional}"/></div>
-				        	<span class="error-message  optional-error"></span>
-				        </div>
-				      	<div th:unless="${customField.inputType.name() == 'DROPDOWN_LIST'}"  class="display-table-row">
-							<label for="cuf-default-value" th:text="#{label.defaultValue}">Default value</label>
-							<div th:unless="${customField.inputType.name() == 'CHECKBOX'}" id="cuf-default-value" class="display-table-cell" th:text="${customField.defaultValue}">false</div>
-							<div th:if="${customField.inputType.name() == 'CHECKBOX'}" id="cuf-default-value" class="display-table-cell" th:text="#{'label.'+${customField.defaultValue}}">false</div>
-							<span class="error-message  defaultValue-error"></span>
-						</div>
-					</div>
-				</div>
-			</div>
-			<div class="toggle-panel" th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}"  >
-				<span class="not-displayed toggle-panel-buttons">
-				<input id="add-cuf-option-button" class="add-option button" type="button" th:value="#{label.addOption}" value="Add an option" />
-				</span>
-				<div id="cuf-options-panel"  class="toggle-panel-main">
-					 <table id="options-table">
-						<thead>
-							<tr>
-							 <th>#</th>
-              					<th th:text="#{label.option}">Option</th>
-              					<th th:text="#{label.code}">Code</th>
-              					<th th:text="#{label.default}">Default</th>
-              					<th>&nbsp;</th>
-            				</tr>
-          				</thead>
-				         <tbody>
-				             <tr th:each="cfOpt , iterStat: ${customField.options}" th:object="${cfOpt}">
-				              	<td th:text="${iterStat.count}">1</td>
-				             	<td th:text="*{label}">label1</td>
-				             	<td th:text="*{code}">code1</td>
-				             	<td><input type="checkbox" name="default" th:value="*{label}" value="label1" th:checked="${customField.defaultValue == cfOpt.label}" title="Add cuf option"/></td>
-				             	<td></td>
-				             </tr>
-				             <tr th:remove="all">
-				             	<td>2</td>
-				             	<td class="opt-label">label2</td>
-				             	<td class="opt-code">code2</td>
-				             	<td class="is-default"><input type="checkbox" name="default" value="label2" /></td>
-				             	<td class="delete-button"></td>
-				             	 </tr>
-				             <tr th:remove="all">
-				             	<td>3</td>
-				             	<td class="opt-label">label3</td>
-				             	<td class="opt-code">code3</td>
-				             	<td class="is-default"><input type="checkbox" name="default"  value="label3" checked="checked"/></td>
-				             	<td class="delete-button"></td>
-				             </tr>
-				         </tbody>
-				</table>
-				</div>
-			</div>
-		</div>
-		</div>
-		</div>
-   	</div>
-   	</div>
-<!--    ====================================POPUPS======================================================  -->
-<!--    	RENAME POPUP -->
-   	<div id="rename-cuf-popup" class="not-displayed popup-dialog">
-			<label for="rename-cuf-input" th:text="#{ label.Rename }">Rename</label>
-			<input type="text" id="rename-cuf-input" maxlength="255" size="50" />
-			<br />
-			<span class="error-message name-error"></span>
-	</div>
-	
-<!-- 	ADD OPTION POPUP  -->
-	<div id="add-cuf-option-popup" class="not-displayed popup-dialog" th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}" th:title="#{title.addCufOption}">
-		<table class="form-horizontal">
-    	<tr class="control-group">
-    		<td><label class="control-label" for="new-cuf-option-label" th:text="#{label.newOption}">New option</label></td>
-          	<td class="controls">
-	          	<input id="new-cuf-option-label" name="new-cuf-option-label" type="text" value="" maxlength="255" size="50" />
-	          	<span class="help-inline">&nbsp;</span>
-          	</td>
-         </tr><tr class="control-group" >
-          	<td><label class="control-label" for="new-cuf-option-code" th:text="#{label.code}">Code</label></td>
-          	<td class="controls">
-	          	<input id="new-cuf-option-code" name="new-cuf-option-code" type="text" value="" maxlength="30" size="50" />
-	          	<span class="help-inline">&nbsp;</span>
-          	</td>
-         </tr>
-         </table>
-         <div class="popup-dialog-buttonpane">
-		      <input class="confirm" type="button" th:value="#{label.Add}" value="add"/>
-		      <input class="cancel" type="button" th:value="#{label.Cancel}" value="cancel"/>
-		 </div>
-	</div>
-	
-<!-- 	RENAME OPTION POPUP -->
-	<div id="rename-cuf-option-popup" class="not-displayed popup-dialog" th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}">
-		<span class="not-displayed" id="rename-cuf-option-previous"></span>
-		<table class="form-horizontal">
-		<tr class="control-group">
-			<td><label for="rename-cuf-option-label" class="control-label" th:text="#{label.Label}">Label</label></td>
-			<td class="controls">
-		<input id="rename-cuf-option-label" name="rename-cuf-option-label" type="text" value="" maxlength="255" size="50" />
-       <span class="help-inline">&nbsp;</span>
-	      	</td>
-	      	</tr></table>
-	</div>
-	
-<!-- 	CHANGE OPTION CODE POPUP -->
-	<div id="change-cuf-option-code-popup" class="not-displayed popup-dialog" th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}">
-		<span class="not-displayed" id="change-cuf-option-code-label"></span>
-		<table class="form-horizontal">
-		<tr class="control-group">
-			<td><label for="change-cuf-option-code" class="control-label" th:text="#{label.code}">Code</label></td>
-			<td class="controls">
-				<input id="change-cuf-option-code" name="change-cuf-option-code" type="text" value="" maxlength="30" size="50" />
-	      		<span class="help-inline">&nbsp;</span>
-	      	</td>
-       </tr></table>
-	</div>
+  </div>
+  <!--    ====================================POPUPS======================================================  -->
+  <!--    	RENAME POPUP -->
+  <div id="rename-cuf-popup" class="not-displayed popup-dialog">
+    <label for="rename-cuf-input" th:text="#{ label.Rename }">Rename</label> <input type="text" id="rename-cuf-input"
+      maxlength="255" size="50" /> <br /> <span class="error-message name-error"></span>
+  </div>
+
+  <!-- 	ADD OPTION POPUP  -->
+  <div id="add-cuf-option-popup" class="not-displayed popup-dialog"
+    th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}" th:title="#{title.addCufOption}">
+    <table class="form-horizontal">
+      <tr class="control-group">
+        <td>
+          <label class="control-label" for="new-cuf-option-label" th:text="#{label.newOption}">New option</label>
+        </td>
+        <td class="controls">
+          <input id="new-cuf-option-label" name="new-cuf-option-label" type="text" value="" maxlength="255" size="50" />
+          <span class="help-inline">&nbsp;</span>
+        </td>
+      </tr>
+      <tr class="control-group">
+        <td>
+          <label class="control-label" for="new-cuf-option-code" th:text="#{label.code}">Code</label>
+        </td>
+        <td class="controls">
+          <input id="new-cuf-option-code" name="new-cuf-option-code" type="text" value="" maxlength="30" size="50" /> <span
+            class="help-inline">&nbsp;</span>
+        </td>
+      </tr>
+    </table>
+    <div class="popup-dialog-buttonpane">
+      <input class="confirm" type="button" th:value="#{label.Add}" value="add" /> <input class="cancel" type="button"
+        th:value="#{label.Cancel}" value="cancel" />
+    </div>
+  </div>
+
+  <!-- 	RENAME OPTION POPUP -->
+  <div id="rename-cuf-option-popup" class="not-displayed popup-dialog"
+    th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}">
+    <span class="not-displayed" id="rename-cuf-option-previous"></span>
+    <table class="form-horizontal">
+      <tr class="control-group">
+        <td>
+          <label for="rename-cuf-option-label" class="control-label" th:text="#{label.Label}">Label</label>
+        </td>
+        <td class="controls">
+          <input id="rename-cuf-option-label" name="rename-cuf-option-label" type="text" value="" maxlength="255"
+            size="50" /> <span class="help-inline">&nbsp;</span>
+        </td>
+      </tr>
+    </table>
+  </div>
+
+  <!-- 	CHANGE OPTION CODE POPUP -->
+  <div id="change-cuf-option-code-popup" class="not-displayed popup-dialog"
+    th:if="${customField.inputType.name() == 'DROPDOWN_LIST'}">
+    <span class="not-displayed" id="change-cuf-option-code-label"></span>
+    <table class="form-horizontal">
+      <tr class="control-group">
+        <td>
+          <label for="change-cuf-option-code" class="control-label" th:text="#{label.code}">Code</label>
+        </td>
+        <td class="controls">
+          <input id="change-cuf-option-code" name="change-cuf-option-code" type="text" value="" maxlength="30" size="50" />
+          <span class="help-inline">&nbsp;</span>
+        </td>
+      </tr>
+    </table>
+  </div>
 </body>
 </html>

tm/tm.web/src/main/webapp/scripts/app/cf/CustomFieldModificationView.js

-/*
- *     This file is part of the Squashtest platform.
- *     Copyright (C) 2010 - 2012 Henix, henix.fr
- *
- *     See the NOTICE file distributed with this work for additional
- *     information regarding copyright ownership.
- *
- *     This is free software: you can redistribute it and/or modify
- *     it under the terms of the GNU Lesser General Public License as published by
- *     the Free Software Foundation, either version 3 of the License, or
- *     (at your option) any later version.
- *
- *     this software is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *     GNU Lesser General Public License for more details.
- *
- *     You should have received a copy of the GNU Lesser General Public License
- *     along with this software.  If not, see <http://www.gnu.org/licenses/>.
- */
-define([ "jquery", "./NewCustomFieldOptionDialog", "backbone", "jeditable.simpleJEditable",
-		"jeditable.selectJEditable", "app/util/StringUtil", "jquery.squash", "jqueryui", "jquery.squash.togglepanel", "jeditable.selectJEditable",
-		"jquery.squash.datatables", "jquery.squash.oneshotdialog", "jquery.squash.messagedialog" ], function($,
-		NewCustomFieldOptionDialog, Backbone, SimpleJEditable, SelectJEditable, StringUtil) {
-	var cfMod = squashtm.app.cfMod;
-	/*
-	 * Defines the controller for the custom fields table.
-	 */
-	var CustomFieldModificationView = Backbone.View.extend({
-		el : "#information-content",
-		initialize : function() {
-			var self = this;
-			
-			this.optionalCheckbox = this.$("#cf-optional").get(0);
-			
-			this.configureTogglePanels();
-			this.configureEditables();
-			this.configureRenamePopup();
-			this.configureChangeOptionCodePopup();
-			this.configureOptionTable();
-			this.configureButtons();
-			// this line below is here because toggle panel buttons
-			// cannot be bound with the 'events' property of
-			// Backbone.View.
-			// my guess is that the event is bound to the button
-			// before it is moved from it's "span.not-displayed"
-			// to the toggle panel header.
-			// TODO change our way to make toggle panels buttons
-			this.$("#add-cuf-option-button").click(function() {
-				self.openAddOptionPopup.call(self);
-			});
-		},
-
-		events : {
-			"click #cf-optional" : "confirmOptional",
-			"click .is-default>input:checkbox" : "changeDefaultOption",
-			"click .opt-label" : "openRenameOptionPopup",
-			"click .opt-code" : "openChangeOptionCodePopup"
-		},
-
-		confirmOptional : function(event) {
-			var self = this;
-			var checked = event.target.checked;
-			
-			if (checked) {
-				self.sendOptional(checked);
-				
-			} else {
-				var defaultValue = self.findDefaultValue();
-				if (StringUtil.isBlank(defaultValue) || defaultValue === cfMod.richEditPlaceHolder) {
-					$.squash.openMessage(cfMod.popupErrorTitle, cfMod.mandatoryNeedsDefaultMessage, 350);
-					event.target.checked = true;
-					return;
-				}
-				var message = cfMod.confirmMandatoryMessage;
-				message = self.replacePlaceHolderByValue(0, message, defaultValue);
-				oneShotConfirm(cfMod.confirmMandatoryTitle, message, cfMod.confirmLabel, cfMod.cancelLabel, 500).done(
-						function() {
-							self.sendOptional(checked);
-						}).fail(function() {
-					event.target.checked = true;
-				});
-			}
-		},
-
-		findDefaultValue : function() {
-			var defaultValueDiv = this.$('#cuf-default-value');
-			if (defaultValueDiv && defaultValueDiv.length > 0) {
-				return $(defaultValueDiv[0]).text();
-			} else if (this.optionsTable) {
-				var checkedDefault = this.optionsTable.find('td.is-default input:checked');
-				if (checkedDefault) {
-					return checkedDefault.val();
-				}
-			}
-			return "";
-		},
-
-		replacePlaceHolderByValue : function(index, message, replaceValue) {
-			var pattern = /\{[\d,\w,\s]*\}/;
-			var match = pattern.exec(message);
-			var pHolder = match[index];
-			return message.replace(pHolder, replaceValue);
-		},
-
-		sendOptional : function(optional) {
-			return $.ajax({
-				url : cfMod.customFieldUrl + "/optional",
-				type : "post",
-				data : {
-					'value' : optional
-				},
-				dataType : "json"
-			});
-		},
-
-		changeDefaultOption : function(event) {
-			var self = this;
-			var checkbox = event.currentTarget;
-			var option = checkbox.value;
-			var defaultValue = checkbox.checked ? option : "";
-			if (defaultValue === "" && this.isFieldMandatory()) {
-				checkbox.checked = true;
-				$.squash.openMessage(cfMod.popupErrorTitle, cfMod.defaultOptionMandatoryMessage);
-				return;
-			}
-			var uncheckSelector = ".is-default>input:checkbox" + (checkbox.checked ? "[value!='" + option + "']" : "");
-
-			this.sendDefaultValue(defaultValue).done(function() {
-				self.optionsTable.find(uncheckSelector).attr("checked", false);
-			}).fail(function() {
-				checkbox.checked = !checkbox.checked;
-			});
-
-		},
-
-		sendDefaultValue : function(defaultValue) {
-			return $.ajax({
-				url : cfMod.customFieldUrl + "/defaultValue",
-				type : 'POST',
-				data : {
-					'value' : defaultValue
-				},
-				dataType : 'json'
-			});
-		},
-
-		openRenameOptionPopup : function(event) {
-			var self = this;
-			var labelCell = event.currentTarget;
-			var previousValue = $(labelCell).text();
-
-			self.renameCufOptionPopup.find("#rename-cuf-option-previous").text(previousValue);
-			self.renameCufOptionPopup.find("#rename-cuf-option-label").val(previousValue);
-			self.renameCufOptionPopup.dialog("open");
-		},
-
-		openChangeOptionCodePopup : function(event) {
-			var self = this;
-			var codeCell = event.currentTarget;
-			var previousValue = $(codeCell).text();
-			var label = $(codeCell).parent("tr").find("td.opt-label").text();
-			self.changeOptionCodePopup.find("#change-cuf-option-code-label").text(label);
-			self.changeOptionCodePopup.find("#change-cuf-option-code").val(previousValue);
-			self.changeOptionCodePopup.dialog("open");
-		},
-
-		renameOption : function() {
-			var self = this;
-			var previousValue = self.renameCufOptionPopup.find("#rename-cuf-option-previous").text();
-			var newValue = self.renameCufOptionPopup.find("#rename-cuf-option-label").val();
-			$.ajax({
-				type : 'POST',
-				data : {
-					'value' : newValue
-				},
-				dataType : "json",
-				url : cfMod.optionsTable.ajaxSource + "/" + previousValue + "/label"
-			}).done(function(data) {
-				self.optionsTable.refresh();
-			});
-
-		},
-
-		changeOptionCode : function() {
-			var self = this;
-			var label = self.changeOptionCodePopup.find("#change-cuf-option-code-label").text();
-			var newValue = self.changeOptionCodePopup.find("#change-cuf-option-code").val();
-			$.ajax({
-				type : 'POST',
-				data : {
-					'value' : newValue
-				},
-				dataType : "json",
-				url : cfMod.optionsTable.ajaxSource + "/" + label + "/code"
-			}).done(function(data) {
-				self.optionsTable.refresh();
-			});
-
-		},
-
-		configureButtons : function() {
-			$("#back").button();
-			$.squash.decorateButtons();
-		},
-
-		configureTogglePanels : function() {
-			var informationSettings = {
-				initiallyOpen : true,
-				title : cfMod.informationPanelLabel
-			};
-			this.$("#cuf-info-panel").togglePanel(informationSettings);
-			var optionSettings = {
-				initiallyOpen : true,
-				title : cfMod.optionsPanelLabel
-			};
-			this.$("#cuf-options-panel").togglePanel(optionSettings);
-		},
-
-		configureEditables : function() {
-			var self = this;
-			this.makeSimpleJEditable("cuf-label");
-			this.makeSimpleJEditable("cuf-code");
-			if ($("#cuf-inputType").attr('value') === "PLAIN_TEXT") {
-				new SimpleJEditable({
-					language : {
-						richEditPlaceHolder : cfMod.richEditPlaceHolder,
-						okLabel : cfMod.okLabel,
-						cancelLabel : cfMod.cancelLabel
-					},					
-					targetUrl : function(value, settings) {
-						if (self.changeDefaultValueText(value)) {
-							return value;
-						} else {return this.revert;}
-					},
-					componentId : "cuf-default-value",
-					jeditableSettings : { callback: self.enableOptionalChange }
-				});
-				
-			} else if ($("#cuf-inputType").attr('value') === "CHECKBOX") {
-				this.makeDefaultSelectJEditable("cuf-default-value", cfMod.checkboxJsonDefaultValues);
-			}
-			$("#cuf-default-value").click(self.disableOptionalChange);
-		},
-		disableOptionalChange : function(){$("#cf-optional").attr("disabled", true);},
-		enableOptionalChange : function(){$("#cf-optional").removeAttr("disabled");},
-		changeDefaultValueText : function(value) {
-			if (this.isFieldMandatory() && StringUtil.isBlank(value)) {
-				$.squash.openMessage(cfMod.popupErrorTitle, cfMod.defaultValueMandatoryMessage);
-				return false;
-			} else {
-				this.sendDefaultValue(value);
-				return true;
-			}
-		},
-		
-		isFieldMandatory: function() {
-			return !this.optionalCheckbox.checked;
-		},
-
-		renameCuf : function() {
-			var newNameVal = $("#rename-cuf-input").val();
-			$.ajax({
-				type : 'POST',
-				data : {
-					'value' : newNameVal
-				},
-				dataType : "json",
-				url : cfMod.customFieldUrl + "/name"
-				
-			}).done(function(data) {
-				$('#cuf-name-header').html(data.newName);
-				$('#rename-cuf-popup').dialog('close');
-			});
-		},
-
-		configureRenamePopup : function() {
-			var params = {
-				selector : "#rename-cuf-popup",
-				title : cfMod.renameCufTitle,
-				openedBy : "#rename-cuf-button",
-				isContextual : true,
-				usesRichEdit : false,
-				closeOnSuccess : true,
-				buttons : [ {
-					'text' : cfMod.renameLabel,
-					'click' : this.renameCuf
-				}, {
-					'text' : cfMod.cancelLabel,
-					'click' : this.closePopup
-				} ]
-			};
-			squashtm.popup.create(params);
-			$("#rename-cuf-popup").bind("dialogopen", function(event, ui) {
-				var name = $.trim($('#cuf-name-header').text());
-				$("#rename-cuf-input").val($.trim(name));
-			});
-
-		},
-
-		closePopup : function() {
-			$(this).data("answer", "cancel");
-			$(this).dialog('close');
-		},
-
-		makeSimpleJEditable : function(imputId) {
-			new SimpleJEditable({
-				language : {
-					richEditPlaceHolder : cfMod.richEditPlaceHolder,
-					okLabel : cfMod.okLabel,
-					cancelLabel : cfMod.cancelLabel
-				},
-				targetUrl : cfMod.customFieldUrl,
-				componentId : imputId,
-				jeditableSettings : {}
-			});
-		},
-		makeDefaultSelectJEditable : function(inputId, jsonData) {
-			var self = this;
-			new SelectJEditable({
-				language : {
-					richEditPlaceHolder : cfMod.richEditPlaceHolder,
-					okLabel : cfMod.okLabel,
-					cancelLabel : cfMod.cancelLabel
-				},
-				
-				targetUrl : cfMod.customFieldUrl,
-				componentId : inputId,
-				jeditableSettings : {
-					callback: self.enableOptionalChange,
-					data : JSON.stringify(jsonData)
-				}
-			});
-		},
-
-		configureOptionTable : function() {
-			var self = this;
-			if ($("#cuf-inputType").attr('value') !== "DROPDOWN_LIST") {
-				return;
-			}
-			var config = $.extend({
-				"oLanguage" : {
-					"sUrl" : cfMod.optionsTable.languageUrl
-				},
-				"bJQueryUI" : true,
-				"bAutoWidth" : false,
-				"bFilter" : false,
-				"bPaginate" : true,
-				"sPaginationType" : "squash",
-				"iDisplayLength" : cfMod.optionsTable.displayLength,
-				"bProcessing" : true,
-				"bServerSide" : true,
-				"sAjaxSource" : cfMod.optionsTable.ajaxSource,
-				"bDeferRender" : true,
-				"bRetrieve" : true,
-				"sDom" : 't<"dataTables_footer"lirp>',
-				"iDeferLoading" : 0,
-				"fnRowCallback" : function() {
-				},
-				"aoColumnDefs" : [ {
-					'bSortable' : false,
-					'sWidth' : '2em',
-					'sClass' : 'centered ui-state-default drag-handle select-handle',
-					'aTargets' : [ 0 ],
-					'mDataProp' : 'entity-index'
-				}, {
-					'bSortable' : false,
-					"aTargets" : [ 1 ],
-					"sClass" : "opt-label linkWise",
-					"mDataProp" : "opt-label"
-				}, {
-					'bSortable' : false,
-					"aTargets" : [ 2 ],
-					"sClass" : "opt-code linkWise",
-					"mDataProp" : "opt-code"
-				}, {
-					'bSortable' : false,
-					'aTargets' : [ 3 ],
-					'sClass' : "is-default",
-					'mDataProp' : 'opt-default'
-				}, {
-					'bSortable' : false,
-					'sWidth' : '2em',
-					'sClass' : 'delete-button',
-					'aTargets' : [ 4 ],
-					'mDataProp' : 'empty-delete-holder'
-				} ]
-			}, squashtm.datatable.defaults);
-
-			var squashSettings = {
-				enableHover : true,
-				enableDnD : true,
-				confirmPopup : {
-					oklabel : cfMod.confirmLabel,
-					cancellabel : cfMod.cancelLabel
-				},
-
-				deleteButtons : {
-					url : cfMod.optionsTable.ajaxSource + "/{opt-label}",
-					popupmessage : cfMod.optionsTable.deleteConfirmMessage,
-					tooltip : cfMod.optionsTable.deleteTooltip,
-					success : function(data) {
-						self.optionsTable.refresh();
-					},
-					dataType : "json"
-				},
-
-				functions : {
-					dropHandler : function(dropData) {
-						$.post(cfMod.optionsTable.ajaxSource + '/positions', dropData, function() {
-							self.optionsTable.refresh();
-						});
-					},
-					getODataId : function(arg) {
-						return this.fnGetData(arg)['opt-label'];
-					}
-				}
-
-			};
-
-			this.optionsTable = this.$("table");
-			this.optionsTable.squashTable(config, squashSettings);
-		},
-
-		openAddOptionPopup : function() {
-			if ($("#cuf-inputType").attr('value') !== "DROPDOWN_LIST") {
-				return;
-			}
-			var self = this;
-
-			function discard() {
-				self.newOptionDialog.off("newOption.cancel newOption.confirm");
-				self.newOptionDialog.undelegateEvents();
-				self.newOptionDialog = null;
-			}
-
-			function discardAndRefresh() {
-				discard();
-				self.optionsTable.refresh();
-			}
-
-			self.newOptionDialog = new NewCustomFieldOptionDialog({
-				model : {
-					label : "",
-					code : ""
-				}
-			});
-
-			self.newOptionDialog.on("newOption.cancel", discard);
-			self.newOptionDialog.on("newOption.confirm", discardAndRefresh);
-		},
-
-		configureRenameOptionPopup : function() {
-			if ($("#cuf-inputType").attr('value') !== "DROPDOWN_LIST") {
-				return;
-			}
-			var self = this;
-			var params = {
-				selector : "#rename-cuf-option-popup",
-				title : cfMod.optionsTable.renameCufOptionTitle,
-				openedBy : "#rename-cuf-option-popup",
-				isContextual : true,
-				usesRichEdit : false,
-				closeOnSuccess : true,
-				buttons : [ {
-					'text' : cfMod.optionsTable.renameOptionLabel,
-					'click' : function() {
-						self.renameOption.call(self);
-					}
-				}, {
-					'text' : cfMod.cancelLabel,
-					'click' : this.closePopup
-				} ]
-			};
-			squashtm.popup.create(params);
-			this.renameCufOptionPopup = $("#rename-cuf-option-popup");
-		},
-		
-		configureChangeOptionCodePopup : function() {
-			if ($("#cuf-inputType").attr('value') !== "DROPDOWN_LIST") {
-				return;
-			}
-			
-			var self = this;
-			var params = {
-				selector : "#change-cuf-option-code-popup",
-				title : cfMod.optionsTable.changeOptionCodeTitle,
-				openedBy : "#change-cuf-option-code-popup",
-				isContextual : true,
-				usesRichEdit : false,
-				closeOnSuccess : true,
-				buttons : [ {
-					'text' : cfMod.optionsTable.changeOptionCodeLabel,
-					'click' : function() {
-						self.changeOptionCode.call(self);
-					}
-				}, {
-					'text' : cfMod.cancelLabel,
-					'click' : this.closePopup
-				} ]
-			};
-			squashtm.popup.create(params);
-			this.changeOptionCodePopup = $("#change-cuf-option-code-popup");
-		}
-
-	});
-	return CustomFieldModificationView;
-});

tm/tm.web/src/main/webapp/scripts/app/cf/NewCustomFieldOptionDialog.js

-/*
- *     This file is part of the Squashtest platform.
- *     Copyright (C) 2010 - 2012 Henix, henix.fr
- *
- *     See the NOTICE file distributed with this work for additional
- *     information regarding copyright ownership.
- *
- *     This is free software: you can redistribute it and/or modify
- *     it under the terms of the GNU Lesser General Public License as published by
- *     the Free Software Foundation, either version 3 of the License, or
- *     (at your option) any later version.
- *
- *     this software is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *     GNU Lesser General Public License for more details.
- *
- *     You should have received a copy of the GNU Lesser General Public License
- *     along with this software.  If not, see <http://www.gnu.org/licenses/>.
- */
-define([ "jquery", "backbone", "handlebars",  "app/lnf/Forms", "jquery.squash.confirmdialog" ], 
-		function($, Backbone, Handlebars,  Forms) {
-	var View = Backbone.View.extend({
-		el: "#add-cuf-option-popup",
-		
-		initialize: function() {
-			
-			this.$el.find("input:text").val("");
-			this.$el.confirmDialog({
-				autoOpen: true
-			});
-		}, 
-		
-		events: {
-			"confirmdialogcancel": "cancel",
-			"confirmdialogvalidate": "validate",
-			"confirmdialogconfirm": "confirm"
-		},
-
-		cancel: function(event) {
-			this.cleanup();
-			this.trigger("newOption.cancel");
-		},
-		
-		confirm: function(event) {
-			this.cleanup();
-			this.trigger("newOption.confirm");
-		}, 
-		
-		validate: function(event) {
-			var res = true;
-			this.populateModel();
-			var self = this;
-			Forms.form(this.$el).clearState();
-			
-			$.ajax({
-				url: squashtm.app.cfMod.optionsTable.newOptionUrl,
-				type: 'POST',
-				data: self.model,
-				// note : we cannot use promise api with async param. see http://bugs.jquery.com/ticket/11013#comment:40
-				async: false,
-				dataType: 'json',
-			}).fail(function(jqXHR, textStatus, errorThrown) {
-					res = false;
-					event.preventDefault();});
-			
-			return res;
-		},
-		
-		cleanup: function() {
-			this.$el.addClass("not-displayed");
-			Forms.form(this.$el).clearState();
-			this.$el.confirmDialog("destroy");
-		}, 
-		
-		
-		populateModel: function() {
-			var $el = this.$el;			
-			this.model.label = $el.find("#new-cuf-option-label").val();
-			this.model.code = $el.find("#new-cuf-option-code").val();
-		},
-		
-		
-	});
-
-	return View;
-});

tm/tm.web/src/main/webapp/scripts/app/cf/NewCustomFieldPanelView.js

  *     along with this software.  If not, see <http://www.gnu.org/licenses/>.
  */
 define(
-		[ "jquery", "backbone", "handlebars", "app/lnf/SquashDatatablesLnF",
-				"app/lnf/Forms", "jquery.squash.confirmdialog" ],
+		[ "jquery", "backbone", "handlebars", "app/lnf/SquashDatatablesLnF", "app/lnf/Forms", "jquery.squash.confirmdialog" ],
 		function($, Backbone, Handlebars, SD, Forms) {
 			/*
 			 * Defines the controller for the new custom field panel.
 							var self = this;
 							var model = this.model;
 
-							this.defaultValueField = this
-									.$("input:text[name='defaultValue']");
+							this.defaultValueField = this.$("input:text[name='defaultValue']");
 
 							this.$("input:text.strprop").each(function() {
 								var self = this;
 								self.value = model.get(self.name);
 							});
-							this.$("input:checkbox[name='optional']").get()[0].checked = model
-									.get("optional");
-							this.$("select[name='inputType']").val(
-									model.get("inputType"));
+							this.$("input:checkbox[name='optional']").get()[0].checked = model.get("optional");
+							this.$("select[name='inputType']").val(model.get("inputType"));
 
 							this.$("input:button").button();
 
 							this.render();
 							this.$el.confirmDialog({
 								autoOpen : true,
-								close : function(){
+								close : function() {
 									self.cancel.call(self);
 								}
 							});
 
 						render : function() {
 							var inputType = this.model.get("inputType");
-							var source = $("#" + inputType + "-default-tpl")
-									.html();
+							var source = $("#" + inputType + "-default-tpl").html();
 							var template = Handlebars.compile(source);
-							this.$("#default-value-pane").html(
-									template(this.model.toJSON()));
+							this.$("#default-value-pane").html(template(this.model.toJSON()));
 							switch (inputType) {
 							case "DROPDOWN_LIST":
 								this.renderOptionsTable();
 							Forms.form(this.$el).clearState();
 							this.$el.confirmDialog("destroy");
 						},
-						
+
 						renderOptional : function(show) {
 							var renderPane = this.$("#optional-pane");
 							if (show) {
 							var optionCode = optionCodeInput.$el.val();
 
 							try {
-								this.model.addOption([optionLabel, optionCode]);
-								
+								this.model.addOption([ optionLabel, optionCode ]);
+
 								this.optionsTable.dataTable().fnAddData([ optionLabel, optionCode, false, "" ]);
 
 								optionCodeInput.clearState();
 								optionCodeInput.$el.val("");
 								optionLabelInput.clearState();
 								optionLabelInput.$el.val("");
-								
+
 							} catch (ex) {
 								if (ex.name === "ValidationException") {
 									if (ex.validationErrors.optionLabel) {
 							}
 
 						},
-												
+
 						removeOption : function(event) {
 							// target of click event is a <span> inside of
 							// <button>, so we use currentTarget
-							var button = event.currentTarget, 
-								$button = $(button), 
-								option = $button.data("value"), 
-								row = $button.parents("tr")[0];
+							var button = event.currentTarget, $button = $(button), option = $button.data("value"), row = $button
+									.parents("tr")[0];
 
 							this.model.removeOption(option);
 							this.optionsTable.dataTable().fnDeleteRow(row);
 						},
 
 						changeDefaultOption : function(event) {
-							var checkbox = event.currentTarget, 
-								option = checkbox.value, 
-								defaultValue = checkbox.checked ? option : "", uncheckSelector = ".is-default>input:checkbox" + (checkbox.checked ? "[value!='" + option + "']" : ""), 
-								optionsInput = Forms.input(this.$("input[name='options']"));
+							var checkbox = event.currentTarget, option = checkbox.value, defaultValue = checkbox.checked ? option
+									: "", uncheckSelector = ".is-default>input:checkbox"
+									+ (checkbox.checked ? "[value!='" + option + "']" : ""), optionsInput = Forms.input(this
+									.$("input[name='options']"));
 
 							optionsInput.clearState();
 
 						 * returns the function which should be used as a callback.
 						 */
 						decorateOptionRow : function(self) {
-							return function(nRow, aData, iDisplayIndex,
-									iDisplayIndexFull) {
-								var row = $(nRow), 
-									defaultCell = row.find(".is-default"), 
-									removeCell = row.find(".remove-row"), 
-									option = aData[0], 
-									checked = option === self.model.get("defaultValue"), 
-									tplData = {
+							return function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
+								var row = $(nRow), defaultCell = row.find(".is-default"), removeCell = row.find(".remove-row"), option = aData[0], checked = option === self.model
+										.get("defaultValue"), tplData = {
 									option : option,
 									checked : checked
 								};

tm/tm.web/src/main/webapp/scripts/common.js

 requirejs.config({
 	
   packages: [
+    "custom-field-editor",
     "custom-field-binding",
     "execution-processing", 
     "projects-manager", 

tm/tm.web/src/main/webapp/scripts/custom-field-editor/CustomFieldModificationView.js

+/*
+ *     This file is part of the Squashtest platform.
+ *     Copyright (C) 2010 - 2012 Henix, henix.fr
+ *
+ *     See the NOTICE file distributed with this work for additional
+ *     information regarding copyright ownership.
+ *
+ *     This is free software: you can redistribute it and/or modify
+ *     it under the terms of the GNU Lesser General Public License as published by
+ *     the Free Software Foundation, either version 3 of the License, or
+ *     (at your option) any later version.
+ *
+ *     this software is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU Lesser General Public License for more details.
+ *
+ *     You should have received a copy of the GNU Lesser General Public License
+ *     along with this software.  If not, see <http://www.gnu.org/licenses/>.
+ */
+define([ "jquery", "./NewCustomFieldOptionDialog", "backbone", "underscore", "jeditable.simpleJEditable",
+		"jeditable.selectJEditable", "app/util/StringUtil", "jquery.squash", "jqueryui", "jquery.squash.togglepanel", "jeditable.selectJEditable",
+		"jquery.squash.datatables", "jquery.squash.oneshotdialog", "jquery.squash.messagedialog", "jquery.squash.confirmdialog" ], function($,
+		NewCustomFieldOptionDialog, Backbone, _, SimpleJEditable, SelectJEditable, StringUtil) {
+	var cfMod = squashtm.app.cfMod;
+	/*
+	 * Defines the controller for the custom fields table.
+	 */
+	var CustomFieldModificationView = Backbone.View.extend({
+		el : "#information-content",
+		initialize : function() {
+			this.inputType = $("#cuf-inputType").data("type");
+			
+			this.optionalCheckbox = this.$("#cf-optional").get(0);
+			
+			this.configureTogglePanels();
+			this.configureEditables();
+			this.configureRenamePopup();
+			this.configureRenameOptionPopup();
+			this.configureChangeOptionCodePopup();
+			this.configureOptionTable();
+			this.configureButtons();
+			// this line below is here because toggle panel buttons
+			// cannot be bound with the 'events' property of
+			// Backbone.View.
+			// my guess is that the event is bound to the button
+			// before it is moved from it's "span.not-displayed"
+			// to the toggle panel header.
+			// TODO change our way to make toggle panels buttons
+			this.$("#add-cuf-option-button").on("click", $.proxy(this.openAddOptionPopup, this));
+			
+			// dialog is moved from DOM when widgetized => we need to store it
+			this.confirmDeletionDialog = this.$("#delete-warning-pane").confirmDialog();
+			//...and we cannot use the events hash
+			this.confirmDeletionDialog.on("confirmdialogconfirm", $.proxy(this.deleteCustomField, this));
+		},
+
+		events : {
+			"click #cf-optional" : "confirmOptional",
+			"click .is-default>input:checkbox" : "changeDefaultOption",
+			"click .opt-label" : "openRenameOptionPopup",
+			"click .opt-code" : "openChangeOptionCodePopup", 
+			"click #delete-cuf-button" : "confirmCustomFieldDeletion"
+		},
+
+		confirmCustomFieldDeletion: function(event) {
+			this.confirmDeletionDialog.confirmDialog("open");
+		},
+		
+		deleteCustomField: function(event) {
+			var self = this;
+			
+			$.ajax({
+				type: "delete",
+				url: "" 
+			}).done(function() {
+				self.trigger("customfield.delete");
+			});
+			
+		},
+		
+		confirmOptional : function(event) {
+			var self = this;
+			var checked = event.target.checked;
+
+			if (checked) {
+				self.sendOptional(checked);
+
+			} else {
+				var defaultValue = self.findDefaultValue();
+				if (StringUtil.isBlank(defaultValue) || defaultValue === cfMod.richEditPlaceHolder) {
+					$.squash.openMessage(cfMod.popupErrorTitle, cfMod.mandatoryNeedsDefaultMessage, 350);
+					event.target.checked = true;
+					return;
+				}
+				var message = cfMod.confirmMandatoryMessage;
+				message = self.replacePlaceHolderByValue(0, message, defaultValue);
+				oneShotConfirm(cfMod.confirmMandatoryTitle, message, cfMod.confirmLabel, cfMod.cancelLabel, 500).done(
+						function() {
+							self.sendOptional(checked);
+						}).fail(function() {
+					event.target.checked = true;
+				});
+			}
+		},
+
+		findDefaultValue : function() {
+			var defaultValueDiv = this.$('#cuf-default-value');
+			if (defaultValueDiv && defaultValueDiv.length > 0) {
+				return $(defaultValueDiv[0]).text();
+				
+			} else if (this.optionsTable) {
+				var checkedDefault = this.optionsTable.find('td.is-default input:checked');
+				if (checkedDefault) {
+					return checkedDefault.val();
+				}
+			}
+			return "";
+		},
+
+		replacePlaceHolderByValue : function(index, message, replaceValue) {
+			var pattern = /\{[\d,\w,\s]*\}/;
+			var match = pattern.exec(message);
+			var pHolder = match[index];
+			return message.replace(pHolder, replaceValue);
+		},
+
+		sendOptional : function(optional) {
+			return $.ajax({
+				url : cfMod.customFieldUrl + "/optional",
+				type : "post",
+				data : {
+					'value' : optional
+				},
+				dataType : "json"
+			});
+		},
+
+		changeDefaultOption : function(event) {
+			var self = this;
+			var checkbox = event.currentTarget;
+			var option = checkbox.value;
+			var defaultValue = checkbox.checked ? option : "";
+			if (defaultValue === "" && this.isFieldMandatory()) {
+				checkbox.checked = true;
+				$.squash.openMessage(cfMod.popupErrorTitle, cfMod.defaultOptionMandatoryMessage);
+				return;
+			}
+			var uncheckSelector = ".is-default>input:checkbox" + (checkbox.checked ? "[value!='" + option + "']" : "");
+
+			this.sendDefaultValue(defaultValue).done(function() {
+				self.optionsTable.find(uncheckSelector).attr("checked", false);
+			}).fail(function() {
+				checkbox.checked = !checkbox.checked;
+			});
+
+		},
+
+		sendDefaultValue : function(defaultValue) {
+			return $.ajax({
+				url : cfMod.customFieldUrl + "/defaultValue",
+				type : 'POST',
+				data : {
+					'value' : defaultValue
+				},
+				dataType : 'json'
+			});
+		},
+
+		openRenameOptionPopup : function(event) {
+			var self = this;
+			var labelCell = event.currentTarget;
+			var previousValue = $(labelCell).text();
+
+			self.renameCufOptionPopup.find("#rename-cuf-option-previous").text(previousValue);
+			self.renameCufOptionPopup.find("#rename-cuf-option-label").val(previousValue);
+			self.renameCufOptionPopup.dialog("open");
+		},
+
+		openChangeOptionCodePopup : function(event) {
+			var self = this;
+			var codeCell = event.currentTarget;
+			var previousValue = $(codeCell).text();
+			var label = $(codeCell).parent("tr").find("td.opt-label").text();
+			self.changeOptionCodePopup.find("#change-cuf-option-code-label").text(label);
+			self.changeOptionCodePopup.find("#change-cuf-option-code").val(previousValue);
+			self.changeOptionCodePopup.dialog("open");
+		},
+
+		renameOption : function() {
+			var self = this;
+			var previousValue = self.renameCufOptionPopup.find("#rename-cuf-option-previous").text();
+			var newValue = self.renameCufOptionPopup.find("#rename-cuf-option-label").val();
+			$.ajax({
+				type : 'POST',
+				data : {
+					'value' : newValue
+				},
+				dataType : "json",
+				url : cfMod.optionsTable.ajaxSource + "/" + previousValue + "/label"
+			}).done(function(data) {
+				self.optionsTable.refresh();
+			});
+
+		},
+
+		changeOptionCode : function() {
+			var self = this;
+			var label = self.changeOptionCodePopup.find("#change-cuf-option-code-label").text();
+			var newValue = self.changeOptionCodePopup.find("#change-cuf-option-code").val();
+			$.ajax({
+				type : 'POST',
+				data : {
+					'value' : newValue
+				},
+				dataType : "json",
+				url : cfMod.optionsTable.ajaxSource + "/" + label + "/code"
+			}).done(function(data) {
+				self.optionsTable.refresh();
+			});
+
+		},
+