diff --git a/build.json b/build.json new file mode 100644 index 0000000..1468013 --- /dev/null +++ b/build.json @@ -0,0 +1,23 @@ +[ + "src/sidetable.js", + "src/wrappers.js", + "src/wrappers/events.js", + "src/wrappers/NodeList.js", + "src/wrappers/Node.js", + "src/querySelector.js", + "src/wrappers/node-interfaces.js", + "src/wrappers/CharacterData.js", + "src/wrappers/Element.js", + "src/wrappers/HTMLElement.js", + "src/wrappers/HTMLContentElement.js", + "src/wrappers/HTMLShadowElement.js", + "src/wrappers/HTMLTemplateElement.js", + "src/wrappers/HTMLUnknownElement.js", + "src/wrappers/generic.js", + "src/wrappers/ShadowRoot.js", + "src/ShadowRenderer.js", + "src/wrappers/Document.js", + "src/wrappers/Window.js", + "src/wrappers/MutationObserver.js", + "src/wrappers/override-constructors.js" +] diff --git a/conf/karma.conf.js b/conf/karma.conf.js index 382e2c7..fe9189b 100644 --- a/conf/karma.conf.js +++ b/conf/karma.conf.js @@ -1,89 +1,85 @@ -// Sample Karma configuration file, that contain pretty much all the available options -// It's used for running client tests on Travis (http://travis-ci.org/#!/karma-runner/karma) -// Most of the options can be overriden by cli arguments (see karma --help) -// -// For all available config options and default values, see: -// https://github.com/karma-runner/karma/blob/stable/lib/config.js#L54 - - -// base path, that will be used to resolve files and exclude -basePath = '../'; - -// list of files / patterns to load in the browser -files = [ - 'tools/test/mocha-htmltest.js', - 'conf/mocha.conf.js', - 'node_modules/chai/chai.js', - 'shadowdom.js', - 'test/test.main.js', - {pattern: 'src/**/*.js', included: false}, - {pattern: 'test/**/*.js', included: false}, - {pattern: 'test/**/*.html', included: false}, - {pattern: 'tools/**/*.js', included: false} -]; - -// list of files to exclude -exclude = []; - -frameworks = ['mocha']; - -// use dots reporter, as travis terminal does not support escaping sequences -// possible values: 'dots', 'progress', 'junit', 'teamcity' -// CLI --reporters progress -reporters = ['progress']; - -// web server port -// CLI --port 9876 -port = 9876; - -// cli runner port -// CLI --runner-port 9100 -runnerPort = 9100; - -// enable / disable colors in the output (reporters and logs) -// CLI --colors --no-colors -colors = true; - -// level of logging -// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG -// CLI --log-level debug -logLevel = LOG_INFO; - -// enable / disable watching file and executing tests whenever any file changes -// CLI --auto-watch --no-auto-watch -autoWatch = true; - -// Start these browsers, currently available: -// - Chrome -// - ChromeCanary -// - Firefox -// - Opera -// - Safari (only Mac) -// - PhantomJS -// - IE (only Windows) -// CLI --browsers Chrome,Firefox,Safari -browsers = ['ChromeCanary']; - -// If browser does not capture in given timeout [ms], kill it -// CLI --capture-timeout 5000 -captureTimeout = 50000; - -// Auto run tests on start (when browsers are captured) and exit -// CLI --single-run --no-single-run -singleRun = true; - -// report which specs are slower than 500ms -// CLI --report-slower-than 500 -reportSlowerThan = 500; - -// compile coffee scripts -preprocessors = { +module.exports = function(karma) { + karma.configure({ + // base path, that will be used to resolve files and exclude + basePath: '../', + + // list of files / patterns to load in the browser + files: [ + 'tools/test/mocha-htmltest.js', + 'conf/mocha.conf.js', + 'node_modules/chai/chai.js', + 'shadowdom.js', + 'test/test.main.js', + {pattern: 'src/**/*.js', included: false}, + {pattern: 'test/**/*.js', included: false}, + {pattern: 'test/**/*.html', included: false}, + {pattern: 'tools/**/*.js', included: false} + ], + + // list of files to exclude + exclude: [], + + frameworks: ['mocha'], + + // use dots reporter, as travis terminal does not support escaping sequences + // possible values: 'dots', 'progress', 'junit', 'teamcity' + // CLI --reporters progress + reporters: ['progress'], + + // web server port + // CLI --port 9876 + port: 9876, + + // cli runner port + // CLI --runner-port 9100 + runnerPort: 9100, + + // enable / disable colors in the output (reporters and logs) + // CLI --colors --no-colors + colors: true, + + // level of logging + // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG + // CLI --log-level debug + logLevel: LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + // CLI --auto-watch --no-auto-watch + autoWatch: true, + + // Start these browsers, currently available: + // - Chrome + // - ChromeCanary + // - Firefox + // - Opera + // - Safari (only Mac) + // - PhantomJS + // - IE (only Windows) + // CLI --browsers Chrome,Firefox,Safari + browsers: ['ChromeCanary'], + + // If browser does not capture in given timeout [ms], kill it + // CLI --capture-timeout 5000 + captureTimeout: 50000, + + // Auto run tests on start (when browsers are captured) and exit + // CLI --single-run --no-single-run + singleRun: true, + + // report which specs are slower than 500ms + // CLI --report-slower-than 500 + reportSlowerThan: 500, + + // compile coffee scripts + preprocessors: { + }, + + plugins: [ + 'karma-mocha', + 'karma-chrome-launcher', + 'karma-firefox-launcher', + 'karma-script-launcher', + 'karma-crbot-reporter' + ] + }); }; - -plugins = [ - 'karma-mocha', - 'karma-chrome-launcher', - 'karma-firefox-launcher', - 'karma-script-launcher', - 'karma-crbot-reporter' -] diff --git a/gruntfile.js b/gruntfile.js index ab5f923..7e66b60 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -4,32 +4,7 @@ * license that can be found in the LICENSE file. */ module.exports = function(grunt) { - ShadowDOM = [ - 'sidetable.js', - 'wrappers.js', - 'wrappers/events.js', - 'wrappers/NodeList.js', - 'wrappers/Node.js', - 'querySelector.js', - 'wrappers/node-interfaces.js', - 'wrappers/CharacterData.js', - 'wrappers/Element.js', - 'wrappers/HTMLElement.js', - 'wrappers/HTMLContentElement.js', - 'wrappers/HTMLShadowElement.js', - 'wrappers/HTMLTemplateElement.js', - 'wrappers/HTMLUnknownElement.js', - 'wrappers/generic.js', - 'wrappers/ShadowRoot.js', - 'ShadowRenderer.js', - 'wrappers/Document.js', - 'wrappers/Window.js', - 'wrappers/MutationObserver.js', - 'wrappers/override-constructors.js' - ]; - ShadowDOM = ShadowDOM.map(function(p) { - return 'src/' + p; - }); + ShadowDOM = grunt.file.readJSON('build.json'); // karma setup var browsers; (function() { @@ -72,7 +47,8 @@ module.exports = function(grunt) { // TODO(sjmiles): should be false by default (?) // https://github.com/mishoo/UglifyJS2/issues/165 unsafe: false - } + }, + banner: grunt.file.read('LICENSE') //compress: true, Xmangle: true, beautify: true, unsafe: false }, files: { @@ -103,7 +79,7 @@ module.exports = function(grunt) { // plugins grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-yuidoc'); - grunt.loadNpmTasks('grunt-karma-0.9.1'); + grunt.loadNpmTasks('grunt-karma'); // tasks grunt.registerTask('default', ['uglify']); diff --git a/package.json b/package.json index a618271..afc1356 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "grunt": "*", "grunt-contrib-uglify": "*", "grunt-contrib-yuidoc": "~0.4.0", - "grunt-karma-0.9.1": "~0.4.3", + "grunt-karma": "~0.5.0", "karma-mocha": "*", "karma-script-launcher": "*", "karma-crbot-reporter": "*" diff --git a/src/wrappers/events.js b/src/wrappers/events.js index f0bdb02..1d9fb75 100644 --- a/src/wrappers/events.js +++ b/src/wrappers/events.js @@ -171,6 +171,19 @@ return rootOfNode(a) === rootOfNode(b); } + function enclosedBy(a, b) { + if (a === b) + return true; + if (a instanceof wrappers.ShadowRoot) { + var host = scope.getHostForShadowRoot(a); + if (!host) + return false; + return enclosedBy(rootOfNode(host), b); + } + return false; + + } + function isMutationEvent(type) { switch (type) { case 'DOMAttrModified': @@ -389,17 +402,16 @@ var eventPath = eventPathTable.get(this); if (eventPath) { var index = 0; - var found = false; - var currentTarget = currentTargetTable.get(this); var lastIndex = eventPath.length - 1; + var baseRoot = rootOfNode(currentTargetTable.get(this)); + for (var i = 0; i <= lastIndex; i++) { - if (!found) - found = eventPath[i].currentTarget === currentTarget; - if (found) { - var node = eventPath[i].currentTarget; - // Do not include the top Window. - if (i !== lastIndex || node instanceof wrappers.Node) - nodeList[index++] = node; + var currentTarget = eventPath[i].currentTarget; + var currentRoot = rootOfNode(currentTarget); + if (enclosedBy(baseRoot, currentRoot) && + // Make sure we do not add Window to the path. + (i !== lastIndex || currentTarget instanceof wrappers.Node)) { + nodeList[index++] = currentTarget; } } nodeList.length = index; @@ -435,10 +447,17 @@ GenericEvent.prototype = Object.create(SuperEvent.prototype); if (prototype) mixin(GenericEvent.prototype, prototype); - // Firefox does not support FocusEvent - // https://bugzilla.mozilla.org/show_bug.cgi?id=855741 - if (OriginalEvent) - registerWrapper(OriginalEvent, GenericEvent, document.createEvent(name)); + if (OriginalEvent) { + // IE does not support event constructors but FocusEvent can only be + // created using new FocusEvent in Firefox. + // https://bugzilla.mozilla.org/show_bug.cgi?id=882165 + if (OriginalEvent.prototype['init' + name]) { + registerWrapper(OriginalEvent, GenericEvent, + document.createEvent(name)); + } else { + registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent('temp')); + } + } return GenericEvent; } @@ -568,7 +587,7 @@ 'dispatchEvent' ]; - [Element, Window, Document].forEach(function(constructor) { + [Node, Window].forEach(function(constructor) { var p = constructor.prototype; methodNames.forEach(function(name) { Object.defineProperty(p, name + '_', {value: p[name]}); diff --git a/test/js/MutationObserver.js b/test/js/MutationObserver.js index a1c33f5..6f87090 100644 --- a/test/js/MutationObserver.js +++ b/test/js/MutationObserver.js @@ -249,4 +249,29 @@ suite('MutationObserver', function() { document.head.setAttribute('a', newValue()); }); + test('observe text node', function(done) { + if (!window.MutationObserver) { + done(); + return; + } + + div = document.body.appendChild(document.createElement('div')); + var a = document.createTextNode(''); + div.appendChild(a); + + var mo = new MutationObserver(function(records, observer) { + mergeRecords(records); + assert.equal(this, mo); + assert.equal(observer, mo); + assert.equal(records[0].type, 'childList'); + assert.equal(records[0].target, div); + assert.equal(removedNodes.length, 1); + assert.equal(removedNodes[0], a); + done(); + }); + mo.observe(div, {childList: true}); + + div.removeChild(a); + }); + }); diff --git a/test/js/events.js b/test/js/events.js index 361ed74..0114fcc 100644 --- a/test/js/events.js +++ b/test/js/events.js @@ -57,9 +57,8 @@ htmlSuite('Events', function() { } teardown(function() { - if (div) { + if (div && div.parentNode) div.parentNode.removeChild(div); - } div = a = b = c = d = e = f = content = sr = undefined; }); @@ -1174,7 +1173,11 @@ test('retarget order (multiple shadow roots)', function() { div.click(); assert.equal(calls, 2); - assert.isTrue(event.defaultPrevented); + + // defaultPrevented is broken in IE. + // https://connect.microsoft.com/IE/feedback/details/790389/event-defaultprevented-returns-false-after-preventdefault-was-called + if (!/IE/.test(navigator.userAgent)) + assert.isTrue(event.defaultPrevented); }); test('event.path (bubbles)', function() { @@ -1184,6 +1187,9 @@ test('retarget order (multiple shadow roots)', function() { tree.e.addEventListener('x', function f(e) { assertArrayEqual( [ + tree.c, + tree.content, + tree.content2, tree.e, tree.sr2, tree.d, @@ -1200,6 +1206,9 @@ test('retarget order (multiple shadow roots)', function() { tree.sr.addEventListener('x', function f(e) { assertArrayEqual( [ + tree.c, + tree.content, + tree.d, tree.sr, tree.b, tree.a, @@ -1210,13 +1219,25 @@ test('retarget order (multiple shadow roots)', function() { tree.sr.removeEventListener('x', f); }); + tree.c.addEventListener('x', function f(e) { + assertArrayEqual( + [ + tree.c, + tree.b, + tree.a, + tree.div, + ], + e.path); + + tree.c.removeEventListener('x', f); + }); + tree.c.dispatchEvent(e); }); test('event.path on body (bubbles)', function() { var e = new Event('x', {bubbles: true}); var doc = wrap(document); - var win = wrap(window); doc.body.addEventListener('x', function f(e) { assertArrayEqual( @@ -1233,6 +1254,7 @@ test('retarget order (multiple shadow roots)', function() { doc.documentElement.addEventListener('x', function f(e) { assertArrayEqual( [ + doc.body, doc.documentElement, doc, ], @@ -1244,4 +1266,15 @@ test('retarget order (multiple shadow roots)', function() { doc.body.dispatchEvent(e); }); + test('dispatch on text node', function() { + var text = document.createTextNode('x'); + text.addEventListener('x', function f(e) { + assert.equal(e.target, text); + assert.equal(e.currentTarget, text); + assert.equal(this, text); + text.removeEventListener('x', f); + }); + text.dispatchEvent(new Event('x')); + }); + });