Explorar el Código

strip out jquery event handler system. dont overdo globallistener's unneeded

Adam Shaw hace 8 años
padre
commit
28ba0ddafc

+ 13 - 5
src/common/DragListener.ts

@@ -26,6 +26,7 @@ export default class DragListener {
   listenTo: ListenerInterface['listenTo']
   stopListeningTo: ListenerInterface['stopListeningTo']
 
+  $document: any
   options: any
   subjectEl: any
 
@@ -156,10 +157,15 @@ export default class DragListener {
     let globalEmitter = GlobalEmitter.get()
 
     if (this.isGeneric) {
-      this.listenTo($(document), { // might only work on iOS because of GlobalEmitter's bind :(
-        drag: this.handleMove,
-        dragstop: this.endInteraction
-      })
+      if (!this.$document && window['jQuery']) {
+        this.$document = window['jQuery'](document)
+      }
+      if (this.$document) { // we can only attach jQueryUI drag handlers to a jQuery element reference
+        this.listenTo(this.$document, { // might only work on iOS because of GlobalEmitter's bind :(
+          drag: this.handleMove,
+          dragstop: this.endInteraction
+        })
+      }
     } else if (this.isTouch) {
       this.listenTo(globalEmitter, {
         touchmove: this.handleTouchMove,
@@ -182,7 +188,9 @@ export default class DragListener {
 
   unbindHandlers() {
     this.stopListeningTo(GlobalEmitter.get())
-    this.stopListeningTo($(document)) // for isGeneric
+    if (this.$document) {
+      this.stopListeningTo(this.$document) // for isGeneric
+    }
   }
 
 

+ 49 - 50
src/common/EmitterMixin.ts

@@ -12,7 +12,7 @@ after class:
   EmitterMixin.mixInto(TheClass)
 */
 
-import * as $ from 'jquery'
+import { applyAll } from '../util'
 import Mixin from './Mixin'
 
 export interface EmitterInterface {
@@ -26,75 +26,74 @@ export interface EmitterInterface {
 
 export default class EmitterMixin extends Mixin implements EmitterInterface {
 
-  // jQuery-ification via $(this) allows a non-DOM object to have
-  // the same event handling capabilities (including namespaces).
+  _handlers: any
+  _oneHandlers: any
 
-
-  on(types, handler) {
-    $(this).on(types, this._prepareIntercept(handler))
+  on(type, handler) {
+    addToHash(
+      this._handlers || (this._handlers = {}),
+      type,
+      handler
+    )
     return this // for chaining
   }
 
-
-  one(types, handler) {
-    $(this).one(types, this._prepareIntercept(handler))
+  // todo: add comments
+  one(type, handler) {
+    addToHash(
+      this._oneHandlers || (this._oneHandlers = {}),
+      type,
+      handler
+    )
     return this // for chaining
   }
 
-
-  _prepareIntercept(handler) {
-    // handlers are always called with an "event" object as their first param.
-    // sneak the `this` context and arguments into the extra parameter object
-    // and forward them on to the original handler.
-    let intercept = function(ev, extra) {
-      return handler.apply(
-        extra.context || this,
-        extra.args || []
-      )
+  off(type, handler?) {
+    if (this._handlers) {
+      removeFromHash(this._handlers, type, handler)
     }
-
-    // mimick jQuery's internal "proxy" system (risky, I know)
-    // causing all functions with the same .guid to appear to be the same.
-    // https://github.com/jquery/jquery/blob/2.2.4/src/core.js#L448
-    // this is needed for calling .off with the original non-intercept handler.
-    if (!handler.guid) {
-      handler.guid = ($ as any).guid++
+    if (this._oneHandlers) {
+      removeFromHash(this._oneHandlers, type, handler)
     }
-    (intercept as any).guid = handler.guid
-
-    return intercept
+    return this // for chaining
   }
 
-
-  off(types, handler) {
-    $(this).off(types, handler)
-
+  trigger(type, ...args) {
+    this.triggerWith(type, this, args)
     return this // for chaining
   }
 
-
-  trigger(types, ...args) {
-    // pass in "extra" info to the intercept
-    $(this).triggerHandler(types, { args: args })
-
+  triggerWith(type, context, args) {
+    if (this._handlers) {
+      applyAll(this._handlers[type], context, args)
+    }
+    if (this._oneHandlers) {
+      applyAll(this._oneHandlers[type], context, args)
+      delete this._oneHandlers[type]
+    }
     return this // for chaining
   }
 
-
-  triggerWith(types, context, args) {
-
-    // `triggerHandler` is less reliant on the DOM compared to `trigger`.
-    // pass in "extra" info to the intercept.
-    $(this).triggerHandler(types, { context: context, args: args })
-
-    return this // for chaining
+  hasHandlers(type) {
+    return (this._handlers && this._handlers[type] && this._handlers[type].length) ||
+      (this._oneHandlers && this._oneHandlers[type] && this._oneHandlers[type].length)
   }
 
+}
 
-  hasHandlers(type) {
-    let hash = ($ as any)._data(this, 'events') // http://blog.jquery.com/2012/08/09/jquery-1-8-released/
+function addToHash(hash, type, handler) {
+  (hash[type] || (hash[type] = []))
+    .push(handler)
+}
 
-    return hash && hash[type] && hash[type].length > 0
+function removeFromHash(hash, type, handler?) {
+  if (handler) {
+    if (hash[type]) {
+      hash[type] = hash[type].filter(function(func) {
+        return func !== handler;
+      })
+    }
+  } else {
+    delete hash[type];
   }
-
 }

+ 8 - 8
src/common/GlobalEmitter.ts

@@ -1,4 +1,3 @@
-import * as $ from 'jquery'
 import * as exportHooks from '../exports'
 import { default as EmitterMixin, EmitterInterface } from './EmitterMixin'
 import { default as ListenerMixin, ListenerInterface } from './ListenerMixin'
@@ -54,17 +53,18 @@ export default class GlobalEmitter {
 
   // called when the object that originally called needed() doesn't need a GlobalEmitter anymore.
   static unneeded() {
-    neededCount--
-
-    if (!neededCount) { // nobody else needs it
-      globalEmitter.unbind()
-      globalEmitter = null
+    if (neededCount > 0) {
+      neededCount--
+      if (!neededCount) { // nobody else needs it
+        globalEmitter.unbind()
+        globalEmitter = null
+      }
     }
   }
 
 
   bind() {
-    this.listenTo($(document), {
+    this.listenTo(document, {
       touchstart: this.handleTouchStart,
       touchcancel: this.handleTouchCancel,
       touchend: this.handleTouchEnd,
@@ -100,7 +100,7 @@ export default class GlobalEmitter {
   }
 
   unbind() {
-    this.stopListeningTo($(document))
+    this.stopListeningTo(document)
 
     window.removeEventListener(
       'touchmove',

+ 29 - 18
src/common/ListenerMixin.ts

@@ -18,14 +18,12 @@ export interface ListenerInterface {
   stopListeningTo(other, eventName?)
 }
 
-let guid = 0
-
 export default class ListenerMixin extends Mixin implements ListenerInterface {
 
-  listenerId: any
+  _listeners: any // array of [ otherObject, eventName, callbackFunc ]
 
   /*
-  Given an `other` object that has on/off methods, bind the given `callback` to an event by the given name.
+  Given an `other` object that has on/off or addEventListener/removeEventListener methods, bind the given `callback` to an event by the given name.
   The `callback` will be called with the `this` context of the object that .listenTo is being called on.
   Can be called:
     .listenTo(other, eventName, callback)
@@ -42,11 +40,17 @@ export default class ListenerMixin extends Mixin implements ListenerInterface {
           this.listenTo(other, eventName, arg[eventName])
         }
       }
-    } else if (typeof arg === 'string') {
-      other.on(
-        arg + '.' + this.getListenerNamespace(), // use event namespacing to identify this object
-        callback.bind(this) // always use `this` context
-      )
+    } else if (typeof arg === 'string' && callback) {
+      callback = callback.bind(this) // always use `this` context)
+
+      if (other.addEventListener) {
+        other.addEventListener(arg, callback)
+      } else {
+        other.on(arg, callback)
+      }
+
+      ;(this._listeners || (this._listeners = []))
+        .push([ other, arg, callback ])
     }
   }
 
@@ -55,17 +59,24 @@ export default class ListenerMixin extends Mixin implements ListenerInterface {
   `eventName` is optional. If omitted, will stop listening to ALL events on `other`.
   */
   stopListeningTo(other, eventName?) {
-    other.off((eventName || '') + '.' + this.getListenerNamespace())
-  }
+    if (this._listeners) {
+      this._listeners = this._listeners.filter(function(listener) {
+        if (
+          listener[0] === other &&
+          (!eventName || eventName === listener[1])
+        ) {
+          if (other.removeEventListener) {
+            other.removeEventListener(listener[1], listener[2])
+          } else {
+            other.off(listener[1], listener[2])
+          }
 
-  /*
-  Returns a string, unique to this object, to be used for event namespacing
-  */
-  getListenerNamespace() {
-    if (this.listenerId == null) {
-      this.listenerId = guid++
+          return false // remove from array
+        } else {
+          return true // keep in array
+        }
+      })
     }
-    return '_listener' + this.listenerId
   }
 
 }

+ 3 - 3
src/common/MouseFollower.ts

@@ -59,9 +59,9 @@ export default class MouseFollower {
       }
 
       if (getEvIsTouch(ev)) {
-        this.listenTo($(document), 'touchmove', this.handleMove)
+        this.listenTo(document, 'touchmove', this.handleMove)
       } else {
-        this.listenTo($(document), 'mousemove', this.handleMove)
+        this.listenTo(document, 'mousemove', this.handleMove)
       }
     }
   }
@@ -86,7 +86,7 @@ export default class MouseFollower {
     if (this.isFollowing && !this.isAnimating) { // disallow more than one stop animation at a time
       this.isFollowing = false
 
-      this.stopListeningTo($(document))
+      this.stopListeningTo(document)
 
       if (shouldRevert && revertDuration && !this.isHidden) { // do a revert animation?
         this.isAnimating = true

+ 2 - 2
src/common/Popover.ts

@@ -78,7 +78,7 @@ export default class Popover {
     })
 
     if (options.autoHide) {
-      this.listenTo($(document), 'mousedown', this.documentMousedown)
+      this.listenTo(document, 'mousedown', this.documentMousedown)
     }
   }
 
@@ -101,7 +101,7 @@ export default class Popover {
       this.el = null
     }
 
-    this.stopListeningTo($(document), 'mousedown')
+    this.stopListeningTo(document, 'mousedown')
   }
 
 

+ 13 - 5
src/component/interactions/ExternalDropping.ts

@@ -17,6 +17,7 @@ export default class ExternalDropping extends Interaction {
   listenTo: ListenerInterface['listenTo']
   stopListeningTo: ListenerInterface['stopListeningTo']
 
+  $document: any
   dragListener: any
   isDragging: boolean = false // jqui-dragging an external element? boolean
 
@@ -39,15 +40,22 @@ export default class ExternalDropping extends Interaction {
 
 
   bindToDocument() {
-    this.listenTo($(document), {
-      dragstart: this.handleDragStart, // jqui
-      sortstart: this.handleDragStart // jqui
-    })
+    if (!this.$document && window['jQuery']) {
+      this.$document = window['jQuery'](document)
+    }
+    if (this.$document) { // need jquery for attaching jqui handlers
+      this.listenTo(this.$document, {
+        dragstart: this.handleDragStart, // jqui
+        sortstart: this.handleDragStart // jqui
+      })
+    }
   }
 
 
   unbindFromDocument() {
-    this.stopListeningTo($(document))
+    if (this.$document) {
+      this.stopListeningTo(this.$document)
+    }
   }
 
 

+ 0 - 49
tests/automated/legacy/emitter.js

@@ -72,53 +72,4 @@ describe('emitter', function() {
     expect(handlers.something2.calls.count()).toBe(1)
   })
 
-  it('unbinds all', function() {
-    var o = new EmitterMixin()
-    var handlers = {
-      something: function() {},
-      another: function() {}
-    }
-
-    spyOn(handlers, 'something')
-    spyOn(handlers, 'another')
-
-    o.on('something', handlers.something)
-    o.on('another', handlers.another)
-
-    o.trigger('something')
-    o.trigger('another')
-    expect(handlers.something).toHaveBeenCalled()
-    expect(handlers.another).toHaveBeenCalled()
-
-    o.off()
-    o.trigger('something')
-    o.trigger('another')
-    expect(handlers.something.calls.count()).toBe(1)
-    expect(handlers.another.calls.count()).toBe(1)
-  })
-
-  it('unbinds with a namespace', function() {
-    var o = new EmitterMixin()
-    var handlers = {
-      something: function() {},
-      another: function() {}
-    }
-
-    spyOn(handlers, 'something')
-    spyOn(handlers, 'another')
-
-    o.on('something', handlers.something)
-    o.on('another.ns1', handlers.another)
-
-    o.trigger('something')
-    o.trigger('another')
-    expect(handlers.something).toHaveBeenCalled()
-    expect(handlers.another).toHaveBeenCalled()
-
-    o.off('.ns1')
-    o.trigger('something')
-    o.trigger('another')
-    expect(handlers.something.calls.count()).toBe(2)
-    expect(handlers.another.calls.count()).toBe(1)
-  })
 })