├── assets ├── icon.png ├── screen.png ├── thumb.png ├── icon_old.png ├── screen_old.png └── spline_custom_RV_HTML-based.pbix ├── dist └── PowerBI-visuals-spline.pbiviz ├── .travis.yml ├── .gitignore ├── style └── visual.less ├── tsconfig.json ├── package.json ├── README.md ├── dependencies.json ├── pbiviz.json ├── src ├── htmlInjectionUtility.ts ├── objectEnumerationUtility.ts └── visual.ts ├── tslint.json ├── r_files └── flatten_HTML.r ├── capabilities.json └── script.r /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PowerBI-visuals-spline/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PowerBI-visuals-spline/HEAD/assets/screen.png -------------------------------------------------------------------------------- /assets/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PowerBI-visuals-spline/HEAD/assets/thumb.png -------------------------------------------------------------------------------- /assets/icon_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PowerBI-visuals-spline/HEAD/assets/icon_old.png -------------------------------------------------------------------------------- /assets/screen_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PowerBI-visuals-spline/HEAD/assets/screen_old.png -------------------------------------------------------------------------------- /dist/PowerBI-visuals-spline.pbiviz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PowerBI-visuals-spline/HEAD/dist/PowerBI-visuals-spline.pbiviz -------------------------------------------------------------------------------- /assets/spline_custom_RV_HTML-based.pbix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PowerBI-visuals-spline/HEAD/assets/spline_custom_RV_HTML-based.pbix -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | sudo: required 4 | dist: trusty 5 | language: node_js 6 | node_js: 7 | - "7" 8 | install: 9 | - npm install 10 | script: 11 | - npm run lint 12 | - npm run package 13 | notifications: 14 | email: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | try.txt 3 | .tmp 4 | .api 5 | .Rhistory 6 | .RData 7 | out.html_files 8 | out.html_files/* 9 | dist 10 | dist/* 11 | dist/PowerBI-visuals-spline.pbiviz 12 | out.html.tmp 13 | out.html_files 14 | out.html_files/* 15 | out_files 16 | out_files/* 17 | -------------------------------------------------------------------------------- /style/visual.less: -------------------------------------------------------------------------------- 1 | .rcv_autoScaleImageContainer { 2 | position: relative; 3 | 4 | .rcv_autoScaleImage { 5 | max-width: 100%; 6 | max-height: 100%; 7 | position: absolute; 8 | top: 50%; 9 | /* @noflip */ 10 | left: 50%; 11 | transform: translateY(-50%) translateX(-50%); 12 | -webkit-transform: translateY(-50%) translateX(-50%); 13 | } 14 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "emitDecoratorMetadata": true, 5 | "experimentalDecorators": true, 6 | "target": "ES5", 7 | "sourceMap": true, 8 | "out": "./.tmp/build/visual.js" 9 | }, 10 | "files": [ 11 | ".api/v1.7.0/PowerBI-visuals.d.ts", 12 | "src/htmlInjectionUtility.ts", 13 | "src/objectEnumerationUtility.ts", 14 | "src/visual.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "visual", 3 | "scripts": { 4 | "postinstall": "pbiviz update 1.7.0", 5 | "pbiviz": "pbiviz", 6 | "package": "pbiviz package", 7 | "lint": "tslint -r \"node_modules/tslint-microsoft-contrib\" \"+(src)/**/*.ts\"", 8 | "start": "pbiviz start" 9 | }, 10 | "devDependencies": { 11 | "tslint": "^4.4.2", 12 | "tslint-microsoft-contrib": "^4.0.0", 13 | "powerbi-visuals-tools": "1.7.1" 14 | } 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerBI-visuals-spline 2 | R-powered custom visual implements spline smoothing 3 | 4 | ![spline screenshot](https://az158878.vo.msecnd.net/marketing/Partner_21474836617/Product_42949680598/Asset_a6d9c919-8724-42a2-9b08-3ae443cf9ffa/Splinescreenshot3.png) 5 | # Overview 6 | Imagine that you have a scatter (XY) plot. It is not always easy to see the trends and to tell the story behind the data. 7 | 8 | Smoothing spline helps you to visualize and understand noisy scatterplot data. 9 | 10 | Here is how it works: 11 | * Define the fields for horizontal and vertical axes of the scatterplot 12 | * Controll the smoothness of the smoothing spline 13 | * Control the confidence levels or the spline curve (or even turn them off) 14 | * Define the colors and the rest of graphical attributes to meet your specific needs 15 | 16 | R package dependencies(auto-installed): graphics, scales, reshape 17 | 18 | Supports R versions: R 3.3.1, R 3.3.0, MRO 3.3.1, MRO 3.3.0, MRO 3.2.2 19 | 20 | See also [Spline chart at Microsoft Office store](https://store.office.com/en-us/app.aspx?assetid=WA104380860&sourcecorrid=cf75337d-2a42-4e88-913f-aa2158340d82&searchapppos=0&ui=en-US&rs=en-US&ad=US&appredirect=false) -------------------------------------------------------------------------------- /dependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "cranPackages": [ 3 | { 4 | "name": "scales", 5 | "displayName": "scales: Scale Functions for Visualization", 6 | "url": "https://cran.r-project.org/web/packages/scales/index.html" 7 | }, 8 | { 9 | "name": "reshape", 10 | "displayName": "reshape: lets you flexibly restructure and aggregate data", 11 | "url": "https://cran.r-project.org/web/packages/reshape/index.html" 12 | }, 13 | { 14 | "name": "mgcv", 15 | "displayName": "mgvc: Mixed GAM Computation ", 16 | "url": "https://cran.r-project.org/web/packages/mgcv/index.html" 17 | }, 18 | { 19 | "name": "ggplot2", 20 | "displayName": "GG Plot 2", 21 | "url": "https://cran.r-project.org/web/packages/ggplot2/index.html" 22 | }, 23 | { 24 | "name": "plotly", 25 | "displayName": "Plotly", 26 | "url": "https://cran.r-project.org/web/packages/plotly/index.html" 27 | }, 28 | { 29 | "name": "htmlwidgets", 30 | "displayName": "HTML Widgets", 31 | "url": "https://cran.r-project.org/web/packages/htmlwidgets/index.html" 32 | }, 33 | { 34 | "name": "XML", 35 | "displayName": "XML", 36 | "url": "https://cran.r-project.org/web/packages/XML/index.html" 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /pbiviz.json: -------------------------------------------------------------------------------- 1 | { 2 | "visual": { 3 | "name": "PowerBI-visuals-spline", 4 | "displayName": "Spline", 5 | "guid": "PBI_CV_9D783E0D_2610_4C22_9576_88AD092AB59E", 6 | "visualClassName": "Visual", 7 | "version": "1.0.3", 8 | "description": "Smoothing spline helps you visualize and understand noisy data. Control the colors and the rest of attributes to meet your specific needs.

Service prerequisites: R-powered custom visual is used in service seamlessly

Desktop prerequisites: To run R scripts in Power BI Desktop, you must separately install R on your local computer.
You can download and install R for free from the Revolution Open download page or the CRAN Repository

R package dependencies(auto-installed): graphics, scales, reshape, ggplot2, mgcv, htmlWidgets, XML, plotly

Supports R versions: R 3.3.1, R 3.3.0, MRO 3.3.1, MRO 3.3.0, MRO 3.2.2
", 9 | "supportUrl": "http://community.powerbi.com/", 10 | "gitHubUrl": "https://github.com/microsoft/PowerBI-visuals-spline" 11 | }, 12 | "apiVersion": "1.7.0", 13 | "author": { 14 | "name": "Microsoft", 15 | "email": "pbicvsupport@microsoft.com" 16 | }, 17 | "assets": { 18 | "icon": "assets/icon.png" 19 | }, 20 | "externalJS": [], 21 | "style": "style/visual.less", 22 | "capabilities": "capabilities.json", 23 | "dependencies": "dependencies.json" 24 | } 25 | -------------------------------------------------------------------------------- /src/htmlInjectionUtility.ts: -------------------------------------------------------------------------------- 1 | module powerbi.extensibility.visual { 2 | let injectorCounter: number = 0; 3 | 4 | export function ResetInjector() : void { 5 | injectorCounter = 0; 6 | } 7 | 8 | export function injectorReady() : boolean { 9 | return injectorCounter === 0; 10 | } 11 | 12 | export function ParseElement(el: HTMLElement , target: HTMLElement) : Node[] 13 | { 14 | let arr: Node[] = []; 15 | if (!el || !el.hasChildNodes()) 16 | return 17 | 18 | let nodes = el.children; 19 | for (var i=0; inodes.item(i).cloneNode(true); 28 | } 29 | target.appendChild(tempNode); 30 | arr.push(tempNode); 31 | } 32 | return arr; 33 | } 34 | 35 | function createScriptNode(refNode: Element): HTMLElement{ 36 | let script = document.createElement('script'); 37 | let attr = refNode.attributes; 38 | for (var i=0; i { 57 | if (injectorReady()) { 58 | window.clearInterval(intervalVar); 59 | if (window.hasOwnProperty('HTMLWidgets') && window['HTMLWidgets'].staticRender) { 60 | window['HTMLWidgets'].staticRender(); 61 | } 62 | } 63 | }, 100); 64 | } 65 | } -------------------------------------------------------------------------------- /src/objectEnumerationUtility.ts: -------------------------------------------------------------------------------- 1 | module powerbi.extensibility.visual { 2 | /** 3 | * Gets property value for a particular object. 4 | * 5 | * @function 6 | * @param {DataViewObjects} objects - Map of defined objects. 7 | * @param {string} objectName - Name of desired object. 8 | * @param {string} propertyName - Name of desired property. 9 | * @param {T} defaultValue - Default value of desired property. 10 | */ 11 | export function getValue(objects: DataViewObjects, objectName: string, propertyName: string, defaultValue: T): T { 12 | if (objects) { 13 | let object = objects[objectName]; 14 | if (object) { 15 | let property: T = object[propertyName]; 16 | if (property !== undefined) { 17 | return property; 18 | } 19 | } 20 | } 21 | return defaultValue; 22 | } 23 | 24 | /** 25 | * Gets property value for a particular object in a category. 26 | * 27 | * @function 28 | * @param {DataViewCategoryColumn} category - List of category objects. 29 | * @param {number} index - Index of category object. 30 | * @param {string} objectName - Name of desired object. 31 | * @param {string} propertyName - Name of desired property. 32 | * @param {T} defaultValue - Default value of desired property. 33 | */ 34 | export function getCategoricalObjectValue(category: DataViewCategoryColumn, index: number, objectName: string, propertyName: string, defaultValue: T): T { 35 | let categoryObjects = category.objects; 36 | 37 | if (categoryObjects) { 38 | let categoryObject: DataViewObject = categoryObjects[index]; 39 | if (categoryObject) { 40 | let object = categoryObject[objectName]; 41 | if (object) { 42 | let property: T = object[propertyName]; 43 | if (property !== undefined) { 44 | return property; 45 | } 46 | } 47 | } 48 | } 49 | return defaultValue; 50 | } 51 | 52 | // returns value in range 53 | export function inMinMax(a: number, mi: number, ma: number) 54 | { 55 | if(ama) 58 | return ma; 59 | return a; 60 | } 61 | 62 | 63 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "indent": [ 9 | true, 10 | "spaces" 11 | ], 12 | "no-duplicate-variable": true, 13 | "no-eval": true, 14 | "no-internal-module": false, 15 | "no-trailing-whitespace": true, 16 | "no-unsafe-finally": true, 17 | "no-var-keyword": true, 18 | "one-line": [ 19 | true, 20 | "check-open-brace", 21 | "check-whitespace" 22 | ], 23 | "quotemark": [ 24 | false, 25 | "double" 26 | ], 27 | "semicolon": [ 28 | true, 29 | "always" 30 | ], 31 | "triple-equals": [ 32 | true, 33 | "allow-null-check" 34 | ], 35 | "typedef-whitespace": [ 36 | true, 37 | { 38 | "call-signature": "nospace", 39 | "index-signature": "nospace", 40 | "parameter": "nospace", 41 | "property-declaration": "nospace", 42 | "variable-declaration": "nospace" 43 | } 44 | ], 45 | "variable-name": [ 46 | true, 47 | "ban-keywords" 48 | ], 49 | "whitespace": [ 50 | true, 51 | "check-branch", 52 | "check-decl", 53 | "check-operator", 54 | "check-separator", 55 | "check-type" 56 | ], 57 | "insecure-random": true, 58 | "no-banned-terms": true, 59 | "no-cookies": true, 60 | "no-delete-expression": true, 61 | "no-disable-auto-sanitization": true, 62 | "no-document-domain": true, 63 | "no-document-write": true, 64 | "no-exec-script": true, 65 | "no-function-constructor-with-string-args": true, 66 | "no-http-string": [ 67 | true, 68 | "http://www.example.com/?.*", 69 | "http://www.examples.com/?.*" 70 | ], 71 | "no-inner-html": true, 72 | "no-octal-literal": true, 73 | "no-reserved-keywords": true, 74 | "no-string-based-set-immediate": true, 75 | "no-string-based-set-interval": true, 76 | "no-string-based-set-timeout": true, 77 | "non-literal-require": true, 78 | "possible-timing-attack": true, 79 | "react-anchor-blank-noopener": true, 80 | "react-iframe-missing-sandbox": true, 81 | "react-no-dangerous-html": true 82 | } 83 | } -------------------------------------------------------------------------------- /r_files/flatten_HTML.r: -------------------------------------------------------------------------------- 1 | ############### Utility functions ############### 2 | libraryRequireInstall = function(packageName, ...) 3 | { 4 | if(!require(packageName, character.only = TRUE)) 5 | warning(paste("*** The package: '", packageName, "' was not installed ***", sep="")) 6 | } 7 | 8 | libraryRequireInstall("XML") 9 | libraryRequireInstall("htmlwidgets") 10 | 11 | internalSaveWidget <- function(widget, fname) 12 | { 13 | tempFname = paste(fname, ".tmp", sep="") 14 | htmlwidgets::saveWidget(widget, file = tempFname, selfcontained = FALSE) 15 | FlattenHTML(tempFname, fname) 16 | } 17 | 18 | FlattenHTML <- function(fnameIn, fnameOut) 19 | { 20 | # Read and parse HTML file 21 | # Embed all js and css files into one unified file 22 | 23 | if(!file.exists(fnameIn)) 24 | return(FALSE) 25 | 26 | dir = dirname(fnameIn) 27 | html = htmlTreeParse(fnameIn, useInternal = TRUE) 28 | top = xmlRoot(html) 29 | 30 | # extract all