blob: f2c45c833e77d8fb22850b7ba5a7a1464750e77e [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 */
Patrick Williamsc0f7c042017-02-23 20:41:17 -06006var libtoaster = (function () {
7 // prevent conflicts with Bootstrap 2's typeahead (required during
8 // transition from v2 to v3)
9 var typeahead = jQuery.fn.typeahead.noConflict();
10 jQuery.fn._typeahead = typeahead;
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011
Patrick Williamsc0f7c042017-02-23 20:41:17 -060012 /* Make a typeahead from an input element
13 *
14 * _makeTypeahead parameters
15 * jQElement: input element as selected by $('selector')
16 * xhrUrl: the url to get the JSON from; this URL should return JSON in the
17 * format:
18 * { "results": [ { "name": "test", "detail" : "a test thing" }, ... ] }
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019 * xhrParams: the data/parameters to pass to the getJSON url e.g.
Patrick Williamsc0f7c042017-02-23 20:41:17 -060020 * { 'type' : 'projects' }; the text typed will be passed as 'search'.
21 * selectedCB: function to call once an item has been selected; has
22 * signature selectedCB(item), where item is an item in the format shown
23 * in the JSON list above, i.e.
24 * { "name": "name", "detail": "detail" }.
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025 */
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026 function _makeTypeahead(jQElement, xhrUrl, xhrParams, selectedCB) {
27 if (!xhrUrl || xhrUrl.length === 0) {
28 throw("No url supplied for typeahead");
29 }
Patrick Williamsc124f4f2015-09-15 14:41:29 -050030
31 var xhrReq;
32
Patrick Williamsc0f7c042017-02-23 20:41:17 -060033 jQElement._typeahead(
34 {
35 highlight: true,
36 classNames: {
37 open: "dropdown-menu",
38 cursor: "active"
39 }
40 },
41 {
42 source: function (query, syncResults, asyncResults) {
Patrick Williamsc124f4f2015-09-15 14:41:29 -050043 xhrParams.search = query;
44
Patrick Williamsc0f7c042017-02-23 20:41:17 -060045 // if we have a request in progress, cancel it and start another
46 if (xhrReq) {
Patrick Williamsc124f4f2015-09-15 14:41:29 -050047 xhrReq.abort();
Patrick Williamsc0f7c042017-02-23 20:41:17 -060048 }
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049
Patrick Williamsc0f7c042017-02-23 20:41:17 -060050 xhrReq = $.getJSON(xhrUrl, xhrParams, function (data) {
Patrick Williamsc124f4f2015-09-15 14:41:29 -050051 if (data.error !== "ok") {
Patrick Williamsc0f7c042017-02-23 20:41:17 -060052 console.error("Error getting data from server: " + data.error);
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053 return;
54 }
55
56 xhrReq = null;
57
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 asyncResults(data.results);
Patrick Williamsc124f4f2015-09-15 14:41:29 -050059 });
60 },
Patrick Williamsc0f7c042017-02-23 20:41:17 -060061
62 // how the selected item is shown in the input
63 display: function (item) {
64 return item.name;
Patrick Williamsc124f4f2015-09-15 14:41:29 -050065 },
Patrick Williamsc0f7c042017-02-23 20:41:17 -060066
67 templates: {
68 // how the item is displayed in the dropdown
69 suggestion: function (item) {
70 var elt = document.createElement("div");
71 elt.innerHTML = item.name + " " + item.detail;
72 return elt;
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073 }
Patrick Williamsc0f7c042017-02-23 20:41:17 -060074 }
75 }
76 );
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077
Patrick Williamsc0f7c042017-02-23 20:41:17 -060078 // when an item is selected using the typeahead, invoke the callback
79 jQElement.on("typeahead:select", function (event, item) {
80 selectedCB(item);
Patrick Williamsc124f4f2015-09-15 14:41:29 -050081 });
Patrick Williamsc124f4f2015-09-15 14:41:29 -050082 }
83
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050084 /* startABuild:
85 * url: xhr_buildrequest or null for current project
86 * targets: an array or space separated list of targets to build
87 * onsuccess: callback for successful execution
88 * onfail: callback for failed execution
89 */
90 function _startABuild (url, targets, onsuccess, onfail) {
Patrick Williamsc124f4f2015-09-15 14:41:29 -050091
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050092 if (!url)
93 url = libtoaster.ctx.xhrBuildRequestUrl;
94
95 /* Flatten the array of targets into a space spearated list */
96 if (targets instanceof Array){
97 targets = targets.reduce(function(prevV, nextV){
98 return prev + ' ' + next;
99 });
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500100 }
101
102 $.ajax( {
103 type: "POST",
104 url: url,
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500105 data: { 'targets' : targets },
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500106 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
107 success: function (_data) {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500108 if (_data.error !== "ok") {
109 console.warn(_data.error);
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500110 } else {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111 if (onsuccess !== undefined) onsuccess(_data);
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500112 }
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500113 },
114 error: function (_data) {
115 console.warn("Call failed");
116 console.warn(_data);
117 if (onfail) onfail(data);
118 } });
119 }
120
121 /* cancelABuild:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500122 * url: xhr_buildrequest url or null for current project
123 * buildRequestIds: space separated list of build request ids
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124 * onsuccess: callback for successful execution
125 * onfail: callback for failed execution
126 */
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500127 function _cancelABuild(url, buildRequestIds, onsuccess, onfail){
128 if (!url)
129 url = libtoaster.ctx.xhrBuildRequestUrl;
130
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131 $.ajax( {
132 type: "POST",
133 url: url,
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500134 data: { 'buildCancel': buildRequestIds },
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
136 success: function (_data) {
137 if (_data.error !== "ok") {
138 console.warn(_data.error);
139 } else {
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500140 if (onsuccess) onsuccess(_data);
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141 }
142 },
143 error: function (_data) {
144 console.warn("Call failed");
145 console.warn(_data);
146 if (onfail) onfail(_data);
147 }
148 });
149 }
150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 function _getMostRecentBuilds(url, onsuccess, onfail) {
152 $.ajax({
153 url: url,
154 type: 'GET',
155 data : {format: 'json'},
156 headers: {'X-CSRFToken': $.cookie('csrftoken')},
157 success: function (data) {
158 onsuccess ? onsuccess(data) : console.log(data);
159 },
160 error: function (data) {
161 onfail ? onfail(data) : console.error(data);
162 }
163 });
164 }
165
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166 /* Get a project's configuration info */
167 function _getProjectInfo(url, onsuccess, onfail){
168 $.ajax({
169 type: "GET",
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500170 url: url,
171 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
172 success: function (_data) {
173 if (_data.error !== "ok") {
174 console.warn(_data.error);
175 } else {
176 if (onsuccess !== undefined) onsuccess(_data);
177 }
178 },
179 error: function (_data) {
180 console.warn(_data);
181 if (onfail) onfail(_data);
182 }
183 });
184 }
185
186 /* Properties for data can be:
187 * layerDel (csv)
188 * layerAdd (csv)
189 * projectName
190 * projectVersion
191 * machineName
192 */
193 function _editCurrentProject(data, onSuccess, onFail){
194 $.ajax({
195 type: "POST",
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600196 url: libtoaster.ctx.xhrProjectUrl,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 data: data,
198 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
199 success: function (data) {
200 if (data.error != "ok") {
201 console.log(data.error);
202 if (onFail !== undefined)
203 onFail(data);
204 } else {
205 if (onSuccess !== undefined)
206 onSuccess(data);
207 }
208 },
209 error: function (data) {
210 console.log("Call failed");
211 console.log(data);
212 }
213 });
214 }
215
216 function _getLayerDepsForProject(url, onSuccess, onFail){
217 /* Check for dependencies not in the current project */
218 $.getJSON(url,
219 { format: 'json' },
220 function(data) {
221 if (data.error != "ok") {
222 console.log(data.error);
223 if (onFail !== undefined)
224 onFail(data);
225 } else {
226 var deps = {};
227 /* Filter out layer dep ids which are in the
228 * project already.
229 */
230 deps.list = data.layerdeps.list.filter(function(layerObj){
231 return (data.projectlayers.lastIndexOf(layerObj.id) < 0);
232 });
233
234 onSuccess(deps);
235 }
236 }, function() {
237 console.log("E: Failed to make request");
238 });
239 }
240
241 /* parses the query string of the current window.location to an object */
242 function _parseUrlParams() {
243 var string = window.location.search;
244 string = string.substr(1);
245 var stringArray = string.split ("&");
246 var obj = {};
247
248 for (var i in stringArray) {
249 var keyVal = stringArray[i].split ("=");
250 obj[keyVal[0]] = keyVal[1];
251 }
252
253 return obj;
254 }
255
256 /* takes a flat object and outputs it as a query string
257 * e.g. the output of dumpsUrlParams
258 */
259 function _dumpsUrlParams(obj) {
260 var str = "?";
261
262 for (var key in obj){
263 if (!obj[key])
264 continue;
265
266 str += key+ "="+obj[key].toString();
267 str += "&";
268 }
269
270 /* Maintain the current hash */
271 str += window.location.hash;
272
273 return str;
274 }
275
276 function _addRmLayer(layerObj, add, doneCb){
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500277 if (layerObj.xhrLayerUrl === undefined){
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800278 alert("ERROR: missing xhrLayerUrl object. Please file a bug.");
279 return;
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500280 }
281
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282 if (add === true) {
283 /* If adding get the deps for this layer */
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500284 libtoaster.getLayerDepsForProject(layerObj.xhrLayerUrl,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285 function (layers) {
286
287 /* got result for dependencies */
288 if (layers.list.length === 0){
289 var editData = { layerAdd : layerObj.id };
290 libtoaster.editCurrentProject(editData, function() {
291 doneCb([]);
292 });
293 return;
294 } else {
295 try {
296 showLayerDepsModal(layerObj, layers.list, null, null, true, doneCb);
297 } catch (e) {
298 $.getScript(libtoaster.ctx.jsUrl + "layerDepsModal.js", function(){
299 showLayerDepsModal(layerObj, layers.list, null, null, true, doneCb);
300 }, function(){
301 console.warn("Failed to load layerDepsModal");
302 });
303 }
304 }
305 }, null);
306 } else if (add === false) {
307 var editData = { layerDel : layerObj.id };
308
309 libtoaster.editCurrentProject(editData, function () {
310 doneCb([]);
311 }, function () {
312 console.warn ("Removing layer from project failed");
313 doneCb(null);
314 });
315 }
316 }
317
318 function _makeLayerAddRmAlertMsg(layer, layerDepsList, add) {
319 var alertMsg;
320
321 if (layerDepsList.length > 0 && add === true) {
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 alertMsg = $("<span>You have added <strong>"+(layerDepsList.length+1)+"</strong> layers to your project: <a class=\"alert-link\" id=\"layer-affected-name\"></a> and its dependencies </span>");
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323
324 /* Build the layer deps list */
325 layerDepsList.map(function(layer, i){
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600326 var link = $("<a class=\"alert-link\"></a>");
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500327
328 link.attr("href", layer.layerdetailurl);
329 link.text(layer.name);
330 link.tooltip({title: layer.tooltip});
331
332 if (i !== 0)
333 alertMsg.append(", ");
334
335 alertMsg.append(link);
336 });
337 } else if (layerDepsList.length === 0 && add === true) {
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600338 alertMsg = $("<span>You have added <strong>1</strong> layer to your project: <a class=\"alert-link\" id=\"layer-affected-name\"></a></span></span>");
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339 } else if (add === false) {
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600340 alertMsg = $("<span>You have removed <strong>1</strong> layer from your project: <a class=\"alert-link\" id=\"layer-affected-name\"></a></span>");
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341 }
342
343 alertMsg.children("#layer-affected-name").text(layer.name);
344 alertMsg.children("#layer-affected-name").attr("href", layer.layerdetailurl);
345
346 return alertMsg.html();
347 }
348
349 function _showChangeNotification(message){
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600350 $(".alert-dismissible").fadeOut().promise().done(function(){
351 var alertMsg = $("#change-notification-msg");
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500352
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600353 alertMsg.html(message);
354 $("#change-notification, #change-notification *").fadeIn();
355 });
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500356 }
357
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500358 function _createCustomRecipe(name, baseRecipeId, doneCb){
359 var data = {
360 'name' : name,
361 'project' : libtoaster.ctx.projectId,
362 'base' : baseRecipeId,
363 };
364
365 $.ajax({
366 type: "POST",
367 url: libtoaster.ctx.xhrCustomRecipeUrl,
368 data: data,
369 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
370 success: function (ret) {
371 if (doneCb){
372 doneCb(ret);
373 } else if (ret.error !== "ok") {
374 console.warn(ret.error);
375 }
376 },
377 error: function (ret) {
378 console.warn("Call failed");
379 console.warn(ret);
380 }
381 });
382 }
383
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600384 /* Validate project names. Use unique project names
385
386 All arguments accepted by this function are JQeury objects.
387
388 For example if the HTML element has "hint-error-project-name", then
389 it is passed to this function as $("#hint-error-project-name").
390
391 Arg1 - projectName : This is a string object. In the HTML, project name will be entered here.
392 Arg2 - hintEerror : This is a jquery object which will accept span which throws error for
393 duplicate project
394 Arg3 - ctrlGrpValidateProjectName : This object holds the div with class "control-group"
395 Arg4 - enableOrDisableBtn : This object will help the API to enable or disable the form.
396 For example in the new project the create project button will be hidden if the
397 duplicate project exist. Similarly in the projecttopbar the save button will be
398 disabled if the project name already exist.
399
400 Return - This function doesn't return anything. It sets/unsets the behavior of the elements.
401 */
402
403 function _makeProjectNameValidation(projectName, hintError,
404 ctrlGrpValidateProjectName, enableOrDisableBtn ) {
405
406 function checkProjectName(projectName){
407 $.ajax({
408 type: "GET",
409 url: libtoaster.ctx.projectsTypeAheadUrl,
410 data: { 'search' : projectName },
411 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
412 success: function(data){
413 if (data.results.length > 0 &&
414 data.results[0].name === projectName) {
415 // This project name exists hence show the error and disable
416 // the save button
417 ctrlGrpValidateProjectName.addClass('has-error');
418 hintError.show();
419 enableOrDisableBtn.attr('disabled', 'disabled');
420 } else {
421 ctrlGrpValidateProjectName.removeClass('has-error');
422 hintError.hide();
423 enableOrDisableBtn.removeAttr('disabled');
424 }
425 },
426 error: function (data) {
427 console.log(data);
428 },
429 });
430 }
431
432 /* The moment user types project name remove the error */
433 projectName.on("input", function() {
434 var projectName = $(this).val();
435 checkProjectName(projectName)
436 });
437
438 /* Validate new project name */
439 projectName.on("blur", function(){
440 var projectName = $(this).val();
441 checkProjectName(projectName)
442 });
443 }
444
445 // if true, the loading spinner for Ajax requests will be displayed
446 // if requests take more than 1200ms
447 var ajaxLoadingTimerEnabled = true;
448
449 // turn on the page-level loading spinner for Ajax requests
450 function _enableAjaxLoadingTimer() {
451 ajaxLoadingTimerEnabled = true;
452 }
453
454 // turn off the page-level loading spinner for Ajax requests
455 function _disableAjaxLoadingTimer() {
456 ajaxLoadingTimerEnabled = false;
457 }
458
459 /* Utility function to set a notification for the next page load */
460 function _setNotification(name, message){
461 var data = {
462 name: name,
463 message: message
464 };
465
466 $.cookie('toaster-notification', JSON.stringify(data), { path: '/'});
467 }
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800469 /* _updateProject:
470 * url: xhrProjectUpdateUrl or null for current project
471 * onsuccess: callback for successful execution
472 * onfail: callback for failed execution
473 */
474 function _updateProject (url, targets, default_image, onsuccess, onfail) {
475
476 if (!url)
477 url = libtoaster.ctx.xhrProjectUpdateUrl;
478
479 /* Flatten the array of targets into a space spearated list */
480 if (targets instanceof Array){
481 targets = targets.reduce(function(prevV, nextV){
482 return prev + ' ' + next;
483 });
484 }
485
486 $.ajax( {
487 type: "POST",
488 url: url,
489 data: { 'do_update' : 'True' , 'targets' : targets , 'default_image' : default_image , },
490 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
491 success: function (_data) {
492 if (_data.error !== "ok") {
493 console.warn(_data.error);
494 } else {
495 if (onsuccess !== undefined) onsuccess(_data);
496 }
497 },
498 error: function (_data) {
499 console.warn("Call failed");
500 console.warn(_data);
501 if (onfail) onfail(data);
502 } });
503 }
504
505 /* _cancelProject:
506 * url: xhrProjectUpdateUrl or null for current project
507 * onsuccess: callback for successful execution
508 * onfail: callback for failed execution
509 */
510 function _cancelProject (url, onsuccess, onfail) {
511
512 if (!url)
513 url = libtoaster.ctx.xhrProjectCancelUrl;
514
515 $.ajax( {
516 type: "POST",
517 url: url,
518 data: { 'do_cancel' : 'True' },
519 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
520 success: function (_data) {
521 if (_data.error !== "ok") {
522 console.warn(_data.error);
523 } else {
524 if (onsuccess !== undefined) onsuccess(_data);
525 }
526 },
527 error: function (_data) {
528 console.warn("Call failed");
529 console.warn(_data);
530 if (onfail) onfail(data);
531 } });
532 }
533
534 /* _setDefaultImage:
535 * url: xhrSetDefaultImageUrl or null for current project
536 * targets: an array or space separated list of targets to set as default
537 * onsuccess: callback for successful execution
538 * onfail: callback for failed execution
539 */
540 function _setDefaultImage (url, targets, onsuccess, onfail) {
541
542 if (!url)
543 url = libtoaster.ctx.xhrSetDefaultImageUrl;
544
545 /* Flatten the array of targets into a space spearated list */
546 if (targets instanceof Array){
547 targets = targets.reduce(function(prevV, nextV){
548 return prev + ' ' + next;
549 });
550 }
551
552 $.ajax( {
553 type: "POST",
554 url: url,
555 data: { 'targets' : targets },
556 headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
557 success: function (_data) {
558 if (_data.error !== "ok") {
559 console.warn(_data.error);
560 } else {
561 if (onsuccess !== undefined) onsuccess(_data);
562 }
563 },
564 error: function (_data) {
565 console.warn("Call failed");
566 console.warn(_data);
567 if (onfail) onfail(data);
568 } });
569 }
570
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571 return {
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600572 enableAjaxLoadingTimer: _enableAjaxLoadingTimer,
573 disableAjaxLoadingTimer: _disableAjaxLoadingTimer,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574 reload_params : reload_params,
575 startABuild : _startABuild,
576 cancelABuild : _cancelABuild,
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600577 getMostRecentBuilds: _getMostRecentBuilds,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500578 makeTypeahead : _makeTypeahead,
579 getProjectInfo: _getProjectInfo,
580 getLayerDepsForProject : _getLayerDepsForProject,
581 editCurrentProject : _editCurrentProject,
582 debug: false,
583 parseUrlParams : _parseUrlParams,
584 dumpsUrlParams : _dumpsUrlParams,
585 addRmLayer : _addRmLayer,
586 makeLayerAddRmAlertMsg : _makeLayerAddRmAlertMsg,
587 showChangeNotification : _showChangeNotification,
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500588 createCustomRecipe: _createCustomRecipe,
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600589 makeProjectNameValidation: _makeProjectNameValidation,
590 setNotification: _setNotification,
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800591 updateProject : _updateProject,
592 cancelProject : _cancelProject,
593 setDefaultImage : _setDefaultImage,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594 };
595})();
596
597/* keep this in the global scope for compatability */
598function reload_params(params) {
599 var uri = window.location.href;
600 var splitlist = uri.split("?");
601 var url = splitlist[0];
602 var parameters = splitlist[1];
603 // deserialize the call parameters
604 var cparams = [];
605 if(parameters)
606 cparams = parameters.split("&");
607
608 var nparams = {};
609 for (var i = 0; i < cparams.length; i++) {
610 var temp = cparams[i].split("=");
611 nparams[temp[0]] = temp[1];
612 }
613 // update parameter values
614 for (i in params) {
615 nparams[encodeURIComponent(i)] = encodeURIComponent(params[i]);
616 }
617 // serialize the structure
618 var callparams = [];
619 for (i in nparams) {
620 callparams.push(i+"="+nparams[i]);
621 }
622 window.location.href = url+"?"+callparams.join('&');
623}
624
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500625/* Things that happen for all pages */
626$(document).ready(function() {
627
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600628 (function showNotificationRequest(){
629 var cookie = $.cookie('toaster-notification');
630
631 if (!cookie)
632 return;
633
634 var notificationData = JSON.parse(cookie);
635
636 libtoaster.showChangeNotification(notificationData.message);
637
638 $.removeCookie('toaster-notification', { path: "/"});
639 })();
640
641
642
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643 var ajaxLoadingTimer;
644
645 /* If we don't have a console object which might be the case in some
646 * browsers, no-op it to avoid undefined errors.
647 */
648 if (!window.console) {
649 window.console = {};
650 window.console.warn = function() {};
651 window.console.error = function() {};
652 }
653
654 /*
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500655 * highlight plugin.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656 */
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500657 hljs.initHighlightingOnLoad();
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500658
659 // Prevent invalid links from jumping page scroll
660 $('a[href=#]').click(function() {
661 return false;
662 });
663
664
665 /* START TODO Delete this section now redundant */
666 /* Belen's additions */
667
668 // turn Edit columns dropdown into a multiselect menu
669 $('.dropdown-menu input, .dropdown-menu label').click(function(e) {
670 e.stopPropagation();
671 });
672
673 // enable popovers in any table cells that contain an anchor with the
674 // .btn class applied, and make sure popovers work on click, are mutually
675 // exclusive and they close when your click outside their area
676
677 $('html').click(function(){
678 $('td > a.btn').popover('hide');
679 });
680
681 $('td > a.btn').popover({
682 html:true,
683 placement:'left',
684 container:'body',
685 trigger:'manual'
686 }).click(function(e){
687 $('td > a.btn').not(this).popover('hide');
688 // ideally we would use 'toggle' here
689 // but it seems buggy in our Bootstrap version
690 $(this).popover('show');
691 e.stopPropagation();
692 });
693
694 // enable tooltips for applied filters
695 $('th a.btn-primary').tooltip({container:'body', html:true, placement:'bottom', delay:{hide:1500}});
696
697 // hide applied filter tooltip when you click on the filter button
698 $('th a.btn-primary').click(function () {
699 $('.tooltip').hide();
700 });
701
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500702 /* Initialise bootstrap tooltips */
703 $(".get-help, [data-toggle=tooltip]").tooltip({
704 container : 'body',
705 html : true,
706 delay: { show : 300 }
707 });
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600709 // show help bubble on hover inside tables
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500710 $("table").on("mouseover", "th, td", function () {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500711 $(this).find(".hover-help").css("visibility","visible");
712 });
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500713
714 $("table").on("mouseleave", "th, td", function () {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715 $(this).find(".hover-help").css("visibility","hidden");
716 });
717
718 /* END TODO Delete this section now redundant */
719
720 // show task type and outcome in task details pages
721 $(".task-info").tooltip({ container: 'body', html: true, delay: {show: 200}, placement: 'right' });
722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 // initialise the tooltips for the edit icons
724 $(".glyphicon-edit").tooltip({ container: 'body', html: true, delay: {show: 400}, title: "Change" });
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
726 // initialise the tooltips for the download icons
727 $(".icon-download-alt").tooltip({ container: 'body', html: true, delay: { show: 200 } });
728
729 // initialise popover for debug information
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600730 $(".glyphicon-info-sign").popover( { placement: 'bottom', html: true, container: 'body' });
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731
732 // linking directly to tabs
733 $(function(){
734 var hash = window.location.hash;
735 $('ul.nav a[href="' + hash + '"]').tab('show');
736
737 $('.nav-tabs a').click(function () {
738 $(this).tab('show');
739 $('body').scrollTop();
740 });
741 });
742
743 // toggle for long content (variables, python stack trace, etc)
744 $('.full, .full-hide').hide();
745 $('.full-show').click(function(){
746 $('.full').slideDown(function(){
747 $('.full-hide').show();
748 });
749 $(this).hide();
750 });
751 $('.full-hide').click(function(){
752 $(this).hide();
753 $('.full').slideUp(function(){
754 $('.full-show').show();
755 });
756 });
757
758 //toggle the errors and warnings sections
759 $('.show-errors').click(function() {
760 $('#collapse-errors').addClass('in');
761 });
762 $('.toggle-errors').click(function() {
763 $('#collapse-errors').toggleClass('in');
764 });
765 $('.show-warnings').click(function() {
766 $('#collapse-warnings').addClass('in');
767 });
768 $('.toggle-warnings').click(function() {
769 $('#collapse-warnings').toggleClass('in');
770 });
771 $('.show-exceptions').click(function() {
772 $('#collapse-exceptions').addClass('in');
773 });
774 $('.toggle-exceptions').click(function() {
775 $('#collapse-exceptions').toggleClass('in');
776 });
777
778
779 $("#hide-alert").click(function(){
780 $(this).parent().fadeOut();
781 });
782
783 //show warnings section when requested from the previous page
784 if (location.href.search('#warnings') > -1) {
785 $('#collapse-warnings').addClass('in');
786 }
787
788 /* Show the loading notification if nothing has happend after 1.5
789 * seconds
790 */
791 $(document).bind("ajaxStart", function(){
792 if (ajaxLoadingTimer)
793 window.clearTimeout(ajaxLoadingTimer);
794
795 ajaxLoadingTimer = window.setTimeout(function() {
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600796 if (libtoaster.ajaxLoadingTimerEnabled) {
797 $("#loading-notification").fadeIn();
798 }
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799 }, 1200);
800 });
801
802 $(document).bind("ajaxStop", function(){
803 if (ajaxLoadingTimer)
804 window.clearTimeout(ajaxLoadingTimer);
805
806 $("#loading-notification").fadeOut();
807 });
808
809 $(document).ajaxError(function(event, jqxhr, settings, errMsg){
810 if (errMsg === 'abort')
811 return;
812
813 console.warn("Problem with xhr call");
814 console.warn(errMsg);
815 console.warn(jqxhr.responseText);
816 });
817
818 function check_for_duplicate_ids () {
819 /* warn about duplicate element ids */
820 var ids = {};
821 $("[id]").each(function() {
822 if (this.id && ids[this.id]) {
823 console.warn('Duplicate element id #'+this.id);
824 }
825 ids[this.id] = true;
826 });
827 }
828
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 /* Make sure we don't have a notification overlay a modal */
830 $(".modal").on('show.bs.modal', function(){
831 $(".alert-dismissible").fadeOut();
832 });
833
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834 if (libtoaster.debug) {
835 check_for_duplicate_ids();
836 } else {
837 /* Debug is false so supress warnings by overriding the functions */
838 window.console.warn = function () {};
839 window.console.error = function () {};
840 }
841});