summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/toastergui/static
diff options
context:
space:
mode:
authorMichael Wood <michael.g.wood@intel.com>2015-07-31 15:09:12 +0300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2015-08-01 11:26:12 +0100
commit80c1586bbb7f661f37754de5ba637c8d6e2185de (patch)
tree56011e83e1f192a68f4e9014ca266dd6cd76c719 /bitbake/lib/toaster/toastergui/static
parent258c929973e233f212938ea69eec8f83f3285854 (diff)
downloadpoky-80c1586bbb7f661f37754de5ba637c8d6e2185de.tar.gz
bitbake: toastergui: Add new project page and navigation
This brings in the new project page design and improved navigation. As this also removes the dependency on Angular it also required that the entry points to the project page such as machine-change notifications are also updated. [YOCTO #7329] (Bitbake rev: 6489e6eb5c3b0d59063b6d60521fc33fe563e707) Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/static')
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/base.js81
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/importlayer.js4
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/layerdetails.js5
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/libtoaster.js15
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/projectpage.js429
5 files changed, 485 insertions, 49 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/base.js b/bitbake/lib/toaster/toastergui/static/js/base.js
index 6fd77ea457..0fb1f8b6bf 100644
--- a/bitbake/lib/toaster/toastergui/static/js/base.js
+++ b/bitbake/lib/toaster/toastergui/static/js/base.js
@@ -6,13 +6,20 @@ function basePageInit(ctx) {
6 var newBuildTargetInput; 6 var newBuildTargetInput;
7 var newBuildTargetBuildBtn; 7 var newBuildTargetBuildBtn;
8 8
9 /* initially the current project is used unless overridden by the new build
10 * button in top right nav
11 */
9 var selectedProject = libtoaster.ctx; 12 var selectedProject = libtoaster.ctx;
13
10 var selectedTarget; 14 var selectedTarget;
11 15
12 var newBuildProjectInput = $("#new-build-button #project-name-input"); 16 var newBuildProjectInput = $("#new-build-button #project-name-input");
13 var newBuildProjectSaveBtn = $("#new-build-button #save-project-button"); 17 var newBuildProjectSaveBtn = $("#new-build-button #save-project-button");
14 18
15 $("#config-nav .nav li a").each(function(){ 19
20 _checkProjectBuildable();
21
22 $("#project-topbar .nav li a").each(function(){
16 if (window.location.pathname === $(this).attr('href')) 23 if (window.location.pathname === $(this).attr('href'))
17 $(this).parent().addClass('active'); 24 $(this).parent().addClass('active');
18 else 25 else
@@ -26,7 +33,7 @@ function basePageInit(ctx) {
26 }); 33 });
27 } 34 }
28 35
29 /* Hide the button if we're on the project,newproject or importlyaer page 36 /* Hide the button if we're on the project,newproject or importlyaer page
30 * or if there are no projects yet defined 37 * or if there are no projects yet defined
31 * only show if there isn't already a build-target-input already 38 * only show if there isn't already a build-target-input already
32 */ 39 */
@@ -37,8 +44,8 @@ function basePageInit(ctx) {
37 newBuildTargetInput = $("#new-build-button .build-target-input"); 44 newBuildTargetInput = $("#new-build-button .build-target-input");
38 newBuildTargetBuildBtn = $("#new-build-button .build-button"); 45 newBuildTargetBuildBtn = $("#new-build-button .build-button");
39 46
40 newBuildButton.show();
41 _setupNewBuildButton(); 47 _setupNewBuildButton();
48 newBuildButton.show();
42 } else if ($(".build-target-input").length > 0) { 49 } else if ($(".build-target-input").length > 0) {
43 newBuildTargetInput = $("#project-topbar .build-target-input"); 50 newBuildTargetInput = $("#project-topbar .build-target-input");
44 newBuildTargetBuildBtn = $("#project-topbar .build-button"); 51 newBuildTargetBuildBtn = $("#project-topbar .build-button");
@@ -52,9 +59,35 @@ function basePageInit(ctx) {
52 $('#project .icon-pencil').hide(); 59 $('#project .icon-pencil').hide();
53 } 60 }
54 61
62 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) {
63 /* successfully selected a target */
64 selectedTarget = item;
65 });
55 66
56 _checkProjectBuildable(); 67 newBuildTargetInput.on('input', function () {
68 if ($(this).val().length === 0) {
69 newBuildTargetBuildBtn.attr("disabled", "disabled");
70 } else {
71 newBuildTargetBuildBtn.removeAttr("disabled");
72 }
73 });
74
75 newBuildTargetBuildBtn.click(function (e) {
76 e.preventDefault();
77
78 if (!newBuildTargetInput.val()) {
79 return;
80 }
57 81
82 if (!selectedTarget) {
83 selectedTarget = { name: newBuildTargetInput.val() };
84 }
85 /* Fire off the build */
86 libtoaster.startABuild(selectedProject.projectBuildsUrl,
87 selectedProject.projectId, selectedTarget.name, function(){
88 window.location.replace(selectedProject.projectBuildsUrl);
89 }, null);
90 });
58 91
59 function _checkProjectBuildable() { 92 function _checkProjectBuildable() {
60 if (selectedProject.projectId === undefined) { 93 if (selectedProject.projectId === undefined) {
@@ -74,21 +107,12 @@ function basePageInit(ctx) {
74 /* we can build this project; enable input fields */ 107 /* we can build this project; enable input fields */
75 newBuildTargetInput.prop("disabled", false); 108 newBuildTargetInput.prop("disabled", false);
76 newBuildTargetBuildBtn.prop("disabled", false); 109 newBuildTargetBuildBtn.prop("disabled", false);
77 110 }
78 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) {
79 /* successfully selected a target */
80 selectedProject.projectPageUrl = item.projectPageUrl;
81 selectedProject.projectName = item.name;
82 selectedProject.projectId = item.id;
83 selectedProject.projectBuildsUrl = item.projectBuildsUrl;
84 });
85
86 }
87 }, null); 111 }, null);
88 } 112 }
89 113
114 /* Setup New build button in the top nav bar */
90 function _setupNewBuildButton() { 115 function _setupNewBuildButton() {
91 /* Setup New build button */
92 116
93 /* If we don't have a current project then present the set project 117 /* If we don't have a current project then present the set project
94 * form. 118 * form.
@@ -98,7 +122,6 @@ function basePageInit(ctx) {
98 $('#project .icon-pencil').hide(); 122 $('#project .icon-pencil').hide();
99 } 123 }
100 124
101
102 libtoaster.makeTypeahead(newBuildProjectInput, selectedProject.projectsUrl, { format : "json" }, function (item) { 125 libtoaster.makeTypeahead(newBuildProjectInput, selectedProject.projectsUrl, { format : "json" }, function (item) {
103 /* successfully selected a project */ 126 /* successfully selected a project */
104 newBuildProjectSaveBtn.removeAttr("disabled"); 127 newBuildProjectSaveBtn.removeAttr("disabled");
@@ -115,40 +138,16 @@ function basePageInit(ctx) {
115 newBuildProjectSaveBtn.attr("disabled", "disabled"); 138 newBuildProjectSaveBtn.attr("disabled", "disabled");
116 }); 139 });
117 140
118 newBuildTargetInput.on('input', function () {
119 if ($(this).val().length === 0) {
120 newBuildTargetBuildBtn.attr("disabled", "disabled");
121 } else {
122 newBuildTargetBuildBtn.removeAttr("disabled");
123 }
124 });
125
126 newBuildTargetBuildBtn.click(function () {
127 if (!newBuildTargetInput.val()) {
128 return;
129 }
130
131 if (!selectedTarget) {
132 selectedTarget = { name: newBuildTargetInput.val() };
133 }
134 /* fire and forget */
135 libtoaster.startABuild(selectedProject.projectBuildsUrl, selectedProject.projectId, selectedTarget.name, null, null);
136 window.location.replace(selectedProject.projectPageUrl);
137 });
138 141
139 newBuildProjectSaveBtn.click(function () { 142 newBuildProjectSaveBtn.click(function () {
140 selectedProject.projectId = selectedProject.pk; 143 selectedProject.projectId = selectedProject.pk;
141 /* Update the typeahead project_id paramater */ 144 /* Update the typeahead project_id paramater */
142 _checkProjectBuildable(); 145 _checkProjectBuildable();
143 146
144 /* we set the effective context of the page to the currently selected project */
145 /* TBD: do we override even if we already have a context project ?? */
146 /* TODO: replace global library context with references to the "selected" project */
147
148 /* we can create a target typeahead only after we have a project selected */
149 newBuildTargetInput.prop("disabled", false); 147 newBuildTargetInput.prop("disabled", false);
150 newBuildTargetBuildBtn.prop("disabled", false); 148 newBuildTargetBuildBtn.prop("disabled", false);
151 149
150 /* Update the typeahead to use the new selectedProject */
152 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) { 151 libtoaster.makeTypeahead(newBuildTargetInput, selectedProject.projectTargetsUrl, { format: "json" }, function (item) {
153 /* successfully selected a target */ 152 /* successfully selected a target */
154 selectedTarget = item; 153 selectedTarget = item;
diff --git a/bitbake/lib/toaster/toastergui/static/js/importlayer.js b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
index 560e25a01c..03274c00f3 100644
--- a/bitbake/lib/toaster/toastergui/static/js/importlayer.js
+++ b/bitbake/lib/toaster/toastergui/static/js/importlayer.js
@@ -16,8 +16,6 @@ function importLayerPageInit (ctx) {
16 var currentLayerDepSelection; 16 var currentLayerDepSelection;
17 var validLayerName = /^(\w|-)+$/; 17 var validLayerName = /^(\w|-)+$/;
18 18
19 $("#new-project-button").hide();
20
21 libtoaster.makeTypeahead(layerDepInput, libtoaster.ctx.projectLayersUrl, { include_added: "true" }, function(item){ 19 libtoaster.makeTypeahead(layerDepInput, libtoaster.ctx.projectLayersUrl, { include_added: "true" }, function(item){
22 currentLayerDepSelection = item; 20 currentLayerDepSelection = item;
23 21
@@ -154,7 +152,7 @@ function importLayerPageInit (ctx) {
154 } else { 152 } else {
155 /* Success layer import now go to the project page */ 153 /* Success layer import now go to the project page */
156 $.cookie('layer-imported-alert', JSON.stringify(data), { path: '/'}); 154 $.cookie('layer-imported-alert', JSON.stringify(data), { path: '/'});
157 window.location.replace(libtoaster.ctx.projectPageUrl+'#/layerimported'); 155 window.location.replace(libtoaster.ctx.projectPageUrl+'?notify=layer-imported');
158 } 156 }
159 }, 157 },
160 error: function (data) { 158 error: function (data) {
diff --git a/bitbake/lib/toaster/toastergui/static/js/layerdetails.js b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
index 291ed98c34..be6bbcd20f 100644
--- a/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
+++ b/bitbake/lib/toaster/toastergui/static/js/layerdetails.js
@@ -16,14 +16,15 @@ function layerDetailsPageInit (ctx) {
16 }); 16 });
17 17
18 $(".breadcrumb li:first a").click(function(e){ 18 $(".breadcrumb li:first a").click(function(e){
19 e.preventDefault();
19 /* By default this link goes to the project configuration page. However 20 /* By default this link goes to the project configuration page. However
20 * if we have some builds we go there instead of the default href 21 * if we have some builds we go there instead of the default href
21 */ 22 */
22 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, function(prjInfo){ 23 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, function(prjInfo){
23 if (prjInfo.builds && prjInfo.builds.length > 0) { 24 if (prjInfo.builds && prjInfo.builds.length > 0) {
24 window.location.replace(libtoaster.ctx.projectBuildsUrl); 25 window.location.replace(libtoaster.ctx.projectBuildsUrl);
25 e.preventDefault(); 26 } else {
26 return; 27 window.location.replace(libtoaster.ctx.projectPageUrl);
27 } 28 }
28 }); 29 });
29 }); 30 });
diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
index afd665ca99..079bbcb0b8 100644
--- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
+++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
@@ -79,11 +79,12 @@ var libtoaster = (function (){
79 data: data, 79 data: data,
80 headers: { 'X-CSRFToken' : $.cookie('csrftoken')}, 80 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
81 success: function (_data) { 81 success: function (_data) {
82 /* No proper reponse YOCTO #7995
82 if (_data.error !== "ok") { 83 if (_data.error !== "ok") {
83 console.warn(_data.error); 84 console.warn(_data.error);
84 } else { 85 } else { */
85 if (onsuccess !== undefined) onsuccess(_data); 86 if (onsuccess !== undefined) onsuccess(_data);
86 } 87 // }
87 }, 88 },
88 error: function (_data) { 89 error: function (_data) {
89 console.warn("Call failed"); 90 console.warn("Call failed");
@@ -180,7 +181,15 @@ var libtoaster = (function (){
180 if (onFail !== undefined) 181 if (onFail !== undefined)
181 onFail(data); 182 onFail(data);
182 } else { 183 } else {
183 onSuccess(data.layerdeps); 184 var deps = {};
185 /* Filter out layer dep ids which are in the
186 * project already.
187 */
188 deps.list = data.layerdeps.list.filter(function(layerObj){
189 return (data.projectlayers.lastIndexOf(layerObj.id) < 0);
190 });
191
192 onSuccess(deps);
184 } 193 }
185 }, function() { 194 }, function() {
186 console.log("E: Failed to make request"); 195 console.log("E: Failed to make request");
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectpage.js b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
new file mode 100644
index 0000000000..b7cb074f11
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
@@ -0,0 +1,429 @@
1"use strict";
2
3function projectPageInit(ctx) {
4
5 var layerAddInput = $("#layer-add-input");
6 var layersInPrjList = $("#layers-in-project-list");
7 var layerAddBtn = $("#add-layer-btn");
8
9 var machineChangeInput = $("#machine-change-input");
10 var machineChangeBtn = $("#machine-change-btn");
11 var machineForm = $("#select-machine-form");
12 var machineChangeFormToggle = $("#change-machine-toggle");
13 var machineNameTitle = $("#project-machine-name");
14 var machineChangeCancel = $("#cancel-machine-change");
15
16 var freqBuildBtn = $("#freq-build-btn");
17 var freqBuildList = $("#freq-build-list");
18
19 var releaseChangeFormToggle = $("#release-change-toggle");
20 var releaseTitle = $("#project-release-title");
21 var releaseForm = $("#change-release-form");
22 var releaseModal = $("#change-release-modal");
23 var cancelReleaseChange = $("#cancel-release-change");
24
25 var currentLayerAddSelection;
26 var currentMachineAddSelection = {};
27
28 var urlParams = libtoaster.parseUrlParams();
29
30 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, function(prjInfo){
31 updateProjectLayers(prjInfo.layers);
32 updateFreqBuildRecipes(prjInfo.freqtargets);
33 updateProjectRelease(prjInfo.release);
34 updateProjectReleases(prjInfo.releases, prjInfo.release);
35
36 /* If we're receiving a machine set from the url and it's different from
37 * our current machine then activate set machine sequence.
38 */
39 if (urlParams.hasOwnProperty('setMachine') &&
40 urlParams.setMachine !== prjInfo.machine.name){
41 currentMachineAddSelection.name = urlParams.setMachine;
42 machineChangeBtn.click();
43 } else {
44 updateMachineName(prjInfo.machine.name);
45 }
46
47 /* Now we're really ready show the page */
48 $("#project-page").show();
49 });
50
51 (function notificationRequest(){
52
53 if (urlParams.hasOwnProperty('notify')){
54 switch (urlParams.notify){
55 case 'new-project':
56 $("#project-created-notification").show();
57 break;
58 case 'layer-imported':
59 layerImportedNotification();
60 break;
61 default:
62 break;
63 }
64 }
65 })();
66
67 /* Layer imported notification */
68 function layerImportedNotification(){
69 var imported = $.cookie("layer-imported-alert");
70 var message = "Layer imported";
71
72 if (!imported)
73 return;
74 else
75 imported = JSON.parse(imported);
76
77 if (imported.deps_added.length === 0) {
78 message = "You have imported <strong><a href=\""+imported.imported_layer.layerdetailurl+"\">"+imported.imported_layer.name+"</a></strong> and added it to your project.";
79 } else {
80
81 var links = "<a href=\""+imported.imported_layer.layerdetailurl+"\">"+imported.imported_layer.name+"</a>, ";
82
83 imported.deps_added.map (function(item, index){
84 links +='<a href="'+item.layerdetailurl+'">'+item.name+'</a>';
85 /*If we're at the last element we don't want the trailing comma */
86 if (imported.deps_added[index+1] !== undefined)
87 links += ', ';
88 });
89
90 /* Length + 1 here to do deps + the imported layer */
91 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>';
92 }
93
94 libtoaster.showChangeNotification(message);
95
96 $.removeCookie("layer-imported-alert", { path: "/"});
97 }
98
99 /* Add/Rm layer functionality */
100
101 libtoaster.makeTypeahead(layerAddInput, libtoaster.ctx.projectLayersUrl, { include_added: "false" }, function(item){
102 currentLayerAddSelection = item;
103 layerAddBtn.removeAttr("disabled");
104 });
105
106 layerAddBtn.click(function(e){
107 e.preventDefault();
108 var layerObj = currentLayerAddSelection;
109
110 addRmLayer(layerObj, true);
111 /* Reset the text input */
112 layerAddInput.val("");
113 });
114
115 function addRmLayer(layerObj, add){
116
117 libtoaster.addRmLayer(layerObj, add, function(layerDepsList){
118 if (add){
119 updateProjectLayers([layerObj]);
120 updateProjectLayers(layerDepsList);
121 }
122
123 /* Show the alert message */
124 var message = libtoaster.makeLayerAddRmAlertMsg(layerObj, layerDepsList, add);
125 libtoaster.showChangeNotification(message);
126 });
127 }
128
129 function updateProjectLayers(layers){
130
131 /* No layers to add */
132 if (layers.length === 0){
133 updateLayersCount();
134 return;
135 }
136
137 for (var i in layers){
138 var layerObj = layers[i];
139
140 var projectLayer = $("<li><a></a><span class=\"icon-trash\" data-toggle=\"tooltip\" title=\"Delete\"></span></li>");
141
142 projectLayer.data('layer', layerObj);
143 projectLayer.children("span").tooltip();
144
145 var link = projectLayer.children("a");
146
147 link.attr("href", layerObj.layerdetailurl);
148 link.text(layerObj.name);
149 /* YOCTO #8024
150 link.tooltip({title: layerObj.giturl + " | "+ layerObj.branch.name, placement: "right"});
151 branch name not accessible sometimes it is revision instead
152 */
153
154 var trashItem = projectLayer.children("span");
155 trashItem.click(function (e) {
156 e.preventDefault();
157 var layerObjToRm = $(this).parent().data('layer');
158
159 addRmLayer(layerObjToRm, false);
160
161 $(this).parent().fadeOut(function (){
162 $(this).remove();
163 updateLayersCount();
164 });
165 });
166
167 layersInPrjList.append(projectLayer);
168
169 updateLayersCount();
170 }
171 }
172
173 function updateLayersCount(){
174 var count = $("#layers-in-project-list").children().length;
175
176 if (count === 0)
177 $("#no-layers-in-project").fadeIn();
178 else
179 $("#no-layers-in-project").hide();
180
181 $("#project-layers-count").text(count);
182
183 return count;
184 }
185
186 /* Frequent builds functionality */
187 function updateFreqBuildRecipes(recipes) {
188 var noMostBuilt = $("#no-most-built");
189
190 if (recipes.length === 0){
191 noMostBuilt.show();
192 freqBuildBtn.hide();
193 } else {
194 noMostBuilt.hide();
195 freqBuildBtn.show();
196 }
197
198 for (var i in recipes){
199 var freqTargetCheck = $('<li><label class="checkbox"><input type="checkbox" /><span class="freq-target-name"></span></label></li>');
200 freqTargetCheck.find(".freq-target-name").text(recipes[i]);
201 freqTargetCheck.find("input").val(recipes[i]);
202 freqTargetCheck.click(function(){
203 if (freqBuildList.find(":checked").length > 0)
204 freqBuildBtn.removeAttr("disabled");
205 else
206 freqBuildBtn.attr("disabled", "disabled");
207 });
208
209 freqBuildList.append(freqTargetCheck);
210 }
211 }
212
213 freqBuildBtn.click(function(e){
214 e.preventDefault();
215
216 var toBuild = "";
217 freqBuildList.find(":checked").each(function(){
218 toBuild += $(this).val();
219 toBuild += " ";
220 });
221
222 libtoaster.startABuild(libtoaster.ctx.projectBuildsUrl, libtoaster.ctx.projectId, toBuild, function(){
223 /* Build started */
224 window.location.replace(libtoaster.ctx.projectBuildsUrl);
225 },
226 function(){
227 /* Build start failed */
228 /* [YOCTO #7995] */
229 window.location.replace(libtoaster.ctx.projectBuildsUrl);
230 });
231 });
232
233
234 /* Change machine functionality */
235
236 machineChangeFormToggle.click(function(){
237 machineForm.slideDown();
238 machineNameTitle.hide();
239 $(this).hide();
240 });
241
242 machineChangeCancel.click(function(){
243 machineForm.slideUp(function(){
244 machineNameTitle.show();
245 machineChangeFormToggle.show();
246 });
247 });
248
249 function updateMachineName(machineName){
250 machineChangeInput.val(machineName);
251 machineNameTitle.text(machineName);
252 }
253
254 libtoaster.makeTypeahead(machineChangeInput, libtoaster.ctx.projectMachinesUrl, { }, function(item){
255 currentMachineAddSelection = item;
256 machineChangeBtn.removeAttr("disabled");
257 });
258
259 machineChangeBtn.click(function(e){
260 e.preventDefault();
261 if (currentMachineAddSelection.name === undefined)
262 return;
263
264 libtoaster.editCurrentProject({ machineName : currentMachineAddSelection.name },
265 function(){
266 /* Success machine changed */
267 updateMachineName(currentMachineAddSelection.name);
268 machineChangeCancel.click();
269
270 /* Show the alert message */
271 var message = $('<span class="lead">You have changed the machine to: <strong><span id="notify-machine-name"></span></strong></span>');
272 message.find("#notify-machine-name").text(currentMachineAddSelection.name);
273 libtoaster.showChangeNotification(message);
274 },
275 function(){
276 /* Failed machine changed */
277 console.log("failed to change machine");
278 });
279 });
280
281
282 /* Change release functionality */
283 function updateProjectRelease(release){
284 releaseTitle.text(release.description);
285 }
286
287 function updateProjectReleases(releases, current){
288 for (var i in releases){
289 var releaseOption = $("<option></option>");
290
291 releaseOption.val(releases[i].id);
292 releaseOption.text(releases[i].description);
293 releaseOption.data('release', releases[i]);
294
295 if (releases[i].id == current.id)
296 releaseOption.attr("selected", "selected");
297
298 releaseForm.children("select").append(releaseOption);
299 }
300 }
301
302 releaseChangeFormToggle.click(function(){
303 releaseForm.slideDown();
304 releaseTitle.hide();
305 $(this).hide();
306 });
307
308 cancelReleaseChange.click(function(e){
309 e.preventDefault();
310 releaseForm.slideUp(function(){
311 releaseTitle.show();
312 releaseChangeFormToggle.show();
313 });
314 });
315
316 function changeProjectRelease(release, layersToRm){
317 libtoaster.editCurrentProject({ projectVersion : release.id },
318 function(){
319 /* Success */
320 /* Update layers list with new layers */
321 layersInPrjList.addClass('muted');
322 libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl,
323 function(prjInfo){
324 layersInPrjList.children().remove();
325 updateProjectLayers(prjInfo.layers);
326 layersInPrjList.removeClass('muted');
327 releaseChangedNotification(release, prjInfo.layers, layersToRm);
328 });
329 updateProjectRelease(release);
330 cancelReleaseChange.click();
331 });
332 }
333
334 /* Create a notification to show the changes to the layer configuration
335 * caused by changing a release.
336 */
337
338 function releaseChangedNotification(release, layers, layersToRm){
339
340 var message;
341
342 if (layers.length === 0 && layersToRm.length === 0){
343 message = $('<span><span class="lead">You have changed the project release to: <strong><span id="notify-release-name"></span></strong>.');
344 message.find("#notify-release-name").text(release.description);
345 libtoaster.showChangeNotification(message);
346 return;
347 }
348
349 /* Create the whitespace separated list of layers removed */
350 var layersDelList = "";
351
352 layersToRm.map(function(layer, i){
353 layersDelList += layer.name;
354 if (layersToRm[i+1] !== undefined)
355 layersDelList += ', ';
356 });
357
358 message = $('<span><span class="lead">You have changed the project release to: <strong><span id="notify-release-name"></span></strong>. This has caused the following changes in your project layers:</span><ul id="notify-layers-changed-list"></ul></span>');
359
360 var changedList = message.find("#notify-layers-changed-list");
361
362 message.find("#notify-release-name").text(release.description);
363
364 /* Manually construct the list item for changed layers */
365 var li = '<li><strong>'+layers.length+'</strong> layers changed to the <strong>'+release.name+'</strong> release: ';
366 for (var i in layers){
367 li += '<a href='+layers[i].layerdetailurl+'>'+layers[i].name+'</a>';
368 if (i !== 0)
369 li += ', ';
370 }
371
372 changedList.append($(li));
373
374 /* Layers removed */
375 if (layersToRm && layersToRm.length > 0){
376 if (layersToRm.length == 1)
377 li = '<li><strong>1</strong> layer deleted: '+layersToRm[0].name+'</li>';
378 else
379 li = '<li><strong>'+layersToRm.length+'</strong> layers deleted: '+layersDelList+'</li>';
380
381 changedList.append($(li));
382 }
383
384 libtoaster.showChangeNotification(message);
385 }
386
387 /* Show the modal dialog which gives the option to remove layers which
388 * aren't compatible with the proposed release
389 */
390 function showReleaseLayerChangeModal(release, layers){
391 var layersToRmList = releaseModal.find("#layers-to-remove-list");
392 layersToRmList.text("");
393
394 releaseModal.find(".proposed-release-change-name").text(release.description);
395 releaseModal.data("layers", layers);
396 releaseModal.data("release", release);
397
398 for (var i in layers){
399 layersToRmList.append($("<li></li>").text(layers[i].name));
400 }
401 releaseModal.modal('show');
402 }
403
404 $("#change-release-btn").click(function(e){
405 e.preventDefault();
406
407 var newRelease = releaseForm.find("option:selected").data('release');
408
409 $.getJSON(ctx.typeaheadUrl,
410 { search: newRelease.id, type: "versionlayers" },
411 function(layers) {
412 if (layers.rows.length === 0){
413 /* No layers to change for this release */
414 changeProjectRelease(newRelease, []);
415 } else {
416 showReleaseLayerChangeModal(newRelease, layers.rows);
417 }
418 });
419 });
420
421 /* Release change modal accept */
422 $("#change-release-and-rm-layers").click(function(){
423 var layers = releaseModal.data("layers");
424 var release = releaseModal.data("release");
425
426 changeProjectRelease(release, layers);
427 });
428
429}