Method: getOrCreateLinks

Parameters:
el - {DOMElement}

Return:
{Object} Object with zoomIn and zoomOut properties referencing links.
557081
function(el) {
        var zoomIn = document.getElementById(this.zoomInId),
            zoomOut = document.getElementById(this.zoomOutId);
        if (!zoomIn) {
            zoomIn = document.createElement("a");
            zoomIn.href = "#zoomIn";
            zoomIn.appendChild(document.createTextNode(this.zoomInText));
            zoomIn.className = "olControlZoomIn";
            el.appendChild(zoomIn);
        }
        OpenLayers.Element.addClass(zoomIn, "olButton");
        if (!zoomOut) {
            zoomOut = document.createElement("a");
            zoomOut.href = "#zoomOut";
            zoomOut.appendChild(document.createTextNode(this.zoomOutText));
            zoomOut.className = "olControlZoomOut";
            el.appendChild(zoomOut);
        }
        OpenLayers.Element.addClass(zoomOut, "olButton");
        return {
            zoomIn: zoomIn, zoomOut: zoomOut
        };
    }
273184
function (el, attrs) {
            var carousel = this,
                elId     = el,  // save for a rainy day
                parse    = false,
                selected;

            if (!el) {
                YAHOO.log(el + " is neither an HTML element, nor a string",
                        "error", WidgetName);
                return;
            }

            carousel._hasRendered = false;
            carousel._navBtns     = { prev: [], next: [] };
            carousel._pages       = { el: null, num: 0, cur: 0 };
            carousel._pagination  = {};
            carousel._itemAttrCache = {};

            carousel._itemsTable  = { loading: {}, numItems: 0,
                                      items: [], size: 0 };

            YAHOO.log("Component initialization", WidgetName);

            if (JS.isString(el)) {
                el = Dom.get(el);
            } else if (!el.nodeName) {
                YAHOO.log(el + " is neither an HTML element, nor a string",
                        "error", WidgetName);
                return;
            }

            Carousel.superclass.init.call(carousel, el, attrs);

            // check if we're starting somewhere in the middle
            selected = carousel.get("selectedItem");
            if(selected > 0){
                carousel.set("firstVisible",getFirstVisibleForPosition.call(carousel,selected));
            }

            if (el) {
                if (!el.id) {   // in case the HTML element is passed
                    el.setAttribute("id", Dom.generateId());
                }
                parse = carousel._parseCarousel(el);
                if (!parse) {
                    carousel._createCarousel(elId);
                }
            } else {
                el = carousel._createCarousel(elId);
            }
            elId = el.id;

            carousel.initEvents();

            if (parse) {
                carousel._parseCarouselItems();
            }

            // add the selected class
            if(selected > 0){
                setItemSelection.call(carousel,selected,0);
            }

            if (!attrs || typeof attrs.isVertical == "undefined") {
                carousel.set("isVertical", false);
            }

            carousel._parseCarouselNavigation(el);
            carousel._navEl = carousel._setupCarouselNavigation();

            instances[elId] = { object: carousel };
            carousel._loadItems(Math.min(carousel.get("firstVisible")+carousel.get("numVisible"),carousel.get("numItems"))-1);
        }
620818
function _index(/**HTMLElement*/el) {
		var index = 0;
		while (el && (el = el.previousElementSibling)) {
			if (el.nodeName.toUpperCase() !== 'TEMPLATE') {
				index++;
			}
		}
		return index;
	}
720377
function createChildren(el, attr) {
	var children = [];
	for (var i = 0; i < el.childNodes.length; i++) {
		children.push(createNode(el.childNodes[i], attr));
	};

	return children;
}
21701
function(href) {
        var node = this.createElementDefaultNS("OnlineResource");
        this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple");
        this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href);
        return node;
    }
394332
def createElementNS(self, namespace, qname):
        '''
        
        '''
        document = self._getOwnerDocument()
        node = document.createElementNS(namespace, qname)
        return ElementProxy(self.sw, node)
78068
function(el) {
		return (el instanceof $ ? el : $(el)).find(this.makeSelector.apply(this, [].slice.call(arguments, 1)));
	}
899653
function($el) {

        var el = joint.util.isString($el)
            ? this.viewport.querySelector($el)
            : $el instanceof $ ? $el[0] : $el;

        var id = this.findAttribute('model-id', el);
        if (id) return this._views[id];

        return undefined;
    }
1006846
function (el) {

    return /** @lends dom **/ {

        /**
         * Adds class to element.
         * @param {string} className
         */
        addClass: function (className) {
            if (el.classList) {
                el.classList.add(className);
            } else {
                el.className += ' ' + className;
            }
        },

        /**
         * Removes class from element.
         * @param {string} className
         */
        removeClass: function (className) {
            if (el.classList) {
                el.classList.remove(className);
            } else {
                el.className = el.className.replace(
                    new RegExp('(^|\\b)' + className + '(\\b|$)', 'gi'), ' '
                );
            }
        },

        /**
         * Prepends child nodes to base element.
         * @param {Node[]} nodesToPrepend
         */
        prepend: function (nodesToPrepend) {
            let nodes = Array.prototype.slice.call(nodesToPrepend),
                i = nodes.length;

            while (i--) {
                el.insertBefore(nodes[i], el.firstChild);
            }
        },

        /**
         * Appends child nodes to base element.
         * @param {Node[]} nodesToAppend
         */
        append: function (nodesToAppend) {
            let nodes = Array.prototype.slice.call(nodesToAppend);

            for (let i = 0, len = nodes.length; i < len; ++i) {
                el.appendChild(nodes[i]);
            }
        },

        /**
         * Inserts base element after refEl.
         * @param {Node} refEl - node after which base element will be inserted
         * @returns {Node} - inserted element
         */
        insertAfter: function (refEl) {
            return refEl.parentNode.insertBefore(el, refEl.nextSibling);
        },

        /**
         * Inserts base element before refEl.
         * @param {Node} refEl - node before which base element will be inserted
         * @returns {Node} - inserted element
         */
        insertBefore: function (refEl) {
            return refEl.parentNode.insertBefore(el, refEl);
        },

        /**
         * Removes base element from DOM.
         */
        remove: function () {
            el.parentNode.removeChild(el);
            el = null;
        },

        /**
         * Returns true if base element contains given child.
         * @param {Node|HTMLElement} child
         * @returns {boolean}
         */
        contains: function (child) {
            return el !== child && el.contains(child);
        },

        /**
         * Wraps base element in wrapper element.
         * @param {HTMLElement} wrapper
         * @returns {HTMLElement} wrapper element
         */
        wrap: function (wrapper) {
            if (el.parentNode) {
                el.parentNode.insertBefore(wrapper, el);
            }

            wrapper.appendChild(el);
            return wrapper;
        },

        /**
         * Unwraps base element.
         * @returns {Node[]} - child nodes of unwrapped element.
         */
        unwrap: function () {
            let nodes = Array.prototype.slice.call(el.childNodes),
                wrapper;

            nodes.forEach(function (node) {
                wrapper = node.parentNode;
                dom(node).insertBefore(node.parentNode);
                dom(wrapper).remove();
            });

            return nodes;
        },

        /**
         * Returns array of base element parents.
         * @returns {HTMLElement[]}
         */
        parents: function () {
            let parent, path = [];

            while (!!(parent = el.parentNode)) {
                path.push(parent);
                el = parent;
            }

            return path;
        },

        /**
         * Normalizes text nodes within base element, ie. merges sibling text nodes and assures that every
         * element node has only one text node.
         * It should does the same as standard element.normalize, but IE implements it incorrectly.
         */
        normalizeTextNodes: function () {
            if (!el) {
                return;
            }

            if (el.nodeType === NODE_TYPE.TEXT_NODE) {
                while (el.nextSibling && el.nextSibling.nodeType === NODE_TYPE.TEXT_NODE) {
                    el.nodeValue += el.nextSibling.nodeValue;
                    el.parentNode.removeChild(el.nextSibling);
                }
            } else {
                dom(el.firstChild).normalizeTextNodes();
            }
            dom(el.nextSibling).normalizeTextNodes();
        },

        /**
         * Returns element background color.
         * @returns {CSSStyleDeclaration.backgroundColor}
         */
        color: function () {
            return el.style.backgroundColor;
        },

        /**
         * Creates dom element from given html string.
         * @param {string} html
         * @returns {NodeList}
         */
        fromHTML: function (html) {
            let div = document.createElement('div');
            div.innerHTML = html;
            return div.childNodes;
        },

        /**
         * Returns first range of the window of base element.
         * @returns {Range}
         */
        getRange: function () {
            let selection = dom(el).getSelection(),
                range;

            if (selection.rangeCount > 0) {
                range = selection.getRangeAt(0);
            }

            return range;
        },

        /**
         * Removes all ranges of the window of base element.
         */
        removeAllRanges: function () {
            let selection = dom(el).getSelection();
            selection.removeAllRanges();
        },

        /**
         * Returns selection object of the window of base element.
         * @returns {Selection}
         */
        getSelection: function () {
            return dom(el).getWindow().getSelection();
        },

        /**
         * Returns window of the base element.
         * @returns {Window}
         */
        getWindow: function () {
            return dom(el).getDocument().defaultView;
        },

        /**
         * Returns document of the base element.
         * @returns {HTMLDocument}
         */
        getDocument: function () {
            // if ownerDocument is null then el is the document itself.
            return el.ownerDocument || el;
        }

    };
}
580476
function link(scope, element, attrs) {
      scope.showProp = showProp;
      scope.display = ModelDefinitions.display;

      function showProp(propDef) {
        return !scope.narrowMode || propDef.displayPriority !== 'low';
      }
    }
640567
def clone_bs4_elem(el):
    
    if isinstance(el, NavigableString):
        return type(el)(el)

    copy = Tag(None, el.builder, el.name, el.namespace, el.nsprefix)
    # work around bug where there is no builder set
    # https://bugs.launchpad.net/beautifulsoup/+bug/1307471
    copy.attrs = dict(el.attrs)
    for attr in ('can_be_empty_element', 'hidden'):
        setattr(copy, attr, getattr(el, attr))
    for child in el.contents:
        copy.append(clone_bs4_elem(child))
    return copy
1106821
function( el ) {
			var j = Syn.jquery()(el),
				o = j.offset();
			return {
				pageX: o.left + (j.width() / 2),
				pageY: o.top + (j.height() / 2)
			}
		}
517397
def element_creator(namespace=None):
    
    ELEMENT_MAKER = _objectify.ElementMaker(namespace=namespace,
                                            annotate=False)

    def create_elem(tag, attr=None, text=None):
        """:class:`objectify.Element` wrapper with namespace defined.

        Args:
            tag (str): Tag name
            attr (dict): Default attributes for tag
            text (str): Text content for the tag

        Returns:
            _objectify.ObjectifiedElement: objectify element
        """
        if not attr:
            attr = {}
        if text:
            element = getattr(ELEMENT_MAKER, tag)(text, **attr)
        else:
            element = getattr(ELEMENT_MAKER, tag)(**attr)
        return element

    return create_elem
608058
def iter_links_object_element(cls, element):
        '''
        '''
        base_link = element.attrib.get('codebase', None)

        if base_link:
            # lxml returns codebase as inline
            link_type = element.attrib.get(base_link)
            yield LinkInfo(
                element=element, tag=element.tag, attrib='codebase',
                link=base_link,
                inline=True, linked=False,
                base_link=None,
                value_type='plain',
                link_type=link_type
            )

        for attribute in ('code', 'src', 'classid', 'data'):
            if attribute in element.attrib:
                link_type = identify_link_type(element.attrib.get(attribute))
                yield LinkInfo(
                    element=element, tag=element.tag, attrib=attribute,
                    link=element.attrib.get(attribute),
                    inline=True, linked=False,
                    base_link=base_link,
                    value_type='plain',
                    link_type=link_type
                )

        if 'archive' in element.attrib:
            for match in re.finditer(r'[^ ]+', element.attrib.get('archive')):
                value = match.group(0)
                link_type = identify_link_type(value)
                yield LinkInfo(
                    element=element, tag=element.tag, attrib='archive',
                    link=value,
                    inline=True, linked=False,
                    base_link=base_link,
                    value_type='list',
                    link_type=link_type
                )
456182
function() {
        var root = this.oDomContainer,
            cal = this.parent || this,
            anchor = "a",
            click = "click";

        var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root),
            linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);

        if (linkLeft && linkLeft.length > 0) {
            this.linkLeft = linkLeft[0];
            Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true);
        }

        if (linkRight && linkRight.length > 0) {
            this.linkRight = linkRight[0];
            Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true);
        }

        if (cal.cfg.getProperty("navigator") !== null) {
            this.applyNavListeners();
        }

        if (this.domEventMap) {
            var el,elements;
            for (var cls in this.domEventMap) { 
                if (Lang.hasOwnProperty(this.domEventMap, cls)) {
                    var items = this.domEventMap[cls];
    
                    if (! (items instanceof Array)) {
                        items = [items];
                    }
    
                    for (var i=0;i<items.length;i++) {
                        var item = items[i];
                        elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
    
                        for (var c=0;c<elements.length;c++) {
                            el = elements[c];
                             Event.addListener(el, item.event, item.handler, item.scope, item.correct );
                        }
                    }
                }
            }
        }

        Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
        Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
        Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
    }
143271
function () {

        var oElement,
            oAnchor;

        if (!m_oMenuItemTemplate) {

            m_oMenuItemTemplate = document.createElement(_LI_LOWERCASE);
            m_oMenuItemTemplate.innerHTML = _ANCHOR_TEMPLATE;

        }

        oElement = m_oMenuItemTemplate.cloneNode(true);
        oElement.className = this.CSS_CLASS_NAME;

        oAnchor = oElement.firstChild;
        oAnchor.className = this.CSS_LABEL_CLASS_NAME;

        this.element = oElement;
        this._oAnchor = oAnchor;

    }
264802
function createPanZoom(domElement, options) {
  options = options || {}

  var panController = options.controller

  if (!panController) {
    if (domElement instanceof SVGElement) {
      panController = makeSvgController(domElement, options)
    }

    if (domElement instanceof HTMLElement) {
      panController = makeDomController(domElement, options)
    }
  }

  if (!panController) {
    throw new Error('Cannot create panzoom for the current type of dom element')
  }
  var owner = panController.getOwner()
  // just to avoid GC pressure, every time we do intermediate transform
  // we return this object. For internal use only. Never give it back to the consumer of this library
  var storedCTMResult = {x: 0, y: 0}

  var isDirty = false
  var transform = new Transform()

  if (panController.initTransform) {
    panController.initTransform(transform)
  }

  var filterKey = typeof options.filterKey === 'function' ? options.filterKey : noop;
  // TODO: likely need to unite pinchSpeed with zoomSpeed
  var pinchSpeed = typeof options.pinchSpeed === 'number' ? options.pinchSpeed : 1;
  var bounds = options.bounds
  var maxZoom = typeof options.maxZoom === 'number' ? options.maxZoom : Number.POSITIVE_INFINITY
  var minZoom = typeof options.minZoom === 'number' ? options.minZoom : 0

  var boundsPadding = typeof options.boundsPadding === 'number' ? options.boundsPadding : 0.05
  var zoomDoubleClickSpeed = typeof options.zoomDoubleClickSpeed === 'number' ? options.zoomDoubleClickSpeed : defaultDoubleTapZoomSpeed
  var beforeWheel = options.beforeWheel || noop
  var speed = typeof options.zoomSpeed === 'number' ? options.zoomSpeed : defaultZoomSpeed

  validateBounds(bounds)

  if (options.autocenter) {
    autocenter()
  }

  var frameAnimation

  var lastTouchEndTime = 0

  var touchInProgress = false

  // We only need to fire panstart when actual move happens
  var panstartFired = false

  // cache mouse coordinates here
  var mouseX
  var mouseY

  var pinchZoomLength

  var smoothScroll
  if ('smoothScroll' in options && !options.smoothScroll) {
    // If user explicitly asked us not to use smooth scrolling, we obey
    smoothScroll = rigidScroll()
  } else {
    // otherwise we use forward smoothScroll settings to kinetic API
    // which makes scroll smoothing.
    smoothScroll = kinetic(getPoint, scroll, options.smoothScroll)
  }

  var moveByAnimation
  var zoomToAnimation

  var multiTouch
  var paused = false

  listenForEvents()

  var api = {
    dispose: dispose,
    moveBy: internalMoveBy,
    moveTo: moveTo,
    centerOn: centerOn,
    zoomTo: publicZoomTo,
    zoomAbs: zoomAbs,
    smoothZoom: smoothZoom,
    getTransform: getTransformModel,
    showRectangle: showRectangle,

    pause: pause,
    resume: resume,
    isPaused: isPaused,
  }

  eventify(api);

  return api;

  function pause() {
    releaseEvents()
    paused = true
  }

  function resume() {
    if (paused) {
      listenForEvents()
      paused = false
    }
  }

  function isPaused() {
    return paused;
  }

  function showRectangle(rect) {
    // TODO: this duplicates autocenter. I think autocenter should go.
    var clientRect = owner.getBoundingClientRect()
    var size = transformToScreen(clientRect.width, clientRect.height)

    var rectWidth = rect.right - rect.left
    var rectHeight = rect.bottom - rect.top
    if (!Number.isFinite(rectWidth) || !Number.isFinite(rectHeight)) {
      throw new Error('Invalid rectangle');
    }

    var dw = size.x/rectWidth
    var dh = size.y/rectHeight
    var scale = Math.min(dw, dh)
    transform.x = -(rect.left + rectWidth/2) * scale + size.x/2
    transform.y = -(rect.top + rectHeight/2) * scale + size.y/2
    transform.scale = scale
  }

  function transformToScreen(x, y) {
    if (panController.getScreenCTM) {
      var parentCTM = panController.getScreenCTM()
      var parentScaleX = parentCTM.a
      var parentScaleY = parentCTM.d
      var parentOffsetX = parentCTM.e
      var parentOffsetY = parentCTM.f
      storedCTMResult.x = x * parentScaleX - parentOffsetX
      storedCTMResult.y = y * parentScaleY - parentOffsetY
    } else {
      storedCTMResult.x = x
      storedCTMResult.y = y
    }

    return storedCTMResult
  }

  function autocenter() {
    var w // width of the parent
    var h // height of the parent
    var left = 0
    var top = 0
    var sceneBoundingBox = getBoundingBox()
    if (sceneBoundingBox) {
      // If we have bounding box - use it.
      left = sceneBoundingBox.left
      top = sceneBoundingBox.top
      w = sceneBoundingBox.right - sceneBoundingBox.left
      h = sceneBoundingBox.bottom - sceneBoundingBox.top
    } else {
      // otherwise just use whatever space we have
      var ownerRect = owner.getBoundingClientRect();
      w = ownerRect.width
      h = ownerRect.height
    }
    var bbox = panController.getBBox()
    if (bbox.width === 0 || bbox.height === 0) {
      // we probably do not have any elements in the SVG
      // just bail out;
      return;
    }
    var dh = h/bbox.height
    var dw = w/bbox.width
    var scale = Math.min(dw, dh)
    transform.x = -(bbox.left + bbox.width/2) * scale + w/2 + left
    transform.y = -(bbox.top + bbox.height/2) * scale + h/2 + top
    transform.scale = scale
  }

  function getTransformModel() {
    // TODO: should this be read only?
    return transform
  }

  function getPoint() {
    return {
      x: transform.x,
      y: transform.y
    }
  }

  function moveTo(x, y) {
    transform.x = x
    transform.y = y

    keepTransformInsideBounds()

    triggerEvent('pan')
    makeDirty()
  }

  function moveBy(dx, dy) {
    moveTo(transform.x + dx, transform.y + dy)
  }

  function keepTransformInsideBounds() {
    var boundingBox = getBoundingBox()
    if (!boundingBox) return

    var adjusted = false
    var clientRect = getClientRect()

    var diff = boundingBox.left - clientRect.right
    if (diff > 0) {
      transform.x += diff
      adjusted = true
    }
    // check the other side:
    diff = boundingBox.right - clientRect.left
    if (diff < 0) {
      transform.x += diff
      adjusted = true
    }

    // y axis:
    diff = boundingBox.top - clientRect.bottom
    if (diff > 0) {
      // we adjust transform, so that it matches exactly our bounding box:
      // transform.y = boundingBox.top - (boundingBox.height + boundingBox.y) * transform.scale =>
      // transform.y = boundingBox.top - (clientRect.bottom - transform.y) =>
      // transform.y = diff + transform.y =>
      transform.y += diff
      adjusted = true
    }

    diff = boundingBox.bottom - clientRect.top
    if (diff < 0) {
      transform.y += diff
      adjusted = true
    }
    return adjusted
  }

  /**
   * Returns bounding box that should be used to restrict scene movement.
   */
  function getBoundingBox() {
    if (!bounds) return // client does not want to restrict movement

    if (typeof bounds === 'boolean') {
      // for boolean type we use parent container bounds
      var ownerRect = owner.getBoundingClientRect()
      var sceneWidth = ownerRect.width
      var sceneHeight = ownerRect.height

      return {
        left: sceneWidth * boundsPadding,
        top: sceneHeight * boundsPadding,
        right: sceneWidth * (1 - boundsPadding),
        bottom: sceneHeight * (1 - boundsPadding),
      }
    }

    return bounds
  }

  function getClientRect() {
    var bbox = panController.getBBox()
    var leftTop = client(bbox.left, bbox.top)

    return {
      left: leftTop.x,
      top: leftTop.y,
      right: bbox.width * transform.scale + leftTop.x,
      bottom: bbox.height * transform.scale + leftTop.y
    }
  }

  function client(x, y) {
    return {
      x: (x * transform.scale) + transform.x,
      y: (y * transform.scale) + transform.y
    }
  }

  function makeDirty() {
    isDirty = true
    frameAnimation = window.requestAnimationFrame(frame)
  }

  function zoomByRatio(clientX, clientY, ratio) {
    if (isNaN(clientX) || isNaN(clientY) || isNaN(ratio)) {
      throw new Error('zoom requires valid numbers')
    }

    var newScale = transform.scale * ratio

    if (newScale < minZoom) {
      if (transform.scale === minZoom) return;

      ratio = minZoom / transform.scale
    }
    if (newScale > maxZoom) {
      if (transform.scale === maxZoom) return;

      ratio = maxZoom / transform.scale
    }

    var size = transformToScreen(clientX, clientY)

    transform.x = size.x - ratio * (size.x - transform.x)
    transform.y = size.y - ratio * (size.y - transform.y)

    var transformAdjusted = keepTransformInsideBounds()
    if (!transformAdjusted) transform.scale *= ratio

    triggerEvent('zoom')

    makeDirty()
  }

  function zoomAbs(clientX, clientY, zoomLevel) {
    var ratio = zoomLevel / transform.scale
    zoomByRatio(clientX, clientY, ratio)
  }

  function centerOn(ui) {
    var parent = ui.ownerSVGElement
    if (!parent) throw new Error('ui element is required to be within the scene')

    // TODO: should i use controller's screen CTM?
    var clientRect = ui.getBoundingClientRect()
    var cx = clientRect.left + clientRect.width/2
    var cy = clientRect.top + clientRect.height/2

    var container = parent.getBoundingClientRect()
    var dx = container.width/2 - cx
    var dy = container.height/2 - cy

    internalMoveBy(dx, dy, true)
  }

  function internalMoveBy(dx, dy, smooth) {
    if (!smooth) {
      return moveBy(dx, dy)
    }

    if (moveByAnimation) moveByAnimation.cancel()

    var from = { x: 0, y: 0 }
    var to = { x: dx, y : dy }
    var lastX = 0
    var lastY = 0

    moveByAnimation = animate(from, to, {
      step: function(v) {
        moveBy(v.x - lastX, v.y - lastY)

        lastX = v.x
        lastY = v.y
      }
    })
  }

  function scroll(x, y) {
    cancelZoomAnimation()
    moveTo(x, y)
  }

  function dispose() {
    releaseEvents();
  }

  function listenForEvents() {
    owner.addEventListener('mousedown', onMouseDown)
    owner.addEventListener('dblclick', onDoubleClick)
    owner.addEventListener('touchstart', onTouch)
    owner.addEventListener('keydown', onKeyDown)

    // Need to listen on the owner container, so that we are not limited
    // by the size of the scrollable domElement
    wheel.addWheelListener(owner, onMouseWheel)

    makeDirty()
  }

  function releaseEvents() {
    wheel.removeWheelListener(owner, onMouseWheel)
    owner.removeEventListener('mousedown', onMouseDown)
    owner.removeEventListener('keydown', onKeyDown)
    owner.removeEventListener('dblclick', onDoubleClick)
    owner.removeEventListener('touchstart', onTouch)

    if (frameAnimation) {
      window.cancelAnimationFrame(frameAnimation)
      frameAnimation = 0
    }

    smoothScroll.cancel()

    releaseDocumentMouse()
    releaseTouches()

    triggerPanEnd()
  }


  function frame() {
    if (isDirty) applyTransform()
  }

  function applyTransform() {
    isDirty = false

    // TODO: Should I allow to cancel this?
    panController.applyTransform(transform)

    triggerEvent('transform')
    frameAnimation = 0
  }

  function onKeyDown(e) {
    var x = 0, y = 0, z = 0
    if (e.keyCode === 38) {
      y = 1 // up
    } else if (e.keyCode === 40) {
      y = -1 // down
    } else if (e.keyCode === 37) {
      x = 1 // left
    } else if (e.keyCode === 39) {
      x = -1 // right
    } else if (e.keyCode === 189 || e.keyCode === 109) { // DASH or SUBTRACT
      z = 1 // `-` -  zoom out
    } else if (e.keyCode === 187 || e.keyCode === 107) { // EQUAL SIGN or ADD
      z = -1 // `=` - zoom in (equal sign on US layout is under `+`)
    }

    if (filterKey(e, x, y, z)) {
      // They don't want us to handle the key: https://github.com/anvaka/panzoom/issues/45
      return;
    }

    if (x || y) {
      e.preventDefault()
      e.stopPropagation()

      var clientRect = owner.getBoundingClientRect()
      // movement speed should be the same in both X and Y direction:
      var offset = Math.min(clientRect.width, clientRect.height)
      var moveSpeedRatio = 0.05
      var dx = offset * moveSpeedRatio * x
      var dy = offset * moveSpeedRatio * y

      // TODO: currently we do not animate this. It could be better to have animation
      internalMoveBy(dx, dy)
    }

    if (z) {
      var scaleMultiplier = getScaleMultiplier(z)
      var ownerRect = owner.getBoundingClientRect()
      publicZoomTo(ownerRect.width/2, ownerRect.height/2, scaleMultiplier)
    }
  }

  function onTouch(e) {
    // let the override the touch behavior
    beforeTouch(e);

    if (e.touches.length === 1) {
      return handleSingleFingerTouch(e, e.touches[0])
    } else if (e.touches.length === 2) {
      // handleTouchMove() will care about pinch zoom.
      pinchZoomLength = getPinchZoomLength(e.touches[0], e.touches[1])
      multiTouch  = true
      startTouchListenerIfNeeded()
    }
  }

  function beforeTouch(e) {
    if (options.onTouch && !options.onTouch(e)) {
      // if they return `false` from onTouch, we don't want to stop
      // events propagation. Fixes https://github.com/anvaka/panzoom/issues/12
      return
    }

    e.stopPropagation()
    e.preventDefault()
  }

  function beforeDoubleClick(e) {
    if (options.onDoubleClick && !options.onDoubleClick(e)) {
      // if they return `false` from onTouch, we don't want to stop
      // events propagation. Fixes https://github.com/anvaka/panzoom/issues/46
      return
    }

    e.preventDefault()
    e.stopPropagation()
  }

  function handleSingleFingerTouch(e) {
    var touch = e.touches[0]
    var offset = getOffsetXY(touch)
    mouseX = offset.x
    mouseY = offset.y

    smoothScroll.cancel()
    startTouchListenerIfNeeded()
  }

  function startTouchListenerIfNeeded() {
    if (!touchInProgress) {
      touchInProgress = true
      document.addEventListener('touchmove', handleTouchMove)
      document.addEventListener('touchend', handleTouchEnd)
      document.addEventListener('touchcancel', handleTouchEnd)
    }
  }

  function handleTouchMove(e) {
    if (e.touches.length === 1) {
      e.stopPropagation()
      var touch = e.touches[0]

      var offset = getOffsetXY(touch)

      var dx = offset.x - mouseX
      var dy = offset.y - mouseY

      if (dx !== 0 && dy !== 0) {
        triggerPanStart()
      }
      mouseX = offset.x
      mouseY = offset.y
      var point = transformToScreen(dx, dy)
      internalMoveBy(point.x, point.y)
    } else if (e.touches.length === 2) {
      // it's a zoom, let's find direction
      multiTouch = true
      var t1 = e.touches[0]
      var t2 = e.touches[1]
      var currentPinchLength = getPinchZoomLength(t1, t2)

      // since the zoom speed is always based on distance from 1, we need to apply
      // pinch speed only on that distance from 1:
      var scaleMultiplier = 1 + (currentPinchLength / pinchZoomLength - 1) * pinchSpeed

      mouseX = (t1.clientX + t2.clientX)/2
      mouseY = (t1.clientY + t2.clientY)/2

      publicZoomTo(mouseX, mouseY, scaleMultiplier)

      pinchZoomLength = currentPinchLength
      e.stopPropagation()
      e.preventDefault()
    }
  }

  function handleTouchEnd(e) {
    if (e.touches.length > 0) {
      var offset = getOffsetXY(e.touches[0])
      mouseX = offset.x
      mouseY = offset.y
    } else {
      var now = new Date()
      if (now - lastTouchEndTime < doubleTapSpeedInMS) {
        smoothZoom(mouseX, mouseY, zoomDoubleClickSpeed)
      }

      lastTouchEndTime = now

      touchInProgress = false
      triggerPanEnd()
      releaseTouches()
    }
  }

  function getPinchZoomLength(finger1, finger2) {
    var dx = finger1.clientX - finger2.clientX
    var dy = finger1.clientY - finger2.clientY
    return Math.sqrt(dx * dx + dy * dy)
  }

  function onDoubleClick(e) {
    beforeDoubleClick(e);
    var offset = getOffsetXY(e)
    smoothZoom(offset.x, offset.y, zoomDoubleClickSpeed)
  }

  function onMouseDown(e) {
    if (touchInProgress) {
      // modern browsers will fire mousedown for touch events too
      // we do not want this: touch is handled separately.
      e.stopPropagation()
      return false
    }
    // for IE, left click == 1
    // for Firefox, left click == 0
    var isLeftButton = ((e.button === 1 && window.event !== null) || e.button === 0)
    if (!isLeftButton) return

    smoothScroll.cancel()

    var offset = getOffsetXY(e);
    var point = transformToScreen(offset.x, offset.y)
    mouseX = point.x
    mouseY = point.y

    // We need to listen on document itself, since mouse can go outside of the
    // window, and we will loose it
    document.addEventListener('mousemove', onMouseMove)
    document.addEventListener('mouseup', onMouseUp)

    preventTextSelection.capture(e.target || e.srcElement)

    return false
  }

  function onMouseMove(e) {
    // no need to worry about mouse events when touch is happening
    if (touchInProgress) return

    triggerPanStart()

    var offset = getOffsetXY(e);
    var point = transformToScreen(offset.x, offset.y)
    var dx = point.x - mouseX
    var dy = point.y - mouseY

    mouseX = point.x
    mouseY = point.y

    internalMoveBy(dx, dy)
  }

  function onMouseUp() {
    preventTextSelection.release()
    triggerPanEnd()
    releaseDocumentMouse()
  }

  function releaseDocumentMouse() {
    document.removeEventListener('mousemove', onMouseMove)
    document.removeEventListener('mouseup', onMouseUp)
    panstartFired = false
  }

  function releaseTouches() {
    document.removeEventListener('touchmove', handleTouchMove)
    document.removeEventListener('touchend', handleTouchEnd)
    document.removeEventListener('touchcancel', handleTouchEnd)
    panstartFired = false
    multiTouch = false
  }

  function onMouseWheel(e) {
    // if client does not want to handle this event - just ignore the call
    if (beforeWheel(e)) return

    smoothScroll.cancel()

    var scaleMultiplier = getScaleMultiplier(e.deltaY)

    if (scaleMultiplier !== 1) {
      var offset = getOffsetXY(e)
      publicZoomTo(offset.x, offset.y, scaleMultiplier)
      e.preventDefault()
    }
  }

  function getOffsetXY(e) {
    var offsetX, offsetY;
    // I tried using e.offsetX, but that gives wrong results for svg, when user clicks on a path.
    var ownerRect = owner.getBoundingClientRect();
    offsetX = e.clientX - ownerRect.left
    offsetY = e.clientY - ownerRect.top

    return {x: offsetX, y: offsetY};
  }

  function smoothZoom(clientX, clientY, scaleMultiplier) {
      var fromValue = transform.scale
      var from = {scale: fromValue}
      var to = {scale: scaleMultiplier * fromValue}

      smoothScroll.cancel()
      cancelZoomAnimation()

      zoomToAnimation = animate(from, to, {
        step: function(v) {
          zoomAbs(clientX, clientY, v.scale)
        }
      })
  }

  function publicZoomTo(clientX, clientY, scaleMultiplier) {
      smoothScroll.cancel()
      cancelZoomAnimation()
      return zoomByRatio(clientX, clientY, scaleMultiplier)
  }

  function cancelZoomAnimation() {
      if (zoomToAnimation) {
          zoomToAnimation.cancel()
          zoomToAnimation = null
      }
  }

  function getScaleMultiplier(delta) {
    var scaleMultiplier = 1
    if (delta > 0) { // zoom out
      scaleMultiplier = (1 - speed)
    } else if (delta < 0) { // zoom in
      scaleMultiplier = (1 + speed)
    }

    return scaleMultiplier
  }

  function triggerPanStart() {
    if (!panstartFired) {
      triggerEvent('panstart')
      panstartFired = true
      smoothScroll.start()
    }
  }

  function triggerPanEnd() {
    if (panstartFired) {
      // we should never run smooth scrolling if it was multiTouch (pinch zoom animation):
      if (!multiTouch) smoothScroll.stop()
      triggerEvent('panend')
    }
  }

  function triggerEvent(name) {
    api.fire(name, api);
  }
}