1"use strict";
2
3/*
4Used for the newcustomimage_modal actions
5
6The .data('recipe') value on the outer element determines which
7recipe ID is used as the basis for the new custom image recipe created via
8this modal.
9
10Use newCustomImageModalSetRecipes() to set the recipes available as a base
11for the new custom image. This will manage the addition of radio buttons
12to select the base image (or remove the radio buttons, if there is only a
13single base image available).
14*/
15
16function newCustomImageModalInit(){
17
18  var newCustomImgBtn = $("#create-new-custom-image-btn");
19  var imgCustomModal = $("#new-custom-image-modal");
20  var invalidNameHelp = $("#invalid-name-help");
21  var invalidRecipeHelp = $("#invalid-recipe-help");
22  var nameInput = imgCustomModal.find('input');
23
24  var invalidNameMsg = "Image names cannot contain spaces or capital letters. The only allowed special character is dash (-).";
25  var duplicateNameMsg = "An image with this name already exists. Image names must be unique.";
26  var duplicateImageInProjectMsg = "An image with this name already exists in this project."
27  var invalidBaseRecipeIdMsg = "Please select an image to customise.";
28  var missingParentRecipe = "The parent recipe file was not found. Cancel this action, build any target (like 'quilt-native') to force all new layers to clone, and try again";
29  var unknownError = "Unexpected error: ";
30
31  // set button to "submit" state and enable text entry so user can
32  // enter the custom recipe name
33  showSubmitState();
34
35  /* capture clicks on radio buttons inside the modal; when one is selected,
36   * set the recipe on the modal
37   */
38  imgCustomModal.on("click", "[name='select-image']", function(e) {
39    clearRecipeError();
40		$(".radio").each(function(){
41			$(this).removeClass("has-error");
42		});
43
44    var recipeId = $(e.target).attr('data-recipe');
45    imgCustomModal.data('recipe', recipeId);
46  });
47
48  newCustomImgBtn.click(function(e){
49    // disable the button and text entry
50    showLoadingState();
51
52    e.preventDefault();
53
54    var baseRecipeId = imgCustomModal.data('recipe');
55
56    if (!baseRecipeId) {
57      showRecipeError(invalidBaseRecipeIdMsg);
58			$(".radio").each(function(){
59				$(this).addClass("has-error");
60			});
61      return;
62    }
63
64    if (nameInput.val().length > 0) {
65      libtoaster.createCustomRecipe(nameInput.val(), baseRecipeId,
66      function(ret) {
67        showSubmitState();
68        if (ret.error !== "ok") {
69          console.warn(ret.error);
70          if (ret.error === "invalid-name") {
71            showNameError(invalidNameMsg);
72            return;
73          } else if (ret.error === "recipe-already-exists") {
74            showNameError(duplicateNameMsg);
75            return;
76          } else if (ret.error === "image-already-exists") {
77            showNameError(duplicateImageInProjectMsg);
78            return;
79          } else if (ret.error === "recipe-parent-not-exist") {
80            showNameError(missingParentRecipe);
81          } else {
82            showNameError(unknownError + ret.error);
83          }
84        } else {
85          imgCustomModal.modal('hide');
86          imgCustomModal.one('hidden.bs.modal', showSubmitState);
87          window.location.replace(ret.url + '?notify=new');
88        }
89      });
90    }
91  });
92
93  // enable text entry, show "Create image" button text
94  function showSubmitState() {
95    libtoaster.enableAjaxLoadingTimer();
96    newCustomImgBtn.find('[data-role="loading-state"]').hide();
97    newCustomImgBtn.find('[data-role="submit-state"]').show();
98    newCustomImgBtn.removeAttr('disabled');
99    nameInput.removeAttr('disabled');
100  }
101
102  // disable text entry, show "Creating image..." button text;
103  // we also disabled the page-level ajax loading spinner while this spinner
104  // is active
105  function showLoadingState() {
106    libtoaster.disableAjaxLoadingTimer();
107    newCustomImgBtn.find('[data-role="submit-state"]').hide();
108    newCustomImgBtn.find('[data-role="loading-state"]').show();
109    newCustomImgBtn.attr('disabled', 'disabled');
110    nameInput.attr('disabled', 'disabled');
111  }
112
113  function showNameError(text){
114    invalidNameHelp.text(text);
115    invalidNameHelp.show();
116    nameInput.parent().addClass('has-error');
117  }
118
119  function showRecipeError(text){
120    invalidRecipeHelp.text(text);
121    invalidRecipeHelp.show();
122  }
123
124  function clearRecipeError(){
125    invalidRecipeHelp.hide();
126  }
127
128  nameInput.on('keyup', function(){
129    if (nameInput.val().length === 0){
130      newCustomImgBtn.prop("disabled", true);
131      return
132    }
133
134    if (nameInput.val().search(/[^a-z|0-9|-]/) != -1){
135      showNameError(invalidNameMsg);
136      newCustomImgBtn.prop("disabled", true);
137      nameInput.parent().addClass('has-error');
138    } else {
139      invalidNameHelp.hide();
140      newCustomImgBtn.prop("disabled", false);
141      nameInput.parent().removeClass('has-error');
142    }
143  });
144}
145
146/* Set the image recipes which can used as the basis for the custom
147 * image recipe the user is creating
148 * baseRecipes: a list of one or more recipes which can be
149 * used as the base for the new custom image recipe in the format:
150 * [{'id': <recipe ID>, 'name': <recipe name>'}, ...]
151 *
152 * if recipes is a single recipe, just show the text box to set the
153 * name for the new custom image; if recipes contains multiple recipe objects,
154 * show a set of radio buttons so the user can decide which to use as the
155 * basis for the new custom image
156 */
157function newCustomImageModalSetRecipes(baseRecipes) {
158  var imgCustomModal = $("#new-custom-image-modal");
159  var imageSelector = $('#new-custom-image-modal [data-role="image-selector"]');
160  var imageSelectRadiosContainer = $('#new-custom-image-modal [data-role="image-selector-radios"]');
161
162  // remove any existing radio buttons + labels
163  imageSelector.remove('[data-role="image-radio"]');
164
165  if (baseRecipes.length === 1) {
166    // hide the radio button container
167    imageSelector.hide();
168
169    /* set the single recipe ID on the modal as it's the only one
170     * we can build from.
171     */
172    imgCustomModal.data('recipe', baseRecipes[0].id);
173  }
174  else {
175    // add radio buttons; note that the handlers for the radio buttons
176    // are set in newCustomImageModalInit via event delegation
177    for (var i = 0; i < baseRecipes.length; i++) {
178      var recipe = baseRecipes[i];
179      imageSelectRadiosContainer.append(
180        '<div class="radio"><label data-role="image-radio">' +
181        '<input type="radio" name="select-image" ' +
182        'data-recipe="' + recipe.id + '">' +
183        recipe.name +
184        '</label></div>'
185      );
186    }
187
188    /* select the first radio button as default selection. Radio button
189     * groups should always display with an option checked
190     */
191    imageSelectRadiosContainer.find("input:radio:first").attr("checked", "checked");
192
193    /* check which radio button is selected by default inside the modal,
194     * and set the recipe on the modal accordingly
195     */
196    imageSelectRadiosContainer.find("input:radio").each(function(){
197      if ( $(this).is(":checked") ) {
198        var recipeId = $(this).attr("data-recipe");
199        imgCustomModal.data("recipe", recipeId);
200      }
201    });
202
203    // show the radio button container
204    imageSelector.show();
205  }
206}
207