<commit_before>import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {isPresent} from 'angular2/src/facade/lang';
import {DomAdapter, setRootDomAdapter} from './dom_adapter';

var _attrToPropMap = {
  'inner-html': 'innerHTML',
  'readonly': 'readOnly',
  'tabindex': 'tabIndex',
};

export class BrowserDomAdapter extends DomAdapter {
  static makeCurrent() {
    setRootDomAdapter(new BrowserDomAdapter());
  }

  get attrToPropMap() {
    return _attrToPropMap;
  }

  query(selector) {
    return document.querySelector(selector);
  }
  querySelector(el, selector:string):Node {
    return el.querySelector(selector);
  }
  querySelectorAll(el, selector:string):NodeList {
    return el.querySelectorAll(selector);
  }
  on(el, evt, listener) {
    el.addEventListener(evt, listener, false);
  }
  dispatchEvent(el, evt) {
    el.dispatchEvent(evt);
  }
  createMouseEvent(eventType) {
    var evt = new MouseEvent(eventType);
    evt.initEvent(eventType, true, true);
    return evt;
  }
  createEvent(eventType) {
    return new Event(eventType, true);
  }
  getInnerHTML(el) {
    return el.innerHTML;
  }
  getOuterHTML(el) {
    return el.outerHTML;
  }
  nodeName(node:Node):string {
    return node.nodeName;
  }
  nodeValue(node:Node):string {
    return node.nodeValue;
  }
  type(node:string) {
    return node.type;
  }
  content(node:HTMLTemplateElement):Node {
    return node.content;
  }
  firstChild(el):Node {
    return el.firstChild;
  }
  nextSibling(el):Node {
    return el.nextSibling;
  }
  parentElement(el) {
    return el.parentElement;
  }
  childNodes(el):NodeList {
    return el.childNodes;
  }
  childNodesAsList(el):List {
    var childNodes = el.childNodes;
    var res = ListWrapper.createFixedSize(childNodes.length);
    for (var i=0; i<childNodes.length; i++) {
      res[i] = childNodes[i];
    }
    return res;
  }
  clearNodes(el) {
    el.innerHTML = '';
  }
  appendChild(el, node) {
    el.appendChild(node);
  }
  removeChild(el, node) {
    el.removeChild(node);
  }
  remove(el) {
    var parent = el.parentNode;
    parent.removeChild(el);
    return el;
  }
  insertBefore(el, node) {
    el.parentNode.insertBefore(node, el);
  }
  insertAllBefore(el, nodes) {
    ListWrapper.forEach(nodes, (n) => {
      el.parentNode.insertBefore(n, el);
    });
  }
  insertAfter(el, node) {
    el.parentNode.insertBefore(node, el.nextSibling);
  }
  setInnerHTML(el, value) {
    el.innerHTML = value;
  }
  getText(el) {
    return el.textContent;
  }
  // TODO(vicb): removed Element type because it does not support StyleElement
  setText(el, value:string) {
    el.textContent = value;
  }
  getValue(el) {
    return el.value;
  }
  setValue(el, value:string) {
    el.value = value;
  }
  getChecked(el) {
    return el.checked;
  }
  setChecked(el, value:boolean) {
    el.checked = value;
  }
  createTemplate(html) {
    var t = document.createElement('template');
    t.innerHTML = html;
    return t;
  }
  createElement(tagName, doc=document) {
    return doc.createElement(tagName);
  }
  createTextNode(text: string, doc=document) {
    return doc.createTextNode(text);
  }
  createScriptTag(attrName:string, attrValue:string, doc=document) {
    var el = doc.createElement('SCRIPT');
    el.setAttribute(attrName, attrValue);
    return el;
  }
  createStyleElement(css:string, doc=document):HTMLStyleElement {
    var style = doc.createElement('STYLE');
    style.innerText = css;
    return style;
  }
  createShadowRoot(el:HTMLElement): ShadowRoot {
    return el.createShadowRoot();
  }
  getShadowRoot(el:HTMLElement): ShadowRoot {
    return el.shadowRoot;
  }
  clone(node:Node) {
    return node.cloneNode(true);
  }
  hasProperty(element, name:string) {
    return name in element;
  }
  getElementsByClassName(element, name:string) {
    return element.getElementsByClassName(name);
  }
  getElementsByTagName(element, name:string) {
    return element.getElementsByTagName(name);
  }
  classList(element):List {
    return Array.prototype.slice.call(element.classList, 0);
  }
  addClass(element, classname:string) {
    element.classList.add(classname);
  }
  removeClass(element, classname:string) {
    element.classList.remove(classname);
  }
  hasClass(element, classname:string) {
    return element.classList.contains(classname);
  }
  setStyle(element, stylename:string, stylevalue:string) {
    element.style[stylename] = stylevalue;
  }
  removeStyle(element, stylename:string) {
    element.style[stylename] = null;
  }
  getStyle(element, stylename:string) {
    return element.style[stylename];
  }
  tagName(element):string {
    return element.tagName;
  }
  attributeMap(element) {
    var res = MapWrapper.create();
    var elAttrs = element.attributes;
    for (var i = 0; i < elAttrs.length; i++) {
      var attrib = elAttrs[i];
      MapWrapper.set(res, attrib.name, attrib.value);
    }
    return res;
  }
  getAttribute(element, attribute:string) {
    return element.getAttribute(attribute);
  }
  setAttribute(element, name:string, value:string) {
    element.setAttribute(name, value);
  }
  removeAttribute(element, attribute:string) {
    return element.removeAttribute(attribute);
  }
  templateAwareRoot(el) {
    return el instanceof HTMLTemplateElement ? el.content : el;
  }
  createHtmlDocument() {
    return document.implementation.createHTMLDocument();
  }
  defaultDoc() {
    return document;
  }
  elementMatches(n, selector:string):boolean {
    return n instanceof HTMLElement && n.matches(selector);
  }
  isTemplateElement(el:any):boolean {
    return el instanceof HTMLTemplateElement;
  }
  isTextNode(node:Node):boolean {
    return node.nodeType === Node.TEXT_NODE;
  }
  isCommentNode(node:Node):boolean {
    return node.nodeType === Node.TEXT_NODE;
  }
  isElementNode(node:Node):boolean {
    return node.nodeType === Node.ELEMENT_NODE;
  }
  hasShadowRoot(node):boolean {
    return node instanceof HTMLElement && isPresent(node.shadowRoot);
  }
  importIntoDoc(node:Node) {
    return document.importNode(node, true);
  }
  isPageRule(rule) {
    return rule.type === CSSRule.PAGE_RULE;
  }
  isStyleRule(rule) {
    return rule.type === CSSRule.STYLE_RULE;
  }
  isMediaRule(rule) {
    return rule.type === CSSRule.MEDIA_RULE;
  }
  isKeyframesRule(rule) {
    return rule.type === CSSRule.KEYFRAMES_RULE;
  }
}
<commit_msg>fix(browser_adapter): work around WebKit bug with importing template elements<commit_after>import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {isPresent} from 'angular2/src/facade/lang';
import {DomAdapter, setRootDomAdapter} from './dom_adapter';

var _attrToPropMap = {
  'inner-html': 'innerHTML',
  'readonly': 'readOnly',
  'tabindex': 'tabIndex',
};

export class BrowserDomAdapter extends DomAdapter {
  static makeCurrent() {
    setRootDomAdapter(new BrowserDomAdapter());
  }

  get attrToPropMap() {
    return _attrToPropMap;
  }

  query(selector) {
    return document.querySelector(selector);
  }
  querySelector(el, selector:string):Node {
    return el.querySelector(selector);
  }
  querySelectorAll(el, selector:string):NodeList {
    return el.querySelectorAll(selector);
  }
  on(el, evt, listener) {
    el.addEventListener(evt, listener, false);
  }
  dispatchEvent(el, evt) {
    el.dispatchEvent(evt);
  }
  createMouseEvent(eventType) {
    var evt = new MouseEvent(eventType);
    evt.initEvent(eventType, true, true);
    return evt;
  }
  createEvent(eventType) {
    return new Event(eventType, true);
  }
  getInnerHTML(el) {
    return el.innerHTML;
  }
  getOuterHTML(el) {
    return el.outerHTML;
  }
  nodeName(node:Node):string {
    return node.nodeName;
  }
  nodeValue(node:Node):string {
    return node.nodeValue;
  }
  type(node:string) {
    return node.type;
  }
  content(node:HTMLTemplateElement):Node {
    return node.content;
  }
  firstChild(el):Node {
    return el.firstChild;
  }
  nextSibling(el):Node {
    return el.nextSibling;
  }
  parentElement(el) {
    return el.parentElement;
  }
  childNodes(el):NodeList {
    return el.childNodes;
  }
  childNodesAsList(el):List {
    var childNodes = el.childNodes;
    var res = ListWrapper.createFixedSize(childNodes.length);
    for (var i=0; i<childNodes.length; i++) {
      res[i] = childNodes[i];
    }
    return res;
  }
  clearNodes(el) {
    el.innerHTML = '';
  }
  appendChild(el, node) {
    el.appendChild(node);
  }
  removeChild(el, node) {
    el.removeChild(node);
  }
  remove(el) {
    var parent = el.parentNode;
    parent.removeChild(el);
    return el;
  }
  insertBefore(el, node) {
    el.parentNode.insertBefore(node, el);
  }
  insertAllBefore(el, nodes) {
    ListWrapper.forEach(nodes, (n) => {
      el.parentNode.insertBefore(n, el);
    });
  }
  insertAfter(el, node) {
    el.parentNode.insertBefore(node, el.nextSibling);
  }
  setInnerHTML(el, value) {
    el.innerHTML = value;
  }
  getText(el) {
    return el.textContent;
  }
  // TODO(vicb): removed Element type because it does not support StyleElement
  setText(el, value:string) {
    el.textContent = value;
  }
  getValue(el) {
    return el.value;
  }
  setValue(el, value:string) {
    el.value = value;
  }
  getChecked(el) {
    return el.checked;
  }
  setChecked(el, value:boolean) {
    el.checked = value;
  }
  createTemplate(html) {
    var t = document.createElement('template');
    t.innerHTML = html;
    return t;
  }
  createElement(tagName, doc=document) {
    return doc.createElement(tagName);
  }
  createTextNode(text: string, doc=document) {
    return doc.createTextNode(text);
  }
  createScriptTag(attrName:string, attrValue:string, doc=document) {
    var el = doc.createElement('SCRIPT');
    el.setAttribute(attrName, attrValue);
    return el;
  }
  createStyleElement(css:string, doc=document):HTMLStyleElement {
    var style = doc.createElement('STYLE');
    style.innerText = css;
    return style;
  }
  createShadowRoot(el:HTMLElement): ShadowRoot {
    return el.createShadowRoot();
  }
  getShadowRoot(el:HTMLElement): ShadowRoot {
    return el.shadowRoot;
  }
  clone(node:Node) {
    return node.cloneNode(true);
  }
  hasProperty(element, name:string) {
    return name in element;
  }
  getElementsByClassName(element, name:string) {
    return element.getElementsByClassName(name);
  }
  getElementsByTagName(element, name:string) {
    return element.getElementsByTagName(name);
  }
  classList(element):List {
    return Array.prototype.slice.call(element.classList, 0);
  }
  addClass(element, classname:string) {
    element.classList.add(classname);
  }
  removeClass(element, classname:string) {
    element.classList.remove(classname);
  }
  hasClass(element, classname:string) {
    return element.classList.contains(classname);
  }
  setStyle(element, stylename:string, stylevalue:string) {
    element.style[stylename] = stylevalue;
  }
  removeStyle(element, stylename:string) {
    element.style[stylename] = null;
  }
  getStyle(element, stylename:string) {
    return element.style[stylename];
  }
  tagName(element):string {
    return element.tagName;
  }
  attributeMap(element) {
    var res = MapWrapper.create();
    var elAttrs = element.attributes;
    for (var i = 0; i < elAttrs.length; i++) {
      var attrib = elAttrs[i];
      MapWrapper.set(res, attrib.name, attrib.value);
    }
    return res;
  }
  getAttribute(element, attribute:string) {
    return element.getAttribute(attribute);
  }
  setAttribute(element, name:string, value:string) {
    element.setAttribute(name, value);
  }
  removeAttribute(element, attribute:string) {
    return element.removeAttribute(attribute);
  }
  templateAwareRoot(el) {
    return el instanceof HTMLTemplateElement ? el.content : el;
  }
  createHtmlDocument() {
    return document.implementation.createHTMLDocument();
  }
  defaultDoc() {
    return document;
  }
  elementMatches(n, selector:string):boolean {
    return n instanceof HTMLElement && n.matches(selector);
  }
  isTemplateElement(el:any):boolean {
    return el instanceof HTMLTemplateElement;
  }
  isTextNode(node:Node):boolean {
    return node.nodeType === Node.TEXT_NODE;
  }
  isCommentNode(node:Node):boolean {
    return node.nodeType === Node.TEXT_NODE;
  }
  isElementNode(node:Node):boolean {
    return node.nodeType === Node.ELEMENT_NODE;
  }
  hasShadowRoot(node):boolean {
    return node instanceof HTMLElement && isPresent(node.shadowRoot);
  }
  importIntoDoc(node:Node) {
    var result = document.importNode(node, true);
    // Workaround WebKit https://bugs.webkit.org/show_bug.cgi?id=137619
    if (this.isTemplateElement(result) &&
        !result.content.childNodes.length && node.content.childNodes.length) {
      var childNodes = node.content.childNodes;
      for (var i = 0; i < childNodes.length; ++i) {
        result.content.appendChild(
            this.importIntoDoc(childNodes[i]));
      }
    }
    return result;
  }
  isPageRule(rule) {
    return rule.type === CSSRule.PAGE_RULE;
  }
  isStyleRule(rule) {
    return rule.type === CSSRule.STYLE_RULE;
  }
  isMediaRule(rule) {
    return rule.type === CSSRule.MEDIA_RULE;
  }
  isKeyframesRule(rule) {
    return rule.type === CSSRule.KEYFRAMES_RULE;
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var config = require('./config/config.js');
var unreadNotifications = [];
var readNotifications = [];

// find index for new notification based on its timestamp
// assumes 'array' is sorted in ascending order according to .date fields
var findId = function(date, array) {
	for (var i = 0; i < array.length; i++) {
		if(array[i].date >= date)
			return i;
	}

	return array.length;
};

// store notification in (un)readNotifications array at a date sorted index
var storeNotification = function(data_json, read) {
	data_json.text = data_json.text.replace('\t',' '); // tabs to single spaces
	data_json.text = data_json.text.replace(/^\s*/, ""); // get rid of leading ws
	data_json.text = data_json.text.replace(/\s*$/, ""); // get rid of trailing ws

	// plugin did not provide timestamp, create one from current time
	if(!data_json.date)
		data_json.date = new Date().valueOf();

	var primaryArray = unreadNotifications; var secondaryArray = readNotifications;
	if(read) {
		primaryArray = readNotifications; secondaryArray = unreadNotifications;
	}

	// look for notification with duplicate UID in both arrays. if found, remove
	if(data_json.uid) {
		var i;
		for(i = primaryArray.length - 1; i >= 0; i--) {
			if(primaryArray[i].uid === data_json.uid) {
				primaryArray.splice(i, 1);
			}
		}
		// look in unread array too, if duplicate UID found there, remove it
		for(i = secondaryArray.length - 1; i >= 0; i--) {
			if(secondaryArray[i].uid === data_json.uid) {
				secondaryArray.splice(i, 1);
			}
		}
	}

	if (read) {
		// insert notification at end of readNotifications array
		readNotifications.push(data_json);

		// if readNotifications is full, pop from beginning
		if(readNotifications.length >= config.numReadToKeep) {
			readNotifications.splice(0, 1);
		}

		return readNotifications.length - 1;
	} else {
		// insert notification to unreadNotifications array
		var id = findId(data_json.date, unreadNotifications);
		unreadNotifications.splice(id, 0, data_json);

		return id;
	}
};

// return notifications with matching id, uid, source, context
// if id is given only it will be used. if id is undefined search using other
// fields. undefined fields not included in search.
var range_re = /(\d*)\.\.(\d*)/;
var searchNotifications = function(id, uid, source, context, read) {
	var array = read ? readNotifications : unreadNotifications;

	if(id) {
		var range = id.match(range_re);
		if(range) {
			var min = range[1] || 0;
			var max = range[2] || 9999999999999;
			min = parseInt(min);
			max = parseInt(max);

			if (min > max) {
				var temp = min;
				min = max;
				max = temp;
			}
			return array.filter(function (notification, i) {
				return (i >= min && i <= max);
			});
		} else {
			return array.filter(function (notification, i) {
				return i == id;
			});
		}
	} else {
		return array.filter(function (notification) {
			return (!uid || (notification.uid == uid)) && (!source || (notification.source.toUpperCase() == source.toUpperCase())) && (!context || (notification.context.toUpperCase() == context.toUpperCase()));
		});
	}
};

// update .(un)readID and .read properties of all notifications
var updateIDRead = function() {
	var i;
	for (i = 0; i < unreadNotifications.length; i++) {
		unreadNotifications[i].unreadID = i;
		unreadNotifications[i].read = false;
	}
	for (i = 0; i < readNotifications.length; i++) {
		readNotifications[i].readID = i;
		readNotifications[i].read = true;
	}
};

// mark notifications as (un)read
// move notifications between (un)readNotifications arrays accordingly
// NOTE: all given notifications must already be in (un)readNotifications
var markAs = function(notifications, read) {
	var msg = "";

	// check if arg is object, then make it into an array
	if(Object.prototype.toString.call(notifications) === '[object Object]') {
		notifications = [notifications];
	}

	var i;
	var notification;

	// update read boolean field of every given notification
	for (i = 0; i < notifications.length; i++) {
		notifications[i].read = read;
	}

	// loop through (un)readNotifications. see if we can find any that were
	// marked as (un)read and thus belong to the other array, move these
	if(read) {
		for(i = unreadNotifications.length - 1; i >= 0; i--) {
			if(unreadNotifications[i].read) {
				notification = unreadNotifications[i];
				unreadNotifications.splice(i, 1);
				storeNotification(notification, true);
			}
		}
	} else {
		for(i = readNotifications.length - 1; i >= 0; i--) {
			if(!readNotifications[i].read) {
				notification = readNotifications[i];
				readNotifications.splice(i, 1);
				storeNotification(notification, false);
			}
		}
	}
};

// networking
var tls = require('tls')
var fs = require('fs');
var options = {
	key: fs.readFileSync('config/nodifier-key.pem'),
	cert: fs.readFileSync('config/nodifier-cert.pem'),
	ca: fs.readFileSync('config/nodifier-cert.pem'),
	requestCert: true,
	rejectUnauthorized: true
};
var sockets = [];
var server = tls.createServer(options, function(socket) {
	sockets.push(socket);
	var notifications;

	var recvBuffer = "";
	var incomingLength = 0;

	socket.on('data', function(data) {
		if(!incomingLength) {
			// first message is always the length
			incomingLength = data.toString();
		} else {
			recvBuffer += data.toString();

			// recv'd entire message
			if(recvBuffer.length >= incomingLength) {
				data = JSON.parse(recvBuffer);
				recvBuffer = "";
				incomingLength = 0;
				if(data[0] !== 'data')
					socket.emit(data[0], data[1]);
			}
		}
	});
	socket.send = function(evt, data) {
		data = JSON.stringify([evt, data]);
		socket.write(data.length.toString());
		socket.write(data);
	};
	socket.broadcast = function(evt, data, ignoreSelf) {
		for(var i = 0; i < sockets.length; i++) {
			if(!ignoreSelf || sockets[i] !== socket) {
				sockets[i].send(evt, data);
			}
		}
	};
	socket.on('end', function() {
		if(sockets.indexOf(socket) !== -1)
			sockets.splice(sockets.indexOf(socket), 1);
	});

	socket.on('newNotification', function(notification) {
		// add new notification
		var id = storeNotification(notification, false);
		updateIDRead(); // indices may have changed, fix them

		// broadcast new notification to all connected clients
		socket.broadcast('newNotification', unreadNotifications[id]);
	});
	socket.on('markAs', function(search) {
		// search for notifications and mark results as (un)read according to s.read
		notifications = searchNotifications(search.id, search.uid, search.source, search.context, !search.read);
		if(notifications)
			markAs(notifications, search.read);
		updateIDRead(); // indices/read states may have changed, fix them

		socket.send('notifications', notifications);

		// broadcast updated notifications to all other connected clients
		socket.broadcast('markAs', notifications, true);
	});
	socket.on('getRead', function() {
		socket.send('notifications', readNotifications);
	});
	socket.on('getUnread', function(search) {
		// get unread notifications by id, or all notifications if no search terms
		if(!search) {
			notifications = unreadNotifications;
		} else {
			notifications = searchNotifications(search.id, search.uid, search.source, search.context, false);
		}

		socket.send('notifications', notifications);
	});
});

server.listen(config.port);
console.log('nodifier tls server listening on port ' + config.port);

process.on('uncaughtException', function (err) {
	console.error(err.stack);
	console.log("ERROR! Node not exiting.");
});
<commit_msg>generate UIDs for new notifications that lack UIDs<commit_after>#!/usr/bin/env node

var config = require('./config/config.js');
var unreadNotifications = [];
var readNotifications = [];

// find index for new notification based on its timestamp
// assumes 'array' is sorted in ascending order according to .date fields
var findId = function(date, array) {
	for (var i = 0; i < array.length; i++) {
		if(array[i].date >= date)
			return i;
	}

	return array.length;
};

// store notification in (un)readNotifications array at a date sorted index
var storeNotification = function(data_json, read) {
	data_json.text = data_json.text.replace('\t',' '); // tabs to single spaces
	data_json.text = data_json.text.replace(/^\s*/, ""); // get rid of leading ws
	data_json.text = data_json.text.replace(/\s*$/, ""); // get rid of trailing ws

	// plugin did not provide timestamp, create one from current time
	if(!data_json.date)
		data_json.date = new Date().valueOf();

	var primaryArray = unreadNotifications; var secondaryArray = readNotifications;
	if(read) {
		primaryArray = readNotifications; secondaryArray = unreadNotifications;
	}

	if(data_json.uid) {
		// notification has a set UID:
		// look for notification with duplicate UID in both arrays. if found, remove
		var i;
		for(i = primaryArray.length - 1; i >= 0; i--) {
			if(primaryArray[i].uid === data_json.uid) {
				primaryArray.splice(i, 1);
			}
		}
		// look in unread array too, if duplicate UID found there, remove it
		for(i = secondaryArray.length - 1; i >= 0; i--) {
			if(secondaryArray[i].uid === data_json.uid) {
				secondaryArray.splice(i, 1);
			}
		}
	} else {
		// notification lacks UID: generate a pseudo random hash and set it as the UID
		var shasum = crypto.createHash('md5');
		shasum.update(String(Math.random()));
		data_json.uid = shasum.digest('base64');
	}

	if (read) {
		// insert notification at end of readNotifications array
		readNotifications.push(data_json);

		// if readNotifications is full, pop from beginning
		if(readNotifications.length >= config.numReadToKeep) {
			readNotifications.splice(0, 1);
		}

		return readNotifications.length - 1;
	} else {
		// insert notification to unreadNotifications array
		var id = findId(data_json.date, unreadNotifications);
		unreadNotifications.splice(id, 0, data_json);

		return id;
	}
};

// return notifications with matching id, uid, source, context
// if id is given only it will be used. if id is undefined search using other
// fields. undefined fields not included in search.
var range_re = /(\d*)\.\.(\d*)/;
var searchNotifications = function(id, uid, source, context, read) {
	var array = read ? readNotifications : unreadNotifications;

	if(id) {
		var range = id.match(range_re);
		if(range) {
			var min = range[1] || 0;
			var max = range[2] || 9999999999999;
			min = parseInt(min);
			max = parseInt(max);

			if (min > max) {
				var temp = min;
				min = max;
				max = temp;
			}
			return array.filter(function (notification, i) {
				return (i >= min && i <= max);
			});
		} else {
			return array.filter(function (notification, i) {
				return i == id;
			});
		}
	} else {
		return array.filter(function (notification) {
			return (!uid || (notification.uid == uid)) && (!source || (notification.source.toUpperCase() == source.toUpperCase())) && (!context || (notification.context.toUpperCase() == context.toUpperCase()));
		});
	}
};

// update .(un)readID and .read properties of all notifications
var updateIDRead = function() {
	var i;
	for (i = 0; i < unreadNotifications.length; i++) {
		unreadNotifications[i].unreadID = i;
		unreadNotifications[i].read = false;
	}
	for (i = 0; i < readNotifications.length; i++) {
		readNotifications[i].readID = i;
		readNotifications[i].read = true;
	}
};

// mark notifications as (un)read
// move notifications between (un)readNotifications arrays accordingly
// NOTE: all given notifications must already be in (un)readNotifications
var markAs = function(notifications, read) {
	var msg = "";

	// check if arg is object, then make it into an array
	if(Object.prototype.toString.call(notifications) === '[object Object]') {
		notifications = [notifications];
	}

	var i;
	var notification;

	// update read boolean field of every given notification
	for (i = 0; i < notifications.length; i++) {
		notifications[i].read = read;
	}

	// loop through (un)readNotifications. see if we can find any that were
	// marked as (un)read and thus belong to the other array, move these
	if(read) {
		for(i = unreadNotifications.length - 1; i >= 0; i--) {
			if(unreadNotifications[i].read) {
				notification = unreadNotifications[i];
				unreadNotifications.splice(i, 1);
				storeNotification(notification, true);
			}
		}
	} else {
		for(i = readNotifications.length - 1; i >= 0; i--) {
			if(!readNotifications[i].read) {
				notification = readNotifications[i];
				readNotifications.splice(i, 1);
				storeNotification(notification, false);
			}
		}
	}
};

// networking
var tls = require('tls')
var fs = require('fs');
var options = {
	key: fs.readFileSync('config/nodifier-key.pem'),
	cert: fs.readFileSync('config/nodifier-cert.pem'),
	ca: fs.readFileSync('config/nodifier-cert.pem'),
	requestCert: true,
	rejectUnauthorized: true
};
var sockets = [];
var server = tls.createServer(options, function(socket) {
	sockets.push(socket);
	var notifications;

	var recvBuffer = "";
	var incomingLength = 0;

	socket.on('data', function(data) {
		if(!incomingLength) {
			// first message is always the length
			incomingLength = data.toString();
		} else {
			recvBuffer += data.toString();

			// recv'd entire message
			if(recvBuffer.length >= incomingLength) {
				data = JSON.parse(recvBuffer);
				recvBuffer = "";
				incomingLength = 0;
				if(data[0] !== 'data')
					socket.emit(data[0], data[1]);
			}
		}
	});
	socket.send = function(evt, data) {
		data = JSON.stringify([evt, data]);
		socket.write(data.length.toString());
		socket.write(data);
	};
	socket.broadcast = function(evt, data, ignoreSelf) {
		for(var i = 0; i < sockets.length; i++) {
			if(!ignoreSelf || sockets[i] !== socket) {
				sockets[i].send(evt, data);
			}
		}
	};
	socket.on('end', function() {
		if(sockets.indexOf(socket) !== -1)
			sockets.splice(sockets.indexOf(socket), 1);
	});

	socket.on('newNotification', function(notification) {
		// add new notification
		var id = storeNotification(notification, false);
		updateIDRead(); // indices may have changed, fix them

		// broadcast new notification to all connected clients
		socket.broadcast('newNotification', unreadNotifications[id]);
	});
	socket.on('markAs', function(search) {
		// search for notifications and mark results as (un)read according to s.read
		notifications = searchNotifications(search.id, search.uid, search.source, search.context, !search.read);
		if(notifications)
			markAs(notifications, search.read);
		updateIDRead(); // indices/read states may have changed, fix them

		socket.send('notifications', notifications);

		// broadcast updated notifications to all other connected clients
		socket.broadcast('markAs', notifications, true);
	});
	socket.on('getRead', function() {
		socket.send('notifications', readNotifications);
	});
	socket.on('getUnread', function(search) {
		// get unread notifications by id, or all notifications if no search terms
		if(!search) {
			notifications = unreadNotifications;
		} else {
			notifications = searchNotifications(search.id, search.uid, search.source, search.context, false);
		}

		socket.send('notifications', notifications);
	});
});

server.listen(config.port);
console.log('nodifier tls server listening on port ' + config.port);

process.on('uncaughtException', function (err) {
	console.error(err.stack);
	console.log("ERROR! Node not exiting.");
});
<|endoftext|>


<commit_before>const ActionParser = require("./ActionParser")
const ModuleManager = require("./ModuleManager.js")

const Discord = require("discord.js")
const lowdb = require("lowdb")
const path = require("path")
const fs = require("fs")

const crypto = require("crypto")
const request = require("request")

class Bot {
    constructor(config) {
        const defaultSettings = {
            "modulePaths": ["node_modules"],
            "intervalInSeconds": 300,
            "loadModules": true,
            "useBuiltinActions": true,
            "administrators": [],
            "botAdminRightsAlsoApplyInServers": true,
            "databaseLocation": "data/"
        }

        this.client = new Discord.Client()

        this.config = Object.assign({}, defaultSettings, config)
        this.dbs = {}

        if(this.config.loadModules) {
            this.moduleManager = new ModuleManager(this.config.modulePaths)
        } else {
            console.warn("WARN: You've disabled the ability to load modules.")
        }

        this.actions = new ActionParser(this)
        this.intervalActions = {}
        this.messageObservers = []

        this._isReady = false
        this._isSetup = false
        this.readyCallbacks = []
        this.setupCallbacks = []

        this.client.on("ready", this.onReady.bind(this))
        this.client.on("message", this.onMessage.bind(this))

        this.client.login(this.config.discordToken)
    }

    get id() {
        return this.client.user.id
    }

    get name() {
        return this.client.user.username
    }

    get discriminator() {
        return this.client.user.discriminator
    }

    get isReady() {
        return this._isReady
    }

    get servers() {
        return this.client.guilds.array()
    }

    user(id) {
        return this.client.users.get(id)
    }

    db(handle) {
        if(!handle) {
            console.error("handle was null?", handle)
            return null
        }

        // to be able to handle just responses, makes it easier to support DMs with the bot
        if(handle.constructor.name === "Response") {
            if(handle.server) {
                return this.db(handle.server)
            }

            if(handle.channel.type === "dm") {
                return this.db(handle.author)
            }
        } else if(handle.constructor.name === "Guild") {
            return this.rawdb(`S${handle.id}`)
        } else if(handle.constructor.name === "User" || handle.constructor.name === "ClientUser") {
            return this.rawdb(`U${handle.id}`)
        }

        if(!handle.id) {
            console.error("Unknown handle, can't create db from it: ", handle)
            return null
        }

        return this.rawdb(handle.id)
    }

    rawdb(dbName) {
        if(!this.dbs[dbName]) {
            this.dbs[dbName] = lowdb(path.resolve(process.cwd(), this.config.databaseLocation, `${dbName}.json`), {
                writeOnChange: true,
                storage: require("lowdb/lib/file-async")
            })
        }

        return this.dbs[dbName]
    }

    get mydb() {
        return this.db(this.client.user)
    }

    forEveryDatabase(condition, callback) {
        fs.readdir(path.resolve(process.cwd(), this.config.databaseLocation), (err, items) => {
            if(err) {
                console.error(err)
                throw err;
            }

            function getDatabaseOwners(prefix, getOwner) {
                let databases = items.filter(i => i.startsWith(prefix))

                let owners = []

                for(let db of databases) {
                    let regex = new RegExp(`${prefix}(.*)\.json`, "g")

                    let matches = regex.exec(db)

                    if(matches.length > 1) {
                        const id = matches[1]
                        owners.push(getOwner(id))
                    }
                }

                return owners
            }

            let servers = getDatabaseOwners("S", id => this.client.guilds.get(id))
            let users = getDatabaseOwners("U", id => this.client.users.get(id))

            let owners = servers.concat(users)

            let filtered = owners.map(o => ({owner: o, db: this.db(o)})).filter(pair => condition(pair.owner, pair.db))

            filtered.forEach(pair => callback(pair.owner, pair.db))
        })
    }

    hash(str) {
        const sha = crypto.createHash("sha256")
        sha.update(str)
        return sha.digest("hex")
    }

    request(args) {
        return request.apply(null, arguments)
    }

    onReady() {
        console.log(`${this.name}#${this.discriminator} is ready.`)

        if(!this._isSetup) {
            this._isReady = true

            if(this.config.useBuiltinActions) {
                this.registerBuiltinActions()
            }

            if(this.config.loadModules) {
                this.moduleManager.register(this)
            }

            for(let setupCallback of this.setupCallbacks) {
                setupCallback()
            }

            this.intervalId = setInterval(this.onInterval.bind(this), this.config.intervalInSeconds * 1000)
            this.onInterval()

            this._isSetup = true
        }

        // ready callbacks should be called on every ready event from discord, which is why this
        // is not part of the if statement above
        for(let readyCallback of this.readyCallbacks) {
            readyCallback()
        }
    }

    onInterval() {
        for(let ident of Object.keys(this.intervalActions)) {
            try {
                this.intervalActions[ident]()
            } catch(ex) {
                console.error(`ERR: Interval "${ident}" failed`, ex)
            }
        }
    }

    interval(ident, func) {
        console.log("\tregistered interval action " + ident)
        this.intervalActions[ident] = func
    }

    clearInterval(ident) {
        console.log("\t- unregistered interval action " + ident)
        if(this.intervalActions[ident])
            delete this.intervalActions[ident]
    }

    message(func) {
        console.log("\tregistered message observer")
        this.messageObservers.push(func)
    }

    ready(callback) {
        this.readyCallbacks.push(callback)
    }

    setup(callback) {
        this.setupCallbacks.push(callback)
    }

    onMessage(message) {
        let isBotString = message.author.bot ? "[BOT] " : ""
        console.log(`${isBotString}${message.author.username}#${message.author.discriminator} (${message.author.id}): ${message.content}`)

        // don't listen to yourself
        if(message.author.id !== this.client.user.id && !message.author.bot) {
            this.actions.update(message)

            try {
                this.messageObservers.every(obs => obs(message))
            } catch(ex) {
                console.error(`ERR: Message listener for message "${message.content}" failed`, ex)
            }
        }
    }

    sendMessageToChannel(channel, message) {
        let promise = channel.send(message)

        promise.then(message => {
            // TODO: only show this when debugging
            console.log("Sent message: ", message.cleanContent)
        }).catch(err => {
            console.error(err)
        })

        return promise
    }

    isAdministrator(user) {
        return this.config.administrators.indexOf(user.id) > -1
    }

    isServerAdministrator(server, user) {
        if(this.config.botAdminRightsAlsoApplyInServers && this.isAdministrator(user)) {
            return true
        }

        return this.hasPermission(server, user, "ADMINISTRATOR")
    }

    hasPermission(server, user, permissions) {
        if(!server || !user) {
            return false
        }

        if(!Array.isArray(permissions)) {
            permissions = [permissions]
        }

        let serverUser = server.members.get(user.id)
        return serverUser.hasPermissions(permissions)
    }

    canI(server, permissions) {
        return this.hasPermission(server, this.client.user, permissions)
    }

    command(commandName, callback) {
        if(!this.isReady) {
            console.warn("WARN: Discord is not yet ready, please wait a moment before you register any commands")
            return
        }

        console.log(`\tregistered command action: "${commandName}"`)

        this.actions.register(new RegExp(`^!${commandName}\s?(.*)`), (res) => {
            let args = res.matches[1].trim().split(" ")

            // special case for when there are no args, cuz split leaves an empty string
            if(args.length == 1 && args[0].length == 0) {
                args = []
            }

            callback(res, args)
        })
    }

    hear(regex, callback) {
        if(!this.isReady) {
            console.warn("WARN: Discord is not yet ready, please wait a moment before you register any commands")
            return
        }

        console.log(`\tregistered hear action: "${regex.source}"`)

        this.actions.register(regex, (res) => {
            callback(res)
        })
    }

    respond(regex, callback) {
        if(!this.isReady) {
            console.warn("WARN: Discord is not yet ready, please wait a moment before you register any commands")
            return
        }

        console.log(`\tregistered respond action: "${regex.source}"`)

        let prefix = new RegExp(`<@${this.client.user.id}> `)
        this.actions.register(new RegExp(prefix.source + regex.source), (res) => {
            callback(res)
        })
    }

    registerBuiltinActions() {
        console.log("Register builtin actions:")

        this.respond(/ping/g, res => {
            res.reply("PONG")
        })

        this.respond(/(who am i|whoami)/g, res => {
            let author = res.message.author

            res.reply(`\nID: ${author.id}\n` +
                `Username: ${author.username}#${author.discriminator}\n` +
                `Created at: ${author.createdAt.toISOString().slice(0, 10)}\n` +
                `Avatar:\n${author.avatarURL}`
            )
        })

        this.respond(/am i admin/g, res => {
            if(res.authorIsAdministrator && res.authorIsServerAdministrator) {
                res.reply("Yes, master.")
            } else if(res.authorIsAdministrator && !res.authorIsServerAdministrator) {
                res.reply("Yes you are my master, but I can't help you on this server :'(")
            } else if(res.authorIsServerAdministrator) {
                res.reply("Yes you have Administrator rights on this server.")
            } else {
                res.reply("No.")
            }
        })

        this.command("botlink", (res, args) => {
            if(res.authorIsAdministrator) {
                res.sendDirectMessage(`You can add me to servers by using this URL:\n\n` +
                    `https://discordapp.com/api/oauth2/authorize?client_id=${this.client.user.id}&scope=bot&permissions=0`)
                res.send(":ok_hand:")
            } else {
                res.send(":thumbsdown:")
            }
        })

        this.command("setusername", (res, args) => {
            if(res.authorIsAdministrator) {
                let newUsername = args.join(" ")
                this.client.user.setUsername(newUsername)
                res.reply("I've changed my username to " + newUsername)
            } else {
                res.reply("You don't have permission to do this.")
            }
        })

        this.command("setavatar", (res, args) => {
            if(res.authorIsAdministrator) {
                const request = require("request").defaults({ encoding: null })

                let url = args[0]

                request.get(url, (err, response, body) => {
                    if(!err && response.statusCode == 200) {
                        const data = "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString("base64")
                        this.client.user.setAvatar(data)
                        res.reply("I've changed my avatar to <" + url + ">, does it suit me? :)")
                    } else {
                        res.reply("I was not able to change my avatar to <" + url + "> :(")
                    }
                })
            } else {
                res.reply("You don't have permission to do this, scrub.")
            }
        })

        this.command("hcf", (res, args) => {
            if(res.authorIsAdministrator) {
                res.reply("Aye, sir! I will proceed to kill myself.").then(_ => process.exit(0))
            } else {
                res.reply("You don't have the permission to initiate the self destruct protocol v2.1...")
            }
        })

        this.command("prune", (res, args) => {
            if(res.authorIsServerAdministrator) {
                if(args.length > 0) {
                    if(!isNaN(args[0])) {
                        if(res.canI("MANAGE_MESSAGES")) {
                            res.message.channel.bulkDelete(args[0]).then(messages => {
                                res.send(`Deleted ${messages.array().length} messages and added this one! :wastebasket:`).then(message => {
                                    message.delete(5000) // delete this message after 5s
                                })
                            }).catch(err => {
                                console.error(err.response.body.message)
                                res.send(err.response.body.message)
                            })
                        } else {
                            res.send("I'd love to clean up your mess, but I don't have the permission to manage messages... :angry:")
                        }
                    } else {
                        res.send("What is this \"" + args[0] + "\", doesn't look like a number for me :(")
                    }
                } else {
                    res.reply("I appreciate the idea of me cleaning this room but you could at least tell me how much you want me to delete :P")
                }
            } else {
                res.reply("You don't have the permission to do this mate...")
            }
        })
    }
}

module.exports = Bot
<commit_msg>dont log sent messages anymore<commit_after>const ActionParser = require("./ActionParser")
const ModuleManager = require("./ModuleManager.js")

const Discord = require("discord.js")
const lowdb = require("lowdb")
const path = require("path")
const fs = require("fs")

const crypto = require("crypto")
const request = require("request")

class Bot {
    constructor(config) {
        const defaultSettings = {
            "modulePaths": ["node_modules"],
            "intervalInSeconds": 300,
            "loadModules": true,
            "useBuiltinActions": true,
            "administrators": [],
            "botAdminRightsAlsoApplyInServers": true,
            "databaseLocation": "data/"
        }

        this.client = new Discord.Client()

        this.config = Object.assign({}, defaultSettings, config)
        this.dbs = {}

        if(this.config.loadModules) {
            this.moduleManager = new ModuleManager(this.config.modulePaths)
        } else {
            console.warn("WARN: You've disabled the ability to load modules.")
        }

        this.actions = new ActionParser(this)
        this.intervalActions = {}
        this.messageObservers = []

        this._isReady = false
        this._isSetup = false
        this.readyCallbacks = []
        this.setupCallbacks = []

        this.client.on("ready", this.onReady.bind(this))
        this.client.on("message", this.onMessage.bind(this))

        this.client.login(this.config.discordToken)
    }

    get id() {
        return this.client.user.id
    }

    get name() {
        return this.client.user.username
    }

    get discriminator() {
        return this.client.user.discriminator
    }

    get isReady() {
        return this._isReady
    }

    get servers() {
        return this.client.guilds.array()
    }

    user(id) {
        return this.client.users.get(id)
    }

    db(handle) {
        if(!handle) {
            console.error("handle was null?", handle)
            return null
        }

        // to be able to handle just responses, makes it easier to support DMs with the bot
        if(handle.constructor.name === "Response") {
            if(handle.server) {
                return this.db(handle.server)
            }

            if(handle.channel.type === "dm") {
                return this.db(handle.author)
            }
        } else if(handle.constructor.name === "Guild") {
            return this.rawdb(`S${handle.id}`)
        } else if(handle.constructor.name === "User" || handle.constructor.name === "ClientUser") {
            return this.rawdb(`U${handle.id}`)
        }

        if(!handle.id) {
            console.error("Unknown handle, can't create db from it: ", handle)
            return null
        }

        return this.rawdb(handle.id)
    }

    rawdb(dbName) {
        if(!this.dbs[dbName]) {
            this.dbs[dbName] = lowdb(path.resolve(process.cwd(), this.config.databaseLocation, `${dbName}.json`), {
                writeOnChange: true,
                storage: require("lowdb/lib/file-async")
            })
        }

        return this.dbs[dbName]
    }

    get mydb() {
        return this.db(this.client.user)
    }

    forEveryDatabase(condition, callback) {
        fs.readdir(path.resolve(process.cwd(), this.config.databaseLocation), (err, items) => {
            if(err) {
                console.error(err)
                throw err;
            }

            function getDatabaseOwners(prefix, getOwner) {
                let databases = items.filter(i => i.startsWith(prefix))

                let owners = []

                for(let db of databases) {
                    let regex = new RegExp(`${prefix}(.*)\.json`, "g")

                    let matches = regex.exec(db)

                    if(matches.length > 1) {
                        const id = matches[1]
                        owners.push(getOwner(id))
                    }
                }

                return owners
            }

            let servers = getDatabaseOwners("S", id => this.client.guilds.get(id))
            let users = getDatabaseOwners("U", id => this.client.users.get(id))

            let owners = servers.concat(users)

            let filtered = owners.map(o => ({owner: o, db: this.db(o)})).filter(pair => condition(pair.owner, pair.db))

            filtered.forEach(pair => callback(pair.owner, pair.db))
        })
    }

    hash(str) {
        const sha = crypto.createHash("sha256")
        sha.update(str)
        return sha.digest("hex")
    }

    request(args) {
        return request.apply(null, arguments)
    }

    onReady() {
        console.log(`${this.name}#${this.discriminator} is ready.`)

        if(!this._isSetup) {
            this._isReady = true

            if(this.config.useBuiltinActions) {
                this.registerBuiltinActions()
            }

            if(this.config.loadModules) {
                this.moduleManager.register(this)
            }

            for(let setupCallback of this.setupCallbacks) {
                setupCallback()
            }

            this.intervalId = setInterval(this.onInterval.bind(this), this.config.intervalInSeconds * 1000)
            this.onInterval()

            this._isSetup = true
        }

        // ready callbacks should be called on every ready event from discord, which is why this
        // is not part of the if statement above
        for(let readyCallback of this.readyCallbacks) {
            readyCallback()
        }
    }

    onInterval() {
        for(let ident of Object.keys(this.intervalActions)) {
            try {
                this.intervalActions[ident]()
            } catch(ex) {
                console.error(`ERR: Interval "${ident}" failed`, ex)
            }
        }
    }

    interval(ident, func) {
        console.log("\tregistered interval action " + ident)
        this.intervalActions[ident] = func
    }

    clearInterval(ident) {
        console.log("\t- unregistered interval action " + ident)
        if(this.intervalActions[ident])
            delete this.intervalActions[ident]
    }

    message(func) {
        console.log("\tregistered message observer")
        this.messageObservers.push(func)
    }

    ready(callback) {
        this.readyCallbacks.push(callback)
    }

    setup(callback) {
        this.setupCallbacks.push(callback)
    }

    onMessage(message) {
        let isBotString = message.author.bot ? "[BOT] " : ""
        console.log(`${isBotString}${message.author.username}#${message.author.discriminator} (${message.author.id}): ${message.content}`)

        // don't listen to yourself
        if(message.author.id !== this.client.user.id && !message.author.bot) {
            this.actions.update(message)

            try {
                this.messageObservers.every(obs => obs(message))
            } catch(ex) {
                console.error(`ERR: Message listener for message "${message.content}" failed`, ex)
            }
        }
    }

    sendMessageToChannel(channel, message) {
        let promise = channel.send(message)

        promise.then(message => {
            // TODO: only show this when debugging
            // console.log("Sent message: ", message.cleanContent)
        }).catch(err => {
            console.error(err)
        })

        return promise
    }

    isAdministrator(user) {
        return this.config.administrators.indexOf(user.id) > -1
    }

    isServerAdministrator(server, user) {
        if(this.config.botAdminRightsAlsoApplyInServers && this.isAdministrator(user)) {
            return true
        }

        return this.hasPermission(server, user, "ADMINISTRATOR")
    }

    hasPermission(server, user, permissions) {
        if(!server || !user) {
            return false
        }

        if(!Array.isArray(permissions)) {
            permissions = [permissions]
        }

        let serverUser = server.members.get(user.id)
        return serverUser.hasPermissions(permissions)
    }

    canI(server, permissions) {
        return this.hasPermission(server, this.client.user, permissions)
    }

    command(commandName, callback) {
        if(!this.isReady) {
            console.warn("WARN: Discord is not yet ready, please wait a moment before you register any commands")
            return
        }

        console.log(`\tregistered command action: "${commandName}"`)

        this.actions.register(new RegExp(`^!${commandName}\s?(.*)`), (res) => {
            let args = res.matches[1].trim().split(" ")

            // special case for when there are no args, cuz split leaves an empty string
            if(args.length == 1 && args[0].length == 0) {
                args = []
            }

            callback(res, args)
        })
    }

    hear(regex, callback) {
        if(!this.isReady) {
            console.warn("WARN: Discord is not yet ready, please wait a moment before you register any commands")
            return
        }

        console.log(`\tregistered hear action: "${regex.source}"`)

        this.actions.register(regex, (res) => {
            callback(res)
        })
    }

    respond(regex, callback) {
        if(!this.isReady) {
            console.warn("WARN: Discord is not yet ready, please wait a moment before you register any commands")
            return
        }

        console.log(`\tregistered respond action: "${regex.source}"`)

        let prefix = new RegExp(`<@${this.client.user.id}> `)
        this.actions.register(new RegExp(prefix.source + regex.source), (res) => {
            callback(res)
        })
    }

    registerBuiltinActions() {
        console.log("Register builtin actions:")

        this.respond(/ping/g, res => {
            res.reply("PONG")
        })

        this.respond(/(who am i|whoami)/g, res => {
            let author = res.message.author

            res.reply(`\nID: ${author.id}\n` +
                `Username: ${author.username}#${author.discriminator}\n` +
                `Created at: ${author.createdAt.toISOString().slice(0, 10)}\n` +
                `Avatar:\n${author.avatarURL}`
            )
        })

        this.respond(/am i admin/g, res => {
            if(res.authorIsAdministrator && res.authorIsServerAdministrator) {
                res.reply("Yes, master.")
            } else if(res.authorIsAdministrator && !res.authorIsServerAdministrator) {
                res.reply("Yes you are my master, but I can't help you on this server :'(")
            } else if(res.authorIsServerAdministrator) {
                res.reply("Yes you have Administrator rights on this server.")
            } else {
                res.reply("No.")
            }
        })

        this.command("botlink", (res, args) => {
            if(res.authorIsAdministrator) {
                res.sendDirectMessage(`You can add me to servers by using this URL:\n\n` +
                    `https://discordapp.com/api/oauth2/authorize?client_id=${this.client.user.id}&scope=bot&permissions=0`)
                res.send(":ok_hand:")
            } else {
                res.send(":thumbsdown:")
            }
        })

        this.command("setusername", (res, args) => {
            if(res.authorIsAdministrator) {
                let newUsername = args.join(" ")
                this.client.user.setUsername(newUsername)
                res.reply("I've changed my username to " + newUsername)
            } else {
                res.reply("You don't have permission to do this.")
            }
        })

        this.command("setavatar", (res, args) => {
            if(res.authorIsAdministrator) {
                const request = require("request").defaults({ encoding: null })

                let url = args[0]

                request.get(url, (err, response, body) => {
                    if(!err && response.statusCode == 200) {
                        const data = "data:" + response.headers["content-type"] + ";base64," + new Buffer(body).toString("base64")
                        this.client.user.setAvatar(data)
                        res.reply("I've changed my avatar to <" + url + ">, does it suit me? :)")
                    } else {
                        res.reply("I was not able to change my avatar to <" + url + "> :(")
                    }
                })
            } else {
                res.reply("You don't have permission to do this, scrub.")
            }
        })

        this.command("hcf", (res, args) => {
            if(res.authorIsAdministrator) {
                res.reply("Aye, sir! I will proceed to kill myself.").then(_ => process.exit(0))
            } else {
                res.reply("You don't have the permission to initiate the self destruct protocol v2.1...")
            }
        })

        this.command("prune", (res, args) => {
            if(res.authorIsServerAdministrator) {
                if(args.length > 0) {
                    if(!isNaN(args[0])) {
                        if(res.canI("MANAGE_MESSAGES")) {
                            res.message.channel.bulkDelete(args[0]).then(messages => {
                                res.send(`Deleted ${messages.array().length} messages and added this one! :wastebasket:`).then(message => {
                                    message.delete(5000) // delete this message after 5s
                                })
                            }).catch(err => {
                                console.error(err.response.body.message)
                                res.send(err.response.body.message)
                            })
                        } else {
                            res.send("I'd love to clean up your mess, but I don't have the permission to manage messages... :angry:")
                        }
                    } else {
                        res.send("What is this \"" + args[0] + "\", doesn't look like a number for me :(")
                    }
                } else {
                    res.reply("I appreciate the idea of me cleaning this room but you could at least tell me how much you want me to delete :P")
                }
            } else {
                res.reply("You don't have the permission to do this mate...")
            }
        })
    }
}

module.exports = Bot
<|endoftext|>


<commit_before>/**
 * @copyright 2015+, Prometheus Research, LLC
 * @flow
 */

import React, {PropTypes} from 'react';
import {create} from 'react-dom-stylesheet';

type SizeUnit = string | number;

function choose(a?: SizeUnit, b?: SizeUnit, c?: SizeUnit): SizeUnit | void {
  if (a != null) {
    return a;
  } else if (b != null) {
    return b;
  } else if (c != null) {
    return c;
  } else {
    return undefined;
  }
}

/**
 * Flexbox layout primitive.
 *
 * See https://css-tricks.com/snippets/css/a-guide-to-flexbox/ for docs.
 */
export default class Box extends React.Component {

  static stylesheet = create({
    boxSizing: 'border-box',
    position: 'relative',

    margin: 0,
    padding: 0,

    display: 'flex',
    alignItems: 'stretch',
    flexBasis: 'auto',
    flexShrink: 0,

    minHeight: 0,
    minWidth: 0
  }, 'Box');

  static propTypes = {
    /**
     * Sets `align-content` style property.
     *
     * See https://css-tricks.com/almanac/properties/a/align-content/ for docs.
     */
    alignContent: PropTypes.oneOf([
      'flex-start',
      'flex-end',
      'center',
      'space-between',
      'space-around',
      'stretch'
    ]),

    /**
     * Sets `align-items` style property.
     *
     * See https://css-tricks.com/almanac/properties/a/align-items/ for docs.
     */
    alignItems: PropTypes.oneOf([
      'flex-start',
      'flex-end',
      'center',
      'baseline',
      'stretch'
    ]),

    /**
     * Sets `align-self` style property.
     *
     * See https://css-tricks.com/almanac/properties/a/align-self/ for docs.
     */
    alignSelf: PropTypes.oneOf([
      'auto',
      'flex-start',
      'flex-end',
      'center',
      'baseline',
      'stretch'
    ]),

    /**
     * Sets `flex` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex/ for docs.
     */
    flex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     *
     * Sets `flex-basis` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-basis/ for docs.
     */
    basis: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-direction` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-direction/ for docs.
     */
    direction: PropTypes.oneOf([
      'row',
      'row-reverse',
      'column',
      'column-reverse'
    ]),

    /**
     * Sets `flex-flow` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-flow/ for docs.
     */
    flow: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-grow` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-grow/ for docs.
     */
    grow: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-shrink` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-shrink/ for docs.
     */
    shrink: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-wrap` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-wrap/ for docs.
     */
    wrap: PropTypes.oneOf([
      'nowrap',
      'wrap',
      'wrap-reverse'
    ]),

    /**
     * Sets `justify-content` style property.
     *
     * See https://css-tricks.com/almanac/properties/j/justify-content/ for docs.
     */
    justifyContent: PropTypes.oneOf([
      'flex-start',
      'flex-end',
      'center',
      'space-between',
      'space-around'
    ]),

    /**
     * Sets `order` style property.
     *
     * See https://css-tricks.com/almanac/properties/o/order/ for docs.
     */
    order: PropTypes.number,

    /**
     * Sets `overflow` style property.
     */
    overflow: PropTypes.oneOf([
      'auto',
      'scroll',
      'visible',
      'hidden',
    ]),

    /**
     * Sets `top` style property.
     *
     * See https://css-tricks.com/almanac/properties/t/top/ for docs.
     */
    top: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `left` style property.
     *
     * See https://css-tricks.com/almanac/properties/l/left/ for docs.
     */
    left: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `bottom` style property.
     *
     * See https://css-tricks.com/almanac/properties/b/bottom/ for docs.
     */
    bottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `right` style property.
     *
     * See https://css-tricks.com/almanac/properties/r/right/ for docs.
     */
    right: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `height` style property.
     *
     * See https://css-tricks.com/almanac/properties/h/height/ for docs.
     */
    height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `width` style property.
     *
     * See https://css-tricks.com/almanac/properties/w/width/ for docs.
     */
    width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `min-width` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/min-width/ for docs.
     */
    minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `min-height` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/min-height/ for docs.
     */
    minHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `max-width` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/max-width/ for docs.
     */
    maxWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `max-height` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/max-height/ for docs.
     */
    maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin/ for docs.
     */
    margin: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-right` and `margin-left` style properties.
     */
    marginH: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-top` and `margin-bottom` style properties.
     */
    marginV: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-right` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-right/ for docs.
     */
    marginRight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-left` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-left/ for docs.
     */
    marginLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-top` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-top/ for docs.
     */
    marginTop: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-bottom` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-bottom/ for docs.
     */
    marginBottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding/ for docs.
     */
    padding: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-right` and `padding-left` style properties.
     */
    paddingH: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-top` and `padding-bottom` style properties.
     */
    paddingV: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-right` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-right/ for docs.
     */
    paddingRight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-left` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-left/ for docs.
     */
    paddingLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-top` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-top/ for docs.
     */
    paddingTop: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-bottom` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-bottom/ for docs.
     */
    paddingBottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Extra CSS style properties.
     */
    style: PropTypes.object,

    /**
     * Styleable variant.
     */
    variant: PropTypes.object,

    /**
     * Component to render.
     */
    Component: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
  };

  static defaultProps = {
    Component: 'div'
  };

  render() {
    let {
      alignContent,
      alignItems,
      alignSelf,
      flex,
      basis: flexBasis,
      direction: flexDirection,
      flow: flexFlow,
      grow: flexGrow,
      shrink: flexShrink,
      wrap: flexWrap,
      justifyContent,
      order,
      overflow,
      style,
      variant,
      Component,
      top, left, bottom, right,
      height, width,
      minWidth, minHeight,
      maxWidth, maxHeight,
      margin,
      marginH, marginV,
      marginRight, marginLeft, marginTop, marginBottom,
      padding,
      paddingH, paddingV,
      paddingRight, paddingLeft, paddingTop, paddingBottom,
      ...props
    } = this.props;
    style = {

      alignContent,
      alignItems,
      alignSelf,
      flexBasis,
      flexDirection,
      flexFlow,
      flexGrow: choose(flexGrow, flex),
      flexShrink: choose(flexShrink, flex),
      flexWrap,
      justifyContent,
      order,

      overflow,

      top,
      left,
      bottom,
      right,

      height, width,

      minWidth, minHeight,
      maxWidth, maxHeight,

      marginLeft: choose(marginLeft, marginH, margin),
      marginRight: choose(marginRight, marginH, margin),
      marginTop: choose(marginTop, marginV, margin),
      marginBottom: choose(marginBottom, marginV, margin),

      paddingLeft: choose(paddingLeft, paddingH, padding),
      paddingRight: choose(paddingRight, paddingH, padding),
      paddingTop: choose(paddingTop, paddingV, padding),
      paddingBottom: choose(paddingBottom, paddingV, padding),

      ...style,
    };
    let className = this.constructor.stylesheet.asClassName(variant);
    return <Component className={className} {...props} style={style} />;
  }

  componentDidMount() {
    this.constructor.stylesheet.use();
  }

  componentWillUnmount() {
    this.constructor.stylesheet.dispose();
  }

  static style(stylesheet, displayName = this.displayName) {
    return class extends Box {
      static displayName = displayName;
      static stylesheet = this.stylesheet.override(stylesheet);
    };
  }
}
<commit_msg>fix: add missing position prop<commit_after>/**
 * @copyright 2015+, Prometheus Research, LLC
 * @flow
 */

import React, {PropTypes} from 'react';
import {create} from 'react-dom-stylesheet';

type SizeUnit = string | number;

function choose(a?: SizeUnit, b?: SizeUnit, c?: SizeUnit): SizeUnit | void {
  if (a != null) {
    return a;
  } else if (b != null) {
    return b;
  } else if (c != null) {
    return c;
  } else {
    return undefined;
  }
}

/**
 * Flexbox layout primitive.
 *
 * See https://css-tricks.com/snippets/css/a-guide-to-flexbox/ for docs.
 */
export default class Box extends React.Component {

  static stylesheet = create({
    boxSizing: 'border-box',
    position: 'relative',

    margin: 0,
    padding: 0,

    display: 'flex',
    alignItems: 'stretch',
    flexBasis: 'auto',
    flexShrink: 0,

    minHeight: 0,
    minWidth: 0
  }, 'Box');

  static propTypes = {
    /**
     * Sets `align-content` style property.
     *
     * See https://css-tricks.com/almanac/properties/a/align-content/ for docs.
     */
    alignContent: PropTypes.oneOf([
      'flex-start',
      'flex-end',
      'center',
      'space-between',
      'space-around',
      'stretch'
    ]),

    /**
     * Sets `align-items` style property.
     *
     * See https://css-tricks.com/almanac/properties/a/align-items/ for docs.
     */
    alignItems: PropTypes.oneOf([
      'flex-start',
      'flex-end',
      'center',
      'baseline',
      'stretch'
    ]),

    /**
     * Sets `align-self` style property.
     *
     * See https://css-tricks.com/almanac/properties/a/align-self/ for docs.
     */
    alignSelf: PropTypes.oneOf([
      'auto',
      'flex-start',
      'flex-end',
      'center',
      'baseline',
      'stretch'
    ]),

    /**
     * Sets `flex` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex/ for docs.
     */
    flex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     *
     * Sets `flex-basis` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-basis/ for docs.
     */
    basis: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-direction` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-direction/ for docs.
     */
    direction: PropTypes.oneOf([
      'row',
      'row-reverse',
      'column',
      'column-reverse'
    ]),

    /**
     * Sets `flex-flow` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-flow/ for docs.
     */
    flow: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-grow` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-grow/ for docs.
     */
    grow: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-shrink` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-shrink/ for docs.
     */
    shrink: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `flex-wrap` style property.
     *
     * See https://css-tricks.com/almanac/properties/f/flex-wrap/ for docs.
     */
    wrap: PropTypes.oneOf([
      'nowrap',
      'wrap',
      'wrap-reverse'
    ]),

    /**
     * Sets `justify-content` style property.
     *
     * See https://css-tricks.com/almanac/properties/j/justify-content/ for docs.
     */
    justifyContent: PropTypes.oneOf([
      'flex-start',
      'flex-end',
      'center',
      'space-between',
      'space-around'
    ]),

    /**
     * Sets `order` style property.
     *
     * See https://css-tricks.com/almanac/properties/o/order/ for docs.
     */
    order: PropTypes.number,

    /**
     * Sets `overflow` style property.
     *
     * See https://css-tricks.com/almanac/properties/o/overflow/ for docs.
     */
    overflow: PropTypes.oneOf([
      'auto',
      'scroll',
      'visible',
      'hidden',
    ]),

    /**
     * Sets `position` style property.
     *  See https://css-tricks.com/almanac/properties/p/position/ for docs
     */
    position: PropTypes.oneOf([
      'static',
      'relative',
      'absolute',
      'fixed',
      'sticky',
    ]),

    /**
     * Sets `top` style property.
     *
     * See https://css-tricks.com/almanac/properties/t/top/ for docs.
     */
    top: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `left` style property.
     *
     * See https://css-tricks.com/almanac/properties/l/left/ for docs.
     */
    left: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `bottom` style property.
     *
     * See https://css-tricks.com/almanac/properties/b/bottom/ for docs.
     */
    bottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `right` style property.
     *
     * See https://css-tricks.com/almanac/properties/r/right/ for docs.
     */
    right: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `height` style property.
     *
     * See https://css-tricks.com/almanac/properties/h/height/ for docs.
     */
    height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `width` style property.
     *
     * See https://css-tricks.com/almanac/properties/w/width/ for docs.
     */
    width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `min-width` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/min-width/ for docs.
     */
    minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `min-height` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/min-height/ for docs.
     */
    minHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `max-width` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/max-width/ for docs.
     */
    maxWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `max-height` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/max-height/ for docs.
     */
    maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin/ for docs.
     */
    margin: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-right` and `margin-left` style properties.
     */
    marginH: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-top` and `margin-bottom` style properties.
     */
    marginV: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-right` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-right/ for docs.
     */
    marginRight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-left` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-left/ for docs.
     */
    marginLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-top` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-top/ for docs.
     */
    marginTop: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `margin-bottom` style property.
     *
     * See https://css-tricks.com/almanac/properties/m/margin-bottom/ for docs.
     */
    marginBottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding/ for docs.
     */
    padding: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-right` and `padding-left` style properties.
     */
    paddingH: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-top` and `padding-bottom` style properties.
     */
    paddingV: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-right` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-right/ for docs.
     */
    paddingRight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-left` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-left/ for docs.
     */
    paddingLeft: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-top` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-top/ for docs.
     */
    paddingTop: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Sets `padding-bottom` style property.
     *
     * See https://css-tricks.com/almanac/properties/p/padding-bottom/ for docs.
     */
    paddingBottom: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Extra CSS style properties.
     */
    style: PropTypes.object,

    /**
     * Styleable variant.
     */
    variant: PropTypes.object,

    /**
     * Component to render.
     */
    Component: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
  };

  static defaultProps = {
    Component: 'div'
  };

  render() {
    let {
      alignContent,
      alignItems,
      alignSelf,
      flex,
      basis: flexBasis,
      direction: flexDirection,
      flow: flexFlow,
      grow: flexGrow,
      shrink: flexShrink,
      wrap: flexWrap,
      justifyContent,
      order,
      overflow,
      style,
      variant,
      Component,
      position,
      top, left, bottom, right,
      height, width,
      minWidth, minHeight,
      maxWidth, maxHeight,
      margin,
      marginH, marginV,
      marginRight, marginLeft, marginTop, marginBottom,
      padding,
      paddingH, paddingV,
      paddingRight, paddingLeft, paddingTop, paddingBottom,
      ...props
    } = this.props;
    style = {

      alignContent,
      alignItems,
      alignSelf,
      flexBasis,
      flexDirection,
      flexFlow,
      flexGrow: choose(flexGrow, flex),
      flexShrink: choose(flexShrink, flex),
      flexWrap,
      justifyContent,
      order,

      overflow,

      position,

      top,
      left,
      bottom,
      right,

      height, width,

      minWidth, minHeight,
      maxWidth, maxHeight,

      marginLeft: choose(marginLeft, marginH, margin),
      marginRight: choose(marginRight, marginH, margin),
      marginTop: choose(marginTop, marginV, margin),
      marginBottom: choose(marginBottom, marginV, margin),

      paddingLeft: choose(paddingLeft, paddingH, padding),
      paddingRight: choose(paddingRight, paddingH, padding),
      paddingTop: choose(paddingTop, paddingV, padding),
      paddingBottom: choose(paddingBottom, paddingV, padding),

      ...style,
    };
    let className = this.constructor.stylesheet.asClassName(variant);
    return <Component className={className} {...props} style={style} />;
  }

  componentDidMount() {
    this.constructor.stylesheet.use();
  }

  componentWillUnmount() {
    this.constructor.stylesheet.dispose();
  }

  static style(stylesheet, displayName = this.displayName) {
    return class extends Box {
      static displayName = displayName;
      static stylesheet = this.stylesheet.override(stylesheet);
    };
  }
}
<|endoftext|>


<commit_before>import {translate} from 'focus-core/translation';

const React = require('react');
const ReactDOM = require('react-dom');

import builder from 'focus-core/component/builder';
import type from 'focus-core/component/types';
const uuid = require('uuid');
const find = require('lodash/collection/find');
const {uniqueId} = require('lodash/utility');

// Components
const Icon = require('../../common/icon').component;
const Dropdown = require('../../common/select-action').component;

const scopeMixin = {
    /**
    * Component tag name.
    * @type {String}
    */
    displayName: 'Scope',
    /**
    * Component default properties.
    * @return {Object} the default props.
    */
    getDefaultProps() {
        return {
            list: []
        };
    },
    /**
    * Scope property validation.
    * @type {Object}
    */
    propTypes: {
        list: type('array'),
        value: type(['string', 'number'])
    },
    /**
    * Called when component will mount.
    */
    componentWillMount() {
        this.scopesId = uniqueId('scopes_');
    },
    /**
    * Called when component is mounted.
    */
    componentDidMount() {
        if (ReactDOM.findDOMNode(this.refs.scopeDropdown)) {
            componentHandler.upgradeElement(ReactDOM.findDOMNode(this.refs.scopeDropdown));
        }
    },
    /**
    * Component will receive props.
    * @param {Object} nextProps the next props
    */
    componentWillReceiveProps(nextProps) {
        if (ReactDOM.findDOMNode(this.refs.scopeDropdown)) {
            componentHandler.upgradeElement(ReactDOM.findDOMNode(this.refs.scopeDropdown));
        }
    },
    /**
    * Called before component is unmounted.
    */
    componentWillUnmount() {
        if (ReactDOM.findDOMNode(this.refs.scopeDropdown)) {
            componentHandler.downgradeElements(ReactDOM.findDOMNode(this.refs.scopeDropdown));
        }
    },
    /**
    * Get the scope click handler, based on the scope given as an argument.
    * @param  {String} code   the clicked scope's code
    * @return {Function}  the scope click handler
    */
    _getScopeClickHandler({code}) {
        const {onScopeSelection} = this.props;
        return () => {
            if (onScopeSelection) {
                onScopeSelection(code);
            }
        };
    },
    _getActiveScope() {
        const {list, value} = this.props;
        const activeScope = find(list, {code: value});
        return activeScope || {};
    },
    /**
    * Render the scope list if it is deployed
    * @return {HTML} the rendered scope list
    */
    _renderScopeList() {
        const {scopesId} = this;
        const {list: scopeList, value} = this.props;
        return (
            <ul className={`mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effect`} data-focus='search-bar-scopes' htmlFor={scopesId} ref='scopeDropdown'>
                {0 < scopeList.length && scopeList.map(scope => {
                    const {code, icon, label, ...otherScopeProps} = scope;
                    const scopeId = uniqueId('scopes_');
                    const isActive = value === code;
                    return (

                            {scope.code &&
                                <Icon name={icon || code} {...otherScopeProps}/>
                            }
                            {translate(label)}

                    );
                })}
                {0 === scopeList.length &&

                        {translate('scopes.empty')}

                }

        );
    },
    /**
    * Render the complete scope element.
    * @return {object} - The jsx element.
    */
    render() {
        const {scopesId} = this;
        const activeScope = this._getActiveScope();
        const {code, icon, label, ...otherScopeProps} = activeScope;
        return (

                <button className='mdl-button mdl-js-button' id={scopesId} data-scope={code}>
                    <Icon name={icon || code} {...otherScopeProps}/>
                    {translate(label)}
                </button>
                {this._renderScopeList()}

        );
    }
};
module.exports = builder(scopeMixin);
<commit_msg>Fix #897<commit_after>import {translate} from 'focus-core/translation';

const React = require('react');
const ReactDOM = require('react-dom');

import builder from 'focus-core/component/builder';
import type from 'focus-core/component/types';
const uuid = require('uuid');
const find = require('lodash/collection/find');
const {uniqueId} = require('lodash/utility');

// Components
const Icon = require('../../common/icon').component;
const Dropdown = require('../../common/select-action').component;

const scopeMixin = {
    /**
    * Component tag name.
    * @type {String}
    */
    displayName: 'Scope',
    /**
    * Component default properties.
    * @return {Object} the default props.
    */
    getDefaultProps() {
        return {
            list: []
        };
    },
    /**
    * Scope property validation.
    * @type {Object}
    */
    propTypes: {
        list: type('array'),
        value: type(['string', 'number'])
    },
    /**
    * Called when component will mount.
    */
    componentWillMount() {
        this.scopesId = uniqueId('scopes_');
    },
    /**
    * Called when component is mounted.
    */
    componentDidMount() {
        if (ReactDOM.findDOMNode(this.refs.scopeDropdown)) {
            componentHandler.upgradeElement(ReactDOM.findDOMNode(this.refs.scopeDropdown));
        }
    },
    /**
    * Component will receive props.
    * @param {Object} nextProps the next props
    */
    componentWillReceiveProps(nextProps) {
        if (ReactDOM.findDOMNode(this.refs.scopeDropdown)) {
            componentHandler.upgradeElement(ReactDOM.findDOMNode(this.refs.scopeDropdown));
        }
    },
    /**
    * Called before component is unmounted.
    */
    componentWillUnmount() {
        if (ReactDOM.findDOMNode(this.refs.scopeDropdown)) {
            componentHandler.downgradeElements(ReactDOM.findDOMNode(this.refs.scopeDropdown));
        }
    },
    /**
    * Get the scope click handler, based on the scope given as an argument.
    * @param  {String} code   the clicked scope's code
    * @return {Function}  the scope click handler
    */
    _getScopeClickHandler({code}) {
        const {onScopeSelection} = this.props;
        return () => {
            if (onScopeSelection) {
                onScopeSelection(code);
            }
            // Fix MDL issue with closing a dropdown
            const parentRef = this.refs.parent;
            if (parentRef) {
                const dropdownDiv = parentRef.getElementsByTagName('div')[0];
                dropdownDiv.className = dropdownDiv.className.replace(' is-visible', '');
            }
        };
    },
    _getActiveScope() {
        const {list, value} = this.props;
        const activeScope = find(list, {code: value});
        return activeScope || {};
    },
    /**
    * Render the scope list if it is deployed
    * @return {HTML} the rendered scope list
    */
    _renderScopeList() {
        const {scopesId} = this;
        const {list: scopeList, value} = this.props;
        return (
            <ul className={`mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effect`} data-focus='search-bar-scopes' htmlFor={scopesId} ref='scopeDropdown'>
                {0 < scopeList.length && scopeList.map(scope => {
                    const {code, icon, label, ...otherScopeProps} = scope;
                    const scopeId = uniqueId('scopes_');
                    const isActive = value === code;
                    return (

                            {scope.code &&
                                <Icon name={icon || code} {...otherScopeProps}/>
                            }
                            {translate(label)}

                    );
                })}
                {0 === scopeList.length &&

                        {translate('scopes.empty')}

                }

        );
    },
    /**
    * Render the complete scope element.
    * @return {object} - The jsx element.
    */
    render() {
        const {scopesId} = this;
        const activeScope = this._getActiveScope();
        const {code, icon, label, ...otherScopeProps} = activeScope;
        return (

                <button className='mdl-button mdl-js-button' id={scopesId} data-scope={code}>
                    <Icon name={icon || code} {...otherScopeProps}/>
                    {translate(label)}
                </button>
                {this._renderScopeList()}

        );
    }
};
module.exports = builder(scopeMixin);
<|endoftext|>


<commit_before>'use strict';
angular
    .module('app.core')
    .controller('HomeController', function($scope, PageValues) {
        //Set page title and description
        PageValues.title = "HOME";
        PageValues.description = "Learn AngularJS using best practice real world examples.";
        //Setup view model object
        var vm = this;
        vm.tutorials = [
            {
                title: "INTRODUCTION",
                description: "An introduction to the AngularJS by example application and the tutorial series. All about why this project exists, what the tutorial series is likely to include and who the tutorials are for.",
                link: "http://www.revillweb.com/angularjs-by-example/1-introduction/"
            },
            {
                title: "PROJECT STRUCTURE & MODULARITY",
                description: "Looking at project structure in terms of AngularJS modularity and also how best to lay out your directories to make development a breeze.",
                link: "#"
            },
            {
                title: "CONTROLLERS",
                description: "Investigating the different ways you can write AngularJS controllers along with recommended best practices.",
                link: "#"
            },
            {
                title: "SHARING DATA WITH SERVICES",
                description: "How to effectively share data around your AngularJS application and when best to use the different types of service available.",
                link: "#"
            },
            {
                title: "WRITING DIRECTIVES",
                description: "Theories behind directive design and how to best implement them within your application.",
                link: "#"
            },
            {
                title: "BUILD PROCESS",
                description: "So you have an awesome AngularJS app, how do you get it ready for production and deployment? Using Gulp & GitFlow to get the job done.",
                link: "#"
            }
        ];
    });<commit_msg>Update home.ctrl.js<commit_after>'use strict';
angular
    .module('app.core')
    .controller('HomeController', function($scope, PageValues) {
        //Set page title and description
        PageValues.title = "HOME";
        PageValues.description = "Learn AngularJS using best practice real world examples.";
        //Setup view model object
        var vm = this;
        vm.tutorials = [
            {
                title: "INTRODUCTION",
                description: "An introduction to the AngularJS by example application and the tutorial series. All about why this project exists, what the tutorial series is likely to include and who the tutorials are for.",
                link: "http://www.revillweb.com/angularjs-by-example/1-introduction/"
            },
            {
                title: "PROJECT STRUCTURE & MODULARITY",
                description: "Looking at project structure in terms of AngularJS modularity and also how best to lay out your directories to make development a breeze.",
                link: "http://www.revillweb.com/angularjs-by-example/2-project-structure/"
            },
            {
                title: "CONTROLLERS",
                description: "Investigating the different ways you can write AngularJS controllers along with recommended best practices.",
                link: "#"
            },
            {
                title: "SHARING DATA WITH SERVICES",
                description: "How to effectively share data around your AngularJS application and when best to use the different types of service available.",
                link: "#"
            },
            {
                title: "WRITING DIRECTIVES",
                description: "Theories behind directive design and how to best implement them within your application.",
                link: "#"
            },
            {
                title: "BUILD PROCESS",
                description: "So you have an awesome AngularJS app, how do you get it ready for production and deployment? Using Gulp & GitFlow to get the job done.",
                link: "#"
            }
        ];
    });
<|endoftext|>