1"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 var localDirPath = $("#local-dir-path"); 14 15 var layerDeps = {}; 16 var layerDepsDeps = {}; 17 var currentLayerDepSelection; 18 var validLayerName = /^(\w|-)+$/; 19 20 libtoaster.makeTypeahead(layerDepInput, 21 libtoaster.ctx.layersTypeAheadUrl, 22 { include_added: "true" }, function(item){ 23 currentLayerDepSelection = item; 24 layerDepBtn.removeAttr("disabled"); 25 }); 26 27 layerDepInput.on("typeahead:select", function(event, data){ 28 currentLayerDepSelection = data; 29 }); 30 31 // Disable local dir repo when page is loaded. 32 $('#local-dir').hide(); 33 34 // disable the "Add layer" button when the layer input typeahead is empty 35 // or not in the typeahead choices 36 layerDepInput.on("input change", function(){ 37 layerDepBtn.attr("disabled","disabled"); 38 }); 39 40 /* We automatically add "openembedded-core" layer for convenience as a 41 * dependency as pretty much all layers depend on this one 42 */ 43 $.getJSON(libtoaster.ctx.layersTypeAheadUrl, 44 { include_added: "true" , search: "openembedded-core" }, 45 function(layer) { 46 if (layer.results.length > 0) { 47 currentLayerDepSelection = layer.results[0]; 48 layerDepBtn.click(); 49 } 50 }); 51 52 layerDepBtn.click(function(){ 53 if (currentLayerDepSelection == undefined) 54 return; 55 56 layerDeps[currentLayerDepSelection.id] = currentLayerDepSelection; 57 58 /* Make a list item for the new layer dependency */ 59 var newLayerDep = $("<li><a></a><span class=\"glyphicon glyphicon-trash\" data-toggle=\"tooltip\" title=\"Remove\"></span></li>"); 60 61 newLayerDep.data('layer-id', currentLayerDepSelection.id); 62 newLayerDep.children("span").tooltip(); 63 64 var link = newLayerDep.children("a"); 65 link.attr("href", currentLayerDepSelection.layerdetailurl); 66 link.text(currentLayerDepSelection.name); 67 link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"}); 68 69 var trashItem = newLayerDep.children("span"); 70 trashItem.click(function () { 71 var toRemove = $(this).parent().data('layer-id'); 72 delete layerDeps[toRemove]; 73 $(this).parent().fadeOut(function (){ 74 $(this).remove(); 75 }); 76 }); 77 78 $("#layer-deps-list").append(newLayerDep); 79 80 libtoaster.getLayerDepsForProject(currentLayerDepSelection.layerdetailurl, 81 function (data){ 82 /* These are the dependencies of the layer added as a dependency */ 83 if (data.list.length > 0) { 84 currentLayerDepSelection.url = currentLayerDepSelection.layerdetailurl; 85 layerDeps[currentLayerDepSelection.id].deps = data.list; 86 } 87 88 /* Clear the current selection */ 89 layerDepInput.val(""); 90 currentLayerDepSelection = undefined; 91 layerDepBtn.attr("disabled","disabled"); 92 }, null); 93 }); 94 95 importAndAddBtn.click(function(e){ 96 e.preventDefault(); 97 /* This is a list of the names from layerDeps for the layer deps 98 * modal dialog body 99 */ 100 var depNames = []; 101 102 /* arrray of all layer dep ids includes parent and child deps */ 103 var allDeps = []; 104 105 /* temporary object to use to do a reduce on the dependencies for each 106 * layer dependency added 107 */ 108 var depDeps = {}; 109 110 /* the layers that have dependencies have an extra property "deps" 111 * look in this for each layer and reduce this to a unquie object 112 * of deps. 113 */ 114 for (var key in layerDeps){ 115 if (layerDeps[key].hasOwnProperty('deps')){ 116 for (var dep in layerDeps[key].deps){ 117 var layer = layerDeps[key].deps[dep]; 118 depDeps[layer.id] = layer; 119 } 120 } 121 depNames.push(layerDeps[key].name); 122 allDeps.push(layerDeps[key].id); 123 } 124 125 /* we actually want it as an array so convert it now */ 126 var depDepsArray = []; 127 for (var key in depDeps) 128 depDepsArray.push (depDeps[key]); 129 130 if (depDepsArray.length > 0) { 131 var layer = { name: layerNameInput.val(), url: "#", id: -1 }; 132 var title = "Layer"; 133 var body = "<strong>"+layer.name+"</strong>'s dependencies ("+ 134 depNames.join(", ")+"</span>) require some layers that are not added to your project. Select the ones you want to add:</p>"; 135 136 showLayerDepsModal(layer, 137 depDepsArray, 138 title, body, false, function(layerObsList){ 139 /* Add the accepted layer dependencies' ids to the allDeps array */ 140 for (var key in layerObsList){ 141 allDeps.push(layerObsList[key].id); 142 } 143 import_and_add (); 144 }); 145 } else { 146 import_and_add (); 147 } 148 149 function import_and_add () { 150 /* convert to a csv of all the deps to be added */ 151 var layerDepsCsv = allDeps.join(","); 152 153 var layerData = { 154 name: layerNameInput.val(), 155 vcs_url: vcsURLInput.val(), 156 git_ref: gitRefInput.val(), 157 dir_path: $("#layer-subdir").val(), 158 project_id: libtoaster.ctx.projectId, 159 layer_deps: layerDepsCsv, 160 local_source_dir: $('#local-dir-path').val(), 161 add_to_project: true, 162 }; 163 164 if ($('input[name=repo]:checked').val() == "git") { 165 layerData.local_source_dir = ""; 166 } else { 167 layerData.vcs_url = ""; 168 layerData.git_ref = ""; 169 } 170 171 $.ajax({ 172 type: "PUT", 173 url: ctx.xhrLayerUrl, 174 data: JSON.stringify(layerData), 175 headers: { 'X-CSRFToken' : $.cookie('csrftoken')}, 176 success: function (data) { 177 if (data.error != "ok") { 178 console.log(data.error); 179 /* let the user know why nothing happened */ 180 alert(data.error) 181 } else { 182 createImportedNotification(data); 183 window.location.replace(libtoaster.ctx.projectPageUrl); 184 } 185 }, 186 error: function (data) { 187 console.log("Call failed"); 188 console.log(data); 189 } 190 }); 191 } 192 }); 193 194 /* Layer imported notification */ 195 function createImportedNotification(imported){ 196 var message = "Layer imported"; 197 198 if (imported.deps_added.length === 0) { 199 message = "You have imported <strong><a class=\"alert-link\" href=\""+imported.imported_layer.layerdetailurl+"\">"+imported.imported_layer.name+"</a></strong> and added it to your project."; 200 } else { 201 202 var links = "<a href=\""+imported.imported_layer.layerdetailurl+"\">"+imported.imported_layer.name+"</a>, "; 203 204 imported.deps_added.map (function(item, index){ 205 links +='<a href="'+item.layerdetailurl+'">'+item.name+'</a>'; 206 /*If we're at the last element we don't want the trailing comma */ 207 if (imported.deps_added[index+1] !== undefined) 208 links += ', '; 209 }); 210 211 /* Length + 1 here to do deps + the imported layer */ 212 message = 'You have imported <strong><a href="'+imported.imported_layer.layerdetailurl+'">'+imported.imported_layer.name+'</a></strong> and added <strong>'+(imported.deps_added.length+1)+'</strong> layers to your project: <strong>'+links+'</strong>'; 213 } 214 215 libtoaster.setNotification("layer-imported", message); 216 } 217 218 function enable_import_btn(enabled) { 219 var importAndAddHint = $("#import-and-add-hint"); 220 221 if (enabled) { 222 importAndAddBtn.removeAttr("disabled"); 223 importAndAddHint.hide(); 224 return; 225 } 226 227 importAndAddBtn.attr("disabled", "disabled"); 228 importAndAddHint.show(); 229 } 230 231 function check_form() { 232 var valid = false; 233 var inputs = $("input:required"); 234 var inputStr = inputs.val().split(""); 235 236 for (var i=0; i<inputs.val().length; i++){ 237 if (!(valid = inputStr[i])){ 238 enable_import_btn(false); 239 break; 240 } 241 } 242 243 if (valid) { 244 if ($("#local-dir-radio").prop("checked") && 245 localDirPath.val().length > 0) { 246 enable_import_btn(true); 247 } 248 249 if ($("#git-repo-radio").prop("checked")) { 250 if (gitRefInput.val().length > 0 && 251 gitRefInput.val() == 'HEAD') { 252 $('#invalid-layer-revision-hint').show(); 253 $('#layer-revision-ctrl').addClass('has-error'); 254 enable_import_btn(false); 255 } else if (vcsURLInput.val().length > 0 && 256 gitRefInput.val().length > 0) { 257 $('#invalid-layer-revision-hint').hide(); 258 $('#layer-revision-ctrl').removeClass('has-error'); 259 enable_import_btn(true); 260 } 261 } 262 } 263 264 if (inputs.val().length == 0) 265 enable_import_btn(false); 266 } 267 268 function layerExistsError(layer){ 269 var dupLayerInfo = $("#duplicate-layer-info"); 270 271 if (layer.local_source_dir) { 272 $("#git-layer-dup").hide(); 273 $("#local-layer-dup").fadeIn(); 274 dupLayerInfo.find(".dup-layer-name").text(layer.name); 275 dupLayerInfo.find(".dup-layer-link").attr("href", layer.layerdetailurl); 276 dupLayerInfo.find("#dup-local-source-dir-name").text(layer.local_source_dir); 277 } else { 278 $("#git-layer-dup").fadeIn(); 279 $("#local-layer-dup").hide(); 280 dupLayerInfo.find(".dup-layer-name").text(layer.name); 281 dupLayerInfo.find(".dup-layer-link").attr("href", layer.layerdetailurl); 282 dupLayerInfo.find("#dup-layer-vcs-url").text(layer.vcs_url); 283 dupLayerInfo.find("#dup-layer-revision").text(layer.vcs_reference); 284 } 285 $(".fields-apart-from-layer-name").fadeOut(function(){ 286 287 dupLayerInfo.fadeIn(); 288 }); 289 } 290 291 layerNameInput.on('blur', function() { 292 if (!$(this).val()){ 293 return; 294 } 295 var name = $(this).val(); 296 297 /* Check if the layer name exists */ 298 $.getJSON(libtoaster.ctx.layersTypeAheadUrl, 299 { include_added: "true" , search: name, format: "json" }, 300 function(layer) { 301 if (layer.results.length > 0) { 302 for (var i in layer.results){ 303 if (layer.results[i].name == name) { 304 layerExistsError(layer.results[i]); 305 } 306 } 307 } 308 }); 309 }); 310 311 vcsURLInput.on('input', function() { 312 check_form(); 313 }); 314 315 gitRefInput.on('input', function() { 316 check_form(); 317 }); 318 319 layerNameInput.on('input', function() { 320 if ($(this).val() && !validLayerName.test($(this).val())){ 321 layerNameCtrl.addClass("has-error") 322 $("#invalid-layer-name-hint").show(); 323 enable_import_btn(false); 324 return; 325 } 326 327 if ($("#duplicate-layer-info").css("display") != "None"){ 328 $("#duplicate-layer-info").fadeOut(function(){ 329 $(".fields-apart-from-layer-name").show(); 330 radioDisplay(); 331 }); 332 333 } 334 335 radioDisplay(); 336 337 /* Don't remove the error class if we're displaying the error for another 338 * reason. 339 */ 340 if (!duplicatedLayerName.is(":visible")) 341 layerNameCtrl.removeClass("has-error") 342 343 $("#invalid-layer-name-hint").hide(); 344 check_form(); 345 }); 346 347 /* Setup 'blank' typeahead */ 348 libtoaster.makeTypeahead(gitRefInput, 349 ctx.xhrGitRevTypeAheadUrl, 350 { git_url: null }, function(){}); 351 352 353 vcsURLInput.focusout(function (){ 354 if (!$(this).val()) 355 return; 356 357 /* If we a layer name specified don't overwrite it or if there isn't a 358 * url typed in yet return 359 */ 360 if (!layerNameInput.val() && $(this).val().search("/")){ 361 var urlPts = $(this).val().split("/"); 362 /* Add a suggestion of the layer name */ 363 var suggestion = urlPts[urlPts.length-1].replace(".git",""); 364 layerNameInput.val(suggestion); 365 } 366 367 /* Now actually setup the typeahead properly with the git url entered */ 368 gitRefInput._typeahead('destroy'); 369 370 libtoaster.makeTypeahead(gitRefInput, 371 ctx.xhrGitRevTypeAheadUrl, 372 { git_url: $(this).val() }, 373 function(selected){ 374 gitRefInput._typeahead("close"); 375 }); 376 377 }); 378 379 function radioDisplay() { 380 if ($('input[name=repo]:checked').val() == "local") { 381 $('#git-repo').hide(); 382 $('#import-git-layer-and-add-hint').hide(); 383 $('#local-dir').fadeIn(); 384 $('#import-local-dir-and-add-hint').fadeIn(); 385 } else { 386 $('#local-dir').hide(); 387 $('#import-local-dir-and-add-hint').hide(); 388 $('#git-repo').fadeIn(); 389 $('#import-git-layer-and-add-hint').fadeIn(); 390 } 391 } 392 393 $('input:radio[name="repo"]').change(function() { 394 radioDisplay(); 395 if ($("#local-dir-radio").prop("checked")) { 396 if (localDirPath.val().length > 0) { 397 enable_import_btn(true); 398 } else { 399 enable_import_btn(false); 400 } 401 } 402 if ($("#git-repo-radio").prop("checked")) { 403 if (vcsURLInput.val().length > 0 && gitRefInput.val().length > 0) { 404 enable_import_btn(true); 405 } else { 406 enable_import_btn(false); 407 } 408 } 409 }); 410 411 localDirPath.on('input', function(){ 412 if ($(this).val().trim().length == 0) { 413 $('#import-and-add-btn').attr("disabled","disabled"); 414 $('#local-dir').addClass('has-error'); 415 $('#hintError-dir-abs-path').show(); 416 $('#hintError-dir-path-starts-with-slash').show(); 417 } else { 418 var input = $(this); 419 var reBeginWithSlash = /^\//; 420 var reCheckVariable = /^\$/; 421 var re = /([ <>\\|":%\?\*]+)/; 422 423 var invalidDir = re.test(input.val()); 424 var invalidSlash = reBeginWithSlash.test(input.val()); 425 var invalidVar = reCheckVariable.test(input.val()); 426 427 if (!invalidSlash && !invalidVar) { 428 $('#local-dir').addClass('has-error'); 429 $('#import-and-add-btn').attr("disabled","disabled"); 430 $('#hintError-dir-abs-path').show(); 431 $('#hintError-dir-path-starts-with-slash').show(); 432 } else if (invalidDir) { 433 $('#local-dir').addClass('has-error'); 434 $('#import-and-add-btn').attr("disabled","disabled"); 435 $('#hintError-dir-path').show(); 436 } else { 437 $('#local-dir').removeClass('has-error'); 438 if (layerNameInput.val().length > 0) { 439 $('#import-and-add-btn').removeAttr("disabled"); 440 } 441 $('#hintError-dir-abs-path').hide(); 442 $('#hintError-dir-path-starts-with-slash').hide(); 443 $('#hintError-dir-path').hide(); 444 } 445 } 446 }); 447} 448