!function($) {
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
  var Observer = function(target, selector, onAdded, onRemoved) {
    var self    = this
    this.target = target
    
    var childsOnly = selector[0] === '>'
      , search = childsOnly ? selector.substr(1) : selector
      
    function apply(nodes, callback) {
      Array.prototype.slice.call(nodes).forEach(function(node) {
        if (childsOnly && self.target[0] !== $(node).parent()[0]) return
        if ($(node).is(search)) callback.call(node)
        if (childsOnly) return
        $(selector, node).each(function() {
          callback.call(this)
        })
      })
    }
    
    this.observer = new MutationObserver(function(mutations) {
      self.disconnect()
      
      mutations.forEach(function(mutation) {
        if (onAdded)   apply(mutation.addedNodes,   onAdded)
        if (onRemoved) apply(mutation.removedNodes, onRemoved)
      })
  
      self.observe()
    })
    
    // call onAdded for existing elements
    $(selector, target).each(function() {
      onAdded.call(this)
    })
    
    this.observe()
  }
  
  Observer.prototype.disconnect = function() {
    this.observer.disconnect()
  }
  
  Observer.prototype.observe = function() {
    var self = this
    this.target.forEach(function(target) {
      self.observer.observe(target, { childList: true, subtree: true })
    })
  }
  
  $.fn.observe = function(selector, onAdded, onRemoved) {
    if (!this.length) return
    var contracts = this.data('contracts')
    if (!contracts) contracts = []
    var contract = contracts.filter(function(c) { return c.selector === selector })
    if (contract.length) {
      contract[0].onAdded   = onAdded
      contract[0].onRemoved = onRemoved
      return
    }
    var target = 'makeArray' in $ ? $.makeArray(this) : this
    var observer = new Observer(target, selector, onAdded, onRemoved)
    contracts.push(observer)
    this.data('contracts', contracts)
    return this
  }
}(window.Zepto || window.jQuery)

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
function matches(el, selector) {
  return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector)
}
function toArr(nodeList) {
  return Array.prototype.slice.call(nodeList)
}

var SelectorObserver = function(targets, selector, onAdded, onRemoved) {
  var self     = this
  this.targets = targets instanceof NodeList
                   ? Array.prototype.slice.call(targets)
                   : [targets]

  // support selectors starting with the childs only selector `>`
  var childsOnly = selector[0] === '>'
    , search = childsOnly ? selector.substr(1) : selector

  function apply(nodes, callback) {
    toArr(nodes).forEach(function(node) {
      // if looking for childs only, the node's parentNode
      // should be one of our targets
      if (childsOnly && self.targets.indexOf(node.parentNode) === -1) {
        return
      }

      // test if the node itself matches the selector
      if (matches(node, search)) {
        callback.call(node)
      }

      if (childsOnly) return

      toArr(node.querySelectorAll(selector)).forEach(function(node) {
        callback.call(node)
      })
    })
  }

  this.observer = new MutationObserver(function(mutations) {
    self.disconnect()

    mutations.forEach(function(mutation) {
      if (onAdded)   apply(mutation.addedNodes,   onAdded)
      if (onRemoved) apply(mutation.removedNodes, onRemoved)
    })

    self.observe()
  })

  // call onAdded for existing elements
  if (onAdded) apply(this.targets, onAdded)

  this.observe()
}

SelectorObserver.prototype.disconnect = function() {
  this.observer.disconnect()
}

SelectorObserver.prototype.observe = function() {
  var self = this
  this.targets.forEach(function(target) {
    self.observer.observe(target, { childList: true, subtree: true })
  })
}

if (typeof exports !== 'undefined') {
  module.exports = SelectorObserver
}

remove jQuery/Zepto dependency