From f56665f894170b4351e3a87da72b7efaee86a2f9 Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Tue, 20 Dec 2016 10:41:12 +0000 Subject: [PATCH 1/7] Use karma-browserify to run tests We do not need to build artifacts to run tests anymore. --- README.md | 2 +- karma.conf.js | 30 +- package.json | 4 +- test/.eslintrc.js | 6 +- test/unit/api-points-spec.js | 258 ++++++++--------- test/unit/api-segments-spec.js | 406 +++++++++++++------------- test/unit/api-spec.js | 427 ++++++++++++++-------------- test/unit/api-time-spec.js | 92 +++--- test/unit/api-zoom-spec.js | 152 +++++----- test/unit/waveform-segments-spec.js | 136 ++++----- 10 files changed, 759 insertions(+), 754 deletions(-) diff --git a/README.md b/README.md index 8debfd842..72812efb8 100644 --- a/README.md +++ b/README.md @@ -530,7 +530,7 @@ Then open http://localhost:9000 in a Web browser. `npm test` should work for simple one time testing. -If you are developing and want to repeatedly run tests in a browser on your machine simply launch `npm run test-watch`. +If you are developing and want to repeatedly run tests in a browser on your machine simply launch `npm run watch`. # License diff --git a/karma.conf.js b/karma.conf.js index 4a7e2dcab..3d603f75d 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -6,15 +6,15 @@ function filterBrowsers(browsers, re) { }); } -module.exports = function (config) { - var isCI = Boolean(process.env.CI) && Boolean(process.env.BROWSER_STACK_ACCESS_KEY); +module.exports = function(config) { + var isCI = Boolean(process.env.CI) && Boolean(process.env.BROWSER_STACK_ACCESS_KEY); // Karma configuration config.set({ // base path, that will be used to resolve files and exclude basePath: '', - frameworks: ['mocha', 'sinon-chai'], + frameworks: ['mocha', 'sinon-chai', 'browserify'], client: { chai: { @@ -25,27 +25,31 @@ module.exports = function (config) { } }, + browserify: { + debug: true, + transform: [ + 'deamdify' + ] + }, + // list of files / patterns to load in the browser files: [ - 'peaks.js', { pattern: 'test/test_img/*', included: false }, { pattern: 'test_data/*', included: false }, { pattern: 'test_data/sample.{dat,json}', included: false, served: true }, { pattern: 'test/*.html' }, 'test/load-fixtures.js', - 'test/unit/**/*.js', + 'test/unit/**/*.js' ], preprocessors: { + 'test/unit/**/*.js': ['browserify'], 'test/*.html': ['html2js'] }, - // list of files to exclude - //exclude: ['lib/js/main.js'], - // test results reporter to use // possible values: dots || progress || growl || spec - reporters : isCI ? ['dots'] : ['spec'], + reporters: isCI ? ['dots'] : ['spec'], // web server port port: 8080, @@ -138,16 +142,14 @@ module.exports = function (config) { }, // If browser does not capture in given timeout [ms], kill it - captureTimeout : 120000, + captureTimeout: 120000, // Continuous Integration mode // if true, it capture browsers, run tests and exit - singleRun : true + singleRun: true }); config.set({ - browsers: isCI - ? filterBrowsers(config.customLaunchers, /^BS/) - : ['Chrome', 'Safari', 'Firefox', 'IE9 - Win7'] + browsers: isCI ? filterBrowsers(config.customLaunchers, /^BS/) : ['Chrome'] }); }; diff --git a/package.json b/package.json index 3e2209e5e..c48456ea1 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "github-changes": "^1.0.0", "jsdoc": "~3.4.1", "karma": "^1.1.0", + "karma-browserify": "^5.1.0", "karma-browserstack-launcher": "^1.0.0", "karma-chrome-launcher": "^1.0.0", "karma-firefox-launcher": "^1.0.0", @@ -78,7 +79,8 @@ "mocha": "^2.5.0", "serve": "^1.4.0", "sinon": "^1.17.2", - "sinon-chai": "^2.8.0" + "sinon-chai": "^2.8.0", + "watchify": "^3.8.0" }, "dependencies": { "eventemitter2": "~1.0.0", diff --git a/test/.eslintrc.js b/test/.eslintrc.js index 212292d0b..5014664f4 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -1,7 +1,8 @@ module.exports = { "env": { "browser": true, - "mocha": true + "mocha": true, + "node": true, }, "extends": "../.eslintrc.js", "globals": { @@ -13,6 +14,7 @@ module.exports = { "brace-style": ["error", "stroustrup", { "allowSingleLine": true }], // Disable no-unused-expressions for chai expectations "no-unused-expressions": "off", - "newline-after-var": "off" + "newline-after-var": "off", + "max-len": "off" } }; diff --git a/test/unit/api-points-spec.js b/test/unit/api-points-spec.js index 48d51f8bf..4df79ded1 100644 --- a/test/unit/api-points-spec.js +++ b/test/unit/api-points-spec.js @@ -1,173 +1,173 @@ -/* eslint-disable max-len */ +'use strict'; -(function(Peaks) { - describe('Peaks.points', function() { - var p, deprecationLogger; +var Peaks = require('../../src/main.js'); - /** - * SETUP ========================================================= - */ +describe('Peaks.points', function() { + var p, deprecationLogger; - beforeEach(function(done) { - deprecationLogger = sinon.spy(); + /** + * SETUP ========================================================= + */ - p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: 'base/test_data/sample.json', - keyboard: true, - height: 240, - deprecationLogger: deprecationLogger - }); + beforeEach(function(done) { + deprecationLogger = sinon.spy(); - p.on('points.ready', done); + p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: 'base/test_data/sample.json', + keyboard: true, + height: 240, + deprecationLogger: deprecationLogger }); - afterEach(function() { - p.destroy(); - }); + p.on('points.ready', done); + }); - /** - * TESTS ========================================================= - */ + afterEach(function() { + p.destroy(); + }); - describe('getPoints', function() { - it('should return an empty array by default', function() { - expect(p.points.getPoints()).to.be.an('array').and.have.length.of(0); - }); + /** + * TESTS ========================================================= + */ - it('should return any added point', function() { - p.points.add({ timestamp: 10 }); - p.points.add({ timestamp: 12 }); - expect(p.points.getPoints()).to.have.length.of(2); - }); + describe('getPoints', function() { + it('should return an empty array by default', function() { + expect(p.points.getPoints()).to.be.an('array').and.have.length.of(0); }); - describe('add', function() { - it('should create a point from the supplied object', function() { - p.points.add({ timestamp: 10, editable: true, color: '#ff0000', labelText: 'A point' }); + it('should return any added point', function() { + p.points.add({ timestamp: 10 }); + p.points.add({ timestamp: 12 }); + expect(p.points.getPoints()).to.have.length.of(2); + }); + }); - expect(p.points.getPoints()) - .to.have.a.lengthOf(1) - .and.to.have.deep.property('[0].timestamp', 10); - }); + describe('add', function() { + it('should create a point from the supplied object', function() { + p.points.add({ timestamp: 10, editable: true, color: '#ff0000', labelText: 'A point' }); - it('should accept an array of point objects', function() { - var points = [ - { timestamp: 10, editable: true, color: '#ff0000', labelText: 'A point' }, - { timestamp: 12, editable: true, color: '#ff0000', labelText: 'Another point' } - ]; + expect(p.points.getPoints()) + .to.have.a.lengthOf(1) + .and.to.have.deep.property('[0].timestamp', 10); + }); - p.points.add(points); + it('should accept an array of point objects', function() { + var points = [ + { timestamp: 10, editable: true, color: '#ff0000', labelText: 'A point' }, + { timestamp: 12, editable: true, color: '#ff0000', labelText: 'Another point' } + ]; - expect(p.points.getPoints()).to.have.length.of(2); - expect(p.points.getPoints()[1]).to.include.keys('timestamp', 'labelText'); - }); + p.points.add(points); - it('should accept spread-arguments (deprecated)', function() { - p.points.add(10, true, '#ff0000', 'A point'); + expect(p.points.getPoints()).to.have.length.of(2); + expect(p.points.getPoints()[1]).to.include.keys('timestamp', 'labelText'); + }); - expect(p.points.getPoints()) - .to.have.a.lengthOf(1) - .and.to.have.deep.property('[0].timestamp', 10); + it('should accept spread-arguments (deprecated)', function() { + p.points.add(10, true, '#ff0000', 'A point'); - expect(deprecationLogger).to.have.been.calledOnce; - }); + expect(p.points.getPoints()) + .to.have.a.lengthOf(1) + .and.to.have.deep.property('[0].timestamp', 10); - it('should accept a point id if passed', function() { - p.points.add({ timestamp: 10, id: 500 }); + expect(deprecationLogger).to.have.been.calledOnce; + }); - expect(p.points.getPoints()).to.have.a.lengthOf(1) - .and.to.have.deep.property('[0].id', 500); - }); + it('should accept a point id if passed', function() { + p.points.add({ timestamp: 10, id: 500 }); - it('should allow 0 for a point id', function() { - p.points.add({ timestamp: 10, id: 0 }); + expect(p.points.getPoints()).to.have.a.lengthOf(1) + .and.to.have.deep.property('[0].id', 500); + }); - expect(p.points.getPoints()).to.have.a.lengthOf(1) - .and.to.have.deep.property('[0].id', 0); - }); + it('should allow 0 for a point id', function() { + p.points.add({ timestamp: 10, id: 0 }); - it('should throw an exception if timestamp argument is undefined', function() { - expect(function() { - p.points.add({ timestamp: undefined }); - }).to.throw(TypeError); - }); + expect(p.points.getPoints()).to.have.a.lengthOf(1) + .and.to.have.deep.property('[0].id', 0); + }); - it('should throw an exception if timestamp argument is null', function() { - expect(function() { - p.points.add({ timestamp: null }); - }).to.throw(TypeError); - }); + it('should throw an exception if timestamp argument is undefined', function() { + expect(function() { + p.points.add({ timestamp: undefined }); + }).to.throw(TypeError); + }); - it('should throw an exception if the timestamp argument is NaN', function() { - expect(function() { - p.points.add(NaN); - }).to.throw(TypeError); + it('should throw an exception if timestamp argument is null', function() { + expect(function() { + p.points.add({ timestamp: null }); + }).to.throw(TypeError); + }); - expect(function() { - p.points.add({ timestamp: NaN }); - }).to.throw(TypeError); - }); + it('should throw an exception if the timestamp argument is NaN', function() { + expect(function() { + p.points.add(NaN); + }).to.throw(TypeError); - it('should throw an exception if the timestamp argument is missing', function() { - expect(function() { - p.points.add({}); - }).to.throw(TypeError); + expect(function() { + p.points.add({ timestamp: NaN }); + }).to.throw(TypeError); + }); - expect(function() { - p.points.add([ - { editable: true } - ]); - }).to.throw(TypeError); - }); + it('should throw an exception if the timestamp argument is missing', function() { + expect(function() { + p.points.add({}); + }).to.throw(TypeError); + + expect(function() { + p.points.add([ + { editable: true } + ]); + }).to.throw(TypeError); }); + }); - describe('removeByTime', function() { - beforeEach(function() { - p.points.add({ timestamp: 10, editable: true, id: 123 }); - p.points.add({ timestamp: 12, editable: false, id: 456 }); - }); + describe('removeByTime', function() { + beforeEach(function() { + p.points.add({ timestamp: 10, editable: true, id: 123 }); + p.points.add({ timestamp: 12, editable: false, id: 456 }); + }); - it('should remove one of the points', function() { - p.points.removeByTime(10); + it('should remove one of the points', function() { + p.points.removeByTime(10); - expect(p.points.getPoints()).to.have.length.of(1); - }); + expect(p.points.getPoints()).to.have.length.of(1); + }); - it('should leave the other point intact', function() { - p.points.removeByTime(10); + it('should leave the other point intact', function() { + p.points.removeByTime(10); - expect(p.points.getPoints()).to.have.deep.property('[0].id', 456); - expect(p.points.getPoints()).to.have.deep.property('[0].timestamp', 12); - }); + expect(p.points.getPoints()).to.have.deep.property('[0].id', 456); + expect(p.points.getPoints()).to.have.deep.property('[0].timestamp', 12); }); + }); - describe('removeById', function() { - it('should remove the point by matching id', function() { - p.points.add([ - { timestamp: 0, endTime: 10, id: 123 }, - { timestamp: 15, endTime: 25, id: 456 } - ]); + describe('removeById', function() { + it('should remove the point by matching id', function() { + p.points.add([ + { timestamp: 0, endTime: 10, id: 123 }, + { timestamp: 15, endTime: 25, id: 456 } + ]); - p.points.removeById(123); + p.points.removeById(123); - expect(p.points.getPoints()).to.have.a.lengthOf(1); - expect(p.points.getPoints()[0].id).to.eq(456); - }); + expect(p.points.getPoints()).to.have.a.lengthOf(1); + expect(p.points.getPoints()[0].id).to.eq(456); + }); - it('should remove all points with the given id', function() { - p.points.add([ - { timestamp: 0, endTime: 10, id: 123 }, - { timestamp: 15, endTime: 25, id: 456 }, - { timestamp: 30, endTime: 40, id: 456 } - ]); + it('should remove all points with the given id', function() { + p.points.add([ + { timestamp: 0, endTime: 10, id: 123 }, + { timestamp: 15, endTime: 25, id: 456 }, + { timestamp: 30, endTime: 40, id: 456 } + ]); - p.points.removeById(456); + p.points.removeById(456); - expect(p.points.getPoints()).to.have.a.lengthOf(1); - expect(p.points.getPoints()[0].id).to.eq(123); - }); + expect(p.points.getPoints()).to.have.a.lengthOf(1); + expect(p.points.getPoints()[0].id).to.eq(123); }); }); -})(peaks); +}); diff --git a/test/unit/api-segments-spec.js b/test/unit/api-segments-spec.js index fb2b73d25..56011b61b 100644 --- a/test/unit/api-segments-spec.js +++ b/test/unit/api-segments-spec.js @@ -1,281 +1,281 @@ -/* eslint-disable max-len */ +'use strict'; -(function(Peaks) { - describe('Peaks.segments', function() { - var p, deprecationLogger; +var Peaks = require('../../src/main.js'); - /** - * SETUP ========================================================= - */ +describe('Peaks.segments', function() { + var p, deprecationLogger; - beforeEach(function before(done) { - deprecationLogger = sinon.spy(); + /** + * SETUP ========================================================= + */ - p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: { - json: 'base/test_data/sample.json' - }, - keyboard: true, - height: 240, - deprecationLogger: deprecationLogger - }); + beforeEach(function before(done) { + deprecationLogger = sinon.spy(); - p.on('segments.ready', done); + p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: { + json: 'base/test_data/sample.json' + }, + keyboard: true, + height: 240, + deprecationLogger: deprecationLogger }); - afterEach(function() { - p.destroy(); - }); + p.on('segments.ready', done); + }); - /** - * TESTS ========================================================= - */ + afterEach(function() { + p.destroy(); + }); - describe('getSegments', function() { - it('should return an empty array by default', function() { - expect(p.segments.getSegments()).to.be.an('array').and.have.length.of(0); - }); + /** + * TESTS ========================================================= + */ + + describe('getSegments', function() { + it('should return an empty array by default', function() { + expect(p.segments.getSegments()).to.be.an('array').and.have.length.of(0); + }); - it('should return any added segment', function() { - p.segments.add({ startTime: 0, endTime: 10 }); - p.segments.add({ startTime: 2, endTime: 12 }); + it('should return any added segment', function() { + p.segments.add({ startTime: 0, endTime: 10 }); + p.segments.add({ startTime: 2, endTime: 12 }); - expect(p.segments.getSegments()).to.have.length.of(2); - }); + expect(p.segments.getSegments()).to.have.length.of(2); }); + }); - describe('add', function() { - it('should accept a single segment object', function() { - p.segments.add({ startTime: 0, endTime: 10 }); + describe('add', function() { + it('should accept a single segment object', function() { + p.segments.add({ startTime: 0, endTime: 10 }); - expect(p.segments.getSegments()).to.have.length.of(1); - expect(p.segments.getSegments()[0]).to.include.keys('startTime', 'endTime'); - expect(p.segments.getSegments()[0].startTime).to.equal(0); - expect(p.segments.getSegments()[0].endTime).to.equal(10); + expect(p.segments.getSegments()).to.have.length.of(1); + expect(p.segments.getSegments()[0]).to.include.keys('startTime', 'endTime'); + expect(p.segments.getSegments()[0].startTime).to.equal(0); + expect(p.segments.getSegments()[0].endTime).to.equal(10); - expect(deprecationLogger).to.not.have.been.called; - }); + expect(deprecationLogger).to.not.have.been.called; + }); - it('should accept a list of properties for a single segment (deprecated)', function() { - p.segments.add(0, 10); + it('should accept a list of properties for a single segment (deprecated)', function() { + p.segments.add(0, 10); - expect(p.segments.getSegments()).to.have.length.of(1); - expect(p.segments.getSegments()[0]).to.include.keys('startTime', 'endTime'); - expect(p.segments.getSegments()[0].startTime).to.equal(0); - expect(p.segments.getSegments()[0].endTime).to.equal(10); + expect(p.segments.getSegments()).to.have.length.of(1); + expect(p.segments.getSegments()[0]).to.include.keys('startTime', 'endTime'); + expect(p.segments.getSegments()[0].startTime).to.equal(0); + expect(p.segments.getSegments()[0].endTime).to.equal(10); - expect(deprecationLogger).to.have.been.calledOnce; - }); + expect(deprecationLogger).to.have.been.calledOnce; + }); - it('should throw an exception if the startTime argument is missing', function() { - expect(function() { - p.segments.add({ endTime: 10 }); - }).to.throw(TypeError); - }); + it('should throw an exception if the startTime argument is missing', function() { + expect(function() { + p.segments.add({ endTime: 10 }); + }).to.throw(TypeError); + }); - it('should throw an exception if the endTime argument is missing', function() { - expect(function() { - p.segments.add({ startTime: 0 }); - }).to.throw(TypeError); - }); + it('should throw an exception if the endTime argument is missing', function() { + expect(function() { + p.segments.add({ startTime: 0 }); + }).to.throw(TypeError); + }); - it('should accept an optional id for each segment', function() { - p.segments.add({ startTime: 0, endTime: 10, id: 123 }); + it('should accept an optional id for each segment', function() { + p.segments.add({ startTime: 0, endTime: 10, id: 123 }); - expect(p.segments.getSegments()[0].id).to.equal(123); - }); + expect(p.segments.getSegments()[0].id).to.equal(123); + }); - it('should allow 0 for a segment id', function() { - p.segments.add({ startTime: 0, endTime: 10, id: 0 }); + it('should allow 0 for a segment id', function() { + p.segments.add({ startTime: 0, endTime: 10, id: 0 }); - expect(p.segments.getSegments()[0].id).to.equal(0); - }); + expect(p.segments.getSegments()[0].id).to.equal(0); + }); - it('should assign a default id if not specified', function() { - p.segments.add({ startTime: 0, endTime: 10 }); + it('should assign a default id if not specified', function() { + p.segments.add({ startTime: 0, endTime: 10 }); - expect(p.segments.getSegments()[0].id).to.equal('peaks.segment.0'); - }); + expect(p.segments.getSegments()[0].id).to.equal('peaks.segment.0'); + }); - it('should accept an optional segment color', function() { - p.segments.add({ startTime: 0, endTime: 10, color: '#888' }); + it('should accept an optional segment color', function() { + p.segments.add({ startTime: 0, endTime: 10, color: '#888' }); - expect(p.segments.getSegments()[0].color).to.equal('#888'); - }); + expect(p.segments.getSegments()[0].color).to.equal('#888'); + }); - it('should assign a default (random) color if not specified', function() { - p.segments.add({ startTime: 0, endTime: 10 }); + it('should assign a default (random) color if not specified', function() { + p.segments.add({ startTime: 0, endTime: 10 }); - expect(p.segments.getSegments()[0].color).to.match(/rgba\(\d+, \d+, \d+, 1\)/); - }); + expect(p.segments.getSegments()[0].color).to.match(/rgba\(\d+, \d+, \d+, 1\)/); + }); - it('should accept an optional label text', function() { - p.segments.add({ startTime: 0, endTime: 10, labelText: 'test' }); + it('should accept an optional label text', function() { + p.segments.add({ startTime: 0, endTime: 10, labelText: 'test' }); - expect(p.segments.getSegments()[0].labelText).to.equal('test'); - }); + expect(p.segments.getSegments()[0].labelText).to.equal('test'); + }); - it('should assign a default label text if not specified', function() { - p.segments.add({ startTime: 0, endTime: 10 }); + it('should assign a default label text if not specified', function() { + p.segments.add({ startTime: 0, endTime: 10 }); - expect(p.segments.getSegments()[0].labelText).to.equal(''); - }); + expect(p.segments.getSegments()[0].labelText).to.equal(''); + }); - it('should accept an array of segment objects', function() { - var segments = [{ startTime: 0, endTime: 10 }, { startTime: 5, endTime: 10 }]; + it('should accept an array of segment objects', function() { + var segments = [{ startTime: 0, endTime: 10 }, { startTime: 5, endTime: 10 }]; - p.segments.add(segments); + p.segments.add(segments); - expect(p.segments.getSegments()).to.have.length.of(2); - expect(p.segments.getSegments()[0]).to.include.keys('startTime', 'endTime'); - expect(p.segments.getSegments()[1]).to.include.keys('startTime', 'endTime'); - }); + expect(p.segments.getSegments()).to.have.length.of(2); + expect(p.segments.getSegments()[0]).to.include.keys('startTime', 'endTime'); + expect(p.segments.getSegments()[1]).to.include.keys('startTime', 'endTime'); + }); - it('should throw an exception if arguments are not matching any previous accepted signature form', function() { - expect(function() { p.segments.add({}); }).to.throw(TypeError); - expect(function() { p.segments.add(undefined); }).to.throw(TypeError); - expect(function() { p.segments.add(null); }).to.throw(TypeError); - expect(function() { p.segments.add(NaN, NaN); }).to.throw(TypeError); - }); + it('should throw an exception if arguments are not matching any previous accepted signature form', function() { + expect(function() { p.segments.add({}); }).to.throw(TypeError); + expect(function() { p.segments.add(undefined); }).to.throw(TypeError); + expect(function() { p.segments.add(null); }).to.throw(TypeError); + expect(function() { p.segments.add(NaN, NaN); }).to.throw(TypeError); + }); - it('should throw an exception if the startTime is NaN', function() { - expect(function() { - p.points.add({ startTime: NaN, endTime: 1.0 }); - }).to.throw(TypeError); - }); + it('should throw an exception if the startTime is NaN', function() { + expect(function() { + p.points.add({ startTime: NaN, endTime: 1.0 }); + }).to.throw(TypeError); + }); - it('should throw an exception if the endTime is NaN', function() { - expect(function() { - p.segments.add({ startTime: 1.0, endTime: NaN }); - }).to.throw(TypeError); - }); + it('should throw an exception if the endTime is NaN', function() { + expect(function() { + p.segments.add({ startTime: 1.0, endTime: NaN }); + }).to.throw(TypeError); }); + }); - describe('remove', function() { - beforeEach(function() { - p.segments.add({ startTime: 10, endTime: 12 }); - }); + describe('remove', function() { + beforeEach(function() { + p.segments.add({ startTime: 10, endTime: 12 }); + }); - it('should throw an exception if you remove a segment which does not exist', function() { - expect(function() { p.segments.remove({}); }).to.throw(); - }); + it('should throw an exception if you remove a segment which does not exist', function() { + expect(function() { p.segments.remove({}); }).to.throw(); + }); - it('should return the deleted segment object if properly deleted', function() { - var segment = p.segments.getSegments()[0]; + it('should return the deleted segment object if properly deleted', function() { + var segment = p.segments.getSegments()[0]; - expect(p.segments.remove(segment)).to.equal(segment); - }); + expect(p.segments.remove(segment)).to.equal(segment); + }); - it('should remove the segment from the segments array', function() { - var segment = p.segments.getSegments()[0]; + it('should remove the segment from the segments array', function() { + var segment = p.segments.getSegments()[0]; - p.segments.remove(segment); + p.segments.remove(segment); - expect(p.segments.getSegments()).to.not.include(segment); - }); + expect(p.segments.getSegments()).to.not.include(segment); + }); - it('should remove the segment from both view layers', function() { - var segment = p.segments.getSegments()[0]; + it('should remove the segment from both view layers', function() { + var segment = p.segments.getSegments()[0]; - p.segments.remove(segment); + p.segments.remove(segment); - expect(p.waveform.waveformOverview.segmentLayer.children).to.not.include(segment.overview); - expect(p.waveform.waveformZoomView.segmentLayer.children).to.not.include(segment.zoom); - }); + expect(p.waveform.waveformOverview.segmentLayer.children).to.not.include(segment.overview); + expect(p.waveform.waveformZoomView.segmentLayer.children).to.not.include(segment.zoom); }); + }); - describe('removeByTime', function() { - beforeEach(function() { - p.segments.add({ startTime: 10, endTime: 12 }); - p.segments.add({ startTime: 5, endTime: 12 }); + describe('removeByTime', function() { + beforeEach(function() { + p.segments.add({ startTime: 10, endTime: 12 }); + p.segments.add({ startTime: 5, endTime: 12 }); - p.segments.add({ startTime: 3, endTime: 6 }); - p.segments.add({ startTime: 3, endTime: 10 }); - }); + p.segments.add({ startTime: 3, endTime: 6 }); + p.segments.add({ startTime: 3, endTime: 10 }); + }); - it('should not remove any segment if the startTime does not match with any segment', function() { - p.segments.removeByTime(6); + it('should not remove any segment if the startTime does not match with any segment', function() { + p.segments.removeByTime(6); - expect(p.segments.getSegments()).to.have.a.lengthOf(4); - }); + expect(p.segments.getSegments()).to.have.a.lengthOf(4); + }); - it('should not remove any segment if only the endTime matches the end of a segment', function() { - p.segments.removeByTime(6, 12); + it('should not remove any segment if only the endTime matches the end of a segment', function() { + p.segments.removeByTime(6, 12); - expect(p.segments.getSegments()).to.have.a.lengthOf(4); - }); + expect(p.segments.getSegments()).to.have.a.lengthOf(4); + }); - it('should remove the only segment matching the startTime', function() { - p.segments.removeByTime(5); + it('should remove the only segment matching the startTime', function() { + p.segments.removeByTime(5); - expect(p.segments.getSegments()).to.have.a.lengthOf(3); - expect(p.segments.getSegments()[0].startTime).to.equal(10); - expect(p.segments.getSegments()[1].startTime).to.equal(3); - expect(p.segments.getSegments()[2].startTime).to.equal(3); - }); + expect(p.segments.getSegments()).to.have.a.lengthOf(3); + expect(p.segments.getSegments()[0].startTime).to.equal(10); + expect(p.segments.getSegments()[1].startTime).to.equal(3); + expect(p.segments.getSegments()[2].startTime).to.equal(3); + }); - it('should return the number of deleted segments', function() { - expect(p.segments.removeByTime(3)).to.equal(2); - }); + it('should return the number of deleted segments', function() { + expect(p.segments.removeByTime(3)).to.equal(2); + }); - it('should remove the two segments matching the startTime', function() { - p.segments.removeByTime(3); + it('should remove the two segments matching the startTime', function() { + p.segments.removeByTime(3); - expect(p.segments.getSegments()).to.have.a.lengthOf(2); - }); + expect(p.segments.getSegments()).to.have.a.lengthOf(2); + }); - it('should remove only the segment matching both starTime and endTime', function() { - p.segments.removeByTime(3, 6); + it('should remove only the segment matching both starTime and endTime', function() { + p.segments.removeByTime(3, 6); - expect(p.segments.getSegments()).to.have.a.lengthOf(3); - }); + expect(p.segments.getSegments()).to.have.a.lengthOf(3); }); + }); - describe('removeById', function() { - it('should remove the segment by matching id', function() { - p.segments.add([ - { startTime: 0, endTime: 10, id: 123 }, - { startTime: 15, endTime: 25, id: 456 } - ]); + describe('removeById', function() { + it('should remove the segment by matching id', function() { + p.segments.add([ + { startTime: 0, endTime: 10, id: 123 }, + { startTime: 15, endTime: 25, id: 456 } + ]); - p.segments.removeById(123); - expect(p.segments.getSegments()).to.have.a.lengthOf(1); - expect(p.segments.getSegments()[0].id).to.eq(456); - }); + p.segments.removeById(123); + expect(p.segments.getSegments()).to.have.a.lengthOf(1); + expect(p.segments.getSegments()[0].id).to.eq(456); + }); - it('should remove all segments with matching id', function() { - p.segments.add([ - { startTime: 0, endTime: 10, id: 123 }, - { startTime: 15, endTime: 25, id: 456 }, - { startTime: 30, endTime: 40, id: 456 } - ]); + it('should remove all segments with matching id', function() { + p.segments.add([ + { startTime: 0, endTime: 10, id: 123 }, + { startTime: 15, endTime: 25, id: 456 }, + { startTime: 30, endTime: 40, id: 456 } + ]); - p.segments.removeById(456); - expect(p.segments.getSegments()).to.have.a.lengthOf(1); - expect(p.segments.getSegments()[0].id).to.eq(123); - }); + p.segments.removeById(456); + expect(p.segments.getSegments()).to.have.a.lengthOf(1); + expect(p.segments.getSegments()[0].id).to.eq(123); }); + }); - describe('removeAll', function() { - beforeEach(function() { - p.segments.add({ startTime: 10, endTime: 12 }); - p.segments.add({ startTime: 5, endTime: 12 }); - }); + describe('removeAll', function() { + beforeEach(function() { + p.segments.add({ startTime: 10, endTime: 12 }); + p.segments.add({ startTime: 5, endTime: 12 }); + }); - it('should clear all segments objects', function() { - p.segments.removeAll(); + it('should clear all segments objects', function() { + p.segments.removeAll(); - expect(p.segments.getSegments()).to.be.empty; - }); + expect(p.segments.getSegments()).to.be.empty; + }); - it('should clear views groups as well', function() { - p.segments.removeAll(); + it('should clear views groups as well', function() { + p.segments.removeAll(); - expect(p.waveform.waveformOverview.segmentLayer.children).to.have.a.property('length', 0); - expect(p.waveform.waveformZoomView.segmentLayer.children).to.have.a.property('length', 0); - }); + expect(p.waveform.waveformOverview.segmentLayer.children).to.have.a.property('length', 0); + expect(p.waveform.waveformZoomView.segmentLayer.children).to.have.a.property('length', 0); }); }); -})(peaks); +}); diff --git a/test/unit/api-spec.js b/test/unit/api-spec.js index ad9c789be..b8e555b22 100644 --- a/test/unit/api-spec.js +++ b/test/unit/api-spec.js @@ -1,287 +1,286 @@ -/* eslint-disable max-len */ +'use strict'; -(function(Peaks) { +var Peaks = require('../../src/main.js'); + +describe('Peaks API interface', function() { + var sandbox; var TestAudioContext = window.AudioContext || window.mozAudioContext || window.webkitAudioContext; - describe('Peaks API interface', function() { - var sandbox; + /** + * SETUP ========================================================= + */ - /** - * SETUP ========================================================= - */ + beforeEach(function beforeEach(done) { + sandbox = sinon.sandbox.create(); + setTimeout(done, 100); + }); - beforeEach(function beforeEach(done) { - sandbox = sinon.sandbox.create(); - setTimeout(done, 100); - }); + /** + * TEARDOWN ====================================================== + */ - /** - * TEARDOWN ====================================================== - */ + afterEach(function() { + sandbox.restore(); + }); - afterEach(function() { - sandbox.restore(); + /** + * TESTS ========================================================= + */ + describe('create', function() { + it('should throw an exception if no mediaElement is provided', function() { + expect(function() { + Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + dataUri: { arraybuffer: 'base/test_data/sample.dat' } + }); + }).to.throw(/provide an audio element/); }); - /** - * TESTS ========================================================= - */ - describe('create', function() { - it('should throw an exception if no mediaElement is provided', function() { - expect(function() { - Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - dataUri: { arraybuffer: 'base/test_data/sample.dat' } - }); - }).to.throw(/provide an audio element/); - }); - - it('should throw an exception if mediaElement is not an HTMLMediaElement', function() { - expect(function() { - Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.createElement('div'), - dataUri: { arraybuffer: 'base/test_data/sample.dat' } - }); - }).to.throw(/HTMLMediaElement/); - }); - - it('should thrown an exception if no container is provided', function() { - expect(function() { - Peaks.init({ - mediaElement: document.querySelector('audio'), - dataUri: { arraybuffer: 'base/test_data/sample.dat' } - }); - }).to.throw(/provide a container/); - }); + it('should throw an exception if mediaElement is not an HTMLMediaElement', function() { + expect(function() { + Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.createElement('div'), + dataUri: { arraybuffer: 'base/test_data/sample.dat' } + }); + }).to.throw(/HTMLMediaElement/); + }); - it('should thrown an exception if the container has no layout', function() { - expect(function() { - Peaks.init({ - container: document.createElement('div'), - mediaElement: document.querySelector('audio'), - dataUri: { arraybuffer: 'base/test_data/sample.dat' } - }); - }).to.throw(/width/); - }); + it('should thrown an exception if no container is provided', function() { + expect(function() { + Peaks.init({ + mediaElement: document.querySelector('audio'), + dataUri: { arraybuffer: 'base/test_data/sample.dat' } + }); + }).to.throw(/provide a container/); + }); - it('should thrown an exception if the logger is defined and not a function', function() { - expect(function() { - Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: 'base/test_data/sample.json', - logger: 'foo' - }); - }).to.throw(/logger/); - }); + it('should thrown an exception if the container has no layout', function() { + expect(function() { + Peaks.init({ + container: document.createElement('div'), + mediaElement: document.querySelector('audio'), + dataUri: { arraybuffer: 'base/test_data/sample.dat' } + }); + }).to.throw(/width/); + }); - it('should broadcast errors to a configurable logger', function(done) { - var p = Peaks.init({ + it('should thrown an exception if the logger is defined and not a function', function() { + expect(function() { + Peaks.init({ container: document.getElementById('waveform-visualiser-container'), mediaElement: document.querySelector('audio'), dataUri: 'base/test_data/sample.json', - logger: sandbox.spy() + logger: 'foo' }); + }).to.throw(/logger/); + }); - p.emit('error', new Error('Expected to be logged.')); - - setTimeout(function() { - expect(p.logger).to.have.been.calledOnce; - done(); - }, 0); + it('should broadcast errors to a configurable logger', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: 'base/test_data/sample.json', + logger: sandbox.spy() }); + + p.emit('error', new Error('Expected to be logged.')); + + setTimeout(function() { + expect(p.logger).to.have.been.calledOnce; + done(); + }, 0); }); + }); - describe('destroy', function() { - var container; + describe('destroy', function() { + var container; - beforeEach(function() { - var regularContainer = document.getElementById('waveform-visualiser-container'); - container = document.createElement('div'); - container.style.width = '400px'; - container.style.height = '100px'; - regularContainer.parentNode.appendChild(container); - }); + beforeEach(function() { + var regularContainer = document.getElementById('waveform-visualiser-container'); + container = document.createElement('div'); + container.style.width = '400px'; + container.style.height = '100px'; + regularContainer.parentNode.appendChild(container); + }); - afterEach(function() { - if (container.parentNode) { - container.parentNode.removeChild(container); - } + afterEach(function() { + if (container.parentNode) { + container.parentNode.removeChild(container); + } + }); + + it('should clean up event listeners', function(done) { + var errorSpy = sinon.spy().named('window.onerror'); + var resizeSpy = sinon.spy().named('window_resized'); + var oldOnError = window.onerror; + window.onerror = errorSpy; + + var p = Peaks.init({ + container: container, + mediaElement: document.querySelector('audio'), + audioContext: new TestAudioContext() }); - it('should clean up event listeners', function(done) { - var errorSpy = sinon.spy().named('window.onerror'); - var resizeSpy = sinon.spy().named('window_resized'); - var oldOnError = window.onerror; - window.onerror = errorSpy; + p.on('window_resized', resizeSpy); - var p = Peaks.init({ - container: container, - mediaElement: document.querySelector('audio'), - audioContext: new TestAudioContext() - }); + p.on('waveform_ready.overview', function() { + container.parentNode.removeChild(container); - p.on('window_resized', resizeSpy); + // Give peaks chance to bind its resize listener: + setTimeout(function() { + p.destroy(); - p.on('waveform_ready.overview', function() { - container.parentNode.removeChild(container); + // Fire a resize event, which would normally cause peaks to redraw + var e = document.createEvent('HTMLEvents'); + e.initEvent('resize', true, false); + window.dispatchEvent(e); - // Give peaks chance to bind its resize listener: + // Our resize handler is asynchronously throttled, so give it a little time to settle. setTimeout(function() { - p.destroy(); - - // Fire a resize event, which would normally cause peaks to redraw - var e = document.createEvent('HTMLEvents'); - e.initEvent('resize', true, false); - window.dispatchEvent(e); - - // Our resize handler is asynchronously throttled, so give it a little time to settle. - setTimeout(function() { - window.onerror = oldOnError; - expect(resizeSpy).to.not.have.been.called; - expect(errorSpy).to.not.have.been.called; - done(); - }, 600); - }, 1); - }); + window.onerror = oldOnError; + expect(resizeSpy).to.not.have.been.called; + expect(errorSpy).to.not.have.been.called; + done(); + }, 600); + }, 1); }); }); + }); - describe('core#getRemoteData', function() { - it('should use the dataUriDefaultFormat value as a format URL if dataUri is provided as string', function(done) { - var p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: 'base/test_data/sample.json' - }); + describe('core#getRemoteData', function() { + it('should use the dataUriDefaultFormat value as a format URL if dataUri is provided as string', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: 'base/test_data/sample.json' + }); - var spy = sandbox.spy(p.waveform, 'handleRemoteData'); + var spy = sandbox.spy(p.waveform, 'handleRemoteData'); - p.on('segments.ready', function() { - var xhr = spy.getCall(0).args[1]; + p.on('segments.ready', function() { + var xhr = spy.getCall(0).args[1]; - expect(xhr.getResponseHeader('content-type')).to.equal('application/json'); + expect(xhr.getResponseHeader('content-type')).to.equal('application/json'); - done(); - }); + done(); }); + }); - it('should emit an error if the data handling fails', function(done) { - var p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: 'base/test_data/404-file.json' - }); + it('should emit an error if the data handling fails', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: 'base/test_data/404-file.json' + }); - p.on('error', function(err) { - done(); - }); + p.on('error', function(err) { + done(); }); + }); - it.skip('should emit an error if the data handling fails due to a network error', function(done) { - var p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: 'file:///test.json' - }); + it.skip('should emit an error if the data handling fails due to a network error', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: 'file:///test.json' + }); - p.on('error', function(err) { - expect(err).to.be.an.instanceof(Error); - expect(err.message).to.equal('XHR Failed'); - done(); - }); + p.on('error', function(err) { + expect(err).to.be.an.instanceof(Error); + expect(err.message).to.equal('XHR Failed'); + done(); }); + }); - it('should use the JSON dataUri connector', function(done) { - var p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: { - json: 'base/test_data/sample.json' - } - }); + it('should use the JSON dataUri connector', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: { + json: 'base/test_data/sample.json' + } + }); - var spy = sandbox.spy(p.waveform, 'handleRemoteData'); + var spy = sandbox.spy(p.waveform, 'handleRemoteData'); - p.on('segments.ready', function() { - var xhr = spy.getCall(0).args[1]; + p.on('segments.ready', function() { + var xhr = spy.getCall(0).args[1]; - expect(xhr.getResponseHeader('content-type')).to.equal('application/json'); + expect(xhr.getResponseHeader('content-type')).to.equal('application/json'); - done(); - }); + done(); }); + }); - ('ArrayBuffer' in window) && it('should use the arraybuffer dataUri connector or fail if not available', function(done) { - var p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: { - arraybuffer: 'base/test_data/sample.dat' - } - }); - - var spy = sandbox.spy(p.waveform, 'handleRemoteData'); + ('ArrayBuffer' in window) && it('should use the arraybuffer dataUri connector or fail if not available', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: { + arraybuffer: 'base/test_data/sample.dat' + } + }); - p.on('segments.ready', function() { - var xhr = spy.getCall(0).args[1]; + var spy = sandbox.spy(p.waveform, 'handleRemoteData'); - expect(xhr.getResponseHeader('content-type')).to.equal('text/plain'); + p.on('segments.ready', function() { + var xhr = spy.getCall(0).args[1]; - done(); - }); - }); + expect(xhr.getResponseHeader('content-type')).to.equal('text/plain'); - !('ArrayBuffer' in window) && it('should throw an exception if the only available format is browser incompatible', function() { - expect(function() { - Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: { - arraybuffer: 'base/test_data/sample.dat' - } - }); - }).to.throw(); + done(); }); + }); - it('should pick the arraybuffer format over the JSON one', function(done) { - var p = Peaks.init({ + !('ArrayBuffer' in window) && it('should throw an exception if the only available format is browser incompatible', function() { + expect(function() { + Peaks.init({ container: document.getElementById('waveform-visualiser-container'), mediaElement: document.querySelector('audio'), dataUri: { - arraybuffer: 'base/test_data/sample.dat', - json: 'base/test_data/sample.json' + arraybuffer: 'base/test_data/sample.dat' } }); + }).to.throw(); + }); + + it('should pick the arraybuffer format over the JSON one', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: { + arraybuffer: 'base/test_data/sample.dat', + json: 'base/test_data/sample.json' + } + }); - var spy = sandbox.spy(p.waveform, 'handleRemoteData'); - var expectedContentType = window.ArrayBuffer ? 'text/plain' : 'application/json'; + var spy = sandbox.spy(p.waveform, 'handleRemoteData'); + var expectedContentType = window.ArrayBuffer ? 'text/plain' : 'application/json'; - p.on('segments.ready', function() { - var xhr = spy.getCall(0).args[1]; + p.on('segments.ready', function() { + var xhr = spy.getCall(0).args[1]; - expect(xhr.getResponseHeader('content-type')).to.equal(expectedContentType); + expect(xhr.getResponseHeader('content-type')).to.equal(expectedContentType); - done(); - }); + done(); }); + }); - ('AudioBuffer' in window) && it('should build using WebAudio if the API is available and no dataUri is provided', function(done) { - var p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - audioContext: new TestAudioContext - }); + ('AudioBuffer' in window) && it('should build using WebAudio if the API is available and no dataUri is provided', function(done) { + var p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + audioContext: new TestAudioContext + }); - var spy = sandbox.spy(p.waveform, 'handleRemoteData'); + var spy = sandbox.spy(p.waveform, 'handleRemoteData'); - p.on('segments.ready', function() { - expect(spy).to.have.been.calledOnce; + p.on('segments.ready', function() { + expect(spy).to.have.been.calledOnce; - done(); - }); + done(); }); }); }); -})(peaks); +}); diff --git a/test/unit/api-time-spec.js b/test/unit/api-time-spec.js index 707311c74..2c68389f9 100644 --- a/test/unit/api-time-spec.js +++ b/test/unit/api-time-spec.js @@ -1,59 +1,59 @@ -/* eslint-disable max-len */ - -(function(Peaks) { - describe('Peaks.time', function() { - var sandbox, p; - - beforeEach(function beforeEach(done) { - sandbox = sinon.sandbox.create(); - - p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: { - json: 'base/test_data/sample.json' - }, - keyboard: true, - height: 240 - }); +'use strict'; - p.on('segments.ready', done); - }); +var Peaks = require('../../src/main.js'); + +describe('Peaks.time', function() { + var sandbox, p; + + beforeEach(function beforeEach(done) { + sandbox = sinon.sandbox.create(); - afterEach(function() { - p.destroy(); - sandbox.restore(); + p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: { + json: 'base/test_data/sample.json' + }, + keyboard: true, + height: 240 }); - describe.skip('getCurrentTime', function() { - var newTime = 6.0; + p.on('segments.ready', done); + }); - it('should return the actual value of the audio element', function() { - expect(p.time.getCurrentTime()).to.equal(0); - }); + afterEach(function() { + p.destroy(); + sandbox.restore(); + }); + + describe.skip('getCurrentTime', function() { + var newTime = 6.0; - // @see https://github.com/bbc/peaks.js/issues/9 - // @see https://github.com/bbc/peaks.js/issues/12 - // For some reason, the event is not emitted during the tests - it('should return an updated time if it has been modified through the audio element', function(done) { - p.on('player_seek', function(currentTime) { - expect(p.time.getCurrentTime()).to.equal(newTime); - expect(currentTime).to.equal(newTime); - done(); - }); - - document.querySelector('audio').currentTime = newTime; + it('should return the actual value of the audio element', function() { + expect(p.time.getCurrentTime()).to.equal(0); + }); + + // @see https://github.com/bbc/peaks.js/issues/9 + // @see https://github.com/bbc/peaks.js/issues/12 + // For some reason, the event is not emitted during the tests + it('should return an updated time if it has been modified through the audio element', function(done) { + p.on('player_seek', function(currentTime) { + expect(p.time.getCurrentTime()).to.equal(newTime); + expect(currentTime).to.equal(newTime); + done(); }); + + document.querySelector('audio').currentTime = newTime; }); + }); - describe.skip('setCurrentTime', function() { - var newTime = 6.0; + describe.skip('setCurrentTime', function() { + var newTime = 6.0; - it('should alter the currentTime value of the audio element', function() { - p.time.setCurrentTime(newTime); + it('should alter the currentTime value of the audio element', function() { + p.time.setCurrentTime(newTime); - expect(p.time.getCurrentTime()).to.equal(newTime); - }); + expect(p.time.getCurrentTime()).to.equal(newTime); }); }); -})(peaks); +}); diff --git a/test/unit/api-zoom-spec.js b/test/unit/api-zoom-spec.js index 34230122b..91c86c09d 100644 --- a/test/unit/api-zoom-spec.js +++ b/test/unit/api-zoom-spec.js @@ -1,102 +1,102 @@ -/* eslint-disable max-len */ - -(function(Peaks) { - describe('Peaks.zoom', function() { - var p, sandbox; - - /** - * SETUP ========================================================= - */ - - beforeEach(function beforeEach(done) { - sandbox = sinon.sandbox.create(); - - p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: { - json: 'base/test_data/sample.json' - }, - keyboard: true, - height: 240, - zoomLevels: [512, 1024] - }); - - p.on('segments.ready', done); - }); +'use strict'; + +var Peaks = require('../../src/main.js'); + +describe('Peaks.zoom', function() { + var p, sandbox; + + /** + * SETUP ========================================================= + */ - afterEach(function() { - p.destroy(); - sandbox.restore(); + beforeEach(function beforeEach(done) { + sandbox = sinon.sandbox.create(); + + p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: { + json: 'base/test_data/sample.json' + }, + keyboard: true, + height: 240, + zoomLevels: [512, 1024] }); - /** - * TESTS ========================================================= - */ + p.on('segments.ready', done); + }); + + afterEach(function() { + p.destroy(); + sandbox.restore(); + }); + + /** + * TESTS ========================================================= + */ - describe('getZoom', function() { - it('should be able to get the initial zoom level value, which is 0', function() { - expect(p.zoom.getZoom()).to.equal(0); - }); + describe('getZoom', function() { + it('should be able to get the initial zoom level value, which is 0', function() { + expect(p.zoom.getZoom()).to.equal(0); }); + }); - describe('setZoom', function() { - it('should update the zoom level value', function() { - p.zoom.setZoom(1); + describe('setZoom', function() { + it('should update the zoom level value', function() { + p.zoom.setZoom(1); - expect(p.zoom.getZoom()).to.equal(1); - }); + expect(p.zoom.getZoom()).to.equal(1); + }); - it('should dispatch zoom.update with the associated zoom level value', function() { - var spy = sandbox.spy(); + it('should dispatch zoom.update with the associated zoom level value', function() { + var spy = sandbox.spy(); - p.on('zoom.update', spy); - p.zoom.setZoom(1); + p.on('zoom.update', spy); + p.zoom.setZoom(1); - expect(spy.calledWith(1024)).to.equal(true); - }); + expect(spy.calledWith(1024)).to.equal(true); + }); - it('should limit the zoom level index value to the minimum valid index', function() { - p.zoom.setZoom(-1); + it('should limit the zoom level index value to the minimum valid index', function() { + p.zoom.setZoom(-1); - expect(p.zoom.getZoom()).to.equal(0); - }); + expect(p.zoom.getZoom()).to.equal(0); + }); - it('should limit the zoom level index to the maximum valid index', function() { - p.zoom.setZoom(2); + it('should limit the zoom level index to the maximum valid index', function() { + p.zoom.setZoom(2); - expect(p.zoom.getZoom()).to.equal(1); - }); + expect(p.zoom.getZoom()).to.equal(1); + }); - it('should not throw an exception if an existing zoom level does not have sufficient data', function() { - expect(function() { - p.zoom.setZoom(3); - }).not.to.throw(); - }); + it('should not throw an exception if an existing zoom level does not have sufficient data', function() { + expect(function() { + p.zoom.setZoom(3); + }).not.to.throw(); }); + }); - describe('zoomOut', function() { - it('should call setZoom with a bigger zoom level', function() { - var spy = sandbox.spy(); + describe('zoomOut', function() { + it('should call setZoom with a bigger zoom level', function() { + var spy = sandbox.spy(); - p.on('zoom.update', spy); - p.zoom.zoomOut(); + p.on('zoom.update', spy); + p.zoom.zoomOut(); - expect(spy).to.have.been.calledWith(1024, 512); - }); + expect(spy).to.have.been.calledWith(1024, 512); }); + }); - describe('zoomIn', function() { - it('should call setZoom with a smaller zoom level', function() { - p.zoom.setZoom(1); + describe('zoomIn', function() { + it('should call setZoom with a smaller zoom level', function() { + p.zoom.setZoom(1); - var spy = sandbox.spy(); + var spy = sandbox.spy(); - p.on('zoom.update', spy); - p.zoom.zoomIn(); + p.on('zoom.update', spy); + p.zoom.zoomIn(); - expect(spy).to.have.been.calledWith(512, 1024); - }); + expect(spy).to.have.been.calledWith(512, 1024); }); }); -})(peaks); +}); diff --git a/test/unit/waveform-segments-spec.js b/test/unit/waveform-segments-spec.js index 1cd78b794..cb84a217e 100644 --- a/test/unit/waveform-segments-spec.js +++ b/test/unit/waveform-segments-spec.js @@ -1,85 +1,85 @@ -/* eslint-disable max-len */ - -(function(Peaks) { - describe('player/waveform/waveform.segments', function() { - var sandbox, p; - - beforeEach(function beforeEach(done) { - sandbox = sinon.sandbox.create(); - - p = Peaks.init({ - container: document.getElementById('waveform-visualiser-container'), - mediaElement: document.querySelector('audio'), - dataUri: { - json: 'base/test_data/sample.json' - }, - keyboard: true, - height: 240 - }); +'use strict'; - p.on('segments.ready', done); - }); +var Peaks = require('../../src/main.js'); + +describe('player/waveform/waveform.segments', function() { + var sandbox, p; + + beforeEach(function beforeEach(done) { + sandbox = sinon.sandbox.create(); - afterEach(function() { - p.destroy(); - sandbox.restore(); + p = Peaks.init({ + container: document.getElementById('waveform-visualiser-container'), + mediaElement: document.querySelector('audio'), + dataUri: { + json: 'base/test_data/sample.json' + }, + keyboard: true, + height: 240 }); - describe('segments.add', function() { - it('should accept a single Segment object', function() { - var spy = sandbox.spy(p.waveform.segments, 'createSegment'); + p.on('segments.ready', done); + }); - p.segments.add({ startTime: 0, endTime: 10, editable: false }); - expect(spy.callCount).to.equal(1); - expect(spy.args[0][0]).to.deep.equal({ startTime: 0, endTime: 10, editable: false }); - }); + afterEach(function() { + p.destroy(); + sandbox.restore(); + }); - it('should accept spreaded arguments (deprecated)', function() { - p.options.deprecationLogger = sinon.spy(); - var stub = sandbox.stub(p.waveform.segments, 'createSegment'); + describe('segments.add', function() { + it('should accept a single Segment object', function() { + var spy = sandbox.spy(p.waveform.segments, 'createSegment'); - p.segments.add(0, 10, false); + p.segments.add({ startTime: 0, endTime: 10, editable: false }); + expect(spy.callCount).to.equal(1); + expect(spy.args[0][0]).to.deep.equal({ startTime: 0, endTime: 10, editable: false }); + }); + + it('should accept spreaded arguments (deprecated)', function() { + p.options.deprecationLogger = sinon.spy(); + var stub = sandbox.stub(p.waveform.segments, 'createSegment'); - expect(stub.callCount).to.equal(1); - expect(stub.args[0][0]).to.deep.equal({ - startTime: 0, - endTime: 10, - editable: false, - color: undefined, - labelText: undefined - }); + p.segments.add(0, 10, false); - expect(p.options.deprecationLogger).to.be.calledOnce; + expect(stub.callCount).to.equal(1); + expect(stub.args[0][0]).to.deep.equal({ + startTime: 0, + endTime: 10, + editable: false, + color: undefined, + labelText: undefined }); - it('should accept an array of Segment objects', function() { - var spy = sandbox.spy(p.waveform.segments, 'createSegment'); - - p.segments.add([ - { startTime: 0, endTime: 10, editable: false }, - { startTime: 10, endTime: 20, editable: true, color: 'rgba(255, 161, 39, 1)', labelText: 'dummy text' } - ]); - - expect(spy.callCount).to.equal(2); - expect(spy.args[1][0]).to.deep.equal({ - startTime: 10, - endTime: 20, - editable: true, - color: 'rgba(255, 161, 39, 1)', - labelText: 'dummy text' - }); + expect(p.options.deprecationLogger).to.be.calledOnce; + }); + + it('should accept an array of Segment objects', function() { + var spy = sandbox.spy(p.waveform.segments, 'createSegment'); + + p.segments.add([ + { startTime: 0, endTime: 10, editable: false }, + { startTime: 10, endTime: 20, editable: true, color: 'rgba(255, 161, 39, 1)', labelText: 'dummy text' } + ]); + + expect(spy.callCount).to.equal(2); + expect(spy.args[1][0]).to.deep.equal({ + startTime: 10, + endTime: 20, + editable: true, + color: 'rgba(255, 161, 39, 1)', + labelText: 'dummy text' }); + }); - it('should paint once, and not after each segment addition', function() { - var spy = sandbox.spy(p.waveform.segments.views[0].segmentLayer, 'draw'); + it('should paint once, and not after each segment addition', function() { + var spy = sandbox.spy(p.waveform.segments.views[0].segmentLayer, 'draw'); - p.segments.add([ - { startTime: 0, endTime: 10, editable: false }, - { startTime: 10, endTime: 20, editable: true, color: 'rgba(255, 161, 39, 1)', labelText: 'dummy text' } - ]); + p.segments.add([ + { startTime: 0, endTime: 10, editable: false }, + { startTime: 10, endTime: 20, editable: true, color: 'rgba(255, 161, 39, 1)', labelText: 'dummy text' } + ]); - expect(spy.callCount).to.equal(1); // currently called as many times as we have segments - }); + expect(spy.callCount).to.equal(1); // currently called as many times as we have segments }); }); -})(peaks); +}); From b46a59f25aeaeab3a74c4d1144e8fc9d5b88c44b Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Tue, 20 Dec 2016 10:42:02 +0000 Subject: [PATCH 2/7] Rework scripts organisation for build, demo and testing. --- package.json | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c48456ea1..36c36da5f 100644 --- a/package.json +++ b/package.json @@ -42,15 +42,17 @@ }, "license": "LGPL-3.0", "scripts": { - "prebuild": "npm run lint", - "build": "npm run build:main && npm run build:standalone", + "build": "npm-run-all --parallel build:** --serial doc", "build:main": "browserify -e ./src/main.js -t deamdify | derequire - > index.js", "build:standalone": "browserify -d -e ./src/main.js -t deamdify -s peaks | exorcist peaks.js.map | derequire - > peaks.js", "doc": "jsdoc --private --destination docs --recurse src", "lint": "eslint src/**/*.js test/**/*.js", - "pretest": "npm run build", "test": "./node_modules/karma/bin/karma start", - "test-watch": "npm test -- --auto-watch --no-single-run", + "posttest": "npm run lint", + "watch": "npm-run-all --parallel watch:**", + "watch:test": "npm test -- --auto-watch --no-single-run", + "watch:build": "watchify -vf -e ./src/main.js -t deamdify -s peaks -o peaks.js", + "watch:serve": "serve --cors -p 9000", "changelog": "github-changes -o bbc -r peaks.js -n ${npm_package_version} --only-pulls --use-commit-body --file CHANGELOG.${npm_package_version}.md", "prestart": "npm run build", "prepublish": "npm run build", @@ -63,7 +65,6 @@ "derequire": "^2.0.3", "eslint": "^3.2.2", "exorcist": "^0.4.0", - "github-changes": "^1.0.0", "jsdoc": "~3.4.1", "karma": "^1.1.0", "karma-browserify": "^5.1.0", @@ -77,6 +78,7 @@ "karma-sinon-chai": "^1.0.0", "karma-spec-reporter": "~0.0.26", "mocha": "^2.5.0", + "npm-run-all": "^3.1.2", "serve": "^1.4.0", "sinon": "^1.17.2", "sinon-chai": "^2.8.0", From 8f1edff17081e38a7d5672a431d2f98e0cc00b6f Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Tue, 20 Dec 2016 13:52:06 +0000 Subject: [PATCH 3/7] Merge ZoomView and Overview into a single view object They can be configured independently but lack of a few singular features for now (ref rectangle, event subscription, zoom lock) --- package.json | 3 +- src/main/views/waveform.overview.js | 249 ------------------ src/main/views/zooms/animated.js | 24 +- src/main/waveform/waveform.axis.js | 4 +- src/main/waveform/waveform.core.js | 65 +++-- .../waveform.view.js} | 174 ++++++------ 6 files changed, 154 insertions(+), 365 deletions(-) delete mode 100644 src/main/views/waveform.overview.js rename src/main/{views/waveform.zoomview.js => waveform/waveform.view.js} (67%) diff --git a/package.json b/package.json index 36c36da5f..8912ada04 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,7 @@ "peaks/waveform/waveform.core": "./src/main/waveform/waveform.core.js", "peaks/waveform/waveform.mixins": "./src/main/waveform/waveform.mixins.js", "peaks/waveform/waveform.utils": "./src/main/waveform/waveform.utils.js", - "peaks/views/waveform.overview": "./src/main/views/waveform.overview.js", - "peaks/views/waveform.zoomview": "./src/main/views/waveform.zoomview.js", + "peaks/waveform/waveform.view": "./src/main/waveform/waveform.view.js", "peaks/views/helpers/mousedraghandler": "./src/main/views/helpers/mousedraghandler.js", "peaks/views/zooms/animated": "./src/main/views/zooms/animated.js", "peaks/views/zooms/static": "./src/main/views/zooms/static.js", diff --git a/src/main/views/waveform.overview.js b/src/main/views/waveform.overview.js deleted file mode 100644 index 9b5b91009..000000000 --- a/src/main/views/waveform.overview.js +++ /dev/null @@ -1,249 +0,0 @@ -/** - * @file - * - * Defines the {@link WaveformOverview} class. - * - * @module peaks/views/waveform.overview - */ -define([ - 'peaks/waveform/waveform.axis', - 'peaks/waveform/waveform.mixins', - 'peaks/views/helpers/mousedraghandler', - 'konva' -], function(WaveformAxis, mixins, MouseDragHandler, Konva) { - 'use strict'; - - /** - * Creates the overview waveform view. - * - * @class - * @alias WaveformOverview - * - * @param {WaveformData} waveformData - * @param {HTMLElement} container - * @param {Peaks} peaks - */ - function WaveformOverview(waveformData, container, peaks) { - var self = this; - - self.originalWaveformData = waveformData; - self.container = container; - self.peaks = peaks; - - self.options = peaks.options; - self.width = container.clientWidth; - self.height = container.clientHeight || self.options.height; - self.frameOffset = 0; - - self.data = waveformData.resample(self.width); - - self.stage = new Konva.Stage({ - container: container, - width: self.width, - height: self.height - }); - - self.backgroundLayer = new Konva.Layer(); - self.waveformLayer = new Konva.FastLayer(); - - self.background = new Konva.Rect({ - x: 0, - y: 0, - width: self.width, - height: self.height - }); - - self.backgroundLayer.add(self.background); - self.stage.add(self.backgroundLayer); - - self.createWaveform(); - self.createRefWaveform(); - self.createUi(); - - self.mouseDragHandler = new MouseDragHandler(self.stage, { - onMouseDown: function(layerX) { - self.peaks.emit('user_seek.overview', self.data.time(layerX)); - }, - - onMouseMove: function(layerX) { - if (layerX < 0) { - layerX = 0; - } - else if (layerX > self.width) { - layerX = self.width; - } - - self.peaks.emit('user_seek.overview', self.data.time(layerX)); - } - }); - - // EVENTS ==================================================== - - function trackPlayheadPosition(time, frame) { - self.playheadPixel = self.data.at_time(time); - self.updateUi(self.playheadPixel); - } - - peaks.on('player_time_update', trackPlayheadPosition); - // peaks.on('user_seek.zoomview', trackPlayheadPosition); - // peaks.on('user_seek.overview', trackPlayheadPosition); - - peaks.on('zoomview.displaying', function(start, end) { - self.updateRefWaveform(start, end); - }); - - peaks.on('window_resize', function() { - self.container.hidden = true; - }); - - peaks.on('window_resize_complete', function(width) { - self.width = width; - self.data = self.originalWaveformData.resample(self.width); - self.stage.setWidth(self.width); - // self.updateWaveform(); - self.container.removeAttribute('hidden'); - }); - } - - WaveformOverview.prototype.createWaveform = function() { - this.waveformShape = new Konva.Shape({ - fill: this.options.overviewWaveformColor, - strokeWidth: 0 - }); - - this.waveformShape.sceneFunc(mixins.waveformDrawFunction.bind(this.waveformShape, this)); - - this.waveformLayer.add(this.waveformShape); - this.stage.add(this.waveformLayer); - }; - - // Green Reference Waveform to inform users where they are in overview - // waveform based on current zoom level - - WaveformOverview.prototype.createRefWaveform = function() { - this.refLayer = new Konva.Layer(); - - /* - this.refWaveformShape = new Konva.Shape({ - drawFunc: function(canvas) { - mixins.waveformDrawFunction.call( - this, - this.data, - canvas, - mixins.interpolateHeight(this.height) - ); - }, - fill: this.options.zoomWaveformColor, - strokeWidth: 0 - }); - */ - - this.highlightRect = new Konva.Rect({ - x: 0, - y: 11, - width: 0, - stroke: this.options.overviewHighlightRectangleColor, - strokeWidth: 1, - height: this.height - (11 * 2), - fill: this.options.overviewHighlightRectangleColor, - opacity: 0.3, - cornerRadius: 2 - }); - - this.refLayer.add(this.highlightRect); - this.stage.add(this.refLayer); - }; - - WaveformOverview.prototype.createUi = function() { - this.playheadLine = new Konva.Line({ - points: [0.5, 0, 0.5, this.height], - stroke: this.options.playheadColor, - strokeWidth: 1, - x: 0 - }); - - this.uiLayer = new Konva.Layer({ index: 100 }); - this.axis = new WaveformAxis(this); - - this.uiLayer.add(this.playheadLine); - this.stage.add(this.uiLayer); - this.uiLayer.moveToTop(); - }; - - /* - WaveformOverview.prototype.updateWaveform = function() { - var self = this; - self.waveformShape.sceneFunc(function(canvas) { - mixins.waveformDrawFunction.call( - this, - self.data, - canvas, - mixins.interpolateHeight(self.height) - ); - }); - self.waveformLayer.draw(); - }; - - WaveformOverview.prototype.updateRefWaveform = function (time_in, time_out) { - var self = this; - - var offset_in = self.data.at_time(time_in); - var offset_out = self.data.at_time(time_out); - - self.refWaveformShape.sceneFunc(function(canvas) { - self.data.set_segment(offset_in, offset_out, "zoom"); - - mixins.waveformOffsetDrawFunction.call( - this, - self.data, - canvas, - mixins.interpolateHeight(self.height)); - }); - - self.refWaveformShape.setWidth(self.data.at_time(time_out) - self.data.at_time(time_in)); - self.refLayer.draw(); - }; - */ - - WaveformOverview.prototype.updateRefWaveform = function(timeIn, timeOut) { - if (isNaN(timeIn)) { - // eslint-disable-next-line max-len - throw new Error('WaveformOverview#updateRefWaveform timeIn parameter is not a number: ' + timeIn); - } - - if (isNaN(timeOut)) { - // eslint-disable-next-line max-len - throw new Error('WaveformOverview#updateRefWaveform timeOut parameter is not a number: ' + timeOut); - } - - var offsetIn = this.data.at_time(timeIn); - var offsetOut = this.data.at_time(timeOut); - - this.data.set_segment(offsetIn, offsetOut, 'zoom'); - - this.highlightRect.setAttrs({ - x: this.data.segments.zoom.offset_start - this.data.offset_start, - width: this.data.at_time(timeOut) - this.data.at_time(timeIn) - }); - - this.refLayer.draw(); - }; - - // WaveformZoomView equivalent: updateZoomWaveform - - WaveformOverview.prototype.updateUi = function(pixel) { - if (isNaN(pixel)) { - throw new Error('WaveformOverview#updateUi passed a value that is not a number: ' + pixel); - } - - this.playheadLine.setAttr('x', pixel); - this.uiLayer.draw(); - }; - - WaveformOverview.prototype.destroy = function() { - this.stage.destroy(); - this.stage = null; - }; - - return WaveformOverview; -}); diff --git a/src/main/views/zooms/animated.js b/src/main/views/zooms/animated.js index 749b405c6..341220b8a 100644 --- a/src/main/views/zooms/animated.js +++ b/src/main/views/zooms/animated.js @@ -17,7 +17,7 @@ define(['konva'], function(Konva) { var outputIndex; var lastFrameOffsetTime; - var rootData = view.originalWaveformData; + var waveformData = view.waveformData; // Fade out the time axis and the segments // view.axis.axisShape.setAttr('opacity', 0); @@ -41,27 +41,27 @@ define(['konva'], function(Konva) { // Determine the timeframe for the zoom animation (start and end of // dataset for zooming animation) - var newWidthSeconds = view.width * frameScale / rootData.adapter.sample_rate; + var newWidthSeconds = view.width * frameScale / waveformData.adapter.sample_rate; if ((currentTime >= 0) && (currentTime <= 0 + newWidthSeconds / 2)) { inputIndex = 0; outputIndex = 0; } - else if ((currentTime <= rootData.duration) && - (currentTime >= rootData.duration - newWidthSeconds / 2)) { - lastFrameOffsetTime = rootData.duration - newWidthSeconds; + else if ((currentTime <= waveformData.duration) && + (currentTime >= waveformData.duration - newWidthSeconds / 2)) { + lastFrameOffsetTime = waveformData.duration - newWidthSeconds; // sample rate = 44100 - inputIndex = (lastFrameOffsetTime * rootData.adapter.sample_rate) / previousScale; - outputIndex = (lastFrameOffsetTime * rootData.adapter.sample_rate) / frameScale; + inputIndex = (lastFrameOffsetTime * waveformData.adapter.sample_rate) / previousScale; + outputIndex = (lastFrameOffsetTime * waveformData.adapter.sample_rate) / frameScale; } else { // This way calculates the index of the start time at the scale we // are coming from and the scale we are going to // sample rate = 44100 - var oldPixelIndex = (currentTime * rootData.adapter.sample_rate) / previousScale; - var newPixelIndex = (currentTime * rootData.adapter.sample_rate) / frameScale; + var oldPixelIndex = (currentTime * waveformData.adapter.sample_rate) / previousScale; + var newPixelIndex = (currentTime * waveformData.adapter.sample_rate) / frameScale; inputIndex = oldPixelIndex - (view.width / 2); outputIndex = newPixelIndex - (view.width / 2); @@ -71,8 +71,8 @@ define(['konva'], function(Konva) { inputIndex = 0; } - // rootData should be swapped for your resampled dataset: - var resampled = rootData.resample({ + // waveformData should be swapped for your resampled dataset: + var resampled = waveformData.resample({ scale: frameScale, input_index: Math.floor(inputIndex), output_index: Math.floor(outputIndex), @@ -98,7 +98,7 @@ define(['konva'], function(Konva) { if (frameData.length) { // Send correct resampled waveform data object to drawFunc and draw it view.intermediateData = frameData.shift(); - view.zoomWaveformLayer.draw(); + view.waveformLayer.draw(); } else { this.stop(); diff --git a/src/main/waveform/waveform.axis.js b/src/main/waveform/waveform.axis.js index 3e9243a50..3e9b2877a 100644 --- a/src/main/waveform/waveform.axis.js +++ b/src/main/waveform/waveform.axis.js @@ -106,12 +106,12 @@ define(['peaks/waveform/waveform.utils', 'konva'], function(Utils, Konva) { // Distance between waveform start time and first axis marker (pixels) var axisLabelOffsetPixels = this.view.data.at_time(axisLabelOffsetSecs); - context.setAttr('strokeStyle', this.view.options.axisGridlineColor); + context.setAttr('strokeStyle', this.view.peaks.options.axisGridlineColor); context.setAttr('lineWidth', 1); // Set text style context.setAttr('font', '11px sans-serif'); - context.setAttr('fillStyle', this.view.options.axisLabelColor); + context.setAttr('fillStyle', this.view.peaks.options.axisLabelColor); context.setAttr('textAlign', 'left'); context.setAttr('textBaseline', 'bottom'); diff --git a/src/main/waveform/waveform.core.js b/src/main/waveform/waveform.core.js index c52ed66c2..114420774 100644 --- a/src/main/waveform/waveform.core.js +++ b/src/main/waveform/waveform.core.js @@ -8,20 +8,39 @@ define([ 'waveform-data', 'waveform-data/webaudio', - 'peaks/views/waveform.overview', - 'peaks/views/waveform.zoomview', + 'peaks/waveform/waveform.view', 'peaks/markers/waveform.segments', - 'peaks/markers/waveform.points' + 'peaks/markers/waveform.points', + 'peaks/views/zooms/animated', + 'peaks/views/zooms/static' ], function( WaveformData, webaudioBuilder, - WaveformOverview, - WaveformZoomView, + WaveformView, WaveformSegments, - WaveformPoints) { + WaveformPoints, + AnimatedZoomAdapter, + StaticZoomAdapter) { 'use strict'; - var isXhr2 = ('withCredentials' in new XMLHttpRequest()); + var IS_XHR2 = ('withCredentials' in new XMLHttpRequest()); + + var ZOOM_ADAPTER_MAP = { + 'animated': AnimatedZoomAdapter, + 'static': StaticZoomAdapter + }; + + function getZoomAdapter(adapter) { + if (typeof adapter === 'function') { + return adapter; + } + + if ((adapter in ZOOM_ADAPTER_MAP) === false) { + throw new Error('Unknown zoom adapter (should be "animated" or "static")'); + } + + return ZOOM_ADAPTER_MAP[adapter]; + } /** * Bootstraps all our waveform components and manages initialisation as well @@ -115,7 +134,7 @@ // open an XHR request to the data source file xhr.open('GET', uri, true); - if (isXhr2) { + if (IS_XHR2) { try { xhr.responseType = requestType; } @@ -182,11 +201,15 @@ remoteData : WaveformData.create(remoteData); - this.waveformOverview = new WaveformOverview( - this.originalWaveformData, - this.ui.overview, - this.peaks - ); + this.waveformOverview = new WaveformView('overview', { + waveformData: this.originalWaveformData, + container: this.ui.overview, + peaks: this.peaks, + scale: { + width: Number(this.ui.overview.clientWidth) + }, + zoomAdapter: StaticZoomAdapter + }); this.peaks.emit('waveform_ready.overview', this.waveformOverview); } @@ -201,7 +224,7 @@ Waveform.prototype._bindEvents = function() { var self = this; - self.peaks.on('user_seek.*', function(time) { + self.peaks.on('user_seek', function(time) { self.peaks.player.seekBySeconds(time); }); @@ -209,11 +232,15 @@ }; Waveform.prototype.openZoomView = function() { - this.waveformZoomView = new WaveformZoomView( - this.originalWaveformData, - this.ui.zoom, - this.peaks - ); + this.waveformZoomView = new WaveformView('zoomview', { + waveformData: this.originalWaveformData, + container: this.ui.zoom, + peaks: this.peaks, + scale: { + scale: this.peaks.options.zoomLevels[this.peaks.zoom.getZoom()] + }, + zoomAdapter: getZoomAdapter(this.peaks.options.zoomAdapter) + }); this.segments = new WaveformSegments(this.peaks); this.segments.init(); diff --git a/src/main/views/waveform.zoomview.js b/src/main/waveform/waveform.view.js similarity index 67% rename from src/main/views/waveform.zoomview.js rename to src/main/waveform/waveform.view.js index fb810e3af..db10e8744 100644 --- a/src/main/views/waveform.zoomview.js +++ b/src/main/waveform/waveform.view.js @@ -1,70 +1,83 @@ /** * @file * - * Defines the {@link WaveformZoomView} class. + * Defines the {@link WaveformView} class. * - * @module peaks/views/waveform.zoomview + * @module peaks/waveform/waveform.view */ define([ 'peaks/waveform/waveform.axis', 'peaks/waveform/waveform.mixins', 'peaks/waveform/waveform.utils', 'peaks/views/helpers/mousedraghandler', - 'peaks/views/zooms/animated', - 'peaks/views/zooms/static', 'konva' ], function( WaveformAxis, mixins, Utils, MouseDragHandler, - AnimatedZoomAdapter, - StaticZoomAdapter, Konva) { 'use strict'; /** - * Creates the zoomed-in waveform view. + * Creates a waveform view. * * @class - * @alias WaveformZoomView + * @alias WaveformView * * @param {WaveformData} waveformData * @param {HTMLElement} container * @param {Peaks} peaks + * @param {Object} options */ - function WaveformZoomView(waveformData, container, peaks) { + function WaveformView(name, options) { var self = this; - self.originalWaveformData = waveformData; - self.container = container; - self.peaks = peaks; + if (!options.waveformData) { + throw new Error('WaveformView requires `options.waveformData` to be a waveform-data object'); + } + + if (!options.container) { + throw new Error('WaveformView requires `options.container` to be a DOM element'); + } + + if (!options.peaks) { + throw new Error('WaveformView requires `options.peaks` to be a peaks instance'); + } - self.options = peaks.options; + if (!options.zoomAdapter) { + throw new Error('WaveformView requires `options.zoomAdapter` to be a views/zooms instance'); + } + + var peaksOptions = options.peaks.options; + + self.name = name; + self.waveformData = options.waveformData; + self.container = options.container; + self.peaks = options.peaks; + self.zoomAdapter = options.zoomAdapter; self.playing = false; self.intermediateData = null; - self.data = self.originalWaveformData.resample({ - scale: self.options.zoomLevels[peaks.zoom.getZoom()] - }); - self.playheadPixel = self.data.at_time(self.options.mediaElement.currentTime); + self.data = self.waveformData.resample(options.scale); + self.playheadPixel = self.data.at_time(peaksOptions.mediaElement.currentTime); self.pixelLength = self.data.adapter.length; self.frameOffset = 0; // the pixel offset of the current frame being displayed - self.width = container.clientWidth; - self.height = container.clientHeight || self.options.height; + self.width = self.container.clientWidth; + self.height = self.container.clientHeight || peaksOptions.height; self.data.offset(self.frameOffset, self.frameOffset + self.width); self.stage = new Konva.Stage({ - container: container, + container: self.container, width: self.width, height: self.height }); self.backgroundLayer = new Konva.Layer(); - self.zoomWaveformLayer = new Konva.FastLayer(); + self.waveformLayer = new Konva.FastLayer(); self.uiLayer = new Konva.Layer(); self.background = new Konva.Rect({ @@ -79,9 +92,16 @@ define([ self.axis = new WaveformAxis(self); - self.createZoomWaveform(); + self.createWaveform(); self.createUi(); + self.emit = function emitNamespacedEvent() { + var name = [arguments[0], self.name].join('.'); + var args = Array.prototype.slice.call(arguments, 1); + + self.peaks.emit.apply(self.peaks, [name].concat(args)); + }; + self.mouseDragHandler = new MouseDragHandler(self.stage, { onMouseDown: function(layerX) { this.initialFrameOffset = self.frameOffset; @@ -103,7 +123,7 @@ define([ } if (newFrameOffset !== this.initialFrameOffset) { - self.peaks.emit('user_scroll.zoomview', newFrameOffset); + self.emit('user_scroll', newFrameOffset); } }, @@ -112,7 +132,7 @@ define([ if (!self.mouseDragHandler.isDragging()) { var pos = self.frameOffset + this.mouseDownLayerX; - self.peaks.emit('user_seek.zoomview', self.data.time(pos), pos); + self.peaks.emit('user_seek', self.data.time(pos), pos); } } }); @@ -137,9 +157,10 @@ define([ self.peaks.on('player_seek', userSeekHandler); - self.peaks.on('user_scroll.zoomview', function(pixelOffset) { - self.updateZoomWaveform(pixelOffset); - }); + // TODO make sure we update the view on a more generic event + // self.peaks.on('user_scroll.zoomview', function(pixelOffset) { + // self.updateWaveform(pixelOffset); + // }); self.peaks.on('player_play', function(time) { self.playing = true; @@ -167,43 +188,32 @@ define([ return; } - self.data = self.originalWaveformData.resample({ + self.data = self.waveformData.resample({ scale: currentScale }); - var zoomAdapterMap = { - 'animated': AnimatedZoomAdapter, - 'static': StaticZoomAdapter - }; - - var zoomAdapter = zoomAdapterMap[self.peaks.options.zoomAdapter]; - - if (!zoomAdapter) { - throw new Error('Invalid zoomAdapter: ' + self.peaks.options.zoomAdapter); - } - - var adapter = zoomAdapter.create(currentScale, previousScale, self); - - adapter.start(); + self.zoomAdapter + .create(currentScale, previousScale, self) + .start(); }); - peaks.on('window_resize', function() { + self.peaks.on('window_resize', function() { self.container.hidden = true; }); self.peaks.on('window_resize_complete', function(width) { self.width = width; self.stage.setWidth(self.width); - self.updateZoomWaveform(self.frameOffset); + self.updateWaveform(self.frameOffset); self.container.removeAttribute('hidden'); }); // KEYBOARD EVENTS ========================================= function nudgeFrame(step) { - var time = self.options.mediaElement.currentTime; + var time = self.peaks.options.mediaElement.currentTime; - time += self.options.nudgeIncrement * step; + time += self.peaks.options.nudgeIncrement * step; self.seekFrame(self.data.at_time(time)); } @@ -215,30 +225,31 @@ define([ // WAVEFORM ZOOMVIEW FUNCTIONS ========================================= - WaveformZoomView.prototype.createZoomWaveform = function() { - this.zoomWaveformShape = new Konva.Shape({ - fill: this.options.zoomWaveformColor, + WaveformView.prototype.createWaveform = function() { + this.waveformShape = new Konva.Shape({ + fill: this.peaks.options.zoomWaveformColor, strokeWidth: 0 }); - this.zoomWaveformShape.sceneFunc( - mixins.waveformDrawFunction.bind(this.zoomWaveformShape, this) + this.waveformShape.sceneFunc( + mixins.waveformDrawFunction.bind(this.waveformShape, this) ); - this.zoomWaveformLayer.add(this.zoomWaveformShape); - this.stage.add(this.zoomWaveformLayer); + this.waveformLayer.add(this.waveformShape); + this.stage.add(this.waveformLayer); - this.peaks.emit( - 'zoomview.displaying', - 0 * this.data.seconds_per_pixel, - this.width * this.data.seconds_per_pixel - ); + // TODO sort out how one view subscribe to another one + // this.peaks.emit( + // 'zoomview.displaying', + // 0 * this.data.seconds_per_pixel, + // this.width * this.data.seconds_per_pixel + // ); }; - WaveformZoomView.prototype.createUi = function() { + WaveformView.prototype.createUi = function() { this.playheadLine = new Konva.Line({ points: [0.5, 0, 0.5, this.height], - stroke: this.options.playheadColor, + stroke: this.peaks.options.playheadColor, strokeWidth: 1 }); @@ -248,7 +259,7 @@ define([ text: '00:00:00', fontSize: 11, fontFamily: 'sans-serif', - fill: this.options.playheadTextColor, + fill: this.peaks.options.playheadTextColor, align: 'right' }); @@ -266,10 +277,10 @@ define([ this.playheadGroup.moveToTop(); }; - WaveformZoomView.prototype.updateZoomWaveform = function(pixelOffset) { + WaveformView.prototype.updateWaveform = function(pixelOffset) { if (isNaN(pixelOffset)) { // eslint-disable-next-line max-len - throw new Error('WaveformZoomView#updateZoomWaveform passed a pixel offset that is not a number: ' + pixelOffset); + throw new Error('WaveformView#updateWaveform passed a pixel offset that is not a number: ' + pixelOffset); } this.pixelLength = this.data.adapter.length; @@ -308,17 +319,18 @@ define([ } this.uiLayer.draw(); - this.zoomWaveformLayer.draw(); + this.waveformLayer.draw(); // if (this.snipWaveformShape) { // this.updateSnipWaveform(this.currentSnipStartTime, this.currentSnipEndTime); // } - this.peaks.emit( - 'zoomview.displaying', - pixelOffset * this.data.seconds_per_pixel, - (pixelOffset + this.width) * this.data.seconds_per_pixel - ); + // TODO sort out + // this.peaks.emit( + // 'zoomview.displaying', + // pixelOffset * this.data.seconds_per_pixel, + // (pixelOffset + this.width) * this.data.seconds_per_pixel + // ); }; // UI functions ============================== @@ -329,7 +341,7 @@ define([ * @param {Number} time Position in time where the playhead starts * @param {Integer} start Position Position in frame index where the playhead starts */ - WaveformZoomView.prototype.playFrom = function(time, startPosition) { + WaveformView.prototype.playFrom = function(time, startPosition) { var self = this; if (self.playheadLineAnimation) { @@ -355,17 +367,17 @@ define([ self.playheadLineAnimation.start(); }; - WaveformZoomView.prototype.newFrame = function(frameOffset) { + WaveformView.prototype.newFrame = function(frameOffset) { if (isNaN(frameOffset)) { // eslint-disable-next-line max-len - throw new Error('WaveformZoomView#newFrame passed a frame offset that is not a number: ' + frameOffset); + throw new Error('WaveformView#newFrame passed a frame offset that is not a number: ' + frameOffset); } var nextOffset = frameOffset + this.width; if (nextOffset < this.data.adapter.length) { this.frameOffset = nextOffset; - this.updateZoomWaveform(nextOffset); + this.updateWaveform(nextOffset); return true; } @@ -373,10 +385,10 @@ define([ return false; }; - WaveformZoomView.prototype.syncPlayhead = function(pixelIndex) { + WaveformView.prototype.syncPlayhead = function(pixelIndex) { if (isNaN(pixelIndex)) { // eslint-disable-next-line max-len - throw new Error('WaveformZoomView#syncPlayhead passed a pixel index that is not a number: ' + pixelIndex); + throw new Error('WaveformView#syncPlayhead passed a pixel index that is not a number: ' + pixelIndex); } var display = (pixelIndex >= this.frameOffset) && @@ -385,7 +397,7 @@ define([ this.playheadPixel = pixelIndex; if (display) { - // Place playhead at centre of zoom frame i.e. remPixels = 500 + // Place playhead at centre of frame i.e. remPixels = 500 var remPixels = this.playheadPixel - this.frameOffset; this.playheadGroup.show().setAttr('x', remPixels); @@ -398,10 +410,10 @@ define([ this.uiLayer.draw(); }; - WaveformZoomView.prototype.seekFrame = function(pixelIndex, offset) { + WaveformView.prototype.seekFrame = function(pixelIndex, offset) { if (isNaN(pixelIndex)) { // eslint-disable-next-line max-len - throw new Error('WaveformZoomView#seekFrame passed a pixel index that is not a number: ' + pixelIndex); + throw new Error('WaveformView#seekFrame passed a pixel index that is not a number: ' + pixelIndex); } var upperLimit = this.data.adapter.length - this.width; @@ -418,13 +430,13 @@ define([ } this.syncPlayhead(pixelIndex); - this.updateZoomWaveform(this.frameOffset); + this.updateWaveform(this.frameOffset); }; - WaveformZoomView.prototype.destroy = function() { + WaveformView.prototype.destroy = function() { this.stage.destroy(); this.stage = null; }; - return WaveformZoomView; + return WaveformView; }); From c203de202e3c8ee2215b4e2a43cdd5f929c4d474 Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Tue, 20 Dec 2016 16:10:36 +0000 Subject: [PATCH 4/7] Add FixedZoom and ref-segment feature --- package.json | 2 + src/main/views/ref-segment.js | 59 ++++++++++++++++++++++++++++++ src/main/views/zooms/animated.js | 8 +++- src/main/views/zooms/fixed.js | 20 ++++++++++ src/main/views/zooms/static.js | 4 ++ src/main/waveform/waveform.core.js | 14 +++++-- src/main/waveform/waveform.view.js | 40 ++++++++------------ 7 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 src/main/views/ref-segment.js create mode 100644 src/main/views/zooms/fixed.js diff --git a/package.json b/package.json index 8912ada04..2f18c167a 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,11 @@ "peaks/waveform/waveform.mixins": "./src/main/waveform/waveform.mixins.js", "peaks/waveform/waveform.utils": "./src/main/waveform/waveform.utils.js", "peaks/waveform/waveform.view": "./src/main/waveform/waveform.view.js", + "peaks/views/ref-segment": "./src/main/views/ref-segment.js", "peaks/views/helpers/mousedraghandler": "./src/main/views/helpers/mousedraghandler.js", "peaks/views/zooms/animated": "./src/main/views/zooms/animated.js", "peaks/views/zooms/static": "./src/main/views/zooms/static.js", + "peaks/views/zooms/fixed": "./src/main/views/zooms/fixed", "peaks/markers/waveform.points": "./src/main/markers/waveform.points.js", "peaks/markers/waveform.segments": "./src/main/markers/waveform.segments.js", "peaks/markers/shapes/base": "./src/main/markers/shapes/base.js", diff --git a/src/main/views/ref-segment.js b/src/main/views/ref-segment.js new file mode 100644 index 000000000..9839b60d6 --- /dev/null +++ b/src/main/views/ref-segment.js @@ -0,0 +1,59 @@ +/** + * @file + * + * Defines a zoom view adapter with no animations. + * + * @module peaks/views/ref-segment + */ +define(['konva'], function(Konva) { + 'use strict'; + + return { + install: function(view, peaks) { + var refLayer = new Konva.Layer(); + var waveformData = view.data; + var highlightRect = new Konva.Rect({ + x: 0, + y: 11, + width: 0, + stroke: peaks.options.overviewHighlightRectangleColor, + strokeWidth: 1, + height: view.height - (11 * 2), + fill: peaks.options.overviewHighlightRectangleColor, + opacity: 0.3, + cornerRadius: 2 + }); + + refLayer.add(highlightRect); + view.stage.add(refLayer); + + return { + subscribeTo: function refSegmentSubscribeTo(renderEventName) { + peaks.on(renderEventName, function(timeIn, timeOut) { + if (isNaN(timeIn)) { + // eslint-disable-next-line max-len + throw new Error('WaveformOverview#updateRefWaveform timeIn parameter is not a number: ' + timeIn); + } + + if (isNaN(timeOut)) { + // eslint-disable-next-line max-len + throw new Error('WaveformOverview#updateRefWaveform timeOut parameter is not a number: ' + timeOut); + } + + var offsetIn = waveformData.at_time(timeIn); + var offsetOut = waveformData.at_time(timeOut); + + waveformData.set_segment(offsetIn, offsetOut, 'zoom'); + + highlightRect.setAttrs({ + x: waveformData.segments.zoom.offset_start - waveformData.offset_start, + width: waveformData.at_time(timeOut) - waveformData.at_time(timeIn) + }); + + refLayer.draw(); + }); + } + }; + } + }; +}); diff --git a/src/main/views/zooms/animated.js b/src/main/views/zooms/animated.js index 341220b8a..4cc91e08c 100644 --- a/src/main/views/zooms/animated.js +++ b/src/main/views/zooms/animated.js @@ -84,10 +84,10 @@ define(['konva'], function(Konva) { previousScale = frameScale; } - return new Konva.Animation(this.onFrameData(view, frameData), view); + return new Konva.Animation(this.onFrameData(view, frameData, currentScale), view); }, - onFrameData: function(view, frameData) { + onFrameData: function(view, frameData, currentScale) { view.intermediateData = null; /** @@ -101,6 +101,10 @@ define(['konva'], function(Konva) { view.waveformLayer.draw(); } else { + view.data = view.waveformData.resample({ + scale: currentScale + }); + this.stop(); view.intermediateData = null; view.segmentLayer.setVisible(true); diff --git a/src/main/views/zooms/fixed.js b/src/main/views/zooms/fixed.js new file mode 100644 index 000000000..73b5d749e --- /dev/null +++ b/src/main/views/zooms/fixed.js @@ -0,0 +1,20 @@ +/** + * @file + * + * Defines a zoom view adapter with no animations. + * + * @module peaks/views/zooms/static + */ +define([], function() { + 'use strict'; + + return { + create: function(currentScale, previousScale, view) { + return { + start: function() { + return true; + } + }; + } + }; +}); diff --git a/src/main/views/zooms/static.js b/src/main/views/zooms/static.js index 5655373bd..57f98b181 100644 --- a/src/main/views/zooms/static.js +++ b/src/main/views/zooms/static.js @@ -12,6 +12,10 @@ define([], function() { create: function(currentScale, previousScale, view) { return { start: function() { + view.data = view.waveformData.resample({ + scale: currentScale + }); + view.segmentLayer.draw(); view.pointLayer.draw(); diff --git a/src/main/waveform/waveform.core.js b/src/main/waveform/waveform.core.js index 114420774..8a6fa4bc7 100644 --- a/src/main/waveform/waveform.core.js +++ b/src/main/waveform/waveform.core.js @@ -12,7 +12,9 @@ 'peaks/markers/waveform.segments', 'peaks/markers/waveform.points', 'peaks/views/zooms/animated', - 'peaks/views/zooms/static' + 'peaks/views/zooms/static', + 'peaks/views/zooms/fixed', + 'peaks/views/ref-segment' ], function( WaveformData, webaudioBuilder, @@ -20,7 +22,9 @@ WaveformSegments, WaveformPoints, AnimatedZoomAdapter, - StaticZoomAdapter) { + StaticZoomAdapter, + FixedZoomAdapter, + ReferenceSegment) { 'use strict'; var IS_XHR2 = ('withCredentials' in new XMLHttpRequest()); @@ -208,9 +212,13 @@ scale: { width: Number(this.ui.overview.clientWidth) }, - zoomAdapter: StaticZoomAdapter + zoomAdapter: FixedZoomAdapter }); + ReferenceSegment + .install(this.waveformOverview, this.peaks) + .subscribeTo('waveform.render.zoomview'); + this.peaks.emit('waveform_ready.overview', this.waveformOverview); } catch (e) { diff --git a/src/main/waveform/waveform.view.js b/src/main/waveform/waveform.view.js index db10e8744..dbcd07856 100644 --- a/src/main/waveform/waveform.view.js +++ b/src/main/waveform/waveform.view.js @@ -56,6 +56,7 @@ define([ self.container = options.container; self.peaks = options.peaks; self.zoomAdapter = options.zoomAdapter; + self.features = options.features || []; self.playing = false; @@ -92,9 +93,6 @@ define([ self.axis = new WaveformAxis(self); - self.createWaveform(); - self.createUi(); - self.emit = function emitNamespacedEvent() { var name = [arguments[0], self.name].join('.'); var args = Array.prototype.slice.call(arguments, 1); @@ -102,6 +100,9 @@ define([ self.peaks.emit.apply(self.peaks, [name].concat(args)); }; + self.createWaveform(); + self.createUi(); + self.mouseDragHandler = new MouseDragHandler(self.stage, { onMouseDown: function(layerX) { this.initialFrameOffset = self.frameOffset; @@ -178,20 +179,15 @@ define([ }); self.peaks.on('zoom.update', function(currentScale, previousScale) { + // We currently don't allow changing the zoom level during playback. if (self.playing) { - // We currently don't allow changing the zoom level during playback. return; } if (currentScale === previousScale) { - // Nothing to do. return; } - self.data = self.waveformData.resample({ - scale: currentScale - }); - self.zoomAdapter .create(currentScale, previousScale, self) .start(); @@ -238,12 +234,11 @@ define([ this.waveformLayer.add(this.waveformShape); this.stage.add(this.waveformLayer); - // TODO sort out how one view subscribe to another one - // this.peaks.emit( - // 'zoomview.displaying', - // 0 * this.data.seconds_per_pixel, - // this.width * this.data.seconds_per_pixel - // ); + this.emit( + 'waveform.render', + 0 * this.data.seconds_per_pixel, + this.width * this.data.seconds_per_pixel + ); }; WaveformView.prototype.createUi = function() { @@ -321,16 +316,11 @@ define([ this.uiLayer.draw(); this.waveformLayer.draw(); - // if (this.snipWaveformShape) { - // this.updateSnipWaveform(this.currentSnipStartTime, this.currentSnipEndTime); - // } - - // TODO sort out - // this.peaks.emit( - // 'zoomview.displaying', - // pixelOffset * this.data.seconds_per_pixel, - // (pixelOffset + this.width) * this.data.seconds_per_pixel - // ); + this.emit( + 'waveform.render', + pixelOffset * this.data.seconds_per_pixel, + (pixelOffset + this.width) * this.data.seconds_per_pixel + ); }; // UI functions ============================== From 103ac418146a611e45dd75529f949c3d476839c1 Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Wed, 21 Dec 2016 12:00:10 +0000 Subject: [PATCH 5/7] Install PointerSeek behaviour for WaveformView.overview --- package.json | 1 + src/main/views/pointers/seek.js | 22 ++++++++++++ src/main/waveform/waveform.core.js | 7 ++-- src/main/waveform/waveform.view.js | 56 +++++++----------------------- 4 files changed, 40 insertions(+), 46 deletions(-) create mode 100644 src/main/views/pointers/seek.js diff --git a/package.json b/package.json index 2f18c167a..1f162ed47 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "peaks/waveform/waveform.mixins": "./src/main/waveform/waveform.mixins.js", "peaks/waveform/waveform.utils": "./src/main/waveform/waveform.utils.js", "peaks/waveform/waveform.view": "./src/main/waveform/waveform.view.js", + "peaks/views/pointers/seek": "./src/main/views/pointers/seek.js", "peaks/views/ref-segment": "./src/main/views/ref-segment.js", "peaks/views/helpers/mousedraghandler": "./src/main/views/helpers/mousedraghandler.js", "peaks/views/zooms/animated": "./src/main/views/zooms/animated.js", diff --git a/src/main/views/pointers/seek.js b/src/main/views/pointers/seek.js new file mode 100644 index 000000000..d758a6dbd --- /dev/null +++ b/src/main/views/pointers/seek.js @@ -0,0 +1,22 @@ +'use strict'; + +define(['peaks/views/helpers/mousedraghandler'], function(MouseDragHandler) { + return function(view) { + return new MouseDragHandler(view.stage, { + onMouseDown: function(layerX) { + view.emit('user_seek', view.data.time(layerX)); + }, + + onMouseMove: function(layerX) { + if (layerX < 0) { + layerX = 0; + } + else if (layerX > view.width) { + layerX = view.width; + } + + view.emit('user_seek', view.data.time(layerX)); + } + }); + }; +}); diff --git a/src/main/waveform/waveform.core.js b/src/main/waveform/waveform.core.js index 8a6fa4bc7..64994c0c7 100644 --- a/src/main/waveform/waveform.core.js +++ b/src/main/waveform/waveform.core.js @@ -14,6 +14,7 @@ 'peaks/views/zooms/animated', 'peaks/views/zooms/static', 'peaks/views/zooms/fixed', + 'peaks/views/pointers/seek', 'peaks/views/ref-segment' ], function( WaveformData, @@ -24,6 +25,7 @@ AnimatedZoomAdapter, StaticZoomAdapter, FixedZoomAdapter, + PointerHandlerSeek, ReferenceSegment) { 'use strict'; @@ -212,7 +214,8 @@ scale: { width: Number(this.ui.overview.clientWidth) }, - zoomAdapter: FixedZoomAdapter + zoomAdapter: FixedZoomAdapter, + mouseDragHandler: PointerHandlerSeek }); ReferenceSegment @@ -232,7 +235,7 @@ Waveform.prototype._bindEvents = function() { var self = this; - self.peaks.on('user_seek', function(time) { + self.peaks.on('user_seek.*', function(time) { self.peaks.player.seekBySeconds(time); }); diff --git a/src/main/waveform/waveform.view.js b/src/main/waveform/waveform.view.js index dbcd07856..5790f782d 100644 --- a/src/main/waveform/waveform.view.js +++ b/src/main/waveform/waveform.view.js @@ -9,13 +9,11 @@ define([ 'peaks/waveform/waveform.axis', 'peaks/waveform/waveform.mixins', 'peaks/waveform/waveform.utils', - 'peaks/views/helpers/mousedraghandler', 'konva' ], function( WaveformAxis, mixins, Utils, - MouseDragHandler, Konva) { 'use strict'; @@ -49,6 +47,10 @@ define([ throw new Error('WaveformView requires `options.zoomAdapter` to be a views/zooms instance'); } + if (!options.mouseDragHandler) { + throw new Error('WaveformView requires `options.mouseDragHandler` to be a views/pointers instance'); + } + var peaksOptions = options.peaks.options; self.name = name; @@ -93,51 +95,10 @@ define([ self.axis = new WaveformAxis(self); - self.emit = function emitNamespacedEvent() { - var name = [arguments[0], self.name].join('.'); - var args = Array.prototype.slice.call(arguments, 1); - - self.peaks.emit.apply(self.peaks, [name].concat(args)); - }; - + self.mouseDragHandler = options.mouseDragHandler(self); self.createWaveform(); self.createUi(); - self.mouseDragHandler = new MouseDragHandler(self.stage, { - onMouseDown: function(layerX) { - this.initialFrameOffset = self.frameOffset; - this.mouseDownLayerX = layerX; - }, - - onMouseMove: function(layerX) { - // Moving the mouse to the left increases the time position of the - // left-hand edge of the visible waveform. - var diff = this.mouseDownLayerX - layerX; - - var newFrameOffset = this.initialFrameOffset + diff; - - if (newFrameOffset < 0) { - newFrameOffset = 0; - } - else if (newFrameOffset > (self.pixelLength - self.width)) { - newFrameOffset = self.pixelLength - self.width; - } - - if (newFrameOffset !== this.initialFrameOffset) { - self.emit('user_scroll', newFrameOffset); - } - }, - - onMouseUp: function(layerX) { - // Set playhead position only on click release, when not dragging - if (!self.mouseDragHandler.isDragging()) { - var pos = self.frameOffset + this.mouseDownLayerX; - - self.peaks.emit('user_seek', self.data.time(pos), pos); - } - } - }); - // EVENTS ==================================================== function userSeekHandler(time) { @@ -219,6 +180,13 @@ define([ self.peaks.on('keyboard.shift_right', nudgeFrame.bind(self, 10)); } + WaveformView.prototype.emit = function() { + var name = [arguments[0], this.name].join('.'); + var args = Array.prototype.slice.call(arguments, 1); + + this.peaks.emit.apply(this.peaks, [name].concat(args)); + }; + // WAVEFORM ZOOMVIEW FUNCTIONS ========================================= WaveformView.prototype.createWaveform = function() { From 090381b670c1433aae4379e35147d43d80b09c60 Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Wed, 21 Dec 2016 12:22:27 +0000 Subject: [PATCH 6/7] Install PointerDrag behaviour for WaveformView.zoomview --- package.json | 1 + src/main/views/pointers/drag.js | 40 ++++++++++++++++++++++++++++++ src/main/waveform/waveform.core.js | 5 +++- src/main/waveform/waveform.view.js | 7 +++--- 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 src/main/views/pointers/drag.js diff --git a/package.json b/package.json index 1f162ed47..1be563437 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "peaks/waveform/waveform.mixins": "./src/main/waveform/waveform.mixins.js", "peaks/waveform/waveform.utils": "./src/main/waveform/waveform.utils.js", "peaks/waveform/waveform.view": "./src/main/waveform/waveform.view.js", + "peaks/views/pointers/drag": "./src/main/views/pointers/drag.js", "peaks/views/pointers/seek": "./src/main/views/pointers/seek.js", "peaks/views/ref-segment": "./src/main/views/ref-segment.js", "peaks/views/helpers/mousedraghandler": "./src/main/views/helpers/mousedraghandler.js", diff --git a/src/main/views/pointers/drag.js b/src/main/views/pointers/drag.js new file mode 100644 index 000000000..89b352e5e --- /dev/null +++ b/src/main/views/pointers/drag.js @@ -0,0 +1,40 @@ +'use strict'; + +define(['peaks/views/helpers/mousedraghandler'], function(MouseDragHandler) { + return function(view) { + return new MouseDragHandler(view.stage, { + onMouseDown: function(layerX) { + this.initialFrameOffset = view.frameOffset; + this.mouseDownLayerX = layerX; + }, + + onMouseMove: function(layerX) { + // Moving the mouse to the left increases the time position of the + // left-hand edge of the visible waveform. + var diff = this.mouseDownLayerX - layerX; + + var newFrameOffset = this.initialFrameOffset + diff; + + if (newFrameOffset < 0) { + newFrameOffset = 0; + } + else if (newFrameOffset > (view.pixelLength - view.width)) { + newFrameOffset = view.pixelLength - view.width; + } + + if (newFrameOffset !== this.initialFrameOffset) { + view.emit('user_scroll', newFrameOffset); + } + }, + + onMouseUp: function(layerX) { + // Set playhead position only on click release, when not dragging + if (!view.mouseDragHandler.isDragging()) { + var pos = view.frameOffset + this.mouseDownLayerX; + + view.emit('user_seek', view.data.time(pos), pos); + } + } + }); + }; +}); diff --git a/src/main/waveform/waveform.core.js b/src/main/waveform/waveform.core.js index 64994c0c7..eb9b7a888 100644 --- a/src/main/waveform/waveform.core.js +++ b/src/main/waveform/waveform.core.js @@ -14,6 +14,7 @@ 'peaks/views/zooms/animated', 'peaks/views/zooms/static', 'peaks/views/zooms/fixed', + 'peaks/views/pointers/drag', 'peaks/views/pointers/seek', 'peaks/views/ref-segment' ], function( @@ -25,6 +26,7 @@ AnimatedZoomAdapter, StaticZoomAdapter, FixedZoomAdapter, + PointerHandlerDrag, PointerHandlerSeek, ReferenceSegment) { 'use strict'; @@ -250,7 +252,8 @@ scale: { scale: this.peaks.options.zoomLevels[this.peaks.zoom.getZoom()] }, - zoomAdapter: getZoomAdapter(this.peaks.options.zoomAdapter) + zoomAdapter: getZoomAdapter(this.peaks.options.zoomAdapter), + mouseDragHandler: PointerHandlerDrag }); this.segments = new WaveformSegments(this.peaks); diff --git a/src/main/waveform/waveform.view.js b/src/main/waveform/waveform.view.js index 5790f782d..7c02151f4 100644 --- a/src/main/waveform/waveform.view.js +++ b/src/main/waveform/waveform.view.js @@ -119,10 +119,9 @@ define([ self.peaks.on('player_seek', userSeekHandler); - // TODO make sure we update the view on a more generic event - // self.peaks.on('user_scroll.zoomview', function(pixelOffset) { - // self.updateWaveform(pixelOffset); - // }); + self.peaks.on('user_scroll.' + self.name, function(pixelOffset) { + self.updateWaveform(pixelOffset); + }); self.peaks.on('player_play', function(time) { self.playing = true; From b1f587a8e57fc8f431981de11c812842477ec66a Mon Sep 17 00:00:00 2001 From: Thomas Parisot Date: Wed, 21 Dec 2016 12:35:39 +0000 Subject: [PATCH 7/7] Sync Points and Segments on waveform.render.zoomview instead of zoomview.displaying --- src/main/markers/waveform.points.js | 2 +- src/main/markers/waveform.segments.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/markers/waveform.points.js b/src/main/markers/waveform.points.js index 4e726714b..54210a0db 100644 --- a/src/main/markers/waveform.points.js +++ b/src/main/markers/waveform.points.js @@ -164,7 +164,7 @@ define([ }; WaveformPoints.prototype.init = function() { - this.peaks.on('zoomview.displaying', this.updatePoints.bind(this)); + this.peaks.on('waveform.render.zoomview', this.updatePoints.bind(this)); this.peaks.emit('points.ready'); }; diff --git a/src/main/markers/waveform.segments.js b/src/main/markers/waveform.segments.js index b90e00da0..51b4163e3 100644 --- a/src/main/markers/waveform.segments.js +++ b/src/main/markers/waveform.segments.js @@ -253,7 +253,7 @@ define([ }; WaveformSegments.prototype.init = function() { - this.peaks.on('zoomview.displaying', this.updateSegments.bind(this)); + this.peaks.on('waveform.render.zoomview', this.updateSegments.bind(this)); this.peaks.emit('segments.ready'); };