Przeglądaj źródła

Merge branch 'develop' into feature/StemDirection

# Conflicts:
#	src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts
Matthias Uiberacker 7 lat temu
rodzic
commit
5d3b38f653

+ 17 - 0
.appveyor.yml

@@ -0,0 +1,17 @@
+image: Visual Studio 2017
+environment:
+  matrix:
+    - nodejs_version: "6"
+    - nodejs_version: "7"
+    - nodejs_version: "8"
+    - nodejs_version: "9"    
+install:
+  - ps: Install-Product node $env:nodejs_version
+  - npm install
+  - node --version
+  - npm --version
+build_script:
+  - npm run lint
+  - npm run build
+test_script:
+  - npm run test

+ 5 - 0
.eslintignore

@@ -0,0 +1,5 @@
+node_modules
+dist
+build
+bin
+demo

+ 3 - 0
.eslintrc.yml

@@ -0,0 +1,3 @@
+extends: standard
+rules:
+  indent: [2, 4]

+ 0 - 3
.gitignore

@@ -23,9 +23,6 @@ lib-cov
 # Coverage directory used by tools like istanbul
 coverage
 
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
 # node-waf configuration
 .lock-wscript
 

+ 0 - 91
.jshintrc

@@ -1,91 +0,0 @@
-{
-    // JSHint Default Configuration File (as on JSHint website)
-    // See http://jshint.com/docs/ for more details
-
-    "maxerr"        : 50,       // {int} Maximum error before stopping
-
-    // Enforcing
-    "bitwise"       : true,     // true: Prohibit bitwise operators (&, |, ^, etc.)
-    "camelcase"     : true,     // true: Identifiers must be in camelCase
-    "curly"         : true,     // true: Require {} for every new block or scope
-    "eqeqeq"        : true,     // true: Require triple equals (===) for comparison
-    "forin"         : true,     // true: Require filtering for..in loops with obj.hasOwnProperty()
-    "freeze"        : true,     // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
-    "immed"         : false,    // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
-    "latedef"       : false,    // true: Require variables/functions to be defined before being used
-    "newcap"        : false,    // true: Require capitalization of all constructor functions e.g. `new F()`
-    "noarg"         : true,     // true: Prohibit use of `arguments.caller` and `arguments.callee`
-    "noempty"       : true,     // true: Prohibit use of empty blocks
-    "nonbsp"        : true,     // true: Prohibit "non-breaking whitespace" characters.
-    "nonew"         : false,    // true: Prohibit use of constructors for side-effects (without assignment)
-    "plusplus"      : false,    // true: Prohibit use of `++` and `--`
-    "quotmark"      : true,     // Quotation mark consistency:
-                                //   false    : do nothing (default)
-                                //   true     : ensure whatever is used is consistent
-                                //   "single" : require single quotes
-                                //   "double" : require double quotes
-    "undef"         : true,     // true: Require all non-global variables to be declared (prevents global leaks)
-    "unused"        : true,     // Unused variables:
-                                //   true     : all variables, last function parameter
-                                //   "vars"   : all variables only
-                                //   "strict" : all variables, all function parameters
-    "strict"        : true,     // true: Requires all functions run in ES5 Strict Mode
-    "maxparams"     : false,    // {int} Max number of formal params allowed per function
-    "maxdepth"      : false,    // {int} Max depth of nested blocks (within functions)
-    "maxstatements" : false,    // {int} Max number statements per function
-    "maxcomplexity" : false,    // {int} Max cyclomatic complexity per function
-    "maxlen"        : false,    // {int} Max number of characters per line
-    "varstmt"       : false,    // true: Disallow any var statements. Only `let` and `const` are allowed.
-
-    // Relaxing
-    "asi"           : false,     // true: Tolerate Automatic Semicolon Insertion (no semicolons)
-    "boss"          : false,     // true: Tolerate assignments where comparisons would be expected
-    "debug"         : false,     // true: Allow debugger statements e.g. browser breakpoints.
-    "eqnull"        : false,     // true: Tolerate use of `== null`
-    "esversion"     : 5,         // {int} Specify the ECMAScript version to which the code must adhere.
-    "moz"           : false,     // true: Allow Mozilla specific syntax (extends and overrides esnext features)
-                                 // (ex: `for each`, multiple try/catch, function expression…)
-    "evil"          : false,     // true: Tolerate use of `eval` and `new Function()`
-    "expr"          : false,     // true: Tolerate `ExpressionStatement` as Programs
-    "funcscope"     : false,     // true: Tolerate defining variables inside control statements
-    "globalstrict"  : false,     // true: Allow global "use strict" (also enables 'strict')
-    "iterator"      : false,     // true: Tolerate using the `__iterator__` property
-    "lastsemic"     : false,     // true: Tolerate omitting a semicolon for the last statement of a 1-line block
-    "laxbreak"      : false,     // true: Tolerate possibly unsafe line breakings
-    "laxcomma"      : false,     // true: Tolerate comma-first style coding
-    "loopfunc"      : false,     // true: Tolerate functions being defined in loops
-    "multistr"      : false,     // true: Tolerate multi-line strings
-    "noyield"       : false,     // true: Tolerate generator functions with no yield statement in them.
-    "notypeof"      : false,     // true: Tolerate invalid typeof operator values
-    "proto"         : false,     // true: Tolerate using the `__proto__` property
-    "scripturl"     : false,     // true: Tolerate script-targeted URLs
-    "shadow"        : false,     // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
-    "sub"           : false,     // true: Tolerate using `[]` notation when it can still be expressed in dot notation
-    "supernew"      : false,     // true: Tolerate `new function () { ... };` and `new Object;`
-    "validthis"     : false,     // true: Tolerate using this in a non-constructor function
-
-    // Environments
-    "browser"       : true,     // Web Browser (window, document, etc)
-    "browserify"    : false,    // Browserify (node.js code in the browser)
-    "couch"         : false,    // CouchDB
-    "devel"         : true,     // Development/debugging (alert, confirm, etc)
-    "dojo"          : false,    // Dojo Toolkit
-    "jasmine"       : false,    // Jasmine
-    "jquery"        : false,    // jQuery
-    "mocha"         : true,     // Mocha
-    "mootools"      : false,    // MooTools
-    "node"          : true,    // Node.js
-    "nonstandard"   : false,    // Widely adopted globals (escape, unescape, etc)
-    "phantom"       : false,    // PhantomJS
-    "prototypejs"   : false,    // Prototype and Scriptaculous
-    "qunit"         : false,    // QUnit
-    "rhino"         : false,    // Rhino
-    "shelljs"       : false,    // ShellJS
-    "typed"         : false,    // Globals for typed array constructions
-    "worker"        : false,    // Web Workers
-    "wsh"           : false,    // Windows Scripting Host
-    "yui"           : false,    // Yahoo User Interface
-
-    // Custom Globals
-    "globals"       : {}        // additional predefined global variables
-}

+ 1 - 0
AUTHORS

@@ -2,3 +2,4 @@ Andrea Condoluci <a.condoluci@phonicscore.com> (http://llliii.ooo)
 Oliver Hörbinger <o.hoerbinger@phonicscore.com> ()
 Matthias Uiberacker <m.uiberacker@phonicscore.com> ()
 Sebastian Haas <s.haas@phonicscore.com> (http://sebastianhaas.at)
+Benjamin Giesinger <benjamin.giesinger@gmail.com> (http://benjamingiesinger.de)

+ 0 - 192
Gruntfile.js

@@ -1,192 +0,0 @@
-/*global module*/
-var webpackCfg = require('./webpack.config.js');
-
-module.exports = function (grunt) {
-    'use strict';
-
-    // The banner on top of the build
-    var banner = '/**\n' +
-        ' * Open Sheet Music Display <%= pkg.version %> built on <%= grunt.template.today("yyyy-mm-dd") %>.\n' +
-        ' * Copyright (c) 2016 PhonicScore\n' +
-        ' *\n' +
-        ' * https://github.com/opensheetmusicdisplay/opensheetmusicdisplay\n' +
-        ' */\n',
-        typings = [
-            'typings/index.d.ts',
-            // Additional manual typings:
-            'external/vexflow/vexflow.d.ts'
-        ];
-
-    // Paths
-    var src = ['src/**/*.ts'];
-    var test = ['test/**/*.ts'];
-
-    // Grunt configuration following:
-    grunt.initConfig({
-        pkg: grunt.file.readJSON('package.json'),
-        banner: '',
-        // Build output directories
-        outputDir: {
-            build: 'build',
-            dist: 'dist'
-        },
-        // Browserify
-        browserify: {
-            dist: {
-                src: ['src/OSMD/OSMD.ts'],
-                dest: '<%= outputDir.build %>/osmd.js',
-                options: {
-                    banner: '<%= banner %>',
-                    browserifyOptions: {
-                        standalone: 'opensheetmusicdisplay'
-                    }
-                }
-            },
-            debug: {
-                src: ['src/OSMD/OSMD.ts'],
-                dest: '<%= outputDir.build %>/osmd-debug.js',
-                options: {
-                    banner: '<%= banner %>',
-                    browserifyOptions: {
-                        debug: true,
-                        standalone: 'opensheetmusicdisplay'
-                    }
-                }
-            },
-            test: {
-                src: [].concat(typings, src, test),
-                dest: '<%= outputDir.build %>/osmd-test.js',
-                options: {
-                    banner: '<%= banner %>',
-                    browserifyOptions: {
-                        debug: true
-                    }
-                }
-            },
-            options: {
-                plugin: ['tsify']
-            }
-        },
-        // Uglify
-        uglify: {
-            options: {
-                compress: {
-                    'drop_console': true
-                },
-                banner: banner,
-                mangle: true,
-                mangleProperties: true
-            },
-            bundle: {
-                files: {
-                    'build/osmd.min.js': ['build/osmd.js']
-                }
-            }
-        },
-        // Webpack
-        webpack: {
-          options: {
-            progress: true,
-          },
-          build: webpackCfg,
-          dev: Object.assign({ watch: true }, webpackCfg)
-        },
-        'webpack-dev-server': {
-          options: {
-              webpack: webpackCfg
-          },
-          start: webpackCfg.devServer,
-        },
-        // Karma setup
-        karma: {
-            // For continuous integration
-            ci: {
-                configFile: 'karma.conf.js',
-                options: {
-                    browsers: ['PhantomJS']
-                }
-            },
-            firefox: {
-                configFile: 'karma.conf.js',
-                options: {
-                    singleRun: false,
-                    browsers: ['Firefox']
-                }
-            },
-            chrome: {
-                configFile: 'karma.conf.js',
-                options: {
-                    singleRun: false,
-                    browsers: ['Chrome']
-                }
-            }
-        },
-        // Typescript compilation for ES6 module (npm package)
-        ts: {
-          default : {
-            tsconfig: true
-          }
-        },
-        // Cleaning task setup
-        clean: {
-            options: {
-                force: true
-            },
-            all: {
-                src: [
-                    '<%= outputDir.build %>',
-                    '<%= outputDir.dist %>',
-                    '.tscache',
-                    'src/**/*.js', 'test/**/*.js' // if something went wrong, delete JS from TypeScript source directories
-                ]
-            }
-        },
-        copy: {
-            demo: {
-                files: [
-                    { src: ['*'], dest: '<%= outputDir.build %>/demo/sheets/', cwd: './test/data/', expand: true },
-                    { src: ['*.js', '*.css', '*.html', '*.ico'], cwd: './demo/', expand: true, dest: '<%= outputDir.build %>/demo/' },
-                    { src: ['osmd-debug.js'], cwd: './build/', expand: true, dest: '<%= outputDir.build %>/demo/' }
-                ]
-            }
-        },
-        // http-server
-        'http-server': {
-            'demo': {
-                root: 'build/demo',
-                port: 8000,
-                host: '0.0.0.0',
-                showDir : true,
-                autoIndex: true,
-                runInBackground: false,
-                openBrowser : true
-            }
-        }
-
-    });
-
-    // Load npm tasks
-    grunt.loadNpmTasks('grunt-browserify');
-    grunt.loadNpmTasks('grunt-contrib-clean');
-    grunt.loadNpmTasks('grunt-contrib-copy');
-    grunt.loadNpmTasks('grunt-contrib-uglify');
-    grunt.loadNpmTasks('grunt-contrib-watch');
-    grunt.loadNpmTasks('grunt-http-server');
-    grunt.loadNpmTasks('grunt-karma');
-    grunt.loadNpmTasks('grunt-ts');
-    grunt.loadNpmTasks('grunt-webpack');
-
-    // Build tasks
-    grunt.registerTask('build:demo',  'Builds the demo.',                            ['browserify:debug', 'copy:demo']);
-    grunt.registerTask('build:test',  'Builds the tests',                            ['browserify:test']);
-    grunt.registerTask('build:dist',  'Builds for distribution on npm and Bower.',   ['browserify:dist', 'uglify', 'ts']);
-    grunt.registerTask('build:pack',  'Builds using webpack',                        ['webpack:build', 'uglify']);
-
-    // Tests
-    grunt.registerTask('test',        'Runs unit, regression and e2e tests.',        ['build:test', 'karma:ci']);
-
-    // Webpack dev server
-    grunt.registerTask('webpack-server', ['webpack-dev-server:start']);
-    // Default task (if grunt is run without any argument, used in contiuous integration)
-    grunt.registerTask('default',     'Default task, running all other tasks. (CI)', ['test', 'build:demo', 'build:dist']);
-};

+ 4 - 3
README.md

@@ -1,15 +1,16 @@
-<img alt="OSMD logo" src="http://opensheetmusicdisplay.org/assets/osmd_logo.svg" width="200"/>
+<img alt="OSMD logo" src="https://opensheetmusicdisplay.org/assets/osmd_logo.svg" width="200"/>
 
 # OpenSheetMusicDisplay
 
 [![Greenkeeper badge](https://badges.greenkeeper.io/opensheetmusicdisplay/opensheetmusicdisplay.svg)](https://greenkeeper.io/)
-[![Build Status](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay.svg?branch=master)](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay)
+[![Travis Build Status](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay.svg?branch=master)](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay)
+[![Appveyor Build status](https://ci.appveyor.com/api/projects/status/r88lnffso55nq1ko?svg=true)](https://ci.appveyor.com/project/sebastianhaas/opensheetmusicdisplay/branch/master)
 [![Dependency Status](https://david-dm.org/opensheetmusicdisplay/opensheetmusicdisplay.png)](https://david-dm.org/opensheetmusicdisplay/opensheetmusicdisplay)
 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/opensheetmusicdisplay/opensheetmusicdisplay?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Code Climate](https://codeclimate.com/github/opensheetmusicdisplay/opensheetmusicdisplay/badges/gpa.svg)](https://codeclimate.com/github/opensheetmusicdisplay/opensheetmusicdisplay)
 [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
 
-http://www.opensheetmusicdisplay.org/
+https://www.opensheetmusicdisplay.org/
 
 OpenSheeMusicDisplay is the missing link between MusicXML and VexFlow. Built upon many years of experience in both sheet music interactivity and engraving, it is the perfect solution for app developers seeking to build digital sheet music services.
 

+ 1 - 1
bin/publish_gh_page.sh

@@ -2,7 +2,7 @@
 
 # Prepare files to be published
 npm run docs
-grunt build:demo
+npm run build
 
 # Clone github page
 git clone git@github.com:opensheetmusicdisplay/opensheetmusicdisplay.github.io.git

+ 71 - 53
demo/index.html

@@ -3,65 +3,83 @@
 <head>
     <meta charset="utf-8">
 
-    <title>OpenSheetMusicDisplay Demo</title>
+    <title><%= htmlWebpackPlugin.options.title %></title>
     <meta name="description" content="A showcase for OpenSheetMusicDisplay.">
     <meta name="author" content="OpenSheetMusicDisplay contributors">
-
-    <!-- Include opensheetmusicdisplay -->
-    <!-- <script src="./osmd.js" type="text/javascript"></script> -->
-
-    <!-- Include code and styles for this demo -->
-    <script src="./demo.js" type="text/javascript"></script>
-    <link href="./demo.css" media="all" rel="stylesheet"/>
+    <!-- Demo js file is included automatically by webpack druing build -->
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>    
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.js" type="text/javascript"></script>    
+    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.css" media="all" rel="stylesheet"/>
     <link rel="icon" href="./favicon.ico?" type="image/x-icon"/>
 </head>
 <body>
+<br>
+<h1 class="ui centered header">
+    <img src="./favicon.ico?" class="ui image">
+    <%= htmlWebpackPlugin.options.title %>
+</h1>
+<div class="ui three column grid container">
+    <div class="column">
+        <h3 class="ui header">Select a sample:</h3>
+        <select class="ui selection dropdown" style="width:100px"  id="select"></select>
+    </div>
+    <div class="column">
+        <h3 class="ui header">Render backend:</h3>
+        <select class="ui dropdown" id="backend-select" value="canvas">
+            <option value="canvas">Canvas</option>>
+            <option value="svg">SVG</option>
+        </select>
+    </div>
+    <div class="column">
+        <h3 class="ui header">Cursor controls:</h3>
+        <div class="ui buttons">
+            <div class="ui animated fade button" id="show-cursor-btn">
+                <div class="visible content">Show</div>
+                <div class="hidden content">
+                    <i class="eye icon"></i>
+                </div>
+            </div>
+            <div class="ui animated fade button" id="hide-cursor-btn">
+                <div class="visible content">Hide</div>
+                <div class="hidden content">
+                    <i class="eye slash icon"></i>
+                </div>
+            </div>
+            <div class="ui animated fade button" id="next-cursor-btn">
+                <div class="visible content">Next</div>
+                <div class="hidden content">
+                    <i class="arrow right icon"></i>
+                </div>
+            </div>
+            <div class="ui animated fade button" id="reset-cursor-btn">
+                <div class="visible content">Reset</div>
+                <div class="hidden content">
+                    <i class="undo icon"></i>
+                </div>
+            </div>
+        </div>
+    </div>         
+    <div class="column">
+        <h3 class="ui header">Zoom controls:</h3>
+        <div class="ui buttons">
+                <div class="ui button" id="zoom-in-btn">
+                    <i class="search plus icon"></i>
+                </div>
+                <div class="ui button" id="zoom-out-btn">
+                    <i class="search minus icon"></i>
+                </div>
+        </div>
+    </div>
+    <div class="column">
+        <h3 class="ui header">Zoom factor:</h3>
+        <h4 class="ui header" id="zoom-str">???</h4>
+    </div>
+    <div class="column">
+            <h3 class="ui header">Current width:</h3>                
+            <h4 class="ui header" id="size-str">???</h4>
+    </div>
+</div>
 <table cellspacing="0" style="max-width:700px;">
-    <tr>
-        <td>
-            <img src="./favicon.ico?" style="width:64px;height:64px;"/>
-        </td>
-        <td>
-            <h1>OpenSheetMusicDisplay Demo</h1>
-        </td>
-    </tr>
-</table>
-<table cellspacing="0" style="max-width:700px;">
-    <tr>
-        <td valign="top">
-            <select id="select"></select>
-            <p> Select a sample from the list ... </p>
-            <p>... or just drop your MusicXML file on this page.</p>
-        </td>
-        <td valign="top" halign="right">
-            <p>Renderer Backend
-                <select id="backend-select" value="canvas">
-                    <option value="canvas">Canvas</option>>
-                    <option value="svg">SVG</option>
-                </select>
-            <p>Cursor controls:
-                <input type="button" value="show" id="show-cursor-btn"/>
-                <input type="button" value="hide" id="hide-cursor-btn"/>
-                <input type="button" value="next" id="next-cursor-btn"/>
-                <input type="button" value="reset" id="reset-cursor-btn"/>
-            </p>
-            <p>
-                Zoom controls:
-                <input type="button" value="zoom in" id="zoom-in-btn"/>
-                <input type="button" value="zoom out" id="zoom-out-btn"/>
-            </p>
-            <table cellspacing="0">
-                <tr>
-                    <td>
-                        <p>Zoom factor: <span id="zoom-str">???</span>%</p>
-                    </td>
-                    <td>
-                        <p>Current width: <span id="size-str">???</span>px</p>
-                    </td>
-                </tr>
-            </table>
-        </td>
-    </tr>
     <tr id="error-tr">
         <td></td>
         <td id="error-td"></td>

+ 4 - 4
demo/index.js

@@ -5,7 +5,7 @@ import { OSMD } from '../src/OSMD/OSMD';
     "use strict";
     var osmdObj;
     // The folder of the demo files
-    var folder = "",
+    var folder = process.env.STATIC_FILES_SUBFOLDER ? process.env.STATIC_FILES_SUBFOLDER + "/" : "",
     // The available demos
         demos = {
             "Beethoven - AnDieFerneGeliebte": "Beethoven_AnDieFerneGeliebte.xml",
@@ -135,7 +135,7 @@ import { OSMD } from '../src/OSMD/OSMD';
             var value = e.target.value;
             // clears the canvas element
             canvas.innerHTML = "";
-            osmdObj = new opensheetmusicdisplay.OSMD(canvas, false, value);
+            osmdObj = new OSMD(canvas, false, value);
             osmdObj.setLogLevel('info');
             selectOnChange();
 
@@ -205,8 +205,8 @@ import { OSMD } from '../src/OSMD/OSMD';
     }
 
     function logCanvasSize() {
-        size.innerHTML = canvas.offsetWidth;
-        zoomDiv.innerHTML = Math.floor(zoom * 100.0);
+        size.innerHTML = canvas.offsetWidth + "px";
+        zoomDiv.innerHTML = Math.floor(zoom * 100.0) + "%";
     }
 
     function scale() {

+ 44 - 12
karma.conf.js

@@ -1,8 +1,7 @@
-// Karma configuration
-// Generated on Fri Feb 05 2016 12:36:08 GMT+0100 (CET)
-/*globals module*/
+var common = require('./webpack.common.js')
+
 module.exports = function (config) {
-    'use strict';
+    'use strict'
     config.set({
         // base path that will be used to resolve all patterns (eg. files, exclude)
         basePath: '',
@@ -15,13 +14,11 @@ module.exports = function (config) {
         exclude: [],
 
         files: [{
-            pattern: 'build/osmd-test.js'
-        }, {
             pattern: 'src/**/*.ts',
             included: false
         }, {
             pattern: 'test/**/*.ts',
-            included: false
+            included: true
         }, {
             pattern: 'test/data/*.xml',
             included: true
@@ -39,7 +36,34 @@ module.exports = function (config) {
         // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
         preprocessors: {
             'test/data/*.xml': ['xml2js'],
-            'test/data/*.mxl.base64': ['base64-to-js']
+            'test/data/*.mxl.base64': ['base64-to-js'],
+            // add webpack as preprocessor
+            'src/**/*.ts': ['webpack'],
+            'test/**/*.ts': ['webpack']
+        },
+
+        webpack: {
+            // karma watches the test entry points
+            // (you don't need to specify the entry option)
+            // webpack watches dependencies
+
+            // copy parts of webpack configuration to use minimal effort here
+            devtool: 'cheap-module-eval-source-map',
+            module: {
+                loaders: common.module.loaders
+            },
+            resolve: common.resolve
+        },
+        webpackMiddleware: {
+            // webpack-dev-middleware configuration
+            // i. e.
+            noInfo: true
+        },
+
+        // Required for Firefox and Chorme to work
+        // see https://github.com/webpack-contrib/karma-webpack/issues/188
+        mime: {
+            'text/x-typescript': ['ts', 'tsx']
         },
 
         // test results reporter to use
@@ -67,8 +91,16 @@ module.exports = function (config) {
         autoWatch: false,
 
         // start these browsers
-        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
-        browsers: [], // Will be overruled by karma grunt task options
+        browsers: [process.env.CI ? 'ChromeHeadlessNoSandbox' : 'ChromeHeadless'],
+
+        // For security reasons, Google Chrome is unable to provide sandboxing
+        // when it is running in container-based environments (e.g. CI).
+        customLaunchers: {
+            ChromeHeadlessNoSandbox: {
+                base: 'ChromeHeadless',
+                flags: ['--no-sandbox']
+            }
+        },
 
         // Continuous Integration mode
         // if true, Karma captures browsers, runs the tests and exits
@@ -77,5 +109,5 @@ module.exports = function (config) {
         // Concurrency level
         // how many browser should be started simultaneous
         concurrency: Infinity
-    });
-};
+    })
+}

+ 32 - 31
package.json

@@ -5,14 +5,18 @@
   "main": "dist/src/OSMD/OSMD.js",
   "typings": "dist/src/OSMD/OSMD",
   "scripts": {
-    "docs": "typedoc --mode file --out build/docs --module commonjs --target ES5 --name opensheetmusicdisplay ./src",
-    "lint": "npm run jshint && npm run tslint",
-    "jshint": "jshint . --exclude node_modules,dist,build,bin,demo",
-    "test": "npm run lint && grunt test",
+    "docs": "typedoc --out ./build/docs --name OpenSheetMusicDisplay --module commonjs --target ES5 --mode file ./src/**/*.ts",
+    "eslint": "eslint .",
     "tslint": "tslint --project tsconfig.json \"src/**/*.ts\" \"test/**/*.ts\"",
-    "typedoc": "typedoc --out ./build/docs --name OpenSheetMusicDisplay --module commonjs --target ES5 --mode file ./src/**/*.ts",
-    "prepublish": "grunt build:dist",
-    "start": "http-server build/demo"
+    "lint": "npm-run-all eslint tslint",
+    "test": "karma start --single-run --no-auto-watch",
+    "test:watch": "karma start --no-single-run --auto-watch --browsers Chrome",
+    "prepare": "npm run build",
+    "build": "npm-run-all lint build:webpack",
+    "build:doc": "cross-env STATIC_FILES_SUBFOLDER=sheets npm run build",
+    "build:webpack": "webpack --progress --colors --config webpack.prod.js",
+    "build:webpack-dev": "webpack --progress --colors --config webpack.dev.js",
+    "start": "webpack-dev-server --progress --colors --config webpack.dev.js"
   },
   "pre-commit": [
     "lint"
@@ -27,8 +31,7 @@
     "external",
     "demo",
     "tsconfig.json",
-    "tslint.json",
-    "Gruntfile.js"
+    "tslint.json"
   ],
   "repository": {
     "type": "git",
@@ -58,46 +61,44 @@
     "@types/chai": "^4.0.3",
     "@types/loglevel": "^1.4.29",
     "@types/mocha": "^2.2.40",
-    "babel-core": "^6.26.0",
-    "babel-loader": "^7.1.2",
-    "babel-preset-es2015": "^6.24.1",
-    "browserify": "^14.0.0",
     "chai": "^4.1.0",
+    "clean-webpack-plugin": "^0.1.18",
+    "cross-env": "^5.1.3",
     "cz-conventional-changelog": "^2.0.0",
-    "grunt": "^1.0.1",
-    "grunt-browserify": "^5.0.0",
-    "grunt-contrib-clean": "^1.0.0",
-    "grunt-contrib-copy": "^1.0.0",
-    "grunt-contrib-uglify": "^3.0.0",
-    "grunt-contrib-watch": "^1.0.0",
-    "grunt-http-server": "",
-    "grunt-karma": "^2.0.0",
-    "grunt-ts": "^6.0.0-beta.3",
-    "grunt-webpack": "^3.0.0",
-    "grunt-webpack-server": "^0.1.0",
+    "eslint": "^4.18.1",
+    "eslint-config-standard": "^11.0.0",
+    "eslint-plugin-import": "^2.9.0",
+    "eslint-plugin-node": "^6.0.1",
+    "eslint-plugin-promise": "^3.6.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "file-loader": "^1.1.8",
+    "html-webpack-plugin": "^2.30.1",
     "http-server": "^0.11.0",
     "jquery": "^3.2.1",
-    "jshint": "^2.9.4",
-    "karma": "^1.1.1",
+    "karma": "^2.0.0",
     "karma-base64-to-js-preprocessor": "^0.0.1",
     "karma-chai": "^0.1.0",
-    "karma-chrome-launcher": "^2.0.0",
+    "karma-chrome-launcher": "^2.2.0",
     "karma-firefox-launcher": "^1.0.0",
     "karma-mocha": "^1.1.1",
     "karma-mocha-reporter": "^2.0.4",
-    "karma-phantomjs-launcher": "^1.0.1",
+    "karma-webpack": "^2.0.9",
     "karma-xml2js-preprocessor": "^0.0.3",
-    "mocha": "^4.0.0",
-    "phantomjs-prebuilt": "^2.1.8",
+    "mocha": "^4.1.0",
+    "npm-run-all": "^4.1.2",
     "pre-commit": "^1.2.2",
     "ts-loader": "^3.0.0",
     "tsify": "^3.0.0",
     "tslint": "^5.8.0",
+    "tslint-loader": "^3.5.3",
     "typedoc": "^0.10.0",
     "typescript": "^2.6.1",
     "uglifyjs-webpack-plugin": "^1.0.1",
+    "underscore-template-loader": "^0.8.0",
     "webpack": "^3.5.5",
-    "webpack-dev-server": "2.7.1"
+    "webpack-dev-server": "2.7.1",
+    "webpack-merge": "^4.1.1",
+    "webpack-visualizer-plugin": "^0.1.11"
   },
   "config": {
     "commitizen": {

+ 39 - 1
src/MusicalScore/Graphical/DrawingEnums.ts

@@ -1,3 +1,5 @@
+import * as Collections from "typescript-collections";
+
 /**
  * The supported styles to draw a rectangle on the music sheet
  */
@@ -37,13 +39,49 @@ export enum OutlineAndFillStyleEnum {
     Comment10
 }
 
+// tslint:disable-next-line:max-line-length A linebreak would be more confusing here
+export const OUTLINE_AND_FILL_STYLE_DICT: Collections.Dictionary<OutlineAndFillStyleEnum, string> = new Collections.Dictionary<OutlineAndFillStyleEnum, string>();
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.BaseWritingColor, "Thistle");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.FollowingCursor, "Aqua");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.AlternativeFollowingCursor, "Azure");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.PlaybackCursor, "Bisque");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Highlighted, "CadetBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.ErrorUnderlay, "DarkBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Selected, "DarkGoldenRod");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.SelectionSymbol, "BlanchedAlmond");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.DebugColor1, "Chartreuse");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.DebugColor2, "DarkGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.DebugColor3, "DarkOrange");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.SplitScreenDivision, "FireBrick");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.GreyTransparentOverlay, "DarkSalmon");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea1, "DarkSeaGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea2, "DarkOrchid");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea3, "Aquamarine");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea4, "DarkKhaki");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea5, "ForestGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea6, "AliceBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea7, "DeepPink");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea8, "Coral");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea9, "DarkOliveGreen");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.MarkedArea10, "Chocolate");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment1, "DodgerBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment2, "Blue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment3, "Beige");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment4, "Crimson");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment5, "Fuchsia");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment6, "Brown");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment7, "BlanchedAlmond");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment8, "CornflowerBlue");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment9, "Cornsilk");
+OUTLINE_AND_FILL_STYLE_DICT.setValue(OutlineAndFillStyleEnum.Comment10, "DarkGrey");
+
 export enum StyleSets {
     MarkedArea,
     Comment
 }
 
 /**
- * The layers which one can draw on (not suppoerted)
+ * The layers which one can draw on (not supported)
  */
 export enum GraphicalLayers {
     Background,

+ 30 - 1
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -201,7 +201,7 @@ export abstract class MusicSheetDrawer {
         // empty
     }
 
-    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number, alpha: number = 1): void {
         throw new Error("not implemented");
     }
 
@@ -407,6 +407,7 @@ export abstract class MusicSheetDrawer {
         if (!this.isVisible(page.PositionAndShape)) {
             return;
         }
+
         for (const system of page.MusicSystems) {
             if (this.isVisible(system.PositionAndShape)) {
                 this.drawMusicSystem(system);
@@ -417,6 +418,34 @@ export abstract class MusicSheetDrawer {
                 this.drawLabel(label, <number>GraphicalLayers.Notes);
             }
         }
+        // Draw bounding boxes for debug purposes. This has to be at the end because only
+        // then all the calculations and recalculations are done
+        if (process.env.DRAW_BOUNDING_BOX_ELEMENT) {
+            this.drawBoundingBoxes(page.PositionAndShape, 0, process.env.DRAW_BOUNDING_BOX_ELEMENT);
+        }
+
+    }
+
+    /**
+     * Draw bounding boxes aroung GraphicalObjects
+     * @param startBox Bounding Box that is used as a staring point to recursively go through all child elements
+     * @param layer Layer to draw to
+     * @param type Type of element to show bounding boxes for as string.
+     */
+    private drawBoundingBoxes(startBox: BoundingBox, layer: number = 0, type: string = "all"): void {
+        const dataObjectString: string = (startBox.DataObject.constructor as any).name;
+        if (startBox.BoundingRectangle !== undefined && (dataObjectString === type || type === "all")) {
+            const relBoundingRect: RectangleF2D = startBox.BoundingRectangle;
+            let tmpRect: RectangleF2D = new RectangleF2D(startBox.AbsolutePosition.x + startBox.BorderLeft,
+                                                         startBox.AbsolutePosition.y + startBox.BorderTop ,
+                                                         (relBoundingRect.width + 0), (relBoundingRect.height + 0));
+            tmpRect = this.applyScreenTransformationForRect(tmpRect);
+            this.renderRectangle(tmpRect, <number>GraphicalLayers.Background, layer, 0.5);
+            this.renderLabel(new GraphicalLabel(new Label(dataObjectString), 1.2, TextAlignment.CenterCenter),
+                             layer, tmpRect.width, tmpRect.height, tmpRect.height, new PointF2D(tmpRect.x, tmpRect.y + 12));
+        }
+        layer++;
+        startBox.ChildElements.forEach(bb => this.drawBoundingBoxes(bb, layer, type));
     }
 
     private drawMarkedAreas(system: MusicSystem): void {

+ 3 - 1
src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts

@@ -52,11 +52,13 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
         this.canvasRenderingCtx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
         this.canvasRenderingCtx.font = old;
     }
-    public renderRectangle(rectangle: RectangleF2D, styleId: number): void {
+    public renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number = 1): void {
         const old: string | CanvasGradient | CanvasPattern = this.canvasRenderingCtx.fillStyle;
         this.canvasRenderingCtx.fillStyle = VexFlowConverter.style(styleId);
+        this.canvasRenderingCtx.globalAlpha = alpha;
         this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
         this.canvasRenderingCtx.fillStyle = old;
+        this.canvasRenderingCtx.globalAlpha = 1;
     }
 
     private ctx: Vex.Flow.CanvasContext;

+ 6 - 1
src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts

@@ -58,8 +58,13 @@ export class SvgVexFlowBackend extends VexFlowBackend {
         this.ctx.fillText(text, screenPosition.x, screenPosition.y + heightInPixel);
         this.ctx.restore();
     }
-    public renderRectangle(rectangle: RectangleF2D, styleId: number): void {
+    public renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number = 1): void {
+        this.ctx.save();
+        this.ctx.attributes.fill = VexFlowConverter.style(styleId);
+        this.ctx.attributes["fill-opacity"] = alpha;
         this.ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+        this.ctx.restore();
+        this.ctx.attributes["fill-opacity"] = 1;
     }
 
     private ctx: Vex.Flow.SVGContext;

+ 9 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts

@@ -44,7 +44,15 @@ export abstract class VexFlowBackend {
   public abstract translate(x: number, y: number): void;
   public abstract renderText(fontHeight: number, fontStyle: FontStyles, font: Fonts, text: string,
                              heightInPixel: number, screenPosition: PointF2D): void;
-  public abstract renderRectangle(rectangle: RectangleF2D, styleId: number): void;
+  /**
+   * Renders a rectangle with the given style to the screen.
+   * It is given in screen coordinates.
+   * @param rectangle the rect in screen coordinates
+   * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
+   * @param styleId the style id
+   * @param alpha alpha value between 0 and 1
+   */
+  public abstract renderRectangle(rectangle: RectangleF2D, styleId: number, alpha: number): void;
 
   public abstract getBackendType(): number;
 

+ 3 - 3
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -14,7 +14,7 @@ import {GraphicalNote} from "../GraphicalNote";
 import {SystemLinesEnum} from "../SystemLinesEnum";
 import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
-import {OutlineAndFillStyleEnum} from "../DrawingEnums";
+import {OutlineAndFillStyleEnum, OUTLINE_AND_FILL_STYLE_DICT} from "../DrawingEnums";
 import {Logging} from "../../../Common/Logging";
 
 /**
@@ -430,7 +430,7 @@ export class VexFlowConverter {
      * @returns {string}
      */
     public static style(styleId: OutlineAndFillStyleEnum): string {
-        // TODO To be implemented
-        return "lightgreen";
+        const ret: string = OUTLINE_AND_FILL_STYLE_DICT.getValue(styleId);
+        return ret;
     }
 }

+ 0 - 2
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -35,8 +35,6 @@ import {LinkedVoice} from "../../VoiceData/LinkedVoice";
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
   constructor() {
     super(new VexFlowGraphicalSymbolFactory());
-    let a: LyricsEntry = new LyricsEntry(undefined, undefined, undefined);
-    a = a;
     MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer();
   }
 

+ 3 - 2
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -117,9 +117,10 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
      * @param rectangle the rect in screen coordinates
      * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
      * @param styleId the style id
+     * @param alpha alpha value between 0 and 1
      */
-    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
-       this.backend.renderRectangle(rectangle, styleId);
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number, alpha: number): void {
+       this.backend.renderRectangle(rectangle, styleId, alpha);
     }
 
     /**

+ 2 - 1
tsconfig.json

@@ -4,7 +4,8 @@
     "module": "commonjs",
     "moduleResolution": "node",
     "outDir": "dist",
-    "declaration": true
+    "declaration": true,
+    "sourceMap": true
   },
   "exclude": [
     "node_modules",

+ 75 - 0
webpack.common.js

@@ -0,0 +1,75 @@
+var path = require('path')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var webpack = require('webpack')
+
+module.exports = {
+    entry: {
+        'osmd': './src/OSMD/OSMD.ts', // Main library
+        'demo': './demo/index.js' // Demo index
+    },
+    output: {
+        path: path.resolve(__dirname, 'build'),
+        filename: '[name].js'
+    },
+    resolve: {
+        // Add '.ts' and '.tsx' as a resolvable extension.
+        extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js']
+    },
+    module: {
+        loaders: [
+            // all files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'
+            {
+                test: /\.ts$/,
+                loader: 'ts-loader',
+                exclude: /(node_modules|bower_components)/
+            },
+            // FIXME: TSLint loader is horribly slow therefore check only at beginning
+            // https://github.com/wbuchwalter/tslint-loader/issues/76
+            // // ts lint loader. will pre-lint the ts files
+            // {
+            //     test: /\.ts$/,
+            //     enforce: 'pre',
+            //     loader: 'tslint-loader',
+            //     options: {
+            //         typeCheck: true
+            //     }
+            // },
+            // For html loader generation
+            {
+                test: /\.html$/,
+                loader: 'underscore-template-loader'
+            },
+            {
+                test: /\.(jpg|jpeg|gif|png|ico)$/,
+                exclude: /node_modules/,
+                loader: 'file-loader?name=img/[path][name].[ext]&context=./app/images'
+            }
+        ]
+    },
+    plugins: [
+        new webpack.ProvidePlugin({
+            $: 'jquery',
+            jQuery: 'jquery'
+        }),
+        new webpack.EnvironmentPlugin({
+            STATIC_FILES_SUBFOLDER: false, // Set to other directory if NOT using webpack-dev-server
+            NODE_ENV: 'development', // use 'development' unless process.env.NODE_ENV is defined
+            DEBUG: false,
+            DRAW_BOUNDING_BOX_ELEMENT: false //  Specifies the element to draw bounding boxes for (e.g. 'GraphicalLabels'). If 'all', bounding boxes are drawn for all elements.
+        }),
+        // add a demo page to the build folder
+        new HtmlWebpackPlugin({
+            template: 'demo/index.html',
+            title: 'OpenSheetMusicDisplay Demo'
+        })
+    ],
+    devServer: {
+        contentBase: [
+            path.join(__dirname, 'test/data'),
+            path.join(__dirname, 'build'),
+            path.join(__dirname, 'demo')
+        ],
+        port: 8000,
+        compress: false
+    }
+}

+ 0 - 58
webpack.config.js

@@ -1,58 +0,0 @@
-var path = require('path');
-var webpack = require('webpack');
-
-module.exports = {
-  entry: {
-    'osmd': './src/OSMD/OSMD.ts', // Main library
-    'demo': './demo/index.js' // Demo index
-  },
-  output: {
-      path: path.resolve(__dirname, 'build'),
-      filename: '[name].js',
-   },
-   resolve: {
-       // Add '.ts' and '.tsx' as a resolvable extension.
-       extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js']
-   },
-   module: {
-       loaders: [
-           // all files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'
-           { test: /\.tsx?$/, loader: 'ts-loader' },
-           // all files with a '.js' extension. Mostly for the web demo.
-           { test: /\.jsx?$/, loader: 'babel-loader', exclude: /(node_modules|bower_components)/,
-              query: {
-                presets: ['es2015'] 
-              }
-          },
-       ]
-   },
-   plugins: [
-     // build optimization plugins
-     new webpack.LoaderOptionsPlugin({
-      minimize: true,
-      debug: true
-    }),
-    new webpack.ProvidePlugin({
-      $: 'jquery',
-      jQuery: 'jquery'
-    }),
-    // FIXME: use environment variable to control uglify.
-    // new webpack.optimize.UglifyJsPlugin({
-    //     warnings: false,
-    //     beautify: false,
-    //     compress: true,
-    //     comments: false,
-    //     sourceMap: true
-    //   })
-  ],
-  devServer: {
-    contentBase: [
-      path.join(__dirname, 'test/data'),
-      path.join(__dirname, 'build'),
-      path.join(__dirname, 'demo')
-      // TODO: fill in paths for demo data
-    ],
-    port: 8000,
-    compress: false,
-  },
-};

+ 6 - 0
webpack.dev.js

@@ -0,0 +1,6 @@
+var merge = require('webpack-merge')
+var common = require('./webpack.common.js')
+
+module.exports = merge(common, {
+    devtool: process.env.DEBUG ? false : 'source-map'
+})

+ 38 - 0
webpack.prod.js

@@ -0,0 +1,38 @@
+var merge = require('webpack-merge')
+var webpack = require('webpack')
+var path = require('path')
+var common = require('./webpack.common.js')
+var Visualizer = require('webpack-visualizer-plugin')
+var Cleaner = require('clean-webpack-plugin')
+
+var pathsToClean = [
+    'dist/**',
+    'build/**'
+]
+
+module.exports = merge(common, {
+    output: {
+        filename: '[name].min.js',
+        path: path.resolve(__dirname, 'build')
+    },
+    plugins: [
+        new webpack.optimize.UglifyJsPlugin({
+            warnings: false,
+            beautify: false,
+            compress: true,
+            comments: false,
+            sourceMap: true,
+            parallel: true
+        }),
+        // build optimization plugins
+        new webpack.LoaderOptionsPlugin({
+            minimize: true,
+            debug: true
+        }),
+        new Visualizer({
+            path: path.resolve(__dirname, 'build'),
+            filename: './statistics.html'
+        }),
+        new Cleaner(pathsToClean, {verbose: true, dry: false})
+    ]
+})