From 4d63a39f6d07d508b97768c1e8d4988c7ab2305b Mon Sep 17 00:00:00 2001 From: Matt Mazzola Date: Mon, 22 Aug 2016 15:06:55 -0700 Subject: [PATCH] Auto-format all files to use consistent tab size of 2. --- .vscode/settings.json | 5 +- .vscode/tasks.json | 46 +-- demo/app/app.js | 98 +++--- gulpfile.js | 270 ++++++++-------- karma.conf.js | 62 ++-- src/embed.ts | 556 ++++++++++++++++---------------- src/factories.ts | 28 +- src/ifilterable.ts | 38 +-- src/page.ts | 278 ++++++++-------- src/powerbi.ts | 26 +- src/report.ts | 424 ++++++++++++------------ src/service.ts | 522 +++++++++++++++--------------- src/tile.ts | 18 +- src/util.ts | 86 ++--- src/visual.ts | 128 ++++---- test/test.spec.ts | 60 ++-- test/utility/mockReportEmbed.ts | 38 +-- test/utility/mockWpmp.ts | 18 +- tsconfig.json | 44 +-- webpack.test.tsconfig.json | 40 +-- 20 files changed, 1397 insertions(+), 1388 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 811c7d32..4479811e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ // Place your settings in this file to overwrite default and user settings. { - "editor.tabSize": 4, - "editor.insertSpaces": true + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.detectIndentation": false } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 087928f8..a7b839de 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,21 +1,29 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "0.1.0", - "command": "npm", - "isShellCommand": true, - "showOutput": "always", - "suppressTaskName": true, - "tasks": [ - { - "taskName": "build", - "args": ["run", "build"], - "isBuildCommand": true - }, - { - "taskName": "test", - "args": ["run", "test", "--", "--chrome"], - "isTestCommand": true - } - ] + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "npm", + "isShellCommand": true, + "showOutput": "always", + "suppressTaskName": true, + "tasks": [ + { + "taskName": "build", + "args": [ + "run", + "build" + ], + "isBuildCommand": true + }, + { + "taskName": "test", + "args": [ + "run", + "test", + "--", + "--chrome" + ], + "isTestCommand": true + } + ] } \ No newline at end of file diff --git a/demo/app/app.js b/demo/app/app.js index 72da7868..dcc84bd8 100644 --- a/demo/app/app.js +++ b/demo/app/app.js @@ -115,7 +115,7 @@ $(function () { customPageNavReport.getPages() .then(function (pages) { console.log('pages: ', pages); - if(pages.length > 0) { + if (pages.length > 0) { const firstPage = pages[0]; firstPage.isActive = true; @@ -161,7 +161,7 @@ $(function () { customFilterPaneReport.on('loaded', function (event) { createAppliedFiltersPane(null, customFilterPaneReport); - + console.log('custom filter pane report loaded'); customFilterPaneReport.getPages() .then(function (pages) { @@ -224,7 +224,7 @@ $(function () { .each(function (index, element) { var $element = $(element); var buttonPage = $element.data('page'); - if(buttonPage.isActive) { + if (buttonPage.isActive) { buttonPage.isActive = false; $element.removeClass('active'); } @@ -235,7 +235,7 @@ $(function () { .each(function (index, element) { var $element = $(element); var buttonPage = $element.data('page'); - if(buttonPage.name === newPage.name) { + if (buttonPage.name === newPage.name) { buttonPage.isActive = true; $element.addClass('active'); } @@ -271,7 +271,7 @@ $(function () { .data('page', page) .text(page.displayName); - if(page.isActive) { + if (page.isActive) { $page.addClass('active'); } @@ -287,7 +287,7 @@ $(function () { var $nextButton = $('#nextbutton'); var $cycleButton = $('#cyclebutton'); var cycleIntervalId; - + // When report button is clicked embed the report $reportsList.on('click', 'button', function (event) { var button = event.target; @@ -300,11 +300,11 @@ $(function () { }) .then(function (reportWithToken) { var reportConfig = $.extend({ - type: 'report', - settings: { - filterPaneEnabled: false, - navContentPaneEnabled: false - } + type: 'report', + settings: { + filterPaneEnabled: false, + navContentPaneEnabled: false + } }, reportWithToken); powerbi.embed($dynamicReportContainer.get(0), reportConfig); @@ -323,7 +323,7 @@ $(function () { $cycleButton.toggleClass('active'); $cycleButton.data('cycle', !$cycleButton.data('cycle')); - if($cycleButton.data('cycle')) { + if ($cycleButton.data('cycle')) { cycleIntervalId = setInterval(function () { console.log('cycle page: '); changePage(true); @@ -359,31 +359,31 @@ $(function () { .each(function (index, element) { var $element = $(element); var buttonPage = $element.data('page'); - if(buttonPage.isActive) { + if (buttonPage.isActive) { $activeButtonIndex = index; } }); - if(forwards) { + if (forwards) { $activeButtonIndex += 1; } else { $activeButtonIndex -= 1; } - if($activeButtonIndex > reportButtons.length - 1) { + if ($activeButtonIndex > reportButtons.length - 1) { $activeButtonIndex = 0; } - if($activeButtonIndex < 0) { + if ($activeButtonIndex < 0) { $activeButtonIndex = reportButtons.length - 1; } reportButtons .each(function (index, element) { - if($activeButtonIndex === index) { + if ($activeButtonIndex === index) { var $element = $(element); var buttonPage = $element.data('page'); - + customPageNavReport.setPage(buttonPage.name); } }); @@ -407,41 +407,41 @@ $(function () { table: "Store", column: "Name" }, "And", [ - { - operator: "Contains", - value: "Direct" - } - ]); + { + operator: "Contains", + value: "Direct" + } + ]); var $predefinedFilter2 = $('#predefinedFilter2'); var predefinedFilter2 = new models.AdvancedFilter({ table: "Store", column: "Name" }, "Or", [ - { - operator: "Contains", - value: "Wash" - }, - { - operator: "Contains", - value: "Park" - } - ]); + { + operator: "Contains", + value: "Wash" + }, + { + operator: "Contains", + value: "Park" + } + ]); var $predefinedFilter3 = $('#predefinedFilter3'); var predefinedFilter3 = new models.AdvancedFilter({ table: "Store", column: "Name" }, "Or", [ - { - operator: "Contains", - value: "Wash" - }, - { - operator: "Contains", - value: "Park" - } - ]); + { + operator: "Contains", + value: "Wash" + }, + { + operator: "Contains", + value: "Park" + } + ]); $customFilterForm.on('submit', function (event) { event.preventDefault(); @@ -485,14 +485,14 @@ $(function () { $operatorTypeFields.on('change', function (event) { var checkedType = $('#customfilterform input[name=operatorType]:checked').val(); console.log('operator change', checkedType); - + updateFieldsForOperator(checkedType.toLowerCase()); }); $targetTypeFields.on('change', function (event) { var checkedTarget = $('#customfilterform input[name=filterTarget]:checked').val(); console.log('target change', checkedTarget); - + updateTargetFields(checkedTarget.toLowerCase()); }); @@ -526,14 +526,14 @@ $(function () { var filterTypeTarget = {}; filterTypeTarget.table = $('#filtertable').val(); - if(filterType === "column") { + if (filterType === "column") { filterTypeTarget.column = $('#filtercolumn').val(); } - else if(filterType === "hierarchy") { + else if (filterType === "hierarchy") { filterTypeTarget.hierarchy = $('#filterhierarchy').val(); filterTypeTarget.hierarchyLevel = $('#filterhierarchylevel').val(); } - else if(filterType === "measure") { + else if (filterType === "measure") { filterTypeTarget.measure = $('#filtermeasure').val(); } @@ -571,7 +571,7 @@ $(function () { var target = { type: checkedTarget }; - + if (checkedTarget === "page") { target.name = $('#filtertargetpage').val(); } @@ -611,7 +611,7 @@ $(function () { .data('filter', filter) .html('×') ; - + var $filterText = $('
') .addClass('filter__text') .text(JSON.stringify(filter, null, ' ')) @@ -718,7 +718,7 @@ $(function () { filters.some(function (filter, i) { if (JSON.stringify(filter) === JSON.stringify(filterToRemove)) { index = i; - return true; + return true; } }); diff --git a/gulpfile.js b/gulpfile.js index 2eb9deb9..23009c4e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,197 +1,197 @@ var gulp = require('gulp-help')(require('gulp')); var ghPages = require('gulp-gh-pages'), - header = require('gulp-header'), - rename = require('gulp-rename'), - concat = require('gulp-concat'), - uglify = require('gulp-uglify'), - replace = require('gulp-replace'), - sourcemaps = require('gulp-sourcemaps'), - tslint = require("gulp-tslint"), - ts = require('gulp-typescript'), - flatten = require('gulp-flatten'), - fs = require('fs'), - del = require('del'), - moment = require('moment'), - karma = require('karma'), - typedoc = require('gulp-typedoc'), - webpack = require('webpack'); - webpackStream = require('webpack-stream'), - webpackConfig = require('./webpack.config'), - webpackTestConfig = require('./webpack.test.config'), - runSequence = require('run-sequence'), - argv = require('yargs').argv; - ; + header = require('gulp-header'), + rename = require('gulp-rename'), + concat = require('gulp-concat'), + uglify = require('gulp-uglify'), + replace = require('gulp-replace'), + sourcemaps = require('gulp-sourcemaps'), + tslint = require("gulp-tslint"), + ts = require('gulp-typescript'), + flatten = require('gulp-flatten'), + fs = require('fs'), + del = require('del'), + moment = require('moment'), + karma = require('karma'), + typedoc = require('gulp-typedoc'), + webpack = require('webpack'), + webpackStream = require('webpack-stream'), + webpackConfig = require('./webpack.config'), + webpackTestConfig = require('./webpack.test.config'), + runSequence = require('run-sequence'), + argv = require('yargs').argv; + ; var package = require('./package.json'); var webpackBanner = package.name + " v" + package.version + " | (c) 2016 Microsoft Corporation " + package.license; var gulpBanner = "/*! " + webpackBanner + " */\n"; gulp.task('ghpages', 'Deploy documentation to gh-pages', ['nojekyll'], function () { - return gulp.src(['./docs/**/*'], { - dot: true - }) - .pipe(ghPages({ - force: true, - message: 'Update ' + moment().format('LLL') - })); + return gulp.src(['./docs/**/*'], { + dot: true + }) + .pipe(ghPages({ + force: true, + message: 'Update ' + moment().format('LLL') + })); }); gulp.task("docs", 'Compile documentation from src code', function () { - return gulp - .src(["src/**/*.ts"]) - .pipe(typedoc({ - mode: 'modules', - includeDeclarations: true, - - // Output options (see typedoc docs) - out: "./docs", - json: "./docs/json/" + package.name + ".json", - - // TypeDoc options (see typedoc docs) - ignoreCompilerErrors: true, - version: true - })) - ; + return gulp + .src(["src/**/*.ts"]) + .pipe(typedoc({ + mode: 'modules', + includeDeclarations: true, + + // Output options (see typedoc docs) + out: "./docs", + json: "./docs/json/" + package.name + ".json", + + // TypeDoc options (see typedoc docs) + ignoreCompilerErrors: true, + version: true + })) + ; }); gulp.task('copydemotodocs', 'Copy the demo to the docs', function () { - return gulp.src(["demo/**/*"]) - .pipe(gulp.dest("docs/demo")) - ; + return gulp.src(["demo/**/*"]) + .pipe(gulp.dest("docs/demo")) + ; }); gulp.task('nojekyll', 'Add .nojekyll file to docs directory', function (done) { - fs.writeFile('./docs/.nojekyll', '', function (error) { - if (error) { - throw error; - } + fs.writeFile('./docs/.nojekyll', '', function (error) { + if (error) { + throw error; + } - done(); - }); + done(); + }); }); gulp.task('watch', 'Watches for changes', ['lint'], function () { - gulp.watch(['./src/**/*.ts', './test/**/*.ts'], ['lint:ts']); - gulp.watch(['./test/**/*.ts'], ['test']); + gulp.watch(['./src/**/*.ts', './test/**/*.ts'], ['lint:ts']); + gulp.watch(['./test/**/*.ts'], ['test']); }); gulp.task('lint', 'Lints all files', function (done) { - runSequence( - 'lint:ts', - done - ); + runSequence( + 'lint:ts', + done + ); }); gulp.task('test', 'Runs all tests', function (done) { - runSequence( - 'lint:ts', - 'config', - 'compile:spec', - 'test:js', - done - ); + runSequence( + 'lint:ts', + 'config', + 'compile:spec', + 'test:js', + done + ); }); gulp.task('build', 'Runs a full build', function (done) { - runSequence( - 'lint:ts', - 'clean', - 'config', - ['compile:ts', 'compile:dts'], - 'min:js', - 'header', - done - ); + runSequence( + 'lint:ts', + 'clean', + 'config', + ['compile:ts', 'compile:dts'], + 'min:js', + 'header', + done + ); }); gulp.task('build:docs', 'Build docs folder', function (done) { - return runSequence( - 'clean:docs', - 'docs', - 'nojekyll', - 'copydemotodocs', - done - ); + return runSequence( + 'clean:docs', + 'docs', + 'nojekyll', + 'copydemotodocs', + done + ); }); gulp.task('config', 'Update config version with package version', function () { - return gulp.src(['./src/config.ts'], {base: "./"}) - .pipe(replace(/version: '([^']+)'/, `version: '${package.version}'`)) - .pipe(gulp.dest('.')); + return gulp.src(['./src/config.ts'], { base: "./" }) + .pipe(replace(/version: '([^']+)'/, `version: '${package.version}'`)) + .pipe(gulp.dest('.')); }); gulp.task('header', 'Add header to distributed files', function () { - return gulp.src(['./dist/*.d.ts']) - .pipe(header(gulpBanner)) - .pipe(gulp.dest('./dist')); + return gulp.src(['./dist/*.d.ts']) + .pipe(header(gulpBanner)) + .pipe(gulp.dest('./dist')); }); -gulp.task('clean', 'Cleans destination folder', function(done) { - return del([ - './dist/**/*' - ]); +gulp.task('clean', 'Cleans destination folder', function (done) { + return del([ + './dist/**/*' + ]); }); gulp.task('clean:docs', 'Clean docs directory', function () { - return del([ - 'docs/**/*', - 'docs' - ]); + return del([ + 'docs/**/*', + 'docs' + ]); }); -gulp.task('lint:ts', 'Lints all TypeScript', function() { - return gulp.src(['./src/**/*.ts', './test/**/*.ts']) - .pipe(tslint({ - formatter: "verbose" - })) - .pipe(tslint.report()); +gulp.task('lint:ts', 'Lints all TypeScript', function () { + return gulp.src(['./src/**/*.ts', './test/**/*.ts']) + .pipe(tslint({ + formatter: "verbose" + })) + .pipe(tslint.report()); }); -gulp.task('min:js', 'Creates minified JavaScript file', function() { - return gulp.src(['!./dist/*.min.js', './dist/*.js']) - .pipe(uglify({ - preserveComments: 'license' - })) - .pipe(rename({ - suffix: '.min' - })) - .pipe(gulp.dest('./dist')); +gulp.task('min:js', 'Creates minified JavaScript file', function () { + return gulp.src(['!./dist/*.min.js', './dist/*.js']) + .pipe(uglify({ + preserveComments: 'license' + })) + .pipe(rename({ + suffix: '.min' + })) + .pipe(gulp.dest('./dist')); }); -gulp.task('compile:ts', 'Compile typescript for powerbi library', function() { - webpackConfig.plugins = [ - new webpack.BannerPlugin(webpackBanner) - ]; +gulp.task('compile:ts', 'Compile typescript for powerbi library', function () { + webpackConfig.plugins = [ + new webpack.BannerPlugin(webpackBanner) + ]; - return gulp.src(['./src/powerbi.ts']) - .pipe(webpackStream(webpackConfig)) - .pipe(gulp.dest('dist/')); + return gulp.src(['./src/powerbi.ts']) + .pipe(webpackStream(webpackConfig)) + .pipe(gulp.dest('dist/')); }); gulp.task('compile:dts', 'Generate dts files from modules', function () { - var tsProject = ts.createProject('tsconfig.json', { - declaration: true, - sourceMap: false - }); + var tsProject = ts.createProject('tsconfig.json', { + declaration: true, + sourceMap: false + }); + + var tsResult = tsProject.src() + .pipe(ts(tsProject)); - var tsResult = tsProject.src() - .pipe(ts(tsProject)); - - return tsResult.dts - .pipe(flatten()) - .pipe(gulp.dest('./dist')); + return tsResult.dts + .pipe(flatten()) + .pipe(gulp.dest('./dist')); }); gulp.task('compile:spec', 'Compile spec tests', function () { - return gulp.src(['./test/test.spec.ts']) - .pipe(webpackStream(webpackTestConfig)) - .pipe(gulp.dest('./tmp')); + return gulp.src(['./test/test.spec.ts']) + .pipe(webpackStream(webpackTestConfig)) + .pipe(gulp.dest('./tmp')); }); gulp.task('test:js', 'Run js tests', function (done) { - new karma.Server.start({ - configFile: __dirname + '/karma.conf.js', - singleRun: argv.debug ? false : true, - captureTimeout: argv.timeout || 60000 - }, done); + new karma.Server.start({ + configFile: __dirname + '/karma.conf.js', + singleRun: argv.debug ? false : true, + captureTimeout: argv.timeout || 60000 + }, done); }); \ No newline at end of file diff --git a/karma.conf.js b/karma.conf.js index 9a36a297..bf6cedde 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,35 +1,35 @@ var argv = require('yargs').argv; module.exports = function (config) { - config.set({ - frameworks: ['jasmine'], - files: [ - './node_modules/jquery/dist/jquery.js', - './node_modules/es6-promise/dist/es6-promise.js', - './tmp/**/*.js', - { pattern: './test/**/*.html', served: true, included: false } - ], - exclude: [], - reporters: argv.debug ? ['spec'] : ['spec', 'coverage'], - autoWatch: true, - browsers: [argv.chrome ? 'Chrome' : 'PhantomJS'], - plugins: [ - 'karma-chrome-launcher', - 'karma-jasmine', - 'karma-spec-reporter', - 'karma-phantomjs-launcher', - 'karma-coverage' - ], - preprocessors: { './tmp/**/*.js': ['coverage'] }, - coverageReporter: { - reporters: [ - { type: 'html' }, - { type: 'text-summary' } - ] - }, - logLevel: argv.debug ? config.LOG_DEBUG : config.LOG_INFO, - client: { - args: argv.logMessages ? ['logMessages']: [] - } - }); + config.set({ + frameworks: ['jasmine'], + files: [ + './node_modules/jquery/dist/jquery.js', + './node_modules/es6-promise/dist/es6-promise.js', + './tmp/**/*.js', + { pattern: './test/**/*.html', served: true, included: false } + ], + exclude: [], + reporters: argv.debug ? ['spec'] : ['spec', 'coverage'], + autoWatch: true, + browsers: [argv.chrome ? 'Chrome' : 'PhantomJS'], + plugins: [ + 'karma-chrome-launcher', + 'karma-jasmine', + 'karma-spec-reporter', + 'karma-phantomjs-launcher', + 'karma-coverage' + ], + preprocessors: { './tmp/**/*.js': ['coverage'] }, + coverageReporter: { + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + logLevel: argv.debug ? config.LOG_DEBUG : config.LOG_INFO, + client: { + args: argv.logMessages ? ['logMessages'] : [] + } + }); }; \ No newline at end of file diff --git a/src/embed.ts b/src/embed.ts index c4595d13..f44381c9 100644 --- a/src/embed.ts +++ b/src/embed.ts @@ -4,21 +4,21 @@ import * as models from 'powerbi-models'; import * as hpm from 'http-post-message'; declare global { - interface Document { - // Mozilla Fullscreen - mozCancelFullScreen: Function; + interface Document { + // Mozilla Fullscreen + mozCancelFullScreen: Function; - // Ms Fullscreen - msExitFullscreen: Function; - } + // Ms Fullscreen + msExitFullscreen: Function; + } - interface HTMLIFrameElement { - // Mozilla Fullscreen - mozRequestFullScreen: Function; + interface HTMLIFrameElement { + // Mozilla Fullscreen + mozRequestFullScreen: Function; - // Ms Fullscreen - msRequestFullscreen: Function; - } + // Ms Fullscreen + msRequestFullscreen: Function; + } } // TODO: Re-use ILoadConfiguration interface to prevent duplicating properties. @@ -30,25 +30,25 @@ declare global { * @interface IEmbedConfiguration */ export interface IEmbedConfiguration { - type?: string; - id?: string; - uniqueId?: string; - embedUrl?: string; - accessToken?: string; - settings?: models.ISettings; - pageName?: string; - filters?: (models.IBasicFilter | models.IAdvancedFilter)[]; + type?: string; + id?: string; + uniqueId?: string; + embedUrl?: string; + accessToken?: string; + settings?: models.ISettings; + pageName?: string; + filters?: (models.IBasicFilter | models.IAdvancedFilter)[]; } export interface IInternalEmbedConfiguration extends models.ILoadConfiguration { - uniqueId: string; - type: string; - embedUrl: string; + uniqueId: string; + type: string; + embedUrl: string; } export interface IInternalEventHandler { - test(event: service.IEvent): boolean; - handle(event: service.ICustomEvent): void; + test(event: service.IEvent): boolean; + handle(event: service.ICustomEvent): void; } /** @@ -59,274 +59,274 @@ export interface IInternalEventHandler { * @class Embed */ export abstract class Embed { - static allowedEvents = ["loaded"]; - static accessTokenAttribute = 'powerbi-access-token'; - static embedUrlAttribute = 'powerbi-embed-url'; - static nameAttribute = 'powerbi-name'; - static typeAttribute = 'powerbi-type'; - static type: string; - - private static defaultSettings: models.ISettings = { - filterPaneEnabled: true - }; - - allowedEvents = []; - - /** - * Gets or set the event handler registered for this embed component - * - * @type {IInternalEventHandler[]} - */ - eventHandlers: IInternalEventHandler[]; - - /** - * Gets or sets the Power BI embed service - * - * @type {service.Service} - */ - service: service.Service; - - /** - * Gets or sets the HTML element containing the Power BI embed component - * - * @type {HTMLElement} - */ - element: HTMLElement; - - /** - * Gets or sets the HTML iframe element that renders the Power BI embed component - * - * @type {HTMLIFrameElement} - */ - iframe: HTMLIFrameElement; - - /** - * Gets or sets the configuration settings for the embed component - * - * @type {IInternalEmbedConfiguration} - */ - config: IInternalEmbedConfiguration; - - /** - * Creates an instance of Embed. - * - * Note: there is circular reference between embeds and service - * The service has list of all embeds on the host page, and each embed has reference to the service that created it. - * - * @param {service.Service} service - * @param {HTMLElement} element - * @param {IEmbedConfiguration} config - */ - constructor(service: service.Service, element: HTMLElement, config: IEmbedConfiguration) { - Array.prototype.push.apply(this.allowedEvents, Embed.allowedEvents); - this.eventHandlers = []; - this.service = service; - this.element = element; - - // TODO: Change when Object.assign is available. - const settings = utils.assign({}, Embed.defaultSettings, config.settings); - this.config = utils.assign({ settings }, config); - this.config.accessToken = this.getAccessToken(service.accessToken); - this.config.embedUrl = this.getEmbedUrl(); - this.config.id = this.getId(); - this.config.uniqueId = this.getUniqueId(); - - const iframeHtml = ``; - - this.element.innerHTML = iframeHtml; - this.iframe = this.element.childNodes[0]; - this.iframe.addEventListener('load', () => this.load(this.config), false); - } - - /** - * Sends load configuration data. - * - * ```javascript - * report.load({ - * type: 'report', - * id: '5dac7a4a-4452-46b3-99f6-a25915e0fe55', - * accessToken: 'eyJ0eXA ... TaE2rTSbmg', - * settings: { - * navContentPaneEnabled: false - * }, - * pageName: "DefaultPage", - * filters: [ - * { - * ... DefaultReportFilter ... - * } - * ] - * }) - * .catch(error => { ... }); - * ``` - * - * @param {models.ILoadConfiguration} config - * @returns {Promise} - */ - load(config: models.ILoadConfiguration): Promise { - const errors = models.validateLoad(config); - if(errors) { - throw errors; - } - - return this.service.hpm.post('/report/load', config, { uid: this.config.uniqueId }, this.iframe.contentWindow) - .then(response => { - return response.body; - }, - response => { - throw response.body; - }); + static allowedEvents = ["loaded"]; + static accessTokenAttribute = 'powerbi-access-token'; + static embedUrlAttribute = 'powerbi-embed-url'; + static nameAttribute = 'powerbi-name'; + static typeAttribute = 'powerbi-type'; + static type: string; + + private static defaultSettings: models.ISettings = { + filterPaneEnabled: true + }; + + allowedEvents = []; + + /** + * Gets or set the event handler registered for this embed component + * + * @type {IInternalEventHandler[]} + */ + eventHandlers: IInternalEventHandler[]; + + /** + * Gets or sets the Power BI embed service + * + * @type {service.Service} + */ + service: service.Service; + + /** + * Gets or sets the HTML element containing the Power BI embed component + * + * @type {HTMLElement} + */ + element: HTMLElement; + + /** + * Gets or sets the HTML iframe element that renders the Power BI embed component + * + * @type {HTMLIFrameElement} + */ + iframe: HTMLIFrameElement; + + /** + * Gets or sets the configuration settings for the embed component + * + * @type {IInternalEmbedConfiguration} + */ + config: IInternalEmbedConfiguration; + + /** + * Creates an instance of Embed. + * + * Note: there is circular reference between embeds and service + * The service has list of all embeds on the host page, and each embed has reference to the service that created it. + * + * @param {service.Service} service + * @param {HTMLElement} element + * @param {IEmbedConfiguration} config + */ + constructor(service: service.Service, element: HTMLElement, config: IEmbedConfiguration) { + Array.prototype.push.apply(this.allowedEvents, Embed.allowedEvents); + this.eventHandlers = []; + this.service = service; + this.element = element; + + // TODO: Change when Object.assign is available. + const settings = utils.assign({}, Embed.defaultSettings, config.settings); + this.config = utils.assign({ settings }, config); + this.config.accessToken = this.getAccessToken(service.accessToken); + this.config.embedUrl = this.getEmbedUrl(); + this.config.id = this.getId(); + this.config.uniqueId = this.getUniqueId(); + + const iframeHtml = ``; + + this.element.innerHTML = iframeHtml; + this.iframe = this.element.childNodes[0]; + this.iframe.addEventListener('load', () => this.load(this.config), false); + } + + /** + * Sends load configuration data. + * + * ```javascript + * report.load({ + * type: 'report', + * id: '5dac7a4a-4452-46b3-99f6-a25915e0fe55', + * accessToken: 'eyJ0eXA ... TaE2rTSbmg', + * settings: { + * navContentPaneEnabled: false + * }, + * pageName: "DefaultPage", + * filters: [ + * { + * ... DefaultReportFilter ... + * } + * ] + * }) + * .catch(error => { ... }); + * ``` + * + * @param {models.ILoadConfiguration} config + * @returns {Promise} + */ + load(config: models.ILoadConfiguration): Promise { + const errors = models.validateLoad(config); + if (errors) { + throw errors; } - /** - * Removes event handler(s) from list of handlers. - * - * If reference to existing handle function is specified remove specific handler. - * If handler is not specified, remove all handlers for the event name specified. - * - * ```javascript - * report.off('pageChanged') - * - * or - * - * const logHandler = function (event) { - * console.log(event); - * }; - * - * report.off('pageChanged', logHandler); - * ``` - * - * @template T - * @param {string} eventName - * @param {service.IEventHandler} [handler] - */ - off(eventName: string, handler?: service.IEventHandler): void { - const fakeEvent: service.IEvent = { name: eventName, type: null, id: null, value: null }; - if(handler) { - utils.remove(eventHandler => eventHandler.test(fakeEvent) && (eventHandler.handle === handler), this.eventHandlers); - this.element.removeEventListener(eventName, handler); - } - else { - const eventHandlersToRemove = this.eventHandlers - .filter(eventHandler => eventHandler.test(fakeEvent)); - - eventHandlersToRemove - .forEach(eventHandlerToRemove => { - utils.remove(eventHandler => eventHandler === eventHandlerToRemove, this.eventHandlers); - this.element.removeEventListener(eventName, eventHandlerToRemove.handle); - }); - } + return this.service.hpm.post('/report/load', config, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => { + return response.body; + }, + response => { + throw response.body; + }); + } + + /** + * Removes event handler(s) from list of handlers. + * + * If reference to existing handle function is specified remove specific handler. + * If handler is not specified, remove all handlers for the event name specified. + * + * ```javascript + * report.off('pageChanged') + * + * or + * + * const logHandler = function (event) { + * console.log(event); + * }; + * + * report.off('pageChanged', logHandler); + * ``` + * + * @template T + * @param {string} eventName + * @param {service.IEventHandler} [handler] + */ + off(eventName: string, handler?: service.IEventHandler): void { + const fakeEvent: service.IEvent = { name: eventName, type: null, id: null, value: null }; + if (handler) { + utils.remove(eventHandler => eventHandler.test(fakeEvent) && (eventHandler.handle === handler), this.eventHandlers); + this.element.removeEventListener(eventName, handler); } - - /** - * Adds event handler for specific event. - * - * ```javascript - * report.on('pageChanged', (event) => { - * console.log('PageChanged: ', event.page.name); - * }); - * ``` - * - * @template T - * @param {string} eventName - * @param {service.IEventHandler} handler - */ - on(eventName: string, handler: service.IEventHandler): void { - if(this.allowedEvents.indexOf(eventName) === -1) { - throw new Error(`eventName is must be one of ${this.allowedEvents}. You passed: ${eventName}`); - } - - this.eventHandlers.push({ - test: (event: service.IEvent) => event.name === eventName, - handle: handler + else { + const eventHandlersToRemove = this.eventHandlers + .filter(eventHandler => eventHandler.test(fakeEvent)); + + eventHandlersToRemove + .forEach(eventHandlerToRemove => { + utils.remove(eventHandler => eventHandler === eventHandlerToRemove, this.eventHandlers); + this.element.removeEventListener(eventName, eventHandlerToRemove.handle); }); - - this.element.addEventListener(eventName, handler) } - - /** - * Get access token from first available location: config, attribute, global. - * - * @private - * @param {string} globalAccessToken - * @returns {string} - */ - private getAccessToken(globalAccessToken: string): string { - const accessToken = this.config.accessToken || this.element.getAttribute(Embed.accessTokenAttribute) || globalAccessToken; - - if (!accessToken) { - throw new Error(`No access token was found for element. You must specify an access token directly on the element using attribute '${Embed.accessTokenAttribute}' or specify a global token at: powerbi.accessToken.`); - } - - return accessToken; + } + + /** + * Adds event handler for specific event. + * + * ```javascript + * report.on('pageChanged', (event) => { + * console.log('PageChanged: ', event.page.name); + * }); + * ``` + * + * @template T + * @param {string} eventName + * @param {service.IEventHandler} handler + */ + on(eventName: string, handler: service.IEventHandler): void { + if (this.allowedEvents.indexOf(eventName) === -1) { + throw new Error(`eventName is must be one of ${this.allowedEvents}. You passed: ${eventName}`); } - /** - * Get embed url from first available location: options, attribute. - * - * @private - * @returns {string} - */ - private getEmbedUrl(): string { - const embedUrl = this.config.embedUrl || this.element.getAttribute(Embed.embedUrlAttribute); + this.eventHandlers.push({ + test: (event: service.IEvent) => event.name === eventName, + handle: handler + }); + + this.element.addEventListener(eventName, handler) + } + + /** + * Get access token from first available location: config, attribute, global. + * + * @private + * @param {string} globalAccessToken + * @returns {string} + */ + private getAccessToken(globalAccessToken: string): string { + const accessToken = this.config.accessToken || this.element.getAttribute(Embed.accessTokenAttribute) || globalAccessToken; + + if (!accessToken) { + throw new Error(`No access token was found for element. You must specify an access token directly on the element using attribute '${Embed.accessTokenAttribute}' or specify a global token at: powerbi.accessToken.`); + } - if (typeof embedUrl !== 'string' || embedUrl.length === 0) { - throw new Error(`Embed Url is required, but it was not found. You must provide an embed url either as part of embed configuration or as attribute '${Embed.embedUrlAttribute}'.`); - } + return accessToken; + } - return embedUrl; - } + /** + * Get embed url from first available location: options, attribute. + * + * @private + * @returns {string} + */ + private getEmbedUrl(): string { + const embedUrl = this.config.embedUrl || this.element.getAttribute(Embed.embedUrlAttribute); - /** - * Get unique id from first available location: options, attribute. - * If neither is provided generate unique string. - * - * @private - * @returns {string} - */ - private getUniqueId(): string { - return this.config.uniqueId || this.element.getAttribute(Embed.nameAttribute) || utils.createRandomString(); + if (typeof embedUrl !== 'string' || embedUrl.length === 0) { + throw new Error(`Embed Url is required, but it was not found. You must provide an embed url either as part of embed configuration or as attribute '${Embed.embedUrlAttribute}'.`); } - /** - * Get report id from first available location: options, attribute. - * - * @abstract - * @returns {string} - */ - abstract getId(): string; - - /** - * Request the browser to make the component's iframe fullscreen. - */ - fullscreen(): void { - const requestFullScreen = this.iframe.requestFullscreen || this.iframe.msRequestFullscreen || this.iframe.mozRequestFullScreen || this.iframe.webkitRequestFullscreen; - requestFullScreen.call(this.iframe); + return embedUrl; + } + + /** + * Get unique id from first available location: options, attribute. + * If neither is provided generate unique string. + * + * @private + * @returns {string} + */ + private getUniqueId(): string { + return this.config.uniqueId || this.element.getAttribute(Embed.nameAttribute) || utils.createRandomString(); + } + + /** + * Get report id from first available location: options, attribute. + * + * @abstract + * @returns {string} + */ + abstract getId(): string; + + /** + * Request the browser to make the component's iframe fullscreen. + */ + fullscreen(): void { + const requestFullScreen = this.iframe.requestFullscreen || this.iframe.msRequestFullscreen || this.iframe.mozRequestFullScreen || this.iframe.webkitRequestFullscreen; + requestFullScreen.call(this.iframe); + } + + /** + * Exit fullscreen. + */ + exitFullscreen(): void { + if (!this.isFullscreen(this.iframe)) { + return; } - /** - * Exit fullscreen. - */ - exitFullscreen(): void { - if (!this.isFullscreen(this.iframe)) { - return; - } - - const exitFullscreen = document.exitFullscreen || document.mozCancelFullScreen || document.webkitExitFullscreen || document.msExitFullscreen; - exitFullscreen.call(document); - } + const exitFullscreen = document.exitFullscreen || document.mozCancelFullScreen || document.webkitExitFullscreen || document.msExitFullscreen; + exitFullscreen.call(document); + } - /** - * Return true if iframe is fullscreen, - * otherwise return false - * - * @private - * @param {HTMLIFrameElement} iframe - * @returns {boolean} - */ - private isFullscreen(iframe: HTMLIFrameElement): boolean { - const options = ['fullscreenElement', 'webkitFullscreenElement', 'mozFullscreenScreenElement', 'msFullscreenElement']; + /** + * Return true if iframe is fullscreen, + * otherwise return false + * + * @private + * @param {HTMLIFrameElement} iframe + * @returns {boolean} + */ + private isFullscreen(iframe: HTMLIFrameElement): boolean { + const options = ['fullscreenElement', 'webkitFullscreenElement', 'mozFullscreenScreenElement', 'msFullscreenElement']; - return options.some(option => document[option] === iframe); - } + return options.some(option => document[option] === iframe); + } } \ No newline at end of file diff --git a/src/factories.ts b/src/factories.ts index 4ea6ebf3..6058cee9 100644 --- a/src/factories.ts +++ b/src/factories.ts @@ -17,23 +17,23 @@ export { * TODO: Need to get sdk version and settings from package.json, Generate config file via gulp task? */ export const hpmFactory: IHpmFactory = (wpmp, defaultTargetWindow, sdkVersion = config.version, sdkType = config.type) => { - return new hpm.HttpPostMessage(wpmp, { - 'x-sdk-type': sdkType, - 'x-sdk-version': sdkVersion - }, defaultTargetWindow); + return new hpm.HttpPostMessage(wpmp, { + 'x-sdk-type': sdkType, + 'x-sdk-version': sdkVersion + }, defaultTargetWindow); }; export const wpmpFactory: IWpmpFactory = (name?: string, logMessages?: boolean, eventSourceOverrideWindow?: Window) => { - return new wpmp.WindowPostMessageProxy({ - processTrackingProperties: { - addTrackingProperties: hpm.HttpPostMessage.addTrackingProperties, - getTrackingProperties: hpm.HttpPostMessage.getTrackingProperties, - }, - isErrorMessage: hpm.HttpPostMessage.isErrorMessage, - name, - logMessages, - eventSourceOverrideWindow - }); + return new wpmp.WindowPostMessageProxy({ + processTrackingProperties: { + addTrackingProperties: hpm.HttpPostMessage.addTrackingProperties, + getTrackingProperties: hpm.HttpPostMessage.getTrackingProperties, + }, + isErrorMessage: hpm.HttpPostMessage.isErrorMessage, + name, + logMessages, + eventSourceOverrideWindow + }); }; export const routerFactory: IRouterFactory = (wpmp) => { diff --git a/src/ifilterable.ts b/src/ifilterable.ts index a820a8e3..a9b09f52 100644 --- a/src/ifilterable.ts +++ b/src/ifilterable.ts @@ -8,23 +8,23 @@ import * as models from 'powerbi-models'; * @interface IFilterable */ export interface IFilterable { - /** - * Gets the filters currently applied to the object - * - * @returns {(Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>)} - */ - getFilters(): Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>; - /** - * Replaces all filters on the current object with the specified filter values - * - * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters - * @returns {Promise} - */ - setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise; - /** - * Removes all filters from the current object - * - * @returns {Promise} - */ - removeFilters(): Promise; + /** + * Gets the filters currently applied to the object + * + * @returns {(Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>)} + */ + getFilters(): Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>; + /** + * Replaces all filters on the current object with the specified filter values + * + * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters + * @returns {Promise} + */ + setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise; + /** + * Removes all filters from the current object + * + * @returns {Promise} + */ + removeFilters(): Promise; } \ No newline at end of file diff --git a/src/page.ts b/src/page.ts index 8bbe40c0..c1bd44e3 100644 --- a/src/page.ts +++ b/src/page.ts @@ -10,8 +10,8 @@ import * as models from 'powerbi-models'; * @interface IPageNode */ export interface IPageNode { - report: IReportNode; - name: string; + report: IReportNode; + name: string; } /** @@ -23,150 +23,150 @@ export interface IPageNode { * @implements {IFilterable} */ export class Page implements IPageNode, IFilterable { - /** - * The parent Power BI report that this page is a member of - * - * @type {IReportNode} - */ - report: IReportNode; - /** - * The report page name - * - * @type {string} - */ - name: string; + /** + * The parent Power BI report that this page is a member of + * + * @type {IReportNode} + */ + report: IReportNode; + /** + * The report page name + * + * @type {string} + */ + name: string; - /** - * The user defined display name of the report page - * This can be undefined in cases where page is created manually - * - * @type {string} - */ - displayName: string; + /** + * The user defined display name of the report page + * This can be undefined in cases where page is created manually + * + * @type {string} + */ + displayName: string; - /** - * Creates an instance of a Power BI report page. - * - * @param {IReportNode} report - * @param {string} name - * @param {string} [displayName] - */ - constructor(report: IReportNode, name: string, displayName?: string) { - this.report = report; - this.name = name; - this.displayName = displayName; - } + /** + * Creates an instance of a Power BI report page. + * + * @param {IReportNode} report + * @param {string} name + * @param {string} [displayName] + */ + constructor(report: IReportNode, name: string, displayName?: string) { + this.report = report; + this.name = name; + this.displayName = displayName; + } - /** - * Gets all page level filters within report - * - * ```javascript - * page.getFilters() - * .then(pages => { ... }); - * ``` - * - * @returns {(Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>)} - */ - getFilters(): Promise<(models.IBasicFilter | models.IAdvancedFilter)[]> { - return this.report.service.hpm.get(`/report/pages/${this.name}/filters`, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) - .then(response => response.body, - response => { - throw response.body; - }); - } + /** + * Gets all page level filters within report + * + * ```javascript + * page.getFilters() + * .then(pages => { ... }); + * ``` + * + * @returns {(Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>)} + */ + getFilters(): Promise<(models.IBasicFilter | models.IAdvancedFilter)[]> { + return this.report.service.hpm.get(`/report/pages/${this.name}/filters`, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) + .then(response => response.body, + response => { + throw response.body; + }); + } - /** - * Gets all the visuals on the page. - * - * ```javascript - * page.getVisuals() - * .then(visuals => { ... }); - * ``` - * - * @returns {Promise} - */ - getVisuals(): Promise { - return this.report.service.hpm.get(`/report/pages/${this.name}/visuals`, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) - .then(response => { - return response.body - .map(visual => { - return new Visual(this, visual.name); - }); - }, response => { - throw response.body; - }); - } + /** + * Gets all the visuals on the page. + * + * ```javascript + * page.getVisuals() + * .then(visuals => { ... }); + * ``` + * + * @returns {Promise} + */ + getVisuals(): Promise { + return this.report.service.hpm.get(`/report/pages/${this.name}/visuals`, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) + .then(response => { + return response.body + .map(visual => { + return new Visual(this, visual.name); + }); + }, response => { + throw response.body; + }); + } - /** - * Remove all filters on this page within the report - * - * ```javascript - * page.removeFilters(); - * ``` - * - * @returns {Promise} - */ - removeFilters(): Promise { - return this.setFilters([]); - } + /** + * Remove all filters on this page within the report + * + * ```javascript + * page.removeFilters(); + * ``` + * + * @returns {Promise} + */ + removeFilters(): Promise { + return this.setFilters([]); + } - /** - * Make the current page the active page of the report. - * - * ```javascripot - * page.setActive(); - * ``` - * - * @returns {Promise} - */ - setActive(): Promise { - const page: models.IPage = { - name: this.name, - displayName: null - }; + /** + * Make the current page the active page of the report. + * + * ```javascripot + * page.setActive(); + * ``` + * + * @returns {Promise} + */ + setActive(): Promise { + const page: models.IPage = { + name: this.name, + displayName: null + }; - return this.report.service.hpm.put('/report/pages/active', page, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) - .catch(response => { - throw response.body; - }); - } + return this.report.service.hpm.put('/report/pages/active', page, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) + .catch(response => { + throw response.body; + }); + } - /** - * Sets all filters on the current page. - * - * ```javascript - * page.setFilters(filters); - * .catch(errors => { ... }); - * ``` - * - * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters - * @returns {Promise} - */ - setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise { - return this.report.service.hpm.put(`/report/pages/${this.name}/filters`, filters, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) - .catch(response => { - throw response.body; - }); - } + /** + * Sets all filters on the current page. + * + * ```javascript + * page.setFilters(filters); + * .catch(errors => { ... }); + * ``` + * + * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters + * @returns {Promise} + */ + setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise { + return this.report.service.hpm.put(`/report/pages/${this.name}/filters`, filters, { uid: this.report.config.uniqueId }, this.report.iframe.contentWindow) + .catch(response => { + throw response.body; + }); + } - /** - * Creates new Visual object given a name of the visual. - * - * Normally you would get Visual objects by calling `page.getVisuals()` but in the case - * that the visual name is known and you want to perform an action on a visaul such as setting a filters - * without having to retrieve it first you can create it directly. - * - * Note: Since you are creating the visual manually there is no guarantee that the visual actually exists in the report and the subsequence requests could fail. - * - * ```javascript - * const visual = report.page('ReportSection1').visual('BarChart1'); - * visual.setFilters(filters); - * ``` - * - * @param {string} name - * @returns {Visual} - */ - visual(name: string): Visual { - return new Visual(this, name); - } + /** + * Creates new Visual object given a name of the visual. + * + * Normally you would get Visual objects by calling `page.getVisuals()` but in the case + * that the visual name is known and you want to perform an action on a visaul such as setting a filters + * without having to retrieve it first you can create it directly. + * + * Note: Since you are creating the visual manually there is no guarantee that the visual actually exists in the report and the subsequence requests could fail. + * + * ```javascript + * const visual = report.page('ReportSection1').visual('BarChart1'); + * visual.setFilters(filters); + * ``` + * + * @param {string} name + * @returns {Visual} + */ + visual(name: string): Visual { + return new Visual(this, name); + } } \ No newline at end of file diff --git a/src/powerbi.ts b/src/powerbi.ts index 50ece85b..5a7ae057 100644 --- a/src/powerbi.ts +++ b/src/powerbi.ts @@ -4,33 +4,33 @@ import * as models from 'powerbi-models'; import { IFilterable } from './ifilterable'; export { - IFilterable, - service, - factories, - models + IFilterable, + service, + factories, + models }; export { - Report + Report } from './report'; export { - Tile + Tile } from './tile'; export { - IEmbedConfiguration, - Embed + IEmbedConfiguration, + Embed } from './embed'; export { - Page + Page } from './page'; export { - Visual + Visual } from './visual'; declare var powerbi: service.Service; declare global { - interface Window { - powerbi: service.Service; - } + interface Window { + powerbi: service.Service; + } } /** diff --git a/src/report.ts b/src/report.ts index c1abf4e5..0697334b 100644 --- a/src/report.ts +++ b/src/report.ts @@ -14,9 +14,9 @@ import { IPageNode, Page } from './page'; * @interface IReportNode */ export interface IReportNode { - iframe: HTMLIFrameElement; - service: service.IService; - config: embed.IInternalEmbedConfiguration + iframe: HTMLIFrameElement; + service: service.IService; + config: embed.IInternalEmbedConfiguration } /** @@ -29,217 +29,217 @@ export interface IReportNode { * @implements {IFilterable} */ export class Report extends embed.Embed implements IReportNode, IFilterable { - static allowedEvents = ["dataSelected", "filtersApplied", "pageChanged", "error"]; - static reportIdAttribute = 'powerbi-report-id'; - static filterPaneEnabledAttribute = 'powerbi-settings-filter-pane-enabled'; - static navContentPaneEnabledAttribute = 'powerbi-settings-nav-content-pane-enabled'; - static typeAttribute = 'powerbi-type'; - static type = "Report"; - - /** - * Creates an instance of a Power BI Report. - * - * @param {service.Service} service - * @param {HTMLElement} element - * @param {embed.IEmbedConfiguration} config - */ - constructor(service: service.Service, element: HTMLElement, config: embed.IEmbedConfiguration) { - const filterPaneEnabled = (config.settings && config.settings.filterPaneEnabled) || !(element.getAttribute(Report.filterPaneEnabledAttribute) === "false"); - const navContentPaneEnabled = (config.settings && config.settings.navContentPaneEnabled) || !(element.getAttribute(Report.navContentPaneEnabledAttribute) === "false"); - const settings = utils.assign({ - filterPaneEnabled, - navContentPaneEnabled - }, config.settings); - const configCopy = utils.assign({ settings }, config); - - super(service, element, configCopy); - Array.prototype.push.apply(this.allowedEvents, Report.allowedEvents); + static allowedEvents = ["dataSelected", "filtersApplied", "pageChanged", "error"]; + static reportIdAttribute = 'powerbi-report-id'; + static filterPaneEnabledAttribute = 'powerbi-settings-filter-pane-enabled'; + static navContentPaneEnabledAttribute = 'powerbi-settings-nav-content-pane-enabled'; + static typeAttribute = 'powerbi-type'; + static type = "Report"; + + /** + * Creates an instance of a Power BI Report. + * + * @param {service.Service} service + * @param {HTMLElement} element + * @param {embed.IEmbedConfiguration} config + */ + constructor(service: service.Service, element: HTMLElement, config: embed.IEmbedConfiguration) { + const filterPaneEnabled = (config.settings && config.settings.filterPaneEnabled) || !(element.getAttribute(Report.filterPaneEnabledAttribute) === "false"); + const navContentPaneEnabled = (config.settings && config.settings.navContentPaneEnabled) || !(element.getAttribute(Report.navContentPaneEnabledAttribute) === "false"); + const settings = utils.assign({ + filterPaneEnabled, + navContentPaneEnabled + }, config.settings); + const configCopy = utils.assign({ settings }, config); + + super(service, element, configCopy); + Array.prototype.push.apply(this.allowedEvents, Report.allowedEvents); + } + + /** + * This adds backwards compatibility for older config which used the reportId query param to specify report id. + * E.g. http://embedded.powerbi.com/appTokenReportEmbed?reportId=854846ed-2106-4dc2-bc58-eb77533bf2f1 + * + * By extracting the id we can ensure id is always explicitly provided as part of the load configuration. + * + * @static + * @param {string} url + * @returns {string} + */ + static findIdFromEmbedUrl(url: string): string { + const reportIdRegEx = /reportId="?([^&]+)"?/ + const reportIdMatch = url.match(reportIdRegEx); + + let reportId; + if (reportIdMatch) { + reportId = reportIdMatch[1]; } - /** - * This adds backwards compatibility for older config which used the reportId query param to specify report id. - * E.g. http://embedded.powerbi.com/appTokenReportEmbed?reportId=854846ed-2106-4dc2-bc58-eb77533bf2f1 - * - * By extracting the id we can ensure id is always explicitly provided as part of the load configuration. - * - * @static - * @param {string} url - * @returns {string} - */ - static findIdFromEmbedUrl(url: string): string { - const reportIdRegEx = /reportId="?([^&]+)"?/ - const reportIdMatch = url.match(reportIdRegEx); - - let reportId; - if (reportIdMatch) { - reportId = reportIdMatch[1]; - } - - return reportId; + return reportId; + } + + /** + * Get filters that are applied at the report level + * + * ```javascript + * // Get filters applied at report level + * report.getFilters() + * .then(filters => { + * ... + * }); + * ``` + * + * @returns {Promise} + */ + getFilters(): Promise { + return this.service.hpm.get(`/report/filters`, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => response.body, + response => { + throw response.body; + }); + } + + /** + * Get report id from first available location: options, attribute, embed url. + * + * @returns {string} + */ + getId(): string { + const reportId = this.config.id || this.element.getAttribute(Report.reportIdAttribute) || Report.findIdFromEmbedUrl(this.config.embedUrl); + + if (typeof reportId !== 'string' || reportId.length === 0) { + throw new Error(`Report id is required, but it was not found. You must provide an id either as part of embed configuration or as attribute '${Report.reportIdAttribute}'.`); } - /** - * Get filters that are applied at the report level - * - * ```javascript - * // Get filters applied at report level - * report.getFilters() - * .then(filters => { - * ... - * }); - * ``` - * - * @returns {Promise} - */ - getFilters(): Promise { - return this.service.hpm.get(`/report/filters`, { uid: this.config.uniqueId }, this.iframe.contentWindow) - .then(response => response.body, - response => { - throw response.body; - }); - } - - /** - * Get report id from first available location: options, attribute, embed url. - * - * @returns {string} - */ - getId(): string { - const reportId = this.config.id || this.element.getAttribute(Report.reportIdAttribute) || Report.findIdFromEmbedUrl(this.config.embedUrl); - - if (typeof reportId !== 'string' || reportId.length === 0) { - throw new Error(`Report id is required, but it was not found. You must provide an id either as part of embed configuration or as attribute '${Report.reportIdAttribute}'.`); - } - - return reportId; - } - - /** - * Get the list of pages within the report - * - * ```javascript - * report.getPages() - * .then(pages => { - * ... - * }); - * ``` - * - * @returns {Promise} - */ - getPages(): Promise { - return this.service.hpm.get('/report/pages', { uid: this.config.uniqueId }, this.iframe.contentWindow) - .then(response => { - return response.body - .map(page => { - return new Page(this, page.name, page.displayName); - }); - }, response => { - throw response.body; - }); - } - - /** - * Create new Page instance. - * - * Normally you would get Page objects by calling `report.getPages()` but in the case - * that the page name is known and you want to perform an action on a page without having to retrieve it - * you can create it directly. - * - * Note: Since you are creating the page manually there is no guarantee that the page actually exists in the report and the subsequence requests could fail. - * - * ```javascript - * const page = report.page('ReportSection1'); - * page.setActive(); - * ``` - * - * @param {string} name - * @param {string} [displayName] - * @returns {Page} - */ - page(name: string, displayName?: string): Page { - return new Page(this, name, displayName); - } - - /** - * Remove all filters at report level - * - * ```javascript - * report.removeFilters(); - * ``` - * - * @returns {Promise} - */ - removeFilters(): Promise { - return this.setFilters([]); - } - - /** - * Set the active page - * - * ```javascript - * report.setPage("page2") - * .catch(error => { ... }); - * ``` - * - * @param {string} pageName - * @returns {Promise} - */ - setPage(pageName: string): Promise { - const page: models.IPage = { - name: pageName, - displayName: null - }; - - return this.service.hpm.put('/report/pages/active', page, { uid: this.config.uniqueId }, this.iframe.contentWindow) - .catch(response => { - throw response.body; - }); - } - - /** - * Sets filters - * - * ```javascript - * const filters: [ - * ... - * ]; - * - * report.setFilters(filters) - * .catch(errors => { - * ... - * }); - * ``` - * - * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters - * @returns {Promise} - */ - setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise { - return this.service.hpm.put(`/report/filters`, filters, { uid: this.config.uniqueId }, this.iframe.contentWindow) - .catch(response => { - throw response.body; - }); - } - - /** - * Update settings of report (filter pane visibility, page navigation visibility) - * - * ```javascript - * const newSettings = { - * navContentPaneEnabled: true, - * filterPaneEnabled: false - * }; - * - * report.updateSettings(newSettings) - * .catch(error => { ... }); - * ``` - * - * @param {models.ISettings} settings - * @returns {Promise} - */ - updateSettings(settings: models.ISettings): Promise { - return this.service.hpm.patch('/report/settings', settings, { uid: this.config.uniqueId }, this.iframe.contentWindow) - .catch(response => { - throw response.body; - }); - } + return reportId; + } + + /** + * Get the list of pages within the report + * + * ```javascript + * report.getPages() + * .then(pages => { + * ... + * }); + * ``` + * + * @returns {Promise} + */ + getPages(): Promise { + return this.service.hpm.get('/report/pages', { uid: this.config.uniqueId }, this.iframe.contentWindow) + .then(response => { + return response.body + .map(page => { + return new Page(this, page.name, page.displayName); + }); + }, response => { + throw response.body; + }); + } + + /** + * Create new Page instance. + * + * Normally you would get Page objects by calling `report.getPages()` but in the case + * that the page name is known and you want to perform an action on a page without having to retrieve it + * you can create it directly. + * + * Note: Since you are creating the page manually there is no guarantee that the page actually exists in the report and the subsequence requests could fail. + * + * ```javascript + * const page = report.page('ReportSection1'); + * page.setActive(); + * ``` + * + * @param {string} name + * @param {string} [displayName] + * @returns {Page} + */ + page(name: string, displayName?: string): Page { + return new Page(this, name, displayName); + } + + /** + * Remove all filters at report level + * + * ```javascript + * report.removeFilters(); + * ``` + * + * @returns {Promise} + */ + removeFilters(): Promise { + return this.setFilters([]); + } + + /** + * Set the active page + * + * ```javascript + * report.setPage("page2") + * .catch(error => { ... }); + * ``` + * + * @param {string} pageName + * @returns {Promise} + */ + setPage(pageName: string): Promise { + const page: models.IPage = { + name: pageName, + displayName: null + }; + + return this.service.hpm.put('/report/pages/active', page, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .catch(response => { + throw response.body; + }); + } + + /** + * Sets filters + * + * ```javascript + * const filters: [ + * ... + * ]; + * + * report.setFilters(filters) + * .catch(errors => { + * ... + * }); + * ``` + * + * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters + * @returns {Promise} + */ + setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise { + return this.service.hpm.put(`/report/filters`, filters, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .catch(response => { + throw response.body; + }); + } + + /** + * Update settings of report (filter pane visibility, page navigation visibility) + * + * ```javascript + * const newSettings = { + * navContentPaneEnabled: true, + * filterPaneEnabled: false + * }; + * + * report.updateSettings(newSettings) + * .catch(error => { ... }); + * ``` + * + * @param {models.ISettings} settings + * @returns {Promise} + */ + updateSettings(settings: models.ISettings): Promise { + return this.service.hpm.patch('/report/settings', settings, { uid: this.config.uniqueId }, this.iframe.contentWindow) + .catch(response => { + throw response.body; + }); + } } \ No newline at end of file diff --git a/src/service.ts b/src/service.ts index 5b58c9ab..01b4090a 100644 --- a/src/service.ts +++ b/src/service.ts @@ -9,50 +9,50 @@ import * as router from 'powerbi-router'; import * as models from 'powerbi-models'; export interface IEvent { - type: string; - id: string; - name: string; - value: T; + type: string; + id: string; + name: string; + value: T; } export interface ICustomEvent extends CustomEvent { - detail: T; + detail: T; } export interface IEventHandler { - (event: ICustomEvent): any; + (event: ICustomEvent): any; } export interface IHpmFactory { - (wpmp: wpmp.WindowPostMessageProxy, targetWindow?: Window, version?: string, type?: string, origin?: string): hpm.HttpPostMessage; + (wpmp: wpmp.WindowPostMessageProxy, targetWindow?: Window, version?: string, type?: string, origin?: string): hpm.HttpPostMessage; } export interface IWpmpFactory { - (name?: string, logMessages?: boolean, eventSourceOverrideWindow?: Window): wpmp.WindowPostMessageProxy; + (name?: string, logMessages?: boolean, eventSourceOverrideWindow?: Window): wpmp.WindowPostMessageProxy; } export interface IRouterFactory { - (wpmp: wpmp.WindowPostMessageProxy): router.Router; + (wpmp: wpmp.WindowPostMessageProxy): router.Router; } export interface IPowerBiElement extends HTMLElement { - powerBiEmbed: embed.Embed; + powerBiEmbed: embed.Embed; } export interface IDebugOptions { - logMessages?: boolean; - wpmpName?: string; + logMessages?: boolean; + wpmpName?: string; } export interface IServiceConfiguration extends IDebugOptions { - autoEmbedOnContentLoaded?: boolean; - onError?: (error: any) => any; - version?: string; - type?: string; + autoEmbedOnContentLoaded?: boolean; + onError?: (error: any) => any; + version?: string; + type?: string; } export interface IService { - hpm: hpm.HttpPostMessage; + hpm: hpm.HttpPostMessage; } /** @@ -64,269 +64,269 @@ export interface IService { */ export class Service implements IService { - /** - * List of components this service can embed. - */ - private static components: (typeof Report | typeof Tile)[] = [ - Tile, - Report - ]; - - /** - * Default configuration for service. - */ - private static defaultConfig: IServiceConfiguration = { - autoEmbedOnContentLoaded: false, - onError: (...args) => console.log(args[0], args.slice(1)) - }; - - /** - * Gets or sets the access token as fallback/global token to use when local token for report/tile is not provided. - * - * @type {string} - */ - accessToken: string; - - /** Configuration object */ - private config: IServiceConfiguration; - - /** List of components (Reports/Tiles) that have been embedded using this service instance. */ - private embeds: embed.Embed[]; - /** TODO: Look for way to make hpm private without sacraficing ease of maitenance. This should be private but in embed needs to call methods. */ - hpm: hpm.HttpPostMessage; - /** TODO: Look for way to make wpmp private. This is only public to allow stopping the wpmp in tests */ - wpmp: wpmp.WindowPostMessageProxy; - private router: router.Router; + /** + * List of components this service can embed. + */ + private static components: (typeof Report | typeof Tile)[] = [ + Tile, + Report + ]; + + /** + * Default configuration for service. + */ + private static defaultConfig: IServiceConfiguration = { + autoEmbedOnContentLoaded: false, + onError: (...args) => console.log(args[0], args.slice(1)) + }; + + /** + * Gets or sets the access token as fallback/global token to use when local token for report/tile is not provided. + * + * @type {string} + */ + accessToken: string; + + /** Configuration object */ + private config: IServiceConfiguration; + + /** List of components (Reports/Tiles) that have been embedded using this service instance. */ + private embeds: embed.Embed[]; + /** TODO: Look for way to make hpm private without sacraficing ease of maitenance. This should be private but in embed needs to call methods. */ + hpm: hpm.HttpPostMessage; + /** TODO: Look for way to make wpmp private. This is only public to allow stopping the wpmp in tests */ + wpmp: wpmp.WindowPostMessageProxy; + private router: router.Router; + + /** + * Creates an instance of Power BI embed service. + * + * @param {IHpmFactory} hpmFactory The http post message factory used in the postMessage communication layer + * @param {IWpmpFactory} wpmpFactory The window post message factory used in the postMessage communication layer + * @param {IRouterFactory} routerFactory The router factory used in the postMessage communication layer + * @param {IServiceConfiguration} [config={}] + */ + constructor(hpmFactory: IHpmFactory, wpmpFactory: IWpmpFactory, routerFactory: IRouterFactory, config: IServiceConfiguration = {}) { + this.wpmp = wpmpFactory(config.wpmpName, config.logMessages); + this.hpm = hpmFactory(this.wpmp, null, config.version, config.type); + this.router = routerFactory(this.wpmp); /** - * Creates an instance of Power BI embed service. - * - * @param {IHpmFactory} hpmFactory The http post message factory used in the postMessage communication layer - * @param {IWpmpFactory} wpmpFactory The window post message factory used in the postMessage communication layer - * @param {IRouterFactory} routerFactory The router factory used in the postMessage communication layer - * @param {IServiceConfiguration} [config={}] + * Add handler for report events */ - constructor(hpmFactory: IHpmFactory, wpmpFactory: IWpmpFactory, routerFactory: IRouterFactory, config: IServiceConfiguration = {}) { - this.wpmp = wpmpFactory(config.wpmpName, config.logMessages); - this.hpm = hpmFactory(this.wpmp, null, config.version, config.type); - this.router = routerFactory(this.wpmp); - - /** - * Add handler for report events - */ - this.router.post(`/reports/:uniqueId/events/:eventName`, (req, res) => { - const event: IEvent = { - type: 'report', - id: req.params.uniqueId, - name: req.params.eventName, - value: req.body - }; - - this.handleEvent(event); - }); - this.router.post(`/reports/:uniqueId/pages/:pageName/events/:eventName`, (req, res) => { - const event: IEvent = { - type: 'report', - id: req.params.uniqueId, - name: req.params.eventName, - value: req.body - }; - - this.handleEvent(event); - }); - this.router.post(`/reports/:uniqueId/pages/:pageName/visuals/:pageName/events/:eventName`, (req, res) => { - const event: IEvent = { - type: 'report', - id: req.params.uniqueId, - name: req.params.eventName, - value: req.body - }; - - this.handleEvent(event); - }); - - this.embeds = []; - - // TODO: Change when Object.assign is available. - this.config = utils.assign({}, Service.defaultConfig, config); - - if (this.config.autoEmbedOnContentLoaded) { - this.enableAutoEmbed(); - } + this.router.post(`/reports/:uniqueId/events/:eventName`, (req, res) => { + const event: IEvent = { + type: 'report', + id: req.params.uniqueId, + name: req.params.eventName, + value: req.body + }; + + this.handleEvent(event); + }); + this.router.post(`/reports/:uniqueId/pages/:pageName/events/:eventName`, (req, res) => { + const event: IEvent = { + type: 'report', + id: req.params.uniqueId, + name: req.params.eventName, + value: req.body + }; + + this.handleEvent(event); + }); + this.router.post(`/reports/:uniqueId/pages/:pageName/visuals/:pageName/events/:eventName`, (req, res) => { + const event: IEvent = { + type: 'report', + id: req.params.uniqueId, + name: req.params.eventName, + value: req.body + }; + + this.handleEvent(event); + }); + + this.embeds = []; + + // TODO: Change when Object.assign is available. + this.config = utils.assign({}, Service.defaultConfig, config); + + if (this.config.autoEmbedOnContentLoaded) { + this.enableAutoEmbed(); } - - /** - * Handler for DOMContentLoaded which searches DOM for elements having 'powerbi-embed-url' attribute - * and automatically attempts to embed a powerbi component based on information from the attributes. - * Only runs if `config.autoEmbedOnContentLoaded` is true when the service is created. - * - * @param {HTMLElement} [container] - * @param {embed.IEmbedConfiguration} [config=undefined] - * @returns {embed.Embed[]} - */ - init(container?: HTMLElement, config: embed.IEmbedConfiguration = undefined): embed.Embed[] { - container = (container && container instanceof HTMLElement) ? container : document.body; - - const elements = Array.prototype.slice.call(container.querySelectorAll(`[${embed.Embed.embedUrlAttribute}]`)); - return elements.map(element => this.embed(element, config)); + } + + /** + * Handler for DOMContentLoaded which searches DOM for elements having 'powerbi-embed-url' attribute + * and automatically attempts to embed a powerbi component based on information from the attributes. + * Only runs if `config.autoEmbedOnContentLoaded` is true when the service is created. + * + * @param {HTMLElement} [container] + * @param {embed.IEmbedConfiguration} [config=undefined] + * @returns {embed.Embed[]} + */ + init(container?: HTMLElement, config: embed.IEmbedConfiguration = undefined): embed.Embed[] { + container = (container && container instanceof HTMLElement) ? container : document.body; + + const elements = Array.prototype.slice.call(container.querySelectorAll(`[${embed.Embed.embedUrlAttribute}]`)); + return elements.map(element => this.embed(element, config)); + } + + /** + * Given an html element embed component based on configuration. + * If component has already been created and attached to element re-use component instance and existing iframe, + * otherwise create a new component instance + * + * @param {HTMLElement} element + * @param {embed.IEmbedConfiguration} [config={}] + * @returns {embed.Embed} + */ + embed(element: HTMLElement, config: embed.IEmbedConfiguration = {}): embed.Embed { + let component: embed.Embed; + let powerBiElement = element; + + if (powerBiElement.powerBiEmbed) { + component = this.embedExisting(powerBiElement, config); } - - /** - * Given an html element embed component based on configuration. - * If component has already been created and attached to element re-use component instance and existing iframe, - * otherwise create a new component instance - * - * @param {HTMLElement} element - * @param {embed.IEmbedConfiguration} [config={}] - * @returns {embed.Embed} - */ - embed(element: HTMLElement, config: embed.IEmbedConfiguration = {}): embed.Embed { - let component: embed.Embed; - let powerBiElement = element; - - if (powerBiElement.powerBiEmbed) { - component = this.embedExisting(powerBiElement, config); - } - else { - component = this.embedNew(powerBiElement, config); - } - - return component; + else { + component = this.embedNew(powerBiElement, config); } - /** - * Given an html element embed component base configuration. - * Save component instance on element for later lookup. - * - * @private - * @param {IPowerBiElement} element - * @param {embed.IEmbedConfiguration} config - * @returns {embed.Embed} - */ - private embedNew(element: IPowerBiElement, config: embed.IEmbedConfiguration): embed.Embed { - const componentType = config.type || element.getAttribute(embed.Embed.typeAttribute); - if (!componentType) { - throw new Error(`Attempted to embed using config ${JSON.stringify(config)} on element ${element.outerHTML}, but could not determine what type of component to embed. You must specify a type in the configuration or as an attribute such as '${embed.Embed.typeAttribute}="${Report.type.toLowerCase()}"'.`); - } - - // Save type on configuration so it can be referenced later at known location - config.type = componentType; - - const Component = utils.find(component => componentType === component.type.toLowerCase(), Service.components); - if (!Component) { - throw new Error(`Attempted to embed component of type: ${componentType} but did not find any matching component. Please verify the type you specified is intended.`); - } - - const component = new Component(this, element, config); - element.powerBiEmbed = component; - this.embeds.push(component); - - return component; + return component; + } + + /** + * Given an html element embed component base configuration. + * Save component instance on element for later lookup. + * + * @private + * @param {IPowerBiElement} element + * @param {embed.IEmbedConfiguration} config + * @returns {embed.Embed} + */ + private embedNew(element: IPowerBiElement, config: embed.IEmbedConfiguration): embed.Embed { + const componentType = config.type || element.getAttribute(embed.Embed.typeAttribute); + if (!componentType) { + throw new Error(`Attempted to embed using config ${JSON.stringify(config)} on element ${element.outerHTML}, but could not determine what type of component to embed. You must specify a type in the configuration or as an attribute such as '${embed.Embed.typeAttribute}="${Report.type.toLowerCase()}"'.`); } - /** - * Given and element which arleady contains embed, load with new configuration - * - * @private - * @param {IPowerBiElement} element - * @param {embed.IEmbedConfiguration} config - * @returns {embed.Embed} - */ - private embedExisting(element: IPowerBiElement, config: embed.IEmbedConfiguration): embed.Embed { - const component = utils.find(x => x.element === element, this.embeds); - if (!component) { - throw new Error(`Attempted to embed using config ${JSON.stringify(config)} on element ${element.outerHTML} which already has embedded comopnent associated, but could not find the existing comopnent in the list of active components. This could indicate the embeds list is out of sync with the DOM, or the component is referencing the incorrect HTML element.`); - } - - component.load(config); + // Save type on configuration so it can be referenced later at known location + config.type = componentType; - return component; + const Component = utils.find(component => componentType === component.type.toLowerCase(), Service.components); + if (!Component) { + throw new Error(`Attempted to embed component of type: ${componentType} but did not find any matching component. Please verify the type you specified is intended.`); } - /** - * Adds event handler for DOMContentLoaded which finds all elements in DOM with attribute powerbi-embed-url - * then attempts to initiate the embed process based on data from other powerbi-* attributes. - * (This is usually only useful for applications rendered on by the server since all the data needed will be available by the time the handler is called.) - */ - enableAutoEmbed(): void { - window.addEventListener('DOMContentLoaded', (event: Event) => this.init(document.body), false); + const component = new Component(this, element, config); + element.powerBiEmbed = component; + this.embeds.push(component); + + return component; + } + + /** + * Given and element which arleady contains embed, load with new configuration + * + * @private + * @param {IPowerBiElement} element + * @param {embed.IEmbedConfiguration} config + * @returns {embed.Embed} + */ + private embedExisting(element: IPowerBiElement, config: embed.IEmbedConfiguration): embed.Embed { + const component = utils.find(x => x.element === element, this.embeds); + if (!component) { + throw new Error(`Attempted to embed using config ${JSON.stringify(config)} on element ${element.outerHTML} which already has embedded comopnent associated, but could not find the existing comopnent in the list of active components. This could indicate the embeds list is out of sync with the DOM, or the component is referencing the incorrect HTML element.`); } - /** - * Returns instance of component associated with element. - * - * @param {HTMLElement} element - * @returns {(Report | Tile)} - */ - get(element: HTMLElement): Report | Tile { - const powerBiElement = element; - - if (!powerBiElement.powerBiEmbed) { - throw new Error(`You attempted to get an instance of powerbi component associated with element: ${element.outerHTML} but there was no associated instance.`); - } - - return powerBiElement.powerBiEmbed; + component.load(config); + + return component; + } + + /** + * Adds event handler for DOMContentLoaded which finds all elements in DOM with attribute powerbi-embed-url + * then attempts to initiate the embed process based on data from other powerbi-* attributes. + * (This is usually only useful for applications rendered on by the server since all the data needed will be available by the time the handler is called.) + */ + enableAutoEmbed(): void { + window.addEventListener('DOMContentLoaded', (event: Event) => this.init(document.body), false); + } + + /** + * Returns instance of component associated with element. + * + * @param {HTMLElement} element + * @returns {(Report | Tile)} + */ + get(element: HTMLElement): Report | Tile { + const powerBiElement = element; + + if (!powerBiElement.powerBiEmbed) { + throw new Error(`You attempted to get an instance of powerbi component associated with element: ${element.outerHTML} but there was no associated instance.`); } - /** - * Find embed instance by name / unique id provided. - * - * @param {string} uniqueId - * @returns {(Report | Tile)} - */ - find(uniqueId: string): Report | Tile { - return utils.find(x => x.config.uniqueId === uniqueId, this.embeds); + return powerBiElement.powerBiEmbed; + } + + /** + * Find embed instance by name / unique id provided. + * + * @param {string} uniqueId + * @returns {(Report | Tile)} + */ + find(uniqueId: string): Report | Tile { + return utils.find(x => x.config.uniqueId === uniqueId, this.embeds); + } + + /** + * Given an html element which has component embedded within it, remove the component from list of embeds, remove association with component, and remove the iframe. + * + * @param {HTMLElement} element + * @returns {void} + */ + reset(element: HTMLElement): void { + const powerBiElement = element; + + if (!powerBiElement.powerBiEmbed) { + return; } - /** - * Given an html element which has component embedded within it, remove the component from list of embeds, remove association with component, and remove the iframe. - * - * @param {HTMLElement} element - * @returns {void} - */ - reset(element: HTMLElement): void { - const powerBiElement = element; - - if (!powerBiElement.powerBiEmbed) { - return; - } - - /** Remove component from internal list */ - utils.remove(x => x === powerBiElement.powerBiEmbed, this.embeds); - /** Delete property from html element */ - delete powerBiElement.powerBiEmbed; - /** Remove iframe from element */ - const iframe = element.querySelector('iframe'); - if (iframe) { - iframe.remove(); - } + /** Remove component from internal list */ + utils.remove(x => x === powerBiElement.powerBiEmbed, this.embeds); + /** Delete property from html element */ + delete powerBiElement.powerBiEmbed; + /** Remove iframe from element */ + const iframe = element.querySelector('iframe'); + if (iframe) { + iframe.remove(); } - - /** - * Given an event object, find embed with matching type and id and invoke its handleEvent method with event. - * - * @private - * @param {IEvent} event - */ - private handleEvent(event: IEvent): void { - const embed = utils.find(embed => { - return (embed.config.type === event.type - && embed.config.uniqueId === event.id); - }, this.embeds); - - if (embed) { - const value = event.value; - - if (event.name === 'pageChanged') { - const pageKey = 'newPage'; - const page: models.IPage = value[pageKey]; - if (!page) { - throw new Error(`Page model not found at 'event.value.${pageKey}'.`); - } - value[pageKey] = new Page(embed, page.name, page.displayName); - } - - utils.raiseCustomEvent(embed.element, event.name, value); + } + + /** + * Given an event object, find embed with matching type and id and invoke its handleEvent method with event. + * + * @private + * @param {IEvent} event + */ + private handleEvent(event: IEvent): void { + const embed = utils.find(embed => { + return (embed.config.type === event.type + && embed.config.uniqueId === event.id); + }, this.embeds); + + if (embed) { + const value = event.value; + + if (event.name === 'pageChanged') { + const pageKey = 'newPage'; + const page: models.IPage = value[pageKey]; + if (!page) { + throw new Error(`Page model not found at 'event.value.${pageKey}'.`); } + value[pageKey] = new Page(embed, page.name, page.displayName); + } + + utils.raiseCustomEvent(embed.element, event.name, value); } + } } \ No newline at end of file diff --git a/src/tile.ts b/src/tile.ts index 0cc9d0c5..65fe84fe 100644 --- a/src/tile.ts +++ b/src/tile.ts @@ -8,14 +8,14 @@ import { Embed } from './embed'; * @extends {Embed} */ export class Tile extends Embed { - static type = "Tile"; + static type = "Tile"; - /** - * The the id of the tile - * - * @returns {string} - */ - getId(): string { - throw Error('Not implemented. Embedding tiles is not supported yet.'); - } + /** + * The the id of the tile + * + * @returns {string} + */ + getId(): string { + throw Error('Not implemented. Embedding tiles is not supported yet.'); + } } \ No newline at end of file diff --git a/src/util.ts b/src/util.ts index 67d45cb6..3721bdf9 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,19 +8,19 @@ * @param {*} eventData */ export function raiseCustomEvent(element: HTMLElement, eventName: string, eventData: any): void { - let customEvent; - if (typeof CustomEvent === 'function') { - customEvent = new CustomEvent(eventName, { - detail: eventData, - bubbles: true, - cancelable: true - }); - } else { - customEvent = document.createEvent('CustomEvent'); - customEvent.initCustomEvent(eventName, true, true, eventData); - } + let customEvent; + if (typeof CustomEvent === 'function') { + customEvent = new CustomEvent(eventName, { + detail: eventData, + bubbles: true, + cancelable: true + }); + } else { + customEvent = document.createEvent('CustomEvent'); + customEvent.initCustomEvent(eventName, true, true, eventData); + } - element.dispatchEvent(customEvent); + element.dispatchEvent(customEvent); } /** @@ -33,19 +33,19 @@ export function raiseCustomEvent(element: HTMLElement, eventName: string, eventD * @returns {number} */ export function findIndex(predicate: (x: T) => boolean, xs: T[]): number { - if (!Array.isArray(xs)) { - throw new Error(`You attempted to call find with second parameter that was not an array. You passed: ${xs}`); - } + if (!Array.isArray(xs)) { + throw new Error(`You attempted to call find with second parameter that was not an array. You passed: ${xs}`); + } - let index; - xs.some((x, i) => { - if (predicate(x)) { - index = i; - return true; - } - }); + let index; + xs.some((x, i) => { + if (predicate(x)) { + index = i; + return true; + } + }); - return index; + return index; } /** @@ -58,13 +58,13 @@ export function findIndex(predicate: (x: T) => boolean, xs: T[]): number { * @returns {T} */ export function find(predicate: (x: T) => boolean, xs: T[]): T { - const index = findIndex(predicate, xs); - return xs[index]; + const index = findIndex(predicate, xs); + return xs[index]; } export function remove(predicate: (x: T) => boolean, xs: T[]): void { - const index = findIndex(predicate, xs); - xs.splice(index, 1); + const index = findIndex(predicate, xs); + xs.splice(index, 1); } // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign @@ -77,25 +77,25 @@ export function remove(predicate: (x: T) => boolean, xs: T[]): void { * @returns */ export function assign(...args) { - var target = args[0]; + var target = args[0]; - 'use strict'; - if (target === undefined || target === null) { - throw new TypeError('Cannot convert undefined or null to object'); - } + 'use strict'; + if (target === undefined || target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } - var output = Object(target); - for (var index = 1; index < arguments.length; index++) { - var source = arguments[index]; - if (source !== undefined && source !== null) { - for (var nextKey in source) { - if (source.hasOwnProperty(nextKey)) { - output[nextKey] = source[nextKey]; - } - } + var output = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source !== undefined && source !== null) { + for (var nextKey in source) { + if (source.hasOwnProperty(nextKey)) { + output[nextKey] = source[nextKey]; } + } } - return output; + } + return output; } /** @@ -105,5 +105,5 @@ export function assign(...args) { * @returns {string} */ export function createRandomString(): string { - return (Math.random() + 1).toString(36).substring(7); + return (Math.random() + 1).toString(36).substring(7); } diff --git a/src/visual.ts b/src/visual.ts index 3405a3b0..5d63ff70 100644 --- a/src/visual.ts +++ b/src/visual.ts @@ -9,8 +9,8 @@ import { IPageNode, Page } from './page'; * @interface IVisualNode */ export interface IVisualNode { - name: string; - page: IPageNode; + name: string; + page: IPageNode; } /** @@ -22,70 +22,70 @@ export interface IVisualNode { * @implements {IFilterable} */ export class Visual implements IVisualNode, IFilterable { - /** - * The visual name - * - * @type {string} - */ - name: string; - /** - * The parent Power BI page containing this visual - * - * @type {IPageNode} - */ - page: IPageNode; + /** + * The visual name + * + * @type {string} + */ + name: string; + /** + * The parent Power BI page containing this visual + * + * @type {IPageNode} + */ + page: IPageNode; - constructor(page: IPageNode, name: string) { - this.name = name; - this.page = page; - } + constructor(page: IPageNode, name: string) { + this.name = name; + this.page = page; + } - /** - * Gets all page level filters within report - * - * ```javascript - * visual.getFilters() - * .then(pages => { ... }); - * ``` - * - * @returns {(Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>)} - */ - getFilters(): Promise<(models.IBasicFilter | models.IAdvancedFilter)[]> { - return this.page.report.service.hpm.get(`/report/pages/${this.page.name}/visuals/${this.name}/filters`, { uid: this.page.report.config.uniqueId }, this.page.report.iframe.contentWindow) - .then(response => response.body, - response => { - throw response.body; - }); - } + /** + * Gets all page level filters within report + * + * ```javascript + * visual.getFilters() + * .then(pages => { ... }); + * ``` + * + * @returns {(Promise<(models.IBasicFilter | models.IAdvancedFilter)[]>)} + */ + getFilters(): Promise<(models.IBasicFilter | models.IAdvancedFilter)[]> { + return this.page.report.service.hpm.get(`/report/pages/${this.page.name}/visuals/${this.name}/filters`, { uid: this.page.report.config.uniqueId }, this.page.report.iframe.contentWindow) + .then(response => response.body, + response => { + throw response.body; + }); + } - /** - * Remove all filters on this page within the report - * - * ```javascript - * visual.removeFilters(); - * ``` - * - * @returns {Promise} - */ - removeFilters(): Promise { - return this.setFilters([]); - } + /** + * Remove all filters on this page within the report + * + * ```javascript + * visual.removeFilters(); + * ``` + * + * @returns {Promise} + */ + removeFilters(): Promise { + return this.setFilters([]); + } - /** - * Set all filters at the visual level of the page - * - * ```javascript - * visual.setFilters(filters) - * .catch(errors => { ... }); - * ``` - * - * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters - * @returns {Promise} - */ - setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise { - return this.page.report.service.hpm.put(`/report/pages/${this.page.name}/visuals/${this.name}/filters`, filters, { uid: this.page.report.config.uniqueId }, this.page.report.iframe.contentWindow) - .catch(response => { - throw response.body; - }); - } + /** + * Set all filters at the visual level of the page + * + * ```javascript + * visual.setFilters(filters) + * .catch(errors => { ... }); + * ``` + * + * @param {((models.IBasicFilter | models.IAdvancedFilter)[])} filters + * @returns {Promise} + */ + setFilters(filters: (models.IBasicFilter | models.IAdvancedFilter)[]): Promise { + return this.page.report.service.hpm.put(`/report/pages/${this.page.name}/visuals/${this.name}/filters`, filters, { uid: this.page.report.config.uniqueId }, this.page.report.iframe.contentWindow) + .catch(response => { + throw response.body; + }); + } } \ No newline at end of file diff --git a/test/test.spec.ts b/test/test.spec.ts index 0338e43b..faecf795 100644 --- a/test/test.spec.ts +++ b/test/test.spec.ts @@ -1168,7 +1168,7 @@ describe('Protocol', function () { }); }); }); - + describe('filters (page level)', function () { it('GET /report/pages/xyz/filters returns 200 with body as array of filters', function (done) { // Arrange @@ -1940,8 +1940,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ] }; @@ -1956,8 +1956,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ], expectedErrors: { body: [ @@ -1984,8 +1984,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ] }; @@ -2154,8 +2154,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ], response: { body: [] @@ -2175,8 +2175,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ], expectedErrors: { body: [ @@ -2203,8 +2203,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ] }; @@ -2449,8 +2449,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ] }; @@ -2467,8 +2467,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ], expectedErrors: { body: [ @@ -2495,8 +2495,8 @@ describe('SDK-to-HPM', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() ] }; @@ -2535,7 +2535,7 @@ describe('SDK-to-HPM', function () { done(); }); }); - }); + }); }); describe('SDK-to-Router (Event subscription)', function () { @@ -2767,7 +2767,7 @@ describe('SDK-to-MockApp', function () { let report2: report.Report; beforeAll(function () { - + powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory, { wpmpName: 'SDK-to-MockApp HostWpmp', logMessages @@ -3003,7 +3003,7 @@ describe('SDK-to-MockApp', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() + (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() ], expectedErrors: [ { @@ -3032,7 +3032,7 @@ describe('SDK-to-MockApp', function () { const testData = { filters: [(new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON()] }; - + iframeLoaded .then(() => { spyApp.validateFilter.and.returnValue(Promise.resolve(null)); @@ -3173,7 +3173,7 @@ describe('SDK-to-MockApp', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() + (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() ], expectedErrors: [ { @@ -3202,7 +3202,7 @@ describe('SDK-to-MockApp', function () { const testData = { filters: [(new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON()] }; - + iframeLoaded .then(() => { spyApp.validateFilter.and.returnValue(Promise.resolve(null)); @@ -3322,7 +3322,7 @@ describe('SDK-to-MockApp', function () { } ] }; - + // Act iframeLoaded .then(() => { @@ -3342,14 +3342,14 @@ describe('SDK-to-MockApp', function () { }); }); }); - + describe('visual', function () { describe('filters', function () { it('visual.getFilters() returns promise that rejects with validation errors if the page or visual was invalid', function (done) { // Arrange const testData = { errors: [ - { + { message: 'visual uvw was not found on page xyx' } ] @@ -3428,7 +3428,7 @@ describe('SDK-to-MockApp', function () { // Arrange const testData = { filters: [ - (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() + (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() ], errors: [ { @@ -3461,7 +3461,7 @@ describe('SDK-to-MockApp', function () { const testData = { filters: [(new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON()] }; - + iframeLoaded .then(() => { spyApp.validatePage.and.returnValue(Promise.resolve(null)); diff --git a/test/utility/mockReportEmbed.ts b/test/utility/mockReportEmbed.ts index 8a940321..a24f0aa8 100644 --- a/test/utility/mockReportEmbed.ts +++ b/test/utility/mockReportEmbed.ts @@ -10,8 +10,8 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, const parent = parentWindow || iframeContentWindow.parent; const wpmp = new Wpmp.WindowPostMessageProxy({ processTrackingProperties: { - addTrackingProperties: Hpm.HttpPostMessage.addTrackingProperties, - getTrackingProperties: Hpm.HttpPostMessage.getTrackingProperties, + addTrackingProperties: Hpm.HttpPostMessage.addTrackingProperties, + getTrackingProperties: Hpm.HttpPostMessage.getTrackingProperties, }, isErrorMessage: Hpm.HttpPostMessage.isErrorMessage, receiveWindow: iframeContentWindow, @@ -24,7 +24,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, }, parent); const router = new Router.Router(wpmp); const app = mockApp; - + /** * Setup not found handlers. */ @@ -54,13 +54,13 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, }, error => { hpm.post(`/reports/${uniqueId}/events/error`, error); }); - + res.send(202); }, error => { res.send(400, error); }); }); - + router.get('/report/pages', (req, res) => { return app.getPages() .then(pages => { @@ -69,7 +69,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, res.send(500, error); }); }); - + router.put('/report/pages/active', (req, res) => { const uniqueId = req.headers['uid']; const page = req.body; @@ -85,13 +85,13 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, }, error => { hpm.post(`/reports/${uniqueId}/events/error`, error); }); - + res.send(202); }, errors => { res.send(400, errors); }); }); - + /** * Phase 2 */ @@ -107,7 +107,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, router.put('/report/filters', (req, res) => { const uniqueId = req.headers['uid']; const filters = req.body; - + return Promise.all(filters.map(filter => app.validateFilter(filter))) .then(() => { app.setFilters(filters) @@ -120,13 +120,13 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, }, error => { hpm.post(`/reports/${uniqueId}/events/error`, error); }); - + res.send(202); }, error => { res.send(400, error); }); }); - + /** * Phase 3 */ @@ -135,7 +135,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, name: req.params.pageName, displayName: null }; - + return app.validatePage(page) .then(() => { return app.getFilters() @@ -171,7 +171,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, }, error => { hpm.post(`/reports/${uniqueId}/events/error`, error); }); - + res.send(202); }, errors => { res.send(400, errors); @@ -233,7 +233,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, }, error => { hpm.post(`/reports/${uniqueId}/events/error`, error); }); - + res.send(202); }, errors => { res.send(400, errors); @@ -243,7 +243,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, router.patch('/report/settings', (req, res) => { const uniqueId = req.headers['uid']; const settings = req.body; - + return app.validateSettings(settings) .then(() => { app.updateSettings(settings) @@ -256,13 +256,13 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, }, error => { hpm.post(`/reports/${uniqueId}/events/error`, error); }); - + res.send(202); }, errors => { res.send(400, errors); }); }); - + /** * Phase 4 */ @@ -285,7 +285,7 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, res.send(400, errors); }); }); - + /** * Phase 5 */ @@ -295,6 +295,6 @@ export function setupMockApp(iframeContentWindow: Window, parentWindow: Window, res.send(200, data); }); }); - + return hpm; } \ No newline at end of file diff --git a/test/utility/mockWpmp.ts b/test/utility/mockWpmp.ts index eadfdacd..3b2cf8f9 100644 --- a/test/utility/mockWpmp.ts +++ b/test/utility/mockWpmp.ts @@ -16,7 +16,7 @@ export const spyWpmp = { spyWpmp.postMessageSpy(message); return Promise.resolve(null); }, - + start: jasmine.createSpy("start"), stop: jasmine.createSpy("stop"), @@ -24,14 +24,14 @@ export const spyWpmp = { let message: any = event.data; const handled = spyWpmp.handlers.some(handler => { - if(handler.test(message)) { - Promise.resolve(handler.handle(message)) - - return true; - } - }); - - if(!handled) { + if (handler.test(message)) { + Promise.resolve(handler.handle(message)) + + return true; + } + }); + + if (!handled) { throw Error(`nothing handled message`); } } diff --git a/tsconfig.json b/tsconfig.json index ede13aa7..293510cf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,24 +1,24 @@ { - "compilerOptions": { - "target": "es5", - "noImplicitAny": false, - "sourceMap": true, - "noImplicitUseStrict": true, - "outDir": "dist" - }, - "exclude": [ - "bower_components", - "node_modules", - "typings/main", - "typings/main.d.ts", - "typings/browser", - "typings/browser.d.ts", - "typings/index.d.ts", - "demo", - "dist", - "docs", - "test", - "tmp", - ".publish" - ] + "compilerOptions": { + "target": "es5", + "noImplicitAny": false, + "sourceMap": true, + "noImplicitUseStrict": true, + "outDir": "dist" + }, + "exclude": [ + "bower_components", + "node_modules", + "typings/main", + "typings/main.d.ts", + "typings/browser", + "typings/browser.d.ts", + "typings/index.d.ts", + "demo", + "dist", + "docs", + "test", + "tmp", + ".publish" + ] } \ No newline at end of file diff --git a/webpack.test.tsconfig.json b/webpack.test.tsconfig.json index d51eb1ee..f0d1915a 100644 --- a/webpack.test.tsconfig.json +++ b/webpack.test.tsconfig.json @@ -1,22 +1,22 @@ { - "compilerOptions": { - "target": "es5", - "noImplicitAny": false, - "sourceMap": true - }, - "exclude": [ - "bower_components", - "node_modules", - "typings/main", - "typings/main.d.ts", - "typings/browser", - "typings/browser.d.ts", - "typings/index.d.ts", - "demo", - "dist", - "docs", - "test", - "tmp", - ".publish" - ] + "compilerOptions": { + "target": "es5", + "noImplicitAny": false, + "sourceMap": true + }, + "exclude": [ + "bower_components", + "node_modules", + "typings/main", + "typings/main.d.ts", + "typings/browser", + "typings/browser.d.ts", + "typings/index.d.ts", + "demo", + "dist", + "docs", + "test", + "tmp", + ".publish" + ] } \ No newline at end of file