blob: c04f7aba2be1ec00fb1171a14b35843756ec2d8e [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"use strict";
2/* All shared functionality to go in libtoaster object.
3 * This object really just helps readability since we can then have
4 * a traceable namespace.
5 */
6var libtoaster = (function (){
7
8 /* makeTypeahead parameters
9 * elementSelector: JQuery elementSelector string
10 * xhrUrl: the url to get the JSON from expects JSON in the form:
11 * { "list": [ { "name": "test", "detail" : "a test thing" }, .... ] }
12 * xhrParams: the data/parameters to pass to the getJSON url e.g.
13 * { 'type' : 'projects' } the text typed will be passed as 'search'.
14 * selectedCB: function to call once an item has been selected one
15 * arg of the item.
16 */
17 function _makeTypeahead (jQElement, xhrUrl, xhrParams, selectedCB) {
18 if (!xhrUrl || xhrUrl.length === 0)
19 throw("No url to typeahead supplied");
20
21 var xhrReq;
22
23 jQElement.typeahead({
24 source: function(query, process){
25 xhrParams.search = query;
26
27 /* If we have a request in progress don't fire off another one*/
28 if (xhrReq)
29 xhrReq.abort();
30
31 xhrReq = $.getJSON(xhrUrl, this.options.xhrParams, function(data){
32 if (data.error !== "ok") {
33 console.log("Error getting data from server "+data.error);
34 return;
35 }
36
37 xhrReq = null;
38
39 return process(data.results);
40 });
41 },
42 updater: function(item) {
43 var itemObj = this.$menu.find('.active').data('itemObject');
44 selectedCB(itemObj);
45 return item;
46 },
47 matcher: function(item) {
48 if (!item.hasOwnProperty('name')) {
49 console.log("Name property missing in data");
50 return 0;
51 }
52
53 if (this.$element.val().length === 0)
54 return 0;
55
56 return 1;
57 },
58 highlighter: function (item) {
59 /* Use jquery to escape the item name and detail */
60 var current = $("<span></span>").text(item.name + ' '+item.detail);
61 current = current.html();
62
63 var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
64 return current.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
65 return '<strong>' + match + '</strong>'
66 })
67 },
68 sorter: function (items) { return items; },
69 xhrUrl: xhrUrl,
70 xhrParams: xhrParams,
71 xhrReq: xhrReq,
72 });
73
74
75 /* Copy of bootstrap's render func but sets selectedObject value */
76 function customRenderFunc (items) {
77 var that = this;
78
79 items = $(items).map(function (i, item) {
80 i = $(that.options.item).attr('data-value', item.name).data('itemObject', item);
81 i.find('a').html(that.highlighter(item));
82 return i[0];
83 });
84
85 items.first().addClass('active');
86 this.$menu.html(items);
87 return this;
88 }
89
90 jQElement.data('typeahead').render = customRenderFunc;
91 }
92
93 /*
94 * url - the url of the xhr build */
95 function _startABuild (url, project_id, targets, onsuccess, onfail) {
96
97 var data = {
98 project_id : project_id,
99 targets : targets,
100 }
101
102 $.ajax( {
103 type: "POST",
104 url: url,
105 data: data,
106 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
107 success: function (_data) {
108 /* No proper reponse YOCTO #7995
109 if (_data.error !== "ok") {
110 console.warn(_data.error);
111 } else { */
112 if (onsuccess !== undefined) onsuccess(_data);
113 // }
114 },
115 error: function (_data) {
116 console.warn("Call failed");
117 console.warn(_data);
118 if (onfail) onfail(data);
119 } });
120 }
121
122 /* cancelABuild:
123 * url: projectbuilds
124 * builds_ids: space separated list of build request ids
125 * onsuccess: callback for successful execution
126 * onfail: callback for failed execution
127 */
128 function _cancelABuild(url, build_ids, onsuccess, onfail){
129 $.ajax( {
130 type: "POST",
131 url: url,
132 data: { 'buildCancel': build_ids },
133 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
134 success: function (_data) {
135 if (_data.error !== "ok") {
136 console.warn(_data.error);
137 } else {
138 if (onsuccess !== undefined) onsuccess(_data);
139 }
140 },
141 error: function (_data) {
142 console.warn("Call failed");
143 console.warn(_data);
144 if (onfail) onfail(_data);
145 }
146 });
147 }
148
149 /* Get a project's configuration info */
150 function _getProjectInfo(url, onsuccess, onfail){
151 $.ajax({
152 type: "GET",
153 data : { format: "json" },
154 url: url,
155 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
156 success: function (_data) {
157 if (_data.error !== "ok") {
158 console.warn(_data.error);
159 } else {
160 if (onsuccess !== undefined) onsuccess(_data);
161 }
162 },
163 error: function (_data) {
164 console.warn(_data);
165 if (onfail) onfail(_data);
166 }
167 });
168 }
169
170 /* Properties for data can be:
171 * layerDel (csv)
172 * layerAdd (csv)
173 * projectName
174 * projectVersion
175 * machineName
176 */
177 function _editCurrentProject(data, onSuccess, onFail){
178 $.ajax({
179 type: "POST",
180 url: libtoaster.ctx.projectPageUrl + "?format=json",
181 data: data,
182 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
183 success: function (data) {
184 if (data.error != "ok") {
185 console.log(data.error);
186 if (onFail !== undefined)
187 onFail(data);
188 } else {
189 if (onSuccess !== undefined)
190 onSuccess(data);
191 }
192 },
193 error: function (data) {
194 console.log("Call failed");
195 console.log(data);
196 }
197 });
198 }
199
200 function _getLayerDepsForProject(url, onSuccess, onFail){
201 /* Check for dependencies not in the current project */
202 $.getJSON(url,
203 { format: 'json' },
204 function(data) {
205 if (data.error != "ok") {
206 console.log(data.error);
207 if (onFail !== undefined)
208 onFail(data);
209 } else {
210 var deps = {};
211 /* Filter out layer dep ids which are in the
212 * project already.
213 */
214 deps.list = data.layerdeps.list.filter(function(layerObj){
215 return (data.projectlayers.lastIndexOf(layerObj.id) < 0);
216 });
217
218 onSuccess(deps);
219 }
220 }, function() {
221 console.log("E: Failed to make request");
222 });
223 }
224
225 /* parses the query string of the current window.location to an object */
226 function _parseUrlParams() {
227 var string = window.location.search;
228 string = string.substr(1);
229 var stringArray = string.split ("&");
230 var obj = {};
231
232 for (var i in stringArray) {
233 var keyVal = stringArray[i].split ("=");
234 obj[keyVal[0]] = keyVal[1];
235 }
236
237 return obj;
238 }
239
240 /* takes a flat object and outputs it as a query string
241 * e.g. the output of dumpsUrlParams
242 */
243 function _dumpsUrlParams(obj) {
244 var str = "?";
245
246 for (var key in obj){
247 if (!obj[key])
248 continue;
249
250 str += key+ "="+obj[key].toString();
251 str += "&";
252 }
253
254 /* Maintain the current hash */
255 str += window.location.hash;
256
257 return str;
258 }
259
260 function _addRmLayer(layerObj, add, doneCb){
261 if (add === true) {
262 /* If adding get the deps for this layer */
263 libtoaster.getLayerDepsForProject(layerObj.layerdetailurl,
264 function (layers) {
265
266 /* got result for dependencies */
267 if (layers.list.length === 0){
268 var editData = { layerAdd : layerObj.id };
269 libtoaster.editCurrentProject(editData, function() {
270 doneCb([]);
271 });
272 return;
273 } else {
274 try {
275 showLayerDepsModal(layerObj, layers.list, null, null, true, doneCb);
276 } catch (e) {
277 $.getScript(libtoaster.ctx.jsUrl + "layerDepsModal.js", function(){
278 showLayerDepsModal(layerObj, layers.list, null, null, true, doneCb);
279 }, function(){
280 console.warn("Failed to load layerDepsModal");
281 });
282 }
283 }
284 }, null);
285 } else if (add === false) {
286 var editData = { layerDel : layerObj.id };
287
288 libtoaster.editCurrentProject(editData, function () {
289 doneCb([]);
290 }, function () {
291 console.warn ("Removing layer from project failed");
292 doneCb(null);
293 });
294 }
295 }
296
297 function _makeLayerAddRmAlertMsg(layer, layerDepsList, add) {
298 var alertMsg;
299
300 if (layerDepsList.length > 0 && add === true) {
301 alertMsg = $("<span>You have added <strong>"+(layerDepsList.length+1)+"</strong> layers to your project: <a id=\"layer-affected-name\"></a> and its dependencies </span>");
302
303 /* Build the layer deps list */
304 layerDepsList.map(function(layer, i){
305 var link = $("<a></a>");
306
307 link.attr("href", layer.layerdetailurl);
308 link.text(layer.name);
309 link.tooltip({title: layer.tooltip});
310
311 if (i !== 0)
312 alertMsg.append(", ");
313
314 alertMsg.append(link);
315 });
316 } else if (layerDepsList.length === 0 && add === true) {
317 alertMsg = $("<span>You have added <strong>1</strong> layer to your project: <a id=\"layer-affected-name\"></a></span></span>");
318 } else if (add === false) {
319 alertMsg = $("<span>You have deleted <strong>1</strong> layer from your project: <a id=\"layer-affected-name\"></a></span>");
320 }
321
322 alertMsg.children("#layer-affected-name").text(layer.name);
323 alertMsg.children("#layer-affected-name").attr("href", layer.layerdetailurl);
324
325 return alertMsg.html();
326 }
327
328 function _showChangeNotification(message){
329 var alertMsg = $("#change-notification-msg");
330
331 alertMsg.html(message);
332 $("#change-notification, #change-notification *").fadeIn();
333 }
334
335
336 return {
337 reload_params : reload_params,
338 startABuild : _startABuild,
339 cancelABuild : _cancelABuild,
340 makeTypeahead : _makeTypeahead,
341 getProjectInfo: _getProjectInfo,
342 getLayerDepsForProject : _getLayerDepsForProject,
343 editCurrentProject : _editCurrentProject,
344 debug: false,
345 parseUrlParams : _parseUrlParams,
346 dumpsUrlParams : _dumpsUrlParams,
347 addRmLayer : _addRmLayer,
348 makeLayerAddRmAlertMsg : _makeLayerAddRmAlertMsg,
349 showChangeNotification : _showChangeNotification,
350 };
351})();
352
353/* keep this in the global scope for compatability */
354function reload_params(params) {
355 var uri = window.location.href;
356 var splitlist = uri.split("?");
357 var url = splitlist[0];
358 var parameters = splitlist[1];
359 // deserialize the call parameters
360 var cparams = [];
361 if(parameters)
362 cparams = parameters.split("&");
363
364 var nparams = {};
365 for (var i = 0; i < cparams.length; i++) {
366 var temp = cparams[i].split("=");
367 nparams[temp[0]] = temp[1];
368 }
369 // update parameter values
370 for (i in params) {
371 nparams[encodeURIComponent(i)] = encodeURIComponent(params[i]);
372 }
373 // serialize the structure
374 var callparams = [];
375 for (i in nparams) {
376 callparams.push(i+"="+nparams[i]);
377 }
378 window.location.href = url+"?"+callparams.join('&');
379}
380
381
382/* Things that happen for all pages */
383$(document).ready(function() {
384
385 var ajaxLoadingTimer;
386
387 /* If we don't have a console object which might be the case in some
388 * browsers, no-op it to avoid undefined errors.
389 */
390 if (!window.console) {
391 window.console = {};
392 window.console.warn = function() {};
393 window.console.error = function() {};
394 }
395
396 /*
397 * PrettyPrint plugin.
398 *
399 */
400 // Init
401 prettyPrint();
402
403 // Prevent invalid links from jumping page scroll
404 $('a[href=#]').click(function() {
405 return false;
406 });
407
408
409 /* START TODO Delete this section now redundant */
410 /* Belen's additions */
411
412 // turn Edit columns dropdown into a multiselect menu
413 $('.dropdown-menu input, .dropdown-menu label').click(function(e) {
414 e.stopPropagation();
415 });
416
417 // enable popovers in any table cells that contain an anchor with the
418 // .btn class applied, and make sure popovers work on click, are mutually
419 // exclusive and they close when your click outside their area
420
421 $('html').click(function(){
422 $('td > a.btn').popover('hide');
423 });
424
425 $('td > a.btn').popover({
426 html:true,
427 placement:'left',
428 container:'body',
429 trigger:'manual'
430 }).click(function(e){
431 $('td > a.btn').not(this).popover('hide');
432 // ideally we would use 'toggle' here
433 // but it seems buggy in our Bootstrap version
434 $(this).popover('show');
435 e.stopPropagation();
436 });
437
438 // enable tooltips for applied filters
439 $('th a.btn-primary').tooltip({container:'body', html:true, placement:'bottom', delay:{hide:1500}});
440
441 // hide applied filter tooltip when you click on the filter button
442 $('th a.btn-primary').click(function () {
443 $('.tooltip').hide();
444 });
445
446 // enable help information tooltip
447 $(".get-help").tooltip({container:'body', html:true, delay:{show:300}});
448
449 // show help bubble only on hover inside tables
450 $(".hover-help").css("visibility","hidden");
451 $("th, td").hover(function () {
452 $(this).find(".hover-help").css("visibility","visible");
453 });
454 $("th, td").mouseleave(function () {
455 $(this).find(".hover-help").css("visibility","hidden");
456 });
457
458 /* END TODO Delete this section now redundant */
459
460 // show task type and outcome in task details pages
461 $(".task-info").tooltip({ container: 'body', html: true, delay: {show: 200}, placement: 'right' });
462
463 // initialise the tooltips for the icon-pencil icons
464 $(".icon-pencil").tooltip({ container: 'body', html: true, delay: {show: 400}, title: "Change" });
465
466 // initialise the tooltips for the download icons
467 $(".icon-download-alt").tooltip({ container: 'body', html: true, delay: { show: 200 } });
468
469 // initialise popover for debug information
470 $(".icon-info-sign").popover( { placement: 'bottom', html: true, container: 'body' });
471
472 // linking directly to tabs
473 $(function(){
474 var hash = window.location.hash;
475 $('ul.nav a[href="' + hash + '"]').tab('show');
476
477 $('.nav-tabs a').click(function () {
478 $(this).tab('show');
479 $('body').scrollTop();
480 });
481 });
482
483 // toggle for long content (variables, python stack trace, etc)
484 $('.full, .full-hide').hide();
485 $('.full-show').click(function(){
486 $('.full').slideDown(function(){
487 $('.full-hide').show();
488 });
489 $(this).hide();
490 });
491 $('.full-hide').click(function(){
492 $(this).hide();
493 $('.full').slideUp(function(){
494 $('.full-show').show();
495 });
496 });
497
498 //toggle the errors and warnings sections
499 $('.show-errors').click(function() {
500 $('#collapse-errors').addClass('in');
501 });
502 $('.toggle-errors').click(function() {
503 $('#collapse-errors').toggleClass('in');
504 });
505 $('.show-warnings').click(function() {
506 $('#collapse-warnings').addClass('in');
507 });
508 $('.toggle-warnings').click(function() {
509 $('#collapse-warnings').toggleClass('in');
510 });
511 $('.show-exceptions').click(function() {
512 $('#collapse-exceptions').addClass('in');
513 });
514 $('.toggle-exceptions').click(function() {
515 $('#collapse-exceptions').toggleClass('in');
516 });
517
518
519 $("#hide-alert").click(function(){
520 $(this).parent().fadeOut();
521 });
522
523 //show warnings section when requested from the previous page
524 if (location.href.search('#warnings') > -1) {
525 $('#collapse-warnings').addClass('in');
526 }
527
528 /* Show the loading notification if nothing has happend after 1.5
529 * seconds
530 */
531 $(document).bind("ajaxStart", function(){
532 if (ajaxLoadingTimer)
533 window.clearTimeout(ajaxLoadingTimer);
534
535 ajaxLoadingTimer = window.setTimeout(function() {
536 $("#loading-notification").fadeIn();
537 }, 1200);
538 });
539
540 $(document).bind("ajaxStop", function(){
541 if (ajaxLoadingTimer)
542 window.clearTimeout(ajaxLoadingTimer);
543
544 $("#loading-notification").fadeOut();
545 });
546
547 $(document).ajaxError(function(event, jqxhr, settings, errMsg){
548 if (errMsg === 'abort')
549 return;
550
551 console.warn("Problem with xhr call");
552 console.warn(errMsg);
553 console.warn(jqxhr.responseText);
554 });
555
556 function check_for_duplicate_ids () {
557 /* warn about duplicate element ids */
558 var ids = {};
559 $("[id]").each(function() {
560 if (this.id && ids[this.id]) {
561 console.warn('Duplicate element id #'+this.id);
562 }
563 ids[this.id] = true;
564 });
565 }
566
567 if (libtoaster.debug) {
568 check_for_duplicate_ids();
569 } else {
570 /* Debug is false so supress warnings by overriding the functions */
571 window.console.warn = function () {};
572 window.console.error = function () {};
573 }
574});