Complete Yocto mirror with license table for TQMa6UL (2038-compliance)
- 264 license table entries with exact download URLs (224/264 resolved) - Complete sources/ directory with all BitBake recipes - Build configuration: tqma6ul-multi-mba6ulx, spaetzle (musl) - Full traceability for Softwarefreigabeantrag - GCC 13.4.0, Linux 6.6.102, U-Boot 2023.04, musl 1.2.4 - License distribution: GPL-2.0 (24), MIT (23), GPL-2.0+ (18), BSD-3 (16)
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"curly" : false,
|
||||
"predef" : [ "$","libtoaster", "prettyPrint" ],
|
||||
"eqnull": true,
|
||||
"plusplus" : false,
|
||||
"browser" : true,
|
||||
"jquery" : true,
|
||||
"devel" : true,
|
||||
"unused" : true,
|
||||
"maxerr" : 60
|
||||
}
|
||||
2580
sources/poky/bitbake/lib/toaster/toastergui/static/js/bootstrap-3.4.1.js
vendored
Normal file
2580
sources/poky/bitbake/lib/toaster/toastergui/static/js/bootstrap-3.4.1.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
sources/poky/bitbake/lib/toaster/toastergui/static/js/bootstrap-3.4.1.min.js
vendored
Normal file
6
sources/poky/bitbake/lib/toaster/toastergui/static/js/bootstrap-3.4.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,322 @@
|
||||
"use strict";
|
||||
|
||||
function customRecipePageInit(ctx) {
|
||||
|
||||
var urlParams = libtoaster.parseUrlParams();
|
||||
var customiseTable = $("#selectpackagestable");
|
||||
var addPkgDepsModalBtn = $("#add-package-deps-modal-btn");
|
||||
var rmdPkgReverseDepsModalBtn = $("#rm-package-reverse-deps-modal-btn");
|
||||
|
||||
if (urlParams.hasOwnProperty('notify') && urlParams.notify === 'new'){
|
||||
$("#image-created-notification").show();
|
||||
}
|
||||
|
||||
customiseTable.on('table-done', function(e, total){
|
||||
/* Table is done so now setup the click handler for the package buttons */
|
||||
$(".add-rm-package-btn").click(function(e){
|
||||
e.preventDefault();
|
||||
var targetPkg = $(this).data();
|
||||
|
||||
checkPackageDeps(targetPkg, function(pkgData){
|
||||
if (targetPkg.directive === 'add'){
|
||||
/* If we're adding a package we may need to show the modal to advise
|
||||
* on dependencies for this package.
|
||||
*/
|
||||
if (pkgData.unsatisfied_dependencies.length === 0){
|
||||
addRemovePackage(targetPkg);
|
||||
} else {
|
||||
showPackageDepsModal(targetPkg, pkgData);
|
||||
}
|
||||
} else if (targetPkg.directive === 'remove') {
|
||||
if (pkgData.reverse_dependencies.length === 0){
|
||||
addRemovePackage(targetPkg);
|
||||
} else {
|
||||
showPackageReverseDepsModal(targetPkg, pkgData);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkPackageDeps(targetPkg, doneCb){
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: targetPkg.packageUrl,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function(data){
|
||||
if (data.error !== 'ok'){
|
||||
console.warn(data.error);
|
||||
return;
|
||||
}
|
||||
doneCb(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showPackageDepsModal(targetPkg, pkgData){
|
||||
var modal = $("#package-deps-modal");
|
||||
var depsList = modal.find("#package-add-dep-list");
|
||||
var deps = pkgData.unsatisfied_dependencies;
|
||||
|
||||
modal.find(".package-to-add-name").text(targetPkg.name);
|
||||
|
||||
depsList.text("");
|
||||
|
||||
for (var i in deps){
|
||||
var li = $('<li></li>').text(deps[i].name);
|
||||
li.append($('<span></span>').text(" ("+
|
||||
deps[i].size_formatted+")"));
|
||||
depsList.append(li);
|
||||
}
|
||||
|
||||
modal.find("#package-deps-total-size").text(
|
||||
pkgData.unsatisfied_dependencies_size_formatted);
|
||||
|
||||
targetPkg.depsAdded = deps;
|
||||
|
||||
addPkgDepsModalBtn.data(targetPkg);
|
||||
modal.modal('show');
|
||||
}
|
||||
|
||||
addPkgDepsModalBtn.click(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
addRemovePackage($(this).data(), null);
|
||||
});
|
||||
|
||||
function showPackageReverseDepsModal(targetPkg, pkgData){
|
||||
var modal = $("#package-reverse-deps-modal");
|
||||
var depsList = modal.find("#package-reverse-dep-list");
|
||||
var deps = pkgData.reverse_dependencies;
|
||||
|
||||
var depsCount = deps.length;
|
||||
var vDepends = "depends";
|
||||
var vPackage = "package";
|
||||
var vThis = "this";
|
||||
if (depsCount > 1) {
|
||||
vDepends = "depend";
|
||||
vPackage = "packages";
|
||||
vThis = "these";
|
||||
}
|
||||
modal.find(".package-to-rm-name").text(targetPkg.name);
|
||||
modal.find(".reverse-deps-count").text(depsCount);
|
||||
modal.find(".reverse-deps-count-plus1").text((depsCount+1) + " packages");
|
||||
modal.find(".reverse-deps-depends").text(vDepends);
|
||||
modal.find(".reverse-deps-package").text(vPackage);
|
||||
modal.find(".reverse-deps-this").text(vThis);
|
||||
|
||||
depsList.text("");
|
||||
|
||||
for (var i in deps){
|
||||
var li = $('<li></li>').text(deps[i].name);
|
||||
li.append($('<span></span>').text(" ("+
|
||||
deps[i].size_formatted+")"));
|
||||
depsList.append(li);
|
||||
}
|
||||
|
||||
modal.find("#package-reverse-deps-total-size").text(
|
||||
pkgData.reverse_dependencies_size_formatted);
|
||||
|
||||
targetPkg.depsRemoved = deps;
|
||||
|
||||
rmdPkgReverseDepsModalBtn.data(targetPkg);
|
||||
modal.modal('show');
|
||||
}
|
||||
|
||||
rmdPkgReverseDepsModalBtn.click(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
addRemovePackage($(this).data(), null);
|
||||
});
|
||||
|
||||
|
||||
function addRemovePackage(targetPkg, tableParams){
|
||||
var method;
|
||||
var msg = "You have ";
|
||||
|
||||
var btnCell = $("#package-btn-cell-" + targetPkg.id);
|
||||
var inlineNotify = btnCell.children(".inline-notification");
|
||||
|
||||
var i;
|
||||
var dep;
|
||||
var depBtnCell;
|
||||
|
||||
if (targetPkg.directive === 'add') {
|
||||
method = 'PUT';
|
||||
/* If the package had dependencies also notify that they were added */
|
||||
if (targetPkg.hasOwnProperty('depsAdded') &&
|
||||
targetPkg.depsAdded.length > 0) {
|
||||
|
||||
msg += "added ";
|
||||
msg += "<strong>" + (targetPkg.depsAdded.length + 1) + "</strong>";
|
||||
msg += " packages to " + ctx.recipe.name + ": ";
|
||||
msg += "<strong>" + targetPkg.name + "</strong> and its dependencies";
|
||||
|
||||
for (i in targetPkg.depsAdded){
|
||||
dep = targetPkg.depsAdded[i];
|
||||
|
||||
msg += " <strong>" + dep.name + "</strong>";
|
||||
|
||||
/* Add any cells currently in view to the list of cells which get
|
||||
* an list-inline notification inside them and which change add/rm state
|
||||
*/
|
||||
depBtnCell = $("#package-btn-cell-" + dep.pk);
|
||||
btnCell = btnCell.add(depBtnCell);
|
||||
|
||||
inlineNotify = inlineNotify.add(
|
||||
depBtnCell.children(".inline-notification"));
|
||||
}
|
||||
|
||||
inlineNotify.text(
|
||||
(targetPkg.depsAdded.length + 1) + " packages added");
|
||||
|
||||
} else {
|
||||
msg += "added <strong>1</strong>";
|
||||
msg += " package to " + ctx.recipe.name + ": ";
|
||||
msg += "<strong>" + targetPkg.name + "</strong>";
|
||||
inlineNotify.text("1 package added");
|
||||
}
|
||||
|
||||
} else if (targetPkg.directive === 'remove') {
|
||||
method = 'DELETE';
|
||||
var numPackageString = "1 package ";
|
||||
var revDepList = "";
|
||||
if (targetPkg.hasOwnProperty('depsRemoved') &&
|
||||
targetPkg.depsRemoved.length > 0) {
|
||||
var depsRemovedLength = targetPkg.depsRemoved.length;
|
||||
var ending = "y: ";
|
||||
var maxRevDepsDisplayed = 5;
|
||||
var d = 0;
|
||||
if (depsRemovedLength > 1) {
|
||||
ending = "ies: ";
|
||||
}
|
||||
numPackageString = (depsRemovedLength + 1) + " packages";
|
||||
revDepList = " and its " + depsRemovedLength + " reverse dependenc" + ending;
|
||||
for (i in targetPkg.depsRemoved){
|
||||
/* include up to maxRevDepsDisplayed rev deps on the page notification */
|
||||
var notShownCount = depsRemovedLength - maxRevDepsDisplayed;
|
||||
dep = targetPkg.depsRemoved[i];
|
||||
if (d < maxRevDepsDisplayed) {
|
||||
if (d > 0) {
|
||||
revDepList += ", ";
|
||||
}
|
||||
revDepList += dep.name;
|
||||
d++;
|
||||
if ((d === maxRevDepsDisplayed) && (notShownCount > 0)) {
|
||||
revDepList += " and " + notShownCount + " more";
|
||||
}
|
||||
}
|
||||
|
||||
/* Add any cells currently in view to the list of cells which get
|
||||
* an list-inline notification inside them and which change add/rm state
|
||||
*/
|
||||
depBtnCell = $("#package-btn-cell-" + dep.pk);
|
||||
btnCell = btnCell.add(depBtnCell);
|
||||
|
||||
inlineNotify = inlineNotify.add(
|
||||
depBtnCell.children(".inline-notification"));
|
||||
}
|
||||
}
|
||||
msg+= "removed " + numPackageString + " from " + ctx.recipe.name + ":";
|
||||
msg += " <strong>" + targetPkg.name + "</strong>";
|
||||
msg += revDepList;
|
||||
|
||||
inlineNotify.text(numPackageString + " removed");
|
||||
} else {
|
||||
throw("Unknown package directive: should be add or remove");
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: method,
|
||||
url: targetPkg.packageUrl,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function(data){
|
||||
if (data.error !== 'ok'){
|
||||
console.warn(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
libtoaster.showChangeNotification(msg);
|
||||
|
||||
/* do the in-cell/inline notification to swap buttoms from add to
|
||||
* remove
|
||||
*/
|
||||
btnCell.children("button").fadeOut().promise().done(function(){
|
||||
inlineNotify.fadeIn().delay(500).fadeOut(function(){
|
||||
if (targetPkg.directive === 'add')
|
||||
btnCell.children("button[data-directive=remove]").fadeIn();
|
||||
else
|
||||
btnCell.children("button[data-directive=add]").fadeIn();
|
||||
});
|
||||
});
|
||||
|
||||
/* Update the total num packages */
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: ctx.recipe.xhrPackageListUrl,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function(data){
|
||||
console.log(data);
|
||||
$("#total-num-packages").text(data.total);
|
||||
$("#total-size-packages").text(data.total_size_formatted);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$("#no-results-show-all-packages").click(function(){
|
||||
$(".no-results-search-input").val("");
|
||||
});
|
||||
|
||||
$("#no-results-remove-search-btn").click(function(){
|
||||
$(".no-results-search-input").val("");
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
/* Trigger a build of your custom image */
|
||||
$(".build-custom-image").click(function(){
|
||||
libtoaster.startABuild(null, ctx.recipe.name,
|
||||
function(){
|
||||
window.location.replace(libtoaster.ctx.projectBuildsUrl);
|
||||
});
|
||||
});
|
||||
|
||||
$("#delete-custom-recipe-confirmed").click(function(e){
|
||||
e.preventDefault();
|
||||
libtoaster.disableAjaxLoadingTimer();
|
||||
$(this).find('[data-role="submit-state"]').hide();
|
||||
$(this).find('[data-role="loading-state"]').show();
|
||||
$(this).attr("disabled", "disabled");
|
||||
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: ctx.recipe.xhrCustomRecipeUrl,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
if (data.error !== "ok") {
|
||||
console.warn(data.error);
|
||||
} else {
|
||||
var msg = $('<span>You have deleted <strong>1</strong> custom image: <strong id="deleted-custom-image-name"></strong></span>');
|
||||
msg.find("#deleted-custom-image-name").text(ctx.recipe.name);
|
||||
|
||||
libtoaster.setNotification("custom-image-recipe-deleted",
|
||||
msg.html());
|
||||
|
||||
window.location.replace(data.gotoUrl);
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.warn(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* Stop the download link from working if it is in disabled state
|
||||
* http://getbootstrap.com/css/#forms-disabled-fieldsets
|
||||
*/
|
||||
$("a[disabled=disabled]").click(function(e){
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
}
|
||||
94
sources/poky/bitbake/lib/toaster/toastergui/static/js/filtersnippet.js
Executable file
94
sources/poky/bitbake/lib/toaster/toastergui/static/js/filtersnippet.js
Executable file
@@ -0,0 +1,94 @@
|
||||
"use strict"
|
||||
|
||||
// The disable removes the 'datepicker' attribute and
|
||||
// settings, so you have to re-initialize it each time
|
||||
// the date range is selected and enabled
|
||||
// DOM is used instead of jQuery to find the elements
|
||||
// in all contexts
|
||||
function date_enable (key, action) {
|
||||
|
||||
var elemFrom=document.getElementById("date_from_"+key);
|
||||
var elemTo=document.getElementById("date_to_"+key);
|
||||
|
||||
if ('enable' == action) {
|
||||
elemFrom.removeAttribute("disabled");
|
||||
elemTo.removeAttribute("disabled");
|
||||
|
||||
$(elemFrom).datepicker();
|
||||
$(elemTo).datepicker();
|
||||
|
||||
$(elemFrom).datepicker( "option", "dateFormat", "dd/mm/yy" );
|
||||
$(elemTo).datepicker( "option", "dateFormat", "dd/mm/yy" );
|
||||
|
||||
$(elemFrom).datepicker( "setDate", elemFrom.getAttribute( "data-setDate") );
|
||||
$(elemTo).datepicker( "setDate", elemTo.getAttribute( "data-setDate") );
|
||||
$(elemFrom).datepicker( "option", "minDate", elemFrom.getAttribute( "data-minDate"));
|
||||
$(elemTo).datepicker( "option", "minDate", elemTo.getAttribute( "data-minDate"));
|
||||
$(elemFrom).datepicker( "option", "maxDate", elemFrom.getAttribute( "data-maxDate"));
|
||||
$(elemTo).datepicker( "option", "maxDate", elemTo.getAttribute( "data-maxDate"));
|
||||
} else {
|
||||
elemFrom.setAttribute("disabled","disabled");
|
||||
elemTo.setAttribute("disabled","disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the date picker elements with their default state variables, and
|
||||
// register the radio button and form actions
|
||||
function date_init (key, from_date, to_date, min_date, max_date, initial_enable) {
|
||||
|
||||
var elemFrom=document.getElementById("date_from_"+key);
|
||||
var elemTo=document.getElementById("date_to_"+key);
|
||||
|
||||
// Were there any daterange filters instantiated? (e.g. no builds found)
|
||||
if (null == elemFrom) {
|
||||
return;
|
||||
}
|
||||
|
||||
// init the datepicker context data
|
||||
elemFrom.setAttribute( "data-setDate", from_date );
|
||||
elemTo.setAttribute( "data-setDate", to_date );
|
||||
elemFrom.setAttribute( "data-minDate", min_date);
|
||||
elemTo.setAttribute( "data-minDate", min_date);
|
||||
elemFrom.setAttribute( "data-maxDate", max_date);
|
||||
elemTo.setAttribute( "data-maxDate", max_date);
|
||||
|
||||
// does the date set start enabled?
|
||||
if (key == initial_enable) {
|
||||
date_enable (key, "enable");
|
||||
} else {
|
||||
date_enable (key, "disable");
|
||||
}
|
||||
|
||||
// catch the radio button selects for enable/disable
|
||||
$('input:radio[name="filter"]').change(function(){
|
||||
if ($(this).val() == 'daterange') {
|
||||
key=$(this).attr("data-key");
|
||||
date_enable (key, 'enable');
|
||||
} else {
|
||||
key=$(this).attr("data-key");
|
||||
date_enable (key, 'disable');
|
||||
}
|
||||
});
|
||||
|
||||
// catch any new 'from' date as minDate for 'to' date
|
||||
$("#date_from_"+key).change(function(){
|
||||
from_date = $("#date_from_"+key).val();
|
||||
$("#date_to_"+key).datepicker( "option", "minDate", from_date );
|
||||
});
|
||||
|
||||
// catch the submit (just once)
|
||||
$("form").unbind('submit');
|
||||
$("form").submit(function(e) {
|
||||
// format a composite daterange filter value so that it can be parsed and post-processed in the view
|
||||
if (key !== undefined) {
|
||||
if ($("#date_from_"+key).length) {
|
||||
var filter=key+"__gte!"+key+"__lt:"+$("#date_from_"+key).val()+"!"+$("#date_to_"+key).val()+"_daterange";
|
||||
$("#last_date_from_"+key).val($("#date_from_"+key).val());
|
||||
$("#last_date_to_"+key).val($("#date_to_"+key).val());
|
||||
$("#filter_value_"+key).val(filter);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,455 @@
|
||||
"use strict"
|
||||
|
||||
function importLayerPageInit (ctx) {
|
||||
|
||||
var layerDepBtn = $("#add-layer-dependency-btn");
|
||||
var importAndAddBtn = $("#import-and-add-btn");
|
||||
var layerNameInput = $("#import-layer-name");
|
||||
var vcsURLInput = $("#layer-git-repo-url");
|
||||
var gitRefInput = $("#layer-git-ref");
|
||||
var layerDepInput = $("#layer-dependency");
|
||||
var layerNameCtrl = $("#layer-name-ctrl");
|
||||
var duplicatedLayerName = $("#duplicated-layer-name-hint");
|
||||
var localDirPath = $("#local-dir-path");
|
||||
|
||||
var layerDeps = {};
|
||||
var layerDepsDeps = {};
|
||||
var currentLayerDepSelection;
|
||||
var validLayerName = /^(\w|-)+$/;
|
||||
|
||||
/* Catch 'disable' race condition between type-ahead started and "input change" */
|
||||
var typeAheadStarted = 0;
|
||||
|
||||
libtoaster.makeTypeahead(layerDepInput,
|
||||
libtoaster.ctx.layersTypeAheadUrl,
|
||||
{ include_added: "true" }, function(item){
|
||||
currentLayerDepSelection = item;
|
||||
layerDepBtn.removeAttr("disabled");
|
||||
typeAheadStarted = 1;
|
||||
});
|
||||
|
||||
layerDepInput.on("typeahead:select", function(event, data){
|
||||
currentLayerDepSelection = data;
|
||||
});
|
||||
|
||||
// Disable local dir repo when page is loaded.
|
||||
$('#local-dir').hide();
|
||||
|
||||
// disable the "Add layer" button when the layer input typeahead is empty
|
||||
// or not in the typeahead choices
|
||||
layerDepInput.on("input change", function(){
|
||||
if (0 == typeAheadStarted) {
|
||||
layerDepBtn.attr("disabled","disabled");
|
||||
}
|
||||
typeAheadStarted = 0;
|
||||
});
|
||||
|
||||
/* We automatically add "openembedded-core" layer for convenience as a
|
||||
* dependency as pretty much all layers depend on this one
|
||||
*/
|
||||
$.getJSON(libtoaster.ctx.layersTypeAheadUrl,
|
||||
{ include_added: "true" , search: "openembedded-core" },
|
||||
function(layer) {
|
||||
if (layer.results.length > 0) {
|
||||
currentLayerDepSelection = layer.results[0];
|
||||
layerDepBtn.click();
|
||||
}
|
||||
});
|
||||
|
||||
layerDepBtn.click(function(){
|
||||
typeAheadStarted = 0;
|
||||
if (currentLayerDepSelection == undefined)
|
||||
return;
|
||||
|
||||
layerDeps[currentLayerDepSelection.id] = currentLayerDepSelection;
|
||||
|
||||
/* Make a list item for the new layer dependency */
|
||||
var newLayerDep = $("<li><a></a><span class=\"glyphicon glyphicon-trash\" data-toggle=\"tooltip\" title=\"Remove\"></span></li>");
|
||||
|
||||
newLayerDep.data('layer-id', currentLayerDepSelection.id);
|
||||
newLayerDep.children("span").tooltip();
|
||||
|
||||
var link = newLayerDep.children("a");
|
||||
link.attr("href", currentLayerDepSelection.layerdetailurl);
|
||||
link.text(currentLayerDepSelection.name);
|
||||
link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"});
|
||||
|
||||
var trashItem = newLayerDep.children("span");
|
||||
trashItem.click(function () {
|
||||
var toRemove = $(this).parent().data('layer-id');
|
||||
delete layerDeps[toRemove];
|
||||
$(this).parent().fadeOut(function (){
|
||||
$(this).remove();
|
||||
});
|
||||
});
|
||||
|
||||
$("#layer-deps-list").append(newLayerDep);
|
||||
|
||||
libtoaster.getLayerDepsForProject(currentLayerDepSelection.xhrLayerUrl,
|
||||
function (data){
|
||||
/* These are the dependencies of the layer added as a dependency */
|
||||
if (data.list.length > 0) {
|
||||
currentLayerDepSelection.url = currentLayerDepSelection.layerdetailurl;
|
||||
layerDeps[currentLayerDepSelection.id].deps = data.list;
|
||||
}
|
||||
|
||||
/* Clear the current selection */
|
||||
layerDepInput.val("");
|
||||
currentLayerDepSelection = undefined;
|
||||
layerDepBtn.attr("disabled","disabled");
|
||||
}, null);
|
||||
});
|
||||
|
||||
importAndAddBtn.click(function(e){
|
||||
e.preventDefault();
|
||||
/* This is a list of the names from layerDeps for the layer deps
|
||||
* modal dialog body
|
||||
*/
|
||||
var depNames = [];
|
||||
|
||||
/* arrray of all layer dep ids includes parent and child deps */
|
||||
var allDeps = [];
|
||||
|
||||
/* temporary object to use to do a reduce on the dependencies for each
|
||||
* layer dependency added
|
||||
*/
|
||||
var depDeps = {};
|
||||
|
||||
/* the layers that have dependencies have an extra property "deps"
|
||||
* look in this for each layer and reduce this to a unquie object
|
||||
* of deps.
|
||||
*/
|
||||
for (var key in layerDeps){
|
||||
if (layerDeps[key].hasOwnProperty('deps')){
|
||||
for (var dep in layerDeps[key].deps){
|
||||
var layer = layerDeps[key].deps[dep];
|
||||
depDeps[layer.id] = layer;
|
||||
}
|
||||
}
|
||||
depNames.push(layerDeps[key].name);
|
||||
allDeps.push(layerDeps[key].id);
|
||||
}
|
||||
|
||||
/* we actually want it as an array so convert it now */
|
||||
var depDepsArray = [];
|
||||
for (var key in depDeps)
|
||||
depDepsArray.push (depDeps[key]);
|
||||
|
||||
if (depDepsArray.length > 0) {
|
||||
var layer = { name: layerNameInput.val(), url: "#", id: -1 };
|
||||
var title = "Layer";
|
||||
var body = "<strong>"+layer.name+"</strong>'s dependencies ("+
|
||||
depNames.join(", ")+"</span>) require some layers that are not added to your project. Select the ones you want to add:</p>";
|
||||
|
||||
showLayerDepsModal(layer,
|
||||
depDepsArray,
|
||||
title, body, false, function(layerObsList){
|
||||
/* Add the accepted layer dependencies' ids to the allDeps array */
|
||||
for (var key in layerObsList){
|
||||
allDeps.push(layerObsList[key].id);
|
||||
}
|
||||
import_and_add ();
|
||||
});
|
||||
} else {
|
||||
import_and_add ();
|
||||
}
|
||||
|
||||
function import_and_add () {
|
||||
/* convert to a csv of all the deps to be added */
|
||||
var layerDepsCsv = allDeps.join(",");
|
||||
|
||||
var layerData = {
|
||||
name: layerNameInput.val(),
|
||||
vcs_url: vcsURLInput.val(),
|
||||
git_ref: gitRefInput.val(),
|
||||
dir_path: $("#layer-subdir").val(),
|
||||
project_id: libtoaster.ctx.projectId,
|
||||
layer_deps: layerDepsCsv,
|
||||
local_source_dir: $('#local-dir-path').val(),
|
||||
add_to_project: true,
|
||||
};
|
||||
|
||||
if ($('input[name=repo]:checked').val() == "git") {
|
||||
layerData.local_source_dir = "";
|
||||
} else {
|
||||
layerData.vcs_url = "";
|
||||
layerData.git_ref = "";
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: ctx.xhrLayerUrl,
|
||||
data: JSON.stringify(layerData),
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
if (data.error != "ok") {
|
||||
console.log(data.error);
|
||||
/* let the user know why nothing happened */
|
||||
alert(data.error)
|
||||
} else {
|
||||
createImportedNotification(data);
|
||||
window.location.replace(libtoaster.ctx.projectPageUrl);
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.log("Call failed");
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* Layer imported notification */
|
||||
function createImportedNotification(imported){
|
||||
var message = "Layer imported";
|
||||
|
||||
if (imported.deps_added.length === 0) {
|
||||
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.";
|
||||
} else {
|
||||
|
||||
var links = "<a href=\""+imported.imported_layer.layerdetailurl+"\">"+imported.imported_layer.name+"</a>, ";
|
||||
|
||||
imported.deps_added.map (function(item, index){
|
||||
links +='<a href="'+item.layerdetailurl+'">'+item.name+'</a>';
|
||||
/*If we're at the last element we don't want the trailing comma */
|
||||
if (imported.deps_added[index+1] !== undefined)
|
||||
links += ', ';
|
||||
});
|
||||
|
||||
/* Length + 1 here to do deps + the imported layer */
|
||||
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>';
|
||||
}
|
||||
|
||||
libtoaster.setNotification("layer-imported", message);
|
||||
}
|
||||
|
||||
function enable_import_btn(enabled) {
|
||||
var importAndAddHint = $("#import-and-add-hint");
|
||||
|
||||
if (enabled) {
|
||||
importAndAddBtn.removeAttr("disabled");
|
||||
importAndAddHint.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
importAndAddBtn.attr("disabled", "disabled");
|
||||
importAndAddHint.show();
|
||||
}
|
||||
|
||||
function check_form() {
|
||||
var valid = false;
|
||||
var inputs = $("input:required");
|
||||
var inputStr = inputs.val().split("");
|
||||
|
||||
for (var i=0; i<inputs.val().length; i++){
|
||||
if (!(valid = inputStr[i])){
|
||||
enable_import_btn(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
if ($("#local-dir-radio").prop("checked") &&
|
||||
localDirPath.val().length > 0) {
|
||||
enable_import_btn(true);
|
||||
}
|
||||
|
||||
if ($("#git-repo-radio").prop("checked")) {
|
||||
if (gitRefInput.val().length > 0 &&
|
||||
gitRefInput.val() == 'HEAD') {
|
||||
$('#invalid-layer-revision-hint').show();
|
||||
$('#layer-revision-ctrl').addClass('has-error');
|
||||
enable_import_btn(false);
|
||||
} else if (vcsURLInput.val().length > 0 &&
|
||||
gitRefInput.val().length > 0) {
|
||||
$('#invalid-layer-revision-hint').hide();
|
||||
$('#layer-revision-ctrl').removeClass('has-error');
|
||||
enable_import_btn(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.val().length == 0)
|
||||
enable_import_btn(false);
|
||||
}
|
||||
|
||||
function layerExistsError(layer){
|
||||
var dupLayerInfo = $("#duplicate-layer-info");
|
||||
|
||||
if (layer.local_source_dir) {
|
||||
$("#git-layer-dup").hide();
|
||||
$("#local-layer-dup").fadeIn();
|
||||
dupLayerInfo.find(".dup-layer-name").text(layer.name);
|
||||
dupLayerInfo.find(".dup-layer-link").attr("href", layer.layerdetailurl);
|
||||
dupLayerInfo.find("#dup-local-source-dir-name").text(layer.local_source_dir);
|
||||
} else {
|
||||
$("#git-layer-dup").fadeIn();
|
||||
$("#local-layer-dup").hide();
|
||||
dupLayerInfo.find(".dup-layer-name").text(layer.name);
|
||||
dupLayerInfo.find(".dup-layer-link").attr("href", layer.layerdetailurl);
|
||||
dupLayerInfo.find("#dup-layer-vcs-url").text(layer.vcs_url);
|
||||
dupLayerInfo.find("#dup-layer-revision").text(layer.vcs_reference);
|
||||
}
|
||||
$(".fields-apart-from-layer-name").fadeOut(function(){
|
||||
|
||||
dupLayerInfo.fadeIn();
|
||||
});
|
||||
}
|
||||
|
||||
layerNameInput.on('blur', function() {
|
||||
if (!$(this).val()){
|
||||
return;
|
||||
}
|
||||
var name = $(this).val();
|
||||
|
||||
/* Check if the layer name exists */
|
||||
$.getJSON(libtoaster.ctx.layersTypeAheadUrl,
|
||||
{ include_added: "true" , search: name, format: "json" },
|
||||
function(layer) {
|
||||
if (layer.results.length > 0) {
|
||||
for (var i in layer.results){
|
||||
if (layer.results[i].name == name) {
|
||||
layerExistsError(layer.results[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
vcsURLInput.on('input', function() {
|
||||
check_form();
|
||||
});
|
||||
|
||||
gitRefInput.on('input', function() {
|
||||
check_form();
|
||||
});
|
||||
|
||||
layerNameInput.on('input', function() {
|
||||
if ($(this).val() && !validLayerName.test($(this).val())){
|
||||
layerNameCtrl.addClass("has-error")
|
||||
$("#invalid-layer-name-hint").show();
|
||||
enable_import_btn(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($("#duplicate-layer-info").css("display") != "None"){
|
||||
$("#duplicate-layer-info").fadeOut(function(){
|
||||
$(".fields-apart-from-layer-name").show();
|
||||
radioDisplay();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
radioDisplay();
|
||||
|
||||
/* Don't remove the error class if we're displaying the error for another
|
||||
* reason.
|
||||
*/
|
||||
if (!duplicatedLayerName.is(":visible"))
|
||||
layerNameCtrl.removeClass("has-error")
|
||||
|
||||
$("#invalid-layer-name-hint").hide();
|
||||
check_form();
|
||||
});
|
||||
|
||||
/* Setup 'blank' typeahead */
|
||||
libtoaster.makeTypeahead(gitRefInput,
|
||||
ctx.xhrGitRevTypeAheadUrl,
|
||||
{ git_url: null }, function(){});
|
||||
|
||||
|
||||
vcsURLInput.focusout(function (){
|
||||
if (!$(this).val())
|
||||
return;
|
||||
|
||||
/* If we a layer name specified don't overwrite it or if there isn't a
|
||||
* url typed in yet return
|
||||
*/
|
||||
if (!layerNameInput.val() && $(this).val().search("/")){
|
||||
var urlPts = $(this).val().split("/");
|
||||
/* Add a suggestion of the layer name */
|
||||
var suggestion = urlPts[urlPts.length-1].replace(".git","");
|
||||
layerNameInput.val(suggestion);
|
||||
}
|
||||
|
||||
/* Now actually setup the typeahead properly with the git url entered */
|
||||
gitRefInput._typeahead('destroy');
|
||||
|
||||
libtoaster.makeTypeahead(gitRefInput,
|
||||
ctx.xhrGitRevTypeAheadUrl,
|
||||
{ git_url: $(this).val() },
|
||||
function(selected){
|
||||
gitRefInput._typeahead("close");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function radioDisplay() {
|
||||
if ($('input[name=repo]:checked').val() == "local") {
|
||||
$('#git-repo').hide();
|
||||
$('#import-git-layer-and-add-hint').hide();
|
||||
$('#local-dir').fadeIn();
|
||||
$('#import-local-dir-and-add-hint').fadeIn();
|
||||
} else {
|
||||
$('#local-dir').hide();
|
||||
$('#import-local-dir-and-add-hint').hide();
|
||||
$('#git-repo').fadeIn();
|
||||
$('#import-git-layer-and-add-hint').fadeIn();
|
||||
}
|
||||
}
|
||||
|
||||
$('input:radio[name="repo"]').change(function() {
|
||||
radioDisplay();
|
||||
if ($("#local-dir-radio").prop("checked")) {
|
||||
if (localDirPath.val().length > 0) {
|
||||
enable_import_btn(true);
|
||||
} else {
|
||||
enable_import_btn(false);
|
||||
}
|
||||
}
|
||||
if ($("#git-repo-radio").prop("checked")) {
|
||||
if (vcsURLInput.val().length > 0 && gitRefInput.val().length > 0) {
|
||||
enable_import_btn(true);
|
||||
} else {
|
||||
enable_import_btn(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
localDirPath.on('input', function(){
|
||||
if ($(this).val().trim().length == 0) {
|
||||
$('#import-and-add-btn').attr("disabled","disabled");
|
||||
$('#local-dir').addClass('has-error');
|
||||
$('#hintError-dir-abs-path').show();
|
||||
$('#hintError-dir-path-starts-with-slash').show();
|
||||
} else {
|
||||
var input = $(this);
|
||||
var reBeginWithSlash = /^\//;
|
||||
var reCheckVariable = /^\$/;
|
||||
var re = /([ <>\\|":%\?\*]+)/;
|
||||
|
||||
var invalidDir = re.test(input.val());
|
||||
var invalidSlash = reBeginWithSlash.test(input.val());
|
||||
var invalidVar = reCheckVariable.test(input.val());
|
||||
|
||||
if (!invalidSlash && !invalidVar) {
|
||||
$('#local-dir').addClass('has-error');
|
||||
$('#import-and-add-btn').attr("disabled","disabled");
|
||||
$('#hintError-dir-abs-path').show();
|
||||
$('#hintError-dir-path-starts-with-slash').show();
|
||||
} else if (invalidDir) {
|
||||
$('#local-dir').addClass('has-error');
|
||||
$('#import-and-add-btn').attr("disabled","disabled");
|
||||
$('#hintError-dir-path').show();
|
||||
} else {
|
||||
$('#local-dir').removeClass('has-error');
|
||||
if (layerNameInput.val().length > 0) {
|
||||
$('#import-and-add-btn').removeAttr("disabled");
|
||||
}
|
||||
$('#hintError-dir-abs-path').hide();
|
||||
$('#hintError-dir-path-starts-with-slash').hide();
|
||||
$('#hintError-dir-path').hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
6
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery-2.0.3.min.js
vendored
Normal file
6
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery-2.0.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery-3.7.1.min.js
vendored
Normal file
2
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery-3.7.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery-ui.min.js
vendored
Executable file
7
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery-ui.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
* jQuery Cookie Plugin v1.4.0
|
||||
* https://github.com/carhartl/jquery-cookie
|
||||
*
|
||||
* Copyright 2013 Klaus Hartl
|
||||
* Released under the MIT license
|
||||
*/
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
|
||||
var pluses = /\+/g;
|
||||
|
||||
function encode(s) {
|
||||
return config.raw ? s : encodeURIComponent(s);
|
||||
}
|
||||
|
||||
function decode(s) {
|
||||
return config.raw ? s : decodeURIComponent(s);
|
||||
}
|
||||
|
||||
function stringifyCookieValue(value) {
|
||||
return encode(config.json ? JSON.stringify(value) : String(value));
|
||||
}
|
||||
|
||||
function parseCookieValue(s) {
|
||||
if (s.indexOf('"') === 0) {
|
||||
// This is a quoted cookie as according to RFC2068, unescape...
|
||||
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
||||
}
|
||||
|
||||
try {
|
||||
// Replace server-side written pluses with spaces.
|
||||
// If we can't decode the cookie, ignore it, it's unusable.
|
||||
s = decodeURIComponent(s.replace(pluses, ' '));
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// If we can't parse the cookie, ignore it, it's unusable.
|
||||
return config.json ? JSON.parse(s) : s;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function read(s, converter) {
|
||||
var value = config.raw ? s : parseCookieValue(s);
|
||||
return $.isFunction(converter) ? converter(value) : value;
|
||||
}
|
||||
|
||||
var config = $.cookie = function (key, value, options) {
|
||||
|
||||
// Write
|
||||
if (value !== undefined && !$.isFunction(value)) {
|
||||
options = $.extend({}, config.defaults, options);
|
||||
|
||||
if (typeof options.expires === 'number') {
|
||||
var days = options.expires, t = options.expires = new Date();
|
||||
t.setDate(t.getDate() + days);
|
||||
}
|
||||
|
||||
return (document.cookie = [
|
||||
encode(key), '=', stringifyCookieValue(value),
|
||||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
||||
options.path ? '; path=' + options.path : '',
|
||||
options.domain ? '; domain=' + options.domain : '',
|
||||
options.secure ? '; secure' : ''
|
||||
].join(''));
|
||||
}
|
||||
|
||||
// Read
|
||||
|
||||
var result = key ? undefined : {};
|
||||
|
||||
// To prevent the for loop in the first place assign an empty array
|
||||
// in case there are no cookies at all. Also prevents odd result when
|
||||
// calling $.cookie().
|
||||
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
||||
|
||||
for (var i = 0, l = cookies.length; i < l; i++) {
|
||||
var parts = cookies[i].split('=');
|
||||
var name = decode(parts.shift());
|
||||
var cookie = parts.join('=');
|
||||
|
||||
if (key && key === name) {
|
||||
// If second argument (value) is a function it's a converter...
|
||||
result = read(cookie, value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Prevent storing a cookie that we couldn't decode.
|
||||
if (!key && (cookie = read(cookie)) !== undefined) {
|
||||
result[name] = cookie;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
config.defaults = {};
|
||||
|
||||
$.removeCookie = function (key, options) {
|
||||
if ($.cookie(key) !== undefined) {
|
||||
// Must not alter options, thus extending a fresh object...
|
||||
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
}));
|
||||
4
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery.dataTables-1.13.8.min.js
vendored
Normal file
4
sources/poky/bitbake/lib/toaster/toastergui/static/js/jquery.dataTables-1.13.8.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,620 @@
|
||||
/*
|
||||
* jQuery treetable Plugin 3.1.0
|
||||
* http://ludo.cubicphuse.nl/jquery-treetable
|
||||
*
|
||||
* Copyright 2013, Ludo van den Boom
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*/
|
||||
(function() {
|
||||
var $, Node, Tree, methods;
|
||||
|
||||
$ = jQuery;
|
||||
|
||||
Node = (function() {
|
||||
function Node(row, tree, settings) {
|
||||
var parentId;
|
||||
|
||||
this.row = row;
|
||||
this.tree = tree;
|
||||
this.settings = settings;
|
||||
|
||||
// TODO Ensure id/parentId is always a string (not int)
|
||||
this.id = this.row.data(this.settings.nodeIdAttr);
|
||||
|
||||
// TODO Move this to a setParentId function?
|
||||
parentId = this.row.data(this.settings.parentIdAttr);
|
||||
if (parentId != null && parentId !== "") {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
this.treeCell = $(this.row.children(this.settings.columnElType)[this.settings.column]);
|
||||
this.expander = $(this.settings.expanderTemplate);
|
||||
this.indenter = $(this.settings.indenterTemplate);
|
||||
this.children = [];
|
||||
this.initialized = false;
|
||||
this.treeCell.prepend(this.indenter);
|
||||
}
|
||||
|
||||
Node.prototype.addChild = function(child) {
|
||||
return this.children.push(child);
|
||||
};
|
||||
|
||||
Node.prototype.ancestors = function() {
|
||||
var ancestors, node;
|
||||
node = this;
|
||||
ancestors = [];
|
||||
while (node = node.parentNode()) {
|
||||
ancestors.push(node);
|
||||
}
|
||||
return ancestors;
|
||||
};
|
||||
|
||||
Node.prototype.collapse = function() {
|
||||
if (this.collapsed()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.row.removeClass("expanded").addClass("collapsed");
|
||||
|
||||
this._hideChildren();
|
||||
this.expander.attr("title", this.settings.stringExpand);
|
||||
|
||||
if (this.initialized && this.settings.onNodeCollapse != null) {
|
||||
this.settings.onNodeCollapse.apply(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype.collapsed = function() {
|
||||
return this.row.hasClass("collapsed");
|
||||
};
|
||||
|
||||
// TODO destroy: remove event handlers, expander, indenter, etc.
|
||||
|
||||
Node.prototype.expand = function() {
|
||||
if (this.expanded()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.row.removeClass("collapsed").addClass("expanded");
|
||||
|
||||
if (this.initialized && this.settings.onNodeExpand != null) {
|
||||
this.settings.onNodeExpand.apply(this);
|
||||
}
|
||||
|
||||
if ($(this.row).is(":visible")) {
|
||||
this._showChildren();
|
||||
}
|
||||
|
||||
this.expander.attr("title", this.settings.stringCollapse);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype.expanded = function() {
|
||||
return this.row.hasClass("expanded");
|
||||
};
|
||||
|
||||
Node.prototype.hide = function() {
|
||||
this._hideChildren();
|
||||
this.row.hide();
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype.isBranchNode = function() {
|
||||
if(this.children.length > 0 || this.row.data(this.settings.branchAttr) === true) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Node.prototype.updateBranchLeafClass = function(){
|
||||
this.row.removeClass('branch');
|
||||
this.row.removeClass('leaf');
|
||||
this.row.addClass(this.isBranchNode() ? 'branch' : 'leaf');
|
||||
};
|
||||
|
||||
Node.prototype.level = function() {
|
||||
return this.ancestors().length;
|
||||
};
|
||||
|
||||
Node.prototype.parentNode = function() {
|
||||
if (this.parentId != null) {
|
||||
return this.tree[this.parentId];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Node.prototype.removeChild = function(child) {
|
||||
var i = $.inArray(child, this.children);
|
||||
return this.children.splice(i, 1)
|
||||
};
|
||||
|
||||
Node.prototype.render = function() {
|
||||
var handler,
|
||||
settings = this.settings,
|
||||
target;
|
||||
|
||||
if (settings.expandable === true && this.isBranchNode()) {
|
||||
handler = function(e) {
|
||||
$(this).parents("table").treetable("node", $(this).parents("tr").data(settings.nodeIdAttr)).toggle();
|
||||
return e.preventDefault();
|
||||
};
|
||||
|
||||
this.indenter.html(this.expander);
|
||||
target = settings.clickableNodeNames === true ? this.treeCell : this.expander;
|
||||
|
||||
target.off("click.treetable").on("click.treetable", handler);
|
||||
target.off("keydown.treetable").on("keydown.treetable", function(e) {
|
||||
if (e.keyCode == 13) {
|
||||
handler.apply(this, [e]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.indenter[0].style.paddingLeft = "" + (this.level() * settings.indent) + "px";
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype.reveal = function() {
|
||||
if (this.parentId != null) {
|
||||
this.parentNode().reveal();
|
||||
}
|
||||
return this.expand();
|
||||
};
|
||||
|
||||
Node.prototype.setParent = function(node) {
|
||||
if (this.parentId != null) {
|
||||
this.tree[this.parentId].removeChild(this);
|
||||
}
|
||||
this.parentId = node.id;
|
||||
this.row.data(this.settings.parentIdAttr, node.id);
|
||||
return node.addChild(this);
|
||||
};
|
||||
|
||||
Node.prototype.show = function() {
|
||||
if (!this.initialized) {
|
||||
this._initialize();
|
||||
}
|
||||
this.row.show();
|
||||
if (this.expanded()) {
|
||||
this._showChildren();
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype.toggle = function() {
|
||||
if (this.expanded()) {
|
||||
this.collapse();
|
||||
} else {
|
||||
this.expand();
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Node.prototype._hideChildren = function() {
|
||||
var child, _i, _len, _ref, _results;
|
||||
_ref = this.children;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
child = _ref[_i];
|
||||
_results.push(child.hide());
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Node.prototype._initialize = function() {
|
||||
var settings = this.settings;
|
||||
|
||||
this.render();
|
||||
|
||||
if (settings.expandable === true && settings.initialState === "collapsed") {
|
||||
this.collapse();
|
||||
} else {
|
||||
this.expand();
|
||||
}
|
||||
|
||||
if (settings.onNodeInitialized != null) {
|
||||
settings.onNodeInitialized.apply(this);
|
||||
}
|
||||
|
||||
return this.initialized = true;
|
||||
};
|
||||
|
||||
Node.prototype._showChildren = function() {
|
||||
var child, _i, _len, _ref, _results;
|
||||
_ref = this.children;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
child = _ref[_i];
|
||||
_results.push(child.show());
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
return Node;
|
||||
})();
|
||||
|
||||
Tree = (function() {
|
||||
function Tree(table, settings) {
|
||||
this.table = table;
|
||||
this.settings = settings;
|
||||
this.tree = {};
|
||||
|
||||
// Cache the nodes and roots in simple arrays for quick access/iteration
|
||||
this.nodes = [];
|
||||
this.roots = [];
|
||||
}
|
||||
|
||||
Tree.prototype.collapseAll = function() {
|
||||
var node, _i, _len, _ref, _results;
|
||||
_ref = this.nodes;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
node = _ref[_i];
|
||||
_results.push(node.collapse());
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Tree.prototype.expandAll = function() {
|
||||
var node, _i, _len, _ref, _results;
|
||||
_ref = this.nodes;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
node = _ref[_i];
|
||||
_results.push(node.expand());
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Tree.prototype.findLastNode = function (node) {
|
||||
if (node.children.length > 0) {
|
||||
return this.findLastNode(node.children[node.children.length - 1]);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
Tree.prototype.loadRows = function(rows) {
|
||||
var node, row, i;
|
||||
|
||||
if (rows != null) {
|
||||
for (i = 0; i < rows.length; i++) {
|
||||
row = $(rows[i]);
|
||||
|
||||
if (row.data(this.settings.nodeIdAttr) != null) {
|
||||
node = new Node(row, this.tree, this.settings);
|
||||
this.nodes.push(node);
|
||||
this.tree[node.id] = node;
|
||||
|
||||
if (node.parentId != null) {
|
||||
this.tree[node.parentId].addChild(node);
|
||||
} else {
|
||||
this.roots.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < this.nodes.length; i++) {
|
||||
node = this.nodes[i].updateBranchLeafClass();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Tree.prototype.move = function(node, destination) {
|
||||
// Conditions:
|
||||
// 1: +node+ should not be inserted as a child of +node+ itself.
|
||||
// 2: +destination+ should not be the same as +node+'s current parent (this
|
||||
// prevents +node+ from being moved to the same location where it already
|
||||
// is).
|
||||
// 3: +node+ should not be inserted in a location in a branch if this would
|
||||
// result in +node+ being an ancestor of itself.
|
||||
var nodeParent = node.parentNode();
|
||||
if (node !== destination && destination.id !== node.parentId && $.inArray(node, destination.ancestors()) === -1) {
|
||||
node.setParent(destination);
|
||||
this._moveRows(node, destination);
|
||||
|
||||
// Re-render parentNode if this is its first child node, and therefore
|
||||
// doesn't have the expander yet.
|
||||
if (node.parentNode().children.length === 1) {
|
||||
node.parentNode().render();
|
||||
}
|
||||
}
|
||||
|
||||
if(nodeParent){
|
||||
nodeParent.updateBranchLeafClass();
|
||||
}
|
||||
if(node.parentNode()){
|
||||
node.parentNode().updateBranchLeafClass();
|
||||
}
|
||||
node.updateBranchLeafClass();
|
||||
return this;
|
||||
};
|
||||
|
||||
Tree.prototype.removeNode = function(node) {
|
||||
// Recursively remove all descendants of +node+
|
||||
this.unloadBranch(node);
|
||||
|
||||
// Remove node from DOM (<tr>)
|
||||
node.row.remove();
|
||||
|
||||
// Clean up Tree object (so Node objects are GC-ed)
|
||||
delete this.tree[node.id];
|
||||
this.nodes.splice($.inArray(node, this.nodes), 1);
|
||||
}
|
||||
|
||||
Tree.prototype.render = function() {
|
||||
var root, _i, _len, _ref;
|
||||
_ref = this.roots;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
root = _ref[_i];
|
||||
|
||||
// Naming is confusing (show/render). I do not call render on node from
|
||||
// here.
|
||||
root.show();
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Tree.prototype.sortBranch = function(node, sortFun) {
|
||||
// First sort internal array of children
|
||||
node.children.sort(sortFun);
|
||||
|
||||
// Next render rows in correct order on page
|
||||
this._sortChildRows(node);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Tree.prototype.unloadBranch = function(node) {
|
||||
var children, i;
|
||||
|
||||
for (i = 0; i < node.children.length; i++) {
|
||||
this.removeNode(node.children[i]);
|
||||
}
|
||||
|
||||
// Reset node's collection of children
|
||||
node.children = [];
|
||||
|
||||
node.updateBranchLeafClass();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Tree.prototype._moveRows = function(node, destination) {
|
||||
var children = node.children, i;
|
||||
|
||||
node.row.insertAfter(destination.row);
|
||||
node.render();
|
||||
|
||||
// Loop backwards through children to have them end up on UI in correct
|
||||
// order (see #112)
|
||||
for (i = children.length - 1; i >= 0; i--) {
|
||||
this._moveRows(children[i], node);
|
||||
}
|
||||
};
|
||||
|
||||
// Special _moveRows case, move children to itself to force sorting
|
||||
Tree.prototype._sortChildRows = function(parentNode) {
|
||||
return this._moveRows(parentNode, parentNode);
|
||||
};
|
||||
|
||||
return Tree;
|
||||
})();
|
||||
|
||||
// jQuery Plugin
|
||||
methods = {
|
||||
init: function(options, force) {
|
||||
var settings;
|
||||
|
||||
settings = $.extend({
|
||||
branchAttr: "ttBranch",
|
||||
clickableNodeNames: false,
|
||||
column: 0,
|
||||
columnElType: "td", // i.e. 'td', 'th' or 'td,th'
|
||||
expandable: false,
|
||||
expanderTemplate: "<a href='#'> </a>",
|
||||
indent: 10,
|
||||
indenterTemplate: "<span class='indenter'></span>",
|
||||
initialState: "collapsed",
|
||||
nodeIdAttr: "ttId", // maps to data-tt-id
|
||||
parentIdAttr: "ttParentId", // maps to data-tt-parent-id
|
||||
stringExpand: "Expand",
|
||||
stringCollapse: "Collapse",
|
||||
|
||||
// Events
|
||||
onInitialized: null,
|
||||
onNodeCollapse: null,
|
||||
onNodeExpand: null,
|
||||
onNodeInitialized: null
|
||||
}, options);
|
||||
|
||||
return this.each(function() {
|
||||
var el = $(this), tree;
|
||||
|
||||
if (force || el.data("treetable") === undefined) {
|
||||
tree = new Tree(this, settings);
|
||||
tree.loadRows(this.rows).render();
|
||||
|
||||
el.addClass("treetable").data("treetable", tree);
|
||||
|
||||
if (settings.onInitialized != null) {
|
||||
settings.onInitialized.apply(tree);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
return this.each(function() {
|
||||
return $(this).removeData("treetable").removeClass("treetable");
|
||||
});
|
||||
},
|
||||
|
||||
collapseAll: function() {
|
||||
this.data("treetable").collapseAll();
|
||||
return this;
|
||||
},
|
||||
|
||||
collapseNode: function(id) {
|
||||
var node = this.data("treetable").tree[id];
|
||||
|
||||
if (node) {
|
||||
node.collapse();
|
||||
} else {
|
||||
throw new Error("Unknown node '" + id + "'");
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
expandAll: function() {
|
||||
this.data("treetable").expandAll();
|
||||
return this;
|
||||
},
|
||||
|
||||
expandNode: function(id) {
|
||||
var node = this.data("treetable").tree[id];
|
||||
|
||||
if (node) {
|
||||
if (!node.initialized) {
|
||||
node._initialize();
|
||||
}
|
||||
|
||||
node.expand();
|
||||
} else {
|
||||
throw new Error("Unknown node '" + id + "'");
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
loadBranch: function(node, rows) {
|
||||
var settings = this.data("treetable").settings,
|
||||
tree = this.data("treetable").tree;
|
||||
|
||||
// TODO Switch to $.parseHTML
|
||||
rows = $(rows);
|
||||
|
||||
if (node == null) { // Inserting new root nodes
|
||||
this.append(rows);
|
||||
} else {
|
||||
var lastNode = this.data("treetable").findLastNode(node);
|
||||
rows.insertAfter(lastNode.row);
|
||||
}
|
||||
|
||||
this.data("treetable").loadRows(rows);
|
||||
|
||||
// Make sure nodes are properly initialized
|
||||
rows.filter("tr").each(function() {
|
||||
tree[$(this).data(settings.nodeIdAttr)].show();
|
||||
});
|
||||
|
||||
if (node != null) {
|
||||
// Re-render parent to ensure expander icon is shown (#79)
|
||||
node.render().expand();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
move: function(nodeId, destinationId) {
|
||||
var destination, node;
|
||||
|
||||
node = this.data("treetable").tree[nodeId];
|
||||
destination = this.data("treetable").tree[destinationId];
|
||||
this.data("treetable").move(node, destination);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
node: function(id) {
|
||||
return this.data("treetable").tree[id];
|
||||
},
|
||||
|
||||
removeNode: function(id) {
|
||||
var node = this.data("treetable").tree[id];
|
||||
|
||||
if (node) {
|
||||
this.data("treetable").removeNode(node);
|
||||
} else {
|
||||
throw new Error("Unknown node '" + id + "'");
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
reveal: function(id) {
|
||||
var node = this.data("treetable").tree[id];
|
||||
|
||||
if (node) {
|
||||
node.reveal();
|
||||
} else {
|
||||
throw new Error("Unknown node '" + id + "'");
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
sortBranch: function(node, columnOrFunction) {
|
||||
var settings = this.data("treetable").settings,
|
||||
prepValue,
|
||||
sortFun;
|
||||
|
||||
columnOrFunction = columnOrFunction || settings.column;
|
||||
sortFun = columnOrFunction;
|
||||
|
||||
if ($.isNumeric(columnOrFunction)) {
|
||||
sortFun = function(a, b) {
|
||||
var extractValue, valA, valB;
|
||||
|
||||
extractValue = function(node) {
|
||||
var val = node.row.find("td:eq(" + columnOrFunction + ")").text();
|
||||
// Ignore trailing/leading whitespace and use uppercase values for
|
||||
// case insensitive ordering
|
||||
return $.trim(val).toUpperCase();
|
||||
}
|
||||
|
||||
valA = extractValue(a);
|
||||
valB = extractValue(b);
|
||||
|
||||
if (valA < valB) return -1;
|
||||
if (valA > valB) return 1;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
this.data("treetable").sortBranch(node, sortFun);
|
||||
return this;
|
||||
},
|
||||
|
||||
unloadBranch: function(node) {
|
||||
this.data("treetable").unloadBranch(node);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.treetable = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || !method) {
|
||||
return methods.init.apply(this, arguments);
|
||||
} else {
|
||||
return $.error("Method " + method + " does not exist on jQuery.treetable");
|
||||
}
|
||||
};
|
||||
|
||||
// Expose classes to world
|
||||
this.TreeTable || (this.TreeTable = {});
|
||||
this.TreeTable.Node = Node;
|
||||
this.TreeTable.Tree = Tree;
|
||||
}).call(this);
|
||||
4
sources/poky/bitbake/lib/toaster/toastergui/static/js/jsrender.min.js
vendored
Normal file
4
sources/poky/bitbake/lib/toaster/toastergui/static/js/jsrender.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,95 @@
|
||||
"use strict";
|
||||
|
||||
function layerBtnsInit() {
|
||||
|
||||
/* Remove any current bindings to avoid duplicated binds */
|
||||
$(".layerbtn").unbind('click');
|
||||
|
||||
$(".layerbtn").click(function (){
|
||||
var layerObj = $(this).data("layer");
|
||||
var add = ($(this).data('directive') === "add");
|
||||
var thisBtn = $(this);
|
||||
|
||||
libtoaster.addRmLayer(layerObj, add, function (layerDepsList){
|
||||
libtoaster.showChangeNotification(libtoaster.makeLayerAddRmAlertMsg(layerObj, layerDepsList, add));
|
||||
|
||||
/* In-cell notification */
|
||||
var notification = $('<div id="temp-inline-notify" style="display: none; font-size: 11px; line-height: 1.3;" class="tooltip-inner"></div>');
|
||||
thisBtn.parent().append(notification);
|
||||
|
||||
if (add){
|
||||
if (layerDepsList.length > 0)
|
||||
notification.text(String(layerDepsList.length + 1) + " layers added");
|
||||
else
|
||||
notification.text("1 layer added");
|
||||
|
||||
var layerBtnsFadeOut = $();
|
||||
var layerExistsBtnFadeIn = $();
|
||||
|
||||
layerBtnsFadeOut = layerBtnsFadeOut.add(".layer-add-" + layerObj.id);
|
||||
layerExistsBtnFadeIn = layerExistsBtnFadeIn.add(".layer-exists-" + layerObj.id);
|
||||
|
||||
for (var i in layerDepsList){
|
||||
layerBtnsFadeOut = layerBtnsFadeOut.add(".layer-add-" + layerDepsList[i].id);
|
||||
layerExistsBtnFadeIn = layerExistsBtnFadeIn.add(".layer-exists-" + layerDepsList[i].id);
|
||||
}
|
||||
|
||||
layerBtnsFadeOut.fadeOut().promise().done(function(){
|
||||
notification.fadeIn().delay(500).fadeOut(function(){
|
||||
/* Fade in the buttons */
|
||||
layerExistsBtnFadeIn.fadeIn();
|
||||
notification.remove();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
notification.text("1 layer removed");
|
||||
/* Deleting a layer we only hanlde the one button */
|
||||
thisBtn.fadeOut(function(){
|
||||
notification.fadeIn().delay(500).fadeOut(function(){
|
||||
$(".layer-add-" + layerObj.id).fadeIn();
|
||||
notification.remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
$("td .build-recipe-btn").unbind('click');
|
||||
$("td .build-recipe-btn").click(function(e){
|
||||
e.preventDefault();
|
||||
var recipe = $(this).data('recipe-name');
|
||||
|
||||
libtoaster.startABuild(null, recipe,
|
||||
function(){
|
||||
/* Success */
|
||||
window.location.replace(libtoaster.ctx.projectBuildsUrl);
|
||||
});
|
||||
});
|
||||
|
||||
$("td .set-default-recipe-btn").unbind('click');
|
||||
$("td .set-default-recipe-btn").click(function(e){
|
||||
e.preventDefault();
|
||||
var recipe = $(this).data('recipe-name');
|
||||
|
||||
libtoaster.setDefaultImage(null, recipe,
|
||||
function(){
|
||||
/* Success */
|
||||
window.location.replace(libtoaster.ctx.projectSpecificPageUrl);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$(".customise-btn").unbind('click');
|
||||
$(".customise-btn").click(function(e){
|
||||
e.preventDefault();
|
||||
var imgCustomModal = $("#new-custom-image-modal");
|
||||
|
||||
if (imgCustomModal.length == 0)
|
||||
throw("Modal new-custom-image not found");
|
||||
|
||||
var recipe = {id: $(this).data('recipe'), name: null}
|
||||
newCustomImageModalSetRecipes([recipe]);
|
||||
imgCustomModal.modal('show');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* layer: Object representing the parent layer { id: .. name: ... url }
|
||||
* dependencies: array of dependency layer objects { id: .. name: ..}
|
||||
* title: optional override for title
|
||||
* body: optional override for body
|
||||
* addToProject: Whether to add layers to project on accept
|
||||
* successAdd: function to run on success
|
||||
*/
|
||||
function showLayerDepsModal(layer,
|
||||
dependencies,
|
||||
title,
|
||||
body,
|
||||
addToProject,
|
||||
successAdd) {
|
||||
|
||||
if ($("#dependencies-modal").length === 0) {
|
||||
$.get(libtoaster.ctx.htmlUrl + "/layer_deps_modal.html", function(html){
|
||||
$("body").append(html);
|
||||
setupModal();
|
||||
});
|
||||
} else {
|
||||
setupModal();
|
||||
}
|
||||
|
||||
function setupModal(){
|
||||
|
||||
if (title) {
|
||||
$('#dependencies-modal #title').text(title);
|
||||
} else {
|
||||
$('#dependencies-modal #title').text(layer.name);
|
||||
}
|
||||
|
||||
if (body) {
|
||||
$("#dependencies-modal #body-text").html(body);
|
||||
} else {
|
||||
$("#dependencies-modal #layer-name").text(layer.name);
|
||||
}
|
||||
|
||||
var deplistHtml = "";
|
||||
for (var i = 0; i < dependencies.length; i++) {
|
||||
deplistHtml += "<li><div class=\"checkbox\"><label><input name=\"dependencies\" value=\"";
|
||||
deplistHtml += dependencies[i].id;
|
||||
deplistHtml +="\" type=\"checkbox\" checked=\"checked\"/>";
|
||||
deplistHtml += dependencies[i].name;
|
||||
deplistHtml += "</label></div></li>";
|
||||
}
|
||||
$('#dependencies-list').html(deplistHtml);
|
||||
|
||||
$("#dependencies-modal").data("deps", dependencies);
|
||||
|
||||
/* Clear any alert notifications before showing the modal */
|
||||
$(".alert").fadeOut(function(){
|
||||
$('#dependencies-modal').modal('show');
|
||||
});
|
||||
|
||||
/* Discard the old submission function */
|
||||
$("#dependencies-modal-form").unbind('submit');
|
||||
|
||||
$("#dependencies-modal-form").submit(function (e) {
|
||||
e.preventDefault();
|
||||
var selectedLayerIds = [];
|
||||
var selectedLayers = [];
|
||||
|
||||
$("input[name='dependencies']:checked").each(function () {
|
||||
selectedLayerIds.push(parseInt($(this).val()));
|
||||
});
|
||||
|
||||
/* -1 is a special dummy Id which we use when the layer isn't yet in the
|
||||
* system, normally we would add the current layer to the selection.
|
||||
*/
|
||||
if (layer.id != -1)
|
||||
selectedLayerIds.push(layer.id);
|
||||
|
||||
/* Find the selected layer objects from our original list */
|
||||
for (var i = 0; i < selectedLayerIds.length; i++) {
|
||||
for (var j = 0; j < dependencies.length; j++) {
|
||||
if (dependencies[j].id == selectedLayerIds[i]) {
|
||||
selectedLayers.push(dependencies[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addToProject) {
|
||||
libtoaster.editCurrentProject({ 'layerAdd': selectedLayerIds.join(",") }, function () {
|
||||
if (successAdd) {
|
||||
successAdd(selectedLayers);
|
||||
}
|
||||
}, function () {
|
||||
console.warn("Adding layers to project failed");
|
||||
});
|
||||
} else {
|
||||
successAdd(selectedLayers);
|
||||
}
|
||||
|
||||
$('#dependencies-modal').modal('hide');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
"use strict";
|
||||
|
||||
function layerDetailsPageInit (ctx) {
|
||||
|
||||
var layerDepInput = $("#layer-dep-input");
|
||||
var layerDepBtn = $("#add-layer-dependency-btn");
|
||||
var layerDepsList = $("#layer-deps-list");
|
||||
var currentLayerDepSelection;
|
||||
var addRmLayerBtn = $("#add-remove-layer-btn");
|
||||
var targetTab = $("#targets-tab");
|
||||
var machineTab = $("#machines-tab");
|
||||
var detailsTab = $("#details-tab");
|
||||
var editLayerSource = $("#edit-layer-source");
|
||||
var saveSourceChangesBtn = $("#save-changes-for-switch");
|
||||
var layerGitRefInput = $("#layer-git-ref");
|
||||
var layerSubDirInput = $('#layer-subdir');
|
||||
|
||||
targetTab.on('show.bs.tab', targetsTabShow);
|
||||
detailsTab.on('show.bs.tab', detailsTabShow);
|
||||
machineTab.on('show.bs.tab', machinesTabShow);
|
||||
|
||||
/* setup the dependencies typeahead */
|
||||
libtoaster.makeTypeahead(layerDepInput,
|
||||
libtoaster.ctx.layersTypeAheadUrl,
|
||||
{ include_added: "true" }, function(item){
|
||||
currentLayerDepSelection = item;
|
||||
layerDepBtn.removeAttr("disabled");
|
||||
});
|
||||
|
||||
/* disable the add layer button if its input field is empty */
|
||||
layerDepInput.on("keyup",function(){
|
||||
if ($(this).val().length === 0) {
|
||||
layerDepBtn.attr("disabled", "disabled");
|
||||
}
|
||||
});
|
||||
|
||||
function addRemoveDep(depLayerId, add, doneCb) {
|
||||
var data = { layer_version_id : ctx.layerVersion.id };
|
||||
if (add)
|
||||
data.add_dep = depLayerId;
|
||||
else
|
||||
data.rm_dep = depLayerId;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: ctx.xhrUpdateLayerUrl,
|
||||
data: data,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
if (data.error != "ok") {
|
||||
console.warn(data.error);
|
||||
} else {
|
||||
doneCb();
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function layerDepRemoveClick() {
|
||||
var toRemove = $(this).parent().data('layer-id');
|
||||
var layerDepItem = $(this);
|
||||
|
||||
addRemoveDep(toRemove, false, function(){
|
||||
layerDepItem.parent().fadeOut(function (){
|
||||
layerDepItem.remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* Add dependency layer button click handler */
|
||||
layerDepBtn.click(function(){
|
||||
if (currentLayerDepSelection === undefined)
|
||||
return;
|
||||
|
||||
addRemoveDep(currentLayerDepSelection.id, true, function(){
|
||||
/* Make a list item for the new layer dependency */
|
||||
var newLayerDep = $("<li><a></a><span class=\"glyphicon glyphicon-trash\" data-toggle=\"tooltip\" title=\"Delete\"></span></li>");
|
||||
|
||||
newLayerDep.data('layer-id', currentLayerDepSelection.id);
|
||||
newLayerDep.children("span").tooltip();
|
||||
|
||||
var link = newLayerDep.children("a");
|
||||
link.attr("href", currentLayerDepSelection.layerdetailurl);
|
||||
link.text(currentLayerDepSelection.name);
|
||||
link.tooltip({title: currentLayerDepSelection.tooltip, placement: "right"});
|
||||
|
||||
/* Connect up the tash icon */
|
||||
var trashItem = newLayerDep.children("span");
|
||||
trashItem.click(layerDepRemoveClick);
|
||||
|
||||
layerDepsList.append(newLayerDep);
|
||||
/* Clear the current selection */
|
||||
layerDepInput.val("");
|
||||
currentLayerDepSelection = undefined;
|
||||
layerDepBtn.attr("disabled", "disabled");
|
||||
});
|
||||
});
|
||||
|
||||
$(".glyphicon-edit").click(function (){
|
||||
var mParent = $(this).parent("dd");
|
||||
mParent.prev().css("margin-top", "10px");
|
||||
mParent.children("form").slideDown();
|
||||
var currentVal = mParent.children(".current-value");
|
||||
currentVal.hide();
|
||||
/* Set the current value to the input field */
|
||||
mParent.find("textarea,input").val(currentVal.text());
|
||||
/* If the input field is empty, disable the submit button */
|
||||
if ( mParent.find("textarea,input").val().length == 0 ) {
|
||||
mParent.find(".change-btn").attr("disabled", "disabled");
|
||||
}
|
||||
/* Hides the "Not set" text */
|
||||
mParent.children(".text-muted").hide();
|
||||
/* We're editing so hide the delete icon */
|
||||
mParent.children(".delete-current-value").hide();
|
||||
mParent.find(".cancel").show();
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
$(".delete-current-value").click(function(){
|
||||
var mParent = $(this).parent("dd");
|
||||
mParent.find("input").val("");
|
||||
mParent.find("textarea").val("");
|
||||
mParent.find(".change-btn").click();
|
||||
});
|
||||
|
||||
$(".cancel").click(function(){
|
||||
var mParent = $(this).parents("dd");
|
||||
$(this).hide();
|
||||
mParent.children("form").slideUp(function(){
|
||||
mParent.children(".current-value").show();
|
||||
/* Show the "Not set" text if we ended up with no value */
|
||||
if (!mParent.children(".current-value").html()){
|
||||
mParent.children(".text-muted").fadeIn();
|
||||
mParent.children(".delete-current-value").hide();
|
||||
} else {
|
||||
mParent.children(".delete-current-value").show();
|
||||
}
|
||||
|
||||
mParent.children(".glyphicon-edit").show();
|
||||
mParent.prev().css("margin-top", "0");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function defaultAddBtnText(){
|
||||
var text = " Add the "+ctx.layerVersion.name+" layer to your project";
|
||||
addRmLayerBtn.text(text);
|
||||
addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-plus\"></span>");
|
||||
addRmLayerBtn.removeClass("btn-danger");
|
||||
}
|
||||
|
||||
function detailsTabShow(){
|
||||
if (!ctx.layerVersion.inCurrentPrj)
|
||||
defaultAddBtnText();
|
||||
|
||||
window.location.hash = "information";
|
||||
}
|
||||
|
||||
function targetsTabShow(){
|
||||
if (!ctx.layerVersion.inCurrentPrj){
|
||||
if (ctx.numTargets > 0) {
|
||||
var text = " Add the "+ctx.layerVersion.name+" layer to your project "+
|
||||
"to enable these recipes";
|
||||
addRmLayerBtn.text(text);
|
||||
addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-plus\"></span>");
|
||||
} else {
|
||||
defaultAddBtnText();
|
||||
}
|
||||
}
|
||||
|
||||
window.location.hash = "recipes";
|
||||
}
|
||||
|
||||
$("#recipestable").on('table-done', function(e, total, tableParams){
|
||||
ctx.numTargets = total;
|
||||
|
||||
if (total === 0 && !tableParams.search) {
|
||||
$("#no-recipes-yet").show();
|
||||
} else {
|
||||
$("#no-recipes-yet").hide();
|
||||
}
|
||||
|
||||
targetTab.removeClass("text-muted");
|
||||
if (window.location.hash === "#recipes"){
|
||||
/* re run the machinesTabShow to update the text */
|
||||
targetsTabShow();
|
||||
}
|
||||
});
|
||||
|
||||
$("#machinestable").on('table-done', function(e, total, tableParams){
|
||||
ctx.numMachines = total;
|
||||
|
||||
if (total === 0 && !tableParams.search)
|
||||
$("#no-machines-yet").show();
|
||||
else
|
||||
$("#no-machines-yet").hide();
|
||||
|
||||
machineTab.removeClass("text-muted");
|
||||
if (window.location.hash === "#machines"){
|
||||
/* re run the machinesTabShow to update the text */
|
||||
machinesTabShow();
|
||||
}
|
||||
|
||||
$(".select-machine-btn").click(function(e){
|
||||
if ($(this).hasClass("disabled"))
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
function machinesTabShow(){
|
||||
if (!ctx.layerVersion.inCurrentPrj) {
|
||||
if (ctx.numMachines > 0){
|
||||
var text = " Add the "+ctx.layerVersion.name+" layer to your project " +
|
||||
"to enable these machines";
|
||||
addRmLayerBtn.text(text);
|
||||
addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-plus\"></span>");
|
||||
} else {
|
||||
defaultAddBtnText();
|
||||
}
|
||||
}
|
||||
|
||||
window.location.hash = "machines";
|
||||
}
|
||||
|
||||
$(".pagesize").change(function(){
|
||||
var search = libtoaster.parseUrlParams();
|
||||
search.limit = this.value;
|
||||
|
||||
window.location.search = libtoaster.dumpsUrlParams(search);
|
||||
});
|
||||
|
||||
/* Enables the Build target and Select Machine buttons and switches the
|
||||
* add/remove button
|
||||
*/
|
||||
function setLayerInCurrentPrj(added) {
|
||||
ctx.layerVersion.inCurrentPrj = added;
|
||||
|
||||
if (added){
|
||||
/* enable and switch all the button states */
|
||||
$(".build-recipe-btn").removeClass("disabled");
|
||||
$(".select-machine-btn").removeClass("disabled");
|
||||
addRmLayerBtn.addClass("btn-danger");
|
||||
addRmLayerBtn.data('directive', "remove");
|
||||
addRmLayerBtn.text(" Remove the "+ctx.layerVersion.name+" layer from your project");
|
||||
addRmLayerBtn.prepend("<span class=\"glyphicon glyphicon-trash\"></span>");
|
||||
|
||||
} else {
|
||||
/* disable and switch all the button states */
|
||||
$(".build-recipe-btn").addClass("disabled");
|
||||
$(".select-machine-btn").addClass("disabled");
|
||||
addRmLayerBtn.removeClass("btn-danger");
|
||||
addRmLayerBtn.data('directive', "add");
|
||||
|
||||
/* "special" handler so that we get the correct button text which depends
|
||||
* on which tab is currently visible. Unfortunately we can't just call
|
||||
* tab('show') as if it's already visible it doesn't run the event.
|
||||
*/
|
||||
switch ($(".nav-tabs .active a").prop('id')){
|
||||
case 'machines-tab':
|
||||
machinesTabShow();
|
||||
break;
|
||||
case 'targets-tab':
|
||||
targetsTabShow();
|
||||
break;
|
||||
default:
|
||||
defaultAddBtnText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$("#dismiss-alert").click(function(){
|
||||
$(this).parent().fadeOut();
|
||||
});
|
||||
|
||||
/* Add or remove this layer from the project */
|
||||
addRmLayerBtn.click(function() {
|
||||
|
||||
var add = ($(this).data('directive') === "add");
|
||||
|
||||
libtoaster.addRmLayer(ctx.layerVersion, add, function (layersList){
|
||||
var alertMsg = $("#alert-msg");
|
||||
alertMsg.html(libtoaster.makeLayerAddRmAlertMsg(ctx.layerVersion, layersList, add));
|
||||
|
||||
setLayerInCurrentPrj(add);
|
||||
|
||||
libtoaster.showChangeNotification(alertMsg);
|
||||
});
|
||||
});
|
||||
|
||||
/* Handler for all of the Change buttons */
|
||||
$(".change-btn").click(function(){
|
||||
var mParent = $(this).parent();
|
||||
var prop = $(this).data('layer-prop');
|
||||
|
||||
/* We have inputs, select and textareas to potentially grab the value
|
||||
* from.
|
||||
*/
|
||||
var entryElement = mParent.find("input");
|
||||
if (entryElement.length === 0)
|
||||
entryElement = mParent.find("textarea");
|
||||
if (entryElement.length === 0) {
|
||||
console.warn("Could not find element to get data from for this change");
|
||||
return;
|
||||
}
|
||||
|
||||
var data = { layer_version_id: ctx.layerVersion.id };
|
||||
data[prop] = entryElement.val();
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: ctx.xhrUpdateLayerUrl,
|
||||
data: data,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
if (data.error != "ok") {
|
||||
console.warn(data.error);
|
||||
} else {
|
||||
/* success layer property changed */
|
||||
var inputArea = mParent.parents("dd");
|
||||
var text;
|
||||
|
||||
text = entryElement.val();
|
||||
|
||||
/* Hide the "Not set" text if it's visible */
|
||||
inputArea.find(".text-muted").hide();
|
||||
inputArea.find(".current-value").text(text);
|
||||
/* Same behaviour as cancel in that we hide the form/show current
|
||||
* value.
|
||||
*/
|
||||
inputArea.find(".cancel").click();
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* Disable the change button when we have no data in the input */
|
||||
$("dl input, dl textarea").on("input",function() {
|
||||
if ($(this).val().length === 0)
|
||||
$(this).parent().next(".change-btn").attr("disabled", "disabled");
|
||||
else
|
||||
$(this).parent().next(".change-btn").removeAttr("disabled");
|
||||
});
|
||||
|
||||
/* This checks to see if the dt's dd has data in it or if the change data
|
||||
* form is visible, otherwise hide it
|
||||
*/
|
||||
$("dl").children().each(function (){
|
||||
if ($(this).is("dt")) {
|
||||
var dd = $(this).next("dd");
|
||||
if (!dd.children("form:visible")|| !dd.find(".current-value").html()){
|
||||
if (ctx.layerVersion.layer_source == ctx.layerSourceTypes.TYPE_IMPORTED ||
|
||||
ctx.layerVersion.layer_source == ctx.layerSourceTypes.TYPE_LOCAL) {
|
||||
/* There's no current value and the layer is editable
|
||||
* so show the "Not set" and hide the delete icon
|
||||
*/
|
||||
dd.find(".text-muted").show();
|
||||
dd.find(".delete-current-value").hide();
|
||||
} else {
|
||||
/* We're not viewing an editable layer so hide the empty dd/dl pair */
|
||||
$(this).hide();
|
||||
dd.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Hide the right column if it contains no information */
|
||||
if ($("dl.item-info").children(':visible').length === 0) {
|
||||
$("dl.item-info").parent().hide();
|
||||
}
|
||||
|
||||
/* Clear the current search selection and reload the results */
|
||||
$(".target-search-clear").click(function(){
|
||||
$("#target-search").val("");
|
||||
$(this).parents("form").submit();
|
||||
});
|
||||
|
||||
$(".machine-search-clear").click(function(){
|
||||
$("#machine-search").val("");
|
||||
$(this).parents("form").submit();
|
||||
});
|
||||
|
||||
$("#layer-delete-confirmed").click(function(){
|
||||
|
||||
$("#delete-layer-modal button[data-dismiss='modal']").hide();
|
||||
|
||||
var message = $('<span>You have deleted <strong>1</strong> layer from your project: <strong id="deleted-layer-name"></strong>');
|
||||
message.find("#deleted-layer-name").text(ctx.layerVersion.name);
|
||||
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: ctx.xhrUpdateLayerUrl,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function(data) {
|
||||
if (data.error != "ok") {
|
||||
console.warn(data.error);
|
||||
} else {
|
||||
libtoaster.setNotification("layer-deleted", message.html());
|
||||
window.location.replace(data.gotoUrl);
|
||||
}
|
||||
},
|
||||
error: function(data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
layerDepsList.find(".glyphicon-trash").click(layerDepRemoveClick);
|
||||
layerDepsList.find("a").tooltip();
|
||||
$(".glyphicon-trash").tooltip();
|
||||
$(".commit").tooltip();
|
||||
|
||||
editLayerSource.click(function() {
|
||||
/* Kindly bring the git layers imported from layerindex to normal page
|
||||
* and not this new page :(
|
||||
*/
|
||||
$(this).hide();
|
||||
saveSourceChangesBtn.attr("disabled", "disabled");
|
||||
|
||||
$("#git-repo-info, #directory-info").hide();
|
||||
$("#edit-layer-source-form").fadeIn();
|
||||
if ($("#layer-dir-path-in-details").val() == "") {
|
||||
//Local dir path is empty...
|
||||
$("#repo").prop("checked", true);
|
||||
$("#layer-git").fadeIn();
|
||||
$("#layer-dir").hide();
|
||||
} else {
|
||||
$("#layer-git").hide();
|
||||
$("#layer-dir").fadeIn();
|
||||
}
|
||||
});
|
||||
|
||||
$('input:radio[name="source-location"]').change(function() {
|
||||
if ($('input[name=source-location]:checked').val() == "repo") {
|
||||
$("#layer-git").fadeIn();
|
||||
$("#layer-dir").hide();
|
||||
if ($("#layer-git-repo-url").val().length === 0 && layerGitRefInput.val().length === 0) {
|
||||
saveSourceChangesBtn.attr("disabled", "disabled");
|
||||
}
|
||||
} else {
|
||||
$("#layer-dir").fadeIn();
|
||||
$("#layer-git").hide();
|
||||
}
|
||||
});
|
||||
|
||||
$("#layer-dir-path-in-details").keyup(function() {
|
||||
saveSourceChangesBtn.removeAttr("disabled");
|
||||
});
|
||||
|
||||
$("#layer-git-repo-url").keyup(function() {
|
||||
if ($("#layer-git-repo-url").val().length > 0 && layerGitRefInput.val().length > 0) {
|
||||
saveSourceChangesBtn.removeAttr("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
layerGitRefInput.keyup(function() {
|
||||
if ($("#layer-git-repo-url").val().length > 0 && layerGitRefInput.val().length > 0) {
|
||||
saveSourceChangesBtn.removeAttr("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
layerSubDirInput.keyup(function(){
|
||||
if ($(this).val().length > 0){
|
||||
saveSourceChangesBtn.removeAttr("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
$('#cancel-changes-for-switch').click(function() {
|
||||
$("#edit-layer-source-form").hide();
|
||||
$("#directory-info, #git-repo-info").fadeIn();
|
||||
editLayerSource.show();
|
||||
});
|
||||
|
||||
saveSourceChangesBtn.click(function() {
|
||||
|
||||
var layerData = {
|
||||
vcs_url: $('#layer-git-repo-url').val(),
|
||||
commit: layerGitRefInput.val(),
|
||||
dirpath: layerSubDirInput.val(),
|
||||
local_source_dir: $('#layer-dir-path-in-details').val(),
|
||||
};
|
||||
|
||||
if ($('input[name=source-location]:checked').val() == "repo") {
|
||||
layerData.local_source_dir = "";
|
||||
} else {
|
||||
layerData.vcs_url = "";
|
||||
layerData.git_ref = "";
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: ctx.xhrUpdateLayerUrl,
|
||||
data: layerData,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
if (data.error != "ok") {
|
||||
console.warn(data.error);
|
||||
} else {
|
||||
/* success layer property changed */
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,841 @@
|
||||
"use strict";
|
||||
/* All shared functionality to go in libtoaster object.
|
||||
* This object really just helps readability since we can then have
|
||||
* a traceable namespace.
|
||||
*/
|
||||
var libtoaster = (function () {
|
||||
// prevent conflicts with Bootstrap 2's typeahead (required during
|
||||
// transition from v2 to v3)
|
||||
var typeahead = jQuery.fn.typeahead.noConflict();
|
||||
jQuery.fn._typeahead = typeahead;
|
||||
|
||||
/* Make a typeahead from an input element
|
||||
*
|
||||
* _makeTypeahead parameters
|
||||
* jQElement: input element as selected by $('selector')
|
||||
* xhrUrl: the url to get the JSON from; this URL should return JSON in the
|
||||
* format:
|
||||
* { "results": [ { "name": "test", "detail" : "a test thing" }, ... ] }
|
||||
* xhrParams: the data/parameters to pass to the getJSON url e.g.
|
||||
* { 'type' : 'projects' }; the text typed will be passed as 'search'.
|
||||
* selectedCB: function to call once an item has been selected; has
|
||||
* signature selectedCB(item), where item is an item in the format shown
|
||||
* in the JSON list above, i.e.
|
||||
* { "name": "name", "detail": "detail" }.
|
||||
*/
|
||||
function _makeTypeahead(jQElement, xhrUrl, xhrParams, selectedCB) {
|
||||
if (!xhrUrl || xhrUrl.length === 0) {
|
||||
throw("No url supplied for typeahead");
|
||||
}
|
||||
|
||||
var xhrReq;
|
||||
|
||||
jQElement._typeahead(
|
||||
{
|
||||
highlight: true,
|
||||
classNames: {
|
||||
open: "dropdown-menu",
|
||||
cursor: "active"
|
||||
}
|
||||
},
|
||||
{
|
||||
source: function (query, syncResults, asyncResults) {
|
||||
xhrParams.search = query;
|
||||
|
||||
// if we have a request in progress, cancel it and start another
|
||||
if (xhrReq) {
|
||||
xhrReq.abort();
|
||||
}
|
||||
|
||||
xhrReq = $.getJSON(xhrUrl, xhrParams, function (data) {
|
||||
if (data.error !== "ok") {
|
||||
console.error("Error getting data from server: " + data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
xhrReq = null;
|
||||
|
||||
asyncResults(data.results);
|
||||
});
|
||||
},
|
||||
|
||||
// how the selected item is shown in the input
|
||||
display: function (item) {
|
||||
return item.name;
|
||||
},
|
||||
|
||||
templates: {
|
||||
// how the item is displayed in the dropdown
|
||||
suggestion: function (item) {
|
||||
var elt = document.createElement("div");
|
||||
elt.innerHTML = item.name + " " + item.detail;
|
||||
return elt;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// when an item is selected using the typeahead, invoke the callback
|
||||
jQElement.on("typeahead:select", function (event, item) {
|
||||
selectedCB(item);
|
||||
});
|
||||
}
|
||||
|
||||
/* startABuild:
|
||||
* url: xhr_buildrequest or null for current project
|
||||
* targets: an array or space separated list of targets to build
|
||||
* onsuccess: callback for successful execution
|
||||
* onfail: callback for failed execution
|
||||
*/
|
||||
function _startABuild (url, targets, onsuccess, onfail) {
|
||||
|
||||
if (!url)
|
||||
url = libtoaster.ctx.xhrBuildRequestUrl;
|
||||
|
||||
/* Flatten the array of targets into a space spearated list */
|
||||
if (targets instanceof Array){
|
||||
targets = targets.reduce(function(prevV, nextV){
|
||||
return prev + ' ' + next;
|
||||
});
|
||||
}
|
||||
|
||||
$.ajax( {
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: { 'targets' : targets },
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (_data) {
|
||||
if (_data.error !== "ok") {
|
||||
console.warn(_data.error);
|
||||
} else {
|
||||
if (onsuccess !== undefined) onsuccess(_data);
|
||||
}
|
||||
},
|
||||
error: function (_data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(_data);
|
||||
if (onfail) onfail(data);
|
||||
} });
|
||||
}
|
||||
|
||||
/* cancelABuild:
|
||||
* url: xhr_buildrequest url or null for current project
|
||||
* buildRequestIds: space separated list of build request ids
|
||||
* onsuccess: callback for successful execution
|
||||
* onfail: callback for failed execution
|
||||
*/
|
||||
function _cancelABuild(url, buildRequestIds, onsuccess, onfail){
|
||||
if (!url)
|
||||
url = libtoaster.ctx.xhrBuildRequestUrl;
|
||||
|
||||
$.ajax( {
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: { 'buildCancel': buildRequestIds },
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (_data) {
|
||||
if (_data.error !== "ok") {
|
||||
console.warn(_data.error);
|
||||
} else {
|
||||
if (onsuccess) onsuccess(_data);
|
||||
}
|
||||
},
|
||||
error: function (_data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(_data);
|
||||
if (onfail) onfail(_data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _getMostRecentBuilds(url, onsuccess, onfail) {
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
data : {format: 'json'},
|
||||
headers: {'X-CSRFToken': $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
onsuccess ? onsuccess(data) : console.log(data);
|
||||
},
|
||||
error: function (data) {
|
||||
onfail ? onfail(data) : console.error(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Get a project's configuration info */
|
||||
function _getProjectInfo(url, onsuccess, onfail){
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (_data) {
|
||||
if (_data.error !== "ok") {
|
||||
console.warn(_data.error);
|
||||
} else {
|
||||
if (onsuccess !== undefined) onsuccess(_data);
|
||||
}
|
||||
},
|
||||
error: function (_data) {
|
||||
console.warn(_data);
|
||||
if (onfail) onfail(_data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Properties for data can be:
|
||||
* layerDel (csv)
|
||||
* layerAdd (csv)
|
||||
* projectName
|
||||
* projectVersion
|
||||
* machineName
|
||||
*/
|
||||
function _editCurrentProject(data, onSuccess, onFail){
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: libtoaster.ctx.xhrProjectUrl,
|
||||
data: data,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
if (data.error != "ok") {
|
||||
console.log(data.error);
|
||||
if (onFail !== undefined)
|
||||
onFail(data);
|
||||
} else {
|
||||
if (onSuccess !== undefined)
|
||||
onSuccess(data);
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.log("Call failed");
|
||||
console.log(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _getLayerDepsForProject(url, onSuccess, onFail){
|
||||
/* Check for dependencies not in the current project */
|
||||
$.getJSON(url,
|
||||
{ format: 'json' },
|
||||
function(data) {
|
||||
if (data.error != "ok") {
|
||||
console.log(data.error);
|
||||
if (onFail !== undefined)
|
||||
onFail(data);
|
||||
} else {
|
||||
var deps = {};
|
||||
/* Filter out layer dep ids which are in the
|
||||
* project already.
|
||||
*/
|
||||
deps.list = data.layerdeps.list.filter(function(layerObj){
|
||||
return (data.projectlayers.lastIndexOf(layerObj.id) < 0);
|
||||
});
|
||||
|
||||
onSuccess(deps);
|
||||
}
|
||||
}, function() {
|
||||
console.log("E: Failed to make request");
|
||||
});
|
||||
}
|
||||
|
||||
/* parses the query string of the current window.location to an object */
|
||||
function _parseUrlParams() {
|
||||
var string = window.location.search;
|
||||
string = string.substr(1);
|
||||
var stringArray = string.split ("&");
|
||||
var obj = {};
|
||||
|
||||
for (var i in stringArray) {
|
||||
var keyVal = stringArray[i].split ("=");
|
||||
obj[keyVal[0]] = keyVal[1];
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* takes a flat object and outputs it as a query string
|
||||
* e.g. the output of dumpsUrlParams
|
||||
*/
|
||||
function _dumpsUrlParams(obj) {
|
||||
var str = "?";
|
||||
|
||||
for (var key in obj){
|
||||
if (!obj[key])
|
||||
continue;
|
||||
|
||||
str += key+ "="+obj[key].toString();
|
||||
str += "&";
|
||||
}
|
||||
|
||||
/* Maintain the current hash */
|
||||
str += window.location.hash;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function _addRmLayer(layerObj, add, doneCb){
|
||||
if (layerObj.xhrLayerUrl === undefined){
|
||||
alert("ERROR: missing xhrLayerUrl object. Please file a bug.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (add === true) {
|
||||
/* If adding get the deps for this layer */
|
||||
libtoaster.getLayerDepsForProject(layerObj.xhrLayerUrl,
|
||||
function (layers) {
|
||||
|
||||
/* got result for dependencies */
|
||||
if (layers.list.length === 0){
|
||||
var editData = { layerAdd : layerObj.id };
|
||||
libtoaster.editCurrentProject(editData, function() {
|
||||
doneCb([]);
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
showLayerDepsModal(layerObj, layers.list, null, null, true, doneCb);
|
||||
} catch (e) {
|
||||
$.getScript(libtoaster.ctx.jsUrl + "layerDepsModal.js", function(){
|
||||
showLayerDepsModal(layerObj, layers.list, null, null, true, doneCb);
|
||||
}, function(){
|
||||
console.warn("Failed to load layerDepsModal");
|
||||
});
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
} else if (add === false) {
|
||||
var editData = { layerDel : layerObj.id };
|
||||
|
||||
libtoaster.editCurrentProject(editData, function () {
|
||||
doneCb([]);
|
||||
}, function () {
|
||||
console.warn ("Removing layer from project failed");
|
||||
doneCb(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _makeLayerAddRmAlertMsg(layer, layerDepsList, add) {
|
||||
var alertMsg;
|
||||
|
||||
if (layerDepsList.length > 0 && add === true) {
|
||||
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>");
|
||||
|
||||
/* Build the layer deps list */
|
||||
layerDepsList.map(function(layer, i){
|
||||
var link = $("<a class=\"alert-link\"></a>");
|
||||
|
||||
link.attr("href", layer.layerdetailurl);
|
||||
link.text(layer.name);
|
||||
link.tooltip({title: layer.tooltip});
|
||||
|
||||
if (i !== 0)
|
||||
alertMsg.append(", ");
|
||||
|
||||
alertMsg.append(link);
|
||||
});
|
||||
} else if (layerDepsList.length === 0 && add === true) {
|
||||
alertMsg = $("<span>You have added <strong>1</strong> layer to your project: <a class=\"alert-link\" id=\"layer-affected-name\"></a></span></span>");
|
||||
} else if (add === false) {
|
||||
alertMsg = $("<span>You have removed <strong>1</strong> layer from your project: <a class=\"alert-link\" id=\"layer-affected-name\"></a></span>");
|
||||
}
|
||||
|
||||
alertMsg.children("#layer-affected-name").text(layer.name);
|
||||
alertMsg.children("#layer-affected-name").attr("href", layer.layerdetailurl);
|
||||
|
||||
return alertMsg.html();
|
||||
}
|
||||
|
||||
function _showChangeNotification(message){
|
||||
$(".alert-dismissible").fadeOut().promise().done(function(){
|
||||
var alertMsg = $("#change-notification-msg");
|
||||
|
||||
alertMsg.html(message);
|
||||
$("#change-notification, #change-notification *").fadeIn();
|
||||
});
|
||||
}
|
||||
|
||||
function _createCustomRecipe(name, baseRecipeId, doneCb){
|
||||
var data = {
|
||||
'name' : name,
|
||||
'project' : libtoaster.ctx.projectId,
|
||||
'base' : baseRecipeId,
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: libtoaster.ctx.xhrCustomRecipeUrl,
|
||||
data: data,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (ret) {
|
||||
if (doneCb){
|
||||
doneCb(ret);
|
||||
} else if (ret.error !== "ok") {
|
||||
console.warn(ret.error);
|
||||
}
|
||||
},
|
||||
error: function (ret) {
|
||||
console.warn("Call failed");
|
||||
console.warn(ret);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Validate project names. Use unique project names
|
||||
|
||||
All arguments accepted by this function are JQeury objects.
|
||||
|
||||
For example if the HTML element has "hint-error-project-name", then
|
||||
it is passed to this function as $("#hint-error-project-name").
|
||||
|
||||
Arg1 - projectName : This is a string object. In the HTML, project name will be entered here.
|
||||
Arg2 - hintEerror : This is a jquery object which will accept span which throws error for
|
||||
duplicate project
|
||||
Arg3 - ctrlGrpValidateProjectName : This object holds the div with class "control-group"
|
||||
Arg4 - enableOrDisableBtn : This object will help the API to enable or disable the form.
|
||||
For example in the new project the create project button will be hidden if the
|
||||
duplicate project exist. Similarly in the projecttopbar the save button will be
|
||||
disabled if the project name already exist.
|
||||
|
||||
Return - This function doesn't return anything. It sets/unsets the behavior of the elements.
|
||||
*/
|
||||
|
||||
function _makeProjectNameValidation(projectName, hintError,
|
||||
ctrlGrpValidateProjectName, enableOrDisableBtn ) {
|
||||
|
||||
function checkProjectName(projectName){
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: libtoaster.ctx.projectsTypeAheadUrl,
|
||||
data: { 'search' : projectName },
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function(data){
|
||||
if (data.results.length > 0 &&
|
||||
data.results[0].name === projectName) {
|
||||
// This project name exists hence show the error and disable
|
||||
// the save button
|
||||
ctrlGrpValidateProjectName.addClass('has-error');
|
||||
hintError.show();
|
||||
enableOrDisableBtn.attr('disabled', 'disabled');
|
||||
} else {
|
||||
ctrlGrpValidateProjectName.removeClass('has-error');
|
||||
hintError.hide();
|
||||
enableOrDisableBtn.removeAttr('disabled');
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.log(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/* The moment user types project name remove the error */
|
||||
projectName.on("input", function() {
|
||||
var projectName = $(this).val();
|
||||
checkProjectName(projectName)
|
||||
});
|
||||
|
||||
/* Validate new project name */
|
||||
projectName.on("blur", function(){
|
||||
var projectName = $(this).val();
|
||||
checkProjectName(projectName)
|
||||
});
|
||||
}
|
||||
|
||||
// if true, the loading spinner for Ajax requests will be displayed
|
||||
// if requests take more than 1200ms
|
||||
var ajaxLoadingTimerEnabled = true;
|
||||
|
||||
// turn on the page-level loading spinner for Ajax requests
|
||||
function _enableAjaxLoadingTimer() {
|
||||
ajaxLoadingTimerEnabled = true;
|
||||
}
|
||||
|
||||
// turn off the page-level loading spinner for Ajax requests
|
||||
function _disableAjaxLoadingTimer() {
|
||||
ajaxLoadingTimerEnabled = false;
|
||||
}
|
||||
|
||||
/* Utility function to set a notification for the next page load */
|
||||
function _setNotification(name, message){
|
||||
var data = {
|
||||
name: name,
|
||||
message: message
|
||||
};
|
||||
|
||||
$.cookie('toaster-notification', JSON.stringify(data), { path: '/'});
|
||||
}
|
||||
|
||||
/* _updateProject:
|
||||
* url: xhrProjectUpdateUrl or null for current project
|
||||
* onsuccess: callback for successful execution
|
||||
* onfail: callback for failed execution
|
||||
*/
|
||||
function _updateProject (url, targets, default_image, onsuccess, onfail) {
|
||||
|
||||
if (!url)
|
||||
url = libtoaster.ctx.xhrProjectUpdateUrl;
|
||||
|
||||
/* Flatten the array of targets into a space spearated list */
|
||||
if (targets instanceof Array){
|
||||
targets = targets.reduce(function(prevV, nextV){
|
||||
return prev + ' ' + next;
|
||||
});
|
||||
}
|
||||
|
||||
$.ajax( {
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: { 'do_update' : 'True' , 'targets' : targets , 'default_image' : default_image , },
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (_data) {
|
||||
if (_data.error !== "ok") {
|
||||
console.warn(_data.error);
|
||||
} else {
|
||||
if (onsuccess !== undefined) onsuccess(_data);
|
||||
}
|
||||
},
|
||||
error: function (_data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(_data);
|
||||
if (onfail) onfail(data);
|
||||
} });
|
||||
}
|
||||
|
||||
/* _cancelProject:
|
||||
* url: xhrProjectUpdateUrl or null for current project
|
||||
* onsuccess: callback for successful execution
|
||||
* onfail: callback for failed execution
|
||||
*/
|
||||
function _cancelProject (url, onsuccess, onfail) {
|
||||
|
||||
if (!url)
|
||||
url = libtoaster.ctx.xhrProjectCancelUrl;
|
||||
|
||||
$.ajax( {
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: { 'do_cancel' : 'True' },
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (_data) {
|
||||
if (_data.error !== "ok") {
|
||||
console.warn(_data.error);
|
||||
} else {
|
||||
if (onsuccess !== undefined) onsuccess(_data);
|
||||
}
|
||||
},
|
||||
error: function (_data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(_data);
|
||||
if (onfail) onfail(data);
|
||||
} });
|
||||
}
|
||||
|
||||
/* _setDefaultImage:
|
||||
* url: xhrSetDefaultImageUrl or null for current project
|
||||
* targets: an array or space separated list of targets to set as default
|
||||
* onsuccess: callback for successful execution
|
||||
* onfail: callback for failed execution
|
||||
*/
|
||||
function _setDefaultImage (url, targets, onsuccess, onfail) {
|
||||
|
||||
if (!url)
|
||||
url = libtoaster.ctx.xhrSetDefaultImageUrl;
|
||||
|
||||
/* Flatten the array of targets into a space spearated list */
|
||||
if (targets instanceof Array){
|
||||
targets = targets.reduce(function(prevV, nextV){
|
||||
return prev + ' ' + next;
|
||||
});
|
||||
}
|
||||
|
||||
$.ajax( {
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: { 'targets' : targets },
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (_data) {
|
||||
if (_data.error !== "ok") {
|
||||
console.warn(_data.error);
|
||||
} else {
|
||||
if (onsuccess !== undefined) onsuccess(_data);
|
||||
}
|
||||
},
|
||||
error: function (_data) {
|
||||
console.warn("Call failed");
|
||||
console.warn(_data);
|
||||
if (onfail) onfail(data);
|
||||
} });
|
||||
}
|
||||
|
||||
return {
|
||||
enableAjaxLoadingTimer: _enableAjaxLoadingTimer,
|
||||
disableAjaxLoadingTimer: _disableAjaxLoadingTimer,
|
||||
reload_params : reload_params,
|
||||
startABuild : _startABuild,
|
||||
cancelABuild : _cancelABuild,
|
||||
getMostRecentBuilds: _getMostRecentBuilds,
|
||||
makeTypeahead : _makeTypeahead,
|
||||
getProjectInfo: _getProjectInfo,
|
||||
getLayerDepsForProject : _getLayerDepsForProject,
|
||||
editCurrentProject : _editCurrentProject,
|
||||
debug: false,
|
||||
parseUrlParams : _parseUrlParams,
|
||||
dumpsUrlParams : _dumpsUrlParams,
|
||||
addRmLayer : _addRmLayer,
|
||||
makeLayerAddRmAlertMsg : _makeLayerAddRmAlertMsg,
|
||||
showChangeNotification : _showChangeNotification,
|
||||
createCustomRecipe: _createCustomRecipe,
|
||||
makeProjectNameValidation: _makeProjectNameValidation,
|
||||
setNotification: _setNotification,
|
||||
updateProject : _updateProject,
|
||||
cancelProject : _cancelProject,
|
||||
setDefaultImage : _setDefaultImage,
|
||||
};
|
||||
})();
|
||||
|
||||
/* keep this in the global scope for compatability */
|
||||
function reload_params(params) {
|
||||
var uri = window.location.href;
|
||||
var splitlist = uri.split("?");
|
||||
var url = splitlist[0];
|
||||
var parameters = splitlist[1];
|
||||
// deserialize the call parameters
|
||||
var cparams = [];
|
||||
if(parameters)
|
||||
cparams = parameters.split("&");
|
||||
|
||||
var nparams = {};
|
||||
for (var i = 0; i < cparams.length; i++) {
|
||||
var temp = cparams[i].split("=");
|
||||
nparams[temp[0]] = temp[1];
|
||||
}
|
||||
// update parameter values
|
||||
for (i in params) {
|
||||
nparams[encodeURIComponent(i)] = encodeURIComponent(params[i]);
|
||||
}
|
||||
// serialize the structure
|
||||
var callparams = [];
|
||||
for (i in nparams) {
|
||||
callparams.push(i+"="+nparams[i]);
|
||||
}
|
||||
window.location.href = url+"?"+callparams.join('&');
|
||||
}
|
||||
|
||||
/* Things that happen for all pages */
|
||||
$(document).ready(function() {
|
||||
|
||||
(function showNotificationRequest(){
|
||||
var cookie = $.cookie('toaster-notification');
|
||||
|
||||
if (!cookie)
|
||||
return;
|
||||
|
||||
var notificationData = JSON.parse(cookie);
|
||||
|
||||
libtoaster.showChangeNotification(notificationData.message);
|
||||
|
||||
$.removeCookie('toaster-notification', { path: "/"});
|
||||
})();
|
||||
|
||||
|
||||
|
||||
var ajaxLoadingTimer;
|
||||
|
||||
/* If we don't have a console object which might be the case in some
|
||||
* browsers, no-op it to avoid undefined errors.
|
||||
*/
|
||||
if (!window.console) {
|
||||
window.console = {};
|
||||
window.console.warn = function() {};
|
||||
window.console.error = function() {};
|
||||
}
|
||||
|
||||
/*
|
||||
* highlight plugin.
|
||||
*/
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
// Prevent invalid links from jumping page scroll
|
||||
$('a[href="#"]').click(function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
/* START TODO Delete this section now redundant */
|
||||
/* Belen's additions */
|
||||
|
||||
// turn Edit columns dropdown into a multiselect menu
|
||||
$('.dropdown-menu input, .dropdown-menu label').click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// enable popovers in any table cells that contain an anchor with the
|
||||
// .btn class applied, and make sure popovers work on click, are mutually
|
||||
// exclusive and they close when your click outside their area
|
||||
|
||||
$('html').click(function(){
|
||||
$('td > a.btn').popover('hide');
|
||||
});
|
||||
|
||||
$('td > a.btn').popover({
|
||||
html:true,
|
||||
placement:'left',
|
||||
container:'body',
|
||||
trigger:'manual'
|
||||
}).click(function(e){
|
||||
$('td > a.btn').not(this).popover('hide');
|
||||
// ideally we would use 'toggle' here
|
||||
// but it seems buggy in our Bootstrap version
|
||||
$(this).popover('show');
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
// enable tooltips for applied filters
|
||||
$('th a.btn-primary').tooltip({container:'body', html:true, placement:'bottom', delay:{hide:1500}});
|
||||
|
||||
// hide applied filter tooltip when you click on the filter button
|
||||
$('th a.btn-primary').click(function () {
|
||||
$('.tooltip').hide();
|
||||
});
|
||||
|
||||
/* Initialise bootstrap tooltips */
|
||||
$(".get-help, [data-toggle=tooltip]").tooltip({
|
||||
container : 'body',
|
||||
html : true,
|
||||
delay: { show : 300 }
|
||||
});
|
||||
|
||||
// show help bubble on hover inside tables
|
||||
$("table").on("mouseover", "th, td", function () {
|
||||
$(this).find(".hover-help").css("visibility","visible");
|
||||
});
|
||||
|
||||
$("table").on("mouseleave", "th, td", function () {
|
||||
$(this).find(".hover-help").css("visibility","hidden");
|
||||
});
|
||||
|
||||
/* END TODO Delete this section now redundant */
|
||||
|
||||
// show task type and outcome in task details pages
|
||||
$(".task-info").tooltip({ container: 'body', html: true, delay: {show: 200}, placement: 'right' });
|
||||
|
||||
// initialise the tooltips for the edit icons
|
||||
$(".glyphicon-edit").tooltip({ container: 'body', html: true, delay: {show: 400}, title: "Change" });
|
||||
|
||||
// initialise the tooltips for the download icons
|
||||
$(".icon-download-alt").tooltip({ container: 'body', html: true, delay: { show: 200 } });
|
||||
|
||||
// initialise popover for debug information
|
||||
$(".glyphicon-info-sign").popover( { placement: 'bottom', html: true, container: 'body' });
|
||||
|
||||
// linking directly to tabs
|
||||
$(function(){
|
||||
var hash = window.location.hash;
|
||||
$('ul.nav a[href="' + hash + '"]').tab('show');
|
||||
|
||||
$('.nav-tabs a').click(function () {
|
||||
$(this).tab('show');
|
||||
$('body').scrollTop();
|
||||
});
|
||||
});
|
||||
|
||||
// toggle for long content (variables, python stack trace, etc)
|
||||
$('.full, .full-hide').hide();
|
||||
$('.full-show').click(function(){
|
||||
$('.full').slideDown(function(){
|
||||
$('.full-hide').show();
|
||||
});
|
||||
$(this).hide();
|
||||
});
|
||||
$('.full-hide').click(function(){
|
||||
$(this).hide();
|
||||
$('.full').slideUp(function(){
|
||||
$('.full-show').show();
|
||||
});
|
||||
});
|
||||
|
||||
//toggle the errors and warnings sections
|
||||
$('.show-errors').click(function() {
|
||||
$('#collapse-errors').addClass('in');
|
||||
});
|
||||
$('.toggle-errors').click(function() {
|
||||
$('#collapse-errors').toggleClass('in');
|
||||
});
|
||||
$('.show-warnings').click(function() {
|
||||
$('#collapse-warnings').addClass('in');
|
||||
});
|
||||
$('.toggle-warnings').click(function() {
|
||||
$('#collapse-warnings').toggleClass('in');
|
||||
});
|
||||
$('.show-exceptions').click(function() {
|
||||
$('#collapse-exceptions').addClass('in');
|
||||
});
|
||||
$('.toggle-exceptions').click(function() {
|
||||
$('#collapse-exceptions').toggleClass('in');
|
||||
});
|
||||
|
||||
|
||||
$("#hide-alert").click(function(){
|
||||
$(this).parent().fadeOut();
|
||||
});
|
||||
|
||||
//show warnings section when requested from the previous page
|
||||
if (location.href.search('#warnings') > -1) {
|
||||
$('#collapse-warnings').addClass('in');
|
||||
}
|
||||
|
||||
/* Show the loading notification if nothing has happend after 1.5
|
||||
* seconds
|
||||
*/
|
||||
$(document).bind("ajaxStart", function(){
|
||||
if (ajaxLoadingTimer)
|
||||
window.clearTimeout(ajaxLoadingTimer);
|
||||
|
||||
ajaxLoadingTimer = window.setTimeout(function() {
|
||||
if (libtoaster.ajaxLoadingTimerEnabled) {
|
||||
$("#loading-notification").fadeIn();
|
||||
}
|
||||
}, 1200);
|
||||
});
|
||||
|
||||
$(document).bind("ajaxStop", function(){
|
||||
if (ajaxLoadingTimer)
|
||||
window.clearTimeout(ajaxLoadingTimer);
|
||||
|
||||
$("#loading-notification").fadeOut();
|
||||
});
|
||||
|
||||
$(document).ajaxError(function(event, jqxhr, settings, errMsg){
|
||||
if (errMsg === 'abort')
|
||||
return;
|
||||
|
||||
console.warn("Problem with xhr call");
|
||||
console.warn(errMsg);
|
||||
console.warn(jqxhr.responseText);
|
||||
});
|
||||
|
||||
function check_for_duplicate_ids () {
|
||||
/* warn about duplicate element ids */
|
||||
var ids = {};
|
||||
$("[id]").each(function() {
|
||||
if (this.id && ids[this.id]) {
|
||||
console.warn('Duplicate element id #'+this.id);
|
||||
}
|
||||
ids[this.id] = true;
|
||||
});
|
||||
}
|
||||
|
||||
/* Make sure we don't have a notification overlay a modal */
|
||||
$(".modal").on('show.bs.modal', function(){
|
||||
$(".alert-dismissible").fadeOut();
|
||||
});
|
||||
|
||||
if (libtoaster.debug) {
|
||||
check_for_duplicate_ids();
|
||||
} else {
|
||||
/* Debug is false so supress warnings by overriding the functions */
|
||||
window.console.warn = function () {};
|
||||
window.console.error = function () {};
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,152 @@
|
||||
|
||||
function mrbSectionInit(ctx){
|
||||
$('#latest-builds').on('click', '.cancel-build-btn', function(e){
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
var url = $(this).data('request-url');
|
||||
var buildReqIds = $(this).data('buildrequest-id');
|
||||
|
||||
libtoaster.cancelABuild(url, buildReqIds, function () {
|
||||
window.location.reload();
|
||||
}, null);
|
||||
});
|
||||
|
||||
$('#latest-builds').on('click', '.rebuild-btn', function(e){
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
|
||||
var url = $(this).data('request-url');
|
||||
var target = $(this).data('target');
|
||||
|
||||
libtoaster.startABuild(url, target, function(){
|
||||
window.location.reload();
|
||||
}, null);
|
||||
});
|
||||
|
||||
// cached version of buildData, so we can determine whether a build has
|
||||
// changed since it was last fetched, and update the DOM appropriately
|
||||
var buildData = {};
|
||||
|
||||
// returns the cached version of this build, or {} is there isn't a cached one
|
||||
function getCached(build) {
|
||||
return buildData[build.id] || {};
|
||||
}
|
||||
|
||||
// returns true if a build's state changed to "Succeeded", "Failed"
|
||||
// or "Cancelled" from some other value
|
||||
function buildFinished(build) {
|
||||
var cached = getCached(build);
|
||||
return cached.state &&
|
||||
cached.state !== build.state &&
|
||||
(build.state == 'Succeeded' || build.state == 'Failed' ||
|
||||
build.state == 'Cancelled');
|
||||
}
|
||||
|
||||
// returns true if the state changed
|
||||
function stateChanged(build) {
|
||||
var cached = getCached(build);
|
||||
return (cached.state !== build.state);
|
||||
}
|
||||
|
||||
// returns true if the tasks_complete_percentage changed
|
||||
function tasksProgressChanged(build) {
|
||||
var cached = getCached(build);
|
||||
return (cached.tasks_complete_percentage !== build.tasks_complete_percentage);
|
||||
}
|
||||
|
||||
// returns true if the number of recipes parsed/to parse changed
|
||||
function recipeProgressChanged(build) {
|
||||
var cached = getCached(build);
|
||||
return (cached.recipes_parsed_percentage !== build.recipes_parsed_percentage);
|
||||
}
|
||||
|
||||
// returns true if the number of repos cloned/to clone changed
|
||||
function cloneProgressChanged(build) {
|
||||
var cached = getCached(build);
|
||||
return (cached.repos_cloned_percentage !== build.repos_cloned_percentage);
|
||||
}
|
||||
|
||||
function refreshMostRecentBuilds(){
|
||||
libtoaster.getMostRecentBuilds(
|
||||
libtoaster.ctx.mostRecentBuildsUrl,
|
||||
|
||||
// success callback
|
||||
function (data) {
|
||||
var build;
|
||||
var tmpl;
|
||||
var container;
|
||||
var selector;
|
||||
var colourClass;
|
||||
var elements;
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
build = data[i];
|
||||
|
||||
if (buildFinished(build)) {
|
||||
// a build finished: reload the whole page so that the build
|
||||
// shows up in the builds table
|
||||
window.location.reload(true);
|
||||
}
|
||||
else if (stateChanged(build)) {
|
||||
// update the whole template
|
||||
build.warnings_pluralise = (build.warnings !== 1 ? 's' : '');
|
||||
build.errors_pluralise = (build.errors !== 1 ? 's' : '');
|
||||
|
||||
tmpl = $.templates("#build-template");
|
||||
|
||||
html = $(tmpl.render(build));
|
||||
|
||||
selector = '[data-latest-build-result="' + build.id + '"] ' +
|
||||
'[data-role="build-status-container"]';
|
||||
container = $(selector);
|
||||
|
||||
// initialize bootstrap tooltips in the new HTML
|
||||
html.find('span.glyphicon-question-sign').tooltip();
|
||||
|
||||
container.html(html);
|
||||
}
|
||||
else if (cloneProgressChanged(build)) {
|
||||
// update the clone progress text
|
||||
selector = '#repos-cloned-percentage-' + build.id;
|
||||
$(selector).html(build.repos_cloned_percentage);
|
||||
selector = '#repos-cloned-progressitem-' + build.id;
|
||||
$(selector).html('('+build.progress_item+')');
|
||||
|
||||
// update the recipe progress bar
|
||||
selector = '#repos-cloned-percentage-bar-' + build.id;
|
||||
$(selector).width(build.repos_cloned_percentage + '%');
|
||||
}
|
||||
else if (tasksProgressChanged(build)) {
|
||||
// update the task progress text
|
||||
selector = '#build-pc-done-' + build.id;
|
||||
$(selector).html(build.tasks_complete_percentage);
|
||||
|
||||
// update the task progress bar
|
||||
selector = '#build-pc-done-bar-' + build.id;
|
||||
$(selector).width(build.tasks_complete_percentage + '%');
|
||||
}
|
||||
else if (recipeProgressChanged(build)) {
|
||||
// update the recipe progress text
|
||||
selector = '#recipes-parsed-percentage-' + build.id;
|
||||
$(selector).html(build.recipes_parsed_percentage);
|
||||
|
||||
// update the recipe progress bar
|
||||
selector = '#recipes-parsed-percentage-bar-' + build.id;
|
||||
$(selector).width(build.recipes_parsed_percentage + '%');
|
||||
}
|
||||
|
||||
buildData[build.id] = build;
|
||||
}
|
||||
},
|
||||
|
||||
// fail callback
|
||||
function (data) {
|
||||
console.error(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
window.setInterval(refreshMostRecentBuilds, 1500);
|
||||
refreshMostRecentBuilds();
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Used for the newcustomimage_modal actions
|
||||
|
||||
The .data('recipe') value on the outer element determines which
|
||||
recipe ID is used as the basis for the new custom image recipe created via
|
||||
this modal.
|
||||
|
||||
Use newCustomImageModalSetRecipes() to set the recipes available as a base
|
||||
for the new custom image. This will manage the addition of radio buttons
|
||||
to select the base image (or remove the radio buttons, if there is only a
|
||||
single base image available).
|
||||
*/
|
||||
|
||||
function newCustomImageModalInit(){
|
||||
|
||||
var newCustomImgBtn = $("#create-new-custom-image-btn");
|
||||
var imgCustomModal = $("#new-custom-image-modal");
|
||||
var invalidNameHelp = $("#invalid-name-help");
|
||||
var invalidRecipeHelp = $("#invalid-recipe-help");
|
||||
var nameInput = imgCustomModal.find('input');
|
||||
|
||||
var invalidNameMsg = "Image names cannot contain spaces or capital letters. The only allowed special character is dash (-).";
|
||||
var duplicateNameMsg = "An image with this name already exists. Image names must be unique.";
|
||||
var duplicateImageInProjectMsg = "An image with this name already exists in this project."
|
||||
var invalidBaseRecipeIdMsg = "Please select an image to customise.";
|
||||
var missingParentRecipe = "The parent recipe file was not found. Cancel this action, build any target (like 'quilt-native') to force all new layers to clone, and try again";
|
||||
var unknownError = "Unexpected error: ";
|
||||
|
||||
// set button to "submit" state and enable text entry so user can
|
||||
// enter the custom recipe name
|
||||
showSubmitState();
|
||||
|
||||
/* capture clicks on radio buttons inside the modal; when one is selected,
|
||||
* set the recipe on the modal
|
||||
*/
|
||||
imgCustomModal.on("click", "[name='select-image']", function(e) {
|
||||
clearRecipeError();
|
||||
$(".radio").each(function(){
|
||||
$(this).removeClass("has-error");
|
||||
});
|
||||
|
||||
var recipeId = $(e.target).attr('data-recipe');
|
||||
imgCustomModal.data('recipe', recipeId);
|
||||
});
|
||||
|
||||
newCustomImgBtn.click(function(e){
|
||||
// disable the button and text entry
|
||||
showLoadingState();
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var baseRecipeId = imgCustomModal.data('recipe');
|
||||
|
||||
if (!baseRecipeId) {
|
||||
showRecipeError(invalidBaseRecipeIdMsg);
|
||||
$(".radio").each(function(){
|
||||
$(this).addClass("has-error");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (nameInput.val().length > 0) {
|
||||
libtoaster.createCustomRecipe(nameInput.val(), baseRecipeId,
|
||||
function(ret) {
|
||||
showSubmitState();
|
||||
if (ret.error !== "ok") {
|
||||
console.warn(ret.error);
|
||||
if (ret.error === "invalid-name") {
|
||||
showNameError(invalidNameMsg);
|
||||
return;
|
||||
} else if (ret.error === "recipe-already-exists") {
|
||||
showNameError(duplicateNameMsg);
|
||||
return;
|
||||
} else if (ret.error === "image-already-exists") {
|
||||
showNameError(duplicateImageInProjectMsg);
|
||||
return;
|
||||
} else if (ret.error === "recipe-parent-not-exist") {
|
||||
showNameError(missingParentRecipe);
|
||||
} else {
|
||||
showNameError(unknownError + ret.error);
|
||||
}
|
||||
} else {
|
||||
imgCustomModal.modal('hide');
|
||||
imgCustomModal.one('hidden.bs.modal', showSubmitState);
|
||||
window.location.replace(ret.url + '?notify=new');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// enable text entry, show "Create image" button text
|
||||
function showSubmitState() {
|
||||
libtoaster.enableAjaxLoadingTimer();
|
||||
newCustomImgBtn.find('[data-role="loading-state"]').hide();
|
||||
newCustomImgBtn.find('[data-role="submit-state"]').show();
|
||||
newCustomImgBtn.removeAttr('disabled');
|
||||
nameInput.removeAttr('disabled');
|
||||
}
|
||||
|
||||
// disable text entry, show "Creating image..." button text;
|
||||
// we also disabled the page-level ajax loading spinner while this spinner
|
||||
// is active
|
||||
function showLoadingState() {
|
||||
libtoaster.disableAjaxLoadingTimer();
|
||||
newCustomImgBtn.find('[data-role="submit-state"]').hide();
|
||||
newCustomImgBtn.find('[data-role="loading-state"]').show();
|
||||
newCustomImgBtn.attr('disabled', 'disabled');
|
||||
nameInput.attr('disabled', 'disabled');
|
||||
}
|
||||
|
||||
function showNameError(text){
|
||||
invalidNameHelp.text(text);
|
||||
invalidNameHelp.show();
|
||||
nameInput.parent().addClass('has-error');
|
||||
}
|
||||
|
||||
function showRecipeError(text){
|
||||
invalidRecipeHelp.text(text);
|
||||
invalidRecipeHelp.show();
|
||||
}
|
||||
|
||||
function clearRecipeError(){
|
||||
invalidRecipeHelp.hide();
|
||||
}
|
||||
|
||||
nameInput.on('keyup', function(){
|
||||
if (nameInput.val().length === 0){
|
||||
newCustomImgBtn.prop("disabled", true);
|
||||
return
|
||||
}
|
||||
|
||||
if (nameInput.val().search(/[^a-z|0-9|-]/) != -1){
|
||||
showNameError(invalidNameMsg);
|
||||
newCustomImgBtn.prop("disabled", true);
|
||||
nameInput.parent().addClass('has-error');
|
||||
} else {
|
||||
invalidNameHelp.hide();
|
||||
newCustomImgBtn.prop("disabled", false);
|
||||
nameInput.parent().removeClass('has-error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Set the image recipes which can used as the basis for the custom
|
||||
* image recipe the user is creating
|
||||
* baseRecipes: a list of one or more recipes which can be
|
||||
* used as the base for the new custom image recipe in the format:
|
||||
* [{'id': <recipe ID>, 'name': <recipe name>'}, ...]
|
||||
*
|
||||
* if recipes is a single recipe, just show the text box to set the
|
||||
* name for the new custom image; if recipes contains multiple recipe objects,
|
||||
* show a set of radio buttons so the user can decide which to use as the
|
||||
* basis for the new custom image
|
||||
*/
|
||||
function newCustomImageModalSetRecipes(baseRecipes) {
|
||||
var imgCustomModal = $("#new-custom-image-modal");
|
||||
var imageSelector = $('#new-custom-image-modal [data-role="image-selector"]');
|
||||
var imageSelectRadiosContainer = $('#new-custom-image-modal [data-role="image-selector-radios"]');
|
||||
|
||||
// remove any existing radio buttons + labels
|
||||
imageSelector.remove('[data-role="image-radio"]');
|
||||
|
||||
if (baseRecipes.length === 1) {
|
||||
// hide the radio button container
|
||||
imageSelector.hide();
|
||||
|
||||
/* set the single recipe ID on the modal as it's the only one
|
||||
* we can build from.
|
||||
*/
|
||||
imgCustomModal.data('recipe', baseRecipes[0].id);
|
||||
}
|
||||
else {
|
||||
// add radio buttons; note that the handlers for the radio buttons
|
||||
// are set in newCustomImageModalInit via event delegation
|
||||
for (var i = 0; i < baseRecipes.length; i++) {
|
||||
var recipe = baseRecipes[i];
|
||||
imageSelectRadiosContainer.append(
|
||||
'<div class="radio"><label data-role="image-radio">' +
|
||||
'<input type="radio" name="select-image" ' +
|
||||
'data-recipe="' + recipe.id + '">' +
|
||||
recipe.name +
|
||||
'</label></div>'
|
||||
);
|
||||
}
|
||||
|
||||
/* select the first radio button as default selection. Radio button
|
||||
* groups should always display with an option checked
|
||||
*/
|
||||
imageSelectRadiosContainer.find("input:radio:first").attr("checked", "checked");
|
||||
|
||||
/* check which radio button is selected by default inside the modal,
|
||||
* and set the recipe on the modal accordingly
|
||||
*/
|
||||
imageSelectRadiosContainer.find("input:radio").each(function(){
|
||||
if ( $(this).is(":checked") ) {
|
||||
var recipeId = $(this).attr("data-recipe");
|
||||
imgCustomModal.data("recipe", recipeId);
|
||||
}
|
||||
});
|
||||
|
||||
// show the radio button container
|
||||
imageSelector.show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
"use strict";
|
||||
|
||||
function projectPageInit(ctx) {
|
||||
|
||||
var layerAddInput = $("#layer-add-input");
|
||||
var layersInPrjList = $("#layers-in-project-list");
|
||||
var layerAddBtn = $("#add-layer-btn");
|
||||
|
||||
var machineChangeInput = $("#machine-change-input");
|
||||
var machineChangeBtn = $("#machine-change-btn");
|
||||
var machineForm = $("#select-machine-form");
|
||||
var machineChangeFormToggle = $("#change-machine-toggle");
|
||||
var machineNameTitle = $("#project-machine-name");
|
||||
var machineChangeCancel = $("#cancel-machine-change");
|
||||
var machineInputForm = $("#machine-input-form");
|
||||
var invalidMachineNameHelp = $("#invalid-machine-name-help");
|
||||
|
||||
var distroChangeInput = $("#distro-change-input");
|
||||
var distroChangeBtn = $("#distro-change-btn");
|
||||
var distroForm = $("#select-distro-form");
|
||||
var distroChangeFormToggle = $("#change-distro-toggle");
|
||||
var distroNameTitle = $("#project-distro-name");
|
||||
var distroChangeCancel = $("#cancel-distro-change");
|
||||
|
||||
var freqBuildBtn = $("#freq-build-btn");
|
||||
var freqBuildList = $("#freq-build-list");
|
||||
|
||||
var releaseChangeFormToggle = $("#release-change-toggle");
|
||||
var releaseTitle = $("#project-release-title");
|
||||
var releaseForm = $("#change-release-form");
|
||||
var releaseModal = $("#change-release-modal");
|
||||
var cancelReleaseChange = $("#cancel-release-change");
|
||||
|
||||
var currentLayerAddSelection;
|
||||
var currentMachineAddSelection = "";
|
||||
var currentDistroAddSelection = "";
|
||||
|
||||
var urlParams = libtoaster.parseUrlParams();
|
||||
|
||||
libtoaster.getProjectInfo(libtoaster.ctx.xhrProjectUrl, function(prjInfo){
|
||||
updateProjectLayers(prjInfo.layers);
|
||||
updateFreqBuildRecipes(prjInfo.freqtargets);
|
||||
updateProjectRelease(prjInfo.release);
|
||||
|
||||
/* If we're receiving a machine set from the url and it's different from
|
||||
* our current machine then activate set machine sequence.
|
||||
*/
|
||||
if (urlParams.hasOwnProperty('setMachine') &&
|
||||
urlParams.setMachine !== prjInfo.machine.name){
|
||||
machineChangeInput.val(urlParams.setMachine);
|
||||
machineChangeBtn.click();
|
||||
} else {
|
||||
updateMachineName(prjInfo.machine.name);
|
||||
}
|
||||
|
||||
/* If we're receiving a distro set from the url and it's different from
|
||||
* our current distro then activate set machine sequence.
|
||||
*/
|
||||
if (urlParams.hasOwnProperty('setDistro') &&
|
||||
urlParams.setDistro !== prjInfo.distro.name){
|
||||
distroChangeInput.val(urlParams.setDistro);
|
||||
distroChangeBtn.click();
|
||||
} else {
|
||||
updateDistroName(prjInfo.distro?.name);
|
||||
}
|
||||
|
||||
/* Now we're really ready show the page */
|
||||
$("#project-page").show();
|
||||
|
||||
/* Set the project name in the delete modal */
|
||||
$("#delete-project-modal .project-name").text(prjInfo.name);
|
||||
});
|
||||
|
||||
if (urlParams.hasOwnProperty('notify') && urlParams.notify === 'new-project'){
|
||||
$("#project-created-notification").show();
|
||||
}
|
||||
|
||||
/* Add/Rm layer functionality */
|
||||
|
||||
libtoaster.makeTypeahead(layerAddInput, libtoaster.ctx.layersTypeAheadUrl, { include_added: "false" }, function(item){
|
||||
currentLayerAddSelection = item;
|
||||
layerAddBtn.removeAttr("disabled");
|
||||
});
|
||||
|
||||
layerAddInput.keyup(function() {
|
||||
if ($(this).val().length == 0) {
|
||||
layerAddBtn.attr("disabled", "disabled")
|
||||
}
|
||||
});
|
||||
|
||||
layerAddBtn.click(function(e){
|
||||
e.preventDefault();
|
||||
var layerObj = currentLayerAddSelection;
|
||||
|
||||
addRmLayer(layerObj, true);
|
||||
/* Reset the text input */
|
||||
layerAddInput.val("");
|
||||
/* Disable the add layer button*/
|
||||
layerAddBtn.attr("disabled", "disabled");
|
||||
});
|
||||
|
||||
function addRmLayer(layerObj, add){
|
||||
|
||||
libtoaster.addRmLayer(layerObj, add, function(layerDepsList){
|
||||
if (add){
|
||||
updateProjectLayers([layerObj]);
|
||||
updateProjectLayers(layerDepsList);
|
||||
}
|
||||
|
||||
/* Show the alert message */
|
||||
var message = libtoaster.makeLayerAddRmAlertMsg(layerObj, layerDepsList, add);
|
||||
libtoaster.showChangeNotification(message);
|
||||
});
|
||||
}
|
||||
|
||||
function updateProjectLayers(layers){
|
||||
|
||||
/* No layers to add */
|
||||
if (layers.length === 0){
|
||||
updateLayersCount();
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i in layers){
|
||||
var layerObj = layers[i];
|
||||
|
||||
var projectLayer = $("<li><a></a><span class=\"glyphicon glyphicon-trash\" data-toggle=\"tooltip\" title=\"Remove\"></span></li>");
|
||||
|
||||
projectLayer.data('layer', layerObj);
|
||||
projectLayer.children("span").tooltip();
|
||||
|
||||
var link = projectLayer.children("a");
|
||||
|
||||
link.attr("href", layerObj.layerdetailurl);
|
||||
link.text(layerObj.name);
|
||||
|
||||
if (layerObj.local_source_dir) {
|
||||
link.tooltip({title: layerObj.local_source_dir, placement: "right"});
|
||||
} else {
|
||||
link.tooltip({title: layerObj.vcs_url + " | "+ layerObj.vcs_reference, placement: "right"});
|
||||
}
|
||||
|
||||
var trashItem = projectLayer.children("span");
|
||||
trashItem.click(function (e) {
|
||||
e.preventDefault();
|
||||
var layerObjToRm = $(this).parent().data('layer');
|
||||
|
||||
addRmLayer(layerObjToRm, false);
|
||||
|
||||
$(this).parent().fadeOut(function (){
|
||||
$(this).remove();
|
||||
updateLayersCount();
|
||||
});
|
||||
});
|
||||
|
||||
layersInPrjList.append(projectLayer);
|
||||
|
||||
updateLayersCount();
|
||||
}
|
||||
}
|
||||
|
||||
function updateLayersCount(){
|
||||
var count = $("#layers-in-project-list").children().length;
|
||||
var noLayerMsg = $("#no-layers-in-project");
|
||||
var buildInput = $("#build-input");
|
||||
|
||||
|
||||
if (count === 0) {
|
||||
noLayerMsg.fadeIn();
|
||||
$("#no-layers-in-project").fadeIn();
|
||||
buildInput.attr("disabled", "disabled");
|
||||
} else {
|
||||
noLayerMsg.hide();
|
||||
buildInput.removeAttr("disabled");
|
||||
}
|
||||
|
||||
$("#project-layers-count").text(count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Frequent builds functionality */
|
||||
function updateFreqBuildRecipes(recipes) {
|
||||
var noMostBuilt = $("#no-most-built");
|
||||
|
||||
if (recipes.length === 0){
|
||||
noMostBuilt.show();
|
||||
freqBuildBtn.hide();
|
||||
} else {
|
||||
noMostBuilt.hide();
|
||||
freqBuildBtn.show();
|
||||
}
|
||||
|
||||
for (var i in recipes){
|
||||
var freqTargetCheck = $('<li><div class="checkbox"><label><input type="checkbox" /><span class="freq-target-name"></span></label></li>');
|
||||
freqTargetCheck.find(".freq-target-name").text(recipes[i]);
|
||||
freqTargetCheck.find("input").val(recipes[i]);
|
||||
freqTargetCheck.click(function(){
|
||||
if (freqBuildList.find(":checked").length > 0)
|
||||
freqBuildBtn.removeAttr("disabled");
|
||||
else
|
||||
freqBuildBtn.attr("disabled", "disabled");
|
||||
});
|
||||
|
||||
freqBuildList.append(freqTargetCheck);
|
||||
}
|
||||
}
|
||||
|
||||
freqBuildBtn.click(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
var toBuild = "";
|
||||
freqBuildList.find(":checked").each(function(){
|
||||
toBuild += $(this).val() + ' ';
|
||||
});
|
||||
|
||||
toBuild = toBuild.trim();
|
||||
|
||||
libtoaster.startABuild(null, toBuild,
|
||||
function(){
|
||||
/* Build request started */
|
||||
window.location.replace(libtoaster.ctx.projectBuildsUrl);
|
||||
},
|
||||
function(){
|
||||
/* Build request failed */
|
||||
console.warn("Build request failed to be created");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/* Change machine functionality */
|
||||
machineChangeInput.keyup(function(){
|
||||
if ($(this).val().indexOf(' ') >= 0) {
|
||||
machineChangeBtn.attr("disabled", "disabled");
|
||||
invalidMachineNameHelp.show();
|
||||
machineInputForm.addClass('has-error');
|
||||
} else {
|
||||
machineChangeBtn.removeAttr("disabled");
|
||||
invalidMachineNameHelp.hide();
|
||||
machineInputForm.removeClass('has-error');
|
||||
}
|
||||
});
|
||||
|
||||
machineChangeFormToggle.click(function(){
|
||||
machineChangeInput.val(machineNameTitle.text());
|
||||
machineChangeBtn.removeAttr("disabled");
|
||||
invalidMachineNameHelp.hide();
|
||||
machineInputForm.removeClass('has-error');
|
||||
machineForm.slideDown();
|
||||
machineNameTitle.hide();
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
machineChangeCancel.click(function(){
|
||||
machineForm.slideUp(function(){
|
||||
machineNameTitle.show();
|
||||
machineChangeFormToggle.show();
|
||||
});
|
||||
});
|
||||
|
||||
function updateMachineName(machineName){
|
||||
machineChangeInput.val(machineName);
|
||||
machineNameTitle.text(machineName);
|
||||
}
|
||||
|
||||
libtoaster.makeTypeahead(machineChangeInput,
|
||||
libtoaster.ctx.machinesTypeAheadUrl,
|
||||
{ }, function(item){
|
||||
currentMachineAddSelection = item.name;
|
||||
machineChangeBtn.removeAttr("disabled");
|
||||
});
|
||||
|
||||
machineChangeBtn.click(function(e){
|
||||
e.preventDefault();
|
||||
/* We accept any value regardless of typeahead selection or not */
|
||||
if (machineChangeInput.val().length === 0)
|
||||
return;
|
||||
|
||||
currentMachineAddSelection = machineChangeInput.val();
|
||||
|
||||
libtoaster.editCurrentProject(
|
||||
{ machineName : currentMachineAddSelection },
|
||||
function(){
|
||||
/* Success machine changed */
|
||||
updateMachineName(currentMachineAddSelection);
|
||||
machineChangeCancel.click();
|
||||
|
||||
/* Show the alert message */
|
||||
var message = $('<span>You have changed the machine to: <strong><span id="notify-machine-name"></span></strong></span>');
|
||||
message.find("#notify-machine-name").text(currentMachineAddSelection);
|
||||
libtoaster.showChangeNotification(message);
|
||||
},
|
||||
function(){
|
||||
/* Failed machine changed */
|
||||
console.warn("Failed to change machine");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/* Change distro functionality */
|
||||
|
||||
distroChangeFormToggle.click(function(){
|
||||
distroForm.slideDown();
|
||||
distroNameTitle.hide();
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
distroChangeCancel.click(function(){
|
||||
distroForm.slideUp(function(){
|
||||
distroNameTitle.show();
|
||||
distroChangeFormToggle.show();
|
||||
});
|
||||
});
|
||||
|
||||
function updateDistroName(distroName){
|
||||
distroChangeInput.val(distroName);
|
||||
distroNameTitle.text(distroName);
|
||||
}
|
||||
|
||||
libtoaster.makeTypeahead(distroChangeInput,
|
||||
libtoaster.ctx.distrosTypeAheadUrl,
|
||||
{ }, function(item){
|
||||
currentDistroAddSelection = item.name;
|
||||
distroChangeBtn.removeAttr("disabled");
|
||||
});
|
||||
|
||||
distroChangeBtn.click(function(e){
|
||||
e.preventDefault();
|
||||
/* We accept any value regardless of typeahead selection or not */
|
||||
if (distroChangeInput.val().length === 0)
|
||||
return;
|
||||
|
||||
currentDistroAddSelection = distroChangeInput.val();
|
||||
|
||||
libtoaster.editCurrentProject(
|
||||
{ distroName : currentDistroAddSelection },
|
||||
function(){
|
||||
/* Success machine changed */
|
||||
updateDistroName(currentDistroAddSelection);
|
||||
distroChangeCancel.click();
|
||||
|
||||
/* Show the alert message */
|
||||
var message = $('<span>You have changed the distro to: <strong><span id="notify-machine-name"></span></strong></span>');
|
||||
message.find("#notify-machine-name").text(currentDistroAddSelection);
|
||||
libtoaster.showChangeNotification(message);
|
||||
},
|
||||
function(){
|
||||
/* Failed machine changed */
|
||||
console.warn("Failed to change distro");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/* Change release functionality */
|
||||
function updateProjectRelease(release){
|
||||
releaseTitle.text(release.description);
|
||||
}
|
||||
|
||||
|
||||
$("#delete-project-confirmed").click(function(e){
|
||||
e.preventDefault();
|
||||
libtoaster.disableAjaxLoadingTimer();
|
||||
$(this).find('[data-role="submit-state"]').hide();
|
||||
$(this).find('[data-role="loading-state"]').show();
|
||||
$(this).attr("disabled", "disabled");
|
||||
$('#delete-project-modal [data-dismiss="modal"]').hide();
|
||||
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
url: libtoaster.ctx.xhrProjectUrl,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (data) {
|
||||
if (data.error !== "ok") {
|
||||
console.warn(data.error);
|
||||
} else {
|
||||
var msg = $('<span>You have deleted <strong>1</strong> project: <strong id="project-deleted"></strong></span>');
|
||||
|
||||
msg.find("#project-deleted").text(libtoaster.ctx.projectName);
|
||||
libtoaster.setNotification("project-deleted", msg.html());
|
||||
|
||||
window.location.replace(data.gotoUrl);
|
||||
}
|
||||
},
|
||||
error: function (data) {
|
||||
console.warn(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
'use strict';
|
||||
|
||||
function projectTopBarInit(ctx) {
|
||||
|
||||
var projectNameForm = $("#project-name-change-form");
|
||||
var projectNameContainer = $("#project-name-container");
|
||||
var projectName = $(".project-name");
|
||||
var projectNameFormToggle = $("#project-change-form-toggle");
|
||||
var projectNameChangeCancel = $("#project-name-change-cancel");
|
||||
|
||||
// this doesn't exist for command-line builds
|
||||
var newBuildTargetInput = $("#build-input");
|
||||
|
||||
var newBuildTargetBuildBtn = $("#build-button");
|
||||
var selectedTarget;
|
||||
|
||||
var updateProjectBtn = $("#update-project-button");
|
||||
var cancelProjectBtn = $("#cancel-project-button");
|
||||
|
||||
/* Project name change functionality */
|
||||
projectNameFormToggle.click(function(e){
|
||||
e.preventDefault();
|
||||
projectNameContainer.hide();
|
||||
projectNameForm.fadeIn();
|
||||
});
|
||||
|
||||
projectNameChangeCancel.click(function(e){
|
||||
e.preventDefault();
|
||||
projectNameForm.hide();
|
||||
projectNameContainer.fadeIn();
|
||||
$("#project-name-change-input").val(projectName.first().text());
|
||||
});
|
||||
|
||||
$("#project-name-change-btn").click(function(){
|
||||
var newProjectName = $("#project-name-change-input").val();
|
||||
|
||||
libtoaster.editCurrentProject({ projectName: newProjectName }, function (){
|
||||
projectName.text(newProjectName);
|
||||
libtoaster.ctx.projectName = newProjectName;
|
||||
projectNameChangeCancel.click();
|
||||
});
|
||||
});
|
||||
|
||||
/* Nav bar activate state switcher */
|
||||
$("#project-topbar .nav li a").each(function(){
|
||||
if (window.location.pathname === $(this).attr('href'))
|
||||
$(this).parent().addClass('active');
|
||||
else
|
||||
$(this).parent().removeClass('active');
|
||||
});
|
||||
|
||||
if (!newBuildTargetInput.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* the following only applies for non-command-line projects */
|
||||
|
||||
/* Recipe build input functionality */
|
||||
if (ctx.numProjectLayers > 0 && ctx.machine){
|
||||
newBuildTargetInput.removeAttr("disabled");
|
||||
}
|
||||
|
||||
libtoaster.makeTypeahead(newBuildTargetInput,
|
||||
libtoaster.ctx.recipesTypeAheadUrl, {}, function (item) {
|
||||
selectedTarget = item;
|
||||
newBuildTargetBuildBtn.removeAttr("disabled");
|
||||
});
|
||||
|
||||
newBuildTargetInput.on('input', function () {
|
||||
if ($(this).val().length === 0) {
|
||||
newBuildTargetBuildBtn.attr("disabled", "disabled");
|
||||
} else {
|
||||
newBuildTargetBuildBtn.removeAttr("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
newBuildTargetBuildBtn.click(function (e) {
|
||||
e.preventDefault();
|
||||
if (!newBuildTargetInput.val().trim()) {
|
||||
return;
|
||||
}
|
||||
/* We use the value of the input field so as to maintain any command also
|
||||
* added e.g. core-image-minimal:clean and because we can build targets
|
||||
* that toaster doesn't yet know about
|
||||
*/
|
||||
selectedTarget = { name: newBuildTargetInput.val().trim() };
|
||||
|
||||
/* Fire off the build */
|
||||
libtoaster.startABuild(null, selectedTarget.name,
|
||||
function(){
|
||||
window.location.replace(libtoaster.ctx.projectBuildsUrl);
|
||||
}, null);
|
||||
});
|
||||
|
||||
updateProjectBtn.click(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
selectedTarget = { name: "_PROJECT_PREPARE_" };
|
||||
|
||||
/* Save current default build image, fire off the build */
|
||||
libtoaster.updateProject(null, selectedTarget.name, newBuildTargetInput.val().trim(),
|
||||
function(){
|
||||
window.location.replace(libtoaster.ctx.projectSpecificPageUrl);
|
||||
}, null);
|
||||
});
|
||||
|
||||
cancelProjectBtn.click(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
/* redirect to 'done/canceled' landing page */
|
||||
window.location.replace(libtoaster.ctx.landingSpecificCancelURL);
|
||||
});
|
||||
|
||||
/* Call makeProjectNameValidation function */
|
||||
libtoaster.makeProjectNameValidation($("#project-name-change-input"),
|
||||
$("#hint-error-project-name"), $("#validate-project-name"),
|
||||
$("#project-name-change-btn"));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
(function(window){var QUnit,config,onErrorFnPrev,loggingCallbacks={},fileName=(sourceFromStacktrace(0)||"").replace(/(:\d+)+\)?/,"").replace(/.+\//,""),toString=Object.prototype.toString,hasOwn=Object.prototype.hasOwnProperty,Date=window.Date,now=Date.now||function(){return new Date().getTime();},globalStartCalled=false,runStarted=false,setTimeout=window.setTimeout,clearTimeout=window.clearTimeout,defined={document:window.document!==undefined,setTimeout:window.setTimeout!==undefined,sessionStorage:(function(){var x="qunit-test-string";try{sessionStorage.setItem(x,x);sessionStorage.removeItem(x);return true;}catch(e){return false;}}())},errorString=function(error){var name,message,errorString=error.toString();if(errorString.substring(0,7)==="[object"){name=error.name?error.name.toString():"Error";message=error.message?error.message.toString():"";if(name&&message){return name+": "+message;}else if(name){return name;}else if(message){return message;}else{return "Error";}}else{return errorString;}},objectValues =function(obj){var key,val,vals=QUnit.is("array",obj)?[]:{};for(key in obj){if(hasOwn.call(obj,key)){val=obj[key];vals[key]=val===Object(val)?objectValues(val):val;}}
|
||||
return vals;};QUnit={};config={queue:[],blocking:true,reorder:true,altertitle:true,scrolltop:true,requireExpects:false,maxDepth:5,urlConfig:[{id:"hidepassed",label:"Hide passed tests",tooltip:"Only show tests and assertions that fail. Stored as query-strings."},{id:"noglobals",label:"Check for Globals",tooltip:"Enabling this will test if any test introduces new properties on the "+
|
||||
"`window` object. Stored as query-strings."},{id:"notrycatch",label:"No try-catch",tooltip:"Enabling this will run tests outside of a try-catch block. Makes debugging "+
|
||||
"exceptions in IE reasonable. Stored as query-strings."}],modules:[],currentModule:{name:"",tests:[]},callbacks:{}};config.modules.push(config.currentModule);(function(){var i,current,location=window.location||{search:"",protocol:"file:"},params=location.search.slice(1).split("&"),length=params.length,urlParams={};if(params[0]){for(i=0;i<length;i++){current=params[i].split("=");current[0]=decodeURIComponent(current[0]);current[1]=current[1]?decodeURIComponent(current[1]):true;if(urlParams[current[0]]){urlParams[current[0]]=[].concat(urlParams[current[0]],current[1]);}else{urlParams[current[0]]=current[1];}}}
|
||||
if(urlParams.filter===true){delete urlParams.filter;}
|
||||
QUnit.urlParams=urlParams;config.filter=urlParams.filter;if(urlParams.maxDepth){config.maxDepth=parseInt(urlParams.maxDepth,10)===-1?Number.POSITIVE_INFINITY:urlParams.maxDepth;}
|
||||
config.testId=[];if(urlParams.testId){urlParams.testId=decodeURIComponent(urlParams.testId).split(",");for(i=0;i<urlParams.testId.length;i++){config.testId.push(urlParams.testId[i]);}}
|
||||
QUnit.isLocal=location.protocol==="file:";QUnit.version="1.18.0";}());extend(QUnit,{module:function(name,testEnvironment){var currentModule={name:name,testEnvironment:testEnvironment,tests:[]};if(testEnvironment&&testEnvironment.setup){testEnvironment.beforeEach=testEnvironment.setup;delete testEnvironment.setup;}
|
||||
if(testEnvironment&&testEnvironment.teardown){testEnvironment.afterEach=testEnvironment.teardown;delete testEnvironment.teardown;}
|
||||
config.modules.push(currentModule);config.currentModule=currentModule;},asyncTest:function(testName,expected,callback){if(arguments.length===2){callback=expected;expected=null;}
|
||||
QUnit.test(testName,expected,callback,true);},test:function(testName,expected,callback,async){var test;if(arguments.length===2){callback=expected;expected=null;}
|
||||
test=new Test({testName:testName,expected:expected,async:async,callback:callback});test.queue();},skip:function(testName){var test=new Test({testName:testName,skip:true});test.queue();},//
|
||||
start:function(count){var globalStartAlreadyCalled=globalStartCalled;if(!config.current){globalStartCalled=true;if(runStarted){throw new Error("Called start() outside of a test context while already started");}else if(globalStartAlreadyCalled||count>1){throw new Error("Called start() outside of a test context too many times");}else if(config.autostart){throw new Error("Called start() outside of a test context when "+
|
||||
"QUnit.config.autostart was true");}else if(!config.pageLoaded){config.autostart=true;return;}}else{config.current.semaphore-=count||1;if(config.current.semaphore>0){return;}
|
||||
if(config.current.semaphore<0){config.current.semaphore=0;QUnit.pushFailure("Called start() while already started (test's semaphore was 0 already)",sourceFromStacktrace(2));return;}}
|
||||
resumeProcessing();},stop:function(count){if(!config.current){throw new Error("Called stop() outside of a test context");}
|
||||
config.current.semaphore+=count||1;pauseProcessing();},config:config,is:function(type,obj){return QUnit.objectType(obj)===type;},objectType:function(obj){if(typeof obj==="undefined"){return "undefined";}//
|
||||
if(obj===null){return "null";}
|
||||
var match=toString.call(obj).match(/^\[object\s(.*)\]$/),type=match&&match[1]||"";switch(type){case "Number":if(isNaN(obj)){return "nan";}
|
||||
return "number";case "String":case "Boolean":case "Array":case "Date":case "RegExp":case "Function":return type.toLowerCase();}
|
||||
if(typeof obj==="object"){return "object";}
|
||||
return undefined;},extend:extend,load:function(){config.pageLoaded=true;extend(config,{stats:{all:0,bad:0},moduleStats:{all:0,bad:0},started:0,updateRate:1000,autostart:true,filter:""},true);config.blocking=false;if(config.autostart){resumeProcessing();}}});(function(){var i,l,key,callbacks=["begin","done","log","testStart","testDone","moduleStart","moduleDone"];function registerLoggingCallback(key){var loggingCallback=function(callback){if(QUnit.objectType(callback)!=="function"){throw new Error("QUnit logging methods require a callback function as their first parameters.");}
|
||||
config.callbacks[key].push(callback);};loggingCallbacks[key]=loggingCallback;return loggingCallback;}
|
||||
for(i=0,l=callbacks.length;i<l;i++){key=callbacks[i];if(QUnit.objectType(config.callbacks[key])==="undefined"){config.callbacks[key]=[];}
|
||||
QUnit[key]=registerLoggingCallback(key);}})();onErrorFnPrev=window.onerror;//
|
||||
window.onerror=function(error,filePath,linerNr){var ret=false;if(onErrorFnPrev){ret=onErrorFnPrev(error,filePath,linerNr);}
|
||||
if(ret!==true){if(QUnit.config.current){if(QUnit.config.current.ignoreGlobalErrors){return true;}
|
||||
QUnit.pushFailure(error,filePath+":"+linerNr);}else{QUnit.test("global failure",extend(function(){QUnit.pushFailure(error,filePath+":"+linerNr);},{validTest:true}));}
|
||||
return false;}
|
||||
return ret;};function done(){var runtime,passed;config.autorun=true;if(config.previousModule){runLoggingCallbacks("moduleDone",{name:config.previousModule.name,tests:config.previousModule.tests,failed:config.moduleStats.bad,passed:config.moduleStats.all-config.moduleStats.bad,total:config.moduleStats.all,runtime:now()-config.moduleStats.started});}
|
||||
delete config.previousModule;runtime=now()-config.started;passed=config.stats.all-config.stats.bad;runLoggingCallbacks("done",{failed:config.stats.bad,passed:passed,total:config.stats.all,runtime:runtime});}
|
||||
function extractStacktrace(e,offset){offset=offset===undefined?4:offset;var stack,include,i;if(e.stack){stack=e.stack.split("\n");if(/^error$/i.test(stack[0])){stack.shift();}
|
||||
if(fileName){include=[];for(i=offset;i<stack.length;i++){if(stack[i].indexOf(fileName)!==-1){break;}
|
||||
include.push(stack[i]);}
|
||||
if(include.length){return include.join("\n");}}
|
||||
return stack[offset];//
|
||||
}else if(e.sourceURL){if(/qunit.js$/.test(e.sourceURL)){return;}
|
||||
return e.sourceURL+":"+e.line;}}
|
||||
function sourceFromStacktrace(offset){var error=new Error();if(!error.stack){try{throw error;}catch(err){error=err;}}
|
||||
return extractStacktrace(error,offset);}
|
||||
function synchronize(callback,last){if(QUnit.objectType(callback)==="array"){while(callback.length){synchronize(callback.shift());}
|
||||
return;}
|
||||
config.queue.push(callback);if(config.autorun&&!config.blocking){process(last);}}
|
||||
function process(last){function next(){process(last);}
|
||||
var start=now();config.depth=(config.depth||0)+1;while(config.queue.length&&!config.blocking){if(!defined.setTimeout||config.updateRate<=0||((now()-start)<config.updateRate)){if(config.current){config.current.usedAsync=false;}
|
||||
config.queue.shift()();}else{setTimeout(next,13);break;}}
|
||||
config.depth--;if(last&&!config.blocking&&!config.queue.length&&config.depth===0){done();}}
|
||||
function begin(){var i,l,modulesLog=[];if(!config.started){config.started=now();verifyLoggingCallbacks();if(config.modules[0].name===""&&config.modules[0].tests.length===0){config.modules.shift();}
|
||||
for(i=0,l=config.modules.length;i<l;i++){modulesLog.push({name:config.modules[i].name,tests:config.modules[i].tests});}
|
||||
runLoggingCallbacks("begin",{totalTests:Test.count,modules:modulesLog});}
|
||||
config.blocking=false;process(true);}
|
||||
function resumeProcessing(){runStarted=true;if(defined.setTimeout){setTimeout(function(){if(config.current&&config.current.semaphore>0){return;}
|
||||
if(config.timeout){clearTimeout(config.timeout);}
|
||||
begin();},13);}else{begin();}}
|
||||
function pauseProcessing(){config.blocking=true;if(config.testTimeout&&defined.setTimeout){clearTimeout(config.timeout);config.timeout=setTimeout(function(){if(config.current){config.current.semaphore=0;QUnit.pushFailure("Test timed out",sourceFromStacktrace(2));}else{throw new Error("Test timed out");}
|
||||
resumeProcessing();},config.testTimeout);}}
|
||||
function saveGlobal(){config.pollution=[];if (config.noglobals){for(var key in window){if(hasOwn.call(window,key)){if(/^qunit-test-output/.test(key)){continue;}
|
||||
config.pollution.push(key);}}}}
|
||||
function checkPollution(){var newGlobals,deletedGlobals,old=config.pollution;saveGlobal();newGlobals=diff(config.pollution,old);if(newGlobals.length>0){QUnit.pushFailure("Introduced global variable(s): "+newGlobals.join(", "));}
|
||||
deletedGlobals=diff(old,config.pollution);if(deletedGlobals.length>0){QUnit.pushFailure("Deleted global variable(s): "+deletedGlobals.join(", "));}}
|
||||
function diff(a,b){var i,j,result=a.slice();for(i=0;i<result.length;i++){for(j=0;j<b.length;j++){if(result[i]===b[j]){result.splice(i,1);i--;break;}}}
|
||||
return result;}
|
||||
function extend(a,b,undefOnly){for(var prop in b){if(hasOwn.call(b,prop)){if(!(prop==="constructor"&&a===window)){if(b[prop]===undefined){delete a[prop];}else if(!(undefOnly&&typeof a[prop]!=="undefined")){a[prop]=b[prop];}}}}
|
||||
return a;}
|
||||
function runLoggingCallbacks(key,args){var i,l,callbacks;callbacks=config.callbacks[key];for(i=0,l=callbacks.length;i<l;i++){callbacks[i](args);}}
|
||||
function verifyLoggingCallbacks(){var loggingCallback,userCallback;for(loggingCallback in loggingCallbacks){if(QUnit[loggingCallback]!==loggingCallbacks[loggingCallback]){userCallback=QUnit[loggingCallback];QUnit[loggingCallback]=loggingCallbacks[loggingCallback];QUnit[loggingCallback](userCallback);if(window.console&&window.console.warn){window.console.warn("QUnit."+loggingCallback+" was replaced with a new value.\n"+
|
||||
"Please, check out the documentation on how to apply logging callbacks.\n"+
|
||||
"Reference: http://api.qunitjs.com/category/callbacks/");}}}}
|
||||
function inArray(elem,array){if(array.indexOf){return array.indexOf(elem);}
|
||||
for(var i=0,length=array.length;i<length;i++){if(array[i]===elem){return i;}}
|
||||
return-1;}
|
||||
function Test(settings){var i,l;++Test.count;extend(this,settings);this.assertions=[];this.semaphore=0;this.usedAsync=false;this.module=config.currentModule;this.stack=sourceFromStacktrace(3);for(i=0,l=this.module.tests;i<l.length;i++){if(this.module.tests[i].name===this.testName){this.testName+=" ";}}
|
||||
this.testId=generateHash(this.module.name,this.testName);this.module.tests.push({name:this.testName,testId:this.testId});if(settings.skip){this.callback=function(){};this.async=false;this.expected=0;}else{this.assert=new Assert(this);}}
|
||||
Test.count=0;Test.prototype={before:function(){if(this.module!==config.previousModule||//
|
||||
!hasOwn.call(config,"previousModule")){if(hasOwn.call(config,"previousModule")){runLoggingCallbacks("moduleDone",{name:config.previousModule.name,tests:config.previousModule.tests,failed:config.moduleStats.bad,passed:config.moduleStats.all-config.moduleStats.bad,total:config.moduleStats.all,runtime:now()-config.moduleStats.started});}
|
||||
config.previousModule=this.module;config.moduleStats={all:0,bad:0,started:now()};runLoggingCallbacks("moduleStart",{name:this.module.name,tests:this.module.tests});}
|
||||
config.current=this;this.testEnvironment=extend({},this.module.testEnvironment);delete this.testEnvironment.beforeEach;delete this.testEnvironment.afterEach;this.started=now();runLoggingCallbacks("testStart",{name:this.testName,module:this.module.name,testId:this.testId});if(!config.pollution){saveGlobal();}},run:function(){var promise;config.current=this;if(this.async){QUnit.stop();}
|
||||
this.callbackStarted=now();if(config.notrycatch){promise=this.callback.call(this.testEnvironment,this.assert);this.resolvePromise(promise);return;}
|
||||
try{promise=this.callback.call(this.testEnvironment,this.assert);this.resolvePromise(promise);}catch(e){this.pushFailure("Died on test #"+(this.assertions.length+1)+" "+
|
||||
this.stack+": "+(e.message||e),extractStacktrace(e,0));saveGlobal();if(config.blocking){QUnit.start();}}},after:function(){checkPollution();},queueHook:function(hook,hookName){var promise,test=this;return function runHook(){config.current=test;if(config.notrycatch){promise=hook.call(test.testEnvironment,test.assert);test.resolvePromise(promise,hookName);return;}
|
||||
try{promise=hook.call(test.testEnvironment,test.assert);test.resolvePromise(promise,hookName);}catch(error){test.pushFailure(hookName+" failed on "+test.testName+": "+(error.message||error),extractStacktrace(error,0));}};},hooks:function(handler){var hooks=[];if(this.skip){return hooks;}
|
||||
if(this.module.testEnvironment&&QUnit.objectType(this.module.testEnvironment[handler])==="function"){hooks.push(this.queueHook(this.module.testEnvironment[handler],handler));}
|
||||
return hooks;},finish:function(){config.current=this;if(config.requireExpects&&this.expected===null){this.pushFailure("Expected number of assertions to be defined, but expect() was "+
|
||||
"not called.",this.stack);}else if(this.expected!==null&&this.expected!==this.assertions.length){this.pushFailure("Expected "+this.expected+" assertions, but "+
|
||||
this.assertions.length+" were run",this.stack);}else if(this.expected===null&&!this.assertions.length){this.pushFailure("Expected at least one assertion, but none were run - call "+
|
||||
"expect(0) to accept zero assertions.",this.stack);}
|
||||
var i,bad=0;this.runtime=now()-this.started;config.stats.all+=this.assertions.length;config.moduleStats.all+=this.assertions.length;for(i=0;i<this.assertions.length;i++){if(!this.assertions[i].result){bad++;config.stats.bad++;config.moduleStats.bad++;}}
|
||||
runLoggingCallbacks("testDone",{name:this.testName,module:this.module.name,skipped:!!this.skip,failed:bad,passed:this.assertions.length-bad,total:this.assertions.length,runtime:this.runtime,assertions:this.assertions,testId:this.testId,duration:this.runtime});QUnit.reset();config.current=undefined;},queue:function(){var bad,test=this;if(!this.valid()){return;}
|
||||
function run(){synchronize([function(){test.before();},test.hooks("beforeEach"),function(){test.run();},test.hooks("afterEach").reverse(),function(){test.after();},function(){test.finish();}]);}
|
||||
bad=QUnit.config.reorder&&defined.sessionStorage&&+sessionStorage.getItem("qunit-test-"+this.module.name+"-"+this.testName);if(bad){run();}else{synchronize(run,true);}},push:function(result,actual,expected,message){var source,details={module:this.module.name,name:this.testName,result:result,message:message,actual:actual,expected:expected,testId:this.testId,runtime:now()-this.started};if(!result){source=sourceFromStacktrace();if(source){details.source=source;}}
|
||||
runLoggingCallbacks("log",details);this.assertions.push({result:!!result,message:message});},pushFailure:function(message,source,actual){if(!this instanceof Test){throw new Error("pushFailure() assertion outside test context, was "+
|
||||
sourceFromStacktrace(2));}
|
||||
var details={module:this.module.name,name:this.testName,result:false,message:message||"error",actual:actual||null,testId:this.testId,runtime:now()-this.started};if(source){details.source=source;}
|
||||
runLoggingCallbacks("log",details);this.assertions.push({result:false,message:message});},resolvePromise:function(promise,phase){var then,message,test=this;if(promise!=null){then=promise.then;if(QUnit.objectType(then)==="function"){QUnit.stop();then.call(promise,QUnit.start,function(error){message="Promise rejected "+(!phase?"during":phase.replace(/Each$/,""))+
|
||||
" "+test.testName+": "+(error.message||error);test.pushFailure(message,extractStacktrace(error,0));saveGlobal();QUnit.start();});}}},valid:function(){var include,filter=config.filter&&config.filter.toLowerCase(),module=QUnit.urlParams.module&&QUnit.urlParams.module.toLowerCase(),fullName=(this.module.name+": "+this.testName).toLowerCase();if(this.callback&&this.callback.validTest){return true;}
|
||||
if(config.testId.length>0&&inArray(this.testId,config.testId)<0){return false;}
|
||||
if(module&&(!this.module.name||this.module.name.toLowerCase()!==module)){return false;}
|
||||
if(!filter){return true;}
|
||||
include=filter.charAt(0)!=="!";if(!include){filter=filter.slice(1);}
|
||||
if(fullName.indexOf(filter)!==-1){return include;}
|
||||
return!include;}};QUnit.reset=function(){if(typeof window==="undefined"){return;}
|
||||
var fixture=defined.document&&document.getElementById&&document.getElementById("qunit-fixture");if(fixture){fixture.innerHTML=config.fixture;}};QUnit.pushFailure=function(){if(!QUnit.config.current){throw new Error("pushFailure() assertion outside test context, in "+
|
||||
sourceFromStacktrace(2));}
|
||||
var currentTest=QUnit.config.current;return currentTest.pushFailure.apply(currentTest,arguments);};function generateHash(module,testName){var hex,i=0,hash=0,str=module+"\x1C"+testName,len=str.length;for(;i<len;i++){hash=((hash<<5)-hash)+str.charCodeAt(i);hash|=0;}
|
||||
hex=(0x100000000+hash).toString(16);if(hex.length<8){hex="0000000"+hex;}
|
||||
return hex.slice(-8);}
|
||||
function Assert(testContext){this.test=testContext;}
|
||||
QUnit.assert=Assert.prototype={expect:function(asserts){if(arguments.length===1){this.test.expected=asserts;}else{return this.test.expected;}},async:function(){var test=this.test,popped=false;test.semaphore+=1;test.usedAsync=true;pauseProcessing();return function done(){if(!popped){test.semaphore-=1;popped=true;resumeProcessing();}else{test.pushFailure("Called the callback returned from `assert.async` more than once",sourceFromStacktrace(2));}};},push:function(){var assert=this,currentTest=(assert instanceof Assert&&assert.test)||QUnit.config.current;if(!currentTest){throw new Error("assertion outside test context, in "+sourceFromStacktrace(2));}
|
||||
if(currentTest.usedAsync===true&¤tTest.semaphore===0){currentTest.pushFailure("Assertion after the final `assert.async` was resolved",sourceFromStacktrace(2));}
|
||||
if(!(assert instanceof Assert)){assert=currentTest.assert;}
|
||||
return assert.test.push.apply(assert.test,arguments);},ok:function(result,message){message=message||(result?"okay":"failed, expected argument to be truthy, was: "+
|
||||
QUnit.dump.parse(result));this.push(!!result,result,true,message);},notOk:function(result,message){message=message||(!result?"okay":"failed, expected argument to be falsy, was: "+
|
||||
QUnit.dump.parse(result));this.push(!result,result,false,message);},equal:function(actual,expected,message){this.push(expected==actual,actual,expected,message);},notEqual:function(actual,expected,message){this.push(expected!=actual,actual,expected,message);},propEqual:function(actual,expected,message){actual=objectValues(actual);expected=objectValues(expected);this.push(QUnit.equiv(actual,expected),actual,expected,message);},notPropEqual:function(actual,expected,message){actual=objectValues(actual);expected=objectValues(expected);this.push(!QUnit.equiv(actual,expected),actual,expected,message);},deepEqual:function(actual,expected,message){this.push(QUnit.equiv(actual,expected),actual,expected,message);},notDeepEqual:function(actual,expected,message){this.push(!QUnit.equiv(actual,expected),actual,expected,message);},strictEqual:function(actual,expected,message){this.push(expected===actual,actual,expected,message);},notStrictEqual:function(actual,expected,message){this.push(expected!==actual,actual,expected,message);},"throws":function(block,expected,message){var actual,expectedType,expectedOutput=expected,ok=false,currentTest=(this instanceof Assert&&this.test)||QUnit.config.current;if(message==null&&typeof expected==="string"){message=expected;expected=null;}
|
||||
currentTest.ignoreGlobalErrors=true;try{block.call(currentTest.testEnvironment);}catch(e){actual=e;}
|
||||
currentTest.ignoreGlobalErrors=false;if(actual){expectedType=QUnit.objectType(expected);if(!expected){ok=true;expectedOutput=null;}else if(expectedType==="regexp"){ok=expected.test(errorString(actual));}else if(expectedType==="string"){ok=expected===errorString(actual);}else if(expectedType==="function"&&actual instanceof expected){ok=true;}else if(expectedType==="object"){ok=actual instanceof expected.constructor&&actual.name===expected.name&&actual.message===expected.message;}else if(expectedType==="function"&&expected.call({},actual)===true){expectedOutput= null;ok=true;}}
|
||||
currentTest.assert.push(ok,actual,expectedOutput,message);}};(function(){Assert.prototype.raises=Assert.prototype["throws"];}());QUnit.equiv=(function(){function bindCallbacks(o,callbacks,args){var prop=QUnit.objectType(o);if(prop){if(QUnit.objectType(callbacks[prop])==="function"){return callbacks[prop].apply(callbacks,args);}else{return callbacks[prop];}}}
|
||||
var innerEquiv,callers=[],parents=[],parentsB=[],getProto=Object.getPrototypeOf||function(obj){return obj.__proto__;},callbacks=(function(){function useStrictEquality(b,a){if(b instanceof a.constructor||a instanceof b.constructor){return a==b;}else{return a===b;}}
|
||||
return{"string":useStrictEquality,"boolean":useStrictEquality,"number":useStrictEquality,"null":useStrictEquality,"undefined":useStrictEquality,"nan":function(b){return isNaN(b);},"date":function(b,a){return QUnit.objectType(b)==="date"&&a.valueOf()===b.valueOf();},"regexp":function(b,a){return QUnit.objectType(b)==="regexp"&&a.source===b.source&&a.global===b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline&&a.sticky===b.sticky;},"function":function(){var caller=callers[callers.length-1];return caller!==Object&&typeof caller!=="undefined";},"array":function(b,a){var i,j,len,loop,aCircular,bCircular;if(QUnit.objectType(b)!=="array"){return false;}
|
||||
len=a.length;if(len!==b.length){return false;}
|
||||
parents.push(a);parentsB.push(b);for(i=0;i<len;i++){loop=false;for(j=0;j<parents.length;j++){aCircular=parents[j]===a[i];bCircular=parentsB[j]===b[i];if(aCircular||bCircular){ if(a[i]===b[i]||aCircular&&bCircular){loop=true;}else{parents.pop();parentsB.pop();return false;}}}
|
||||
if(!loop&&!innerEquiv(a[i],b[i])){parents.pop();parentsB.pop();return false;}}
|
||||
parents.pop();parentsB.pop();return true;},"object":function(b,a){var i,j,loop,aCircular,bCircular,eq=true,aProperties=[],bProperties=[];if(a.constructor!==b.constructor){if(!((getProto(a)===null&&getProto(b)===Object.prototype)||(getProto(b)===null&&getProto(a)===Object.prototype))){return false;}}
|
||||
callers.push(a.constructor);parents.push(a);parentsB.push(b);for(i in a){loop=false;for(j=0;j<parents.length;j++){aCircular=parents[j]===a[i];bCircular=parentsB[j]===b[i];if(aCircular||bCircular){if(a[i]===b[i]||aCircular&&bCircular){loop=true;}else{eq=false;break;}}}
|
||||
aProperties.push(i);if(!loop&&!innerEquiv(a[i],b[i])){eq=false;break;}}
|
||||
parents.pop();parentsB.pop();callers.pop();for(i in b){bProperties.push(i);}
|
||||
return eq&&innerEquiv(aProperties.sort(),bProperties.sort());}};}());innerEquiv=function(){var args=[].slice.apply(arguments);if(args.length<2){return true;}
|
||||
return((function(a,b){if(a===b){return true;}else if(a===null||b===null||typeof a==="undefined"||typeof b==="undefined"||QUnit.objectType(a)!==QUnit.objectType(b)){return false;}else{return bindCallbacks(a,callbacks,[b,a]);}}(args[0],args[1]))&&innerEquiv.apply(this,args.splice(1,args.length-1)));};return innerEquiv;}());QUnit.dump=(function(){function quote(str){return "\""+str.toString().replace(/"/g,"\\\"")+"\"";}
|
||||
function literal(o){return o+"";}
|
||||
function join(pre,arr,post){var s=dump.separator(),base=dump.indent(),inner=dump.indent(1);if(arr.join){arr=arr.join(","+s+inner);}
|
||||
if(!arr){return pre+post;}
|
||||
return[pre,inner+arr,base+post].join(s);}
|
||||
function array(arr,stack){var i=arr.length,ret=new Array(i);if(dump.maxDepth&&dump.depth>dump.maxDepth){return "[object Array]";}
|
||||
this.up();while(i--){ret[i]=this.parse(arr[i],undefined,stack);}
|
||||
this.down();return join("[",ret,"]");}
|
||||
var reName=/^function (\w+)/,dump={parse:function(obj,objType,stack){stack=stack||[];var res,parser,parserType,inStack=inArray(obj,stack);if(inStack!==-1){return "recursion("+(inStack-stack.length)+")";}
|
||||
objType=objType||this.typeOf(obj);parser=this.parsers[objType];parserType=typeof parser;if(parserType==="function"){stack.push(obj);res=parser.call(this,obj,stack);stack.pop();return res;}
|
||||
return(parserType==="string")?parser:this.parsers.error;},typeOf:function(obj){var type;if(obj===null){type="null";}else if(typeof obj==="undefined"){type="undefined";}else if(QUnit.is("regexp",obj)){type="regexp";}else if(QUnit.is("date",obj)){type="date";}else if(QUnit.is("function",obj)){type="function";}else if(obj.setInterval!==undefined&&obj.document!==undefined&&obj.nodeType===undefined){type="window";}else if(obj.nodeType===9){type="document";}else if(obj.nodeType){type="node";}else if(toString.call(obj)==="[object Array]"||(typeof obj.length==="number"&&obj.item!==undefined&&(obj.length?obj.item(0)===obj[ 0]:(obj.item(0)===null&&obj[0]===undefined)))){type="array";}else if(obj.constructor===Error.prototype.constructor){type="error";}else{type=typeof obj;}
|
||||
return type;},separator:function(){return this.multiline?this.HTML?"<br />":"\n":this.HTML?" ":" ";},indent:function(extra){if(!this.multiline){return "";}
|
||||
var chr=this.indentChar;if(this.HTML){chr=chr.replace(/\t/g," ").replace(/ /g," ");}
|
||||
return new Array(this.depth+(extra||0)).join(chr);},up:function(a){this.depth+=a||1;},down:function(a){this.depth-=a||1;},setParser:function(name,parser){this.parsers[name]=parser;},quote:quote,literal:literal,join:join,depth:1,maxDepth:QUnit.config.maxDepth,parsers:{window:"[Window]",document:"[Document]",error:function(error){return "Error(\""+error.message+"\")";},unknown:"[Unknown]","null":"null","undefined":"undefined","function":function(fn){var ret="function",name="name" in fn?fn.name:(reName.exec(fn)||[])[1];if(name){ret+=" "+name;}
|
||||
ret+="( ";ret=[ret,dump.parse(fn,"functionArgs"),"){"].join("");return join(ret,dump.parse(fn,"functionCode"),"}");},array:array,nodelist:array,"arguments":array,object:function(map,stack){var keys,key,val,i,nonEnumerableProperties,ret=[];if(dump.maxDepth&&dump.depth>dump.maxDepth){return "[object Object]";}
|
||||
dump.up(); keys=[];for(key in map){keys.push(key);}
|
||||
nonEnumerableProperties=["message","name"];for(i in nonEnumerableProperties){key=nonEnumerableProperties[i];if(key in map&&inArray(key,keys)<0){keys.push(key);}}
|
||||
keys.sort();for(i=0;i<keys.length;i++){key=keys[i];val=map[key];ret.push(dump.parse(key,"key")+": "+
|
||||
dump.parse(val,undefined,stack));}
|
||||
dump.down();return join("{",ret,"}");},node:function(node){var len,i,val,open=dump.HTML?"<":"<",close=dump.HTML?">":">",tag=node.nodeName.toLowerCase(),ret=open+tag,attrs=node.attributes;if(attrs){for(i=0,len=attrs.length;i<len;i++){val=attrs[i].nodeValue;if(val&&val!=="inherit"){ret+=" "+attrs[i].nodeName+"="+
|
||||
dump.parse(val,"attribute");}}}
|
||||
ret+=close;if(node.nodeType===3||node.nodeType===4){ret+=node.nodeValue;}
|
||||
return ret+open+"/"+tag+close;},functionArgs:function(fn){var args,l=fn.length;if(!l){return "";}
|
||||
args=new Array(l);while(l--){args[l]=String.fromCharCode(97+l);}
|
||||
return " "+args.join(", ")+" ";},key:quote,functionCode:"[code]",attribute:quote,string:quote,date:quote,regexp:literal,number:literal,"boolean":literal},HTML:false,indentChar:" ",multiline:true};return dump;}());QUnit.jsDump=QUnit.dump;if(typeof window!=="undefined"){(function(){var i,assertions=Assert.prototype;function applyCurrent(current){return function(){var assert=new Assert(QUnit.config.current);current.apply(assert,arguments);};}
|
||||
for(i in assertions){QUnit[i]=applyCurrent(assertions[i]);}})();(function(){var i,l,keys=["test","module","expect","asyncTest","start","stop","ok","notOk","equal","notEqual","propEqual","notPropEqual","deepEqual","notDeepEqual","strictEqual","notStrictEqual","throws"];for(i=0,l=keys.length;i<l;i++){window[keys[i]]=QUnit[keys[i]];}})();window.QUnit=QUnit;}
|
||||
if(typeof module!=="undefined"&&module&&module.exports){module.exports=QUnit;module.exports.QUnit=QUnit;}
|
||||
if(typeof exports!=="undefined"&&exports){exports.QUnit=QUnit;}
|
||||
if(typeof define==="function"&&define.amd){define(function(){return QUnit;});QUnit.config.autostart=false;}}((function(){return this;})()));//
|
||||
/**/
|
||||
QUnit.diff=(function(){function DiffMatchPatch(){this.DiffTimeout=1.0;this.DiffEditCost=4;}
|
||||
var DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;DiffMatchPatch.prototype.DiffMain=function(text1,text2,optChecklines,optDeadline){var deadline,checklines,commonlength,commonprefix,commonsuffix,diffs;if(typeof optDeadline==="undefined"){if(this.DiffTimeout<=0){optDeadline=Number.MAX_VALUE;}else{ optDeadline=(new Date()).getTime()+this.DiffTimeout*1000;}}
|
||||
deadline=optDeadline;if(text1===null||text2===null){throw new Error("Null input. (DiffMain)");}
|
||||
if(text1===text2){if(text1){return[[DIFF_EQUAL,text1]];}
|
||||
return[];}
|
||||
if(typeof optChecklines==="undefined"){optChecklines=true;}
|
||||
checklines=optChecklines;commonlength=this.diffCommonPrefix(text1,text2);commonprefix=text1.substring(0,commonlength);text1=text1.substring(commonlength);text2=text2.substring(commonlength);commonlength=this.diffCommonSuffix(text1,text2);commonsuffix=text1.substring(text1.length-commonlength);text1=text1.substring(0,text1.length-commonlength);text2=text2.substring(0,text2.length-commonlength);diffs=this.diffCompute(text1,text2,checklines,deadline);if(commonprefix){diffs.unshift([DIFF_EQUAL,commonprefix]);}
|
||||
if(commonsuffix){diffs.push([DIFF_EQUAL,commonsuffix]);}
|
||||
this.diffCleanupMerge(diffs);return diffs;};DiffMatchPatch.prototype.diffCleanupEfficiency=function(diffs){var changes,equalities,equalitiesLength,lastequality,pointer,preIns,preDel,postIns,postDel;changes=false;equalities=[];equalitiesLength=0;lastequality=null;pointer=0;preIns=false;preDel=false;postIns=false;postDel=false;while(pointer<diffs.length){if(diffs[pointer][0]===DIFF_EQUAL){if(diffs[pointer][1].length<this.DiffEditCost&&(postIns||postDel)){equalities[equalitiesLength++]=pointer;preIns=postIns;preDel=postDel;lastequality=diffs[pointer][1];}else{equalitiesLength=0;lastequality=null;}
|
||||
postIns=postDel=false;}else{if(diffs[pointer][0]===DIFF_DELETE){postDel=true;}else{postIns=true;}
|
||||
if(lastequality&&((preIns&&preDel&&postIns&&postDel)||((lastequality.length<this.DiffEditCost/2)&&(preIns+preDel+postIns+postDel)===3))){diffs.splice(equalities[equalitiesLength-1],0,[DIFF_DELETE,lastequality]);diffs[equalities[equalitiesLength-1]+1][0]=DIFF_INSERT;equalitiesLength--;lastequality=null;if(preIns&&preDel){postIns=postDel=true;equalitiesLength=0;}else{equalitiesLength--;pointer=equalitiesLength>0?equalities[equalitiesLength-1]:-1;postIns=postDel=false;}
|
||||
changes=true;}}
|
||||
pointer++;}
|
||||
if(changes){this.diffCleanupMerge(diffs);}};DiffMatchPatch.prototype.diffPrettyHtml=function(diffs){var op,data,x,html=[];for(x=0;x<diffs.length;x++){op=diffs[x][0];data=diffs[x][1];switch(op){case DIFF_INSERT:html[x]="<ins>"+data+"</ins>";break;case DIFF_DELETE:html[x]="<del>"+data+"</del>";break;case DIFF_EQUAL:html[x]="<span>"+data+"</span>";break;}}
|
||||
return html.join("");};DiffMatchPatch.prototype.diffCommonPrefix=function(text1,text2){var pointermid,pointermax,pointermin,pointerstart;if(!text1||!text2||text1.charAt(0)!==text2.charAt(0)){return 0;}
|
||||
pointermin=0;pointermax=Math.min(text1.length,text2.length);pointermid=pointermax;pointerstart=0;while(pointermin<pointermid){if(text1.substring(pointerstart,pointermid)===text2.substring(pointerstart,pointermid)){pointermin=pointermid;pointerstart=pointermin;}else{pointermax=pointermid;}
|
||||
pointermid=Math.floor((pointermax-pointermin)/2+pointermin);}
|
||||
return pointermid;};DiffMatchPatch.prototype.diffCommonSuffix=function(text1,text2){var pointermid,pointermax,pointermin,pointerend;if(!text1||!text2||text1.charAt(text1.length-1)!==text2.charAt(text2.length-1)){return 0;}
|
||||
pointermin=0;pointermax=Math.min(text1.length,text2.length);pointermid=pointermax;pointerend=0;while(pointermin<pointermid){if(text1.substring(text1.length-pointermid,text1.length-pointerend)===text2.substring(text2.length-pointermid,text2.length-pointerend)){pointermin=pointermid;pointerend=pointermin;}else{pointermax=pointermid;}
|
||||
pointermid=Math.floor((pointermax-pointermin)/2+pointermin);}
|
||||
return pointermid;};/**/
|
||||
DiffMatchPatch.prototype.diffCompute=function(text1,text2,checklines,deadline){var diffs,longtext,shorttext,i,hm,text1A,text2A,text1B,text2B,midCommon,diffsA,diffsB;if(!text1){return[[DIFF_INSERT,text2]];}
|
||||
if(!text2){return[[DIFF_DELETE,text1]];}
|
||||
longtext=text1.length>text2.length?text1:text2;shorttext=text1.length>text2.length?text2:text1;i=longtext.indexOf(shorttext);if(i!==-1){diffs=[[DIFF_INSERT,longtext.substring(0,i)],[DIFF_EQUAL,shorttext],[DIFF_INSERT,longtext.substring(i+shorttext.length)]];if(text1.length>text2.length){diffs[0][0]=diffs[2][0]=DIFF_DELETE;}
|
||||
return diffs;}
|
||||
if(shorttext.length===1){return[[DIFF_DELETE,text1],[DIFF_INSERT,text2]];}
|
||||
hm =this.diffHalfMatch(text1,text2);if(hm){text1A=hm[0];text1B=hm[1];text2A=hm[2];text2B=hm[3];midCommon=hm[4];diffsA=this.DiffMain(text1A,text2A,checklines,deadline);diffsB=this.DiffMain(text1B,text2B,checklines,deadline);return diffsA.concat([[DIFF_EQUAL,midCommon]],diffsB);}
|
||||
if(checklines&&text1.length>100&&text2.length>100){return this.diffLineMode(text1,text2,deadline);}
|
||||
return this.diffBisect(text1,text2,deadline);};DiffMatchPatch.prototype.diffHalfMatch=function(text1,text2){var longtext,shorttext,dmp,text1A,text2B,text2A,text1B,midCommon,hm1,hm2,hm;if(this.DiffTimeout<=0){return null;}
|
||||
longtext=text1.length>text2.length?text1:text2;shorttext=text1.length>text2.length?text2:text1;if(longtext.length<4||shorttext.length*2<longtext.length){return null;}
|
||||
dmp=this;/**/
|
||||
function diffHalfMatchI(longtext,shorttext,i){var seed,j,bestCommon,prefixLength,suffixLength,bestLongtextA,bestLongtextB,bestShorttextA,bestShorttextB;seed=longtext.substring(i,i+Math.floor(longtext.length/4));j=-1;bestCommon="";while((j=shorttext.indexOf(seed,j+1))!==-1){prefixLength=dmp.diffCommonPrefix(longtext.substring(i),shorttext.substring(j));suffixLength=dmp.diffCommonSuffix(longtext.substring(0,i),shorttext.substring(0,j));if(bestCommon.length<suffixLength+prefixLength){bestCommon=shorttext.substring(j-suffixLength,j)+
|
||||
shorttext.substring(j,j+prefixLength);bestLongtextA=longtext.substring(0,i-suffixLength);bestLongtextB=longtext.substring(i+prefixLength);bestShorttextA=shorttext.substring(0,j-suffixLength);bestShorttextB=shorttext.substring(j+prefixLength);}}
|
||||
if(bestCommon.length*2>=longtext.length){return[bestLongtextA,bestLongtextB,bestShorttextA,bestShorttextB,bestCommon
|
||||
];}else{return null;}}
|
||||
hm1=diffHalfMatchI(longtext,shorttext,Math.ceil(longtext.length/4));hm2=diffHalfMatchI(longtext,shorttext,Math.ceil(longtext.length/2));if(!hm1&&!hm2){return null;}else if(!hm2){hm=hm1;}else if(!hm1){hm=hm2;}else{hm=hm1[4].length>hm2[4].length?hm1:hm2;}
|
||||
text1A,text1B,text2A,text2B;if(text1.length>text2.length){text1A=hm[0];text1B=hm[1];text2A=hm[2];text2B=hm[3];}else{text2A=hm[0];text2B=hm[1];text1A=hm[2];text1B=hm[3];}
|
||||
midCommon=hm[4];return[text1A,text1B,text2A,text2B,midCommon];};DiffMatchPatch.prototype.diffLineMode=function(text1,text2,deadline){var a,diffs,linearray,pointer,countInsert,countDelete,textInsert,textDelete,j;a=this.diffLinesToChars(text1,text2);text1=a.chars1;text2=a.chars2;linearray=a.lineArray;diffs=this.DiffMain(text1,text2,false,deadline);
|
||||
this.diffCharsToLines(diffs,linearray);this.diffCleanupSemantic(diffs);diffs.push([DIFF_EQUAL,""]);pointer=0;countDelete=0;countInsert=0;textDelete="";textInsert="";while(pointer<diffs.length){switch(diffs[pointer][0]){case DIFF_INSERT:countInsert++;textInsert+=diffs[pointer][1];break;case DIFF_DELETE:countDelete++;textDelete+=diffs[pointer][1];break;case DIFF_EQUAL:if(countDelete>=1&&countInsert>=1){diffs.splice(pointer-countDelete-countInsert,countDelete+countInsert);pointer=pointer-countDelete-countInsert;a=this.DiffMain(textDelete,textInsert,false,deadline);for(j=a.length-1;j>=0;j--){diffs.splice(pointer,0,a[j]);}
|
||||
pointer=pointer+a.length;}
|
||||
countInsert=0;countDelete=0;textDelete="";textInsert="";break;}
|
||||
pointer++;}
|
||||
diffs.pop();return diffs;};DiffMatchPatch.prototype.diffBisect=function(text1,text2,deadline){var text1Length,text2Length,maxD,vOffset,vLength,v1,v2,x,delta,front,k1start,k1end,k2start,k2end,k2Offset,k1Offset,x1,x2,y1,y2,d,k1,k2;text1Length=text1.length;text2Length=text2.length;maxD=Math.ceil((text1Length+text2Length)/2);vOffset=maxD;vLength=2*maxD;v1=new Array(vLength);v2=new Array(vLength);for(x=0;x<vLength;x++){v1[x]=-1;v2[x]=-1;}
|
||||
v1[vOffset+1]=0;v2[vOffset+1]=0;delta=text1Length-text2Length;front=(delta%2!==0);k1start=0;k1end=0;k2start=0;k2end=0;for(d=0;d<maxD;d++){if((new Date()).getTime()>deadline){break;}
|
||||
for(k1=-d+k1start;k1<=d-k1end;k1+=2){k1Offset=vOffset+k1;if(k1===-d||(k1!==d&&v1[k1Offset-1]<v1[k1Offset+1])){x1=v1[k1Offset+1];}else{x1=v1[k1Offset-1]+1;}
|
||||
y1=x1-k1;while(x1<text1Length&&y1<text2Length&&text1.charAt(x1)===text2.charAt(y1)){x1++;y1++;}
|
||||
v1[k1Offset]=x1;if(x1>text1Length){k1end+=2;}else if(y1>text2Length){k1start+=2;}else if(front){k2Offset=vOffset+delta-k1;if(k2Offset>=0&&k2Offset<vLength&&v2[k2Offset]!==-1){x2=text1Length-v2[k2Offset];if(x1>=x2){return this.diffBisectSplit(text1,text2,x1,y1,deadline);}}
|
||||
}}
|
||||
for(k2=-d+k2start;k2<=d-k2end;k2+=2){k2Offset=vOffset+k2;if(k2===-d||(k2!==d&&v2[k2Offset-1]<v2[k2Offset+1])){x2=v2[k2Offset+1];}else{x2=v2[k2Offset-1]+1;}
|
||||
y2=x2-k2;while(x2<text1Length&&y2<text2Length&&text1.charAt(text1Length-x2-1)===text2.charAt(text2Length-y2-1)){x2++;y2++;}
|
||||
v2[k2Offset]=x2;if(x2>text1Length){k2end+=2;}else if(y2>text2Length){k2start+=2;}else if(!front){k1Offset=vOffset+delta-k2;if(k1Offset>=0&&k1Offset<vLength&&v1[k1Offset]!==-1){x1=v1[k1Offset];y1=vOffset+x1-k1Offset;x2=text1Length-x2;if(x1>=x2){return this.diffBisectSplit(text1,text2,x1,y1,deadline);}}}}}
|
||||
return[[DIFF_DELETE,text1],[DIFF_INSERT,text2]];};/**/
|
||||
DiffMatchPatch.prototype.diffBisectSplit=function(text1,text2,x,y,deadline){var text1a,text1b,text2a,text2b,diffs,diffsb;text1a=text1.substring(0,x);text2a=text2.substring(0,y);text1b=text1.substring(x);text2b=text2.substring(y);diffs=this.DiffMain(text1a,text2a,false,deadline);diffsb=this.DiffMain(text1b,text2b,false,deadline);return diffs.concat(diffsb);};DiffMatchPatch.prototype.diffCleanupSemantic=function(diffs){var changes,equalities,equalitiesLength,lastequality,pointer,lengthInsertions2,lengthDeletions2,lengthInsertions1,lengthDeletions1,deletion,insertion,overlapLength1,overlapLength2;changes=false;equalities=[];equalitiesLength=0;lastequality=null;pointer=0;lengthInsertions1=0;lengthDeletions1=0;lengthInsertions2=0;lengthDeletions2=0;while(pointer<diffs.length){if(diffs[pointer][0]===DIFF_EQUAL){equalities[equalitiesLength++]=pointer;lengthInsertions1=lengthInsertions2;lengthDeletions1=lengthDeletions2;lengthInsertions2=0;lengthDeletions2=0;lastequality=diffs[pointer][1];}else{if(diffs[pointer][0]===DIFF_INSERT){lengthInsertions2+=diffs[pointer][1].length;}else{lengthDeletions2+=diffs[pointer][1].length;}
|
||||
if(lastequality&&(lastequality.length<=Math.max(lengthInsertions1,lengthDeletions1))&&(lastequality.length<=Math.max(lengthInsertions2,lengthDeletions2))){diffs.splice(equalities[equalitiesLength-1],0,[DIFF_DELETE,lastequality]);diffs[equalities[equalitiesLength-1]+1][0]=DIFF_INSERT;equalitiesLength--;equalitiesLength--;pointer=equalitiesLength>0?equalities[equalitiesLength-1]:-1;lengthInsertions1=0;lengthDeletions1=0;lengthInsertions2=0;lengthDeletions2=0;lastequality=null;changes=true;}}
|
||||
pointer++;}
|
||||
if(changes){this.diffCleanupMerge(diffs);}
|
||||
pointer=1;while(pointer<diffs.length){if(diffs[pointer-1][0]===DIFF_DELETE&&diffs[pointer][0]===DIFF_INSERT){deletion=diffs[pointer-1][1];insertion=diffs[pointer][1];overlapLength1=this.diffCommonOverlap(deletion,insertion);overlapLength2=this.diffCommonOverlap(insertion,deletion);if(overlapLength1>=overlapLength2){if(overlapLength1>=deletion.length/2||overlapLength1>=insertion.length/2){diffs.splice(pointer,0,[DIFF_EQUAL,insertion.substring(0,overlapLength1)]);diffs[pointer-1][1]=deletion.substring(0,deletion.length-overlapLength1);diffs[pointer+1][1]=insertion.substring(overlapLength1);pointer++;}}else{if(overlapLength2>=deletion.length/2||overlapLength2>=insertion.length/2){diffs.splice(pointer,0,[DIFF_EQUAL,deletion.substring(0,overlapLength2)]);diffs[pointer-1][0]=DIFF_INSERT;diffs[pointer-1][1]=insertion.substring(0,insertion.length-overlapLength2);diffs[pointer+1][0]=DIFF_DELETE;diffs[pointer+1][1]=deletion.substring(overlapLength2);pointer++;}}
|
||||
pointer++;}
|
||||
pointer++;}};DiffMatchPatch.prototype.diffCommonOverlap=function(text1,text2){var text1Length,text2Length,textLength,best,length,pattern,found;text1Length=text1.length;text2Length=text2.length;if(text1Length===0||text2Length===0){return 0;}
|
||||
if(text1Length>text2Length){text1=text1.substring(text1Length-text2Length);}else if(text1Length<text2Length){ text2=text2.substring(0,text1Length);}
|
||||
textLength=Math.min(text1Length,text2Length);if(text1===text2){return textLength;}
|
||||
best=0;length=1;while(true){pattern=text1.substring(textLength-length);found=text2.indexOf(pattern);if(found===-1){return best;}
|
||||
length+=found;if(found===0||text1.substring(textLength-length)===text2.substring(0,length)){best=length;length++;}}};/**/
|
||||
DiffMatchPatch.prototype.diffLinesToChars=function(text1,text2){var lineArray,lineHash,chars1,chars2;lineArray=[];lineHash={};lineArray[0]="";function diffLinesToCharsMunge(text){var chars,lineStart,lineEnd,lineArrayLength,line;chars="";lineStart=0;lineEnd=-1;lineArrayLength=lineArray.length;while(lineEnd<text.length-1){lineEnd=text.indexOf("\n",lineStart);if(lineEnd===-1){lineEnd=text.length-1;}
|
||||
line=text.substring(lineStart,lineEnd+1);lineStart=lineEnd+1;if(lineHash.hasOwnProperty?lineHash.hasOwnProperty(line):(lineHash[line]!==undefined)){chars+=String.fromCharCode(lineHash[line]);}else{chars+=String.fromCharCode(lineArrayLength);lineHash[line]=lineArrayLength;lineArray[lineArrayLength++]=line;}}
|
||||
return chars;}
|
||||
chars1=diffLinesToCharsMunge(text1);chars2=diffLinesToCharsMunge(text2);return{chars1:chars1,chars2:chars2,lineArray:lineArray};};DiffMatchPatch.prototype.diffCharsToLines=function(diffs,lineArray){var x,chars,text,y;for(x=0;x<diffs.length;x++){chars=diffs[x][1];text=[];for(y=0;y<chars.length;y++){text[y]=lineArray[chars.charCodeAt(y)];}
|
||||
diffs[x][1]=text.join("");}};DiffMatchPatch.prototype.diffCleanupMerge=function(diffs){var pointer,countDelete,countInsert,textInsert,textDelete,commonlength,changes;diffs.push([DIFF_EQUAL,""]);pointer=0;countDelete=0;countInsert=0;textDelete="";textInsert="";commonlength;while(pointer<diffs.length){switch(diffs[pointer][0]){case DIFF_INSERT:countInsert++;textInsert+=diffs[pointer][1];pointer++;break;case DIFF_DELETE:countDelete++;textDelete+=diffs[pointer][1];pointer++;break;case DIFF_EQUAL:if(countDelete+countInsert>1){if(countDelete!==0&&countInsert!==0){commonlength=this.diffCommonPrefix(textInsert,textDelete);if(commonlength!==0){if((pointer-countDelete-countInsert)>0&&diffs[pointer-countDelete-countInsert-1][0]===
|
||||
DIFF_EQUAL){diffs[pointer-countDelete-countInsert-1][1]+=textInsert.substring(0,commonlength);}else{diffs.splice(0,0,[DIFF_EQUAL,textInsert.substring(0,commonlength)]);pointer++;}
|
||||
textInsert=textInsert.substring(commonlength);textDelete=textDelete.substring(commonlength);}
|
||||
commonlength=this.diffCommonSuffix(textInsert,textDelete);if(commonlength!==0){diffs[pointer][1]=textInsert.substring(textInsert.length-
|
||||
commonlength)+diffs[pointer][1];textInsert=textInsert.substring(0,textInsert.length-
|
||||
commonlength);textDelete=textDelete.substring(0,textDelete.length-
|
||||
commonlength);}}
|
||||
if(countDelete===0){diffs.splice(pointer-countInsert,countDelete+countInsert,[DIFF_INSERT,textInsert]);}else if(countInsert===0){diffs.splice(pointer-countDelete,countDelete+countInsert,[DIFF_DELETE,textDelete]);}else{diffs.splice(pointer-countDelete-countInsert, countDelete+countInsert,[DIFF_DELETE,textDelete],[DIFF_INSERT,textInsert]);}
|
||||
pointer=pointer-countDelete-countInsert+(countDelete?1:0)+(countInsert?1:0)+1;}else if(pointer!==0&&diffs[pointer-1][0]===DIFF_EQUAL){diffs[pointer-1][1]+=diffs[pointer][1];diffs.splice(pointer,1);}else{pointer++;}
|
||||
countInsert=0;countDelete=0;textDelete="";textInsert="";break;}}
|
||||
if(diffs[diffs.length-1][1]===""){diffs.pop();}
|
||||
changes=false;pointer=1;while(pointer<diffs.length-1){if(diffs[pointer-1][0]===DIFF_EQUAL&&diffs[pointer+1][0]===DIFF_EQUAL){if(diffs[pointer][1].substring(diffs[pointer][1].length-
|
||||
diffs[pointer-1][1].length)===diffs[pointer-1][1]){diffs[pointer][1]=diffs[pointer-1][1]+
|
||||
diffs[pointer][1].substring(0,diffs[pointer][1].length-
|
||||
diffs[pointer-1][1].length);diffs[pointer+1][1]=diffs[pointer-1][1]+diffs[pointer+1][1];diffs.splice(pointer-1,1);changes=true;}else if(diffs[pointer][1].substring(0,diffs[pointer+1][1].length)===diffs[pointer+1][1]){diffs[pointer-1][1]+=diffs[pointer+1][1];diffs[pointer][1]=diffs[pointer][1].substring(diffs[pointer+1][1].length)+
|
||||
diffs[pointer+1][1];diffs.splice(pointer+1,1);changes=true;}}
|
||||
pointer++;}
|
||||
if(changes){this.diffCleanupMerge(diffs);}};return function(o,n){var diff,output,text;diff=new DiffMatchPatch();output=diff.DiffMain(o,n);diff.diffCleanupEfficiency(output);text=diff.diffPrettyHtml(output);return text;};}());//
|
||||
(function(){QUnit.init=function(){var tests,banner,result,qunit,config=QUnit.config;config.stats={all:0,bad:0};config.moduleStats={all:0,bad:0};config.started=0;config.updateRate=1000;config.blocking=false;config.autostart=true;config.autorun=false;config.filter="";config.queue=[];if(typeof window==="undefined"){return;}
|
||||
qunit=id("qunit");if(qunit){qunit.innerHTML="<h1 id='qunit-header'>"+escapeText(document.title)+"</h1>"+
|
||||
"<h2 id='qunit-banner'></h2>"+
|
||||
"<div id='qunit-testrunner-toolbar'></div>"+
|
||||
"<h2 id='qunit-userAgent'></h2>"+
|
||||
"<ol id='qunit-tests'></ol>";}
|
||||
tests=id("qunit-tests");banner=id("qunit-banner");result=id("qunit-testresult");if(tests){tests.innerHTML="";}
|
||||
if(banner){banner.className="";}
|
||||
if(result){result.parentNode.removeChild(result);}
|
||||
if(tests){result=document.createElement("p");result.id="qunit-testresult";result.className="result";tests.parentNode.insertBefore(result,tests);result.innerHTML="Running...<br /> ";}};if(typeof window==="undefined"){return;}
|
||||
var config=QUnit.config,hasOwn=Object.prototype.hasOwnProperty,defined={document:window.document!==undefined,sessionStorage:(function(){var x="qunit-test-string";try{sessionStorage.setItem(x,x);sessionStorage.removeItem(x);return true;}catch(e){return false;}}())},modulesList=[];function escapeText(s){if(!s){return "";}
|
||||
s=s+"";return s.replace(/['"<>&]/g,function(s){switch(s){case "'":return "'";case "\"":return """;case "<":return "<";case ">":return ">";case "&":return "&";}});}
|
||||
function addEvent(elem,type,fn){if(elem.addEventListener){elem.addEventListener(type,fn,false);}else if(elem.attachEvent){elem.attachEvent("on"+type,function(){var event=window.event;if(!event.target){event.target=event.srcElement||document;}
|
||||
fn.call(elem,event);});}}
|
||||
function addEvents(elems,type,fn){var i=elems.length;while(i--){addEvent(elems[i],type,fn);}}
|
||||
function hasClass(elem,name){return(" "+elem.className+" ").indexOf(" "+name+" ")>=0;}
|
||||
function addClass(elem,name){if(!hasClass(elem,name)){elem.className+=(elem.className?" ":"")+name;}}
|
||||
function toggleClass(elem,name){if(hasClass(elem,name)){removeClass(elem,name);}else{addClass(elem,name);}}
|
||||
function removeClass(elem,name){var set=" "+elem.className+" ";while(set.indexOf(" "+name+" ")>=0){set=set.replace(" "+name+" "," ");}
|
||||
elem.className=typeof set.trim==="function"?set.trim():set.replace(/^\s+|\s+$/g,"");}
|
||||
function id(name){return defined.document&&document.getElementById&&document.getElementById(name);}
|
||||
function getUrlConfigHtml(){var i,j,val,escaped,escapedTooltip,selection=false,len=config.urlConfig.length,urlConfigHtml="";for(i=0;i<len;i++){val=config.urlConfig[i];if(typeof val==="string"){val={id:val,label:val};}
|
||||
escaped=escapeText(val.id);escapedTooltip=escapeText(val.tooltip);if(config[val.id]===undefined){config[val.id]=QUnit.urlParams[val.id];}
|
||||
if(!val.value||typeof val.value==="string"){urlConfigHtml+="<input id='qunit-urlconfig-"+escaped+
|
||||
"' name='"+escaped+"' type='checkbox'"+(val.value?" value='"+escapeText(val.value)+"'":"")+(config[val.id]?" checked='checked'":"")+
|
||||
" title='"+escapedTooltip+"' /><label for='qunit-urlconfig-"+escaped+
|
||||
"' title='"+escapedTooltip+"'>"+val.label+"</label>";}else{urlConfigHtml+="<label for='qunit-urlconfig-"+escaped+
|
||||
"' title='"+escapedTooltip+"'>"+val.label+
|
||||
": </label><select id='qunit-urlconfig-"+escaped+
|
||||
"' name='"+escaped+"' title='"+escapedTooltip+"'><option></option>";if(QUnit.is("array",val.value)){for(j=0;j<val.value.length;j++){escaped=escapeText(val.value[j]);urlConfigHtml+="<option value='"+escaped+"'"+(config[val.id]===val.value[j]?(selection=true)&&" selected='selected'":"")+
|
||||
">"+escaped+"</option>";}}else{for(j in val.value){if(hasOwn.call(val.value,j)){urlConfigHtml+="<option value='"+escapeText(j)+"'"+(config[val.id]===j?(selection=true)&&" selected='selected'":"")+
|
||||
">"+escapeText(val.value[j])+"</option>";}}}
|
||||
if(config[val.id]&&!selection){escaped=escapeText(config[val.id]);urlConfigHtml+="<option value='"+escaped+
|
||||
"' selected='selected' disabled='disabled'>"+escaped+"</option>";}
|
||||
urlConfigHtml+="</select>";}}
|
||||
return urlConfigHtml;}
|
||||
function toolbarChanged(){var updatedUrl,value,field=this,params={};//
|
||||
if("selectedIndex" in field){value=field.options[field.selectedIndex].value||undefined;}else{value=field.checked?(field.defaultValue||true):undefined;}
|
||||
params[field.name]=value;updatedUrl=setUrl(params);if("hidepassed"===field.name&&"replaceState" in window.history){config[field.name]=value||false;if(value){addClass(id("qunit-tests"),"hidepass");}else{removeClass(id("qunit-tests"),"hidepass");}
|
||||
window.history.replaceState(null,"",updatedUrl);}else{window.location=updatedUrl;}}
|
||||
function setUrl(params){var key,querystring="?";params=QUnit.extend(QUnit.extend({},QUnit.urlParams),params);for(key in params){if(hasOwn.call(params,key)){if(params[key]===undefined){continue;}
|
||||
querystring+=encodeURIComponent(key);if(params[key]!==true){querystring+="="+encodeURIComponent(params[key]);}
|
||||
querystring+="&";}}
|
||||
return location.protocol+"//"+location.host+
|
||||
location.pathname+querystring.slice(0,-1);}
|
||||
function applyUrlParams(){var selectedModule,modulesList=id("qunit-modulefilter"),filter=id("qunit-filter-input").value;selectedModule=modulesList?decodeURIComponent(modulesList.options[modulesList.selectedIndex].value):undefined;window.location=setUrl({module:(selectedModule==="")?undefined:selectedModule,filter:(filter==="")?undefined:filter,testId:undefined});}
|
||||
function toolbarUrlConfigContainer(){var urlConfigContainer=document.createElement("span");urlConfigContainer.innerHTML=getUrlConfigHtml();addClass(urlConfigContainer,"qunit-url-config");addEvents(urlConfigContainer.getElementsByTagName("input"),"click",toolbarChanged);addEvents(urlConfigContainer.getElementsByTagName("select"),"change",toolbarChanged);return urlConfigContainer;}
|
||||
function toolbarLooseFilter(){var filter=document.createElement("form"),label=document.createElement("label"),input=document.createElement("input"),button=document.createElement("button");addClass(filter,"qunit-filter");label.innerHTML="Filter: ";input.type="text";input.value=config.filter||"";input.name="filter";input.id="qunit-filter-input";button.innerHTML="Go";label.appendChild(input);filter.appendChild(label);filter.appendChild(button);addEvent(filter,"submit",function(ev){applyUrlParams();if(ev&&ev.preventDefault){ev.preventDefault();}
|
||||
return false;});return filter;}
|
||||
function toolbarModuleFilterHtml(){var i,moduleFilterHtml="";if(!modulesList.length){return false;}
|
||||
modulesList.sort(function(a,b){return a.localeCompare(b);});moduleFilterHtml+="<label for='qunit-modulefilter'>Module: </label>"+
|
||||
"<select id='qunit-modulefilter' name='modulefilter'><option value='' "+(QUnit.urlParams.module===undefined?"selected='selected'":"")+
|
||||
">< All Modules ></option>";for(i=0;i<modulesList.length;i++){moduleFilterHtml+="<option value='"+
|
||||
escapeText(encodeURIComponent(modulesList[i]))+"' "+(QUnit.urlParams.module===modulesList[i]?"selected='selected'":"")+
|
||||
">"+escapeText(modulesList[i])+"</option>";}
|
||||
moduleFilterHtml+="</select>";return moduleFilterHtml;}
|
||||
function toolbarModuleFilter(){var toolbar=id("qunit-testrunner-toolbar"),moduleFilter=document.createElement("span"),moduleFilterHtml=toolbarModuleFilterHtml();if(!toolbar||!moduleFilterHtml){return false;}
|
||||
moduleFilter.setAttribute("id","qunit-modulefilter-container");moduleFilter.innerHTML=moduleFilterHtml;addEvent(moduleFilter.lastChild,"change",applyUrlParams);toolbar.appendChild(moduleFilter);}
|
||||
function appendToolbar(){var toolbar=id("qunit-testrunner-toolbar");if(toolbar){toolbar.appendChild(toolbarUrlConfigContainer());toolbar.appendChild(toolbarLooseFilter());}}
|
||||
function appendHeader(){var header=id("qunit-header");if(header){header.innerHTML="<a href='"+
|
||||
setUrl({filter:undefined,module:undefined,testId:undefined})+
|
||||
"'>"+header.innerHTML+"</a> ";}}
|
||||
function appendBanner(){var banner=id("qunit-banner");if(banner){banner.className="";}}
|
||||
function appendTestResults(){var tests=id("qunit-tests"),result=id("qunit-testresult");if(result){result.parentNode.removeChild(result);}
|
||||
if(tests){tests.innerHTML="";result=document.createElement("p");result.id="qunit-testresult";result.className="result";tests.parentNode.insertBefore(result,tests);result.innerHTML="Running...<br /> ";}}
|
||||
function storeFixture(){var fixture=id("qunit-fixture");if(fixture){config.fixture=fixture.innerHTML;}}
|
||||
function appendUserAgent(){var userAgent=id("qunit-userAgent");if(userAgent){userAgent.innerHTML="";userAgent.appendChild(document.createTextNode("QUnit "+QUnit.version+"; "+navigator.userAgent));}}
|
||||
function appendTestsList(modules){var i,l,x,z,test,moduleObj;for(i=0,l=modules.length;i<l;i++){moduleObj=modules[i];if(moduleObj.name){modulesList.push(moduleObj.name);}
|
||||
for(x=0,z=moduleObj.tests.length;x<z;x++){test=moduleObj.tests[x];appendTest(test.name,test.testId,moduleObj.name);}}}
|
||||
function appendTest(name,testId,moduleName){var title,rerunTrigger,testBlock,assertList,tests=id("qunit-tests");if(!tests){return;}
|
||||
title=document.createElement("strong");title.innerHTML=getNameHtml(name,moduleName);rerunTrigger=document.createElement("a");rerunTrigger.innerHTML="Rerun";rerunTrigger.href=setUrl({testId:testId});testBlock=document.createElement("li");testBlock.appendChild(title);testBlock.appendChild(rerunTrigger);testBlock.id="qunit-test-output-"+testId;assertList=document.createElement("ol");assertList.className="qunit-assert-list";testBlock.appendChild(assertList);tests.appendChild(testBlock);}
|
||||
QUnit.begin(function(details){var qunit=id("qunit");storeFixture();if(qunit){qunit.innerHTML="<h1 id='qunit-header'>"+escapeText(document.title)+"</h1>"+
|
||||
"<h2 id='qunit-banner'></h2>"+
|
||||
"<div id='qunit-testrunner-toolbar'></div>"+
|
||||
"<h2 id='qunit-userAgent'></h2>"+
|
||||
"<ol id='qunit-tests'></ol>";}
|
||||
appendHeader();appendBanner();appendTestResults();appendUserAgent();appendToolbar();appendTestsList(details.modules);toolbarModuleFilter();if(qunit&&config.hidepassed){addClass(qunit.lastChild,"hidepass");}});QUnit.done(function(details){var i,key,banner=id("qunit-banner"),tests=id("qunit-tests"),html=["Tests completed in ",details.runtime," milliseconds.<br />","<span class='passed'>",details.passed,"</span> assertions of <span class='total'>",details.total,"</span> passed, <span class='failed'>",details.failed,"</span> failed."].join("");if(banner){banner.className=details.failed?"qunit-fail":"qunit-pass";}
|
||||
if(tests){id("qunit-testresult").innerHTML=html;}
|
||||
if(config.altertitle&&defined.document&&document.title){document.title=[(details.failed?"\u2716":"\u2714"),document.title.replace(/^[\u2714\u2716] /i,"")].join(" ");}
|
||||
if(config.reorder&&defined.sessionStorage&&details.failed===0){for(i=0;i<sessionStorage.length;i++){key=sessionStorage.key(i++);if(key.indexOf("qunit-test-")===0){sessionStorage.removeItem(key);}}}
|
||||
if(config.scrolltop&&window.scrollTo){window.scrollTo(0,0);}});function getNameHtml(name,module){var nameHtml="";if(module){nameHtml="<span class='module-name'>"+escapeText(module)+"</span>: ";}
|
||||
nameHtml+="<span class='test-name'>"+escapeText(name)+"</span>";return nameHtml;}
|
||||
QUnit.testStart(function(details){var running,testBlock,bad;testBlock=id("qunit-test-output-"+details.testId);if(testBlock){testBlock.className="running";}else{appendTest(details.name,details.testId,details.module);}
|
||||
running=id("qunit-testresult");if(running){bad=QUnit.config.reorder&&defined.sessionStorage&&+sessionStorage.getItem("qunit-test-"+details.module+"-"+details.name);running.innerHTML=(bad?"Rerunning previously failed test: <br />":"Running: <br />")+
|
||||
getNameHtml(details.name,details.module);}});QUnit.log(function(details){var assertList,assertLi,message,expected,actual,testItem=id("qunit-test-output-"+details.testId);if(!testItem){return;}
|
||||
message=escapeText(details.message)||(details.result?"okay":"failed");message="<span class='test-message'>"+message+"</span>";message+="<span class='runtime'>@ "+details.runtime+" ms</span>";if(!details.result&&hasOwn.call(details,"expected")){expected=escapeText(QUnit.dump.parse(details.expected));actual=escapeText(QUnit.dump.parse(details.actual));message+="<table><tr class='test-expected'><th>Expected: </th><td><pre>"+
|
||||
expected+
|
||||
"</pre></td></tr>";if(actual!==expected){message+="<tr class='test-actual'><th>Result: </th><td><pre>"+
|
||||
actual+"</pre></td></tr>"+
|
||||
"<tr class='test-diff'><th>Diff: </th><td><pre>"+
|
||||
QUnit.diff(expected,actual)+"</pre></td></tr>";}else{if(expected.indexOf("[object Array]")!==-1||expected.indexOf("[object Object]")!==-1){message+="<tr class='test-message'><th>Message: </th><td>"+
|
||||
"Diff suppressed as the depth of object is more than current max depth ("+
|
||||
QUnit.config.maxDepth+").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to "+
|
||||
" run with a higher max depth or <a href='"+setUrl({maxDepth:-1})+"'>"+
|
||||
"Rerun</a> without max depth.</p></td></tr>";}}
|
||||
if(details.source){message+="<tr class='test-source'><th>Source: </th><td><pre>"+
|
||||
escapeText(details.source)+"</pre></td></tr>";}
|
||||
message+="</table>";}else if(!details.result&&details.source){message+="<table>"+
|
||||
"<tr class='test-source'><th>Source: </th><td><pre>"+
|
||||
escapeText(details.source)+"</pre></td></tr>"+
|
||||
"</table>";}
|
||||
assertList=testItem.getElementsByTagName("ol")[0];assertLi=document.createElement("li");assertLi.className=details.result?"pass":"fail";assertLi.innerHTML=message;assertList.appendChild(assertLi);});QUnit.testDone(function(details){var testTitle,time,testItem,assertList,good,bad,testCounts,skipped,tests=id("qunit-tests");if(!tests){return;}
|
||||
testItem=id("qunit-test-output-"+details.testId);assertList=testItem.getElementsByTagName("ol")[0];good=details.passed;bad=details.failed;if(config.reorder&&defined.sessionStorage){if(bad){sessionStorage.setItem("qunit-test-"+details.module+"-"+details.name,bad);}else{sessionStorage.removeItem("qunit-test-"+ details.module+"-"+details.name);}}
|
||||
if(bad===0){addClass(assertList,"qunit-collapsed");}
|
||||
testTitle=testItem.firstChild;testCounts=bad?"<b class='failed'>"+bad+"</b>, "+"<b class='passed'>"+good+"</b>, ":"";testTitle.innerHTML+=" <b class='counts'>("+testCounts+
|
||||
details.assertions.length+")</b>";if(details.skipped){testItem.className="skipped";skipped=document.createElement("em");skipped.className="qunit-skipped-label";skipped.innerHTML="skipped";testItem.insertBefore(skipped,testTitle);}else{addEvent(testTitle,"click",function(){toggleClass(assertList,"qunit-collapsed");});testItem.className=bad?"fail":"pass";time=document.createElement("span");time.className="runtime";time.innerHTML=details.runtime+" ms";testItem.insertBefore(time,assertList);}});if(defined.document){if(document.readyState==="complete"){QUnit.load();}else{addEvent(window,"load",QUnit.load);}}else{config.pageLoaded=true;config.autorun=true;}})();
|
||||
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
|
||||
function recipeDetailsPageInit(ctx){
|
||||
|
||||
$(".customise-btn").click(function(e){
|
||||
e.preventDefault();
|
||||
var imgCustomModal = $("#new-custom-image-modal");
|
||||
|
||||
if (imgCustomModal.length === 0)
|
||||
throw("Modal new-custom-image not found");
|
||||
|
||||
var recipe = {id: $(this).data('recipe'), name: null}
|
||||
newCustomImageModalSetRecipes([recipe]);
|
||||
imgCustomModal.modal('show');
|
||||
});
|
||||
|
||||
$("#add-layer-btn").click(function(){
|
||||
var btn = $(this);
|
||||
|
||||
libtoaster.addRmLayer(ctx.recipe.layer_version,
|
||||
true,
|
||||
function (layersList){
|
||||
var msg = libtoaster.makeLayerAddRmAlertMsg(ctx.recipe.layer_version,
|
||||
layersList,
|
||||
true);
|
||||
|
||||
libtoaster.showChangeNotification(msg);
|
||||
|
||||
var toShow = $("#customise-build-btns");
|
||||
|
||||
/* If we have no packages built yet also fade in the build packages
|
||||
* hint message
|
||||
*/
|
||||
if (ctx.recipe.totalPackages === 0){
|
||||
toShow = toShow.add("#build-to-get-packages-msg");
|
||||
}
|
||||
|
||||
$("#packages-alert").add(btn).fadeOut(function(){
|
||||
toShow.fadeIn();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* Trigger a build of your custom image */
|
||||
$(".build-recipe-btn").click(function(){
|
||||
libtoaster.startABuild(null, ctx.recipe.name,
|
||||
function(){
|
||||
window.location.replace(libtoaster.ctx.projectBuildsUrl);
|
||||
});
|
||||
});
|
||||
}
|
||||
857
sources/poky/bitbake/lib/toaster/toastergui/static/js/table.js
Normal file
857
sources/poky/bitbake/lib/toaster/toastergui/static/js/table.js
Normal file
@@ -0,0 +1,857 @@
|
||||
'use strict';
|
||||
|
||||
function tableInit(ctx){
|
||||
|
||||
if (ctx.url.length === 0) {
|
||||
throw "No url supplied for retreiving data";
|
||||
}
|
||||
|
||||
var tableChromeDone = false;
|
||||
var tableTotal = 0;
|
||||
|
||||
var tableParams = {
|
||||
limit : 25,
|
||||
page : 1,
|
||||
orderby : null,
|
||||
filter : null,
|
||||
search : null,
|
||||
default_orderby: null,
|
||||
};
|
||||
|
||||
var defaultHiddenCols = [];
|
||||
|
||||
var table = $("#" + ctx.tableName);
|
||||
|
||||
/* if we're loading clean from a url use it's parameters as the default */
|
||||
var urlParams = libtoaster.parseUrlParams();
|
||||
|
||||
/* Merge the tableParams and urlParams object properties */
|
||||
tableParams = $.extend(tableParams, urlParams);
|
||||
|
||||
/* Now fix the types that .extend changed for us */
|
||||
tableParams.limit = Number(tableParams.limit);
|
||||
tableParams.page = Number(tableParams.page);
|
||||
|
||||
loadData(tableParams);
|
||||
|
||||
// clicking on this set of elements removes the search
|
||||
var clearSearchElements = $('.remove-search-btn-'+ctx.tableName +
|
||||
', .show-all-'+ctx.tableName);
|
||||
|
||||
function loadData(tableParams){
|
||||
table.trigger("table-loading");
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: ctx.url,
|
||||
data: tableParams,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function(tableData) {
|
||||
updateTable(tableData);
|
||||
window.history.replaceState(null, null,
|
||||
libtoaster.dumpsUrlParams(tableParams));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateTable(tableData) {
|
||||
var tableBody = table.children("tbody");
|
||||
var pagination = $('#pagination-'+ctx.tableName);
|
||||
var paginationBtns = pagination.children('ul');
|
||||
var tableContainer = $("#table-container-"+ctx.tableName);
|
||||
|
||||
tableContainer.css("visibility", "hidden");
|
||||
/* To avoid page re-layout flicker when paging set fixed height */
|
||||
table.css("padding-bottom", table.height());
|
||||
|
||||
/* Reset table components */
|
||||
tableBody.html("");
|
||||
paginationBtns.html("");
|
||||
|
||||
if (tableParams.search)
|
||||
clearSearchElements.show();
|
||||
else
|
||||
clearSearchElements.hide();
|
||||
|
||||
$('.table-count-' + ctx.tableName).text(tableData.total);
|
||||
tableTotal = tableData.total;
|
||||
|
||||
if (tableData.total === 0){
|
||||
tableContainer.hide();
|
||||
/* No results caused by a search returning nothing */
|
||||
if (tableParams.search) {
|
||||
if ($("#no-results-special-"+ctx.tableName).length > 0) {
|
||||
/* use this page's special no-results form instead of the default */
|
||||
$("#no-results-search-input-"+ctx.tableName).val(tableParams.search);
|
||||
$("#no-results-special-"+ctx.tableName).show();
|
||||
$("#results-found-"+ctx.tableName).hide();
|
||||
} else {
|
||||
$("#new-search-input-"+ctx.tableName).val(tableParams.search);
|
||||
$("#no-results-"+ctx.tableName).show();
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* No results caused by there being no data */
|
||||
$("#empty-state-"+ctx.tableName).show();
|
||||
}
|
||||
table.trigger("table-done", [tableData.total, tableParams]);
|
||||
|
||||
return;
|
||||
} else {
|
||||
tableContainer.show();
|
||||
$("#no-results-"+ctx.tableName).hide();
|
||||
$("#empty-state-"+ctx.tableName).hide();
|
||||
}
|
||||
|
||||
setupTableChrome(tableData);
|
||||
|
||||
/* Add table data rows */
|
||||
var column_index;
|
||||
for (var i in tableData.rows){
|
||||
/* only display if the column is display-able */
|
||||
var row = $("<tr></tr>");
|
||||
column_index = -1;
|
||||
for (var key_j in tableData.rows[i]){
|
||||
var td = $("<td></td>");
|
||||
td.prop("class", key_j);
|
||||
if (tableData.rows[i][key_j]){
|
||||
td.html(tableData.rows[i][key_j]);
|
||||
}
|
||||
row.append(td);
|
||||
}
|
||||
tableBody.append(row);
|
||||
|
||||
/* If we have layerbtns then initialise them */
|
||||
layerBtnsInit();
|
||||
|
||||
/* If we have popovers initialise them now */
|
||||
$('td > a.btn').popover({
|
||||
html:true,
|
||||
placement:'left',
|
||||
container:'body',
|
||||
trigger:'manual'
|
||||
}).click(function(e){
|
||||
$('td > a.btn').not(this).popover('hide');
|
||||
/* ideally we would use 'toggle' here
|
||||
* but it seems buggy in our Bootstrap version
|
||||
*/
|
||||
$(this).popover('show');
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
/* enable help information tooltip */
|
||||
$(".get-help").tooltip({container:'body', html:true, delay:{show:300}});
|
||||
}
|
||||
|
||||
/* Setup the pagination controls */
|
||||
|
||||
var start = tableParams.page - 2;
|
||||
var end = tableParams.page + 2;
|
||||
var numPages = Math.ceil(tableData.total/tableParams.limit);
|
||||
|
||||
if (numPages > 1){
|
||||
if (tableParams.page < 3)
|
||||
end = 5;
|
||||
|
||||
for (var page_i=1; page_i <= numPages; page_i++){
|
||||
if (page_i >= start && page_i <= end){
|
||||
var btn = $('<li><a href="#" class="page">'+page_i+'</a></li>');
|
||||
|
||||
if (page_i === tableParams.page){
|
||||
btn.addClass("active");
|
||||
}
|
||||
|
||||
/* Add the click handler */
|
||||
btn.click(pageButtonClicked);
|
||||
paginationBtns.append(btn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadColumnsPreference();
|
||||
|
||||
table.css("padding-bottom", 0);
|
||||
tableContainer.css("visibility", "visible");
|
||||
|
||||
/* If we have a hash in the url try and highlight that item in the table */
|
||||
if (window.location.hash){
|
||||
var highlight = $("table a[name="+window.location.hash.replace('#',''));
|
||||
if (highlight.length > 0){
|
||||
highlight.parents("tr").addClass('highlight');
|
||||
window.scroll(0, highlight.position().top - 50);
|
||||
}
|
||||
}
|
||||
|
||||
table.trigger("table-done", [tableData.total, tableParams]);
|
||||
}
|
||||
|
||||
function setupTableChrome(tableData){
|
||||
if (tableChromeDone === true)
|
||||
return;
|
||||
|
||||
var tableHeadRow = table.find("thead > tr");
|
||||
var editColMenu = $("#table-chrome-"+ctx.tableName).find(".editcol");
|
||||
|
||||
tableHeadRow.html("");
|
||||
editColMenu.html("");
|
||||
|
||||
tableParams.default_orderby = tableData.default_orderby;
|
||||
|
||||
if (!tableParams.orderby && tableData.default_orderby){
|
||||
tableParams.orderby = tableData.default_orderby;
|
||||
}
|
||||
|
||||
/* Add table header and column toggle menu */
|
||||
var column_edit_entries = [];
|
||||
for (var i in tableData.columns){
|
||||
var col = tableData.columns[i];
|
||||
if (col.displayable === false) {
|
||||
continue;
|
||||
}
|
||||
var header = $("<th></th>");
|
||||
header.prop("class", col.field_name);
|
||||
|
||||
/* Setup the help text */
|
||||
if (col.help_text.length > 0) {
|
||||
var help_text = $('<span class="glyphicon glyphicon-question-sign get-help"> </span>');
|
||||
help_text.tooltip({title: col.help_text});
|
||||
header.append(help_text);
|
||||
}
|
||||
|
||||
/* Setup the orderable title */
|
||||
if (col.orderable) {
|
||||
var title = $('<a href=\"#\" ></a>');
|
||||
|
||||
title.data('field-name', col.field_name);
|
||||
title.attr('data-sort-field', col.field_name);
|
||||
title.text(col.title);
|
||||
title.click(sortColumnClicked);
|
||||
|
||||
header.append(title);
|
||||
|
||||
header.append(' <i class="icon-caret-down" style="display:none"></i>');
|
||||
header.append(' <i class="icon-caret-up" style="display:none"></i>');
|
||||
|
||||
/* If we're currently ordered setup the visual indicator */
|
||||
if (col.field_name === tableParams.orderby ||
|
||||
'-' + col.field_name === tableParams.orderby){
|
||||
header.children("a").addClass("sorted");
|
||||
|
||||
if (tableParams.orderby.indexOf("-") === -1){
|
||||
header.find('.icon-caret-down').show();
|
||||
} else {
|
||||
header.find('.icon-caret-up').show();
|
||||
}
|
||||
}
|
||||
|
||||
if (col.field_name === tableData.default_orderby){
|
||||
title.addClass("default-orderby");
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Not orderable */
|
||||
header.css("font-weight", "normal");
|
||||
header.append('<span class="text-muted">' + col.title + '</span> ');
|
||||
}
|
||||
|
||||
/* Setup the filter button */
|
||||
if (col.filter_name){
|
||||
var filterBtn = $('<a href="#" role="button" data-filter-on="' + col.filter_name + '" class="pull-right btn btn-link btn-xs" data-toggle="modal"><i class="glyphicon glyphicon-filter filtered"></i></a>');
|
||||
|
||||
filterBtn.data('filter-name', col.filter_name);
|
||||
filterBtn.prop('id', col.filter_name);
|
||||
filterBtn.click(filterOpenClicked);
|
||||
|
||||
/* If we're currently being filtered setup the visial indicator */
|
||||
if (tableParams.filter &&
|
||||
tableParams.filter.match('^'+col.filter_name)) {
|
||||
|
||||
filterBtnActive(filterBtn, true);
|
||||
}
|
||||
header.append(filterBtn);
|
||||
}
|
||||
|
||||
/* Done making the header now add it */
|
||||
tableHeadRow.append(header);
|
||||
|
||||
/* Now setup the checkbox state and click handler */
|
||||
var toggler = $('<li><div class="checkbox"><label><input type="checkbox" id="checkbox-'+ col.field_name +'" class="col-toggle" value="'+col.field_name+'" />'+col.title+'</label></div></li>');
|
||||
|
||||
var togglerInput = toggler.find("input");
|
||||
|
||||
togglerInput.attr("checked","checked");
|
||||
|
||||
/* If we can hide the column enable the checkbox action */
|
||||
if (col.hideable){
|
||||
togglerInput.click(colToggleClicked);
|
||||
} else {
|
||||
toggler.find("label").addClass("text-muted");
|
||||
toggler.find("label").parent().addClass("disabled");
|
||||
togglerInput.attr("disabled", "disabled");
|
||||
}
|
||||
|
||||
if (col.hidden) {
|
||||
defaultHiddenCols.push(col.field_name);
|
||||
}
|
||||
|
||||
/* Gather the Edit Column entries */
|
||||
column_edit_entries.push({'title':col.title,'html':toggler});
|
||||
|
||||
} /* End for each column */
|
||||
|
||||
/* Append the sorted Edit Column toggler entries */
|
||||
column_edit_entries.sort(function(a,b) {return (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0);} );
|
||||
for (var col in column_edit_entries){
|
||||
editColMenu.append(column_edit_entries[col].html);
|
||||
}
|
||||
|
||||
tableChromeDone = true;
|
||||
}
|
||||
|
||||
/* Toggles the active state of the filter button */
|
||||
function filterBtnActive(filterBtn, active){
|
||||
if (active) {
|
||||
filterBtn.removeClass("btn-link");
|
||||
filterBtn.addClass("btn-primary");
|
||||
|
||||
filterBtn.tooltip({
|
||||
html: true,
|
||||
title: '<button class="btn btn-sm btn-primary" onClick=\'$("#clear-filter-btn-'+ ctx.tableName +'").click();\'>Clear filter</button>',
|
||||
placement: 'bottom',
|
||||
delay: {
|
||||
hide: 1500,
|
||||
show: 400,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
filterBtn.removeClass("btn-primary");
|
||||
filterBtn.addClass("btn-link");
|
||||
filterBtn.tooltip('destroy');
|
||||
}
|
||||
}
|
||||
|
||||
/* Display or hide table columns based on the cookie preference or defaults */
|
||||
function loadColumnsPreference(){
|
||||
var cookie_data = $.cookie("cols");
|
||||
|
||||
if (cookie_data) {
|
||||
var cols_hidden = JSON.parse($.cookie("cols"));
|
||||
|
||||
/* For each of the columns check if we should hide them
|
||||
* also update the checked status in the Edit columns menu
|
||||
*/
|
||||
$("#"+ctx.tableName+" th").each(function(){
|
||||
for (var i in cols_hidden){
|
||||
if ($(this).hasClass(cols_hidden[i])){
|
||||
table.find("."+cols_hidden[i]).hide();
|
||||
$("#checkbox-"+cols_hidden[i]).removeAttr("checked");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
/* Disable these columns by default when we have no columns
|
||||
* user setting.
|
||||
*/
|
||||
for (var i in defaultHiddenCols) {
|
||||
table.find("."+defaultHiddenCols[i]).hide();
|
||||
$("#checkbox-"+defaultHiddenCols[i]).removeAttr("checked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply an ordering to the current table.
|
||||
*
|
||||
* 1. Find the column heading matching the sortSpecifier
|
||||
* 2. Set its up/down arrow and add .sorted
|
||||
*
|
||||
* orderby: e.g. "-started_on", "completed_on"
|
||||
* colHeading: column heading element to activate (by showing the caret
|
||||
* up/down, depending on sort order); if not set, the correct column
|
||||
* heading is selected from the DOM using orderby as a key
|
||||
*/
|
||||
function applyOrderby(orderby, colHeading) {
|
||||
if (!orderby) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We only have one sort at a time so remove existing sort indicators
|
||||
$("#" + ctx.tableName + " th .icon-caret-down").hide();
|
||||
$("#" + ctx.tableName + " th .icon-caret-up").hide();
|
||||
$("#" + ctx.tableName + " th a").removeClass("sorted");
|
||||
|
||||
// normalise the orderby so we can use it to find the link we want
|
||||
// to style
|
||||
var fieldName = orderby;
|
||||
if (fieldName.indexOf('-') === 0) {
|
||||
fieldName = fieldName.slice(1);
|
||||
}
|
||||
|
||||
// find the table header element which corresponds to the sort field
|
||||
// (if we don't already have it)
|
||||
if (!colHeading) {
|
||||
colHeading = $('[data-sort-field="' + fieldName + '"]');
|
||||
}
|
||||
|
||||
colHeading.addClass("sorted");
|
||||
|
||||
var parent = colHeading.parent();
|
||||
|
||||
if (orderby.indexOf('-') === 0) {
|
||||
parent.children('.icon-caret-up').show();
|
||||
}
|
||||
else {
|
||||
parent.children('.icon-caret-down').show();
|
||||
}
|
||||
|
||||
tableParams.orderby = orderby;
|
||||
loadData(tableParams);
|
||||
}
|
||||
|
||||
function sortColumnClicked(e){
|
||||
e.preventDefault();
|
||||
|
||||
/* if we're already sorted sort the other way */
|
||||
var orderby = $(this).data('field-name');
|
||||
if (tableParams.orderby === orderby &&
|
||||
tableParams.orderby.indexOf('-') === -1) {
|
||||
orderby = '-' + orderby;
|
||||
}
|
||||
|
||||
applyOrderby(orderby, $(this));
|
||||
}
|
||||
|
||||
function pageButtonClicked(e) {
|
||||
tableParams.page = Number($(this).text());
|
||||
loadData(tableParams);
|
||||
/* Stop page jumps when clicking on # links */
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
/* Toggle a table column */
|
||||
function colToggleClicked (){
|
||||
var col = $(this).val();
|
||||
var disabled_cols = [];
|
||||
|
||||
if ($(this).prop("checked")) {
|
||||
table.find("."+col).show();
|
||||
} else {
|
||||
table.find("."+col).hide();
|
||||
// If we're ordered by the column we're hiding remove the order by
|
||||
// and apply the default one instead
|
||||
if (col === tableParams.orderby ||
|
||||
'-' + col === tableParams.orderby){
|
||||
tableParams.orderby = null;
|
||||
|
||||
applyOrderby(tableParams.default_orderby);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the cookie with the unchecked columns */
|
||||
$(".col-toggle").not(":checked").map(function(){
|
||||
disabled_cols.push($(this).val());
|
||||
});
|
||||
|
||||
$.cookie("cols", JSON.stringify(disabled_cols));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DOM/JS for the client side of a TableFilterActionToggle
|
||||
* or TableFilterActionDay
|
||||
*
|
||||
* filterName: (string) internal name for the filter action
|
||||
* filterActionData: (object)
|
||||
* filterActionData.count: (number) The number of items this filter will
|
||||
* show when selected
|
||||
*
|
||||
* NB this triggers a filtervalue event each time its radio button is checked
|
||||
*/
|
||||
function createActionRadio(filterName, filterActionData) {
|
||||
var hasNoRecords = (Number(filterActionData.count) == 0);
|
||||
|
||||
var actionStr = '<div class="radio">' +
|
||||
'<label class="filter-title' +
|
||||
(hasNoRecords ? ' text-muted' : '') + '"' +
|
||||
' for="' + filterName + '">' +
|
||||
'<input type="radio" name="filter"' +
|
||||
' value="' + filterName + '"';
|
||||
|
||||
if (hasNoRecords) {
|
||||
actionStr += ' disabled="disabled"';
|
||||
}
|
||||
|
||||
actionStr += ' id="' + filterName + '">' +
|
||||
'<input type="hidden" name="filter_value" value="on"' +
|
||||
' data-value-for="' + filterName + '">' +
|
||||
filterActionData.title +
|
||||
' (' + filterActionData.count + ')' +
|
||||
'</label>' +
|
||||
'</div>';
|
||||
|
||||
var action = $(actionStr);
|
||||
|
||||
// fire the filtervalue event from this action when the radio button
|
||||
// is active so that the apply button can be enabled
|
||||
action.find('[type="radio"]').change(function () {
|
||||
if ($(this).is(':checked')) {
|
||||
action.trigger('filtervalue', 'on');
|
||||
}
|
||||
});
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DOM/JS for the client side of a TableFilterActionDateRange
|
||||
*
|
||||
* filterName: (string) internal name for the filter action
|
||||
* filterValue: (string) from,to date range in format yyyy-mm-dd,yyyy-mm-dd;
|
||||
* used to select the current values for the from/to datepickers;
|
||||
* if this is partial (e.g. "yyyy-mm-dd,") only the applicable datepicker
|
||||
* will have a date pre-selected; if empty, neither will
|
||||
* filterActionData: (object) data for generating the action's HTML
|
||||
* filterActionData.title: label for the radio button
|
||||
* filterActionData.max: (string) maximum date for the pickers, in ISO 8601
|
||||
* datetime format
|
||||
* filterActionData.min: (string) minimum date for the pickers, ISO 8601
|
||||
* datetime
|
||||
*
|
||||
* NB this triggers a filtervalue event each time its radio button is checked
|
||||
*/
|
||||
function createActionDateRange(filterName, filterValue, filterActionData) {
|
||||
var action = $('<div class="radio">' +
|
||||
'<label class="filter-title"' +
|
||||
' for="' + filterName + '">' +
|
||||
'<input type="radio" name="filter"' +
|
||||
' value="' + filterName + '" ' +
|
||||
' id="' + filterName + '">' +
|
||||
'<input type="hidden" name="filter_value" value=""' +
|
||||
' data-value-for="' + filterName + '">' +
|
||||
filterActionData.title +
|
||||
'</label>' +
|
||||
'<div class="form-inline form-group date-filter-controls">' +
|
||||
'<input type="text" maxlength="10" class="form-control"' +
|
||||
' data-date-from-for="' + filterName + '">' +
|
||||
'<span>to</span>' +
|
||||
'<input type="text" maxlength="10" class="form-control"' +
|
||||
' data-date-to-for="' + filterName + '">' +
|
||||
'<span class="help-inline get-help">(yyyy-mm-dd)</span>' +
|
||||
'</div></div>');
|
||||
|
||||
var radio = action.find('[type="radio"]');
|
||||
var value = action.find('[data-value-for]');
|
||||
|
||||
// make the datepickers for the range
|
||||
var options = {
|
||||
dateFormat: 'yy-mm-dd',
|
||||
maxDate: new Date(filterActionData.max),
|
||||
minDate: new Date(filterActionData.min)
|
||||
};
|
||||
|
||||
// create date pickers, setting currently-selected from and to dates
|
||||
var selectedFrom = null;
|
||||
var selectedTo = null;
|
||||
|
||||
var selectedFromAndTo = [];
|
||||
if (filterValue) {
|
||||
selectedFromAndTo = filterValue.split(',');
|
||||
}
|
||||
|
||||
if (selectedFromAndTo.length == 2) {
|
||||
selectedFrom = selectedFromAndTo[0];
|
||||
selectedTo = selectedFromAndTo[1];
|
||||
}
|
||||
|
||||
options.defaultDate = selectedFrom;
|
||||
var inputFrom =
|
||||
action.find('[data-date-from-for]').datepicker(options);
|
||||
inputFrom.val(selectedFrom);
|
||||
|
||||
options.defaultDate = selectedTo;
|
||||
var inputTo =
|
||||
action.find('[data-date-to-for]').datepicker(options);
|
||||
inputTo.val(selectedTo);
|
||||
|
||||
// set filter_value based on date pickers when
|
||||
// one of their values changes; if either from or to are unset,
|
||||
// the new value is null;
|
||||
// this triggers a 'filter_value-change' event on the action's element,
|
||||
// which is used to determine the disabled/enabled state of the "Apply"
|
||||
// button
|
||||
var changeHandler = function () {
|
||||
var fromValue = inputFrom.val();
|
||||
var toValue = inputTo.val();
|
||||
|
||||
var newValue = undefined;
|
||||
if (fromValue !== '' && toValue !== '') {
|
||||
newValue = fromValue + ',' + toValue;
|
||||
}
|
||||
|
||||
value.val(newValue);
|
||||
|
||||
// if this action is selected, fire an event for the new range
|
||||
if (radio.is(':checked')) {
|
||||
action.trigger('filtervalue', newValue);
|
||||
}
|
||||
};
|
||||
|
||||
inputFrom.change(changeHandler);
|
||||
inputTo.change(changeHandler);
|
||||
|
||||
// check the associated radio button on clicking a date picker
|
||||
var checkRadio = function () {
|
||||
radio.prop('checked', 'checked');
|
||||
|
||||
// checking the radio button this way doesn't cause the "change"
|
||||
// event to fire, so we manually call the changeHandler
|
||||
changeHandler();
|
||||
};
|
||||
|
||||
inputFrom.focus(checkRadio);
|
||||
inputTo.focus(checkRadio);
|
||||
|
||||
// selecting a date in a picker constrains the date you can
|
||||
// set in the other picker
|
||||
inputFrom.change(function () {
|
||||
inputTo.datepicker('option', 'minDate', inputFrom.val());
|
||||
});
|
||||
|
||||
inputTo.change(function () {
|
||||
inputFrom.datepicker('option', 'maxDate', inputTo.val());
|
||||
});
|
||||
|
||||
// checking the radio input causes the "Apply" button disabled state to
|
||||
// change, depending on which from/to dates are supplied
|
||||
radio.change(changeHandler);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
function filterOpenClicked(){
|
||||
var filterName = $(this).data('filter-name');
|
||||
|
||||
/* We need to pass in the current search so that the filter counts take
|
||||
* into account the current search term
|
||||
*/
|
||||
var params = {
|
||||
'name' : filterName,
|
||||
'search': tableParams.search,
|
||||
'cmd': 'filterinfo',
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: ctx.url,
|
||||
data: params,
|
||||
headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
|
||||
success: function (filterData) {
|
||||
/*
|
||||
filterData structure:
|
||||
|
||||
{
|
||||
title: '<title for the filter popup>',
|
||||
filter_actions: [
|
||||
{
|
||||
title: '<label for radio button inside the popup>',
|
||||
name: '<name of the filter action>',
|
||||
count: <number of items this filter will show>,
|
||||
... additional data for the action ...
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
each filter_action gets a radio button; the value of this is
|
||||
set to filterName + ':' + filter_action.name; e.g.
|
||||
|
||||
in_current_project:in_project
|
||||
|
||||
specifies the "in_project" action of the "in_current_project"
|
||||
filter
|
||||
|
||||
the filterName is set on the column filter icon, and corresponds
|
||||
to a value in the table's filter map
|
||||
|
||||
when the filter popup's "Apply" button is clicked, the
|
||||
value for the radio button which is checked is passed in the
|
||||
querystring, along with a filter_value, and applied to the
|
||||
queryset on the table
|
||||
*/
|
||||
var filterActionRadios = $('#filter-actions-' + ctx.tableName);
|
||||
var filterApplyBtn = $('[data-cat="filter-apply"]');
|
||||
|
||||
var setApplyButtonState = function (e, filterActionValue) {
|
||||
if (filterActionValue !== undefined) {
|
||||
filterApplyBtn.removeAttr('disabled');
|
||||
}
|
||||
else {
|
||||
filterApplyBtn.attr('disabled', 'disabled');
|
||||
}
|
||||
};
|
||||
|
||||
$('#filter-modal-title-' + ctx.tableName).text(filterData.title);
|
||||
|
||||
filterActionRadios.empty();
|
||||
|
||||
// create a radio button + form elements for each action associated
|
||||
// with the filter on this column of the table
|
||||
for (var i in filterData.filter_actions) {
|
||||
var action = null;
|
||||
var filterActionData = filterData.filter_actions[i];
|
||||
var filterName = filterData.name + ':' +
|
||||
filterActionData.action_name;
|
||||
|
||||
if (filterActionData.type === 'toggle' ||
|
||||
filterActionData.type === 'day') {
|
||||
action = createActionRadio(filterName, filterActionData);
|
||||
}
|
||||
else if (filterActionData.type === 'daterange') {
|
||||
// current values for the from/to dates
|
||||
var filterValue = tableParams.filter_value;
|
||||
|
||||
action = createActionDateRange(
|
||||
filterName,
|
||||
filterValue,
|
||||
filterActionData
|
||||
);
|
||||
}
|
||||
|
||||
if (action) {
|
||||
// Setup the current selected filter; default to 'all' if
|
||||
// no current filter selected
|
||||
var radioInput = action.find('input[name="filter"]');
|
||||
if ((tableParams.filter &&
|
||||
tableParams.filter === radioInput.val()) ||
|
||||
filterActionData.action_name == 'all') {
|
||||
radioInput.prop("checked", "checked");
|
||||
}
|
||||
|
||||
filterActionRadios.append(action);
|
||||
|
||||
// if the action's filter_value changes but is falsy, disable
|
||||
// the "Apply" button
|
||||
action.on('filtervalue', setApplyButtonState);
|
||||
}
|
||||
}
|
||||
|
||||
$('#filter-modal-'+ctx.tableName).modal('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Allow pages to trigger reload event */
|
||||
table.on('reload', function(e, newTableParams){
|
||||
if (newTableParams)
|
||||
loadData(newTableParams);
|
||||
else
|
||||
loadData(tableParams)
|
||||
});
|
||||
|
||||
$(".get-help").tooltip({container:'body', html:true, delay:{show:300}});
|
||||
|
||||
/* Keep the Edit columns menu open after click by eating the event */
|
||||
$('.dropdown-menu').click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$(".pagesize-"+ctx.tableName).val(tableParams.limit);
|
||||
|
||||
/* page size selector */
|
||||
$(".pagesize-"+ctx.tableName).change(function(e){
|
||||
tableParams.limit = Number(this.value);
|
||||
if ((tableParams.page * tableParams.limit) > tableTotal)
|
||||
tableParams.page = 1;
|
||||
|
||||
loadData(tableParams);
|
||||
/* sync the other selectors on the page */
|
||||
$(".pagesize-"+ctx.tableName).val(this.value);
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$("#search-submit-"+ctx.tableName).click(function(e){
|
||||
e.preventDefault();
|
||||
var searchTerm = $("#search-input-"+ctx.tableName).val();
|
||||
|
||||
tableParams.page = 1;
|
||||
tableParams.search = searchTerm;
|
||||
|
||||
/* If a filter was active we remove it */
|
||||
if (tableParams.filter) {
|
||||
var filterBtn = $("#" + tableParams.filter.split(":")[0]);
|
||||
filterBtnActive(filterBtn, false);
|
||||
tableParams.filter = null;
|
||||
}
|
||||
|
||||
loadData(tableParams);
|
||||
});
|
||||
|
||||
clearSearchElements.click(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
tableParams.page = 1;
|
||||
tableParams.search = null;
|
||||
loadData(tableParams);
|
||||
|
||||
$("#search-input-"+ctx.tableName).val("");
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
$("#search-input-"+ctx.tableName).keyup(function(e){
|
||||
if (e.which === 13)
|
||||
$('#search-submit-'+ctx.tableName).click();
|
||||
});
|
||||
|
||||
/* Stop page jumps when clicking on # links */
|
||||
$('a[href="#"]').click(function(e){
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$("#clear-filter-btn-"+ctx.tableName).click(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
var filterBtn = $("#" + tableParams.filter.split(":")[0]);
|
||||
filterBtnActive(filterBtn, false);
|
||||
|
||||
tableParams.filter = null;
|
||||
loadData(tableParams);
|
||||
});
|
||||
|
||||
$("#filter-modal-form-"+ctx.tableName).submit(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
/* remove active status from all filter buttons so that only one filter
|
||||
can be active at a time */
|
||||
$('[data-filter-on]').each(function (index, filterBtn) {
|
||||
filterBtnActive($(filterBtn), false);
|
||||
});
|
||||
|
||||
// checked radio button
|
||||
var checkedFilter = $(this).find("input[name='filter']:checked");
|
||||
tableParams.filter = checkedFilter.val();
|
||||
|
||||
// hidden field holding the value for the checked filter
|
||||
var checkedFilterValue = $(this).find("input[data-value-for='" +
|
||||
tableParams.filter + "']");
|
||||
tableParams.filter_value = checkedFilterValue.val();
|
||||
|
||||
/* All === remove filter */
|
||||
if (tableParams.filter.match(":all$")) {
|
||||
tableParams.filter = null;
|
||||
tableParams.filter_value = null;
|
||||
} else {
|
||||
var filterBtn = $("#" + tableParams.filter.split(":")[0]);
|
||||
filterBtnActive(filterBtn, true);
|
||||
}
|
||||
|
||||
loadData(tableParams);
|
||||
|
||||
|
||||
$('#filter-modal-'+ctx.tableName).modal('hide');
|
||||
});
|
||||
|
||||
table.on("table-loading", function(){
|
||||
table.css("opacity", 0.5);
|
||||
});
|
||||
|
||||
table.on("table-done", function(){
|
||||
table.css("opacity", 1);
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
"use strict";
|
||||
/* Unit tests for Toaster's JS */
|
||||
|
||||
/* libtoaster tests */
|
||||
QUnit.test("Layer alert notification", function(assert) {
|
||||
var layer = {
|
||||
"layerdetailurl":"/toastergui/project/1/layer/22",
|
||||
"xhrLayerUrl":"/toastergui/xhr_layer/1/9",
|
||||
"vcs_url":"git://example.com/example.git",
|
||||
"detail":"[ git://example.com/example.git | master ]",
|
||||
"vcs_reference":"master",
|
||||
"id": 22,
|
||||
"name":"meta-example"
|
||||
};
|
||||
|
||||
var layerDepsList = [
|
||||
{
|
||||
"layerdetailurl":"/toastergui/project/1/layer/9",
|
||||
"xhrLayerUrl":"/toastergui/xhr_layer/1/9",
|
||||
"vcs_url":"git://example.com/example.git",
|
||||
"detail":"[ git://example.com/example.git | master ]",
|
||||
"vcs_reference":"master",
|
||||
"id": 9,
|
||||
"name":"meta-example-two"
|
||||
},
|
||||
{
|
||||
"layerdetailurl":"/toastergui/project/1/layer/9",
|
||||
"xhrLayerUrl":"/toastergui/xhr_layer/1/9",
|
||||
"vcs_url":"git://example.com/example.git",
|
||||
"detail":"[ git://example.com/example.git | master ]",
|
||||
"vcs_reference":"master",
|
||||
"id": 10,
|
||||
"name":"meta-example-three"
|
||||
},
|
||||
];
|
||||
|
||||
var msg = libtoaster.makeLayerAddRmAlertMsg(layer, layerDepsList, true);
|
||||
var test = $("<div></div>");
|
||||
|
||||
test.html(msg);
|
||||
|
||||
assert.equal(test.children("strong").text(), "3");
|
||||
assert.equal(test.children("a").length, 3);
|
||||
});
|
||||
|
||||
QUnit.test("Project info", function(assert){
|
||||
var done = assert.async();
|
||||
libtoaster.getProjectInfo(libtoaster.ctx.xhrProjectUrl, function(prjInfo){
|
||||
assert.ok(prjInfo.machine.name);
|
||||
assert.ok(prjInfo.layers.length > 0);
|
||||
assert.ok(prjInfo.freqtargets);
|
||||
assert.ok(prjInfo.release);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("Show notification", function(assert){
|
||||
var msg = "Testing";
|
||||
var element = $("#change-notification-msg");
|
||||
|
||||
libtoaster.showChangeNotification(msg);
|
||||
|
||||
assert.equal(element.text(), msg);
|
||||
assert.ok(element.is(":visible"));
|
||||
|
||||
$("#change-notification").hide();
|
||||
});
|
||||
|
||||
var layer = {
|
||||
"id": 1,
|
||||
"name": "meta-testing",
|
||||
"layerdetailurl": "/toastergui/project/1/layer/1",
|
||||
"xhrLayerUrl": "/toastergui/xhr_layer/1/1"
|
||||
};
|
||||
|
||||
QUnit.test("Add layer", function(assert){
|
||||
var done = assert.async();
|
||||
|
||||
/* Wait for the modal to be added to the dom */
|
||||
var checkModal = setInterval(function(){
|
||||
if ($("#dependencies-modal").length > 0) {
|
||||
$("#dependencies-modal .btn-primary").click();
|
||||
clearInterval(checkModal);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
/* Compare the number of layers before and after the add in the project */
|
||||
libtoaster.getProjectInfo(libtoaster.ctx.xhrProjectUrl, function(prjInfo){
|
||||
var origNumLayers = prjInfo.layers.length;
|
||||
|
||||
libtoaster.addRmLayer(layer, true, function(deps){
|
||||
libtoaster.getProjectInfo(libtoaster.ctx.xhrProjectUrl,
|
||||
function(prjInfo){
|
||||
assert.ok(prjInfo.layers.length > origNumLayers,
|
||||
"Layer not added to project");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("Rm layer", function(assert){
|
||||
var done = assert.async();
|
||||
|
||||
libtoaster.addRmLayer(layer, false, function(deps){
|
||||
assert.equal(deps.length, 0);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
QUnit.test("Parse url params", function(assert){
|
||||
var params = libtoaster.parseUrlParams();
|
||||
assert.ok(params);
|
||||
});
|
||||
|
||||
QUnit.test("Dump url params", function(assert){
|
||||
var params = libtoaster.dumpsUrlParams();
|
||||
assert.ok(params);
|
||||
});
|
||||
|
||||
QUnit.test("Make typeaheads", function(assert){
|
||||
var layersT = $("#layers");
|
||||
var machinesT = $("#machines");
|
||||
var projectsT = $("#projects");
|
||||
var recipesT = $("#recipes");
|
||||
|
||||
libtoaster.makeTypeahead(layersT,
|
||||
libtoaster.ctx.layersTypeAheadUrl, {}, function(){});
|
||||
|
||||
libtoaster.makeTypeahead(machinesT,
|
||||
libtoaster.ctx.machinesTypeAheadUrl, {}, function(){});
|
||||
|
||||
libtoaster.makeTypeahead(projectsT,
|
||||
libtoaster.ctx.projectsTypeAheadUrl, {}, function(){});
|
||||
|
||||
libtoaster.makeTypeahead(recipesT,
|
||||
libtoaster.ctx.recipesTypeAheadUrl, {}, function(){});
|
||||
|
||||
assert.ok(recipesT.data('ttTypeahead'));
|
||||
assert.ok(layersT.data('ttTypeahead'));
|
||||
assert.ok(projectsT.data('ttTypeahead'));
|
||||
assert.ok(recipesT.data('ttTypeahead'));
|
||||
});
|
||||
|
||||
|
||||
|
||||
/* Page init functions */
|
||||
|
||||
QUnit.test("Import layer page init", function(assert){
|
||||
assert.throws(importLayerPageInit({ xhrGitRevTypeAheadUrl: "url" }));
|
||||
});
|
||||
|
||||
QUnit.test("Project page init", function(assert){
|
||||
assert.throws(projectPageInit());
|
||||
});
|
||||
|
||||
QUnit.test("Layer details page init", function(assert){
|
||||
assert.throws(layerDetailsPageInit());
|
||||
});
|
||||
|
||||
QUnit.test("Layer btns init", function(assert){
|
||||
assert.throws(layerBtnsInit());
|
||||
});
|
||||
|
||||
QUnit.test("Table init", function(assert){
|
||||
assert.throws(tableInit({ url : ctx.tableUrl }));
|
||||
});
|
||||
|
||||
$(document).ajaxError(function(event, jqxhr, settings, errMsg){
|
||||
if (errMsg === 'abort')
|
||||
return;
|
||||
|
||||
QUnit.test("Ajax error", function(assert){
|
||||
assert.notOk(jqxhr.responseText);
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
10
sources/poky/bitbake/lib/toaster/toastergui/static/js/ui-bootstrap-tpls-0.11.0.min.js
vendored
Normal file
10
sources/poky/bitbake/lib/toaster/toastergui/static/js/ui-bootstrap-tpls-0.11.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user