summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster/toastergui/static/js/projectapp.js
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/toaster/toastergui/static/js/projectapp.js')
-rw-r--r--bitbake/lib/toaster/toastergui/static/js/projectapp.js531
1 files changed, 531 insertions, 0 deletions
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectapp.js b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
new file mode 100644
index 0000000000..e674d8ffd1
--- /dev/null
+++ b/bitbake/lib/toaster/toastergui/static/js/projectapp.js
@@ -0,0 +1,531 @@
1// vim: set tabstop=4 expandtab ai:
2// BitBake Toaster Implementation
3//
4// Copyright (C) 2013 Intel Corporation
5//
6// This program is free software; you can redistribute it and/or modify
7// it under the terms of the GNU General Public License version 2 as
8// published by the Free Software Foundation.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License along
16// with this program; if not, write to the Free Software Foundation, Inc.,
17// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19angular_formpost = function($httpProvider) {
20 // Use x-www-form-urlencoded Content-Type
21 // By Ezekiel Victor, http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/, no license, with attribution
22 $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
23
24 /**
25 * The workhorse; converts an object to x-www-form-urlencoded serialization.
26 * @param {Object} obj
27 * @return {String}
28 */
29 var param = function(obj) {
30 var query = '', name, value, fullSubName, subName, subValue, innerObj, i;
31
32 for(name in obj) {
33 value = obj[name];
34
35 if(value instanceof Array) {
36 for(i=0; i<value.length; ++i) {
37 subValue = value[i];
38 fullSubName = name + '[' + i + ']';
39 innerObj = {};
40 innerObj[fullSubName] = subValue;
41 query += param(innerObj) + '&';
42 }
43 }
44 else if(value instanceof Object) {
45 for(subName in value) {
46 subValue = value[subName];
47 fullSubName = name + '[' + subName + ']';
48 innerObj = {};
49 innerObj[fullSubName] = subValue;
50 query += param(innerObj) + '&';
51 }
52 }
53 else if(value !== undefined && value !== null)
54 query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
55 }
56
57 return query.length ? query.substr(0, query.length - 1) : query;
58 };
59
60 // Override $http service's default transformRequest
61 $httpProvider.defaults.transformRequest = [function(data) {
62 return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
63 }];
64}
65
66
67/**
68 * Helper to execute callback on elements from array differences; useful for incremental UI updating.
69 * @param {Array} oldArray
70 * @param {Array} newArray
71 * @param {function} compareElements
72 * @param {function} onAdded
73 * @param {function} onDeleted
74 *
75 * no return
76 */
77function _diffArrays(oldArray, newArray, compareElements, onAdded, onDeleted ) {
78 if (onDeleted !== undefined) {
79 oldArray.filter(function (e) { var found = 0; newArray.map(function (f) { if (compareElements(e, f)) {found = 1};}); return !found;}).map(onDeleted);
80 }
81 if (onAdded !== undefined) {
82 newArray.filter(function (e) { var found = 0; oldArray.map(function (f) { if (compareElements(e, f)) {found = 1};}); return !found;}).map(onAdded);
83 }
84}
85
86
87var projectApp = angular.module('project', ['ui.bootstrap', 'ngCookies'], angular_formpost);
88
89// modify the template tag markers to prevent conflicts with Django
90projectApp.config(function($interpolateProvider) {
91 $interpolateProvider.startSymbol("{[");
92 $interpolateProvider.endSymbol("]}");
93});
94
95// main controller for the project page
96projectApp.controller('prjCtrl', function($scope, $modal, $http, $interval, $location, $cookies, $q, $sce) {
97
98 $scope.getSuggestions = function(type, currentValue) {
99 var deffered = $q.defer();
100
101 $http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: type, value: currentValue}})
102 .success(function (_data) {
103 if (_data.error != "ok") {
104 alert(_data.error);
105 deffered.reject(_data.error);
106 }
107 deffered.resolve(_data.list);
108 });
109
110 return deffered.promise;
111 }
112
113 var inXHRcall = false;
114
115 // default handling of XHR calls that handles errors and updates commonly-used pages
116 $scope._makeXHRCall = function(callparams) {
117 if (inXHRcall) {
118 if (callparams.data === undefined) {
119 // we simply skip the data refresh calls
120 console.log("race on XHR, aborted");
121 return;
122 } else {
123 // we return a promise that we'll solve by reissuing the command later
124 var delayed = $q.defer();
125 console.log("race on XHR, delayed");
126 $interval(function () {$scope._makeXHRCall(callparams).then(function (d) { delayed.resolve(d); });}, 100, 1);
127
128 return delayed.promise;
129 }
130
131 }
132 var deffered = $q.defer();
133
134 if (undefined === callparams.headers) { callparams.headers = {} };
135 callparams.headers['X-CSRFToken'] = $cookies.csrftoken;
136
137 $http(callparams).success(function(_data, _status, _headers, _config) {
138 if (_data.error != "ok") {
139 alert("Failed XHR request (" + _status + "): " + _data.error);
140 console.error("Failed XHR request: ", _data, _status, _headers, _config);
141 deffered.reject(_data.error);
142 }
143 else {
144 // TODO: update screen data if we have fields here
145
146 if (_data.builds !== undefined) {
147
148 var oldbuilds = $scope.builds;
149 $scope.builds = _data.builds;
150
151 // identify canceled builds here, so we can display them.
152 _diffArrays(oldbuilds, $scope.builds,
153 function (e,f) { return e.status == f.status && e.id == f.id }, // compare
154 undefined, // added
155 function (e) { // deleted
156 if (e.status == "deleted") return;
157 e.status = "deleted";
158 for (var i = 0; i < $scope.builds.length; i++) {
159 if ($scope.builds[i].status == "queued" && $scope.builds[i].id > e.id)
160 continue;
161 $scope.builds.splice(i, 0, e);
162 break;
163 }
164 });
165
166 }
167 if (_data.layers !== undefined) {
168 var oldlayers = $scope.layers;
169 $scope.layers = _data.layers;
170
171 // show added/deleted layer notifications
172 var addedLayers = [];
173 var deletedLayers = [];
174 _diffArrays( oldlayers, $scope.layers, function (e, f) { return e.id == f.id },
175 function (e) { console.log("new layer", e);addedLayers.push(e); },
176 function (e) { console.log("del layer", e);deletedLayers.push(e); });
177
178 if (addedLayers.length > 0) {
179 $scope.displayAlert($scope.zone2alerts, "You have added <b>"+addedLayers.length+"</b> layer" + ((addedLayers.length>1)?"s: ":": ") + addedLayers.map(function (e) { return "<a href=\""+e.layerdetailurl+"\">"+e.name+"</a>" }).join(", "), "alert-info");
180 }
181 if (deletedLayers.length > 0) {
182 $scope.displayAlert($scope.zone2alerts, "You have deleted <b>"+deletedLayers.length+"</b> layer" + ((deletedLayers.length>1)?"s: ":": ") + deletedLayers.map(function (e) { return "<a href=\""+e.layerdetailurl+"\">"+e.name+"</a>" }).join(", "), "alert-info");
183 }
184
185 }
186 if (_data.targets !== undefined) {
187 $scope.targets = _data.targets;
188 }
189 if (_data.machine !== undefined) {
190 $scope.machine = _data.machine;
191 }
192 if (_data.user !== undefined) {
193 $scope.user = _data.user;
194 }
195
196 if (_data.prj !== undefined) {
197 $scope.project = _data.prj;
198
199 // update breadcrumb, outside the controller
200 $('#project_name').text($scope.project.name);
201 }
202
203 $scope.validateData();
204 inXHRcall = false;
205 deffered.resolve(_data);
206 }
207 }).error(function(_data, _status, _headers, _config) {
208 alert("Failed HTTP XHR request (" + _status + ")" + _data);
209 console.error("Failed HTTP XHR request: ", _data, _status, _headers, _config);
210 inXHRcall = false;
211 deffered.reject(_data.error);
212 });
213
214 return deffered.promise;
215 }
216
217 $scope.layeralert = undefined;
218 // shows user alerts on invalid project data
219 $scope.validateData = function () {
220 if ($scope.layers.length == 0) {
221 $scope.layeralert = $scope.displayAlert($scope.zone1alerts, "You need to add some layers to this project. <a href=\""+$scope.urls.layers+"\">View all layers available in Toaster</a> or <a href=\""+$scope.urls.importlayer+"\">import a layer</a>");
222 } else {
223 if ($scope.layeralert != undefined) {
224 $scope.layeralert.close();
225 $scope.layeralert = undefined;
226 }
227 }
228 }
229
230 $scope.targetExistingBuild = function(targets) {
231 var oldTargetName = $scope.targetName;
232 $scope.targetName = targets.map(function(v,i,a){return v.target}).join(' ');
233 $scope.targetNamedBuild();
234 $scope.targetName = oldTargetName;
235 }
236
237 $scope.targetNamedBuild = function(target) {
238 if ($scope.targetName === undefined){
239 alert("No target defined, please type in a target name");
240 return;
241 }
242
243 $scope.sanitizeTargetName();
244
245 $scope._makeXHRCall({
246 method: "POST", url: $scope.urls.xhr_build,
247 data : {
248 targets: $scope.targetName
249 }
250 }).then(function (data) {
251 console.log("received ", data);
252 $scope.targetName = undefined;
253 });
254 }
255
256 $scope.sanitizeTargetName = function() {
257 if (undefined === $scope.targetName) return;
258 $scope.targetName = $scope.targetName.replace(/\[.*\]/, '').trim();
259 }
260
261 $scope.buildCancel = function(id) {
262 $scope._makeXHRCall({
263 method: "POST", url: $scope.urls.xhr_build,
264 data: {
265 buildCancel: id,
266 }
267 });
268 }
269
270 $scope.onLayerSelect = function (item, model, label) {
271 $scope.layerAddId = item.id;
272 }
273
274 $scope.layerAdd = function() {
275
276 $http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: "layerdeps", value: $scope.layerAddId }})
277 .success(function (_data) {
278 if (_data.error != "ok") {
279 alert(_data.error);
280 } else {
281 if (_data.list.length > 0) {
282 // activate modal
283 var modalInstance = $modal.open({
284 templateUrl: 'dependencies_modal',
285 controller: function ($scope, $modalInstance, items, layerAddName) {
286 $scope.items = items;
287 $scope.layerAddName = layerAddName;
288 $scope.selectedItems = (function () { s = {}; for (var i = 0; i < items.length; i++) { s[items[i].id] = true; };return s; })();
289
290 $scope.ok = function() {
291 console.log("scope selected is ", $scope.selectedItems);
292 $modalInstance.close(Object.keys($scope.selectedItems).filter(function (e) { return $scope.selectedItems[e];}));
293 };
294
295 $scope.cancel = function() {
296 $modalInstance.dismiss('cancel');
297 };
298
299 $scope.update = function() {
300 console.log("updated ", $scope.selectedItems);
301 };
302 },
303 resolve: {
304 items: function () {
305 return _data.list;
306 },
307 layerAddName: function () {
308 return $scope.layerAddName;
309 },
310 }
311 });
312
313 modalInstance.result.then(function (selectedArray) {
314 selectedArray.push($scope.layerAddId);
315 console.log("selected", selectedArray);
316
317 $scope._makeXHRCall({
318 method: "POST", url: $scope.urls.xhr_edit,
319 data: {
320 layerAdd: selectedArray.join(","),
321 }
322 }).then(function () {
323 $scope.layerAddName = undefined;
324 });
325 });
326 }
327 else {
328 $scope._makeXHRCall({
329 method: "POST", url: $scope.urls.xhr_edit,
330 data: {
331 layerAdd: $scope.layerAddId,
332 }
333 }).then(function () {
334 $scope.layerAddName = undefined;
335 });
336 }
337 }
338 });
339 }
340
341 $scope.layerDel = function(id) {
342 $scope._makeXHRCall({
343 method: "POST", url: $scope.urls.xhr_edit,
344 data: {
345 layerDel: id,
346 }
347 });
348 }
349
350
351 $scope.test = function(elementid) {
352 $http({method:"GET", url: $scope.urls.xhr_datatypeahead, params : { type: "versionlayers", value: $scope.projectVersion }}).
353 success(function (_data) {
354 if (_data.error != "ok") {
355 alert (_data.error);
356 }
357 else {
358 if (_data.list.length > 0) {
359 // activate modal
360 var modalInstance = $modal.open({
361 templateUrl: 'change_version_modal',
362 controller: function ($scope, $modalInstance, items, releaseName) {
363 $scope.items = items;
364 $scope.releaseName = releaseName;
365
366 $scope.ok = function() {
367 $modalInstance.close();
368 };
369
370 $scope.cancel = function() {
371 $modalInstance.dismiss('cancel');
372 };
373
374 },
375 resolve: {
376 items: function () {
377 return _data.list;
378 },
379 releaseName: function () {
380 return $scope.releases.filter(function (e) { if (e.id == $scope.projectVersion) return e;})[0].name;
381 },
382 }
383 });
384
385 modalInstance.result.then(function () { $scope.edit(elementid)});
386 } else {
387 $scope.edit(elementid);
388 }
389 }
390 });
391 }
392
393 $scope.edit = function(elementid) {
394 var data = {};
395 console.log("edit with ", elementid);
396 var alertText = undefined;
397 var alertZone = undefined;
398 switch(elementid) {
399 case '#select-machine':
400 alertText = "You have changed the machine to: <b>" + $scope.machineName + "</b>";
401 alertZone = $scope.zone2alerts;
402 data['machineName'] = $scope.machineName;
403 break;
404 case '#change-project-name':
405 data['projectName'] = $scope.projectName;
406 alertText = "You have changed the project name to: <b>" + $scope.projectName + "</b>";
407 alertZone = $scope.zone3alerts;
408 break;
409 case '#change-project-version':
410 data['projectVersion'] = $scope.projectVersion;
411 alertText = "You have changed the release to: ";
412 alertZone = $scope.zone3alerts;
413 break;
414 default:
415 throw "FIXME: implement conversion for element " + elementid;
416 }
417
418 console.log("calling edit with ", data);
419 $scope._makeXHRCall({
420 method: "POST", url: $scope.urls.xhr_edit, data: data,
421 }).then( function () {
422 $scope.toggle(elementid);
423 if (data['projectVersion'] != undefined) {
424 alertText += "<b>" + $scope.release.name + "</b>";
425 }
426 $scope.displayAlert(alertZone, alertText, "alert-info");
427 });
428 }
429
430
431 $scope.executeCommands = function() {
432 cmd = $location.path();
433
434 function _cmdExecuteWithParam(param, f) {
435 if (cmd.indexOf(param)==0) {
436 if (cmd.indexOf("=") > -1) {
437 var parameter = cmd.split("=", 2)[1];
438 if (parameter != undefined && parameter.length > 0) {
439 f(parameter);
440 }
441 } else {
442 f();
443 };
444 }
445 }
446
447 _cmdExecuteWithParam("/newproject", function () {
448 $scope.displayAlert($scope.zone1alerts,
449 "Your project <strong>" + $scope.project.name +
450 "</strong> has been created. You can now <a href=\""+ $scope.urls.layers +
451 "\">add layers</a> and <a href=\""+ $scope.urls.targets +
452 "\">select targets</a> you want to build.", "alert-success");
453 });
454
455 _cmdExecuteWithParam("/targetbuild=", function (targets) {
456 var oldTargetName = $scope.targetName;
457 $scope.targetName = targets.split(",").join(" ");
458 $scope.targetNamedBuild();
459 $scope.targetName = oldTargetName;
460 });
461
462 _cmdExecuteWithParam("/machineselect=", function (machine) {
463 $scope.machineName = machine;
464 $scope.toggle('#select-machine');
465 });
466
467
468 _cmdExecuteWithParam("/layeradd=", function (layer) {
469 angular.forEach(layer.split(","), function (l) {
470 $scope.layerAddId = l;
471 $scope.layerAdd();
472 });
473 });
474 }
475
476 $scope.displayAlert = function(zone, text, type) {
477 if (zone.maxid === undefined) { zone.maxid = 0; }
478 var crtid = zone.maxid ++;
479 angular.forEach(zone, function (o) { o.close() });
480 o = {
481 id: crtid, text: $sce.trustAsHtml(text), type: type,
482 close: function() {
483 zone.splice((function(id){ for (var i = 0; i < zone.length; i++) if (id == zone[i].id) { return i}; return undefined;})(crtid), 1);
484 },
485 }
486 zone.push(o);
487 return o;
488 }
489
490 $scope.toggle = function(id) {
491 $scope.projectName = $scope.project.name;
492 $scope.projectVersion = $scope.project.release.id;
493 $scope.machineName = $scope.machine.name;
494
495 angular.element(id).toggle();
496 angular.element(id+"-opposite").toggle();
497 }
498
499 $scope.selectedMostBuildTargets = function () {
500 keys = Object.keys($scope.mostBuiltTargets);
501 keys = keys.filter(function (e) { if ($scope.mostBuiltTargets[e]) return e });
502 return keys.length == 0;
503
504 }
505
506 // init code
507 //
508 $scope.init = function() {
509 $scope.pollHandle = $interval(function () { $scope._makeXHRCall({method: "POST", url: $scope.urls.xhr_edit, data: undefined});}, 4000, 0);
510 }
511
512 $scope.init();
513});
514
515
516/**
517 TESTING CODE
518*/
519
520function test_diff_arrays() {
521 _diffArrays([1,2,3], [2,3,4], function(e,f) { return e==f; }, function(e) {console.log("added", e)}, function(e) {console.log("deleted", e);})
522}
523
524var s = undefined;
525
526function test_set_alert(text) {
527 s = angular.element("div#main").scope();
528 s.displayAlert(s.zone3alerts, text);
529 console.log(s.zone3alerts);
530 s.$digest();
531}