<commit_before>"use strict";

/**
 * Element is the base object for all GUI elements.
 *
 * All GUI elements are based on the Element class, components can be inserted into other componens or into DOM elements.
 *
 * @class Element
 * @param {Element} parent Parent element.
 * @param {String} type Type of the based DOM element.
 */
function Element(parent, type)
{
	/**
	 * Base DOM element for this component.
	 *
	 * Different components may use diferent base element types.
	 *
	 * @attribute element
	 * @type {DOM}
	 */
	this.element = document.createElement(type !== undefined ? type : "div");
	this.element.style.position = "absolute";
	this.element.style.overflow = "hidden";

	/**
	 * The parent element that contains this Element.
	 *
	 * Can be a DOM element or another Element.
	 *
	 * @attribute parent
	 * @type {Element}
	 */
	this.parent = null;
	if(parent !== undefined)
	{
		this.attachTo(parent);
	}

	/**
	 * True if the element is visible.
	 *
	 * @attribute visible
	 * @type {Boolean}
	 */
	this.visible = true;

	/**
	 * Size of this component in px.
	 *
	 * @attribute size
	 * @type {THREE.Vector2}
	 */
	this.size = new THREE.Vector2(0, 0);

	/**
	 * Position of this component relatively to its parent in px.
	 *
	 * @attribute position
	 * @type {THREE.Vector2}
	 */
	this.position = new THREE.Vector2(0, 0);

	/**
	 * Positioning mode, indicates how to anchor the component.
	 *
	 * @attribute mode
	 * @type {Number}
	 */
	this._mode = Element.TOP_LEFT;
}

Element.prototype.constructor = Element;

Element.prototype.isElement = true;

/**
 * Top-left positioning.
 *
 * @static
 * @attribute TOP_LEFT
 * @type {Number}
 */
Element.TOP_LEFT = 0;

/**
 * Top-right positioning.
 *
 * @static
 * @attribute TOP_RIGHT
 * @type {Number}
 */
Element.TOP_RIGHT = 1;

/**
 * Bottom-left positioning.
 *
 * @static
 * @attribute BOTTOM_LEFT
 * @type {Number}
 */
Element.BOTTOM_LEFT = 2;

/**
 * Bottom-right positioning.
 *
 * @static
 * @attribute BOTTOM_RIGHT
 * @type {Number}
 */
Element.BOTTOM_RIGHT = 3;

Element.preventDefault = function(event)
{
	event.preventDefault();
};

/**
 * Add a CSS class to the base DOM element of this Element.
 *
 * @method addClass
 * @param {String} name Name of the class to be added.
 */
Element.prototype.addClass = function(name)
{
	this.element.classList.add(name);
};

/**
 * Remove a CSS class from the base DOM element of this Element.
 *
 * @method removeClass
 * @param {String} name Name of the class to be removed.
 */
Element.prototype.removeClass = function(name)
{
	if(this.element.classList.contains(name))
	{
		this.element.classList.remove(name);
	}
};

/**
 * Change style of the base DOM element.
 *
 * @method setStyle
 * @param {String} attribute Name of the style attribute.
 * @param {String} value Value of the style.
 */
Element.prototype.setStyle = function(attribute, value)
{
	this.element.style[attribute] = value;
};

/**
 * Add and drag and drop default event prevention to this component.
 *
 * Usefull to avoid unwanted actions on draggable components.
 *
 * @method preventDragEvents
 */
Element.prototype.preventDragEvents = function()
{
	this.element.ondrop = Element.preventDefault;
	this.element.ondragover = Element.preventDefault;
};

/**
 * Set alt text, that is displayed when the mouse is over the element. Returns the element created that is attached to the document body.
 *
 * @method setAltText
 * @param {String} altText Alt text.
 */
Element.prototype.setAltText = function(altText)
{
	var element = document.createElement("div");
	element.style.position = "absolute";
	element.style.display = "none";
	element.style.alignItems = "center";
	element.style.zIndex = "10000";
	element.style.border = "3px solid";
	element.style.borderRadius = "5px";
	element.style.color = Editor.theme.textColor;
	element.style.backgroundColor = Editor.theme.barColor;
	element.style.borderColor = Editor.theme.barColor;
	element.style.width = "fit-content";
	element.style.height = "fit-content";
	document.body.appendChild(element);

	//Text
	var text = document.createTextNode(altText);
	element.appendChild(text);

	//Destroy
	var destroyFunction = this.destroy;
	this.destroy = function()
	{
		destroyFunction.call(this);

		if(document.body.contains(element))
		{
			document.body.removeChild(element);
		}
	};

	this.element.style.pointerEvents = "auto";

	//Mouse mouse move event
	this.element.onmousemove = function(event)
	{
		element.style.display = "flex";
		element.style.left = event.clientX + "px";
		element.style.top = (event.clientY - 30) + "px";
	};

	//Mouse out event
	this.element.onmouseout = function()
	{
		element.style.display = "none";
	};

	return element;
};

/**
 * Set method to be called on component click.
 *
 * @method setOnClick
 * @param {Function} callback Function called when the component is clicked.
 */
Element.prototype.setOnClick = function(callback)
{
	this.element.onclick = callback;
};

/**
 * Remove all DOM children from the element.
 *
 * @method removeAllChildren
 */
Element.prototype.removeAllChildren = function()
{
	while(this.element.firstChild)
	{
		this.element.removeChild(this.element.firstChild);
	}
};

/**
 * Attach this component to a new parent component.
 *
 * Destroys the object and reataches the base DOM element to the new parent element.
 *
 * @method attachTo
 * @param {Container} parent Parent container.
 */
Element.prototype.attachTo = function(parent)
{
	if(this.parent === parent || parent === undefined)
	{
		return;
	}

	if(this.parent !== null)
	{
		Element.prototype.destroy.call(this);
	}

	this.parent = parent;

	if(parent.isElement === true)
	{
		parent.element.appendChild(this.element);
	}
	else
	{
		console.warn("nunuStudio: Parent is not a Element." , this);
		this.parent.appendChild(this.element);
	}
};

/**
 * Called to destroy a component.
 *
 * Destroy the element and removes it from its DOM parent.
 *
 * @method destroy
 */
Element.prototype.destroy = function()
{
	if(this.parent !== null)
	{
		if(this.parent.isElement === true)
		{
			if(this.parent.element.contains(this.element))
			{
				this.parent.element.removeChild(this.element);
				this.parent = null;
			}
		}
		else
		{
			console.warn("nunuStudio: Parent is not a Element.", this);
			if(this.parent.contains(this.element))
			{
				this.parent.removeChild(this.element);
				this.parent = null;
			}
		}
	}
};

/**
 * Set positioning mode.
 *
 * @method setMode
 * @param {Number} setMode
 */
Element.prototype.setMode = function(mode)
{
	this._mode = mode;
	this.element.style.bottom = null;
	this.element.style.top = null;
	this.element.style.right = null;
	this.element.style.left = null;
};

/**
 * Set the multiple styles to the DOM element.
 *
 * Style are described in a object that uses the same attribute names as the normal DOM access.
 *
 * Here is an exaple of a style object:
 * {
 * backgroundColor: "#FF0000",
 * color: "#FFFFFF"
 * }
 *
 * @method setStyleList
 * @param {Object} styleList Object describing the style to be applied to the object.
 */
Element.prototype.setStyleList = function(styleList)
{
	for(var i in styleList)
	{
		this.element.style[i] = styleList[i];
	}
};

/**
 * Calculate the position of the container to make it centered.
 *
 * Calculated relatively to its parent size.
 *
 * @method center
 */
Element.prototype.center = function()
{
	this.position.set((this.parent.size.x - this.size.x) / 2, (this.parent.size.y - this.size.y) / 2);
};

/**
 * Update visibility of this element.
 *
 * @method setVisibility
 */
Element.prototype.setVisibility = function(visible)
{
	this.visible = visible;
	this.updateVisibility();
};

/**
 * Update the visibility of this element.
 *
 * @method updateVisibility
 */
Element.prototype.updateVisibility = function()
{
	this.element.style.display = this.visible ? "block" : "none";
};

/**
 * Update the position of this element.
 *
 * @method updatePosition
 */
Element.prototype.updatePosition = function(mode)
{
	if(mode !== undefined)
	{
		this._mode = mode;
	}

	if(this._mode === Element.TOP_LEFT || this._mode === Element.TOP_RIGHT)
	{
		this.element.style.top = this.position.y + "px";
	}
	else
	{
		this.element.style.bottom = this.position.y + "px";
	}

	if(this._mode === Element.TOP_LEFT || this._mode === Element.BOTTOM_LEFT)
	{
		this.element.style.left = this.position.x + "px";
	}
	else
	{
		this.element.style.right = this.position.x + "px";
	}
};

/**
 * Update the size of this element.
 *
 * @method updateSize
 */
Element.prototype.updateSize = function()
{
	this.element.style.width = this.size.x + "px";
	this.element.style.height = this.size.y + "px";
};

/**
 * Update component appearance.
 *
 * Should be called after changing size or position.
 *
 * Uses the updateVisibility and if the element is visible calls the updateSize and updatePosition (in this order) methods to update the interface.
 *
 * @method update
 */
Element.prototype.updateInterface = function()
{
	this.updateVisibility();

	if(this.visible)
	{
		this.updateSize();
		this.updatePosition();
	}
};
<commit_msg>Fix hint out of screen<commit_after>"use strict";

/**
 * Element is the base object for all GUI elements.
 *
 * All GUI elements are based on the Element class, components can be inserted into other componens or into DOM elements.
 *
 * @class Element
 * @param {Element} parent Parent element.
 * @param {String} type Type of the based DOM element.
 */
function Element(parent, type)
{
	/**
	 * Base DOM element for this component.
	 *
	 * Different components may use diferent base element types.
	 *
	 * @attribute element
	 * @type {DOM}
	 */
	this.element = document.createElement(type !== undefined ? type : "div");
	this.element.style.position = "absolute";
	this.element.style.overflow = "hidden";

	/**
	 * The parent element that contains this Element.
	 *
	 * Can be a DOM element or another Element.
	 *
	 * @attribute parent
	 * @type {Element}
	 */
	this.parent = null;
	if(parent !== undefined)
	{
		this.attachTo(parent);
	}

	/**
	 * True if the element is visible.
	 *
	 * @attribute visible
	 * @type {Boolean}
	 */
	this.visible = true;

	/**
	 * Size of this component in px.
	 *
	 * @attribute size
	 * @type {THREE.Vector2}
	 */
	this.size = new THREE.Vector2(0, 0);

	/**
	 * Position of this component relatively to its parent in px.
	 *
	 * @attribute position
	 * @type {THREE.Vector2}
	 */
	this.position = new THREE.Vector2(0, 0);

	/**
	 * Positioning mode, indicates how to anchor the component.
	 *
	 * @attribute mode
	 * @type {Number}
	 */
	this._mode = Element.TOP_LEFT;
}

Element.prototype.constructor = Element;

Element.prototype.isElement = true;

/**
 * Top-left positioning.
 *
 * @static
 * @attribute TOP_LEFT
 * @type {Number}
 */
Element.TOP_LEFT = 0;

/**
 * Top-right positioning.
 *
 * @static
 * @attribute TOP_RIGHT
 * @type {Number}
 */
Element.TOP_RIGHT = 1;

/**
 * Bottom-left positioning.
 *
 * @static
 * @attribute BOTTOM_LEFT
 * @type {Number}
 */
Element.BOTTOM_LEFT = 2;

/**
 * Bottom-right positioning.
 *
 * @static
 * @attribute BOTTOM_RIGHT
 * @type {Number}
 */
Element.BOTTOM_RIGHT = 3;

Element.preventDefault = function(event)
{
	event.preventDefault();
};

/**
 * Add a CSS class to the base DOM element of this Element.
 *
 * @method addClass
 * @param {String} name Name of the class to be added.
 */
Element.prototype.addClass = function(name)
{
	this.element.classList.add(name);
};

/**
 * Remove a CSS class from the base DOM element of this Element.
 *
 * @method removeClass
 * @param {String} name Name of the class to be removed.
 */
Element.prototype.removeClass = function(name)
{
	if(this.element.classList.contains(name))
	{
		this.element.classList.remove(name);
	}
};

/**
 * Change style of the base DOM element.
 *
 * @method setStyle
 * @param {String} attribute Name of the style attribute.
 * @param {String} value Value of the style.
 */
Element.prototype.setStyle = function(attribute, value)
{
	this.element.style[attribute] = value;
};

/**
 * Add and drag and drop default event prevention to this component.
 *
 * Usefull to avoid unwanted actions on draggable components.
 *
 * @method preventDragEvents
 */
Element.prototype.preventDragEvents = function()
{
	this.element.ondrop = Element.preventDefault;
	this.element.ondragover = Element.preventDefault;
};

/**
 * Set alt text, that is displayed when the mouse is over the element. Returns the element created that is attached to the document body.
 *
 * @method setAltText
 * @param {String} altText Alt text.
 */
Element.prototype.setAltText = function(altText)
{
	var element = document.createElement("div");
	element.style.position = "absolute";
	element.style.display = "none";
	element.style.alignItems = "center";
	element.style.zIndex = "10000";
	element.style.border = "3px solid";
	element.style.borderRadius = "5px";
	element.style.color = Editor.theme.textColor;
	element.style.backgroundColor = Editor.theme.barColor;
	element.style.borderColor = Editor.theme.barColor;
	element.style.height = "fit-content";
	document.body.appendChild(element);

	//Text
	var text = document.createTextNode(altText);
	element.appendChild(text);

	//Destroy
	var destroyFunction = this.destroy;
	this.destroy = function()
	{
		destroyFunction.call(this);

		if(document.body.contains(element))
		{
			document.body.removeChild(element);
		}
	};

	this.element.style.pointerEvents = "auto";

	//Mouse mouse move event
	this.element.onmousemove = function(event)
	{
		element.style.display = "flex";
		element.style.left = (event.clientX + 8) + "px";
		element.style.top = (event.clientY - 20) + "px";
	};

	//Mouse out event
	this.element.onmouseout = function()
	{
		element.style.display = "none";
	};

	return element;
};

/**
 * Set method to be called on component click.
 *
 * @method setOnClick
 * @param {Function} callback Function called when the component is clicked.
 */
Element.prototype.setOnClick = function(callback)
{
	this.element.onclick = callback;
};

/**
 * Remove all DOM children from the element.
 *
 * @method removeAllChildren
 */
Element.prototype.removeAllChildren = function()
{
	while(this.element.firstChild)
	{
		this.element.removeChild(this.element.firstChild);
	}
};

/**
 * Attach this component to a new parent component.
 *
 * Destroys the object and reataches the base DOM element to the new parent element.
 *
 * @method attachTo
 * @param {Container} parent Parent container.
 */
Element.prototype.attachTo = function(parent)
{
	if(this.parent === parent || parent === undefined)
	{
		return;
	}

	if(this.parent !== null)
	{
		Element.prototype.destroy.call(this);
	}

	this.parent = parent;

	if(parent.isElement === true)
	{
		parent.element.appendChild(this.element);
	}
	else
	{
		console.warn("nunuStudio: Parent is not a Element." , this);
		this.parent.appendChild(this.element);
	}
};

/**
 * Called to destroy a component.
 *
 * Destroy the element and removes it from its DOM parent.
 *
 * @method destroy
 */
Element.prototype.destroy = function()
{
	if(this.parent !== null)
	{
		if(this.parent.isElement === true)
		{
			if(this.parent.element.contains(this.element))
			{
				this.parent.element.removeChild(this.element);
				this.parent = null;
			}
		}
		else
		{
			console.warn("nunuStudio: Parent is not a Element.", this);
			if(this.parent.contains(this.element))
			{
				this.parent.removeChild(this.element);
				this.parent = null;
			}
		}
	}
};

/**
 * Set positioning mode.
 *
 * @method setMode
 * @param {Number} setMode
 */
Element.prototype.setMode = function(mode)
{
	this._mode = mode;
	this.element.style.bottom = null;
	this.element.style.top = null;
	this.element.style.right = null;
	this.element.style.left = null;
};

/**
 * Set the multiple styles to the DOM element.
 *
 * Style are described in a object that uses the same attribute names as the normal DOM access.
 *
 * Here is an exaple of a style object:
 * {
 * backgroundColor: "#FF0000",
 * color: "#FFFFFF"
 * }
 *
 * @method setStyleList
 * @param {Object} styleList Object describing the style to be applied to the object.
 */
Element.prototype.setStyleList = function(styleList)
{
	for(var i in styleList)
	{
		this.element.style[i] = styleList[i];
	}
};

/**
 * Calculate the position of the container to make it centered.
 *
 * Calculated relatively to its parent size.
 *
 * @method center
 */
Element.prototype.center = function()
{
	this.position.set((this.parent.size.x - this.size.x) / 2, (this.parent.size.y - this.size.y) / 2);
};

/**
 * Update visibility of this element.
 *
 * @method setVisibility
 */
Element.prototype.setVisibility = function(visible)
{
	this.visible = visible;
	this.updateVisibility();
};

/**
 * Update the visibility of this element.
 *
 * @method updateVisibility
 */
Element.prototype.updateVisibility = function()
{
	this.element.style.display = this.visible ? "block" : "none";
};

/**
 * Update the position of this element.
 *
 * @method updatePosition
 */
Element.prototype.updatePosition = function(mode)
{
	if(mode !== undefined)
	{
		this._mode = mode;
	}

	if(this._mode === Element.TOP_LEFT || this._mode === Element.TOP_RIGHT)
	{
		this.element.style.top = this.position.y + "px";
	}
	else
	{
		this.element.style.bottom = this.position.y + "px";
	}

	if(this._mode === Element.TOP_LEFT || this._mode === Element.BOTTOM_LEFT)
	{
		this.element.style.left = this.position.x + "px";
	}
	else
	{
		this.element.style.right = this.position.x + "px";
	}
};

/**
 * Update the size of this element.
 *
 * @method updateSize
 */
Element.prototype.updateSize = function()
{
	this.element.style.width = this.size.x + "px";
	this.element.style.height = this.size.y + "px";
};

/**
 * Update component appearance.
 *
 * Should be called after changing size or position.
 *
 * Uses the updateVisibility and if the element is visible calls the updateSize and updatePosition (in this order) methods to update the interface.
 *
 * @method update
 */
Element.prototype.updateInterface = function()
{
	this.updateVisibility();

	if(this.visible)
	{
		this.updateSize();
		this.updatePosition();
	}
};
<|endoftext|>


<commit_before>import jwt from 'jsonwebtoken';
import config from '../../../config/config';
import User from '../models/user.model';

const debug = true;
const socketConnection = (io) => {
  io.on('connection', (client) => {
    client.on('authenticate', async (data) => {
      try {
        const decoded = jwt.verify(data.token, config.jwtSecret);
        if (decoded !== undefined) {
          client.authenticated = true; // eslint-disable-line
          // join user to a room according to his unique id
          client.join(decoded.id);
          // mark user as online
          const user = await User.get(decoded.id);
          user.online = true;
          await user.save();
          client.emit('authenticated', { auth: true });
          io.to(user._id).emit('update_feed', {});
        } else {
          client.emit('authenticated', { auth: false });
        }
      } catch (err) {
        client.emit('authenticated', { auth: false });
        client.authenticated = false; // eslint-disable-line
        client.disconnect(); // force disconnect not authorized client
      }
    });
    client.on('disconnect', async () => {
      try {
        const user = await User.get(client.id);
        user.online = false;
        await user.save();
        client.disconnect();
      } catch (e) {
        client.authenticated = false; // eslint-disable-line
        client.disconnect();
      }
    });
  });
};

const socketEmitter = (io) => {
  const object = {
    sendToUser(follower, post) {
      io.sockets.emit('update_feed', {});
      io.to(follower._id).emit('update_feed', post);
    }
  };
  return object;
};

export { socketConnection, socketEmitter };
<commit_msg>test live data v3<commit_after>import jwt from 'jsonwebtoken';
import config from '../../../config/config';
import User from '../models/user.model';

const debug = true;
let clients = {};
const socketConnection = (io) => {
  io.on('connection', (client) => {

    client.on('authenticate', async (data) => {
      try {
        const decoded = jwt.verify(data.token, config.jwtSecret);
        if (decoded !== undefined) {
          client.authenticated = true; // eslint-disable-line
          // join user to a room according to his unique id
          client.join(decoded.id);
          // mark user as online
          const user = await User.get(decoded.id);
          user.online = true;
          await user.save();
          clients[decoded.id] = client;
          client.emit('authenticated', { auth: true });
        } else {
          client.emit('authenticated', { auth: false });
        }
      } catch (err) {
        client.emit('authenticated', { auth: false });
        client.authenticated = false; // eslint-disable-line
        client.disconnect(); // force disconnect not authorized client
      }
    });
    client.on('disconnect', async () => {
      try {
        const user = await User.get(client.id);
        user.online = false;
        await user.save();
        client.disconnect();
      } catch (e) {
        client.authenticated = false; // eslint-disable-line
        client.disconnect();
      }
    });
  });
};

const socketEmitter = (io) => {
  const object = {
    sendToUser(follower, post) {
      console.log('sendingg', follower._id);
      const socket = clients[follower._id];
      socket.emit('update_feed', post);
    }
  };
  return object;
};

export { socketConnection, socketEmitter };
<|endoftext|>


<commit_before>// Voat Chat Enhancer v.0.1.8
// if you were linked directly here,
// please go to https://voat.co/v/riverstechstuff/comments/171279
// for the latest version and usage instruction.

var my_username=document.getElementsByClassName('user')[0].firstElementChild.textContent;
var std_blocklist=['billajong9', 'caffeine_pills_', 'campsont29', 'bpiniggiger', 'mkwarmansBRODAWG', 'GreatRedditMigration'];

var ChatImprover = function() {
    //default options.
    this.blocked_users=std_blocklist;
    this.mentions=[my_username, 'yo!'];
    this.images=true;
    this.video=true;
    this.mediaSize='150px';
    this.csshide = { att: 'display', val:'none'};
    this.cssmentioned = { att: 'color', val: 'red'};
    this.color_whole_message=false;

    this.checked_up_to=0;
    this.stop=false;
    // A global incremented lock (forgive the loose
    // use of term) for being able to easily
    // hotswap an older version of this class for a newer one.
    this.lockname='chatImproverLock';
    if (! window.hasOwnProperty(this.lockname)) {
        this.locknum=0;
    } else { this.locknum = window[this.lockname]+=1; }
    window[this.lockname] = this.locknum;
};
ChatImprover.prototype.update = function() {
    this.checked_up_to = 0;
};

//convenience functions
ChatImprover.prototype.block = function(username) {
    this.blocked_users.push(username);
};

ChatImprover.prototype.ment = function(searchterm) {
    this.mentions.push(searchterm);
};

ChatImprover.prototype.stringToColour = function(str) {
    //this particular stringToColor function is credit
    //to Joe Freeman, yckart and CDSanchez at stack overflow
    //stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-javascript

    for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));

    // int/hash to hex
    for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2));

    return colour;
};
ChatImprover.prototype.loop = function() {
    // if manually instructed to stop
    // or newer version added end.
    if (this.stop || window[this.lockname] != this.locknum) {
        this.stop=false;
        return 0;
    }
    //find chatbox element
    chatbox = document.getElementById('subverseChatRoom');
    if (chatbox === null) {
        console.log("err, couldn't find chatbox. ChatImprover is stopping");
        return 1;
    }
    //create array of all messages
    // (obvious O(linear) improvements could be made here
    // TODO: instead just iterate back to find the last marked
    // element then nextElementSibling fwd and apply changes)
    var i,j, el=chatbox.firstElementChild, arr=[el];
    while (el=el.nextElementSibling) { arr.push(el); }

    //assuming no options change, so only start checking
    //at last element.
    i=this.checked_up_to;
    this.checked_up_to=arr.length;
    for(; i<arr.length; i++) {
        //for each message
        el=arr[i];
        // if it has a child element assume first one
        // is a username.
        if (el.firstElementChild == undefined) {
            continue;
        }
        if (! ('original' in el.attributes)) {
            el.attributes['original'] = el.innerHTML;
        }
        x = document.createElement('div');
        x.innerHTML=el.attributes['original'];
        speaker = x.firstElementChild.textContent;
        // expect an exact match.
        if (this.blocked_users.indexOf(speaker) != -1) {
            x.style[this.csshide.att] = this.csshide.val;
        }
	if (this.color_whole_message) {
	    x.style['color']=this.stringToColour(speaker);
        } else {
	    x.firstElementChild.style['color']=this.stringToColour(speaker);
        }
        // for every highlight term, do a search
        // of the textContent - you can
        // use a regex if you want.
        var text = x.textContent;
        for (j=0; j<this.mentions.length; j++) {
            if (text.search(this.mentions[j]) != -1){
                x.style[this.cssmentioned.att] = this.cssmentioned.val;
                break;
            }
        }

        //yes this section needs to be cleaned up.
        if (this.images) {
            //check for images and tag em.
            //blocking question mark so people can't
            //post CSRF links, but really CSRF
            //is on the 3rd party server to solve. Even if
            //we didn't display images automatically,
            //like voat or other site people click lots of
            //unknown links all the time.
            x.innerHTML = (x.innerHTML+' ').replace(new RegExp('([^">])(https?://[^ ?$]*\.(jpg|png)) ', 'gi'), '$1 ').slice(0,-1);
        }
        if (this.video) {
            //check for videos and tag em.
            x.innerHTML = (x.innerHTML+' ').replace(new RegExp('([^">])(https?://[^ ?$]*\.(webm)) ', 'gi'), '$1<video src="$2" style="max-height:' + this.mediaSize +'; vertical-align: bottom" controls>$2</video> ').slice(0,-1);
        }
        //the way we avoid re-adding images and videos
        //for now is to just not add tags to links
        //which begin with a quote or tagend.
        x.innerHTML = x.innerHTML.replace(new RegExp('([^">])(https?://[^ $]*)', 'gi'), '$1$2');

        el.innerHTML=x.innerHTML;

        el.style.cssText=x.style.cssText;
    };
    // run this function again in 30ms after end
    // we use timeOut to put a damper on potential scaling
    // delays / whatever browser scheduling for now,
    // and aspire to 30ms so spammers messages are
    // barely a visual disruption. (most humans can notice
    // differences that last about 10ms)
    var context = this;
    var reloop=function() { context.loop(); };
    window.setTimeout(reloop, 20);
    return 0;
};

window.chat = chat = new ChatImprover();
chat.loop();

//begin options
//these are the default options, modify them as you see fit.
//after you set one or more of these, to apply them retroactively run
//chat.update();
//if you have a long chatlog this may take a little time

//chat.blocked_users=std_blocklist;
//chat.block('annoying_person');
//chat.mentions=[my_username, 'yo!'];
//chat.ment('cool_person');
//chat.images=true;
//chat.video=true;
//chat.mediaSize='150px';

//end options
<commit_msg>linting<commit_after>// Voat Chat Enhancer v.0.1.8
// if you were linked directly here,
// please go to https://voat.co/v/riverstechstuff/comments/171279
// for the latest version and usage instruction.

var my_username=document.getElementsByClassName('user')[0].firstElementChild.textContent;
var std_blocklist=['billajong9', 'caffeine_pills_', 'campsont29', 'bpiniggiger', 'mkwarmansBRODAWG', 'GreatRedditMigration'];

var ChatImprover = function() {
    //default options.
    this.blocked_users=std_blocklist;
    this.mentions=[my_username, 'yo!'];
    this.images=true;
    this.video=true;
    this.mediaSize='150px';
    this.csshide = { att: 'display', val:'none'};
    this.cssmentioned = { att: 'color', val: 'red'};
    this.color_whole_message=false;

    this.checked_up_to=0;
    this.stop=false;
    // A global incremented lock (forgive the loose
    // use of term) for being able to easily
    // hotswap an older version of this class for a newer one.
    this.lockname='chatImproverLock';
    if (! window.hasOwnProperty(this.lockname)) {
        this.locknum=0;
    } else { this.locknum = window[this.lockname]+=1; }
    window[this.lockname] = this.locknum;
};
ChatImprover.prototype.update = function() {
    this.checked_up_to = 0;
};

//convenience functions
ChatImprover.prototype.block = function(username) {
    this.blocked_users.push(username);
};

ChatImprover.prototype.ment = function(searchterm) {
    this.mentions.push(searchterm);
};

ChatImprover.prototype.stringToColour = function(str) {
    //this particular stringToColor function is credit
    //to Joe Freeman, yckart and CDSanchez at stack overflow
    //stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-javascript

    for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));

    // int/hash to hex
    for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2));

    return colour;
};
ChatImprover.prototype.loop = function() {
    // if manually instructed to stop
    // or newer version added end.
    if (this.stop || window[this.lockname] != this.locknum) {
        this.stop=false;
        return 0;
    }
    //find chatbox element
    var chatbox = document.getElementById('subverseChatRoom');
    if (chatbox === null) {
        console.log("err, couldn't find chatbox. ChatImprover is stopping");
        return 1;
    }
    //create array of all messages
    // (obvious O(linear) improvements could be made here
    // TODO: instead just iterate back to find the last marked
    // element then nextElementSibling fwd and apply changes)
    var x,i,j, el=chatbox.firstElementChild, arr=[];
    var speaker;
    while (el) {
        arr.push(el);
        el=el.nextElementSibling;
    }

    //assuming no options change, so only start checking
    //at last element.
    i=this.checked_up_to;
    this.checked_up_to=arr.length;
    for(; i<arr.length; i++) {
        //for each message
        el=arr[i];
        // if it has a child element assume first one
        // is a username.
        if (el.firstElementChild === undefined) {
            continue;
        }
        if (! ('original' in el.attributes)) {
            el.attributes.original = el.innerHTML;
        }
        x = document.createElement('div');
        x.innerHTML=el.attributes.original;
        speaker = x.firstElementChild.textContent;
        // expect an exact match.
        if (this.blocked_users.indexOf(speaker) != -1) {
            x.style[this.csshide.att] = this.csshide.val;
        }
	if (this.color_whole_message) {
	    x.style.color=this.stringToColour(speaker);
        } else {
	        x.firstElementChild.style.color=this.stringToColour(speaker);
        }
        // for every highlight term, do a search
        // of the textContent - you can
        // use a regex if you want.
        var text = x.textContent;
        for (j=0; j<this.mentions.length; j++) {
            if (text.search(this.mentions[j]) != -1){
                x.style[this.cssmentioned.att] = this.cssmentioned.val;
                break;
            }
        }

        //yes this section needs to be cleaned up.
        if (this.images) {
            //check for images and tag em.
            //blocking question mark so people can't
            //post CSRF links, but really CSRF
            //is on the 3rd party server to solve. Even if
            //we didn't display images automatically,
            //like voat or other site people click lots of
            //unknown links all the time.
            x.innerHTML = (x.innerHTML+' ').replace(new RegExp('([^">])(https?://[^ ?$]*\.(jpg|png)) ', 'gi'), '$1 ').slice(0,-1);
        }
        if (this.video) {
            //check for videos and tag em.
            x.innerHTML = (x.innerHTML+' ').replace(new RegExp('([^">])(https?://[^ ?$]*\.(webm)) ', 'gi'), '$1<video src="$2" style="max-height:' + this.mediaSize +'; vertical-align: bottom" controls>$2</video> ').slice(0,-1);
        }
        //the way we avoid re-adding images and videos
        //for now is to just not add tags to links
        //which begin with a quote or tagend.
        x.innerHTML = x.innerHTML.replace(new RegExp('([^">])(https?://[^ $]*)', 'gi'), '$1$2');

        el.innerHTML=x.innerHTML;

        el.style.cssText=x.style.cssText;
    }
    // run this function again in 30ms after end
    // we use timeOut to put a damper on potential scaling
    // delays / whatever browser scheduling for now,
    // and aspire to 30ms so spammers messages are
    // barely a visual disruption. (most humans can notice
    // differences that last about 10ms)
    var context = this;
    var reloop=function() { context.loop(); };
    window.setTimeout(reloop, 20);
    return 0;
};
var chat;
window.chat = chat = new ChatImprover();
chat.loop();

//begin options
//these are the default options, modify them as you see fit.
//after you set one or more of these, to apply them retroactively run
//chat.update();
//if you have a long chatlog this may take a little time

//chat.blocked_users=std_blocklist;
//chat.block('annoying_person');
//chat.mentions=[my_username, 'yo!'];
//chat.ment('cool_person');
//chat.images=true;
//chat.video=true;
//chat.mediaSize='150px';

//end options
<|endoftext|>


<commit_before>import Path          from 'path';
import BrowserWindow from 'browser-window';
import Util          from '../common/Util.js';
import { IPCKeys }   from '../common/Constants.js';

/**
 * Manage the window.
 */
export default class WindowManager {
  /**
   * Initialize instance.
   *
   * @param {Main} context Application context.
   */
  constructor( context ) {
    /**
     * Application context.
     * @type {Main}
     */
    this._context = context;

    /**
     * Collection of a managed window.
     * @type {Map.<String, BrowserWindow>}
     */
    this._windows = new Map();

    /**
     * About dialog.
     * @type {BrowserWindow}
     */
    this._aboutDialog = null;

    context.ipc.on( IPCKeys.RequestCreateNewWindow, this._onRequestCreateNewWindow.bind( this ) );
    context.ipc.on( IPCKeys.RequestSendMessage, this._onRequestSendMessage.bind( this ) );
    context.ipc.on( IPCKeys.RequestGetWindowIDs, this._onRequestGetWindowIDs.bind( this ) );
  }

  /**
   * Reload the focused window, For debug.
   */
  reload() {
    const w = BrowserWindow.getFocusedWindow();
    if( w ) {
      w.reload();
    }
  }

  /**
   * Switch the display of the developer tools window at focused window, For debug.
   */
  toggleDevTools() {
    const w = BrowserWindow.getFocusedWindow();
    if( w ) {
      w.toggleDevTools();
    }
  }

  /**
   * Create a new window.
   *
   * @return {BrowserWindow} Created window.
   */
  createNewWindow() {
    const w = new BrowserWindow( {
      width: 400,
      height: 400,
      minWidth: 400,
      minHeight: 400,
      resizable: true
    } );

    const id = w.id;

    w.on( 'closed', () => {
      if( DEBUG ) { Util.log( 'Window was closed, id = ' + id ); }

      // Unregister
      this._windows.delete( id );

      if( this._windows.size === 0 && this._aboutDialog ) {
        this._aboutDialog.close();
      }
    } );

    const filePath = Path.join( __dirname, 'window-main.html' );
    w.loadURL( 'file://' + filePath + '#' + w.id );
    this._windows.set( id, w );

    return w;
  }

  /**
   * Show the about application window.
   */
  createAboutWindow() {
    if( this._aboutDialog ) { return; }

    const w = new BrowserWindow( {
      width: 400,
      heigh: 256,
      resizable: false,
      alwaysOnTop: true
    } );

    w.setMenu( null );

    w.on( 'closed', () => {
      if( DEBUG ) { Util.log( 'The about application window was closed.' ); }

      this._aboutDialog = null;
    } );

    const filePath = Path.join( __dirname, 'window-about.html' );
    w.loadURL( 'file://' + filePath );

    this._aboutDialog = w;
  }

  /**
   * Occurs when a show new window requested.
   *
   * @param {IPCEvent} ev Event data.
   */
  _onRequestCreateNewWindow( ev ) {
    const createdWindow = this.createNewWindow();
    ev.sender.send( IPCKeys.FinishCreateNewWindow );

    const windowIDs = [];
    for( let key of this._windows.keys() ) {
      windowIDs.push( key );
    }

    this._windows.forEach( ( w ) => {
      // Because it may not receive the message, explicit request ( RequestGetWindowIDs ) later
      if( w.id === createdWindow.id ) { return; }

      w.webContents.send( IPCKeys.UpdateWindowIDs, windowIDs );
    } );
  }

  /**
   * Occurs when a send message requested.
   *
   * @param {IPCEvent} ev      Event data.
   * @param {Number}   id      Target window's identifier.
   * @param {String}   message Message.
   */
  _onRequestSendMessage( ev, id, message ) {
    const w = this._windows.get( id );
    if( w ) {
      w.webContents.send( IPCKeys.UpdateMessage, message );
    }

    ev.sender.send( IPCKeys.FinishSendMessage );
  }

  /**
   * Occurs when a get window identifiers requested.
   *
   * @param {IPCEvent} ev Event data.
   */
  _onRequestGetWindowIDs( ev ) {
    const windowIDs = Array.from( this._windows.keys() );
    ev.sender.send( IPCKeys.FinishGetWindowIDs, windowIDs );
  }
}
<commit_msg>#66 Again commit because fixes were missing<commit_after>import Path          from 'path';
import BrowserWindow from 'browser-window';
import Util          from '../common/Util.js';
import { IPCKeys }   from '../common/Constants.js';

/**
 * Manage the window.
 */
export default class WindowManager {
  /**
   * Initialize instance.
   *
   * @param {Main} context Application context.
   */
  constructor( context ) {
    /**
     * Application context.
     * @type {Main}
     */
    this._context = context;

    /**
     * Collection of a managed window.
     * @type {Map.<String, BrowserWindow>}
     */
    this._windows = new Map();

    /**
     * About dialog.
     * @type {BrowserWindow}
     */
    this._aboutDialog = null;

    context.ipc.on( IPCKeys.RequestCreateNewWindow, this._onRequestCreateNewWindow.bind( this ) );
    context.ipc.on( IPCKeys.RequestSendMessage, this._onRequestSendMessage.bind( this ) );
    context.ipc.on( IPCKeys.RequestGetWindowIDs, this._onRequestGetWindowIDs.bind( this ) );
  }

  /**
   * Reload the focused window, For debug.
   */
  reload() {
    const w = BrowserWindow.getFocusedWindow();
    if( w ) {
      w.reload();
    }
  }

  /**
   * Switch the display of the developer tools window at focused window, For debug.
   */
  toggleDevTools() {
    const w = BrowserWindow.getFocusedWindow();
    if( w ) {
      w.toggleDevTools();
    }
  }

  /**
   * Create a new window.
   *
   * @return {BrowserWindow} Created window.
   */
  createNewWindow() {
    const w = new BrowserWindow( {
      width: 400,
      height: 400,
      minWidth: 400,
      minHeight: 400,
      resizable: true
    } );

    const id = w.id;

    w.on( 'closed', () => {
      if( DEBUG ) { Util.log( 'Window was closed, id = ' + id ); }

      // Unregister
      this._windows.delete( id );
      this._notifyUpdateWindowIDs( id );

      if( this._windows.size === 0 && this._aboutDialog ) {
        this._aboutDialog.close();
      }
    } );

    const filePath = Path.join( __dirname, 'window-main.html' );
    w.loadURL( 'file://' + filePath + '#' + w.id );
    this._windows.set( id, w );

    return w;
  }

  /**
   * Show the about application window.
   */
  createAboutWindow() {
    if( this._aboutDialog ) { return; }

    const w = new BrowserWindow( {
      width: 400,
      heigh: 256,
      resizable: false,
      alwaysOnTop: true
    } );

    w.setMenu( null );

    w.on( 'closed', () => {
      if( DEBUG ) { Util.log( 'The about application window was closed.' ); }

      this._aboutDialog = null;
    } );

    const filePath = Path.join( __dirname, 'window-about.html' );
    w.loadURL( 'file://' + filePath );

    this._aboutDialog = w;
  }

  /**
   * Notify that the window ID list has been updated.
   *
   * @param {Number} excludeID Exclude ID.
   */
  _notifyUpdateWindowIDs( excludeID ) {
    const windowIDs = [];
    for( let key of this._windows.keys() ) {
      windowIDs.push( key );
    }

    this._windows.forEach( ( w ) => {
      if( w.id === excludeID ) { return; }

      w.webContents.send( IPCKeys.UpdateWindowIDs, windowIDs );
    } );
  }

  /**
   * Occurs when a show new window requested.
   *
   * @param {IPCEvent} ev Event data.
   */
  _onRequestCreateNewWindow( ev ) {
    const createdWindow = this.createNewWindow();
    ev.sender.send( IPCKeys.FinishCreateNewWindow );

    this._notifyUpdateWindowIDs( createdWindow.id );
  }

  /**
   * Occurs when a send message requested.
   *
   * @param {IPCEvent} ev      Event data.
   * @param {Number}   id      Target window's identifier.
   * @param {String}   message Message.
   */
  _onRequestSendMessage( ev, id, message ) {
    const w = this._windows.get( id );
    if( w ) {
      w.webContents.send( IPCKeys.UpdateMessage, message );
    }

    ev.sender.send( IPCKeys.FinishSendMessage );
  }

  /**
   * Occurs when a get window identifiers requested.
   *
   * @param {IPCEvent} ev Event data.
   */
  _onRequestGetWindowIDs( ev ) {
    const windowIDs = Array.from( this._windows.keys() );
    ev.sender.send( IPCKeys.FinishGetWindowIDs, windowIDs );
  }
}
<|endoftext|>


<commit_before>'use strict';

// React's test can only work in NODE_ENV=test because of how things
// are set up. So we might as well enforce it.
process.env.NODE_ENV = 'test';

var path = require('path');

var babel = require('babel-core');
var coffee = require('coffee-script');

var tsPreprocessor = require('./ts-preprocessor');

// This assumes the module map has been built. This might not be safe.
// We should consider consuming this from a built fbjs module from npm.
var moduleMap = require('fbjs/module-map');
var babelPluginModules = require('fbjs-scripts/babel-6/rewrite-modules');
var createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction');

// Use require.resolve to be resilient to file moves, npm updates, etc
var pathToBabel = path.join(require.resolve('babel-core'), '..', 'package.json');
var pathToModuleMap = require.resolve('fbjs/module-map');
var pathToBabelPluginDevWithCode = require.resolve('../error-codes/dev-expression-with-codes');
var pathToBabelPluginModules = require.resolve('fbjs-scripts/babel-6/rewrite-modules');
var pathToBabelrc = path.join(__dirname, '..', '..', '.babelrc');

// TODO: make sure this stays in sync with gulpfile
var babelOptions = {
  plugins: [
    pathToBabelPluginDevWithCode, // this pass has to run before `rewrite-modules`
    [babelPluginModules, {
      map: Object.assign(
        {},
        moduleMap,
        {
          'object-assign': 'object-assign',
        }
      ),
    }],
  ],
  retainLines: true,
};

module.exports = {
  process: function(src, filePath) {
    if (filePath.match(/\.coffee$/)) {
      return coffee.compile(src, {'bare': true});
    }
    if (filePath.match(/\.ts$/) && !filePath.match(/\.d\.ts$/)) {
      return tsPreprocessor.compile(src, filePath);
    }
    if (
      !filePath.match(/\/node_modules\//) &&
      !filePath.match(/\/third_party\//)
    ) {
      return babel.transform(
        src,
        Object.assign(
          {filename: path.relative(process.cwd(), filePath)},
          babelOptions
        )
      ).code;
    }
    return src;
  },

  getCacheKey: createCacheKeyFunction([
    __filename,
    pathToBabel,
    pathToBabelrc,
    pathToModuleMap,
    pathToBabelPluginDevWithCode,
    pathToBabelPluginModules,
  ]),
};
<commit_msg>Jest preprocessor better aware of error-codes/codes.json WRT caching<commit_after>'use strict';

// React's test can only work in NODE_ENV=test because of how things
// are set up. So we might as well enforce it.
process.env.NODE_ENV = 'test';

var path = require('path');

var babel = require('babel-core');
var coffee = require('coffee-script');

var tsPreprocessor = require('./ts-preprocessor');

// This assumes the module map has been built. This might not be safe.
// We should consider consuming this from a built fbjs module from npm.
var moduleMap = require('fbjs/module-map');
var babelPluginModules = require('fbjs-scripts/babel-6/rewrite-modules');
var createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction');

// Use require.resolve to be resilient to file moves, npm updates, etc
var pathToBabel = path.join(require.resolve('babel-core'), '..', 'package.json');
var pathToModuleMap = require.resolve('fbjs/module-map');
var pathToBabelPluginDevWithCode = require.resolve('../error-codes/dev-expression-with-codes');
var pathToBabelPluginModules = require.resolve('fbjs-scripts/babel-6/rewrite-modules');
var pathToBabelrc = path.join(__dirname, '..', '..', '.babelrc');
var pathToErrorCodes = require.resolve('../error-codes/codes.json');

// TODO: make sure this stays in sync with gulpfile
var babelOptions = {
  plugins: [
    pathToBabelPluginDevWithCode, // this pass has to run before `rewrite-modules`
    [babelPluginModules, {
      map: Object.assign(
        {},
        moduleMap,
        {
          'object-assign': 'object-assign',
        }
      ),
    }],
  ],
  retainLines: true,
};

module.exports = {
  process: function(src, filePath) {
    if (filePath.match(/\.coffee$/)) {
      return coffee.compile(src, {'bare': true});
    }
    if (filePath.match(/\.ts$/) && !filePath.match(/\.d\.ts$/)) {
      return tsPreprocessor.compile(src, filePath);
    }
    if (
      !filePath.match(/\/node_modules\//) &&
      !filePath.match(/\/third_party\//)
    ) {
      return babel.transform(
        src,
        Object.assign(
          {filename: path.relative(process.cwd(), filePath)},
          babelOptions
        )
      ).code;
    }
    return src;
  },

  getCacheKey: createCacheKeyFunction([
    __filename,
    pathToBabel,
    pathToBabelrc,
    pathToModuleMap,
    pathToBabelPluginDevWithCode,
    pathToBabelPluginModules,
    pathToErrorCodes,
  ]),
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @version      DEV 1.0.1
// @description  Adds a bunch of optional features to sites in the Stack Overflow Network.
// @contributor  ᴉʞuǝ (stackoverflow.com/users/1454538/)
// @contributor  ᔕᖺᘎᕊ (stackexchange.com/users/4337810/)
// @updateURL    https://rawgit.com/soscripted/sox/master/sox.user.js
// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// @require      https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js
// @require      https://cdn.rawgit.com/timdown/rangyinputs/master/rangyinputs-jquery-src.js
// @require      https://cdn.rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js
// @require      https://cdn.rawgit.com/camagu/jquery-feeds/master/jquery.feeds.js
// @require      https://rawgit.com/soscripted/sox/dev/sox.helpers.js?v=02262016j
// @require      https://rawgit.com/soscripted/sox/dev/sox.features.js?v=02262016j
// @resource     settingsDialog https://rawgit.com/soscripted/sox/dev/sox-dialog.html
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_getResourceText
// ==/UserScript==
/*jshint multistr: true */
(function(sox, $, undefined) {
    const SOX = "Stack Overflow Extras";
    const SOX_SETTINGS = "SOXSETTINGS";

    var $settingsDialog = $(GM_getResourceText("settingsDialog")),
        $soxSettingsDialog,
        $soxSettingsDialogFeatures,
        $soxSettingsSave,
        $soxSettingsReset,
        $soxSettingsClose;

    sox.init = function() {

        // add sox CSS file and font-awesome CSS file
        $("head").append("<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'>")
                 .append("<link rel='stylesheet' type='text/css' href='https://rawgit.com/soscripted/sox/dev/sox.css?v=02262016j' />");
        $("body").append($settingsDialog);

        $soxSettingsDialog = $("#sox-settings-dialog");
        $soxSettingsDialogFeatures = $soxSettingsDialog.find("#sox-settings-dialog-features");
        $soxSettingsSave = $soxSettingsDialog.find("#sox-settings-dialog-save");
        $soxSettingsReset = $soxSettingsDialog.find("#sox-settings-dialog-reset");
        $soxSettingsClose = $soxSettingsDialog.find("#sox-settings-dialog-close");

        loadFeatures(); //load all the features in the settings dialog

        // add settings icon to navbar
        var $soxSettingsButton = $("<a/>", {
                id: "soxSettingsButton",
                class: "topbar-icon sox-settings-button",
                title: "Change SOX Settings",
                click: function(e) {
                    e.preventDefault();
                    $("#sox-settings-dialog").toggle();
                }
            }),
            $icon = $("<i/>", {
                class: "fa fa-cogs"
            });
        $soxSettingsButton.append($icon).appendTo("div.network-items");

        // add click handlers for the buttons in the settings dialog
        $soxSettingsClose.on("click", function() {
            $soxSettingsDialog.hide();
        });
        $soxSettingsReset.on("click", function() {
            reset();
            location.reload(); // reload page to reflect changed settings
        });
        $soxSettingsSave.on("click", function() {
            extras = [];
            $soxSettingsDialogFeatures.find('input[type=checkbox]:checked').each(function() {
                var x = $(this).attr('id');
                extras.push(x); //Add the function's ID (also the checkbox's ID) to the array
            });
            save(extras);
            location.reload(); // reload page to reflect changed settings
        });

        // check if settings exist and execute desired functions
        if (isAvailable()) {
            extras = getSettings();
            if (isDeprecated()) { //if the setting is set but a deprecated, non-existent feature exists, then delete the setting and act as if it is new
                reset();
            } else {
                for (i = 0; i < extras.length; ++i) {
                    $soxSettingsDialogFeatures.find("#" + extras[i]).prop('checked', true);
                    try {
                        features[extras[i]](); //Call the functions that were chosen
                    } catch(err) {
                        $soxSettingsDialogFeatures.find("#" + extras[i]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
                        console.log('SOX error: There was an error loading the feature "' + extras[i] + '". Please raise an issue on GitHub.');
                        i++;
                    }
                }
            }
        } else {
            // no settings found, mark all inputs as checked and display settings dialog
            $soxSettingsDialogFeatures.find("input").prop("checked", true);

            setTimeout(function(){
                $soxSettingsDialog.show();
            }, 500);

            //$soxSettingsDialog.delay(800).show(); //not working -- why!?
        }

    };

    function isAvailable() {
        //return ~GM_getValue("sox-featureOptions", -1) ? false : true;
        return (GM_getValue(SOX_SETTINGS, -1) == -1 ? false : true);
    }

    function getSettings() {
        return JSON.parse(GM_getValue(SOX_SETTINGS));
    }

    function reset() {
        GM_deleteValue(SOX_SETTINGS);
    }

    function save(options) {
        GM_setValue(SOX_SETTINGS, JSON.stringify(options));
        console.log("sox settings saved: " + JSON.stringify(options));
    }

    function isDeprecated() { //checks whether the saved settings contain a deprecated feature
        //TODO: add function names to an array and loop instead of || .. || .. ||
        settings = getSettings();
        if (settings.indexOf('answerCountSidebar') != -1 ||
            settings.indexOf('highlightClosedQuestions') != -1 ||
            settings.indexOf('unHideAnswer') != -1 ||
            settings.indexOf('flaggingPercentages') != -1) {
            return true;
        }
    }

    function addFeatures(features) {
        $.each(features, function(i, o) {
            var $div = $("<div/>"),
                $label = $("<label/>"),
                $input = $("<input/>", {
                    id: o[0],
                    "type": "checkbox",
                    style: "margin-right: 5px;"
                });
            $div.append($label);
            $label.append($input);
            $input.after(o[1]);
            $soxSettingsDialogFeatures.append($div);
        });
    }

    function addCategory(name) {
        var $div = $("<div/>"),
            $h3 = $("<h3/>", {
                text: name
            });
        $div.append($h3);
        $soxSettingsDialogFeatures.append($div);
    }

    function loadFeatures() {
        addCategory("Appearance");
        addFeatures([
            ["grayOutVotes", "Gray out deleted votes"],
            ["moveBounty", "Move 'start bounty' to top"],
            ["dragBounty", "Make bounty box draggable"],
            ["renameChat", "Prepend 'Chat - ' to chat tabs' titles"],
            ["exclaim", "Remove exclamation mark on message"],
            ["employeeStar", "Add star after employee names"],
            ["bulletReplace", "Replace triangular bullets with normal ones"],
            ["addEllipsis", "Add ellipsis to long names"],
            ["fixedTopbar", "Fix topbar position"],
            ["displayName", "Display name before gravatar"],
            ["unspoil", "Add a link to show all spoilers in a post"],
            ["spoilerTip", "Differentiate spoilers from empty blockquotes"],
            ["stickyVoteButtons", "Make vote buttons next to posts sticky whilst scrolling on that post"],
            ["betterCSS", "Add extra CSS for voting signs and favourite button (currently only on Android SE)"],
            ["standOutDupeCloseMigrated", "Add colourful, more apparent signs to questions that are on hold, duplicates, closed or migrated on question lists"],
            ["colorAnswerer", "Color answerer's comments"],
            ["highlightQuestions", "Alternate favourite questions highlighing"],
            ["metaNewQuestionAlert", "Add an extra mod diamond to the topbar that alerts you if there is a new question posted on the child-meta of the current site"],
            ["hideHotNetworkQuestions", "Hide the Hot Network Questions module"],
            ["hideHireMe", "Hide the Looking for a Job module"],
            ["hideCommunityBulletin", "Hide the Community Bulletin module"],
            ["hideSearchBar", "Replaces the searchbar with an link to the search page"]
        ]);

        addCategory("Flags");
        addFeatures([
            ["flagOutcomeTime", "Show the flag outcome time when viewing your Flag History"],
            ["flagPercentages", "Show flagging percentages for each type in the Flag Summary"]
            // lots more to come
        ]);

        addCategory("Editing");
        addFeatures([
            ["kbdAndBullets", "Add KBD and list buttons to editor toolbar"],
            ["editComment", "Pre-defined edit comment options"],
            ["editReasonTooltip", "Add a tooltip to posts showing the latest revision's comment on 'edited [date] at [time]'"],
            ["addSBSBtn", "Add a button to the editor toolbar to start side-by-side editing"],
            ["linkQuestionAuthorName", "Add a button in the editor toolbar to insert a hyperlink to a post and add the author automatically"],
            ["titleEditDiff", "Make title edits show seperately rather than merged"]
        ]);

        addCategory("Comments");
        addFeatures([
            ["moveCommentsLink", "Move 'show x more comments' to the top"],
            ["commentShortcuts", "Use Ctrl+I,B,K (to italicise, bolden and add code backticks) in comments"],
            ["quickCommentShortcutsMain", "Add shortcuts to add pre-defined comments to comment fields"],
            ["commentReplies", "Add reply links to comments for quick replying (without having to type someone's username)"],
            ["autoShowCommentImages", "View linked images (to imgur) in comments inline"],
            ["showCommentScores", "Show your comment and comment replies scores in your profile tabs"]
        ]);

        addCategory("Unsorted");
        addFeatures([
            //other
            ["shareLinksMarkdown", "Change 'share' link to format of [post-name](url)"],
            ["parseCrossSiteLinks", "Parse titles to links cross-SE-sites"],
            ["confirmNavigateAway", "Add a confirmation dialog before navigating away on pages whilst you are still typing a comment"],
            ["sortByBountyAmount", "Add an option to filter bounties by their amount"],
            ["isQuestionHot", "Add a label on questions which are hot-network questions"],
            ["answerTagsSearch", "Show tags for the question an answer belongs to on search pages (for better context)"],
            ["metaChatBlogStackExchangeButton", "Show meta, chat and blog buttons on hover of a site under the StackExchange button"],
            ["alwaysShowImageUploadLinkBox", "Always show the 'Link from the web' box when uploading an image"],
            ["addAuthorNameToInboxNotifications", "Add the author's name to notifications in the inbox"],
            ["scrollToTop", "Add Scroll To Top button"],
            ["linkedPostsInline", "Display linked posts inline (with an arrow)"]
        ]);
    }

}(window.sox = window.sox || {}, jQuery));

// initialize sox
sox.init(); //TODO: not sure if we really need this to be a function, script is wrapped in IIFE so init code could just go at the top.
<commit_msg>add yes-hover to sox button<commit_after>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @version      DEV 1.0.1
// @description  Adds a bunch of optional features to sites in the Stack Overflow Network.
// @contributor  ᴉʞuǝ (stackoverflow.com/users/1454538/)
// @contributor  ᔕᖺᘎᕊ (stackexchange.com/users/4337810/)
// @updateURL    https://rawgit.com/soscripted/sox/master/sox.user.js
// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// @require      https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js
// @require      https://cdn.rawgit.com/timdown/rangyinputs/master/rangyinputs-jquery-src.js
// @require      https://cdn.rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js
// @require      https://cdn.rawgit.com/camagu/jquery-feeds/master/jquery.feeds.js
// @require      https://rawgit.com/soscripted/sox/dev/sox.helpers.js?v=02262016j
// @require      https://rawgit.com/soscripted/sox/dev/sox.features.js?v=02262016j
// @resource     settingsDialog https://rawgit.com/soscripted/sox/dev/sox-dialog.html
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_getResourceText
// ==/UserScript==
/*jshint multistr: true */
(function(sox, $, undefined) {
    const SOX = "Stack Overflow Extras";
    const SOX_SETTINGS = "SOXSETTINGS";

    var $settingsDialog = $(GM_getResourceText("settingsDialog")),
        $soxSettingsDialog,
        $soxSettingsDialogFeatures,
        $soxSettingsSave,
        $soxSettingsReset,
        $soxSettingsClose;

    sox.init = function() {

        // add sox CSS file and font-awesome CSS file
        $("head").append("<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'>")
                 .append("<link rel='stylesheet' type='text/css' href='https://rawgit.com/soscripted/sox/dev/sox.css?v=02262016j' />");
        $("body").append($settingsDialog);

        $soxSettingsDialog = $("#sox-settings-dialog");
        $soxSettingsDialogFeatures = $soxSettingsDialog.find("#sox-settings-dialog-features");
        $soxSettingsSave = $soxSettingsDialog.find("#sox-settings-dialog-save");
        $soxSettingsReset = $soxSettingsDialog.find("#sox-settings-dialog-reset");
        $soxSettingsClose = $soxSettingsDialog.find("#sox-settings-dialog-close");

        loadFeatures(); //load all the features in the settings dialog

        // add settings icon to navbar
        var $soxSettingsButton = $("<a/>", {
                id: "soxSettingsButton",
                class: "topbar-icon sox-settings-button yes-hover",
                title: "Change SOX Settings",
                click: function(e) {
                    e.preventDefault();
                    $("#sox-settings-dialog").toggle();
                }
            }),
            $icon = $("<i/>", {
                class: "fa fa-cogs"
            });
        $soxSettingsButton.append($icon).appendTo("div.network-items");

        // add click handlers for the buttons in the settings dialog
        $soxSettingsClose.on("click", function() {
            $soxSettingsDialog.hide();
        });
        $soxSettingsReset.on("click", function() {
            reset();
            location.reload(); // reload page to reflect changed settings
        });
        $soxSettingsSave.on("click", function() {
            extras = [];
            $soxSettingsDialogFeatures.find('input[type=checkbox]:checked').each(function() {
                var x = $(this).attr('id');
                extras.push(x); //Add the function's ID (also the checkbox's ID) to the array
            });
            save(extras);
            location.reload(); // reload page to reflect changed settings
        });

        // check if settings exist and execute desired functions
        if (isAvailable()) {
            extras = getSettings();
            if (isDeprecated()) { //if the setting is set but a deprecated, non-existent feature exists, then delete the setting and act as if it is new
                reset();
            } else {
                for (i = 0; i < extras.length; ++i) {
                    $soxSettingsDialogFeatures.find("#" + extras[i]).prop('checked', true);
                    try {
                        features[extras[i]](); //Call the functions that were chosen
                    } catch(err) {
                        $soxSettingsDialogFeatures.find("#" + extras[i]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
                        console.log('SOX error: There was an error loading the feature "' + extras[i] + '". Please raise an issue on GitHub.');
                        i++;
                    }
                }
            }
        } else {
            // no settings found, mark all inputs as checked and display settings dialog
            $soxSettingsDialogFeatures.find("input").prop("checked", true);

            setTimeout(function(){
                $soxSettingsDialog.show();
            }, 500);

            //$soxSettingsDialog.delay(800).show(); //not working -- why!?
        }

    };

    function isAvailable() {
        //return ~GM_getValue("sox-featureOptions", -1) ? false : true;
        return (GM_getValue(SOX_SETTINGS, -1) == -1 ? false : true);
    }

    function getSettings() {
        return JSON.parse(GM_getValue(SOX_SETTINGS));
    }

    function reset() {
        GM_deleteValue(SOX_SETTINGS);
    }

    function save(options) {
        GM_setValue(SOX_SETTINGS, JSON.stringify(options));
        console.log("sox settings saved: " + JSON.stringify(options));
    }

    function isDeprecated() { //checks whether the saved settings contain a deprecated feature
        //TODO: add function names to an array and loop instead of || .. || .. ||
        settings = getSettings();
        if (settings.indexOf('answerCountSidebar') != -1 ||
            settings.indexOf('highlightClosedQuestions') != -1 ||
            settings.indexOf('unHideAnswer') != -1 ||
            settings.indexOf('flaggingPercentages') != -1) {
            return true;
        }
    }

    function addFeatures(features) {
        $.each(features, function(i, o) {
            var $div = $("<div/>"),
                $label = $("<label/>"),
                $input = $("<input/>", {
                    id: o[0],
                    "type": "checkbox",
                    style: "margin-right: 5px;"
                });
            $div.append($label);
            $label.append($input);
            $input.after(o[1]);
            $soxSettingsDialogFeatures.append($div);
        });
    }

    function addCategory(name) {
        var $div = $("<div/>"),
            $h3 = $("<h3/>", {
                text: name
            });
        $div.append($h3);
        $soxSettingsDialogFeatures.append($div);
    }

    function loadFeatures() {
        addCategory("Appearance");
        addFeatures([
            ["grayOutVotes", "Gray out deleted votes"],
            ["moveBounty", "Move 'start bounty' to top"],
            ["dragBounty", "Make bounty box draggable"],
            ["renameChat", "Prepend 'Chat - ' to chat tabs' titles"],
            ["exclaim", "Remove exclamation mark on message"],
            ["employeeStar", "Add star after employee names"],
            ["bulletReplace", "Replace triangular bullets with normal ones"],
            ["addEllipsis", "Add ellipsis to long names"],
            ["fixedTopbar", "Fix topbar position"],
            ["displayName", "Display name before gravatar"],
            ["unspoil", "Add a link to show all spoilers in a post"],
            ["spoilerTip", "Differentiate spoilers from empty blockquotes"],
            ["stickyVoteButtons", "Make vote buttons next to posts sticky whilst scrolling on that post"],
            ["betterCSS", "Add extra CSS for voting signs and favourite button (currently only on Android SE)"],
            ["standOutDupeCloseMigrated", "Add colourful, more apparent signs to questions that are on hold, duplicates, closed or migrated on question lists"],
            ["colorAnswerer", "Color answerer's comments"],
            ["highlightQuestions", "Alternate favourite questions highlighing"],
            ["metaNewQuestionAlert", "Add an extra mod diamond to the topbar that alerts you if there is a new question posted on the child-meta of the current site"],
            ["hideHotNetworkQuestions", "Hide the Hot Network Questions module"],
            ["hideHireMe", "Hide the Looking for a Job module"],
            ["hideCommunityBulletin", "Hide the Community Bulletin module"],
            ["hideSearchBar", "Replaces the searchbar with an link to the search page"]
        ]);

        addCategory("Flags");
        addFeatures([
            ["flagOutcomeTime", "Show the flag outcome time when viewing your Flag History"],
            ["flagPercentages", "Show flagging percentages for each type in the Flag Summary"]
            // lots more to come
        ]);

        addCategory("Editing");
        addFeatures([
            ["kbdAndBullets", "Add KBD and list buttons to editor toolbar"],
            ["editComment", "Pre-defined edit comment options"],
            ["editReasonTooltip", "Add a tooltip to posts showing the latest revision's comment on 'edited [date] at [time]'"],
            ["addSBSBtn", "Add a button to the editor toolbar to start side-by-side editing"],
            ["linkQuestionAuthorName", "Add a button in the editor toolbar to insert a hyperlink to a post and add the author automatically"],
            ["titleEditDiff", "Make title edits show seperately rather than merged"]
        ]);

        addCategory("Comments");
        addFeatures([
            ["moveCommentsLink", "Move 'show x more comments' to the top"],
            ["commentShortcuts", "Use Ctrl+I,B,K (to italicise, bolden and add code backticks) in comments"],
            ["quickCommentShortcutsMain", "Add shortcuts to add pre-defined comments to comment fields"],
            ["commentReplies", "Add reply links to comments for quick replying (without having to type someone's username)"],
            ["autoShowCommentImages", "View linked images (to imgur) in comments inline"],
            ["showCommentScores", "Show your comment and comment replies scores in your profile tabs"]
        ]);

        addCategory("Unsorted");
        addFeatures([
            //other
            ["shareLinksMarkdown", "Change 'share' link to format of [post-name](url)"],
            ["parseCrossSiteLinks", "Parse titles to links cross-SE-sites"],
            ["confirmNavigateAway", "Add a confirmation dialog before navigating away on pages whilst you are still typing a comment"],
            ["sortByBountyAmount", "Add an option to filter bounties by their amount"],
            ["isQuestionHot", "Add a label on questions which are hot-network questions"],
            ["answerTagsSearch", "Show tags for the question an answer belongs to on search pages (for better context)"],
            ["metaChatBlogStackExchangeButton", "Show meta, chat and blog buttons on hover of a site under the StackExchange button"],
            ["alwaysShowImageUploadLinkBox", "Always show the 'Link from the web' box when uploading an image"],
            ["addAuthorNameToInboxNotifications", "Add the author's name to notifications in the inbox"],
            ["scrollToTop", "Add Scroll To Top button"],
            ["linkedPostsInline", "Display linked posts inline (with an arrow)"]
        ]);
    }

}(window.sox = window.sox || {}, jQuery));

// initialize sox
sox.init(); //TODO: not sure if we really need this to be a function, script is wrapped in IIFE so init code could just go at the top.
<|endoftext|>


<commit_before>import React, {Component} from 'react';

import PlayerSongListPanel from '../components/PlayerSongListPanel';
import PlayerView from '../components/PlayerView';
import PlayListsView from '../components/PlayListsView';
import SearchView from '../components/SearchView';
import ServiceClient from '../service/ServiceClient';
import TrackInfoView from '../components/TrackInfoView';
import TrackTableView from '../components/TrackTableView';

import TrackInfoModel from '../model/TrackInfoModel';

export default class Application extends Component {
    constructor(props) {
        super(props);

        this.playSelectionChange = this.playSelectionChange.bind(this);
        this.searchSelectionChange = this.searchSelectionChange.bind(this);
        this.songlistAddChange = this.songlistAddChange.bind(this);
        this.songlistSelectionChange = this.songlistSelectionChange.bind(this);
        this.togglePlayerLock = this.togglePlayerLock.bind(this);
        this.toggleSonglistOpen = this.toggleSonglistOpen.bind(this);
        this.trackSelectionChange = this.trackSelectionChange.bind(this);
        this.trackInfoChange = this.trackInfoChange.bind(this);
    }

    static propTypes = {
        userId: React.PropTypes.string.isRequired
    }

    static defaultProps = {
        userId: ""
    }

    state = {
        selectedPlaylistId: null,
        selectedTrack: null,
        songlist: [],
        trackInfo: null,
        songlistOpen: false,
        playerLockState: false,
    }

    render() {
        return (

                    <SearchView className="nm-search-view"
                                placeholder="请输入"
                                handleSearchSelectionChange={this.searchSelectionChange}
                    />

                        <PlayListsView className="nm-play-list-view"
                                       userId={ this.props.userId }
                                       handleSelectionChange={this.playSelectionChange}
                        />

                        <TrackInfoView ref="trackInfoView" className="nm-track-info-view"
                                       data={ this.state.trackInfo }
                                       handleSelectionChange={this.trackSelectionChange}
                                       songlistAddChange={this.songlistAddChange}
                        />
                        <TrackTableView className="nm-track-table-view striped"
                                        playlistId={ this.state.selectedPlaylistId }
                                        handleSelectionChange={this.trackSelectionChange}
                                        songlistAddChange={this.songlistAddChange}
                                        handleInfoChange={this.trackInfoChange}
                        />

                    <PlayerView className="nm-player-view"
                                lock={this.state.playerLockState}
                                selectedTrack={this.state.selectedTrack}
                                songlist={this.state.songlist}
                                handleSelectionChange={this.trackSelectionChange}
                                handleSonglistOpenChange={this.toggleSonglistOpen}
                                handleLockChange={this.togglePlayerLock}
                    />
                    <PlayerSongListPanel className="nm-player-songlist-panel"
                    playingTrack={this.state.selectedTrack}
                    songlist={this.state.songlist}
                    handleToggleChange={this.toggleSonglistOpen}
                    handleSelectionChange={this.songlistSelectionChange}
                    open={this.state.songlistOpen}
                    />

        );
    }

    playSelectionChange(playlistId) {
        if (playlistId !== this.state.selectedPlaylistId) {
            // $('section.content').animate({opacity: 0}, 50, 'linear').animate({opacity: 1}, 50, 'linear');
            // $('section.content').css({opacity: 0});
            $('section.content').scrollTop(0);
            this.setState({selectedPlaylistId: playlistId});
        }

    }

    trackSelectionChange(track) {
        if (track) {
            if (this.state.selectedTrack) {
                if (track.id !== this.state.selectedTrack.id) {
                    this.setState({selectedTrack: track});
                }
            }
            else {
                this.setState({selectedTrack: track});
            }
        }
    }

    songlistAddChange(value) {
        if (value === null || value === undefined) {
            console.log("songlistAddChange params's value cann't be null or undefined!");
            return;
        }
        const sameSong = this.state.songlist.find(item => item.id === value.id);
        if (sameSong === undefined) {
            this.setState({
                songlist: this.state.songlist.concat(value)
            });
        }
    }

    songlistSelectionChange(value) {
        if (!this.state.selectedTrack || (this.state.selectedTrack.id !== value.id)) {
            this.setState({selectedTrack: value});
        }
    }

// 闲置， 可能会用到
    getPlayingSongIndexOfSonglist() {
        const track = this.state.selectedTrack;
        const index = this.state.songlist.findIndex(item => track.id === item.id);
        return index;
    }

    trackInfoChange(data) {
        if (data instanceof TrackInfoModel) {
            this.setState({
                trackInfo: data
            });
        }
        else {
            console.log("trackInfoChange params data's type is error. ");
        }

    }

    searchSelectionChange(data) {
        const trackInfo = new TrackInfoModel({data, type: '单曲'});
        this.setState({
            selectedPlaylistId: null
        });
        this.trackSelectionChange(data);
        this.trackInfoChange(trackInfo);
        this.songlistAddChange(trackInfo);
    }

    toggleSonglistOpen(state = !this.state.songlistOpen) {
        this.setState({
            songlistOpen: state
        });
    }
    togglePlayerLock(state = !this.state.playerLockState) {
        this.setState({
            playerLockState: state
        });
    }

    tempFunc = () => {
        return null;
    }
}
<commit_msg>Fixed the error of songlistAddchange.<commit_after>import React, {Component} from 'react';

import PlayerSongListPanel from '../components/PlayerSongListPanel';
import PlayerView from '../components/PlayerView';
import PlayListsView from '../components/PlayListsView';
import SearchView from '../components/SearchView';
import ServiceClient from '../service/ServiceClient';
import TrackInfoView from '../components/TrackInfoView';
import TrackTableView from '../components/TrackTableView';

import TrackInfoModel from '../model/TrackInfoModel';

export default class Application extends Component {
    constructor(props) {
        super(props);

        this.playSelectionChange = this.playSelectionChange.bind(this);
        this.searchSelectionChange = this.searchSelectionChange.bind(this);
        this.songlistAddChange = this.songlistAddChange.bind(this);
        this.songlistSelectionChange = this.songlistSelectionChange.bind(this);
        this.togglePlayerLock = this.togglePlayerLock.bind(this);
        this.toggleSonglistOpen = this.toggleSonglistOpen.bind(this);
        this.trackSelectionChange = this.trackSelectionChange.bind(this);
        this.trackInfoChange = this.trackInfoChange.bind(this);
    }

    static propTypes = {
        userId: React.PropTypes.string.isRequired
    }

    static defaultProps = {
        userId: ""
    }

    state = {
        selectedPlaylistId: null,
        selectedTrack: null,
        songlist: [],
        trackInfo: null,
        songlistOpen: false,
        playerLockState: false,
    }

    render() {
        return (

                    <SearchView className="nm-search-view"
                                placeholder="请输入"
                                handleSearchSelectionChange={this.searchSelectionChange}
                    />

                        <PlayListsView className="nm-play-list-view"
                                       userId={ this.props.userId }
                                       handleSelectionChange={this.playSelectionChange}
                        />

                        <TrackInfoView ref="trackInfoView" className="nm-track-info-view"
                                       data={ this.state.trackInfo }
                                       handleSelectionChange={this.trackSelectionChange}
                                       songlistAddChange={this.songlistAddChange}
                        />
                        <TrackTableView className="nm-track-table-view striped"
                                        playlistId={ this.state.selectedPlaylistId }
                                        handleSelectionChange={this.trackSelectionChange}
                                        songlistAddChange={this.songlistAddChange}
                                        handleInfoChange={this.trackInfoChange}
                        />

                    <PlayerView className="nm-player-view"
                                lock={this.state.playerLockState}
                                selectedTrack={this.state.selectedTrack}
                                songlist={this.state.songlist}
                                handleSelectionChange={this.trackSelectionChange}
                                handleSonglistOpenChange={this.toggleSonglistOpen}
                                handleLockChange={this.togglePlayerLock}
                    />
                    <PlayerSongListPanel className="nm-player-songlist-panel"
                    playingTrack={this.state.selectedTrack}
                    songlist={this.state.songlist}
                    handleToggleChange={this.toggleSonglistOpen}
                    handleSelectionChange={this.songlistSelectionChange}
                    open={this.state.songlistOpen}
                    />

        );
    }

    playSelectionChange(playlistId) {
        if (playlistId !== this.state.selectedPlaylistId) {
            // $('section.content').animate({opacity: 0}, 50, 'linear').animate({opacity: 1}, 50, 'linear');
            // $('section.content').css({opacity: 0});
            $('section.content').scrollTop(0);
            this.setState({selectedPlaylistId: playlistId});
        }

    }

    trackSelectionChange(track) {
        if (track) {
            if (this.state.selectedTrack) {
                if (track.id !== this.state.selectedTrack.id) {
                    this.setState({selectedTrack: track});
                }
            }
            else {
                this.setState({selectedTrack: track});
            }
        }
    }

    songlistAddChange(value) {
        if (value === null || value === undefined) {
            console.log("songlistAddChange params's value cann't be null or undefined!");
            return;
        }

        const sameSong = this.state.songlist.find(item => item.id === value.id);
        if (sameSong === undefined) {
            this.setState({
                songlist: [value].concat(this.state.songlist)
            });
        }
    }

    songlistSelectionChange(value) {
        if (!this.state.selectedTrack || (this.state.selectedTrack.id !== value.id)) {
            this.setState({selectedTrack: value});
        }
    }

// 闲置， 可能会用到
    getPlayingSongIndexOfSonglist() {
        const track = this.state.selectedTrack;
        const index = this.state.songlist.findIndex(item => track.id === item.id);
        return index;
    }

    trackInfoChange(data) {
        if (data instanceof TrackInfoModel) {
            this.setState({
                trackInfo: data
            });
        }
        else {
            console.log("trackInfoChange params data's type is error. ");
        }

    }

    searchSelectionChange(data) {
        const trackInfo = new TrackInfoModel({data, type: '单曲'});
        this.setState({
            selectedPlaylistId: null
        });
        this.trackSelectionChange(data);
        this.trackInfoChange(trackInfo);
        this.songlistAddChange(trackInfo);
    }

    toggleSonglistOpen(state = !this.state.songlistOpen) {
        this.setState({
            songlistOpen: state
        });
    }
    togglePlayerLock(state = !this.state.playerLockState) {
        this.setState({
            playerLockState: state
        });
    }

    tempFunc = () => {
        return null;
    }
}
<|endoftext|>


<commit_before>import {
  all,
  call,
  put,
  select,
  throttle,
} from 'redux-saga/effects';

import every from 'lodash/every';

import {getCurrentProject, getErrors} from '../selectors';
import generatePreview from '../util/generatePreview';
import {projectCompiled} from '../actions';

export function* validatedSource() {
  const errors = yield select(getErrors);
  if (every(errors, errorList => errorList.state === 'passed')) {
    const currentProject = yield select(getCurrentProject);
    const timestamp = Date.now();
    const preview = yield call(
      generatePreview,
      currentProject,
      {
        nonBlockingAlertsAndPrompts: true,
        targetBaseTop: true,
        propagateErrorsToParent: true,
        listenForMessages: true,
      },
    );
    yield put(projectCompiled(preview, timestamp));
  }
}

export default function* () {
  yield all([throttle(100, 'VALIDATED_SOURCE', validatedSource)]);
}
<commit_msg>Validates source on project creation, closes #1257<commit_after>import {
  all,
  call,
  put,
  select,
  throttle,
  takeEvery,
} from 'redux-saga/effects';

import every from 'lodash/every';

import {getCurrentProject, getErrors} from '../selectors';
import generatePreview from '../util/generatePreview';
import {projectCompiled} from '../actions';

export function* validatedSource() {
  const errors = yield select(getErrors);
  if (every(errors, errorList => errorList.state === 'passed')) {
    const currentProject = yield select(getCurrentProject);
    const timestamp = Date.now();
    const preview = yield call(
      generatePreview,
      currentProject,
      {
        nonBlockingAlertsAndPrompts: true,
        targetBaseTop: true,
        propagateErrorsToParent: true,
        listenForMessages: true,
      },
    );
    yield put(projectCompiled(preview, timestamp));
  }
}

export default function* () {
  yield all([
    throttle(100, 'VALIDATED_SOURCE', validatedSource),
    takeEvery('PROJECT_CREATED', validatedSource),
  ]);
}
<|endoftext|>


<commit_before>"use strict";

var Coffee = {
  debug: 0,
  debugString: "200\n1. March 14:28:371",

  msgNoPots: 'Ingen kanner i dag',
  msgNoCoffee: 'Kaffen har ikke blitt satt på',
  msgFormatError: 'Feil i kaffeformat',
  msgConnectionError: 'Frakoblet fra kaffekanna',
  msgComforting: 'Så så, det er sikkert kaffe :)',
  msgNotification: 'Kaffen er satt på, straks klar :)',

  get: function(pretty, callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var api = Affiliation.org[localStorage.affiliationKey1].hw.apis.coffee;

    // Receives the status for the coffee pot
    var self = this;
    Ajaxer.getPlainText({
      url: api,
      success: function(data) {

        // If coffee debugging is enabled
        if (self.debug) {
          data = self.debugString;
        }

        try {
          // Split into pot number and age of last pot
          var pieces = data.split("\n");
          var pots = Number(pieces[0]);
          var ageString = pieces[1];

          // Coffee made today?
          if (self.isMadeToday(ageString)) {

            // Calculate minute difference from right now
            var now = new Date();
            var coffeeTime = String(ageString.match(/\d+:\d+:\d+/)).split(':');
            var then = new Date(now.getFullYear(), now.getMonth(), now.getDate(), coffeeTime[0], coffeeTime[1]);
            var age = self.minuteDiff(then);

            // If pretty strings are requested
            if (pretty) {
              age = self.prettyAgeString(age, coffeeTime);
              pots = self.prettyPotsString(pots);
            }

            // Call it back
            callback(pots, age);
          }
          else {
            // Coffee was not made today
            if (pretty) {
              callback(self.msgNoPots, self.msgNoCoffee);
            }
            else {
              callback(0, 0);
            }
          }
        } catch (err) {
          if (self.debug) console.log('ERROR: Coffee format is wrong:', err);
          callback(self.msgFormatError, self.msgComforting);
        }
      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR: Failed to get coffee pot status.');
        callback(self.msgConnectionError, self.msgComforting);
      },
    });
  },

  isMadeToday: function(ageString) {
    // Get now
    var now = new Date();
    // Figure out which date the coffee was made
    var dateObject = ageString.match(/\d+\. [a-zA-Z]+/);
    var dateString = String(dateObject);
    dateString = dateString.replace('.', ''); // Remove period
    dateString = dateString + ', ' + now.getFullYear();
    var coffeeDate = new Date(dateString);
    // Check if the coffee pots were made today
    return coffeeDate.getDate() == now.getDate();
  },

  minuteDiff: function(then) {
    // Get now
    var now = new Date();
    var one_minute = 1000 * 60;
    // Calculate difference between the two dates, and convert to minutes
    return Math.floor(( now.getTime() - then.getTime() ) / one_minute);
  },

  prettyAgeString: function(diff, coffeeTime) {
    // Create a proper time string from all the minutes
    if (0 <= diff && diff <= 9)
      return 'Kaffen ble nettopp laget';
    else if (10 <= diff && diff <= 59)
      return 'Kaffen ble laget for '+diff+' min siden';
    else if (60 <= diff)
      return 'Kaffen ble laget kl '+coffeeTime[0]+':'+coffeeTime[1];
    else
      // time is negative, computer is likely in a timezone between GMT -12 and +1
      return 'God reise! Håper de har kaffe! :)';
  },

  prettyPotsString: function(pots) {
    return (pots=='0'?'Ingen kanner':pots=='1'?'1 kanne':pots+' kanner') + ' i dag';
  },

  showNotification: function(pots, age) { // Parameters 'pots' and 'age' not in use yet.
    var demo = (typeof pots == 'undefined' && typeof age == 'undefined');
    // If the computer has slept for a while and there are
    // suddenly four new coffeepots then they will all be
    // lined up for notifications, giving the user four
    // notifications at once. This is prevented here by not
    // allowing two consecutive notifications within 4 minutes
    // of each other.
    var showIt = true;
    try {
      var then = JSON.parse(localStorage.lastSubscriptionTime);
      if (this.minuteDiff(then) < 5) {
        showIt = false;
      }
    }
    catch (err) {
      if (this.debug) console.log('ERROR: failed to calculate coffee subscription time difference');
    }

    if (showIt || demo) {

      // Save timestamp if this was a real coffee notification
      if (!demo)
        localStorage.lastSubscriptionTime = JSON.stringify(new Date());

      var key = localStorage.affiliationKey1;
      var memes = [];

      // Add regular memes
      var amount = MEME_AMOUNT; // Number of memes, in regular human numbers, not zero-indexed
      for (var i = 1; i <= amount; i++) {
        memes.push('./meme/'+i+'.jpg');
      }

      // Add affiliation memes
      if (Affiliation.org[key].hw.memePath) {
        var amount = Affiliation.getMemeCount(key);
        var path = Affiliation.org[key].hw.memePath;
        for (var i = 1; i <= amount; i++) {
          memes.push(path+i+'.jpg');
        }
      }

      // Randomize image
      var random = 1 + (Math.floor(Math.random() * memes.length));
      if (this.debug) console.log('memes['+(random-1)+'] of '+0+'-'+(memes.length-1)+' is "'+memes[random-1]+'"');
      var image = memes[random - 1]; // the list is zero-indexed

      // Create the notification
      var item = {
        title: Affiliation.org[key].name + ' Notifier',
        description: this.msgNotification,
        image: image,
        link: 'options.html',
        feedKey: key,
      }
      if (!demo) {
        Browser.createNotification(item);
      }
      else {
        // Need to run it by the background process because the event listeners are there
        Browser.getBackgroundProcess().Browser.createNotification(item);
      }
    }
    else {
      if (this.debug) console.log('ERROR: coffee notification displayed less than four minutes ago');
    }
  },

}
<commit_msg>coffee: improved error handling and debugging features<commit_after>"use strict";

var Coffee = {
  debug: 0,
  debugString: 0,
  debugThisString: "200\n30. October 14:28:371",

  msgNoPots: 'Ingen kanner i dag',
  msgNoCoffee: 'Kaffen har ikke blitt satt på',
  msgFormatError: 'Feil i kaffeformat',
  msgConnectionError: 'Frakoblet fra kaffekanna',
  msgComforting: 'Så så, det er sikkert kaffe :)',
  msgNotification: 'Kaffen er satt på, straks klar :)',

  get: function(pretty, callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var api = Affiliation.org[localStorage.affiliationKey1].hw.apis.coffee;

    // Receives the status for the coffee pot
    var self = this;
    Ajaxer.getPlainText({
      url: api,
      success: function(data) {

        // If coffee debugging is enabled
        if (self.debug && self.debugString) {
          data = self.debugThisString;
        }

        try {
          // Split into pot number and age of last pot
          var pieces = data.split("\n");
          var pots = Number(pieces[0]);
          var ageString = pieces[1];

          // Coffee made today?
          if (self.isMadeToday(ageString)) {

            // Calculate minute difference from right now
            var now = new Date();
            var coffeeTime = String(ageString.match(/\d+:\d+:\d+/)).split(':');
            var then = new Date(now.getFullYear(), now.getMonth(), now.getDate(), coffeeTime[0], coffeeTime[1]);
            var age = self.minuteDiff(then);

            // If pretty strings are requested
            if (pretty) {
              age = self.prettyAgeString(age, coffeeTime);
              pots = self.prettyPotsString(pots);
            }

            // Call it back
            callback(pots, age);
          }
          else {
            // Coffee was not made today
            if (pretty) {
              callback(self.msgNoPots, self.msgNoCoffee);
            }
            else {
              callback(0, 0);
            }
          }
        } catch (err) {
          if (self.debug) console.log('ERROR: Coffee format is wrong:', err);
          callback(self.msgFormatError, self.msgComforting);
        }
      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR: Failed to get coffee pot status.');
        callback(self.msgConnectionError, self.msgComforting);
      },
    });
  },

  isMadeToday: function(ageString) {
    // Get now
    var now = new Date();
    // Figure out which date the coffee was made
    var dateObject = ageString.match(/\d+\. [a-zA-Z]+/);
    var dateString = String(dateObject);
    dateString = dateString.replace('.', ''); // Remove period
    dateString = dateString + ', ' + now.getFullYear();
    var coffeeDate = new Date(dateString);
    // Check if the coffee pots were made today
    return coffeeDate.getDate() == now.getDate();
  },

  minuteDiff: function(then) {
    // Get now
    var now = new Date();
    var one_minute = 1000 * 60;
    // Calculate difference between the two dates, and convert to minutes
    return Math.floor(( now.getTime() - then.getTime() ) / one_minute);
  },

  prettyAgeString: function(diff, coffeeTime) {
    // Create a proper time string from all the minutes
    if (0 <= diff && diff <= 9)
      return 'Kaffen ble nettopp laget';
    else if (10 <= diff && diff <= 59)
      return 'Kaffen ble laget for '+diff+' min siden';
    else if (60 <= diff)
      return 'Kaffen ble laget kl '+coffeeTime[0]+':'+coffeeTime[1];
    else
      // time is negative, computer is likely in a timezone between GMT -12 and +1
      return 'God reise! Håper de har kaffe! :)';
  },

  prettyPotsString: function(pots) {
    return (pots=='0'?'Ingen kanner':pots=='1'?'1 kanne':pots+' kanner') + ' i dag';
  },

  showNotification: function(pots, age) { // Parameters 'pots' and 'age' not in use yet.
    var demo = (typeof pots == 'undefined' && typeof age == 'undefined');
    // If the computer has slept for a while and there are
    // suddenly four new coffeepots then they will all be
    // lined up for notifications, giving the user four
    // notifications at once. This is prevented here by not
    // allowing two consecutive notifications within 4 minutes
    // of each other.
    var showIt = true;
    if (typeof localStorage.lastSubscriptionTime !== 'undefined') {
      try {
        var then = JSON.parse(localStorage.lastSubscriptionTime);
        if (this.minuteDiff(then) < 5) {
          showIt = false;
        }
      }
      catch (err) {
        if (this.debug) console.log('ERROR: failed to calculate coffee subscription time difference');
      }
    }

    if (showIt || demo) {

      // Save timestamp if this was a real coffee notification
      if (!demo)
        localStorage.lastSubscriptionTime = JSON.stringify(new Date());

      var key = localStorage.affiliationKey1;
      var memes = [];

      // Add regular memes
      var amount = MEME_AMOUNT; // Number of memes, in regular human numbers, not zero-indexed
      for (var i = 1; i <= amount; i++) {
        memes.push('./meme/'+i+'.jpg');
        // memes.push('./meme/'+i+'.png');
      }

      // Add affiliation memes
      if (Affiliation.org[key].hw.memePath) {
        var amount = Affiliation.getMemeCount(key);
        var path = Affiliation.org[key].hw.memePath;
        for (var i = 1; i <= amount; i++) {
          memes.push(path+i+'.jpg');
          // memes.push(path+i+'.png');
        }
      }

      // Randomize image
      var random = 1 + (Math.floor(Math.random() * memes.length));
      if (this.debug) console.log('memes['+(random-1)+'] of '+0+'-'+(memes.length-1)+' is "'+memes[random-1]+'"');
      var image = memes[random - 1]; // the list is zero-indexed

      // Create the notification
      var item = {
        title: Affiliation.org[key].name + ' Notifier',
        description: this.msgNotification,
        image: image,
        link: 'options.html',
        feedKey: key,
      }
      if (!demo) {
        Browser.createNotification(item);
      }
      else {
        // Need to run it by the background process because the event listeners are there
        Browser.getBackgroundProcess().Browser.createNotification(item);
      }
    }
    else {
      if (this.debug) console.log('ERROR: coffee notification displayed less than four minutes ago');
    }
  },

}
<|endoftext|>