From 3c1afb45a0042fec005ab79e5c2ff432435a362f Mon Sep 17 00:00:00 2001 From: cdtank Date: Fri, 15 Apr 2011 18:36:20 +0000 Subject: [PATCH] 1. Completed the feature for ability to select organizations/people in temporal vis see http://issues.library.cornell.edu/browse/NIHVIVO-2022 2. Refactored legacy front-end code to remove unneceesary repetitive computations for activity count. 3. Fixed the overlay when parameter switch happens (earlier it used to go in the center of the screen). --- .../visualization/entitycomparison/layout.css | 25 +++ .../entitycomparison/gui-event-manager.js | 159 ++++++++++++++- .../js/visualization/entitycomparison/util.js | 190 ++++++++---------- .../entitycomparison/entityComparisonBody.ftl | 11 +- 4 files changed, 263 insertions(+), 122 deletions(-) diff --git a/productMods/css/visualization/entitycomparison/layout.css b/productMods/css/visualization/entitycomparison/layout.css index a9cd0398..4b09adfc 100644 --- a/productMods/css/visualization/entitycomparison/layout.css +++ b/productMods/css/visualization/entitycomparison/layout.css @@ -21,10 +21,35 @@ display: none; } +/* +This is for the overlay that happens when you change the parameter. Somehow the top +for the message is getting set to 50%, hence we need to override it here. +*/ +.blockElement { + top: 10px !important; +} + .easy-deselect-label a.temporal-vis-url { float: right; } +#people-organizations-filter { + margin-bottom: 9px; + display: none; +} + +.filter-option { + color: #2485AE; + cursor: pointer; + /*padding-right: 5px;*/ + text-decoration: underline; +} + +.active-filter { + color: #595B5B; + text-decoration: none; +} + .current-year-bar, .known-bar { display: inline-block; diff --git a/productMods/js/visualization/entitycomparison/gui-event-manager.js b/productMods/js/visualization/entitycomparison/gui-event-manager.js index aa380acf..fea447f6 100644 --- a/productMods/js/visualization/entitycomparison/gui-event-manager.js +++ b/productMods/js/visualization/entitycomparison/gui-event-manager.js @@ -17,8 +17,6 @@ $(document).ready(function() { graphContainer = $("#graphContainer"); tableDiv = $('#paginatedTable'); - //temporalGraphProcessor.initiateTemporalGraphRenderProcess(graphContainer, jsonString); - /* * When the intra-entity parameters are clicked, * update the status accordingly. @@ -52,7 +50,7 @@ $(document).ready(function() { }; setupLoadingScreen(options.responseContainer); - + $.ajax({ url: options.dataURL, dataType: "json", @@ -82,13 +80,16 @@ $(document).ready(function() { options.errorContainer.show(); options.responseContainer.unblock(); } else { + options.bodyContainer.show(); options.errorContainer.hide(); temporalGraphProcessor.redoTemporalGraphRenderProcess(graphContainer, data); options.responseContainer.unblock(); + } } }); + }); }); @@ -153,6 +154,27 @@ $("#copy-vis-viewlink-icon").live('click', function() { }); +$(".filter-option").live('click', function() { + + if (!$(this).hasClass('active-filter')) { + + if ($(this).attr('id') === 'people-filter') { + + $("#organizations-filter").removeClass('active-filter'); + temporalGraphProcessor.currentSelectedFilter = "PEOPLE"; + + } else if ($(this).attr('id') === 'organizations-filter') { + + $("#people-filter").removeClass('active-filter'); + temporalGraphProcessor.currentSelectedFilter = "ORGANIZATIONS"; + } + + $(this).addClass('active-filter'); + + temporalGraphProcessor.dataTable.fnDraw(); + } +}); + function getCurrentParameterVisViewLink() { return location.protocol + "//" + location.host + COMPARISON_PARAMETERS_INFO[currentParameter].viewLink; } @@ -200,8 +222,53 @@ function performEntityCheckboxClickedRedrawActions() { var processJSONData = { isParentEntityAvailable: false, + + currentEntityLevel: "Organizations", + + + /** + * This is used to find out the sum of all the counts of a particular entity. This is + * especially useful to render the bars below the line graph where it doesnt matter if + * a count has any associated year to it or not. + * @returns sum{values}. + */ + aggregateActivityCounts: function(allActivities) { + + var known = 0; + var unknown = 0; + var currentYear = 0; + + $.each(allActivities, function(index, data){ + + if (this[0] === -1) { + unknown += this[1]; + } else { + known += this[1]; + + if (this[0] === globalDateObject.getFullYear()) { + currentYear += this[1]; + } + } + + }); + + sum = { + knownYearCount: known, + unknownYearCount: unknown, + currentYearCount: currentYear + }; + return sum; + }, + setupGlobals: function(jsonContent) { + + var entityLevels = new Array(); + var entityActivityCount = { + PERSON: 0, + ORGANIZATION: 0 + }; + $.each(jsonContent, function (index, val) { /* @@ -215,6 +282,29 @@ var processJSONData = { if (val.lastCachedAtDateTime) { lastCachedAtDateTimes[lastCachedAtDateTimes.length] = val.lastCachedAtDateTime; } + + /* + * Setup value to be used to determine whether to show people or organizations filter, + * by default. + * */ + var activityCountInfoForEntity = processJSONData.aggregateActivityCounts(val.data); + + URIToEntityRecord[val.entityURI].activityCount = activityCountInfoForEntity; + + entityActivityCount[val.visMode] += + activityCountInfoForEntity.knownYearCount + + activityCountInfoForEntity.unknownYearCount; + + /* + * Setup the entity level + * */ + if (val.visMode === "PERSON"){ + entityLevels.push("People"); + } else if (val.visMode === "ORGANIZATION"){ + entityLevels.push("Organizations"); + } + + } else if (val.subjectEntityLabel) { /* @@ -230,13 +320,48 @@ var processJSONData = { processJSONData.isParentEntityAvailable = true; }); } + }); + if (entityActivityCount.ORGANIZATION >= entityActivityCount.PERSON) { + + temporalGraphProcessor.currentSelectedFilter = "ORGANIZATIONS"; + + $("#organizations-filter").addClass("active-filter"); + $("#people-filter").removeClass("active-filter"); + + + } else { + + temporalGraphProcessor.currentSelectedFilter = "PEOPLE"; + + $("#people-filter").addClass("active-filter"); + $("#organizations-filter").removeClass("active-filter"); + + } + if (processJSONData.isParentEntityAvailable) { $("#subject-parent-entity").show(); } else { $("#subject-parent-entity").hide(); } + + var uniqueEntityLevels = $.unique(entityLevels); + + /* + * This case is when organizations & people are mixed because both are directly attached + * to the parent organization. + * */ + if (uniqueEntityLevels.length > 1) { + processJSONData.currentEntityLevel = "Organizations & People"; + $("#people-organizations-filter").show(); + } else if (uniqueEntityLevels.length === 1) { + processJSONData.currentEntityLevel = uniqueEntityLevels[0]; + $("#people-organizations-filter").hide(); + } else { + /* To provide graceful degradation set entity level to a default error message.*/ + processJSONData.currentEntityLevel = "ENTITY LEVEL UNDEFINED ERROR"; + } }, /* @@ -248,8 +373,9 @@ var processJSONData = { processJSONData.setupGlobals(jsonData); - prepareTableForDataTablePagination(jsonData, dataTableParams); - setEntityLevel(getEntityVisMode(jsonData)); + temporalGraphProcessor.dataTable = prepareTableForDataTablePagination(jsonData, dataTableParams); + + setEntityLevel(processJSONData.currentEntityLevel); entityCheckboxOperatedOnEventListener(); }, @@ -263,8 +389,9 @@ var processJSONData = { processJSONData.setupGlobals(jsonData); - reloadDataTablePagination(preselectedEntityURIs, jsonData); - setEntityLevel(getEntityVisMode(jsonData)); + temporalGraphProcessor.dataTable = reloadDataTablePagination(preselectedEntityURIs, jsonData); + + setEntityLevel(processJSONData.currentEntityLevel); $("a#csv").attr("href", csvDownloadURL); } @@ -299,6 +426,8 @@ function entityCheckboxOperatedOnEventListener() { function renderTemporalGraphVisualization(parameters) { setupLoadingScreen(parameters.responseContainer); + + //return; getTemporalGraphData(parameters.dataURL, parameters.bodyContainer, @@ -319,8 +448,8 @@ function setupLoadingScreen(visContainerDIV) { $.blockUI.defaults.css.width = '500px'; $.blockUI.defaults.css.border = '0px'; - $.blockUI.defaults.css.top = '1%'; - + $.blockUI.defaults.css.top = '10px'; + visContainerDIV.block({ message: '

 Loading data for ' @@ -366,10 +495,12 @@ function getTemporalGraphData(temporalGraphDataURL, errorBodyDIV.show(); visContainerDIV.unblock(); } else { + graphBodyDIV.show(); errorBodyDIV.hide(); temporalGraphProcessor.initiateTemporalGraphRenderProcess(graphContainer, data); visContainerDIV.unblock(); + } }, complete: function() { @@ -431,6 +562,10 @@ var entitySelector = { temporalGraphProcessor = { loadingScreenTimeout: '', + + currentSelectedFilter: 'ORGANIZATIONS', + + dataTable: '', initiateTemporalGraphRenderProcess: function(givenGraphContainer, jsonData) { @@ -520,6 +655,12 @@ temporalGraphProcessor = { }); } + /* + * Table has to be redrawn now & not later to avoid checkboxes not being selected, which is + * caused if they are not visible at that point of time. + * */ + temporalGraphProcessor.dataTable.fnDraw(); + if ($("#incomplete-data-disclaimer").length > 0 && lastCachedAtDateTimes.length > 0) { var disclaimerText = "This information is based solely on " diff --git a/productMods/js/visualization/entitycomparison/util.js b/productMods/js/visualization/entitycomparison/util.js index 3a341601..029dedfd 100644 --- a/productMods/js/visualization/entitycomparison/util.js +++ b/productMods/js/visualization/entitycomparison/util.js @@ -170,7 +170,7 @@ } } }; - + $.fn.ellipsis = function () { return this.each(function () { var el = $(this); @@ -208,6 +208,35 @@ })(jQuery); +var DatatableCustomFilters = { + + peopleOrOrganizations: function(oSettings, aData, iDataIndex) { + + /* + * We know for a fact that the unique identifier for each row is the value for the checkbox, + * that is found in the first column for each row. + * */ + var row_data = aData[0]; + var entityURI = $(row_data).filter("input[type=checkbox]").val(); + + var currentEntityVisMode = URIToEntityRecord[entityURI].visMode; + + if (currentEntityVisMode === "ORGANIZATION" + && temporalGraphProcessor.currentSelectedFilter === "ORGANIZATIONS") { + return true; + } else if (currentEntityVisMode === "PERSON" + && temporalGraphProcessor.currentSelectedFilter === "PEOPLE") { + return true; + } else { +// console.log(entityURI); + return false; + } + + return true; + } + +}; + /** * init sets some initial options for the default graph. i.e for when the page @@ -224,10 +253,9 @@ function init(graphContainer) { $("#comparisonParameter").text("Total Number of " + optionSelected); $('#yaxislabel').html("Number of " + optionSelected).mbFlipText(false); $('#comparisonHeader').html(optionSelected).css('font-weight', 'bold'); + $('#legend-unknown-bar-text').text(COMPARISON_PARAMETERS_INFO[currentParameter].name + " with unknown year"); $('#legend-known-bar-text').text(COMPARISON_PARAMETERS_INFO[currentParameter].name + " with known year"); $('#legend-current-year-bar-text').text(COMPARISON_PARAMETERS_INFO[currentParameter].name + " from current incomplete year"); - $('#legend-unknown-bar-text').text(COMPARISON_PARAMETERS_INFO[currentParameter].name + " with unknown year"); - var defaultFlotOptions = { xaxis : { @@ -371,30 +399,23 @@ function stuffZerosIntoLineGraphs(jsonObject, year) { $.each(jsonObject, function(key, val) { - var position = normalizedYearRange.normalizedMinYear, i = 0; - - //console.log(key, val, position, (arrayOfMinAndMaxYears[1] - arrayOfMinAndMaxYears[0]) + 1); + var position = normalizedYearRange.normalizedMinYear, i = 0; - for (i = 0; i < normalizedYearRange.normalizedRange + 1; i++) { + for (i = 0; i < normalizedYearRange.normalizedRange + 1; i++) { - //console.log("val.data[i]", val.data[i]); - - if (val.data[i]) { + if (val.data[i]) { - if (val.data[i][0] != position - && position <= normalizedYearRange.normalizedMaxYear) { - val.data.splice(i, 0, [ position, 0 ]); - } - } - - else { - val.data.push( [ position, 0 ]); - } - position++; + if (val.data[i][0] != position + && position <= normalizedYearRange.normalizedMaxYear) { + val.data.splice(i, 0, [ position, 0 ]); } - }); - - //console.log("after stuffing", jsonObject); + } + else { + val.data.push( [ position, 0 ]); + } + position++; + } + }); } /** * During runtime, when the user checks/unchecks a checkbox, the zeroes have to @@ -473,7 +494,9 @@ function calcMaxOfComparisonParameter(allEntities) { var validCountsInData = new Array(); $.each(allEntities, function(key, currentEntity) { - combinedCount = calcSumOfComparisonParameter(currentEntity); + + combinedCount = currentEntity.activityCount; + validCountsInData.push(combinedCount.knownYearCount + combinedCount.unknownYearCount); }); @@ -503,41 +526,6 @@ function calcMaxWithinComparisonParameter(jsonObject){ return Math.max.apply(Math, validCountsInData); } -/** - * This is used to find out the sum of all the counts of a particular entity. This is - * especially useful to render the bars below the line graph where it doesnt matter if - * a count has any associated year to it or not. - * @returns sum{values}. - */ -function calcSumOfComparisonParameter(entity) { - - var known = 0; - var unknown = 0; - var currentYear = 0; - - $.each(entity.data, function(index, data){ - - if (this[0] === -1) { - unknown += this[1]; - } else { - known += this[1]; - - if (this[0] === globalDateObject.getFullYear()) { - currentYear += this[1]; - } - } - - }); - - sum = { - knownYearCount: known, - unknownYearCount: unknown, - currentYearCount: currentYear - }; - - return sum; -} - /** * A simple function to see if the passed * @@ -710,7 +698,7 @@ function createLegendRow(entity, bottomDiv) { function renderBarAndLabel(entity, divBar, divLabel, spanElement) { - var combinedCount = calcSumOfComparisonParameter(entity); + var combinedCount = entity.activityCount; var sum = combinedCount.knownYearCount + combinedCount.unknownYearCount; @@ -1040,6 +1028,13 @@ function removeCheckBoxFromGlobalSet(checkbox){ */ function prepareTableForDataTablePagination(jsonData, dataTableParams){ +// console.log(processJSONData.currentEntityLevel); + + if (processJSONData.currentEntityLevel.toUpperCase() === "ORGANIZATIONS & PEOPLE") { + $.fn.dataTableExt.afnFiltering.push(DatatableCustomFilters.peopleOrOrganizations); + } + + var table = $(''); table.attr('cellpadding', '0'); table.attr('cellspacing', '0'); @@ -1095,7 +1090,7 @@ function prepareTableForDataTablePagination(jsonData, dataTableParams){ var publicationCountTD = $('
'); - var combinedCount = calcSumOfComparisonParameter(val); + var combinedCount = val.activityCount; publicationCountTD.html(combinedCount.knownYearCount + combinedCount.unknownYearCount); @@ -1148,11 +1143,7 @@ function prepareTableForDataTablePagination(jsonData, dataTableParams){ entityListTable.fnFilter(""); }); - /* - var filterInfo = $(".filterInfo").detach(); - $("#infoContainer").append(filterInfo); - */ - + return entityListTable; } @@ -1162,12 +1153,23 @@ function prepareTableForDataTablePagination(jsonData, dataTableParams){ * returned is used to populate the pagination table. */ function reloadDataTablePagination(preselectedEntityURIs, jsonData){ - - /* - * In case no entities are selected, we want that redraw should happen so that top entities are - * pre-selected. - * */ - var shouldRedraw = preselectedEntityURIs.length ? false : true; + + if (processJSONData.currentEntityLevel.toUpperCase() === "ORGANIZATIONS & PEOPLE") { + + /* + * This will make sure that duplicate filters are not added. + * */ + if($.inArray(DatatableCustomFilters.peopleOrOrganizations, $.fn.dataTableExt.afnFiltering) < 0) { + $.fn.dataTableExt.afnFiltering.push(DatatableCustomFilters.peopleOrOrganizations); + } + } else { + + var indexOfPeopleOrOrganizationFilter = $.inArray(DatatableCustomFilters.peopleOrOrganizations, $.fn.dataTableExt.afnFiltering); + + if (indexOfPeopleOrOrganizationFilter >= 0) { + $.fn.dataTableExt.afnFiltering.splice(indexOfPeopleOrOrganizationFilter, 1); + } + } var currentDataTable = $('#datatable').dataTable(); @@ -1188,7 +1190,8 @@ function reloadDataTablePagination(preselectedEntityURIs, jsonData){ var labelTD = entity.label; - var combinedCount = calcSumOfComparisonParameter(entity); + var combinedCount = entity.activityCount; + var publicationCountTD = combinedCount.knownYearCount + combinedCount.unknownYearCount; var entityTypeTD = removeStopWords(entity); @@ -1201,13 +1204,7 @@ function reloadDataTablePagination(preselectedEntityURIs, jsonData){ /* * Dont redraw the table, so no sorting, no filtering. * */ - currentDataTable.fnAddData(newRow, shouldRedraw); - - /* - * Dont redraw the table, so no sorting, no filtering. - * */ - currentDataTable.fnDraw(shouldRedraw); - + currentDataTable.fnAddData(newRow, false); } /* @@ -1236,6 +1233,9 @@ function reloadDataTablePagination(preselectedEntityURIs, jsonData){ * We should change to the first page so that checkboxes are selectable. * */ currentDataTable.fnPageChange('first'); + + + return currentDataTable; } function updateRowHighlighter(linkedCheckBox){ @@ -1258,36 +1258,6 @@ function setEntityLevel(entityLevel){ $('#headerText').css("color", "#2485ae"); } -function getEntityVisMode(jsonData){ - - var entityLevels = new Array(); - - $.each(jsonData, function(index, val) { - if (val.visMode == "PERSON"){ - entityLevels.push("People"); - } else { - entityLevels.push("Organizations"); - } - }); - - var uniqueEntityLevels = $.unique(entityLevels); - - /* - * This case is when organizations & people are mixed because both are directly attached - * to the parent organization. - * */ - if (uniqueEntityLevels.length > 1) { - entityLevel = "Organizations & People"; - } else if (uniqueEntityLevels.length === 1) { - entityLevel = uniqueEntityLevels[0]; - } else { - /* To provide graceful degradation set entity level to a default error message.*/ - entitylevel = "ENTITY LEVEL UNDEFINED ERROR"; - } - - return entityLevel; -} - function toCamelCase(string){ return string ? (string.substr(0,1).toUpperCase() + string.substr(1, string.length-1).toLowerCase()) : ""; } diff --git a/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonBody.ftl b/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonBody.ftl index 417af4fe..58e5d163 100644 --- a/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonBody.ftl +++ b/productMods/templates/freemarker/visualization/entitycomparison/entityComparisonBody.ftl @@ -61,7 +61,12 @@ -

Who do you want to compare?

+

What do you want to compare?

+
+ Organizations + People +
+

Legend

+   ${currentParameterObject.name} with unknown year
  ${currentParameterObject.name} with known year
-   ${currentParameterObject.name} from current incomplete year
-   ${currentParameterObject.name} with unknown year +   ${currentParameterObject.name} from current incomplete year \ No newline at end of file