-
+
-
+
diff --git a/js/components/cohort-comparison-print-friendly.js b/js/components/cohort-comparison-print-friendly.js
index 52464746b..e895af1eb 100644
--- a/js/components/cohort-comparison-print-friendly.js
+++ b/js/components/cohort-comparison-print-friendly.js
@@ -1,10 +1,10 @@
-define(['jquery', 'knockout', 'text!./cohort-comparison-print-friendly.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'nvd3', 'css!./styles/nv.d3.min.css'],
+define(['jquery', 'knockout', 'text!./cohort-comparison-print-friendly.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis'],
function ($, ko, view, config, cohortComparison, options) {
function cohortComparisonPrintFriendly(params) {
var self = this;
self.config = config;
self.loading = ko.observable(true);
- self.cohortComparison = params.cohortComparison;
+ self.cohortComparison = params.cohortComparison;
}
var component = {
@@ -14,4 +14,4 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-print-friendly.html', 'a
ko.components.register('cohort-comparison-print-friendly', component);
return component;
- });
\ No newline at end of file
+ });
diff --git a/js/components/cohort-comparison-r-code.js b/js/components/cohort-comparison-r-code.js
index d50254e35..6f67d9b7f 100644
--- a/js/components/cohort-comparison-r-code.js
+++ b/js/components/cohort-comparison-r-code.js
@@ -1,9 +1,9 @@
-define(['jquery', 'knockout', 'text!./cohort-comparison-r-code.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'vocabularyprovider', 'nvd3', 'css!./styles/nv.d3.min.css', 'prism', 'css!./styles/prism.css'],
- function ($, ko, view, config, cohortComparison, vocabularyAPI, options) {
+define(['jquery', 'knockout', 'text!./cohort-comparison-r-code.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'prism', 'css!./styles/prism.css'],
+ function ($, ko, view, config) {
function cohortComparisonRCode(params) {
var self = this;
self.config = config;
- self.cohortComparison = params.cohortComparison;
+ self.cohortComparison = params.cohortComparison;
}
var component = {
@@ -13,4 +13,4 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-r-code.html', 'appConfig
ko.components.register('cohort-comparison-r-code', component);
return component;
- });
\ No newline at end of file
+ });
diff --git a/js/components/cohort-comparison-results.html b/js/components/cohort-comparison-results.html
new file mode 100644
index 000000000..e5f76d382
--- /dev/null
+++ b/js/components/cohort-comparison-results.html
@@ -0,0 +1,150 @@
+
PLEASE NOTE: THIS COMPONENT IS NO LONGER WORKING.
+
+
Available Data Sources
+
+
+
+
+
+ Generate
+
+ View
+
+ ( Results)
+
+
+
+
+
+
+
+
+
+
+
+
loading results
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The result of the analysis when comparing the rate of the outcome defined by the cohort ( ) between the treatment cohort ( ) and the comparator cohort ( ) is .
+
+
+
+
+
+
+
+
+
+
+
+
+ Description
+ Treated Persons
+ Comparator Persons
+ Treated Exposures
+ Comparator Exposures
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/components/cohort-comparison-results.js b/js/components/cohort-comparison-results.js
new file mode 100644
index 000000000..8886d5a69
--- /dev/null
+++ b/js/components/cohort-comparison-results.js
@@ -0,0 +1,763 @@
+/*
+ README: This component was originally written and working
+ with D3 v3 and utilized the nvd3 library: http://nvd3.org/
+ which is incompatable with D3 V4.
+
+ WARNING: The code in this component is here for reference and currently
+ in disrepair.
+*/
+define(['jquery',
+ 'knockout',
+ 'text!./cohort-comparison-results.html',
+ 'lodash',
+ 'appConfig',
+ 'ohdsi.util',
+ //'nvd3',
+ //'databindings/d3ChartBinding',
+ //'css!./styles/nv.d3.min.css',
+ ],
+ function ($, ko, view, _, config, ohdsiUtil) {
+ function cohortComparisonResults(params) {
+ var DEBUG = true;
+ var self = this;
+ self.cohortComparisonId = params.cohortComparisonId;
+ self.cohortComparison = params.cohortComparison;
+
+ self.config = config;
+ self.loading = ko.observable(true);
+ self.loadingExecution = ko.observable(false);
+ self.loadingExecutionFailure = ko.observable(false);
+ self.covariates = ko.observableArray();
+ self.currentExecutionId = ko.observable();
+ self.currentExecutionAuc = ko.observable();
+ self.poppropdist = ko.observableArray();
+ self.popprefdist = ko.observableArray();
+ self.psmodeldist = ko.observableArray();
+ self.attrition = ko.observableArray();
+ self.sources = ko.observableArray();
+ self.currentExecutionSourceName = ko.observable();
+ self.sourceHistoryDisplay = {};
+ self.sourceProcessingStatus = {};
+ self.sourceExecutions = {};
+ self.expressionMode = ko.observable('print');
+ self.om = ko.observable();
+ self.modifiedJSON = "";
+ self.importJSON = ko.observable();
+ self.expressionJSON = ko.pureComputed({
+ read: function () {
+ return ko.toJSON(self.cohortComparison(), function (key, value) {
+ if (value === 0 || value) {
+ delete value.analysisId;
+ delete value.name;
+ return value;
+ } else {
+ return
+ }
+ }, 2);
+ },
+ write: function (value) {
+ self.modifiedJSON = value;
+ }
+ });
+
+ // for new scatter balance chart
+ /*
+ self.chartObj = ko.observable();
+ self.domElement = ko.observable();
+ self.chartData = ko.observableArray(self.chartData && self.chartData() || []);
+ self.sharedCrossfilter = ko.observable(new ohdsiUtil.SharedCrossfilter([]));
+ window.scf = self.sharedCrossfilter();
+ self.chartData.subscribe(function(recs) {
+ self.sharedCrossfilter().replaceData(recs);
+ });
+ $(self.sharedCrossfilter()).on('filterEvt', function(evt, stuff) {
+ console.log("filter in sharedCrossfilter", stuff);
+ });
+ $(self.sharedCrossfilter()).on('newData', function(evt, stuff) {
+ console.log("new data in sharedCrossfilter; shouldn't happen much", stuff);
+ });
+ self.chartResolution = ko.observable(); // junk
+ self.jqEventSpace = params.jqEventSpace || {};
+ self.fields = ko.observable([]);
+ self.chartObj.subscribe(function(chart) {
+ });
+ self.ready = ko.computed(function() {
+ return self.chartObj() &&
+ self.chartData().length &&
+ self.domElement() &&
+ self.pillMode() === 'balance';
+ });
+ self.chartOptions = chartOptions();
+ self.ready.subscribe(function(ready) {
+ if (ready) {
+ initializeBalanceComponents();
+ }
+ });
+ var initializeBalanceComponents = _.once(function() {
+ var opts = _.merge(self.chartObj().defaultOptions, self.chartOptions);
+ var fields = _.map(opts,
+ (opt, name) => {
+ if (opt.isField) {
+ if (!(opt instanceof ohdsiUtil.Field)) {
+ opt = new ohdsiUtil.Field(name, opt, opts);
+ }
+ opt.bindParams({data:self.chartData()}, false);
+ }
+ return opts[name] = opt;
+ });
+ self.fields(fields);
+ self.chartObj().chartSetup(self.domElement(), 460, 150, opts);
+ self.chartObj().render(self.chartData(), self.domElement(), 460, 150, opts);
+ self.sharedCrossfilter().dimField('xy', opts.xy);
+ self.pillMode.subscribe(function(pillMode) {
+ if (pillMode === 'balance')
+ self.chartObj().render(self.chartData(), self.domElement(), 460, 150, opts);
+ });
+ //self.chartOptions.xy.accessor = self.chartOptions.xy.accessors.value;
+ });
+ $(self.jqEventSpace).on('brush', function(evt, {empty, x1,x2,y1,y2} = {}) {
+ //console.log('brush event', arguments);
+ var xyFilt;
+ if (empty) {
+ xyFilt = null;
+ util.deleteState('filters.brush');
+ } else {
+ xyFilt = ([x,y] = [], i) => {
+ return x >= x1 &&
+ x <= x2 &&
+ y >= y1 &&
+ y <= y2;
+ };
+ util.setState('filters.brush', {x1,x2,y1,y2});
+ }
+ self.sharedCrossfilter().filter('xy', xyFilt,
+ {source:'brush', x1, x2, y1, y2, empty});
+ });
+ $(self.sharedCrossfilter()).on('filterEvt',
+ function(evt, {dimField, source, x1, x2, y1, y2, empty, waitForMore} = {}) {
+ if (source === 'brush') {
+ // scatter has already zoomed.
+ if (empty) {
+ self.chartObj().cp.x.setZoomScale();
+ self.chartObj().cp.y.setZoomScale();
+ } else {
+ self.chartObj().cp.x.setZoomScale([x1,x2]);
+ self.chartObj().cp.y.setZoomScale([y1,y2]);
+ }
+ self.chartObj().updateData(self.sharedCrossfilter().dimRecs('xy'));
+ } else {
+ if (!waitForMore || waitForMore === 'done') {
+ self.chartObj().updateData(self.sharedCrossfilter().filteredRecs());
+ }
+ }
+ });
+ */
+ /*
+ function dataSetup(raw) {
+ var points = raw.map(d => ({
+ x: Math.abs(d.beforeMatchingStdDiff),
+ y: Math.abs(d.afterMatchingStdDiff),
+ tooltip: d.covariateName
+ }));
+ return points;
+ }
+ */
+
+ // end for balance chart
+
+ var initSources = config.api.sources.filter(s => s.hasCDM);
+ for (var i = 0; i < initSources.length; i++) {
+ self.sourceHistoryDisplay[initSources[i].sourceKey] = ko.observable(false);
+ self.sourceProcessingStatus[initSources[i].sourceKey] = ko.observable(false);
+ }
+
+ self.sources(initSources);
+
+ self.loadExecutions = function () {
+ // reset before load
+ $.each(self.sources(), function (i, s) {
+ if (!self.sourceExecutions[s.sourceKey]) {
+ self.sourceExecutions[s.sourceKey] = ko.observableArray();
+ } else {
+ self.sourceExecutions[s.sourceKey].removeAll();
+ }
+ });
+
+ ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId() + '/executions',
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (response) {
+
+ response = response.sort(function (a, b) {
+ return a.executed - b.executed;
+ });
+
+ $.each(response, function (i, d) {
+ var sourceKey = self.sources().filter(s => s.sourceId == d.sourceId)[0].sourceKey;
+ var executedTimestamp = new Date(d.executed);
+ d.executedCaption = executedTimestamp.toLocaleDateString() + ' ' + executedTimestamp.toLocaleTimeString();
+
+ var h = Math.floor(((d.duration % 31536000) % 86400) / 3600);
+ var m = Math.floor((((d.duration % 31536000) % 86400) % 3600) / 60);
+ var s = (((d.duration % 31536000) % 86400) % 3600) % 60;
+
+ d.durationCaption = ''
+ if (h > 0) d.durationCaption += h + 'h ';
+ if (m > 0) d.durationCaption += m + 'm ';
+ if (s > 0) d.durationCaption += s + 's ';
+
+ if (h == 0 && m == 0 && s == 0) {
+ d.durationCaption = 'n/a';
+ }
+
+ // this will ensure that if the last execution is still running that we don't allow additional executions to begin
+ if (d.executionStatus != 'COMPLETED') {
+ self.sourceProcessingStatus[sourceKey](true);
+ } else {
+ self.sourceProcessingStatus[sourceKey](false);
+ }
+
+ self.sourceExecutions[sourceKey].push(d);
+ });
+ }
+ });
+ }
+
+ self.loadExecutions();
+
+ self.toggleHistoryDisplay = function (sourceKey) {
+ self.sourceHistoryDisplay[sourceKey](!self.sourceHistoryDisplay[sourceKey]());
+ }
+
+ self.covariateColumns = [
+ {
+ title: 'Id',
+ data: 'id'
+ },
+ {
+ title: 'Name',
+ data: 'name'
+ },
+ {
+ title: 'Coefficient',
+ data: function (d) {
+ return d3.round(d.value, 2);
+ }
+ },
+ {
+ title: '|Coefficient|',
+ data: function (d) {
+ return d3.round(Math.abs(d.value), 2);
+ }
+ }
+ ];
+
+ self.covariateOptions = {
+ lengthMenu: [[10, -1], ['10', 'All']],
+ Facets: [
+ {
+ 'caption': 'Value',
+ 'binding': function (o) {
+ if (o.value > 2) {
+ return '> 2';
+ } else if (o.value < -2) {
+ return '< -2';
+ } else {
+ return 'Other';
+ }
+ }
+ },
+ {
+ 'caption': 'Analysis',
+ 'binding': function (o) {
+ return o.name.split(/[:,=]/)[0];
+ }
+ }
+ ]
+ };
+
+ self.resultsMode = ko.observable('sources');
+ self.pillMode = ko.observable('covariates');
+
+ self.pillMode.subscribe(function (d) {
+ window.setTimeout(function (d) {
+ window.dispatchEvent(new Event('resize'));
+ }, 1);
+ })
+
+ self.covariateSelected = function (d) {
+ console.log(d);
+ }
+
+ self.closeExecution = function () {
+ self.resultsMode('sources');
+ }
+
+ self.viewLastExecution = function (source) {
+ var executionCount = self.sourceExecutions[source.sourceKey]().length - 1;
+ for (var e = executionCount; e >= 0; e--) {
+ var execution = self.sourceExecutions[source.sourceKey]()[e];
+ if (execution.executionStatus == 'COMPLETED') {
+ self.executionSelected(execution);
+ break;
+ }
+ }
+ }
+
+ self.executionSelected = function (d) {
+ self.loadingExecutionFailure(false);
+ self.resultsMode('execution');
+ self.loadingExecution(true);
+
+ var sourceName = self.sources().filter(s => s.sourceId == d.sourceId)[0].sourceName;
+ self.currentExecutionSourceName(sourceName);
+
+ var p1 = ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/psmodel',
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (response) {
+ self.currentExecutionId(d.executionId);
+ self.currentExecutionAuc(response.auc);
+ self.covariates(response.covariates);
+ }
+ });
+
+ var p2 = ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/attrition',
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (response) {
+ self.attrition(response);
+ }
+ });
+
+ var p3 = ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/balance',
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (response) {
+
+ // self.chartData(response);
+
+ nv.addGraph(function () {
+ var points = response.map(d => ({
+ x: Math.abs(d.beforeMatchingStdDiff),
+ y: Math.abs(d.afterMatchingStdDiff),
+ tooltip: d.covariateName
+ }));
+ var data = [{
+ key: 'Covariates',
+ values: points
+ }];
+
+ var balanceChart = nv.models.scatterChart()
+ .showDistX(true)
+ .showDistY(true)
+ .color(
+ d3.scaleOrdinal()
+ .range(d3.schemeCategory10)
+ );
+
+ balanceChart.tooltip.contentGenerator(function (d) {
+ return '
';
+ });
+
+ //Axis settings
+ balanceChart.xAxis.tickFormat(d3.format('.02f'));
+ balanceChart.yAxis.tickFormat(d3.format('.02f'));
+
+ d3.select('#balanceChart svg')
+ .datum(data)
+ .call(balanceChart);
+
+ nv.utils.windowResize(balanceChart.update);
+
+ return balanceChart;
+ });
+ }
+ });
+
+ var p4 = ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/poppropdist',
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (response) {
+ self.poppropdist(response);
+
+ var data = [
+ {
+ values: response.map(d => ({
+ 'x': d.score,
+ 'y': d.comparator
+ })).sort(function (a, b) {
+ return a.x - b.x
+ }),
+ key: 'Comparator',
+ color: '#000088',
+ area: true
+ },
+ {
+ values: response.map(d => ({
+ 'x': d.score,
+ 'y': d.treatment
+ })).sort(function (a, b) {
+ return a.x - b.x
+ }),
+ key: 'Treatment',
+ color: '#880000',
+ area: true
+ }
+ ];
+
+ nv.addGraph(function () {
+ var matchedChart = nv.models.lineChart()
+ .useInteractiveGuideline(true)
+ .interpolate("basis");
+
+ matchedChart.duration(0);
+
+ d3.select("#popdistChart svg")
+ .datum(data)
+ .call(matchedChart);
+
+ nv.utils.windowResize(matchedChart.update);
+ return matchedChart;
+ });
+ }
+ });
+
+ var p5 = ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/psmodelpropscore',
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (response) {
+ var data = [
+ {
+ values: response.map(d => ({
+ 'x': d.score,
+ 'y': d.comparator
+ })).sort(function (a, b) {
+ return a.x - b.x
+ }),
+ key: 'Comparator',
+ color: '#000088',
+ area: true
+ },
+ {
+ values: response.map(d => ({
+ 'x': d.score,
+ 'y': d.treatment
+ })).sort(function (a, b) {
+ return a.x - b.x
+ }),
+ key: 'Treatment',
+ color: '#880000',
+ area: true
+ }
+ ];
+
+
+ nv.addGraph(function () {
+ var modelChart = nv.models.lineChart()
+ .useInteractiveGuideline(true)
+ .interpolate("basis")
+ .showYAxis(false);
+
+ modelChart.tooltip.enabled(false);
+
+ modelChart.duration(0);
+
+ d3.select("#psmodeldistChart svg")
+ .datum(data)
+ .call(modelChart);
+
+ nv.utils.windowResize(modelChart.update);
+ return modelChart;
+ });
+ }
+ });
+
+ var p6 = ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/om',
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (response) {
+ response.forEach(function (r) {
+ r.caption = r.estimate + ' (' + d3.round(r.lower95, 2) + '-' + d3.round(r.upper95, 2) + ')';
+ });
+ self.om(response);
+ }
+ });
+
+
+ Promise.all([p1, p2, p3, p4, p5])
+ .then(results => {
+ self.loadingExecution(false);
+ })
+ .catch(error => {
+ self.loadingExecution(false);
+ self.resultsMode('sources');
+ self.loadingExecutionFailure(true);
+ });
+ }
+
+ self.isHistoryVisible = function (d) {
+ return "fa fa-angle-double-down";
+ }
+
+ self.monitorJobExecution = function (jobExecutionId, sourceKey) {
+ setTimeout(function () {
+ ohdsiUtil.cachedAjax({
+ url: config.api.url + 'job/execution/' + jobExecutionId,
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (d) {
+ if (d.status == 'COMPLETED') {
+ completed = true;
+ self.sourceProcessingStatus[sourceKey](false);
+ self.loadExecutions();
+ } else {
+ self.monitorJobExecution(jobExecutionId, sourceKey);
+ }
+ }
+ });
+ }, 60000);
+ }
+
+ self.executeCohortComparison = function (sourceKey) {
+ self.sourceProcessingStatus[sourceKey](true);
+
+ var generatePromise = ohdsiUtil.cachedAjax({
+ url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId() + '/execute/' + sourceKey,
+ method: 'GET',
+ contentType: 'application/json',
+ success: function (c, status, xhr) {
+ self.monitorJobExecution(c.executionId, sourceKey);
+ }
+ });
+ };
+ }
+
+ var component = {
+ viewModel: cohortComparisonResults,
+ template: view
+ };
+
+ ko.components.register('cohort-comparison-results', component);
+ return component;
+
+ function chartOptions() {
+ var junk = 1;
+ return {
+ x: {
+ value: d => {
+ return Math.abs(d.beforeMatchingStdDiff);
+ },
+ label: 'Before matching StdDiff',
+ tooltipOrder: 1,
+ propName: 'beforeMatchingStdDiff',
+ isColumn: true,
+ colIdx: 1,
+ isField: true,
+ },
+ beforeMatchingDirection: {
+ value: d => d.beforeMatchingStdDiff > 0 ? "positive" : d.beforeMatchingStdDiff < 0 ? "negative" : "0",
+ tooltipOrder: 1.5,
+ isField: true,
+ isFacet: true,
+ label: "Direction before matching",
+ },
+ y: {
+ value: d => Math.abs(d.afterMatchingStdDiff),
+ label: "After matching StdDiff",
+ /*
+ format: d => {
+ var str = d.toString();
+ var idx = str.indexOf('.');
+ if (idx == -1) {
+ return d3.format('0%')(d);
+ }
+
+ var precision = (str.length - (idx+1) - 2).toString();
+ return d3.format('0.' + precision + '%')(d);
+ },
+ */
+ tooltipOrder: 2,
+ propName: 'afterMatchingStdDiff',
+ isColumn: true,
+ colIdx: 1,
+ isField: true,
+ },
+ afterMatchingDirection: {
+ value: d => d.afterMatchingStdDiff > 0 ? "positive" : d.afterMatchingStdDiff < 0 ? "negative" : "0",
+ tooltipOrder: 2.5,
+ isField: true,
+ isFacet: true,
+ label: "Direction after matching",
+ },
+ xy: { // for brushing
+ _accessors: {
+ value: {
+ func: function (d, allFields) {
+ return [
+ allFields.x.accessors.value(d),
+ allFields.y.accessors.value(d)];
+ },
+ posParams: ['d', 'allFields'],
+ },
+ },
+ isField: true,
+ },
+ /*
+ size: {
+ //value: d=>d.afterMatchingMeanTreated,
+ propName: 'afterMatchingMeanTreated',
+ //scale: d3.scale.log(),
+ label: "After matching mean treated",
+ tooltipOrder: 3,
+ isField: true,
+ _accessors: {
+ avg: {
+ posParams: ['data','allFields'],
+ func: (data, allFields) => {
+ return d3.mean(data.map(allFields.size.accessors.value));
+ },
+ },
+ tooltip: {
+ posParams: ['d','allFields'],
+ func: (d, allFields) => {
+ return {
+ name: `After matching mean treated
+ (avg: ${round(allFields.size.accessors.avg(),4)})`,
+ value: round(allFields.size.accessors.value(d), 4),
+ }
+ },
+ },
+ range: {
+ func: () => [1,8],
+ },
+ },
+ },
+ series: {
+ value: d => ['A','B','C','D'][Math.floor(Math.random() * 4)],
+ sortBy: d => d.afterMatchingStdDiff,
+ tooltipOrder: 5,
+ isField: true,
+ },
+ color: {
+ _accessors: {
+ value: {
+ func: function(d,i,j,allFields,data,series) {
+ return allFields.series.value(d,i,j,data,series);
+ },
+ posParams: ['d','i','j','allFields','data','series'],
+ },
+ domain: {
+ func: function(data, series, allFields) {
+ return _.uniq(data.map(allFields.series.value));
+ },
+ posParams: ['data', 'series', 'allFields'],
+ },
+ range: {
+ func: () => ['red', 'green', 'pink', 'blue'],
+ },
+ },
+ //value: d=>nthroot(d.coefficient, 7),
+ //value: d=>d.coefficient,
+ /*
+ ['NA','N/A','null','.']
+ .indexOf(d.coefficient &&
+ d.coefficient.toLowerCase()
+ .trim()) > -1
+ ? 0 : d.coefficient || 0, // (set NA = 0)
+ * /
+ //label: "Coefficient",
+ label: "Nonsense series",
+ scale: d3.scale.ordinal(),
+ //range: ['#ef8a62','#ddd','#67a9cf'],
+ //range: ['red', 'green', 'pink', 'blue'],
+ isField: true,
+ //domainFuncNeedsExtent: true,
+ //domainFunc: (data, ext) => [ext[0], 0, ext[1]],
+ /*
+ rangeFunc: (layout, prop) => {
+ prop.scale.rangePoints(
+ ['#ef8a62','#ddd','#67a9cf']);
+ },
+ domainFunc: (data, prop) => {
+ var vals = data.map(prop.value).sort(d3.ascending);
+ return vals;
+ var preScale = d3.scale.ordinal()
+ .domain(vals)
+ .rangePoints([-1, 0, 1]);
+
+
+ },
+ * /
+ //range: ['red', 'yellow', 'blue'],
+ },
+ shape: {
+ value: () => junk++ % 3,
+ label: "Random",
+ tooltipOrder: 4,
+ isField: true,
+ },
+ CIup: { // support CI in both directions
+ value: d => d.upperBound,
+ value: d => y(d) - d.upperBoundDiff,
+ },
+ */
+ covariateName: {
+ propName: 'covariateName',
+ value: d => {
+ return d.covariateName.split(/:/).shift();
+ },
+ isColumn: true,
+ isFacet: true,
+ colIdx: 0,
+ tooltipOrder: 7,
+ label: 'Covariate Name',
+ isField: true,
+ },
+ covariateValue: {
+ propName: 'covariateName',
+ value: d => d.covariateName.split(/:/).pop(),
+ isColumn: true,
+ colIdx: 0,
+ tooltipOrder: 8,
+ label: 'Covariate Value',
+ isField: true,
+ /*
+ _accessors: {
+ tooltip: {
+ posParams: ['d','allFields'],
+ func: (d, allFields) => {
+ return {
+ name: `Covariate value`,
+ value: allFields.covariateName.accessors.value(d).split(/:/).pop(),
+ }
+ },
+ },
+ },
+ */
+ },
+ /*
+ conceptId: {
+ propName: 'conceptId',
+ isColumn: true,
+ isFacet: true,
+ colIdx: 3,
+ tooltipOrder: 5,
+ label: 'Concept ID',
+ needsValueFunc: true, // so ChartProps will make one
+ // even though this isn't a normal
+ // zoomScatter field
+ isField: true,
+ },
+ */
+ };
+ }
+
+ });
diff --git a/js/components/cohort-comparison-spec-editor.html b/js/components/cohort-comparison-spec-editor.html
new file mode 100644
index 000000000..c14faf565
--- /dev/null
+++ b/js/components/cohort-comparison-spec-editor.html
@@ -0,0 +1,450 @@
+
+
+
+
Choose your target cohort:
+
+
+
+
+
+
+
+
The target cohort specified (# ) was deleted. Please select a different cohort.
+
+
+
Choose your comparator cohort:
+
+
+
+
+
+
+
+
The comparator cohort specified (# ) was deleted. Please select a different cohort.
+
+
+
+
Choose your outcome cohort:
+
+
+
+
+
+
+
+
+
Specify the statistical model used to estimate the risk of outcome between target and comparator cohorts:
+
+
+
+
+
+
Define the time-at-risk window start, relative to target/comparator cohort entry:
+
+ days from cohort start date
+
+
+
+
Define the time-at-risk window end:
+
+ days from
+
+
+
+
+
Minimum washout period applied to target and comparator cohorts:
+
+
+
+
+
+
Minimum required days at risk, applied to target and comparator cohorts:
+
+
+
+
+
+
Remove patients who enter both cohorts?
+
+
+
+
+
+
Remove patients who have observed the outcome prior to cohort entry?
+
+
+
+
+
+
Use propensity score adjustment as a confounding adjustment strategy for baseline covariates?
+
+
+
+
+
+
+
Which types of baseline covariates do you want to include in the propensity score model?
+
+
+
+ Condition aggregation
+
+
+
+
+
+
+
+
+
+
+
+
What concepts do you want to include in baseline covariates in the propensity score model? (Leave blank if you want to include everything)
+
+
+
+
+
+
+
+
The concept set specified (# ) was deleted. Please select a different concept set.
+
+
+
What concepts do you want to exclude from baseline covariates in the propensity score model? (Leave blank if you want to include everything)
+
+
+
+
+
+
+
+
The concept set specified (# ) was deleted. Please select a different concept set.
+
+
+
How do you want to restrict your cohorts based on the propensity score distribution?
+
+
+ Trim Fraction (1-100%):
+
+
+
+
+
+
Do you want to adjust for baseline covariates in the outcome model?
+
+
+
+
+
+
+
Which types of baseline covariates do you want to include in the outcome model?
+
+
+
+ Condition aggregation
+
+
+
+
+
+
+
+
+
+
+
+
What concepts do you want to include in baseline covariates in the outcome model? (Leave blank if you want to include everything)
+
+
+
+
+
+
+
+
The concept set specified (# ) was deleted. Please select a different concept set.
+
+
+
What concepts do you want to exclude from baseline covariates in the outcome model? (Leave blank if you want to include everything)
+
+
+
+
+
+
+
+
The concept set specified (# ) was deleted. Please select a different concept set.
+
+
+
+
What outcomes would you like to use as your negative controls? These are concepts known not to be associated with either the target or comparator group, such that we can assume the true relative risk should equal 1. These negative control outcomes will be used for empirical calibration.
+
+
+
+
+
+
+
+
The concept set specified (# ) was deleted. Please select a different concept set.
+
+
+
+
+
+
+
+
diff --git a/js/components/cohort-comparison-spec-editor.js b/js/components/cohort-comparison-spec-editor.js
new file mode 100644
index 000000000..10d5e011f
--- /dev/null
+++ b/js/components/cohort-comparison-spec-editor.js
@@ -0,0 +1,217 @@
+define(['jquery', 'knockout', 'text!./cohort-comparison-spec-editor.html',
+ 'webapi/CohortDefinitionAPI', 'appConfig', 'typeahead', 'bloodhound',
+ 'cohortcomparison/ComparativeCohortAnalysis', 'cohortbuilder/options',
+ 'cohortbuilder/CohortDefinition', 'vocabularyprovider', 'webapi/ComparativeCohortAnalysisAPI',
+ 'conceptsetbuilder/InputTypes/ConceptSet', 'cohortcomparison/OutcomeDefinition', 'ko.typeahead', ],
+ function ($, ko, view, cohortDefinitionAPI, config, typeahead, bloodhound,
+ ComparativeCohortAnalysis, options, CohortDefinition, vocabularyAPI, comparativeCohortAnalysisAPI,
+ ConceptSet, OutcomeDefinition) {
+ function cohortComparisonSpecificationEditor(params) {
+ var self = this;
+ self.cohortComparison = params.cohortComparison;
+ self.options = options;
+
+ self.outcomeCohortLoading = ko.observable(false);
+ self.outcomeLoadingMsg = ko.pureComputed(function () {
+ if (self.outcomeCohortLoading()) {
+ return "
Loading";
+ } else {
+ return "
";
+ }
+ })
+ self.outcomeListEmpty = ko.pureComputed(function () {
+ return (self.cohortComparison().outcomeList().length == 1 && self.cohortComparison().outcomeList()[0].outcomeId() == 0);
+ });
+
+ self.cohortDefinitionQueryURL = `${config.webAPIRoot}cohortdefinition/search?searchTerm=%QUERY`;
+ self.outcomeAutocompleteValue = ko.observable();
+ self.outcomeAutocompleteValue.subscribe(function (d) {
+ if (d != null && d.id != null) {
+ // Check to see if the outcome is already present.
+ var match = ko.utils.arrayFirst(self.cohortComparison().outcomeList(), function (item) {
+ return d.id === item.outcomeId();
+ });
+ if (!match) {
+ self.outcomeCohortLoading(true);
+ // Get the outcome cohort definition information from the server.
+ cohortDefinitionAPI.getCohortDefinition(d.id).then(function (cohortDefinition) {
+ od = new OutcomeDefinition();
+ od.outcomeId(d.id);
+ od.outcomeCaption(d.name);
+ cohortDefinition.expression = JSON.parse(cohortDefinition.expression);
+ od.outcomeCohortDefinition(new CohortDefinition(cohortDefinition));
+ self.cohortComparison().addOutcome(od);
+ self.outcomeCohortLoading(false);
+ });
+ } else {
+ console.log('It is already in the list!')
+ }
+ }
+ });
+ self.removeOutcomeCohort = function (id) {
+ console.log('Remove ' + id);
+ self.cohortComparison().outcomeList.remove(function (item) {
+ return item.outcomeId() == id;
+ });
+ }
+
+
+ self.cohortSelected = function (id) {
+ $('#modalCohortDefinition').modal('hide');
+ cohortDefinitionAPI.getCohortDefinition(id).then(function (cohortDefinition) {
+ self.targetId(cohortDefinition.id);
+ self.targetCaption(cohortDefinition.name);
+ cohortDefinition.expression = JSON.parse(cohortDefinition.expression);
+ self.targetCohortDefinition(new CohortDefinition(cohortDefinition));
+ });
+ }
+
+ self.conceptsetSelected = function (d) {
+ $('#modalConceptSet').modal('hide');
+ vocabularyAPI.getConceptSetExpression(d.id).then(function (csExpression) {
+ self.targetId(d.id);
+ self.targetCaption(d.name);
+ var conceptSetData = new ConceptSet({
+ id: d.id,
+ name: d.name,
+ expression: csExpression
+ });
+ self.targetExpression.removeAll();
+ self.targetExpression.push(conceptSetData);
+
+ vocabularyAPI.getConceptSetExpressionSQL(csExpression).then(
+ function (data) {
+ self.targetConceptSetSQL(data);
+ });
+ });
+ }
+
+ self.chooseTarget = function () {
+ $('#modalCohortDefinition').modal('show');
+ self.targetId = self.cohortComparison().targetComparator().targetId;
+ self.targetCaption = self.cohortComparison().targetComparator().targetCaption;
+ self.targetCohortDefinition = self.cohortComparison().targetComparator().targetCohortDefinition;
+ }
+
+ self.chooseComparator = function () {
+ $('#modalCohortDefinition').modal('show');
+ self.targetId = self.cohortComparison().targetComparator().comparatorId;
+ self.targetCaption = self.cohortComparison().targetComparator().comparatorCaption;
+ self.targetCohortDefinition = self.cohortComparison().targetComparator().comparatorCohortDefinition;
+ }
+
+ self.chooseOutcome = function () {
+ $('#modalCohortDefinition').modal('show');
+ self.targetId = self.cohortComparison().outcome().outcomeId;
+ self.targetCaption = self.cohortComparison().outcome().outcomeCaption;
+ self.targetCohortDefinition = self.cohortComparison().outcome().outcomeCohortDefinition;
+ }
+
+ self.clearTarget = function () {
+ self.cohortComparison().targetComparator().targetId(0);
+ self.cohortComparison().targetComparator().targetCaption(null);
+ self.cohortComparison().targetComparator().targetCohortDefinition(null);
+ }
+
+ self.clearComparator = function () {
+ self.cohortComparison().targetComparator().comparatorId(0);
+ self.cohortComparison().targetComparator().comparatorCaption(null);
+ self.cohortComparison().targetComparator().comparatorCohortDefinition(null);
+ }
+
+ self.clearOutcome = function () {
+ self.cohortComparison().outcome().outcomeId(0);
+ self.cohortComparison().outcome().outcomeCaption(null);
+ self.cohortComparison().outcome().outcomeCohortDefinition(null);
+ }
+
+ self.clearPsExclusion = function () {
+ self.cohortComparison().targetComparator().psExclusionId(0);
+ self.cohortComparison().targetComparator().psExclusionCaption(null);
+ self.cohortComparison().targetComparator().psExclusionConceptSet.removeAll();
+ self.cohortComparison().targetComparator().psExclusionConceptSetSQL(null);
+ }
+
+ self.clearPsInclusion = function () {
+ self.cohortComparison().targetComparator().psInclusionId(0);
+ self.cohortComparison().targetComparator().psInclusionCaption(null);
+ self.cohortComparison().targetComparator().psInclusionConceptSet.removeAll();
+ self.cohortComparison().targetComparator().psInclusionConceptSetSQL(null);
+ }
+
+ self.clearOmExclusion = function () {
+ self.cohortComparison().analysis().omExclusionId(0);
+ self.cohortComparison().analysis().omExclusionCaption(null);
+ self.cohortComparison().analysis().omExclusionConceptSet.removeAll();
+ self.cohortComparison().analysis().omExclusionConceptSetSQL(null);
+ }
+
+ self.clearOmInclusion = function () {
+ self.cohortComparison().analysis().omInclusionId(0);
+ self.cohortComparison().analysis().omInclusionCaption(null);
+ self.cohortComparison().analysis().omInclusionConceptSet.removeAll();
+ self.cohortComparison().analysis().omInclusionConceptSetSQL(null);
+ }
+
+ self.clearNegativeControl = function () {
+ self.cohortComparison().analysis().negativeControlId(0);
+ self.cohortComparison().analysis().negativeControlCaption(null);
+ self.cohortComparison().analysis().negativeControlConceptSet.removeAll();
+ self.cohortComparison().analysis().negativeControlConceptSetSQL(null);
+ }
+
+ self.choosePsExclusion = function () {
+ $('#modalConceptSet').modal('show');
+ self.targetId = self.cohortComparison().targetComparator().psExclusionId;
+ self.targetCaption = self.cohortComparison().targetComparator().psExclusionCaption;
+ self.targetExpression = self.cohortComparison().targetComparator().psExclusionConceptSet;
+ self.targetConceptSetSQL = self.cohortComparison().targetComparator().psExclusionConceptSetSQL;
+ }
+
+ self.choosePsInclusion = function () {
+ $('#modalConceptSet').modal('show');
+ self.targetId = self.cohortComparison().targetComparator().psInclusionId;
+ self.targetCaption = self.cohortComparison().targetComparator().psInclusionCaption;
+ self.targetExpression = self.cohortComparison().targetComparator().psInclusionConceptSet;
+ self.targetConceptSetSQL = self.cohortComparison().targetComparator().psInclusionConceptSetSQL;
+ }
+
+ self.chooseOmExclusion = function () {
+ $('#modalConceptSet').modal('show');
+ self.targetId = self.cohortComparison().analysis().omExclusionId;
+ self.targetCaption = self.cohortComparison().analysis().omExclusionCaption;
+ self.targetExpression = self.cohortComparison().analysis().omExclusionConceptSet;
+ self.targetConceptSetSQL = self.cohortComparison().analysis().omExclusionConceptSetSQL;
+ }
+
+ self.chooseOmInclusion = function () {
+ $('#modalConceptSet').modal('show');
+ self.targetId = self.cohortComparison().analysis().omInclusionId;
+ self.targetCaption = self.cohortComparison().analysis().omInclusionCaption;
+ self.targetExpression = self.cohortComparison().analysis().omInclusionConceptSet;
+ self.targetConceptSetSQL = self.cohortComparison().analysis().omInclusionConceptSetSQL;
+ }
+
+ self.chooseNegativeControl = function () {
+ $('#modalConceptSet').modal('show');
+ self.targetId = self.cohortComparison().analysis().negativeControlId;
+ self.targetCaption = self.cohortComparison().analysis().negativeControlCaption;
+ self.targetExpression = self.cohortComparison().analysis().negativeControlConceptSet;
+ self.targetConceptSetSQL = self.cohortComparison().analysis().negativeControlConceptSetSQL;
+ }
+
+ self.chooseConceptSet = function (conceptSetType, observable) {
+ self.targetObservable = observable;
+ $('#modalConceptSet').modal('show');
+ }
+ }
+
+ var component = {
+ viewModel: cohortComparisonSpecificationEditor,
+ template: view
+ };
+
+ ko.components.register('cohort-comparison-spec-editor', component);
+ return component;
+ }
+);
diff --git a/js/ko.typeahead.js b/js/ko.typeahead.js
new file mode 100644
index 000000000..8c9c2c21d
--- /dev/null
+++ b/js/ko.typeahead.js
@@ -0,0 +1,93 @@
+define(['jquery', 'knockout', 'bloodhound', 'typeahead'], function ($, ko, bloodhound) {
+ ko.bindingHandlers.typeahead = {
+ update: function (element, valueAccessor, allBindings) {
+ // http://stackoverflow.com/a/19366003/1247130 get value to update properly when typeahead choice is selected.
+
+ var templateName = ko.unwrap(allBindings().templateName);
+ var mapping = ko.unwrap(allBindings().mappingFunction);
+ var displayedProperty = ko.unwrap(allBindings().displayKey);
+ var value = allBindings.get("value");
+
+ var val = ko.unwrap(valueAccessor());
+ var remoteFilter = ko.unwrap(allBindings.get("remoteFilter"));
+ var wildcard = ko.unwrap(allBindings.get("wildcard")) || '%QUERY';
+ var auth = (allBindings.has("authToken")) ? {
+ "Authorization": "Bearer " + ko.unwrap(allBindings().authToken)
+ } : {};
+ var resultsLimit = allBindings.get("limit") || 10;
+ var datumTokenizer = ko.unwrap(allBindings.get("datumTokenizer")) || bloodhound.tokenizers.whitespace;
+
+ var localData = null;
+ var url = null;
+ var isLocal;
+ if (Array.isArray(val)) {
+ localData = val;
+ isLocal = true;
+ } else {
+ url = val;
+ isLocal = false;
+ }
+
+ // If the URL is specified, set up the remote filter for suggestions
+ var suggestions = null;
+ if (isLocal) {
+ suggestions = new bloodhound({
+ datumTokenizer: datumTokenizer,
+ queryTokenizer: bloodhound.tokenizers.whitespace,
+ local: localData
+ });
+ } else {
+ var remoteData = {
+ url: url,
+ wildcard: wildcard,
+ ajax: {
+ headers: auth
+ }
+ };
+ if (remoteFilter) {
+ remoteData.filter = remoteFilter;
+ };
+ suggestions = new bloodhound({
+ datumTokenizer: datumTokenizer,
+ queryTokenizer: bloodhound.tokenizers.whitespace,
+ remote: remoteData
+ });
+ }
+
+ suggestions.initialize();
+
+ $(element).typeahead("destroy");
+
+ var typeaheadOpts = {
+ source: suggestions.ttAdapter(),
+ displayKey: displayedProperty || function (item) {
+ return item;
+ },
+ limit: resultsLimit
+ };
+
+ if (templateName) {
+ typeaheadOpts.templates = {
+ suggestion: function (item) {
+ var temp = document.createElement("div");
+ var model = mapping ? mapping(item) : item;
+ ko.renderTemplate(templateName, model, null, temp, "replaceChildren");
+
+ return temp;
+ }
+ };
+ }
+
+ $(element)
+ .typeahead({
+ hint: true,
+ highlight: true
+ }, typeaheadOpts)
+ .on("typeahead:selected typeahead:autocompleted", function (e, suggestion) {
+ if (value && ko.isObservable(value)) {
+ value(suggestion);
+ }
+ });
+ }
+ };
+});
diff --git a/js/main.js b/js/main.js
index 9f1c0dce3..46a700972 100644
--- a/js/main.js
+++ b/js/main.js
@@ -69,6 +69,8 @@ requirejs.config({
"optional": "plugins/optional",
"clipboard": "clipboard.min",
"knockout": "knockout.min",
+ "typeahead": "plugins/typeahead.jquery.min",
+ "bloodhound": "plugins/bloodhound.min",
"ko.sortable": "https://cdnjs.cloudflare.com/ajax/libs/knockout-sortable/1.1.0/knockout-sortable.min",
"knockout-mapping": "knockout.mapping",
"datatables.net": "jquery.dataTables.min",
@@ -93,6 +95,8 @@ requirejs.config({
"cohort-comparison-browser": "components/cohort-comparison-browser",
"cohort-comparison-print-friendly": "components/cohort-comparison-print-friendly",
"cohort-comparison-r-code": "components/cohort-comparison-r-code",
+ "cohort-comparison-results": "components/cohort-comparison-results",
+ "cohort-comparison-spec-editor": "components/cohort-comparison-spec-editor",
"cohort-comparison-multi-r-code": "components/cohort-comparison-multi-r-code",
"user-bar": "components/user-bar",
"report-manager": "components/report-manager",
@@ -104,7 +108,7 @@ requirejs.config({
"cohortcomparison": "modules/cohortcomparison",
"r-manager": "components/r-manager",
"negative-controls": "components/negative-controls",
- "nvd3": "nv.d3",
+ //"nvd3": "nv.d3",
"atlascharts": "https://unpkg.com/@ohdsi/atlascharts@1.1.0/dist/atlascharts.min",
"jnj_chart": "jnj.chart", // scatterplot is not ported to separate library
"lodash": "lodash.4.15.0.full",
diff --git a/js/modules/WebAPIProvider/ComparativeCohortAnalysisAPI.js b/js/modules/WebAPIProvider/ComparativeCohortAnalysisAPI.js
new file mode 100644
index 000000000..c78809ba7
--- /dev/null
+++ b/js/modules/WebAPIProvider/ComparativeCohortAnalysisAPI.js
@@ -0,0 +1,68 @@
+define(function (require, exports) {
+
+ var $ = require('jquery');
+ var config = require('appConfig');
+
+ function getComparativeCohortAnalysisList() {
+ var promise = $.ajax({
+ url: config.api.url + 'comparativecohortanalysis',
+ method: 'GET',
+ contentType: 'application/json',
+ error: function (error) {
+ console.log("Error: " + error);
+ }
+ });
+ return promise;
+ }
+
+ function getComparativeCohortAnalysis(id) {
+ var promise = $.ajax({
+ url: config.api.url + 'comparativecohortanalysis/' + id,
+ method: 'GET',
+ contentType: 'application/json',
+ error: function (error) {
+ console.log("Error: " + error);
+ }
+ });
+ return promise;
+ }
+
+ function deleteAnalysis(analysisId) {
+ var promise = $.ajax({
+ url: config.api.url + 'comparativecohortanalysis/' + analysisId,
+ method: 'DELETE',
+ contentType: 'application/json',
+ error: function (error) {
+ console.log("Error: " + error);
+ }
+ });
+ return promise;
+ }
+
+ function saveAnalysis(analysis) {
+ var cca = analysis.jsonify();
+ var json = JSON.stringify(cca);
+
+ var promise = $.ajax({
+ url: config.api.url + 'comparativecohortanalysis/' + (analysis.analysisId || ''),
+ method: analysis.analysisId ? 'PUT' : 'POST',
+ contentType: 'application/json',
+ data: json,
+ dataType: 'json',
+ error: function (error) {
+ console.log("Error: " + error);
+ }
+ });
+
+ return promise;
+ }
+
+ var api = {
+ deleteAnalysis: deleteAnalysis,
+ getComparativeCohortAnalysis: getComparativeCohortAnalysis,
+ getComparativeCohortAnalysisList: getComparativeCohortAnalysisList,
+ saveAnalysis: saveAnalysis,
+ }
+
+ return api;
+});
diff --git a/js/modules/cohortcomparison/AnalysisDefinition.js b/js/modules/cohortcomparison/AnalysisDefinition.js
new file mode 100644
index 000000000..0144fc7be
--- /dev/null
+++ b/js/modules/cohortcomparison/AnalysisDefinition.js
@@ -0,0 +1,570 @@
+define(function (require, exports) {
+
+ var $ = require('jquery');
+ var ko = require('knockout');
+ var ConceptSet = require('conceptsetbuilder/InputTypes/ConceptSet')
+
+ function AnalysisDefinition(data, conceptSetList) {
+ var self = this;
+ var data = data || {};
+ var conceptSetData;
+ var selectedConceptSet;
+
+ // Options
+ self.modelTypeOptions = [
+ {
+ name: 'Logistic regression',
+ cmArgValue: '"logistic"',
+ rate: 'odds',
+ id: 1
+ }, {
+ name: 'Poisson regression',
+ cmArgValue: '"poisson"',
+ rate: 'rate',
+ id: 2
+ }, {
+ name: 'Cox proportional hazards',
+ cmArgValue: '"cox"',
+ rate: 'hazards',
+ id: 3
+ }];
+ self.timeAtRiskEndOptions = [
+ {
+ name: 'cohort end date',
+ id: 1
+ }, {
+ name: 'cohort start date',
+ id: 0
+ }];
+ self.trimOptions = [
+ {
+ name: 'None',
+ id: 0
+ }, {
+ name: 'by Percentile',
+ id: 1
+ }, {
+ name: 'by Equipoise',
+ id: 2
+ }];
+ self.matchingOptions = [
+ {
+ name: 'No matching/stratification',
+ id: 0
+ }, {
+ name: 'Matching',
+ id: 1
+ }, {
+ name: 'Stratification',
+ id: 2
+ }];
+
+ self.id = data.id;
+ self.description = ko.observable(data.description || null);
+ self.timeAtRiskStart = ko.observable(data.timeAtRiskStart != null ? data.timeAtRiskStart : 0);
+ self.timeAtRiskEnd = ko.observable(data.timeAtRiskEnd != null ? data.timeAtRiskEnd : 0);
+ self.addExposureDaysToEnd = ko.observable(data.addExposureDaysToEnd != null ? data.addExposureDaysToEnd : 1);
+ self.addExposureDaysToEndFormatted = ko.pureComputed(function () {
+ return self.addExposureDaysToEnd() == 1;
+ })
+ self.minimumWashoutPeriod = ko.observable(data.minimumWashoutPeriod != null ? data.minimumWashoutPeriod : 0);
+ self.minimumDaysAtRisk = ko.observable(data.minimumDaysAtRisk != null ? data.minimumDaysAtRisk : 0);
+ self.rmSubjectsInBothCohorts = ko.observable(data.rmSubjectsInBothCohorts != null ? data.rmSubjectsInBothCohorts : 1);
+ self.rmSubjectsInBothCohortsFormatted = ko.pureComputed(function () {
+ return self.rmSubjectsInBothCohorts() == 1;
+ });
+ self.rmPriorOutcomes = ko.observable(data.rmPriorOutcomes != null ? data.rmPriorOutcomes : 1);
+ self.rmPriorOutcomesFormatted = ko.pureComputed(function () {
+ return self.rmPriorOutcomes() == 1;
+ });
+
+ self.omExclusionId = ko.observable(data.omExclusionId != null ? data.omExclusionId : 0);
+ self.omExclusionCaption = ko.observable(null);
+ self.omExclusionConceptSet = ko.observableArray(null);
+ self.omExclusionConceptSetSQL = ko.observable(null);
+ if (conceptSetList && conceptSetList.length > 0) {
+ selectedConceptSet = $.grep(conceptSetList, function (f) {
+ return f.id == data.omExclusionId;
+ });
+ if (selectedConceptSet.length > 0) {
+ self.omExclusionCaption(selectedConceptSet[0].name);
+ conceptSetData = {
+ id: self.omExclusionId(),
+ name: self.omExclusionCaption(),
+ expression: selectedConceptSet[0].expression,
+ };
+ self.omExclusionConceptSet.push(new ConceptSet(conceptSetData));
+ self.omExclusionConceptSetSQL(selectedConceptSet[0].sql);
+ }
+ }
+
+ self.omInclusionId = ko.observable(data.omInclusionId != null ? data.omInclusionId : 0);
+ self.omInclusionCaption = ko.observable(null);
+ self.omInclusionConceptSet = ko.observableArray(null);
+ self.omInclusionConceptSetSQL = ko.observable(null);
+ if (conceptSetList && conceptSetList.length > 0) {
+ selectedConceptSet = $.grep(conceptSetList, function (f) {
+ return f.id == data.omInclusionId;
+ });
+ if (selectedConceptSet.length > 0) {
+ self.omInclusionCaption(selectedConceptSet[0].name);
+ conceptSetData = {
+ id: self.omInclusionId(),
+ name: self.omInclusionCaption(),
+ expression: selectedConceptSet[0].expression,
+ };
+ self.omInclusionConceptSet.push(new ConceptSet(conceptSetData));
+ self.omInclusionConceptSetSQL(selectedConceptSet[0].sql);
+ }
+ }
+
+ self.negativeControlId = ko.observable(data.negativeControlId != null ? data.negativeControlId : 0);
+ self.negativeControlCaption = ko.observable(null);
+ self.negativeControlConceptSet = ko.observableArray(null);
+ self.negativeControlConceptSetSQL = ko.observable(null);
+ if (conceptSetList && conceptSetList.length > 0) {
+ selectedConceptSet = $.grep(conceptSetList, function (f) {
+ return f.id == data.negativeControlId;
+ });
+ if (selectedConceptSet.length > 0) {
+ self.negativeControlCaption(selectedConceptSet[0].name);
+ conceptSetData = {
+ id: self.negativeControlId(),
+ name: self.negativeControlCaption(),
+ expression: selectedConceptSet[0].expression,
+ };
+ self.negativeControlConceptSet.push(new ConceptSet(conceptSetData));
+ self.negativeControlConceptSetSQL(selectedConceptSet[0].sql);
+ }
+ }
+
+ self.modelType = ko.observable(data.modelType != null ? data.modelType : null);
+ self.delCovariatesSmallCount = ko.observable(data.delCovariatesSmallCount != null ? data.delCovariatesSmallCount : 100);
+
+ // Derived fields
+ self.modelTypeRate = function () {
+ var returnVal = '';
+ if (self.modelType() != null) {
+ returnVal = self.modelTypeOptions.filter(function (item) {
+ return item.id == self.modelType();
+ })[0].rate
+ }
+ return returnVal;
+ }
+
+ self.modelTypeName = function () {
+ var returnVal = '';
+ if (self.modelType() != null) {
+ returnVal = self.modelTypeOptions.filter(function (item) {
+ return item.id == self.modelType();
+ })[0].name
+ }
+ return returnVal;
+ }
+
+ self.modelTypeCmArgValue = function () {
+ var returnVal = '';
+ if (self.modelType() != null) {
+ returnVal = self.modelTypeOptions.filter(function (item) {
+ return item.id == self.modelType();
+ })[0].cmArgValue
+ }
+ return returnVal;
+ }
+
+ self.addExposureDaysToEndDescription = function () {
+ var returnVal = '';
+ if (self.addExposureDaysToEnd() != null) {
+ returnVal = self.timeAtRiskEndOptions.filter(function (item) {
+ return item.id == self.addExposureDaysToEnd();
+ })[0].name
+ }
+ return returnVal;
+ }
+
+ // Propensity Score Settings
+ self.psAdjustment = ko.observable(data.psAdjustment != null ? data.psAdjustment : 1);
+
+ self.psTrim = ko.observable(data.psTrim != null ? data.psTrim : 0);
+ self.psTrimFraction = ko.observable(data.psTrimFraction != null ? data.psTrimFraction : 5);
+ self.psTrimFractionFormatted = ko.pureComputed(function () {
+ var trimFraction = self.psTrimFraction();
+ if (trimFraction > 0) {
+ trimFraction = trimFraction / 100;
+ }
+ if (self.psTrim() == 1) {
+ return trimFraction;
+ }
+ if (self.psTrim() == 2) {
+ return trimFraction + ", " + (1 - trimFraction);
+ }
+ });
+ self.psMatch = ko.observable(data.psMatch != null ? data.psMatch : 1);
+ self.psMatchMaxRatio = ko.observable(data.psMatchMaxRatio != null ? data.psMatchMaxRatio : 1);
+ self.psStratNumStrata = ko.observable(data.psStratNumStrata != null ? data.psStratNumStrata : 5);
+
+ self.psDemographicsGender = ko.observable((data.psDemographicsGender == 1) || false);
+ self.psDemographicsRace = ko.observable((data.psDemographicsRace == 1) || false);
+ self.psDemographicsEthnicity = ko.observable((data.psDemographicsEthnicity == 1) || false);
+ self.psDemographicsAge = ko.observable((data.psDemographicsAge == 1) || false);
+ self.psDemographicsYear = ko.observable((data.psDemographicsYear == 1) || false);
+ self.psDemographicsMonth = ko.observable((data.psDemographicsMonth == 1) || false);
+
+ self.psConditionOcc365d = ko.observable((data.psConditionOcc365d == 1) || false);
+ self.psConditionOcc30d = ko.observable((data.psConditionOcc30d == 1) || false);
+ self.psConditionOccInpt180d = ko.observable((data.psConditionOccInpt180d == 1) || false);
+
+ self.psConditionEraEver = ko.observable((data.psConditionEraEver == 1) || false);
+ self.psConditionEraOverlap = ko.observable((data.psConditionEraOverlap == 1) || false);
+ self.psConditionGroupMeddra = ko.observable((data.psConditionGroupMeddra == 1) || false);
+ self.psConditionGroupSnomed = ko.observable((data.psConditionGroupSnomed == 1) || false);
+
+ self.psDrugExposure = ko.observable((data.psDrugExposure == 1) || false);
+ self.psDrugInPrior30d = ko.observable((data.psDrugExposure30d == 1 || data.psDrugEra30d == 1) || false);
+ self.psDrugInPrior365d = ko.observable((data.psDrugExposure365d == 1 || data.psDrugEra365d == 1) || false);
+
+ self.psDrugEra = ko.observable((data.psDrugEra == 1) || false);
+ self.psDrugEraOverlap = ko.observable((data.psDrugEraOverlap == 1) || false);
+ self.psDrugEraEver = ko.observable((data.psDrugEraEver == 1) || false);
+ self.psDrugGroup = ko.observable((data.psDrugGroup == 1) || false);
+
+ self.psProcedureOcc365d = ko.observable((data.psProcedureOcc365d == 1) || false);
+ self.psProcedureOcc30d = ko.observable((data.psProcedureOcc30d == 1) || false);
+ self.psProcedureGroup = ko.observable((data.psProcedureGroup == 1) || false);
+
+ self.psObservation = ko.observable((data.psObservation == 1) || false);
+ self.psObservation365d = ko.observable((data.psObservation365d == 1) || false);
+ self.psObservation30d = ko.observable((data.psObservation30d == 1) || false);
+ self.psObservationCount365d = ko.observable((data.psObservationCount365d == 1) || false);
+
+ self.psMeasurement365d = ko.observable((data.psMeasurement365d == 1) || false);
+ self.psMeasurement30d = ko.observable((data.psMeasurement30d == 1) || false);
+ self.psMeasurementCount365d = ko.observable((data.psMeasurementCount365d == 1) || false);
+ self.psMeasurementBelow = ko.observable((data.psMeasurementBelow == 1) || false);
+ self.psMeasurementAbove = ko.observable((data.psMeasurementAbove == 1) || false);
+
+ self.psConceptCounts = ko.observable((data.psConceptCounts == 1) || false);
+
+ self.psRiskScoresCharlson = ko.observable((data.psRiskScoresCharlson == 1) || false);
+ self.psRiskScoresDcsi = ko.observable((data.psRiskScoresDcsi == 1) || false);
+ self.psRiskScoresChads2 = ko.observable((data.psRiskScoresChads2 == 1) || false);
+ self.psRiskScoresChads2vasc = ko.observable((data.psRiskScoresChads2vasc == 1) || false);
+
+ self.psInteractionYear = ko.observable((data.psInteractionYear == 1) || false);
+ self.psInteractionMonth = ko.observable((data.psInteractionMonth == 1) || false);
+
+ // Propensity Score Settings - Derived
+ self.psDemographics = ko.pureComputed(function () {
+ return (self.psDemographicsGender() || self.psDemographicsRace() || self.psDemographicsEthnicity() || self.psDemographicsAge() || self.psDemographicsYear() || self.psDemographicsMonth());
+ });
+ self.psDemographicsIndeterminate = function () {
+ var propCount = 0;
+ propCount += self.psDemographicsGender() | 0;
+ propCount += self.psDemographicsRace() | 0;
+ propCount += self.psDemographicsEthnicity() | 0;
+ propCount += self.psDemographicsAge() | 0;
+ propCount += self.psDemographicsYear() | 0;
+ propCount += self.psDemographicsMonth() | 0;
+ return (propCount > 0 && propCount < 6);
+ }
+ self.psStrat = ko.pureComputed(function () {
+ return self.psMatch() == 2;
+ });
+ self.psStratOrMatch = ko.pureComputed(function () {
+ return self.psMatch() > 0;
+ })
+ self.psConditionOcc = ko.pureComputed(function () {
+ return (self.psConditionOcc365d() || self.psConditionOcc30d() || self.psConditionOccInpt180d())
+ });
+ self.psConditionEra = ko.pureComputed(function () {
+ return (self.psConditionEraEver() || self.psConditionEraOverlap())
+ });
+ self.psConditionGroup = ko.pureComputed(function () {
+ return (self.psConditionGroupMeddra() || self.psConditionGroupSnomed())
+ });
+ self.psCondition = ko.pureComputed(function () {
+ return (self.psConditionOcc() || self.psConditionEra())
+ });
+ self.psDrug = ko.pureComputed(function () {
+ return (self.psDrugInPrior30d() || self.psDrugInPrior365d() || self.psDrugEraOverlap() || self.psDrugEraEver())
+ });
+ self.psDrugAggregation = ko.pureComputed(function () {
+ return (self.psDrugExposure() || self.psDrugEra() || self.psDrugGroup())
+ });
+ self.psDrugExposure365d = ko.pureComputed(function () {
+ return (self.psDrugExposure() && self.psDrugInPrior365d())
+ });
+ self.psDrugExposure30d = ko.pureComputed(function () {
+ return (self.psDrugExposure() && self.psDrugInPrior30d())
+ });
+ self.psDrugEra365d = ko.pureComputed(function () {
+ return (self.psDrugEra() && self.psDrugInPrior365d())
+ });
+ self.psDrugEra30d = ko.pureComputed(function () {
+ return (self.psDrugEra() && self.psDrugInPrior30d())
+ });
+ self.psProcedureOcc = ko.pureComputed(function () {
+ return (self.psProcedureOcc365d() || self.psProcedureOcc30d() || self.psProcedureGroup())
+ })
+ self.psMeasurement = ko.pureComputed(function () {
+ return (self.psMeasurement365d() || self.psMeasurement30d() || self.psMeasurementCount365d() || self.psMeasurementBelow() || self.psMeasurementAbove())
+ })
+ self.psRiskScores = ko.pureComputed(function () {
+ return (self.psRiskScoresCharlson() || self.psRiskScoresDcsi() || self.psRiskScoresChads2() || self.psRiskScoresChads2vasc())
+ })
+ self.psInteraction = ko.pureComputed(function () {
+ return (self.psInteractionYear() || self.psInteractionMonth())
+ })
+
+ // Outcome model settings
+ self.omCovariates = ko.observable(data.omCovariates != null ? data.omCovariates : 0);
+
+ self.omTrim = ko.observable(data.omTrim != null ? data.omTrim : 0);
+ self.omTrimFraction = ko.observable(data.omTrimFraction != null ? data.omTrimFraction : 5);
+ self.omMatch = ko.observable(data.omMatch != null ? data.omMatch : 1);
+ self.omMatchMaxRatio = ko.observable(data.omMatchMaxRatio != null ? data.omMatchMaxRatio : 1);
+ self.omStratNumStrata = ko.observable(data.omStratNumStrata != null ? data.omStratNumStrata : 5);
+
+ self.omDemographicsGender = ko.observable((data.omDemographicsGender == 1) || false);
+ self.omDemographicsRace = ko.observable((data.omDemographicsRace == 1) || false);
+ self.omDemographicsEthnicity = ko.observable((data.omDemographicsEthnicity == 1) || false);
+ self.omDemographicsAge = ko.observable((data.omDemographicsAge == 1) || false);
+ self.omDemographicsYear = ko.observable((data.omDemographicsYear == 1) || false);
+ self.omDemographicsMonth = ko.observable((data.omDemographicsMonth == 1) || false);
+
+ self.omConditionOcc365d = ko.observable((data.omConditionOcc365d == 1) || false);
+ self.omConditionOcc30d = ko.observable((data.omConditionOcc30d == 1) || false);
+ self.omConditionOccInpt180d = ko.observable((data.omConditionOccInpt180d == 1) || false);
+
+ self.omConditionEraEver = ko.observable((data.omConditionEraEver == 1) || false);
+ self.omConditionEraOverlap = ko.observable((data.omConditionEraOverlap == 1) || false);
+ self.omConditionGroupMeddra = ko.observable((data.omConditionGroupMeddra == 1) || false);
+ self.omConditionGroupSnomed = ko.observable((data.omConditionGroupSnomed == 1) || false);
+
+ self.omDrugExposure = ko.observable((data.omDrugExposure == 1) || false);
+ self.omDrugInPrior30d = ko.observable((data.omDrugExposure30d == 1 || data.omDrugEraExposure30d == 1) || false);
+ self.omDrugInPrior365d = ko.observable((data.omDrugExposure365d == 1 || data.omDrugEraExposure365d == 1) || false);
+
+ self.omDrugEra = ko.observable((data.omDrugEra == 1) || false);
+ self.omDrugEraOverlap = ko.observable((data.omDrugEraOverlap == 1) || false);
+ self.omDrugEraEver = ko.observable((data.omDrugEraEver == 1) || false);
+ self.omDrugGroup = ko.observable((data.omDrugGroup == 1) || false);
+
+ self.omProcedureOcc365d = ko.observable((data.omProcedureOcc365d == 1) || false);
+ self.omProcedureOcc30d = ko.observable((data.omProcedureOcc30d == 1) || false);
+ self.omProcedureGroup = ko.observable((data.omProcedureGroup == 1) || false);
+
+ self.omObservation = ko.observable((data.omObservation == 1) || false);
+ self.omObservation365d = ko.observable((data.omObservation365d == 1) || false);
+ self.omObservation30d = ko.observable((data.omObservation30d == 1) || false);
+ self.omObservationCount365d = ko.observable((data.omObservationCount365d == 1) || false);
+
+ self.omMeasurement365d = ko.observable((data.omMeasurement365d == 1) || false);
+ self.omMeasurement30d = ko.observable((data.omMeasurement30d == 1) || false);
+ self.omMeasurementCount365d = ko.observable((data.omMeasurementCount365d == 1) || false);
+ self.omMeasurementBelow = ko.observable((data.omMeasurementBelow == 1) || false);
+ self.omMeasurementAbove = ko.observable((data.omMeasurementAbove == 1) || false);
+
+ self.omConceptCounts = ko.observable((data.omConceptCounts == 1) || false);
+
+ self.omRiskScoresCharlson = ko.observable((data.omRiskScoresCharlson == 1) || false);
+ self.omRiskScoresDcsi = ko.observable((data.omRiskScoresDcsi == 1) || false);
+ self.omRiskScoresChads2 = ko.observable((data.omRiskScoresChads2 == 1) || false);
+ self.omRiskScoresChads2vasc = ko.observable((data.omRiskScoresChads2vasc == 1) || false);
+
+ self.omInteractionYear = ko.observable((data.omInteractionYear == 1) || false);
+ self.omInteractionMonth = ko.observable((data.omInteractionMonth == 1) || false);
+
+ // Outcome Model Settings - Derived
+ self.omDemographics = ko.pureComputed(function () {
+ return (self.omDemographicsGender() || self.omDemographicsRace() || self.omDemographicsEthnicity() || self.omDemographicsAge() || self.omDemographicsYear() || self.omDemographicsMonth());
+ });
+ self.omDemographicsIndeterminate = function () {
+ var propCount = 0;
+ propCount += self.omDemographicsGender() | 0;
+ propCount += self.omDemographicsRace() | 0;
+ propCount += self.omDemographicsEthnicity() | 0;
+ propCount += self.omDemographicsAge() | 0;
+ propCount += self.omDemographicsYear() | 0;
+ propCount += self.omDemographicsMonth() | 0;
+ return (propCount > 0 && propCount < 6);
+ }
+ self.omStrat = ko.pureComputed(function () {
+ return !self.omMatch();
+ });
+ self.omConditionOcc = ko.pureComputed(function () {
+ return (self.omConditionOcc365d() || self.omConditionOcc30d() || self.omConditionOccInpt180d())
+ });
+ self.omConditionEra = ko.pureComputed(function () {
+ return (self.omConditionEraEver() || self.omConditionEraOverlap())
+ });
+ self.omConditionGroup = ko.pureComputed(function () {
+ return (self.omConditionGroupMeddra() || self.omConditionGroupSnomed())
+ });
+ self.omCondition = ko.pureComputed(function () {
+ return (self.omConditionOcc() || self.omConditionEra())
+ });
+ self.omDrug = ko.pureComputed(function () {
+ return (self.omDrugInPrior30d() || self.omDrugInPrior365d() || self.omDrugEraOverlap() || self.omDrugEraEver())
+ });
+ self.omDrugAggregation = ko.pureComputed(function () {
+ return (self.omDrugExposure() || self.omDrugEra() || self.omDrugGroup())
+ });
+ self.omDrugExposure365d = ko.pureComputed(function () {
+ return (self.omDrugExposure() && self.omDrugInPrior365d())
+ });
+ self.omDrugExposure30d = ko.pureComputed(function () {
+ return (self.omDrugExposure() && self.omDrugInPrior30d())
+ });
+ self.omDrugEra365d = ko.pureComputed(function () {
+ return (self.omDrugEra() && self.omDrugInPrior365d())
+ });
+ self.omDrugEra30d = ko.pureComputed(function () {
+ return (self.omDrugEra() && self.omDrugInPrior30d())
+ });
+ self.omProcedureOcc = ko.pureComputed(function () {
+ return (self.omProcedureOcc365d() || self.omProcedureOcc30d() || self.omProcedureGroup())
+ });
+ self.omMeasurement = ko.pureComputed(function () {
+ return (self.omMeasurement365d() || self.omMeasurement30d() || self.omMeasurementCount365d() || self.omMeasurementBelow() || self.omMeasurementAbove())
+ });
+ self.omRiskScores = ko.pureComputed(function () {
+ return (self.omRiskScoresCharlson() || self.omRiskScoresDcsi() || self.omRiskScoresChads2() || self.omRiskScoresChads2vasc())
+ });
+ self.omInteraction = ko.pureComputed(function () {
+ return (self.omInteractionYear() || self.omInteractionMonth())
+ });
+
+ self.jsonify = function () {
+ return {
+ id: self.id || null,
+ description: self.description(),
+ modelType: self.modelType(),
+ timeAtRiskStart: self.timeAtRiskStart(),
+ timeAtRiskEnd: self.timeAtRiskEnd(),
+ addExposureDaysToEnd: self.addExposureDaysToEnd(),
+ minimumWashoutPeriod: self.minimumWashoutPeriod(),
+ minimumDaysAtRisk: self.minimumDaysAtRisk(),
+ rmSubjectsInBothCohorts: self.rmSubjectsInBothCohorts(),
+ rmPriorOutcomes: self.rmPriorOutcomes(),
+ psAdjustment: self.psAdjustment(),
+ psDemographics: self.psDemographics() | 0,
+ psDemographicsGender: self.psDemographicsGender() | 0,
+ psDemographicsRace: self.psDemographicsRace() | 0,
+ psDemographicsEthnicity: self.psDemographicsEthnicity() | 0,
+ psDemographicsAge: self.psDemographicsAge() | 0,
+ psDemographicsYear: self.psDemographicsYear() | 0,
+ psDemographicsMonth: self.psDemographicsMonth() | 0,
+ psTrim: self.psTrim(),
+ psTrimFraction: self.psTrimFraction(),
+ psMatch: self.psMatch(),
+ psMatchMaxRatio: self.psMatchMaxRatio(),
+ psStrat: self.psStrat() | 0,
+ psStratNumStrata: self.psStratNumStrata(),
+ psConditionOcc: self.psConditionOcc() | 0,
+ psConditionOcc365d: self.psConditionOcc365d() | 0,
+ psConditionOcc30d: self.psConditionOcc30d() | 0,
+ psConditionOccInpt180d: self.psConditionOccInpt180d() | 0,
+ psConditionEra: self.psConditionEra() | 0,
+ psConditionEraEver: self.psConditionEraEver() | 0,
+ psConditionEraOverlap: self.psConditionEraOverlap() | 0,
+ psConditionGroup: self.psConditionGroup() | 0,
+ psConditionGroupMeddra: self.psConditionGroupMeddra() | 0,
+ psConditionGroupSnomed: self.psConditionGroupSnomed() | 0,
+ psDrugExposure: self.psDrugExposure() | 0,
+ psDrugExposure365d: self.psDrugExposure365d() | 0,
+ psDrugExposure30d: self.psDrugExposure30d() | 0,
+ psDrugEra: self.psDrugEra() | 0,
+ psDrugEra365d: self.psDrugEra365d() | 0,
+ psDrugEra30d: self.psDrugEra30d() | 0,
+ psDrugEraOverlap: self.psDrugEraOverlap() | 0,
+ psDrugEraEver: self.psDrugEraEver() | 0,
+ psDrugGroup: self.psDrugGroup() | 0,
+ psProcedureOcc: self.psProcedureOcc() | 0,
+ psProcedureOcc365d: self.psProcedureOcc365d() | 0,
+ psProcedureOcc30d: self.psProcedureOcc30d() | 0,
+ psProcedureGroup: self.psProcedureGroup() | 0,
+ psObservation: self.psObservation() | 0,
+ psObservation365d: self.psObservation365d() | 0,
+ psObservation30d: self.psObservation30d() | 0,
+ psObservationCount365d: self.psObservationCount365d() | 0,
+ psMeasurement: self.psMeasurement() | 0,
+ psMeasurement365d: self.psMeasurement365d() | 0,
+ psMeasurement30d: self.psMeasurement30d() | 0,
+ psMeasurementCount365d: self.psMeasurementCount365d() | 0,
+ psMeasurementBelow: self.psMeasurementBelow() | 0,
+ psMeasurementAbove: self.psMeasurementAbove() | 0,
+ psConceptCounts: self.psConceptCounts() | 0,
+ psRiskScores: self.psRiskScores() | 0,
+ psRiskScoresCharlson: self.psRiskScoresCharlson() | 0,
+ psRiskScoresDcsi: self.psRiskScoresDcsi() | 0,
+ psRiskScoresChads2: self.psRiskScoresChads2() | 0,
+ psRiskScoresChads2vasc: self.psRiskScoresChads2vasc() | 0,
+ psInteractionYear: self.psInteractionYear() | 0,
+ psInteractionMonth: self.psInteractionMonth() | 0,
+ omCovariates: self.omCovariates(),
+ omExclusionId: self.omExclusionId(),
+ omInclusionId: self.omInclusionId(),
+ omDemographics: self.omDemographics() | 0,
+ omDemographicsGender: self.omDemographicsGender() | 0,
+ omDemographicsRace: self.omDemographicsRace() | 0,
+ omDemographicsEthnicity: self.omDemographicsEthnicity() | 0,
+ omDemographicsAge: self.omDemographicsAge() | 0,
+ omDemographicsYear: self.omDemographicsYear() | 0,
+ omDemographicsMonth: self.omDemographicsMonth() | 0,
+ omTrim: self.omTrim(),
+ omTrimFraction: self.omTrimFraction(),
+ omMatch: self.omMatch(),
+ omMatchMaxRatio: self.omMatchMaxRatio(),
+ omStrat: self.omStrat() | 0,
+ omStratNumStrata: self.omStratNumStrata(),
+ omConditionOcc: self.omConditionOcc() | 0,
+ omConditionOcc365d: self.omConditionOcc365d() | 0,
+ omConditionOcc30d: self.omConditionOcc30d() | 0,
+ omConditionOccInpt180d: self.omConditionOccInpt180d() | 0,
+ omConditionEra: self.omConditionEra() | 0,
+ omConditionEraEver: self.omConditionEraEver() | 0,
+ omConditionEraOverlap: self.omConditionEraOverlap() | 0,
+ omConditionGroup: self.omConditionGroup() | 0,
+ omConditionGroupMeddra: self.omConditionGroupMeddra() | 0,
+ omConditionGroupSnomed: self.omConditionGroupSnomed() | 0,
+ omDrugExposure: self.omDrugExposure() | 0,
+ omDrugExposure365d: self.omDrugExposure365d() | 0,
+ omDrugExposure30d: self.omDrugExposure30d() | 0,
+ omDrugEra: self.omDrugEra() | 0,
+ omDrugEra365d: self.omDrugEra365d() | 0,
+ omDrugEra30d: self.omDrugEra30d() | 0,
+ omDrugEraOverlap: self.omDrugEraOverlap() | 0,
+ omDrugEraEver: self.omDrugEraEver() | 0,
+ omDrugGroup: self.omDrugGroup() | 0,
+ omProcedureOcc: self.omProcedureOcc() | 0,
+ omProcedureOcc365d: self.omProcedureOcc365d() | 0,
+ omProcedureOcc30d: self.omProcedureOcc30d() | 0,
+ omProcedureGroup: self.omProcedureGroup() | 0,
+ omObservation: self.omObservation() | 0,
+ omObservation365d: self.omObservation365d() | 0,
+ omObservation30d: self.omObservation30d() | 0,
+ omObservationCount365d: self.omObservationCount365d() | 0,
+ omMeasurement: self.omMeasurement() | 0,
+ omMeasurement365d: self.omMeasurement365d() | 0,
+ omMeasurement30d: self.omMeasurement30d() | 0,
+ omMeasurementCount365d: self.omMeasurementCount365d() | 0,
+ omMeasurementBelow: self.omMeasurementBelow() | 0,
+ omMeasurementAbove: self.omMeasurementAbove() | 0,
+ omConceptCounts: self.omConceptCounts() | 0,
+ omRiskScores: self.omRiskScores() | 0,
+ omRiskScoresCharlson: self.omRiskScoresCharlson() | 0,
+ omRiskScoresDcsi: self.omRiskScoresDcsi() | 0,
+ omRiskScoresChads2: self.omRiskScoresChads2() | 0,
+ omRiskScoresChads2vasc: self.omRiskScoresChads2vasc() | 0,
+ omInteractionYear: self.omInteractionYear() | 0,
+ omInteractionMonth: self.omInteractionMonth() | 0,
+ delCovariatesSmallCount: self.delCovariatesSmallCount(),
+ negativeControlId: self.negativeControlId()
+ };
+ }
+
+ }
+
+ return AnalysisDefinition;
+});
diff --git a/js/modules/cohortcomparison/ComparativeCohortAnalysis.js b/js/modules/cohortcomparison/ComparativeCohortAnalysis.js
index 0811c07a7..d18a4e673 100644
--- a/js/modules/cohortcomparison/ComparativeCohortAnalysis.js
+++ b/js/modules/cohortcomparison/ComparativeCohortAnalysis.js
@@ -1,462 +1,164 @@
define(function (require, exports) {
var ko = require('knockout');
- var CohortDefinition = require('cohortbuilder/CohortDefinition')
- var ConceptSet = require('conceptsetbuilder/InputTypes/ConceptSet')
+ var TargetComparatorDefinition = require('cohortcomparison/TargetComparatorDefinition')
+ var OutcomeDefinition = require('cohortcomparison/OutcomeDefinition')
+ var AnalysisDefinition = require('cohortcomparison/AnalysisDefinition')
function ComparativeCohortAnalysis(data) {
var self = this;
var data = data || {};
- // Options
- self.modelTypeOptions = [{name: 'Logistic regression', cmArgValue: '"logistic"', rate: 'odds', id: 1}, {name: 'Poisson regression', cmArgValue: '"poisson"', rate: 'rate', id: 2}, {name: 'Cox proportional hazards', cmArgValue: '"cox"', rate: 'hazards', id: 3}];
- self.timeAtRiskEndOptions = [{name: 'cohort end date', id: 1}, {name: 'cohort start date', id: 0}];
- self.trimOptions = [{name: 'None', id: 0}, {name: 'by Percentile', id: 1}, {name: 'by Equipoise', id: 2}];
- self.matchingOptions = [{name: 'No matching/stratification', id: 0}, {name: 'Matching', id: 1}, {name: 'Stratification', id: 2}];
-
- // Properties
+ // Properties
self.analysisId = data.analysisId || null;
self.name = ko.observable(data.name || null);
- self.nameMultiLine = ko.pureComputed(function() {
- var maxLength = 45;
- var nameFormatted = [];
- if (self.name() && self.name().length > 0) {
+ self.nameMultiLine = ko.pureComputed(function () {
+ var maxLength = 45;
+ var nameFormatted = [];
+ if (self.name() && self.name().length > 0) {
var nameSplit = self.name().split(" ");
var curName = "";
- for(i=0; i < nameSplit.length; i++) {
+ for (i = 0; i < nameSplit.length; i++) {
if (curName.length > maxLength) {
nameFormatted.push(curName);
curName = "";
}
curName += nameSplit[i] + " ";
}
- nameFormatted.push(curName);
- }
- return nameFormatted;
- })
- self.timeAtRiskStart = ko.observable(data.timeAtRiskStart != null ? data.timeAtRiskStart : 0);
- self.timeAtRiskEnd = ko.observable(data.timeAtRiskEnd != null ? data.timeAtRiskEnd : 0);
- self.addExposureDaysToEnd = ko.observable(data.addExposureDaysToEnd != null ? data.addExposureDaysToEnd : 1);
- self.addExposureDaysToEndFormatted = ko.pureComputed(function() {
- return self.addExposureDaysToEnd() == 1;
- })
- self.minimumWashoutPeriod = ko.observable(data.minimumWashoutPeriod != null ? data.minimumWashoutPeriod : 0);
- self.minimumDaysAtRisk = ko.observable(data.minimumDaysAtRisk != null ? data.minimumDaysAtRisk : 0);
- self.rmSubjectsInBothCohorts = ko.observable(data.rmSubjectsInBothCohorts != null ? data.rmSubjectsInBothCohorts : 1);
- self.rmSubjectsInBothCohortsFormatted = ko.pureComputed(function() {
- return self.rmSubjectsInBothCohorts() == 1;
- });
- self.rmPriorOutcomes = ko.observable(data.rmPriorOutcomes != null ? data.rmPriorOutcomes : 1);
- self.rmPriorOutcomesFormatted = ko.pureComputed(function () {
- return self.rmPriorOutcomes() == 1;
- });
-
- self.treatmentId = ko.observable(data.treatmentId != null ? data.treatmentId : 0);
- self.treatmentCaption = ko.observable(data.treatmentCaption != null ? data.treatmentCaption : null);
- if (data.treatmentCohortDefinition != null) {
- jsonCohortDefinition = JSON.parse(data.treatmentCohortDefinition);
- self.treatmentCohortDefinition = ko.observable(new CohortDefinition(jsonCohortDefinition));
- } else {
- self.treatmentCohortDefinition = ko.observable(null);
- }
-
- self.comparatorId = ko.observable(data.comparatorId != null ? data.comparatorId : 0);
- self.comparatorCaption = ko.observable(data.comparatorCaption != null ? data.comparatorCaption : null);
- if (data.comparatorCohortDefinition != null) {
- jsonCohortDefinition = JSON.parse(data.comparatorCohortDefinition);
- self.comparatorCohortDefinition = ko.observable(new CohortDefinition(jsonCohortDefinition));
- } else {
- self.comparatorCohortDefinition = ko.observable(null);
- }
-
- self.outcomeId = ko.observable(data.outcomeId != null ? data.outcomeId : 0);
- self.outcomeCaption = ko.observable(data.outcomeCaption != null ? data.outcomeCaption : null);
- if (data.outcomeCohortDefinition != null) {
- jsonCohortDefinition = JSON.parse(data.outcomeCohortDefinition);
- self.outcomeCohortDefinition = ko.observable(new CohortDefinition(jsonCohortDefinition));
- } else {
- self.outcomeCohortDefinition = ko.observable(null);
- }
-
- self.psExclusionId = ko.observable(data.psExclusionId != null ? data.psExclusionId : 0);
- self.psExclusionCaption = ko.observable(data.psExclusionCaption != null ? data.psExclusionCaption : null);
- self.psExclusionConceptSet = ko.observableArray(null);
- if (self.psExclusionId() > 0) {
- var conceptSetData = {
- id: self.psExclusionId(),
- name: self.psExclusionCaption(),
- expression: data.psExclusionConceptSet
- };
- self.psExclusionConceptSet.push(new ConceptSet(conceptSetData));
- self.psExclusionConceptSetSQL = ko.observable(data.psExclusionConceptSetSql)
- } else {
- self.psExclusionConceptSetSQL = ko.observable(null);
- }
-
- self.psInclusionId = ko.observable(data.psInclusionId != null ? data.psInclusionId : 0);
- self.psInclusionCaption = ko.observable(data.psInclusionCaption != null ? data.psInclusionCaption : null);
- self.psInclusionConceptSet = ko.observableArray(null);
- if (self.psInclusionId() > 0) {
- var conceptSetData = {
- id: self.psInclusionId(),
- name: self.psInclusionCaption(),
- expression: data.psInclusionConceptSet
- };
- self.psInclusionConceptSet.push(new ConceptSet(conceptSetData));
- self.psInclusionConceptSetSQL = ko.observable(data.psInclusionConceptSetSql)
- } else {
- self.psInclusionConceptSetSQL = ko.observable(null);
- }
-
- self.omExclusionId = ko.observable(data.omExclusionId != null ? data.omExclusionId : 0);
- self.omExclusionCaption = ko.observable(data.omExclusionCaption != null ? data.omExclusionCaption : null);
- self.omExclusionConceptSet = ko.observableArray(null);
- if (self.omExclusionId() > 0) {
- var conceptSetData = {
- id: self.omExclusionId(),
- name: self.omExclusionCaption(),
- expression: data.omExclusionConceptSet
- };
- self.omExclusionConceptSet.push(new ConceptSet(conceptSetData));
- self.omExclusionConceptSetSQL = ko.observable(data.omExclusionConceptSetSql)
- } else {
- self.omExclusionConceptSetSQL = ko.observable(null);
- }
-
- self.omInclusionId = ko.observable(data.omInclusionId != null ? data.omInclusionId : 0);
- self.omInclusionCaption = ko.observable(data.omExclusionCaption != null ? data.omExclusionCaption : null);
- self.omInclusionConceptSet = ko.observableArray(null);
- if (self.omInclusionId() > 0) {
- var conceptSetData = {
- id: self.omInclusionId(),
- name: self.omInclusionCaption(),
- expression: data.omInclusionConceptSet
- };
- self.omInclusionConceptSet.push(new ConceptSet(conceptSetData));
- self.omInclusionConceptSetSQL = ko.observable(data.omInclusionConceptSetSql)
- } else {
- self.omInclusionConceptSetSQL = ko.observable(null);
- }
-
- self.negativeControlId = ko.observable(data.negativeControlId != null ? data.negativeControlId : 0);
- self.negativeControlCaption = ko.observable(data.negativeControlCaption != null ? data.negativeControlCaption : null);
- self.negativeControlConceptSet = ko.observableArray(null);
- if (self.negativeControlId() > 0) {
- var conceptSetData = {
- id: self.negativeControlId(),
- name: self.negativeControlCaption(),
- expression: data.negativeControlConceptSet
- };
- self.negativeControlConceptSet.push(new ConceptSet(conceptSetData));
- self.negativeControlConceptSetSQL = ko.observable(data.negativeControlConceptSetSql)
- } else {
- self.negativeControlConceptSetSQL = ko.observable(null);
- }
-
- self.modelType = ko.observable(data.modelType != null ? data.modelType : null);
- self.delCovariatesSmallCount = ko.observable(data.delCovariatesSmallCount != null ? data.delCovariatesSmallCount : 100);
-
- // Derived fields
- self.modelTypeRate = function() {
- returnVal = '';
- if(self.modelType() != null) {
- returnVal = self.modelTypeOptions.filter(function(item) {
- return item.id == self.modelType();
- })[0].rate
- }
- return returnVal;
- }
-
- self.modelTypeName = function() {
- returnVal = '';
- if(self.modelType() != null) {
- returnVal = self.modelTypeOptions.filter(function(item) {
- return item.id == self.modelType();
- })[0].name
- }
- return returnVal;
- }
-
- self.modelTypeCmArgValue = function() {
- returnVal = '';
- if(self.modelType() != null) {
- returnVal = self.modelTypeOptions.filter(function(item) {
- return item.id == self.modelType();
- })[0].cmArgValue
- }
- return returnVal;
- }
-
- self.addExposureDaysToEndDescription = function() {
- returnVal = '';
- if(self.addExposureDaysToEnd() != null) {
- returnVal = self.timeAtRiskEndOptions.filter(function(item) {
- return item.id == self.addExposureDaysToEnd();
- })[0].name
- }
- return returnVal;
- }
-
- self.readyForDisplay = function() {
- return (self.comparatorId() != null && self.comparatorId() > 0 &&
- self.treatmentId() != null && self.treatmentId() > 0 &&
- self.outcomeId() != null && self.outcomeId() > 0 &&
- self.modelType() != null)
- }
-
-
- // Propensity Score Settings
- self.psAdjustment = ko.observable(data.psAdjustment != null ? data.psAdjustment : 1);
-
- self.psTrim = ko.observable(data.psTrim != null ? data.psTrim : 0);
- self.psTrimFraction = ko.observable(data.psTrimFraction != null ? data.psTrimFraction : 5);
- self.psTrimFractionFormatted = ko.pureComputed(function() {
- var trimFraction = self.psTrimFraction();
- if (trimFraction > 0) {
- trimFraction = trimFraction / 100;
- }
- if (self.psTrim() == 1) {
- return trimFraction;
- }
- if (self.psTrim() == 2) {
- return trimFraction + ", " + (1 - trimFraction);
- }
- });
- self.psMatch = ko.observable(data.psMatch != null ? data.psMatch : 1);
- self.psMatchMaxRatio = ko.observable(data.psMatchMaxRatio != null ? data.psMatchMaxRatio : 1);
- self.psStratNumStrata = ko.observable(data.psStratNumStrata != null ? data.psStratNumStrata : 5);
-
- self.psDemographicsGender = ko.observable((data.psDemographicsGender == 1)|| false);
- self.psDemographicsRace = ko.observable((data.psDemographicsRace == 1)||false);
- self.psDemographicsEthnicity = ko.observable((data.psDemographicsEthnicity == 1)||false);
- self.psDemographicsAge = ko.observable((data.psDemographicsAge == 1)||false);
- self.psDemographicsYear = ko.observable((data.psDemographicsYear == 1)||false);
- self.psDemographicsMonth = ko.observable((data.psDemographicsMonth == 1)||false);
-
- self.psConditionOcc365d = ko.observable((data.psConditionOcc365d == 1)||false);
- self.psConditionOcc30d = ko.observable((data.psConditionOcc30d == 1)||false);
- self.psConditionOccInpt180d = ko.observable((data.psConditionOccInpt180d == 1)||false);
-
- self.psConditionEraEver = ko.observable((data.psConditionEraEver == 1)||false);
- self.psConditionEraOverlap = ko.observable((data.psConditionEraOverlap == 1)||false);
- self.psConditionGroupMeddra = ko.observable((data.psConditionGroupMeddra == 1)||false);
- self.psConditionGroupSnomed = ko.observable((data.psConditionGroupSnomed == 1)||false);
-
- self.psDrugExposure = ko.observable((data.psDrugExposure == 1)||false);
- self.psDrugInPrior30d = ko.observable((data.psDrugExposure30d == 1 || data.psDrugEra30d == 1) || false);
- self.psDrugInPrior365d = ko.observable((data.psDrugExposure365d == 1 || data.psDrugEra365d == 1) || false);
-
- self.psDrugEra = ko.observable((data.psDrugEra == 1)||false);
- self.psDrugEraOverlap = ko.observable((data.psDrugEraOverlap == 1)||false);
- self.psDrugEraEver = ko.observable((data.psDrugEraEver == 1)||false);
- self.psDrugGroup = ko.observable((data.psDrugGroup == 1)||false);
-
- self.psProcedureOcc365d = ko.observable((data.psProcedureOcc365d == 1)||false);
- self.psProcedureOcc30d = ko.observable((data.psProcedureOcc30d == 1)||false);
- self.psProcedureGroup = ko.observable((data.psProcedureGroup == 1)||false);
-
- self.psObservation = ko.observable((data.psObservation == 1)||false);
- self.psObservation365d = ko.observable((data.psObservation365d == 1)||false);
- self.psObservation30d = ko.observable((data.psObservation30d == 1)||false);
- self.psObservationCount365d = ko.observable((data.psObservationCount365d == 1)||false);
-
- self.psMeasurement365d = ko.observable((data.psMeasurement365d == 1)||false);
- self.psMeasurement30d = ko.observable((data.psMeasurement30d == 1)||false);
- self.psMeasurementCount365d = ko.observable((data.psMeasurementCount365d == 1)||false);
- self.psMeasurementBelow = ko.observable((data.psMeasurementBelow == 1)||false);
- self.psMeasurementAbove = ko.observable((data.psMeasurementAbove == 1)||false);
-
- self.psConceptCounts = ko.observable((data.psConceptCounts == 1)||false);
-
- self.psRiskScoresCharlson = ko.observable((data.psRiskScoresCharlson == 1)||false);
- self.psRiskScoresDcsi = ko.observable((data.psRiskScoresDcsi == 1)||false);
- self.psRiskScoresChads2 = ko.observable((data.psRiskScoresChads2 == 1)||false);
- self.psRiskScoresChads2vasc = ko.observable((data.psRiskScoresChads2vasc == 1)||false);
-
- self.psInteractionYear = ko.observable((data.psInteractionYear == 1)||false);
- self.psInteractionMonth = ko.observable((data.psInteractionMonth == 1)||false);
-
- // Propensity Score Settings - Derived
- self.psDemographics = ko.pureComputed(function () {
- return (self.psDemographicsGender() || self.psDemographicsRace() || self.psDemographicsEthnicity() || self.psDemographicsAge() || self.psDemographicsYear() || self.psDemographicsMonth());
+ nameFormatted.push(curName);
+ }
+ return nameFormatted;
+ })
+
+ self.targetComparatorList = ko.observableArray();
+ if (data.targetComparatorList && data.targetComparatorList.length > 0) {
+ for (var i = 0; i < data.targetComparatorList.length; i++) {
+ var tc = new TargetComparatorDefinition(data.targetComparatorList[i], data.cohortInfo, data.conceptSetInfo);
+ self.targetComparatorList.push(tc);
+ }
+ }
+ self.targetComparator = ko.pureComputed(function() {
+ if (self.targetComparatorList().length <= 0) {
+ self.targetComparatorList.push(new TargetComparatorDefinition());
+ }
+ return self.targetComparatorList()[0];
});
- self.psDemographicsIndeterminate = function() {
- var propCount = 0;
- propCount += self.psDemographicsGender() | 0;
- propCount += self.psDemographicsRace() | 0;
- propCount += self.psDemographicsEthnicity() | 0;
- propCount += self.psDemographicsAge() | 0;
- propCount += self.psDemographicsYear() | 0;
- propCount += self.psDemographicsMonth() | 0;
- return (propCount > 0 && propCount < 6);
- }
- self.psStrat = ko.pureComputed(function () {
- return self.psMatch() == 2;
+
+ self.outcomeList = ko.observableArray();
+ self.originalOutcomeList = [];
+ if (data.outcomeList && data.outcomeList.length > 0) {
+ for (i = 0; i < data.outcomeList.length; i++) {
+ var o = new OutcomeDefinition(data.outcomeList[i], data.cohortInfo);
+ self.outcomeList.push(o);
+ self.originalOutcomeList.push(o);
+ }
+ }
+ self.outcome = ko.pureComputed(function() {
+ if (self.outcomeList().length <= 0) {
+ self.outcomeList.push(new OutcomeDefinition());
+ }
+ return self.outcomeList()[0];
});
- self.psStratOrMatch = ko.pureComputed(function() {
- return self.psMatch() > 0;
- })
- self.psConditionOcc = ko.pureComputed(function() {
- return (self.psConditionOcc365d() || self.psConditionOcc30d() || self.psConditionOccInpt180d())
- });
- self.psConditionEra = ko.pureComputed(function() {
- return (self.psConditionEraEver() || self.psConditionEraOverlap())
- });
- self.psConditionGroup = ko.pureComputed(function() {
- return (self.psConditionGroupMeddra() || self.psConditionGroupSnomed())
- });
- self.psCondition = ko.pureComputed(function() {
- return (self.psConditionOcc() || self.psConditionEra())
- });
- self.psDrug = ko.pureComputed(function() {
- return (self.psDrugInPrior30d() || self.psDrugInPrior365d() || self.psDrugEraOverlap() || self.psDrugEraEver())
- });
- self.psDrugAggregation = ko.pureComputed(function() {
- return (self.psDrugExposure() || self.psDrugEra() || self.psDrugGroup())
- });
- self.psDrugExposure365d = ko.pureComputed(function() {
- return (self.psDrugExposure() && self.psDrugInPrior365d())
- });
- self.psDrugExposure30d = ko.pureComputed(function() {
- return (self.psDrugExposure() && self.psDrugInPrior30d())
- });
- self.psDrugEra365d = ko.pureComputed(function() {
- return (self.psDrugEra() && self.psDrugInPrior365d())
- });
- self.psDrugEra30d = ko.pureComputed(function() {
- return (self.psDrugEra() && self.psDrugInPrior30d())
- });
- self.psProcedureOcc = ko.pureComputed(function() {
- return (self.psProcedureOcc365d() || self.psProcedureOcc30d() || self.psProcedureGroup())
- })
- self.psMeasurement = ko.pureComputed(function() {
- return (self.psMeasurement365d() || self.psMeasurement30d() || self.psMeasurementCount365d() || self.psMeasurementBelow() || self.psMeasurementAbove())
- })
- self.psRiskScores = ko.pureComputed(function() {
- return (self.psRiskScoresCharlson() || self.psRiskScoresDcsi() || self.psRiskScoresChads2() || self.psRiskScoresChads2vasc())
- })
- self.psInteraction = ko.pureComputed(function() {
- return (self.psInteractionYear() || self.psInteractionMonth())
- })
-
- // Outcome model settings
- self.omCovariates = ko.observable(data.omCovariates != null ? data.omCovariates : 0);
-
- self.omTrim = ko.observable(data.omTrim != null ? data.omTrim : 0);
- self.omTrimFraction = ko.observable(data.omTrimFraction != null ? data.omTrimFraction : 5);
- self.omMatch = ko.observable(data.omMatch != null ? data.omMatch : 1);
- self.omMatchMaxRatio = ko.observable(data.omMatchMaxRatio != null ? data.omMatchMaxRatio : 1);
- self.omStratNumStrata = ko.observable(data.omStratNumStrata != null ? data.omStratNumStrata : 5);
-
- self.omDemographicsGender = ko.observable((data.omDemographicsGender == 1)|| false);
- self.omDemographicsRace = ko.observable((data.omDemographicsRace == 1)||false);
- self.omDemographicsEthnicity = ko.observable((data.omDemographicsEthnicity == 1)||false);
- self.omDemographicsAge = ko.observable((data.omDemographicsAge == 1)||false);
- self.omDemographicsYear = ko.observable((data.omDemographicsYear == 1)||false);
- self.omDemographicsMonth = ko.observable((data.omDemographicsMonth == 1)||false);
-
- self.omConditionOcc365d = ko.observable((data.omConditionOcc365d == 1)||false);
- self.omConditionOcc30d = ko.observable((data.omConditionOcc30d == 1)||false);
- self.omConditionOccInpt180d = ko.observable((data.omConditionOccInpt180d == 1)||false);
-
- self.omConditionEraEver = ko.observable((data.omConditionEraEver == 1)||false);
- self.omConditionEraOverlap = ko.observable((data.omConditionEraOverlap == 1)||false);
- self.omConditionGroupMeddra = ko.observable((data.omConditionGroupMeddra == 1)||false);
- self.omConditionGroupSnomed = ko.observable((data.omConditionGroupSnomed == 1)||false);
-
- self.omDrugExposure = ko.observable((data.omDrugExposure == 1)||false);
- self.omDrugInPrior30d = ko.observable((data.omDrugExposure30d == 1 || data.omDrugEraExposure30d == 1) || false);
- self.omDrugInPrior365d = ko.observable((data.omDrugExposure365d == 1 || data.omDrugEraExposure365d == 1) || false);
-
- self.omDrugEra = ko.observable((data.omDrugEra == 1)||false);
- self.omDrugEraOverlap = ko.observable((data.omDrugEraOverlap == 1)||false);
- self.omDrugEraEver = ko.observable((data.omDrugEraEver == 1)||false);
- self.omDrugGroup = ko.observable((data.omDrugGroup == 1)||false);
-
- self.omProcedureOcc365d = ko.observable((data.omProcedureOcc365d == 1)||false);
- self.omProcedureOcc30d = ko.observable((data.omProcedureOcc30d == 1)||false);
- self.omProcedureGroup = ko.observable((data.omProcedureGroup == 1)||false);
-
- self.omObservation = ko.observable((data.omObservation == 1)||false);
- self.omObservation365d = ko.observable((data.omObservation365d == 1)||false);
- self.omObservation30d = ko.observable((data.omObservation30d == 1)||false);
- self.omObservationCount365d = ko.observable((data.omObservationCount365d == 1)||false);
-
- self.omMeasurement365d = ko.observable((data.omMeasurement365d == 1)||false);
- self.omMeasurement30d = ko.observable((data.omMeasurement30d == 1)||false);
- self.omMeasurementCount365d = ko.observable((data.omMeasurementCount365d == 1)||false);
- self.omMeasurementBelow = ko.observable((data.omMeasurementBelow == 1)||false);
- self.omMeasurementAbove = ko.observable((data.omMeasurementAbove == 1)||false);
-
- self.omConceptCounts = ko.observable((data.omConceptCounts == 1)||false);
-
- self.omRiskScoresCharlson = ko.observable((data.omRiskScoresCharlson == 1)||false);
- self.omRiskScoresDcsi = ko.observable((data.omRiskScoresDcsi == 1)||false);
- self.omRiskScoresChads2 = ko.observable((data.omRiskScoresChads2 == 1)||false);
- self.omRiskScoresChads2vasc = ko.observable((data.omRiskScoresChads2vasc == 1)||false);
-
- self.omInteractionYear = ko.observable((data.omInteractionYear == 1)||false);
- self.omInteractionMonth = ko.observable((data.omInteractionMonth == 1)||false);
-
- // Outcome Model Settings - Derived
- self.omDemographics = ko.pureComputed(function () {
- return (self.omDemographicsGender() || self.omDemographicsRace() || self.omDemographicsEthnicity() || self.omDemographicsAge() || self.omDemographicsYear() || self.omDemographicsMonth());
+ self.addOutcome = function(od) {
+ // If the outcome was already part of the original definition.
+ // If yes, then preserve the internal id
+ var match = self.originalOutcomeList.find(function(item) {
+ return item.outcomeId() == od.outcomeId();
+ });
+ if (match) {
+ od.id = match.id;
+ }
+ self.outcomeList.push(od);
+
+ // Remove any default outcome definitions that might be sitting there
+ self.outcomeList.remove(function (item) {
+ return item.outcomeId() == 0;
+ });
+ }
+ self.outcomeCaption = ko.pureComputed(function() {
+ if (self.outcomeList().length > 0) {
+ var outcomeCaption = "";
+ for (var i = 0; i < self.outcomeList().length; i++) {
+ outcomeCaption += self.outcomeList()[i].outcomeCaption();
+ if (i >= 0 && i < self.outcomeList().length - 2) {
+ outcomeCaption += ", ";
+ } else if (i >= 0 && self.outcomeList().length >= 2 && i == self.outcomeList().length - 2) {
+ outcomeCaption += " and ";
+ }
+ }
+ return outcomeCaption;
+ } else {
+ self.outcome().outcomeCaption();
+ }
});
- self.omDemographicsIndeterminate = function() {
- var propCount = 0;
- propCount += self.omDemographicsGender() | 0;
- propCount += self.omDemographicsRace() | 0;
- propCount += self.omDemographicsEthnicity() | 0;
- propCount += self.omDemographicsAge() | 0;
- propCount += self.omDemographicsYear() | 0;
- propCount += self.omDemographicsMonth() | 0;
- return (propCount > 0 && propCount < 6);
- }
- self.omStrat = ko.pureComputed(function () {
- return !self.omMatch();
+ self.outcomeIdList = ko.pureComputed(function() {
+ if (self.outcomeList().length > 0) {
+ var outcomeIds = "";
+ for (var i = 0; i < self.outcomeList().length; i++) {
+ outcomeIds += self.outcomeList()[i].outcomeId();
+ if (i >= 0 && i < self.outcomeList().length - 1) {
+ outcomeIds += ", ";
+ }
+ }
+ return outcomeIds;
+ } else {
+ self.outcome().outcomeId();
+ }
+ });
+
+ self.analysisList = ko.observableArray();
+ if (data.analysisList && data.analysisList.length > 0) {
+ for (i = 0; i < data.analysisList.length; i++) {
+ var a = new AnalysisDefinition(data.analysisList[i], data.conceptSetInfo);
+ self.analysisList.push(a);
+ }
+ }
+ self.analysis = ko.pureComputed(function() {
+ if (self.analysisList().length <= 0) {
+ self.analysisList.push(new AnalysisDefinition());
+ }
+ return self.analysisList()[0];
+ });
+
+ self.readyForDisplay = ko.pureComputed(function () {
+ return (self.targetComparator().comparatorId() != null && self.targetComparator().comparatorId() > 0 &&
+ self.targetComparator().targetId() != null && self.targetComparator().targetId() > 0 &&
+ self.outcome().outcomeId() != null && self.outcome().outcomeId() > 0 &&
+ self.analysis().modelType() != null)
});
- self.omConditionOcc = ko.pureComputed(function() {
- return (self.omConditionOcc365d() || self.omConditionOcc30d() || self.omConditionOccInpt180d())
- });
- self.omConditionEra = ko.pureComputed(function() {
- return (self.omConditionEraEver() || self.omConditionEraOverlap())
- });
- self.omConditionGroup = ko.pureComputed(function() {
- return (self.omConditionGroupMeddra() || self.omConditionGroupSnomed())
- });
- self.omCondition = ko.pureComputed(function() {
- return (self.omConditionOcc() || self.omConditionEra())
- });
- self.omDrug = ko.pureComputed(function() {
- return (self.omDrugInPrior30d() || self.omDrugInPrior365d() || self.omDrugEraOverlap() || self.omDrugEraEver())
- });
- self.omDrugAggregation = ko.pureComputed(function() {
- return (self.omDrugExposure() || self.omDrugEra() || self.omDrugGroup())
- });
- self.omDrugExposure365d = ko.pureComputed(function() {
- return (self.omDrugExposure() && self.omDrugInPrior365d())
- });
- self.omDrugExposure30d = ko.pureComputed(function() {
- return (self.omDrugExposure() && self.omDrugInPrior30d())
- });
- self.omDrugEra365d = ko.pureComputed(function() {
- return (self.omDrugEra() && self.omDrugInPrior365d())
- });
- self.omDrugEra30d = ko.pureComputed(function() {
- return (self.omDrugEra() && self.omDrugInPrior30d())
- });
- self.omProcedureOcc = ko.pureComputed(function() {
- return (self.omProcedureOcc365d() || self.omProcedureOcc30d() || self.omProcedureGroup())
- });
- self.omMeasurement = ko.pureComputed(function() {
- return (self.omMeasurement365d() || self.omMeasurement30d() || self.omMeasurementCount365d() || self.omMeasurementBelow() || self.omMeasurementAbove())
- });
- self.omRiskScores = ko.pureComputed(function() {
- return (self.omRiskScoresCharlson() || self.omRiskScoresDcsi() || self.omRiskScoresChads2() || self.omRiskScoresChads2vasc())
- });
- self.omInteraction = ko.pureComputed(function() {
- return (self.omInteractionYear() || self.omInteractionMonth())
- });
+
+ self.jsonify = function() {
+ var cca = {
+ analysisId: self.analysisId || null,
+ name: self.name(),
+ targetComparatorList: [],
+ outcomeList: [],
+ analysisList: []
+ };
+
+ if (self.targetComparatorList() && self.targetComparatorList().length > 0) {
+ for (i = 0; i < self.targetComparatorList().length; i++) {
+ var tc = self.targetComparatorList()[i].jsonify();
+ cca.targetComparatorList.push(tc);
+ }
+ }
+
+ if (self.outcomeList() && self.outcomeList().length > 0) {
+ for (i = 0; i < self.outcomeList().length; i++) {
+ var o = self.outcomeList()[i].jsonify();
+ cca.outcomeList.push(o);
+ }
+ }
+
+ if (self.analysisList() && self.analysisList().length > 0) {
+ for (i = 0; i < self.analysisList().length; i++) {
+ var a = self.analysisList()[i].jsonify();
+ cca.analysisList.push(a);
+ }
+ }
+
+ return cca;
+ }
}
return ComparativeCohortAnalysis;
-});
\ No newline at end of file
+});
diff --git a/js/modules/cohortcomparison/OutcomeDefinition.js b/js/modules/cohortcomparison/OutcomeDefinition.js
new file mode 100644
index 000000000..6267d71a3
--- /dev/null
+++ b/js/modules/cohortcomparison/OutcomeDefinition.js
@@ -0,0 +1,40 @@
+define(function (require, exports) {
+
+ var $ = require('jquery');
+ var ko = require('knockout');
+ var CohortDefinition = require('cohortbuilder/CohortDefinition')
+
+ function OutcomeDefinition(data, cohortList) {
+ var self = this;
+ var data = data || {};
+ var cohortData;
+
+ self.id = data.id;
+ self.outcomeId = ko.observable(data.outcomeId != null ? data.outcomeId : 0);
+ self.outcomeCaption = ko.observable(null);
+ self.outcomeCohortDefinition = ko.observable(null);
+ if (cohortList && cohortList.length > 0) {
+ var selectedCohort = $.grep(cohortList, function (f) {
+ return f.id == data.outcomeId;
+ });
+ if (selectedCohort.length > 0) {
+ self.outcomeCaption(selectedCohort[0].name);
+ cohortData = {
+ id: selectedCohort[0].id,
+ name: selectedCohort[0].name,
+ expression: JSON.parse(selectedCohort[0].expression),
+ }
+ self.outcomeCohortDefinition(new CohortDefinition(cohortData));
+ }
+ }
+
+ self.jsonify = function() {
+ return {
+ id: self.id,
+ outcomeId: self.outcomeId(),
+ }
+ }
+ }
+
+ return OutcomeDefinition;
+});
diff --git a/js/modules/cohortcomparison/TargetComparatorDefinition.js b/js/modules/cohortcomparison/TargetComparatorDefinition.js
new file mode 100644
index 000000000..6b65eba30
--- /dev/null
+++ b/js/modules/cohortcomparison/TargetComparatorDefinition.js
@@ -0,0 +1,105 @@
+define(function (require, exports) {
+
+ var $ = require('jquery');
+ var ko = require('knockout');
+ var CohortDefinition = require('cohortbuilder/CohortDefinition')
+ var ConceptSet = require('conceptsetbuilder/InputTypes/ConceptSet')
+
+ function TargetComparatorDefinition(data, cohortList, conceptSetList) {
+ var self = this;
+ var data = data || {};
+ var conceptSetData;
+ var cohortData;
+ var selectedCohort;
+ var selectedConceptSet;
+
+ self.id = data.id;
+ self.targetId = ko.observable(data.targetId != null ? data.targetId : 0);
+ self.targetCaption = ko.observable(null);
+ self.targetCohortDefinition = ko.observable(null);
+ if (cohortList && cohortList.length > 0) {
+ selectedCohort = $.grep(cohortList, function (f) {
+ return f.id == data.targetId;
+ });
+ if (selectedCohort.length > 0) {
+ self.targetCaption(selectedCohort[0].name);
+ cohortData = {
+ id: selectedCohort[0].id,
+ name: selectedCohort[0].name,
+ expression: JSON.parse(selectedCohort[0].expression),
+ }
+ self.targetCohortDefinition(new CohortDefinition(cohortData));
+ }
+ }
+
+ self.comparatorId = ko.observable(data.comparatorId != null ? data.comparatorId : 0);
+ self.comparatorCaption = ko.observable(null);
+ self.comparatorCohortDefinition = ko.observable(null);
+ if (cohortList && cohortList.length > 0) {
+ selectedCohort = $.grep(cohortList, function (f) {
+ return f.id == data.comparatorId;
+ });
+ if (selectedCohort.length > 0) {
+ self.comparatorCaption(selectedCohort[0].name);
+ cohortData = {
+ id: selectedCohort[0].id,
+ name: selectedCohort[0].name,
+ expression: JSON.parse(selectedCohort[0].expression),
+ }
+ self.comparatorCohortDefinition(new CohortDefinition(cohortData));
+ }
+ }
+
+ self.psExclusionId = ko.observable(data.psExclusionId != null ? data.psExclusionId : 0);
+ self.psExclusionCaption = ko.observable(null);
+ self.psExclusionConceptSet = ko.observableArray(null);
+ self.psExclusionConceptSetSQL = ko.observable(null);
+ if (conceptSetList && conceptSetList.length > 0) {
+ selectedConceptSet = $.grep(conceptSetList, function (f) {
+ return f.id == data.psExclusionId;
+ });
+ if (selectedConceptSet.length > 0) {
+ self.psExclusionCaption(selectedConceptSet[0].name);
+ conceptSetData = {
+ id: self.psExclusionId(),
+ name: self.psExclusionCaption(),
+ expression: selectedConceptSet[0].expression,
+ };
+ self.psExclusionConceptSet.push(new ConceptSet(conceptSetData));
+ self.psExclusionConceptSetSQL(selectedConceptSet[0].sql);
+ }
+ }
+
+ self.psInclusionId = ko.observable(data.psInclusionId != null ? data.psInclusionId : 0);
+ self.psInclusionCaption = ko.observable(null);
+ self.psInclusionConceptSet = ko.observableArray(null);
+ self.psInclusionConceptSetSQL = ko.observable(null);
+ if (conceptSetList && conceptSetList.length > 0) {
+ selectedConceptSet = $.grep(conceptSetList, function (f) {
+ return f.id == data.psInclusionId;
+ });
+ if (selectedConceptSet.length > 0) {
+ self.psInclusionCaption(selectedConceptSet[0].name);
+ conceptSetData = {
+ id: self.psInclusionId(),
+ name: self.psInclusionCaption(),
+ expression: selectedConceptSet[0].expression,
+ };
+ self.psInclusionConceptSet.push(new ConceptSet(conceptSetData));
+ self.psInclusionConceptSetSQL(selectedConceptSet[0].sql);
+ }
+ }
+
+ self.jsonify = function() {
+ return {
+ id: self.id,
+ targetId: self.targetId(),
+ comparatorId: self.comparatorId(),
+ psExclusionId: self.psExclusionId(),
+ psInclusionId: self.psInclusionId(),
+ };
+ }
+ }
+
+ return TargetComparatorDefinition;
+});
diff --git a/js/plugins/bloodhound.min.js b/js/plugins/bloodhound.min.js
new file mode 100644
index 000000000..ee0891454
--- /dev/null
+++ b/js/plugins/bloodhound.min.js
@@ -0,0 +1,7 @@
+/*!
+ * typeahead.js 0.11.1
+ * https://github.com/twitter/typeahead.js
+ * Copyright 2013-2016 Twitter, Inc. and other contributors; Licensed MIT
+ */
+
+!function(a,b){"function"==typeof define&&define.amd?define("bloodhound",["jquery"],function(c){return a.Bloodhound=b(c)}):"object"==typeof exports?module.exports=b(require("jquery")):a.Bloodhound=b(jQuery)}(this,function(a){var b=function(){"use strict";return{isMsie:function(){return/(msie|trident)/i.test(navigator.userAgent)?navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]:!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},isElement:function(a){return!(!a||1!==a.nodeType)},isJQuery:function(b){return b instanceof a},toStr:function(a){return b.isUndefined(a)||null===a?"":a+""},bind:a.proxy,each:function(b,c){function d(a,b){return c(b,a)}a.each(b,d)},map:a.map,filter:a.grep,every:function(b,c){var d=!0;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?void 0:!1}),!!d):d},some:function(b,c){var d=!1;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?!1:void 0}),!!d):d},mixin:a.extend,identity:function(a){return a},clone:function(b){return a.extend(!0,{},b)},getIdGenerator:function(){var a=0;return function(){return a++}},templatify:function(b){function c(){return String(b)}return a.isFunction(b)?b:c},defer:function(a){setTimeout(a,0)},debounce:function(a,b,c){var d,e;return function(){var f,g,h=this,i=arguments;return f=function(){d=null,c||(e=a.apply(h,i))},g=c&&!d,clearTimeout(d),d=setTimeout(f,b),g&&(e=a.apply(h,i)),e}},throttle:function(a,b){var c,d,e,f,g,h;return g=0,h=function(){g=new Date,e=null,f=a.apply(c,d)},function(){var i=new Date,j=b-(i-g);return c=this,d=arguments,0>=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},stringify:function(a){return b.isString(a)?a:JSON.stringify(a)},noop:function(){}}}(),c="0.11.1",d=function(){"use strict";function a(a){return a=b.toStr(a),a?a.split(/\s+/):[]}function c(a){return a=b.toStr(a),a?a.split(/\W+/):[]}function d(a){return function(c){return c=b.isArray(c)?c:[].slice.call(arguments,0),function(d){var e=[];return b.each(c,function(c){e=e.concat(a(b.toStr(d[c])))}),e}}}return{nonword:c,whitespace:a,obj:{nonword:d(c),whitespace:d(a)}}}(),e=function(){"use strict";function c(c){this.maxSize=b.isNumber(c)?c:100,this.reset(),this.maxSize<=0&&(this.set=this.get=a.noop)}function d(){this.head=this.tail=null}function e(a,b){this.key=a,this.val=b,this.prev=this.next=null}return b.mixin(c.prototype,{set:function(a,b){var c,d=this.list.tail;this.size>=this.maxSize&&(this.list.remove(d),delete this.hash[d.key],this.size--),(c=this.hash[a])?(c.val=b,this.list.moveToFront(c)):(c=new e(a,b),this.list.add(c),this.hash[a]=c,this.size++)},get:function(a){var b=this.hash[a];return b?(this.list.moveToFront(b),b.val):void 0},reset:function(){this.size=0,this.hash={},this.list=new d}}),b.mixin(d.prototype,{add:function(a){this.head&&(a.next=this.head,this.head.prev=a),this.head=a,this.tail=this.tail||a},remove:function(a){a.prev?a.prev.next=a.next:this.head=a.next,a.next?a.next.prev=a.prev:this.tail=a.prev},moveToFront:function(a){this.remove(a),this.add(a)}}),c}(),f=function(){"use strict";function c(a,c){this.prefix=["__",a,"__"].join(""),this.ttlKey="__ttl__",this.keyMatcher=new RegExp("^"+b.escapeRegExChars(this.prefix)),this.ls=c||h,!this.ls&&this._noop()}function d(){return(new Date).getTime()}function e(a){return JSON.stringify(b.isUndefined(a)?null:a)}function f(b){return a.parseJSON(b)}function g(a){var b,c,d=[],e=h.length;for(b=0;e>b;b++)(c=h.key(b)).match(a)&&d.push(c.replace(a,""));return d}var h;try{h=window.localStorage,h.setItem("~~~","!"),h.removeItem("~~~")}catch(i){h=null}return b.mixin(c.prototype,{_prefix:function(a){return this.prefix+a},_ttlKey:function(a){return this._prefix(a)+this.ttlKey},_noop:function(){this.get=this.set=this.remove=this.clear=this.isExpired=b.noop},_safeSet:function(a,b){try{this.ls.setItem(a,b)}catch(c){"QuotaExceededError"===c.name&&(this.clear(),this._noop())}},get:function(a){return this.isExpired(a)&&this.remove(a),f(this.ls.getItem(this._prefix(a)))},set:function(a,c,f){return b.isNumber(f)?this._safeSet(this._ttlKey(a),e(d()+f)):this.ls.removeItem(this._ttlKey(a)),this._safeSet(this._prefix(a),e(c))},remove:function(a){return this.ls.removeItem(this._ttlKey(a)),this.ls.removeItem(this._prefix(a)),this},clear:function(){var a,b=g(this.keyMatcher);for(a=b.length;a--;)this.remove(b[a]);return this},isExpired:function(a){var c=f(this.ls.getItem(this._ttlKey(a)));return b.isNumber(c)&&d()>c?!0:!1}}),c}(),g=function(){"use strict";function c(a){a=a||{},this.cancelled=!1,this.lastReq=null,this._send=a.transport,this._get=a.limiter?a.limiter(this._get):this._get,this._cache=a.cache===!1?new e(0):h}var d=0,f={},g=6,h=new e(10);return c.setMaxPendingRequests=function(a){g=a},c.resetCache=function(){h.reset()},b.mixin(c.prototype,{_fingerprint:function(b){return b=b||{},b.url+b.type+a.param(b.data||{})},_get:function(a,b){function c(a){b(null,a),k._cache.set(i,a)}function e(){b(!0)}function h(){d--,delete f[i],k.onDeckRequestArgs&&(k._get.apply(k,k.onDeckRequestArgs),k.onDeckRequestArgs=null)}var i,j,k=this;i=this._fingerprint(a),this.cancelled||i!==this.lastReq||((j=f[i])?j.done(c).fail(e):g>d?(d++,f[i]=this._send(a).done(c).fail(e).always(h)):this.onDeckRequestArgs=[].slice.call(arguments,0))},get:function(c,d){var e,f;d=d||a.noop,c=b.isString(c)?{url:c}:c||{},f=this._fingerprint(c),this.cancelled=!1,this.lastReq=f,(e=this._cache.get(f))?d(null,e):this._get(c,d)},cancel:function(){this.cancelled=!0}}),c}(),h=window.SearchIndex=function(){"use strict";function c(c){c=c||{},c.datumTokenizer&&c.queryTokenizer||a.error("datumTokenizer and queryTokenizer are both required"),this.identify=c.identify||b.stringify,this.datumTokenizer=c.datumTokenizer,this.queryTokenizer=c.queryTokenizer,this.reset()}function d(a){return a=b.filter(a,function(a){return!!a}),a=b.map(a,function(a){return a.toLowerCase()})}function e(){var a={};return a[i]=[],a[h]={},a}function f(a){for(var b={},c=[],d=0,e=a.length;e>d;d++)b[a[d]]||(b[a[d]]=!0,c.push(a[d]));return c}function g(a,b){var c=0,d=0,e=[];a=a.sort(),b=b.sort();for(var f=a.length,g=b.length;f>c&&g>d;)a[c]
b[d]?d++:(e.push(a[c]),c++,d++);return e}var h="c",i="i";return b.mixin(c.prototype,{bootstrap:function(a){this.datums=a.datums,this.trie=a.trie},add:function(a){var c=this;a=b.isArray(a)?a:[a],b.each(a,function(a){var f,g;c.datums[f=c.identify(a)]=a,g=d(c.datumTokenizer(a)),b.each(g,function(a){var b,d,g;for(b=c.trie,d=a.split("");g=d.shift();)b=b[h][g]||(b[h][g]=e()),b[i].push(f)})})},get:function(a){var c=this;return b.map(a,function(a){return c.datums[a]})},search:function(a){var c,e,j=this;return c=d(this.queryTokenizer(a)),b.each(c,function(a){var b,c,d,f;if(e&&0===e.length)return!1;for(b=j.trie,c=a.split("");b&&(d=c.shift());)b=b[h][d];return b&&0===c.length?(f=b[i].slice(0),void(e=e?g(e,f):f)):(e=[],!1)}),e?b.map(f(e),function(a){return j.datums[a]}):[]},all:function(){var a=[];for(var b in this.datums)a.push(this.datums[b]);return a},reset:function(){this.datums={},this.trie=e()},serialize:function(){return{datums:this.datums,trie:this.trie}}}),c}(),i=function(){"use strict";function a(a){this.url=a.url,this.ttl=a.ttl,this.cache=a.cache,this.prepare=a.prepare,this.transform=a.transform,this.transport=a.transport,this.thumbprint=a.thumbprint,this.storage=new f(a.cacheKey)}var c;return c={data:"data",protocol:"protocol",thumbprint:"thumbprint"},b.mixin(a.prototype,{_settings:function(){return{url:this.url,type:"GET",dataType:"json"}},store:function(a){this.cache&&(this.storage.set(c.data,a,this.ttl),this.storage.set(c.protocol,location.protocol,this.ttl),this.storage.set(c.thumbprint,this.thumbprint,this.ttl))},fromCache:function(){var a,b={};return this.cache?(b.data=this.storage.get(c.data),b.protocol=this.storage.get(c.protocol),b.thumbprint=this.storage.get(c.thumbprint),a=b.thumbprint!==this.thumbprint||b.protocol!==location.protocol,b.data&&!a?b.data:null):null},fromNetwork:function(a){function b(){a(!0)}function c(b){a(null,e.transform(b))}var d,e=this;a&&(d=this.prepare(this._settings()),this.transport(d).fail(b).done(c))},clear:function(){return this.storage.clear(),this}}),a}(),j=function(){"use strict";function a(a){this.url=a.url,this.prepare=a.prepare,this.transform=a.transform,this.transport=new g({cache:a.cache,limiter:a.limiter,transport:a.transport})}return b.mixin(a.prototype,{_settings:function(){return{url:this.url,type:"GET",dataType:"json"}},get:function(a,b){function c(a,c){b(a?[]:e.transform(c))}var d,e=this;if(b)return a=a||"",d=this.prepare(a,this._settings()),this.transport.get(d,c)},cancelLastRequest:function(){this.transport.cancel()}}),a}(),k=function(){"use strict";function d(d){var e;return d?(e={url:null,ttl:864e5,cache:!0,cacheKey:null,thumbprint:"",prepare:b.identity,transform:b.identity,transport:null},d=b.isString(d)?{url:d}:d,d=b.mixin(e,d),!d.url&&a.error("prefetch requires url to be set"),d.transform=d.filter||d.transform,d.cacheKey=d.cacheKey||d.url,d.thumbprint=c+d.thumbprint,d.transport=d.transport?h(d.transport):a.ajax,d):null}function e(c){var d;if(c)return d={url:null,cache:!0,prepare:null,replace:null,wildcard:null,limiter:null,rateLimitBy:"debounce",rateLimitWait:300,transform:b.identity,transport:null},c=b.isString(c)?{url:c}:c,c=b.mixin(d,c),!c.url&&a.error("remote requires url to be set"),c.transform=c.filter||c.transform,c.prepare=f(c),c.limiter=g(c),c.transport=c.transport?h(c.transport):a.ajax,delete c.replace,delete c.wildcard,delete c.rateLimitBy,delete c.rateLimitWait,c}function f(a){function b(a,b){return b.url=f(b.url,a),b}function c(a,b){return b.url=b.url.replace(g,encodeURIComponent(a)),b}function d(a,b){return b}var e,f,g;return e=a.prepare,f=a.replace,g=a.wildcard,e?e:e=f?b:a.wildcard?c:d}function g(a){function c(a){return function(c){return b.debounce(c,a)}}function d(a){return function(c){return b.throttle(c,a)}}var e,f,g;return e=a.limiter,f=a.rateLimitBy,g=a.rateLimitWait,e||(e=/^throttle$/i.test(f)?d(g):c(g)),e}function h(c){return function(d){function e(a){b.defer(function(){g.resolve(a)})}function f(a){b.defer(function(){g.reject(a)})}var g=a.Deferred();return c(d,e,f),g}}return function(c){var f,g;return f={initialize:!0,identify:b.stringify,datumTokenizer:null,queryTokenizer:null,sufficient:5,sorter:null,local:[],prefetch:null,remote:null},c=b.mixin(f,c||{}),!c.datumTokenizer&&a.error("datumTokenizer is required"),!c.queryTokenizer&&a.error("queryTokenizer is required"),g=c.sorter,c.sorter=g?function(a){return a.sort(g)}:b.identity,c.local=b.isFunction(c.local)?c.local():c.local,c.prefetch=d(c.prefetch),c.remote=e(c.remote),c}}(),l=function(){"use strict";function c(a){a=k(a),this.sorter=a.sorter,this.identify=a.identify,this.sufficient=a.sufficient,this.local=a.local,this.remote=a.remote?new j(a.remote):null,this.prefetch=a.prefetch?new i(a.prefetch):null,this.index=new h({identify:this.identify,datumTokenizer:a.datumTokenizer,queryTokenizer:a.queryTokenizer}),a.initialize!==!1&&this.initialize()}var e;return e=window&&window.Bloodhound,c.noConflict=function(){return window&&(window.Bloodhound=e),c},c.tokenizers=d,b.mixin(c.prototype,{__ttAdapter:function(){function a(a,b,d){return c.search(a,b,d)}function b(a,b){return c.search(a,b)}var c=this;return this.remote?a:b},_loadPrefetch:function(){function b(a,b){return a?c.reject():(e.add(b),e.prefetch.store(e.index.serialize()),void c.resolve())}var c,d,e=this;return c=a.Deferred(),this.prefetch?(d=this.prefetch.fromCache())?(this.index.bootstrap(d),c.resolve()):this.prefetch.fromNetwork(b):c.resolve(),c.promise()},_initialize:function(){function a(){b.add(b.local)}var b=this;return this.clear(),(this.initPromise=this._loadPrefetch()).done(a),this.initPromise},initialize:function(a){return!this.initPromise||a?this._initialize():this.initPromise},add:function(a){return this.index.add(a),this},get:function(a){return a=b.isArray(a)?a:[].slice.call(arguments),this.index.get(a)},search:function(a,c,d){function e(a){var c=[];b.each(a,function(a){!b.some(f,function(b){return g.identify(a)===g.identify(b)})&&c.push(a)}),d&&d(c)}var f,g=this;return f=this.sorter(this.index.search(a)),c(this.remote?f.slice():f),this.remote&&f.length=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},stringify:function(a){return b.isString(a)?a:JSON.stringify(a)},noop:function(){}}}(),c=function(){"use strict";function a(a){var g,h;return h=b.mixin({},f,a),g={css:e(),classes:h,html:c(h),selectors:d(h)},{css:g.css,html:g.html,classes:g.classes,selectors:g.selectors,mixin:function(a){b.mixin(a,g)}}}function c(a){return{wrapper:' ',menu:''}}function d(a){var c={};return b.each(a,function(a,b){c[b]="."+a}),c}function e(){var a={wrapper:{position:"relative",display:"inline-block"},hint:{position:"absolute",top:"0",left:"0",borderColor:"transparent",boxShadow:"none",opacity:"1"},input:{position:"relative",verticalAlign:"top",backgroundColor:"transparent"},inputWithNoHint:{position:"relative",verticalAlign:"top"},menu:{position:"absolute",top:"100%",left:"0",zIndex:"100",display:"none"},ltr:{left:"0",right:"auto"},rtl:{left:"auto",right:" 0"}};return b.isMsie()&&b.mixin(a.input,{backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"}),a}var f={wrapper:"twitter-typeahead",input:"tt-input",hint:"tt-hint",menu:"tt-menu",dataset:"tt-dataset",suggestion:"tt-suggestion",selectable:"tt-selectable",empty:"tt-empty",open:"tt-open",cursor:"tt-cursor",highlight:"tt-highlight"};return a}(),d=function(){"use strict";function c(b){b&&b.el||a.error("EventBus initialized without el"),this.$el=a(b.el)}var d,e;return d="typeahead:",e={render:"rendered",cursorchange:"cursorchanged",select:"selected",autocomplete:"autocompleted"},b.mixin(c.prototype,{_trigger:function(b,c){var e;return e=a.Event(d+b),(c=c||[]).unshift(e),this.$el.trigger.apply(this.$el,c),e},before:function(a){var b,c;return b=[].slice.call(arguments,1),c=this._trigger("before"+a,b),c.isDefaultPrevented()},trigger:function(a){var b;this._trigger(a,[].slice.call(arguments,1)),(b=e[a])&&this._trigger(b,[].slice.call(arguments,1))}}),c}(),e=function(){"use strict";function a(a,b,c,d){var e;if(!c)return this;for(b=b.split(i),c=d?h(c,d):c,this._callbacks=this._callbacks||{};e=b.shift();)this._callbacks[e]=this._callbacks[e]||{sync:[],async:[]},this._callbacks[e][a].push(c);return this}function b(b,c,d){return a.call(this,"async",b,c,d)}function c(b,c,d){return a.call(this,"sync",b,c,d)}function d(a){var b;if(!this._callbacks)return this;for(a=a.split(i);b=a.shift();)delete this._callbacks[b];return this}function e(a){var b,c,d,e,g;if(!this._callbacks)return this;for(a=a.split(i),d=[].slice.call(arguments,1);(b=a.shift())&&(c=this._callbacks[b]);)e=f(c.sync,this,[b].concat(d)),g=f(c.async,this,[b].concat(d)),e()&&j(g);return this}function f(a,b,c){function d(){for(var d,e=0,f=a.length;!d&&f>e;e+=1)d=a[e].apply(b,c)===!1;return!d}return d}function g(){var a;return a=window.setImmediate?function(a){setImmediate(function(){a()})}:function(a){setTimeout(function(){a()},0)}}function h(a,b){return a.bind?a.bind(b):function(){a.apply(b,[].slice.call(arguments,0))}}var i=/\s+/,j=g();return{onSync:c,onAsync:b,off:d,trigger:e}}(),f=function(a){"use strict";function c(a,c,d){for(var e,f=[],g=0,h=a.length;h>g;g++)f.push(b.escapeRegExChars(a[g]));return e=d?"\\b("+f.join("|")+")\\b":"("+f.join("|")+")",c?new RegExp(e):new RegExp(e,"i")}var d={node:null,pattern:null,tagName:"strong",className:null,wordsOnly:!1,caseSensitive:!1};return function(e){function f(b){var c,d,f;return(c=h.exec(b.data))&&(f=a.createElement(e.tagName),e.className&&(f.className=e.className),d=b.splitText(c.index),d.splitText(c[0].length),f.appendChild(d.cloneNode(!0)),b.parentNode.replaceChild(f,d)),!!c}function g(a,b){for(var c,d=3,e=0;e').css({position:"absolute",visibility:"hidden",whiteSpace:"pre",fontFamily:b.css("font-family"),fontSize:b.css("font-size"),fontStyle:b.css("font-style"),fontVariant:b.css("font-variant"),fontWeight:b.css("font-weight"),wordSpacing:b.css("word-spacing"),letterSpacing:b.css("letter-spacing"),textIndent:b.css("text-indent"),textRendering:b.css("text-rendering"),textTransform:b.css("text-transform")}).insertAfter(b)}function f(a,b){return c.normalizeQuery(a)===c.normalizeQuery(b)}function g(a){return a.altKey||a.ctrlKey||a.metaKey||a.shiftKey}var h;return h={9:"tab",27:"esc",37:"left",39:"right",13:"enter",38:"up",40:"down"},c.normalizeQuery=function(a){return b.toStr(a).replace(/^\s*/g,"").replace(/\s{2,}/g," ")},b.mixin(c.prototype,e,{_onBlur:function(){this.resetInputValue(),this.trigger("blurred")},_onFocus:function(){this.queryWhenFocused=this.query,this.trigger("focused")},_onKeydown:function(a){var b=h[a.which||a.keyCode];this._managePreventDefault(b,a),b&&this._shouldTrigger(b,a)&&this.trigger(b+"Keyed",a)},_onInput:function(){this._setQuery(this.getInputValue()),this.clearHintIfInvalid(),this._checkLanguageDirection()},_managePreventDefault:function(a,b){var c;switch(a){case"up":case"down":c=!g(b);break;default:c=!1}c&&b.preventDefault()},_shouldTrigger:function(a,b){var c;switch(a){case"tab":c=!g(b);break;default:c=!0}return c},_checkLanguageDirection:function(){var a=(this.$input.css("direction")||"ltr").toLowerCase();this.dir!==a&&(this.dir=a,this.$hint.attr("dir",a),this.trigger("langDirChanged",a))},_setQuery:function(a,b){var c,d;c=f(a,this.query),d=c?this.query.length!==a.length:!1,this.query=a,b||c?!b&&d&&this.trigger("whitespaceChanged",this.query):this.trigger("queryChanged",this.query)},bind:function(){var a,c,d,e,f=this;return a=b.bind(this._onBlur,this),c=b.bind(this._onFocus,this),d=b.bind(this._onKeydown,this),e=b.bind(this._onInput,this),this.$input.on("blur.tt",a).on("focus.tt",c).on("keydown.tt",d),!b.isMsie()||b.isMsie()>9?this.$input.on("input.tt",e):this.$input.on("keydown.tt keypress.tt cut.tt paste.tt",function(a){h[a.which||a.keyCode]||b.defer(b.bind(f._onInput,f,a))}),this},focus:function(){this.$input.focus()},blur:function(){this.$input.blur()},getLangDir:function(){return this.dir},getQuery:function(){return this.query||""},setQuery:function(a,b){this.setInputValue(a),this._setQuery(a,b)},hasQueryChangedSinceLastFocus:function(){return this.query!==this.queryWhenFocused},getInputValue:function(){return this.$input.val()},setInputValue:function(a){this.$input.val(a),this.clearHintIfInvalid(),this._checkLanguageDirection()},resetInputValue:function(){this.setInputValue(this.query)},getHint:function(){return this.$hint.val()},setHint:function(a){this.$hint.val(a)},clearHint:function(){this.setHint("")},clearHintIfInvalid:function(){var a,b,c,d;a=this.getInputValue(),b=this.getHint(),c=a!==b&&0===b.indexOf(a),d=""!==a&&c&&!this.hasOverflow(),!d&&this.clearHint()},hasFocus:function(){return this.$input.is(":focus")},hasOverflow:function(){var a=this.$input.width()-2;return this.$overflowHelper.text(this.getInputValue()),this.$overflowHelper.width()>=a},isCursorAtEnd:function(){var a,c,d;return a=this.$input.val().length,c=this.$input[0].selectionStart,b.isNumber(c)?c===a:document.selection?(d=document.selection.createRange(),d.moveStart("character",-a),a===d.text.length):!0},destroy:function(){this.$hint.off(".tt"),this.$input.off(".tt"),this.$overflowHelper.remove(),this.$hint=this.$input=this.$overflowHelper=a("")}}),c}(),h=function(){"use strict";function c(c,e){c=c||{},c.templates=c.templates||{},c.templates.notFound=c.templates.notFound||c.templates.empty,c.source||a.error("missing source"),c.node||a.error("missing node"),c.name&&!h(c.name)&&a.error("invalid dataset name: "+c.name),e.mixin(this),this.highlight=!!c.highlight,this.name=c.name||j(),this.limit=c.limit||5,this.displayFn=d(c.display||c.displayKey),this.templates=g(c.templates,this.displayFn),this.source=c.source.__ttAdapter?c.source.__ttAdapter():c.source,this.async=b.isUndefined(c.async)?this.source.length>2:!!c.async,this._resetLastSuggestion(),this.$el=a(c.node).addClass(this.classes.dataset).addClass(this.classes.dataset+"-"+this.name)}function d(a){function c(b){return b[a]}return a=a||b.stringify,b.isFunction(a)?a:c}function g(c,d){function e(b){return a("
").text(d(b))}return{notFound:c.notFound&&b.templatify(c.notFound),pending:c.pending&&b.templatify(c.pending),header:c.header&&b.templatify(c.header),footer:c.footer&&b.templatify(c.footer),suggestion:c.suggestion||e}}function h(a){return/^[_a-zA-Z0-9-]+$/.test(a)}var i,j;return i={val:"tt-selectable-display",obj:"tt-selectable-object"},j=b.getIdGenerator(),c.extractData=function(b){var c=a(b);return c.data(i.obj)?{val:c.data(i.val)||"",obj:c.data(i.obj)||null}:null},b.mixin(c.prototype,e,{_overwrite:function(a,b){b=b||[],b.length?this._renderSuggestions(a,b):this.async&&this.templates.pending?this._renderPending(a):!this.async&&this.templates.notFound?this._renderNotFound(a):this._empty(),this.trigger("rendered",this.name,b,!1)},_append:function(a,b){b=b||[],b.length&&this.$lastSuggestion.length?this._appendSuggestions(a,b):b.length?this._renderSuggestions(a,b):!this.$lastSuggestion.length&&this.templates.notFound&&this._renderNotFound(a),this.trigger("rendered",this.name,b,!0)},_renderSuggestions:function(a,b){var c;c=this._getSuggestionsFragment(a,b),this.$lastSuggestion=c.children().last(),this.$el.html(c).prepend(this._getHeader(a,b)).append(this._getFooter(a,b))},_appendSuggestions:function(a,b){var c,d;c=this._getSuggestionsFragment(a,b),d=c.children().last(),this.$lastSuggestion.after(c),this.$lastSuggestion=d},_renderPending:function(a){var b=this.templates.pending;this._resetLastSuggestion(),b&&this.$el.html(b({query:a,dataset:this.name}))},_renderNotFound:function(a){var b=this.templates.notFound;this._resetLastSuggestion(),b&&this.$el.html(b({query:a,dataset:this.name}))},_empty:function(){this.$el.empty(),this._resetLastSuggestion()},_getSuggestionsFragment:function(c,d){var e,g=this;return e=document.createDocumentFragment(),b.each(d,function(b){var d,f;f=g._injectQuery(c,b),d=a(g.templates.suggestion(f)).data(i.obj,b).data(i.val,g.displayFn(b)).addClass(g.classes.suggestion+" "+g.classes.selectable),e.appendChild(d[0])}),this.highlight&&f({className:this.classes.highlight,node:e,pattern:c}),a(e)},_getFooter:function(a,b){return this.templates.footer?this.templates.footer({query:a,suggestions:b,dataset:this.name}):null},_getHeader:function(a,b){return this.templates.header?this.templates.header({query:a,suggestions:b,dataset:this.name}):null},_resetLastSuggestion:function(){this.$lastSuggestion=a()},_injectQuery:function(a,c){return b.isObject(c)?b.mixin({_query:a},c):c},update:function(b){function c(a){g||(g=!0,a=(a||[]).slice(0,e.limit),h=a.length,e._overwrite(b,a),h
")}}),c}(),i=function(){"use strict";function c(c,d){function e(b){var c=f.$node.find(b.node).first();return b.node=c.length?c:a("").appendTo(f.$node),new h(b,d)}var f=this;c=c||{},c.node||a.error("node is required"),d.mixin(this),this.$node=a(c.node),this.query=null,this.datasets=b.map(c.datasets,e)}return b.mixin(c.prototype,e,{_onSelectableClick:function(b){this.trigger("selectableClicked",a(b.currentTarget))},_onRendered:function(a,b,c,d){this.$node.toggleClass(this.classes.empty,this._allDatasetsEmpty()),this.trigger("datasetRendered",b,c,d)},_onCleared:function(){this.$node.toggleClass(this.classes.empty,this._allDatasetsEmpty()),this.trigger("datasetCleared")},_propagate:function(){this.trigger.apply(this,arguments)},_allDatasetsEmpty:function(){function a(a){return a.isEmpty()}return b.every(this.datasets,a)},_getSelectables:function(){return this.$node.find(this.selectors.selectable)},_removeCursor:function(){var a=this.getActiveSelectable();a&&a.removeClass(this.classes.cursor)},_ensureVisible:function(a){var b,c,d,e;b=a.position().top,c=b+a.outerHeight(!0),d=this.$node.scrollTop(),e=this.$node.height()+parseInt(this.$node.css("paddingTop"),10)+parseInt(this.$node.css("paddingBottom"),10),0>b?this.$node.scrollTop(d+b):c>e&&this.$node.scrollTop(d+(c-e))},bind:function(){var a,c=this;return a=b.bind(this._onSelectableClick,this),this.$node.on("click.tt",this.selectors.selectable,a),b.each(this.datasets,function(a){a.onSync("asyncRequested",c._propagate,c).onSync("asyncCanceled",c._propagate,c).onSync("asyncReceived",c._propagate,c).onSync("rendered",c._onRendered,c).onSync("cleared",c._onCleared,c)}),this},isOpen:function(){return this.$node.hasClass(this.classes.open)},open:function(){this.$node.addClass(this.classes.open)},close:function(){this.$node.removeClass(this.classes.open),this._removeCursor()},setLanguageDirection:function(a){this.$node.attr("dir",a)},selectableRelativeToCursor:function(a){var b,c,d,e;return c=this.getActiveSelectable(),b=this._getSelectables(),d=c?b.index(c):-1,e=d+a,e=(e+1)%(b.length+1)-1,e=-1>e?b.length-1:e,-1===e?null:b.eq(e)},setCursor:function(a){this._removeCursor(),(a=a&&a.first())&&(a.addClass(this.classes.cursor),this._ensureVisible(a))},getSelectableData:function(a){return a&&a.length?h.extractData(a):null},getActiveSelectable:function(){var a=this._getSelectables().filter(this.selectors.cursor).first();return a.length?a:null},getTopSelectable:function(){var a=this._getSelectables().first();return a.length?a:null},update:function(a){function c(b){b.update(a)}var d=a!==this.query;return d&&(this.query=a,b.each(this.datasets,c)),d},empty:function(){function a(a){a.clear()}b.each(this.datasets,a),this.query=null,this.$node.addClass(this.classes.empty)},destroy:function(){function c(a){a.destroy()}this.$node.off(".tt"),this.$node=a("
"),b.each(this.datasets,c)}}),c}(),j=function(){"use strict";function a(){i.apply(this,[].slice.call(arguments,0))}var c=i.prototype;return b.mixin(a.prototype,i.prototype,{open:function(){return!this._allDatasetsEmpty()&&this._show(),c.open.apply(this,[].slice.call(arguments,0))},close:function(){return this._hide(),c.close.apply(this,[].slice.call(arguments,0))},_onRendered:function(){return this._allDatasetsEmpty()?this._hide():this.isOpen()&&this._show(),c._onRendered.apply(this,[].slice.call(arguments,0))},_onCleared:function(){return this._allDatasetsEmpty()?this._hide():this.isOpen()&&this._show(),c._onCleared.apply(this,[].slice.call(arguments,0))},setLanguageDirection:function(a){return this.$node.css("ltr"===a?this.css.ltr:this.css.rtl),c.setLanguageDirection.apply(this,[].slice.call(arguments,0))},_hide:function(){this.$node.hide()},_show:function(){this.$node.css("display","block")}}),a}(),k=function(){"use strict";function c(c,e){var f,g,h,i,j,k,l,m,n,o,p;c=c||{},c.input||a.error("missing input"),c.menu||a.error("missing menu"),c.eventBus||a.error("missing event bus"),e.mixin(this),this.eventBus=c.eventBus,this.minLength=b.isNumber(c.minLength)?c.minLength:1,this.input=c.input,this.menu=c.menu,this.enabled=!0,this.active=!1,this.input.hasFocus()&&this.activate(),this.dir=this.input.getLangDir(),this._hacks(),this.menu.bind().onSync("selectableClicked",this._onSelectableClicked,this).onSync("asyncRequested",this._onAsyncRequested,this).onSync("asyncCanceled",this._onAsyncCanceled,this).onSync("asyncReceived",this._onAsyncReceived,this).onSync("datasetRendered",this._onDatasetRendered,this).onSync("datasetCleared",this._onDatasetCleared,this),f=d(this,"activate","open","_onFocused"),g=d(this,"deactivate","_onBlurred"),h=d(this,"isActive","isOpen","_onEnterKeyed"),i=d(this,"isActive","isOpen","_onTabKeyed"),j=d(this,"isActive","_onEscKeyed"),k=d(this,"isActive","open","_onUpKeyed"),l=d(this,"isActive","open","_onDownKeyed"),m=d(this,"isActive","isOpen","_onLeftKeyed"),n=d(this,"isActive","isOpen","_onRightKeyed"),o=d(this,"_openIfActive","_onQueryChanged"),p=d(this,"_openIfActive","_onWhitespaceChanged"),this.input.bind().onSync("focused",f,this).onSync("blurred",g,this).onSync("enterKeyed",h,this).onSync("tabKeyed",i,this).onSync("escKeyed",j,this).onSync("upKeyed",k,this).onSync("downKeyed",l,this).onSync("leftKeyed",m,this).onSync("rightKeyed",n,this).onSync("queryChanged",o,this).onSync("whitespaceChanged",p,this).onSync("langDirChanged",this._onLangDirChanged,this)}function d(a){var c=[].slice.call(arguments,1);return function(){var d=[].slice.call(arguments);b.each(c,function(b){return a[b].apply(a,d)})}}return b.mixin(c.prototype,{_hacks:function(){var c,d;c=this.input.$input||a("
"),d=this.menu.$node||a("
"),c.on("blur.tt",function(a){var e,f,g;e=document.activeElement,f=d.is(e),g=d.has(e).length>0,b.isMsie()&&(f||g)&&(a.preventDefault(),a.stopImmediatePropagation(),b.defer(function(){c.focus()}))}),d.on("mousedown.tt",function(a){a.preventDefault()})},_onSelectableClicked:function(a,b){this.select(b)},_onDatasetCleared:function(){this._updateHint()},_onDatasetRendered:function(a,b,c,d){this._updateHint(),this.eventBus.trigger("render",c,d,b)},_onAsyncRequested:function(a,b,c){this.eventBus.trigger("asyncrequest",c,b)},_onAsyncCanceled:function(a,b,c){this.eventBus.trigger("asynccancel",c,b)},_onAsyncReceived:function(a,b,c){this.eventBus.trigger("asyncreceive",c,b)},_onFocused:function(){this._minLengthMet()&&this.menu.update(this.input.getQuery())},_onBlurred:function(){this.input.hasQueryChangedSinceLastFocus()&&this.eventBus.trigger("change",this.input.getQuery())},_onEnterKeyed:function(a,b){var c;(c=this.menu.getActiveSelectable())&&this.select(c)&&b.preventDefault()},_onTabKeyed:function(a,b){var c;(c=this.menu.getActiveSelectable())?this.select(c)&&b.preventDefault():(c=this.menu.getTopSelectable())&&this.autocomplete(c)&&b.preventDefault()},_onEscKeyed:function(){this.close()},_onUpKeyed:function(){this.moveCursor(-1)},_onDownKeyed:function(){this.moveCursor(1)},_onLeftKeyed:function(){"rtl"===this.dir&&this.input.isCursorAtEnd()&&this.autocomplete(this.menu.getTopSelectable())},_onRightKeyed:function(){"ltr"===this.dir&&this.input.isCursorAtEnd()&&this.autocomplete(this.menu.getTopSelectable())},_onQueryChanged:function(a,b){this._minLengthMet(b)?this.menu.update(b):this.menu.empty()},_onWhitespaceChanged:function(){this._updateHint()},_onLangDirChanged:function(a,b){this.dir!==b&&(this.dir=b,this.menu.setLanguageDirection(b))},_openIfActive:function(){this.isActive()&&this.open()},_minLengthMet:function(a){return a=b.isString(a)?a:this.input.getQuery()||"",a.length>=this.minLength},_updateHint:function(){var a,c,d,e,f,h,i;a=this.menu.getTopSelectable(),c=this.menu.getSelectableData(a),d=this.input.getInputValue(),!c||b.isBlankString(d)||this.input.hasOverflow()?this.input.clearHint():(e=g.normalizeQuery(d),f=b.escapeRegExChars(e),h=new RegExp("^(?:"+f+")(.+$)","i"),i=h.exec(c.val),i&&this.input.setHint(d+i[1]))},isEnabled:function(){return this.enabled},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},isActive:function(){return this.active},activate:function(){return this.isActive()?!0:!this.isEnabled()||this.eventBus.before("active")?!1:(this.active=!0,this.eventBus.trigger("active"),!0)},deactivate:function(){return this.isActive()?this.eventBus.before("idle")?!1:(this.active=!1,this.close(),this.eventBus.trigger("idle"),!0):!0},isOpen:function(){return this.menu.isOpen()},open:function(){return this.isOpen()||this.eventBus.before("open")||(this.menu.open(),this._updateHint(),this.eventBus.trigger("open")),this.isOpen()},close:function(){return this.isOpen()&&!this.eventBus.before("close")&&(this.menu.close(),this.input.clearHint(),this.input.resetInputValue(),this.eventBus.trigger("close")),!this.isOpen()},setVal:function(a){this.input.setQuery(b.toStr(a))},getVal:function(){return this.input.getQuery()},select:function(a){var b=this.menu.getSelectableData(a);return b&&!this.eventBus.before("select",b.obj)?(this.input.setQuery(b.val,!0),this.eventBus.trigger("select",b.obj),this.close(),!0):!1},autocomplete:function(a){var b,c,d;return b=this.input.getQuery(),c=this.menu.getSelectableData(a),d=c&&b!==c.val,d&&!this.eventBus.before("autocomplete",c.obj)?(this.input.setQuery(c.val),this.eventBus.trigger("autocomplete",c.obj),!0):!1},moveCursor:function(a){var b,c,d,e,f;return b=this.input.getQuery(),c=this.menu.selectableRelativeToCursor(a),d=this.menu.getSelectableData(c),e=d?d.obj:null,f=this._minLengthMet()&&this.menu.update(b),f||this.eventBus.before("cursorchange",e)?!1:(this.menu.setCursor(c),d?this.input.setInputValue(d.val):(this.input.resetInputValue(),this._updateHint()),this.eventBus.trigger("cursorchange",e),!0)},destroy:function(){this.input.destroy(),this.menu.destroy()}}),c}();!function(){"use strict";function e(b,c){b.each(function(){var b,d=a(this);(b=d.data(p.typeahead))&&c(b,d)})}function f(a,b){return a.clone().addClass(b.classes.hint).removeData().css(b.css.hint).css(l(a)).prop("readonly",!0).removeAttr("id name placeholder required").attr({autocomplete:"off",spellcheck:"false",tabindex:-1})}function h(a,b){a.data(p.attrs,{dir:a.attr("dir"),autocomplete:a.attr("autocomplete"),spellcheck:a.attr("spellcheck"),style:a.attr("style")}),a.addClass(b.classes.input).attr({autocomplete:"off",spellcheck:!1});try{!a.attr("dir")&&a.attr("dir","auto")}catch(c){}return a}function l(a){return{backgroundAttachment:a.css("background-attachment"),backgroundClip:a.css("background-clip"),backgroundColor:a.css("background-color"),backgroundImage:a.css("background-image"),backgroundOrigin:a.css("background-origin"),backgroundPosition:a.css("background-position"),backgroundRepeat:a.css("background-repeat"),backgroundSize:a.css("background-size")}}function m(a){var c,d;c=a.data(p.www),d=a.parent().filter(c.selectors.wrapper),b.each(a.data(p.attrs),function(c,d){b.isUndefined(c)?a.removeAttr(d):a.attr(d,c)}),a.removeData(p.typeahead).removeData(p.www).removeData(p.attr).removeClass(c.classes.input),d.length&&(a.detach().insertAfter(d),d.remove())}function n(c){var d,e;return d=b.isJQuery(c)||b.isElement(c),e=d?a(c).first():[],e.length?e:null}var o,p,q;o=a.fn.typeahead,p={www:"tt-www",attrs:"tt-attrs",typeahead:"tt-typeahead"},q={initialize:function(e,l){function m(){var c,m,q,r,s,t,u,v,w,x,y;b.each(l,function(a){a.highlight=!!e.highlight}),c=a(this),m=a(o.html.wrapper),q=n(e.hint),r=n(e.menu),s=e.hint!==!1&&!q,t=e.menu!==!1&&!r,s&&(q=f(c,o)),t&&(r=a(o.html.menu).css(o.css.menu)),q&&q.val(""),c=h(c,o),(s||t)&&(m.css(o.css.wrapper),c.css(s?o.css.input:o.css.inputWithNoHint),c.wrap(m).parent().prepend(s?q:null).append(t?r:null)),y=t?j:i,u=new d({el:c}),v=new g({hint:q,input:c},o),w=new y({node:r,datasets:l},o),x=new k({input:v,menu:w,eventBus:u,minLength:e.minLength},o),c.data(p.www,o),c.data(p.typeahead,x)}var o;return l=b.isArray(l)?l:[].slice.call(arguments,1),e=e||{},o=c(e.classNames),this.each(m)},isEnabled:function(){var a;return e(this.first(),function(b){a=b.isEnabled()}),a},enable:function(){return e(this,function(a){a.enable()}),this},disable:function(){return e(this,function(a){a.disable()}),this},isActive:function(){var a;return e(this.first(),function(b){a=b.isActive()}),a},activate:function(){return e(this,function(a){a.activate()}),this},deactivate:function(){return e(this,function(a){a.deactivate()}),this},isOpen:function(){var a;return e(this.first(),function(b){a=b.isOpen()}),a},open:function(){return e(this,function(a){a.open()}),this},close:function(){return e(this,function(a){a.close()}),this},select:function(b){var c=!1,d=a(b);return e(this.first(),function(a){c=a.select(d)}),c},autocomplete:function(b){var c=!1,d=a(b);return e(this.first(),function(a){c=a.autocomplete(d)}),c},moveCursor:function(a){var b=!1;return e(this.first(),function(c){b=c.moveCursor(a)}),b},val:function(a){var b;return arguments.length?(e(this,function(b){b.setVal(a)}),this):(e(this.first(),function(a){b=a.getVal()}),b)},destroy:function(){return e(this,function(a,b){m(b),a.destroy()}),this}},a.fn.typeahead=function(a){return q[a]?q[a].apply(this,[].slice.call(arguments,1)):q.initialize.apply(this,arguments)},a.fn.typeahead.noConflict=function(){return a.fn.typeahead=o,this}}()});
\ No newline at end of file
diff --git a/js/styles/atlas.css b/js/styles/atlas.css
index b3b2d2712..87c9181b3 100644
--- a/js/styles/atlas.css
+++ b/js/styles/atlas.css
@@ -1977,3 +1977,22 @@ span.new-role {
-webkit-box-shadow: none;
box-shadow: none;
}
+
+.panel-body-min {
+ margin:0px;
+ padding:5px;
+}
+
+.flat-list {
+ margin:0px;
+ padding: 0px;
+ list-style:none;
+}
+
+.button-box {
+ border: 1px solid black;
+ display: inline-block;
+ padding: 5px 10px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+}
\ No newline at end of file
diff --git a/js/styles/typeahead.css b/js/styles/typeahead.css
new file mode 100644
index 000000000..9159f0449
--- /dev/null
+++ b/js/styles/typeahead.css
@@ -0,0 +1,200 @@
+/*
+ * typehead.js-bootstrap3.less
+ * @version 0.2.3
+ * https://github.com/hyspace/typeahead.js-bootstrap3.less
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+.has-warning .twitter-typeahead .tt-input,
+.has-warning .twitter-typeahead .tt-hint {
+ border-color: #8a6d3b;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.has-warning .twitter-typeahead .tt-input:focus,
+.has-warning .twitter-typeahead .tt-hint:focus {
+ border-color: #66512c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
+}
+.has-error .twitter-typeahead .tt-input,
+.has-error .twitter-typeahead .tt-hint {
+ border-color: #a94442;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.has-error .twitter-typeahead .tt-input:focus,
+.has-error .twitter-typeahead .tt-hint:focus {
+ border-color: #843534;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
+}
+.has-success .twitter-typeahead .tt-input,
+.has-success .twitter-typeahead .tt-hint {
+ border-color: #3c763d;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+.has-success .twitter-typeahead .tt-input:focus,
+.has-success .twitter-typeahead .tt-hint:focus {
+ border-color: #2b542c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
+}
+.input-group .twitter-typeahead:first-child .tt-input,
+.input-group .twitter-typeahead:first-child .tt-hint {
+ border-bottom-left-radius: 4px;
+ border-top-left-radius: 4px;
+ width: 100%;
+}
+.input-group .twitter-typeahead:last-child .tt-input,
+.input-group .twitter-typeahead:last-child .tt-hint {
+ border-bottom-right-radius: 4px;
+ border-top-right-radius: 4px;
+ width: 100%;
+}
+.input-group.input-group-sm .twitter-typeahead .tt-input,
+.input-group.input-group-sm .twitter-typeahead .tt-hint {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+select.input-group.input-group-sm .twitter-typeahead .tt-input,
+select.input-group.input-group-sm .twitter-typeahead .tt-hint {
+ height: 30px;
+ line-height: 30px;
+}
+textarea.input-group.input-group-sm .twitter-typeahead .tt-input,
+textarea.input-group.input-group-sm .twitter-typeahead .tt-hint,
+select[multiple].input-group.input-group-sm .twitter-typeahead .tt-input,
+select[multiple].input-group.input-group-sm .twitter-typeahead .tt-hint {
+ height: auto;
+}
+.input-group.input-group-sm .twitter-typeahead:not(:first-child):not(:last-child) .tt-input,
+.input-group.input-group-sm .twitter-typeahead:not(:first-child):not(:last-child) .tt-hint {
+ border-radius: 0;
+}
+.input-group.input-group-sm .twitter-typeahead:first-child .tt-input,
+.input-group.input-group-sm .twitter-typeahead:first-child .tt-hint {
+ border-bottom-left-radius: 3px;
+ border-top-left-radius: 3px;
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+.input-group.input-group-sm .twitter-typeahead:last-child .tt-input,
+.input-group.input-group-sm .twitter-typeahead:last-child .tt-hint {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ border-bottom-right-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.input-group.input-group-lg .twitter-typeahead .tt-input,
+.input-group.input-group-lg .twitter-typeahead .tt-hint {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
+}
+select.input-group.input-group-lg .twitter-typeahead .tt-input,
+select.input-group.input-group-lg .twitter-typeahead .tt-hint {
+ height: 46px;
+ line-height: 46px;
+}
+textarea.input-group.input-group-lg .twitter-typeahead .tt-input,
+textarea.input-group.input-group-lg .twitter-typeahead .tt-hint,
+select[multiple].input-group.input-group-lg .twitter-typeahead .tt-input,
+select[multiple].input-group.input-group-lg .twitter-typeahead .tt-hint {
+ height: auto;
+}
+.input-group.input-group-lg .twitter-typeahead:not(:first-child):not(:last-child) .tt-input,
+.input-group.input-group-lg .twitter-typeahead:not(:first-child):not(:last-child) .tt-hint {
+ border-radius: 0;
+}
+.input-group.input-group-lg .twitter-typeahead:first-child .tt-input,
+.input-group.input-group-lg .twitter-typeahead:first-child .tt-hint {
+ border-bottom-left-radius: 6px;
+ border-top-left-radius: 6px;
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+}
+.input-group.input-group-lg .twitter-typeahead:last-child .tt-input,
+.input-group.input-group-lg .twitter-typeahead:last-child .tt-hint {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ border-bottom-right-radius: 6px;
+ border-top-right-radius: 6px;
+}
+.twitter-typeahead {
+ width: 100%;
+ float: left;
+}
+.input-group .twitter-typeahead {
+ display: table-cell !important;
+}
+.twitter-typeahead .tt-hint {
+ color: #999999;
+}
+.twitter-typeahead .tt-input {
+ z-index: 2;
+}
+.twitter-typeahead .tt-input[disabled],
+.twitter-typeahead .tt-input[readonly],
+fieldset[disabled] .twitter-typeahead .tt-input {
+ cursor: not-allowed;
+ background-color: #eeeeee !important;
+}
+.tt-dropdown-menu,
+.tt-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ min-width: 160px;
+ width: 100%;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ font-size: 14px;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ background-clip: padding-box;
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+}
+.tt-dropdown-menu .tt-suggestion,
+.tt-menu .tt-suggestion {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.42857143;
+ color: #333333;
+}
+.tt-dropdown-menu .tt-suggestion.tt-cursor,
+.tt-menu .tt-suggestion.tt-cursor,
+.tt-dropdown-menu .tt-suggestion:hover,
+.tt-menu .tt-suggestion:hover {
+ cursor: pointer;
+ text-decoration: none;
+ outline: 0;
+ background-color: #f5f5f5;
+ color: #262626;
+}
+.tt-dropdown-menu .tt-suggestion.tt-cursor a,
+.tt-menu .tt-suggestion.tt-cursor a,
+.tt-dropdown-menu .tt-suggestion:hover a,
+.tt-menu .tt-suggestion:hover a {
+ color: #262626;
+}
+.tt-dropdown-menu .tt-suggestion p,
+.tt-menu .tt-suggestion p {
+ margin: 0;
+}
\ No newline at end of file