blob: 5a59799bc51227166f0da008e4d39d374a52d737 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"use strict"
2
3function importLayerPageInit (ctx) {
4
5 var layerDepBtn = $("#add-layer-dependency-btn");
6 var importAndAddBtn = $("#import-and-add-btn");
7 var layerNameInput = $("#import-layer-name");
8 var vcsURLInput = $("#layer-git-repo-url");
9 var gitRefInput = $("#layer-git-ref");
10 var layerDepInput = $("#layer-dependency");
11 var layerNameCtrl = $("#layer-name-ctrl");
12 var duplicatedLayerName = $("#duplicated-layer-name-hint");
13
14 var layerDeps = {};
15 var layerDepsDeps = {};
16 var currentLayerDepSelection;
17 var validLayerName = /^(\w|-)+$/;
18
19 libtoaster.makeTypeahead(layerDepInput, libtoaster.ctx.layersTypeAheadUrl, { include_added: "true" }, function(item){
20 currentLayerDepSelection = item;
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021 });
22
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050023 // choices available in the typeahead
24 var layerDepsChoices = {};
25
26 // when the typeahead choices change, store an array of the available layer
27 // choices locally, to use for enabling/disabling the "Add layer" button
28 layerDepInput.on("typeahead-choices-change", function (event, data) {
29 layerDepsChoices = {};
30
31 if (data.choices) {
32 data.choices.forEach(function (item) {
33 layerDepsChoices[item.name] = item;
34 });
35 }
36 });
37
38 // disable the "Add layer" button when the layer input typeahead is empty
39 // or not in the typeahead choices
40 layerDepInput.on("input change", function () {
41 // get the choices from the typeahead
42 var choice = layerDepsChoices[$(this).val()];
43
44 if (choice) {
45 layerDepBtn.removeAttr("disabled");
46 currentLayerDepSelection = choice;
47 }
48 else {
49 layerDepBtn.attr("disabled", "disabled");
50 currentLayerDepSelection = undefined;
51 }
52 });
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053
54 /* We automatically add "openembedded-core" layer for convenience as a
55 * dependency as pretty much all layers depend on this one
56 */
57 $.getJSON(libtoaster.ctx.layersTypeAheadUrl,
58 { include_added: "true" , search: "openembedded-core" },
59 function(layer) {
60 if (layer.results.length > 0) {
61 currentLayerDepSelection = layer.results[0];
62 layerDepBtn.click();
63 }
64 });
65
66 layerDepBtn.click(function(){
67 if (currentLayerDepSelection == undefined)
68 return;
69
70 layerDeps[currentLayerDepSelection.id] = currentLayerDepSelection;
71
72 /* Make a list item for the new layer dependency */
73 var newLayerDep = $("<li><a></a><span class=\"icon-trash\" data-toggle=\"tooltip\" title=\"Delete\"></span></li>");
74
75 newLayerDep.data('layer-id', currentLayerDepSelection.id);
76 newLayerDep.children("span").tooltip();
77
78 var link = newLayerDep.children("a");
79 link.attr("href", currentLayerDepSelection.layerdetailurl);
80 link.text(currentLayerDepSelection.name);
81 link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"});
82
83 var trashItem = newLayerDep.children("span");
84 trashItem.click(function () {
85 var toRemove = $(this).parent().data('layer-id');
86 delete layerDeps[toRemove];
87 $(this).parent().fadeOut(function (){
88 $(this).remove();
89 });
90 });
91
92 $("#layer-deps-list").append(newLayerDep);
93
94 libtoaster.getLayerDepsForProject(currentLayerDepSelection.layerdetailurl, function (data){
95 /* These are the dependencies of the layer added as a dependency */
96 if (data.list.length > 0) {
97 currentLayerDepSelection.url = currentLayerDepSelection.layerdetailurl;
98 layerDeps[currentLayerDepSelection.id].deps = data.list;
99 }
100
101 /* Clear the current selection */
102 layerDepInput.val("");
103 currentLayerDepSelection = undefined;
104 layerDepBtn.attr("disabled","disabled");
105 }, null);
106 });
107
108 importAndAddBtn.click(function(){
109 /* This is a list of the names from layerDeps for the layer deps
110 * modal dialog body
111 */
112 var depNames = [];
113
114 /* arrray of all layer dep ids includes parent and child deps */
115 var allDeps = [];
116
117 /* temporary object to use to do a reduce on the dependencies for each
118 * layer dependency added
119 */
120 var depDeps = {};
121
122 /* the layers that have dependencies have an extra property "deps"
123 * look in this for each layer and reduce this to a unquie object
124 * of deps.
125 */
126 for (var key in layerDeps){
127 if (layerDeps[key].hasOwnProperty('deps')){
128 for (var dep in layerDeps[key].deps){
129 var layer = layerDeps[key].deps[dep];
130 depDeps[layer.id] = layer;
131 }
132 }
133 depNames.push(layerDeps[key].name);
134 allDeps.push(layerDeps[key].id);
135 }
136
137 /* we actually want it as an array so convert it now */
138 var depDepsArray = [];
139 for (var key in depDeps)
140 depDepsArray.push (depDeps[key]);
141
142 if (depDepsArray.length > 0) {
143 var layer = { name: layerNameInput.val(), url: "#", id: -1 };
144 var title = "Layer";
145 var body = "<strong>"+layer.name+"</strong>'s dependencies ("+
146 depNames.join(", ")+"</span>) require some layers that are not added to your project. Select the ones you want to add:</p>";
147
148 showLayerDepsModal(layer, depDepsArray, title, body, false, function(layerObsList){
149 /* Add the accepted layer dependencies' ids to the allDeps array */
150 for (var key in layerObsList){
151 allDeps.push(layerObsList[key].id);
152 }
153 import_and_add ();
154 });
155 } else {
156 import_and_add ();
157 }
158
159 function import_and_add () {
160 /* convert to a csv of all the deps to be added */
161 var layerDepsCsv = allDeps.join(",");
162
163 var layerData = {
164 name: layerNameInput.val(),
165 vcs_url: vcsURLInput.val(),
166 git_ref: gitRefInput.val(),
167 dir_path: $("#layer-subdir").val(),
168 project_id: libtoaster.ctx.projectId,
169 layer_deps: layerDepsCsv,
170 };
171
172 $.ajax({
173 type: "POST",
174 url: ctx.xhrImportLayerUrl,
175 data: layerData,
176 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
177 success: function (data) {
178 if (data.error != "ok") {
179 console.log(data.error);
180 } else {
181 /* Success layer import now go to the project page */
182 $.cookie('layer-imported-alert', JSON.stringify(data), { path: '/'});
183 window.location.replace(libtoaster.ctx.projectPageUrl+'?notify=layer-imported');
184 }
185 },
186 error: function (data) {
187 console.log("Call failed");
188 console.log(data);
189 }
190 });
191 }
192 });
193
194 function enable_import_btn(enabled) {
195 var importAndAddHint = $("#import-and-add-hint");
196
197 if (enabled) {
198 importAndAddBtn.removeAttr("disabled");
199 importAndAddHint.hide();
200 return;
201 }
202
203 importAndAddBtn.attr("disabled", "disabled");
204 importAndAddHint.show();
205 }
206
207 function check_form() {
208 var valid = false;
209 var inputs = $("input:required");
210
211 for (var i=0; i<inputs.length; i++){
212 if (!(valid = inputs[i].value)){
213 enable_import_btn(false);
214 break;
215 }
216 }
217
218 if (valid)
219 enable_import_btn(true);
220 }
221
222 function layerExistsError(layer){
223 var dupLayerInfo = $("#duplicate-layer-info");
224 dupLayerInfo.find(".dup-layer-name").text(layer.name);
225 dupLayerInfo.find(".dup-layer-link").attr("href", layer.layerdetailurl);
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500226 dupLayerInfo.find("#dup-layer-vcs-url").text(layer.vcs_url);
227 dupLayerInfo.find("#dup-layer-revision").text(layer.vcs_reference);
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500228
229 $(".fields-apart-from-layer-name").fadeOut(function(){
230
231 dupLayerInfo.fadeIn();
232 });
233 }
234
235 layerNameInput.on('blur', function() {
236 if (!$(this).val()){
237 return;
238 }
239 var name = $(this).val();
240
241 /* Check if the layer name exists */
242 $.getJSON(libtoaster.ctx.layersTypeAheadUrl,
243 { include_added: "true" , search: name, format: "json" },
244 function(layer) {
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500245 if (layer.results.length > 0) {
246 for (var i in layer.results){
247 if (layer.results[i].name == name) {
248 layerExistsError(layer.results[i]);
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249 }
250 }
251 }
252 });
253 });
254
255 vcsURLInput.on('input', function() {
256 check_form();
257 });
258
259 gitRefInput.on('input', function() {
260 check_form();
261 });
262
263 layerNameInput.on('input', function() {
264 if ($(this).val() && !validLayerName.test($(this).val())){
265 layerNameCtrl.addClass("error")
266 $("#invalid-layer-name-hint").show();
267 enable_import_btn(false);
268 return;
269 }
270
271 if ($("#duplicate-layer-info").css("display") != "None"){
272 $("#duplicate-layer-info").fadeOut(function(){
273 $(".fields-apart-from-layer-name").show();
274 });
275
276 }
277
278 /* Don't remove the error class if we're displaying the error for another
279 * reason.
280 */
281 if (!duplicatedLayerName.is(":visible"))
282 layerNameCtrl.removeClass("error")
283
284 $("#invalid-layer-name-hint").hide();
285 check_form();
286 });
287
288 /* Have a guess at the layer name */
289 vcsURLInput.focusout(function (){
290 /* If we a layer name specified don't overwrite it or if there isn't a
291 * url typed in yet return
292 */
293 if (layerNameInput.val() || !$(this).val())
294 return;
295
296 if ($(this).val().search("/")){
297 var urlPts = $(this).val().split("/");
298 var suggestion = urlPts[urlPts.length-1].replace(".git","");
299 layerNameInput.val(suggestion);
300 }
301 });
302
303}