<commit_before>// ==UserScript==
// @name        Stack Exchange Pronoun Assistant
// @namespace   https://github.com/Glorfindel83/
// @description Displays users' pronouns (mentioned in their profiles) in chat
// @author      Glorfindel
// @contributor ArtOfCode
// @contributor wizzwizz4
// @updateURL   https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/pronoun-assistant/pronoun-assistant.user.js
// @downloadURL https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/pronoun-assistant/pronoun-assistant.user.js
// @supportURL  https://stackapps.com/questions/8440/pronoun-assistant
// @version     2.1
// @match       *://chat.stackexchange.com/rooms/*
// @match       *://chat.stackoverflow.com/rooms/*
// @match       *://chat.meta.stackexchange.com/rooms/*
// @match       *://*.stackexchange.com/questions/*
// @match       *://*.stackoverflow.com/questions/*
// @match       *://*.superuser.com/questions/*
// @match       *://*.serverfault.com/questions/*
// @match       *://*.askubuntu.com/questions/*
// @match       *://*.stackapps.com/questions/*
// @match       *://*.mathoverflow.net/questions/*
// @exclude     *://*.stackexchange.com/questions/ask
// @exclude     *://*.stackoverflow.com/questions/ask
// @exclude     *://*.superuser.com/questions/ask
// @exclude     *://*.serverfault.com/questions/ask
// @exclude     *://*.askubuntu.com/questions/ask
// @exclude     *://*.stackapps.com/questions/ask
// @exclude     *://*.mathoverflow.net/questions/ask
// @grant       GM_addStyle
// @require     https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @require     https://gist.github.com/raw/2625891/waitForKeyElements.js
// @require     https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js
// ==/UserScript==

/* global $, waitForKeyElements */

GM_addStyle(`
.tiny-signature {
  display: inline-flex;
  flex-direction: row-reverse;
  align-items: center;
  width: 100%;
}

.username {
  height: unset !important;
}

.pronouns {
  color: #777;
  padding-left: 5px;
}
`)

// List of pronouns to look out for
let allPronouns = [
  "him", "his",
  "she", "her?", // that covers 'he' as well
  "they", "them", "their",
  "ze", "hir", "zir",
  "xey", "xem", "xyr"
].join("|");
let pronounListRegex = new RegExp('\\W*((' + allPronouns + ')(\\s*/\\s*(' + allPronouns + '))+)\\W*', 'i');
let myPronounIsRegex = /(https?:\/\/)?(my\.)?pronoun\.is\/[\w/]+/i;
let explicitPronounsRegex = /pronouns:\s*([^.\n)]*)(\.|\n|\)|$)/im;

// Keys:   user IDs
// Values: either a list of DOM elements (specifically, the anchors to chat profiles)
//         or a string with pronouns.
var users = {};
// TODO: cache in local storage

// Adds pronoun information to a user's 'signature' in chat.
function showPronounsForChat(element, pronouns) {
  if (pronouns == "") {
    return;
  }
  // the element might contain both a tiny and a full signature
  element.find("div.username").each(function (index, usernameElement) {
    usernameElement.innerHTML = '' + usernameElement.innerHTML + '
'
      + '' + pronouns.replace(/(<([^>]+)>)/ig, "") + '';
  });
}

//
function showPronouns(element, pronouns) {
  if (pronouns == "") {
    return;
  }
  element.after($('' + pronouns.replace(/(<([^>]+)>)/ig, "") + ''));
}

// Check the user's 'about' in their chat profile for pronoun indicators
function getPronouns(aboutMe) {
  // Link to Pronoun Island, e.g.
  // http://my.pronoun.is/she
  var match = myPronounIsRegex.exec(aboutMe);
  if (match != null) {
    return match[0];
  }

  // Explicit pronouns specification, e.g.
  // Pronouns: he/him.
  // The end is indicated by a dot, a newline, or simply the end of the text.
  match = explicitPronounsRegex.exec(aboutMe);
  if (match != null) {
    return match[1];
  }

  // Check for pronouns (see above for the list) joined by a forward slash, e.g.
  // she/her
  // (he/him/his)
  // they / them
  match = pronounListRegex.exec(aboutMe);
  if (match != null) {
    return match[1];
  }

  // No pronouns found
  return "";
}

// Converts the hostname (e.g. "meta.stackexchange.com") into the site parameter for the API ("meta")
function getSite(hostname) {
  if (hostname.contains(".stackexchange.com")) {
    return hostname.split(".stackexchange.com")[0];
  } else if (hostname.contains(".com")) {
    return hostname.split(".com")[0];
  } else {
    // MathOverflow, MathOverflow Meta
    return hostname;
  }
}

// Chat signatures
waitForKeyElements("a.signature", function(jNode) {
  let userID = jNode.attr("href").split("/users/")[1];
  if (!users.hasOwnProperty(userID)) {
    users[userID] = [];
    users[userID].push(jNode);
    // Read chat profile
    $.get("https://" + location.host + "/users/thumbs/" + userID + "?showUsage=true", function(data) {
      let pronouns = data.user_message == null ? "" : getPronouns(data.user_message);
      users[userID].forEach(function (element) {
        showPronounsForChat(element, pronouns);
      });
      users[userID] = pronouns;
    });
  } else if (typeof users[userID] == 'string') {
    // We already have the pronouns, we can just use them.
    showPronounsForChat(jNode, users[userID]);
  } else {
    // User appearing multiple times, their profile is already being fetched.
    users[userID].push(jNode);
  }
});

// Q&A site user cards & comment usernames
(async () => {
  const sleep = async (ms) => {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  };

  const userIds = [];
  const profiles = {};

  const $userElements = $("div.user-details > a, a.comment-user");

  // Grab all the user IDs out of a page first. We'll go back over them later to add pronouns in.
  $userElements.each(function() {
    const link = $(this).attr("href");
    if (!link.startsWith("/users/")) {
      // not a user card, but a community wiki post
      return;
    }
    const userId = parseInt(link.split("/users/")[1]);
    if (userIds.indexOf(userId) === -1) {
      userIds.push(userId);
    }
  });

  // Split the list into 100-item pages and grab profiles for each page.
  // This works because splice modifies the userIds array in-place, removing elements from the front and
  // returning them into the `page` variable. When we've used them all, the array will be empty.
  while (userIds.length > 0) {
    const page = userIds.splice(0, 100);

    const resp = await fetch("https://api.stackexchange.com/2.2/users/" + page.join(';') + "?site=" + getSite(location.hostname)
                             + "&key=L8n*CvRX3ZsedRQicxnjIA((&filter=!23IboywNfWUzv_nydJbn*&pagesize=100");
    const data = await resp.json();
    if (typeof data.items != 'undefined') {
      data.items.forEach(i => {
        profiles[i.user_id] = i.about_me;
      });
    }

    if (data.backoff) {
      // Respect backoffs, not just pronouns.
      await sleep(data.backoff * 1000);
    }
  }

  $userElements.each(function() {
    const link = $(this).attr("href");
    const userId = parseInt(link.split("/users/")[1]);
    if (!users.hasOwnProperty(userId)) {
      // No pronouns calculated yet, we need to calculate and store them.
      users[userId] = getPronouns(profiles[userId]);
      showPronouns($(this), users[userId]);
    }
    else {
      // We already have the pronouns, we can just use them.
      showPronouns($(this), users[userId]);
    }
  });
})();
<commit_msg>Cache user data on Q&A sites<commit_after>// ==UserScript==
// @name        Stack Exchange Pronoun Assistant
// @namespace   https://github.com/Glorfindel83/
// @description Displays users' pronouns (mentioned in their profiles) in chat
// @author      Glorfindel
// @contributor ArtOfCode
// @contributor wizzwizz4
// @updateURL   https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/pronoun-assistant/pronoun-assistant.user.js
// @downloadURL https://raw.githubusercontent.com/Glorfindel83/SE-Userscripts/master/pronoun-assistant/pronoun-assistant.user.js
// @supportURL  https://stackapps.com/questions/8440/pronoun-assistant
// @version     2.1
// @match       *://chat.stackexchange.com/rooms/*
// @match       *://chat.stackoverflow.com/rooms/*
// @match       *://chat.meta.stackexchange.com/rooms/*
// @match       *://*.stackexchange.com/questions/*
// @match       *://*.stackoverflow.com/questions/*
// @match       *://*.superuser.com/questions/*
// @match       *://*.serverfault.com/questions/*
// @match       *://*.askubuntu.com/questions/*
// @match       *://*.stackapps.com/questions/*
// @match       *://*.mathoverflow.net/questions/*
// @exclude     *://*.stackexchange.com/questions/ask
// @exclude     *://*.stackoverflow.com/questions/ask
// @exclude     *://*.superuser.com/questions/ask
// @exclude     *://*.serverfault.com/questions/ask
// @exclude     *://*.askubuntu.com/questions/ask
// @exclude     *://*.stackapps.com/questions/ask
// @exclude     *://*.mathoverflow.net/questions/ask
// @grant       GM_addStyle
// @require     https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @require     https://gist.github.com/raw/2625891/waitForKeyElements.js
// @require     https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js
// ==/UserScript==

/* global $, waitForKeyElements */

GM_addStyle(`
.tiny-signature {
  display: inline-flex;
  flex-direction: row-reverse;
  align-items: center;
  width: 100%;
}

.username {
  height: unset !important;
}

.pronouns {
  color: #777;
  padding-left: 5px;
}
`)

// List of pronouns to look out for
let allPronouns = [
  "him", "his",
  "she", "her?", // that covers 'he' as well
  "they", "them", "their",
  "ze", "hir", "zir",
  "xey", "xem", "xyr"
].join("|");
let pronounListRegex = new RegExp('\\W*((' + allPronouns + ')(\\s*/\\s*(' + allPronouns + '))+)\\W*', 'i');
let myPronounIsRegex = /(https?:\/\/)?(my\.)?pronoun\.is\/[\w/]+/i;
let explicitPronounsRegex = /pronouns:\s*([^.\n)]*)(\.|\n|\)|$)/im;

// Keys:   user IDs
// Values: either a list of DOM elements (specifically, the anchors to chat profiles)
//         or a string with pronouns.
var users = {};

// If we're on a Q&A site, also cache all changes to the `users` object to save on API calls
if (location.hostname.indexOf("chat") === -1) {
  const localStorageData = localStorage.stackPronounAssistant_users;
  const cached = localStorageData ? JSON.parse(localStorage.stackPronounAssistant_users) : {};
  const userData = Object.keys(cached).forEach(k => userData[parseInt(k, 10)] = cached[k]);
  users = new Proxy(userData, {
    get: (obj, prop) => userData,
    set: (obj, prop, value) => {
      obj[prop] = value;
      localStorage.stackPronounAssistant_users = JSON.stringify(userData);
  });
}

// Adds pronoun information to a user's 'signature' in chat.
function showPronounsForChat(element, pronouns) {
  if (pronouns == "") {
    return;
  }
  // the element might contain both a tiny and a full signature
  element.find("div.username").each(function (index, usernameElement) {
    usernameElement.innerHTML = '' + usernameElement.innerHTML + '
'
      + '' + pronouns.replace(/(<([^>]+)>)/ig, "") + '';
  });
}

function showPronouns(element, pronouns) {
  if (pronouns == "") {
    return;
  }
  element.after($('' + pronouns.replace(/(<([^>]+)>)/ig, "") + ''));
}

// Check the user's 'about' in their chat profile for pronoun indicators
function getPronouns(aboutMe) {
  // Link to Pronoun Island, e.g.
  // http://my.pronoun.is/she
  var match = myPronounIsRegex.exec(aboutMe);
  if (match != null) {
    return match[0];
  }

  // Explicit pronouns specification, e.g.
  // Pronouns: he/him.
  // The end is indicated by a dot, a newline, or simply the end of the text.
  match = explicitPronounsRegex.exec(aboutMe);
  if (match != null) {
    return match[1];
  }

  // Check for pronouns (see above for the list) joined by a forward slash, e.g.
  // she/her
  // (he/him/his)
  // they / them
  match = pronounListRegex.exec(aboutMe);
  if (match != null) {
    return match[1];
  }

  // No pronouns found
  return "";
}

// Chat signatures
waitForKeyElements("a.signature", function(jNode) {
  let userID = jNode.attr("href").split("/users/")[1];
  if (!users.hasOwnProperty(userID)) {
    users[userID] = [];
    users[userID].push(jNode);
    // Read chat profile
    $.get("https://" + location.host + "/users/thumbs/" + userID + "?showUsage=true", function(data) {
      let pronouns = data.user_message == null ? "" : getPronouns(data.user_message);
      users[userID].forEach(function (element) {
        showPronounsForChat(element, pronouns);
      });
      users[userID] = pronouns;
    });
  } else if (typeof users[userID] == 'string') {
    // We already have the pronouns, we can just use them.
    showPronounsForChat(jNode, users[userID]);
  } else {
    // User appearing multiple times, their profile is already being fetched.
    users[userID].push(jNode);
  }
});

// Q&A site user cards & comment usernames
(async () => {
  const sleep = async (ms) => {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  };

  const userIds = [];
  const profiles = {};

  const $userElements = $("div.user-details > a, a.comment-user");

  // Grab all the user IDs out of a page first. We'll go back over them later to add pronouns in.
  $userElements.each(function() {
    const link = $(this).attr("href");
    if (!link.startsWith("/users/")) {
      // not a user card, but a community wiki post
      return;
    }
    const userId = parseInt(link.split("/users/")[1]);
    if (userIds.indexOf(userId) === -1) {
      userIds.push(userId);
    }
  });

  // Split the list into 100-item pages and grab profiles for each page.
  // This works because splice modifies the userIds array in-place, removing elements from the front and
  // returning them into the `page` variable. When we've used them all, the array will be empty.
  while (userIds.length > 0) {
    const page = userIds.splice(0, 100);

    const resp = await fetch("https://api.stackexchange.com/2.2/users/" + page.join(';') + "?site=" + location.hostname
                             + "&key=L8n*CvRX3ZsedRQicxnjIA((&filter=!23IboywNfWUzv_nydJbn*&pagesize=100");
    const data = await resp.json();
    if (typeof data.items !== 'undefined') {
      data.items.forEach(i => {
        profiles[i.user_id] = i.about_me;
      });
    }

    if (data.backoff) {
      // Respect backoffs, not just pronouns.
      await sleep(data.backoff * 1000);
    }
  }

  $userElements.each(function() {
    const link = $(this).attr("href");
    const userId = parseInt(link.split("/users/")[1]);
    if (!users.hasOwnProperty(userId)) {
      // No pronouns calculated yet, we need to calculate and store them.
      users[userId] = getPronouns(profiles[userId]);
      showPronouns($(this), users[userId]);
    }
    else {
      // We already have the pronouns, we can just use them.
      showPronouns($(this), users[userId]);
    }
  });
})();
<|endoftext|>


<commit_before>const assert = require("assert");

const JSONRPC = {};
JSONRPC.EndpointBase = require("./EndpointBase");
JSONRPC.Exception = require("./Exception");
JSONRPC.Server = require("./Server");
JSONRPC.RouterBase = require("./RouterBase");

const { URL } = require("url");
const querystring = require("querystring");

module.exports =
class IncomingRequest
{
	constructor()
	{
		this._bAuthenticated = false;
		this._bAuthorized = false;
		this._mxRequestBody = null;
		this._requestObject = null;
		this._endpoint = null;
		this._router = null;

		this._mxResult = null;
		this._objResponseToBeSerialized = null;
		this._mxResultSerialized = null;
		this._bMethodCalled = false;

		this._nConnectionID = null;

		this._classClient = null;

		this._objHeaders = {};
		this._strRemoteAddress = "";
		this._strLocalAddress = "";

		this._objHTTPIncomingMessage = null;
		this._objHTTPServerResponse = null;

		//this._webSocket
		//this._httpRequest

		Object.seal(this);
	}

	/**
	 * @returns {boolean}
	 */
	get isAuthenticated()
	{
		return this._bAuthenticated;
	}

	/**
	 * @returns {number|null}
	 */
	get connectionID()
	{
		return this._nConnectionID;
	}

	/**
	 * @param {number} nConnectionID
	 */
	set connectionID(nConnectionID)
	{
		assert(typeof nConnectionID === "number" && parseInt(nConnectionID, 10) === nConnectionID, "Connection ID must be an integer.");
		this._nConnectionID = nConnectionID;
	}

	/**
	 * @param {boolean} bAuthenticated
	 */
	set isAuthenticated(bAuthenticated)
	{
		assert(typeof bAuthenticated === "boolean", "bAuthenticated must be of type boolean.");
		this._bAuthenticated = bAuthenticated;
	}

	/**
	 * @returns {boolean}
	 */
	get isAuthorized()
	{
		return this._bAuthorized;
	}

	/**
	 * @param {boolean} bAuthorized
	 */
	set isAuthorized(bAuthorized)
	{
		assert(typeof bAuthorized === "boolean", "bAuthorized must be of type boolean.");
		this._bAuthorized = bAuthorized;
	}

	/**
	 * @returns {string|Object|null}
	 */
	get requestBody()
	{
		return this._mxRequestBody;
	}

	/**
	 * @param {string|Object} mxRequestBody
	 */
	set requestBody(mxRequestBody)
	{
		this._mxRequestBody = mxRequestBody;
	}

	/**
	 * Contains the query of the HTTP GET request as an object.
	 *
	 * @returns {Object|null}
	 */
	get requestHTTPGetQuery()
	{
		if(typeof this.httpIncomingMessage === "undefined" || this.httpIncomingMessage === null)
		{
			return null;
		}
		let strQuery = this.httpIncomingMessage.url;
		if(strQuery.includes("?"))
		{
			strQuery = strQuery.split("?")[1];
		}

		return querystring.parse(strQuery);
	}

	/**
	 * Contains the HTTP method of the request. The value is converted to uppercase.
	 * Supported values are: GET and POST.
	 * This is null when the request is not HTTP.
	 *
	 * @returns {string|null}
	 */
	get requestHTTPMethod()
	{
		if(typeof this.httpIncomingMessage === "undefined" || this.httpIncomingMessage === null)
		{
			return null;
		}

		return this.httpIncomingMessage.method;
	}

	/**
	 * @returns {Object|Array|null}
	 */
	get requestObject()
	{
		return this._requestObject;
	}

	/**
	 * @param {Object|Array} objRequest
	 */
	set requestObject(objRequest)
	{
		assert(typeof objRequest === "object" || Array.isArray(objRequest), "objRequest must be of type Array or Object.");

		this._requestObject = objRequest;
	}

	/**
	 * JSON-RPC 2.0 specification:
	 * An identifier established by the Client that MUST contain a String, Number, or NULL value if included.
	 * If it is not included it is assumed to be a notification.
	 * The value SHOULD normally not be Null and Numbers SHOULD NOT contain fractional parts.
	 *
	 * @returns {boolean}
	 */
	get isNotification()
	{
		return (
			this._requestObject !== null
			&& typeof this._requestObject === "object"
			&& !this.requestObject.hasOwnProperty("id")
		);
	}

	/**
	 * @returns {JSONRPC.EndpointBase|null}
	 */
	get endpoint()
	{
		return this._endpoint;
	}

	/**
	 * endpoint.ReverseCallsClientClass may be null or a class for an API client.
	 * See .router
	 *
	 * @param {JSONRPC.EndpointBase} endpoint
	 */
	set endpoint(endpoint)
	{
		assert(endpoint instanceof JSONRPC.EndpointBase, "endpoint must extend JSONRPC.EndpointBase");

		this._endpoint = endpoint;
	}

	/**
	 * @param {JSONRPC.RouterBase} router
	 */
	set router(router)
	{
		//assert(router.constructor.name === "BidirectionalWebsocketRouter", "router must be an instance of BidirectionalWebsocketRouter.");
		assert(router instanceof JSONRPC.RouterBase, "router must extend JSONRPC.RouterBase.");

		this._router = router;
	}

	/**
	 * @returns {Class}
	 */
	get reverseCallsClient()
	{
		if(this._classClient === null)
		{
			if(
				this.connectionID !== null
				&& this.endpoint
				&& this.endpoint.ReverseCallsClientClass
			)
			{
				this._classClient = this._router.connectionIDToSingletonClient(this.connectionID, this.endpoint.ReverseCallsClientClass);
			}
		}

		return this._classClient;
	}

	/**
	 * @returns {boolean}
	 */
	get isMethodCalled()
	{
		return this._bMethodCalled;
	}

	/**
	 * @param {boolean} bMethodCalled
	 */
	set isMethodCalled(bMethodCalled)
	{
		assert(typeof bMethodCalled === "boolean", "bMethodCalled must be of type boolean.");
		this._bMethodCalled = bMethodCalled;
	}

	/**
	 * @returns {number|string|null|Object|Array|Error}
	 */
	get callResult()
	{
		return this._mxResult;
	}

	/**
	 * @param {number|string|null|Object|Array|Error} mxResult
	 */
	set callResult(mxResult)
	{
		this.isMethodCalled = true;
		this._mxResult = mxResult;
	}

	/**
	 * @returns {Object}
	 */
	get callResultToBeSerialized()
	{
		return this._objResponseToBeSerialized;
	}

	/**
	 * @param {Object} objResultToBeSerialized
	 */
	set callResultToBeSerialized(objResultToBeSerialized)
	{
		this._objResponseToBeSerialized = objResultToBeSerialized;
	}

	/**
	 * @returns {string|Buffer|Object}
	 */
	get callResultSerialized()
	{
		return this._mxResultSerialized;
	}

	/**
	 * @param {string|Buffer|Object} mxResultSerialized
	 */
	set callResultSerialized(mxResultSerialized)
	{
		this._mxResultSerialized = mxResultSerialized;
	}

	/**
	 * @returns {http.ServerResponse | null}
	 */
	get httpServerResponse()
	{
		return this._objHTTPServerResponse || null;
	}

	/**
	 * Sets the HTTP Server Response.
	 *
	 * @param {Object | null} value
	 */
	set httpServerResponse(value)
	{
		if(typeof value !== "object")
		{
			throw new TypeError(`Invalid type ${typeof value} for httpServerResponse property in ${this.constructor.name}. Expected "object".`);
		}

		this._objHTTPServerResponse = value;
	}

	/**
	 * @returns {http.IncomingMessage | null}
	 */
	get httpIncomingMessage()
	{
		return this._objHTTPIncomingMessage || null;
	}

	/**
	 * Sets the HTTP Incoming Request.
	 *
	 * @param {Object | null} value
	 */
	set httpIncomingMessage(value)
	{
		if(typeof value !== "object")
		{
			throw new TypeError(`Invalid type ${typeof value} for httpServerResponse property in ${this.constructor.name}. Expected "object".`);
		}

		this._objHTTPIncomingMessage = value;
	}

	/**
	 * @returns {Object}
	 */
	get headers()
	{
		return this._objHeaders;
	}

	/**
	 * @param {Object} objHeaders
	 */
	set headers(objHeaders)
	{
		this._objHeaders = objHeaders;
	}

	/**
	 * @param {Object} objResponseHeaders
	 */
	addHTTPResponseHeaders(objResponseHeaders)
	{
		if(typeof this.httpServerResponse === "undefined" || this.httpServerResponse === null)
		{
			throw new Error("Error when trying to add HTTP response headers. The httpServerResponse is not set on the IncomingRequest instance.");
		}

		if(typeof objResponseHeaders !== "object")
		{
			throw new TypeError(`Invalid type "${typeof objResponseHeaders}" for extraResponseHeaders set on incomingRequest. Expected "object".`);
		};

		// Make sure headers are lowercased, like the specification from Node.js.
		// And avoid header injection.
		for(let strHeaderName in objResponseHeaders)
		{
			if(strHeaderName !== strHeaderName.toLowerCase())
			{
				throw new TypeError(`Invalid extra response header key ${strHeaderName}. Keys must be lowercased.`);
			}

			let mxHeaderValue = objResponseHeaders[strHeaderName];

			if(typeof mxHeaderValue !== "string")
			{
				throw new TypeError(`Invalid extra response header value ${JSON.stringify(mxHeaderValue)} for key ${strHeaderName}. Only string values are allowed.`);
			}

			mxHeaderValue = querystring.unescape(mxHeaderValue);

			if(
				mxHeaderValue.includes("\n")
				|| mxHeaderValue.includes("\r")
			)
			{
				throw new Error(`Invalid extra response header value ${JSON.stringify(mxHeaderValue)}.`);
			}
		}

		for(let strHeaderName in objResponseHeaders)
		{
			this.httpServerResponse.setHeader(strHeaderName, objResponseHeaders[strHeaderName]);
		}
	}

	/**
	 * @returns {string}
	 */
	get remoteAddress()
	{
		if(
			this.httpIncomingMessage
			&& (
				typeof this._strRemoteAddress === "undefined"
				|| this._strRemoteAddress === null
			)
		)
		{
			this._strRemoteAddress = this.httpIncomingMessage.socket.remoteAddress;
		}

		return this._strRemoteAddress;
	}

	/**
	 * @param {string} strRemoteAddress
	 */
	set remoteAddress(strRemoteAddress)
	{
		this._strRemoteAddress = strRemoteAddress;
	}

	/**
	 * @returns {string}
	 */
	get localAddress()
	{
		if(
			this.httpIncomingMessage
			&& (
				typeof this._strLocalAddress === "undefined"
				|| this._strLocalAddress === null
			)
		)
		{
			this._strLocalAddress = this.httpIncomingMessage.socket.localAddress;
		}

		return this._strLocalAddress;
	}

	/**
	 * @param {string} strLocalAddress
	 */
	set localAddress(strLocalAddress)
	{
		this._strLocalAddress = strLocalAddress;
	}

	/**
	 * Sets "location" header in the extra response headers.
	 * For redirect HTTP status code the default is 307 - Temporary Redirect.
	 * Warning! Without the status code set to 3xx, browsers do not make the redirect
	 *
	 * @param {string} strRedirectURL
	 * @param {Integer} nRedirectHTTPStatusCode
	 */
	setRedirectURL(strRedirectURL, nRedirectHTTPStatusCode = 307 /*Temporary Redirect*/)
	{
		if(strRedirectURL.includes("\n\t\r"))
		{
			throw new TypeError(`Invalid redirect URL ${JSON.stringify(strRedirectURL)}`);
		}

		assert(
			Number.isInteger(nRedirectHTTPStatusCode) && nRedirectHTTPStatusCode >= 300 && nRedirectHTTPStatusCode <= 399,
			`Invalid redirect HTTP status code. Expected a number between 300 and 399, but got ${JSON.stringify(nRedirectHTTPStatusCode)}.`
		);

		//Validate URL
		const redirectURL = new URL(strRedirectURL);
		this.addHTTPResponseHeaders({
			"location": redirectURL.toString()
		});

		this.httpServerResponse.statusCode = nRedirectHTTPStatusCode;
	}

	/**
	 * @returns {Object}
	 */
	toResponseObject()
	{
		let objResponse = {id: null, "jsonrpc": "2.0"};

		if(this.callResult instanceof Error)
		{
			objResponse.error = {
				message: this.callResult.message,
				code: (this.callResult instanceof JSONRPC.Exception) ? this.callResult.code : 0,
				data: this.callResult.stack.split(/[\r\n]+/mg)
			};
		}
		else
		{
			objResponse.result = this.callResult === undefined ? null : this.callResult;
		}

		if(
			this._requestObject !== null
			&& typeof this._requestObject === "object"
			&& this._requestObject.hasOwnProperty("id")
		)
		{
			objResponse.id = this._requestObject.id;
		}

		return objResponse;
	}
};
<commit_msg>Added session object on IncommingRequest. Does not implies any logic.<commit_after>const assert = require("assert");

const JSONRPC = {};
JSONRPC.EndpointBase = require("./EndpointBase");
JSONRPC.Exception = require("./Exception");
JSONRPC.Server = require("./Server");
JSONRPC.RouterBase = require("./RouterBase");

const { URL } = require("url");
const querystring = require("querystring");

module.exports =
class IncomingRequest
{
	constructor()
	{
		this._bAuthenticated = false;
		this._bAuthorized = false;
		this._mxRequestBody = null;
		this._requestObject = null;
		this._endpoint = null;
		this._router = null;

		this._mxResult = null;
		this._objResponseToBeSerialized = null;
		this._mxResultSerialized = null;
		this._bMethodCalled = false;

		this._nConnectionID = null;

		this._classClient = null;

		this._objHeaders = {};
		this._strRemoteAddress = "";
		this._strLocalAddress = "";

		this._objHTTPIncomingMessage = null;
		this._objHTTPServerResponse = null;

		this._objSession = null;
		//this._webSocket
		//this._httpRequest

		Object.seal(this);
	}

	/**
	 * @returns {boolean}
	 */
	get isAuthenticated()
	{
		return this._bAuthenticated;
	}

	/**
	 * @returns {number|null}
	 */
	get connectionID()
	{
		return this._nConnectionID;
	}

	/**
	 * @param {number} nConnectionID
	 */
	set connectionID(nConnectionID)
	{
		assert(typeof nConnectionID === "number" && parseInt(nConnectionID, 10) === nConnectionID, "Connection ID must be an integer.");
		this._nConnectionID = nConnectionID;
	}

	/**
	 * @param {boolean} bAuthenticated
	 */
	set isAuthenticated(bAuthenticated)
	{
		assert(typeof bAuthenticated === "boolean", "bAuthenticated must be of type boolean.");
		this._bAuthenticated = bAuthenticated;
	}

	/**
	 * @returns {boolean}
	 */
	get isAuthorized()
	{
		return this._bAuthorized;
	}

	/**
	 * @param {boolean} bAuthorized
	 */
	set isAuthorized(bAuthorized)
	{
		assert(typeof bAuthorized === "boolean", "bAuthorized must be of type boolean.");
		this._bAuthorized = bAuthorized;
	}

	/**
	 * @returns {string|Object|null}
	 */
	get requestBody()
	{
		return this._mxRequestBody;
	}

	/**
	 * @param {string|Object} mxRequestBody
	 */
	set requestBody(mxRequestBody)
	{
		this._mxRequestBody = mxRequestBody;
	}

	/**
	 * Contains the query of the HTTP GET request as an object.
	 *
	 * @returns {Object|null}
	 */
	get requestHTTPGetQuery()
	{
		if(typeof this.httpIncomingMessage === "undefined" || this.httpIncomingMessage === null)
		{
			return null;
		}
		let strQuery = this.httpIncomingMessage.url;
		if(strQuery.includes("?"))
		{
			strQuery = strQuery.split("?")[1];
		}

		return querystring.parse(strQuery);
	}

	/**
	 * Contains the HTTP method of the request. The value is converted to uppercase.
	 * Supported values are: GET and POST.
	 * This is null when the request is not HTTP.
	 *
	 * @returns {string|null}
	 */
	get requestHTTPMethod()
	{
		if(typeof this.httpIncomingMessage === "undefined" || this.httpIncomingMessage === null)
		{
			return null;
		}

		return this.httpIncomingMessage.method;
	}

	/**
	 * @returns {Object|Array|null}
	 */
	get requestObject()
	{
		return this._requestObject;
	}

	/**
	 * @param {Object|Array} objRequest
	 */
	set requestObject(objRequest)
	{
		assert(typeof objRequest === "object" || Array.isArray(objRequest), "objRequest must be of type Array or Object.");

		this._requestObject = objRequest;
	}

	/**
	 * JSON-RPC 2.0 specification:
	 * An identifier established by the Client that MUST contain a String, Number, or NULL value if included.
	 * If it is not included it is assumed to be a notification.
	 * The value SHOULD normally not be Null and Numbers SHOULD NOT contain fractional parts.
	 *
	 * @returns {boolean}
	 */
	get isNotification()
	{
		return (
			this._requestObject !== null
			&& typeof this._requestObject === "object"
			&& !this.requestObject.hasOwnProperty("id")
		);
	}

	/**
	 * @returns {JSONRPC.EndpointBase|null}
	 */
	get endpoint()
	{
		return this._endpoint;
	}

	/**
	 * endpoint.ReverseCallsClientClass may be null or a class for an API client.
	 * See .router
	 *
	 * @param {JSONRPC.EndpointBase} endpoint
	 */
	set endpoint(endpoint)
	{
		assert(endpoint instanceof JSONRPC.EndpointBase, "endpoint must extend JSONRPC.EndpointBase");

		this._endpoint = endpoint;
	}

	/**
	 * @param {JSONRPC.RouterBase} router
	 */
	set router(router)
	{
		//assert(router.constructor.name === "BidirectionalWebsocketRouter", "router must be an instance of BidirectionalWebsocketRouter.");
		assert(router instanceof JSONRPC.RouterBase, "router must extend JSONRPC.RouterBase.");

		this._router = router;
	}

	/**
	 * @returns {Class}
	 */
	get reverseCallsClient()
	{
		if(this._classClient === null)
		{
			if(
				this.connectionID !== null
				&& this.endpoint
				&& this.endpoint.ReverseCallsClientClass
			)
			{
				this._classClient = this._router.connectionIDToSingletonClient(this.connectionID, this.endpoint.ReverseCallsClientClass);
			}
		}

		return this._classClient;
	}

	/**
	 * @returns {boolean}
	 */
	get isMethodCalled()
	{
		return this._bMethodCalled;
	}

	/**
	 * @param {boolean} bMethodCalled
	 */
	set isMethodCalled(bMethodCalled)
	{
		assert(typeof bMethodCalled === "boolean", "bMethodCalled must be of type boolean.");
		this._bMethodCalled = bMethodCalled;
	}

	/**
	 * @returns {number|string|null|Object|Array|Error}
	 */
	get callResult()
	{
		return this._mxResult;
	}

	/**
	 * @param {number|string|null|Object|Array|Error} mxResult
	 */
	set callResult(mxResult)
	{
		this.isMethodCalled = true;
		this._mxResult = mxResult;
	}

	/**
	 * @returns {Object}
	 */
	get callResultToBeSerialized()
	{
		return this._objResponseToBeSerialized;
	}

	/**
	 * @param {Object} objResultToBeSerialized
	 */
	set callResultToBeSerialized(objResultToBeSerialized)
	{
		this._objResponseToBeSerialized = objResultToBeSerialized;
	}

	/**
	 * @returns {string|Buffer|Object}
	 */
	get callResultSerialized()
	{
		return this._mxResultSerialized;
	}

	/**
	 * @param {string|Buffer|Object} mxResultSerialized
	 */
	set callResultSerialized(mxResultSerialized)
	{
		this._mxResultSerialized = mxResultSerialized;
	}

	/**
	 * @returns {http.ServerResponse | null}
	 */
	get httpServerResponse()
	{
		return this._objHTTPServerResponse || null;
	}

	/**
	 * Sets the HTTP Server Response.
	 *
	 * @param {Object | null} value
	 */
	set httpServerResponse(value)
	{
		if(typeof value !== "object")
		{
			throw new TypeError(`Invalid type ${typeof value} for httpServerResponse property in ${this.constructor.name}. Expected "object".`);
		}

		this._objHTTPServerResponse = value;
	}

	/**
	 * @returns {http.IncomingMessage | null}
	 */
	get httpIncomingMessage()
	{
		return this._objHTTPIncomingMessage || null;
	}

	/**
	 * Sets the HTTP Incoming Request.
	 *
	 * @param {Object | null} value
	 */
	set httpIncomingMessage(value)
	{
		if(typeof value !== "object")
		{
			throw new TypeError(`Invalid type ${typeof value} for httpServerResponse property in ${this.constructor.name}. Expected "object".`);
		}

		this._objHTTPIncomingMessage = value;
	}

	/**
	 * @returns {Object}
	 */
	get headers()
	{
		return this._objHeaders;
	}

	/**
	 * @param {Object} objHeaders
	 */
	set headers(objHeaders)
	{
		this._objHeaders = objHeaders;
	}

	/**
	 * @returns {Object}
	 */
	get session()
	{
		return this._objSession;
	}

	/**
	 * @param {Object} objSession
	 */
	set session(objSession)
	{
		if(typeof objSession !== "object")
		{
			throw new TypeError(`Invalid set value of type "${typeof objSession}" for session. Expected "object"`);
		}

		this._objSession = objSession;
	}

	/**
	 * @param {Object} objResponseHeaders
	 */
	addHTTPResponseHeaders(objResponseHeaders)
	{
		if(typeof this.httpServerResponse === "undefined" || this.httpServerResponse === null)
		{
			throw new Error("Error when trying to add HTTP response headers. The httpServerResponse is not set on the IncomingRequest instance.");
		}

		if(typeof objResponseHeaders !== "object")
		{
			throw new TypeError(`Invalid type "${typeof objResponseHeaders}" for extraResponseHeaders set on incomingRequest. Expected "object".`);
		};

		// Make sure headers are lowercased, like the specification from Node.js.
		// And avoid header injection.
		for(let strHeaderName in objResponseHeaders)
		{
			if(strHeaderName !== strHeaderName.toLowerCase())
			{
				throw new TypeError(`Invalid extra response header key ${strHeaderName}. Keys must be lowercased.`);
			}

			let mxHeaderValue = objResponseHeaders[strHeaderName];

			if(typeof mxHeaderValue !== "string")
			{
				throw new TypeError(`Invalid extra response header value ${JSON.stringify(mxHeaderValue)} for key ${strHeaderName}. Only string values are allowed.`);
			}

			mxHeaderValue = querystring.unescape(mxHeaderValue);

			if(
				mxHeaderValue.includes("\n")
				|| mxHeaderValue.includes("\r")
			)
			{
				throw new Error(`Invalid extra response header value ${JSON.stringify(mxHeaderValue)}.`);
			}
		}

		for(let strHeaderName in objResponseHeaders)
		{
			this.httpServerResponse.setHeader(strHeaderName, objResponseHeaders[strHeaderName]);
		}
	}

	/**
	 * @returns {string}
	 */
	get remoteAddress()
	{
		if(
			this.httpIncomingMessage
			&& (
				typeof this._strRemoteAddress === "undefined"
				|| this._strRemoteAddress === null
			)
		)
		{
			this._strRemoteAddress = this.httpIncomingMessage.socket.remoteAddress;
		}

		return this._strRemoteAddress;
	}

	/**
	 * @param {string} strRemoteAddress
	 */
	set remoteAddress(strRemoteAddress)
	{
		this._strRemoteAddress = strRemoteAddress;
	}

	/**
	 * @returns {string}
	 */
	get localAddress()
	{
		if(
			this.httpIncomingMessage
			&& (
				typeof this._strLocalAddress === "undefined"
				|| this._strLocalAddress === null
			)
		)
		{
			this._strLocalAddress = this.httpIncomingMessage.socket.localAddress;
		}

		return this._strLocalAddress;
	}

	/**
	 * @param {string} strLocalAddress
	 */
	set localAddress(strLocalAddress)
	{
		this._strLocalAddress = strLocalAddress;
	}

	/**
	 * Sets "location" header in the extra response headers.
	 * For redirect HTTP status code the default is 307 - Temporary Redirect.
	 * Warning! Without the status code set to 3xx, browsers do not make the redirect
	 *
	 * @param {string} strRedirectURL
	 * @param {Integer} nRedirectHTTPStatusCode
	 */
	setRedirectURL(strRedirectURL, nRedirectHTTPStatusCode = 307 /*Temporary Redirect*/)
	{
		if(strRedirectURL.includes("\n\t\r"))
		{
			throw new TypeError(`Invalid redirect URL ${JSON.stringify(strRedirectURL)}`);
		}

		assert(
			Number.isInteger(nRedirectHTTPStatusCode) && nRedirectHTTPStatusCode >= 300 && nRedirectHTTPStatusCode <= 399,
			`Invalid redirect HTTP status code. Expected a number between 300 and 399, but got ${JSON.stringify(nRedirectHTTPStatusCode)}.`
		);

		//Validate URL
		const redirectURL = new URL(strRedirectURL);
		this.addHTTPResponseHeaders({
			"location": redirectURL.toString()
		});

		this.httpServerResponse.statusCode = nRedirectHTTPStatusCode;
	}

	/**
	 * @returns {Object}
	 */
	toResponseObject()
	{
		let objResponse = {id: null, "jsonrpc": "2.0"};

		if(this.callResult instanceof Error)
		{
			objResponse.error = {
				message: this.callResult.message,
				code: (this.callResult instanceof JSONRPC.Exception) ? this.callResult.code : 0,
				data: this.callResult.stack.split(/[\r\n]+/mg)
			};
		}
		else
		{
			objResponse.result = this.callResult === undefined ? null : this.callResult;
		}

		if(
			this._requestObject !== null
			&& typeof this._requestObject === "object"
			&& this._requestObject.hasOwnProperty("id")
		)
		{
			objResponse.id = this._requestObject.id;
		}

		return objResponse;
	}
};
<|endoftext|>


<commit_before><commit_msg>added more ui options to ligand-viewer<commit_after><|endoftext|>


<commit_before>'use strict';

angular.module('OpenTMIControllers')
  .controller('BuildListController',
             ['$scope', 'Builds', '$stateParams', '$log',
    function ($scope,   Builds,   $stateParams,    $log) {

    $log.info('init BuildListController')

    var linkCellTemplate = '
' +
                       '{{ row.entity[col.field] }}' +
                       '
';
    var dlCellTemplate = '
' +
                       '{{ COL_FIELD }}' +
                       '
';
    var defaultCellTemplate = '
{{COL_FIELD}}
';

    $scope.columns = [
      { field: 'cre.time', width:200, displayName: 'Date' },
      { field: 'target.os', width:100, displayName: 'OS' },
      //{ field: 'target.type', width:80, displayName: 'Type'  },
      { field: 'target.hw.model', width:150, displayName: 'Target' },
      { field: 'configuration.name', width:150, displayName: 'Configuration' },
      { field: 'configuration.toolchain.name', width:100, displayName: 'Toolchain' },
      { field: 'files[0].name', width:250, cellTemplate: dlCellTemplate, displayName: 'File' },
      { field: 'name', displayName: 'Name' }
    ];
    $scope.gridOptions = {
      columnDefs: $scope.columns,
      enableColumnResizing: true,
      enableFiltering: true,
      enableRowSelection: true,
      //showFooter: true,
      exporterMenuCsv: true,
      enableGridMenu: true
    };

    function doUpdateList(q)
    {
      if(!q) q = {};
      Builds.query({q: JSON.stringify(q), f: "_id name target files configuration cre.time"})
        .$promise.then( function(builds){
          $log.info(builds);
          $scope.dataBuilds = builds;
          $scope.$root.$broadcast('buildListStatus', status);
      });
    }

    $scope.gridOptions.data = 'dataBuilds';
    doUpdateList();
    /*
    $scope.$on('tcFilter', function(event, data) {

      var q = {}
      data.tags.forEach( function(tag){
        if( !q.$or ) q.$or = [];
        q.$and.push( {target: {"$regex": ("/"+tag+"/"), "$options":"i"}} );
      });
      doUpdateList(q);

    });*/

    $scope.gridOptions.onRegisterApi = function(gridApi){
      //set gridApi on scope
      $scope.gridApi = gridApi;
    };

  }])
  /*
  .filter('mapStatus', function() {
    var statusHash = {
      'released': 'released',
      'develop': 'develop',
      'maintenance': 'maintenance',
      'unknown': 'unknown'
    };

    return function(input) {
      if (!input){
        return '';
      } else {
        return statusHash[input];
      }
    };
  })*/

  ;<commit_msg>build list view: add link to vsc url<commit_after>'use strict';

angular.module('OpenTMIControllers')
  .controller('BuildListController',
             ['$scope', 'Builds', '$stateParams', '$log',
    function ($scope,   Builds,   $stateParams,    $log) {

    $log.info('init BuildListController')

    var linkCellTemplate = '
' +
                       '{{ row.entity[col.field] }}' +
                       '
';
    var dlCellTemplate = '
' +
                       '{{ COL_FIELD }} ({{(row.entity.files[0].size/1024).toFixed(0)}} kB)' +
                       '
';
    var defaultCellTemplate = '
{{COL_FIELD}}
';

    $scope.columns = [
      { field: 'cre.time', width:200, displayName: 'Date' },
      { field: 'target.os', width:100, displayName: 'OS' },
      { field: 'target.hw.model', width:100, displayName: 'Target' },
      { field: 'configuration.name', width:150, displayName: 'Configuration' },
      { field: 'configuration.toolchain.name', width:100, displayName: 'Toolchain' },
      { field: 'files[0].name', width:300, cellTemplate: dlCellTemplate, displayName: 'File' },
      { field: 'name', cellTemplate: linkCellTemplate, displayName: 'Name' }
    ];
    $scope.gridOptions = {
      columnDefs: $scope.columns,
      enableColumnResizing: true,
      enableFiltering: true,
      enableRowSelection: true,
      //showFooter: true,
      exporterMenuCsv: true,
      enableGridMenu: true
    };

    function doUpdateList(q)
    {
      if(!q) q = {};
      Builds.query({q: JSON.stringify(q), f: "_id name target files configuration cre.time vcs"})
        .$promise.then( function(builds){
          $log.info(builds);
          $scope.dataBuilds = builds;
          $scope.$root.$broadcast('buildListStatus', status);
      });
    }

    $scope.gridOptions.data = 'dataBuilds';
    doUpdateList();
    /*
    $scope.$on('tcFilter', function(event, data) {

      var q = {}
      data.tags.forEach( function(tag){
        if( !q.$or ) q.$or = [];
        q.$and.push( {target: {"$regex": ("/"+tag+"/"), "$options":"i"}} );
      });
      doUpdateList(q);

    });*/

    $scope.gridOptions.onRegisterApi = function(gridApi){
      //set gridApi on scope
      $scope.gridApi = gridApi;
    };

  }])
  /*
  .filter('mapStatus', function() {
    var statusHash = {
      'released': 'released',
      'develop': 'develop',
      'maintenance': 'maintenance',
      'unknown': 'unknown'
    };

    return function(input) {
      if (!input){
        return '';
      } else {
        return statusHash[input];
      }
    };
  })*/

  ;<|endoftext|>


<commit_before>module.exports = {
    port: 8000,
    cdns: ['bootstrap', 'cdnjs', 'google', 'jsdelivr', 'jquery'],
    syncUrl: 'http://jsdelivrapi-sync.aws.af.cm/data/',
    tasks: {
        sync: {minute: 0}
    },
    maxcdn: {
        alias: 'replace this',
        key: 'replace this',
        secret: 'replace this',
        zoneId: 0 // replace with some zone id
    },
    github: '' // replace with github token
};
<commit_msg>commenting out jsdelivr and jquery<commit_after>module.exports = {
    port: 8000,
    //cdns: ['bootstrap', 'cdnjs', 'google', 'jsdelivr', 'jquery'],
    cdns: ['bootstrap', 'cdnjs', 'google'],
    syncUrl: 'http://jsdelivrapi-sync.aws.af.cm/data/',
    tasks: {
        sync: {minute: 0}
    },
    maxcdn: {
        alias: 'replace this',
        key: 'replace this',
        secret: 'replace this',
        zoneId: 0 // replace with some zone id
    },
    github: '' // replace with github token
};
<|endoftext|>


<commit_before>/*global $:false */
/*global jQuery:false */
/*global Backbone:false */
/*global jRespond:false */
/*global _:false */
var gc2table = (function () {
    "use strict";
    var host, js, isLoaded, object, init,
        scriptsLoaded = false,
        scriptSource = (function (scripts) {
            scripts = document.getElementsByTagName('script');
            var script = scripts[scripts.length - 1];
            if (script.getAttribute.length !== undefined) {
                return script.src;
            }
            return script.getAttribute('src', -1);
        }());

    // Try to set host from script if not set already
    if (typeof window.geocloud_host === "undefined") {
        host = (scriptSource.charAt(0) === "/") ? "" : scriptSource.split("/")[0] + "//" + scriptSource.split("/")[2];
    } else {
        host = window.geocloud_host;
    }

    if (typeof $ === "undefined") {
        js = document.createElement("script");
        js.type = "text/javascript";
        js.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js";
        document.getElementsByTagName("head")[0].appendChild(js);
    }
    (function pollForjQuery() {
        if (typeof jQuery !== "undefined") {
            if (typeof jQuery().typeahead === "undefined") {
                $.getScript(host + "/js/typeahead.js-0.10.5/dist/typeahead.bundle.js");
            }
            if (typeof jQuery().bootstrapTable === "undefined") {
                $.getScript(host + "/js/bootstrap-table/bootstrap-table.js");
            }
            if (typeof _ === 'undefined') {
                $.getScript(host + "/js/underscore/underscore-min.js");
            }
            if (typeof jRespond === "undefined") {
                $.getScript(host + "/js/div/jRespond.js");
            }
            (function pollForDependencies() {
                if (typeof jQuery().typeahead !== "undefined" &&
                    typeof jQuery().bootstrapTable !== "undefined" &&
                    typeof _ !== 'undefined' &&
                    typeof jRespond !== "undefined") {
                    if (typeof jQuery().bootstrapTable.defaults.filterControl === "undefined") {
                        $.getScript(host + "/js/bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.js");
                    }
                    if (typeof Backbone === "undefined") {
                        $.getScript("http://backbonejs.org/backbone.js");
                    }
                    (function pollForDependants() {
                        if (typeof jQuery().bootstrapTable.defaults.filterControl !== "undefined" &&
                            typeof Backbone !== "undefined") {
                            scriptsLoaded = true;
                        } else {
                            setTimeout(pollForDependants, 10);
                        }
                    }());
                } else {
                    setTimeout(pollForDependencies, 10);
                }
            }());
            $('<link/>').attr({
                rel: 'stylesheet',
                type: 'text/css',
                href: host + '/js/bootstrap-table/bootstrap-table.min.css'
            }).appendTo('head');
        } else {
            setTimeout(pollForjQuery, 10);
        }
    }());

    isLoaded = function () {
        return scriptsLoaded;
    };
    object = {};
    init = function (conf) {
        var defaults = {
                el: "#table",
                autoUpdate: false,
                height: 300,
                setSelectedStyle: true,
                openPopUp: false,
                setViewOnSelect: true,
                responsive: true,
                autoPan: false,
                locale: 'en-US',
                styleSelected: {
                    weight: 5,
                    color: '#666',
                    dashArray: '',
                    fillOpacity: 0.7
                }
            }, prop,
            uid = '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);
            });
        if (conf) {
            for (prop in conf) {
                defaults[prop] = conf[prop];
            }
        }
        var data,
            m = defaults.geocloud2,
            store = defaults.store,
            cm = defaults.cm,
            autoUpdate = defaults.autoUpdate,
            height = defaults.height,
            styleSelected = defaults.styleSelected,
            el = defaults.el, click, loadDataInTable,
            setSelectedStyle = defaults.setSelectedStyle,
            setViewOnSelect = defaults.setViewOnSelect,
            openPopUp = defaults.openPopUp,
            autoPan = defaults.autoPan,
            responsive = defaults.responsive,
            locale = defaults.locale;

        (function poll() {
            if (scriptsLoaded) {
                var originalLayers, filters;
                _.extend(object, Backbone.Events);
                object.on("selected" + "_" + uid, function (id) {
                    $(el + ' tr').removeClass("selected");
                    $.each(store.layer._layers, function (i, v) {
                        try {
                            store.layer.resetStyle(v);
                        } catch (e) {
                        }
                    });
                    var row = $('*[data-uniqueid="' + id + '"]');
                    row.addClass("selected");
                    if (setSelectedStyle) {
                        m.map._layers[id].setStyle(styleSelected);
                    }
                    if (openPopUp) {
                        var str = "
";
                        $.each(cm, function (i, v) {
                            str = str + "
" + v.header + "	" + m.map._layers[id].feature.properties[v.dataIndex] + "
";
                        });
                        str = str + "
";
                        m.map._layers[id].bindPopup(str, {
                            className: "custom-popup",
                            autoPan: autoPan,
                            closeButton: true
                        }).openPopup();
                    }

                });

                click = function (e) {
                    var row = $('*[data-uniqueid="' + e.target._leaflet_id + '"]');
                    $(el).bootstrapTable('scrollTo', row.index() * row.height());
                    object.trigger("selected" + "_" + uid, e.target._leaflet_id);
                };
                $.each(store.layer._layers, function (i, layer) {
                    layer.on({
                        click: click
                    });
                });

                $(el).append("

");
                $.each(cm, function (i, v) {
                    $(el + ' thead tr').append("" + v.header + "	");
                });

                var filterMap = function () {
                    $.each(store.layer._layers, function (i, v) {
                        m.map.removeLayer(v);
                    });
                    $.each(originalLayers, function (i, v) {
                        m.map.addLayer(v);
                    });
                    filters = {};
                    $.each(cm, function (i, v) {
                        if (v.filterControl) {
                            filters[v.dataIndex] = $("." + v.dataIndex).val();
                        }
                    });
                    $.each(store.layer._layers, function (i, v) {
                        $.each(v.feature.properties, function (u, n) {
                            if (typeof filters[u] !== "undefined" && filters[u] !== null && (n.toLowerCase().indexOf(filters[u].toLowerCase()) === -1) && filters[u] !== "") {
                                m.map.removeLayer(v);
                            }

                        });
                    });
                    bindEvent();
                };

                var bindEvent = function (e) {
                    setTimeout(function () {
                        $(el + ' tr').on("click", function (e) {
                            object.trigger("selected" + "_" + uid, $(this).data('uniqueid'));
                            var layer = m.map._layers[$(this).data('uniqueid')];
                            setTimeout(function () {
                                if (setViewOnSelect) {
                                    m.map.fitBounds(layer.getBounds());
                                }
                            }, 100);
                        });
                    }, 100);
                };
                $(el).bootstrapTable({
                    uniqueId: "_id",
                    height: height,
                    locale: locale,
                    onToggle: bindEvent,
                    onSort: bindEvent,
                    onColumnSwitch: bindEvent,
                    onColumnSearch: filterMap
                });

                // Define a callback for when the SQL returns
                var customOnLoad = store.onLoad;
                store.onLoad = function () {
                    loadDataInTable();
                };
                loadDataInTable = function () {
                    data = [];
                    $.each(store.layer._layers, function (i, v) {
                        v.feature.properties._id = i;
                        $.each(v.feature.properties, function (n, m) {
                            $.each(cm, function (j, k) {
                                if (k.dataIndex === n && k.link === true) {
                                    v.feature.properties[n] = "" + "Link" + "";
                                }

                            });
                        });
                        data.push(v.feature.properties);
                        v.on({
                            click: click
                        });

                    });
                    originalLayers = jQuery.extend(true, {}, store.layer._layers);
                    $(el).bootstrapTable('load', data);
                    filterMap();
                    customOnLoad();
                };
                if (autoUpdate) {
                    m.on("moveend", _.debounce(function () {
                            store.reset();
                            store.load();
                        }, 200)
                    );
                }
                var jRes = jRespond([
                    {
                        label: 'handheld',
                        enter: 0,
                        exit: 400
                    },
                    {
                        label: 'desktop',
                        enter: 401,
                        exit: 100000
                    }
                ]);
                if (responsive) {
                    jRes.addFunc({
                        breakpoint: ['handheld'],
                        enter: function () {
                            $(el).bootstrapTable('toggleView');
                        },
                        exit: function () {
                            $(el).bootstrapTable('toggleView');
                        }
                    });
                    jRes.addFunc({
                        breakpoint: ['desktop'],
                        enter: function () {
                        },
                        exit: function () {
                        }
                    });
                }
            } else {
                setTimeout(poll, 20);
            }
        }());
        return {
            loadDataInTable: loadDataInTable,
            object: object,
            uid: uid
        };
    };
    return {
        init: init,
        isLoaded: isLoaded
    };
}());
<commit_msg>Upgrade of bootstrap-tables<commit_after>/*global $:false */
/*global jQuery:false */
/*global Backbone:false */
/*global jRespond:false */
/*global _:false */
var gc2table = (function () {
    "use strict";
    var host, js, isLoaded, object, init,
        scriptsLoaded = false,
        scriptSource = (function (scripts) {
            scripts = document.getElementsByTagName('script');
            var script = scripts[scripts.length - 1];
            if (script.getAttribute.length !== undefined) {
                return script.src;
            }
            return script.getAttribute('src', -1);
        }());

    // Try to set host from script if not set already
    if (typeof window.geocloud_host === "undefined") {
        host = (scriptSource.charAt(0) === "/") ? "" : scriptSource.split("/")[0] + "//" + scriptSource.split("/")[2];
    } else {
        host = window.geocloud_host;
    }

    if (typeof $ === "undefined") {
        js = document.createElement("script");
        js.type = "text/javascript";
        js.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js";
        document.getElementsByTagName("head")[0].appendChild(js);
    }
    (function pollForjQuery() {
        if (typeof jQuery !== "undefined") {
            if (typeof jQuery().typeahead === "undefined") {
                $.getScript(host + "/js/typeahead.js-0.10.5/dist/typeahead.bundle.js");
            }
            if (typeof jQuery().bootstrapTable === "undefined") {
                $.getScript(host + "/js/bootstrap-table/bootstrap-table.js");
            }
            if (typeof _ === 'undefined') {
                $.getScript(host + "/js/underscore/underscore-min.js");
            }
            if (typeof jRespond === "undefined") {
                $.getScript(host + "/js/div/jRespond.js");
            }
            (function pollForDependencies() {
                if (typeof jQuery().typeahead !== "undefined" &&
                    typeof jQuery().bootstrapTable !== "undefined" &&
                    typeof _ !== 'undefined' &&
                    typeof jRespond !== "undefined") {
                    if (typeof jQuery().bootstrapTable.defaults.filterControl === "undefined") {
                        $.getScript(host + "/js/bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.js");
                    }
                    if (typeof Backbone === "undefined") {
                        $.getScript("http://backbonejs.org/backbone.js");
                    }
                    (function pollForDependants() {
                        if (typeof jQuery().bootstrapTable.defaults.filterControl !== "undefined" &&
                            typeof Backbone !== "undefined") {
                            scriptsLoaded = true;
                        } else {
                            setTimeout(pollForDependants, 10);
                        }
                    }());
                } else {
                    setTimeout(pollForDependencies, 10);
                }
            }());
            $('<link/>').attr({
                rel: 'stylesheet',
                type: 'text/css',
                href: host + '/js/bootstrap-table/bootstrap-table.min.css'
            }).appendTo('head');
        } else {
            setTimeout(pollForjQuery, 10);
        }
    }());

    isLoaded = function () {
        return scriptsLoaded;
    };
    object = {};
    init = function (conf) {
        var defaults = {
                el: "#table",
                autoUpdate: false,
                height: 300,
                setSelectedStyle: true,
                openPopUp: false,
                setViewOnSelect: true,
                responsive: true,
                autoPan: false,
                locale: 'en-US',
                styleSelected: {
                    weight: 5,
                    color: '#666',
                    dashArray: '',
                    fillOpacity: 0.7
                }
            }, prop,
            uid = '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);
            });
        if (conf) {
            for (prop in conf) {
                defaults[prop] = conf[prop];
            }
        }
        var data,
            m = defaults.geocloud2,
            store = defaults.store,
            cm = defaults.cm,
            autoUpdate = defaults.autoUpdate,
            height = defaults.height,
            styleSelected = defaults.styleSelected,
            el = defaults.el, click, loadDataInTable,
            setSelectedStyle = defaults.setSelectedStyle,
            setViewOnSelect = defaults.setViewOnSelect,
            openPopUp = defaults.openPopUp,
            autoPan = defaults.autoPan,
            responsive = defaults.responsive,
            locale = defaults.locale;

        (function poll() {
            if (scriptsLoaded) {
                var originalLayers, filters;
                _.extend(object, Backbone.Events);
                object.on("selected" + "_" + uid, function (id) {
                    $(el + ' tr').removeClass("selected");
                    $.each(store.layer._layers, function (i, v) {
                        try {
                            store.layer.resetStyle(v);
                        } catch (e) {
                        }
                    });
                    var row = $('*[data-uniqueid="' + id + '"]');
                    row.addClass("selected");
                    if (setSelectedStyle) {
                        m.map._layers[id].setStyle(styleSelected);
                    }
                    if (openPopUp) {
                        var str = "
";
                        $.each(cm, function (i, v) {
                            str = str + "
" + v.header + "	" + m.map._layers[id].feature.properties[v.dataIndex] + "
";
                        });
                        str = str + "
";
                        m.map._layers[id].bindPopup(str, {
                            className: "custom-popup",
                            autoPan: autoPan,
                            closeButton: true
                        }).openPopup();
                    }

                });

                click = function (e) {
                    var row = $('*[data-uniqueid="' + e.target._leaflet_id + '"]');
                    $(el).bootstrapTable('scrollTo', row.index() * row.height());
                    object.trigger("selected" + "_" + uid, e.target._leaflet_id);
                };
                $.each(store.layer._layers, function (i, layer) {
                    layer.on({
                        click: click
                    });
                });

                $(el).append("

");
                $.each(cm, function (i, v) {
                    $(el + ' thead tr').append("" + v.header + "	");
                });

                var filterMap = function () {
                    $.each(store.layer._layers, function (i, v) {
                        m.map.removeLayer(v);
                    });
                    $.each(originalLayers, function (i, v) {
                        m.map.addLayer(v);
                    });
                    filters = {};
                    $.each(cm, function (i, v) {
                        if (v.filterControl) {
                            filters[v.dataIndex] = $(".bootstrap-table-filter-control-" + v.dataIndex).val();
                        }
                    });
                    $.each(store.layer._layers, function (i, v) {
                        $.each(v.feature.properties, function (u, n) {
                            if (typeof filters[u] !== "undefined" && filters[u] !== null && (n.toLowerCase().indexOf(filters[u].toLowerCase()) === -1) && filters[u] !== "") {
                                m.map.removeLayer(v);
                            }

                        });
                    });
                    bindEvent();
                };

                var bindEvent = function (e) {
                    setTimeout(function () {
                        $(el + ' tr').on("click", function (e) {
                            object.trigger("selected" + "_" + uid, $(this).data('uniqueid'));
                            var layer = m.map._layers[$(this).data('uniqueid')];
                            setTimeout(function () {
                                if (setViewOnSelect) {
                                    m.map.fitBounds(layer.getBounds());
                                }
                            }, 100);
                        });
                    }, 100);
                };
                $(el).bootstrapTable({
                    uniqueId: "_id",
                    height: height,
                    locale: locale,
                    onToggle: bindEvent,
                    onSort: bindEvent,
                    onColumnSwitch: bindEvent,
                    onColumnSearch: filterMap
                });

                // Define a callback for when the SQL returns
                var customOnLoad = store.onLoad;
                store.onLoad = function () {
                    loadDataInTable();
                };
                loadDataInTable = function () {
                    data = [];
                    $.each(store.layer._layers, function (i, v) {
                        v.feature.properties._id = i;
                        $.each(v.feature.properties, function (n, m) {
                            $.each(cm, function (j, k) {
                                if (k.dataIndex === n && k.link === true) {
                                    v.feature.properties[n] = "" + "Link" + "";
                                }

                            });
                        });
                        data.push(v.feature.properties);
                        v.on({
                            click: click
                        });

                    });
                    originalLayers = jQuery.extend(true, {}, store.layer._layers);
                    $(el).bootstrapTable('load', data);
                    filterMap();
                    customOnLoad();
                };
                if (autoUpdate) {
                    m.on("moveend", _.debounce(function () {
                            store.reset();
                            store.load();
                        }, 200)
                    );
                }
                var jRes = jRespond([
                    {
                        label: 'handheld',
                        enter: 0,
                        exit: 400
                    },
                    {
                        label: 'desktop',
                        enter: 401,
                        exit: 100000
                    }
                ]);
                if (responsive) {
                    jRes.addFunc({
                        breakpoint: ['handheld'],
                        enter: function () {
                            $(el).bootstrapTable('toggleView');
                        },
                        exit: function () {
                            $(el).bootstrapTable('toggleView');
                        }
                    });
                    jRes.addFunc({
                        breakpoint: ['desktop'],
                        enter: function () {
                        },
                        exit: function () {
                        }
                    });
                }
            } else {
                setTimeout(poll, 20);
            }
        }());
        return {
            loadDataInTable: loadDataInTable,
            object: object,
            uid: uid
        };
    };
    return {
        init: init,
        isLoaded: isLoaded
    };
}());
<|endoftext|>


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

// eslint-disable-next-line import/no-unresolved
// eslint-disable-next-line import/no-duplicates
import simpleExampleCode from '!!raw-loader!../../../components/Examples/resizeable/SimpleResizeableEditor';
// eslint-disable-next-line import/no-unresolved
import simpleExampleEditorStylesCode from '!!raw-loader!../../../components/Examples/resizeable/SimpleResizeableEditor/editorStyles.module.css';
// eslint-disable-next-line import/no-unresolved
import simpleExampleColorBlockCode from '!!raw-loader!../../../components/Examples/resizeable/SimpleResizeableEditor/colorBlockPlugin';

import Container from '../../../components/Container/Container';
import AlternateContainer from '../../../components/AlternateContainer/AlternateContainer';
import Heading from '../../../components/Heading/Heading';
import styles from './styles.module.css';
import Code from '../../../components/Code/Code';
// eslint-disable-next-line import/no-duplicates
import SimpleResizeableEditor from '../../../components/Examples/resizeable/SimpleResizeableEditor';
import PluginPageFrame from '../../../components/PluginPageFrame/PluginPageFrame';

export default class App extends Component {
  render() {
    return (
      <PluginPageFrame>
        <Container>
          <Heading level={2}>Resizeable</Heading>
          <Heading level={3}>Prerequisite</Heading>

            This plugin exposes a decorator for blocks. You can use it in
            combination with any kind of plugin that manages a Draft.js block
            e.g. image or video. Keep in mind the plugin must accept a decorator
            for the block. The `Simple Resizeable Example` further down contains
            an example plugin rendering a colored div. In addition this plugin
            only works in combination with the @draft-js-plugins/focus.

          <Heading level={3}>Usage</Heading>

            Hover with the mouse on the right side of the block and drag it to
            resize. At which edge resize is possible is configurable.

          <Heading level={3}>Supported Environment</Heading>

Desktop: Yes

Mobile: No

Screen-reader: No

        </Container>
        <AlternateContainer>
          <Heading level={2}>Getting Started</Heading>

        </AlternateContainer>
        <Container>
          <Heading level={2}>Configuration Parameters</Heading>
        </Container>
        <Container>
          <Heading level={2}>Simple Resizeable Example</Heading>
          <SimpleResizeableEditor />

        </Container>
      </PluginPageFrame>
    );
  }
}
<commit_msg>add notes for resizing (#2313)<commit_after>import React, { Component } from 'react';

// eslint-disable-next-line import/no-unresolved
// eslint-disable-next-line import/no-duplicates
import simpleExampleCode from '!!raw-loader!../../../components/Examples/resizeable/SimpleResizeableEditor';
// eslint-disable-next-line import/no-unresolved
import simpleExampleEditorStylesCode from '!!raw-loader!../../../components/Examples/resizeable/SimpleResizeableEditor/editorStyles.module.css';
// eslint-disable-next-line import/no-unresolved
import simpleExampleColorBlockCode from '!!raw-loader!../../../components/Examples/resizeable/SimpleResizeableEditor/colorBlockPlugin';

import Container from '../../../components/Container/Container';
import AlternateContainer from '../../../components/AlternateContainer/AlternateContainer';
import Heading from '../../../components/Heading/Heading';
import styles from './styles.module.css';
import Code from '../../../components/Code/Code';
// eslint-disable-next-line import/no-duplicates
import SimpleResizeableEditor from '../../../components/Examples/resizeable/SimpleResizeableEditor';
import PluginPageFrame from '../../../components/PluginPageFrame/PluginPageFrame';
import ExternalLink from '../../../components/Link/Link';

export default class App extends Component {
  render() {
    return (
      <PluginPageFrame>
        <Container>
          <Heading level={2}>Resizeable</Heading>
          <Heading level={3}>Prerequisite</Heading>

            This plugin exposes a decorator for blocks. You can use it in
            combination with any kind of plugin that manages a Draft.js block
            e.g. image or video. Keep in mind the plugin must accept a decorator
            for the block. The `Simple Resizeable Example` further down contains
            an example plugin rendering a colored div. In addition this plugin
            only works in combination with the @draft-js-plugins/focus.

          <Heading level={3}>Usage</Heading>

            Hover with the mouse on the right side of the block and drag it to
            resize. At which edge resize is possible is configurable.

          <Heading level={3}>Supported Environment</Heading>

Desktop: Yes

Mobile: No

Screen-reader: No

        </Container>
        <AlternateContainer>
          <Heading level={2}>Getting Started</Heading>

          <Heading level={3}>Notes</Heading>

            This plugin needs to read and write the size information to the
            resized HTML element. Therefore the resized component needs to use{' '}
            <ExternalLink href="https://reactjs.org/docs/forwarding-refs.html">
              forwarding ref
            </ExternalLink>
            . As reported in issue{' '}
            <ExternalLink href="https://github.com/draft-js-plugins/draft-js-plugins/issues/2272#issuecomment-909642353">
              #2272
            </ExternalLink>{' '}
            in some cases it might be usefult to wrap the ref with{' '}
            <ExternalLink href="https://reactjs.org/docs/hooks-reference.html#useimperativehandle">
              useImperativeHandle
            </ExternalLink>
            .

        </AlternateContainer>
        <Container>
          <Heading level={2}>Configuration Parameters</Heading>
        </Container>
        <Container>
          <Heading level={2}>Simple Resizeable Example</Heading>
          <SimpleResizeableEditor />

        </Container>
      </PluginPageFrame>
    );
  }
}
<|endoftext|>


<commit_before>"use strict";

var yeoman  = require( "yeoman-generator" )
,   yosay   = require( "yosay" )
,   varname = require( "varname" )
,   fs      = require( "fs" )
;

// Get the current running directory name
//
var fullPath        = process.cwd()
,   rootLocation    = fullPath
;

module.exports = yeoman.generators.Base.extend(
{
    // Function is used to determine if we are currently in the root off the project
    // if not, try to find the root and change to that directory
    //
    determineRoot: function()
    {
        var callback        = this.async()
        ,   rootFound       = false
        ,   tries           = 0
        ;

        if( fs.existsSync( "src" ) === false )
        {
            while( rootFound === false && tries < 10 )
            {
                // Split old path
                //
                var previousLocation = rootLocation.split( "/" );

                // Pop the last folder from the path
                //
                previousLocation.pop();

                // Create the new path and open it
                //
                rootLocation = previousLocation.join( "/" );

                // Change the process location
                //
                process.chdir( rootLocation );

                // Check if we found the project root, up the counter
                // we should stop looking some time.....
                //
                rootFound = fs.existsSync( "src" );
                tries++;
            }

            // If we couldn't find the root, let the user know and exit the proces...
            //
            if( rootFound === false )
            {
                yeoman.log( "Failed to find root of the project, check that you are somewhere within your project." );
                process.exit();
            }
        }

        callback();
    }

,   askSomeQuestions: function ()
    {
        var callback = this.async();

        // Have Yeoman greet the user.
        //
        this.log( yosay( "So you want an BAT collection?" ) );

        // Ask the user for the webapp details
        //
        var prompts = [
            {
                name:       "collectionName"
            ,   message:    "What's the name of this collection you so desire? ( use camelcasing! )"
            ,   default:    "myCollection"
            }
        ,   {
                name:       "description"
            ,   message:    "What's the description for this collection?"
            ,   default:    "No description"
            }
        ,   {
                type:       "confirm"
            ,   name:       "singleton"
            ,   message:    "Should this collection be a singleton?"
            ,   default:    false
            }
        ];

        this.prompt( prompts, function( props )
        {
            this.collectionName = props.collectionName;
            this.description    = props.description;
            this.singleton      = props.singleton;

            callback();
        }.bind( this ) );
    }

,   askModelQuestions: function()
    {
        var callback = this.async();

        // We are gonna do a "smart" guess for the modelName to have a default value
        // If the last characters of the collectionName is a "s" we are gonna assume it's
        // plural and remove the trailing as and use that a default modelName
        //
        var modelName = this.collectionName;

        if( this.collectionName.slice( -1 ) === "s" )
        {
            modelName = this.collectionName.slice( 0, -1 );
        }

        var prompts = [
            {
                name:       "modelName"
            ,   message:    "Whats the model name for this collection ( use camelcasing! )"
            ,   default:    modelName
            }
        ,   {
                type:       "confirm"
            ,   name:       "createModel"
            ,   message:    "Should i create this model now as well?"
            ,   default:    true
            }
        ];

        this.prompt( prompts, function( props )
        {
            this.modelName      = props.modelName;
            this.createModel    = props.createModel;

            callback();
        }.bind( this ) );

    }

,   createCollection: function()
    {
        // Create the needed variables
        this.modelClass     = this.modelName.charAt(0).toUpperCase() + this.modelName.slice(1);
        this.className      = this.collectionName.charAt(0).toUpperCase() + this.collectionName.slice(1);
        this.modelFileName  = varname.dash( this.modelName );
        this.fileName       = varname.dash( this.collectionName );

        // Create the collection
        this.template( "collection.coffee", "src/collections/" + this.fileName + ".coffee" );

        // Create the model if needed
        if( this.crateModel === true )
        {
            this.invoke( "bat:model", {
                options: {
                    nested:         true
                ,   modelName:      this.modelName
                ,   description:    "Model for the " + this.collectionName
                ,   singleton:      false
                }
            } );
        }
    }
} );
<commit_msg>fix(collection subgenerator): remove confusing default collection name<commit_after>"use strict";

var yeoman  = require( "yeoman-generator" )
,   yosay   = require( "yosay" )
,   varname = require( "varname" )
,   fs      = require( "fs" )
;

// Get the current running directory name
//
var fullPath        = process.cwd()
,   rootLocation    = fullPath
;

module.exports = yeoman.generators.Base.extend(
{
    // Function is used to determine if we are currently in the root off the project
    // if not, try to find the root and change to that directory
    //
    determineRoot: function()
    {
        var callback        = this.async()
        ,   rootFound       = false
        ,   tries           = 0
        ;

        if( fs.existsSync( "src" ) === false )
        {
            while( rootFound === false && tries < 10 )
            {
                // Split old path
                //
                var previousLocation = rootLocation.split( "/" );

                // Pop the last folder from the path
                //
                previousLocation.pop();

                // Create the new path and open it
                //
                rootLocation = previousLocation.join( "/" );

                // Change the process location
                //
                process.chdir( rootLocation );

                // Check if we found the project root, up the counter
                // we should stop looking some time.....
                //
                rootFound = fs.existsSync( "src" );
                tries++;
            }

            // If we couldn't find the root, let the user know and exit the proces...
            //
            if( rootFound === false )
            {
                yeoman.log( "Failed to find root of the project, check that you are somewhere within your project." );
                process.exit();
            }
        }

        callback();
    }

,   askSomeQuestions: function ()
    {
        var callback = this.async();

        // Have Yeoman greet the user.
        //
        this.log( yosay( "So you want an BAT collection?" ) );

        // Ask the user for the webapp details
        //
        var prompts = [
            {
                name:       "collectionName"
            ,   message:    "What's the name of this collection you so desire? ( use camelcasing! )"
            }
        ,   {
                name:       "description"
            ,   message:    "What's the description for this collection?"
            ,   default:    "No description"
            }
        ,   {
                type:       "confirm"
            ,   name:       "singleton"
            ,   message:    "Should this collection be a singleton?"
            ,   default:    false
            }
        ];

        this.prompt( prompts, function( props )
        {
            this.collectionName = props.collectionName;
            this.description    = props.description;
            this.singleton      = props.singleton;

            callback();
        }.bind( this ) );
    }

,   askModelQuestions: function()
    {
        var callback = this.async();

        // We are gonna do a "smart" guess for the modelName to have a default value
        // If the last characters of the collectionName is a "s" we are gonna assume it's
        // plural and remove the trailing as and use that a default modelName
        //
        var modelName = this.collectionName;

        if( this.collectionName.slice( -1 ) === "s" )
        {
            modelName = this.collectionName.slice( 0, -1 );
        }

        var prompts = [
            {
                name:       "modelName"
            ,   message:    "Whats the model name for this collection ( use camelcasing! )"
            ,   default:    modelName
            }
        ,   {
                type:       "confirm"
            ,   name:       "createModel"
            ,   message:    "Should i create this model now as well?"
            ,   default:    true
            }
        ];

        this.prompt( prompts, function( props )
        {
            this.modelName      = props.modelName;
            this.createModel    = props.createModel;

            callback();
        }.bind( this ) );

    }

,   createCollection: function()
    {
        // Create the needed variables
        this.modelClass     = this.modelName.charAt(0).toUpperCase() + this.modelName.slice(1);
        this.className      = this.collectionName.charAt(0).toUpperCase() + this.collectionName.slice(1);
        this.modelFileName  = varname.dash( this.modelName );
        this.fileName       = varname.dash( this.collectionName );

        // Create the collection
        this.template( "collection.coffee", "src/collections/" + this.fileName + ".coffee" );

        // Create the model if needed
        if( this.crateModel === true )
        {
            this.invoke( "bat:model", {
                options: {
                    nested:         true
                ,   modelName:      this.modelName
                ,   description:    "Model for the " + this.collectionName
                ,   singleton:      false
                }
            } );
        }
    }
} );
<|endoftext|>