<commit_before>var EventEmitter = require('events').EventEmitter;
var util = require('util');
var request;
if (process.title === 'browser')
    request = require('browser-request');
else {
    var requestPath = 'request';
    request = require(requestPath);
}
var ltx = require('ltx');

function BOSHConnection(opts) {
    var that = this;
    EventEmitter.call(this);

    this.boshURL = opts.boshURL;
    this.jid = opts.jid;
    this.xmlnsAttrs = {
	xmlns: "http://jabber.org/protocol/httpbind",
	'xmlns:xmpp': "urn:xmpp:xbosh",
	'xmlns:stream': "http://etherx.jabber.org/streams"
    };
    if (opts.xmlns)
	for(var prefix in opts.xmlns)
	    if (prefix)
		this.xmlnsAttrs["xmlns:" + prefix] = opts.xmlns[prefix];
	    else
		this.xmlnsAttrs["xmlns"] = opts.xmlns[prefix];
    this.currentRequests = 0;
    this.queue = [];
    this.rid = Math.ceil(Math.random() * 9999999999);

    this.request({
	to: this.jid.domain,
	ver: "1.6",
	wait: "10",
	hold: "1",
	content: this.contentType
    }, [], function(err, bodyEl) {
	if (err) {
	    that.emit('error', err);
	} else if (bodyEl && bodyEl.attrs) {
	    that.sid = bodyEl.attrs.sid;
	    that.maxRequests = parseInt(bodyEl.attrs.requests, 10) || 2;
	    if (that.sid && that.maxRequests > 0) {
		that.emit('connect');
		that.processResponse(bodyEl);
		process.nextTick(that.mayRequest.bind(that));
	    } else
		that.emit('error', "Invalid parameters");
	}
    });
}
util.inherits(BOSHConnection, EventEmitter);
exports.BOSHConnection = BOSHConnection;

BOSHConnection.prototype.contentType = "text/xml; charset=utf-8";

BOSHConnection.prototype.send = function(stanza) {
    this.queue.push({ stanza: stanza.root() });
    this.mayRequest();
};

BOSHConnection.prototype.processResponse = function(bodyEl) {
    if (bodyEl && bodyEl.children) {
	for(var i = 0; i < bodyEl.children.length; i++) {
	    var child = bodyEl.children[i];
	    if (child.name && child.attrs && child.children)
		this.emit('stanza', child);
	}
    }
    if (bodyEl && bodyEl.attrs.type === 'terminate') {
	this.emit('error', new Error(bodyEl.attrs.condition || "Session terminated"));
	this.emit('close');
    }
};

BOSHConnection.prototype.mayRequest = function() {
    var that = this;
    var canRequest =
	/* Must have a session already */
	this.sid &&
	/* We can only receive when one request is in flight */
	(this.currentRequests === 0 ||
	 /* Is there something to send, and are we allowed? */
	 ((this.queue.length > 0 && this.currentRequests < this.maxRequests))
	);
    if (!canRequest)
	return;

    var stanzas = this.queue.map(function(queued) {
	return queued.stanza;
    });
    this.queue = [];
    this.request({}, stanzas, function(err, bodyEl) {
	if (!err && bodyEl)
	    that.processResponse(bodyEl);

	process.nextTick(that.mayRequest.bind(that));
    });
};

BOSHConnection.prototype.end = function(stanzas) {
    var that = this;
    stanzas = stanzas || [];
    if (typeof stanzas !== 'array')
	stanzas = [stanzas];

    this.request({ type: 'terminate' }, stanzas, function(err, bodyEl) {
	if (bodyEl)
	    that.processResponse(bodyEl);

	that.emit('end');
	that.emit('close');
	delete that.sid;
    });
};

BOSHConnection.prototype.request = function(attrs, children, cb) {
    var that = this;

    attrs.rid = this.rid.toString();
    this.rid++;
    if (this.sid)
	attrs.sid = this.sid;

    for(var k in this.xmlnsAttrs)
	attrs[k] = this.xmlnsAttrs[k];
    var boshEl = new ltx.Element('body', attrs);
    for(var i = 0; i < children.length; i++)
	boshEl.cnode(children[i]);

    this.currentRequests++;
    request({
	uri: this.boshURL,
	method: 'POST',
	headers: {
	    "Content-Type": this.contentType
	},
	body: boshEl.toString()
    }, function(err, res, body) {
	that.currentRequests--;

	if (err)
	    return cb(err);
	if (res.statusCode < 200 || res.statusCode >= 400)
	    return cb(new Error("HTTP status " + res.statusCode));

	var bodyEl;
	try {
	    bodyEl = ltx.parse(body);
	} catch(e) {
	    return cb(e);
	}

	if (bodyEl && bodyEl.attrs.type === 'terminate')
	    cb(new Error(bodyEl.attrs.condition));
	else if (bodyEl)
	    cb(null, bodyEl);
	else
	    cb(new Error('no <body/>'));
    });
};
<commit_msg>bosh: send mayRequest() in nextTick<commit_after>var EventEmitter = require('events').EventEmitter;
var util = require('util');
var request;
if (process.title === 'browser')
    request = require('browser-request');
else {
    var requestPath = 'request';
    request = require(requestPath);
}
var ltx = require('ltx');

function BOSHConnection(opts) {
    var that = this;
    EventEmitter.call(this);

    this.boshURL = opts.boshURL;
    this.jid = opts.jid;
    this.xmlnsAttrs = {
	xmlns: "http://jabber.org/protocol/httpbind",
	'xmlns:xmpp': "urn:xmpp:xbosh",
	'xmlns:stream': "http://etherx.jabber.org/streams"
    };
    if (opts.xmlns)
	for(var prefix in opts.xmlns)
	    if (prefix)
		this.xmlnsAttrs["xmlns:" + prefix] = opts.xmlns[prefix];
	    else
		this.xmlnsAttrs["xmlns"] = opts.xmlns[prefix];
    this.currentRequests = 0;
    this.queue = [];
    this.rid = Math.ceil(Math.random() * 9999999999);

    this.request({
	to: this.jid.domain,
	ver: "1.6",
	wait: "10",
	hold: "1",
	content: this.contentType
    }, [], function(err, bodyEl) {
	if (err) {
	    that.emit('error', err);
	} else if (bodyEl && bodyEl.attrs) {
	    that.sid = bodyEl.attrs.sid;
	    that.maxRequests = parseInt(bodyEl.attrs.requests, 10) || 2;
	    if (that.sid && that.maxRequests > 0) {
		that.emit('connect');
		that.processResponse(bodyEl);
		process.nextTick(that.mayRequest.bind(that));
	    } else
		that.emit('error', "Invalid parameters");
	}
    });
}
util.inherits(BOSHConnection, EventEmitter);
exports.BOSHConnection = BOSHConnection;

BOSHConnection.prototype.contentType = "text/xml; charset=utf-8";

BOSHConnection.prototype.send = function(stanza) {
    this.queue.push({ stanza: stanza.root() });
    process.nextTick(this.mayRequest.bind(this));
};

BOSHConnection.prototype.processResponse = function(bodyEl) {
    if (bodyEl && bodyEl.children) {
	for(var i = 0; i < bodyEl.children.length; i++) {
	    var child = bodyEl.children[i];
	    if (child.name && child.attrs && child.children)
		this.emit('stanza', child);
	}
    }
    if (bodyEl && bodyEl.attrs.type === 'terminate') {
	this.emit('error', new Error(bodyEl.attrs.condition || "Session terminated"));
	this.emit('close');
    }
};

BOSHConnection.prototype.mayRequest = function() {
    var that = this;
    var canRequest =
	/* Must have a session already */
	this.sid &&
	/* We can only receive when one request is in flight */
	(this.currentRequests === 0 ||
	 /* Is there something to send, and are we allowed? */
	 ((this.queue.length > 0 && this.currentRequests < this.maxRequests))
	);
    if (!canRequest)
	return;

    var stanzas = this.queue.map(function(queued) {
	return queued.stanza;
    });
    this.queue = [];
    this.request({}, stanzas, function(err, bodyEl) {
	if (!err && bodyEl)
	    that.processResponse(bodyEl);

	process.nextTick(that.mayRequest.bind(that));
    });
};

BOSHConnection.prototype.end = function(stanzas) {
    var that = this;
    stanzas = stanzas || [];
    if (typeof stanzas !== 'array')
	stanzas = [stanzas];

    this.request({ type: 'terminate' }, stanzas, function(err, bodyEl) {
	if (bodyEl)
	    that.processResponse(bodyEl);

	that.emit('end');
	that.emit('close');
	delete that.sid;
    });
};

BOSHConnection.prototype.request = function(attrs, children, cb) {
    var that = this;

    attrs.rid = this.rid.toString();
    this.rid++;
    if (this.sid)
	attrs.sid = this.sid;

    for(var k in this.xmlnsAttrs)
	attrs[k] = this.xmlnsAttrs[k];
    var boshEl = new ltx.Element('body', attrs);
    for(var i = 0; i < children.length; i++)
	boshEl.cnode(children[i]);

    this.currentRequests++;
    request({
	uri: this.boshURL,
	method: 'POST',
	headers: {
	    "Content-Type": this.contentType
	},
	body: boshEl.toString()
    }, function(err, res, body) {
	that.currentRequests--;

	if (err)
	    return cb(err);
	if (res.statusCode < 200 || res.statusCode >= 400)
	    return cb(new Error("HTTP status " + res.statusCode));

	var bodyEl;
	try {
	    bodyEl = ltx.parse(body);
	} catch(e) {
	    return cb(e);
	}

	if (bodyEl && bodyEl.attrs.type === 'terminate')
	    cb(new Error(bodyEl.attrs.condition));
	else if (bodyEl)
	    cb(null, bodyEl);
	else
	    cb(new Error('no <body/>'));
    });
};
<|endoftext|>


<commit_before>/** Ephellon Dantzler - 2015, 2018
  * How to use ([...] is optional):
  * Simply call on SynQ([attribute-name]), and that's it
  * FAQ:
  * Q1) What if I don't want to update elements, just some data?
  * A2) Use SynQ.push(string:name, string:data[, string:password]),
        SynQ.pull(string:name[, string:password]),
        OR SynQ.pop(string:name[, string:password])
  * Q2) What if I'd like to get rid of all of my data (in SynQ)?
  * A2) Use SynQ.clear()**
  * Q3) What if I'd like to synchronize across my entire domain?
  * A3) Set the use_global_synq_token to a defined value
  * Q4) How do I check on the status of my data?
  * A4) You can use SynQ.data([boolean:show-private]) to show your data
  * Q5) What about the other things I see under SynQ?
  * A6) Those are for future technologies, but you can use them as you see fit.
  *
  * ** SynQ.clear will only remove local*** items if the "use_global_synq_token" isn't set
  * *** By "local" I mean for a unique page identifier (URL), e.g. "https://example.com/page-1" won't share "local" data with ".../page-2"
  */

var NO_NAME = "Resource name is missing.",
    use_global_synq_token;

// IE and Edge?
if(!("addEventListener" in window))
  window.addEventListener = window.attachEvent;

// The main function
function SynQ(name) {
  name = name || SynQ.syn.join("],[");

  var messages = [],
      uuids    = [],
      copies   = {},
      query    = document.querySelectorAll("[" + name + "]");

  function fetch(e) {
    var a = e.attributes,
        n =
    ("synq-data" in a)?
      ("value" in e)?
        "value":
      "innerText":
    ("synq-text" in a)?
      "innerHTML":
    "outerHTML";

    return e[n];
  }

  for(var index = 0, element, uuid, value, info, id; index < query.length; index++) {
    // Element information
    element       = query[index];
    info          = element.tagName;
    id            = element.attributes.id;
    copies[info] |= 0;

    // Add copies of elements for the UUID to work properly
    info += (id != undefined && id != null)?
      "#" + id:
    ":nth-child(" + (++copies[info]) + ")";

    // Push the element's UUID
    uuid = SynQ.sign(info, 1);
    uuids.push(uuid);

    // Set the element's UUID for future references
    element.setAttribute("uuid", uuid);

    // Push the messages
    value = fetch(element);
    messages.push(value);
    SynQ.push(".values#" + uuid, value);
  }

  SynQ.push(".values", messages.join(SynQ.esc));
  SynQ.push(".uuids", uuids.join(","));

  SynQ.eventlistener("SynQ.");
};

SynQ.syn = "synq-data synq-text synq-html".split(" ");
  // change this accordingly; this is the syncronizing attribute for your elements
  // synq-data: .innerText OR .value
  // synq-text: .innerHTML
  // synq-html: .outerHTML
SynQ.esc  = "<!-- synq-delimeter -->";
  // change to your liking; this is the list delimeter

// The event-listener
SynQ.eventlistener = function(event) {
  var query = document.querySelectorAll("[" + SynQ.syn.join("],[") + "]");

  function write(e, m) {
    var a = e.attributes,
        n =
    ("synq-data" in a)?
      ("value" in e)?
        "value":
      "innerText":
    ("synq-text" in a)?
      "innerHTML":
    "outerHTML";

    e[n] = m;
  }

  update:
  for(var index = 0, values, value, uuids, uuid, element; index < query.length; index++) {
    element = query[index];
    values  = SynQ.pull(".values");

    if(values == undefined || values == null)
      break update;
    values = values.split(SynQ.esc);

    // UUID is set
    // Write confidently, even if the HTML document has changed
    if(element.uuid != undefined && element.uuid != null)
      value = SynQ.pull(".values#" + element.uuid) || values[index],
      write(element, value);
    // UUID isn't set
    // Write, assuming the HTML document hasn't changed
    else
      value = values[index],
      write(element, value);
  }
};

// Parse all URL types (even unconventional ones)
SynQ.parseURL = function(url) {
    var data = url.match(/^(([^:\/?#]+):)?\/{2}([^:\/?#]*)?(:\d+)?([^?#]*)(\?[^#]*)?(#.*)?/), i = 0;

    return {
        href:     data[i++],
        protocol: data[i++],
        scheme:   data[i++],
        host:     data[i++],
        port:     data[i++],
        path:     data[i++],
        search:   data[i++],
        hash:     data[i++]
    };
};

// URI encoder/decoder
SynQ.enocdeURL = function(url) {
  for(var index = 0, characters = "% \"<>[\\]^`{|}"; index < characters.length; index++)
    url = url.replace(RegExp( "\\" + characters[index], "g" ), "%" + characters.charCodeAt(index).toString(16));

  return url;
};

SynQ.decodeURL = function(url) {
  for(var regexp = /%(\d{2})/; regexp.test(url);)
    url = url.replace(regexp, function($0, $1, $_) {
      return String.fromCharCode(+("0x" + $1)) + "\r\f"; // prevent intentional %code from being removed
    });

  return url.replace(/(% "<>\[\\\]\^`\{\|\})\r\f/g, "$1");
};

// List all of the current SynQ's data (paths)
SynQ.data = function(all) {
  var regexp = RegExp("^(" +
                 ("synq://" + SynQ.sign(location, 1) + "/").replace(/(\W)/g, "\\$1") +
                 "|" +
                 ("synq://" + SynQ.sign(location.origin, 1) + "/").replace(/(\W)/g, "\\$1")
               + ")" + (all? ".+": "[^\\.].+") + "$"),
      array  = {};

  for(item in localStorage)
    if(regexp.test(item))
      array[item] = localStorage[item];

  return array;
};

// Push (set) a resource
SynQ.push = function(name, data, key) {
  SynQ.prevent(name, [undefined, null], NO_NAME);

  if(key != undefined && key != null)
    data = SynQ.lock(data, key),
    key  = ".";
  else
    key = "";

  localStorage.setItem(SynQ.signature + key + name, data);

  return data;
};

// Pull (get) a resource
SynQ.pull = function(name, key) {
  SynQ.prevent(name, [undefined, null], NO_NAME);

  var data;

  if(key != undefined && key != null)
    data = SynQ.unlock(localStorage.getItem(SynQ.signature + "." + name), key);
  else
    data = localStorage.getItem(SynQ.signature + name);

  return data;
};

// Remove a resource
SynQ.pop = function(name, key) {
  SynQ.prevent(name, [undefined, null, ""], NO_NAME);

  var data = SynQ.pull(name, key);

  if(key != undefined && key != null)
    localStorage.removeItem(SynQ.signature + "." + name);
  else
    localStorage.removeItem(SynQ.signature + name);

  return data;
};

// The "clear all" option
SynQ.clear = function() {
  var regexp = RegExp("^" + SynQ.signature.replace(/(\W)/g, "\\$1") + "$");

  for(item in localStorage)
    if(regexp.test(item))
      localStorage.removeItem(item);
};

// Lock and unlock data (weak security)
SynQ.lock = function(data, key) {
  key = SynQ.salt(SynQ.sign(key, 0));

  for(var index = 0, salted = []; index < data.length; index++)
    salted.push(String.fromCharCode(
      data.charCodeAt(index) ^ key.charCodeAt(index % 256)
    ));

  return salted.join("");
};

SynQ.unlock = function(data, key) {
  return SynQ.lock(data, key);
};

// KSA algorithm
SynQ.salt = function(text) {
  for(var index = 0, exdex = 0, swap = 0, salted = []; index < 256; index++)
    salted[index] = index;

  for(index = 0; index < 256; index++)
    exdex = (exdex + salted[index] + text.charCodeAt(index % text.length)) % 256,
    salted[swap]  = salted[index],
    salted[index] = salted[exdex],
    salted[exdex] = salted[swap];

  for(index = 0; index < 256; index++)
    salted[index] = String.fromCharCode( salted[index] );

  return salted.join("");
};

// The hash/signature function
// From my Mi/o project
SynQ.sign = function(string, fidelity) {
  var array = (string + "").split(/([^]{16})/),
      gamma = 0,
      method;

  fidelity = 18 - Math.floor((fidelity || 0) * 16);
  method = function(s) {
    return (s != undefined)? s.charCodeAt(0): s;
  };

  array.forEach(function(value, index, self) {
    return (value == "")?
      self.splice(index, 1):
    gamma += value.charCodeAt(0);
  });

  for(var index = 0, length = array.length, last = length - 1, result = []; index < length; index++)
    for(var self = array[index], next = (array[index + 1] || ""), mirror = array[last], a, b, c, d, e, f, g = gamma, i = 0, j = self.length, k = mirror.length, l = length, m = k - 1, q = fidelity; i < j; ++i, --m, g = gamma += a + b + c + d + e + f)
      a = method(self[i])         | 0,
      b = method(self[j - i - 1]) | 0,
      c = method(mirror[m])       | 0,
      d = method(mirror[k - m])   | 0,
      e = method(next[i])         | 0,
      f = method(next[m])         | 0,
      result.push(Math.abs(
        (((a ^ ~b) << (i + k)) |  (j & e) | g) ^
        (((b | -c) ^  (m + j)) |  (j & f) | g) ^
        (((c & ~d) << (e - k)) >> (k ^ q) + g) ^
        (((d << a) ^  (f - j)) >> (k ^ q) + g) ^

        ((a & b | c ^ d) ^ e - f) << (q & e & f)
      ));

  result.splice(fidelity, result.length - fidelity);

  result.forEach(function(value, index, self) {
    return self.splice(index, 1, (value ^ gamma).toString((gamma % 20) + 16));
  });

  return result.join("").slice(0, 256);
};

// Handle errors
SynQ.prevent = function(variable, failures, message) {
  for(var index = 0, length = failures.length; index < length; index++)
    if(variable == failures[index])
      throw new Error(message);
};

// Auto-update & run
if(use_global_synq_token == undefined)
  SynQ.signature = "synq://" + SynQ.sign(location, 1) + "/";
else
  SynQ.signature = "synq://" + SynQ.sign(location.origin, 1) + "/";

SynQ.eventlistener("SynQ initializer.");
window.addEventListener("storage", window.onstorage = SynQ.eventlistener, false);
<commit_msg>Update synq.js<commit_after>/** Ephellon Dantzler - 2015, 2018
  * How to use ([...] is optional):
  * Simply call on SynQ([attribute-name]), and that's it
  * FAQ:
  * Q1) What if I don't want to update elements, just some data?
  * A2) Use SynQ.push(string:name, string:data[, string:password]),
        SynQ.pull(string:name[, string:password]),
        OR SynQ.pop(string:name[, string:password])
  * Q2) What if I'd like to get rid of all of my data (in SynQ)?
  * A2) Use SynQ.clear()**
  * Q3) What if I'd like to synchronize across my entire domain?
  * A3) Set the use_global_synq_token to a defined value
  * Q4) How do I check on the status of my data?
  * A4) You can use SynQ.data([boolean:show-private]) to show your data
  * Q5) What about the other things I see under SynQ?
  * A6) Those are for future technologies, but you can use them as you see fit.
  *
  * ** SynQ.clear will only remove local*** items if the "use_global_synq_token" isn't set
  * *** By "local" I mean for a unique page identifier (URL), e.g. "https://example.com/page-1" won't share "local" data with ".../page-2"
  */

var NO_NAME = "Resource name is missing.",
    use_global_synq_token, localStorage, sessionStorage, storage;

// IE and Edge?
if(!("addEventListener" in window))
  window.addEventListener = window.attachEvent;

// The main function
function SynQ(name) {
  name = name || SynQ.syn.join("],[");

  var messages = [],
      uuids    = [],
      copies   = {},
      query    = document.querySelectorAll("[" + name + "]");

  function fetch(e) {
    var a = e.attributes,
        n =
    ("synq-data" in a)?
      ("value" in e)?
        "value":
      "innerText":
    ("synq-text" in a)?
      "innerHTML":
    "outerHTML";

    return e[n];
  }

  for(var index = 0, element, uuid, value, info, id; index < query.length; index++) {
    // Element information
    element       = query[index];
    info          = element.tagName;
    id            = element.attributes.id;
    copies[info] |= 0;

    // Add copies of elements for the UUID to work properly
    info += (id != undefined && id != null)?
      "#" + id:
    ":nth-child(" + (++copies[info]) + ")";

    // Push the element's UUID
    uuid = SynQ.sign(info, 1);
    uuids.push(uuid);

    // Set the element's UUID for future references
    element.setAttribute("uuid", uuid);

    // Push the messages
    value = fetch(element);
    messages.push(value);
    SynQ.push(".values#" + uuid, value);
  }

  SynQ.push(".values", messages.join(SynQ.esc));
  SynQ.push(".uuids", uuids.join(","));

  SynQ.eventlistener("SynQ.");
};

SynQ.syn = "synq-data synq-text synq-html".split(" ");
  // change this accordingly; this is the syncronizing attribute for your elements
  // synq-data: .innerText OR .value
  // synq-text: .innerHTML
  // synq-html: .outerHTML
SynQ.esc  = "<!-- synq-delimeter -->";
  // change to your liking; this is the list delimeter

// The event-listener
SynQ.eventlistener = function(event) {
  var query = document.querySelectorAll("[" + SynQ.syn.join("],[") + "]");

  function write(e, m) {
    var a = e.attributes,
        n =
    ("synq-data" in a)?
      ("value" in e)?
        "value":
      "innerText":
    ("synq-text" in a)?
      "innerHTML":
    "outerHTML";

    e[n] = m;
  }

  update:
  for(var index = 0, values, value, uuids, uuid, element; index < query.length; index++) {
    element = query[index];
    values  = SynQ.pull(".values");

    if(values == undefined || values == null)
      break update;
    values = values.split(SynQ.esc);

    // UUID is set
    // Write confidently, even if the HTML document has changed
    if(element.uuid != undefined && element.uuid != null)
      value = SynQ.pull(".values#" + element.uuid) || values[index],
      write(element, value);
    // UUID isn't set
    // Write, assuming the HTML document hasn't changed
    else
      value = values[index],
      write(element, value);
  }
};

// Parse all URL types (even unconventional ones)
SynQ.parseURL = function(url) {
    var data = url.match(/^(([^:\/?#]+):)?\/{2}([^:\/?#]*)?(:\d+)?([^?#]*)(\?[^#]*)?(#.*)?/), i = 0;

    return {
        href:     data[i++],
        protocol: data[i++],
        scheme:   data[i++],
        host:     data[i++],
        port:     data[i++],
        path:     data[i++],
        search:   data[i++],
        hash:     data[i++]
    };
};

// URI encoder/decoder
SynQ.enocdeURL = function(url) {
  for(var index = 0, characters = "% \"<>[\\]^`{|}"; index < characters.length; index++)
    url = url.replace(RegExp( "\\" + characters[index], "g" ), "%" + characters.charCodeAt(index).toString(16));

  return url;
};

SynQ.decodeURL = function(url) {
  for(var regexp = /%(\d{2})/; regexp.test(url);)
    url = url.replace(regexp, function($0, $1, $_) {
      return String.fromCharCode(+("0x" + $1)) + "\r\f"; // prevent intentional %code from being removed
    });

  return url.replace(/(% "<>\[\\\]\^`\{\|\})\r\f/g, "$1");
};

// List all of the current SynQ's data (paths)
SynQ.data = function(all) {
  var regexp = RegExp("^(" +
                 ("synq://" + SynQ.sign(location, 1) + "/").replace(/(\W)/g, "\\$1") +
                 "|" +
                 ("synq://" + SynQ.sign(location.origin, 1) + "/").replace(/(\W)/g, "\\$1")
               + ")" + (all? ".+": "[^\\.].+") + "$"),
      array  = {};

  for(item in storage)
    if(regexp.test(item))
      array[item] = storage[item];

  return array;
};

// Push (set) a resource
SynQ.push = function(name, data, key) {
  SynQ.prevent(name, [undefined, null], NO_NAME);

  if(key != undefined && key != null)
    data = SynQ.lock(data, key),
    key  = ".";
  else
    key = "";

  storage.setItem(SynQ.signature + key + name, data);

  return data;
};

// Pull (get) a resource
SynQ.pull = function(name, key) {
  SynQ.prevent(name, [undefined, null], NO_NAME);

  var data;

  if(key != undefined && key != null)
    data = SynQ.unlock(storage.getItem(SynQ.signature + "." + name), key);
  else
    data = storage.getItem(SynQ.signature + name);

  return data;
};

// Remove a resource
SynQ.pop = function(name, key) {
  SynQ.prevent(name, [undefined, null, ""], NO_NAME);

  var data = SynQ.pull(name, key);

  if(key != undefined && key != null)
    storage.removeItem(SynQ.signature + "." + name);
  else
    storage.removeItem(SynQ.signature + name);

  return data;
};

// The "clear all" option
SynQ.clear = function() {
  var regexp = RegExp("^" + SynQ.signature.replace(/(\W)/g, "\\$1") + "$");

  for(item in storage)
    if(regexp.test(item))
      storage.removeItem(item);
};

// Lock and unlock data (weak security)
SynQ.lock = function(data, key) {
  key = SynQ.salt(SynQ.sign(key, 0));

  for(var index = 0, salted = []; index < data.length; index++)
    salted.push(String.fromCharCode(
      data.charCodeAt(index) ^ key.charCodeAt(index % 256)
    ));

  return salted.join("");
};

SynQ.unlock = function(data, key) {
  return SynQ.lock(data, key);
};

// KSA algorithm
SynQ.salt = function(text) {
  for(var index = 0, exdex = 0, swap = 0, salted = []; index < 256; index++)
    salted[index] = index;

  for(index = 0; index < 256; index++)
    exdex = (exdex + salted[index] + text.charCodeAt(index % text.length)) % 256,
    salted[swap]  = salted[index],
    salted[index] = salted[exdex],
    salted[exdex] = salted[swap];

  for(index = 0; index < 256; index++)
    salted[index] = String.fromCharCode( salted[index] );

  return salted.join("");
};

// The hash/signature function
// From my Mi/o project
SynQ.sign = function(string, fidelity) {
  var array = (string + "").split(/([^]{16})/),
      gamma = 0,
      method;

  fidelity = 18 - Math.floor((fidelity || 0) * 16);
  method = function(s) {
    return (s != undefined)? s.charCodeAt(0): s;
  };

  array.forEach(function(value, index, self) {
    return (value == "")?
      self.splice(index, 1):
    gamma += value.charCodeAt(0);
  });

  for(var index = 0, length = array.length, last = length - 1, result = []; index < length; index++)
    for(var self = array[index], next = (array[index + 1] || ""), mirror = array[last], a, b, c, d, e, f, g = gamma, i = 0, j = self.length, k = mirror.length, l = length, m = k - 1, q = fidelity; i < j; ++i, --m, g = gamma += a + b + c + d + e + f)
      a = method(self[i])         | 0,
      b = method(self[j - i - 1]) | 0,
      c = method(mirror[m])       | 0,
      d = method(mirror[k - m])   | 0,
      e = method(next[i])         | 0,
      f = method(next[m])         | 0,
      result.push(Math.abs(
        (((a ^ ~b) << (i + k)) |  (j & e) | g) ^
        (((b | -c) ^  (m + j)) |  (j & f) | g) ^
        (((c & ~d) << (e - k)) >> (k ^ q) + g) ^
        (((d << a) ^  (f - j)) >> (k ^ q) + g) ^

        ((a & b | c ^ d) ^ e - f) << (q & e & f)
      ));

  result.splice(fidelity, result.length - fidelity);

  result.forEach(function(value, index, self) {
    return self.splice(index, 1, (value ^ gamma).toString((gamma % 20) + 16));
  });

  return result.join("").slice(0, 256);
};

// Handle errors
SynQ.prevent = function(variable, failures, message) {
  for(var index = 0, length = failures.length; index < length; index++)
    if(variable == failures[index])
      throw new Error(message);
};

// Polyfill - Mozilla
if(!("localStorage" in window))
  Object.defineProperty(window, "localStorage", new(function() {
    var keys = [], StorageObject = {};

    Object.defineProperty(StorageObject, "getItem", {
      value: function(key) {
        return(key)?
          this[key]:
        null;
      },
      writable:     false,
      configurable: false,
      enumerable:   false
    });

    Object.defineProperty(StorageObject, "key", {
      value: function(keyID) {
        return keys[keyID];
      },
      writable:     false,
      configurable: false,
      enumerable:   false
    });

    Object.defineProperty(StorageObject, "setItem", {
      value: function(key, value) {
        if(key == undefined || key == null)
          return;
        document.cookie = escape(key) + "=" + escape(value) + "; expires=Thu, Dec 31 2099 23:59:59 GMT; path=/";
      },
      writable:     false,
      configurable: false,
      enumerable:   false
    });

    Object.defineProperty(StorageObject, "length", {
      get: function() {
        return keys.length;
      },
      configurable: false,
      enumerable:   false
    });

    Object.defineProperty(StorageObject, "removeItem", {
      value: function(key) {
        if(key == undefined || key == null)
          return;
        document.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
      },
      writable:     false,
      configurable: false,
      enumerable:   false
    });

    Object.defineProperty(StorageObject, "clear", {
      value: function() {
        if(keys.length == undefined || key.slength == null)
          return;
        for(var key in keys)
          document.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
      },
      writable:     false,
      configurable: false,
      enumerable:   false
    });

    this.get = function() {
      var index;

      for(var key in StorageObject) {
        index = keys.indexOf(key);
        if(index == -1)
          StorageObject.setItem(key, StorageObject[key]);
        else
          keys.splice(index, 1);
        delete StorageObject[key];
      }

      for(keys; keys.length > 0; keys.splice(0, 1))
        StorageObject.removeItem(keys[0]);

      for(var cookie, key, index = 0, cookies = document.cookie.split(/\s*;\s*/); index < cookies.length; index++) {
        cookie = cookies[index].split(/\s*=\s*/);
        if(cookie.length > 1)
          key = unescape(cookie[0]),
          StorageObject[key] = unescape(cookie[1]),
          keys.push(key);
      }

      return StorageObject;
    };

    this.configurable = false;
    this.enumerable   = true;
  })());

// Auto-update & run
if(use_global_synq_token == undefined)
  SynQ.signature = "synq://" + SynQ.sign(location, 1) + "/";
else
  SynQ.signature = "synq://" + SynQ.sign(location.origin, 1) + "/";

SynQ.eventlistener("SynQ initializer.");
window.addEventListener("storage", window.onstorage = SynQ.eventlistener, false);
<|endoftext|>


<commit_before>/*eslint-disable */
module.exports = {
  "code": "es-ES",
  "time_format": "HH:mm",
  "date_locale": "es",
  "date_format": "DD\/MM\/YYYY",
  "datetime_format": "DD\/MM\/YYYY HH:mm",
  "colloquial_date_format": "ddd Do MMMM YYYY",

  /* Home Directory */

/* home.hbs */

  "Dashboards": "Tableros",
  "create now": "crear ahora",
  "ERROR":"ERROR",
  "Ideas for a": "Ideas para una",
  "hackathon":"hackatón",
  "Collections":"Colecciones",
  "Projects":"Proyectos",
  "People":"Personas",
  "team":"equipo",
  "partners":"socios",
  "The HackDash was born":"HackDash nació por accidente y por necesidad. Estábamos buscando una plataforma para hacer seguimiento de ideas durante los hackatones en la línea de Hacks/Hackers Media Party organizado por @HacksHackersBA en la que hackers y periodistas comparten ideas. Corrimos la voz de nuestra necesidad por Twitter y ese fue el contexto en el que nació HackDash. @blejman tuvo una idea y a @dzajdband le interesó implementar esa idea. Así que empezamos a crear la aplicación esperando llegar al Buenos Aires Media Party con algo que no fuera horrible. El día del hackatón de Media Party llegó acompañado de una grata sorpresa: No solamente HackDash le gustó a la gente, sino que también algunos programadores agregaron la mejora de HackDash como su proyecto de Hackatón. Después del Media Party nos dimos cuenta de que esta pequeña aplicación estaba cubriendo una necesidad real. Tres años después, el tablero se está convirtiendo en un estándar para hacer seguimiento de ideas innovadores alrededor del mundo.
Creá tu propio tablero, sé parte de una comunidad global.
",

/* collection.hbs */

/* counts.hbs */

"dashboards":"tableros",
"projects":"proyectos",
"registered users":"usuarios registrados",
"collections":"colectciones",
"released projects":"proyectos lanzados",

/* footer.hbs */

"up":"subir",

/* search.hbs */

"Inform Progress to community.":"Informa el progreso a la comunidad.",
"Upload your project to the platform.":"Sube tu proyecto a la plataforma.",
"find it":"encuéntralo",
"Add Collaborators to your projects.":"Agrega colaboradores a tu proyecto.",
"Share your app to the world.":"Comparte tu aplicación con el mundo.",

/* tabContent.hbs */

"HEAD":"ENCABEZADO",

/* Collections directory */

/* list.hbs */

"My Collections: adding":"Mis colecciones: agregando",

/* listItem.hbs */

"View":"Ver",

/* Dashboard directory */

/* addAdmin.hbs */

"Warning! you will NOT":"¡Atención! ¡NO podrás eliminar este tablero si agregas un administrador!",
"type name or username":"escribe nombre o nombre de usuario",
"cancel":"cancelar",

/* index.hbs */

"Open dashboard website":"Abrir el sitio del tablero",
"Share this Dashboard":"Compartir este tablero",
"Create Project":"Crear proyecto",

/* share.hbs */

"embed this dashboard":"insertar este tablero",
"Slider":"Slider",
"ANY STATUS":"CUALQUIER ESTADO",
"Add this dashboard to your website by coping this code below":"Agrega este tablero a tu sitio web copiando el código de abajo",
"By Name":"Por nombre",
"By Date":"Por fecha",
"Showcase":"Galería",
"Share Link":"Enlace para compartir",
"Preview":"Previsualización",
"embedded_code":"El código de inserción mostrará exactamente lo que está abajo",

/* users.hbs */

"Add admins":"Agregar administradores",

/* Footer directory */

/* footer.hbs */

"Export .CSV File":"Exportar archivo .CSV",
"Open":"Abrir",
"Close":"Cerrar",
"Dashboard Status":"Estado del tablero",
"off":"apagar",
"Edit Showcase":"Editar garlería",

/* Header directory */

/* header.hbs */

"Log out":"Salir",
"Log in":"Ingresar",

/* Profile directory */

/* card.hbs */

"[ Log in to reveal e-mail ]":"[ Ingresa para revelar el correo ]",

/* cardEdit.hbs */

"Edit Your Profile":"Edita tu perfil",
"all fields required":"todos los campos obligatorios",
"email only visible for logged in users":"correo sólo visible para usuarios ingresados",
"about_you":"Algo sobre tí",
"saving...":"guardando...",
"Save profile":"Guardar perfil",
"Profile saved, going back to business ...":"Perfil guardado, volviendo...",

/* listItem.hbs */

"Remove":"Eliminar",

/* profile.hbs */

"Contributions":"Colaboraciones",
"following":"Siguiendo",

/* Project directory */

/* card.hbs */

"Join":"Unirse",
"Follow":"Seguir",
"Leave":"Abandonar",
"Unfollow":"Dejar de seguir",
"Demo":"Demo",

/* edit.hbs */

"Project Title":"Título del proyecto",
"Import Project":"Importar proyecto",
"GitHub":"GitHub",
"LOADING":"CARGANDO",
"import":"importar",
"Tags":"Etiquetas (valores separados por comas)",
"Project URL Demo":"URL de demo del proyecto",
"Save":"Guardar",
"Cancel":"Cancelar",

/* full.hbs */

"leaving...":"abandondando...",
"joining...":"uniéndose...",
"unfollowing...":"dejando de seguir...",
"following...":"siguiendo...",
"Share this Project":"Comparte este proyecto",
"Managed by":"Administrado por",
"Contributors":"Colaboradores",
"Edit":"Editar",

/* share.hbs */

"embed this project":"inserta este proyecto",
"Add this project to your website by coping this code below":"Agrega este proyecto a tu sitio copiando el código de abajo",

/* templates directory */

"Access with":"Acceder con",

"embed/insert":"insertar"

};
<commit_msg>subo es.js por olvido<commit_after>/*eslint-disable */
module.exports = {
  "code": "es-ES",
  "time_format": "HH:mm",
  "date_locale": "es",
  "date_format": "DD\/MM\/YYYY",
  "datetime_format": "DD\/MM\/YYYY HH:mm",
  "colloquial_date_format": "ddd Do MMMM YYYY",

  /* Home Directory */

/* home.hbs */

  "Dashboards": "Tableros",
  "create now": "crear ahora",
  "ERROR":"ERROR",
  "Ideas for a": "Ideas para una",
  "hackathon":"hackatón",
  "Collections":"Colecciones",
  "Projects":"Proyectos",
  "People":"Personas",
  "team":"equipo",
  "partners":"socios",
  "The HackDash was born":"HackDash nació por accidente y por necesidad. Estábamos buscando una plataforma para hacer seguimiento de ideas durante los hackatones en la línea de Hacks/Hackers Media Party organizado por @HacksHackersBA en la que hackers y periodistas comparten ideas. Corrimos la voz de nuestra necesidad por Twitter y ese fue el contexto en el que nació HackDash. @blejman tuvo una idea y a @dzajdband le interesó implementar esa idea. Así que empezamos a crear la aplicación esperando llegar al Buenos Aires Media Party con algo que no fuera horrible. El día del hackatón de Media Party llegó acompañado de una grata sorpresa: No solamente HackDash le gustó a la gente, sino que también algunos programadores agregaron la mejora de HackDash como su proyecto de Hackatón. Después del Media Party nos dimos cuenta de que esta pequeña aplicación estaba cubriendo una necesidad real. Tres años después, el tablero se está convirtiendo en un estándar para hacer seguimiento de ideas innovadores alrededor del mundo.
Creá tu propio tablero, sé parte de una comunidad global.
",

/* collection.hbs */

/* counts.hbs */

"dashboards":"tableros",
"projects":"proyectos",
"registered users":"usuarios registrados",
"collections":"colectciones",
"released projects":"proyectos lanzados",

/* footer.hbs */

"up":"subir",

/* search.hbs */

"Inform Progress to community.":"Informa el progreso a la comunidad.",
"Upload your project to the platform.":"Sube tu proyecto a la plataforma.",
"find it":"encuéntralo",
"Add Collaborators to your projects.":"Agrega colaboradores a tu proyecto.",
"Share your app to the world.":"Comparte tu aplicación con el mundo.",

/* tabContent.hbs */

"HEAD":"ENCABEZADO",

/* Collections directory */

/* list.hbs */

"My Collections: adding":"Mis colecciones: agregando",

/* listItem.hbs */

"View":"Ver",

/* Dashboard directory */

/* addAdmin.hbs */

"Warning! you will NOT":"¡Atención! ¡NO podrás eliminar este tablero si agregas un administrador!",
"type name or username":"escribe nombre o nombre de usuario",
"cancel":"cancelar",

/* index.hbs */

"Open dashboard website":"Abrir el sitio del tablero",
"Share this Dashboard":"Compartir este tablero",
"Create Project":"Crear proyecto",

/* share.hbs */

"embed this dashboard":"insertar este tablero",
"Slider":"Slider",
"ANY STATUS":"CUALQUIER ESTADO",
"Add this dashboard to your website by coping this code below":"Agrega este tablero a tu sitio web copiando el código de abajo",
"By Name":"Por nombre",
"By Date":"Por fecha",
"Showcase":"Galería",
"Share Link":"Enlace para compartir",
"Preview":"Previsualización",
"embedded_code":"El código de inserción mostrará exactamente lo que está abajo",

/* users.hbs */

"Add admins":"Agregar administradores",

/* Footer directory */

/* footer.hbs */

"Export .CSV File":"Exportar archivo .CSV",
"Open":"Abrir",
"Close":"Cerrar",
"Dashboard Status":"Estado del tablero",
"off":"apagar",
"Edit Showcase":"Editar garlería",

/* Header directory */

/* header.hbs */

"Log out":"Salir",
"Log in":"Ingresar",

/* Profile directory */

/* card.hbs */

"[ Log in to reveal e-mail ]":"[ Ingresa para revelar el correo ]",

/* cardEdit.hbs */

"Edit Your Profile":"Edita tu perfil",
"all fields required":"todos los campos obligatorios",
"email only visible for logged in users":"correo sólo visible para usuarios ingresados",
"about_you":"Algo sobre tí",
"saving...":"guardando...",
"Save profile":"Guardar perfil",
"Profile saved, going back to business ...":"Perfil guardado, volviendo...",

/* listItem.hbs */

"Remove":"Eliminar",

/* profile.hbs */

"Contributions":"Colaboraciones",
"following":"Siguiendo",

/* Project directory */

/* card.hbs */

"Join":"Unirse",
"Follow":"Seguir",
"Leave":"Abandonar",
"Unfollow":"Dejar de seguir",
"Demo":"Demo",

/* edit.hbs */

"Project Title":"Título del proyecto",
"Import Project":"Importar proyecto",
"GitHub":"GitHub",
"LOADING":"CARGANDO",
"import":"importar",
"Tags":"Etiquetas (valores separados por comas)",
"Project URL Demo":"URL de demo del proyecto",
"Save":"Guardar",
"Cancel":"Cancelar",

/* full.hbs */

"leaving...":"abandondando...",
"joining...":"uniéndose...",
"unfollowing...":"dejando de seguir...",
"following...":"siguiendo...",
"Share this Project":"Comparte este proyecto",
"Managed by":"Administrado por",
"Contributors":"Colaboradores",
"Edit":"Editar",

/* share.hbs */

"embed this project":"inserta este proyecto",
"Add this project to your website by coping this code below":"Agrega este proyecto a tu sitio copiando el código de abajo",

/* templates directory */

"Access with":"Acceder con",
"embed/insert":"insertar"

/* ----------------------- js files ------------------------ */

/* Sharer.js */

"Hacking at":"Hackeando en",

/* Collection directory */

/* Collection.js */

"Collection of Hackathons Title":"Título de la colección de hackatones",
"brief description of this collection of hackathons":"breve descripción de esta colección de hackatones",

/* List.js */

" has been added to ":" ha sido agregado a ",
" has been removed from ":" ha sido eliminado de ",

/* Dashboard directory */

/* Dashboard.js */

"Hackathon Title":"Título de Hackatón",
"brief description of this hackathon":"breve descripción de esta hackatón",
"url to hackathon site":"url del sitio de la hackatón",

/* Share.js */

"Title":"Título",
"Description":"Descripción",
"Hackdash Logo":"Logo de Hackdash",
"Progress":"Progreso",
"Action Bar":"Barra de Acciones",

/* Footer directory */

/* index.js */

"This Dashboard is open: click to close":"Este tablero está abierto: clic para cerrar",
"This Dashboard is closed: click to reopen":"Este tablero está cerrado: clic para reabrir",
"off":"apagado",
"Edit Showcase":"Editar Galería",
"Save Showcase":"Guardar Galería",

/* Header directory */

/* index.js */

"Enter your keywords":"Ingresá las palabras clave",

/* Home directory */
/* index.js */

"5 to 10 chars, no spaces or special":"5 a 10 caracteres, sin espacios o caracteres especiales",
"Sorry, that one is in use. Try another one.":"Perdón, ya está en uso. Prueba otro.",

/* Profile directory */
/* CardEdit.js */

"Name is required":"El nombre es requerido",
"Email is required":"El correo es requerido",
"Invalid Email":"Correo inválido",

/* ListItem.js */

"Only the Owner can remove this Dashboard.":"Sólo el dueño puede eliminar este tablero.",
"Only Dashboards with ONE admin can be removed.":"Sólo tableros con UN admin pueden ser eliminador",
"Only Dashboards without Projects can be removed.":"Sólo tableros sin proyectos pueden ser eliminados.",
'This action will remove Dashboard ':'Esta acción eliminará el Tablero ',
'. Are you sure?':'. ¿Estás seguro?',
"Cannot Remove ":"No se puede eliminar el tablero ", // no se puede remover el tablero
" Dashboard":"", // dejar vacío en castellano

/* Projects directory */
/* Edit.js */

"Title is required":"El título es requerido",
"Description is required":"La descripción es requerida",
'Drop Image Here':'Suelta la imagen aquí',
'File is too big, 500 Kb is the max':'El archivo es muy grande, 500 Kb es el máximo',
'Only jpg, png and gif are allowed':'Sólo se permiten jpg, png y gif',

/* Full.js */

"This project is going to be deleted. Are you sure?":"Este proyecto será eliminado. ¿Estás seguro?",

/* Share.js */

'Picture':'Imagen',
'Title':'Título',
'Description':'Descripción',

};
<|endoftext|>


<commit_before>var socket = io(),
  inputs = document.getElementsByTagName('input'),
  inputsLength = inputs.length,
  allInputsEmpty = true,
  i

socket.on('response', function(response) {
  var conceptLine, apisLine, frameworksLine, finalLine,
    defaultLine = 'Marque suas dificuldades e verei como posso ajudar...',
    errorLine = 'Parece que não inventaram ainda isso aí...';

  if(typeof response.Concepts === 'undefined' && allInputsEmpty) {
    document.getElementById('response').innerHTML = defaultLine;
    return false;
  } else if(typeof response.Concepts === 'undefined' && !allInputsEmpty) {
    document.getElementById('response').innerHTML = errorLine;
    return false;
  }

  conceptLine = response.Concepts.length > 1 ? "Você pode usar um desses conceitos:
" : "O melhor conceito nesse caso é:
";
  apisLine = response.APIs.length > 1 ? "Você pode usar um desses tipos de API:
" : "O melhor tipo de API nesse caso é:
";
  frameworksLine = response.Frameworks.length > 1 ? "Você pode usar um desses tipos de frameworks:
" : "O melhor tipo de framework nesse caso é:
";
  finalLine = conceptLine + response.Concepts.join(' ou ') + '.

' + apisLine + response.APIs.join(' ou ') + '.

' + frameworksLine + response.Frameworks.join(' ou ') + '.

';

  document.getElementById('response').innerHTML = finalLine;
});

iterateInputs(function(input) {
  input.addEventListener('change', syncRequest);
});

function iterateInputs(callback) {
  for(i = 0; i < inputsLength; i++) {
    callback(inputs[i])
  }
}

function syncRequest() {
  var request = {};
  allInputsEmpty = true;

  iterateInputs(function(input) {
    if(input.checked) {
      allInputsEmpty = false;
      request[input.name] = request[input.name] || [];
      request[input.name].push(input.value);
    }
  });

  makeRequest(request);
}

function makeRequest(request) {
  socket.emit('change', request)
}
<commit_msg>Client.js refactor<commit_after>(function() {
  var socket = io(),
    inputs = document.getElementsByTagName('input'),
    responseElement = document.getElementById('response'),
    inputsLength = inputs.length,
    allInputsEmpty = true,
    defaultLine = 'Marque suas dificuldades e verei como posso ajudar...',
    errorLine = 'Parece que não inventaram ainda isso aí...',
    moreThanOneConceptLine = 'Você pode usar um desses conceitos:
',
    oneConceptLine = 'O melhor conceito nesse caso é:
',
    moreThanOneApiLine = 'Você pode usar um desses tipos de API:
',
    oneApiLine = 'O melhor tipo de API nesse caso é:
',
    moreThanOneFrameworkLine = 'Você pode usar um desses tipos de frameworks:
',
    oneFrameworkLine = 'O melhor tipo de framework nesse caso é:
',
    itemsSeparator = ' ou ',
    linesSeparator = '.

',
    i, conceptLine, apisLine, frameworksLine, finalLine;

  init();

  function init() {
    iterateInputs(function(input) {
      input.addEventListener('change', syncRequest);
    });

    socket.on('response', gotResponseFromServer);
  }

  function gotResponseFromServer(response) {
    if(noData(response) && (allInputsEmpty || !allInputsEmpty)) {
      responseElement.innerHTML = allInputsEmpty ? defaultLine : errorLine;
      return false;
    }

    conceptLine = response.Concepts.length > 1 ? moreThanOneConceptLine : oneConceptLine;
    apisLine = response.APIs.length > 1 ? moreThanOneApiLine : oneApiLine;
    frameworksLine = response.Frameworks.length > 1 ? moreThanOneFrameworkLine : oneFrameworkLine;
    finalLine = conceptLine + response.Concepts.join(itemsSeparator) +
                  linesSeparator + apisLine + response.APIs.join(itemsSeparator) +
                  linesSeparator + frameworksLine + response.Frameworks.join(itemsSeparator) +
                  linesSeparator;

    responseElement.innerHTML = finalLine;
  }

  function noData(response) {
    return typeof response.Concepts === 'undefined';
  }

  function iterateInputs(callback) {
    for(i = 0; i < inputsLength; i++) {
      callback(inputs[i])
    }
  }

  function syncRequest() {
    var request = {};
    allInputsEmpty = true;

    iterateInputs(function(input) {
      if(input.checked) {
        allInputsEmpty = false;
        request[input.name] = request[input.name] || [];
        request[input.name].push(input.value);
      }
    });

    makeRequest(request);
  }

  function makeRequest(request) {
    socket.emit('change', request)
  }
})();
<|endoftext|>


<commit_before>(function(){
  'use strict';

  angular.module('application', [
    'ui.router',
    'ngAnimate',

    // Foundation
    'foundation',
    'foundation.dynamicRouting',
    'foundation.dynamicRouting.animations',

    // Libraries
    'ngOrwell',

    // Fusion Seed App
    'fusionSeedApp.components',
    'fusionSeedApp.services',
    'fusionSeedApp.controllers',
    'ngOrwell'
  ])
    .constant('_', window._)
    .config(config)
    .run(run)
  ;

  config.$inject = ['$urlRouterProvider', '$httpProvider','$locationProvider', 'ApiBaseProvider', 'ConfigServiceProvider'];
  run.$inject = ['$log', 'ConfigService', 'ApiBase', 'QueryService','Orwell'];

  function config($urlProvider, $httpProvider, $locationProvider, ApiBaseProvider, ConfigServiceProvider) {
    $urlProvider.otherwise('/');
    $httpProvider.interceptors.push('SessionInjector');

    $locationProvider.html5Mode({
      enabled:true,
      requireBase: false
    });

    $locationProvider.hashPrefix('!');
    ApiBaseProvider.setEndpoint(ConfigServiceProvider.getFusionUrl());
  }

  function run($log, ConfigService, ApiBase, QueryService) {
    FastClick.attach(document.body);
  }
})();
<commit_msg>eslint<commit_after>(function(){
  'use strict';

  angular.module('application', [
    'ui.router',
    'ngAnimate',

    // Foundation
    'foundation',
    'foundation.dynamicRouting',
    'foundation.dynamicRouting.animations',

    // Libraries
    'ngOrwell',

    // Fusion Seed App
    'fusionSeedApp.components',
    'fusionSeedApp.services',
    'fusionSeedApp.controllers',
    'ngOrwell'
  ])
    .constant('_', window._)//eslint-disable-line
    .config(config)
    .run(run);

  config.$inject = ['$urlRouterProvider', '$httpProvider','$locationProvider', 'ApiBaseProvider', 'ConfigServiceProvider'];
  run.$inject = ['$document', '$log', 'ConfigService', 'ApiBase', 'QueryService'];

  function config($urlProvider, $httpProvider, $locationProvider, ApiBaseProvider, ConfigServiceProvider) {
    $urlProvider.otherwise('/');
    $httpProvider.interceptors.push('SessionInjector');

    $locationProvider.html5Mode({
      enabled:true,
      requireBase: false
    });

    $locationProvider.hashPrefix('!');
    ApiBaseProvider.setEndpoint(ConfigServiceProvider.getFusionUrl());
  }

  function run($document, $log, ConfigService, ApiBase, QueryService) {
    FastClick.attach($document.body);
  }
})();
<|endoftext|>


<commit_before>/** @preserve yerbajs - simple javascript data management
 *  @copyright 2014 Kevin von Flotow <vonflow@gmail.com>
 *  @license MIT License <http://opensource.org/licenses/MIT>
 */

(function () {
	var useToISOString = Date.prototype.toISOString ? 'toISOString' : 'getTime';

	// obj must be an array of items
	function yerba( obj ) {
		this._init( obj );
	}

	// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
	function genUID() {
		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( /[xy]/g, function( c ) {
			var r = Math.random() * 16 | 0, v = c == 'x' ? r : ( r & 0x3 | 0x8 );
			return v.toString( 16 );
		});
	}

	function isFunction( func ) {
		return Object.prototype.toString.call( func ) === '[object Function]';
	}

	function isString( str ) {
		return typeof( str ) === 'string' || str instanceof String;
	}

	function isNumber( num ) {
		return !isNaN( parseFloat( num ) ) && isFinite( num );
	}

	yerba.prototype = {
		_init: function ( obj ) {
			obj = obj || [];

			this.lastUpdate = 0;

			this.length = 0;

			for ( var i = 0, l = obj.length; i < l; i++ )
				this.add( obj[ i ] );
		},

		// used internally
		_setLastUpdate: function () {
			// fallback to getTime if toISOString is not available
			this.lastUpdate = new Date()[ useToISOString ]();
		},

		// used internally
		_generateUID: function () {
			var r = genUID();

			if ( !this.get( r ) )
				return r;
			else
				return this._generateUID();
		},

		each: function ( callback ) {
			for ( var i = 0, l = this.length; i < l; ++i )
				callback.call( this, i, this[ i ] );

			return this;
		},

		get: function ( dataID ) {
			for ( var i = 0, l = this.length; i < l; ++i )
				if ( this[ i ]._id === dataID )
					return this[ i ];

			// return false if nothing found
			return false;
		},

		find: function ( obj ) {
			/*
				obj: {
					query: { 'foo': 'bar' },
					opts: { caseSensitive: true },
					done: function ( err, item )
				}
			*/
			if ( !obj )
				return false;

			var c = {
				query: undefined,
				opts: {},
				limit: 0,
				done: function ( item ) {}
			};

			if ( isFunction( obj ) ) {
				c.done = arguments[ 0 ];
			} else {
				// assume it's an object
				c.query = typeof obj.query !== 'undefined' ? obj.query : c.query;
				c.opts = obj.opts || c.opts;
				c.limit = typeof obj.limit !== 'undefined' ? obj.limit : c.limit;
				c.done = obj.done || c.done;
			}

			c.opts.caseSensitive = typeof c.opts.caseSensitive !== 'undefined' ? c.opts.caseSensitive : true;

			var r = [];

			this.each( function ( yerbaKey, yerbaValue ) {
				var total = 0, successes = 0;

				if ( isString( c.query ) || isNumber( c.query ) ) {
					total = 1;

					// think it might need == instead of === but we'll see
					if ( typeof yerbaValue.value !== 'undefined' && yerbaValue.value == c.query )
						successes = 1;
				} else {
					for ( var key in c.query ) {
						// incoming long line...
						if ( yerbaValue.hasOwnProperty( key ) && ( ( c.opts.caseSensitive && yerbaValue[ key ] === c.query[ key ] ) || ( !c.opts.caseSensitive && yerbaValue[ key ].toLowerCase() === c.query[ key ].toLowerCase() ) ) )
							successes++;

						total++;
					}
				}

				if ( successes === total )
					r.push( yerbaValue );
			});

			return c.done( r );

			//return r;
		},

		shuffle: function () {
			var i = this.length,
				j, t;

			while ( --i > 0 ) {
				j = ( Math.random() * ( i + 1 ) ) | 0;
				t = this[ j ];
				this[ j ] = this[ i ];
				this[ i ] = t;
			}

			return this;
		},

		clone: function () {
			return this.slice( 0 );
		},

		random: function () {
			// clone and shuffle, then return random from that object instead - makes it much more 'random'
			var n = this.clone().shuffle();

			return n[ ( Math.random() * n.length ) | 0 ];
		},

		addOne: function ( data ) {
			if ( isString( data ) || isNumber( data ) ) {
				data = {
					value: data
				};
			}

			data._id = data._id || this._generateUID();

			if ( this.get( data._id ) ) {
				for ( var i = 0, l = this.length; i < l; ++i ) {
					if ( this[ i ]._id === data._id ) {
						this[ i ] = data;

						break;
					}
				}
			} else {
				this[ this.length++ ] = data;
			}

			// set this.lastUpdate
			this._setLastUpdate();
		},

		// allows you to add multiple items
		// like... data.addMultiple( [ 'one' ], [ 'two' ], [ 'three' ] );
		add: function () {
			for ( var i = 0, l = arguments.length; i < l; ++i )
				this.addOne( arguments[ i ] );
		},

		remove: function ( dataID ) {
			// need to rewrite
			var current = this.get( data._id );

			if ( current ) {
				delete current;

				this.length--;

				return true;
			}

			return false;
		},

		slice: function ( start, end ) {
			return new yerba( Array.prototype.slice.call( this, start, end ) );
		},

		// removes the last item and returns it
		pop: function () {
			return Array.prototype.pop.call( this );
		},

		// removes the first item and returns it
		shift: function () {
			return Array.prototype.shift.call( this );
		},

		flush: function () {
			// reset
			for ( var i = 0, l = this.length; i < l; ++i )
				delete this[ i ];

			this.length = 0;
		}
	};

	/* var test = new yerba(),
		testStrings = new yerba();

	test.add( { 'foo': 'bar' } );

	testStrings.add( 'testone', 'testtwo', { 'mixed': 'type' }, 3 );

	testStrings.find({
		query: '3',
		opts: { 'caseSensitive': false },
		done: function ( item ) {
			console.log( item );
		}
	}); */

	if ( typeof module !== 'undefined' && module.exports )
		// probably in nodejs
		module.exports = yerba;

	else if ( window )
		// in a web browser
		window.yerba = yerba;

	else
		// not in node or a browser... so just return it?
		return yerba;

})();
<commit_msg>improved setLastUpdate<commit_after>/** @preserve yerbajs - simple javascript data management
 *  @copyright 2014 Kevin von Flotow <vonflow@gmail.com>
 *  @license MIT License <http://opensource.org/licenses/MIT>
 */

(function () {
	var timeMethod = Date.prototype.toISOString ? 'toISOString' : 'getTime';

	// obj must be an array of items
	function yerba( obj ) {
		this._init( obj );
	}

	// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
	function genUID() {
		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( /[xy]/g, function( c ) {
			var r = Math.random() * 16 | 0, v = c == 'x' ? r : ( r & 0x3 | 0x8 );
			return v.toString( 16 );
		});
	}

	function isFunction( func ) {
		return Object.prototype.toString.call( func ) === '[object Function]';
	}

	function isString( str ) {
		return typeof( str ) === 'string' || str instanceof String;
	}

	function isNumber( num ) {
		return !isNaN( parseFloat( num ) ) && isFinite( num );
	}

	yerba.prototype = {
		_init: function ( obj ) {
			obj = obj || [];

			this.lastUpdate = 0;

			this.length = 0;

			for ( var i = 0, l = obj.length; i < l; i++ )
				this.add( obj[ i ] );
		},

		// used internally
		_setLastUpdate: function () {
			// fallback to getTime if toISOString is not available
			this.lastUpdate = new Date()[ timeMethod ]();
		},

		// used internally
		_generateUID: function () {
			var r = genUID();

			if ( !this.get( r ) )
				return r;
			else
				return this._generateUID();
		},

		each: function ( callback ) {
			for ( var i = 0, l = this.length; i < l; ++i )
				callback.call( this, i, this[ i ] );

			return this;
		},

		get: function ( dataID ) {
			for ( var i = 0, l = this.length; i < l; ++i )
				if ( this[ i ]._id === dataID )
					return this[ i ];

			// return false if nothing found
			return false;
		},

		find: function ( obj ) {
			/*
				obj: {
					query: { 'foo': 'bar' },
					opts: { caseSensitive: true },
					done: function ( err, item )
				}
			*/
			if ( !obj )
				return false;

			var c = {
				query: undefined,
				opts: {},
				limit: 0,
				done: function ( item ) {}
			};

			if ( isFunction( obj ) ) {
				c.done = arguments[ 0 ];
			} else {
				// assume it's an object
				c.query = typeof obj.query !== 'undefined' ? obj.query : c.query;
				c.opts = obj.opts || c.opts;
				c.limit = typeof obj.limit !== 'undefined' ? obj.limit : c.limit;
				c.done = obj.done || c.done;
			}

			c.opts.caseSensitive = typeof c.opts.caseSensitive !== 'undefined' ? c.opts.caseSensitive : true;

			var r = [];

			this.each( function ( yerbaKey, yerbaValue ) {
				var total = 0, successes = 0;

				if ( isString( c.query ) || isNumber( c.query ) ) {
					total = 1;

					// think it might need == instead of === but we'll see
					if ( typeof yerbaValue.value !== 'undefined' && yerbaValue.value == c.query )
						successes = 1;
				} else {
					for ( var key in c.query ) {
						// incoming long line...
						if ( yerbaValue.hasOwnProperty( key ) && ( ( c.opts.caseSensitive && yerbaValue[ key ] === c.query[ key ] ) || ( !c.opts.caseSensitive && yerbaValue[ key ].toLowerCase() === c.query[ key ].toLowerCase() ) ) )
							successes++;

						total++;
					}
				}

				if ( successes === total )
					r.push( yerbaValue );
			});

			return c.done( r );

			//return r;
		},

		shuffle: function () {
			var i = this.length,
				j, t;

			while ( --i > 0 ) {
				j = ( Math.random() * ( i + 1 ) ) | 0;
				t = this[ j ];
				this[ j ] = this[ i ];
				this[ i ] = t;
			}

			return this;
		},

		clone: function () {
			return this.slice( 0 );
		},

		random: function () {
			// clone and shuffle, then return random from that object instead - makes it much more 'random'
			var n = this.clone().shuffle();

			return n[ ( Math.random() * n.length ) | 0 ];
		},

		addOne: function ( data ) {
			if ( isString( data ) || isNumber( data ) ) {
				data = {
					value: data
				};
			}

			data._id = data._id || this._generateUID();

			if ( this.get( data._id ) ) {
				for ( var i = 0, l = this.length; i < l; ++i ) {
					if ( this[ i ]._id === data._id ) {
						this[ i ] = data;

						break;
					}
				}
			} else {
				this[ this.length++ ] = data;
			}

			// set this.lastUpdate
			this._setLastUpdate();
		},

		// allows you to add multiple items
		// like... data.addMultiple( [ 'one' ], [ 'two' ], [ 'three' ] );
		add: function () {
			for ( var i = 0, l = arguments.length; i < l; ++i )
				this.addOne( arguments[ i ] );
		},

		remove: function ( dataID ) {
			// need to rewrite
			var current = this.get( data._id );

			if ( current ) {
				delete current;

				this.length--;

				return true;
			}

			return false;
		},

		slice: function ( start, end ) {
			return new yerba( Array.prototype.slice.call( this, start, end ) );
		},

		// removes the last item and returns it
		pop: function () {
			return Array.prototype.pop.call( this );
		},

		// removes the first item and returns it
		shift: function () {
			return Array.prototype.shift.call( this );
		},

		flush: function () {
			// reset
			for ( var i = 0, l = this.length; i < l; ++i )
				delete this[ i ];

			this.length = 0;
		}
	};

	/* var test = new yerba(),
		testStrings = new yerba();

	test.add( { 'foo': 'bar' } );

	testStrings.add( 'testone', 'testtwo', { 'mixed': 'type' }, 3 );

	testStrings.find({
		query: '3',
		opts: { 'caseSensitive': false },
		done: function ( item ) {
			console.log( item );
		}
	}); */

	if ( typeof module !== 'undefined' && module.exports )
		// probably in nodejs
		module.exports = yerba;

	else if ( window )
		// in a web browser
		window.yerba = yerba;

	else
		// not in node or a browser... so just return it?
		return yerba;

})();
<|endoftext|>


<commit_before>H5.changePath = (function (Math, BezierCurvePath, LinePath, Vectors, CirclePath) {
    "use strict";

    function changePath(path, startX_or_x, startY_or_y, endX_or_radius, endY, p1_x, p1_y, p2_x, p2_y) {

        var curve = path.curve;
        if (curve instanceof LinePath) {
            var vector = Vectors.get(startX_or_x, startY_or_y, endX_or_radius, endY);
            var length = Vectors.magnitude(vector.x, vector.y);
            var unitVector = Vectors.normalize(vector.x, vector.y);

            curve.startX = startX_or_x;
            curve.startY = startY_or_y;
            curve.endX = endX_or_radius;
            curve.endY = endY;
            curve.length = length;
            curve.unitVectorX = unitVector.x;
            curve.unitVectorY = unitVector.y;
            curve.vectorX = vector.x;
            curve.vectorY = vector.y;

        } else if (curve instanceof CirclePath) {
            curve.x = startX_or_x;
            curve.y = startY_or_y;
            curve.radius = endX_or_radius

        } else if (curve instanceof BezierCurvePath) {
            curve.pointA_x = startX_or_x;
            curve.pointA_y = startY_or_y;
            curve.pointB_x = p1_x;
            curve.pointB_y = p1_y;
            curve.pointC_x = p2_x;
            curve.pointC_y = p2_y;
            curve.pointD_x = endX_or_radius;
            curve.pointD_y = endY;
        }
    }

    return changePath;
})(Math, H5.BezierCurvePath, H5.LinePath, H5.Vectors, H5.CirclePath);<commit_msg>optimized imports #core<commit_after>H5.changePath = (function (BezierCurvePath, LinePath, Vectors, CirclePath) {
    "use strict";

    function changePath(path, startX_or_x, startY_or_y, endX_or_radius, endY, p1_x, p1_y, p2_x, p2_y) {

        var curve = path.curve;
        if (curve instanceof LinePath) {
            var vector = Vectors.get(startX_or_x, startY_or_y, endX_or_radius, endY);
            var length = Vectors.magnitude(vector.x, vector.y);
            var unitVector = Vectors.normalize(vector.x, vector.y);

            curve.startX = startX_or_x;
            curve.startY = startY_or_y;
            curve.endX = endX_or_radius;
            curve.endY = endY;
            curve.length = length;
            curve.unitVectorX = unitVector.x;
            curve.unitVectorY = unitVector.y;
            curve.vectorX = vector.x;
            curve.vectorY = vector.y;

        } else if (curve instanceof CirclePath) {
            curve.x = startX_or_x;
            curve.y = startY_or_y;
            curve.radius = endX_or_radius

        } else if (curve instanceof BezierCurvePath) {
            curve.pointA_x = startX_or_x;
            curve.pointA_y = startY_or_y;
            curve.pointB_x = p1_x;
            curve.pointB_y = p1_y;
            curve.pointC_x = p2_x;
            curve.pointC_y = p2_y;
            curve.pointD_x = endX_or_radius;
            curve.pointD_y = endY;
        }
    }

    return changePath;
})(H5.BezierCurvePath, H5.LinePath, H5.Vectors, H5.CirclePath);<|endoftext|>


<commit_before>import _ from 'lodash';
import api from 'plugins/http/api';
import Vue from 'vue';

const vue = new Vue();

/**
 * Representation of a Site as returned by the API
 * @typedef {Object} Site
 * @property {number} id - Unique ID for this Site.
 * @property {string} name - The human-readable name for this Site.
 * @property {string} host - The domain name for this Site, eg. www.example.com/
 * @property {string} path - The path from the domain name for this site. Can be empty, or e.g. /sub/folder/names.
 * @property {string} created_at - The YYYY-MM-DD HH:MM:SS timestamp for when this site was created.
 * @property {string} updated_at - The YYYY-MM-DD HH:MM:SS timestamp for when this site was updated.
 * @property {string} created_by - The username of the user who created this Site.
 * @property {string} updated_by - The username of the user who last updated this Site.
 * @property {Page} homepage - Representation of the Page which is the home page for this site. May be null if data not loaded.
 */

/**
 * Maintains the state of the Site being edited, including available Layouts and site page hierarchy.
 * @example
Access site data from within a component.

 * this.$store.site.layouts; // get the layouts
 *
 * @namespace state/site
 *
 * @property {Array} pages - Array of Pages in the current Site.
 * @property {number} site - ID of the current Site.
 * @property {Site} currentSite - Representation of the currently selected Site in the editor, as returned by the API.
 * @property {Object} editPageModal - Configuration for the edit page modal dialog.
 * @property {boolean} editPageModal.visible - Whether the modal is visible or not.
 * @property {string} editPageModal.title - The page title field value for the modal.
 * @property {string} editPageModal.slug - The page slug field value for the modal.
 * @propoerty {number} editPageModal.id - The id of the page who's settings are being edited.
 * @property {boolean} editPageModal.editSlug - Whether or not to allow editing of the page slug in the modal (root pages cannot have their slug changed).
 */
const state = {
	pages: [],
	site: 1,
	layouts: [],
	maxDepth: 3,
	pageModal: {
		visible: false,
		parentId: null
	},
	editPageModal: {
		visible: false,
		title: '',
		slug: '',
		id: 0,
		editSlug: false
	}
};

const mutations = {

	/**
	 * Set the current site id stored in the store.
	 * @function
	 * @memberof state/site#
	 * @param {object} state
	 * @param {int} id The id of the site to set as current site.
	 */
	updateCurrentSiteID(state, id) {
		state.site = id;
	},

	setSite(state, pages) {
		state.pages = pages;
	},

	setLayouts(state, layouts) {
		state.layouts = layouts;
	},

	addPage(state, { parent, index, page, push = false }) {
		if(push) {
			parent.children.push(page);
		}
		else {
			parent.children.splice(index, 0, page);
		}
	},

	removePage(state, { parent, index }) {
		parent.children.splice(index, 1);
	},

	updatePageDepth(state, { page, depth }) {
		page.depth = depth;
	},

	setPageModalVisibility(state, visible) {
		state.pageModal.visible = visible;
	},

	setPageModalParent(state, pageId) {
		state.pageModal.parentId = pageId;
	},

	setEditPageModalVisibility(state, visible) {
		state.editPageModal.visible = visible;
	},

	setEditPageModalParent(state, pageId) {
		state.editPageModal.parentId = pageId;
	},

	/**
	 * Sets the title, slug and id for the edit page settings modal.
	 *
	 * @param state
	 * @param {Object} page The Page to use data from.
	 */
	setPageMeta(state, page) {
		state.editPageModal.title = page.title;
		state.editPageModal.slug = page.slug;
		state.editPageModal.id = page.id;
		state.editPageModal.editSlug = !!page.parent_id;
	},

	/**
	 * Mutates a page title, both in the pages list and in the editor if it is the page being edited.
	 *
	 * @param state
	 * @param {string} title - The new title.
	 */
	setPageTitleInPagesList(state, { id, title }) {
		const page = findPageById(state.pages, id);

		if(page) {
			page.title = title;
		}
	},

	/**
	 * Updates the slug for a page and also updates its path.
	 *
	 * @param state
	 * @param {string} slug - The new slug.
	 * @param {Object} page - Page data object.
	 *
	 */
	setPageSlugAndPathsInPagesList(state, { id, slug }) {
		const page = findPageById(state.pages, id);

		if(page) {
			page.slug = slug;

			updatePaths(
				page,
				page.path.substr(0, page.path.lastIndexOf(page.slug)) + slug
			);
		}
	},

	/**
	 * Updates the status for a page.
	 *
	 * @param state
	 * @param {string} arrayPath - The array path to the page in the page list
	 * eg. "0.0.1" for pagelist[0][0][1]
	 * @param {string} id - The page id.
	 * @param {string} status - The new status.
	 *
	 */
	setPageStatusInPagesList(state, { arrayPath, id, status }) {
		if(arrayPath) {
			getPage(state.pages, arrayPath).status = status;
		}
		else if(id) {
			findPageById(state.pages, id).status = status;
		}
	}
};

const actions = {

	fetchSite({ commit, state }) {
		api
			.get(`sites/${state.site}/tree?include=revision`)
			.then((response) => {
				commit('setSite', [response.data.data]);
			});
	},

	fetchLayouts({ commit }) {
		api
			.get('layouts/definitions')
			.then((response) => {
				commit('setLayouts', response.data.data)
			})
	},

	deletePage({ dispatch }, page) {
		api
			.delete(`pages/${page.id}`)
			.then(() => {
				dispatch('fetchSite');
			});
	},

	createPage({ dispatch }, page) {
		api
			.post('pages', {
				parent_id: page.route.parent_id,
				slug: page.route.slug,
				layout: {
					name: page.layout_name,
					version: page.layout_version,
				},
				title: page.title
			})
			.then((response) => {
				page.id = response.data.data.id;
				dispatch('updatePage', page);
			})
			.catch((error) => {
				vue.$notify({
					title: 'Page not added',
					message: 'Please ensure that there is not already a page with the same slug',
					type: 'error',
					duration: 0
				});
			})
	},

	updatePage({ dispatch }, page) {
		api
			.put(`pages/${page.id}/content`, {
				blocks: page.blocks
			})
			.then(() => {
				dispatch('fetchSite');
			})
	},

	/**
	 * Updates the Page meta details for the specified Page.
	 *
	 * @param dispatch
	 * @param page
	 * @returns {Promise<R>|Promise.<TResult>|Promise<R2|R1>}
	 */
	updatePageMeta({ dispatch }, page) {
		return api
			.put(`pages/${page.id}`, {
				title: page.title,
				options: {}
			})
			.then((response) => {
				dispatch('setPageTitleGlobally', response.data.data, { root: true });
			})
			.then(() => {
				if(state.editPageModal.editSlug) {
					return api.put(`pages/${page.id}/slug`, {
						slug: page.slug
					})
					.then((response) => {
						dispatch(
							'setPageSlugAndPathGlobally',
							{ ...response.data.data, id: page.id },
							{ root: true }
						);
					})
				}
			}).catch((err) => {
				throw(err.response);
			});
	},

	movePageApi({ dispatch }, move) {
		// If-Unmodified-Since
		api
			.patch(`sites/${state.site}/tree`, move)
			.then(() => {
				dispatch('fetchSite');
			});
	},

	movePage({ dispatch, commit, state }, { toPath, fromPath }) {
		const
			newPage = getPageInfo(toPath),
			oldPage = getPageInfo(fromPath),
			canDrop = newPage.parent.depth + getDepth(oldPage.data) <= state.maxDepth,
			newPos = Number.parseInt(toPath.substr(toPath.lastIndexOf('.') + 1, toPath.length));

		if(canDrop) {

			const page = _.cloneDeep(oldPage.data);

			// remove old page
			commit('removePage', oldPage);
			// update current and child page depths
			updateDepths(page, newPage.parent.depth + 1);
			// splice page in if page already exists in new position otherwise add it
			commit('addPage', { ...newPage, page, push: !newPage.data });

			const nextId = newPos + 1 < newPage.parent.children.length ?
				newPage.parent.children[newPos + 1].id : null;

			dispatch('movePageApi', {
				page_id: page.id,
				parent_id: newPage.parent.id,
				next_id: nextId //newPage.data && newPage.parent.children[newPos+1] ? newPage.parent.children[newPos+1].id : null
			});
		}
		else {
			vue.$snackbar.open({
				message: `
					Unable to drop page(s) here.
					The site structure must be less than ${state.maxDepth} levels deep.
				`
			});
		}
	},

	showPageModal({ commit }, { id }) {
		commit('setPageModalParent', id);
		commit('setPageModalVisibility', true);
	},

	hidePageModal({ commit }) {
		commit('setPageModalVisibility', false);
	},

	/**
	 * Display the edit page settings modal.
	 * @param commit
	 * @param {object} page - The Page object.
	 */
	showEditPageModal({ commit }, page) {
		commit('setPageMeta', page);
		commit('setEditPageModalVisibility', true);
	},

	hideEditPageModal({ commit }) {
		commit('setEditPageModalVisibility', false);
	}
};

const getters = {

	getPage: (state) => (path) => {
		if(path === null) {
			return {};
		}

		return getPage(state.pages, path);
	}
};

const
	getPageInfo = (path) => {
		return {
			data: getPage(state.pages, path),
			parent: getPage(state.pages, path.substr(0, path.lastIndexOf('.'))),
			index: Number.parseInt(path.substr(path.lastIndexOf('.') + 1, path.length))
		}
	},

	getPage = (page, fullPath) => {
		const path = Array.isArray(fullPath) ? fullPath : fullPath.split('.');

		for(var i = 0, length = path.length; page !== void 0 && i < length; i++) {
			page = i > 0 ? page.children[path[i]] : page[path[i]];
		}

		return i && i === length ? page : false;
	},

	getDepth = (page, fromPage = true) => {
		let depth = fromPage ? 1 : page.depth;

		while(page !== void 0 && page.children && page.children.length) {
			page = page.children[0];
			depth++;
		}

		return depth;
	},

	updateDepths = (currPage, depth) => {
		currPage.depth = depth;

		if(currPage.children && currPage.children.length) {
			currPage.children.forEach(page => updateDepths(page, depth + 1));
		}
	},

	/**
	 * Finds and returns the page with the specified id if it is present in the pages tree.
	 *
	 * @param {Array} pages - Array of Pages to search.
	 * @param {number} id - The page id to search for.
	 * @returns {Object|null}
	 */
	findPageById = (pages, id) => {
		for(var i in pages) {
			let page = pages[i];
			if(page.id === Number.parseInt(id)) {
				return page;
			}
			if(page.children && page.children.length) {
				let result = findPageById(page.children, id);
				if(result) {
					return result;
				}
			}
		}
		return null;
	},

	updatePaths = (currPage, path) => {
		currPage.path = path;

		if(currPage.children && currPage.children.length) {
			currPage.children.forEach(page => updatePaths(page, path + '/' + page.slug));
		}
	};

export default {
	namespaced: true,
	state,
	mutations,
	actions,
	getters
};
<commit_msg>Allow passing in either the page id or arrayPath, to get a page from the page list.<commit_after>import _ from 'lodash';
import api from 'plugins/http/api';
import Vue from 'vue';

const vue = new Vue();

/**
 * Representation of a Site as returned by the API
 * @typedef {Object} Site
 * @property {number} id - Unique ID for this Site.
 * @property {string} name - The human-readable name for this Site.
 * @property {string} host - The domain name for this Site, eg. www.example.com/
 * @property {string} path - The path from the domain name for this site. Can be empty, or e.g. /sub/folder/names.
 * @property {string} created_at - The YYYY-MM-DD HH:MM:SS timestamp for when this site was created.
 * @property {string} updated_at - The YYYY-MM-DD HH:MM:SS timestamp for when this site was updated.
 * @property {string} created_by - The username of the user who created this Site.
 * @property {string} updated_by - The username of the user who last updated this Site.
 * @property {Page} homepage - Representation of the Page which is the home page for this site. May be null if data not loaded.
 */

/**
 * Maintains the state of the Site being edited, including available Layouts and site page hierarchy.
 * @example
Access site data from within a component.

 * this.$store.site.layouts; // get the layouts
 *
 * @namespace state/site
 *
 * @property {Array} pages - Array of Pages in the current Site.
 * @property {number} site - ID of the current Site.
 * @property {Site} currentSite - Representation of the currently selected Site in the editor, as returned by the API.
 * @property {Object} editPageModal - Configuration for the edit page modal dialog.
 * @property {boolean} editPageModal.visible - Whether the modal is visible or not.
 * @property {string} editPageModal.title - The page title field value for the modal.
 * @property {string} editPageModal.slug - The page slug field value for the modal.
 * @propoerty {number} editPageModal.id - The id of the page who's settings are being edited.
 * @property {boolean} editPageModal.editSlug - Whether or not to allow editing of the page slug in the modal (root pages cannot have their slug changed).
 */
const state = {
	pages: [],
	site: 1,
	layouts: [],
	maxDepth: 3,
	pageModal: {
		visible: false,
		parentId: null
	},
	editPageModal: {
		visible: false,
		title: '',
		slug: '',
		id: 0,
		editSlug: false
	}
};

const mutations = {

	/**
	 * Set the current site id stored in the store.
	 * @function
	 * @memberof state/site#
	 * @param {object} state
	 * @param {int} id The id of the site to set as current site.
	 */
	updateCurrentSiteID(state, id) {
		state.site = id;
	},

	setSite(state, pages) {
		state.pages = pages;
	},

	setLayouts(state, layouts) {
		state.layouts = layouts;
	},

	addPage(state, { parent, index, page, push = false }) {
		if(push) {
			parent.children.push(page);
		}
		else {
			parent.children.splice(index, 0, page);
		}
	},

	removePage(state, { parent, index }) {
		parent.children.splice(index, 1);
	},

	updatePageDepth(state, { page, depth }) {
		page.depth = depth;
	},

	setPageModalVisibility(state, visible) {
		state.pageModal.visible = visible;
	},

	setPageModalParent(state, pageId) {
		state.pageModal.parentId = pageId;
	},

	setEditPageModalVisibility(state, visible) {
		state.editPageModal.visible = visible;
	},

	setEditPageModalParent(state, pageId) {
		state.editPageModal.parentId = pageId;
	},

	/**
	 * Sets the title, slug and id for the edit page settings modal.
	 *
	 * @param state
	 * @param {Object} page The Page to use data from.
	 */
	setPageMeta(state, page) {
		state.editPageModal.title = page.title;
		state.editPageModal.slug = page.slug;
		state.editPageModal.id = page.id;
		state.editPageModal.editSlug = !!page.parent_id;
	},

	/**
	 * Mutates a page title, both in the pages list and in the editor if it is the page being edited.
	 *
	 * @param state
	 * @param {string} title - The new title.
	 */
	setPageTitleInPagesList(state, { id, title }) {
		const page = findPageById(state.pages, id);

		if(page) {
			page.title = title;
		}
	},

	/**
	 * Updates the slug for a page and also updates its path.
	 *
	 * @param state
	 * @param {string} slug - The new slug.
	 * @param {Object} page - Page data object.
	 *
	 */
	setPageSlugAndPathsInPagesList(state, { id, slug }) {
		const page = findPageById(state.pages, id);

		if(page) {
			page.slug = slug;

			updatePaths(
				page,
				page.path.substr(0, page.path.lastIndexOf(page.slug)) + slug
			);
		}
	},

	/**
	 * Updates the status for a page.
	 *
	 * @param state
	 * @param {string} arrayPath - The array path to the page in the page list
	 * eg. "0.0.1" for pagelist[0][0][1]
	 * @param {string} id - The page id.
	 * @param {string} status - The new status.
	 *
	 */
	setPageStatusInPagesList(state, { arrayPath, id, status }) {
		if(arrayPath) {
			getPage(state.pages, arrayPath).status = status;
		}
		else if(id) {
			findPageById(state.pages, id).status = status;
		}
	}
};

const actions = {

	fetchSite({ commit, state }) {
		api
			.get(`sites/${state.site}/tree?include=revision`)
			.then((response) => {
				commit('setSite', [response.data.data]);
			});
	},

	fetchLayouts({ commit }) {
		api
			.get('layouts/definitions')
			.then((response) => {
				commit('setLayouts', response.data.data)
			})
	},

	deletePage({ dispatch }, page) {
		api
			.delete(`pages/${page.id}`)
			.then(() => {
				dispatch('fetchSite');
			});
	},

	createPage({ dispatch }, page) {
		api
			.post('pages', {
				parent_id: page.route.parent_id,
				slug: page.route.slug,
				layout: {
					name: page.layout_name,
					version: page.layout_version,
				},
				title: page.title
			})
			.then((response) => {
				page.id = response.data.data.id;
				dispatch('updatePage', page);
			})
			.catch((error) => {
				vue.$notify({
					title: 'Page not added',
					message: 'Please ensure that there is not already a page with the same slug',
					type: 'error',
					duration: 0
				});
			})
	},

	updatePage({ dispatch }, page) {
		api
			.put(`pages/${page.id}/content`, {
				blocks: page.blocks
			})
			.then(() => {
				dispatch('fetchSite');
			})
	},

	/**
	 * Updates the Page meta details for the specified Page.
	 *
	 * @param dispatch
	 * @param page
	 * @returns {Promise<R>|Promise.<TResult>|Promise<R2|R1>}
	 */
	updatePageMeta({ dispatch }, page) {
		return api
			.put(`pages/${page.id}`, {
				title: page.title,
				options: {}
			})
			.then((response) => {
				dispatch('setPageTitleGlobally', response.data.data, { root: true });
			})
			.then(() => {
				if(state.editPageModal.editSlug) {
					return api.put(`pages/${page.id}/slug`, {
						slug: page.slug
					})
					.then((response) => {
						dispatch(
							'setPageSlugAndPathGlobally',
							{ ...response.data.data, id: page.id },
							{ root: true }
						);
					})
				}
			}).catch((err) => {
				throw(err.response);
			});
	},

	movePageApi({ dispatch }, move) {
		// If-Unmodified-Since
		api
			.patch(`sites/${state.site}/tree`, move)
			.then(() => {
				dispatch('fetchSite');
			});
	},

	movePage({ dispatch, commit, state }, { toPath, fromPath }) {
		const
			newPage = getPageInfo(toPath),
			oldPage = getPageInfo(fromPath),
			canDrop = newPage.parent.depth + getDepth(oldPage.data) <= state.maxDepth,
			newPos = Number.parseInt(toPath.substr(toPath.lastIndexOf('.') + 1, toPath.length));

		if(canDrop) {

			const page = _.cloneDeep(oldPage.data);

			// remove old page
			commit('removePage', oldPage);
			// update current and child page depths
			updateDepths(page, newPage.parent.depth + 1);
			// splice page in if page already exists in new position otherwise add it
			commit('addPage', { ...newPage, page, push: !newPage.data });

			const nextId = newPos + 1 < newPage.parent.children.length ?
				newPage.parent.children[newPos + 1].id : null;

			dispatch('movePageApi', {
				page_id: page.id,
				parent_id: newPage.parent.id,
				next_id: nextId //newPage.data && newPage.parent.children[newPos+1] ? newPage.parent.children[newPos+1].id : null
			});
		}
		else {
			vue.$snackbar.open({
				message: `
					Unable to drop page(s) here.
					The site structure must be less than ${state.maxDepth} levels deep.
				`
			});
		}
	},

	showPageModal({ commit }, { id }) {
		commit('setPageModalParent', id);
		commit('setPageModalVisibility', true);
	},

	hidePageModal({ commit }) {
		commit('setPageModalVisibility', false);
	},

	/**
	 * Display the edit page settings modal.
	 * @param commit
	 * @param {object} page - The Page object.
	 */
	showEditPageModal({ commit }, page) {
		commit('setPageMeta', page);
		commit('setEditPageModalVisibility', true);
	},

	hideEditPageModal({ commit }) {
		commit('setEditPageModalVisibility', false);
	}
};

const getters = {

	getPage: (state) => ({ arrayPath, id }) => {
		if(arrayPath === null || id === null) {
			return null;
		}
		else if(arrayPath) {
			return getPage(state.pages, arrayPath);
		}

		return findPageById(state.pages, id);
	}
};

const
	getPageInfo = (path) => {
		return {
			data: getPage(state.pages, path),
			parent: getPage(state.pages, path.substr(0, path.lastIndexOf('.'))),
			index: Number.parseInt(path.substr(path.lastIndexOf('.') + 1, path.length))
		}
	},

	getPage = (page, fullPath) => {
		const path = Array.isArray(fullPath) ? fullPath : fullPath.split('.');

		for(var i = 0, length = path.length; page !== void 0 && i < length; i++) {
			page = i > 0 ? page.children[path[i]] : page[path[i]];
		}

		return i && i === length ? page : false;
	},

	getDepth = (page, fromPage = true) => {
		let depth = fromPage ? 1 : page.depth;

		while(page !== void 0 && page.children && page.children.length) {
			page = page.children[0];
			depth++;
		}

		return depth;
	},

	updateDepths = (currPage, depth) => {
		currPage.depth = depth;

		if(currPage.children && currPage.children.length) {
			currPage.children.forEach(page => updateDepths(page, depth + 1));
		}
	},

	/**
	 * Finds and returns the page with the specified id if it is present in the pages tree.
	 *
	 * @param {Array} pages - Array of Pages to search.
	 * @param {number} id - The page id to search for.
	 * @returns {Object|null}
	 */
	findPageById = (pages, id) => {
		for(var i in pages) {
			let page = pages[i];
			if(page.id === Number.parseInt(id)) {
				return page;
			}
			if(page.children && page.children.length) {
				let result = findPageById(page.children, id);
				if(result) {
					return result;
				}
			}
		}
		return null;
	},

	updatePaths = (currPage, path) => {
		currPage.path = path;

		if(currPage.children && currPage.children.length) {
			currPage.children.forEach(page => updatePaths(page, path + '/' + page.slug));
		}
	};

export default {
	namespaced: true,
	state,
	mutations,
	actions,
	getters
};
<|endoftext|>


<commit_before>//
// This is a basic router example
//
// This script runs a simple WAMP router on port 9000
// It illustrates:
// - how to filter out incoming connections,
// - how to declare a router-embedded RPC,
// - how to subscribe to router events.
//

WAMPRT_TRACE = true;

var Router = require('../lib/wamp.rt');
var program = require('commander');

program
    .option('-p, --port <port>', 'Server IP port', parseInt,9000);

function onRPCRegistered(uri) {
    console.log('onRPCRegistered RPC registered', uri);
}

function onRPCUnregistered(uri) {
    console.log('onRPCUnregistered RPC unregistered', uri);
}

function onPublish(topicUri, args) {
    console.log('onPublish Publish', topicUri, args);
}

//
// WebSocket server
//
var app = new Router(
    { port: program.port,
      // The router will select the appropriate protocol,
      // but we can still deny the connection
      // TODO: this should be the other way round, really ...
      handleProtocols: function(protocols,cb) {
          console.log(protocols);
          cb(true,protocols[0]);
          //cb(false);
      }
    }
);

app.on('RPCRegistered', onRPCRegistered);
app.on('RPCUnregistered', onRPCUnregistered);
app.on('Publish', onPublish);

app.regrpc('wamp.rt.foo', function(id,args) {
    console.log('called with ' + args);
    app.resrpc(id,["bar", "bar2"], {"key1": "bar1", "key2": "bar2"});
});
<commit_msg>Fix router-embedded RPC example<commit_after>//
// This is a basic router example
//
// This script runs a simple WAMP router on port 9000
// It illustrates:
// - how to filter out incoming connections,
// - how to declare a router-embedded RPC,
// - how to subscribe to router events.
//

WAMPRT_TRACE = true;

var Router = require('../lib/wamp.rt');
var program = require('commander');

program
    .option('-p, --port <port>', 'Server IP port', parseInt,9000);

function onRPCRegistered(uri) {
    console.log('onRPCRegistered RPC registered', uri);
}

function onRPCUnregistered(uri) {
    console.log('onRPCUnregistered RPC unregistered', uri);
}

function onPublish(topicUri, args) {
    console.log('onPublish Publish', topicUri, args);
}

//
// WebSocket server
//
var app = new Router(
    { port: program.port,
      // The router will select the appropriate protocol,
      // but we can still deny the connection
      // TODO: this should be the other way round, really ...
      handleProtocols: function(protocols,cb) {
          console.log(protocols);
          cb(true,protocols[0]);
          //cb(false);
      }
    }
);

app.on('RPCRegistered', onRPCRegistered);
app.on('RPCUnregistered', onRPCUnregistered);
app.on('Publish', onPublish);

app.regrpc('wamp.rt.foo', function(id,args) {
    console.log('called with ' + args);
    app.resrpc(id,null,[["bar", "bar2"], {"key1": "bar1", "key2": "bar2"}]);
});
<|endoftext|>


<commit_before>/**
 *
 * Anagram.js Made By Dragotic. (Original idea by jd)
 * Starts a game of anagram, utilizes origindb to award bucks.
 *
 */

'use strict';

function shuffle(string) {
	let shuffledString = '';
	let pokemonName = string.split('');
	for (var i = pokemonName.length; i > 0; i--) {
		let index = Math.floor(Math.random() * i);
		shuffledString += pokemonName[index];
		pokemonName.splice(index, 1);
	}
	return shuffledString;
}

function getPoke() {
	let pokemonNum = Math.floor(Math.random() * 720);
	let pokemon;

	for (let i in Tools.data.Pokedex) {
		let poke = Tools.getTemplate(i);
		if (poke.num === pokemonNum) {
			pokemon = poke.id;
			break;
		}
	}
	return pokemon;
}

function endAnagram(room) {
	delete room.anagram;
	delete room.anagramWord;
	delete room.shuffledAnagramWord;
}

exports.commands = {
	anagram: {
		start: 'create',
		new: 'create',
		create: function (target, room, user) {
			if (!this.can('makechatroom')) return false;
			if (!this.canTalk()) return this.errorReply('You cannot do this while unable to talk.');
			if (room.chatRoomData.anagramWord) return this.add('|raw|Pokémon: ' + room.chatRoomData.shuffledAnagramWord + '');
			if (!room.chatRoomData.anagram) room.chatRoomData.anagram = true;
			if (!room.chatRoomData.anagramWord) room.chatRoomData.anagramWord = getPoke();
			if (!room.chatRoomData.shuffledAnagramWord) room.chatRoomData.shuffledAnagramWord = shuffle(room.chatRoomData.anagramWord);
			this.add('|raw|
A game of anagram has started, use /guessanagram [pokémon] or /gan [pokémon] to guess the Pokémon.
');
			this.add('|raw|Pokémon: ' + room.chatRoomData.shuffledAnagramWord + '');

			room.chat = function (user, message, connection) {
				if (room.chatRoomData.anagramWord === toId(message)) message = '/gan ' + message;
				message = CommandParser.parse(message, this, user, connection);

				if (message && message !== true) {
					this.add('|c|' + user.getIdentity(this.id) + '|' + message);
				}
				this.update();
			}

			return this.privateModCommand("(A game of anagram was started by " + user.name + ".)");
		},
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('makechatroom')) return false;
			if (!this.canTalk()) return this.errorReply('You cannot do this while unable to talk.');
			if (!room.chatRoomData.anagram) return this.errorReply('There is no game of anagram running in this room.');
			if (room.chatRoomData.anagram) {
				endAnagram(room.chatRoomData);
			}

			this.add('(The game of anagram was ended.)');
			return	this.privateModCommand('(The game of anagram was ended by ' + user.name + '.)');
		},
		'': function (target, room, user) {
			this.parse('/anagram create');
		},
	},
	gan: 'guessanagram',
	guessanagram: function (target, room, user) {
		if (!this.canTalk()) return this.errorReply('You cannot do this while unable to talk.');
		if (!room.chatRoomData.anagram) return this.errorReply('There is no game of anagram running in this room.');

		let word = room.chatRoomData.anagramWord;

		if (toId(target) === word) {
			endAnagram(room.chatRoomData);
			Db('money').set(user.userid, Db('money').get(user.userid, 0) + 1);
			if (Users.get(user.userid)) Users(user.userid).popup('You have been awarded with a buck for winning the game of anagram.');
			return this.add('|raw|
The Pokémon has been guessed. Congratulations to ' + user.name + ' for guessing correctly!
');
		} else {
			return this.sendReply('Incorrect guess!');
		}
	},
};<commit_msg>Minor changes.<commit_after>/**
 *
 * Anagram.js Made By Dragotic. (Original idea by jd)
 * Starts a game of anagram, utilizes origindb to award bucks.
 *
 */

'use strict';

function shuffle(string) {
	let shuffledString = '';
	let pokemonName = string.split('');
	for (let i = pokemonName.length; i > 0; i--) {
		let index = Math.floor(Math.random() * i);
		shuffledString += pokemonName[index];
		pokemonName.splice(index, 1);
	}
	return shuffledString;
}

function getPoke() {
	let pokemonNum = Math.floor(Math.random() * 720);
	let pokemon;

	for (let i in Tools.data.Pokedex) {
		let poke = Tools.getTemplate(i);
		if (poke.num === pokemonNum) {
			pokemon = poke.id;
			break;
		}
	}
	return pokemon;
}

function endAnagram(room) {
	delete room.anagram;
	delete room.anagramWord;
	delete room.shuffledAnagramWord;
	delete room.anagramRounds;
	delete room.anagramMaxRounds;
}

exports.commands = {
	anagram: {
		start: 'create',
		new: 'create',
		create: function (target, room, user) {
			if (!this.can('makechatroom')) return false;
			if (!this.canTalk()) return this.errorReply('You cannot do this while unable to talk.');
			if (!target && !room.anagram) target = 1;
			if (isNaN(target)) return this.errorReply('The rounds should be in numbers!');
			if (!room.anagram) room.anagram = true;
			if (!room.anagramWord) room.anagramWord = getPoke();
			if (room.shuffledAnagramWord && room.anagramRounds) return this.add('|raw|Pokémon: ' + room.shuffledAnagramWord + '');
			if (!room.shuffledAnagramWord) room.shuffledAnagramWord = shuffle(room.anagramWord);
			if (room.anagramWord === room.shuffledAnagramWord) room.shuffledAnagramWord = shuffle(room.anagramWord);
			if (!room.anagramRounds) room.anagramRounds = 1;
			if (!room.anagramMaxRounds) room.anagramMaxRounds = Number(target);
			if (room.anagramRounds === 1) {
				this.add('|raw|
A game of anagram for ' + room.anagramMaxRounds + ' round(s) has started, use /guessanagram [pokémon] or /gan [pokémon] to guess the Pokémon.
');
				this.add('|raw|Pokémon: ' + room.shuffledAnagramWord + '');
				this.privateModCommand("(A game of anagram was started by " + user.name + ".)");
			} else {
				this.add('|raw|
Anagrams!, Round: ' + room.anagramRounds + ', the pokémon is: ' + room.shuffledAnagramWord + '.');
			}

			room.chat = function (user, message, connection) {
				if (room.anagramWord === toId(message)) message = '/gan ' + message;
				message = CommandParser.parse(message, this, user, connection);

				if (message && message !== true) {
					this.add('|c|' + user.getIdentity(this.id) + '|' + message);
				}
				this.update();
			};
		},
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('makechatroom')) return false;
			if (!this.canTalk()) return this.errorReply('You cannot do this while unable to talk.');
			if (!room.anagram) return this.errorReply('There is no game of anagram running in this room.');
			if (room.anagram) {
				endAnagram(room);
			}

			this.add('(The game of anagram was ended.)');
			return	this.privateModCommand('(The game of anagram was ended by ' + user.name + '.)');
		},
		'': function (target, room, user) {
			this.parse('/anagram create');
		},
	},
	gan: 'guessanagram',
	guessanagram: function (target, room, user) {
		if (!this.canTalk()) return this.errorReply('You cannot do this while unable to talk.');
		if (!room.anagram) return this.errorReply('There is no game of anagram running in this room.');

		let word = room.anagramWord;

		if (toId(target) === word) {
			Db('money').set(user.userid, Db('money').get(user.userid, 0) + 1);
			if (Users.get(user.userid)) Users(user.userid).popup('You have been awarded with a buck for winning the game of anagram.');
			room.anagramRounds++;
			this.add('|raw|
The Pokémon was ' + room.anagramWord + '. Congratulations to ' + user.name + ' for guessing correctly!
');
			if (room.anagram && room.anagramRounds < room.anagramMaxRounds) {
				delete room.anagramWord;
				delete room.shuffledAnagramWord;
				this.parse('/anagram create');
			} else {
				endAnagram(room);
			}
		} else {
			return this.sendReply('Incorrect guess!');
		}
	},
	anagramhelp: ['Commands for anagram are:',
			'- /anagram - Creates a game of anagram for 1 round. Requires ~',
			'- /anagram create [rounds] - Creates games of anagram for specified rounds. Requires ~',
			'- /anagram end - Ends the game of anagram. Requires ~',
			'- /gan [pokémon] - Guesses the pokemon in the anagram.',
			'- User can also type in chat to guess the pokémon in the anagram.'],
};
<|endoftext|>


<commit_before>export default {
  init() {
    // JavaScript to be fired on all pages
    const navItem = document.querySelectorAll('.menu-item-has-children')
    function showMenu(){
      this.classList.add('trigger-enter')
      setTimeout(() => this.classList.contains('trigger-enter') && this.classList.add('trigger-enter-active'), 150)
    }
    function hideMenu(){
      this.classList.remove('trigger-enter', 'trigger-enter-active')
    }

    navItem.forEach(item => item.addEventListener('mouseenter', showMenu))
    navItem.forEach(item => item.addEventListener('mouseleave', hideMenu))
  },
  finalize() {
    // JavaScript to be fired on all pages, after page specific JS is fired
  },
};
<commit_msg>started on touch detection<commit_after>export default {
  init() {
    // JavaScript to be fired on all pages
    const navItem = document.querySelectorAll('.menu-item-has-children')
    function showMenu(){
      if (is_touch_device()){
        // e.preventDefault()
      }
      this.classList.add('trigger-enter')
      setTimeout(() => this.classList.contains('trigger-enter') && this.classList.add('trigger-enter-active'), 150)
    }
    function hideMenu(){
      this.classList.remove('trigger-enter', 'trigger-enter-active')
    }

    navItem.forEach(item => item.addEventListener('mouseenter', showMenu))
    navItem.forEach(item => item.addEventListener('mouseleave', hideMenu))

    function is_touch_device() {
      // works on most browsers, IE10/11 and Surface
      if ('ontouchstart' in window || navigator.maxTouchPoints) {
        return true
      } else {
        return false
      }
    }

    console.log(is_touch_device())
  },
  finalize() {
    // JavaScript to be fired on all pages, after page specific JS is fired
  },
};
<|endoftext|>