mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-11-16 06:06:56 +08:00
1181 lines
31 KiB
Text
1181 lines
31 KiB
Text
// Custom jQuery function that finds elements including
|
|
// the parent element
|
|
$.fn.findWithSelf = function(selector) {
|
|
return this.filter(selector).add(this.find(selector));
|
|
};
|
|
|
|
var REPORT_CONTENT = "#report-content";
|
|
var SIDEBAR_PARENT_TREE = "#report-sidebar-tree";
|
|
var ADD_CONTENTS_FORM_ID = "#add-contents-form";
|
|
var SAVE_REPORT_FORM_ID = "#save-report-form";
|
|
|
|
var hotTableContainers = null;
|
|
var addContentsModal = null;
|
|
var addContentsModalBody = null;
|
|
var saveReportModal = null;
|
|
var saveReportModalBody = null;
|
|
|
|
var ignoreUnsavedWorkAlert;
|
|
|
|
/**
|
|
* INITIALIZATION FUNCTIONS
|
|
*/
|
|
|
|
/**
|
|
* Initializes page
|
|
*/
|
|
function init() {
|
|
initializeReportElements($(REPORT_CONTENT));
|
|
initializeGlobalReportSort();
|
|
initializePrintPopup();
|
|
initializeSaveToPdf();
|
|
initializeSaveReport();
|
|
initializeAddContentsModal();
|
|
initializeSidebarNavigation();
|
|
initializeUnsavedWorkDialog();
|
|
initTutorial();
|
|
|
|
$('.report-nav-link').each(function() {
|
|
truncateLongString($(this), <%= Constants::NAME_TRUNCATION_LENGTH %>);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the hands on table on the given
|
|
* element with the specified data.
|
|
* @param el - The jQuery element/s selector.
|
|
*/
|
|
function initializeHandsonTable(el) {
|
|
var input = el.siblings("input.hot-table-contents");
|
|
var inputObj = JSON.parse(input.attr("value"));
|
|
var data = inputObj.data;
|
|
|
|
// Special handling if this is a samples table
|
|
if (input.hasClass("hot-samples")) {
|
|
var headers = inputObj.headers;
|
|
var parentEl = el.closest(".report-module-samples-element");
|
|
var order = parentEl.attr("data-order") === "asc";
|
|
|
|
el.handsontable({
|
|
disableVisualSelection: true,
|
|
rowHeaders: true,
|
|
colHeaders: headers,
|
|
columnSorting: true,
|
|
editor: false,
|
|
copyPaste: false,
|
|
formulas: true
|
|
});
|
|
el.handsontable("getInstance").loadData(data);
|
|
el.handsontable("getInstance").sort(3, order);
|
|
|
|
// "Hack" to disable user sorting rows by clicking on
|
|
// header elements
|
|
el.handsontable("getInstance")
|
|
.addHook("afterRender", function() {
|
|
el.find(".colHeader.columnSorting")
|
|
.removeClass("columnSorting");
|
|
});
|
|
} else {
|
|
el.handsontable({
|
|
disableVisualSelection: true,
|
|
rowHeaders: true,
|
|
colHeaders: true,
|
|
editor: false,
|
|
copyPaste: false,
|
|
formulas: true
|
|
});
|
|
el.handsontable("getInstance").loadData(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the controls for the specified report element.
|
|
* @param el - The element in the report.
|
|
*/
|
|
function initializeElementControls(el) {
|
|
var controls = el.find(".report-element-header:first .controls");
|
|
controls.find("[data-action='sort-asc']").click(function(e) {
|
|
var el = $(this).closest(".report-element");
|
|
if (el.hasClass("report-comments-element")) {
|
|
sortCommentsElement(el, true);
|
|
} else if (el.hasClass("report-module-activity-element")) {
|
|
sortModuleActivityElement(el, true);
|
|
} else if (el.hasClass("report-module-samples-element")) {
|
|
sortModuleSamplesElement(el, true);
|
|
} else {
|
|
sortElementChildren(el, true, false);
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
controls.find("[data-action='sort-desc']").click(function(e) {
|
|
var el = $(this).closest(".report-element");
|
|
if (el.hasClass("report-comments-element")) {
|
|
sortCommentsElement(el, false);
|
|
} else if (el.hasClass("report-module-activity-element")) {
|
|
sortModuleActivityElement(el, false);
|
|
} else if (el.hasClass("report-module-samples-element")) {
|
|
sortModuleSamplesElement(el, false);
|
|
} else {
|
|
sortElementChildren(el, false, false);
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
controls.find("[data-action='move-up']").click(function(e) {
|
|
var el = $(this).closest(".report-element");
|
|
moveElement(el, true);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
controls.find("[data-action='move-down']").click(function(e) {
|
|
var el = $(this).closest(".report-element");
|
|
moveElement(el, false);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
controls.find("[data-action='remove']").click(function(e) {
|
|
var el = $(this).closest(".report-element");
|
|
if (el.hasClass("report-result-comments-element")) {
|
|
removeResultCommentsElement(el);
|
|
} else {
|
|
removeElement(el);
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize all the neccesary report elements stuff for all
|
|
* descendants of the provided parent DOM element.
|
|
* @param parentElement - The parent DOM element.
|
|
*/
|
|
function initializeReportElements(parentElement) {
|
|
// Initialize handsontable containers
|
|
_.each(parentElement.findWithSelf(".hot-table-container"), function(el) {
|
|
initializeHandsonTable($(el));
|
|
});
|
|
|
|
// Add event listeners element to controls
|
|
_.each(parentElement.findWithSelf(".report-element"), function(el) {
|
|
initializeElementControls($(el));
|
|
updateElementControls($(el));
|
|
});
|
|
|
|
// Initialize new element click actions
|
|
_.each(parentElement.findWithSelf(".new-element"), function(el) {
|
|
initializeNewElement($(el));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the new element click action.
|
|
* @param newEl - The "new element" element in the report.
|
|
*/
|
|
function initializeNewElement(newEl) {
|
|
newEl.click(function(e) {
|
|
var el = $(this);
|
|
var dh = $("#data-holder");
|
|
var parent = el.closest(".report-element");
|
|
|
|
var parentElementId;
|
|
var url;
|
|
var modalTitle;
|
|
|
|
if (!parent.length) {
|
|
// No parent, this means adding is done on report level
|
|
parentElementId = "null";
|
|
url = dh.data("add-project-contents-url");
|
|
modalTitle = dh.data("project-modal-title");
|
|
} else {
|
|
parentElementId = parent.data("id");
|
|
modalTitle = parent.data("modal-title");
|
|
|
|
// Select correct AJAX URL based on type
|
|
switch (parent.data("type")) {
|
|
case "experiment":
|
|
url = dh.data("add-experiment-contents-url"); break;
|
|
case "my_module":
|
|
url = dh.data("add-module-contents-url"); break;
|
|
}
|
|
}
|
|
|
|
// Send AJAX request to retrieve the modal contents
|
|
$.ajax({
|
|
url: url,
|
|
type: "GET",
|
|
dataType: "json",
|
|
data: {
|
|
id: parentElementId
|
|
},
|
|
success: function(data, status, jqxhr) {
|
|
// Open modal, set its title
|
|
addContentsModal.find(".modal-title").text(modalTitle);
|
|
|
|
// Display module contents and add checkbox logic
|
|
addContentsModalBody.html(data.html);
|
|
addContentsModalBody.checkboxTreeLogic();
|
|
|
|
// Bind to the ajax events of the modal form in its body
|
|
$(ADD_CONTENTS_FORM_ID)
|
|
.on("ajax:beforeSend", function(){
|
|
animateSpinner(this);
|
|
})
|
|
.on("ajax:success", function(e, xhr, opts, data) {
|
|
if (data.status == 204) {
|
|
// No content was added, simply hide modal
|
|
} else if (data.status == 200) {
|
|
// Add elements
|
|
addElements(el, data.responseJSON.elements);
|
|
|
|
// Update sidebar
|
|
initializeSidebarNavigation();
|
|
}
|
|
})
|
|
.on("ajax:error", function(e, xhr, settings, error) {
|
|
// TODO
|
|
})
|
|
.on("ajax:complete", function(){
|
|
animateSpinner(this, false);
|
|
addContentsModal.modal("hide");
|
|
});
|
|
|
|
// Execute any JS code the response might contain
|
|
_.each(addContentsModalBody.find("script"), function (script) {
|
|
eval(script.text);
|
|
});
|
|
|
|
// Finally, show the modal
|
|
addContentsModal.modal("show");
|
|
},
|
|
error: function(jqxhr, status, error) {
|
|
// TODO
|
|
}
|
|
});
|
|
|
|
// Prevent page reload
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the modal window for adding content.
|
|
*/
|
|
function initializeAddContentsModal() {
|
|
addContentsModal = $("#add-contents-modal");
|
|
addContentsModalBody = addContentsModal.find(".modal-body");
|
|
|
|
// Remove "add contents" modal content when modal
|
|
// window is closed
|
|
addContentsModal.on("hidden.bs.modal", function () {
|
|
addContentsModalBody.html("");
|
|
});
|
|
|
|
// Bind click action of "add" button in modal footer to
|
|
// submit the form in the modal body
|
|
addContentsModal.find(".modal-footer button[data-action='add']")
|
|
.click(function(e) {
|
|
$(ADD_CONTENTS_FORM_ID).submit();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the save report modal window etc.
|
|
* for saving the report.
|
|
*/
|
|
function initializeSaveReport() {
|
|
var dh = $("#data-holder");
|
|
var saveReportLink = $("#save-report-link");
|
|
|
|
saveReportModal = $("#save-report-modal");
|
|
saveReportModalBody = saveReportModal.find(".modal-body");
|
|
|
|
var modalContentsUrl = dh.data("save-report-url");
|
|
|
|
var reportId = dh.data("report-id");
|
|
|
|
// Remove "save report" modal content when modal
|
|
// window is closed
|
|
saveReportModal.on("hidden.bs.modal", function () {
|
|
saveReportModalBody.html("");
|
|
});
|
|
|
|
saveReportLink.click(function(e) {
|
|
// Send AJAX request to retrieve the modal contents
|
|
$.ajax({
|
|
url: modalContentsUrl,
|
|
type: "POST",
|
|
global: false,
|
|
dataType: "json",
|
|
data: {
|
|
id: reportId,
|
|
contents: JSON.stringify(constructReportContentsJson())
|
|
},
|
|
success: function(data, status, jqxhr) {
|
|
// Display module contents
|
|
saveReportModalBody.html(data.html);
|
|
|
|
// Bind to the ajax events of the modal form in its body
|
|
$(SAVE_REPORT_FORM_ID)
|
|
.on("ajax:beforeSend", function() {
|
|
animateSpinner(this);
|
|
})
|
|
.on("ajax:success", function(e, xhr, opts, data) {
|
|
if (data.status == 200) {
|
|
// Redirect back to index
|
|
|
|
// Turn off all hooks related to alert window
|
|
ignoreUnsavedWorkAlert = true;
|
|
$(window).off('beforeunload');
|
|
$(document).off('page:before-change');
|
|
|
|
$(location).attr("href", xhr.url);
|
|
}
|
|
})
|
|
.on("ajax:error", function(e, xhr, settings, error) {
|
|
// Display errors
|
|
if (xhr.status == 422) {
|
|
$(this).renderFormErrors("report", xhr.responseJSON);
|
|
} else {
|
|
// TODO
|
|
}
|
|
})
|
|
.on("ajax:complete", function () {
|
|
animateSpinner(this, false);
|
|
});
|
|
|
|
// Finally, show the modal
|
|
saveReportModal.modal("show");
|
|
},
|
|
error: function(jqxhr, status, error) {
|
|
// TODO
|
|
}
|
|
});
|
|
});
|
|
|
|
// Bind click action of "save" button in modal footer to
|
|
// submit the form in the modal body
|
|
saveReportModal.find(".modal-footer button[data-action='save']")
|
|
.click(function(e) {
|
|
$(SAVE_REPORT_FORM_ID).submit();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize global report sorting action.
|
|
*/
|
|
function initializeGlobalReportSort() {
|
|
$("#sort-report .dropdown-menu a[data-sort]").click(function(ev) {
|
|
var dh = $("#data-holder");
|
|
var $this = $(this);
|
|
var asc = true;
|
|
|
|
if ($(ev.target).data("sort") == "desc") {
|
|
asc = false;
|
|
}
|
|
|
|
if (confirm(dh.data("global-sort-text"))) {
|
|
sortWholeReport(asc);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the print popup functionality.
|
|
*/
|
|
function initializePrintPopup() {
|
|
$("#print-report").click(function() {
|
|
var html = $(REPORT_CONTENT).html();
|
|
var dh = $("#data-holder");
|
|
|
|
var print_window = window.open(
|
|
"",
|
|
"_blank",
|
|
"width=" + screen.width +
|
|
",height=" + screen.height +
|
|
",fullscreen=yes" +
|
|
",scrollbars=yes"
|
|
);
|
|
|
|
print_window.document.open();
|
|
print_window.document.write(
|
|
"<html><head>" +
|
|
"<link rel='stylesheet' href='" +
|
|
dh.data("stylesheet-url") +
|
|
"' />" +
|
|
"<title>" +
|
|
dh.data("print-title") +
|
|
"</title>" +
|
|
"</head>" +
|
|
"<body class='print-report-body' onload='window.print();'>" +
|
|
"<div class='print-report'>" +
|
|
html +
|
|
"</div>" +
|
|
"</body>" +
|
|
"</html>"
|
|
);
|
|
print_window.document.close();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the save to PDF functionality.
|
|
*/
|
|
function initializeSaveToPdf() {
|
|
var saveToPdfForm = $(".get-report-pdf-form");
|
|
var hiddenInput = saveToPdfForm.find("input[type='hidden']");
|
|
var saveToPdfBtn = saveToPdfForm.find("#get-report-pdf");
|
|
|
|
saveToPdfBtn.click(function(e) {
|
|
var content = $(REPORT_CONTENT);
|
|
|
|
// Fill hidden input element
|
|
hiddenInput.attr("value", content.html());
|
|
|
|
// Fire form submission
|
|
saveToPdfForm.submit();
|
|
|
|
// Clear form
|
|
hiddenInput.attr("value", "");
|
|
|
|
// Prevent page reload
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
}
|
|
|
|
function initializeUnsavedWorkDialog() {
|
|
var dh = $('#data-holder');
|
|
var alertText = dh.attr('data-unsaved-work-text');
|
|
|
|
ignoreUnsavedWorkAlert = false;
|
|
|
|
/**
|
|
* Before unload event logic
|
|
*/
|
|
function beforeUnload() {
|
|
var exit;
|
|
if (ignoreUnsavedWorkAlert) {
|
|
exit = true;
|
|
} else {
|
|
exit = confirm(alertText);
|
|
}
|
|
if (exit) {
|
|
// We leave the page so remove all listeners
|
|
$(window).off('beforeunload');
|
|
$(document).off('page:before-change');
|
|
}
|
|
return exit;
|
|
}
|
|
|
|
$(window).on('beforeunload', beforeUnload);
|
|
$(document).on('page:before-change', beforeUnload);
|
|
}
|
|
|
|
/**
|
|
* SIDEBAR CODE
|
|
*/
|
|
|
|
/**
|
|
* Get the sidebar <li> element for the specified report element.
|
|
* @param reportEl - The .report-element in the report.
|
|
* @return The corresponding sidebar <li>.
|
|
*/
|
|
function getSidebarEl(reportEl) {
|
|
var type = reportEl.data("type");
|
|
var id = reportEl.data("id");
|
|
return $(SIDEBAR_PARENT_TREE).find(
|
|
"li" +
|
|
"[data-type='" + type + "']" +
|
|
"[data-id='" + id + "']"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the report <div.report-element> element for the specified
|
|
* sidebar element.
|
|
* @param sidebarEl - The <li> sidebar element.
|
|
* @return The corresponding report element.
|
|
*/
|
|
function getReportEl(sidebarEl) {
|
|
var type = sidebarEl.data("type");
|
|
var id = sidebarEl.data("id");
|
|
return $(REPORT_CONTENT).find(
|
|
"div.report-element" +
|
|
"[data-type='" + type + "']" +
|
|
"[data-id='" + id + "']"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Initialize the sidebar navigation pane.
|
|
*/
|
|
function initializeSidebarNavigation() {
|
|
var reportContent = $(REPORT_CONTENT);
|
|
var treeParent = $(SIDEBAR_PARENT_TREE);
|
|
|
|
// Remove existing contents (also remove click listeners)
|
|
treeParent.find(".report-nav-link").off("click");
|
|
treeParent.children().remove();
|
|
|
|
// Re-populate the sidebar
|
|
_.each(reportContent.children(".report-element"), function(child) {
|
|
var li = initSidebarElement($(child));
|
|
li.appendTo(treeParent);
|
|
});
|
|
|
|
// Add click listener on all links
|
|
treeParent.find(".report-nav-link").click(function(e) {
|
|
var el = $(this).closest("li");
|
|
scrollToElement(el);
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return false;
|
|
});
|
|
|
|
// Call to sidebar function to re-initialize tree functionality
|
|
setupSidebarTree();
|
|
}
|
|
|
|
/**
|
|
* Recursive call to initialize sidebar elements.
|
|
* @param reportEl - The report element for which to
|
|
* generate the sidebar.
|
|
* @return A <li> jQuery element containing sidebar entry.
|
|
*/
|
|
function initSidebarElement(reportEl) {
|
|
var elChildrenContainer = reportEl.children(".report-element-children");
|
|
var type = reportEl.data("type");
|
|
var name = reportEl.data("name");
|
|
var id = reportEl.data("id");
|
|
var iconClass = "glyphicon " + reportEl.data("icon-class");
|
|
|
|
// Generate list element
|
|
var newLi = $(document.createElement("li"));
|
|
newLi
|
|
.attr("data-type", type)
|
|
.attr("data-id", id);
|
|
|
|
var newSpan = $(document.createElement("span"));
|
|
newSpan.appendTo(newLi);
|
|
var newI = $(document.createElement("i"));
|
|
newI.appendTo(newSpan);
|
|
var newHref = $(document.createElement("a"));
|
|
newHref
|
|
.attr("href", "")
|
|
.addClass("report-nav-link")
|
|
.text(name)
|
|
.appendTo(newSpan);
|
|
var newIcon = $(document.createElement("span"));
|
|
newIcon.addClass(iconClass).prependTo(newHref);
|
|
|
|
if (elChildrenContainer.length && elChildrenContainer.length > 0) {
|
|
var elChildren = elChildrenContainer.children(".report-element");
|
|
if (elChildren.length && elChildren.length > 0) {
|
|
var newUl = $(document.createElement("ul"));
|
|
newUl.appendTo(newLi);
|
|
|
|
_.each(elChildren, function(child) {
|
|
var li = initSidebarElement($(child));
|
|
li.appendTo(newUl);
|
|
});
|
|
}
|
|
}
|
|
|
|
return newLi;
|
|
}
|
|
|
|
/**
|
|
* Scroll to the specified element in the report.
|
|
* @param sidebarEl - The sidebar element.
|
|
*/
|
|
function scrollToElement(sidebarEl) {
|
|
var el = getReportEl(sidebarEl);
|
|
|
|
if (el.length && el.length == 1) {
|
|
var content = $("body");
|
|
content.scrollTo(
|
|
el,
|
|
{
|
|
axis: 'y',
|
|
duration: 500,
|
|
offset: -150
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* INDIVIDUAL ELEMENTS SORTING/MODIFYING FUNCTIONS
|
|
*/
|
|
|
|
/**
|
|
* Update the enabled/disabled state of element controls.
|
|
* @param el - The element in the report.
|
|
*/
|
|
function updateElementControls(el) {
|
|
var controls = el.find(".report-element-header:first .controls");
|
|
|
|
var isFirst = !el.prevAll(".report-element:not(.report-project-header-element)").length;
|
|
var isLast = !el.nextAll(".report-element").length;
|
|
|
|
controls.find("[data-action='move-up']")
|
|
.css("display", isFirst ? "none" : "");
|
|
controls.find("[data-action='move-down']")
|
|
.css("display", isLast ? "none" : "");
|
|
}
|
|
|
|
/**
|
|
* Sort the whole report with all of its elements.
|
|
* @param asc - True to sort in ascending order, false to sort
|
|
* in descending order.
|
|
*/
|
|
function sortWholeReport(asc) {
|
|
animateLoading();
|
|
var reportContent = $(REPORT_CONTENT);
|
|
var experimentElements = reportContent.children(".report-experiment-element");
|
|
var newEls = reportContent.children(".new-element");
|
|
|
|
if (
|
|
experimentElements.length === 0 || // Nothing to sort
|
|
experimentElements.length != newEls.length - 1 // This should never happen
|
|
) {
|
|
return;
|
|
}
|
|
|
|
experimentElements = _.sortBy(experimentElements, function(el) {
|
|
if (!asc)
|
|
{
|
|
return -$(el).data("ts");
|
|
}
|
|
return $(el).data("ts");
|
|
});
|
|
|
|
newEls.detach();
|
|
experimentElements = $(experimentElements);
|
|
experimentElements.detach();
|
|
|
|
// Re-insert the children into DOM
|
|
reportContent.append(newEls[0]);
|
|
for (var i = 0; i < experimentElements.length; i++) {
|
|
reportContent.append(experimentElements[i]);
|
|
reportContent.append(newEls[i + 1]);
|
|
}
|
|
|
|
// Finally, fix their controls
|
|
_.each(experimentElements, function(el) {
|
|
updateElementControls($(el));
|
|
sortElementChildren($(el), asc, true);
|
|
});
|
|
|
|
// Reinitialize sidebar
|
|
initializeSidebarNavigation();
|
|
animateLoading(false);
|
|
}
|
|
|
|
/**
|
|
* Sort the element's children.
|
|
* @param el - The element in the report.
|
|
* @param asc - True to sort in ascending order, false to sort
|
|
* in descending order.
|
|
* @param recursive - True to recursively sort the element's
|
|
* children; false otherwise.
|
|
*/
|
|
function sortElementChildren(el, asc, recursive) {
|
|
var childrenEl = el.find(".report-element-children:first");
|
|
var newEls = childrenEl.children(".new-element");
|
|
var children = childrenEl.children(".report-element");
|
|
|
|
if (
|
|
children.length === 0 || // No children, keep things
|
|
children.length != newEls.length - 1 // This should never happen
|
|
) {
|
|
return;
|
|
}
|
|
|
|
children = _.sortBy(children, function(child) {
|
|
if (!asc)
|
|
{
|
|
return -$(child).data("ts");
|
|
}
|
|
return $(child).data("ts");
|
|
});
|
|
|
|
newEls.detach();
|
|
children = $(children);
|
|
children.detach();
|
|
|
|
// Re-insert the children into DOM
|
|
childrenEl.append(newEls[0]);
|
|
for (var i = 0; i < children.length; i++) {
|
|
childrenEl.append(children[i]);
|
|
childrenEl.append(newEls[i + 1]);
|
|
}
|
|
|
|
// Finally, fix their controls
|
|
_.each(children, function(child) {
|
|
updateElementControls($(child));
|
|
if (recursive) {
|
|
sortElementChildren($(child), asc, true);
|
|
}
|
|
});
|
|
|
|
// Update sidebar
|
|
var prevEl = null;
|
|
_.each(children, function(child) {
|
|
var sidebarEl = getSidebarEl($(child));
|
|
if (sidebarEl.length && sidebarEl.length == 1) {
|
|
var sidebarParent = sidebarEl.closest("ul");
|
|
sidebarEl.detach();
|
|
if (prevEl === null) {
|
|
sidebarParent.prepend(sidebarEl);
|
|
} else {
|
|
prevEl.after(sidebarEl);
|
|
}
|
|
prevEl = sidebarEl;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sort the comments element (special handling needs to be
|
|
* done in this case).
|
|
* @param el - The comments element in the report.
|
|
* @param asc - True to sort in ascending order, false to sort
|
|
* in descending order.
|
|
*/
|
|
function sortCommentsElement(el, asc) {
|
|
var commentsList = el.find(".comments-list:first");
|
|
var comments = commentsList.children(".comment");
|
|
|
|
comments = _.sortBy(comments, function(comment) {
|
|
if (!asc)
|
|
{
|
|
return -$(comment).data("ts");
|
|
}
|
|
return $(comment).data("ts");
|
|
});
|
|
|
|
comments = $(comments);
|
|
comments.detach();
|
|
_.each(comments, function(comment) {
|
|
commentsList.append(comment);
|
|
});
|
|
|
|
// Update data attribute on sorting on the element
|
|
el.attr("data-order", asc ? "asc" : "desc");
|
|
}
|
|
|
|
/**
|
|
* Sort the module activity element (special handling needs
|
|
* to be done in this case).
|
|
* @param el - The module activity element in the report.
|
|
* @param asc - True to sort in ascending order, false to sort
|
|
* in descending order.
|
|
*/
|
|
function sortModuleActivityElement(el, asc) {
|
|
var activityList = el.find(".activity-list:first");
|
|
var activities = activityList.children(".activity");
|
|
|
|
activities = _.sortBy(activities, function(activity) {
|
|
if (!asc)
|
|
{
|
|
return -$(activity).data("ts");
|
|
}
|
|
return $(activity).data("ts");
|
|
});
|
|
|
|
activities = $(activities);
|
|
activities.detach();
|
|
_.each(activities, function(activity) {
|
|
activityList.append(activity);
|
|
});
|
|
|
|
// Update data attribute on sorting on the element
|
|
el.attr("data-order", asc ? "asc" : "desc");
|
|
}
|
|
|
|
/**
|
|
* Sort the module samples element (special handling needs
|
|
* to be done in this case).
|
|
* @param el - The module samples element in the report.
|
|
* @param asc - True to sort in ascending order, false to sort
|
|
* in descending order.
|
|
*/
|
|
function sortModuleSamplesElement(el, asc) {
|
|
var hotEl = el.find(".report-element-body .hot-table-container");
|
|
var hotInstance = hotEl.handsontable("getInstance");
|
|
|
|
hotInstance.sort(3, asc);
|
|
|
|
// Update data attribute on sorting on the element
|
|
el.attr("data-order", asc ? "asc" : "desc");
|
|
}
|
|
|
|
/**
|
|
* Move the specified element up or down.
|
|
* @param el - The element in the report.
|
|
* @param up - True to move element up; false to move it down.
|
|
*/
|
|
function moveElement(el, up) {
|
|
var prevNewEl = el.prev();
|
|
var nextNewEl = el.next();
|
|
if (
|
|
!prevNewEl.length ||
|
|
!prevNewEl.hasClass("new-element") ||
|
|
!nextNewEl.length ||
|
|
!nextNewEl.hasClass("new-element")) {
|
|
return;
|
|
}
|
|
|
|
var sidebarEl;
|
|
if (up) {
|
|
var prevEl = prevNewEl.prev();
|
|
if (!prevEl.length || !prevEl.hasClass("report-element")) {
|
|
return;
|
|
}
|
|
|
|
// Move sidebar element up
|
|
sidebarEl = getSidebarEl(el);
|
|
var sidebarPrev = sidebarEl.prev();
|
|
sidebarEl.detach();
|
|
sidebarPrev.before(sidebarEl);
|
|
|
|
el.detach();
|
|
nextNewEl.detach();
|
|
prevEl.before(el);
|
|
prevEl.before(nextNewEl);
|
|
updateElementControls(prevEl);
|
|
|
|
|
|
} else {
|
|
var nextEl = nextNewEl.next();
|
|
if (!nextEl.length || !nextEl.hasClass("report-element")) {
|
|
return;
|
|
}
|
|
|
|
// Move sidebar element up
|
|
sidebarEl = getSidebarEl(el);
|
|
var sidebarNext = sidebarEl.next();
|
|
sidebarEl.detach();
|
|
sidebarNext.after(sidebarEl);
|
|
|
|
prevNewEl.detach();
|
|
el.detach();
|
|
nextEl.after(el);
|
|
nextEl.after(prevNewEl);
|
|
updateElementControls(nextEl);
|
|
}
|
|
|
|
updateElementControls(el);
|
|
}
|
|
|
|
/**
|
|
* Remove the specified element (and all its children)
|
|
* from the report.
|
|
* @param el - The element in the report.
|
|
*/
|
|
function removeElement(el) {
|
|
var prevNewEl = el.prev();
|
|
var nextNewEl = el.next();
|
|
|
|
if (
|
|
!prevNewEl.length ||
|
|
!prevNewEl.hasClass("new-element") ||
|
|
!nextNewEl.length ||
|
|
!nextNewEl.hasClass("new-element")) {
|
|
return;
|
|
}
|
|
|
|
// TODO Remove event listeners
|
|
|
|
// Remove sidebar entry
|
|
var sidebarEl = getSidebarEl(el);
|
|
sidebarEl.remove();
|
|
|
|
prevNewEl.remove();
|
|
el.remove();
|
|
|
|
// Fix controls of previous / next element
|
|
var prevEl = prevNewEl.prev();
|
|
var nextEl = nextNewEl.next();
|
|
|
|
if (prevEl.length && prevEl.hasClass("report-element")) {
|
|
updateElementControls(prevEl);
|
|
}
|
|
if (nextEl.length && nextEl.hasClass("report-element")) {
|
|
updateElementControls(nextEl);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the specified comment element from the report.
|
|
* @param el - The comments element in the report.
|
|
*/
|
|
function removeResultCommentsElement(el) {
|
|
var parent = el.closest(".report-element-children");
|
|
|
|
// TODO Remove event listeners
|
|
|
|
// Remove sidebar entry
|
|
var sidebarEl = getSidebarEl(el);
|
|
sidebarEl.remove();
|
|
|
|
// Remove element, show the new element container
|
|
el.remove();
|
|
parent.children(".new-element").removeClass("hidden");
|
|
}
|
|
|
|
/**
|
|
* ADDING CONTENT FUNCTIONS
|
|
*/
|
|
|
|
/**
|
|
* Inject new elements into the place where the "old",
|
|
* new element <div> was previously located. Also take
|
|
* care of everything else.
|
|
* @param newElToBeReplaced - The "new element" to be replaced.
|
|
* @param elements - A JSON array of elements to be injected.
|
|
*/
|
|
function addElements(newElToBeReplaced, elements) {
|
|
var parent;
|
|
|
|
// Remove event listener on the new element to be replaced
|
|
newElToBeReplaced.off("click");
|
|
|
|
parent = newElToBeReplaced.parent();
|
|
newElToBeReplaced.addClass("original-new-el");
|
|
var prevEl = newElToBeReplaced;
|
|
var lastChild, secLastChild;
|
|
var newElements = [];
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var newEl = addElement(elements[i], prevEl);
|
|
prevEl = newEl;
|
|
newElements.push(newEl);
|
|
}
|
|
|
|
if (parent.length && parent.length > 0) {
|
|
// Remove a potential last new child element remaining
|
|
lastChild = parent.children().last();
|
|
if (lastChild.length && lastChild.length > 0) {
|
|
secLastChild = lastChild.prev();
|
|
if (secLastChild.length && secLastChild.length > 0) {
|
|
if (lastChild.hasClass("new-element") &&
|
|
secLastChild.hasClass("new-element")) {
|
|
// TODO Remove event listeners on existing element
|
|
|
|
lastChild.remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove the temporary class from all
|
|
// added new elements
|
|
$(".new-element.added-new-element")
|
|
.removeClass("added-new-element");
|
|
|
|
// Remove the "replaced" element
|
|
newElToBeReplaced.remove();
|
|
|
|
// Initialize everything on all elements
|
|
_.each(newElements, function(element) {
|
|
initializeReportElements($(element));
|
|
|
|
// Update previous and next element controls
|
|
updateElementControls(element.prev())
|
|
updateElementControls(element.next())
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add a single element to the DOM after the
|
|
* previous element. This function is recursive.
|
|
* @param jsonEl - The JSON element to be inserted.
|
|
* @param prevEl - The jQuery element preceeding
|
|
* the newly injected element.
|
|
* @returns The newly created jQuery element.
|
|
*/
|
|
function addElement(jsonEl, prevEl) {
|
|
var el = $(jsonEl.html);
|
|
|
|
// If such an element already exists in the report, remove it
|
|
// (this only applies to report elements)
|
|
if (el.hasClass("report-element")) {
|
|
var existing = $(REPORT_CONTENT)
|
|
.find(
|
|
".report-element" +
|
|
"[data-type='" + el.attr("data-type") + "']" +
|
|
"[data-id='" + el.attr("data-id") + "']"
|
|
);
|
|
if (existing.length && existing.length > 0) {
|
|
// TODO Remove event listeners on existing element
|
|
|
|
// First, remove the "new element" before the existing one
|
|
var prevNewEl = existing.prev();
|
|
if (prevNewEl.hasClass("new-element") &&
|
|
!prevNewEl.hasClass("original-new-el") &&
|
|
!prevNewEl.hasClass("added-new-element")) {
|
|
// TODO Remove event listeners on existing element
|
|
|
|
prevNewEl.remove();
|
|
}
|
|
existing.remove();
|
|
}
|
|
} else if (el.hasClass("new-element")) {
|
|
el.addClass("added-new-element");
|
|
}
|
|
|
|
// Add the new element after the previous one
|
|
prevEl.after(el);
|
|
|
|
// If element has children, recursively add them to the element
|
|
var children = jsonEl.children;
|
|
var childrenContainer = el.find(".report-element-children");
|
|
var child, prevChild;
|
|
var lastChild, secLastChild;
|
|
if (!_.isUndefined(children) && children.length > 0) {
|
|
for (var i = 0; i < children.length; i++) {
|
|
if (i === 0) {
|
|
// Make a "dummy" child so following children
|
|
// can be added after it recursively
|
|
var tmpDiv = $("<div class='hidden'></div>");
|
|
childrenContainer.append(tmpDiv);
|
|
child = addElement(children[i], tmpDiv);
|
|
tmpDiv.remove();
|
|
} else {
|
|
child = addElement(children[i], prevChild);
|
|
}
|
|
prevChild = child;
|
|
}
|
|
|
|
// Remove a potential last new child element remaining
|
|
lastChild = childrenContainer.children().last();
|
|
if (lastChild.length && lastChild.length > 0) {
|
|
secLastChild = lastChild.prev();
|
|
if (secLastChild.length && secLastChild.length > 0) {
|
|
if (lastChild.hasClass("new-element") &&
|
|
secLastChild.hasClass("new-element")) {
|
|
// TODO Remove event listeners on existing element
|
|
|
|
lastChild.remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return el;
|
|
}
|
|
|
|
/**
|
|
* Construct the report contents JSON. This is used
|
|
* when saving/updating the report.
|
|
* @return The JSON representation of report contents.
|
|
*/
|
|
function constructReportContentsJson() {
|
|
var res = [];
|
|
var rootEls = $(REPORT_CONTENT).children(".report-element");
|
|
|
|
_.each(rootEls, function(el) {
|
|
res.push(constructElementContentsJson($(el)));
|
|
});
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Recursive function to retrieve element JSON contents.
|
|
* @return The JSON representation of the element.
|
|
*/
|
|
function constructElementContentsJson(el) {
|
|
var jsonEl = {};
|
|
jsonEl["type_of"] = el.data("type");
|
|
jsonEl["id"] = el.data("id");
|
|
jsonEl["sort_order"] = null;
|
|
if (!_.isUndefined(el.data("order"))) {
|
|
jsonEl["sort_order"] = el.data("order");
|
|
}
|
|
|
|
var jsonChildren = [];
|
|
var childrenContainer = el.children(".report-element-children");
|
|
if (childrenContainer.length && childrenContainer.length == 1) {
|
|
var children = childrenContainer.children(".report-element");
|
|
_.each(children, function(child) {
|
|
jsonChildren.push(constructElementContentsJson($(child)));
|
|
});
|
|
}
|
|
jsonEl["children"] = jsonChildren;
|
|
|
|
return jsonEl;
|
|
}
|
|
|
|
/**
|
|
* Binds listeners to sidebar
|
|
* that truncate long strings
|
|
*/
|
|
function initializeReportSidebartruncation() {
|
|
var target = document.getElementById("report-sidebar-tree");
|
|
var observer = new MutationObserver(
|
|
function() {
|
|
$.each($("a.report-nav-link"),
|
|
function(){
|
|
truncateLongString($(this),
|
|
<%= Constants::NAME_TRUNCATION_LENGTH %>);
|
|
});
|
|
}
|
|
);
|
|
var config = { childList: true };
|
|
|
|
observer.observe(target, config);
|
|
}
|
|
|
|
/**
|
|
* Initializes tutorial
|
|
*/
|
|
function initTutorial() {
|
|
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
|
|
if (stepNum >= 20 && stepNum <= 21) {
|
|
ignoreUnsavedWorkAlert = true;
|
|
|
|
var nextPage = $('.navbar-brand').attr('href');
|
|
var steps = [{
|
|
element: $('.new-element')[0],
|
|
intro: I18n.t('tutorial.new_report_html'),
|
|
position: 'left'
|
|
}, {
|
|
element: $('#team-link')[0],
|
|
intro: I18n.t('tutorial.new_report_click_team_html',
|
|
{ private_team: $('#team-link span').last().text() }),
|
|
position: 'left'
|
|
}];
|
|
initPageTutorialSteps(20, 21, nextPage, function() {}, function() {
|
|
ignoreUnsavedWorkAlert = false;
|
|
},
|
|
steps);
|
|
}
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
// Check if we are actually at new report page
|
|
if ($(REPORT_CONTENT).length) {
|
|
init();
|
|
}
|
|
})
|
|
|
|
$(document).change(function(){
|
|
setTimeout(function(){
|
|
$(".report-nav-link").each( function(){
|
|
truncateLongString( $(this), <%= Constants::NAME_TRUNCATION_LENGTH %>);
|
|
});
|
|
}, 1000);
|
|
});
|