<commit_before>import ContextMenu from "./ContextMenu.js";
import EventRaiser from "../shared/EventRaiser.mjs";

/*

 *

 *

 *

 */

export class Tab extends EventRaiser
{
  constructor(identifier, content, tabElement, container)
  {
    super();

    this.identifier = identifier;
    this.content = content;
    this.tabElement = tabElement;
    this.container = container;
    this.locked = false;

    this.nameElement = document.createElement("span");
    this.closeElement = document.createElement("span");

    this.tabElement.className = "tab";
    this.tabElement.addEventListener("contextmenu", (e) => this.onContextMenu(e));
    this.tabElement.addEventListener("mouseup", (e) => this.onMouseUp(e));

    this.nameElement.className = "name";

    this.closeElement.className = "close";
    this.closeElement.innerHTML = "x";
    this.closeElement.addEventListener("click", () => this.destroy());

    this.tabElement.appendChild(this.nameElement);
    this.tabElement.appendChild(this.closeElement);

    this.addEvent("active");
    this.addEvent("resize");
    this.addEvent("contextmenu");
    this.addEvent("close");
  }

  get isActive()
  {
    return this.container.activeTab == this;
  }

  get hoverText()
  {
    return this.tabElement.title;
  }

  set hoverText(value)
  {
    this.tabElement.title = value;
  }

  lock()
  {
    this.closeElement.style.display = "none";
    this.locked = true;
  }

  get name()
  {
    return this.nameElement.innerText;
  }

  set name(value)
  {
    this.nameElement.innerText = value;
  }

  onContextMenu(e)
  {
    let menu = new ContextMenu(e.clientX, e.clientY);

    menu.addButton("Close", () => this.destroy());
    menu.addButton("Close Others", () => this.closeOthers());
    menu.addButton("Close All", () => this.closeAll());

    menu.appendToElement(document.body);

    e.preventDefault();
  }

  closeOthers()
  {
    let tabs = [...this.container.tabs];

    for(let tab of tabs)
      if(tab != this)
        tab.destroy();
  }

  closeAll()
  {
    let tabs = [...this.container.tabs];

    for(let tab of tabs)
      tab.destroy();
  }

  onMouseUp(e)
  {
    switch(e.which) {
    case 1:
      this.makeActive();
      break;
    case 2:
      if(!this.locked)
        this.destroy();
      break;
    }
  }

  makeActive()
  {
    if(this.container.activeTab != undefined) {
      this.container.activeTab.content.style.display = "none";
      this.container.activeTab.tabElement.className = "tab";
    }

    this.container.activeTab = this;
    this.content.style.display = "";
    this.tabElement.className = "tab active";

    this.container.triggerEvent("swap", this);
    this.triggerEvent("active");
  }

  resized()
  {
    this.triggerEvent("resize");
  }

  destroy()
  {
    let index = this.container.tabs.indexOf(this);

    if(this.isActive && this.container.tabs.length > 1) {
      let activeIndex = index == 0 ? 1 : index - 1;

      this.container.tabs[activeIndex].makeActive();
    }

    this.tabElement.remove();
    this.content.remove();
    this.container.tabs.splice(index, 1);

    this.triggerEvent("close");
  }
}

export class TabbedContainer extends EventRaiser
{
  // css selector or element object
  constructor(element)
  {
    super();

    if(typeof element == "string")
      element = document.querySelector(element);

    this.element = element;
    this.activeTab = undefined;
    this.tabs = [];

    this.tabcontainer = element.querySelector(".tab-container");
    this.tabcontent = element.querySelector(".tab-content");

    this.addEvent("swap");
    this.addEvent("resize");
  }

  get length()
  {
    return this.tabs.length;
  }

  addTab(identifier, name, content)
  {
    let tabElement = document.createElement("div");
    let tab = new Tab(identifier, content, tabElement, this);

    tab.name = name;
    content.style.display = "none";

    this.tabcontainer.appendChild(tabElement);
    this.tabcontent.appendChild(content);

    this.tabs.push(tab);

    return tab;
  }

  removeTab(identifier)
  {
    let tab = this.getTab(identifier);
    tab.destroy()
  }

  getTab(identifier)
  {
    for(let tab of this.tabs)
      if(tab.identifier == identifier)
          return tab;

    return undefined;
  }

  resized()
  {
    if(this.activeTab != null)
      this.activeTab.resized();

    this.triggerEvent("resize");
  }
}<commit_msg>Fix resizing bug caused by non active tabs not adjusting when resized<commit_after>import ContextMenu from "./ContextMenu.js";
import EventRaiser from "../shared/EventRaiser.mjs";

/*

 *

 *

 *

 */

export class Tab extends EventRaiser
{
  constructor(identifier, content, tabElement, container)
  {
    super();

    this.identifier = identifier;
    this.content = content;
    this.tabElement = tabElement;
    this.container = container;
    this.locked = false;

    this.nameElement = document.createElement("span");
    this.closeElement = document.createElement("span");

    this.tabElement.className = "tab";
    this.tabElement.addEventListener("contextmenu", (e) => this.onContextMenu(e));
    this.tabElement.addEventListener("mouseup", (e) => this.onMouseUp(e));

    this.nameElement.className = "name";

    this.closeElement.className = "close";
    this.closeElement.innerHTML = "x";
    this.closeElement.addEventListener("click", () => this.destroy());

    this.tabElement.appendChild(this.nameElement);
    this.tabElement.appendChild(this.closeElement);

    this.addEvent("active");
    this.addEvent("resize");
    this.addEvent("contextmenu");
    this.addEvent("close");
  }

  get isActive()
  {
    return this.container.activeTab == this;
  }

  get hoverText()
  {
    return this.tabElement.title;
  }

  set hoverText(value)
  {
    this.tabElement.title = value;
  }

  lock()
  {
    this.closeElement.style.display = "none";
    this.locked = true;
  }

  get name()
  {
    return this.nameElement.innerText;
  }

  set name(value)
  {
    this.nameElement.innerText = value;
  }

  onContextMenu(e)
  {
    let menu = new ContextMenu(e.clientX, e.clientY);

    menu.addButton("Close", () => this.destroy());
    menu.addButton("Close Others", () => this.closeOthers());
    menu.addButton("Close All", () => this.closeAll());

    menu.appendToElement(document.body);

    e.preventDefault();
  }

  closeOthers()
  {
    let tabs = [...this.container.tabs];

    for(let tab of tabs)
      if(tab != this)
        tab.destroy();
  }

  closeAll()
  {
    let tabs = [...this.container.tabs];

    for(let tab of tabs)
      tab.destroy();
  }

  onMouseUp(e)
  {
    switch(e.which) {
    case 1:
      this.makeActive();
      break;
    case 2:
      if(!this.locked)
        this.destroy();
      break;
    }
  }

  makeActive()
  {
    if(this.container.activeTab != undefined) {
      this.container.activeTab.content.style.display = "none";
      this.container.activeTab.tabElement.className = "tab";
    }

    this.container.activeTab = this;
    this.content.style.display = "";
    this.tabElement.className = "tab active";

    this.container.triggerEvent("swap", this);
    this.triggerEvent("active");
    this.resized();
  }

  resized()
  {
    this.triggerEvent("resize");
  }

  destroy()
  {
    let index = this.container.tabs.indexOf(this);

    if(this.isActive && this.container.tabs.length > 1) {
      let activeIndex = index == 0 ? 1 : index - 1;

      this.container.tabs[activeIndex].makeActive();
    }

    this.tabElement.remove();
    this.content.remove();
    this.container.tabs.splice(index, 1);

    this.triggerEvent("close");
  }
}

export class TabbedContainer extends EventRaiser
{
  // css selector or element object
  constructor(element)
  {
    super();

    if(typeof element == "string")
      element = document.querySelector(element);

    this.element = element;
    this.activeTab = undefined;
    this.tabs = [];

    this.tabcontainer = element.querySelector(".tab-container");
    this.tabcontent = element.querySelector(".tab-content");

    this.addEvent("swap");
    this.addEvent("resize");
  }

  get length()
  {
    return this.tabs.length;
  }

  addTab(identifier, name, content)
  {
    let tabElement = document.createElement("div");
    let tab = new Tab(identifier, content, tabElement, this);

    tab.name = name;
    content.style.display = "none";

    this.tabcontainer.appendChild(tabElement);
    this.tabcontent.appendChild(content);

    this.tabs.push(tab);

    return tab;
  }

  removeTab(identifier)
  {
    let tab = this.getTab(identifier);
    tab.destroy()
  }

  getTab(identifier)
  {
    for(let tab of this.tabs)
      if(tab.identifier == identifier)
          return tab;

    return undefined;
  }

  resized()
  {
    if(this.activeTab != null)
      this.activeTab.resized();

    this.triggerEvent("resize");
  }
}<|endoftext|>


<commit_before>var EventEmitter = require("events").EventEmitter;

function nop () {}

exports.OT = function (opts) {
    var store = opts.store || require("./stores/memory-store"),
        manager = new EventEmitter();

    manager.newDocument = function (callback) {
        callback = callback || nop;
        store.newDocument(function (err, doc) {
            if ( err ) {
                this.emit("error", err);
                return callback(err, null);
            } else {
                this.emit("new", doc);
                return callback(null, doc);
            }
        }.bind(this));
    };

    manager.applyOperations = function (message) {
        var id = message.id,
            parentRev = message.rev,
            ops = message.ops,
            emit = this.emit.bind(this);

        store.getDocument(id, function (err, doc) {
            if ( err ) {
                emit("error", err);
            } else {
                if ( rev === doc.rev ) {
                    try {
                        doc.doc = apply(ops, doc.doc);
                    } catch (err) {
                        emit("error", err);
                        return;
                    }

                    doc.rev++;
                    store.saveDocument(doc, function (err, doc) {
                        if ( err ) {
                            // Bad revisions aren't considered an error at this
                            // level, just ignored.
                            if ( ! (err instanceof errors.BadRevision) ) {
                                emit("error", err);
                            }
                        } else {
                            emit("update", doc);
                        }
                    });
                }
            }
        });
    };

    return manager;
};
<commit_msg>Piecing lost work back together :( ... making ot.js use the proper AMD format<commit_after>define(['events'], function (events) {

    function nop () {}

    return function (opts) {
        var store = opts.store || require("./stores/memory-store"),
            manager = new events.EventEmitter();

        manager.newDocument = function (callback) {
            callback = callback || nop;
            store.newDocument(function (err, doc) {
                if ( err ) {
                    this.emit("error", err);
                    return callback(err, null);
                } else {
                    this.emit("new", doc);
                    return callback(null, doc);
                }
            }.bind(this));
        };

        manager.applyOperations = function (message) {
            var id = message.id,
                parentRev = message.rev,
                ops = message.ops,
                emit = this.emit.bind(this);

            store.getDocument(id, function (err, doc) {
                if ( err ) {
                    emit("error", err);
                } else {
                    if ( rev === doc.rev ) {
                        try {
                            doc.doc = apply(ops, doc.doc);
                        } catch (err) {
                            emit("error", err);
                            return;
                        }

                        doc.rev++;
                        store.saveDocument(doc, function (err, doc) {
                            if ( err ) {
                                // Bad revisions aren't considered an error at this
                                // level, just ignored.
                                if ( ! (err instanceof errors.BadRevision) ) {
                                    emit("error", err);
                                }
                            } else {
                                emit("update", doc);
                            }
                        });
                    }
                }
            });
        };

        return manager;
    };

});<|endoftext|>


<commit_before>function API() {
	this.verison = 0.1;
	this.preferences = SeleniumIDE.Preferences;
};

// add the provided chrome url to the list of user extensions provided through plugins
// -- or not if it already exists
API.prototype.addPluginProvidedUserExtension = function(url) {
	var options = {};
	current = this.preferences.getString("pluginProvidedUserExtensions");
	if (typeof current == 'undefined'){
 		options["pluginProvidedUserExtensions"] = url;
		this.preferences.save(options, "pluginProvidedUserExtensions");
	} else {
 		if (current.search(url) == -1) {
 			options["pluginProvidedUserExtensions"] = current + ' ' + url;
			this.preferences.save(options, "pluginProvidedUserExtensions");
		}
	}
};<commit_msg>tabs to spaces (forgot to save before the last commit apparently)<commit_after>function API() {
    this.verison = 0.1;
    this.preferences = SeleniumIDE.Preferences;
};

// add the provided chrome url to the list of user extensions provided through plugins
// -- or not if it already exists
API.prototype.addPluginProvidedUserExtension = function(url) {
    var options = {};
    current = this.preferences.getString("pluginProvidedUserExtensions");
    if (typeof current == 'undefined'){
        options["pluginProvidedUserExtensions"] = url;
        this.preferences.save(options, "pluginProvidedUserExtensions");
    } else {
        if (current.search(url) == -1) {
            options["pluginProvidedUserExtensions"] = current + ' ' + url;
            this.preferences.save(options, "pluginProvidedUserExtensions");
        }
    }
};<|endoftext|>


<commit_before>/**
* _Internal Class_, not generally used outside of the engine's internals.
*
* @class Common
*/

var Common = {};

(function() {

    /**
     * Description
     * @method extend
     * @param {} obj
     * @param {boolean} deep
     * @return {} obj extended
     */
    Common.extend = function(obj, deep) {
        var argsStart,
            args,
            deepClone;

        if (typeof deep === 'boolean') {
            argsStart = 2;
            deepClone = deep;
        } else {
            argsStart = 1;
            deepClone = true;
        }

        args = Array.prototype.slice.call(arguments, argsStart);

        for (var i = 0; i < args.length; i++) {
            var source = args[i];

            if (source) {
                for (var prop in source) {
                    if (deepClone && source[prop].constructor === Object) {
                        if (!obj[prop] || obj[prop].constructor === Object) {
                            obj[prop] = obj[prop] || {};
                            Common.extend(obj[prop], deepClone, source[prop]);
                        } else {
                            obj[prop] = source[prop];
                        }
                    } else {
                        obj[prop] = source[prop];
                    }
                }
            }
        }

        return obj;
    };

    /**
     * Creates a new clone of the object, if deep is true references will also be cloned
     * @method clone
     * @param {} obj
     * @param {bool} deep
     * @return {} obj cloned
     */
    Common.clone = function(obj, deep) {
        return Common.extend({}, deep, obj);
    };

    /**
     * Description
     * @method keys
     * @param {} obj
     * @return {string[]} keys
     */
    Common.keys = function(obj) {
        if (Object.keys)
            return Object.keys(obj);

        // avoid hasOwnProperty for performance
        var keys = [];
        for (var key in obj)
            keys.push(key);
        return keys;
    };

    /**
     * Description
     * @method values
     * @param {} obj
     * @return {array} Array of the objects property values
     */
    Common.values = function(obj) {
        var values = [];

        if (Object.keys) {
            var keys = Object.keys(obj);
            for (var i = 0; i < keys.length; i++) {
                values.push(obj[keys[i]]);
            }
            return values;
        }

        // avoid hasOwnProperty for performance
        for (var key in obj)
            values.push(obj[key]);
        return values;
    };

    /**
     * Description
     * @method shadeColor
     * @param {string} color
     * @param {number} percent
     * @return {string} A hex colour string made by lightening or darkening color by percent
     */
    Common.shadeColor = function(color, percent) {
        // http://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color
        var colorInteger = parseInt(color.slice(1),16),
            amount = Math.round(2.55 * percent),
            R = (colorInteger >> 16) + amount,
            B = (colorInteger >> 8 & 0x00FF) + amount,
            G = (colorInteger & 0x0000FF) + amount;
        return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R :255) * 0x10000
                + (B < 255 ? B < 1 ? 0 : B : 255) * 0x100
                + (G < 255 ? G < 1 ? 0 : G : 255)).toString(16).slice(1);
    };

    /**
     * Description
     * @method shuffle
     * @param {array} array
     * @return {array} array shuffled randomly
     */
    Common.shuffle = function(array) {
        for (var i = array.length - 1; i > 0; i--) {
            var j = Math.floor(Math.random() * (i + 1));
            var temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
        return array;
    };

    /**
     * Description
     * @method choose
     * @param {array} choices
     * @return {object} A random choice object from the array
     */
    Common.choose = function(choices) {
        return choices[Math.floor(Math.random() * choices.length)];
    };

    /**
     * Description
     * @method isElement
     * @param {object} obj
     * @return {boolean} True if the object is a HTMLElement, otherwise false
     */
    Common.isElement = function(obj) {
        // http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
        try {
            return obj instanceof HTMLElement;
        }
        catch(e){
            return (typeof obj==="object") &&
              (obj.nodeType===1) && (typeof obj.style === "object") &&
              (typeof obj.ownerDocument ==="object");
        }
    };

    /**
     * Description
     * @method clamp
     * @param {number} value
     * @param {number} min
     * @param {number} max
     * @return {number} The value clamped between min and max inclusive
     */
    Common.clamp = function(value, min, max) {
        if (value < min)
            return min;
        if (value > max)
            return max;
        return value;
    };

    /**
     * Description
     * @method sign
     * @param {number} value
     * @return {number} -1 if negative, +1 if 0 or positive
     */
    Common.sign = function(value) {
        return value < 0 ? -1 : 1;
    };

    /**
     * Description
     * @method now
     * @return {number} the current timestamp (high-res if avaliable)
     */
    Common.now = function() {
        // http://stackoverflow.com/questions/221294/how-do-you-get-a-timestamp-in-javascript
        // https://gist.github.com/davidwaterston/2982531

        var perf = window.performance;

        if (perf) {
            perf.now = perf.now || perf.webkitNow || perf.msNow || perf.oNow || perf.mozNow;
            return +(perf.now());
        }

        return +(new Date());
    };

    /**
     * Description
     * @method random
     * @param {number} min
     * @param {number} max
     * @return {number} A random number between min and max inclusive
     */
    Common.random = function(min, max) {
        return min + Math.random() * (max - min);
    };

})();<commit_msg>fixed issue with extending null properties<commit_after>/**
* _Internal Class_, not generally used outside of the engine's internals.
*
* @class Common
*/

var Common = {};

(function() {

    /**
     * Description
     * @method extend
     * @param {} obj
     * @param {boolean} deep
     * @return {} obj extended
     */
    Common.extend = function(obj, deep) {
        var argsStart,
            args,
            deepClone;

        if (typeof deep === 'boolean') {
            argsStart = 2;
            deepClone = deep;
        } else {
            argsStart = 1;
            deepClone = true;
        }

        args = Array.prototype.slice.call(arguments, argsStart);

        for (var i = 0; i < args.length; i++) {
            var source = args[i];

            if (source) {
                for (var prop in source) {
                    if (deepClone && source[prop] && source[prop].constructor === Object) {
                        if (!obj[prop] || obj[prop].constructor === Object) {
                            obj[prop] = obj[prop] || {};
                            Common.extend(obj[prop], deepClone, source[prop]);
                        } else {
                            obj[prop] = source[prop];
                        }
                    } else {
                        obj[prop] = source[prop];
                    }
                }
            }
        }

        return obj;
    };

    /**
     * Creates a new clone of the object, if deep is true references will also be cloned
     * @method clone
     * @param {} obj
     * @param {bool} deep
     * @return {} obj cloned
     */
    Common.clone = function(obj, deep) {
        return Common.extend({}, deep, obj);
    };

    /**
     * Description
     * @method keys
     * @param {} obj
     * @return {string[]} keys
     */
    Common.keys = function(obj) {
        if (Object.keys)
            return Object.keys(obj);

        // avoid hasOwnProperty for performance
        var keys = [];
        for (var key in obj)
            keys.push(key);
        return keys;
    };

    /**
     * Description
     * @method values
     * @param {} obj
     * @return {array} Array of the objects property values
     */
    Common.values = function(obj) {
        var values = [];

        if (Object.keys) {
            var keys = Object.keys(obj);
            for (var i = 0; i < keys.length; i++) {
                values.push(obj[keys[i]]);
            }
            return values;
        }

        // avoid hasOwnProperty for performance
        for (var key in obj)
            values.push(obj[key]);
        return values;
    };

    /**
     * Description
     * @method shadeColor
     * @param {string} color
     * @param {number} percent
     * @return {string} A hex colour string made by lightening or darkening color by percent
     */
    Common.shadeColor = function(color, percent) {
        // http://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color
        var colorInteger = parseInt(color.slice(1),16),
            amount = Math.round(2.55 * percent),
            R = (colorInteger >> 16) + amount,
            B = (colorInteger >> 8 & 0x00FF) + amount,
            G = (colorInteger & 0x0000FF) + amount;
        return "#" + (0x1000000 + (R < 255 ? R < 1 ? 0 : R :255) * 0x10000
                + (B < 255 ? B < 1 ? 0 : B : 255) * 0x100
                + (G < 255 ? G < 1 ? 0 : G : 255)).toString(16).slice(1);
    };

    /**
     * Description
     * @method shuffle
     * @param {array} array
     * @return {array} array shuffled randomly
     */
    Common.shuffle = function(array) {
        for (var i = array.length - 1; i > 0; i--) {
            var j = Math.floor(Math.random() * (i + 1));
            var temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
        return array;
    };

    /**
     * Description
     * @method choose
     * @param {array} choices
     * @return {object} A random choice object from the array
     */
    Common.choose = function(choices) {
        return choices[Math.floor(Math.random() * choices.length)];
    };

    /**
     * Description
     * @method isElement
     * @param {object} obj
     * @return {boolean} True if the object is a HTMLElement, otherwise false
     */
    Common.isElement = function(obj) {
        // http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
        try {
            return obj instanceof HTMLElement;
        }
        catch(e){
            return (typeof obj==="object") &&
              (obj.nodeType===1) && (typeof obj.style === "object") &&
              (typeof obj.ownerDocument ==="object");
        }
    };

    /**
     * Description
     * @method clamp
     * @param {number} value
     * @param {number} min
     * @param {number} max
     * @return {number} The value clamped between min and max inclusive
     */
    Common.clamp = function(value, min, max) {
        if (value < min)
            return min;
        if (value > max)
            return max;
        return value;
    };

    /**
     * Description
     * @method sign
     * @param {number} value
     * @return {number} -1 if negative, +1 if 0 or positive
     */
    Common.sign = function(value) {
        return value < 0 ? -1 : 1;
    };

    /**
     * Description
     * @method now
     * @return {number} the current timestamp (high-res if avaliable)
     */
    Common.now = function() {
        // http://stackoverflow.com/questions/221294/how-do-you-get-a-timestamp-in-javascript
        // https://gist.github.com/davidwaterston/2982531

        var perf = window.performance;

        if (perf) {
            perf.now = perf.now || perf.webkitNow || perf.msNow || perf.oNow || perf.mozNow;
            return +(perf.now());
        }

        return +(new Date());
    };

    /**
     * Description
     * @method random
     * @param {number} min
     * @param {number} max
     * @return {number} A random number between min and max inclusive
     */
    Common.random = function(min, max) {
        return min + Math.random() * (max - min);
    };

})();<|endoftext|>


<commit_before>/**
 * @class Entity
 */

EZ3.Entity = function() {
  this._dirty = true;

  this._parent = null;
  this._children = [];

  this._scale = vec3.create();
  this._scale.dirty = true;
  vec3.set(this._scale, 1, 1 ,1);

  this._position = vec3.create();
  this._position.dirty = true;
  vec3.set(this._position, 0, 0 ,0);

  this._rotation = quat.create();
  this._rotation.dirty = true;
  quat.set(this._rotation, 0, 0 ,0 ,0);

  this._modelMatrix = mat4.create();
  mat4.identity(this._modelMatrix);

  this._worldMatrix = mat4.create();
  mat4.identity(this._worldMatrix);

  this._normalMatrix = mat3.create();
  mat3.identity(this._normalMatrix);
};

Object.defineProperty(EZ3.Entity.prototype, "children", {
  get: function() {
    return this._children;
  },
  set: function(children) {
    this._children = children;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "scale", {
  get: function() {
    return this._scale;
  },
  set: function(scale) {
    this._scale = bitangents;
    this._scale.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "position", {
  get: function() {
    return this._scale;
  },
  set: function(scale) {
    this._scale = bitangents;
    this._scale.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "rotation", {
  get: function() {
    return this._rotation;
  },
  set: function(scale) {
    this._rotation = rotation;
    this._rotation.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "modelMatrix", {
  get: function() {
    return this._modelMatrix;
  },
  set: function(modelMatrix) {
    this._modelMatrix = modelMatrix;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "worldMatrix", {
  get: function() {
    return this._worldMatrix;
  },
  set: function(worldMatrix) {
    this._worldMatrix = worldMatrix;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "normalMatrix", {
  get: function() {
    return this._normalMatrix;
  },
  set: function(normalMatrix) {
    this._normalMatrix = normalMatrix;
  }
});

EZ3.Entity.prototype.add = function(child) {
  if (child instanceof EZ3.Entity) {

    if (child.parent)
      child.parent.remove(child);

    this._dirty = true;
    child.parent = this;
    this._children.push(child);

  } else {
    throw ('Child object must have a prototype of Entity Or Light');
  }
};

EZ3.Entity.prototype.remove = function(child) {
  var position = this._children.indexOf(child);

  if (~position) {
    this._children.splice(position, 1);
    this._dirty = true;
  }
};

EZ3.Entity.prototype.update = function(parentIsDirty, parentWorldMatrix) {
  this._dirty = this._dirty || this._scale.dirty || this._position.dirty || this._rotation.dirty || parentIsDirty;

  if (this._dirty) {

    if(this._scale.dirty || this._position.dirty || this._rotation.dirty) {

      if (this.scale.dirty)
        this._scale.dirty = false;

      if (this.rotation.dirty)
        this._rotation.dirty = false;

      if (this.position.dirty)
        this._position.dirty = false;

      mat4.fromRotationTranslation(this._modelMatrix, this._rotation, this._position);
      mat4.scale(this._modelMatrix, this._modelMatrix, this._scale);
    }

    if (!parentWorldMatrix)
      mat4.copy(this._worldMatrix, this._modelMatrix);
    else
      mat4.multiply(this._worldMatrix, parentWorldMatrix, this._modelMatrix);

    mat3.normalFromMat4(this._normalMatrix, this._worldMatrix);

    for (var k = this._children.length - 1; k >= 0; --k)
      this._children[k].update(this._dirty, this._worldMatrix);
  }
};
<commit_msg>Updated Entity.js<commit_after>/**
 * @class Entity
 */

EZ3.Entity = function() {
  this._dirty = true;

  this._parent = null;
  this._children = [];

  this._modelMatrix = new EZ3.Matrix4();
  this._worldMatrix = new EZ3.Matrix4();
  this._normalMatrix = new EZ3.Matrix3();

  this._scale = new EZ3.Vector3(1, 1 ,1);
  this._position = new EZ3.Vector3(0, 0, 0);
  this._rotation = new EZ3.Quaternion(1, 0, 0, 0);
};

EZ3.Entity.prototype.add = function(child) {
  if (child instanceof EZ3.Entity) {

    if (child.parent)
      child.parent.remove(child);

    this._dirty = true;
    child.parent = this;
    this._children.push(child);

  } else {
    throw ('Child object must have a prototype of Entity Or Light');
  }
};

EZ3.Entity.prototype.remove = function(child) {
  var position = this._children.indexOf(child);

  if (~position) {
    this._children.splice(position, 1);
    this._dirty = true;
  }
};

EZ3.Entity.prototype.update = function(parentIsDirty, parentWorldMatrix) {
  this.dirty = this.dirty || this.scale.dirty || this.position.dirty || this.rotation.dirty || parentIsDirty;

  if (this.dirty) {

    if(this.scale.dirty || this.position.dirty || this.rotation.dirty) {

      if (this.scale.dirty)
        this.scale.dirty = false;

      if (this.rotation.dirty)
        this.rotation.dirty = false;

      if (this.position.dirty)
        this.position.dirty = false;

      this.modelMatrix = this.modelMatrix.fromRotationTranslation(this.modelMatrix, this.rotation, this.position);
      this.modelMatrix = this.modelMatrix.scale(this.modelMatrix, this.scale);

    }

    if (!parentWorldMatrix)
      this.worldMatrix.copy(this.modelMatrix);
    else
      this.worldMatrix.mul(this.modelMatrix, parentWorldMatrix);

    this.normalMatrix = this.normalMatrix.normalFromMat4(this.worldMatrix);

    for (var k = this._children.length - 1; k >= 0; --k)
      this._children[k].update(this.dirty, this.worldMatrix);
  }
};

Object.defineProperty(EZ3.Entity.prototype, "dirty", {
  get: function() {
    return this._dirty;
  },
  set: function(dirty) {
    this._dirty = dirty;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "parent", {
  get: function() {
    return this._parent;
  },
  set: function(parent) {
    this._parent = parent;
    this.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "children", {
  get: function() {
    return this._children;
  },
  set: function(children) {
    this._children = children;
    this.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "scale", {
  get: function() {
    return this._scale;
  },
  set: function(scale) {
    this._scale = scale;
    this._scale.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "position", {
  get: function() {
    return this._position;
  },
  set: function(position) {
    this._position = position;
    this._position.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "rotation", {
  get: function() {
    return this._rotation;
  },
  set: function(rotation) {
    this._rotation = rotation;
    this._rotation.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "modelMatrix", {
  get: function() {
    return this._modelMatrix;
  },
  set: function(modelMatrix) {
    this._modelMatrix = modelMatrix;
    this._modelMatrix.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "worldMatrix", {
  get: function() {
    return this._worldMatrix;
  },
  set: function(worldMatrix) {
    this._worldMatrix = worldMatrix;
    this._worldMatrix.dirty = true;
  }
});

Object.defineProperty(EZ3.Entity.prototype, "normalMatrix", {
  get: function() {
    return this._normalMatrix;
  },
  set: function(normalMatrix) {
    this._normalMatrix = normalMatrix;
    this._normalMatrix.dirty = true;
  }
});
<|endoftext|>


<commit_before>/*
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.boot.phase2();

foam.CLASS({
  refines: 'foam.core.FObject',

  // documentation: 'Add listener support to FObject.',

  methods: [
  ]
});
<commit_msg>Removed PubSub.js.<commit_after><|endoftext|>


<commit_before>/**

 @author: Hans van den Akker (mysim1)
 @license NPOSL-3.0
 @copyright Bizboard, 2015

 */

import EventEmitter                 from 'eventemitter3';
import {ObjectHelper}               from '../utils/ObjectHelper.js';

/**
 * Abstract Router class which can be implemented to be used in an MVP setup.
 */
export class Router extends EventEmitter {

    constructor() {
        super();
        // make classes behave like context bound
        ObjectHelper.bindAllMethods(this, this);

        // make the router aware of controllers active in the application
        // each controller will need to subscribe itself to this container.
        this.controllers = [];

        // when a default controller is designated. The router will map every route without a
        // controller reference to the HomeController.
        this.defaultController = 'Home';

        // when a default method is designated. the router map will every route without a
        // method reference to the HomeController's method.
        this.defaultMethod = 'Index';
    }

    /**
     * On a route change, calls the corresponding controller method with the given parameter values.
     * @returns {Boolean} Whether the current route was successfully ran.
     */
    run() { }

    /**
     * Sets the initial controller and method to be activated whenever the controllers are activated.
     * @param {Controller|Function|String} controller Default controller instance, controller constructor, or controller name to go to.
     * @param {String} method Default method to call in given controller.
     * @returns {void}
     */
    setDefault(controller, method) { }

    /**
     * Registers a single controller.
     * @param {String} route Route to trigger handler on.
     * @param {Function} handler Method to call on given route.
     * @returns {void}
     */
    add(route, handler) { }

    /**
     * Triggers navigation to one of the controllers
     * @param {Controller|Function|String} controller The controller instance, controller constructor, or controller name to go to.
     * @param {String} method The method to call in given controller.
     * @param {Object} params Dictonary of key-value pairs containing named arguments (i.e. {id: 1, test: "yes"})
     * @returns {void}
     */
    go(controller, method, params) { }

    /**
     * Executes the controller handler associated with a given route, passing the route as a parameter.
     * @param {Object} rule Rule handler to execute.
     * @param {Object} route Route object to pass as parameter.
     * @returns {void}
     * @private
     */
    _executeRoute(rule, route) { }
}<commit_msg>Fixed comment in Router.js<commit_after>/**

 @author: Hans van den Akker (mysim1)
 @license NPOSL-3.0
 @copyright Bizboard, 2015

 */

import EventEmitter                 from 'eventemitter3';
import {ObjectHelper}               from '../utils/ObjectHelper.js';

/**
 * Abstract Router class which can be implemented to be used in an MVP setup.
 */
export class Router extends EventEmitter {

    constructor() {
        super();
        // make classes behave like context bound
        ObjectHelper.bindAllMethods(this, this);

        // make the router aware of controllers active in the application
        // each controller will need to subscribe itself to this container.
        this.controllers = [];

        // when a default controller is designated. The router will map every route without a
        // controller reference to the HomeController.
        this.defaultController = 'Home';

        // when a default method is designated. the router map will every route without a
        // method reference to the HomeController's method.
        this.defaultMethod = 'Index';
    }

    /**
     * On a route change, calls the corresponding controller method with the given parameter values.
     * @returns {Boolean} Whether the current route was successfully ran.
     */
    run() { }

    /**
     * Sets the initial controller and method to be activated whenever the controllers are activated.
     * @param {Controller|Function|String} controller Default controller instance, controller constructor, or controller name to go to.
     * @param {String} method Default method to call in given controller.
     * @returns {void}
     */
    setDefault(controller, method) { }

    /**
     * Registers a single controller.
     * @param {String} route Route to trigger handler on.
     * @param {Function} handler Method to call on given route.
     * @returns {void}
     */
    add(route, handler) { }

    /**
     * Triggers navigation to one of the controllers
     * @param {Controller|Function|String} controller The controller instance, controller constructor, or controller name to go to.
     * @param {String} method The method to call in given controller.
     * @param {Object} [params] Dictonary of key-value pairs containing named arguments (i.e. {id: 1, test: "yes"})
     * @returns {void}
     */
    go(controller, method, params) { }

    /**
     * Executes the controller handler associated with a given route, passing the route as a parameter.
     * @param {Object} rule Rule handler to execute.
     * @param {Object} route Route object to pass as parameter.
     * @returns {void}
     * @private
     */
    _executeRoute(rule, route) { }
}<|endoftext|>


<commit_before>/*jshint white: false, strict: false, plusplus: false, onevar: false,
  nomen: false */
/*global define: false, console: false, window: false, setTimeout: false */

define( function ( require ) {

    var Event = require( './event' );

    /* Entity
     *
     * An entity is a collection of entity components under a single globally unique identifier.
     */

    return function( engine ) {

        var Entity = function( options ) {

            options = options || {};
            var that = this;

            // Members

            var _guid = window.guid();                 // Globally-unique ID
            Object.defineProperty( this, 'id', {
                get: function() {
                    return _guid;
                }
            });

            var _name = options.name || null;           // Name string
            Object.defineProperty( this, 'name', {
                get: function() {
                    return _name;
                }
            });

            var _parent = options.parent || null;       // Parent entity, or null if no parent
            Object.defineProperty( this, 'parent', {
                get: function() {
                    return _parent;
                },
                set: function( value ) {
                    if( value != this && value != _parent ) {
                        if( _parent ) {
                            parent.childRemoved( this );
                        }

                        var previous = _parent;
                        _parent = value;
                        onParentChanged({
                            previous: previous,
                            current: value
                        });

                        if( _parent ) {
                            parent.childAdded( this );
                        }
                    }
                }
            });

            var _children = {};

            var _manager = options.manager || null;     // Component manager
            Object.defineProperty( this, 'manager', {
                get: function() {
                    return _manager;
                },
                set: function( value ) {
                    _manager = value;
                }
            });

            var _size = 0;
            Object.defineProperty( this, 'size', {
                get: function() {
                    return _size;
                }
            });

            var _components = {};       // Dictionary of components, indexed by component type

            // Methods

            // Add a component and return the previous component of the same type, or null.
            this.add = function( component ) {
                var previousComponent = that.remove( component.type );
                component.owner = that;
                _components[component.type] = component;
                ++ _size;
                onComponentAdded( component );
                return previousComponent || null;
            };

            // Remove a component and return it, or null.
            this.remove = function( type ) {
                var previousComponent;
                if( _components.hasOwnProperty( type ) ) {
                    previousComponent = _components[type];
                    delete _components[type];
                    previousComponent.owner = null;
                    -- _size;
                    onComponentRemoved( previousComponent );
                }
                return previousComponent || null;
            };

            // Find the first occurence of a component with a given type and return it, or null.
            this.find = function( type ) {
                if( !_components.hasOwnProperty( type ) )
                    return null;

                return _components[type];
            };

            // Return true if this entity has a component with a given type, or false.
            this.contains = function( type ) {
                return _components.hasOwnProperty( type );
            };

            // Events

            var _parentChanged = new Event();
            Object.defineProperty( this, 'parentChanged', {
                get: function() {
                    return _parentChanged;
                }
            });
            var onParentChanged = function( options ) {
                if( _parentChanged ) {
                    _parentChanged( options );
                }
            };

            var _managerChanged = new Event();
            Object.defineProperty( this, 'managerChanged', {
                get: function() {
                    return _managerChanged;
                }
            });
            var onManagerChanged = function( options ) {
                if( _managerChanged ) {
                    _managerChanged( options );
                }
            };

            var _childAdded = new Event();
            Object.defineProperty( this, 'childAdded', {
                get: function() {
                    return _childAdded;
                }
            });
            var onChildAdded = function( options ) {
                if( _childAdded ) {
                    _childAdded( options );
                }
            };

            var _childRemoved = new Event();
            Object.defineProperty( this, 'childRemoved', {
                get: function() {
                    return _childRemoved;
                }
            });
            var onChildRemoved = function( options ) {
                if( _childRemoved ) {
                    _childRemoved( options );
                }
            };

            var _componentAdded = new Event();
            Object.defineProperty( this, 'componentAdded', {
                get: function() {
                    return _componentAdded;
                }
            });
            var onComponentAdded = function( component ) {
                if( _componentAdded ) {
                    _componentAdded( component );
                }
            };

            var _componentRemoved = new Event();
            Object.defineProperty( this, 'componentRemoved', {
                get: function() {
                    return _componentRemoved;
                }
            });
            var onComponentRemoved = function( component ) {
                if( _componentRemoved ) {
                    _componentRemoved( component );
                }
            };

            // Event handlers

            var handleChildAdded = function( child ) {
                _children[child.id] = child;
            };
            childAdded.subscribe( handleChildAdded );

            var handleChildRemoved = function( child ) {
                delete _children[child.id];
            };
            childRemoved.subscribe( handleChildRemoved );

        };

        return Entity;

    };

});
<commit_msg>Fixed a typo.<commit_after>/*jshint white: false, strict: false, plusplus: false, onevar: false,
  nomen: false */
/*global define: false, console: false, window: false, setTimeout: false */

define( function ( require ) {

    var Event = require( './event' );

    /* Entity
     *
     * An entity is a collection of entity components under a single globally unique identifier.
     */

    return function( engine ) {

        var Entity = function( options ) {

            options = options || {};
            var that = this;

            // Members

            var _guid = window.guid();                 // Globally-unique ID
            Object.defineProperty( this, 'id', {
                get: function() {
                    return _guid;
                }
            });

            var _name = options.name || null;           // Name string
            Object.defineProperty( this, 'name', {
                get: function() {
                    return _name;
                }
            });

            var _parent = options.parent || null;       // Parent entity, or null if no parent
            Object.defineProperty( this, 'parent', {
                get: function() {
                    return _parent;
                },
                set: function( value ) {
                    if( value != this && value != _parent ) {
                        if( _parent ) {
                            parent.childRemoved( this );
                        }

                        var previous = _parent;
                        _parent = value;
                        onParentChanged({
                            previous: previous,
                            current: value
                        });

                        if( _parent ) {
                            parent.childAdded( this );
                        }
                    }
                }
            });

            var _children = {};

            var _manager = options.manager || null;     // Component manager
            Object.defineProperty( this, 'manager', {
                get: function() {
                    return _manager;
                },
                set: function( value ) {
                    _manager = value;
                }
            });

            var _size = 0;
            Object.defineProperty( this, 'size', {
                get: function() {
                    return _size;
                }
            });

            var _components = {};       // Dictionary of components, indexed by component type

            // Methods

            // Add a component and return the previous component of the same type, or null.
            this.add = function( component ) {
                var previousComponent = that.remove( component.type );
                component.owner = that;
                _components[component.type] = component;
                ++ _size;
                onComponentAdded( component );
                return previousComponent || null;
            };

            // Remove a component and return it, or null.
            this.remove = function( type ) {
                var previousComponent;
                if( _components.hasOwnProperty( type ) ) {
                    previousComponent = _components[type];
                    delete _components[type];
                    previousComponent.owner = null;
                    -- _size;
                    onComponentRemoved( previousComponent );
                }
                return previousComponent || null;
            };

            // Find the first occurence of a component with a given type and return it, or null.
            this.find = function( type ) {
                if( !_components.hasOwnProperty( type ) )
                    return null;

                return _components[type];
            };

            // Return true if this entity has a component with a given type, or false.
            this.contains = function( type ) {
                return _components.hasOwnProperty( type );
            };

            // Events

            var _parentChanged = new Event();
            Object.defineProperty( this, 'parentChanged', {
                get: function() {
                    return _parentChanged;
                }
            });
            var onParentChanged = function( options ) {
                if( _parentChanged ) {
                    _parentChanged( options );
                }
            };

            var _managerChanged = new Event();
            Object.defineProperty( this, 'managerChanged', {
                get: function() {
                    return _managerChanged;
                }
            });
            var onManagerChanged = function( options ) {
                if( _managerChanged ) {
                    _managerChanged( options );
                }
            };

            var _childAdded = new Event();
            Object.defineProperty( this, 'childAdded', {
                get: function() {
                    return _childAdded;
                }
            });
            var onChildAdded = function( options ) {
                if( _childAdded ) {
                    _childAdded( options );
                }
            };

            var _childRemoved = new Event();
            Object.defineProperty( this, 'childRemoved', {
                get: function() {
                    return _childRemoved;
                }
            });
            var onChildRemoved = function( options ) {
                if( _childRemoved ) {
                    _childRemoved( options );
                }
            };

            var _componentAdded = new Event();
            Object.defineProperty( this, 'componentAdded', {
                get: function() {
                    return _componentAdded;
                }
            });
            var onComponentAdded = function( component ) {
                if( _componentAdded ) {
                    _componentAdded( component );
                }
            };

            var _componentRemoved = new Event();
            Object.defineProperty( this, 'componentRemoved', {
                get: function() {
                    return _componentRemoved;
                }
            });
            var onComponentRemoved = function( component ) {
                if( _componentRemoved ) {
                    _componentRemoved( component );
                }
            };

            // Event handlers

            var handleChildAdded = function( child ) {
                _children[child.id] = child;
            };
            _childAdded.subscribe( handleChildAdded );

            var handleChildRemoved = function( child ) {
                delete _children[child.id];
            };
            _childRemoved.subscribe( handleChildRemoved );

        };

        return Entity;

    };

});
<|endoftext|>


<commit_before>/**
 * @name pc.events
 * @namespace
 * @description global namespace that allows to extend other objects with events
 * Additionally it can handle global events it self.
 * @example
 * var obj = { };
 * pc.events.attach(obj);
 *
 * // subscribe to an event
 * obj.on('hello', function(str) {
 *     console.log('event hello is fired', str);
 * });
 *
 * // fire event
 * obj.fire('hello', 'world');
 */
pc.events = {
    /**
     * @function
     * @name pc.events.attach
     * @description Attach event methods 'on', 'off', 'fire', 'once' and 'hasEvent' to the target object
     * @param {Object} target The object to add events to.
     * @returns {Object} The target object
     * @example
     * var obj = { };
     * pc.events.attach(obj);
     */
    attach: function (target) {
        var ev = pc.events;
        target.on = ev.on;
        target.off = ev.off;
        target.fire = ev.fire;
        target.once = ev.once;
        target.hasEvent = ev.hasEvent;
        target._callbackActive = { };
        return target;
    },

    _debug: {},

    _mark: function () {
        Object.keys(pc.events._debug).forEach(function (key) {
            pc.events._debug[key].high = Math.max(pc.events._debug[key].curr, pc.events._debug[key].high);
        });
    },

    _print: function () {
        Object.keys(pc.events._debug).forEach(function (key) {
            if (pc.events._debug[key].curr > pc.events._debug[key].high) {
                console.log('broke watermark: ' + key, pc.events._debug[key].curr, pc.events._debug[key].high);
            }
        });
    },

    /**
     * @function
     * @name pc.events.on
     * @description Attach an event handler to an event
     * @param {String} name Name of the event to bind the callback to
     * @param {Function} callback Function that is called when event is fired. Note the callback is limited to 8 arguments.
     * @param {Object} [scope] Object to use as 'this' when the event is fired, defaults to current this
     * @returns {*} 'this' for chaining
     * @example
     * obj.on('test', function (a, b) {
     *     console.log(a + b);
     * });
     * obj.fire('test', 1, 2); // prints 3 to the console
     */
    on: function (name, callback, scope) {
        if (!name || typeof name !== 'string' || !callback)
            return this;

        if (!this._callbacks)
            this._callbacks = { };

        if (!this._callbacks[name])
            this._callbacks[name] = [];

        if (!this._callbackActive)
            this._callbackActive = { };

        if (this._callbackActive[name] && this._callbackActive[name] === this._callbacks[name])
            this._callbackActive[name] = this._callbackActive[name].slice();

        if (name) {
            if (!pc.events._debug[name]) {
                pc.events._debug[name] = {
                    curr: 0,
                    high: 0
                };
            }
            pc.events._debug[name].curr++;
        } else {
            debugger;
        }

        this._callbacks[name].push({
            callback: callback,
            scope: scope || this
        });

        return this;
    },

    /**
     * @function
     * @name pc.events.off
     * @description Detach an event handler from an event. If callback is not provided then all callbacks are unbound from the event,
     * if scope is not provided then all events with the callback will be unbound.
     * @param {String} [name] Name of the event to unbind
     * @param {Function} [callback] Function to be unbound
     * @param {Object} [scope] Scope that was used as the this when the event is fired
     * @returns {*} 'this' for chaining
     * @example
     * var handler = function () {
     * };
     * obj.on('test', handler);
     *
     * obj.off(); // Removes all events
     * obj.off('test'); // Removes all events called 'test'
     * obj.off('test', handler); // Removes all handler functions, called 'test'
     * obj.off('test', handler, this); // Removes all hander functions, called 'test' with scope this
     */
    off: function (name, callback, scope) {
        if (!this._callbacks)
            return this;

        if (this._callbackActive) {
            if (name) {
                if (this._callbackActive[name] && this._callbackActive[name] === this._callbacks[name])
                    this._callbackActive[name] = this._callbackActive[name].slice();
            } else {
                for (var key in this._callbackActive) {
                    if (!this._callbacks[key])
                        continue;

                    if (this._callbacks[key] !== this._callbackActive[key])
                        continue;

                    this._callbackActive[key] = this._callbackActive[key].slice();
                }
            }
        }

        if (name && pc.events._debug[name]) {
            pc.events._debug[name].curr--;
        }

        if (!name) {
            var keys = Object.keys(this._callbacks);
            for (var i = 0; i < keys.length; i++) {
                pc.events._debug[keys[i]].curr -= this._callbacks[keys[i]].length;
            }

            this._callbacks = null;
        } else if (!callback) {
            if (this._callbacks[name])
                delete this._callbacks[name];
        } else {
            var events = this._callbacks[name];
            if (!events)
                return this;

            var i = events.length;

            while (i--) {
                if (events[i].callback !== callback)
                    continue;

                if (scope && events[i].scope !== scope)
                    continue;

                events.splice(i, 1);
            }
        }

        return this;
    },

    // ESLint rule disabled here as documenting arg1, arg2...argN as [...] rest
    // arguments is preferable to documenting each one individually.
    /* eslint-disable valid-jsdoc */
    /**
     * @function
     * @name pc.events.fire
     * @description Fire an event, all additional arguments are passed on to the event listener
     * @param {Object} name Name of event to fire
     * @param {*} [...] Arguments that are passed to the event handler
     * @returns {*} 'this' for chaining
     * @example
     * obj.fire('test', 'This is the message');
     */
    /* eslint-enable valid-jsdoc */
    fire: function (name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
        if (!name || !this._callbacks || !this._callbacks[name])
            return this;

        var callbacks;

        if (!this._callbackActive)
            this._callbackActive = { };

        if (!this._callbackActive[name]) {
            this._callbackActive[name] = this._callbacks[name];
        } else {
            if (this._callbackActive[name] === this._callbacks[name])
                this._callbackActive[name] = this._callbackActive[name].slice();

            callbacks = this._callbacks[name].slice();
        }

        // TODO: What does callbacks do here?
        // In particular this condition check looks wrong: (i < (callbacks || this._callbackActive[name]).length)
        // Because callbacks is not an integer
        // eslint-disable-next-line no-unmodified-loop-condition
        for (var i = 0; (callbacks || this._callbackActive[name]) && (i < (callbacks || this._callbackActive[name]).length); i++) {
            var evt = (callbacks || this._callbackActive[name])[i];
            evt.callback.call(evt.scope, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);

            if (evt.callback.once) {
                var ind = this._callbacks[name].indexOf(evt);
                if (ind !== -1) {
                    if (this._callbackActive[name] === this._callbacks[name])
                        this._callbackActive[name] = this._callbackActive[name].slice();

                    this._callbacks[name].splice(ind, 1);
                }
            }
        }

        if (!callbacks)
            this._callbackActive[name] = null;

        return this;
    },

    /**
     * @function
     * @name pc.events.once
     * @description Attach an event handler to an event. This handler will be removed after being fired once.
     * @param {String} name Name of the event to bind the callback to
     * @param {Function} callback Function that is called when event is fired. Note the callback is limited to 8 arguments.
     * @param {Object} [scope] Object to use as 'this' when the event is fired, defaults to current this
     * @returns {*} 'this' for chaining
     * @example
     * obj.once('test', function (a, b) {
     *     console.log(a + b);
     * });
     * obj.fire('test', 1, 2); // prints 3 to the console
     * obj.fire('test', 1, 2); // not going to get handled
     */
    once: function (name, callback, scope) {
        callback.once = true;
        this.on(name, callback, scope);
        return this;
    },

    /**
     * @function
     * @name pc.events.hasEvent
     * @description Test if there are any handlers bound to an event name
     * @param {String} name The name of the event to test
     * @returns {Boolean} true if the object has handlers bound to the specified event name.
     * @example
     * obj.on('test', function () { }); // bind an event to 'test'
     * obj.hasEvent('test'); // returns true
     * obj.hasEvent('hello'); // returns false
     */
    hasEvent: function (name) {
        return (this._callbacks && this._callbacks[name] && this._callbacks[name].length !== 0) || false;
    }
};
<commit_msg>[FIX] remove erroneous debug logic from event<commit_after>/**
 * @name pc.events
 * @namespace
 * @description global namespace that allows to extend other objects with events
 * Additionally it can handle global events it self.
 * @example
 * var obj = { };
 * pc.events.attach(obj);
 *
 * // subscribe to an event
 * obj.on('hello', function(str) {
 *     console.log('event hello is fired', str);
 * });
 *
 * // fire event
 * obj.fire('hello', 'world');
 */
pc.events = {
    /**
     * @function
     * @name pc.events.attach
     * @description Attach event methods 'on', 'off', 'fire', 'once' and 'hasEvent' to the target object
     * @param {Object} target The object to add events to.
     * @returns {Object} The target object
     * @example
     * var obj = { };
     * pc.events.attach(obj);
     */
    attach: function (target) {
        var ev = pc.events;
        target.on = ev.on;
        target.off = ev.off;
        target.fire = ev.fire;
        target.once = ev.once;
        target.hasEvent = ev.hasEvent;
        target._callbackActive = { };
        return target;
    },

    /**
     * @function
     * @name pc.events.on
     * @description Attach an event handler to an event
     * @param {String} name Name of the event to bind the callback to
     * @param {Function} callback Function that is called when event is fired. Note the callback is limited to 8 arguments.
     * @param {Object} [scope] Object to use as 'this' when the event is fired, defaults to current this
     * @returns {*} 'this' for chaining
     * @example
     * obj.on('test', function (a, b) {
     *     console.log(a + b);
     * });
     * obj.fire('test', 1, 2); // prints 3 to the console
     */
    on: function (name, callback, scope) {
        if (!name || typeof name !== 'string' || !callback)
            return this;

        if (!this._callbacks)
            this._callbacks = { };

        if (!this._callbacks[name])
            this._callbacks[name] = [];

        if (!this._callbackActive)
            this._callbackActive = { };

        if (this._callbackActive[name] && this._callbackActive[name] === this._callbacks[name])
            this._callbackActive[name] = this._callbackActive[name].slice();

        this._callbacks[name].push({
            callback: callback,
            scope: scope || this
        });

        return this;
    },

    /**
     * @function
     * @name pc.events.off
     * @description Detach an event handler from an event. If callback is not provided then all callbacks are unbound from the event,
     * if scope is not provided then all events with the callback will be unbound.
     * @param {String} [name] Name of the event to unbind
     * @param {Function} [callback] Function to be unbound
     * @param {Object} [scope] Scope that was used as the this when the event is fired
     * @returns {*} 'this' for chaining
     * @example
     * var handler = function () {
     * };
     * obj.on('test', handler);
     *
     * obj.off(); // Removes all events
     * obj.off('test'); // Removes all events called 'test'
     * obj.off('test', handler); // Removes all handler functions, called 'test'
     * obj.off('test', handler, this); // Removes all hander functions, called 'test' with scope this
     */
    off: function (name, callback, scope) {
        if (!this._callbacks)
            return this;

        if (this._callbackActive) {
            if (name) {
                if (this._callbackActive[name] && this._callbackActive[name] === this._callbacks[name])
                    this._callbackActive[name] = this._callbackActive[name].slice();
            } else {
                for (var key in this._callbackActive) {
                    if (!this._callbacks[key])
                        continue;

                    if (this._callbacks[key] !== this._callbackActive[key])
                        continue;

                    this._callbackActive[key] = this._callbackActive[key].slice();
                }
            }
        }

        if (!name) {
            this._callbacks = null;
        } else if (!callback) {
            if (this._callbacks[name])
                delete this._callbacks[name];
        } else {
            var events = this._callbacks[name];
            if (!events)
                return this;

            var i = events.length;

            while (i--) {
                if (events[i].callback !== callback)
                    continue;

                if (scope && events[i].scope !== scope)
                    continue;

                events.splice(i, 1);
            }
        }

        return this;
    },

    // ESLint rule disabled here as documenting arg1, arg2...argN as [...] rest
    // arguments is preferable to documenting each one individually.
    /* eslint-disable valid-jsdoc */
    /**
     * @function
     * @name pc.events.fire
     * @description Fire an event, all additional arguments are passed on to the event listener
     * @param {Object} name Name of event to fire
     * @param {*} [...] Arguments that are passed to the event handler
     * @returns {*} 'this' for chaining
     * @example
     * obj.fire('test', 'This is the message');
     */
    /* eslint-enable valid-jsdoc */
    fire: function (name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
        if (!name || !this._callbacks || !this._callbacks[name])
            return this;

        var callbacks;

        if (!this._callbackActive)
            this._callbackActive = { };

        if (!this._callbackActive[name]) {
            this._callbackActive[name] = this._callbacks[name];
        } else {
            if (this._callbackActive[name] === this._callbacks[name])
                this._callbackActive[name] = this._callbackActive[name].slice();

            callbacks = this._callbacks[name].slice();
        }

        // TODO: What does callbacks do here?
        // In particular this condition check looks wrong: (i < (callbacks || this._callbackActive[name]).length)
        // Because callbacks is not an integer
        // eslint-disable-next-line no-unmodified-loop-condition
        for (var i = 0; (callbacks || this._callbackActive[name]) && (i < (callbacks || this._callbackActive[name]).length); i++) {
            var evt = (callbacks || this._callbackActive[name])[i];
            evt.callback.call(evt.scope, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);

            if (evt.callback.once) {
                var ind = this._callbacks[name].indexOf(evt);
                if (ind !== -1) {
                    if (this._callbackActive[name] === this._callbacks[name])
                        this._callbackActive[name] = this._callbackActive[name].slice();

                    this._callbacks[name].splice(ind, 1);
                }
            }
        }

        if (!callbacks)
            this._callbackActive[name] = null;

        return this;
    },

    /**
     * @function
     * @name pc.events.once
     * @description Attach an event handler to an event. This handler will be removed after being fired once.
     * @param {String} name Name of the event to bind the callback to
     * @param {Function} callback Function that is called when event is fired. Note the callback is limited to 8 arguments.
     * @param {Object} [scope] Object to use as 'this' when the event is fired, defaults to current this
     * @returns {*} 'this' for chaining
     * @example
     * obj.once('test', function (a, b) {
     *     console.log(a + b);
     * });
     * obj.fire('test', 1, 2); // prints 3 to the console
     * obj.fire('test', 1, 2); // not going to get handled
     */
    once: function (name, callback, scope) {
        callback.once = true;
        this.on(name, callback, scope);
        return this;
    },

    /**
     * @function
     * @name pc.events.hasEvent
     * @description Test if there are any handlers bound to an event name
     * @param {String} name The name of the event to test
     * @returns {Boolean} true if the object has handlers bound to the specified event name.
     * @example
     * obj.on('test', function () { }); // bind an event to 'test'
     * obj.hasEvent('test'); // returns true
     * obj.hasEvent('hello'); // returns false
     */
    hasEvent: function (name) {
        return (this._callbacks && this._callbacks[name] && this._callbacks[name].length !== 0) || false;
    }
};
<|endoftext|>


<commit_before>import net from 'net';
import logger from 'winston';
import {ClientProxy} from './client-proxy';
import {DNSCache} from './dns-cache';
import {Balancer} from './balancer';
import {Pipe} from './pipe';
import {Profile} from './profile';
import {
  MIDDLEWARE_DIRECTION_UPWARD,
  MIDDLEWARE_DIRECTION_DOWNWARD,
  MIDDLEWARE_TYPE_FRAME,
  MIDDLEWARE_TYPE_CRYPTO,
  MIDDLEWARE_TYPE_PROTOCOL,
  MIDDLEWARE_TYPE_OBFS,
  createMiddleware
} from './middleware';

import {Utils} from '../utils';
import {
  SOCKET_CONNECT_TO_DST,
  PROCESSING_FAILED
} from '../presets/actions';

import {
  UdpRequestMessage
} from '../proxies/socks5';

const dnsCache = DNSCache.create();

export class Socket {

  _id = null;

  _bsocket = null;

  _fsocket = null;

  _pipe = null;

  _proxy = null; // client only

  _lastEndPoint = null;

  constructor({id, socket}) {
    this._id = id;
    this._bsocket = socket;
    this._bsocket.on('error', (err) => this.onError(err));
    this._bsocket.on('close', (had_error) => this.onClose(had_error));
    this._bsocket.on('data', (buffer) => this.onForward(buffer));
    this.onError = this.onError.bind(this);
    this.onClose = this.onClose.bind(this);
    this.onForward = this.onForward.bind(this);
    this.onBackward = this.onBackward.bind(this);
    if (__IS_SERVER__) {
      this.createPipe();
    } else {
      this._proxy = new ClientProxy({
        onHandshakeDone: this.onHandshakeDone.bind(this)
      });
    }
  }

  onForward(buffer) {
    if (__IS_CLIENT__ && !this._proxy.isDone()) {
      // client handshake(multiple-protocols), client only
      this._proxy.makeHandshake(this._bsocket, buffer);
      return;
    }

    let _buffer = buffer;

    // udp compatible
    if (__IS_CLIENT__ && this._socksUdpReady) {
      const request = UdpRequestMessage.parse(buffer);
      if (request !== null) {
        // just drop RSV and FRAG
        _buffer = request.DATA;
      } else {
        logger.warn(`[${this._id}] -x-> dropped unidentified packet ${buffer.length} bytes`);
        return;
      }
    }

    try {
      this._pipe.feed(
        __IS_CLIENT__
          ? MIDDLEWARE_DIRECTION_UPWARD
          : MIDDLEWARE_DIRECTION_DOWNWARD,
        _buffer
      );
      Profile.totalIn += buffer.length;
    } catch (err) {
      logger.error(`[${this._id}]`, err);
    }
  }

  onBackward(buffer) {
    try {
      this._pipe.feed(
        __IS_CLIENT__
          ? MIDDLEWARE_DIRECTION_DOWNWARD
          : MIDDLEWARE_DIRECTION_UPWARD,
        buffer
      );
      Profile.totalIn += buffer.length;
    } catch (err) {
      logger.error(`[${this._id}]`, err);
    }
  }

  onError(err) {
    switch (err.code) {
      case 'EADDRNOTAVAIL':
      case 'ENETUNREACH':
      case 'ECONNREFUSED':
        // TODO: maybe we can switch to another server
        break;
      case 'ETIMEDOUT':
        // just ignore
        break;
      case 'ENETDOWN':
      case 'ECONNRESET':
      case 'EAI_AGAIN':
      case 'EPIPE':
        logger.warn(`[${this._id}] ${err.code} - ${err.message}`);
        break;
      default:
        logger.error(err);
        break;
    }
    Profile.errors += 1;
  }

  onClose() {
    if (this._bsocket !== null && !this._bsocket.destroyed) {
      this._bsocket.end();
      this._bsocket = null;
      logger.info(`[${this._id}] downstream closed`);
      Profile.connections -= 1;
    }
    if (this._fsocket !== null && !this._fsocket.destroyed) {
      this._fsocket.end();
      this._fsocket = null;
      logger.info(`[${this._id}] upstream closed`);
      Profile.connections -= 1;
    }
  }

  send(buffer, flag) {
    if (flag) {
      this._fsocket && this._fsocket.write(buffer);
    } else {
      this._bsocket && this._bsocket.write(buffer);
    }
    Profile.totalOut += buffer.length;
  }

  /**
   * connect to a server, for both client and server
   * @param host
   * @param port
   * @param callback
   * @returns {Promise.<void>}
   */
  async connectTo({host, port}, callback) {
    logger.info(`[${this._id}] connecting to:`, {host, port});
    try {
      const ip = await dnsCache.get(host);
      this._fsocket = net.connect({host: ip, port}, callback);
      this._fsocket.on('error', this.onError);
      this._fsocket.on('close', this.onClose);
      this._fsocket.on('data', this.onBackward);
    } catch (err) {
      logger.error(err.message);
    }
  }

  /**
   * create pipes for both data forward and backward
   */
  createPipe(addr) {
    const pipeProps = {
      onNotified: (action) => {
        if (__IS_SERVER__ && action.type === SOCKET_CONNECT_TO_DST) {
          const [addr, callback] = action.payload;
          return this.connectTo(addr, callback);
        }
        if (action.type === PROCESSING_FAILED) {
          const message = action.payload;
          const timeout = Utils.getRandomInt(10, 40);
          logger.error(`connection will be closed in ${timeout}s due to: ${message}`);
          Profile.fatals += 1;
          setTimeout(() => this.onClose(), timeout * 1e3);
        }
      }
    };
    this._pipe = new Pipe(pipeProps);
    this._pipe.setMiddlewares(MIDDLEWARE_DIRECTION_UPWARD, [
      createMiddleware(MIDDLEWARE_TYPE_FRAME, [addr]),
      createMiddleware(MIDDLEWARE_TYPE_CRYPTO),
      createMiddleware(MIDDLEWARE_TYPE_PROTOCOL),
      createMiddleware(MIDDLEWARE_TYPE_OBFS),
    ]);
    this._pipe.on(`next_${MIDDLEWARE_DIRECTION_UPWARD}`, (buf) => this.send(buf, __IS_CLIENT__));
    this._pipe.on(`next_${MIDDLEWARE_DIRECTION_DOWNWARD}`, (buf) => this.send(buf, __IS_SERVER__));
  }

  /**
   * client handshake
   * @param addr
   * @param callback
   * @returns {Promise.<void>}
   */
  onHandshakeDone(addr, callback) {
    const ep = Balancer.getFastest();
    if (this._lastEndPoint === null || ep.host !== this._lastEndPoint.host || ep.port !== this._lastEndPoint.port) {
      logger.info('[balancer] use:', JSON.stringify(ep));
    }
    this._lastEndPoint = ep;
    return this.connectTo(ep, () => {
      this.createPipe(addr);
      callback(this.onForward);
    });
  }

}
<commit_msg>chore(socket): verbose log connection errors<commit_after>import net from 'net';
import logger from 'winston';
import {ClientProxy} from './client-proxy';
import {DNSCache} from './dns-cache';
import {Balancer} from './balancer';
import {Pipe} from './pipe';
import {Profile} from './profile';
import {
  MIDDLEWARE_DIRECTION_UPWARD,
  MIDDLEWARE_DIRECTION_DOWNWARD,
  MIDDLEWARE_TYPE_FRAME,
  MIDDLEWARE_TYPE_CRYPTO,
  MIDDLEWARE_TYPE_PROTOCOL,
  MIDDLEWARE_TYPE_OBFS,
  createMiddleware
} from './middleware';

import {Utils} from '../utils';
import {
  SOCKET_CONNECT_TO_DST,
  PROCESSING_FAILED
} from '../presets/actions';

import {
  UdpRequestMessage
} from '../proxies/socks5';

const dnsCache = DNSCache.create();

export class Socket {

  _id = null;

  _bsocket = null;

  _fsocket = null;

  _pipe = null;

  _proxy = null; // client only

  _lastEndPoint = null;

  constructor({id, socket}) {
    this._id = id;
    this._bsocket = socket;
    this._bsocket.on('error', (err) => this.onError(err));
    this._bsocket.on('close', (had_error) => this.onClose(had_error));
    this._bsocket.on('data', (buffer) => this.onForward(buffer));
    this.onError = this.onError.bind(this);
    this.onClose = this.onClose.bind(this);
    this.onForward = this.onForward.bind(this);
    this.onBackward = this.onBackward.bind(this);
    if (__IS_SERVER__) {
      this.createPipe();
    } else {
      this._proxy = new ClientProxy({
        onHandshakeDone: this.onHandshakeDone.bind(this)
      });
    }
  }

  onForward(buffer) {
    if (__IS_CLIENT__ && !this._proxy.isDone()) {
      // client handshake(multiple-protocols), client only
      this._proxy.makeHandshake(this._bsocket, buffer);
      return;
    }

    let _buffer = buffer;

    // udp compatible
    if (__IS_CLIENT__ && this._socksUdpReady) {
      const request = UdpRequestMessage.parse(buffer);
      if (request !== null) {
        // just drop RSV and FRAG
        _buffer = request.DATA;
      } else {
        logger.warn(`[${this._id}] -x-> dropped unidentified packet ${buffer.length} bytes`);
        return;
      }
    }

    try {
      this._pipe.feed(
        __IS_CLIENT__
          ? MIDDLEWARE_DIRECTION_UPWARD
          : MIDDLEWARE_DIRECTION_DOWNWARD,
        _buffer
      );
      Profile.totalIn += buffer.length;
    } catch (err) {
      logger.error(`[${this._id}]`, err);
    }
  }

  onBackward(buffer) {
    try {
      this._pipe.feed(
        __IS_CLIENT__
          ? MIDDLEWARE_DIRECTION_DOWNWARD
          : MIDDLEWARE_DIRECTION_UPWARD,
        buffer
      );
      Profile.totalIn += buffer.length;
    } catch (err) {
      logger.error(`[${this._id}]`, err);
    }
  }

  onError(err) {
    switch (err.code) {
      case 'EADDRNOTAVAIL':
      case 'ENETUNREACH':
      case 'ECONNREFUSED':
      case 'ETIMEDOUT':
      case 'ENETDOWN':
      case 'ECONNRESET':
      case 'EAI_AGAIN':
      case 'EPIPE':
        logger.verbose(`[${this._id}] ${err.code} - ${err.message}`);
        break;
      default:
        logger.error(err);
        break;
    }
    Profile.errors += 1;
  }

  onClose() {
    if (this._bsocket !== null && !this._bsocket.destroyed) {
      this._bsocket.end();
      this._bsocket = null;
      logger.info(`[${this._id}] downstream closed`);
      Profile.connections -= 1;
    }
    if (this._fsocket !== null && !this._fsocket.destroyed) {
      this._fsocket.end();
      this._fsocket = null;
      logger.info(`[${this._id}] upstream closed`);
      Profile.connections -= 1;
    }
  }

  send(buffer, flag) {
    if (flag) {
      this._fsocket && this._fsocket.write(buffer);
    } else {
      this._bsocket && this._bsocket.write(buffer);
    }
    Profile.totalOut += buffer.length;
  }

  /**
   * connect to a server, for both client and server
   * @param host
   * @param port
   * @param callback
   * @returns {Promise.<void>}
   */
  async connectTo({host, port}, callback) {
    logger.info(`[${this._id}] connecting to: ${host}:${port}`);
    try {
      const ip = await dnsCache.get(host);
      this._fsocket = net.connect({host: ip, port}, callback);
      this._fsocket.on('error', this.onError);
      this._fsocket.on('close', this.onClose);
      this._fsocket.on('data', this.onBackward);
    } catch (err) {
      logger.error(err.message);
    }
  }

  /**
   * create pipes for both data forward and backward
   */
  createPipe(addr) {
    const pipeProps = {
      onNotified: (action) => {
        if (__IS_SERVER__ && action.type === SOCKET_CONNECT_TO_DST) {
          const [addr, callback] = action.payload;
          return this.connectTo(addr, callback);
        }
        if (action.type === PROCESSING_FAILED) {
          const message = action.payload;
          const timeout = Utils.getRandomInt(10, 40);
          logger.error(`connection will be closed in ${timeout}s due to: ${message}`);
          Profile.fatals += 1;
          setTimeout(() => this.onClose(), timeout * 1e3);
        }
      }
    };
    this._pipe = new Pipe(pipeProps);
    this._pipe.setMiddlewares(MIDDLEWARE_DIRECTION_UPWARD, [
      createMiddleware(MIDDLEWARE_TYPE_FRAME, [addr]),
      createMiddleware(MIDDLEWARE_TYPE_CRYPTO),
      createMiddleware(MIDDLEWARE_TYPE_PROTOCOL),
      createMiddleware(MIDDLEWARE_TYPE_OBFS),
    ]);
    this._pipe.on(`next_${MIDDLEWARE_DIRECTION_UPWARD}`, (buf) => this.send(buf, __IS_CLIENT__));
    this._pipe.on(`next_${MIDDLEWARE_DIRECTION_DOWNWARD}`, (buf) => this.send(buf, __IS_SERVER__));
  }

  /**
   * client handshake
   * @param addr
   * @param callback
   * @returns {Promise.<void>}
   */
  onHandshakeDone(addr, callback) {
    const ep = Balancer.getFastest();
    if (this._lastEndPoint === null || ep.host !== this._lastEndPoint.host || ep.port !== this._lastEndPoint.port) {
      logger.info('[balancer] use:', JSON.stringify(ep));
    }
    this._lastEndPoint = ep;
    return this.connectTo(ep, () => {
      this.createPipe(addr);
      callback(this.onForward);
    });
  }

}
<|endoftext|>


<commit_before>
PROTOTYPE.toggle = function(state, event) {
	var cache = this.cache,
		options = this.options,
		tooltip = this.tooltip;

	// Try to prevent flickering when tooltip overlaps show element
	if(event) {
		if((/over|enter/).test(event.type) && (/out|leave/).test(cache.event.type) &&
			options.show.target.add(event.target).length === options.show.target.length &&
			tooltip.has(event.relatedTarget).length) {
			return this;
		}

		// Cache event
		cache.event = cloneEvent(event);
	}

	// If we're currently waiting and we've just hidden... stop it
	this.waiting && !state && (this.hiddenDuringWait = TRUE);

	// Render the tooltip if showing and it isn't already
	if(!this.rendered) { return state ? this.render(1) : this; }
	else if(this.destroyed || this.disabled) { return this; }

	var type = state ? 'show' : 'hide',
		opts = this.options[type],
		otherOpts = this.options[ !state ? 'show' : 'hide' ],
		posOptions = this.options.position,
		contentOptions = this.options.content,
		width = this.tooltip.css('width'),
		visible = this.tooltip[0].offsetWidth > 0,
		animate = state || opts.target.length === 1,
		sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target,
		identicalState, allow, showEvent, delay, after;

	// Detect state if valid one isn't provided
	if((typeof state).search('boolean|number')) { state = !visible; }

	// Check if the tooltip is in an identical state to the new would-be state
	identicalState = !tooltip.is(':animated') && visible === state && sameTarget;

	// Fire tooltip(show/hide) event and check if destroyed
	allow = !identicalState ? !!this._trigger(type, [90]) : NULL;

	// Check to make sure the tooltip wasn't destroyed in the callback
	if(this.destroyed) { return this; }

	// If the user didn't stop the method prematurely and we're showing the tooltip, focus it
	if(allow !== FALSE && state) { this.focus(event); }

	// If the state hasn't changed or the user stopped it, return early
	if(!allow || identicalState) { return this; }

	// Set ARIA hidden attribute
	$.attr(tooltip[0], 'aria-hidden', !!!state);

	// Execute state specific properties
	if(state) {
		// Store show origin coordinates
		cache.origin = cloneEvent(this.mouse);

		// Update tooltip content & title if it's a dynamic function
		if($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); }
		if($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); }

		// Cache mousemove events for positioning purposes (if not already tracking)
		if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
			$(document).bind('mousemove.'+NAMESPACE, this._storeMouse);
			trackingBound = TRUE;
		}

		// Update the tooltip position (set width first to prevent viewport/max-width issues)
		if(!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); }
		this.reposition(event, arguments[2]);
		if(!width) { tooltip.css('width', ''); }

		// Hide other tooltips if tooltip is solo
		if(!!opts.solo) {
			(typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo))
				.not(tooltip).not(opts.target).qtip('hide', $.Event('tooltipsolo'));
		}
	}
	else {
		// Clear show timer if we're hiding
		clearTimeout(this.timers.show);

		// Remove cached origin on hide
		delete cache.origin;

		// Remove mouse tracking event if not needed (all tracking qTips are hidden)
		if(trackingBound && !$(SELECTOR+'[tracking="true"]:visible', opts.solo).not(tooltip).length) {
			$(document).unbind('mousemove.'+NAMESPACE);
			trackingBound = FALSE;
		}

		// Blur the tooltip
		this.blur(event);
	}

	// Define post-animation, state specific properties
	after = $.proxy(function() {
		if(state) {
			// Prevent antialias from disappearing in IE by removing filter
			if(BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); }

			// Remove overflow setting to prevent tip bugs
			tooltip.css('overflow', '');

			// Autofocus elements if enabled
			if('string' === typeof opts.autofocus) {
				$(this.options.show.autofocus, tooltip).focus();
			}

			// If set, hide tooltip when inactive for delay period
			this.options.show.target.trigger('qtip-'+this.id+'-inactive');
		}
		else {
			// Reset CSS states
			tooltip.css({
				display: '',
				visibility: '',
				opacity: '',
				left: '',
				top: ''
			});
		}

		// tooltipvisible/tooltiphidden events
		this._trigger(state ? 'visible' : 'hidden');
	}, this);

	// If no effect type is supplied, use a simple toggle
	if(opts.effect === FALSE || animate === FALSE) {
		tooltip[ type ]();
		after();
	}

	// Use custom function if provided
	else if($.isFunction(opts.effect)) {
		tooltip.stop(1, 1);
		opts.effect.call(tooltip, this);
		tooltip.queue('fx', function(n) {
			after(); n();
		});
	}

	// Use basic fade function by default
	else { tooltip.fadeTo(90, state ? 1 : 0, after); }

	// If inactive hide method is set, active it
	if(state) { opts.target.trigger('qtip-'+this.id+'-inactive'); }

	return this;
};

PROTOTYPE.show = function(event) { return this.toggle(TRUE, event); };

PROTOTYPE.hide = function(event) { return this.toggle(FALSE, event); };

<commit_msg>Use :visible filter instead of checking offsetWidth<commit_after>
PROTOTYPE.toggle = function(state, event) {
	var cache = this.cache,
		options = this.options,
		tooltip = this.tooltip;

	// Try to prevent flickering when tooltip overlaps show element
	if(event) {
		if((/over|enter/).test(event.type) && (/out|leave/).test(cache.event.type) &&
			options.show.target.add(event.target).length === options.show.target.length &&
			tooltip.has(event.relatedTarget).length) {
			return this;
		}

		// Cache event
		cache.event = cloneEvent(event);
	}

	// If we're currently waiting and we've just hidden... stop it
	this.waiting && !state && (this.hiddenDuringWait = TRUE);

	// Render the tooltip if showing and it isn't already
	if(!this.rendered) { return state ? this.render(1) : this; }
	else if(this.destroyed || this.disabled) { return this; }

	var type = state ? 'show' : 'hide',
		opts = this.options[type],
		otherOpts = this.options[ !state ? 'show' : 'hide' ],
		posOptions = this.options.position,
		contentOptions = this.options.content,
		width = this.tooltip.css('width'),
		visible = this.tooltip.is(':visible'),
		animate = state || opts.target.length === 1,
		sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target,
		identicalState, allow, showEvent, delay, after;

	// Detect state if valid one isn't provided
	if((typeof state).search('boolean|number')) { state = !visible; }

	// Check if the tooltip is in an identical state to the new would-be state
	identicalState = !tooltip.is(':animated') && visible === state && sameTarget;

	// Fire tooltip(show/hide) event and check if destroyed
	allow = !identicalState ? !!this._trigger(type, [90]) : NULL;

	// Check to make sure the tooltip wasn't destroyed in the callback
	if(this.destroyed) { return this; }

	// If the user didn't stop the method prematurely and we're showing the tooltip, focus it
	if(allow !== FALSE && state) { this.focus(event); }

	// If the state hasn't changed or the user stopped it, return early
	if(!allow || identicalState) { return this; }

	// Set ARIA hidden attribute
	$.attr(tooltip[0], 'aria-hidden', !!!state);

	// Execute state specific properties
	if(state) {
		// Store show origin coordinates
		cache.origin = cloneEvent(this.mouse);

		// Update tooltip content & title if it's a dynamic function
		if($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); }
		if($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); }

		// Cache mousemove events for positioning purposes (if not already tracking)
		if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
			$(document).bind('mousemove.'+NAMESPACE, this._storeMouse);
			trackingBound = TRUE;
		}

		// Update the tooltip position (set width first to prevent viewport/max-width issues)
		if(!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); }
		this.reposition(event, arguments[2]);
		if(!width) { tooltip.css('width', ''); }

		// Hide other tooltips if tooltip is solo
		if(!!opts.solo) {
			(typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo))
				.not(tooltip).not(opts.target).qtip('hide', $.Event('tooltipsolo'));
		}
	}
	else {
		// Clear show timer if we're hiding
		clearTimeout(this.timers.show);

		// Remove cached origin on hide
		delete cache.origin;

		// Remove mouse tracking event if not needed (all tracking qTips are hidden)
		if(trackingBound && !$(SELECTOR+'[tracking="true"]:visible', opts.solo).not(tooltip).length) {
			$(document).unbind('mousemove.'+NAMESPACE);
			trackingBound = FALSE;
		}

		// Blur the tooltip
		this.blur(event);
	}

	// Define post-animation, state specific properties
	after = $.proxy(function() {
		if(state) {
			// Prevent antialias from disappearing in IE by removing filter
			if(BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); }

			// Remove overflow setting to prevent tip bugs
			tooltip.css('overflow', '');

			// Autofocus elements if enabled
			if('string' === typeof opts.autofocus) {
				$(this.options.show.autofocus, tooltip).focus();
			}

			// If set, hide tooltip when inactive for delay period
			this.options.show.target.trigger('qtip-'+this.id+'-inactive');
		}
		else {
			// Reset CSS states
			tooltip.css({
				display: '',
				visibility: '',
				opacity: '',
				left: '',
				top: ''
			});
		}

		// tooltipvisible/tooltiphidden events
		this._trigger(state ? 'visible' : 'hidden');
	}, this);

	// If no effect type is supplied, use a simple toggle
	if(opts.effect === FALSE || animate === FALSE) {
		tooltip[ type ]();
		after();
	}

	// Use custom function if provided
	else if($.isFunction(opts.effect)) {
		tooltip.stop(1, 1);
		opts.effect.call(tooltip, this);
		tooltip.queue('fx', function(n) {
			after(); n();
		});
	}

	// Use basic fade function by default
	else { tooltip.fadeTo(90, state ? 1 : 0, after); }

	// If inactive hide method is set, active it
	if(state) { opts.target.trigger('qtip-'+this.id+'-inactive'); }

	return this;
};

PROTOTYPE.show = function(event) { return this.toggle(TRUE, event); };

PROTOTYPE.hide = function(event) { return this.toggle(FALSE, event); };

<|endoftext|>