1"use strict";
2
3function layerDetailsPageInit (ctx) {
4
5  var layerDepInput = $("#layer-dep-input");
6  var layerDepBtn = $("#add-layer-dependency-btn");
7  var layerDepsList = $("#layer-deps-list");
8  var currentLayerDepSelection;
9  var addRmLayerBtn = $("#add-remove-layer-btn");
10  var targetTab = $("#targets-tab");
11  var machineTab = $("#machines-tab");
12  var detailsTab = $("#details-tab");
13  var editLayerSource = $("#edit-layer-source");
14  var saveSourceChangesBtn = $("#save-changes-for-switch");
15  var layerGitRefInput = $("#layer-git-ref");
16  var layerSubDirInput = $('#layer-subdir');
17
18  targetTab.on('show.bs.tab', targetsTabShow);
19  detailsTab.on('show.bs.tab', detailsTabShow);
20  machineTab.on('show.bs.tab', machinesTabShow);
21
22  /* setup the dependencies typeahead */
23  libtoaster.makeTypeahead(layerDepInput,
24                           libtoaster.ctx.layersTypeAheadUrl,
25                           { include_added: "true" }, function(item){
26    currentLayerDepSelection = item;
27    layerDepBtn.removeAttr("disabled");
28  });
29
30  /* disable the add layer button if its input field is empty */
31  layerDepInput.on("keyup",function(){
32    if ($(this).val().length === 0) {
33      layerDepBtn.attr("disabled", "disabled");
34    }
35  });
36
37  function addRemoveDep(depLayerId, add, doneCb) {
38    var data = { layer_version_id : ctx.layerVersion.id };
39    if (add)
40      data.add_dep = depLayerId;
41    else
42      data.rm_dep = depLayerId;
43
44    $.ajax({
45        type: "POST",
46        url: ctx.xhrUpdateLayerUrl,
47        data: data,
48        headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
49        success: function (data) {
50          if (data.error != "ok") {
51            console.warn(data.error);
52          } else {
53            doneCb();
54          }
55        },
56        error: function (data) {
57          console.warn("Call failed");
58          console.warn(data);
59        }
60    });
61  }
62
63  function layerDepRemoveClick() {
64    var toRemove = $(this).parent().data('layer-id');
65    var layerDepItem = $(this);
66
67    addRemoveDep(toRemove, false, function(){
68      layerDepItem.parent().fadeOut(function (){
69        layerDepItem.remove();
70      });
71    });
72  }
73
74  /* Add dependency layer button click handler */
75  layerDepBtn.click(function(){
76    if (currentLayerDepSelection === undefined)
77      return;
78
79    addRemoveDep(currentLayerDepSelection.id, true, function(){
80      /* Make a list item for the new layer dependency */
81      var newLayerDep = $("<li><a></a><span class=\"glyphicon glyphicon-trash\" data-toggle=\"tooltip\" title=\"Delete\"></span></li>");
82
83      newLayerDep.data('layer-id', currentLayerDepSelection.id);
84      newLayerDep.children("span").tooltip();
85
86      var link = newLayerDep.children("a");
87      link.attr("href", currentLayerDepSelection.layerdetailurl);
88      link.text(currentLayerDepSelection.name);
89      link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"});
90
91      /* Connect up the tash icon */
92      var trashItem = newLayerDep.children("span");
93      trashItem.click(layerDepRemoveClick);
94
95      layerDepsList.append(newLayerDep);
96      /* Clear the current selection */
97      layerDepInput.val("");
98      currentLayerDepSelection = undefined;
99      layerDepBtn.attr("disabled", "disabled");
100    });
101  });
102
103  $(".glyphicon-edit").click(function (){
104    var mParent = $(this).parent("dd");
105    mParent.prev().css("margin-top", "10px");
106    mParent.children("form").slideDown();
107    var currentVal = mParent.children(".current-value");
108    currentVal.hide();
109    /* Set the current value to the input field */
110    mParent.find("textarea,input").val(currentVal.text());
111    /* If the input field is empty, disable the submit button */
112    if ( mParent.find("textarea,input").val().length == 0 ) {
113      mParent.find(".change-btn").attr("disabled", "disabled");
114    }
115    /* Hides the "Not set" text */
116    mParent.children(".text-muted").hide();
117    /* We're editing so hide the delete icon */
118    mParent.children(".delete-current-value").hide();
119    mParent.find(".cancel").show();
120    $(this).hide();
121  });
122
123  $(".delete-current-value").click(function(){
124    var mParent = $(this).parent("dd");
125    mParent.find("input").val("");
126    mParent.find("textarea").val("");
127    mParent.find(".change-btn").click();
128  });
129
130  $(".cancel").click(function(){
131    var mParent = $(this).parents("dd");
132    $(this).hide();
133    mParent.children("form").slideUp(function(){
134      mParent.children(".current-value").show();
135      /* Show the "Not set" text if we ended up with no value */
136      if (!mParent.children(".current-value").html()){
137        mParent.children(".text-muted").fadeIn();
138        mParent.children(".delete-current-value").hide();
139      } else {
140        mParent.children(".delete-current-value").show();
141      }
142
143      mParent.children(".glyphicon-edit").show();
144      mParent.prev().css("margin-top", "0");
145    });
146  });
147
148
149  function defaultAddBtnText(){
150      var text = " Add the "+ctx.layerVersion.name+" layer to your project";
151      addRmLayerBtn.text(text);
152      addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-plus\"></span>");
153      addRmLayerBtn.removeClass("btn-danger");
154  }
155
156  function detailsTabShow(){
157    if (!ctx.layerVersion.inCurrentPrj)
158      defaultAddBtnText();
159
160    window.location.hash = "information";
161  }
162
163  function targetsTabShow(){
164    if (!ctx.layerVersion.inCurrentPrj){
165      if (ctx.numTargets > 0) {
166        var text = " Add the "+ctx.layerVersion.name+" layer to your project "+
167          "to enable these recipes";
168        addRmLayerBtn.text(text);
169        addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-plus\"></span>");
170      } else {
171        defaultAddBtnText();
172      }
173    }
174
175    window.location.hash = "recipes";
176  }
177
178  $("#recipestable").on('table-done', function(e, total, tableParams){
179    ctx.numTargets = total;
180
181    if (total === 0 && !tableParams.search) {
182      $("#no-recipes-yet").show();
183    } else {
184      $("#no-recipes-yet").hide();
185    }
186
187    targetTab.removeClass("text-muted");
188    if (window.location.hash === "#recipes"){
189      /* re run the machinesTabShow to update the text */
190      targetsTabShow();
191    }
192  });
193
194  $("#machinestable").on('table-done', function(e, total, tableParams){
195    ctx.numMachines = total;
196
197    if (total === 0 && !tableParams.search)
198      $("#no-machines-yet").show();
199    else
200      $("#no-machines-yet").hide();
201
202    machineTab.removeClass("text-muted");
203    if (window.location.hash === "#machines"){
204      /* re run the machinesTabShow to update the text */
205      machinesTabShow();
206    }
207
208    $(".select-machine-btn").click(function(e){
209      if ($(this).hasClass("disabled"))
210        e.preventDefault();
211    });
212
213  });
214
215
216  function machinesTabShow(){
217    if (!ctx.layerVersion.inCurrentPrj) {
218      if (ctx.numMachines > 0){
219        var text = " Add the "+ctx.layerVersion.name+" layer to your project " +
220          "to enable these machines";
221        addRmLayerBtn.text(text);
222        addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-plus\"></span>");
223      } else {
224        defaultAddBtnText();
225      }
226    }
227
228    window.location.hash = "machines";
229  }
230
231  $(".pagesize").change(function(){
232    var search = libtoaster.parseUrlParams();
233    search.limit = this.value;
234
235    window.location.search = libtoaster.dumpsUrlParams(search);
236  });
237
238  /* Enables the Build target and Select Machine buttons and switches the
239   * add/remove button
240   */
241  function setLayerInCurrentPrj(added) {
242    ctx.layerVersion.inCurrentPrj = added;
243
244    if (added){
245      /* enable and switch all the button states */
246      $(".build-recipe-btn").removeClass("disabled");
247      $(".select-machine-btn").removeClass("disabled");
248      addRmLayerBtn.addClass("btn-danger");
249      addRmLayerBtn.data('directive', "remove");
250      addRmLayerBtn.text(" Remove the "+ctx.layerVersion.name+" layer from your project");
251      addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-trash\"></span>");
252
253    } else {
254      /* disable and switch all the button states */
255      $(".build-recipe-btn").addClass("disabled");
256      $(".select-machine-btn").addClass("disabled");
257      addRmLayerBtn.removeClass("btn-danger");
258      addRmLayerBtn.data('directive', "add");
259
260      /* "special" handler so that we get the correct button text which depends
261       * on which tab is currently visible. Unfortunately we can't just call
262       * tab('show') as if it's already visible it doesn't run the event.
263       */
264      switch ($(".nav-tabs .active a").prop('id')){
265        case 'machines-tab':
266          machinesTabShow();
267          break;
268        case 'targets-tab':
269          targetsTabShow();
270          break;
271        default:
272          defaultAddBtnText();
273          break;
274      }
275    }
276  }
277
278  $("#dismiss-alert").click(function(){
279    $(this).parent().fadeOut();
280  });
281
282  /* Add or remove this layer from the project */
283  addRmLayerBtn.click(function() {
284
285    var add = ($(this).data('directive') === "add");
286
287    libtoaster.addRmLayer(ctx.layerVersion, add, function (layersList){
288      var alertMsg = $("#alert-msg");
289      alertMsg.html(libtoaster.makeLayerAddRmAlertMsg(ctx.layerVersion, layersList, add));
290
291      setLayerInCurrentPrj(add);
292
293      libtoaster.showChangeNotification(alertMsg);
294    });
295  });
296
297  /* Handler for all of the Change buttons */
298  $(".change-btn").click(function(){
299    var mParent = $(this).parent();
300    var prop = $(this).data('layer-prop');
301
302    /* We have inputs, select and textareas to potentially grab the value
303     * from.
304     */
305    var entryElement = mParent.find("input");
306    if (entryElement.length === 0)
307      entryElement = mParent.find("textarea");
308    if (entryElement.length === 0) {
309      console.warn("Could not find element to get data from for this change");
310      return;
311    }
312
313    var data = { layer_version_id: ctx.layerVersion.id };
314    data[prop] = entryElement.val();
315
316    $.ajax({
317        type: "POST",
318        url: ctx.xhrUpdateLayerUrl,
319        data: data,
320        headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
321        success: function (data) {
322          if (data.error != "ok") {
323            console.warn(data.error);
324          } else {
325            /* success layer property changed */
326            var inputArea = mParent.parents("dd");
327            var text;
328
329            text = entryElement.val();
330
331            /* Hide the "Not set" text if it's visible */
332            inputArea.find(".text-muted").hide();
333            inputArea.find(".current-value").text(text);
334            /* Same behaviour as cancel in that we hide the form/show current
335             * value.
336             */
337            inputArea.find(".cancel").click();
338          }
339        },
340        error: function (data) {
341          console.warn("Call failed");
342          console.warn(data);
343        }
344    });
345  });
346
347  /* Disable the change button when we have no data in the input */
348  $("dl input, dl textarea").on("input",function() {
349    if ($(this).val().length === 0)
350      $(this).parent().next(".change-btn").attr("disabled", "disabled");
351    else
352      $(this).parent().next(".change-btn").removeAttr("disabled");
353  });
354
355  /* This checks to see if the dt's dd has data in it or if the change data
356   * form is visible, otherwise hide it
357   */
358  $("dl").children().each(function (){
359    if ($(this).is("dt")) {
360      var dd = $(this).next("dd");
361      if (!dd.children("form:visible")|| !dd.find(".current-value").html()){
362        if (ctx.layerVersion.layer_source == ctx.layerSourceTypes.TYPE_IMPORTED ||
363            ctx.layerVersion.layer_source == ctx.layerSourceTypes.TYPE_LOCAL) {
364        /* There's no current value and the layer is editable
365         * so show the "Not set" and hide the delete icon
366         */
367        dd.find(".text-muted").show();
368        dd.find(".delete-current-value").hide();
369        } else {
370          /* We're not viewing an editable layer so hide the empty dd/dl pair */
371          $(this).hide();
372          dd.hide();
373        }
374      }
375    }
376  });
377
378  /* Hide the right column if it contains no information */
379  if ($("dl.item-info").children(':visible').length === 0) {
380    $("dl.item-info").parent().hide();
381  }
382
383  /* Clear the current search selection and reload the results */
384  $(".target-search-clear").click(function(){
385    $("#target-search").val("");
386    $(this).parents("form").submit();
387  });
388
389  $(".machine-search-clear").click(function(){
390    $("#machine-search").val("");
391    $(this).parents("form").submit();
392  });
393
394  $("#layer-delete-confirmed").click(function(){
395
396    $("#delete-layer-modal button[data-dismiss='modal']").hide();
397
398    var message = $('<span>You have deleted <strong>1</strong> layer from your project: <strong id="deleted-layer-name"></strong>');
399    message.find("#deleted-layer-name").text(ctx.layerVersion.name);
400
401    $.ajax({
402        type: "DELETE",
403        url: ctx.xhrUpdateLayerUrl,
404        headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
405        success: function(data) {
406          if (data.error != "ok") {
407            console.warn(data.error);
408          } else {
409            libtoaster.setNotification("layer-deleted", message.html());
410            window.location.replace(data.gotoUrl);
411          }
412        },
413        error: function(data) {
414          console.warn("Call failed");
415          console.warn(data);
416        }
417    });
418  });
419
420  layerDepsList.find(".glyphicon-trash").click(layerDepRemoveClick);
421  layerDepsList.find("a").tooltip();
422  $(".glyphicon-trash").tooltip();
423  $(".commit").tooltip();
424
425  editLayerSource.click(function() {
426    /* Kindly bring the git layers imported from layerindex to normal page
427     * and not this new page :(
428     */
429    $(this).hide();
430    saveSourceChangesBtn.attr("disabled", "disabled");
431
432    $("#git-repo-info, #directory-info").hide();
433    $("#edit-layer-source-form").fadeIn();
434    if ($("#layer-dir-path-in-details").val() == "") {
435      //Local dir path is empty...
436      $("#repo").prop("checked", true);
437      $("#layer-git").fadeIn();
438      $("#layer-dir").hide();
439    } else {
440      $("#layer-git").hide();
441      $("#layer-dir").fadeIn();
442    }
443  });
444
445  $('input:radio[name="source-location"]').change(function() {
446    if ($('input[name=source-location]:checked').val() == "repo") {
447      $("#layer-git").fadeIn();
448      $("#layer-dir").hide();
449      if ($("#layer-git-repo-url").val().length === 0 && layerGitRefInput.val().length === 0) {
450        saveSourceChangesBtn.attr("disabled", "disabled");
451      }
452    } else {
453      $("#layer-dir").fadeIn();
454      $("#layer-git").hide();
455    }
456  });
457
458  $("#layer-dir-path-in-details").keyup(function() {
459    saveSourceChangesBtn.removeAttr("disabled");
460  });
461
462  $("#layer-git-repo-url").keyup(function() {
463    if ($("#layer-git-repo-url").val().length > 0 && layerGitRefInput.val().length > 0) {
464      saveSourceChangesBtn.removeAttr("disabled");
465    }
466  });
467
468  layerGitRefInput.keyup(function() {
469    if ($("#layer-git-repo-url").val().length > 0 && layerGitRefInput.val().length > 0) {
470      saveSourceChangesBtn.removeAttr("disabled");
471    }
472  });
473
474
475  layerSubDirInput.keyup(function(){
476    if ($(this).val().length > 0){
477      saveSourceChangesBtn.removeAttr("disabled");
478    }
479  });
480
481  $('#cancel-changes-for-switch').click(function() {
482    $("#edit-layer-source-form").hide();
483    $("#directory-info, #git-repo-info").fadeIn();
484    editLayerSource.show();
485  });
486
487  saveSourceChangesBtn.click(function() {
488
489    var layerData = {
490      vcs_url: $('#layer-git-repo-url').val(),
491      commit: layerGitRefInput.val(),
492      dirpath: layerSubDirInput.val(),
493      local_source_dir: $('#layer-dir-path-in-details').val(),
494    };
495
496    if ($('input[name=source-location]:checked').val() == "repo") {
497      layerData.local_source_dir = "";
498    } else {
499      layerData.vcs_url = "";
500      layerData.git_ref = "";
501    }
502
503    $.ajax({
504        type: "POST",
505        url: ctx.xhrUpdateLayerUrl,
506        data: layerData,
507        headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
508        success: function (data) {
509          if (data.error != "ok") {
510            console.warn(data.error);
511          } else {
512            /* success layer property changed */
513            window.location.reload();
514          }
515        },
516        error: function (data) {
517          console.warn("Call failed");
518          console.warn(data);
519        }
520    });
521  });
522}
523