Source: common/util/dom_utils.js

/**
 * Internal utilities for working with the DOM.
 * @name DOMUtils
 * @namespace
 * @private
 */
define(["lib/jquery", "lib/underscore"], function($, _) {
    "use strict";

    var DOMUtils = /** @lends DOMUtils */ {
        /**
         * Fires a custom event.
         * @param  {String}  name  Name of the event.
         * @param  {Element} el    DOM element to fire the event on.
         * @param  {Object}  props Additional properties for the event.
         *
         * @return {Boolean} Returns false if stopPropagation was called on the
         *                   event, true otherwise.
         */
        fire: function(name, el, props) {
            var ev = DOMUtils.buildEvent(name, false, props);
            el.dispatchEvent(ev);

            return !ev._propStopped;
        },

        /**
         * Builds a custom event.
         * @param  {String}  name    Name of the event.
         * @param  {Boolean} bubble  Whether the event bubbles.
         * @param  {Object}  [props] Additional properties for the event.
         */
        buildEvent: function(name, bubble, props) {
            var evObj = document.createEvent("CustomEvent");
            evObj.initCustomEvent(name, bubble, bubble);

            if(props) {
                _.extend(evObj, props);
            }

            evObj.stopPropagation = function() {
                evObj._propStopped = true;
            };

            evObj.preventDefault = function() {
                evObj._defaultPrevented = true;
            };

            return evObj;
        },

        /**
         * Fires a custom event that bubbles up the DOM.
         * @param  {String}  name  Name of the event.
         * @param  {Element} el    DOM element to fire the event on.
         * @param  {Object}  props Additional properties for the event.
         *
         * @return {Boolean} Returns false if preventDefault was called on the
         *                   event, true otherwise.
         */
        fireBubbling: function(name, el, props) {
            var ev = DOMUtils.buildEvent(name, true, props);
            el.dispatchEvent(ev);

            return !ev._defaultPrevented;
        },

        /**
         * Adds a link to a stylesheet into the head of a document.
         * 
         * @param {String} path Path to the stylesheet, relative to the Lens
         *                      root.
         * @param {String} [id] Optional id to apply to the link tag.
         */
        addStylesheet: function(path, id) {
            var link = $("<link />", {
                rel:  "stylesheet",
                type: "text/css",
                href: Lens.rootUrl + "/" + path,
                id:   id,
            });

            $("head").append(link);
        },

        /**
         * Unloads a stylesheet.
         * 
         * @param  {String} path Path to the stylesheet, relative to the Lens root.
         */
        removeStylesheet: function(path) {
            $("link[href=\"" + Lens.rootUrl + "/" + path + "\"]").remove();
        },

        /**
         * Returns the topmost element at (x, y) and all of its parents. The
         * most specific element will be the first item in the array, and the
         * document element will be the last item.
         */
        elementsAt: function(x, y) {
            var el = document.elementFromPoint(x, y);
            if(!el) {
                return [];
            }

            var els = [el];
            while(el.parentNode) {
                el = el.parentNode;
                els.push(el);
            }
            return els;
        }
    };

    return DOMUtils;
});