diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1da0383
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,46 @@
+# build fodler
+build
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# =========================
+# Operating System Files
+# =========================
+
+# OSX
+# =========================
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
diff --git a/.sense-go.yml b/.sense-go.yml
new file mode 100644
index 0000000..aebf586
--- /dev/null
+++ b/.sense-go.yml
@@ -0,0 +1,11 @@
+packageName: "barsPlus"
+wbfolder:
+ enabled: true
+watch:
+ - "./src/**/*.*"
+deployment:
+ toLocal:
+ enabled: true
+ pathFetching: false
+ localExtensionsBaseDir: "C:\\Users\\thaenig\\Documents\\Qlik\\Sense\\Extensions"
+ extensionDirOverride: "barsPlus"
\ No newline at end of file
diff --git a/barsPlus-props.js b/barsPlus-props.js
deleted file mode 100644
index 234875c..0000000
--- a/barsPlus-props.js
+++ /dev/null
@@ -1,899 +0,0 @@
-define( [ ],
-/**
- * barsPlus Properties - set up appearance of accordion properties panel in Qlik Sense
- *
- * Defaults are defined here.
- * Settings modify objects in object: layout.props
- *
- * Author: L. Woodside
- * Modification History:
- *
- * Version Person Date Description
- * V1.0.0 L. Woodside 19-Dec-2016 Initial Release
- * V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
- * V1.2.0 L. Woodside 07-Jan-2017 Allow multiple measures
- * V1.3.0 L. Woodside 15-Jan-2017 Improved color options
- * V1.3.1 L. Woodside 27-Jan-2017 Fix problem with legend properties
- *
-*/
-function () {
- 'use strict';
- var dimensions = {
- uses: "dimensions",
- min: 0,
- max: 2,
- items: {
- attribute: {
- type: "string",
- ref: "qAttributeExpressions.0.qExpression",
- label: "Dimension attribute",
- expression: "always",
- defaultValue: ""
- }
- }
- };
- var measures = {
- uses: "measures",
- min: 1,
- max: 10,
- items: {
- attribute: {
- type: "string",
- component: "expression",
- ref: "qAttributeExpressions.0.qExpression",
- label: "Measure attribute",
- expression: "always",
- defaultValue: ""
- }
- }
- };
- var sorting = {
- uses: "sorting"
- };
- var addons = {
- uses: "addons"
- ,items: {
- dataHandling: {
- uses: "dataHandling"
- },
- selectionMode: {
- type: "string",
- component: "dropdown",
- label: "Selection mode",
- ref: "selectionMode",
- defaultValue: "CONFIRM",
- options: [
- { value: "CONFIRM", label: "Standard"},
- { value: "QUICK", label: "Quick"}
- ]
- }
- }
- };
-
- var appearance = {
- uses: "settings",
- items: {
- presentation: {
- type: "items",
- label: "Presentation",
- items: {
- orientation: {
- type: "string",
- component: "dropdown",
- label: "Orientation",
- ref: "props.orientation",
- defaultValue: "V",
- options: [
- { value: "V", label: "Vertical bars"},
- { value: "H", label: "Horizontal bars"},
- ]
- },
- normalized: {
- type: "boolean",
- component: "switch",
- label: "100% bar chart",
- ref: "props.normalized",
- defaultValue: false,
- options: [
- { value: false, label: "Not 100%"},
- { value: true, label: "100% bars"}
- ],
- show: function(data) {
- return (data.qHyperCubeDef.qDimensions.length > 1
- || (data.qHyperCubeDef.qDimensions.length == 1
- && data.qHyperCubeDef.qMeasures.length > 1
- ));
- }
- },
- showDeltas: {
- type: "boolean",
- component: "switch",
- label: "Show bar connectors",
- ref: "props.showDeltas",
- defaultValue: false,
- options: [
- { value: false, label: "Standard bars"},
- { value: true, label: "Bars with connectors"}
- ],
- show: function(data) {
- return (data.qHyperCubeDef.qDimensions.length > 1
- || (data.qHyperCubeDef.qDimensions.length == 1
- && data.qHyperCubeDef.qMeasures.length > 1
- ));
- }
- },
- barSpacing: {
- type: "number",
- label: "Bar spacing",
- ref: "props.barSpacing",
- defaultValue: 0.3,
- expression: "optional"
- },
- barSpacing2: {
- type: "number",
- component: "slider",
- ref: "props.barSpacing",
- defaultValue: 0.3,
- min: 0,
- max: 1.001, // Excludes 1 otherwise
- step: 0.1
- },
- outerGap: {
- type: "number",
- label: "Outer bar spacing",
- ref: "props.outerGap",
- defaultValue: 0.1,
- expression: "optional"
- },
- outerGap2: {
- type: "number",
- component: "slider",
- ref: "props.outerGap",
- defaultValue: 0.1,
- min: 0,
- max: 1.001, // Excludes 1 otherwise
- step: 0.1
- },
- gridHeight: {
- type: "number",
- label: "Grid height relative to max bar",
- ref: "props.gridHeight",
-// defaultValue: 1.1,
- expression: "optional"
- },
- gridHeight2: {
- type: "number",
- component: "slider",
-// label: "Grid height relative to max",
- ref: "props.gridHeight",
- defaultValue: 1.1,
- min: 1,
- max: 2.001,
- step: 0.1
- },
- backgroundColor: {
- type: "string",
- label: "Background color",
- ref: "props.backgroundColor",
- defaultValue: "white",
- expression: "optional"
- },
- }
- },
- colors: {
- type: "items",
- label: "Colors and Legend",
- items: {
- colorSource: {
- type: "string",
- component: "dropdown",
- label: "Color source",
- ref: "props.colorSource",
- defaultValue: "A",
- options: [
- { value: "A", label: "Assigned"},
- { value: "C", label: "Calculated"}
- ]
- },
- colorAttr: {
- type: "string",
- component: "dropdown",
- label: "Color attribute",
- ref: "props.colorAttr",
- defaultValue: "O",
- options: [
- { value: "O", label: "Attribute is offset in scheme"},
- { value: "C", label: "Attribute is color value"}
- ],
- show: function(data) {
- return data.props.colorSource == "C";
- }
- },
- colorScheme: {
- type: "string",
- component: "dropdown",
- label: "Color scheme",
- ref: "props.colorScheme",
- defaultValue: "category20b",
- options: [
- { value: "category20b", label: "category20b"},
- { value: "category20c", label: "category20c"},
- { value: "category20", label: "catagory20"},
- { value: "category10", label: "category10"},
- { value: "google20", label: "google20"},
- { value: "custom100", label: "custom100"},
- { value: "qlikView18", label: "qlikView18"},
- { value: "qlikSense12", label: "qlikSense12"}
- ],
- show: function(data) {
- return (data.props.colorSource != "C"
- || (data.props.colorSource == "C" && data.props.colorAttr == "O"));
- }
- },
- colorOffset: {
- type: "number",
- label: "Start offset in color scheme",
- ref: "props.colorOffset",
- defaultValue: 0,
- expression: "optional",
- show: function(data) {
- return data.props.colorSource != "C";
- }
- },
- singleColor: {
- type: "boolean",
- component: "switch",
- label: "Single Color",
- ref: "props.singleColor",
- defaultValue: true,
- options: [
- { value: true, label: "Single Color"},
- { value: false, label: "Multi-color"}
- ],
- show: function(data) {
- return ((data.qHyperCubeDef.qDimensions.length == 0
- || (data.qHyperCubeDef.qDimensions.length == 1
- && data.qHyperCubeDef.qMeasures.length == 1
- )) && data.props.colorSource != "C");
- }
- },
- showLegend: {
- type: "boolean",
- component: "switch",
- label: "Show legend",
- ref: "props.showLegend",
- defaultValue: false,
- options: [
- { value: false, label: "No legend"},
- { value: true, label: "Show legend"}
- ],
- show: function(data) {
- return (
- (
- (
- (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
- )
- && (
- (data.props.colorSource != "C" && !data.props.singleColor)
- || data.props.colorSource == "C"
- )
- )
- || data.qHyperCubeDef.qDimensions.length > 1
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
- );
- }
- },
- legendPosition: {
- type: "string",
- component: "dropdown",
- label: "Legend position",
- ref: "props.legendPosition",
- defaultValue: "R",
- options: [
- { value: "R", label: "Right"},
- { value: "L", label: "Left"},
- { value: "T", label: "Top"},
- { value: "B", label: "Bottom"}
- ],
- show: function(data) {
- return data.props.showLegend && (
- (
- (
- (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
- )
- && (
- (data.props.colorSource != "C" && !data.props.singleColor)
- || data.props.colorSource == "C"
- )
- )
- || data.qHyperCubeDef.qDimensions.length > 1
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
- );
- }
- },
- legendSize: {
- type: "string",
- component: "dropdown",
- label: "Legend size",
- ref: "props.legendSize",
- defaultValue: "M",
- options: [
- { value: "N", label: "Narrow"},
- { value: "M", label: "Medium"},
- { value: "W", label: "Wide"}
- ],
- show: function(data) {
- return data.props.showLegend && (
- (
- (
- (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
- )
- && (
- (data.props.colorSource != "C" && !data.props.singleColor)
- || data.props.colorSource == "C"
- )
- )
- || data.qHyperCubeDef.qDimensions.length > 1
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
- );
- }
- },
- legendSpacing: {
- type: "string",
- component: "dropdown",
- label: "Legend item spacing",
- ref: "props.legendSpacing",
- defaultValue: "M",
- options: [
- { value: "N", label: "Narrow"},
- { value: "M", label: "Medium"},
- { value: "W", label: "Wide"}
- ],
- show: function(data) {
- return data.props.showLegend && (data.props.legendPosition == "T" || data.props.legendPosition == "B") && (
- (
- (
- (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
- )
- && (
- (data.props.colorSource != "C" && !data.props.singleColor)
- || data.props.colorSource == "C"
- )
- )
- || data.qHyperCubeDef.qDimensions.length > 1
- || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
- );
- }
- }
- }
- },
- dimensionAxis: {
- type: "items",
- label: function(data) {
- var t = "";
- if (data.qHyperCubeDef.qDimensions.length == 0) {
- if (data.props.axisTitleD.length > 0)
- t = " (" + data.props.axisTitleD + ")"
- }
- else {
- t = data.qHyperCubeDef.qDimensions[0].qDef.qFieldLabels[0];
- if (t.length == 0) {
- t = data.qHyperCubeDef.qDimensions[0].qDef.qFieldDefs[0];
- }
- t = " (" + (t.startsWith("=") ? t.slice(1) : t) + ")";
- };
- return (data.props.orientation == "V" ? "X" : "Y") + "-Axis" + t;
- },
- items: {
- LabelTitleD: {
- type: "string",
- component: "dropdown",
- label: "Labels and title",
- ref: "props.labelTitleD",
- defaultValue: "B",
- options: [
- { value: "B", label: "Labels and title"},
- { value: "L", label: "Labels only"},
- { value: "T", label: "Titles only"},
- { value: "N", label: "None"}
- ]
- },
- axisTitleD: {
- type: "string",
- label: "Dimension axis title",
- ref: "props.axisTitleD",
- defaultValue: "Dimension axis",
- expression: "optional",
- show: function(data) {
- return (data.qHyperCubeDef.qDimensions.length == 0
- && (data.props.labelTitleD == 'B' || data.props.labelTitleD == 'T'));
- }
- },
- LabelStyleD: {
- type: "string",
- component: "dropdown",
- label: "Label style",
- ref: "props.labelStyleD",
- defaultValue: "A",
- options: [
- { value: "A", label: "Auto"},
- { value: "H", label: "Horizontal"},
- { value: "S", label: "Staggered"},
- { value: "T", label: "Tilted"}
- ],
- show: function(data) {
- return data.props.labelTitleD != 'N' && data.props.labelTitleD != 'T';
- }
- },
- gridlinesD: {
- type: "boolean",
- component: "switch",
- label: "Gridlines",
- ref: "props.gridlinesD",
- defaultValue: false,
- options: [
- { value: true, label: "Gridlines"},
- { value: false, label: "No gridlines"}
- ],
- show: function(data) {
- return data.props.labelTitleD != 'N' && data.props.labelTitleD != 'T';
- }
- },
- axisMarginD: {
- type: "string",
- component: "dropdown",
- label: "Dimension margin size",
- ref: "props.axisMarginD",
- defaultValue: "M",
- options: [
- { value: "N", label: "Narrow"},
- { value: "M", label: "Medium"},
- { value: "W", label: "Wide"}
- ],
- show: function(data) {
- return data.props.labelTitleD != 'N' && data.props.labelTitleD != 'T';
- }
- }
- }
- },
- measureAxis: {
- type: "items",
- label: function(data) {
- var t = "";
- if (data.qHyperCubeDef.qDimensions.length == 0 || data.qHyperCubeDef.qMeasures.length > 1) {
- if (data.props.axisTitleM.length > 0)
- t = " (" + data.props.axisTitleM + ")"
- }
- else if (data.qHyperCubeDef.qMeasures.length > 0) {
- if (data.qHyperCubeDef.qMeasures[0].qDef.hasOwnProperty("qLabel")) {
- t = data.qHyperCubeDef.qMeasures[0].qDef.qLabel;
- }
- else {
- t = data.qHyperCubeDef.qMeasures[0].qDef.qDef;
- }
- t = " (" + (t.startsWith("=") ? t.slice(1) : t) + ")";
- };
- return (data.props.orientation == "V" ? "Y" : "X") + "-Axis" + t;
- },
- items: {
- labelTitleM: {
- type: "string",
- component: "dropdown",
- label: "Labels and title",
- ref: "props.labelTitleM",
- defaultValue: "B",
- options: [
- { value: "B", label: "Labels and title"},
- { value: "L", label: "Labels only"},
- { value: "T", label: "Titles only"},
- { value: "N", label: "None"}
- ]
- },
- axisTitleM: {
- type: "string",
- label: "Measure axis title",
- ref: "props.axisTitleM",
- defaultValue: "Measure axis",
- expression: "optional",
- show: function(data) {
- return ((
- data.qHyperCubeDef.qDimensions.length == 0
- || (
- data.qHyperCubeDef.qDimensions.length == 1
- && data.qHyperCubeDef.qMeasures.length > 1
- )
- )
- && (data.props.labelTitleM == 'B' || data.props.labelTitleM == 'T'));
- }
- },
- LabelStyleM: {
- type: "string",
- component: "dropdown",
- label: "Label style",
- ref: "props.labelStyleM",
- defaultValue: "A",
- options: [
- { value: "A", label: "Auto"},
- { value: "H", label: "Horizontal"},
- { value: "S", label: "Staggered"},
- { value: "T", label: "Tilted"}
- ],
- show: function(data) {
- return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
- }
- },
- gridlinesM: {
- type: "boolean",
- component: "switch",
- label: "Gridlines",
- ref: "props.gridlinesM",
- defaultValue: true,
- options: [
- { value: true, label: "Gridlines"},
- { value: false, label: "No gridlines"}
- ],
- show: function(data) {
- return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
- }
- },
- axisMarginM: {
- type: "string",
- component: "dropdown",
- label: "Measure margin size",
- ref: "props.axisMarginM",
- defaultValue: "M",
- options: [
- { value: "N", label: "Narrow"},
- { value: "M", label: "Medium"},
- { value: "W", label: "Wide"}
- ],
- show: function(data) {
- return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
- }
- },
- ticks: {
- type: "string",
- component: "dropdown",
- label: "Measure Tick spacing",
- ref: "props.ticks",
- defaultValue: "5",
- options: [
- { value: "5", label: "Wide"},
- { value: "10", label: "Medium"},
- { value: "20", label: "Narrow"}
- ],
- show: function(data) {
- return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
- }
- },
- axisFormatM: {
- type: "string",
- component: "dropdown",
- label: "Measure axis number format",
- ref: "props.axisFormatM",
- defaultValue: "S",
- options: [
- { value: "A", label: "Auto"},
- { value: "N", label: "Number"},
- { value: "P", label: "Percent"},
- { value: "S", label: "SI-notation (K, M, etc.)"},
- { value: "C", label: "Custom"}
- ],
- show: function(data) {
- return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
- }
- },
- axisFormatMs: {
- type: "string",
- label: "Measure axis number format string",
- ref: "props.axisFormatMs",
- defaultValue: "s",
- expression: "optional",
- show: function(data) {
- return data.props.axisFormatM == "C";
- }
- },
- axisFormatMURL: {
- type: "string",
- component: "link",
- label: "D3 format strings",
- url: "https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md"
- }
- }
- },
- barText: {
- type: "items",
- label: "Text on bars",
- items: {
- showTexts: {
- type: "string",
- component: "dropdown",
- label: "Text on bars",
- ref: "props.showTexts",
- defaultValue: "N",
- options: [
- { value: "N", label: "None"},
- { value: "B", label: "Inside Bars"},
- { value: "T", label: "Total"},
- { value: "A", label: "Both"}
- ]
- },
- showDim: {
- type: "string",
- component: "dropdown",
- label: "Text to show in bars",
- ref: "props.showDim",
- defaultValue: "M",
- options: [
- { value: "M", label: "Measure"},
- { value: "D", label: "Dimension"},
- { value: "P", label: "Percent"}
- ],
- show: function(data) {
- return ~"BA".indexOf(data.props.showTexts) && data.props.normalized;
- }
- },
- showDim2: {
- type: "string",
- component: "dropdown",
- label: "Text to show in bars",
- ref: "props.showDim",
- defaultValue: "M",
- options: [
- { value: "M", label: "Measure"},
- { value: "D", label: "Dimension"}
- ],
- show: function(data) {
- return ~"BA".indexOf(data.props.showTexts) && !data.props.normalized;
- }
- },
- showTot: {
- type: "string",
- component: "dropdown",
- label: "Text to show for total",
- ref: "props.showTot",
- defaultValue: "M",
- options: [
- { value: "M", label: "Measure"},
- { value: "D", label: "Dimension"}
- ],
- show: function(data) {
- return ~"TA".indexOf(data.props.showTexts);
- }
- },
- innerBarPadH: {
- type: "number",
- label: "Horizontal inner bar padding (px)",
- ref: "props.innerBarPadH",
- defaultValue: 2,
- expression: "optional",
- show: function(data) {
- return data.props.showTexts != "N";
- }
- },
- innerBarPadV: {
- type: "number",
- label: "Vertical inner bar padding (px)",
- ref: "props.innerBarPadV",
- defaultValue: 2,
- expression: "optional",
- show: function(data) {
- return data.props.showTexts != "N";
- }
- },
- textSizeAbs: {
- type: "boolean",
- component: "switch",
- label: "Text size proportional",
- ref: "props.textSizeAbs",
- defaultValue: true,
- options: [
- { value: true, label: "Not proportional"},
- { value: false, label: "Proportional"}
- ],
- show: function(data) {
- return data.props.showTexts != "N"
- }
- },
- textSizeFactor: {
- type: "number",
- label: "Text size proportion factor",
- ref: "props.textSizeFactor",
- defaultValue: 1,
- show: function(data) {
- return data.props.showTexts != "N" && !data.props.textSizeAbs;
- }
- },
- textSize: {
- type: "number",
- label: "Text size (px)",
- ref: "props.textSize",
- defaultValue: 10,
- expression: "optional",
- show: function(data) {
- return data.props.showTexts != "N" && data.props.textSizeAbs;
- }
- },
- textSizeMax: {
- type: "number",
- label: "Max text size (px)",
- ref: "props.textSize",
- defaultValue: 18,
- expression: "optional",
- show: function(data) {
- return data.props.showTexts != "N" && !data.props.textSizeAbs;
- }
- },
- textDots: {
- type: "boolean",
- component: "switch",
- label: "Show text at all if ellipsis",
- ref: "props.textDots",
- defaultValue: true,
- options: [
- { value: true, label: "Show"},
- { value: false, label: "Don't show"}
- ],
- show: function(data) {
- return data.props.showTexts != "N";
- }
- },
- textColor: {
- type: "string",
- component: "dropdown",
- label: "Text color",
- ref: "props.textColor",
- defaultValue: "Auto",
- options: [
- { value: "Auto", label: "Auto"},
- { value: "Black", label: "Black"},
- { value: "White", label: "White"}
- ],
- show: function(data) {
- return data.props.showTexts != "N";
- }
- },
- vAlign: {
- type: "string",
- component: "dropdown",
- label: "Vertical alignment",
- ref: "props.vAlign",
- defaultValue: "C",
- options: [
- { value: "C", label: "Center"},
- { value: "T", label: "Top"},
- { value: "B", label: "Bottom"}
- ],
- show: function(data) {
- return data.props.showTexts != "N";
- }
- },
- hAlign: {
- type: "string",
- component: "dropdown",
- label: "Horizontal alignment",
- ref: "props.hAlign",
- defaultValue: "C",
- options: [
- { value: "C", label: "Center"},
- { value: "L", label: "Left"},
- { value: "R", label: "Right"}
- ],
- show: function(data) {
- return data.props.showTexts != "N";
- }
- },
- totalFormatM: {
- type: "string",
- component: "dropdown",
- label: "Total format",
- ref: "props.totalFormatM",
- defaultValue: "S",
- options: [
- { value: "N", label: "Number"},
- { value: "P", label: "Percent"},
- { value: "S", label: "SI-notation (K, M, etc.)"},
- { value: "C", label: "Custom"}
- ],
- show: function(data) {
- return data.props.showTexts != "N";
- }
- },
- totalFormatMs: {
- type: "string",
- label: "Total format string",
- ref: "props.totalFormatMs",
- defaultValue: ",.3s",
- expression: "optional",
- show: function(data) {
- return data.props.showTexts != "N" && data.props.totalFormatM == "C";
- }
- },
- totalFormatMURL: {
- type: "string",
- component: "link",
- label: "D3 format strings",
- url: "https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md",
- show: function(data) {
- return data.props.showTexts != "N" && data.props.totalFormatM == "C";
- }
- }
- }
- },
- transitions: {
- type: "items",
- label: "Transitions",
- items: {
- transitions: {
- type: "boolean",
- component: "switch",
- label: "Transitions",
- ref: "props.transitions",
- defaultValue: true,
- options: [
- { value: true, label: "Transitions enabled"},
- { value: false, label: "Transitions disabled"}
- ]
- },
- transitionDelay: {
- type: "string",
- label: "Transition delay",
- ref: "props.transitionDelay",
- defaultValue: "500",
- expression: "optional",
- show: function(data) {
- return data.props.transitions;
- }
- },
- transitionDuration: {
- type: "string",
- label: "Transition duration",
- ref: "props.transitionDuration",
- defaultValue: "1000",
- expression: "optional",
- show: function(data) {
- return data.props.transitions;
- }
- },
- transitionStyle: {
- type: "string",
- component: "dropdown",
- label: "Transition style",
- ref: "props.ease",
- defaultValue: "linear",
- options: [
- { value: "linear", label: "Linear"},
- { value: "quad", label: "Quad"},
- { value: "cubic", label: "Cubic"},
- { value: "sin", label: "Sine"},
- { value: "exp", label: "Exponential"},
- { value: "circle", label: "Circle"},
- { value: "elastic", label: "Elastic"},
- { value: "back", label: "Back"},
- { value: "bounce", label: "Bounce"}
- ],
- show: function(data) {
- return data.props.transitions;
- }
- }
- }
- }
- }
- }
- return {
- type: "items",
- component: "accordion",
- items: {
- dimensions: dimensions,
- measures: measures,
- sorting: sorting,
- addons: addons,
- appearance: appearance
- }
- };
-
-} );
diff --git a/barsPlus-template.html b/barsPlus-template.html
deleted file mode 100644
index e2a47dd..0000000
--- a/barsPlus-template.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0322f58
--- /dev/null
+++ b/package.json
@@ -0,0 +1,3 @@
+{
+ "name": "barsPlus"
+}
\ No newline at end of file
diff --git a/barsPlus-directive.js b/src/barsPlus-directive.js
similarity index 95%
rename from barsPlus-directive.js
rename to src/barsPlus-directive.js
index e4010ea..23d7bf0 100644
--- a/barsPlus-directive.js
+++ b/src/barsPlus-directive.js
@@ -1,199 +1,204 @@
-/**
- * barsPlus AngularJS Directive
- *
- * Define AngularJS directive
- * Sets up properties of object ldwBarsPlus (variable 'g') and defines watches
- *
- * Author: L. Woodside
- * Modification History:
- *
- * Version Person Date Description
- * V1.0.0 L. Woodside 19-Dec-2016 Initial Release
- * V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
- * V1.2.0 L. Woodside 07-Jan-2017 Allow multiple measures
- * V1.3.0 L. Woodside 15-Jan-2017 Improved color options
- *
-*/
-define( [
- "jquery",
- "qvangular",
- "./ldw-barsPlus",
- "css!./barsPlus.css",
- "./lib/d3.v3"
-],
-
-function ($, qvangular, ldwBarsPlus) {
- 'use strict';
-
- qvangular.directive("barsPlus", [
- function() {
- return {
- restrict: "EA",
- link: function($scope, $element, $attrs) {
-
- $scope.g = Object.create(ldwbarsPlus);
-
- var g = $scope.g;
-
- // package parameters so bar chart js can be tested in standalone HTML
- // with different input data structure
- $scope.initProps = function() {
- var g = $scope.g, l = $scope.layout, p = $scope.layout.props;
-
- g.id = l.qInfo.qId;
- g.inSelections = false;
- g.editMode = false;
- g.selectionMode = l.selectionMode;
-
- // number of defined dimensions and measures
- g.defDims = l.qHyperCube.qDimensionInfo.length;
- g.defMeas = l.qHyperCube.qMeasureInfo.length;
-
- if (g.defDims == 0)
- g.axisTitleD = p.axisTitleD;
- else
- g.axisTitleD = l.qHyperCube.qDimensionInfo[0].qFallbackTitle;
-
- if (g.defDims == 0 || (g.defDims == 1 && g.defMeas > 1)) {
- g.measures = [];
- for (var i = 0; i < g.defMeas; i++) {
- var t = l.qHyperCube.qMeasureInfo[i].qFallbackTitle;
- g.measures.push(t.startsWith("=") ? t.slice(1) : t);
- }
- g.axisTitleM = p.axisTitleM;
- }
- else
- g.axisTitleM = l.qHyperCube.qMeasureInfo[0].qFallbackTitle;
-
- // Presentation
- g.orientation = p.orientation;
- g.normalized = p.normalized;
- g.showDeltas = p.showDeltas
- g.barGap = p.barSpacing;
- g.outerGap = p.outerGap
- g.gridHeight = p.gridHeight;
- g.backgroundColor = p.backgroundColor;
-
- // Colors and Legend
- g.colorSource = p.colorSource;
- g.colorAttr = p.colorAttr;
- g.colorScheme = p.colorScheme;
- g.colorOffset = p.colorOffset;
- g.singleColor = p.singleColor;
- g.showLegend = p.showLegend;
- g.legendPosition = p.legendPosition;
- g.legendSize = p.legendSize;
- g.legendSpacing = p.legendSpacing;
-
- // Dimension Axis
- g.labelTitleD = p.labelTitleD;
- g.labelStyleD = p.labelStyleD;
- g.gridlinesD = p.gridlinesD;
- g.axisMarginD = p.axisMarginD;
-
- // Measure Axis
- g.labelTitleM = p.labelTitleM;
- g.labelStyleM = p.labelStyleM;
- g.gridlinesM = p.gridlinesM;
- g.axisMarginM = p.axisMarginM;
- g.ticks = p.ticks;
- g.axisFormatM = p.axisFormatM;
- g.axisFormatMs = p.axisFormatMs;
-
- // Text on bars
-
- g.showTexts = p.showTexts;
- g.showDim = p.showDim;
- g.showTot = p.showTot;
- g.innerBarPadH = p.innerBarPadH;
- g.innerBarPadV = p.innerBarPadV;
- g.textSizeAbs = p.textSizeAbs;
- g.textSizeFactor = p.textSizeFactor;
- g.textSize = p.textSize;
- g.textDots = p.textDots;
- g.textColor = p.textColor;
- g.vAlign = p.vAlign;
- g.hAlign = p.hAlign;
- g.totalFormatM = p.totalFormatM;
- g.totalFormatMs = p.totalFormatMs;
-
- // Transitions
- g.transitions = p.transitions;
- g.transitionDelay = p.transitionDelay;
- g.transitionDuration = p.transitionDuration;
- g.ease = p.ease
-
- };
-
- $scope.initProps();
-
- g.component = d3.select($element[0]).attr("id",g.id)
-
- g.width = $element.parent().width();
- g.height = $element.parent().height();
- g.rawData = $scope.layout.qHyperCube.qDataPages[0].qMatrix;
-
- g.initData();
- g.refreshChart();
-
- // watch for when data changes
- $scope.$watch(function() { return $scope.layout.qHyperCube.qDataPages[0].qMatrix; }
- ,function(newValue, oldValue) {
- if (newValue != oldValue) {
- g.rawData = newValue;
- g.initData();
- g.updateBars();
- }
- });
- // watch for when chart is resized
- $scope.$watch(
- function () {
- return $element.parent().width() + "," + $element.parent().height();
- }
- ,function(newValue, oldValue) {
- if (newValue != oldValue) {
-
- var t = newValue.split(",");
- g.width = t[0];
- g.height = t[1];
-
- g.refreshChart();
- }
- }
- );
- // watch for selection count going to zero in standard selection mode
- // to partially address QS bug where clear selections button does not reset element classes
- // this does not completely work if there were existing selections on field prior to entering
- // selection mode
-// $scope.$watch(
-// function() {
-// return $scope.layout.qHyperCube.qDimensionInfo[0].qStateCounts.qSelected;
-// }
-// ,function(newValue, oldValue) {
-//
-// if (newValue != oldValue && newValue == 0) {
-// d3.selectAll(".selected").classed("selected",false);
-// }
-// }
-// );
- // watch for when selection mode enabled
- $scope.$watch(
- function() {
- return $scope.layout.qSelectionInfo.qInSelections;
- }
- ,function(newValue, oldValue) {
- if (newValue != oldValue) {
-
- g.inSelections = newValue || false;
- if (!g.inSelections) {
- d3.selectAll(".selected").classed("selected",false);
- }
- }
- }
- );
- }
- }
- }
- ]);
-
-});
+/**
+ * barsPlus AngularJS Directive
+ *
+ * Define AngularJS directive
+ * Sets up properties of object ldwBarsPlus (variable 'g') and defines watches
+ *
+ * Author: L. Woodside
+ * Modification History:
+ *
+ * Version Person Date Description
+ * V1.0.0 L. Woodside 19-Dec-2016 Initial Release
+ * V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
+ * V1.2.0 L. Woodside 07-Jan-2017 Allow multiple measures
+ * V1.3.0 L. Woodside 15-Jan-2017 Improved color options
+ *
+*/
+define( [
+ "jquery",
+ "qvangular",
+ "./ldw-barsPlus",
+ "css!./barsPlus.css",
+ "./lib/d3.v3"
+],
+
+function ($, qvangular, ldwBarsPlus) {
+ 'use strict';
+
+ qvangular.directive("barsPlus", [
+ function() {
+ return {
+ restrict: "EA",
+ link: function($scope, $element, $attrs) {
+
+ $scope.g = Object.create(ldwbarsPlus);
+
+ var g = $scope.g;
+
+ // package parameters so bar chart js can be tested in standalone HTML
+ // with different input data structure
+ $scope.initProps = function() {
+ var g = $scope.g, l = $scope.layout, p = $scope.layout.props;
+
+ g.id = l.qInfo.qId;
+ g.inSelections = false;
+ g.editMode = false;
+ g.selectionMode = l.selectionMode;
+
+ // number of defined dimensions and measures
+ g.defDims = l.qHyperCube.qDimensionInfo.length;
+ g.defMeas = l.qHyperCube.qMeasureInfo.length;
+
+ if (g.defDims == 0)
+ g.axisTitleD = p.axisTitleD;
+ else
+ g.axisTitleD = l.qHyperCube.qDimensionInfo[0].qFallbackTitle;
+
+ if (g.defDims == 0 || (g.defDims == 1 && g.defMeas > 1)) {
+ g.measures = [];
+ for (var i = 0; i < g.defMeas; i++) {
+ var t = l.qHyperCube.qMeasureInfo[i].qFallbackTitle;
+ g.measures.push(t.startsWith("=") ? t.slice(1) : t);
+ }
+ g.axisTitleM = p.axisTitleM;
+ }
+ else
+ g.axisTitleM = l.qHyperCube.qMeasureInfo[0].qFallbackTitle;
+
+ // Presentation
+ g.orientation = p.orientation;
+ g.normalized = p.normalized;
+ g.showDeltas = p.showDeltas
+ g.barGap = p.barSpacing;
+ g.outerGap = p.outerGap
+ g.gridHeight = p.gridHeight;
+ g.backgroundColor = p.backgroundColor;
+
+ // Colors and Legend
+ g.colorSource = p.colorSource;
+ g.colorAttr = p.colorAttr;
+ g.colorScheme = p.colorScheme;
+ g.colorOffset = p.colorOffset;
+ g.singleColor = p.singleColor;
+ g.showLegend = p.showLegend;
+ g.legendPosition = p.legendPosition;
+ g.legendSize = p.legendSize;
+ g.legendSpacing = p.legendSpacing;
+
+ // Dimension Axis
+ g.labelTitleD = p.labelTitleD;
+ g.labelStyleD = p.labelStyleD;
+ g.gridlinesD = p.gridlinesD;
+ g.axisMarginD = p.axisMarginD;
+
+ // Measure Axis
+ g.labelTitleM = p.labelTitleM;
+ g.labelStyleM = p.labelStyleM;
+ g.gridlinesM = p.gridlinesM;
+ g.axisMarginM = p.axisMarginM;
+ g.ticks = p.ticks;
+ g.axisFormatM = p.axisFormatM;
+ g.axisFormatMs = p.axisFormatMs;
+
+ // Text on bars
+
+ g.showTexts = p.showTexts;
+ g.showDim = p.showDim;
+ g.showTot = p.showTot;
+ g.innerBarPadH = p.innerBarPadH;
+ g.innerBarPadV = p.innerBarPadV;
+ g.textSizeAbs = p.textSizeAbs;
+ g.textSizeFactor = p.textSizeFactor;
+ g.textSize = p.textSize;
+ g.textDots = p.textDots;
+ g.textColor = p.textColor;
+ g.vAlign = p.vAlign;
+ g.hAlign = p.hAlign;
+ g.totalFormatM = p.totalFormatM;
+ g.totalFormatMs = p.totalFormatMs;
+
+ // Transitions
+ var inPrinting = typeof window.testprint === "object";
+ if (!inPrinting) {
+ g.transitions = p.transitions;
+ } else {
+ g.transitions = false;
+ }
+ g.transitionDelay = p.transitionDelay;
+ g.transitionDuration = p.transitionDuration;
+ g.ease = p.ease
+
+ };
+
+ $scope.initProps();
+
+ g.component = d3.select($element[0]).attr("id",g.id)
+
+ g.width = $element.parent().width();
+ g.height = $element.parent().height();
+ g.rawData = $scope.layout.qHyperCube.qDataPages[0].qMatrix;
+
+ g.initData();
+ g.refreshChart();
+
+ // watch for when data changes
+ $scope.$watch(function() { return $scope.layout.qHyperCube.qDataPages[0].qMatrix; }
+ ,function(newValue, oldValue) {
+ if (newValue != oldValue) {
+ g.rawData = newValue;
+ g.initData();
+ g.updateBars();
+ }
+ });
+ // watch for when chart is resized
+ $scope.$watch(
+ function () {
+ return $element.parent().width() + "," + $element.parent().height();
+ }
+ ,function(newValue, oldValue) {
+ if (newValue != oldValue) {
+
+ var t = newValue.split(",");
+ g.width = t[0];
+ g.height = t[1];
+
+ g.refreshChart();
+ }
+ }
+ );
+ // watch for selection count going to zero in standard selection mode
+ // to partially address QS bug where clear selections button does not reset element classes
+ // this does not completely work if there were existing selections on field prior to entering
+ // selection mode
+// $scope.$watch(
+// function() {
+// return $scope.layout.qHyperCube.qDimensionInfo[0].qStateCounts.qSelected;
+// }
+// ,function(newValue, oldValue) {
+//
+// if (newValue != oldValue && newValue == 0) {
+// d3.selectAll(".selected").classed("selected",false);
+// }
+// }
+// );
+ // watch for when selection mode enabled
+ $scope.$watch(
+ function() {
+ return $scope.layout.qSelectionInfo.qInSelections;
+ }
+ ,function(newValue, oldValue) {
+ if (newValue != oldValue) {
+
+ g.inSelections = newValue || false;
+ if (!g.inSelections) {
+ d3.selectAll(".selected").classed("selected",false);
+ }
+ }
+ }
+ );
+ }
+ }
+ }
+ ]);
+
+});
diff --git a/barsPlus-initprops.js b/src/barsPlus-initprops.js
similarity index 100%
rename from barsPlus-initprops.js
rename to src/barsPlus-initprops.js
diff --git a/src/barsPlus-props.js b/src/barsPlus-props.js
new file mode 100644
index 0000000..56bab2e
--- /dev/null
+++ b/src/barsPlus-props.js
@@ -0,0 +1,937 @@
+define([],
+ /**
+ * barsPlus Properties - set up appearance of accordion properties panel in Qlik Sense
+ *
+ * Defaults are defined here.
+ * Settings modify objects in object: layout.props
+ *
+ * Author: L. Woodside
+ * Modification History:
+ *
+ * Version Person Date Description
+ * V1.0.0 L. Woodside 19-Dec-2016 Initial Release
+ * V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
+ * V1.2.0 L. Woodside 07-Jan-2017 Allow multiple measures
+ * V1.3.0 L. Woodside 15-Jan-2017 Improved color options
+ * V1.3.1 L. Woodside 27-Jan-2017 Fix problem with legend properties
+ *
+ */
+ function () {
+ 'use strict';
+
+ var param = {
+ "qInfo": {
+ "qType": "DimensionList"
+ },
+ "qDimensionListDef": {
+ "qType": "dimension",
+ "qData": {
+ "title": "/title"
+ }
+ }
+ }
+ let dimensionList = []
+ // window.engineModuleGlobal.currentApp.createSessionObject(param)
+ // .then(function(object) {
+ // return object.getLayout()
+ // })
+ // .then(function(layout) {
+ // for (const dimension of layout.qDimensionList.qItems) {
+ // dimensionList.push({id: dimension.qInfo.qId, title: dimension.qMeta.title})
+ // }
+ // })
+ // .catch(function(error) {
+ // console.log(error)
+ // })
+
+ function getDimensionName(id) {
+ return dimensionList.filter(function (value) {
+ if (value.id === id) {
+ return value
+ }
+ })[0].title;
+ }
+
+ var dimensions = {
+ uses: "dimensions",
+ min: 0,
+ max: 2,
+ items: {
+ attribute: {
+ type: "string",
+ component: "expression",
+ ref: "qAttributeExpressions.0.qExpression",
+ label: "Dimension attribute",
+ defaultValue: ""
+ }
+ }
+ };
+ var measures = {
+ uses: "measures",
+ min: 1,
+ max: 10,
+ items: {
+ attribute: {
+ type: "string",
+ component: "expression",
+ ref: "qAttributeExpressions.0.qExpression",
+ label: "Measure attribute",
+ defaultValue: ""
+ }
+ }
+ };
+ var sorting = {
+ uses: "sorting"
+ };
+ var addons = {
+ uses: "addons"
+ , items: {
+ dataHandling: {
+ uses: "dataHandling"
+ },
+ selectionMode: {
+ type: "string",
+ component: "dropdown",
+ label: "Selection mode",
+ ref: "selectionMode",
+ defaultValue: "CONFIRM",
+ options: [
+ { value: "CONFIRM", label: "Standard" },
+ { value: "QUICK", label: "Quick" }
+ ]
+ }
+ }
+ };
+
+ var appearance = {
+ uses: "settings",
+ items: {
+ presentation: {
+ type: "items",
+ label: "Presentation",
+ items: {
+ orientation: {
+ type: "string",
+ component: "dropdown",
+ label: "Orientation",
+ ref: "props.orientation",
+ defaultValue: "V",
+ options: [
+ { value: "V", label: "Vertical bars" },
+ { value: "H", label: "Horizontal bars" },
+ ]
+ },
+ normalized: {
+ type: "boolean",
+ component: "switch",
+ label: "100% bar chart",
+ ref: "props.normalized",
+ defaultValue: false,
+ options: [
+ { value: false, label: "Not 100%" },
+ { value: true, label: "100% bars" }
+ ],
+ show: function (data) {
+ return (data.qHyperCubeDef.qDimensions.length > 1
+ || (data.qHyperCubeDef.qDimensions.length == 1
+ && data.qHyperCubeDef.qMeasures.length > 1
+ ));
+ }
+ },
+ showDeltas: {
+ type: "boolean",
+ component: "switch",
+ label: "Show bar connectors",
+ ref: "props.showDeltas",
+ defaultValue: false,
+ options: [
+ { value: false, label: "Standard bars" },
+ { value: true, label: "Bars with connectors" }
+ ],
+ show: function (data) {
+ return (data.qHyperCubeDef.qDimensions.length > 1
+ || (data.qHyperCubeDef.qDimensions.length == 1
+ && data.qHyperCubeDef.qMeasures.length > 1
+ ));
+ }
+ },
+ barSpacing: {
+ type: "number",
+ label: "Bar spacing",
+ ref: "props.barSpacing",
+ defaultValue: 0.3,
+ expression: "optional"
+ },
+ barSpacing2: {
+ type: "number",
+ component: "slider",
+ ref: "props.barSpacing",
+ defaultValue: 0.3,
+ min: 0,
+ max: 1.001, // Excludes 1 otherwise
+ step: 0.1
+ },
+ outerGap: {
+ type: "number",
+ label: "Outer bar spacing",
+ ref: "props.outerGap",
+ defaultValue: 0.1,
+ expression: "optional"
+ },
+ outerGap2: {
+ type: "number",
+ component: "slider",
+ ref: "props.outerGap",
+ defaultValue: 0.1,
+ min: 0,
+ max: 1.001, // Excludes 1 otherwise
+ step: 0.1
+ },
+ gridHeight: {
+ type: "number",
+ label: "Grid height relative to max bar",
+ ref: "props.gridHeight",
+ // defaultValue: 1.1,
+ expression: "optional"
+ },
+ gridHeight2: {
+ type: "number",
+ component: "slider",
+ // label: "Grid height relative to max",
+ ref: "props.gridHeight",
+ defaultValue: 1.1,
+ min: 1,
+ max: 2.001,
+ step: 0.1
+ },
+ backgroundColor: {
+ type: "string",
+ label: "Background color",
+ ref: "props.backgroundColor",
+ defaultValue: "white",
+ expression: "optional"
+ },
+ }
+ },
+ colors: {
+ type: "items",
+ label: "Colors and Legend",
+ items: {
+ colorSource: {
+ type: "string",
+ component: "dropdown",
+ label: "Color source",
+ ref: "props.colorSource",
+ defaultValue: "A",
+ options: [
+ { value: "A", label: "Assigned" },
+ { value: "C", label: "Calculated" }
+ ]
+ },
+ colorAttr: {
+ type: "string",
+ component: "dropdown",
+ label: "Color attribute",
+ ref: "props.colorAttr",
+ defaultValue: "O",
+ options: [
+ { value: "O", label: "Attribute is offset in scheme" },
+ { value: "C", label: "Attribute is color value" }
+ ],
+ show: function (data) {
+ return data.props.colorSource == "C";
+ }
+ },
+ colorScheme: {
+ type: "string",
+ component: "dropdown",
+ label: "Color scheme",
+ ref: "props.colorScheme",
+ defaultValue: "category20b",
+ options: [
+ { value: "category20b", label: "category20b" },
+ { value: "category20c", label: "category20c" },
+ { value: "category20", label: "catagory20" },
+ { value: "category10", label: "category10" },
+ { value: "google20", label: "google20" },
+ { value: "custom100", label: "custom100" },
+ { value: "qlikView18", label: "qlikView18" },
+ { value: "qlikSense12", label: "qlikSense12" }
+ ],
+ show: function (data) {
+ return (data.props.colorSource != "C"
+ || (data.props.colorSource == "C" && data.props.colorAttr == "O"));
+ }
+ },
+ colorOffset: {
+ type: "number",
+ label: "Start offset in color scheme",
+ ref: "props.colorOffset",
+ defaultValue: 0,
+ expression: "optional",
+ show: function (data) {
+ return data.props.colorSource != "C";
+ }
+ },
+ singleColor: {
+ type: "boolean",
+ component: "switch",
+ label: "Single Color",
+ ref: "props.singleColor",
+ defaultValue: true,
+ options: [
+ { value: true, label: "Single Color" },
+ { value: false, label: "Multi-color" }
+ ],
+ show: function (data) {
+ return ((data.qHyperCubeDef.qDimensions.length == 0
+ || (data.qHyperCubeDef.qDimensions.length == 1
+ && data.qHyperCubeDef.qMeasures.length == 1
+ )) && data.props.colorSource != "C");
+ }
+ },
+ showLegend: {
+ type: "boolean",
+ component: "switch",
+ label: "Show legend",
+ ref: "props.showLegend",
+ defaultValue: false,
+ options: [
+ { value: false, label: "No legend" },
+ { value: true, label: "Show legend" }
+ ],
+ show: function (data) {
+ return (
+ (
+ (
+ (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
+ )
+ && (
+ (data.props.colorSource != "C" && !data.props.singleColor)
+ || data.props.colorSource == "C"
+ )
+ )
+ || data.qHyperCubeDef.qDimensions.length > 1
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
+ );
+ }
+ },
+ legendPosition: {
+ type: "string",
+ component: "dropdown",
+ label: "Legend position",
+ ref: "props.legendPosition",
+ defaultValue: "R",
+ options: [
+ { value: "R", label: "Right" },
+ { value: "L", label: "Left" },
+ { value: "T", label: "Top" },
+ { value: "B", label: "Bottom" }
+ ],
+ show: function (data) {
+ return data.props.showLegend && (
+ (
+ (
+ (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
+ )
+ && (
+ (data.props.colorSource != "C" && !data.props.singleColor)
+ || data.props.colorSource == "C"
+ )
+ )
+ || data.qHyperCubeDef.qDimensions.length > 1
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
+ );
+ }
+ },
+ legendSize: {
+ type: "string",
+ component: "dropdown",
+ label: "Legend size",
+ ref: "props.legendSize",
+ defaultValue: "M",
+ options: [
+ { value: "N", label: "Narrow" },
+ { value: "M", label: "Medium" },
+ { value: "W", label: "Wide" }
+ ],
+ show: function (data) {
+ return data.props.showLegend && (
+ (
+ (
+ (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
+ )
+ && (
+ (data.props.colorSource != "C" && !data.props.singleColor)
+ || data.props.colorSource == "C"
+ )
+ )
+ || data.qHyperCubeDef.qDimensions.length > 1
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
+ );
+ }
+ },
+ legendSpacing: {
+ type: "string",
+ component: "dropdown",
+ label: "Legend item spacing",
+ ref: "props.legendSpacing",
+ defaultValue: "M",
+ options: [
+ { value: "N", label: "Narrow" },
+ { value: "M", label: "Medium" },
+ { value: "W", label: "Wide" }
+ ],
+ show: function (data) {
+ return data.props.showLegend && (data.props.legendPosition == "T" || data.props.legendPosition == "B") && (
+ (
+ (
+ (data.qHyperCubeDef.qDimensions.length == 0 && data.qHyperCubeDef.qMeasures.length > 1)
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length == 1)
+ )
+ && (
+ (data.props.colorSource != "C" && !data.props.singleColor)
+ || data.props.colorSource == "C"
+ )
+ )
+ || data.qHyperCubeDef.qDimensions.length > 1
+ || (data.qHyperCubeDef.qDimensions.length == 1 && data.qHyperCubeDef.qMeasures.length > 1)
+ );
+ }
+ }
+ }
+ },
+ dimensionAxis: {
+ type: "items",
+ label: function (data) {
+ var t = "";
+ if (data.qHyperCubeDef.qDimensions.length == 0) {
+ if (data.props.axisTitleD.length > 0)
+ t = " (" + data.props.axisTitleD + ")"
+ }
+ else {
+ if (typeof data.qHyperCubeDef.qDimensions[0].qLibraryId !== "undefined") {
+ var libraryId = data.qHyperCubeDef.qDimensions[0].qLibraryId;
+ t = getDimensionName(libraryId);
+ } else {
+ t = data.qHyperCubeDef.qDimensions[0].qDef.qFieldLabels[0];
+ if (t.length == 0) {
+ t = data.qHyperCubeDef.qDimensions[0].qDef.qFieldDefs[0];
+ }
+ }
+ t = " (" + (t.startsWith("=") ? t.slice(1) : t) + ")";
+ };
+ return (data.props.orientation == "V" ? "X" : "Y") + "-Axis" + t;
+ },
+ items: {
+ LabelTitleD: {
+ type: "string",
+ component: "dropdown",
+ label: "Labels and title",
+ ref: "props.labelTitleD",
+ defaultValue: "B",
+ options: [
+ { value: "B", label: "Labels and title" },
+ { value: "L", label: "Labels only" },
+ { value: "T", label: "Titles only" },
+ { value: "N", label: "None" }
+ ]
+ },
+ axisTitleD: {
+ type: "string",
+ label: "Dimension axis title",
+ ref: "props.axisTitleD",
+ defaultValue: "Dimension axis",
+ expression: "optional",
+ show: function (data) {
+ return (data.qHyperCubeDef.qDimensions.length == 0
+ && (data.props.labelTitleD == 'B' || data.props.labelTitleD == 'T'));
+ }
+ },
+ LabelStyleD: {
+ type: "string",
+ component: "dropdown",
+ label: "Label style",
+ ref: "props.labelStyleD",
+ defaultValue: "A",
+ options: [
+ { value: "A", label: "Auto" },
+ { value: "H", label: "Horizontal" },
+ { value: "S", label: "Staggered" },
+ { value: "T", label: "Tilted" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleD != 'N' && data.props.labelTitleD != 'T';
+ }
+ },
+ gridlinesD: {
+ type: "boolean",
+ component: "switch",
+ label: "Gridlines",
+ ref: "props.gridlinesD",
+ defaultValue: false,
+ options: [
+ { value: true, label: "Gridlines" },
+ { value: false, label: "No gridlines" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleD != 'N' && data.props.labelTitleD != 'T';
+ }
+ },
+ axisMarginD: {
+ type: "string",
+ component: "dropdown",
+ label: "Dimension margin size",
+ ref: "props.axisMarginD",
+ defaultValue: "M",
+ options: [
+ { value: "N", label: "Narrow" },
+ { value: "M", label: "Medium" },
+ { value: "W", label: "Wide" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleD != 'N' && data.props.labelTitleD != 'T';
+ }
+ }
+ }
+ },
+ measureAxis: {
+ type: "items",
+ label: function (data) {
+ var t = "";
+ if (data.qHyperCubeDef.qDimensions.length == 0 || data.qHyperCubeDef.qMeasures.length > 1) {
+ if (data.props.axisTitleM.length > 0)
+ t = " (" + data.props.axisTitleM + ")"
+ }
+ else if (data.qHyperCubeDef.qMeasures.length > 0) {
+ if (data.qHyperCubeDef.qMeasures[0].qDef.hasOwnProperty("qLabel")) {
+ t = data.qHyperCubeDef.qMeasures[0].qDef.qLabel;
+ }
+ else {
+ t = data.qHyperCubeDef.qMeasures[0].qDef.qDef;
+ }
+ t = " (" + (t.startsWith("=") ? t.slice(1) : t) + ")";
+ };
+ return (data.props.orientation == "V" ? "Y" : "X") + "-Axis" + t;
+ },
+ items: {
+ labelTitleM: {
+ type: "string",
+ component: "dropdown",
+ label: "Labels and title",
+ ref: "props.labelTitleM",
+ defaultValue: "B",
+ options: [
+ { value: "B", label: "Labels and title" },
+ { value: "L", label: "Labels only" },
+ { value: "T", label: "Titles only" },
+ { value: "N", label: "None" }
+ ]
+ },
+ axisTitleM: {
+ type: "string",
+ label: "Measure axis title",
+ ref: "props.axisTitleM",
+ defaultValue: "Measure axis",
+ expression: "optional",
+ show: function (data) {
+ return ((
+ data.qHyperCubeDef.qDimensions.length == 0
+ || (
+ data.qHyperCubeDef.qDimensions.length == 1
+ && data.qHyperCubeDef.qMeasures.length > 1
+ )
+ )
+ && (data.props.labelTitleM == 'B' || data.props.labelTitleM == 'T'));
+ }
+ },
+ LabelStyleM: {
+ type: "string",
+ component: "dropdown",
+ label: "Label style",
+ ref: "props.labelStyleM",
+ defaultValue: "A",
+ options: [
+ { value: "A", label: "Auto" },
+ { value: "H", label: "Horizontal" },
+ { value: "S", label: "Staggered" },
+ { value: "T", label: "Tilted" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
+ }
+ },
+ gridlinesM: {
+ type: "boolean",
+ component: "switch",
+ label: "Gridlines",
+ ref: "props.gridlinesM",
+ defaultValue: true,
+ options: [
+ { value: true, label: "Gridlines" },
+ { value: false, label: "No gridlines" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
+ }
+ },
+ axisMarginM: {
+ type: "string",
+ component: "dropdown",
+ label: "Measure margin size",
+ ref: "props.axisMarginM",
+ defaultValue: "M",
+ options: [
+ { value: "N", label: "Narrow" },
+ { value: "M", label: "Medium" },
+ { value: "W", label: "Wide" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
+ }
+ },
+ ticks: {
+ type: "string",
+ component: "dropdown",
+ label: "Measure Tick spacing",
+ ref: "props.ticks",
+ defaultValue: "5",
+ options: [
+ { value: "5", label: "Wide" },
+ { value: "10", label: "Medium" },
+ { value: "20", label: "Narrow" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
+ }
+ },
+ axisFormatM: {
+ type: "string",
+ component: "dropdown",
+ label: "Measure axis number format",
+ ref: "props.axisFormatM",
+ defaultValue: "S",
+ options: [
+ { value: "A", label: "Auto" },
+ { value: "N", label: "Number" },
+ { value: "P", label: "Percent" },
+ { value: "S", label: "SI-notation (K, M, etc.)" },
+ { value: "C", label: "Custom" }
+ ],
+ show: function (data) {
+ return data.props.labelTitleM != 'N' && data.props.labelTitleM != 'T';
+ }
+ },
+ axisFormatMs: {
+ type: "string",
+ label: "Measure axis number format string",
+ ref: "props.axisFormatMs",
+ defaultValue: "s",
+ expression: "optional",
+ show: function (data) {
+ return data.props.axisFormatM == "C";
+ }
+ },
+ axisFormatMURL: {
+ type: "string",
+ component: "link",
+ label: "D3 format strings",
+ url: "https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md"
+ }
+ }
+ },
+ barText: {
+ type: "items",
+ label: "Text on bars",
+ items: {
+ showTexts: {
+ type: "string",
+ component: "dropdown",
+ label: "Text on bars",
+ ref: "props.showTexts",
+ defaultValue: "N",
+ options: [
+ { value: "N", label: "None" },
+ { value: "B", label: "Inside Bars" },
+ { value: "T", label: "Total" },
+ { value: "A", label: "Both" }
+ ]
+ },
+ showDim: {
+ type: "string",
+ component: "dropdown",
+ label: "Text to show in bars",
+ ref: "props.showDim",
+ defaultValue: "M",
+ options: [
+ { value: "M", label: "Measure" },
+ { value: "D", label: "Dimension" },
+ { value: "P", label: "Percent" }
+ ],
+ show: function (data) {
+ return ~"BA".indexOf(data.props.showTexts) && data.props.normalized;
+ }
+ },
+ showDim2: {
+ type: "string",
+ component: "dropdown",
+ label: "Text to show in bars",
+ ref: "props.showDim",
+ defaultValue: "M",
+ options: [
+ { value: "M", label: "Measure" },
+ { value: "D", label: "Dimension" }
+ ],
+ show: function (data) {
+ return ~"BA".indexOf(data.props.showTexts) && !data.props.normalized;
+ }
+ },
+ showTot: {
+ type: "string",
+ component: "dropdown",
+ label: "Text to show for total",
+ ref: "props.showTot",
+ defaultValue: "M",
+ options: [
+ { value: "M", label: "Measure" },
+ { value: "D", label: "Dimension" }
+ ],
+ show: function (data) {
+ return ~"TA".indexOf(data.props.showTexts);
+ }
+ },
+ innerBarPadH: {
+ type: "number",
+ label: "Horizontal inner bar padding (px)",
+ ref: "props.innerBarPadH",
+ defaultValue: 2,
+ expression: "optional",
+ show: function (data) {
+ return data.props.showTexts != "N";
+ }
+ },
+ innerBarPadV: {
+ type: "number",
+ label: "Vertical inner bar padding (px)",
+ ref: "props.innerBarPadV",
+ defaultValue: 2,
+ expression: "optional",
+ show: function (data) {
+ return data.props.showTexts != "N";
+ }
+ },
+ textSizeAbs: {
+ type: "boolean",
+ component: "switch",
+ label: "Text size proportional",
+ ref: "props.textSizeAbs",
+ defaultValue: true,
+ options: [
+ { value: true, label: "Not proportional" },
+ { value: false, label: "Proportional" }
+ ],
+ show: function (data) {
+ return data.props.showTexts != "N"
+ }
+ },
+ textSizeFactor: {
+ type: "number",
+ label: "Text size proportion factor",
+ ref: "props.textSizeFactor",
+ defaultValue: 1,
+ show: function (data) {
+ return data.props.showTexts != "N" && !data.props.textSizeAbs;
+ }
+ },
+ textSize: {
+ type: "number",
+ label: "Text size (px)",
+ ref: "props.textSize",
+ defaultValue: 10,
+ expression: "optional",
+ show: function (data) {
+ return data.props.showTexts != "N" && data.props.textSizeAbs;
+ }
+ },
+ textSizeMax: {
+ type: "number",
+ label: "Max text size (px)",
+ ref: "props.textSize",
+ defaultValue: 18,
+ expression: "optional",
+ show: function (data) {
+ return data.props.showTexts != "N" && !data.props.textSizeAbs;
+ }
+ },
+ textDots: {
+ type: "boolean",
+ component: "switch",
+ label: "Show text at all if ellipsis",
+ ref: "props.textDots",
+ defaultValue: true,
+ options: [
+ { value: true, label: "Show" },
+ { value: false, label: "Don't show" }
+ ],
+ show: function (data) {
+ return data.props.showTexts != "N";
+ }
+ },
+ textColor: {
+ type: "string",
+ component: "dropdown",
+ label: "Text color",
+ ref: "props.textColor",
+ defaultValue: "Auto",
+ options: [
+ { value: "Auto", label: "Auto" },
+ { value: "Black", label: "Black" },
+ { value: "White", label: "White" }
+ ],
+ show: function (data) {
+ return data.props.showTexts != "N";
+ }
+ },
+ vAlign: {
+ type: "string",
+ component: "dropdown",
+ label: "Vertical alignment",
+ ref: "props.vAlign",
+ defaultValue: "C",
+ options: [
+ { value: "C", label: "Center" },
+ { value: "T", label: "Top" },
+ { value: "B", label: "Bottom" }
+ ],
+ show: function (data) {
+ return data.props.showTexts != "N";
+ }
+ },
+ hAlign: {
+ type: "string",
+ component: "dropdown",
+ label: "Horizontal alignment",
+ ref: "props.hAlign",
+ defaultValue: "C",
+ options: [
+ { value: "C", label: "Center" },
+ { value: "L", label: "Left" },
+ { value: "R", label: "Right" }
+ ],
+ show: function (data) {
+ return data.props.showTexts != "N";
+ }
+ },
+ totalFormatM: {
+ type: "string",
+ component: "dropdown",
+ label: "Total format",
+ ref: "props.totalFormatM",
+ defaultValue: "S",
+ options: [
+ { value: "N", label: "Number" },
+ { value: "P", label: "Percent" },
+ { value: "S", label: "SI-notation (K, M, etc.)" },
+ { value: "C", label: "Custom" }
+ ],
+ show: function (data) {
+ return data.props.showTexts != "N";
+ }
+ },
+ totalFormatMs: {
+ type: "string",
+ label: "Total format string",
+ ref: "props.totalFormatMs",
+ defaultValue: ",.3s",
+ expression: "optional",
+ show: function (data) {
+ return data.props.showTexts != "N" && data.props.totalFormatM == "C";
+ }
+ },
+ totalFormatMURL: {
+ type: "string",
+ component: "link",
+ label: "D3 format strings",
+ url: "https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md",
+ show: function (data) {
+ return data.props.showTexts != "N" && data.props.totalFormatM == "C";
+ }
+ }
+ }
+ },
+ transitions: {
+ type: "items",
+ label: "Transitions",
+ items: {
+ transitions: {
+ type: "boolean",
+ component: "switch",
+ label: "Transitions",
+ ref: "props.transitions",
+ defaultValue: true,
+ options: [
+ { value: true, label: "Transitions enabled" },
+ { value: false, label: "Transitions disabled" }
+ ]
+ },
+ transitionDelay: {
+ type: "string",
+ label: "Transition delay",
+ ref: "props.transitionDelay",
+ defaultValue: "500",
+ expression: "optional",
+ show: function (data) {
+ return data.props.transitions;
+ }
+ },
+ transitionDuration: {
+ type: "string",
+ label: "Transition duration",
+ ref: "props.transitionDuration",
+ defaultValue: "1000",
+ expression: "optional",
+ show: function (data) {
+ return data.props.transitions;
+ }
+ },
+ transitionStyle: {
+ type: "string",
+ component: "dropdown",
+ label: "Transition style",
+ ref: "props.ease",
+ defaultValue: "linear",
+ options: [
+ { value: "linear", label: "Linear" },
+ { value: "quad", label: "Quad" },
+ { value: "cubic", label: "Cubic" },
+ { value: "sin", label: "Sine" },
+ { value: "exp", label: "Exponential" },
+ { value: "circle", label: "Circle" },
+ { value: "elastic", label: "Elastic" },
+ { value: "back", label: "Back" },
+ { value: "bounce", label: "Bounce" }
+ ],
+ show: function (data) {
+ return data.props.transitions;
+ }
+ }
+ }
+ }
+ }
+ }
+ return {
+ type: "items",
+ component: "accordion",
+ items: {
+ dimensions: dimensions,
+ measures: measures,
+ sorting: sorting,
+ addons: addons,
+ appearance: appearance
+ }
+ };
+
+ });
diff --git a/src/barsPlus-template.html b/src/barsPlus-template.html
new file mode 100644
index 0000000..8f74766
--- /dev/null
+++ b/src/barsPlus-template.html
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/barsPlus-test.html b/src/barsPlus-test.html
similarity index 100%
rename from barsPlus-test.html
rename to src/barsPlus-test.html
diff --git a/barsPlus.css b/src/barsPlus.css
similarity index 92%
rename from barsPlus.css
rename to src/barsPlus.css
index 5a02799..ac47b28 100644
--- a/barsPlus.css
+++ b/src/barsPlus.css
@@ -1,87 +1,95 @@
-/**
- * barsPlus styles
- *
- * Author: L. Woodside
- * Modification History:
- *
- * Version Person Date Description
- * V1.0.0 L. Woodside 19-Dec-2016 Initial Release
- * V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
- *
-*/
-.qv-object-barsPlus
- .ldwaxis path,
- .ldwaxis line {
- fill: none;
- stroke: black;
- shape-rendering: crispEdges;
- }
-.qv-object-barsPlus
- .ldwaxis {
- font-family: sans-serif;
- font-size: 12px;
- }
-.qv-object-barsPlus
- .ldwaxis .axisTitle {
- font-family: sans-serif;
- font-size: 12px;
- }
-.qv-object-barsPlus
- .ldwaxis .tick line {
- opacity: 0.2;
- }
-.qv-object-barsPlus
- .ldwbar.selected {
- stroke: black;
- stroke-width: 2px;
- }
-.qv-object-barsPlus
- .ldwtxt, .ldwtxtref, .ldwtot {
- font-family: sans-serif;
- pointer-events: none;
- }
-.qv-object-barsPlus
- .ldwlegend text {
- font-family: sans-serif;
- font-size: 12px;
- fill: #777;
- }
-.qv-object-barsPlus
- .ldwtooltip {
- position: fixed;
- padding: 5px;
- width: auto;
- background-color: #404040;
- border-radius: 5px;
- z-index: 10;
- pointer-events: none;
- }
-.qv-object-barsPlus
- .ldwtooltip::after {
- content: "";
- position: absolute;
- bottom: -10px;
- left: 50%;
- border-width: 10px 10px 0;
- border-style: solid;
- border-color: #404040 transparent;
- margin-left: -10px;
- pointer-events: none;
- }
-.qv-object-barsPlus
- .ldwtooltip .ldwttheading {
- margin: 5px;
- font-family: sans-serif;
- font-size: 13px;
- font-weight: bold;
- line-height: 18px;
- color: white;
- }
-.qv-object-barsPlus
- .ldwtooltip .ldwttvalue {
- margin: 5px;
- font-family: sans-serif;
- font-size: 13px;
- line-height: 18px;
- color: white;
- }
+/**
+ * barsPlus styles
+ *
+ * Author: L. Woodside
+ * Modification History:
+ *
+ * Version Person Date Description
+ * V1.0.0 L. Woodside 19-Dec-2016 Initial Release
+ * V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
+ *
+*/
+.qv-object-barsPlus
+ .ldwaxis path,
+ .ldwaxis line {
+ fill: none;
+ stroke: black;
+ shape-rendering: crispEdges;
+ }
+.qv-object-barsPlus
+ .ldwaxis {
+ font-family: sans-serif;
+ font-size: 12px;
+ }
+.qv-object-barsPlus
+ .ldwaxis .axisTitle {
+ font-family: sans-serif;
+ font-size: 12px;
+ }
+.qv-object-barsPlus
+ .ldwaxis .tick line {
+ opacity: 0.2;
+ }
+.qv-object-barsPlus
+ .ldwbar.selected {
+ stroke: black;
+ stroke-width: 2px;
+ }
+.qv-object-barsPlus
+ .ldwtxt, .ldwtxtref, .ldwtot {
+ font-family: sans-serif;
+ pointer-events: none;
+ }
+.qv-object-barsPlus
+ .ldwlegend text {
+ font-family: sans-serif;
+ font-size: 12px;
+ fill: #777;
+ }
+.qv-object-barsPlus
+ .ldwtooltip {
+ position: fixed;
+ padding: 5px;
+ width: auto;
+ background-color: #404040;
+ border-radius: 5px;
+ z-index: 10;
+ pointer-events: none;
+ }
+.qv-object-barsPlus
+ .ldwtooltip::after {
+ content: "";
+ position: absolute;
+ bottom: -10px;
+ left: 50%;
+ border-width: 10px 10px 0;
+ border-style: solid;
+ border-color: #404040 transparent;
+ margin-left: -10px;
+ pointer-events: none;
+ }
+.qv-object-barsPlus
+ .ldwtooltip .ldwttheading {
+ margin: 5px;
+ font-family: sans-serif;
+ font-size: 13px;
+ font-weight: bold;
+ line-height: 18px;
+ color: white;
+ }
+.qv-object-barsPlus
+ .ldwtooltip .ldwttvalue {
+ margin: 5px;
+ font-family: sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+ color: white;
+ }
+
+.qv-object-barsPlus .editMode {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ z-index: 1000;
+ top: 0;
+ }
\ No newline at end of file
diff --git a/barsPlus.js b/src/barsPlus.js
similarity index 83%
rename from barsPlus.js
rename to src/barsPlus.js
index f52f9ba..de98823 100644
--- a/barsPlus.js
+++ b/src/barsPlus.js
@@ -22,15 +22,24 @@
*/
define( [
"jquery",
+ "qlik",
"./barsPlus-initprops",
"./barsPlus-props",
"text!./barsPlus-template.html",
"./barsPlus-directive"
],
-function ($, initprops, props, template) {
+function ($, qlik, initprops, props, template) {
'use strict';
+ function isEditMode() {
+ if (qlik.navigation.getMode() === "analysis") {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
return {
initialProperties: initprops,
definition: props,
@@ -43,7 +52,8 @@ function ($, initprops, props, template) {
// console.log('resize>',$element,layout,$element.scope());
// },
template: template,
- controller: ['$scope', function($scope) {
+ controller: ['$scope', function($scope) {
+ $scope.isEditMode = isEditMode;
}],
paint: function ($element, layout) {
var self = this;
diff --git a/barsPlus.png b/src/barsPlus.png
similarity index 100%
rename from barsPlus.png
rename to src/barsPlus.png
diff --git a/barsPlus.qext b/src/barsPlus.qext
similarity index 100%
rename from barsPlus.qext
rename to src/barsPlus.qext
diff --git a/ldw-barsPlus.js b/src/ldw-barsPlus.js
similarity index 96%
rename from ldw-barsPlus.js
rename to src/ldw-barsPlus.js
index 970994e..21f597a 100644
--- a/ldw-barsPlus.js
+++ b/src/ldw-barsPlus.js
@@ -1,1464 +1,1464 @@
-/**
-
- barsPlus - Create D3 bar chart, stacked bar chart, area chart
-
- Author: L. Woodside
- Modification History:
-
- Version Person Date Description
- V1.0.0 L. Woodside 19-Dec-2016 Initial Release
- V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
- V1.2.0 L. Woodside 07-Jan-2017 Allow multiple measures
- V1.3.0 L. Woodside 15-Jan-2017 Improved color options
- V1.3.1 L. Woodside 27-Jan-2017 Fix problem with legend properties
-
- Dependencies: d3.v3.js
-
- This script creates an object with the following methods:
-
- initData() Initialize chart data
- initChart() Initialize a bar chart
- createBars() Create the bars in the chart
- updateBars() modify the bar chart due to changes in data
- refreshChart() refresh chart (calls three above excluding initData)
-
- Presentation Properties
-
- orientation Orientation: H - Horizontal, V - Vertical
- normalized Whether 100% bars
- showDeltas Whether to show bar connectors (delta quadrangles)
- barGap Spacing between bars, 0 - no space, 1 - no bars (area graph)
- outerGap Spacing before first bar and after last bar
- gridHeight Height of grid relative to highest bar
- backgroundColor Grid background color
-
- Colors and Legend
-
- colorSource A - Assigned, C - Calculated
- colorAttr Attribute is: O - an offset in scheme, C - a color value
- colorScheme Named color scheme
- colorOffset Offset of first color in color scheme (colorSource = Assigned)
- singleColor Whether to use single color for 1-dimensional bars
- showLegend Whether to show the legend
- legendPosition Legend position: T - top, R - right, B - bottom, L - left
- legendSize Legend size: N - narrow, M - medium, W - wide
- legendSpacing Legend spacing: N - narrow, M - medium, W - wide
-
- Dimension Axis
-
- axisTitleD Dimension axis title
- labelTitleD Dimension axis: B - labels & title, L - labels only, T - titles only, N - none
- labelStyleD Dimension style: A - Auto, H - horizontal, S - staggered, T - tilted
- gridlinesD Dimension gridlines
- axisMarginD Dimension margin size: N - narrow, M - medium, W - wide
-
- Measure Axis
-
- axisTitleM Measure axis title
- labelTitleM Measure axis: B - labels & title, L - labels only, T - titles only, N - none
- labelStyleM Measure style: A - Auto, H - horizontal, S - staggered, T - tilted
- gridlinesM Measure gridlines
- axisMarginM Measure margin size: N - narrow, M - medium, W - wide
- ticks Recommended number of ticks
- axisFormatM Number format for measure axis, A - Auto, N - Number, P - Percent, S - SI, C - Custom
- axisFormatMs Number format string for measure axis, D3 format
-
- Text on Bars
-
- showTexts Whether to show text on bars: B - on bars, T - total, A - both, N - none
- showDim What to show in bars: M - measure, D - dimension, P - percent
- showTot What to show for total: M - measure, D - dimension
- innerBarPadH Horizontal inner bar padding (px)
- innerBarPadV Vertical inner bar padding (px)
- textSizeAbs Whether to use textSize instead of textSizeFactor (horizontal bars only)
- textSizeFactor Text size as proportion of bar height (horizontal bars only)
- textSize Text size (px), when vertical bars or textSizeAbs = false
- textDots Whether to show text at all if ellipsis would be shown
- textColor "Auto" - Choose white or black depending on bar color, else text color string
- vAlign Vertical alignment: C - center, T - top, B - bottom
- hAlign Horizontal alignment: C - Center, L - left, R - right
- totalFormatM Number format for total: N - Number, P - Percent, S - SI, C - Custom
- totalFormatMs Number format string for total, D3 format
-
- Transitions
-
- transitions Whether to enable transitions
- transitionDelay Delay before start of transition
- transitionDuration Duration of transition
- ease Transition style
-
- Multiple measures
-
- defDims Defined number of dimensions
- defMeas Defined number of measures
- measures Array of measure names
-
- UI-determined Properties
-
- id Unique id of enclosing element
- component D3 selection of enclosing element
- width Width of enclosing element
- height Height of enclosing element
- inSelections Whether selection mode is enabled in Qlik Sense
- editMode Whether edit mode is enabled in Qlik Sense
- selectionMode Selection mode: QUICK or CONFIRM
- rawData Raw data from hypercube
-
-*/
-var ldwbarsPlus = {
-
-/**
- *--------------------------------------
- * Initialize Data
- *--------------------------------------
- * This method will take input QV data and format it for 1 or 2 dimensions
- * Input: g.rawData
- * Output: g.data
- * g.flatData
- * g.allDim2
- * g.allCol2
- * g.nDims
- * g.deltas
-*/
-initData: function() {
- /*
- To support multiple measures, take the input raw data and transform it
- to look like previously supported formats:
- 0 Dimensions, 1 or more measures -> format as 1 dimension, 1 measure
- 1 Dimension, 2 or more measures -> format as 2 dimensions, 1 measure
- If two dimensions and multiple measures specified, ignore all but the first measure
- */
- var g = this;
- var struc = [], flatData = [], q = [], r = [], deltas = [], inData = [];
-
- if (!g.rawData[0]) return; // sometimes undefined
- if (g.defDims + g.defMeas != g.rawData[0].length) return; // sometimes mismatched
-
- if (g.defDims == 0) {
- for (var i = 0; i < g.rawData.length; i++) {
- for (var j = 0; j < g.measures.length; j++) {
- inData.push([
- {qElemNumber: -1, qNum: j+1, qText: g.measures[j]},
- g.rawData[i][j]
- ]);
- }
- }
- }
- else if (g.defDims == 1 && g.defMeas > 1) {
- for (var i = 0; i < g.rawData.length; i++) {
- for (var j = 0; j < g.measures.length; j++) {
- inData.push([
- g.rawData[i][0],
- {qElemNumber: -1, qNum: j+1, qText: g.measures[j]},
- g.rawData[i][j+1]
- ]);
- }
- }
- }
- else {
- inData = g.rawData; // Process as in previous version
- }
- // If calculated color specified, must have provided Attribute
- // otherwise set back to assigned mode
-
- var cx = g.defDims == 1 ? (g.defMeas == 1 ? 0 : 2) : 1; // index for attribute
- if (g.colorSource == "C" && !inData[0][cx].qAttrExps) g.colorSource = "A";
-
- // Function to get dimension/measure attribute for color
- var cf = function(e) {
- var cn = 0;
- if (g.colorSource == "C") {
- var cv = e[cx].qAttrExps.qValues[0].qNum;
- if (!isNaN(cv))
- cn = Math.floor(Math.abs(cv));
- if (g.colorAttr == "C") {
- // if text attribute exists, use it
- if (e[cx].qAttrExps.qValues[0].qText) {
- cn = e[cx].qAttrExps.qValues[0].qText;
- }
- else {
- // number present, convert to hex
- cv = cv.toString(16);
- if (cv.length > 6) cv = cv.substring(cv.length-6);
- cn = "#" + "000000".substring(0,6-cv.length) + cv;
- }
- }
- }
- return cn;
- }
-
- // Process one dimension data
- if (inData[0].length == 2) {
- g.nDims = 1;
- g.normalized = false;
- inData.forEach(function(d) {
- struc.push({dim1: d[0].qText, offset: d[1].qNum});
- flatData.push({
- dim1: d[0].qText,
- dim2: d[0].qText,
- offset: 0,
- qNum: d[1].qNum,
- qText: d[1].qText,
- qTextPct: "",
- qElemNumber: d[0].qElemNumber
- });
- if(q.indexOf(d[0].qText) == -1) {
- q.push(d[0].qText);
- r.push(cf(d));
- }
- });
- g.data = struc;
- g.flatData = flatData;
- g.allDim2 = q;
- g.allCol2 = r;
- return;
- };
-
- // Process two dimensional data
- g.nDims = 2;
-
- var p1 = "", p2, edges = [], b, p = [];
- inData.forEach(function(d) {
- var c2 = d[1].qText;
- if (p.indexOf(d[0].qText) == -1) {
- p.push(d[0].qText);
- }
- if (q.indexOf(d[1].qText) == -1) {
- q.push(d[1].qText);
- r.push(cf(d));
- }
- if (d[0].qText != p1) {
- p1 = d[0].qText;
- }
- else {
- b = false;
- for (var i = 0; i < edges.length; i++) {
- if (edges[i][0] == p2 && edges[i][1] == c2) {
- b = true;
- break;
- }
- }
- if (!b) {
- edges.push([p2,c2])
- }
- }
- p2 = c2;
- });
- // Topological sort will throw an error if inconsistent data (sorting by measure)
- // Just ignore errors and use original sort order
- var qs, ps, rs = [];
- try {
- ps = q.slice();
- qs = this.toposort(q,edges);
- // Replicate qs order in r
- for (var i = 0; i < ps.length; i++) {
- rs.push(r[ps.indexOf(qs[i])]);
- }
- r = rs;
- }
- catch(err) {
- qs = q;
- }
- q = qs;
-
- var n = d3.nest()
- .key(function(d) {return d[0].qText})
- .key(function(d) {return d[1].qText})
- .entries(inData)
- ;
- // sort all nodes in order specified by q
- n.forEach(function(d) {
- d.values.sort(function(a,b) {
- return ( q.indexOf(a.key) < q.indexOf(b.key) ? -1
- : ( q.indexOf(a.key) > q.indexOf(b.key) ? 1 : 0));
- });
- });
- // nest messes up dim1 sort order, sort by order specified in p
- n.sort(function(a,b) {
- return ( p.indexOf(a.key) < p.indexOf(b.key) ? -1
- : ( p.indexOf(a.key) > p.indexOf(b.key) ? 1 : 0));
- });
- n.forEach(function(d, idx) {
- var t = 0, v = [], j = 0, num, txt;
- for (var i = 0; i < q.length; i++) {
- if (d.values.length <= j || d.values[j].key != q[i]) {
- num = 0;
- txt = "-";
- elm = [];
- }
- else {
- num = d.values[j].values[0][2].qNum;
- txt = d.values[j].values[0][2].qText;
- elm = d.values[j].values[0][0].qElemNumber;
- j++;
- }
- v.push({
- dim2: q[i],
- qNum: num,
- qText: txt,
- qElemNumber: elm,
- offset: t
- });
- t += num;
- };
- v.forEach(function(e) {
- e.dim1 = d.key;
- if (g.normalized) {
- e.offset = e.offset/t;
- e.qNum = e.qNum/t;
- e.qTextPct = d3.format(".1%")(e.qNum);
- }
- });
- flatData.push.apply(flatData,v);
- struc.push({dim1: d.key, offset: t, values: v});
-
- if (idx > 0 && g.showDeltas) {
- var p = struc[idx-1].values;
- var c = struc[idx].values;
- for (var k = 0; k < p.length; k++) {
- deltas.push({
- dim1p: p[k].dim1,
- dim1c: c[k].dim1,
- dim2: p[k].dim2,
- delta: c[k].qNum - p[k].qNum,
- deltaPct: 0,
- points: [
- p[k].offset,
- c[k].offset,
- p[k].qNum,
- c[k].qNum
- ]
- });
- }
- }
-
- });
- g.data = struc;
- g.flatData = flatData;
- g.allDim2 = q;
- g.allCol2 = r;
- g.deltas = deltas;
-},
-
-/**
- *--------------------------------------
- * Initialize Chart
- *--------------------------------------
- * Set up initial elements, create axes, create legend
- * create bars, deltas and legend items and bind data
-*/
-initChart: function() {
-
- var g = this;
-
- var xLabelTitle = g.orientation == "V" ? g.labelTitleD : g.labelTitleM;
- g.xAxisHeight = xLabelTitle == "B" || xLabelTitle == "L"
- ? [70,40,25]["WMN".indexOf(g.orientation == "V" ? g.axisMarginD : g.axisMarginM)] : 0;
- var xTitleHeight = xLabelTitle == "B" || xLabelTitle == "T" ? 20 : 0;
- var xAxisPad = 20;
-
- var yLabelTitle = g.orientation == "V" ? g.labelTitleM : g.labelTitleD;
- g.yAxisWidth = yLabelTitle == "B" || yLabelTitle == "L"
- ? [90,50,30]["WMN".indexOf(g.orientation == "V" ? g.axisMarginM : g.axisMarginD)] : 0;
- var yTitleWidth = yLabelTitle == "B" || yLabelTitle == "T" ? 20 : 0;
- var yAxisPad = 20;
-
- var tr; // translate string
- var dTitleHeight = g.labelTitleD == "B" || g.labelTitleD == "T" ? 20 : 0;
- var margin = {
- top: 10, //yAxisPad,
- right: xAxisPad,
- bottom: g.xAxisHeight + xTitleHeight + xAxisPad,
- left: g.yAxisWidth + yTitleWidth + yAxisPad
- };
- var innerWidth = g.width - margin.left - margin.right;
- var innerHeight = g.height - margin.top - margin.bottom;
-
- g.lgn = {
- minDim : [200,100], // min inner dimensions for legend to be displayed
- use : "",
- pad : 0,
- sep : 5,
- box : [12,12], // legend item color box
- itmHeight : 20,
- }
- g.lgn.txtOff = g.lgn.box[0] + g.lgn.pad + g.lgn.sep;
-
- // adjust for legend if any
- g.lgn.use = g.showLegend ? g.legendPosition : "";
- if (g.lgn.use) {
- if (g.lgn.use == "L" || g.lgn.use == "R") {
- if (innerWidth <= g.lgn.minDim[0]) {
- g.lgn.use = "";
- }
- else {
- g.lgn.width = innerWidth/([4,6,10]["WMN".indexOf(g.legendSize)]);
- innerWidth -= (g.lgn.width + yAxisPad);
- g.lgn.height = innerHeight + g.xAxisHeight + xTitleHeight;
- g.lgn.y = margin.top;
- g.lgn.txtWidth = g.lgn.width - g.lgn.pad - g.lgn.sep - g.lgn.box[0];
- if (g.lgn.use == "L") {
- g.lgn.x = yAxisPad;
- margin.left += g.lgn.width + g.lgn.x;
- }
- else {
- g.lgn.x = margin.left + innerWidth + yAxisPad;
- }
- }
- }
- else if (g.lgn.use == "T" || g.lgn.use == "B") {
- if (innerHeight <= g.lgn.minDim[1]) {
- g.lgn.use = "";
- }
- else {
- g.lgn.width = innerWidth + g.yAxisWidth + yTitleWidth;
- g.lgn.height = g.lgn.itmHeight * (3 - "WMN".indexOf(g.legendSize));
- innerHeight -= g.lgn.height;
- g.lgn.x = yAxisPad;
- g.lgn.txtWidth = [100,75,50]["WMN".indexOf(g.legendSpacing)];
- if (g.lgn.use == "T") {
- g.lgn.y = margin.top;
- margin.top += g.lgn.height
- }
- else {
- g.lgn.y = margin.bottom + innerHeight;
- innerHeight -= 10;
- }
- }
- }
- }
- g.component.selectAll("*")
- .remove()
- ;
- var tooltip = g.component.append("div")
- .attr("class","ldwtooltip")
- .style("opacity","0")
- ;
- tooltip.append("p")
- .attr("class","ldwttheading")
- ;
- tooltip.append("p")
- .attr("class","ldwttvalue")
- ;
- g.svg = g.component
- .append("svg")
- .attr("width",g.width)
- .attr("height",g.height)
- .style("background-color",g.backgroundColor)
- .append("g")
- .attr("transform","translate(" + margin.left + "," + margin.top + ")")
- ;
- var dim1 = g.data.map(function(d) { return d.dim1; });
- if (g.orientation == "H") dim1.reverse()
- g.dScale = d3.scale.ordinal()
- .domain(dim1)
- .rangeRoundBands(g.orientation == "V" ? [0, innerWidth] : [innerHeight,0], g.barGap, g.outerGap)
- ;
- g.mScale = d3.scale.linear()
- .domain([0, d3.max(g.data, function(d) { return (g.normalized ? 1 : d.offset)*g.gridHeight; })])
- .range(g.orientation == "V" ? [innerHeight,0] : [0, innerWidth])
- .nice()
- ;
- var dGrp = g.svg.append("g")
- .attr("class", "ldw-d ldwaxis");
- if (g.orientation == "V") {
- dGrp.attr("transform", "translate(0," + innerHeight + ")");
- }
- if (g.labelTitleD == 'B' || g.labelTitleD == 'L') {
- g.dAxis = d3.svg.axis()
- .scale(g.dScale)
- .orient(g.orientation == "V" ? "bottom" : "left")
- .tickSize(g.gridlinesD ? (g.orientation == "V" ? -innerHeight : -innerWidth) : 6)
- .tickPadding(5)
- ;
- dGrp.call(g.dAxis);
- }
- if (g.labelTitleD == 'B' || g.labelTitleD == 'T') {
- if(g.orientation == "V") {
- tr = "translate(" + (innerWidth/2) + "," + (g.xAxisHeight + xTitleHeight) + ")";
- }
- else {
- tr = "translate(-" + (g.yAxisWidth + yTitleWidth/2 + 2) + "," + (innerHeight/2) + ")rotate(-90)";
- }
- dGrp.append("text")
- .attr("class","axisTitle")
- .attr("text-anchor","middle")
- .attr("transform",tr)
- .text(g.axisTitleD)
- ;
- }
- var mGrp = g.svg.append("g")
- .attr("class", "ldw-m ldwaxis")
- ;
- if (g.orientation != "V") {
- mGrp.attr("transform", "translate(0," + innerHeight + ")");
- }
- if (g.labelTitleM == 'B' || g.labelTitleM == 'L') {
- g.mAxis = d3.svg.axis()
- .scale(g.mScale)
- .orient(g.orientation == "V" ? "left" : "bottom")
- .tickSize(g.gridlinesM ? (g.orientation == "V" ? -innerWidth : -innerHeight) : 6)
- .ticks(g.ticks)
- .tickFormat(d3.format([",.3s", ",.0f", ",.0%", ",.3s", g.axisFormatMs]["ANPSC".indexOf(g.axisFormatM)]))
- .tickPadding(5)
- ;
- mGrp.call(g.mAxis);
- }
- if (g.labelTitleM == 'B' || g.labelTitleM == 'T') {
- if(g.orientation == "V") {
- tr = "translate(-" + (g.yAxisWidth + yTitleWidth/2 + 2) + "," + (innerHeight/2) + ")rotate(-90)";
- }
- else {
- tr = "translate(" + (innerWidth/2) + "," + (g.xAxisHeight + xTitleHeight) + ")";
- }
- mGrp.append("text")
- .attr("class","axisTitle")
- .attr("text-anchor","middle")
- .attr("transform",tr)
- .text(g.axisTitleM)
- ;
- }
- if (Object.getPrototypeOf(this).hasOwnProperty(g.colorScheme))
- ca = this[g.colorScheme];
- else
- ca = d3.scale[g.colorScheme]().range();
- g.colorOffset %= ca.length;
- // define various ways to get color
- if (g.nDims == 1 && g.colorSource != "C" && g.singleColor) {
- var rc = ca[g.colorOffset];
- g.cScale = function(d) { return rc; };
- }
- else if (g.colorSource == "C") {
- if (g.colorAttr == "O") { // color from calculated offset
- g.cScale = function(d) {
- return ca[g.allCol2[g.allDim2.indexOf(d)] % ca.length];
- }
- }
- else { // color is direct value
- g.cScale = function(d) {
- return g.allCol2[g.allDim2.indexOf(d)];
- }
- }
- }
- else {
- g.cScale = d3.scale.ordinal().range(
- ca.slice(g.colorOffset,ca.length).concat(ca.slice(0,ca.length))
- ).domain(g.allDim2);
- }
-
- // Create Legend
- if (g.lgn.use) {
- var lgn = d3.select("#" + g.id + " svg")
- .append("g")
- .attr("class","ldwlegend")
- .attr("clip-path","url(#" + g.id + "_lgnClip)")
- .attr("transform","translate(" + g.lgn.x + "," + g.lgn.y + ")")
- ;
- lgn.append("rect")
- .attr("x",0)
- .attr("y",0)
- .attr("width",g.lgn.width)
- .attr("height",g.lgn.height)
- .style("fill",g.backgroundColor)
- ;
- lgn.append("clipPath")
- .attr("id",g.id + "_lgnClip")
- .append("rect")
- .attr("x","0")
- .attr("y","0")
- .attr("width",g.lgn.width)
- .attr("height",g.lgn.height)
- ;
- lgn.append("g")
- .attr("class","ldwlgnitems")
- }
- // Create bars
- g.bars = g.svg.selectAll("#" + g.id + " .ldwbar")
- .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
- ;
- // Text on bars
- if (g.showTexts != "N") {
- // Create text box for determining sizing
- g.tref = g.svg.append("text")
- .attr("x","0")
- .attr("y","-100")
- .attr("class","ldwtxtref")
- ;
-
- if (~"TA".indexOf(g.showTexts) && !g.normalized) {
- // Create bars totals
- g.totals = g.svg.selectAll("#" + g.id + " .ldwtot")
- .data(g.data, function(d) { return d.dim1; } )
- ;
- }
- if (~"BA".indexOf(g.showTexts)) {
- // Create text on bars
- g.texts = g.svg.selectAll("#" + g.id + " .ldwtxt")
- .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
- ;
- }
- }
- // Create deltas
- if (g.showDeltas && g.nDims == 2) {
- g.polys = g.svg.selectAll("#" + g.id + " polygon")
- .data(g.deltas, function(d) { return d.dim1p + "-" + d.dim1c + "," + d.dim2; } )
- ;
- }
- // Create legend items
- if (g.lgn.use) {
- g.lgn.items = d3.select("#" + g.id + " .ldwlgnitems")
- .selectAll("g")
- .data(g.allDim2)
- ;
- }
-
-},
-
-/**
- *--------------------------------------
- * Create Bars
- *--------------------------------------
- * Set up initial properties of bars, deltas, and legend
- * Objects already have had data bound
- * This procedure is also called from updateBars to add new items
-*/
-createBars: function() {
-
- var g = this;
- // Create bars
- g.bars
- .enter()
- .append("rect")
- .attr("ldwdim1", function(d) { return d.qElemNumber; })
- .attr(g.orientation == "V" ? "x" : "y", function(d) { return g.dScale(d.dim1); })
- .attr(g.orientation == "V" ? "y" : "x", function(d) { return g.mScale(0); }) // grow from bottom
-// .attr(g.orientation == "V" ? "y" : "x", function(d) { return g.mScale(d.offset); }) // venetian blinds
- .attr(g.orientation == "V" ? "width" : "height", g.dScale.rangeBand())
- .attr(g.orientation == "V" ? "height" : "width", function(d) { return 0; })
- .style("fill", function(d) { return g.cScale(d.dim2); })
- .style("opacity","0")
- .attr("class","selectable ldwbar")
- .on("click",function(d) {
- if (d.qElemNumber >= 0) { // Cannot select a measure
- if (g.selectionMode == "QUICK") {
- g.self.backendApi.selectValues(0,[d.qElemNumber],true);
- }
- else if (g.selectionMode == "CONFIRM") {
- var t = d3.select(this).classed("selected");
- g.self.selectValues(0,[d.qElemNumber],true);
- // following to address QS bug where clear button does not clear class names
- g.self.clearSelectedValues = function() {
- d3.selectAll("#" + g.id +" .selected").classed("selected",false);
- };
- var x = d3.selectAll("#" + g.id + " [ldwdim1='" + d.qElemNumber + "']")
- .classed("selected",!t);
- d3.select("#" + g.id + " .ldwtooltip")
- .style("opacity","0")
- .transition()
- .remove
- ;
- }
- }
- return;
- })
- .on("mouseenter",function(d) {
- if (g.inSelections || g.editMode) return;
- d3.select(this)
- .style("opacity","0.5")
- .attr("stroke","white")
- .attr("stroke-width","2")
- ;
- // Place text in tooltip
- d3.select("#" + g.id + " .ldwttheading")
- .text(g.nDims == 2 ? d.dim1 + ", " + d.dim2 : d.dim1);
- d3.select("#" + g.id + " .ldwttvalue")
- .text(g.nDims == 2 ?
- (g.normalized ? d.qTextPct + ", " + d.qText : d.qText)
- : d.qText);
-
- var matrix = this.getScreenCTM()
- .translate(+this.getAttribute("x"),+this.getAttribute("y"));
-
- var xPosition = (window.pageXOffset + matrix.e)
- - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientWidth/2
- + (g.orientation == "V" ? g.dScale.rangeBand() : d3.select(this).attr("width"))/2
- ;
- var yPosition = (window.pageYOffset + matrix.f)
- - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientHeight
- -10
- ;
- d3.select("#" + g.id + " .ldwtooltip")
- .style("left", xPosition + "px")
- .style("top", yPosition + "px")
- .transition()
- .delay(750)
- .style("opacity","0.95")
- ;
- })
- .on("mouseleave",function() {
- d3.select(this)
- .style("opacity","1.0")
- .attr("stroke","none")
- ;
- d3.select("#" + g.id + " .ldwtooltip")
- .style("opacity","0")
- .transition()
- .remove
- ;
- })
- ;
-
- if (~"TA".indexOf(g.showTexts) && !g.normalized) {
- // Create totals
- g.totals
- .enter()
- .append("text")
- .attr("class","ldwtot")
- .style("opacity","0")
- .each(function(d) {
- d.qNum = g.mScale.domain()[1] - d.offset;
- d.qText = d3.format([",.0f", ",.0%", ",.3s", g.totalFormatMs]["NPSC".indexOf(g.totalFormatM)])(d.offset);
- var txp = g.barText(d, true);
-
- d3.select(this)
- .style("fill", "black")
- .style("font-size", g.tref.style("font-size"))
- .attr("x",g.orientation == "V" ? txp.x : 0)
- .attr("y",g.orientation == "V" ? g.mScale(0) : txp.y)
- .attr("dy","-.2em")
- .text(txp.text)
- ;
-
- })
- ;
- }
-
- if (~"BA".indexOf(g.showTexts)) {
- // Create text inside bars
- g.texts
- .enter()
- .append("text")
- .attr("class","ldwtxt")
- .style("opacity","0")
- .each(function(d) {
- var txp = g.barText(d);
-
- d3.select(this)
- .style("fill", g.textColor == "Auto" ? g.txtColor(g.cScale(d.dim2)) : g.textColor )
- .style("font-size", g.tref.style("font-size"))
- .attr("x",g.orientation == "V" ? txp.x : 0)
- .attr("y",g.orientation == "V" ? g.mScale(0) : txp.y)
- .attr("dy","-.2em")
- .text(txp.text)
- ;
-
- })
- ;
- }
-
- if (g.showDeltas && g.nDims == 2) {
-
- // Create deltas
- g.polys
- .enter()
- .append("polygon")
- .attr("points",function(d) {
- var p;
- if (g.orientation == "V") {
- p = (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
- g.mScale(0) + " " +
- (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
- g.mScale(0) + " " +
- (g.dScale(d.dim1c)) + "," +
- g.mScale(0) + " " +
- (g.dScale(d.dim1c)) + "," +
- g.mScale(0)
- ;
- }
- else {
- p = g.mScale(0) + "," + (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
- g.mScale(0) + "," + (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
- g.mScale(0) + "," + g.dScale(d.dim1c) + " " +
- g.mScale(0) + "," + g.dScale(d.dim1c)
- ;
- }
- return p;
- })
- .style("fill",function(d) {
- return g.cScale(d.dim2);
- })
- .style("opacity","0")
- .on("mouseenter",function(d) {
- var pt = this.getAttribute("points").split(" ");
- var sx = 0, sy = 0;
- pt.forEach(function(e, i) {
- var x = e.split(",");
- if (g.orientation == "H") {
- if (i < 2) {
- sx += parseFloat(x[0]);
- sy += parseFloat(x[1]);
- }
- }
- else if (i == 0 || i == 3) {
- sx += parseFloat(x[0]);
- sy += parseFloat(x[1]);
- }
- } );
- sx /= 2;
- sy /= 2;
-
- if (g.inSelections || g.editMode) return;
-
- d3.select(this)
- .style("opacity","0.5")
- .attr("stroke","white")
- .attr("stroke-width","2");
- // Place text in tooltip
- d3.select("#" + g.id + " .ldwttheading")
- .text(d.dim2 + ", " + d.dim1p + "-" + d.dim1c);
- d3.select("#" + g.id + " .ldwttvalue")
- .text(d3.format(g.normalized ? "+.1%" : "+.3s")(d.delta));
-
- var matrix = this.getScreenCTM()
- .translate(sx,sy);
-
- var xPosition = (window.pageXOffset + matrix.e)
- - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientWidth/2
- ;
- var yPosition = (window.pageYOffset + matrix.f)
- - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientHeight
- -10
- ;
- d3.select("#" + g.id + " .ldwtooltip")
- .style("left", xPosition + "px")
- .style("top", yPosition + "px")
- .transition()
- .delay(750)
- .style("opacity","0.95")
- ;
- })
- .on("mouseleave",function() {
- d3.select(this)
- .style("opacity",g.barGap == 1 ? "1" : "0.5")
- .attr("stroke","none")
- ;
- d3.select("#" + g.id + " .ldwtooltip")
- .style("opacity","0")
- .transition()
- .remove;
- })
- ;
- }
- // create legend
- if (g.lgn.use) {
- g.lgn.items
- .enter()
- .append("g")
- .attr("class","ldwlgnitem")
- .each(function(d, i) {
- d3.select(this)
- .append("rect")
-// .attr("x","0") // Initialize to zero to have legend grow from top
-// .attr("y","0")
- .attr("x",function(e) {
- var x;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- x = i*(g.lgn.txtOff + g.lgn.txtWidth);
- }
- else {
- x = g.lgn.pad;
- }
- return x;
- })
- .attr("y",function(e) {
- var y;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- y = g.lgn.pad;
- }
- else {
- y = g.lgn.pad + g.lgn.itmHeight*i;
- }
- return y;
- })
- .style("opacity","0")
- .attr("width",g.lgn.box[0])
- .attr("height",g.lgn.box[1])
- .style("fill",function(e) { return g.cScale(e); })
- ;
- d3.select(this)
- .append("text")
-// .attr("x","0") // Initialize to zero to have legend grow from top
-// .attr("y","0")
- .attr("x",function(e) {
- var x;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- x = i*(g.lgn.txtOff + g.lgn.txtWidth) + g.lgn.txtOff;
- }
- else {
- x = g.lgn.txtOff;
- }
- return x;
- })
- .attr("y",function(e) {
- var y;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- y = g.lgn.pad + 11;
- }
- else {
- y = g.lgn.pad + g.lgn.itmHeight*i + 11;
- }
- return y;
- })
- .style("opacity","0")
- .text(function(e) {
- return e;
- })
- ;
- })
- ;
- }
-},
-/**
- *--------------------------------------
- * Bar Text
- *--------------------------------------
- * Get bar text information: x, y and text
-*/
-barText: function(d, total) {
- var tx, txt, origX, bHeight, textX, bb, textY, textLength, ts;
- var g = this;
-
- // Relative text sizing, relative to bar width
- // For total, make larger by reducing unneeded padding
- var hAlign = g.hAlign, vAlign = g.vAlign,
- innerBarPadV = +g.innerBarPadV,
- innerBarPadH = +g.innerBarPadH;
- if (g.textSizeAbs)
- ts = g.textSize;
- else {
- if (total)
- if (g.orientation == "H")
- innerBarPadV = 1;
- else
- innerBarPadH = 1;
- ts = g.textSizeFactor * (g.dScale.rangeBand() - 2 * (g.orientation == "H" ? innerBarPadV : innerBarPadH))
- // limit size to maximum, also stored in textSize
- if (ts > g.textSize) ts = g.textSize;
- }
- g.tref.style("font-size", ts);
-
- if (total) { // Override some alignment for total
- if (g.orientation == "V")
- vAlign = "B"
- else
- hAlign = "L"
- }
- origX = g.orientation == "V" ? g.dScale(d.dim1) : g.mScale(d.offset);
- bHeight = g.orientation == "V" ? g.mScale(0) - g.mScale(d.qNum) : g.dScale.rangeBand();
- textX = g.orientation == "V" ? g.dScale.rangeBand() : g.mScale(d.qNum);
-
- g.tref.text(d.qNum == 0 ? ""
- : (total
- ? (g.showTot == "D" ? d.dim1 : d.qText)
- : (g.showDim == "D" ? d.dim2 : (g.showDim == "P" && g.normalized ? d.qTextPct : d.qText))
- )
- );
-
- bb = g.tref.node().getBBox();
- tx = origX + innerBarPadH;
- if (vAlign == "C")
- textY = g.orientation == "V" ? g.mScale(d.offset) - (g.mScale(0) - g.mScale(d.qNum)) +
- (g.mScale(0) - g.mScale(d.qNum) + bb.height)/2
- : g.dScale(d.dim1) + (g.dScale.rangeBand() + bb.height)/2;
- else if (vAlign == "T")
- textY = g.orientation == "V" ? g.mScale(d.offset) - (g.mScale(0) - g.mScale(d.qNum)) +
- bb.height + innerBarPadV
- : g.dScale(d.dim1) + innerBarPadV + bb.height;
- else
- textY = g.orientation == "V" ? g.mScale(d.offset) - innerBarPadV
- : g.dScale(d.dim1) + g.dScale.rangeBand() - innerBarPadV;
- txt = "";
- if (bb.height + 2 * innerBarPadV <= bHeight || (g.orientation != "V" && !g.textSizeAbs)) {
- if (bb.width + 2 * innerBarPadH <= textX) {
- if (hAlign == "C") {
- tx = origX + (textX - bb.width)/2;
- }
- else if (hAlign == "R") {
- tx = origX + textX - bb.width - innerBarPadH;
- }
- txt = g.tref.text();
- }
- else if (g.textDots) {
- textLength = g.tref.node().getComputedTextLength();
- txt = g.tref.text();
- while (textLength > textX - 2 * innerBarPadH && txt.length > 0) {
- txt = txt.slice(0, -1);
- g.tref.text(txt + '\u2026');
- textLength = g.tref.node().getComputedTextLength();
- }
- if (txt.length != 0) txt = g.tref.text();
- }
- }
- return {x: tx, y: textY, text: txt};
-
-},
-/**
- *--------------------------------------
- * Update Bars
- *--------------------------------------
- * Modify properties of bars, deltas, and legend
-*/
-updateBars: function() {
-
- var g = this;
-
- var dim1 = g.data.map(function(d) { return d.dim1; });
- if (g.orientation == "H") dim1.reverse()
- g.dScale.domain(dim1);
- g.mScale.domain( [0, d3.max(g.data, function(d) { return (g.normalized ? 1 : d.offset)*g.gridHeight; })]);
- var tDelay = g.transitions && !g.editMode ? g.transitionDelay : 0;
- var tDuration = g.transitions && !g.editMode ? g.transitionDuration : 0;
-
- // Procedure to update dimension and measure axes
- var updateAxis = function (labelTitle, labelStyle, axis, axisClass, isXAxis, axisWidth) {
-
- if (labelTitle == 'B' || labelTitle == 'L') {
- // Update axis with transition
- g.svg.select("#" + g.id + " ." + axisClass + ".ldwaxis")
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .call(axis)
- ;
- var lbl = d3.selectAll("#" + g.id + " ." + axisClass + ".ldwaxis");
- var txt = lbl.selectAll(".tick.major text")
- .attr("transform",null); // All horizontal initially
- var maxWidth = isXAxis ? lbl.node().getBBox().width/txt[0].length : g.yAxisWidth - 5;
-
- // If auto labels and any overlap, set to tilted
- if (labelStyle == "A") {
- txt.each(function(d, i) {
- if (d3.select(this).node().getComputedTextLength() > maxWidth) {
- labelStyle = "T" // no break for each
- }
- })
- ;
- }
- // Tilted labels
- if (labelStyle == "T") {
- txt.style("text-anchor","end")
- .attr("transform","translate(" + (isXAxis ? "-12,0" : "-2,-8") + ") rotate(-45)")
- ;
- maxWidth = isXAxis ? g.xAxisHeight - 5 : maxWidth * Math.sqrt(2);
- }
- // Staggered labels
- else if (labelStyle == "S" && isXAxis) {
- txt.each(function(d, i) {
- if (i % 2 == 1) {
- d3.select(this).attr("transform","translate(0,14)");
- }
- })
- ;
- }
- // Horizontal or titled labels, use ellipsis if overlap
- if (labelStyle == "H" || labelStyle == "T") {
- txt.each(function(d, i) {
- var self = d3.select(this),
- textLength = self.node().getComputedTextLength(),
- text = self.text();
- while (textLength > maxWidth && text.length > 0) {
- text = text.slice(0, -1);
- self.text(text + '\u2026');
- textLength = self.node().getComputedTextLength();
- }
- });
- }
- }
- };
- // Update dimension axis
- updateAxis(g.labelTitleD, g.labelStyleD, g.dAxis, "ldw-d", g.orientation == "V");
- // Update measure axis
- updateAxis(g.labelTitleM, g.labelStyleM, g.mAxis, "ldw-m", g.orientation != "V");
-
- g.bars = g.svg.selectAll("#" + g.id + " .ldwbar")
- .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
- ;
- // Remove bars with transition
- g.bars
- .exit()
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity", "0")
- .remove()
- ;
-
- // Remove totals with transition
- if (~"TA".indexOf(g.showTexts)) {
- g.totals = g.svg.selectAll("#" + g.id + " .ldwtot")
- .data(g.data, function(d) { return d.dim1; } )
- ;
- g.totals
- .exit()
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity", "0")
- .remove()
- ;
- }
- // Remove texts with transition
- if (~"BA".indexOf(g.showTexts)) {
- g.texts = g.svg.selectAll("#" + g.id + " .ldwtxt")
- .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
- ;
- g.texts
- .exit()
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity", "0")
- .remove()
- ;
- }
-
- if (g.showDeltas && g.nDims == 2) {
- g.polys = g.svg.selectAll("#" + g.id + " polygon")
- .data(g.deltas, function(d) { return d.dim1p + "-" + d.dim1c + "," + d.dim2; } )
- ;
- // Remove deltas with transition
- g.polys
- .exit()
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity", "0")
- .remove()
- ;
- }
- // remove legend items with transition
- if (g.lgn.use) {
- g.lgn.items = d3.selectAll("#" + g.id + " .ldwlgnitems")
- .selectAll("g")
- .data(g.allDim2, function(d) { return d; })
- ;
- g.lgn.items
- .exit()
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity", "0")
- .remove()
- ;
- }
-
- // Add any new bars/deltas/legend items
- this.createBars();
-
- // Update bars
- if (g.orientation == "V") {
- g.bars.transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity","1")
- .attr("x", function(d, i) {
- return g.dScale(d.dim1) ? g.dScale(d.dim1) : 0; // ignore NaN: causing errors in transitions
- })
- .attr("y", function(d) {
- return g.mScale(d.offset) - (g.mScale(0) - g.mScale(d.qNum));
- })
- .attr("width", g.dScale.rangeBand() && g.dScale.rangeBand() > 0 ? g.dScale.rangeBand() : 0) // ignore NaN: causing errors in transitions
- .attr("height", function(d) {
- return g.mScale(0) > g.mScale(d.qNum) ? g.mScale(0) - g.mScale(d.qNum) : 0; // ignore negatives: causing errors in transitions
- })
- ;
- }
- else {
- g.bars.transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity","1")
- .attr("x", function(d) {
- return g.mScale(d.offset);
- })
- .attr("y", function(d, i) {
- return g.dScale(d.dim1);
- })
- .attr("width", function(d) {
- return g.mScale(d.qNum);
- })
- .attr("height", g.dScale.rangeBand())
- ;
- }
-
- if (~"TA".indexOf(g.showTexts) && !g.normalized) {
- // Update totals
- g.totals
- .each(function(d) {
- d.qNum = g.mScale.domain()[1] - d.offset;
- d.qText = d3.format([",.0f", ",.0%", ",.3s", g.totalFormatMs]["NPSC".indexOf(g.totalFormatM)])(d.offset);
- var txp = g.barText(d, true);
- d3.select(this)
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity","1")
- .style("fill", "black")
- .style("font-size", g.tref.style("font-size"))
- .attr({x: txp.x, y: txp.y, dy: "-.2em"})
- .text(txp.text)
- ;
-
- })
- ;
- }
-
- if (~"BA".indexOf(g.showTexts)) {
- // Update texts
- g.texts
- .each(function(d) {
- var txp = g.barText(d);
-
- d3.select(this)
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .style("opacity","1")
- .style("fill", g.textColor == "Auto" ? g.txtColor(g.cScale(d.dim2)) : g.textColor )
- .style("font-size", g.tref.style("font-size"))
- .attr({x: txp.x, y: txp.y, dy: "-.2em"})
- .text(txp.text)
- ;
-
- })
- ;
- }
-
- if (g.showDeltas && g.nDims == 2) {
-
- // update deltas
- g.polys.transition()
- .delay(tDelay)
- .duration(tDuration)
- .ease(g.ease)
- .attr("points",function(d) {
- var p;
- if (g.orientation == "V") {
- p = (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
- (g.mScale(d.points[0]) - (g.mScale(0) - g.mScale(d.points[2]))) + " " +
-
- (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
- g.mScale(d.points[0]) + " " +
-
- (g.dScale(d.dim1c)) + "," +
- g.mScale(d.points[1]) + " " +
-
- (g.dScale(d.dim1c)) + "," +
- (g.mScale(d.points[1]) - (g.mScale(0) - g.mScale(d.points[3])))
- ;
- }
- else {
- p = (g.mScale(d.points[0]) + g.mScale(d.points[2])) + "," +
- (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
-
- g.mScale(d.points[0]) + "," +
- (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
-
- g.mScale(d.points[1]) + "," +
- g.dScale(d.dim1c) + " " +
-
- (g.mScale(d.points[1]) + g.mScale(d.points[3])) + "," +
- g.dScale(d.dim1c)
- ;
- }
- return p;
- })
- .style("fill",function(d) {
- return g.cScale(d.dim2);
- })
- .style("opacity",g.barGap == 1 ? "1" : "0.5")
- ;
- }
- // update legend items
- if (g.lgn.use) {
-
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- var maxprow = Math.floor(g.lgn.width/(g.lgn.txtOff + g.lgn.txtWidth));
- var nprow = maxprow;
- }
-
- g.lgn.items
- .each(function(d, i) {
- d3.select(this)
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .select("rect")
- .attr("x",function(e) {
- var x;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- x = (i % nprow)*(g.lgn.txtOff + g.lgn.txtWidth);
- }
- else {
- x = g.lgn.pad;
- }
- return x;
- })
- .attr("y",function(e) {
- var y;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- y = g.lgn.pad + Math.floor(i/nprow) * g.lgn.itmHeight;
- }
- else {
- y = g.lgn.pad + g.lgn.itmHeight*i;
- }
- return y;
- })
- .style("opacity","1")
- ;
- var txt = d3.select(this)
- .transition()
- .delay(tDelay)
- .duration(tDuration)
- .select("text")
- .attr("x",function(e) {
- var x;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- x = (i % nprow)*(g.lgn.txtOff + g.lgn.txtWidth) + g.lgn.txtOff;
- }
- else {
- x = g.lgn.txtOff;
- }
- return x;
- })
- .attr("y",function(e) {
- var y;
- if (g.lgn.use == "T" || g.lgn.use == "B") {
- y = g.lgn.pad + Math.floor(i/nprow) * g.lgn.itmHeight + 11;
- }
- else {
- y = g.lgn.pad + g.lgn.itmHeight*i + 11;
- }
- return y;
- })
- .style("opacity","1")
- ;
- txt.each(function(d, i) {
- var self = d3.select(this),
- textLength = self.node().getComputedTextLength(),
- text = self.text();
- while (textLength > g.lgn.txtWidth && text.length > 0) {
- text = text.slice(0, -1);
- self.text(text + '\u2026');
- textLength = self.node().getComputedTextLength();
- }
- });
- ;
- })
- ;
- }
-},
-
-/**
- *--------------------------------------
- * Refresh chart
- *--------------------------------------
- * Refresh chart, no new data
-*/
-refreshChart: function() {
-
- this.initChart();
- this.createBars();
- this.updateBars();
-},
-//--------------------------------------
-// Topological sort
-//--------------------------------------
-/*- begin https://github.com/marcelklehr/toposort */
-toposort: function(nodes, edges) {
- var cursor = nodes.length
- , sorted = new Array(cursor)
- , visited = {}
- , i = cursor
-
- while (i--) {
- if (!visited[i]) visit(nodes[i], i, [])
- }
-
- return sorted
-
- function visit(node, i, predecessors) {
- if(predecessors.indexOf(node) >= 0) {
- throw new Error('Cyclic dependency: '+JSON.stringify(node))
- }
-
-// if (!~nodes.indexOf(node)) {
-// throw new Error('Found unknown node. Make sure to provided all involved nodes. Unknown node: '+JSON.stringify(node))
-// }
-
- if (visited[i]) return;
- visited[i] = true
-
- // outgoing edges
- var outgoing = edges.filter(function(edge){
- return edge[0] === node
- })
- if (i = outgoing.length) {
- var preds = predecessors.concat(node)
- do {
- var child = outgoing[--i][1]
- visit(child, nodes.indexOf(child), preds)
- } while (i)
- }
-
- sorted[--cursor] = node
- }
-},
-/*- end https://github.com/marcelklehr/toposort */
-/*- begin http://stackoverflow.com/questions/11867545 */
-txtColor: function(hexcolor){
- var r = parseInt(hexcolor.substr(1,2),16);
- var g = parseInt(hexcolor.substr(3,2),16);
- var b = parseInt(hexcolor.substr(5,2),16);
- var yiq = ((r*299)+(g*587)+(b*114))/1000;
- return (yiq >= 160) ? 'black' : 'white'; // 128 changed to 160 to give white preference
-},
-/*- end http://stackoverflow.com/questions/11867545 */
-//--------------------------------------
-// Color Schemes
-//--------------------------------------
-custom100: [
-"#99c867","#e43cd0","#e2402a","#66a8db","#3f1a20","#e5aa87","#3c6b59","#aa2a6b","#e9b02e","#7864dd",
-"#65e93c","#5ce4ba","#d0e0da","#d796dd","#64487b","#e4e72b","#6f7330","#932834","#ae6c7d","#986717",
-"#e3cb70","#408c1d","#dd325f","#533d1c","#2a3c54","#db7127","#72e3e2","#e2c1da","#d47555","#7d7f81",
-"#54ae9b","#e9daa6","#3a8855","#5be66e","#ab39a4","#a6e332","#6c469d","#e39e51","#4f1c42","#273c1c",
-"#aa972e","#8bb32a","#bdeca5","#63ec9b","#9c3519","#aaa484","#72256d","#4d749f","#9884df","#e590b8",
-"#44b62b","#ad5792","#c65dea","#e670ca","#e38783","#29312d","#6a2c1e","#d7b1aa","#b1e7c3","#cdc134",
-"#9ee764","#56b8ce","#2c6323","#65464a","#b1cfea","#3c7481","#3a4e96","#6493e1","#db5656","#747259",
-"#bbabe4","#e33f92","#d0607d","#759f79","#9d6b5e","#8574ae","#7e304c","#ad8fac","#4b77de","#647e17",
-"#b9c379","#8da8b0","#b972d9","#786279","#7ec07d","#916436","#2d274f","#dce680","#759748","#dae65a",
-"#459c49","#b7934a","#51c671","#9ead3f","#969a5c","#b9976a","#46531a","#c0f084","#76c146","#bad0ad"
-],
-google20: [
-"#3366cc","#dc3912","#ff9900","#109618","#990099","#0099c6","#dd4477","#66aa00","#b82e2e","#316395",
-"#994499","#22aa99","#aaaa11","#6633cc","#e67300","#8b0707","#651067","#329262","#5574a6","#3b3eac"
-],
-qlikView18: [
-"#8daacb","#fc7362","#bbd854","#ffd92f","#66c296","#e5b694","#e78ad2","#b3b3b3","#a6d8e3","#abe9bc",
-"#1b7d9c","#ffbfc9","#4da741","#c4b2d6","#b22424","#00acac","#be6c2c","#695496"
-],
-qlikSense12: [
-"#332288","#6699cc","#88ccee","#44aa99","#117733","#999933","#ddcc77","#661100","#cc6677","#aa4466",
-"#882255","#aa4499"
-]
+/**
+
+ barsPlus - Create D3 bar chart, stacked bar chart, area chart
+
+ Author: L. Woodside
+ Modification History:
+
+ Version Person Date Description
+ V1.0.0 L. Woodside 19-Dec-2016 Initial Release
+ V1.1.0 L. Woodside 29-Dec-2016 Added text on bars
+ V1.2.0 L. Woodside 07-Jan-2017 Allow multiple measures
+ V1.3.0 L. Woodside 15-Jan-2017 Improved color options
+ V1.3.1 L. Woodside 27-Jan-2017 Fix problem with legend properties
+
+ Dependencies: d3.v3.js
+
+ This script creates an object with the following methods:
+
+ initData() Initialize chart data
+ initChart() Initialize a bar chart
+ createBars() Create the bars in the chart
+ updateBars() modify the bar chart due to changes in data
+ refreshChart() refresh chart (calls three above excluding initData)
+
+ Presentation Properties
+
+ orientation Orientation: H - Horizontal, V - Vertical
+ normalized Whether 100% bars
+ showDeltas Whether to show bar connectors (delta quadrangles)
+ barGap Spacing between bars, 0 - no space, 1 - no bars (area graph)
+ outerGap Spacing before first bar and after last bar
+ gridHeight Height of grid relative to highest bar
+ backgroundColor Grid background color
+
+ Colors and Legend
+
+ colorSource A - Assigned, C - Calculated
+ colorAttr Attribute is: O - an offset in scheme, C - a color value
+ colorScheme Named color scheme
+ colorOffset Offset of first color in color scheme (colorSource = Assigned)
+ singleColor Whether to use single color for 1-dimensional bars
+ showLegend Whether to show the legend
+ legendPosition Legend position: T - top, R - right, B - bottom, L - left
+ legendSize Legend size: N - narrow, M - medium, W - wide
+ legendSpacing Legend spacing: N - narrow, M - medium, W - wide
+
+ Dimension Axis
+
+ axisTitleD Dimension axis title
+ labelTitleD Dimension axis: B - labels & title, L - labels only, T - titles only, N - none
+ labelStyleD Dimension style: A - Auto, H - horizontal, S - staggered, T - tilted
+ gridlinesD Dimension gridlines
+ axisMarginD Dimension margin size: N - narrow, M - medium, W - wide
+
+ Measure Axis
+
+ axisTitleM Measure axis title
+ labelTitleM Measure axis: B - labels & title, L - labels only, T - titles only, N - none
+ labelStyleM Measure style: A - Auto, H - horizontal, S - staggered, T - tilted
+ gridlinesM Measure gridlines
+ axisMarginM Measure margin size: N - narrow, M - medium, W - wide
+ ticks Recommended number of ticks
+ axisFormatM Number format for measure axis, A - Auto, N - Number, P - Percent, S - SI, C - Custom
+ axisFormatMs Number format string for measure axis, D3 format
+
+ Text on Bars
+
+ showTexts Whether to show text on bars: B - on bars, T - total, A - both, N - none
+ showDim What to show in bars: M - measure, D - dimension, P - percent
+ showTot What to show for total: M - measure, D - dimension
+ innerBarPadH Horizontal inner bar padding (px)
+ innerBarPadV Vertical inner bar padding (px)
+ textSizeAbs Whether to use textSize instead of textSizeFactor (horizontal bars only)
+ textSizeFactor Text size as proportion of bar height (horizontal bars only)
+ textSize Text size (px), when vertical bars or textSizeAbs = false
+ textDots Whether to show text at all if ellipsis would be shown
+ textColor "Auto" - Choose white or black depending on bar color, else text color string
+ vAlign Vertical alignment: C - center, T - top, B - bottom
+ hAlign Horizontal alignment: C - Center, L - left, R - right
+ totalFormatM Number format for total: N - Number, P - Percent, S - SI, C - Custom
+ totalFormatMs Number format string for total, D3 format
+
+ Transitions
+
+ transitions Whether to enable transitions
+ transitionDelay Delay before start of transition
+ transitionDuration Duration of transition
+ ease Transition style
+
+ Multiple measures
+
+ defDims Defined number of dimensions
+ defMeas Defined number of measures
+ measures Array of measure names
+
+ UI-determined Properties
+
+ id Unique id of enclosing element
+ component D3 selection of enclosing element
+ width Width of enclosing element
+ height Height of enclosing element
+ inSelections Whether selection mode is enabled in Qlik Sense
+ editMode Whether edit mode is enabled in Qlik Sense
+ selectionMode Selection mode: QUICK or CONFIRM
+ rawData Raw data from hypercube
+
+*/
+var ldwbarsPlus = {
+
+/**
+ *--------------------------------------
+ * Initialize Data
+ *--------------------------------------
+ * This method will take input QV data and format it for 1 or 2 dimensions
+ * Input: g.rawData
+ * Output: g.data
+ * g.flatData
+ * g.allDim2
+ * g.allCol2
+ * g.nDims
+ * g.deltas
+*/
+initData: function() {
+ /*
+ To support multiple measures, take the input raw data and transform it
+ to look like previously supported formats:
+ 0 Dimensions, 1 or more measures -> format as 1 dimension, 1 measure
+ 1 Dimension, 2 or more measures -> format as 2 dimensions, 1 measure
+ If two dimensions and multiple measures specified, ignore all but the first measure
+ */
+ var g = this;
+ var struc = [], flatData = [], q = [], r = [], deltas = [], inData = [];
+
+ if (!g.rawData[0]) return; // sometimes undefined
+ if (g.defDims + g.defMeas != g.rawData[0].length) return; // sometimes mismatched
+
+ if (g.defDims == 0) {
+ for (var i = 0; i < g.rawData.length; i++) {
+ for (var j = 0; j < g.measures.length; j++) {
+ inData.push([
+ {qElemNumber: -1, qNum: j+1, qText: g.measures[j]},
+ g.rawData[i][j]
+ ]);
+ }
+ }
+ }
+ else if (g.defDims == 1 && g.defMeas > 1) {
+ for (var i = 0; i < g.rawData.length; i++) {
+ for (var j = 0; j < g.measures.length; j++) {
+ inData.push([
+ g.rawData[i][0],
+ {qElemNumber: -1, qNum: j+1, qText: g.measures[j]},
+ g.rawData[i][j+1]
+ ]);
+ }
+ }
+ }
+ else {
+ inData = g.rawData; // Process as in previous version
+ }
+ // If calculated color specified, must have provided Attribute
+ // otherwise set back to assigned mode
+
+ var cx = g.defDims == 1 ? (g.defMeas == 1 ? 0 : 2) : 1; // index for attribute
+ if (g.colorSource == "C" && !inData[0][cx].qAttrExps) g.colorSource = "A";
+
+ // Function to get dimension/measure attribute for color
+ var cf = function(e) {
+ var cn = 0;
+ if (g.colorSource == "C") {
+ var cv = e[cx].qAttrExps.qValues[0].qNum;
+ if (!isNaN(cv))
+ cn = Math.floor(Math.abs(cv));
+ if (g.colorAttr == "C") {
+ // if text attribute exists, use it
+ if (e[cx].qAttrExps.qValues[0].qText) {
+ cn = e[cx].qAttrExps.qValues[0].qText;
+ }
+ else {
+ // number present, convert to hex
+ cv = cv.toString(16);
+ if (cv.length > 6) cv = cv.substring(cv.length-6);
+ cn = "#" + "000000".substring(0,6-cv.length) + cv;
+ }
+ }
+ }
+ return cn;
+ }
+
+ // Process one dimension data
+ if (inData[0].length == 2) {
+ g.nDims = 1;
+ g.normalized = false;
+ inData.forEach(function(d) {
+ struc.push({dim1: d[0].qText, offset: d[1].qNum});
+ flatData.push({
+ dim1: d[0].qText,
+ dim2: d[0].qText,
+ offset: 0,
+ qNum: d[1].qNum,
+ qText: d[1].qText,
+ qTextPct: "",
+ qElemNumber: d[0].qElemNumber
+ });
+ if(q.indexOf(d[0].qText) == -1) {
+ q.push(d[0].qText);
+ r.push(cf(d));
+ }
+ });
+ g.data = struc;
+ g.flatData = flatData;
+ g.allDim2 = q;
+ g.allCol2 = r;
+ return;
+ };
+
+ // Process two dimensional data
+ g.nDims = 2;
+
+ var p1 = "", p2, edges = [], b, p = [];
+ inData.forEach(function(d) {
+ var c2 = d[1].qText;
+ if (p.indexOf(d[0].qText) == -1) {
+ p.push(d[0].qText);
+ }
+ if (q.indexOf(d[1].qText) == -1) {
+ q.push(d[1].qText);
+ r.push(cf(d));
+ }
+ if (d[0].qText != p1) {
+ p1 = d[0].qText;
+ }
+ else {
+ b = false;
+ for (var i = 0; i < edges.length; i++) {
+ if (edges[i][0] == p2 && edges[i][1] == c2) {
+ b = true;
+ break;
+ }
+ }
+ if (!b) {
+ edges.push([p2,c2])
+ }
+ }
+ p2 = c2;
+ });
+ // Topological sort will throw an error if inconsistent data (sorting by measure)
+ // Just ignore errors and use original sort order
+ var qs, ps, rs = [];
+ try {
+ ps = q.slice();
+ qs = this.toposort(q,edges);
+ // Replicate qs order in r
+ for (var i = 0; i < ps.length; i++) {
+ rs.push(r[ps.indexOf(qs[i])]);
+ }
+ r = rs;
+ }
+ catch(err) {
+ qs = q;
+ }
+ q = qs;
+
+ var n = d3.nest()
+ .key(function(d) {return d[0].qText})
+ .key(function(d) {return d[1].qText})
+ .entries(inData)
+ ;
+ // sort all nodes in order specified by q
+ n.forEach(function(d) {
+ d.values.sort(function(a,b) {
+ return ( q.indexOf(a.key) < q.indexOf(b.key) ? -1
+ : ( q.indexOf(a.key) > q.indexOf(b.key) ? 1 : 0));
+ });
+ });
+ // nest messes up dim1 sort order, sort by order specified in p
+ n.sort(function(a,b) {
+ return ( p.indexOf(a.key) < p.indexOf(b.key) ? -1
+ : ( p.indexOf(a.key) > p.indexOf(b.key) ? 1 : 0));
+ });
+ n.forEach(function(d, idx) {
+ var t = 0, v = [], j = 0, num, txt;
+ for (var i = 0; i < q.length; i++) {
+ if (d.values.length <= j || d.values[j].key != q[i]) {
+ num = 0;
+ txt = "-";
+ elm = [];
+ }
+ else {
+ num = d.values[j].values[0][2].qNum;
+ txt = d.values[j].values[0][2].qText;
+ elm = d.values[j].values[0][0].qElemNumber;
+ j++;
+ }
+ v.push({
+ dim2: q[i],
+ qNum: num,
+ qText: txt,
+ qElemNumber: elm,
+ offset: t
+ });
+ t += num;
+ };
+ v.forEach(function(e) {
+ e.dim1 = d.key;
+ if (g.normalized) {
+ e.offset = e.offset/t;
+ e.qNum = e.qNum/t;
+ e.qTextPct = d3.format(".1%")(e.qNum);
+ }
+ });
+ flatData.push.apply(flatData,v);
+ struc.push({dim1: d.key, offset: t, values: v});
+
+ if (idx > 0 && g.showDeltas) {
+ var p = struc[idx-1].values;
+ var c = struc[idx].values;
+ for (var k = 0; k < p.length; k++) {
+ deltas.push({
+ dim1p: p[k].dim1,
+ dim1c: c[k].dim1,
+ dim2: p[k].dim2,
+ delta: c[k].qNum - p[k].qNum,
+ deltaPct: 0,
+ points: [
+ p[k].offset,
+ c[k].offset,
+ p[k].qNum,
+ c[k].qNum
+ ]
+ });
+ }
+ }
+
+ });
+ g.data = struc;
+ g.flatData = flatData;
+ g.allDim2 = q;
+ g.allCol2 = r;
+ g.deltas = deltas;
+},
+
+/**
+ *--------------------------------------
+ * Initialize Chart
+ *--------------------------------------
+ * Set up initial elements, create axes, create legend
+ * create bars, deltas and legend items and bind data
+*/
+initChart: function() {
+
+ var g = this;
+
+ var xLabelTitle = g.orientation == "V" ? g.labelTitleD : g.labelTitleM;
+ g.xAxisHeight = xLabelTitle == "B" || xLabelTitle == "L"
+ ? [70,40,25]["WMN".indexOf(g.orientation == "V" ? g.axisMarginD : g.axisMarginM)] : 0;
+ var xTitleHeight = xLabelTitle == "B" || xLabelTitle == "T" ? 20 : 0;
+ var xAxisPad = 20;
+
+ var yLabelTitle = g.orientation == "V" ? g.labelTitleM : g.labelTitleD;
+ g.yAxisWidth = yLabelTitle == "B" || yLabelTitle == "L"
+ ? [90,50,30]["WMN".indexOf(g.orientation == "V" ? g.axisMarginM : g.axisMarginD)] : 0;
+ var yTitleWidth = yLabelTitle == "B" || yLabelTitle == "T" ? 20 : 0;
+ var yAxisPad = 20;
+
+ var tr; // translate string
+ var dTitleHeight = g.labelTitleD == "B" || g.labelTitleD == "T" ? 20 : 0;
+ var margin = {
+ top: 10, //yAxisPad,
+ right: xAxisPad,
+ bottom: g.xAxisHeight + xTitleHeight + xAxisPad,
+ left: g.yAxisWidth + yTitleWidth + yAxisPad
+ };
+ var innerWidth = g.width - margin.left - margin.right;
+ var innerHeight = g.height - margin.top - margin.bottom;
+
+ g.lgn = {
+ minDim : [200,100], // min inner dimensions for legend to be displayed
+ use : "",
+ pad : 0,
+ sep : 5,
+ box : [12,12], // legend item color box
+ itmHeight : 20,
+ }
+ g.lgn.txtOff = g.lgn.box[0] + g.lgn.pad + g.lgn.sep;
+
+ // adjust for legend if any
+ g.lgn.use = g.showLegend ? g.legendPosition : "";
+ if (g.lgn.use) {
+ if (g.lgn.use == "L" || g.lgn.use == "R") {
+ if (innerWidth <= g.lgn.minDim[0]) {
+ g.lgn.use = "";
+ }
+ else {
+ g.lgn.width = innerWidth/([4,6,10]["WMN".indexOf(g.legendSize)]);
+ innerWidth -= (g.lgn.width + yAxisPad);
+ g.lgn.height = innerHeight + g.xAxisHeight + xTitleHeight;
+ g.lgn.y = margin.top;
+ g.lgn.txtWidth = g.lgn.width - g.lgn.pad - g.lgn.sep - g.lgn.box[0];
+ if (g.lgn.use == "L") {
+ g.lgn.x = yAxisPad;
+ margin.left += g.lgn.width + g.lgn.x;
+ }
+ else {
+ g.lgn.x = margin.left + innerWidth + yAxisPad;
+ }
+ }
+ }
+ else if (g.lgn.use == "T" || g.lgn.use == "B") {
+ if (innerHeight <= g.lgn.minDim[1]) {
+ g.lgn.use = "";
+ }
+ else {
+ g.lgn.width = innerWidth + g.yAxisWidth + yTitleWidth;
+ g.lgn.height = g.lgn.itmHeight * (3 - "WMN".indexOf(g.legendSize));
+ innerHeight -= g.lgn.height;
+ g.lgn.x = yAxisPad;
+ g.lgn.txtWidth = [100,75,50]["WMN".indexOf(g.legendSpacing)];
+ if (g.lgn.use == "T") {
+ g.lgn.y = margin.top;
+ margin.top += g.lgn.height
+ }
+ else {
+ g.lgn.y = margin.bottom + innerHeight;
+ innerHeight -= 10;
+ }
+ }
+ }
+ }
+ g.component.selectAll("*")
+ .remove()
+ ;
+ var tooltip = g.component.append("div")
+ .attr("class","ldwtooltip")
+ .style("opacity","0")
+ ;
+ tooltip.append("p")
+ .attr("class","ldwttheading")
+ ;
+ tooltip.append("p")
+ .attr("class","ldwttvalue")
+ ;
+ g.svg = g.component
+ .append("svg")
+ .attr("width",g.width)
+ .attr("height",g.height)
+ .style("background-color",g.backgroundColor)
+ .append("g")
+ .attr("transform","translate(" + margin.left + "," + margin.top + ")")
+ ;
+ var dim1 = g.data.map(function(d) { return d.dim1; });
+ if (g.orientation == "H") dim1.reverse()
+ g.dScale = d3.scale.ordinal()
+ .domain(dim1)
+ .rangeRoundBands(g.orientation == "V" ? [0, innerWidth] : [innerHeight,0], g.barGap, g.outerGap)
+ ;
+ g.mScale = d3.scale.linear()
+ .domain([0, d3.max(g.data, function(d) { return (g.normalized ? 1 : d.offset)*g.gridHeight; })])
+ .range(g.orientation == "V" ? [innerHeight,0] : [0, innerWidth])
+ .nice()
+ ;
+ var dGrp = g.svg.append("g")
+ .attr("class", "ldw-d ldwaxis");
+ if (g.orientation == "V") {
+ dGrp.attr("transform", "translate(0," + innerHeight + ")");
+ }
+ if (g.labelTitleD == 'B' || g.labelTitleD == 'L') {
+ g.dAxis = d3.svg.axis()
+ .scale(g.dScale)
+ .orient(g.orientation == "V" ? "bottom" : "left")
+ .tickSize(g.gridlinesD ? (g.orientation == "V" ? -innerHeight : -innerWidth) : 6)
+ .tickPadding(5)
+ ;
+ dGrp.call(g.dAxis);
+ }
+ if (g.labelTitleD == 'B' || g.labelTitleD == 'T') {
+ if(g.orientation == "V") {
+ tr = "translate(" + (innerWidth/2) + "," + (g.xAxisHeight + xTitleHeight) + ")";
+ }
+ else {
+ tr = "translate(-" + (g.yAxisWidth + yTitleWidth/2 + 2) + "," + (innerHeight/2) + ")rotate(-90)";
+ }
+ dGrp.append("text")
+ .attr("class","axisTitle")
+ .attr("text-anchor","middle")
+ .attr("transform",tr)
+ .text(g.axisTitleD)
+ ;
+ }
+ var mGrp = g.svg.append("g")
+ .attr("class", "ldw-m ldwaxis")
+ ;
+ if (g.orientation != "V") {
+ mGrp.attr("transform", "translate(0," + innerHeight + ")");
+ }
+ if (g.labelTitleM == 'B' || g.labelTitleM == 'L') {
+ g.mAxis = d3.svg.axis()
+ .scale(g.mScale)
+ .orient(g.orientation == "V" ? "left" : "bottom")
+ .tickSize(g.gridlinesM ? (g.orientation == "V" ? -innerWidth : -innerHeight) : 6)
+ .ticks(g.ticks)
+ .tickFormat(d3.format([",.3s", ",.0f", ",.0%", ",.3s", g.axisFormatMs]["ANPSC".indexOf(g.axisFormatM)]))
+ .tickPadding(5)
+ ;
+ mGrp.call(g.mAxis);
+ }
+ if (g.labelTitleM == 'B' || g.labelTitleM == 'T') {
+ if(g.orientation == "V") {
+ tr = "translate(-" + (g.yAxisWidth + yTitleWidth/2 + 2) + "," + (innerHeight/2) + ")rotate(-90)";
+ }
+ else {
+ tr = "translate(" + (innerWidth/2) + "," + (g.xAxisHeight + xTitleHeight) + ")";
+ }
+ mGrp.append("text")
+ .attr("class","axisTitle")
+ .attr("text-anchor","middle")
+ .attr("transform",tr)
+ .text(g.axisTitleM)
+ ;
+ }
+ if (Object.getPrototypeOf(this).hasOwnProperty(g.colorScheme))
+ ca = this[g.colorScheme];
+ else
+ ca = d3.scale[g.colorScheme]().range();
+ g.colorOffset %= ca.length;
+ // define various ways to get color
+ if (g.nDims == 1 && g.colorSource != "C" && g.singleColor) {
+ var rc = ca[g.colorOffset];
+ g.cScale = function(d) { return rc; };
+ }
+ else if (g.colorSource == "C") {
+ if (g.colorAttr == "O") { // color from calculated offset
+ g.cScale = function(d) {
+ return ca[g.allCol2[g.allDim2.indexOf(d)] % ca.length];
+ }
+ }
+ else { // color is direct value
+ g.cScale = function(d) {
+ return g.allCol2[g.allDim2.indexOf(d)];
+ }
+ }
+ }
+ else {
+ g.cScale = d3.scale.ordinal().range(
+ ca.slice(g.colorOffset,ca.length).concat(ca.slice(0,ca.length))
+ ).domain(g.allDim2);
+ }
+
+ // Create Legend
+ if (g.lgn.use) {
+ var lgn = d3.select("#" + g.id + " svg")
+ .append("g")
+ .attr("class","ldwlegend")
+ .attr("clip-path","url(#" + g.id + "_lgnClip)")
+ .attr("transform","translate(" + g.lgn.x + "," + g.lgn.y + ")")
+ ;
+ lgn.append("rect")
+ .attr("x",0)
+ .attr("y",0)
+ .attr("width",g.lgn.width)
+ .attr("height",g.lgn.height)
+ .style("fill",g.backgroundColor)
+ ;
+ lgn.append("clipPath")
+ .attr("id",g.id + "_lgnClip")
+ .append("rect")
+ .attr("x","0")
+ .attr("y","0")
+ .attr("width",g.lgn.width)
+ .attr("height",g.lgn.height)
+ ;
+ lgn.append("g")
+ .attr("class","ldwlgnitems")
+ }
+ // Create bars
+ g.bars = g.svg.selectAll("#" + g.id + " .ldwbar")
+ .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
+ ;
+ // Text on bars
+ if (g.showTexts != "N") {
+ // Create text box for determining sizing
+ g.tref = g.svg.append("text")
+ .attr("x","0")
+ .attr("y","-100")
+ .attr("class","ldwtxtref")
+ ;
+
+ if (~"TA".indexOf(g.showTexts) && !g.normalized) {
+ // Create bars totals
+ g.totals = g.svg.selectAll("#" + g.id + " .ldwtot")
+ .data(g.data, function(d) { return d.dim1; } )
+ ;
+ }
+ if (~"BA".indexOf(g.showTexts)) {
+ // Create text on bars
+ g.texts = g.svg.selectAll("#" + g.id + " .ldwtxt")
+ .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
+ ;
+ }
+ }
+ // Create deltas
+ if (g.showDeltas && g.nDims == 2) {
+ g.polys = g.svg.selectAll("#" + g.id + " polygon")
+ .data(g.deltas, function(d) { return d.dim1p + "-" + d.dim1c + "," + d.dim2; } )
+ ;
+ }
+ // Create legend items
+ if (g.lgn.use) {
+ g.lgn.items = d3.select("#" + g.id + " .ldwlgnitems")
+ .selectAll("g")
+ .data(g.allDim2)
+ ;
+ }
+
+},
+
+/**
+ *--------------------------------------
+ * Create Bars
+ *--------------------------------------
+ * Set up initial properties of bars, deltas, and legend
+ * Objects already have had data bound
+ * This procedure is also called from updateBars to add new items
+*/
+createBars: function() {
+
+ var g = this;
+ // Create bars
+ g.bars
+ .enter()
+ .append("rect")
+ .attr("ldwdim1", function(d) { return d.qElemNumber; })
+ .attr(g.orientation == "V" ? "x" : "y", function(d) { return g.dScale(d.dim1); })
+ .attr(g.orientation == "V" ? "y" : "x", function(d) { return g.mScale(0); }) // grow from bottom
+// .attr(g.orientation == "V" ? "y" : "x", function(d) { return g.mScale(d.offset); }) // venetian blinds
+ .attr(g.orientation == "V" ? "width" : "height", g.dScale.rangeBand())
+ .attr(g.orientation == "V" ? "height" : "width", function(d) { return 0; })
+ .style("fill", function(d) { return g.cScale(d.dim2); })
+ .style("opacity","0")
+ .attr("class","selectable ldwbar")
+ .on("click",function(d) {
+ if (d.qElemNumber >= 0) { // Cannot select a measure
+ if (g.selectionMode == "QUICK") {
+ g.self.backendApi.selectValues(0,[d.qElemNumber],true);
+ }
+ else if (g.selectionMode == "CONFIRM") {
+ var t = d3.select(this).classed("selected");
+ g.self.selectValues(0,[d.qElemNumber],true);
+ // following to address QS bug where clear button does not clear class names
+ g.self.clearSelectedValues = function() {
+ d3.selectAll("#" + g.id +" .selected").classed("selected",false);
+ };
+ var x = d3.selectAll("#" + g.id + " [ldwdim1='" + d.qElemNumber + "']")
+ .classed("selected",!t);
+ d3.select("#" + g.id + " .ldwtooltip")
+ .style("opacity","0")
+ .transition()
+ .remove
+ ;
+ }
+ }
+ return;
+ })
+ .on("mouseenter",function(d) {
+ if (g.inSelections || g.editMode) return;
+ d3.select(this)
+ .style("opacity","0.5")
+ .attr("stroke","white")
+ .attr("stroke-width","2")
+ ;
+ // Place text in tooltip
+ d3.select("#" + g.id + " .ldwttheading")
+ .text(g.nDims == 2 ? d.dim1 + ", " + d.dim2 : d.dim1);
+ d3.select("#" + g.id + " .ldwttvalue")
+ .text(g.nDims == 2 ?
+ (g.normalized ? d.qTextPct + ", " + d.qText : d.qText)
+ : d.qText);
+
+ var matrix = this.getScreenCTM()
+ .translate(+this.getAttribute("x"),+this.getAttribute("y"));
+
+ var xPosition = (window.pageXOffset + matrix.e)
+ - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientWidth/2
+ + (g.orientation == "V" ? g.dScale.rangeBand() : d3.select(this).attr("width"))/2
+ ;
+ var yPosition = (window.pageYOffset + matrix.f)
+ - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientHeight
+ -10
+ ;
+ d3.select("#" + g.id + " .ldwtooltip")
+ .style("left", xPosition + "px")
+ .style("top", yPosition + "px")
+ .transition()
+ .delay(750)
+ .style("opacity","0.95")
+ ;
+ })
+ .on("mouseleave",function() {
+ d3.select(this)
+ .style("opacity","1.0")
+ .attr("stroke","none")
+ ;
+ d3.select("#" + g.id + " .ldwtooltip")
+ .style("opacity","0")
+ .transition()
+ .remove
+ ;
+ })
+ ;
+
+ if (~"TA".indexOf(g.showTexts) && !g.normalized) {
+ // Create totals
+ g.totals
+ .enter()
+ .append("text")
+ .attr("class","ldwtot")
+ .style("opacity","0")
+ .each(function(d) {
+ d.qNum = g.mScale.domain()[1] - d.offset;
+ d.qText = d3.format([",.0f", ",.0%", ",.3s", g.totalFormatMs]["NPSC".indexOf(g.totalFormatM)])(d.offset);
+ var txp = g.barText(d, true);
+
+ d3.select(this)
+ .style("fill", "black")
+ .style("font-size", g.tref.style("font-size"))
+ .attr("x",g.orientation == "V" ? txp.x : 0)
+ .attr("y",g.orientation == "V" ? g.mScale(0) : txp.y)
+ .attr("dy","-.2em")
+ .text(txp.text)
+ ;
+
+ })
+ ;
+ }
+
+ if (~"BA".indexOf(g.showTexts)) {
+ // Create text inside bars
+ g.texts
+ .enter()
+ .append("text")
+ .attr("class","ldwtxt")
+ .style("opacity","0")
+ .each(function(d) {
+ var txp = g.barText(d);
+
+ d3.select(this)
+ .style("fill", g.textColor == "Auto" ? g.txtColor(g.cScale(d.dim2)) : g.textColor )
+ .style("font-size", g.tref.style("font-size"))
+ .attr("x",g.orientation == "V" ? txp.x : 0)
+ .attr("y",g.orientation == "V" ? g.mScale(0) : txp.y)
+ .attr("dy","-.2em")
+ .text(txp.text)
+ ;
+
+ })
+ ;
+ }
+
+ if (g.showDeltas && g.nDims == 2) {
+
+ // Create deltas
+ g.polys
+ .enter()
+ .append("polygon")
+ .attr("points",function(d) {
+ var p;
+ if (g.orientation == "V") {
+ p = (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
+ g.mScale(0) + " " +
+ (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
+ g.mScale(0) + " " +
+ (g.dScale(d.dim1c)) + "," +
+ g.mScale(0) + " " +
+ (g.dScale(d.dim1c)) + "," +
+ g.mScale(0)
+ ;
+ }
+ else {
+ p = g.mScale(0) + "," + (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
+ g.mScale(0) + "," + (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
+ g.mScale(0) + "," + g.dScale(d.dim1c) + " " +
+ g.mScale(0) + "," + g.dScale(d.dim1c)
+ ;
+ }
+ return p;
+ })
+ .style("fill",function(d) {
+ return g.cScale(d.dim2);
+ })
+ .style("opacity","0")
+ .on("mouseenter",function(d) {
+ var pt = this.getAttribute("points").split(" ");
+ var sx = 0, sy = 0;
+ pt.forEach(function(e, i) {
+ var x = e.split(",");
+ if (g.orientation == "H") {
+ if (i < 2) {
+ sx += parseFloat(x[0]);
+ sy += parseFloat(x[1]);
+ }
+ }
+ else if (i == 0 || i == 3) {
+ sx += parseFloat(x[0]);
+ sy += parseFloat(x[1]);
+ }
+ } );
+ sx /= 2;
+ sy /= 2;
+
+ if (g.inSelections || g.editMode) return;
+
+ d3.select(this)
+ .style("opacity","0.5")
+ .attr("stroke","white")
+ .attr("stroke-width","2");
+ // Place text in tooltip
+ d3.select("#" + g.id + " .ldwttheading")
+ .text(d.dim2 + ", " + d.dim1p + "-" + d.dim1c);
+ d3.select("#" + g.id + " .ldwttvalue")
+ .text(d3.format(g.normalized ? "+.1%" : "+.3s")(d.delta));
+
+ var matrix = this.getScreenCTM()
+ .translate(sx,sy);
+
+ var xPosition = (window.pageXOffset + matrix.e)
+ - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientWidth/2
+ ;
+ var yPosition = (window.pageYOffset + matrix.f)
+ - d3.select("#" + g.id + " .ldwtooltip")[0][0].clientHeight
+ -10
+ ;
+ d3.select("#" + g.id + " .ldwtooltip")
+ .style("left", xPosition + "px")
+ .style("top", yPosition + "px")
+ .transition()
+ .delay(750)
+ .style("opacity","0.95")
+ ;
+ })
+ .on("mouseleave",function() {
+ d3.select(this)
+ .style("opacity",g.barGap == 1 ? "1" : "0.5")
+ .attr("stroke","none")
+ ;
+ d3.select("#" + g.id + " .ldwtooltip")
+ .style("opacity","0")
+ .transition()
+ .remove;
+ })
+ ;
+ }
+ // create legend
+ if (g.lgn.use) {
+ g.lgn.items
+ .enter()
+ .append("g")
+ .attr("class","ldwlgnitem")
+ .each(function(d, i) {
+ d3.select(this)
+ .append("rect")
+// .attr("x","0") // Initialize to zero to have legend grow from top
+// .attr("y","0")
+ .attr("x",function(e) {
+ var x;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ x = i*(g.lgn.txtOff + g.lgn.txtWidth);
+ }
+ else {
+ x = g.lgn.pad;
+ }
+ return x;
+ })
+ .attr("y",function(e) {
+ var y;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ y = g.lgn.pad;
+ }
+ else {
+ y = g.lgn.pad + g.lgn.itmHeight*i;
+ }
+ return y;
+ })
+ .style("opacity","0")
+ .attr("width",g.lgn.box[0])
+ .attr("height",g.lgn.box[1])
+ .style("fill",function(e) { return g.cScale(e); })
+ ;
+ d3.select(this)
+ .append("text")
+// .attr("x","0") // Initialize to zero to have legend grow from top
+// .attr("y","0")
+ .attr("x",function(e) {
+ var x;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ x = i*(g.lgn.txtOff + g.lgn.txtWidth) + g.lgn.txtOff;
+ }
+ else {
+ x = g.lgn.txtOff;
+ }
+ return x;
+ })
+ .attr("y",function(e) {
+ var y;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ y = g.lgn.pad + 11;
+ }
+ else {
+ y = g.lgn.pad + g.lgn.itmHeight*i + 11;
+ }
+ return y;
+ })
+ .style("opacity","0")
+ .text(function(e) {
+ return e;
+ })
+ ;
+ })
+ ;
+ }
+},
+/**
+ *--------------------------------------
+ * Bar Text
+ *--------------------------------------
+ * Get bar text information: x, y and text
+*/
+barText: function(d, total) {
+ var tx, txt, origX, bHeight, textX, bb, textY, textLength, ts;
+ var g = this;
+
+ // Relative text sizing, relative to bar width
+ // For total, make larger by reducing unneeded padding
+ var hAlign = g.hAlign, vAlign = g.vAlign,
+ innerBarPadV = +g.innerBarPadV,
+ innerBarPadH = +g.innerBarPadH;
+ if (g.textSizeAbs)
+ ts = g.textSize;
+ else {
+ if (total)
+ if (g.orientation == "H")
+ innerBarPadV = 1;
+ else
+ innerBarPadH = 1;
+ ts = g.textSizeFactor * (g.dScale.rangeBand() - 2 * (g.orientation == "H" ? innerBarPadV : innerBarPadH))
+ // limit size to maximum, also stored in textSize
+ if (ts > g.textSize) ts = g.textSize;
+ }
+ g.tref.style("font-size", ts);
+
+ if (total) { // Override some alignment for total
+ if (g.orientation == "V")
+ vAlign = "B"
+ else
+ hAlign = "L"
+ }
+ origX = g.orientation == "V" ? g.dScale(d.dim1) : g.mScale(d.offset);
+ bHeight = g.orientation == "V" ? g.mScale(0) - g.mScale(d.qNum) : g.dScale.rangeBand();
+ textX = g.orientation == "V" ? g.dScale.rangeBand() : g.mScale(d.qNum);
+
+ g.tref.text(d.qNum == 0 ? ""
+ : (total
+ ? (g.showTot == "D" ? d.dim1 : d.qText)
+ : (g.showDim == "D" ? d.dim2 : (g.showDim == "P" && g.normalized ? d.qTextPct : d.qText))
+ )
+ );
+
+ bb = g.tref.node().getBBox();
+ tx = origX + innerBarPadH;
+ if (vAlign == "C")
+ textY = g.orientation == "V" ? g.mScale(d.offset) - (g.mScale(0) - g.mScale(d.qNum)) +
+ (g.mScale(0) - g.mScale(d.qNum) + bb.height)/2
+ : g.dScale(d.dim1) + (g.dScale.rangeBand() + bb.height)/2;
+ else if (vAlign == "T")
+ textY = g.orientation == "V" ? g.mScale(d.offset) - (g.mScale(0) - g.mScale(d.qNum)) +
+ bb.height + innerBarPadV
+ : g.dScale(d.dim1) + innerBarPadV + bb.height;
+ else
+ textY = g.orientation == "V" ? g.mScale(d.offset) - innerBarPadV
+ : g.dScale(d.dim1) + g.dScale.rangeBand() - innerBarPadV;
+ txt = "";
+ if (bb.height + 2 * innerBarPadV <= bHeight || (g.orientation != "V" && !g.textSizeAbs)) {
+ if (bb.width + 2 * innerBarPadH <= textX) {
+ if (hAlign == "C") {
+ tx = origX + (textX - bb.width)/2;
+ }
+ else if (hAlign == "R") {
+ tx = origX + textX - bb.width - innerBarPadH;
+ }
+ txt = g.tref.text();
+ }
+ else if (g.textDots) {
+ textLength = g.tref.node().getComputedTextLength();
+ txt = g.tref.text();
+ while (textLength > textX - 2 * innerBarPadH && txt.length > 0) {
+ txt = txt.slice(0, -1);
+ g.tref.text(txt + '\u2026');
+ textLength = g.tref.node().getComputedTextLength();
+ }
+ if (txt.length != 0) txt = g.tref.text();
+ }
+ }
+ return {x: tx, y: textY, text: txt};
+
+},
+/**
+ *--------------------------------------
+ * Update Bars
+ *--------------------------------------
+ * Modify properties of bars, deltas, and legend
+*/
+updateBars: function() {
+
+ var g = this;
+
+ var dim1 = g.data.map(function(d) { return d.dim1; });
+ if (g.orientation == "H") dim1.reverse()
+ g.dScale.domain(dim1);
+ g.mScale.domain( [0, d3.max(g.data, function(d) { return (g.normalized ? 1 : d.offset)*g.gridHeight; })]);
+ var tDelay = g.transitions && !g.editMode ? g.transitionDelay : 0;
+ var tDuration = g.transitions && !g.editMode ? g.transitionDuration : 0;
+
+ // Procedure to update dimension and measure axes
+ var updateAxis = function (labelTitle, labelStyle, axis, axisClass, isXAxis, axisWidth) {
+
+ if (labelTitle == 'B' || labelTitle == 'L') {
+ // Update axis with transition
+ g.svg.select("#" + g.id + " ." + axisClass + ".ldwaxis")
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .call(axis)
+ ;
+ var lbl = d3.selectAll("#" + g.id + " ." + axisClass + ".ldwaxis");
+ var txt = lbl.selectAll(".tick.major text")
+ .attr("transform",null); // All horizontal initially
+ var maxWidth = isXAxis ? lbl.node().getBBox().width/txt[0].length : g.yAxisWidth - 5;
+
+ // If auto labels and any overlap, set to tilted
+ if (labelStyle == "A") {
+ txt.each(function(d, i) {
+ if (d3.select(this).node().getComputedTextLength() > maxWidth) {
+ labelStyle = "T" // no break for each
+ }
+ })
+ ;
+ }
+ // Tilted labels
+ if (labelStyle == "T") {
+ txt.style("text-anchor","end")
+ .attr("transform","translate(" + (isXAxis ? "-12,0" : "-2,-8") + ") rotate(-45)")
+ ;
+ maxWidth = isXAxis ? g.xAxisHeight - 5 : maxWidth * Math.sqrt(2);
+ }
+ // Staggered labels
+ else if (labelStyle == "S" && isXAxis) {
+ txt.each(function(d, i) {
+ if (i % 2 == 1) {
+ d3.select(this).attr("transform","translate(0,14)");
+ }
+ })
+ ;
+ }
+ // Horizontal or titled labels, use ellipsis if overlap
+ if (labelStyle == "H" || labelStyle == "T") {
+ txt.each(function(d, i) {
+ var self = d3.select(this),
+ textLength = self.node().getComputedTextLength(),
+ text = self.text();
+ while (textLength > maxWidth && text.length > 0) {
+ text = text.slice(0, -1);
+ self.text(text + '\u2026');
+ textLength = self.node().getComputedTextLength();
+ }
+ });
+ }
+ }
+ };
+ // Update dimension axis
+ updateAxis(g.labelTitleD, g.labelStyleD, g.dAxis, "ldw-d", g.orientation == "V");
+ // Update measure axis
+ updateAxis(g.labelTitleM, g.labelStyleM, g.mAxis, "ldw-m", g.orientation != "V");
+
+ g.bars = g.svg.selectAll("#" + g.id + " .ldwbar")
+ .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
+ ;
+ // Remove bars with transition
+ g.bars
+ .exit()
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity", "0")
+ .remove()
+ ;
+
+ // Remove totals with transition
+ if (~"TA".indexOf(g.showTexts)) {
+ g.totals = g.svg.selectAll("#" + g.id + " .ldwtot")
+ .data(g.data, function(d) { return d.dim1; } )
+ ;
+ g.totals
+ .exit()
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity", "0")
+ .remove()
+ ;
+ }
+ // Remove texts with transition
+ if (~"BA".indexOf(g.showTexts)) {
+ g.texts = g.svg.selectAll("#" + g.id + " .ldwtxt")
+ .data(g.flatData, function(d) { return d.dim1 + '|' + d.dim2; } )
+ ;
+ g.texts
+ .exit()
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity", "0")
+ .remove()
+ ;
+ }
+
+ if (g.showDeltas && g.nDims == 2) {
+ g.polys = g.svg.selectAll("#" + g.id + " polygon")
+ .data(g.deltas, function(d) { return d.dim1p + "-" + d.dim1c + "," + d.dim2; } )
+ ;
+ // Remove deltas with transition
+ g.polys
+ .exit()
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity", "0")
+ .remove()
+ ;
+ }
+ // remove legend items with transition
+ if (g.lgn.use) {
+ g.lgn.items = d3.selectAll("#" + g.id + " .ldwlgnitems")
+ .selectAll("g")
+ .data(g.allDim2, function(d) { return d; })
+ ;
+ g.lgn.items
+ .exit()
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity", "0")
+ .remove()
+ ;
+ }
+
+ // Add any new bars/deltas/legend items
+ this.createBars();
+
+ // Update bars
+ if (g.orientation == "V") {
+ g.bars.transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity","1")
+ .attr("x", function(d, i) {
+ return g.dScale(d.dim1) ? g.dScale(d.dim1) : 0; // ignore NaN: causing errors in transitions
+ })
+ .attr("y", function(d) {
+ return g.mScale(d.offset) - (g.mScale(0) - g.mScale(d.qNum));
+ })
+ .attr("width", g.dScale.rangeBand() && g.dScale.rangeBand() > 0 ? g.dScale.rangeBand() : 0) // ignore NaN: causing errors in transitions
+ .attr("height", function(d) {
+ return g.mScale(0) > g.mScale(d.qNum) ? g.mScale(0) - g.mScale(d.qNum) : 0; // ignore negatives: causing errors in transitions
+ })
+ ;
+ }
+ else {
+ g.bars.transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity","1")
+ .attr("x", function(d) {
+ return g.mScale(d.offset);
+ })
+ .attr("y", function(d, i) {
+ return g.dScale(d.dim1);
+ })
+ .attr("width", function(d) {
+ return g.mScale(d.qNum);
+ })
+ .attr("height", g.dScale.rangeBand())
+ ;
+ }
+
+ if (~"TA".indexOf(g.showTexts) && !g.normalized) {
+ // Update totals
+ g.totals
+ .each(function(d) {
+ d.qNum = g.mScale.domain()[1] - d.offset;
+ d.qText = d3.format([",.0f", ",.0%", ",.3s", g.totalFormatMs]["NPSC".indexOf(g.totalFormatM)])(d.offset);
+ var txp = g.barText(d, true);
+ d3.select(this)
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity","1")
+ .style("fill", "black")
+ .style("font-size", g.tref.style("font-size"))
+ .attr({x: txp.x, y: txp.y, dy: "-.2em"})
+ .text(txp.text)
+ ;
+
+ })
+ ;
+ }
+
+ if (~"BA".indexOf(g.showTexts)) {
+ // Update texts
+ g.texts
+ .each(function(d) {
+ var txp = g.barText(d);
+
+ d3.select(this)
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .style("opacity","1")
+ .style("fill", g.textColor == "Auto" ? g.txtColor(g.cScale(d.dim2)) : g.textColor )
+ .style("font-size", g.tref.style("font-size"))
+ .attr({x: txp.x, y: txp.y, dy: "-.2em"})
+ .text(txp.text)
+ ;
+
+ })
+ ;
+ }
+
+ if (g.showDeltas && g.nDims == 2) {
+
+ // update deltas
+ g.polys.transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .ease(g.ease)
+ .attr("points",function(d) {
+ var p;
+ if (g.orientation == "V") {
+ p = (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
+ (g.mScale(d.points[0]) - (g.mScale(0) - g.mScale(d.points[2]))) + " " +
+
+ (g.dScale(d.dim1p)+g.dScale.rangeBand()) + "," +
+ g.mScale(d.points[0]) + " " +
+
+ (g.dScale(d.dim1c)) + "," +
+ g.mScale(d.points[1]) + " " +
+
+ (g.dScale(d.dim1c)) + "," +
+ (g.mScale(d.points[1]) - (g.mScale(0) - g.mScale(d.points[3])))
+ ;
+ }
+ else {
+ p = (g.mScale(d.points[0]) + g.mScale(d.points[2])) + "," +
+ (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
+
+ g.mScale(d.points[0]) + "," +
+ (g.dScale(d.dim1p) + g.dScale.rangeBand()) + " " +
+
+ g.mScale(d.points[1]) + "," +
+ g.dScale(d.dim1c) + " " +
+
+ (g.mScale(d.points[1]) + g.mScale(d.points[3])) + "," +
+ g.dScale(d.dim1c)
+ ;
+ }
+ return p;
+ })
+ .style("fill",function(d) {
+ return g.cScale(d.dim2);
+ })
+ .style("opacity",g.barGap == 1 ? "1" : "0.5")
+ ;
+ }
+ // update legend items
+ if (g.lgn.use) {
+
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ var maxprow = Math.floor(g.lgn.width/(g.lgn.txtOff + g.lgn.txtWidth));
+ var nprow = maxprow;
+ }
+
+ g.lgn.items
+ .each(function(d, i) {
+ d3.select(this)
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .select("rect")
+ .attr("x",function(e) {
+ var x;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ x = (i % nprow)*(g.lgn.txtOff + g.lgn.txtWidth);
+ }
+ else {
+ x = g.lgn.pad;
+ }
+ return x;
+ })
+ .attr("y",function(e) {
+ var y;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ y = g.lgn.pad + Math.floor(i/nprow) * g.lgn.itmHeight;
+ }
+ else {
+ y = g.lgn.pad + g.lgn.itmHeight*i;
+ }
+ return y;
+ })
+ .style("opacity","1")
+ ;
+ var txt = d3.select(this)
+ .transition()
+ .delay(tDelay)
+ .duration(tDuration)
+ .select("text")
+ .attr("x",function(e) {
+ var x;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ x = (i % nprow)*(g.lgn.txtOff + g.lgn.txtWidth) + g.lgn.txtOff;
+ }
+ else {
+ x = g.lgn.txtOff;
+ }
+ return x;
+ })
+ .attr("y",function(e) {
+ var y;
+ if (g.lgn.use == "T" || g.lgn.use == "B") {
+ y = g.lgn.pad + Math.floor(i/nprow) * g.lgn.itmHeight + 11;
+ }
+ else {
+ y = g.lgn.pad + g.lgn.itmHeight*i + 11;
+ }
+ return y;
+ })
+ .style("opacity","1")
+ ;
+ txt.each(function(d, i) {
+ var self = d3.select(this),
+ textLength = self.node().getComputedTextLength(),
+ text = self.text();
+ while (textLength > g.lgn.txtWidth && text.length > 0) {
+ text = text.slice(0, -1);
+ self.text(text + '\u2026');
+ textLength = self.node().getComputedTextLength();
+ }
+ });
+ ;
+ })
+ ;
+ }
+},
+
+/**
+ *--------------------------------------
+ * Refresh chart
+ *--------------------------------------
+ * Refresh chart, no new data
+*/
+refreshChart: function() {
+
+ this.initChart();
+ this.createBars();
+ this.updateBars();
+},
+//--------------------------------------
+// Topological sort
+//--------------------------------------
+/*- begin https://github.com/marcelklehr/toposort */
+toposort: function(nodes, edges) {
+ var cursor = nodes.length
+ , sorted = new Array(cursor)
+ , visited = {}
+ , i = cursor
+
+ while (i--) {
+ if (!visited[i]) visit(nodes[i], i, [])
+ }
+
+ return sorted
+
+ function visit(node, i, predecessors) {
+ if(predecessors.indexOf(node) >= 0) {
+ throw new Error('Cyclic dependency: '+JSON.stringify(node))
+ }
+
+// if (!~nodes.indexOf(node)) {
+// throw new Error('Found unknown node. Make sure to provided all involved nodes. Unknown node: '+JSON.stringify(node))
+// }
+
+ if (visited[i]) return;
+ visited[i] = true
+
+ // outgoing edges
+ var outgoing = edges.filter(function(edge){
+ return edge[0] === node
+ })
+ if (i = outgoing.length) {
+ var preds = predecessors.concat(node)
+ do {
+ var child = outgoing[--i][1]
+ visit(child, nodes.indexOf(child), preds)
+ } while (i)
+ }
+
+ sorted[--cursor] = node
+ }
+},
+/*- end https://github.com/marcelklehr/toposort */
+/*- begin http://stackoverflow.com/questions/11867545 */
+txtColor: function(hexcolor){
+ var r = parseInt(hexcolor.substr(1,2),16);
+ var g = parseInt(hexcolor.substr(3,2),16);
+ var b = parseInt(hexcolor.substr(5,2),16);
+ var yiq = ((r*299)+(g*587)+(b*114))/1000;
+ return (yiq >= 160) ? 'black' : 'white'; // 128 changed to 160 to give white preference
+},
+/*- end http://stackoverflow.com/questions/11867545 */
+//--------------------------------------
+// Color Schemes
+//--------------------------------------
+custom100: [
+"#99c867","#e43cd0","#e2402a","#66a8db","#3f1a20","#e5aa87","#3c6b59","#aa2a6b","#e9b02e","#7864dd",
+"#65e93c","#5ce4ba","#d0e0da","#d796dd","#64487b","#e4e72b","#6f7330","#932834","#ae6c7d","#986717",
+"#e3cb70","#408c1d","#dd325f","#533d1c","#2a3c54","#db7127","#72e3e2","#e2c1da","#d47555","#7d7f81",
+"#54ae9b","#e9daa6","#3a8855","#5be66e","#ab39a4","#a6e332","#6c469d","#e39e51","#4f1c42","#273c1c",
+"#aa972e","#8bb32a","#bdeca5","#63ec9b","#9c3519","#aaa484","#72256d","#4d749f","#9884df","#e590b8",
+"#44b62b","#ad5792","#c65dea","#e670ca","#e38783","#29312d","#6a2c1e","#d7b1aa","#b1e7c3","#cdc134",
+"#9ee764","#56b8ce","#2c6323","#65464a","#b1cfea","#3c7481","#3a4e96","#6493e1","#db5656","#747259",
+"#bbabe4","#e33f92","#d0607d","#759f79","#9d6b5e","#8574ae","#7e304c","#ad8fac","#4b77de","#647e17",
+"#b9c379","#8da8b0","#b972d9","#786279","#7ec07d","#916436","#2d274f","#dce680","#759748","#dae65a",
+"#459c49","#b7934a","#51c671","#9ead3f","#969a5c","#b9976a","#46531a","#c0f084","#76c146","#bad0ad"
+],
+google20: [
+"#3366cc","#dc3912","#ff9900","#109618","#990099","#0099c6","#dd4477","#66aa00","#b82e2e","#316395",
+"#994499","#22aa99","#aaaa11","#6633cc","#e67300","#8b0707","#651067","#329262","#5574a6","#3b3eac"
+],
+qlikView18: [
+"#8daacb","#fc7362","#bbd854","#ffd92f","#66c296","#e5b694","#e78ad2","#b3b3b3","#a6d8e3","#abe9bc",
+"#1b7d9c","#ffbfc9","#4da741","#c4b2d6","#b22424","#00acac","#be6c2c","#695496"
+],
+qlikSense12: [
+"#332288","#6699cc","#88ccee","#44aa99","#117733","#999933","#ddcc77","#661100","#cc6677","#aa4466",
+"#882255","#aa4499"
+]
}
\ No newline at end of file
diff --git a/lib/angular.js b/src/lib/angular.js
similarity index 100%
rename from lib/angular.js
rename to src/lib/angular.js
diff --git a/lib/d3.v3.js b/src/lib/d3.v3.js
similarity index 100%
rename from lib/d3.v3.js
rename to src/lib/d3.v3.js
diff --git a/lib/jquery.js b/src/lib/jquery.js
similarity index 100%
rename from lib/jquery.js
rename to src/lib/jquery.js