diff --git a/src/domemittermixin.js b/src/domemittermixin.js deleted file mode 100644 index 5e08310c..00000000 --- a/src/domemittermixin.js +++ /dev/null @@ -1,294 +0,0 @@ -/** - * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import EmitterMixin from '../utils/emittermixin.js'; -import uid from '../utils/uid.js'; -import extend from '../utils/lib/lodash/extend.js'; -import isNative from '../utils/lib/lodash/isNative.js'; - -/** - * Mixin that injects the DOM events API into its host. It provides the API - * compatible with {@link utils.EmitterMixin}. - * - * DOM emitter mixin is by default available in the {@link ui.View} class, - * but it can also be mixed into any other class: - * - * import mix from '../utils/mix.js'; - * import DOMEmitterMixin from '../ui/domemittermixin.js'; - * - * class SomeView {} - * mix( SomeView, DOMEmitterMixin ); - * - * const view = new SomeView(); - * view.listenTo( domElement, ( evt, domEvt ) => { - * console.log( evt, domEvt ); - * } ); - * - * @mixin ui.DOMEmitterMixin - * @mixes utils.EmitterMixin - * @implements ui.DOMEmitter - */ -const DOMEmitterMixin = extend( {}, EmitterMixin, { - /** - * Registers a callback function to be executed when an event is fired in a specific Emitter or DOM Node. - * It is backwards compatible with {@link utils.EmitterMixin#listenTo}. - * - * @param {utils.Emitter|Node} emitter The object that fires the event. - * @param {String} event The name of the event. - * @param {Function} callback The function to be called on event. - * @param {Object} [options={}] Additional options. - * @param {utils.PriorityString|Number} [options.priority='normal'] The priority of this event callback. The higher - * the priority value the sooner the callback will be fired. Events having the same priority are called in the - * order they were added. - * @param {Object} [options.context] The object that represents `this` in the callback. Defaults to the object firing the event. - * @param {Boolean} [options.useCapture=false] Indicates that events of this type will be dispatched to the registered - * listener before being dispatched to any EventTarget beneath it in the DOM tree. - * - * @method ui.DOMEmitterMixin#listenTo - */ - listenTo( ...args ) { - const emitter = args[ 0 ]; - - // Check if emitter is an instance of DOM Node. If so, replace the argument with - // corresponding ProxyEmitter (or create one if not existing). - if ( isDomNode( emitter ) ) { - args[ 0 ] = this._getProxyEmitter( emitter ) || new ProxyEmitter( emitter ); - } - - // Execute parent class method with Emitter (or ProxyEmitter) instance. - EmitterMixin.listenTo.apply( this, args ); - }, - - /** - * Stops listening for events. It can be used at different levels: - * It is backwards compatible with {@link utils.EmitterMixin#listenTo}. - * - * * To stop listening to a specific callback. - * * To stop listening to a specific event. - * * To stop listening to all events fired by a specific object. - * * To stop listening to all events fired by all object. - * - * @param {utils.Emitter|Node} [emitter] The object to stop listening to. If omitted, stops it for all objects. - * @param {String} [event] (Requires the `emitter`) The name of the event to stop listening to. If omitted, stops it - * for all events from `emitter`. - * @param {Function} [callback] (Requires the `event`) The function to be removed from the call list for the given - * `event`. - * - * @method ui.DOMEmitterMixin#stopListening - */ - stopListening( ...args ) { - const emitter = args[ 0 ]; - - // Check if emitter is an instance of DOM Node. If so, replace the argument with corresponding ProxyEmitter. - if ( isDomNode( emitter ) ) { - let proxy = this._getProxyEmitter( emitter ); - - // Element has no listeners. - if ( !proxy ) { - return; - } - - args[ 0 ] = proxy; - } - - // Execute parent class method with Emitter (or ProxyEmitter) instance. - EmitterMixin.stopListening.apply( this, args ); - }, - - /** - * Retrieves ProxyEmitter instance for given DOM Node residing in this Host. - * - * @param {Node} node DOM Node of the ProxyEmitter. - * @method ui.DOMEmitterMixin#_getProxyEmitter - * @return {ProxyEmitter} ProxyEmitter instance or null. - */ - _getProxyEmitter( node ) { - let proxy, emitters, emitterInfo; - - // Get node UID. It allows finding Proxy Emitter for this DOM Node. - const uid = getNodeUID( node ); - - // Find existing Proxy Emitter for this DOM Node among emitters. - if ( ( emitters = this._listeningTo ) ) { - if ( ( emitterInfo = emitters[ uid ] ) ) { - proxy = emitterInfo.emitter; - } - } - - return proxy || null; - } -} ); - -export default DOMEmitterMixin; - -/** - * Creates a ProxyEmitter instance. Such an instance is a bridge between a DOM Node firing events - * and any Host listening to them. It is backwards compatible with {@link utils.EmitterMixin#on}. - * - * listenTo( click, ... ) - * +-----------------------------------------+ - * | stopListening( ... ) | - * +----------------------------+ | addEventListener( click, ... ) - * | Host | | +---------------------------------------------+ - * +----------------------------+ | | removeEventListener( click, ... ) | - * | _listeningTo: { | +----------v-------------+ | - * | UID: { | | ProxyEmitter | | - * | emitter: ProxyEmitter, | +------------------------+ +------------v----------+ - * | callbacks: { | | events: { | | Node (HTMLElement) | - * | click: [ callbacks ] | | click: [ callbacks ] | +-----------------------+ - * | } | | }, | | data-cke-expando: UID | - * | } | | _domNode: Node, | +-----------------------+ - * | } | | _domListeners: {}, | | - * | +------------------------+ | | _emitterId: UID | | - * | | DOMEmitterMixin | | +--------------^---------+ | - * | +------------------------+ | | | | - * +--------------^-------------+ | +---------------------------------------------+ - * | | click (DOM Event) - * +-----------------------------------------+ - * fire( click, DOM Event ) - * - * @memberOf ui - * @mixes utils.EmitterMixin - * @implements ui.DOMEmitter - */ -class ProxyEmitter { - /** - * @param {Node} node DOM Node that fires events. - * @returns {Object} ProxyEmitter instance bound to the DOM Node. - */ - constructor( node ) { - // Set emitter ID to match DOM Node "expando" property. - this._emitterId = getNodeUID( node ); - - // Remember the DOM Node this ProxyEmitter is bound to. - this._domNode = node; - } -} - -extend( ProxyEmitter.prototype, EmitterMixin, { - /** - * Collection of native DOM listeners. - * - * @private - * @member {Object} ui.ProxyEmitter#_domListeners - */ - - /** - * Registers a callback function to be executed when an event is fired. - * - * It attaches a native DOM listener to the DOM Node. When fired, - * a corresponding Emitter event will also fire with DOM Event object as an argument. - * - * @param {String} event The name of the event. - * @param {Function} callback The function to be called on event. - * @param {Object} [options={}] Additional options. - * @param {utils.PriorityString|Number} [options.priority='normal'] The priority of this event callback. The higher - * the priority value the sooner the callback will be fired. Events having the same priority are called in the - * order they were added. - * @param {Object} [options.context] The object that represents `this` in the callback. Defaults to the object firing the event. - * @param {Boolean} [options.useCapture=false] Indicates that events of this type will be dispatched to the registered - * listener before being dispatched to any EventTarget beneath it in the DOM tree. - * - * @method ui.ProxyEmitter#on - */ - on( event, callback, options = {} ) { - // Execute parent class method first. - EmitterMixin.on.apply( this, arguments ); - - // If the DOM Listener for given event already exist it is pointless - // to attach another one. - if ( this._domListeners && this._domListeners[ event ] ) { - return; - } - - const domListener = this._createDomListener( event ); - - // Attach the native DOM listener to DOM Node. - this._domNode.addEventListener( event, domListener, !!options.useCapture ); - - if ( !this._domListeners ) { - this._domListeners = {}; - } - - // Store the native DOM listener in this ProxyEmitter. It will be helpful - // when stopping listening to the event. - this._domListeners[ event ] = domListener; - }, - - /** - * Stops executing the callback on the given event. - * - * @param {String} event The name of the event. - * @param {Function} callback The function to stop being called. - * @param {Object} [context] The context object to be removed, pared with the given callback. To handle cases where - * the same callback is used several times with different contexts. - * - * @method ui.ProxyEmitter#off - */ - off( event ) { - // Execute parent class method first. - EmitterMixin.off.apply( this, arguments ); - - let events; - - // Remove native DOM listeners which are orphans. If no callbacks - // are awaiting given event, detach native DOM listener from DOM Node. - // See: {@link on}. - - if ( this._domListeners[ event ] && ( !( events = this._events[ event ] ) || !events.callbacks.length ) ) { - this._domListeners[ event ].removeListener(); - } - }, - - /** - * Create a native DOM listener callback. When the native DOM event - * is fired it will fire corresponding event on this ProxyEmitter. - * Note: A native DOM Event is passed as an argument. - * - * @param {String} event - * - * @method ui.ProxyEmitter#_createDomListener - * @returns {Function} The DOM listener callback. - */ - _createDomListener( event ) { - const domListener = domEvt => { - this.fire( event, domEvt ); - }; - - // Supply the DOM listener callback with a function that will help - // detach it from the DOM Node, when it is no longer necessary. - // See: {@link off}. - domListener.removeListener = () => { - this._domNode.removeEventListener( event, domListener ); - delete this._domListeners[ event ]; - }; - - return domListener; - } -} ); - -// Gets an unique DOM Node identifier. The identifier will be set if not defined. -// -// @private -// @param {Node} node -// @return {String} UID for given DOM Node. -function getNodeUID( node ) { - return node[ 'data-ck-expando' ] || ( node[ 'data-ck-expando' ] = uid() ); -} - -// Checks (naively) if given node is native DOM Node. -// -// @private -// @param {Node} node -// @return {Boolean} True when native DOM Node. -function isDomNode( node ) { - return node && isNative( node.addEventListener ); -} - -/** - * Interface representing classes which mix in {@link ui.DOMEmitter}. - * - * @interface ui.DOMEmitter - */ diff --git a/src/template.js b/src/template.js index 5e400a8c..0db74223 100644 --- a/src/template.js +++ b/src/template.js @@ -852,8 +852,8 @@ function getStyleUpdater( el, styleName ) { function clone( def ) { const clone = cloneDeepWith( def, value => { // Don't clone the `Template.bind`* bindings because of the references to Observable - // and DOMEmitterMixin instances inside, which would also be traversed and cloned by greedy - // cloneDeepWith algorithm. There's no point in cloning Observable/DOMEmitterMixins + // and DomEmitterMixin instances inside, which would also be traversed and cloned by greedy + // cloneDeepWith algorithm. There's no point in cloning Observable/DomEmitterMixins // along with the definition. // // Also don't clone View instances if provided as a child of the Template. The template diff --git a/src/view.js b/src/view.js index f0416d80..0fd7e3eb 100644 --- a/src/view.js +++ b/src/view.js @@ -6,7 +6,7 @@ import CKEditorError from '../utils/ckeditorerror.js'; import ViewCollection from './viewcollection.js'; import Template from './template.js'; -import DOMEmitterMixin from './domemittermixin.js'; +import DomEmmiterMixin from '../utils/dom/emittermixin.js'; import ObservableMixin from '../utils/observablemixin.js'; import Collection from '../utils/collection.js'; import mix from '../utils/mix.js'; @@ -44,8 +44,8 @@ import mix from '../utils/mix.js'; * } ); * * @memberOf ui - * @mixes DOMEmitterMixin - * @mixes ObservableMixin + * @mixes utils.dom.EmmiterMixin + * @mixes utils.ObservableMixin */ export default class View { /** @@ -298,5 +298,5 @@ export default class View { } } -mix( View, DOMEmitterMixin ); +mix( View, DomEmmiterMixin ); mix( View, ObservableMixin ); diff --git a/tests/domemittermixin.js b/tests/domemittermixin.js deleted file mode 100644 index 1cf46c38..00000000 --- a/tests/domemittermixin.js +++ /dev/null @@ -1,468 +0,0 @@ -/** - * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/* globals document, window, Event, MouseEvent */ -/* bender-tags: ui */ - -import testUtils from 'tests/core/_utils/utils.js'; -import DOMEmitterMixin from 'ckeditor5/ui/domemittermixin.js'; -import EmitterMixin from 'ckeditor5/utils/emittermixin.js'; - -testUtils.createSinonSandbox(); - -describe( 'DOMEmitterMixin', () => { - let emitter, domEmitter, node; - - beforeEach( () => { - emitter = Object.create( EmitterMixin ); - domEmitter = Object.create( DOMEmitterMixin ); - node = document.createElement( 'div' ); - } ); - - afterEach( () => { - domEmitter.stopListening(); - } ); - - describe( 'listenTo', () => { - it( 'should listen to EmitterMixin events', () => { - const spy = testUtils.sinon.spy(); - - domEmitter.listenTo( emitter, 'test', spy ); - emitter.fire( 'test' ); - - sinon.assert.calledOnce( spy ); - } ); - - it( 'should listen to native DOM events', () => { - const spy = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'test', spy ); - - node.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy ); - } ); - - it( 'should listen to native DOM events - window as source', () => { - const spy = testUtils.sinon.spy(); - - domEmitter.listenTo( window, 'test', spy ); - - window.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy ); - } ); - - it( 'should listen to native DOM events - document as source', () => { - const spy = testUtils.sinon.spy(); - - domEmitter.listenTo( document, 'test', spy ); - - document.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy ); - } ); - - // #187 - it( 'should work for DOM Nodes belonging to another window', () => { - const spy = testUtils.sinon.spy(); - const iframe = document.createElement( 'iframe' ); - - document.body.appendChild( iframe ); - const iframeNode = iframe.contentWindow.document.createElement( 'div' ); - - domEmitter.listenTo( iframeNode, 'test', spy ); - iframeNode.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy ); - } ); - - describe( 'event capturing', () => { - beforeEach( () => { - document.body.appendChild( node ); - } ); - - afterEach( () => { - document.body.removeChild( node ); - } ); - - it( 'should not use capturing at default', () => { - const spy = testUtils.sinon.spy(); - - domEmitter.listenTo( document, 'test', spy ); - - node.dispatchEvent( new Event( 'test', { bubbles: false } ) ); - - sinon.assert.notCalled( spy ); - } ); - - it( 'should optionally use capturing', () => { - const spy = testUtils.sinon.spy(); - - domEmitter.listenTo( document, 'test', spy, { useCapture: true } ); - - node.dispatchEvent( new Event( 'test', { bubbles: false } ) ); - - sinon.assert.calledOnce( spy ); - } ); - } ); - } ); - - describe( 'stopListening', () => { - it( 'should stop listening to a specific event callback', () => { - const spy1 = testUtils.sinon.spy(); - const spy2 = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'event1', spy1 ); - domEmitter.listenTo( node, 'event2', spy2 ); - - node.dispatchEvent( new Event( 'event1' ) ); - node.dispatchEvent( new Event( 'event2' ) ); - - domEmitter.stopListening( node, 'event1', spy1 ); - - node.dispatchEvent( new Event( 'event1' ) ); - node.dispatchEvent( new Event( 'event2' ) ); - - sinon.assert.calledOnce( spy1 ); - sinon.assert.calledTwice( spy2 ); - } ); - - it( 'should stop listening to an specific event', () => { - const spy1a = testUtils.sinon.spy(); - const spy1b = testUtils.sinon.spy(); - const spy2 = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'event1', spy1a ); - domEmitter.listenTo( node, 'event1', spy1b ); - domEmitter.listenTo( node, 'event2', spy2 ); - - node.dispatchEvent( new Event( 'event1' ) ); - node.dispatchEvent( new Event( 'event2' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledOnce( spy2 ); - - domEmitter.stopListening( node, 'event1' ); - - node.dispatchEvent( new Event( 'event1' ) ); - node.dispatchEvent( new Event( 'event2' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledTwice( spy2 ); - } ); - - it( 'should stop listening to all events from a specific node', () => { - const spy1 = testUtils.sinon.spy(); - const spy2 = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'event1', spy1 ); - domEmitter.listenTo( node, 'event2', spy2 ); - - node.dispatchEvent( new Event( 'event1' ) ); - node.dispatchEvent( new Event( 'event2' ) ); - - domEmitter.stopListening( node ); - - node.dispatchEvent( new Event( 'event1' ) ); - node.dispatchEvent( new Event( 'event2' ) ); - - sinon.assert.calledOnce( spy1 ); - sinon.assert.calledOnce( spy2 ); - } ); - - it( 'should stop listening to everything', () => { - const spy1 = testUtils.sinon.spy(); - const spy2 = testUtils.sinon.spy(); - - const node1 = document.createElement( 'div' ); - const node2 = document.createElement( 'div' ); - - domEmitter.listenTo( node1, 'event1', spy1 ); - domEmitter.listenTo( node2, 'event2', spy2 ); - - expect( domEmitter ).to.have.property( '_listeningTo' ); - - node1.dispatchEvent( new Event( 'event1' ) ); - node2.dispatchEvent( new Event( 'event2' ) ); - - domEmitter.stopListening(); - - node1.dispatchEvent( new Event( 'event1' ) ); - node2.dispatchEvent( new Event( 'event2' ) ); - - sinon.assert.calledOnce( spy1 ); - sinon.assert.calledOnce( spy2 ); - - expect( domEmitter ).to.not.have.property( '_listeningTo' ); - } ); - - it( 'should stop listening to everything what left', () => { - const spy1 = testUtils.sinon.spy(); - const spy2 = testUtils.sinon.spy(); - const spy3 = testUtils.sinon.spy(); - const spy4 = testUtils.sinon.spy(); - - const node1 = document.createElement( 'div' ); - const node2 = document.createElement( 'div' ); - - domEmitter.listenTo( node1, 'event1', spy1 ); - domEmitter.listenTo( node1, 'event2', spy2 ); - domEmitter.listenTo( node2, 'event1', spy3 ); - domEmitter.listenTo( node2, 'event2', spy4 ); - - expect( domEmitter ).to.have.property( '_listeningTo' ); - - domEmitter.stopListening( node1, 'event1', spy1 ); - domEmitter.stopListening( node2, 'event1' ); - - node1.dispatchEvent( new Event( 'event1' ) ); - node1.dispatchEvent( new Event( 'event2' ) ); - node2.dispatchEvent( new Event( 'event1' ) ); - node2.dispatchEvent( new Event( 'event2' ) ); - - sinon.assert.notCalled( spy1 ); - sinon.assert.calledOnce( spy2 ); - sinon.assert.notCalled( spy3 ); - sinon.assert.calledOnce( spy4 ); - - domEmitter.stopListening(); - - node1.dispatchEvent( new Event( 'event1' ) ); - node1.dispatchEvent( new Event( 'event2' ) ); - node2.dispatchEvent( new Event( 'event1' ) ); - node2.dispatchEvent( new Event( 'event2' ) ); - - sinon.assert.notCalled( spy1 ); - sinon.assert.calledOnce( spy2 ); - sinon.assert.notCalled( spy3 ); - sinon.assert.calledOnce( spy4 ); - - expect( domEmitter ).to.not.have.property( '_listeningTo' ); - } ); - - it( 'should not stop other nodes when a non-listened node is provided', () => { - const spy = testUtils.sinon.spy(); - - const node1 = document.createElement( 'div' ); - const node2 = document.createElement( 'div' ); - - domEmitter.listenTo( node1, 'test', spy ); - - domEmitter.stopListening( node2 ); - - node1.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.called( spy ); - } ); - - it( 'should pass DOM Event data to the listener', () => { - const spy = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'click', spy ); - - const mouseEvent = new MouseEvent( 'click', { - screenX: 10, - screenY: 20 - } ); - - node.dispatchEvent( mouseEvent ); - - sinon.assert.calledOnce( spy ); - expect( spy.args[ 0 ][ 1 ] ).to.be.equal( mouseEvent ); - } ); - - it( 'should detach native DOM event listener proxy, specific event', () => { - const spy1a = testUtils.sinon.spy(); - const spy1b = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'test', spy1a ); - - const proxyEmitter = domEmitter._getProxyEmitter( node ); - const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' ); - - node.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy2 ); - - domEmitter.stopListening( node, 'test' ); - node.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy2 ); - - // Attach same event again. - domEmitter.listenTo( node, 'test', spy1b ); - node.dispatchEvent( new Event( 'test' ) ); - - expect( proxyEmitter ).to.be.equal( domEmitter._getProxyEmitter( node ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledTwice( spy2 ); - } ); - - it( 'should detach native DOM event listener proxy, specific callback', () => { - const spy1a = testUtils.sinon.spy(); - const spy1b = testUtils.sinon.spy(); - const spy1c = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'test', spy1a ); - domEmitter.listenTo( node, 'test', spy1b ); - - const proxyEmitter = domEmitter._getProxyEmitter( node ); - const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' ); - - node.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledOnce( spy2 ); - - domEmitter.stopListening( node, 'test', spy1a ); - node.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledTwice( spy1b ); - sinon.assert.calledTwice( spy2 ); - - domEmitter.stopListening( node, 'test', spy1b ); - node.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledTwice( spy1b ); - sinon.assert.calledTwice( spy2 ); - - // Attach same event again. - domEmitter.listenTo( node, 'test', spy1c ); - node.dispatchEvent( new Event( 'test' ) ); - - expect( proxyEmitter ).to.be.equal( domEmitter._getProxyEmitter( node ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledTwice( spy1b ); - sinon.assert.calledOnce( spy1c ); - sinon.assert.calledThrice( spy2 ); - } ); - - it( 'should detach native DOM event listener proxy, specific emitter', () => { - const spy1a = testUtils.sinon.spy(); - const spy1b = testUtils.sinon.spy(); - const spy1c = testUtils.sinon.spy(); - const spy1d = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'test1', spy1a ); - domEmitter.listenTo( node, 'test2', spy1b ); - - const proxyEmitter = domEmitter._getProxyEmitter( node ); - const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' ); - - node.dispatchEvent( new Event( 'test1' ) ); - node.dispatchEvent( new Event( 'test2' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledTwice( spy2 ); - - domEmitter.stopListening( node ); - - node.dispatchEvent( new Event( 'test1' ) ); - node.dispatchEvent( new Event( 'test2' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledTwice( spy2 ); - - // Attach same event again. - domEmitter.listenTo( node, 'test1', spy1c ); - domEmitter.listenTo( node, 'test2', spy1d ); - - // Old proxy emitter died when stopped listening to the node. - const proxyEmitter2 = domEmitter._getProxyEmitter( node ); - const spy3 = testUtils.sinon.spy( proxyEmitter2, 'fire' ); - - node.dispatchEvent( new Event( 'test1' ) ); - node.dispatchEvent( new Event( 'test2' ) ); - - expect( proxyEmitter ).to.not.be.equal( proxyEmitter2 ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledOnce( spy1c ); - sinon.assert.calledOnce( spy1d ); - sinon.assert.calledTwice( spy2 ); - sinon.assert.calledTwice( spy3 ); - } ); - - it( 'should detach native DOM event listener proxy, everything', () => { - const spy1a = testUtils.sinon.spy(); - const spy1b = testUtils.sinon.spy(); - const spy1c = testUtils.sinon.spy(); - const spy1d = testUtils.sinon.spy(); - - domEmitter.listenTo( node, 'test1', spy1a ); - domEmitter.listenTo( node, 'test2', spy1b ); - - const proxyEmitter = domEmitter._getProxyEmitter( node ); - const spy2 = testUtils.sinon.spy( proxyEmitter, 'fire' ); - - node.dispatchEvent( new Event( 'test1' ) ); - node.dispatchEvent( new Event( 'test2' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledTwice( spy2 ); - - domEmitter.stopListening(); - - node.dispatchEvent( new Event( 'test1' ) ); - node.dispatchEvent( new Event( 'test2' ) ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledTwice( spy2 ); - - // Attach same event again. - domEmitter.listenTo( node, 'test1', spy1c ); - domEmitter.listenTo( node, 'test2', spy1d ); - - // Old proxy emitter died when stopped listening to the node. - const proxyEmitter2 = domEmitter._getProxyEmitter( node ); - const spy3 = testUtils.sinon.spy( proxyEmitter2, 'fire' ); - - node.dispatchEvent( new Event( 'test1' ) ); - node.dispatchEvent( new Event( 'test2' ) ); - - expect( proxyEmitter ).to.not.be.equal( proxyEmitter2 ); - - sinon.assert.calledOnce( spy1a ); - sinon.assert.calledOnce( spy1b ); - sinon.assert.calledOnce( spy1c ); - sinon.assert.calledOnce( spy1d ); - sinon.assert.calledTwice( spy2 ); - sinon.assert.calledTwice( spy3 ); - } ); - - // #187 - it( 'should work for DOM Nodes belonging to another window', () => { - const spy = testUtils.sinon.spy(); - const iframe = document.createElement( 'iframe' ); - - document.body.appendChild( iframe ); - const iframeNode = iframe.contentWindow.document.createElement( 'div' ); - - domEmitter.listenTo( iframeNode, 'test', spy ); - - iframeNode.dispatchEvent( new Event( 'test' ) ); - domEmitter.stopListening( iframeNode ); - iframeNode.dispatchEvent( new Event( 'test' ) ); - - sinon.assert.calledOnce( spy ); - } ); - } ); -} ); diff --git a/tests/template.js b/tests/template.js index 3f55f119..3c147354 100644 --- a/tests/template.js +++ b/tests/template.js @@ -14,7 +14,7 @@ import ViewCollection from 'ckeditor5/ui/viewcollection.js'; import Model from 'ckeditor5/ui/model.js'; import CKEditorError from 'ckeditor5/utils/ckeditorerror.js'; import EmitterMixin from 'ckeditor5/utils/emittermixin.js'; -import DOMEmitterMixin from 'ckeditor5/ui/domemittermixin.js'; +import DomEmitterMixin from 'ckeditor5/utils/dom/emittermixin.js'; import Collection from 'ckeditor5/utils/collection.js'; import normalizeHtml from 'tests/utils/_utils/normalizehtml.js'; @@ -25,7 +25,7 @@ let el, text; describe( 'Template', () => { describe( 'constructor()', () => { it( 'accepts and normalizes the definition', () => { - const bind = Template.bind( new Model( {} ), Object.create( DOMEmitterMixin ) ); + const bind = Template.bind( new Model( {} ), Object.create( DomEmitterMixin ) ); const tpl = new Template( { tag: 'p', attributes: { @@ -551,7 +551,7 @@ describe( 'Template', () => { baz: 'qux' } ); - domEmitter = Object.create( DOMEmitterMixin ); + domEmitter = Object.create( DomEmitterMixin ); bind = Template.bind( observable, domEmitter ); } ); @@ -809,7 +809,7 @@ describe( 'Template', () => { baz: 'qux' } ); - domEmitter = Object.create( DOMEmitterMixin ); + domEmitter = Object.create( DomEmitterMixin ); bind = Template.bind( observable, domEmitter ); } ); @@ -1525,7 +1525,7 @@ describe( 'Template', () => { baz: 'qux' } ); - emitter = Object.create( DOMEmitterMixin ); + emitter = Object.create( DomEmitterMixin ); bind = Template.bind( observable, emitter ); } );