<commit_before>/*! Remodal - v0.1.6 - 2014-05-16
 * https://github.com/VodkaBears/remodal
 * Copyright (c) 2014 VodkaBears; */
;(function ($) {
    "use strict";

    /**
     * Remodal settings
     */
    var pluginName = "remodal",
        defaults = {
            hashTracking: true,
            closeOnConfirm: true,
            closeOnCancel: true
        };

    /**
     * Special plugin object for instances.
     * @type {Object}
     */
    $[pluginName] = {
        lookup: []
    };

    var current, // current modal
        scrollTop; // scroll position

    /**
     * Get transition duration in ms
     * @return {Number}
     */
    var getTransitionDuration = function ($elem) {
        var duration = $elem.css('transition-duration') ||
            $elem.css('-webkit-transition-duration') ||
            $elem.css('-moz-transition-duration') ||
            $elem.css('-o-transition-duration') ||
            $elem.css('-ms-transition-duration') ||
            0;
        var delay = $elem.css('transition-delay') ||
            $elem.css('-webkit-transition-delay') ||
            $elem.css('-moz-transition-delay') ||
            $elem.css('-o-transition-delay') ||
            $elem.css('-ms-transition-delay') ||
            0;

        return (parseFloat(duration) + parseFloat(delay)) * 1000;
    };

    /**
     * Get a scrollbar width
     * @return {Number}
     */
    var getScrollbarWidth = function () {
        if ($(document.body).height() <= $(window).height()) {
            return 0;
        }

        var outer = document.createElement("div");
        outer.style.visibility = "hidden";
        outer.style.width = "100px";
        document.body.appendChild(outer);

        var widthNoScroll = outer.offsetWidth;
        // force scrollbars
        outer.style.overflow = "scroll";

        // add innerdiv
        var inner = document.createElement("div");
        inner.style.width = "100%";
        outer.appendChild(inner);

        var widthWithScroll = inner.offsetWidth;

        // remove divs
        outer.parentNode.removeChild(outer);

        return widthNoScroll - widthWithScroll;
    };

    /**
     * Lock screen
     */
    var lockScreen = function () {
        $("html, body").addClass(pluginName + "_lock");
        $(document.body).css("padding-right", "+=" + getScrollbarWidth());
    };

    /**
     * Unlock screen
     */
    var unlockScreen = function () {
        $("html, body").removeClass(pluginName + "_lock");
        $(document.body).css("padding-right", "-=" + getScrollbarWidth());
    };

    /**
     * Remodal constructor
     */
    function Remodal(modal, options) {
        this.settings = $.extend({}, defaults, options);
        this.modal = modal;
        this.buildDOM();
        this.addEventListeners();
        this.index = $[pluginName].lookup.push(this) - 1;
        this.busy = false;
    }

    /**
     * Build required DOM
     */
    Remodal.prototype.buildDOM = function () {
        this.body = $(document.body);
        this.bg = $("." + pluginName + "-bg");
        this.modalClose = $("").addClass(pluginName + "-close");
        this.overlay = $("
").addClass(pluginName + "-overlay");
        if (!this.modal.hasClass(pluginName)) {
            this.modal.addClass(pluginName);
        }

        this.modal.css("visibility", "visible");
        this.modal.append(this.modalClose);
        this.overlay.append(this.modal);
        this.body.append(this.overlay);

        this.confirm = this.modal.find("." + pluginName + "-confirm");
        this.cancel = this.modal.find("." + pluginName + "-cancel");

        var tdOverlay = getTransitionDuration(this.overlay),
            tdModal = getTransitionDuration(this.modal),
            tdBg = getTransitionDuration(this.bg);
        this.td = tdModal > tdOverlay ? tdModal : tdOverlay;
        this.td = tdBg > this.td ? tdBg : this.td;
    };

    /**
     * Add event listeners to the current modal window
     */
    Remodal.prototype.addEventListeners = function () {
        var self = this;

        this.modalClose.bind("click." + pluginName, function (e) {
            e.preventDefault();
            self.close();
        });

        this.cancel.bind("click." + pluginName, function (e) {
            e.preventDefault();
            self.modal.trigger("cancel");
            if (self.settings.closeOnCancel) {
                self.close();
            }
        });

        this.confirm.bind("click." + pluginName, function (e) {
            e.preventDefault();
            self.modal.trigger("confirm");
            if (self.settings.closeOnConfirm) {
                self.close();
            }
        });

        $(document).bind('keyup.' + pluginName, function (e) {
            if (e.keyCode === 27) {
                self.close();
            }
        });

        this.overlay.bind("click." + pluginName, function (e) {
            var $target = $(e.target);
            if (!$target.hasClass(pluginName + "-overlay")) {
                return;
            }

            self.close();
        });
    };

    /**
     * Open modal window
     */
    Remodal.prototype.open = function () {
        // check if animation is complete
        if (this.busy) {
            return;
        }
        this.busy = true;

        this.modal.trigger("open");

        var id = this.modal.attr("data-" + pluginName + "-id");
        if (id && this.settings.hashTracking) {
            scrollTop = $(window).scrollTop();
            location.hash = id;
        }

        if (current && current !== this) {
            current.overlay.hide();
            current.body.removeClass(pluginName + "_active");
        }
        current = this;

        lockScreen();
        this.overlay.show();

        var self = this;
        setTimeout(function () {
            self.body.addClass(pluginName + "_active");

            setTimeout(function () {
                self.busy = false;
                self.modal.trigger("opened");
            }, self.td + 50);
        }, 25);
    };

    /**
     * Close modal window
     */
    Remodal.prototype.close = function () {
        // check if animation is complete
        if (this.busy) {
            return;
        }
        this.busy = true;

        this.modal.trigger("close");

        if (this.settings.hashTracking &&
            this.modal.attr("data-" + pluginName + "-id") === location.hash.substr(1)) {
            location.hash = "";
            $(window).scrollTop(scrollTop);
        }

        this.body.removeClass(pluginName + "_active");

        var self = this;
        setTimeout(function () {
            self.overlay.hide();
            unlockScreen();

            self.busy = false;
            self.modal.trigger("closed");
        }, self.td + 50);
    };

    if ($) {
        $["fn"][pluginName] = function (opts) {
            var instance;
            this["each"](function (i, e) {
                var $e = $(e);
                if ($e.data(pluginName) == null) {
                    instance = new Remodal($e, opts);
                    $e.data(pluginName, instance.index);

                    if (instance.settings.hashTracking &&
                        $e.attr("data-" + pluginName + "-id") === location.hash.substr(1)) {
                        instance.open();
                    }
                }
            });

            return instance;
        };
    }

    $(document).ready(function () {
        /**
         * data-remodal-target opens a modal window with a special id without hash change.
         */
        $(document).on("click", "[data-" + pluginName + "-target]", function (e) {
            e.preventDefault();

            var elem = e.currentTarget,
                id = elem.getAttribute("data-" + pluginName + "-target"),
                $target = $("[data-" + pluginName + "-id=" + id + "]");

            $[pluginName].lookup[$target.data(pluginName)].open();
        });

        /**
         * Auto initialization of modal windows.
         * They should have the 'data-remodal' attribute.
         * Also you can pass params into the modal throw the data-remodal-options attribute.
         * data-remodal-options must be a JSON string without brackets.
         */
        $(document).find("." + pluginName).each(function (i, container) {
            var $container = $(container),
                options = $container.data(pluginName + "-options");

            if (!options) {
                options = {};
            }

            $container[pluginName](options);
        });
    });

    /**
     * Hashchange handling to show a modal with a special id.
     */
    var hashHandler = function (e, closeOnEmptyHash) {
        var id = location.hash.replace("#", "");

        if (typeof closeOnEmptyHash === "undefined") {
            closeOnEmptyHash = true;
        }

        if (!id) {
            if (closeOnEmptyHash) {
                // check if we have currently opened modal and animation is complete
                if (current && !current.busy && current.settings.hashTracking) {
                    current.close();
                }
            }
        } else {
            var $elem = $("[data-" + pluginName + "-id=" + id.replace(new RegExp('/', 'g'), "\\/") + "]");

            if ($elem.length) {
                var instance = $[pluginName].lookup[$elem.data(pluginName)];

                if (instance && instance.settings.hashTracking) {
                    instance.open();
                }
            }

        }
    };
    $(window).bind("hashchange." + pluginName, hashHandler);
})(window["jQuery"] || window["Zepto"]);<commit_msg>Change JSDoc<commit_after>/*! Remodal - v0.1.6 - 2014-05-16
 * https://github.com/VodkaBears/remodal
 * Copyright (c) 2014 VodkaBears; */
;(function ($) {
    "use strict";

    /**
     * Remodal settings
     */
    var pluginName = "remodal",
        defaults = {
            hashTracking: true,
            closeOnConfirm: true,
            closeOnCancel: true
        };

    /**
     * Special plugin object for instances.
     * @type {Object}
     */
    $[pluginName] = {
        lookup: []
    };

    var current, // current modal
        scrollTop; // scroll position

    /**
     * Get transition duration in ms
     * @return {Number}
     */
    var getTransitionDuration = function ($elem) {
        var duration = $elem.css('transition-duration') ||
            $elem.css('-webkit-transition-duration') ||
            $elem.css('-moz-transition-duration') ||
            $elem.css('-o-transition-duration') ||
            $elem.css('-ms-transition-duration') ||
            0;
        var delay = $elem.css('transition-delay') ||
            $elem.css('-webkit-transition-delay') ||
            $elem.css('-moz-transition-delay') ||
            $elem.css('-o-transition-delay') ||
            $elem.css('-ms-transition-delay') ||
            0;

        return (parseFloat(duration) + parseFloat(delay)) * 1000;
    };

    /**
     * Get a scrollbar width
     * @return {Number}
     */
    var getScrollbarWidth = function () {
        if ($(document.body).height() <= $(window).height()) {
            return 0;
        }

        var outer = document.createElement("div");
        outer.style.visibility = "hidden";
        outer.style.width = "100px";
        document.body.appendChild(outer);

        var widthNoScroll = outer.offsetWidth;
        // force scrollbars
        outer.style.overflow = "scroll";

        // add innerdiv
        var inner = document.createElement("div");
        inner.style.width = "100%";
        outer.appendChild(inner);

        var widthWithScroll = inner.offsetWidth;

        // remove divs
        outer.parentNode.removeChild(outer);

        return widthNoScroll - widthWithScroll;
    };

    /**
     * Lock screen
     */
    var lockScreen = function () {
        $("html, body").addClass(pluginName + "_lock");
        $(document.body).css("padding-right", "+=" + getScrollbarWidth());
    };

    /**
     * Unlock screen
     */
    var unlockScreen = function () {
        $("html, body").removeClass(pluginName + "_lock");
        $(document.body).css("padding-right", "-=" + getScrollbarWidth());
    };

    /**
     * Remodal constructor
     */
    function Remodal(modal, options) {
        this.settings = $.extend({}, defaults, options);
        this.modal = modal;
        this.buildDOM();
        this.addEventListeners();
        this.index = $[pluginName].lookup.push(this) - 1;
        this.busy = false;
    }

    /**
     * Build required DOM
     */
    Remodal.prototype.buildDOM = function () {
        this.body = $(document.body);
        this.bg = $("." + pluginName + "-bg");
        this.modalClose = $("").addClass(pluginName + "-close");
        this.overlay = $("
").addClass(pluginName + "-overlay");
        if (!this.modal.hasClass(pluginName)) {
            this.modal.addClass(pluginName);
        }

        this.modal.css("visibility", "visible");
        this.modal.append(this.modalClose);
        this.overlay.append(this.modal);
        this.body.append(this.overlay);

        this.confirm = this.modal.find("." + pluginName + "-confirm");
        this.cancel = this.modal.find("." + pluginName + "-cancel");

        var tdOverlay = getTransitionDuration(this.overlay),
            tdModal = getTransitionDuration(this.modal),
            tdBg = getTransitionDuration(this.bg);
        this.td = tdModal > tdOverlay ? tdModal : tdOverlay;
        this.td = tdBg > this.td ? tdBg : this.td;
    };

    /**
     * Add event listeners to the current modal window
     */
    Remodal.prototype.addEventListeners = function () {
        var self = this;

        this.modalClose.bind("click." + pluginName, function (e) {
            e.preventDefault();
            self.close();
        });

        this.cancel.bind("click." + pluginName, function (e) {
            e.preventDefault();
            self.modal.trigger("cancel");
            if (self.settings.closeOnCancel) {
                self.close();
            }
        });

        this.confirm.bind("click." + pluginName, function (e) {
            e.preventDefault();
            self.modal.trigger("confirm");
            if (self.settings.closeOnConfirm) {
                self.close();
            }
        });

        $(document).bind('keyup.' + pluginName, function (e) {
            if (e.keyCode === 27) {
                self.close();
            }
        });

        this.overlay.bind("click." + pluginName, function (e) {
            var $target = $(e.target);
            if (!$target.hasClass(pluginName + "-overlay")) {
                return;
            }

            self.close();
        });
    };

    /**
     * Open modal window
     */
    Remodal.prototype.open = function () {
        // check if animation is complete
        if (this.busy) {
            return;
        }
        this.busy = true;

        this.modal.trigger("open");

        var id = this.modal.attr("data-" + pluginName + "-id");
        if (id && this.settings.hashTracking) {
            scrollTop = $(window).scrollTop();
            location.hash = id;
        }

        if (current && current !== this) {
            current.overlay.hide();
            current.body.removeClass(pluginName + "_active");
        }
        current = this;

        lockScreen();
        this.overlay.show();

        var self = this;
        setTimeout(function () {
            self.body.addClass(pluginName + "_active");

            setTimeout(function () {
                self.busy = false;
                self.modal.trigger("opened");
            }, self.td + 50);
        }, 25);
    };

    /**
     * Close modal window
     */
    Remodal.prototype.close = function () {
        // check if animation is complete
        if (this.busy) {
            return;
        }
        this.busy = true;

        this.modal.trigger("close");

        if (this.settings.hashTracking &&
            this.modal.attr("data-" + pluginName + "-id") === location.hash.substr(1)) {
            location.hash = "";
            $(window).scrollTop(scrollTop);
        }

        this.body.removeClass(pluginName + "_active");

        var self = this;
        setTimeout(function () {
            self.overlay.hide();
            unlockScreen();

            self.busy = false;
            self.modal.trigger("closed");
        }, self.td + 50);
    };

    if ($) {
        $["fn"][pluginName] = function (opts) {
            var instance;
            this["each"](function (i, e) {
                var $e = $(e);
                if ($e.data(pluginName) == null) {
                    instance = new Remodal($e, opts);
                    $e.data(pluginName, instance.index);

                    if (instance.settings.hashTracking &&
                        $e.attr("data-" + pluginName + "-id") === location.hash.substr(1)) {
                        instance.open();
                    }
                }
            });

            return instance;
        };
    }

    $(document).ready(function () {
        /**
         * data-remodal-target opens a modal window with a special id without hash change.
         */
        $(document).on("click", "[data-" + pluginName + "-target]", function (e) {
            e.preventDefault();

            var elem = e.currentTarget,
                id = elem.getAttribute("data-" + pluginName + "-target"),
                $target = $("[data-" + pluginName + "-id=" + id + "]");

            $[pluginName].lookup[$target.data(pluginName)].open();
        });

        /**
         * Auto initialization of modal windows.
         * They should have the 'remodal' class attribute.
         * Also you can pass params into the modal throw the data-remodal-options attribute.
         * data-remodal-options must be a valid JSON string.
         */
        $(document).find("." + pluginName).each(function (i, container) {
            var $container = $(container),
                options = $container.data(pluginName + "-options");

            if (!options) {
                options = {};
            }

            $container[pluginName](options);
        });
    });

    /**
     * Hashchange handling to show a modal with a special id.
     */
    var hashHandler = function (e, closeOnEmptyHash) {
        var id = location.hash.replace("#", "");

        if (typeof closeOnEmptyHash === "undefined") {
            closeOnEmptyHash = true;
        }

        if (!id) {
            if (closeOnEmptyHash) {
                // check if we have currently opened modal and animation is complete
                if (current && !current.busy && current.settings.hashTracking) {
                    current.close();
                }
            }
        } else {
            var $elem = $("[data-" + pluginName + "-id=" + id.replace(new RegExp('/', 'g'), "\\/") + "]");

            if ($elem.length) {
                var instance = $[pluginName].lookup[$elem.data(pluginName)];

                if (instance && instance.settings.hashTracking) {
                    instance.open();
                }
            }

        }
    };
    $(window).bind("hashchange." + pluginName, hashHandler);
})(window["jQuery"] || window["Zepto"]);<|endoftext|>


<commit_before>YUI.add("app-tristis", function(Y) {
    "use strict";

    var gui      = require("nw.gui"),
        conf     = require("config").Twitter,
        Twitter  = require("twit"),

        extensions    = Y.namespace("Extensions"),
        tristis       = Y.namespace("Tristis"),
        appExtensions = Y.namespace("Tristis.Extensions"),
        views         = Y.namespace("Tristis.Views"),
        models        = Y.namespace("Tristis.Models"),
        streams       = Y.namespace("Tristis.Streams"),

        win = gui.Window.get(),

        App;

    App = Y.Base.create("tristis", Y.App, [
        extensions.ViewClasses,
        extensions.ViewParent,

        appExtensions.Routes,
        appExtensions.Events
    ], {
        initializer : function() {
            // Save window size when closing
            win.on("close", function() {
                localStorage.x      = win.x;
                localStorage.y      = win.y;
                localStorage.width  = win.width;
                localStorage.height = win.height;

                tristis.app.destroy();

                process.nextTick(function() {
                    win.close(true);
                });
            });

            if(!localStorage.access_token || !localStorage.access_secret) {
                return this._auth();
            }

            this._setup();
        },

        destructor : function() {
            models.user.destroy();
            models.timelines.destroy();
        },

        _setup : function() {
            var self = this;

            tristis.twitter = new Twitter({
                consumer_key        : conf.consumerKey,
                consumer_secret     : conf.consumerSecret,
                access_token        : localStorage.access_token,
                access_token_secret : localStorage.access_secret
            });

            tristis.lawnchair = new Lawnchair(function() {
                console.log("lawnchair ready");
            });

            models.user      = new models.User();
            models.timelines = new models.Timelines();

            // add child view now because they depend on model references existing
            this.set("children", {
                nav : new views.Nav()
            });

            // go load user details
            models.user.load(function(err) {
                if(err) {
                    return self._auth();
                }

                models.timelines.load({ sync : "lawnchair" }, function(err) {
                    models.timelines.load({ sync : "twitter" });
                });

                streams.user.start();

                self.navigate("/home");

                self._render();
            });
        },

        _render : function() {
            // Restore size & position
            if(localStorage.width && localStorage.height) {
                win.resizeTo(
                    parseInt(localStorage.width, 10),
                    parseInt(localStorage.height, 10)
                );

                win.moveTo(
                    parseInt(localStorage.x, 10),
                    parseInt(localStorage.y, 10)
                );
            }

            this.render();
            win.show();
        },

        _auth : function() {
            this.navigate("/auth");

            this._render();
        }
    }, {
        ATTRS : {
            serverRouting: {
                value : false
            },

            children : null
        }
    });

    if(!conf.consumerKey || !conf.consumerSecret) {
        console.error("You need to specify an API key!");

        return;
    }

    tristis.txt = require("twitter-text");

    tristis.app = new App({
        viewContainer : ".views"
    });

}, "@VERSION@", {
    requires : [
        // YUI
        "base-build",
        "app",

        // Lawnchair
        "external-lawnchair",
        "external-lawnchair-indexed-db",

        // Generic Extensions
        "extension-view-classes",
        "extension-view-parent",

        // Tristis Extensions
        "extension-tristis-events",
        "extension-tristis-routes",

        // Models
        "model-user",
        "model-list-timelines",

        // Views
        "view-nav",

        // CSS
        "css-app",
        "css-nav",

        // Streams
        "stream-user",
        "stream-users"
    ]
});
<commit_msg>Making a note for myself<commit_after>YUI.add("app-tristis", function(Y) {
    "use strict";

    var gui      = require("nw.gui"),
        conf     = require("config").Twitter,
        Twitter  = require("twit"),

        extensions    = Y.namespace("Extensions"),
        tristis       = Y.namespace("Tristis"),
        appExtensions = Y.namespace("Tristis.Extensions"),
        views         = Y.namespace("Tristis.Views"),
        models        = Y.namespace("Tristis.Models"),
        streams       = Y.namespace("Tristis.Streams"),

        win = gui.Window.get(),

        App;

    App = Y.Base.create("tristis", Y.App, [
        extensions.ViewClasses,
        extensions.ViewParent,

        appExtensions.Routes,
        appExtensions.Events
    ], {
        initializer : function() {
            // Save window size when closing
            win.on("close", function() {
                localStorage.x      = win.x;
                localStorage.y      = win.y;
                localStorage.width  = win.width;
                localStorage.height = win.height;

                tristis.app.destroy();

                process.nextTick(function() {
                    win.close(true);
                });
            });

            if(!localStorage.access_token || !localStorage.access_secret) {
                return this._auth();
            }

            this._setup();
        },

        destructor : function() {
            models.user.destroy();
            models.timelines.destroy();
        },

        _setup : function() {
            var self = this;

            tristis.twitter = new Twitter({
                consumer_key        : conf.consumerKey,
                consumer_secret     : conf.consumerSecret,
                access_token        : localStorage.access_token,
                access_token_secret : localStorage.access_secret
            });

            tristis.lawnchair = new Lawnchair(function() {
                console.log("lawnchair ready");
            });

            models.user      = new models.User();
            models.timelines = new models.Timelines();

            // add child view now because they depend on model references existing
            this.set("children", {
                nav : new views.Nav()
            });

            // go load user details
            models.user.load(function(err) {
                if(err) {
                    return self._auth();
                }

                models.timelines.load({ sync : "lawnchair" }, function(err) {
                    // TODO: this shouldn't be a .load call, it should be something like
                    // .update or something similar. Need to add a custom sync action.
                    models.timelines.load({ sync : "twitter" });
                });

                streams.user.start();

                self.navigate("/home");

                self._render();
            });
        },

        _render : function() {
            // Restore size & position
            if(localStorage.width && localStorage.height) {
                win.resizeTo(
                    parseInt(localStorage.width, 10),
                    parseInt(localStorage.height, 10)
                );

                win.moveTo(
                    parseInt(localStorage.x, 10),
                    parseInt(localStorage.y, 10)
                );
            }

            this.render();
            win.show();
        },

        _auth : function() {
            this.navigate("/auth");

            this._render();
        }
    }, {
        ATTRS : {
            serverRouting: {
                value : false
            },

            children : null
        }
    });

    if(!conf.consumerKey || !conf.consumerSecret) {
        console.error("You need to specify an API key!");

        return;
    }

    tristis.txt = require("twitter-text");

    tristis.app = new App({
        viewContainer : ".views"
    });

}, "@VERSION@", {
    requires : [
        // YUI
        "base-build",
        "app",

        // Lawnchair
        "external-lawnchair",
        "external-lawnchair-indexed-db",

        // Generic Extensions
        "extension-view-classes",
        "extension-view-parent",

        // Tristis Extensions
        "extension-tristis-events",
        "extension-tristis-routes",

        // Models
        "model-user",
        "model-list-timelines",

        // Views
        "view-nav",

        // CSS
        "css-app",
        "css-nav",

        // Streams
        "stream-user",
        "stream-users"
    ]
});
<|endoftext|>


<commit_before>/**
 * Contains the logic for the sidebar.
 *
 * @returns {{}}
 */
webvowlApp.sidebar = function () {

	var sidebar = {};

	/**
	 * Setup the menu bar.
	 */
	sidebar.setup = function () {
		setupCollapsing();
	};

	function setupCollapsing() {
		// adapted version of this example: http://www.normansblog.de/simple-jquery-accordion/
		function collapseContainers(containers) {
			containers.style("display", "none");
		}

		function expandContainers(containers) {
			containers.style("display", null);
		}

		var triggers = d3.selectAll(".accordion-trigger");

		// Collapse all inactive triggers on startup
		collapseContainers(d3.selectAll(".accordion-trigger:not(.accordion-trigger-active) + div"));

		triggers.on("click", function () {
			var selectedTrigger = d3.select(this),
				activeTriggers = d3.selectAll(".accordion-trigger-active");

			if (selectedTrigger.classed("accordion-trigger-active")) {
				// Collapse the active (which is also the selected) trigger
				collapseContainers(d3.select(selectedTrigger.node().nextElementSibling));
				selectedTrigger.classed("accordion-trigger-active", false);
			} else {
				// Collapse the other trigger ...
				collapseContainers(d3.selectAll(".accordion-trigger-active + div"));
				activeTriggers.classed("accordion-trigger-active", false);
				// ... and expand the selected one
				expandContainers(d3.select(selectedTrigger.node().nextElementSibling));
				selectedTrigger.classed("accordion-trigger-active", true);
			}
		});
	}

	/**
	 * Updates the information of the passed ontology.
	 * @param data the graph data
	 * @param statistics the statistics module
	 */
	sidebar.updateOntologyInformation = function (data, statistics) {
		displayGraphInformation(data);
		displayGraphStatistics(data.metrics, statistics);
		// Reset the sidebar selection
		sidebar.updateSelectionInformation(undefined);
	};

	function displayGraphInformation(data) {
		var header = data.header;

		d3.select("#title").text(header.title);
		d3.select("#about").attr("href", header.uri).attr("target", "_blank").text(header.uri);
		d3.select("#version").text(header.version);
		d3.select("#authors").text(header.author);
		d3.select("#description").text(header.description);
	}

	function displayGraphStatistics(deliveredMetrics, statistics) {
		// Metrics are optional and may be undefined
		deliveredMetrics = deliveredMetrics || {};

		d3.select("#classCount")
			.text(deliveredMetrics.classCount || statistics.classCount());
		d3.select("#datatypeCount")
			.text(deliveredMetrics.datatypeCount || statistics.datatypeCount());
		d3.select("#propertyCount")
			.text(deliveredMetrics.propertyCount || statistics.propertyCount());
		d3.select("#nodeCount")
			.text(statistics.nodeCount());
		d3.select("#edgeCount")
			.text(statistics.edgeCount());
	}

	/**
	 * Update the information of the selected node.
	 * @param selectedElement the selection or null if nothing is selected
	 */
	sidebar.updateSelectionInformation = function (selectedElement) {
		// Click event was prevented when dragging
		if (d3.event && d3.event.defaultPrevented) {
			return;
		}

		var isTriggerActive = d3.select("#selection-details-trigger").classed("accordion-trigger-active");
		if (selectedElement && !isTriggerActive) {
			d3.select("#selection-details-trigger").node().click();
		} else if (!selectedElement && isTriggerActive) {
			d3.select("#selection-details-trigger").node().click();
			showSelectionAdvice();
			return;
		}

		if (selectedElement instanceof webvowl.labels.BaseLabel) {
			displayLabelInformation(selectedElement);
		} else if (selectedElement instanceof webvowl.nodes.BaseNode) {
			displayNodeInformation(selectedElement);
		}
	};

	function showSelectionAdvice() {
		setSelectionInformationVisibility(false, false, true);
	}

	function setSelectionInformationVisibility(showClasses, showProperties, showAdvice) {
		d3.select("#classSelectionInformation").classed("hidden", !showClasses);
		d3.select("#propertySelectionInformation").classed("hidden", !showProperties);
		d3.select("#noSelectionInformation").classed("hidden", !showAdvice);
	}

	function displayLabelInformation(property) {
		showPropertyInformations();

		setUriLabel(d3.select("#propname"), property.label(), property.uri());
		d3.select("#typeProp").text(property.type());

		if (property.inverse() !== undefined) {
			d3.select("#inverse").style("display", "block");
			setUriLabel(d3.select("#inverse span"), property.inverse().label(), property.inverse().uri());
		} else {
			d3.select("#inverse").style("display", "none");
		}

		var equivalentUriSpan = d3.select("#propEquivUri");
		listNodeArray(property.equivalent(), equivalentUriSpan);

		listNodeArray(property.subproperties(), d3.select("#subproperties"));
		listNodeArray(property.superproperties(), d3.select("#superproperties"));

		if (property.minCardinality() !== undefined) {
			d3.select("#infoCardinality").style("display", "none");
			d3.select("#minCardinality").style("display", "block");
			d3.select("#minCardinality span").text(property.minCardinality());
			d3.select("#maxCardinality").style("display", "block");

			if (property.maxCardinality() !== undefined) {
				d3.select("#maxCardinality span").text(property.maxCardinality());
			} else {
				d3.select("#maxCardinality span").text("*");
			}

		} else if (property.cardinality() !== undefined) {
			d3.select("#minCardinality").style("display", "none");
			d3.select("#maxCardinality").style("display", "none");
			d3.select("#infoCardinality").style("display", "block");
			d3.select("#infoCardinality span").text(property.cardinality());
		} else {
			d3.select("#infoCardinality").style("display", "none");
			d3.select("#minCardinality").style("display", "none");
			d3.select("#maxCardinality").style("display", "none");
		}

		setUriLabel(d3.select("#domain"), property.domain().label(), property.domain().uri());
		setUriLabel(d3.select("#range"), property.range().label(), property.range().uri());

		displayAttributes(property.attributes(), d3.select("#propAttributes"));
	}

	function showPropertyInformations() {
		setSelectionInformationVisibility(false, true, false);
	}

	function setUriLabel(element, name, uri) {
		element.selectAll("*").remove();
		appendUriLabel(element, name, uri);
	}

	function appendUriLabel(element, name, uri) {
		var tag;

		if (uri) {
			tag = element.append("a").attr("href", uri).attr("target", "_blank");
		} else {
			tag = element.append("span");
		}
		tag.text(name);
	}

	function displayAttributes(attributes, textSpan) {
		var spanParent = d3.select(textSpan.node().parentNode);

		if (attributes && attributes.length > 0) {
			// Remove redundant "object" and "datatype" attribute for sidebar
			removeElementFromArray("object", attributes);
			removeElementFromArray("datatype", attributes);
			removeElementFromArray("rdf", attributes);
		}

		if (attributes && attributes.length > 0) {
			textSpan.text(attributes.join(", "));

			spanParent.classed("hidden", false);
		} else {
			spanParent.classed("hidden", true);
		}
	}

	function removeElementFromArray(element, array) {
		var index = array.indexOf(element);
		if (index > -1) {
			array.splice(index, 1);
		}
	}

	function displayNodeInformation(node) {
		showClassInformations();

		setUriLabel(d3.select("#name"), node.label(), node.uri());

		/* Equivalent stuff. */
		var equivalentUriSpan = d3.select("#classEquivUri");
		listNodeArray(node.equivalent(), equivalentUriSpan);

		d3.select("#typeNode").text(node.type());

		/* Disjoint stuff. */
		var disjointNodes = d3.select("#disjointNodes");
		var disjointNodesParent = d3.select(disjointNodes.node().parentNode);

		if (node.disjointWith() !== undefined) {
			disjointNodes.selectAll("*").remove();

			node.disjointWith().forEach(function (element, index) {
				if (index > 0) {
					disjointNodes.append("span").text(", ");
				}
				appendUriLabel(disjointNodes, element.label(), element.uri());
			});

			disjointNodesParent.classed("hidden", false);
		} else {
			disjointNodesParent.classed("hidden", true);
		}

		displayAttributes(node.attributes(), d3.select("#classAttributes"));
	}

	function showClassInformations() {
		setSelectionInformationVisibility(true, false, false);
	}

	function listNodeArray(nodes, textSpan) {
		var spanParent = d3.select(textSpan.node().parentNode);

		if (nodes && nodes.length) {
			textSpan.selectAll("*").remove();
			nodes.forEach(function (element, index) {
				if (index > 0) {
					textSpan.append("span").text(", ");
				}
				appendUriLabel(textSpan, element.label(), element.uri());
			});

			spanParent.classed("hidden", false);
		} else {
			spanParent.classed("hidden", true);
		}
	}

	return sidebar;
};
<commit_msg>Correct comment<commit_after>/**
 * Contains the logic for the sidebar.
 *
 * @returns {{}}
 */
webvowlApp.sidebar = function () {

	var sidebar = {};

	/**
	 * Setup the menu bar.
	 */
	sidebar.setup = function () {
		setupCollapsing();
	};

	function setupCollapsing() {
		// adapted version of this example: http://www.normansblog.de/simple-jquery-accordion/
		function collapseContainers(containers) {
			containers.style("display", "none");
		}

		function expandContainers(containers) {
			containers.style("display", null);
		}

		var triggers = d3.selectAll(".accordion-trigger");

		// Collapse all inactive triggers on startup
		collapseContainers(d3.selectAll(".accordion-trigger:not(.accordion-trigger-active) + div"));

		triggers.on("click", function () {
			var selectedTrigger = d3.select(this),
				activeTriggers = d3.selectAll(".accordion-trigger-active");

			if (selectedTrigger.classed("accordion-trigger-active")) {
				// Collapse the active (which is also the selected) trigger
				collapseContainers(d3.select(selectedTrigger.node().nextElementSibling));
				selectedTrigger.classed("accordion-trigger-active", false);
			} else {
				// Collapse the other trigger ...
				collapseContainers(d3.selectAll(".accordion-trigger-active + div"));
				activeTriggers.classed("accordion-trigger-active", false);
				// ... and expand the selected one
				expandContainers(d3.select(selectedTrigger.node().nextElementSibling));
				selectedTrigger.classed("accordion-trigger-active", true);
			}
		});
	}

	/**
	 * Updates the information of the passed ontology.
	 * @param data the graph data
	 * @param statistics the statistics module
	 */
	sidebar.updateOntologyInformation = function (data, statistics) {
		displayGraphInformation(data);
		displayGraphStatistics(data.metrics, statistics);
		// Reset the sidebar selection
		sidebar.updateSelectionInformation(undefined);
	};

	function displayGraphInformation(data) {
		var header = data.header;

		d3.select("#title").text(header.title);
		d3.select("#about").attr("href", header.uri).attr("target", "_blank").text(header.uri);
		d3.select("#version").text(header.version);
		d3.select("#authors").text(header.author);
		d3.select("#description").text(header.description);
	}

	function displayGraphStatistics(deliveredMetrics, statistics) {
		// Metrics are optional and may be undefined
		deliveredMetrics = deliveredMetrics || {};

		d3.select("#classCount")
			.text(deliveredMetrics.classCount || statistics.classCount());
		d3.select("#datatypeCount")
			.text(deliveredMetrics.datatypeCount || statistics.datatypeCount());
		d3.select("#propertyCount")
			.text(deliveredMetrics.propertyCount || statistics.propertyCount());
		d3.select("#nodeCount")
			.text(statistics.nodeCount());
		d3.select("#edgeCount")
			.text(statistics.edgeCount());
	}

	/**
	 * Update the information of the selected node.
	 * @param selectedElement the selection or null if nothing is selected
	 */
	sidebar.updateSelectionInformation = function (selectedElement) {
		// Click event was prevented when dragging
		if (d3.event && d3.event.defaultPrevented) {
			return;
		}

		var isTriggerActive = d3.select("#selection-details-trigger").classed("accordion-trigger-active");
		if (selectedElement && !isTriggerActive) {
			d3.select("#selection-details-trigger").node().click();
		} else if (!selectedElement && isTriggerActive) {
			d3.select("#selection-details-trigger").node().click();
			showSelectionAdvice();
			return;
		}

		if (selectedElement instanceof webvowl.labels.BaseLabel) {
			displayLabelInformation(selectedElement);
		} else if (selectedElement instanceof webvowl.nodes.BaseNode) {
			displayNodeInformation(selectedElement);
		}
	};

	function showSelectionAdvice() {
		setSelectionInformationVisibility(false, false, true);
	}

	function setSelectionInformationVisibility(showClasses, showProperties, showAdvice) {
		d3.select("#classSelectionInformation").classed("hidden", !showClasses);
		d3.select("#propertySelectionInformation").classed("hidden", !showProperties);
		d3.select("#noSelectionInformation").classed("hidden", !showAdvice);
	}

	function displayLabelInformation(property) {
		showPropertyInformations();

		setUriLabel(d3.select("#propname"), property.label(), property.uri());
		d3.select("#typeProp").text(property.type());

		if (property.inverse() !== undefined) {
			d3.select("#inverse").style("display", "block");
			setUriLabel(d3.select("#inverse span"), property.inverse().label(), property.inverse().uri());
		} else {
			d3.select("#inverse").style("display", "none");
		}

		var equivalentUriSpan = d3.select("#propEquivUri");
		listNodeArray(property.equivalent(), equivalentUriSpan);

		listNodeArray(property.subproperties(), d3.select("#subproperties"));
		listNodeArray(property.superproperties(), d3.select("#superproperties"));

		if (property.minCardinality() !== undefined) {
			d3.select("#infoCardinality").style("display", "none");
			d3.select("#minCardinality").style("display", "block");
			d3.select("#minCardinality span").text(property.minCardinality());
			d3.select("#maxCardinality").style("display", "block");

			if (property.maxCardinality() !== undefined) {
				d3.select("#maxCardinality span").text(property.maxCardinality());
			} else {
				d3.select("#maxCardinality span").text("*");
			}

		} else if (property.cardinality() !== undefined) {
			d3.select("#minCardinality").style("display", "none");
			d3.select("#maxCardinality").style("display", "none");
			d3.select("#infoCardinality").style("display", "block");
			d3.select("#infoCardinality span").text(property.cardinality());
		} else {
			d3.select("#infoCardinality").style("display", "none");
			d3.select("#minCardinality").style("display", "none");
			d3.select("#maxCardinality").style("display", "none");
		}

		setUriLabel(d3.select("#domain"), property.domain().label(), property.domain().uri());
		setUriLabel(d3.select("#range"), property.range().label(), property.range().uri());

		displayAttributes(property.attributes(), d3.select("#propAttributes"));
	}

	function showPropertyInformations() {
		setSelectionInformationVisibility(false, true, false);
	}

	function setUriLabel(element, name, uri) {
		element.selectAll("*").remove();
		appendUriLabel(element, name, uri);
	}

	function appendUriLabel(element, name, uri) {
		var tag;

		if (uri) {
			tag = element.append("a").attr("href", uri).attr("target", "_blank");
		} else {
			tag = element.append("span");
		}
		tag.text(name);
	}

	function displayAttributes(attributes, textSpan) {
		var spanParent = d3.select(textSpan.node().parentNode);

		if (attributes && attributes.length > 0) {
			// Remove redundant redundant attributes for sidebar
			removeElementFromArray("object", attributes);
			removeElementFromArray("datatype", attributes);
			removeElementFromArray("rdf", attributes);
		}

		if (attributes && attributes.length > 0) {
			textSpan.text(attributes.join(", "));

			spanParent.classed("hidden", false);
		} else {
			spanParent.classed("hidden", true);
		}
	}

	function removeElementFromArray(element, array) {
		var index = array.indexOf(element);
		if (index > -1) {
			array.splice(index, 1);
		}
	}

	function displayNodeInformation(node) {
		showClassInformations();

		setUriLabel(d3.select("#name"), node.label(), node.uri());

		/* Equivalent stuff. */
		var equivalentUriSpan = d3.select("#classEquivUri");
		listNodeArray(node.equivalent(), equivalentUriSpan);

		d3.select("#typeNode").text(node.type());

		/* Disjoint stuff. */
		var disjointNodes = d3.select("#disjointNodes");
		var disjointNodesParent = d3.select(disjointNodes.node().parentNode);

		if (node.disjointWith() !== undefined) {
			disjointNodes.selectAll("*").remove();

			node.disjointWith().forEach(function (element, index) {
				if (index > 0) {
					disjointNodes.append("span").text(", ");
				}
				appendUriLabel(disjointNodes, element.label(), element.uri());
			});

			disjointNodesParent.classed("hidden", false);
		} else {
			disjointNodesParent.classed("hidden", true);
		}

		displayAttributes(node.attributes(), d3.select("#classAttributes"));
	}

	function showClassInformations() {
		setSelectionInformationVisibility(true, false, false);
	}

	function listNodeArray(nodes, textSpan) {
		var spanParent = d3.select(textSpan.node().parentNode);

		if (nodes && nodes.length) {
			textSpan.selectAll("*").remove();
			nodes.forEach(function (element, index) {
				if (index > 0) {
					textSpan.append("span").text(", ");
				}
				appendUriLabel(textSpan, element.label(), element.uri());
			});

			spanParent.classed("hidden", false);
		} else {
			spanParent.classed("hidden", true);
		}
	}

	return sidebar;
};
<|endoftext|>


<commit_before>/* ========================================================================
 * ZUI: contextmenu.js
 * http://openzui.com
 * ========================================================================
 * Copyright (c) 2017-2019 cnezsoft.com; Licensed MIT
 * ======================================================================== */

(function($, undefined) {
    'use strict';

    var NAME = 'zui.contextmenu'; // model name

    var DEFAULTS = {
        // onShow: null,
        // onShown: null,
        // onHide: null,
        // onHidden: null,
        // itemCreator: null,
        // x: 0,
        // y: 0,
        // onClickItem: null,
        // menuCreator: null,
        // position: null,
        animation: 'fade',
        menuTemplate: '

',
        toggleTrigger: false,
        duration: 200,
        limitInsideWindow: true
    };

    var isShowingMenu = false;
    var ContextMenu = {};
    var targetId = 'zui-contextmenu-' + $.zui.uuid();
    var mouseX = 0, mouseY = 0;
    var listenMouseMove = function() {
        $(document).off('mousemove.' + NAME).on('mousemove.' + NAME, function(e) {
            mouseX = e.clientX;
            mouseY = e.clientY;
        });
        return ContextMenu;
    };
    var createMenuItem = function(item, index) {
        if (typeof item === 'string') {
            if (item === 'seperator' || item === 'divider' || item === '-' || item === '|') {
                item = {type: 'seperator'};
            } else {
                item = {label: item, id: index};
            }
        }
        if (item.type === 'seperator' || item.type === 'divider') {
            return $('

');
        }
        var $a = $('').attr($.extend({
            href: item.url || '###',
            'class': item.className,
            style: item.style
        }, item.attrs)).data('item', item);
        if (item.html) {
            if (item.html === true) {
                $a.html(item.label || item.text);
            } else {
                $a = $(item.html);
            }
        } else {
            $a.text(item.label || item.text);
        }
        if(item.icon) {
            $a.prepend('');
        }
        if (item.onClick) {
            $a.on('click', item.onClick);
        }
        return $('
').toggleClass('disabled', item.disabled === true).append($a);
    };

    var isContextMenuShow = function(id) {
        var $target = $('#' + targetId);
        return $target.length && $target.hasClass('contextmenu-show') && (!id || ($target.data('options') || {}).id === id);
    };

    var animationTimer = null;
    var hideContextMenu = function(id, callback) {
        if (typeof id === 'function') {
            callback = id;
            id = null;
        }

        if (animationTimer) {
            clearTimeout(animationTimer);
            animationTimer = null;
        }

        var $target = $('#' + targetId);
        if ($target.length) {
            var options = $target.removeClass('contextmenu-show').data('options');
            if (!id || options.id === id) {
                var afterHide = function() {
                    $target.find('.contextmenu-menu').removeClass('open');
                    options.onHidden && options.onHidden();
                    callback && callback();
                };
                options.onHide && options.onHide();
                var animation = options.animation;
                $target.find('.contextmenu-menu').removeClass('in');
                if (animation) {
                    animationTimer = setTimeout(afterHide, options.duration);
                } else {
                    afterHide();
                }
            }
        }
        return ContextMenu;
    };

    var showContextMenu = function(items, options, callback) {
        if ($.isPlainObject(items)) {
            callback = options;
            options = items;
            items = options.items;
        }

        isShowingMenu = true;
        // hideContextMenu();

        options = $.extend({}, DEFAULTS, options);

        var $target = $('#' + targetId);
        if (!$target.length) {
            $target = $('

').appendTo('body');
        }
        var $menu = $target.find('.contextmenu-menu').off('click.' + NAME).on('click.' + NAME, 'a,.contextmenu-item', function(e) {
            var $item = $(this);
            var clickResult = options.onClickItem && options.onClickItem($item.data('item'), $item, e, options);
            if (clickResult !== false) {
                hideContextMenu();
            }
        }).empty();
        $menu.attr('class', 'contextmenu-menu' + (options.className ? (' ' + options.className) : ''))
        $target.attr('class', 'contextmenu contextmenu-show');

        // Create menu items
        var menuCreator = options.menuCreator;
        if (menuCreator) {
            $menu.append(menuCreator(items, options));
        } else {
            $menu.append(options.menuTemplate);
            var $menuList = $menu.children().first();
            var itemCreator = options.itemCreator || createMenuItem;
            var itemsType = typeof items;
            if (itemsType === 'string') {
                items = items.split(',');
            } else if (itemsType === 'function') {
                items = items(options);
            }
            if (!items) {
                return false;
            }
            $.each(items, function(index, item) {
                $menuList.append(itemCreator(item, index, options));
            });
        }

        // Show menu
        var animation = options.animation;
        var duration = options.duration;
        if (animation === true) options.animation = animation = 'fade';
        if (animationTimer) {
            clearTimeout(animationTimer);
            animationTimer = null;
        }
        var afterShow = function() {
            $menu.addClass('in');
            options.onShown && options.onShown();
            callback && callback();
        };
        options.onShow && options.onShow();

        $target.data('options', {
            animation: animation,
            onHide: options.onHide,
            onHidden: options.onHidden,
            id: options.id,
            duration: duration
        });

        var x = options.x;
        var y = options.y;
        if (x === undefined) x = (options.event || options).clientX;
        if (x === undefined) x = mouseX;
        if (y === undefined) y = (options.event || options).clientY;
        if (y === undefined) y = mouseY;
        var $menuList = $menu.children().first();
        var menuWidth = $menuList.outerWidth();
        var menuHeight = $menuList.outerHeight();
        if (options.position) {
            var newPos = options.position({x: x, y: y, width: menuWidth, height: menuHeight}, options, $menu);
            if (newPos) {
                x = newPos.x;
                y = newPos.y;
            }
        }
        if (options.limitInsideWindow) {
            var $w = $(window);
            x = Math.max(0, Math.min(x, $w.width() - menuWidth));
            y = Math.max(0, Math.min(y, $w.height() - menuHeight));
        }

        $target.css({
            left: x,
            top: y
        }).show();

        $menu.addClass('open');
        if (animation) {
            $menu.addClass(animation);
            animationTimer = setTimeout(function() {
                // $menu.show();
                afterShow();
                isShowingMenu = false;
            }, 10);
        } else {
            // $menu.show();
            afterShow();
            isShowingMenu = false;
        }
        return ContextMenu;
    };

    $.extend(ContextMenu, {
        NAME: NAME,
        DEFAULTS: DEFAULTS,
        show: showContextMenu,
        hide: hideContextMenu,
        listenMouse: listenMouseMove,
        isShow: isContextMenuShow
    });
    $.zui({ContextMenu: ContextMenu});

    // The contextmenu model class
    var ContextListener = function(element, options) {
        var that = this;
        that.name = NAME;
        that.$ = $(element);
        that.id = $.zui.uuid();
        options = that.options = $.extend({trigger: 'contextmenu'}, ContextMenu.DEFAULTS, this.$.data(), options);

        var eventHandler = function(e) {
            if (e.type === 'mousedown' && e.button !== 2) {
                return;
            }

            if (options.toggleTrigger && that.isShow()) {
                that.hide();
            } else {
                var config = {
                    x: e.clientX,
                    y: e.clientY,
                    event: e,
                };
                if (that.show(config) === false) {
                    return;
                }
            }
            e.preventDefault();
            e.returnValue = false; // 解决IE8右键弹出
            return false;
        };

        var trigger = options.trigger;
        var eventName = trigger + '.' + NAME;
        if (options.selector) {
            that.$.on(eventName, options.selector, eventHandler);
        } else {
            that.$.on(eventName, eventHandler);
        }

        if (options.show) {
            that.show(typeof options.show === 'object' ? options.show : null);
        }
    };

    ContextListener.prototype.destory = function () {
        that.$.off('.' + NAME);
    };

    ContextListener.prototype.hide = function (callback) {
        return ContextMenu.hide(this.id, callback);
    };

    ContextListener.prototype.show = function (options, callback) {
        options = $.extend({id: this.id, $toggle: this.$}, this.options, options);
        return ContextMenu.show(options, callback);
    };

    ContextListener.prototype.isShow = function () {
        return isContextMenuShow(this.id);
    };

    // Extense jquery element
    $.fn.contextmenu = function(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data(NAME);
            var options = typeof option == 'object' && option;

            if(!data) $this.data(NAME, (data = new ContextListener(this, options)));

            if(typeof option == 'string') data[option]();
        });
    };
    $.fn.contextmenu.Constructor = ContextListener;

    // Use dropdown menu as contextmenu
    $.fn.contextDropdown = function(options) {
        $(this).contextmenu($.extend({
            trigger: 'click',
            animation: 'fade',
            toggleTrigger: true,
            menuCreator: function(items, finalOptions) {
                var $toggle = finalOptions.$toggle;
                var selector = $toggle.attr('data-target')
                if(!selector) {
                    selector = $toggle.attr('href')
                    selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
                }
                var $target = selector ? $(selector) : $toggle.next('.dropdown-menu');
                var transferEvent = finalOptions.transferEvent;
                if (transferEvent !== false) {
                    var indexAttrName = 'data-contextmenu-index';
                    $target.find('a,.contextmenu-item').each(function(index) {
                        $(this).attr(indexAttrName, index);
                    });
                    var $clone = $target.clone();
                    $clone.on(typeof transferEvent === 'string' ? transferEvent : 'click', 'a,.contextmenu-item', function(event) {
                        var $item = $target.find('[' + indexAttrName + '="' + $(this).attr(indexAttrName) + '"]');
                        var item = $item[0];
                        if (!item) return;
                        if (item[event.type]) {
                            item[event.type]();
                        } else {
                            $item.trigger(event.type);
                        }
                        event.preventDefault();
                        event.stopPropagation();
                        return false;
                    });
                    return $clone;
                }
                return $target.clone();
            },
            position: function(pos, finalOptions, $menu) {
                var placement = finalOptions.placement;
                var $toggle = finalOptions.$toggle;
                if (!placement)
                {
                        var $dropmenu = $menu.find('.dropdown-menu');
                        var pullRight = $dropmenu.hasClass('pull-right');
                        var dropUp = $toggle.parent().hasClass('dropup');
                        placement = pullRight ? (dropUp ? 'top-right' : 'bottom-right') : (dropUp ? 'top-left' : 'bottom-left');
                        if (pullRight) $dropmenu.removeClass('pull-right');
                }
                var bounds = $toggle[0].getBoundingClientRect();
                switch (placement) {
                    case 'top-left':
                        return {x: bounds.left, y: Math.floor(bounds.top - pos.height)};
                    case 'top-right':
                        return {x: Math.floor(bounds.right - pos.width), y: Math.floor(bounds.top - pos.height)};
                    case 'bottom-left':
                        return {x: bounds.left, y: bounds.bottom};
                    case 'bottom-right':
                        return {x: Math.floor(bounds.right - pos.width), y: bounds.bottom};
                }
                return pos;
            }
        }, options));
    };

    $(document).on('click', function(e) {
        var $target = $(e.target);
        var $toggle = $target.closest('[data-toggle="context-dropdown"]');
        if ($toggle.length) {
            var contextmenu = $toggle.data(NAME);
            if (!contextmenu) {
                $toggle.contextDropdown({show: true});
            }
        } else if (!isShowingMenu && !$target.closest('.contextmenu').length) {
            hideContextMenu();
        }
    });
}(jQuery, undefined));
<commit_msg>* refactor.<commit_after>/* ========================================================================
 * ZUI: contextmenu.js
 * http://openzui.com
 * ========================================================================
 * Copyright (c) 2017-2019 cnezsoft.com; Licensed MIT
 * ======================================================================== */

(function($, undefined) {
    'use strict';

    var NAME = 'zui.contextmenu'; // model name

    var DEFAULTS = {
        // onShow: null,
        // onShown: null,
        // onHide: null,
        // onHidden: null,
        // itemCreator: null,
        // x: 0,
        // y: 0,
        // onClickItem: null,
        // menuCreator: null,
        // position: null,
        animation: 'fade',
        menuTemplate: '

',
        toggleTrigger: false,
        duration: 200,
    };

    var isShowingMenu = false;
    var ContextMenu = {};
    var targetId = 'zui-contextmenu-' + $.zui.uuid();
    var mouseX = 0, mouseY = 0;
    var listenMouseMove = function() {
        $(document).off('mousemove.' + NAME).on('mousemove.' + NAME, function(e) {
            mouseX = e.clientX;
            mouseY = e.clientY;
        });
        return ContextMenu;
    };
    var createMenuItem = function(item, index) {
        if (typeof item === 'string') {
            if (item === 'seperator' || item === 'divider' || item === '-' || item === '|') {
                item = {type: 'seperator'};
            } else {
                item = {label: item, id: index};
            }
        }
        if (item.type === 'seperator' || item.type === 'divider') {
            return $('

');
        }
        var $a = $('').attr($.extend({
            href: item.url || '###',
            'class': item.className,
            style: item.style
        }, item.attrs)).data('item', item);
        if (item.html) {
            if (item.html === true) {
                $a.html(item.label || item.text);
            } else {
                $a = $(item.html);
            }
        } else {
            $a.text(item.label || item.text);
        }
        if(item.icon) {
            $a.prepend('');
        }
        if (item.onClick) {
            $a.on('click', item.onClick);
        }
        return $('
').toggleClass('disabled', item.disabled === true).append($a);
    };

    var isContextMenuShow = function(id) {
        var $target = $('#' + targetId);
        return $target.length && $target.hasClass('contextmenu-show') && (!id || ($target.data('options') || {}).id === id);
    };

    var animationTimer = null;
    var hideContextMenu = function(id, callback) {
        if (typeof id === 'function') {
            callback = id;
            id = null;
        }

        if (animationTimer) {
            clearTimeout(animationTimer);
            animationTimer = null;
        }

        var $target = $('#' + targetId);
        if ($target.length) {
            var options = $target.removeClass('contextmenu-show').data('options');
            if (!id || options.id === id) {
                var afterHide = function() {
                    $target.find('.contextmenu-menu').removeClass('open');
                    options.onHidden && options.onHidden();
                    callback && callback();
                };
                options.onHide && options.onHide();
                var animation = options.animation;
                $target.find('.contextmenu-menu').removeClass('in');
                if (animation) {
                    animationTimer = setTimeout(afterHide, options.duration);
                } else {
                    afterHide();
                }
            }
        }
        return ContextMenu;
    };

    var showContextMenu = function(items, options, callback) {
        if ($.isPlainObject(items)) {
            callback = options;
            options = items;
            items = options.items;
        }

        isShowingMenu = true;
        // hideContextMenu();

        options = $.extend({}, DEFAULTS, options);

        var $target = $('#' + targetId);
        if (!$target.length) {
            $target = $('

').appendTo('body');
        }
        var $menu = $target.find('.contextmenu-menu').off('click.' + NAME).on('click.' + NAME, 'a,.contextmenu-item', function(e) {
            var $item = $(this);
            var clickResult = options.onClickItem && options.onClickItem($item.data('item'), $item, e, options);
            if (clickResult !== false) {
                hideContextMenu();
            }
        }).empty();
        $menu.attr('class', 'contextmenu-menu' + (options.className ? (' ' + options.className) : ''))
        $target.attr('class', 'contextmenu contextmenu-show');

        // Create menu items
        var menuCreator = options.menuCreator;
        if (menuCreator) {
            $menu.append(menuCreator(items, options));
        } else {
            $menu.append(options.menuTemplate);
            var $menuList = $menu.children().first();
            var itemCreator = options.itemCreator || createMenuItem;
            var itemsType = typeof items;
            if (itemsType === 'string') {
                items = items.split(',');
            } else if (itemsType === 'function') {
                items = items(options);
            }
            if (!items) {
                return false;
            }
            $.each(items, function(index, item) {
                $menuList.append(itemCreator(item, index, options));
            });
        }

        // Show menu
        var animation = options.animation;
        var duration = options.duration;
        if (animation === true) options.animation = animation = 'fade';
        if (animationTimer) {
            clearTimeout(animationTimer);
            animationTimer = null;
        }
        var afterShow = function() {
            $menu.addClass('in');
            options.onShown && options.onShown();
            callback && callback();
        };
        options.onShow && options.onShow();

        $target.data('options', {
            animation: animation,
            onHide: options.onHide,
            onHidden: options.onHidden,
            id: options.id,
            duration: duration
        });

        var x = options.x;
        var y = options.y;
        if (x === undefined) x = (options.event || options).clientX;
        if (x === undefined) x = mouseX;
        if (y === undefined) y = (options.event || options).clientY;
        if (y === undefined) y = mouseY;
        var $menuList = $menu.children().first();
        var menuWidth = $menuList.outerWidth();
        var menuHeight = $menuList.outerHeight();
        if (options.position) {
            var newPos = options.position({x: x, y: y, width: menuWidth, height: menuHeight}, options, $menu);
            if (newPos) {
                x = newPos.x;
                y = newPos.y;
            }
        }
        if (options.limitInsideWindow) {
            var $w = $(window);
            x = Math.max(0, Math.min(x, $w.width() - menuWidth));
            y = Math.max(0, Math.min(y, $w.height() - menuHeight));
        }

        $target.css({
            left: x,
            top: y
        }).show();

        $menu.addClass('open');
        if (animation) {
            $menu.addClass(animation);
            animationTimer = setTimeout(function() {
                afterShow();
                isShowingMenu = false;
            }, 10);
        } else {
            afterShow();
            isShowingMenu = false;
        }
        return ContextMenu;
    };

    $.extend(ContextMenu, {
        NAME: NAME,
        DEFAULTS: DEFAULTS,
        show: showContextMenu,
        hide: hideContextMenu,
        listenMouse: listenMouseMove,
        isShow: isContextMenuShow
    });
    $.zui({ContextMenu: ContextMenu});

    // The contextmenu model class
    var ContextListener = function(element, options) {
        var that = this;
        that.name = NAME;
        that.$ = $(element);
        that.id = $.zui.uuid();
        options = that.options = $.extend({trigger: 'contextmenu'}, ContextMenu.DEFAULTS, this.$.data(), options);

        var eventHandler = function(e) {
            if (e.type === 'mousedown' && e.button !== 2) {
                return;
            }

            if (options.toggleTrigger && that.isShow()) {
                that.hide();
            } else {
                var config = {
                    x: e.clientX,
                    y: e.clientY,
                    event: e,
                };
                if (that.show(config) === false) {
                    return;
                }
            }
            e.preventDefault();
            e.returnValue = false; // 解决IE8右键弹出
            return false;
        };

        var trigger = options.trigger;
        var eventName = trigger + '.' + NAME;
        if (options.selector) {
            that.$.on(eventName, options.selector, eventHandler);
        } else {
            that.$.on(eventName, eventHandler);
        }

        if (options.show) {
            that.show(typeof options.show === 'object' ? options.show : null);
        }
    };

    ContextListener.prototype.destory = function () {
        that.$.off('.' + NAME);
    };

    ContextListener.prototype.hide = function (callback) {
        return ContextMenu.hide(this.id, callback);
    };

    ContextListener.prototype.show = function (options, callback) {
        options = $.extend({id: this.id, $toggle: this.$}, this.options, options);
        return ContextMenu.show(options, callback);
    };

    ContextListener.prototype.isShow = function () {
        return isContextMenuShow(this.id);
    };

    // Extense jquery element
    $.fn.contextmenu = function(option) {
        return this.each(function() {
            var $this = $(this);
            var data = $this.data(NAME);
            var options = typeof option == 'object' && option;

            if(!data) $this.data(NAME, (data = new ContextListener(this, options)));

            if(typeof option == 'string') data[option]();
        });
    };
    $.fn.contextmenu.Constructor = ContextListener;

    // Use dropdown menu as contextmenu
    $.fn.contextDropdown = function(options) {
        $(this).contextmenu($.extend({
            trigger: 'click',
            animation: 'fade',
            toggleTrigger: true,
            menuCreator: function(items, finalOptions) {
                var $toggle = finalOptions.$toggle;
                var selector = $toggle.attr('data-target')
                if(!selector) {
                    selector = $toggle.attr('href')
                    selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
                }
                var $target = selector ? $(selector) : $toggle.next('.dropdown-menu');
                var transferEvent = finalOptions.transferEvent;
                if (transferEvent !== false) {
                    var indexAttrName = 'data-contextmenu-index';
                    $target.find('a,.contextmenu-item').each(function(index) {
                        $(this).attr(indexAttrName, index);
                    });
                    var $clone = $target.clone();
                    $clone.on(typeof transferEvent === 'string' ? transferEvent : 'click', 'a,.contextmenu-item', function(event) {
                        var $item = $target.find('[' + indexAttrName + '="' + $(this).attr(indexAttrName) + '"]');
                        var item = $item[0];
                        if (!item) return;
                        if (item[event.type]) {
                            item[event.type]();
                        } else {
                            $item.trigger(event.type);
                        }
                        event.preventDefault();
                        event.stopPropagation();
                        return false;
                    });
                    return $clone;
                }
                return $target.clone();
            },
            position: function(pos, finalOptions, $menu) {
                var placement = finalOptions.placement;
                var $toggle = finalOptions.$toggle;
                if (!placement)
                {
                        var $dropmenu = $menu.find('.dropdown-menu');
                        var pullRight = $dropmenu.hasClass('pull-right');
                        var dropUp = $toggle.parent().hasClass('dropup');
                        placement = pullRight ? (dropUp ? 'top-right' : 'bottom-right') : (dropUp ? 'top-left' : 'bottom-left');
                        if (pullRight) $dropmenu.removeClass('pull-right');
                }
                var bounds = $toggle[0].getBoundingClientRect();
                switch (placement) {
                    case 'top-left':
                        return {x: bounds.left, y: Math.floor(bounds.top - pos.height)};
                    case 'top-right':
                        return {x: Math.floor(bounds.right - pos.width), y: Math.floor(bounds.top - pos.height)};
                    case 'bottom-left':
                        return {x: bounds.left, y: bounds.bottom};
                    case 'bottom-right':
                        return {x: Math.floor(bounds.right - pos.width), y: bounds.bottom};
                }
                return pos;
            }
        }, options));
    };

    $(document).on('click', function(e) {
        var $target = $(e.target);
        var $toggle = $target.closest('[data-toggle="context-dropdown"]');
        if ($toggle.length) {
            var contextmenu = $toggle.data(NAME);
            if (!contextmenu) {
                $toggle.contextDropdown({show: true});
            }
        } else if (!isShowingMenu && !$target.closest('.contextmenu').length) {
            hideContextMenu();
        }
    });
}(jQuery, undefined));
<|endoftext|>


<commit_before>import { $, Animation, isNumber, isString, query, requestAnimationFrame } from '../util/index';

export default function (UIkit) {

    UIkit.component('sticky', {

        props: {
            top: null,
            bottom: Boolean,
            offset: Number,
            animation: String,
            clsActive: String,
            clsInactive: String,
            widthElement: 'jQuery',
            showOnUp: Boolean,
            media: 'media',
            target: Number
        },

        defaults: {
            top: 0,
            bottom: false,
            offset: 0,
            animation: '',
            clsActive: 'uk-active',
            clsInactive: '',
            widthElement: false,
            showOnUp: false,
            media: false,
            target: false
        },

        ready() {

            this.placeholder = $('

').insertAfter(this.$el).attr('hidden', true);
            this.widthElement = this.widthElement || this.placeholder;
            this.topProp = this.top;
            this.bottomProp = this.bottom;

            var scroll = $(window).scrollTop();
            if (location.hash && scroll > 0 && this.target) {

                var target = query(location.hash);

                if (target) {
                    requestAnimationFrame(() => {

                        var top = target.offset().top,
                            elTop = this.$el.offset().top,
                            elHeight = this.$el.outerHeight(),
                            elBottom = elTop + elHeight;

                        if (elBottom >= top && elTop <= top + target.outerHeight()) {
                            window.scrollTo(0, top - elHeight - this.target - this.offset);
                        }

                    });
                }
            }

        },

        update: {

            handler({type, dir}) {

                var isActive = this.$el.hasClass(this.clsActive) && !this.$el.hasClass('uk-animation-leave'), el;

                if (type !== 'scroll') {

                    var outerHeight = this.$el.outerHeight();

                    this.placeholder.css({
                        height: this.$el.css('position') !== 'absolute' ? outerHeight : '',
                        marginTop: this.$el.css('marginTop'),
                        marginBottom: this.$el.css('marginBottom'),
                        marginLeft: this.$el.css('marginLeft'),
                        marginRight: this.$el.css('marginRight')
                    });

                    this.offsetTop = (isActive ? this.placeholder.offset() : this.$el.offset()).top;
                    this.offsetBottom = this.offsetTop + outerHeight;

                    ['top', 'bottom'].forEach(prop => {

                        this[prop] = this[`${prop}Prop`];

                        if (this[prop] && !isNumber(this[prop])) {

                            if (isString(this[prop]) && this[prop].match(/^-?\d+vh$/)) {
                                this[prop] = window.innerHeight * parseFloat(this[prop]) / 100;
                            } else {

                                el = this[prop] === true ? this.$el.parent() : query(this[prop], this.$el);

                                if (el) {
                                    this[prop] = el.offset().top + el.outerHeight();
                                }

                            }

                        }

                    });

                    this.top = Math.max(parseFloat(this.top), this.offsetTop) - this.offset;
                    this.bottom = this.bottom && this.bottom - outerHeight;

                    this.inactive = this.media && !window.matchMedia(this.media).matches;
                }

                var scroll = $(window).scrollTop();

                if (scroll < 0 || !this.$el.is(':visible') || this.disabled) {
                    return;
                }

                if (this.inactive
                    || scroll <= this.top
                    || this.showOnUp && (dir !== 'up' || dir === 'up' && !isActive && scroll <= this.offsetBottom)
                ) {
                    if (isActive) {

                        var hide = () => {
                            this.$el
                                .removeClass(this.clsActive)
                                .addClass(this.clsInactive)
                                .css({position: '', top: '', width: ''})
                                .trigger('inactive');
                            this.placeholder.attr('hidden', true);
                        };

                        if (this.animation && this.offsetBottom < this.$el.offset().top) {
                            Animation.cancel(this.$el).out(this.$el, this.animation).then(hide);
                        } else {
                            hide();
                        }

                    }

                    return;
                }

                this.placeholder.attr('hidden', false);

                var top = Math.max(0, this.offset);
                if (this.bottom && scroll > this.bottom - this.offset) {
                    top = this.bottom - scroll;
                }

                this.$el.css({
                    position: 'fixed',
                    top: top + 'px',
                    width: this.widthElement[0].getBoundingClientRect().width
                });

                if (isActive) {
                    return;
                }

                if (this.animation) {
                    Animation.cancel(this.$el).in(this.$el, this.animation);
                }

                this.$el
                    .addClass(this.clsActive)
                    .removeClass(this.clsInactive)
                    .trigger('active');

            },

            events: ['scroll', 'load', 'resize', 'orientationchange']

        }

    });

}
<commit_msg>revert sticky fix<commit_after>import { $, Animation, isNumber, isString, query, requestAnimationFrame } from '../util/index';

export default function (UIkit) {

    UIkit.component('sticky', {

        props: {
            top: null,
            bottom: Boolean,
            offset: Number,
            animation: String,
            clsActive: String,
            clsInactive: String,
            widthElement: 'jQuery',
            showOnUp: Boolean,
            media: 'media',
            target: Number
        },

        defaults: {
            top: 0,
            bottom: false,
            offset: 0,
            animation: '',
            clsActive: 'uk-active',
            clsInactive: '',
            widthElement: false,
            showOnUp: false,
            media: false,
            target: false
        },

        ready() {

            this.placeholder = $('

').insertAfter(this.$el).attr('hidden', true);
            this.widthElement = this.widthElement || this.placeholder;
            this.topProp = this.top;
            this.bottomProp = this.bottom;

            var scroll = $(window).scrollTop();
            if (location.hash && scroll > 0 && this.target) {

                var target = query(location.hash);

                if (target) {
                    requestAnimationFrame(() => {

                        var top = target.offset().top,
                            elTop = this.$el.offset().top,
                            elHeight = this.$el.outerHeight(),
                            elBottom = elTop + elHeight;

                        if (elBottom >= top && elTop <= top + target.outerHeight()) {
                            window.scrollTo(0, top - elHeight - this.target - this.offset);
                        }

                    });
                }
            }

        },

        update: {

            handler({type, dir}) {

                var isActive = this.$el.hasClass(this.clsActive) && !this.$el.hasClass('uk-animation-leave'), el;

                if (type !== 'scroll') {

                    var outerHeight = this.$el.outerHeight();

                    this.placeholder.css({
                        height: this.$el.css('position') !== 'absolute' ? outerHeight : '',
                        marginTop: this.$el.css('marginTop'),
                        marginBottom: this.$el.css('marginBottom'),
                        marginLeft: this.$el.css('marginLeft'),
                        marginRight: this.$el.css('marginRight')
                    });

                    this.offsetTop = (isActive ? this.placeholder.offset() : this.$el.offset()).top;
                    this.offsetBottom = this.offsetTop + outerHeight;

                    ['top', 'bottom'].forEach(prop => {

                        this[prop] = this[`${prop}Prop`];

                        if (this[prop] && !isNumber(this[prop])) {

                            if (isString(this[prop]) && this[prop].match(/^-?\d+vh$/)) {
                                this[prop] = window.innerHeight * parseFloat(this[prop]) / 100;
                            } else {

                                el = this[prop] === true ? this.$el.parent() : query(this[prop], this.$el);

                                if (el) {
                                    this[prop] = el.offset().top + el.outerHeight();
                                }

                            }

                        }

                    });

                    this.top = Math.max(parseFloat(this.top), this.offsetTop) - this.offset;
                    this.bottom = this.bottom && this.bottom - outerHeight;

                    this.inactive = this.media && !window.matchMedia(this.media).matches;
                }

                var scroll = $(window).scrollTop();

                if (scroll < 0 || !this.$el.is(':visible') || this.disabled) {
                    return;
                }

                if (this.inactive
                    || scroll < this.top
                    || this.showOnUp && (dir !== 'up' || dir === 'up' && !isActive && scroll <= this.offsetBottom)
                ) {
                    if (isActive) {

                        var hide = () => {
                            this.$el
                                .removeClass(this.clsActive)
                                .addClass(this.clsInactive)
                                .css({position: '', top: '', width: ''})
                                .trigger('inactive');
                            this.placeholder.attr('hidden', true);
                        };

                        if (this.animation && this.offsetBottom < this.$el.offset().top) {
                            Animation.cancel(this.$el).out(this.$el, this.animation).then(hide);
                        } else {
                            hide();
                        }

                    }

                    return;
                }

                this.placeholder.attr('hidden', false);

                var top = Math.max(0, this.offset);
                if (this.bottom && scroll > this.bottom - this.offset) {
                    top = this.bottom - scroll;
                }

                this.$el.css({
                    position: 'fixed',
                    top: top + 'px',
                    width: this.widthElement[0].getBoundingClientRect().width
                });

                if (isActive) {
                    return;
                }

                if (this.animation) {
                    Animation.cancel(this.$el).in(this.$el, this.animation);
                }

                this.$el
                    .addClass(this.clsActive)
                    .removeClass(this.clsInactive)
                    .trigger('active');

            },

            events: ['scroll', 'load', 'resize', 'orientationchange']

        }

    });

}
<|endoftext|>


<commit_before>import {isWithin} from '../util/index';

export default function (UIkit) {

    UIkit.component('toggle', {

        mixins: [UIkit.mixin.toggable],

        props: {
            href: 'jQuery',
            target: 'jQuery'
        },

        defaults: {
            href: false,
            target: false
        },

        ready() {

            this.target = this.target || this.href;

            if (!this.target) {
                return;
            }

            this.target.on('toggle', (e, toggle) => {
                if (this.$el === toggle && !e.isDefaultPrevented()) {
                    this.toggleElement(e.target);
                }
            })

        },

        events: {

            click(e) {

                if ($(e.target).closest('a').attr('href')[0] === '#') {
                    e.preventDefault();
                }

                this.toggle();
            },

            mouseenter() {
                this.target.trigger('toggleenter', [this.$el]);
            },

            mouseleave() {
                this.target.trigger('toggleleave', [this.$el]);
            }

        },

        methods: {

            toggle() {
                this.target.trigger('toggle', [this.$el]);
            }

        }

    });

}
<commit_msg>toggle fix<commit_after>import $ from 'jquery';

export default function (UIkit) {

    UIkit.component('toggle', {

        mixins: [UIkit.mixin.toggable],

        props: {
            href: 'jQuery',
            target: 'jQuery'
        },

        defaults: {
            href: false,
            target: false
        },

        ready() {
            this.target = this.target || this.href;
        },

        events: {

            click(e) {

                if ($(e.target).closest('a').attr('href')[0] === '#') {
                    e.preventDefault();
                }

                this.toggle();
            },

            mouseenter() {
                this.target.trigger('toggleenter', [this.$el]);
            },

            mouseleave() {
                this.target.trigger('toggleleave', [this.$el]);
            }

        },

        methods: {

            toggle() {
                var event = $.Event('toggle');
                this.target.trigger(event, [this.$el]);

                if (!event.isDefaultPrevented()) {
                    this.toggleElement(event.target);
                }
            }

        }

    });

}
<|endoftext|>


<commit_before>/**
= Fontana Datasources =

There are three different types of Datasources:

 * Fontana.datasources.Static
 * Fontana.datasources.Twitter
 * Fontana.datasources.HTML

The datasource interface consist of the constructor taking one parameter.

The datastore provides an instance method `getMessages` that will trigger
a 'messages' event every time there are new messages.

Consumers can listen to the `messages` event by binding a callable that
takes one parameter, an array of messages.

Each datasource has a `stop` method. Calling this method will stop
the datasource from refreshing. This methods is a no-op for the Static
and HTML datasource, since they don't refresh anyway.

Calling `getMessags` will restart a stopped datasource.

== Static ==

A static datasource simply takes a hardcoded array of messages.

The messages themselves are javascript objects modeled after the JSON
response objects returned by http://search.twitter.com/search.json.

The following object describes a bare bones message:

    {
       'created_at': new Date().toString(),
       'text': 'A fake Tweet, in a fake JSON response',
       'from_user': 'tweetfontana',
       'profile_image_url': 'http://api.twitter.com/1/users/profile_image/tweetfontana'
   }

The static datasource will only trigger the `messages` event after
calling `getMessages`. Updating the set of messags is not supported.

== Twitter ==

The Twitter datasource takes a query that's used for
http://search.twitter.com/search.json.

Repeated calls to the datasource will return the most recent set of
tweets for the configured query.

After calling `getMessages` this datasource will poll Twitter for
new messages, triggering the `messages` event each time there are new
messages.

== HTML ==

Given a HTML node this datasource will try to find messages
by looking for the following HTML:

    div class="fontana-message">
        <q>This is not a real Tweet, but it sure looks like one.</q>
        tweetfontana

This is only done once on initialisation, this means that the HTML
datasource triggers the `messages` event each time `getMessages`
is called but will not detect any new messages added to the container.
*/

var Fontana = window.Fontana || {};

Fontana.datasources = (function ($) {
    var Static, Twitter, HTML;

    /**
     * Static datasource.
     *
     * Constructor takes an array of message objects.
     */
    Static = function (data) {
        this.data = data;
    };

    Static.prototype.getMessages = function () {
        this.trigger('messages', this.data);
    };

    Static.prototype.stop = function () {};

    window.MicroEvent.mixin(Static);

    /**
     * Twitter datasource
     *
     * Constructor takes a query for Twitter's search API.
     */
    Twitter = function (q) {
        this.search_url = 'http://search.twitter.com/search.json?result_type=recent&include_entities=true&callback=?';
        this.q = q;
        this.since_id = 0;
        this.refreshTimeout;
    };

    Twitter.prototype.getMessages = function () {
        var self = this;
        $.getJSON(this.search_url, {q: this.q, since_id: this.since_id}, function (data, status) {
            if (status === 'success') {
                if (data.results && data.results.length) {
                    self.updateSinceId(data.results);
                    self.trigger('messages', data.results.reverse());
                }
            }
            self.refreshTimeout = window.setTimeout(function () {
                self.getMessages.call(self)
            }, 45 * 1000);
        });
    };

    Twitter.prototype.stop = function () {
        window.clearTimeout(this.refreshTimeout);
    };

    Twitter.prototype.updateSinceId = function (messages) {
        this.since_id = messages[0].id;
    }

    window.MicroEvent.mixin(Twitter);

    /**
     * HTML datasource
     *
     * Constructor takes a html node with "Tweets".
     */
     HTML = function (node) {
        this.node = node;
        this.data = [];
        this.parseMessages();
     };

     HTML.prototype.parseMessages = function () {
        var self = this;
        $('.fontana-message', this.node).each(function () {
            self.data.push({
                'created_at': new Date().toString(),
                'text': $(this).find('q').text(),
                'from_user': $(this).find('cite').text(),
                'profile_image_url': 'http://api.twitter.com/1/users/profile_image/' + $(this).find('cite').text()
            });
        });
     };

     HTML.prototype.getMessages = function () {
        this.trigger('messages', this.data);
     };

    HTML.prototype.stop = function () {};

    window.MicroEvent.mixin(HTML);

    return {
        'Static': Static,
        'Twitter': Twitter,
        'HTML': HTML
    };

}(window.jQuery));
<commit_msg>Since id needs to be higher than the last seen id.<commit_after>/**
= Fontana Datasources =

There are three different types of Datasources:

 * Fontana.datasources.Static
 * Fontana.datasources.Twitter
 * Fontana.datasources.HTML

The datasource interface consist of the constructor taking one parameter.

The datastore provides an instance method `getMessages` that will trigger
a 'messages' event every time there are new messages.

Consumers can listen to the `messages` event by binding a callable that
takes one parameter, an array of messages.

Each datasource has a `stop` method. Calling this method will stop
the datasource from refreshing. This methods is a no-op for the Static
and HTML datasource, since they don't refresh anyway.

Calling `getMessags` will restart a stopped datasource.

== Static ==

A static datasource simply takes a hardcoded array of messages.

The messages themselves are javascript objects modeled after the JSON
response objects returned by http://search.twitter.com/search.json.

The following object describes a bare bones message:

    {
       'created_at': new Date().toString(),
       'text': 'A fake Tweet, in a fake JSON response',
       'from_user': 'tweetfontana',
       'profile_image_url': 'http://api.twitter.com/1/users/profile_image/tweetfontana'
   }

The static datasource will only trigger the `messages` event after
calling `getMessages`. Updating the set of messags is not supported.

== Twitter ==

The Twitter datasource takes a query that's used for
http://search.twitter.com/search.json.

Repeated calls to the datasource will return the most recent set of
tweets for the configured query.

After calling `getMessages` this datasource will poll Twitter for
new messages, triggering the `messages` event each time there are new
messages.

== HTML ==

Given a HTML node this datasource will try to find messages
by looking for the following HTML:

    div class="fontana-message">
        <q>This is not a real Tweet, but it sure looks like one.</q>
        tweetfontana

This is only done once on initialisation, this means that the HTML
datasource triggers the `messages` event each time `getMessages`
is called but will not detect any new messages added to the container.
*/

var Fontana = window.Fontana || {};

Fontana.datasources = (function ($) {
    var Static, Twitter, HTML;

    /**
     * Static datasource.
     *
     * Constructor takes an array of message objects.
     */
    Static = function (data) {
        this.data = data;
    };

    Static.prototype.getMessages = function () {
        this.trigger('messages', this.data);
    };

    Static.prototype.stop = function () {};

    window.MicroEvent.mixin(Static);

    /**
     * Twitter datasource
     *
     * Constructor takes a query for Twitter's search API.
     */
    Twitter = function (q) {
        this.search_url = 'http://search.twitter.com/search.json?result_type=recent&include_entities=true&callback=?';
        this.q = q;
        this.since_id = 0;
        this.refreshTimeout;
    };

    Twitter.prototype.getMessages = function () {
        var self = this;
        $.getJSON(this.search_url, {q: this.q, since_id: this.since_id}, function (data, status) {
            if (status === 'success') {
                if (data.results && data.results.length) {
                    self.updateSinceId(data.results);
                    self.trigger('messages', data.results.reverse());
                }
            }
            self.refreshTimeout = window.setTimeout(function () {
                self.getMessages.call(self)
            }, 45 * 1000);
        });
    };

    Twitter.prototype.stop = function () {
        window.clearTimeout(this.refreshTimeout);
    };

    Twitter.prototype.updateSinceId = function (messages) {
        this.since_id = messages[0].id + 1;
    }

    window.MicroEvent.mixin(Twitter);

    /**
     * HTML datasource
     *
     * Constructor takes a html node with "Tweets".
     */
     HTML = function (node) {
        this.node = node;
        this.data = [];
        this.parseMessages();
     };

     HTML.prototype.parseMessages = function () {
        var self = this;
        $('.fontana-message', this.node).each(function () {
            self.data.push({
                'created_at': new Date().toString(),
                'text': $(this).find('q').text(),
                'from_user': $(this).find('cite').text(),
                'profile_image_url': 'http://api.twitter.com/1/users/profile_image/' + $(this).find('cite').text()
            });
        });
     };

     HTML.prototype.getMessages = function () {
        this.trigger('messages', this.data);
     };

    HTML.prototype.stop = function () {};

    window.MicroEvent.mixin(HTML);

    return {
        'Static': Static,
        'Twitter': Twitter,
        'HTML': HTML
    };

}(window.jQuery));
<|endoftext|>