text:
shell-utils
===========

Various shell utilities

### jp.sh - Job pool ###

This utility allows creating a named job pool limited by specified size,
and run some commands through it. That means that if a pool size is N,
at most N commands will be run simultaneously, others will wait until
a slot will be freed.

For example, to run at most 3 processes of cURL when downloading a lot of files,
wrap your cURL commands as follows:

```
./jp.sh "My Download Pool" 3 curl http://site1/...
./jp.sh "My Download Pool" 3 curl http://site2/...
./jp.sh "My Download Pool" 3 curl http://site3/...
...
```

##### Executing #####

```
USAGE: ./jp.sh <ID> <limit> <command>

Where:
  <ID>       Job pool identifier
  <limit>    Job pool size
  <command>  Command to run
```

### sysinfo.sh - System information collector ###

System information gathering utility, which you may have to run
when reporting on an issue from a running system.


text:
function LikeSum = EKFbs(parameter)
% EKF bs : Extended Kalman Filter for Black-Scholes model
% Known Inputs: Stock Index Price, Risk Free Rate
% Observation: 1 month Call Option with (ATM) Strike = S
% Hidden Latent Variable: volatility

% EKF appropriate for BS model as analytical equation (vega)
% is available for derivative of call price with respect
% to volatility, i.e., linearization of the observation
% model

% Hidden volatility state follow a regressive process

global obsS obsC obsRF TimeDelta
% Retrieve Data from Filter for Plots
global xVec hxVec MRvec volSvec

    Mu = parameter(1);
    R = parameter(2); %Measurement Noise Covariance
    Sig = parameter(3);
    Alpha = parameter(4);
    Beta = parameter(5); %Beta=M= State Transition Matrix

% Kalman filter equations are described by
% x = volatility = C+M*S(ti-1)+B*u+w  w~N(0,Q)
% y = Call Option Price  = h(x) + v   v~N(0,R)
% h(x) = Non-Linear observation = Black-Scholes Model
% H = Gradient of BS = dc/dvol = Vega
    Strike = obsS(1);
    d1 = (log(obsS(1)/Strike)+(obsRF(1)+Mu.^2/2)*TimeDelta)...
      / (Mu*sqrt(TimeDelta));
    hxVec(1) = BlackScholesCall (Strike,obsS(1),...
                TimeDelta,sqrt(abs(Mu)),obsRF(1),0);
    MRvec(1) = obsC(1) - hxVec(1);
    H = obsS(1)*sqrt(TimeDelta)*myNormPDF(d1); % Vega
    M = Alpha;
    C = Mu;
    B = Beta; % Control Input 'Matrix'
    Q = Sig*Sig*(TimeDelta);
    Pxx=Q; % Initialize State Process Covariance

	nsamples=length(obsC);
	LikeSum=0;%0.5*nsamples*log(2*pi);
    x=Mu;  xVec(1)=x;  varSvec(1) = 0;  MR = 0;

% Filter every time point
   	for i=2:nsamples
        volS= abs((obsS(i)-obsS(i-1))...
            /(sqrt(TimeDelta)*obsS(i-1))); %Control Input = u
% Regressive State Model with volS as control input
        x = M*x+B*volS+C  ;   %x(k|k-1) State Prediction
        Pxx = M*Pxx*M'+ Q ;  %P(k|k-1) Covariance Prediction

        Strike=obsS(i); % at the money call option
        d1=(log(obsS(i)/Strike)+(obsRF(i)+x.^2/2)*TimeDelta)...
                / (x*sqrt(TimeDelta));
        H=obsS(1)*sqrt(TimeDelta)*myNormPDF(d1); % Vega
        hx = BlackScholesCall (Strike,obsS(i),...
            TimeDelta,x,obsRF(i),0);         %h(x)
        MR = obsC(i) - hx ;  %Measurement (Innovation)  Residual
        Pyy = H*Pxx*H' +R; %= Residual (Innovation) Covariance
        Pxy = Pxx*H';
        K = Pxy * inv(Pyy); %Kalman Gain
        K = K/4; % Damp if EKF of BS Performs Poorly when vol ~ 0
        x = x + K*(MR) ;
        % x(k|k) State Correction based on observation res.
        % Joseph Form of Covariance Correction
        Pxx = (1-K*H)*Pxx*(1-K*H)' + K*R*K' ;
        % Pxx = Pxx - K*Pyy*K';  %Pxx(k|k) Covariance Correction
        % the max. likelihood was multiplied by -1
        % to give a minimum for fminsearch
      	LikeSum=LikeSum+0.5*log(det(Pyy))+0.5*MR'*inv(Pyy)*MR;
        % Save Information as Vector to Plot
        xVec(i)=x; hxVec(i)=hx; MRvec(i)=MR; volSvec(i) = volS;
    end

    % Force filter away from unreasonable values
    minx=min(xVec);
    if (Sig <=0), LikeSum=LikeSum+1e2; end
    if (R<1e-7), LikeSum=LikeSum+1e2; end
    if (minx<1e-6), LikeSum=LikeSum+1e10; end
    if (Alpha < 0), LikeSum=LikeSum+1e2; end
    if (Beta < 0), LikeSum=LikeSum+1e3; end
    if (Alpha + Beta >=1), LikeSum=LikeSum+1e2; end
end

redpajama_set_name: RedPajamaGithub


text:
var t = require('./types')

var manipMap = {
  must: 'must',
  should: 'should',
  mustNot: 'must_not'
}

var geoDistanceOpts = [
  'distance',
  'distance_type',
  'optimize_bbox',
  '_name',
  'ignore_malformed',
  'validation_method'
]

function snakecase (str) {
  return str.replace(/([A-Z])/g, function ($1) {
    return '_' + $1.toLowerCase()
  })
}

function NodePath (node, parent) {
  this.node = node
  this.parent = parent
  this.type = t.getType(node)
}

Object.keys(t).forEach(function (key) {
  if (key.indexOf('is') === 0) {
    NodePath.prototype[key] = function () {
      return t[key](this.node)
    }
  }
})

/**
 * Recursively search parent nodes, returning the first that is a "logic" node
 * (must, should, or mustNot), or null if no logic node ancestors are found.
 *
 * @return {NodePath|null}
 */
NodePath.prototype.findLogicParent = function findLogicParent () {
  return this.findParent(function (path) {
    return path.isMust() || path.isShould() || path.isMustNot()
  })
}

/**
 * Recursively apply a predicate function to parent nodes, returning the first
 * node that causes the predicate to return true. If no parent nodes are found,
 * return null.
 *
 * @param {function} fn Predicate function to test nodes
 * @return {NodePath|null}
 */
NodePath.prototype.findParent = function findParent (fn) {
  var parent = this.parent
  while (parent != null && !fn(parent)) {
    parent = parent.parent
  }
  return parent
}

/**
 * Return a deeply nested reference within node, or null if not found.
 *
 * @param {string|string[]} objectPath Path within node to return
 * @return {*}
 */
NodePath.prototype.get = function get (objectPath) {
  var keys = Array.isArray(objectPath) ? objectPath : objectPath.split('.')
  return keys.reduce(function (acc, key) {
    return acc && acc[key]
  }, this.node)
}

/**
 * Returns the node field value.
 *
 * @return {string}
 */
NodePath.prototype.getField = function getField () {
  if (this.type === 'exists') {
    return this.node.exists.field
  } else if (['match', 'range', 'regexp', 'term'].indexOf(this.type) >= 0) {
    return Object.keys(this.node[this.type])[0]
  } else if (this.type === 'geoDistance') {
    return Object.keys(this.node.geo_distance).filter(function (key) {
      return geoDistanceOpts.indexOf(key) === -1
    })[0]
  }
  return null
}

/**
 * Return the field/value pair of the current node.
 *
 * @return {*[]}
 */
NodePath.prototype.getPair = function getPair () {
  return [this.getField(), this.getValue()]
}

/**
 * Resolves given object path as a new path, or returns undefined if the path
 * cannot be resolved. Throws if path resolves to an illegal type.
 *
 * @param {string|string[]} objectPath Path within node to return
 * @return {NodePath}
 */
NodePath.prototype.getPath = function getPath (objectPath) {
  var keys = Array.isArray(objectPath) ? objectPath : objectPath.split('.')

  function go (acc, node) {
    if (!acc.length) {
      return node
    }

    var key = acc[0]
    var nextNode = node.node[key]
    var tmp

    if (key.match(/^\d+$/)) {
      nextNode = node.node[snakecase(node.type)][key]
    }

    if (Array.isArray(nextNode)) {
      tmp = {}
      tmp[key] = nextNode
      nextNode = tmp
    }

    if (nextNode === undefined) {
      return
    }

    return go(acc.slice(1), new NodePath(nextNode, node))
  }

  return go(keys, this)
}

/**
 * Returns sibling nodes of the current node.
 *
 * @param {string} sibling Key/index of sibling node to retrieve
 * @return {NodePath|undefined}
 */
NodePath.prototype.getSibling = function getSibling (sibling) {
  if (this.parent == null) { return }
  var sibNode = this.parent.node[snakecase(this.parent.type)][sibling]
  var tmp
  if (sibNode === undefined) { return }
  if (Array.isArray(sibNode)) {
    tmp = {}
    tmp[sibling] = sibNode
    sibNode = tmp
  }
  return new NodePath(sibNode, this.parent.parent)
}

/**
 * Returns the node value.
 *
 * @return {*}
 */
NodePath.prototype.getValue = function getValue () {
  switch (this.type) {
    case 'geoDistance':
    case 'match':
    case 'range':
    case 'regexp':
    case 'term':
      return this.node[snakecase(this.type)][this.getField()]

    default:
      return null
  }
}

/**
 * Insert given node after context node.
 *
 * @param {object} node Node to insert
 */
NodePath.prototype.insertAfter = function insertAfter (node) {
  var container = this.parent && this.parent.node[manipMap[this.parent.type]]
  if (!container) {
    throw new Error('Method not supported by this node')
  }
  container.splice(container.indexOf(this.node) + 1, 0, node)
}

/**
 * Insert given node before context node.
 *
 * @param {object} node Node to insert
 */
NodePath.prototype.insertBefore = function insertBefore (node) {
  var container = this.parent && this.parent.node[manipMap[this.parent.type]]
  if (!container) {
    throw new Error('Method not supported by this node')
  }
  container.splice(container.indexOf(this.node), 0, node)
}

NodePath.prototype.isRemovable = function isRemovable () {
  return Boolean(this.parent && this.parent.node[manipMap[this.parent.type]])
}

/**
 * Remove node from query.
 */
NodePath.prototype.remove = function remove () {
  this.replaceWithMany([])
}

/**
 * Replace current node with a given node.
 *
 * @param {object} node Node to replace this node with
 */
NodePath.prototype.replaceWith = function replaceWith (node) {
  this.replaceWithMany([node])
}

/**
 * Replace current node with a given nodes.
 *
 * @param {object[]} node Node to replace this node with
 */
NodePath.prototype.replaceWithMany = function replaceWithMany (nodes) {
  var container = this.parent && this.parent.node[manipMap[this.parent.type]]
  if (!container) {
    throw new Error('Method not supported by this node')
  }
  var args = [container.indexOf(this.node), 1].concat(nodes)
  container.splice.apply(container, args)
  this._removed = true
  this.skip()
}

/**
 * Skip traversal of the current branch.
 */
NodePath.prototype.skip = function skip () {
  this._skip = true
}

/**
 * Stop traversal entirely.
 */
NodePath.prototype.stop = function stop () {
  this._stop = true
}

module.exports = NodePath


text:
/*
 *Require:
 *- jquery.js - 1.2.6 and greater
 *- jquery.template.js
 *- tags_editor_templates.js
 *
 *
 *Usage:
 * jQuery.noConflict();
 * jQuery.listing_table = new jQuery.SimpleListing("listing_table");
 * jQuery.listing_table.deploy();
 *
 *
 * TODOs:
 * - style for a button?
 * - how to fill in selects before "analyze me" service is invoked?
 * - is web service "imune" to adding a duplicate tag?
 * - Q: Not sure about controlled=0:1 in response of GetTags.
 *     Is it that controlled==0 is a "free tag", while controlled==1 implies
 *	   a required category?
 * - Remove all console.log

 */
(function( $ ) {

//    $.SimpleListing = function(container_id){
  $.TagsEditor = function(CS){
//--------CONSTANTS

	this.ENDPOINT_TAGGING = "http://kiwi.sunsolutioncenter.de/KiWi/seam/resource/services/widgets/tagging/";
	this.FUNC_ADDTAGS = "addTags";
	this.FUNC_GETTAGS = "listTags.json";
	this.FUNC_REMTAGS = "removeTags";

	this.ENDPOINT_TAG_EXTRACT = "http://kiwi.sunsolutioncenter.de/KiWi/seam/resource/services/ie/tagExtraction/";
	this.FUNC_GET_LIST_REQ = "listOfRequiredTags";
	this.FUNC_EXTRACT_TAX_TAGS = "extractTaxonomyTags";

	this.MAX_SCORE				= 300.0;

	// Tags with MAX_SCORE get this size in "em"
    this.MAX_TAG_FONT_SIZE      = 6;

	// Tags with low score resulting with display size lower than specified
	// below are not shown in the UI (again, in "em")
	this.MIN_TAG_FONT_SIZE		= 0.5;

//--------MEMBER VARS
	this.CS = CS; //container selector
//	this.RESOURCE_URI = "http://localhost/content/7e366380-b147-455e-b2ab-5f8f1cd99b6c";//uri of the resource to be tagged...
//        this.RESOURCE_URI = "http://localhost/content/0b5ae249-b42a-4e99-9c9f-ad9dd1fccd23";
//        this.RESOURCE_URI = "http://kiwi.sunsolutioncenter.de/KiWi/content/65834035-935c-4d78-9fe2-e544190d1bff";
	this.RESOURCE_URI = "http://kiwi.sunsolutioncenter.de/KiWi/content/4bc5290e-1062-4264-b198-c9f364a3baae";

	// templates
	this.template_widget = $.template(TEMPLATE_TAGS_EDITOR_WIDGET);

	// Map of "tag" labels to DOM elements representing editable free tags
	this.freeTags = {};
	// Map of "tag" labels to DOM elements representing recommended tags in tag cloud
	this.optionalTags = {};
	// NEW - Map of "tag" labels to DOM elements representing required tags
	this.requiredTags = {};
	// Map of a category label to a tag object, may be null
	this.currentCategoryValues = {};
	// Map of required category option URIs to tag objects and option elements
	this.requiredTags = {};

//------------------------------------------------------------------------------
//-------------------- AJAX queue
//------------------------------------------------------------------------------
	this.ajaxQueue = [];

	/**
	 * Invokes a function in a "concurrency-safe" way; i.e. if there is a pending
	 * asynchronous request, the function is not called but stored in a queue,
	 * until ajaxDone is called in a callback.
	 *
	 * Adding and Removing a tag needs to be done in this way; otherwise, e.g.
	 * a click on a tag in tag cloud followed by immediate click on the same tag
	 * might end up in an inconsistent state on the server (Remove might be performed
	 * before Add on the server)
	 */
	this.safeInvoke = function(f){
		this.showProgress();

		// Put the function into the queue
		this.ajaxQueue.push(f);

		// If function f is the only element in the queue, invoke it immediately
		// (and wait for the callback to remove it from the queue)
		if (this.ajaxQueue.length == 1) {
			f.apply(this);
		} else {
//			console.log("Queuing only");
		}
	}

	/**
	 * Needs to be called in a callback of an AJAX request sent via
	 * safeInfoke. This performs other calls in sequential way until
	 * the queue is empty
	 */
	this.ajaxDone = function(){
		this.hideProgress();

//		console.log("Ajax done");
		// Remove current function from the queue; and ...
		this.ajaxQueue.shift();

		// ... if there is something more in the queue, invoke it
		if (this.ajaxQueue.length > 0) {
//			console.log("Invoking more ... deferred");
			var f = this.ajaxQueue[0];
			f.apply(this);
		}
	}

	this.progressSemaphor = 0;

	/*
	 * Shows progress indication on this widget;
	 *
	 * Actually works as a semaphor; i.e. need to call as many times hideProgress
	 * as showProgress to effectively hide the progress indication.
	 *
	 * This is useful as there are potentially two non-coordinated background
	 * efforts. First, the queued Add/Remove invocations; and then
	 * loading data (initial, or recommended tags)
	 */
	this.showProgress = function(){
		$(this.CS).find("div.tagsEditor_loading").show();
		this.progressSemaphor++;
	}

	/**
	 * Hides progress indication on this widget
	 */
	this.hideProgress = function(){
		this.progressSemaphor--;
		if (this.progressSemaphor == 0)
			$(this.CS).find("div.tagsEditor_loading").hide();
	}

//------------------------------------------------------------------------------
//-------------------- TAGS
//------------------------------------------------------------------------------

	/**
	 * Invokes GetTags web service and populates UI with tags
	 */
	this.fetchTags = function(){
		this.showProgress();

		// If we don't have live data, invoke a web service
		var self = this;
		if (!this.testData) {
			$.ajax({
						url: self.ENDPOINT_TAGGING + self.FUNC_GETTAGS + "?resource=" + self.RESOURCE_URI,
						dataType: "jsonp",
						jsonp: "jsonpCallback",
						success: function(data){
							self.onSuccessFetchTags(data);
						},
						error: function(xhr, textStatus, errorThrown){
							self.onFailureFetchTags(xhr, textStatus, errorThrown);
						}
			});
		}
		// Otherwise, simulate invocation with test data; call success callback
		// in a deferred way
		else {
			window.setTimeout(function(){
				self.onSuccessFetchTags(self.testData);
			}, 800);
		}
	}

	/**
	 * Success callback for GetTags web service invocation.
	 * Starts populating UI if we have all necessary data
	 */
	this.onSuccessFetchTags = function(data){
		this.hideProgress();
		this.suggester.setSelectedTags(data.items);
		this.tags = data

		// Only when we got both - tags assigned to this document and
		// data for the required categories, we can populate the UI
		if (this.tags && this.requiredCategoryOptions) {
			//this.buildCategoryOptions();
			this.buildTags();
			//this.setRequiredCategoryValues();
			//NEW CODE
			/*
				for(var i = 0;i<this.tags.length;i++){
					//Checking if TAG is controlled
					if(this.tags[i].controlled == 1){
						//Checking if TAG's category-list exists (category's 'required' is 'true')
						if($(this.CS).find(".tagsEditor_requiredTags").children("."+this.tags[i].prefix+"_cont").index()==0){
							//Category-list exists so just put TAG inside
							$(this.CS).find(".tagsEditor_requiredTags").children("."+this.tags[i].prefix+"_cont").append(""+this.tags[i].label+"");
						}
						//Category-list doesn't exists (category's 'required' is 'false') so we have to make additional list for it
						else{
							//We know the 'prefix' for the category but we don't know the 'label' whitch we need
							for(var j = 0;j<this.requiredCategoryOptions.length;j++){
								//We got the label - now we can make wanted additional category-list and pust TAG inside
								if(this.requiredCategoryOptions[j].prefix == this.tags[i].prefix){
									$(this.CS).find(".tagsEditor_requiredTags").append("
"+this.requiredCategoryOptions[j].label+": "+this.tags[i].label+"
");
								}
							}
						}

					}else{
						var freeTagsContainer = $(this.CS).find(".tagsEditor_freeTags");
						var tagEl = $(document.createElement("SPAN"));
						tagEl.html(this.tags[i].label + " ");
						freeTagsContainer.append(tagEl);
					}
				}

			$(this.CS).find(".tagsEditor_requiredTags").children(".category_item").children("span").children("img").click(function(){
				//ADD REMOVE ICON HANDLER from controlled required tags
				alert("required tags");
			})

			$(this.CS).find(".tagsEditor_freeTags").children("span").children("img").click(function(){
				alert("free tags");
				//ADD REMOVE ICON HANDLER from not-controlled required tags (freetags)
			})
			*/
			////END OF NEW CODE
		}
	}

	this.onFailureFetchTags = function(xhr, textStatus, errorThrown){
		this.ajaxDone();
		alert("Error getting tags:" + xhr.responseText);
	}

	/**
	 * Returns true, if given tag belongs to a required category
	 *
	 * @param	tag		A tag object, with a property "uri"
	 *
	 */
	this.isRequiredCategoryTag = function(tag) {
		return this.requiredTags[tag.uri] != null;
	}

	/**
	 *
	 */
	this.invokeAddTag = function(tagsString) {
		var self = this;

		this.safeInvoke(function(){
			// If we are not in test mode, invoke a web service
			if (!self.testAddRemove) {
				$.ajax({
					url: self.ENDPOINT_TAGGING + self.FUNC_ADDTAGS + "?resource=" + self.RESOURCE_URI + "&tags=" + tagsString,
					dataType: "jsonp",
					jsonp: "jsonpCallback",
					success: function(data){
						self.onSuccessAddTag(data);
					},
					error: function(xhr, textStatus, errorThrown){
						self.onFailureAddTag(xhr, textStatus, errorThrown);
					}
				});
			}
			// otherwise, just dummy deferred
			else {
				console.log("TODO:Would invoke AddTag webservice with tags=" + tagsString);
				window.setTimeout(function(){
					self.onSuccessAddTag();
				},500);
			}
		});
	}

	/**
	 *
	 *
	 */
	this.invokeRemoveTag = function(tagsString) {
		var self = this;
		this.safeInvoke(function(){

			// If we are not in test mode, invoke a web service
			if (!self.testAddRemove) {
				$.ajax({
					url: self.ENDPOINT_TAGGING + self.FUNC_REMTAGS + "?resource=" + self.RESOURCE_URI + "&tags=" + tagsString,
					dataType: "jsonp",
					jsonp: "jsonpCallback",
					success: function(data){
						self.onSuccessRemoveTag(data);
					},
					error: function(xhr, textStatus, errorThrown){
						self.onFailureRemoveTag(xhr, textStatus, errorThrown);
					}
				});
			}
			// otherwise, just dummy deferred
			else {
				console.log("TODO: Would invoke RemoveTag webservice with tags=" + tagsString);

				window.setTimeout(function(){
					self.onSuccessRemoveTag();
				},500);
			}
		});
	}

//------------------------------------------------------------------------------
//-------------------- FREE TAGS
//------------------------------------------------------------------------------

	/**
	 * An initialization method; binds DOM event handlers to "free tags" UI;
	 * this includes:
	 * - (x) buttons to remove a tag
	 * - Add button to add tags based on text field value
	 */
	this.bindFreeTagsHandlers = function(){
		var self = this;

		// Hookup event handlers for free tags removal
		var freeTagsContainer = $(this.CS).find(".tagsEditor_freeTags");
		freeTagsContainer.click(function(event){
			var el = $(event.target);
			if (el.attr("tagName") == "IMG") {
				var tagUri = $(el.parent()[0]).attr("taguri");
				var tag = el.parent()[0].firstChild.textContent;
				self.removeFreeTag(tag, tagUri);
				event.stopPropagation();
			}
		});

		/*
		// Hookup event handlers for free tags "Add" button
		$(this.CS).find(".tagsEditor_freeTagsEditor input[type='button']").click(function(event){
			if($(this.CS).find(".tagsEditor_freeTagsEditor input[type='text']").attr("value")!=""){
				self.confirmFreeTagsEntry();
			}
		});

		// Hookup event handleres for key events (Enter, Esc) in free tags text field
		$(this.CS).find(".tagsEditor_freeTagsEditor input[type='text']").keyup(function(event){
			if (event.keyCode == 13 && $(this.CS).find(".tagsEditor_freeTagsEditor input[type='text']").attr("value")!="") {
				self.confirmFreeTagsEntry();
			} else if (event.keyCode == 27) {
				self.cancelFreeTagsEntry();
			}
		});
		*/
	}

	/**
	 * Ends editing of tags in a text field; saves the new data in the back-end
	 * and closes the editing UI
	 */
	this.addTag = function(tagsString){
		//var tagsString = $(this.CS).find(".tagsEditor_freeTagsEditor input[type='text']").attr("value");

		// We are optimistic about the web service call; we'll invoke the
		// service, clear the UI and add the tags to the view immediately;
		// If the web service call fails (rare), we will show an error message
		// (and reloading the page will recover)

		// Populate free tags UI
		var tags = tagsString.split(",");
		var self = this;
		$.each(tags, function(i, tag){
			tag = $.trim(tag);
			self.buildFreeTag({label:tag});

			// If this tag is being added manually and it is also
			// in the tag cloud, we may need to make it "selected"
			if (self.optionalTags[tag]) {
				self.optionalTags[tag].addClass("selectedTag");
			}
		});

		this.invokeAddTag(tagsString);

		// Done with text-entry tags; clear the text field; switch the UI state
		// this.cancelFreeTagsEntry();
	}

	/**
	 * Cancels the free tag key entry and hides the UI (the text field and Add button)
	 */
	/*
	this.cancelFreeTagsEntry = function(){
		$(this.CS).find(".tagsEditor_freeTagsEditor input[type='text']").attr("value", "");
		$(this.CS).find(".tagsEditor_freeTagsEditor").hide();
		$(this.CS).find("A.tagsEditor_addTags").show();
	}
	*/

	/**
	 * Populates UI with free tags
	 */
	this.buildTags = function() {
		$(this.CS).find(".tagsEditor_freeTags").empty();
		var self = this;
		$.each(this.tags.items, function(i, tag){
			if(tag.controlled==0){
				//Tag isn't required
				self.buildFreeTag(tag);
			}else{
				self.buildRequiredTag(tag);
			}
		});
	}
	//Building function for NOT-required Tags
	this.buildFreeTag = function(tag) {
		// Ignore, if we already have this tag in the UI
		if ((this.freeTags[tag.uri]) || (tag.label == ""))
			return;

		// Create element and add it to the DOM
		var tagEl = $(document.createElement("SPAN"));
		tagEl.attr("tagUri", tag.uri);
		tagEl.html(tag.label + " ");
		//tagEl.html(tag.label + "(x) ");

		var freeTagsContainer = $(this.CS).find(".tagsEditor_freeTags");
		freeTagsContainer.append(tagEl);

		//Checking if we haven't add tag from tagCloud via suggester => in that case we have to assing selectedTag class to the selected tag in a tagCloud
		if(this.optionalTags[tag.uri]){
			this.optionalTags[tag.uri].addClass("selectedTag");
		}

		// Keep reference to the DOM element
		this.freeTags[tag.uri] = tagEl;
	}

	//Building function for REQUIRED Tags
	this.buildRequiredTag = function(tag) {
		// Ignore, if we already have this tag in the UI
		if ((this.freeTags[tag.label]) || (tag.label == ""))
			return;

		// Create element and add it to the DOM
		var tagEl = $(document.createElement("SPAN"));
		tagEl.attr("tagUri", tag.uri);
		tagEl.html(tag.label + " ");

		//Checking if TAG's category-list exists (category's 'required' is 'true')
		if($(this.CS).find(".tagsEditor_requiredTags").children("."+tag.prefix+"_cont").index()==0){
			//Category-list exists so just put TAG inside
			$(this.CS).find(".tagsEditor_requiredTags").children("."+tag.prefix+"_cont").append(tagEl);
		}
		//Category-list doesn't exists (category's 'required' is 'false') so we have to make additional list for it
		else{
			//We know the 'prefix' for the category but we don't know the 'label' whitch we need
			for(var j = 0;j<this.requiredCategoryOptions.length;j++){
				//We got the label - now we can make wanted additional category-list and pust TAG inside
				if(this.requiredCategoryOptions[j].prefix == tag.prefix){
					$(this.CS).find(".tagsEditor_requiredTags").append("
"+this.requiredCategoryOptions[j].label+":
");
					$(this.CS).find(".tagsEditor_requiredTags").children("."+tag.prefix+"_cont").append(tagEl);
				}
			}
		}

		// Keep reference to the DOM element
		this.requiredTags[tag.uri] = tagEl;
	}

	/**
	 * Adds a tag based on a tag object. Adds the tag in the backend and in the UI
	 *
	 * Is called when user selects a tag in the tag cloud
	 */
	this.addFreeTag = function(tag){
		this.buildFreeTag(tag);

		this.invokeAddTag(tag.label);
	}

	/**
	 * Success callback for AddTag; does not need to do anything
	 * (besides working with the AJAX queue)
	 */
	this.onSuccessAddTag = function(){
		this.ajaxDone();
	}

	/**
	 * Failure callback for AddTag web service invocation
	 *
	 */
	this.onFailureAddTag = function(xhr, textStatus, errorThrown){
		this.ajaxDone();
		alert("Ooops, we failed to save your new tags:"  + xhr.responseText);
	}

	/**
	 * Removes a tag. Removes it from the UI and in the backend.
	 */
	this.removeTag = function(tagUri) {
		//Checking if tag is 'recommended' = is represented in both freeTags and optionalTags
		if(this.freeTags[tagUri]&&this.optionalTags[tagUri]){
			this.optionalTags[tagUri].removeClass("selectedTag");
			// Remove the free tag from the UI aggressively;
			this.freeTags[tagUri].remove();
			// Also remove the reference to removed DOM element from freeTags
			delete this.freeTags[tagUri];
		}
		//Checking if tag is in freeTags
		else if(this.freeTags[tagUri]){
			// Remove the free tag from the UI aggressively;
			this.freeTags[tagUri].remove();
			// Also remove the reference to removed DOM element
			delete this.freeTags[tagUri];
		}
		//Checking if tag is in reuquiredTags
		else if(this.requiredTags[tagUri]){
			// Remove the required tag from the UI aggressively;
			this.requiredTags[tagUri].remove();
			// Also remove the reference to removed DOM element
			delete this.requiredTags[tagUri];
		}

		// We are optimistic about the webservice not failing;
		// If it fails (rarely), we'll show an error message; reloading the page
		// would fix the problem
		this.invokeRemoveTag(tagUri);
	}

	/**
	 * Does not need to do much ...
	 *
	 */
	this.onSuccessRemoveTag = function(){
		this.ajaxDone();
	}

	/**
	 * Shows an error message
	 *
	 */
	this.onFailureRemoveTag = function(xhr, textStatus, errorThrown){
		this.ajaxDone();
		alert("Ooops, we failed to remove a tag:" + xhr.responseText);
	}

//------------------------------------------------------------------------------
//-------------------- REQUIRED TAGS
//------------------------------------------------------------------------------

	/**
	 * Invokes a web service that returns the list of allowed tags in
	 * required categories and populates the UI (pulldowns)
	 */
	this.fetchRequiredCategoryOptions = function() {
		this.showProgress();

		var self = this;
		// If we don't have test data, invoke a web service'
		if (!this.testDataRequiredCategories) {
			$.ajax({
				url: self.ENDPOINT_TAG_EXTRACT + self.FUNC_GET_LIST_REQ + "?uri=" + self.RESOURCE_URI,
				dataType: "jsonp",
				jsonp: "jsonpCallback",
				success: function(data){
					self.onSuccessFetchRequiredCategoryOptions(data);
				},
				error: function(xhr, textStatus, errorThrown){
					self.onFailureFetchRequiredCategoryOptions(xhr, textStatus, errorThrown);
				}
			});
		}
		// Otherwise, push in the test data in a deferred way
		else {
			window.setTimeout(function(){
				self.onSuccessFetchRequiredCategoryOptions(self.testDataRequiredCategories);
			}, 600);
		}
	}

	/**
	 * Success handler for web service call to retrieve possible
	 * values of required categories
	 */
	this.onSuccessFetchRequiredCategoryOptions = function(data) {
		this.hideProgress();
		this.requiredCategoryOptions = data.required;
		// Only when we got both - tags assigned to this document and
		// data for the required categories, we can populate the UI
		/*
		if (this.tags && this.requiredCategoryOptions) {
			this.buildCategoryOptions();
			this.buildFreeTags();
			this.setRequiredCategoryValues();
		}
		*/
			//NEW CODE
			var requiredTagsContainer = $(this.CS).find(".tagsEditor_requiredTags");
			requiredTagsContainer.empty();
			for(var i = 0;i<this.requiredCategoryOptions.length;i++){
				if(this.requiredCategoryOptions[i].required == true){
					requiredTagsContainer.append("
"+this.requiredCategoryOptions[i].label+":
");
				}
			}
			//END OF NEW CODE

	}

	this.onFailureFetchRequiredCategoryOptions = function(xhr, textStatus, errorThrown){
		this.ajaxDone();
		alert("Error getting list of possible required categories: " + xhr.responseText);
	}

	/**
	 * Builds a set of SELECT compoments based on the required tags.
	 * One select per each required category.
	 *
	 * May be called repeatedly; subsequent calls clear the component and
	 * start over.
	 */
	this.buildCategoryOptions = function(){
		var requiredTagsContainer = $(this.CS).find(".tagsEditor_requiredTags");
		requiredTagsContainer.empty();

		// Reset map of required tag URIs to OPTION elements
		this.requiredTags = {};

		var self = this;
		// For each category, build one select element
		$.each(this.requiredCategoryOptions, function(i, category){
			var selectEl = document.createElement("SELECT");
			selectEl.title = category.label;
			selectEl.id = "SELECT_" + category.label;

			// Every select has a blank option ... (at least initially)
			selectEl.options[0] = new Option("", "");

			// ... and a set of options with allowed tags
			$.each(category.narrower, function(j, tag){
				var optionEl = new Option(tag.label, tag.uri);
				selectEl.options[selectEl.options.length] = optionEl;
				self.requiredTags[tag.uri] = {
					tag: tag,
					el: optionEl
				}
			});

			// When all is done, attach to the DOM
			requiredTagsContainer.append("" + category.label + " ");
			requiredTagsContainer.append(selectEl);
			requiredTagsContainer.append("
");
		});
	}

	/**
	 * Sets currently selected values in required categories (pulldowns)
	 * based on data retrieved from GetTags web service
	 */
	this.setRequiredCategoryValues = function() {
		// Go through all tags belonging to this document;
		var self = this;
		$.each(this.tags.items, function(i, tag){
			// If a tag is in a required category, find the respective
			// OPTION element and set it as currently selected in its parent SELECT
			if (self.isRequiredCategoryTag(tag)) {
				var optionEl = self.requiredTags[tag.uri].el;
				var selectEl = optionEl.parentNode;
				selectEl.value = optionEl.value;

				// Keep reference to the currently selected tag object per each category
				self.currentCategoryValues[selectEl.title] = tag;

				// Also, since this required category already has a value
				// remove the blank option - i.e. don't let user select
				// a blank option again
				if (selectEl.options[0].value == "") {
					selectEl.remove(0);
				}
			}
		});
	}

	/**
	 * Binds DOM event handlers to the pulldowns representing required
	 * categories.
	 *
	 * If user selects different option, we'll remove the current tag
	 * and add the newly selected one
	 */
	this.bindRequiredTagsHandlers = function(){
		var self = this;
		$(this.CS).find(".tagsEditor_requiredTags").bind("change", function(event){
			var selectEl = event.target;
			self.saveSelectedValue(selectEl);
		});

		$(this.CS).find(".tagsEditor_requiredTags").bind("keypress", function(event){
			var selectEl = event.target;
			self.saveSelectedValue(selectEl);
		});
	}

	this.setCategoryTag = function(uri) {
		var tag = this.requiredTags[uri];
		if (!tag)
			return;

		// Select it in the UI
		var optionEl = tag.el;
		var selectEl = optionEl.parentNode;
		selectEl.value = optionEl.value;

		// Also, since this required category already has a value
		// remove the blank option - i.e. don't let user select
		// a blank option again
		if (selectEl.options[0].value == "") {
			selectEl.remove(0);
		}

		// And save to the back-end
		this.saveSelectedValue(selectEl);

	}

	/**
	 * Removes the old tag and adds the newly selected tag in given SELECT
	 * element
	 */
	this.saveSelectedValue = function(selectEl) {
		// Lookup the previously selected tag for this category
		var oldTag = this.currentCategoryValues[selectEl.title];

		// Lookup the newly selected tag (by URI)
		var newTag = null;
		var newTagPairObject = this.requiredTags[selectEl.value];
		if (newTagPairObject) {
			newTag = newTagPairObject.tag;
		}

		// Keep reference to the newly selected tag in the map
		this.currentCategoryValues[selectEl.title] = newTag;

		// Proceed with backend changes only if new tag is different from
		// old tag; this is needed as this method may be called multiple times
		// in case of keypress

		// Old tag may be null -> need to save if new tag is not null
		if (!(
			((oldTag == null) && (newTag != null))
			|| ((oldTag != null) && (newTag != null) && (oldTag.uri != newTag.uri))
			))
		{
			return;
		}

		if (oldTag)
			this.invokeRemoveTag(oldTag.uri);

		if (newTag)
			this.invokeAddTag(newTag.label);

		// Also, if we have just selected a value remove the blank value
		// from options
		if (selectEl.options[0].value == "") {
			selectEl.remove(0);
		}
	}

//------------------------------------------------------------------------------
//-------------------- RECOMMENDED TAGS
//------------------------------------------------------------------------------

	/**
	 * Invokes a web service and populates the tag cloud with recommended tags
	 * in a callback;
	 *
	 * also sets tags into required categories if they had a blank value.
	 *
	 */
	this.fetchRecommendedTags = function(){
		this.showProgress();

		var self = this;

		// If we don't have test data, invoke a web service ...
		if (!this.testDataTagExtract) {
			$.ajax({
				url: self.ENDPOINT_TAG_EXTRACT + self.FUNC_EXTRACT_TAX_TAGS + "?uri=" + self.RESOURCE_URI,
				dataType: "jsonp",
				jsonp: "jsonpCallback",
				success: function(data){
					self.onSuccessFetchRecommendedTags(data);
				},
				error: function(xhr, textStatus, errorThrown){
					self.onFailureFetchRecommendedTags(xhr, textStatus, errorThrown);
				}
			});
		}
		// ... otherwise set the test data in
		else {
			self.onSuccessFetchRecommendedTags(self.testDataTagExtract);
		}
	}

	/**
	 * Web service success callback
	 *
	 */
	this.onSuccessFetchRecommendedTags = function(data){
		this.hideProgress();
		$(this.CS).find(".tagsEditor_tagCloud").show();

		// Option 1: Values in pulldowns are automatically adjusted after recommendation
		this.adjustRequiredCategoryValues(data);

		// Option 2: Recommended categories are only shown above tag cloud
		//this.buildRecommendedCategories(data);

		this.buildRecommendedTags(data);
	}

	this.onFailureFetchRecommendedTags = function(xhr, textStatus, errorThrown){
		this.ajaxDone();
		alert("Error extracting tags from the document: " + xhr.responseText);
	}

	/**
	 * Adjusts tags in required categories, if they are blank,
	 * based on the recommendations.
	 *
	 * If user has previously selected a value, that is different from
	 * the recommended one, a hint is shown.
	 */
	this.adjustRequiredCategoryValues = function(data){
		var self = this;
		// For each category, find SELECT with matching title
		// and possibly adjust its value
		$.each(data.required, function(i, category){

			var selectEl = $(this.CS).find(".tagsEditor_requiredTags")
				.find("SELECT[title='" + category.label + "']");

			// There should be exactly one SELECT element per each category;
			// if not, it's a system-error; not much to do about it on the UI
			// level
			if (selectEl.length == 1) {
				selectEl = selectEl[0];

				// Find the recommended tag for this category
				// i.e. the one with highest score
				var maxScore = -1;
				var maxScoreValue = null;
				$.each(category.narrower, function(j, tag){
					if (maxScore < tag.score) {
						maxScore = tag.score;
						maxScoreValue = tag.uri;
					}
				});
				//The recommended value is selected everytime + fadeOut/set/fadeIn effect.
				if (maxScoreValue) {
					$(selectEl).animate({opacity: 0.1},500,function(){
						selectEl.value = maxScoreValue;
						self.saveSelectedValue(selectEl);
						$(selectEl).animate({opacity: 1},500);
					});
				};
			}
		});
	}

	this.buildRecommendedCategories = function(data) {
		// Clear the UI
		var requiredTagsContainer = $(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_required div");
		requiredTagsContainer.empty();

		var tagsCount = 0;
		var self = this;
		// For each category, find the one tag with highest score (recommended)
		$.each(data.required, function(i, category){

			var selectEl = $(self.CS).find(".tagsEditor_requiredTags")
				.find("SELECT[title='" + category.label + "']");

			// There should be exactly one SELECT element per each category;
			// if not, it's a system-error; not much to do about it on the UI
			// level
			if (selectEl.length == 1) {
				selectEl = selectEl[0];

				// Find the recommended tag for this category
				// i.e. the one with highest score
				var maxScore = -1;
				var maxScoreTag = null;
				$.each(category.narrower, function(j, tag){
					if (maxScore < tag.score) {
						maxScore = tag.score;
						maxScoreTag = tag;
					}
				});

				// If the current value in the category is different from
				// the recommended one, show a hint
				if (selectEl.value != maxScoreTag.uri) {
					var tagEl = $(document.createElement("A"));
					tagEl.html(category.label + ": " + maxScoreTag.label);
					tagEl.attr("title", maxScoreTag.uri);
					requiredTagsContainer.append(tagEl);
					tagsCount++;
				}

			}
		});

		if (tagsCount > 0)
			$(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_required").show();

	}

	/*
	 * Builds a tag cloud based on optional tags.
	 *
	 * May be called repeatedly; subsequent calls clear the component and
	 * start over.
	 */
	this.buildRecommendedTags = function(data){

		// Clear the UI and the internal map
		var optionalTagsContainer = $(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_optional div");
		optionalTagsContainer.empty();
		this.optionalTags = {};

		// Compute average score and max score
		var totalScore = 0;
		var avgScore = 0;
		var maxScore = 0;
		$.each(data.optional, function(i, tag){
			totalScore += tag.score;

			if (tag.score > maxScore)
				maxScore = tag.score;
		});
		avgScore = totalScore / data.optional.length;

		// Compute average deviation of tags higher than average
		var sumDeviation = 0;
		var countDeviation = 0;
		$.each(data.optional, function(i, tag){
			if (tag.score > avgScore) {
				sumDeviation += tag.score - avgScore;
				countDeviation++;
			}
		});
		var avgDeviation = sumDeviation / countDeviation;

		// If the maximal score is 4x higher than average score + average deviation,
		// let the maximal score be average score + 4x avg_deviation
		if (maxScore > avgScore + 4*avgDeviation) {
			maxScore = avgScore + 4*avgDeviation;
		}

		var tagsCount = 0;
		var self = this;
		// For each tag, build one "optional" tag UI
		$.each(data.optional, function(i, tag){
			// Compute the font size for this tag;
			// effectively only show tags with sizes above threshold
			var fontSize = 0;

			// If the tag score is lower than the above computed and limited max
			// compute proportional size;
			if (tag.score < maxScore) {
				fontSize = tag.score / maxScore * self.MAX_TAG_FONT_SIZE;
			}
			// otherwise, just use max size
			else {
				fontSize = self.MAX_TAG_FONT_SIZE;
			}

			if (fontSize >= self.MIN_TAG_FONT_SIZE) {
				var tagEl = $(document.createElement("A"));
				tagEl.attr("tagUri", tag.uri);
				if (tag.controlled == 1) {
					tagEl.addClass("tagsEditor_controlled");
				}

				tagEl.html(tag.label);
				tagEl.css("font-size", fontSize + "em");
				optionalTagsContainer.append(tagEl);
				optionalTagsContainer.append(" ");

				// Keep reference to the DOM element in a map
				self.optionalTags[tag.uri] = tagEl;

				tagsCount++;
			}
		});

		if (tagsCount > 0)
			$(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_optional").show();
	}

	/**
	 * Binds DOM event handlers for the tag cloud
	 *
	 */
	this.bindRecommendedTagsHandlers = function(){
		var self = this;

		// Hookup event handler for recommended categories
		var categoriesContainer = $(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_required div");
		categoriesContainer.click(function(event){
			// URI of the recommended required tag is stored in title
			var uri = event.target.title;
			self.setCategoryTag(uri);

			$(event.target).remove();

			if (categoriesContainer[0].childElementCount == 0) {
				$(self.CS).find(".tagsEditor_tagCloud div.tagsEditor_required").hide();
			}
		});

		// Hookup event handler for tag cloud
		var optionalTagsContainer = $(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_optional div");
		optionalTagsContainer.click(function(event){
			var el = $(event.target);
			el.toggleClass("selectedTag");

			var tag = {
				label: el[0].textContent,
				controlled: el.hasClass("tagsEditor_controlled")
			}

			if (el.hasClass("selectedTag")) {
				self.addFreeTag(tag);
			} else {
				var tagUri = el.attr("taguri");
				self.removeFreeTag(tag.label, tagUri);
			}
		});
	}

	//NEW CODE
	//Combine all click-handling functions for tags
	this.bindTagsHandlers = function(){
		var self = this;

		//1st freeTagsHandler
		// Hookup event handlers for free tags removal
		var freeTagsContainer = $(this.CS).find(".tagsEditor_freeTags");
		freeTagsContainer.click(function(event){
			var el = $(event.target);
			if (el.attr("tagName") == "IMG") {
				var tagUri = $(el.parent()[0]).attr("taguri");
				//var tag = el.parent()[0].firstChild.textContent;
				self.removeTag(tagUri);
				self.suggester.selectSuggestion(tagUri);
				event.stopPropagation();
			}
		});

		//2nd recommendedTagsHandlers - tag cloud
		// Hookup event handler for recommended categories
		/*
		var categoriesContainer = $(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_required div");
		categoriesContainer.click(function(event){
			// URI of the recommended required tag is stored in title
			var uri = event.target.title;
			self.setCategoryTag(uri);

			$(event.target).remove();

			if (categoriesContainer[0].childElementCount == 0) {
				$(self.CS).find(".tagsEditor_tagCloud div.tagsEditor_required").hide();
			}
		});
		*/
		// Hookup event handler for tag cloud
		var optionalTagsContainer = $(this.CS).find(".tagsEditor_tagCloud div.tagsEditor_optional div");
		optionalTagsContainer.click(function(event){
			/*
			var el = $(event.target);
			el.toggleClass("selectedTag");

			var tag = {
				label: el[0].textContent,
				controlled: el.hasClass("tagsEditor_controlled")
			}

			if (el.hasClass("selectedTag")) {
				alert(el.textContent);
				self.addFreeTag(tag);
			} else {
				var tagUri = el.attr("taguri");
				self.removeTag(tagUri);
			}
			*/
			var el = $(event.target);
			//Checking if we are clicking on a tag (represented by link)
			if (el.attr("tagName") == "A") {
				//Checking if tag isn't already selected => already in free tag container
				if (el.hasClass("selectedTag")) {
					//If is tag already selected = is already in freeTags map and container => remove him
					el.removeClass("selectedTag");
					self.removeTag(el.attr("tagUri"));
					self.suggester.selectSuggestion(el.attr("tagUri"));
				} else {
					//Tag hasn't been selected yet => build up new free tag
					el.addClass("selectedTag");
					var tag = {
						label: el[0].textContent,
						uri: el.attr("tagUri")
					}
					self.addFreeTag(tag);
				}
			}
		});

		//3rd requiredTagsHandlers
		var requiredTagsContainer = $(this.CS).find(".tagsEditor_requiredTags");
		requiredTagsContainer.click(function(event){
			var el = $(event.target);
			if (el.attr("tagName") == "IMG") {
				var tagUri = $(el.parent()[0]).attr("taguri");
				//var tag = el.parent()[0].firstChild.textContent;
				self.removeTag(tagUri);
				self.suggester.selectSuggestion(tagUri);
				event.stopPropagation();
			}
		});

	}
	//END OF NEW CODE

//------------------------------------------------------------------------------
//-------------------- DEPLOY
//------------------------------------------------------------------------------

	/**
	 * Disables ability to select and drag text in a DOM element.
	 * This is useful to disable in tag cloud where a subsequent click
	 * on a tag may result in unwanted text selection
	 *
	 * Courtesy of:
	 * http://www.dynamicdrive.com/dynamicindex9/noselect.htm
	 */
	this.disableSelection = function(target){
		if (typeof target.onselectstart!="undefined") //IE route
			target.onselectstart=function(){return false}
		else if (typeof target.style.MozUserSelect!="undefined") //Firefox route
			target.style.MozUserSelect="none"
		else //All other route (ie: Opera)
			target.onmousedown=function(){return false}
			target.style.cursor = "default"
	}

	/**
	 * Builds the DOM of this component and initiates data fetching
	 *
	 */
	this.deploy = function(){
		// TODO: This was breaking selection in the text field; need a fix
		//this.disableSelection($(this.CS)[0]);

		// Create basic DOM skeleton
		$(this.CS).append(this.template_widget, {});

		// Hook up handlers for Add Tags and Recommend Tags links
		var self = this;
		$(this.CS).find("A.tagsEditor_addTags").click(function(event){
			$(self.CS).find("A.tagsEditor_addTags").hide();
			$(self.CS).find(".tagsEditor_freeTagsEditor").show();

			// Activate the suggester component
			self.suggester.show({parent:$(self.CS).find("div.suggesterDiv")});

		});
		$(this.CS).find("A.tagsEditor_recommendTags").click(function(event){
			$(self.CS).find("A.tagsEditor_recommendTags").hide();
			self.fetchRecommendedTags();
		});

		// Initialize suggester
		this.suggester = new jQuery.Suggester();
		this.suggester.addTag = this.addTag;

		this.suggester.onRemoveTag = jQuery.proxy(function(uri){
			this.removeTag(uri);
		},this);

		this.suggester.onAddTag = jQuery.proxy(function(label,uri){
			var tag = {
				label: label,
				uri: uri
			}
			this.buildFreeTag(tag);
		},this);

		// Hookup some more DOM event handlers
		//this.bindFreeTagsHandlers();
		//this.bindRecommendedTagsHandlers();
		//this.bindRequiredTagsHandlers();
		this.bindTagsHandlers();

		// Start fetching initial data
		this.fetchRequiredCategoryOptions();
		this.fetchTags(this.RESOURCE_URI);
	}

  };
})(jQuery);


text:
<project name="PassThrough" default="PassThroughClient" basedir=".">

  <description>PassThrough Client</description>

  <property name="src" location="." />
  <property name="gen" location="../gen-java" />
  <property name="build" location="build" />

  <path id="libs.classpath">
    <fileset dir="/usr/share/java">
      <include name="*.jar" />
      <exclude name="-test.jar" />
    </fileset>
    <fileset dir="/usr/share/java/slf4j">
      <include name="api.jar" />
      <include name="log4j12.jar" />
    </fileset>
  </path>
  <path id="build.classpath">
    <path refid="libs.classpath" />
    <pathelement path="${gen}" />
  </path>
  <path id="passthrough.classpath">
    <path refid="build.classpath" />
    <pathelement path="${build}" />
  </path>

  <target name="init">
    <tstamp />
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init">
    <javac srcdir="${gen}" destdir="${build}" classpathref="libs.classpath" />
    <javac srcdir="${src}" destdir="${build}" classpathref="build.classpath" />
  </target>

  <target name="PassThroughClient" description="Run PassThrough Client" depends="compile">
    <java classname="PassThroughClient"
      classpathref="passthrough.classpath" failonerror="true">
    </java>
  </target>

  <target name="clean">
    <delete dir="${build}" />
  </target>

</project>


text:
// Copyright © 2010 onwards, Andrew Whewell
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//    * Neither the name of the author nor the names of the program's contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VirtualRadar.Interface.Database;
using InterfaceFactory;
using VirtualRadar.Interface.Settings;
using Moq;
using System.IO;
using Test.Framework;

namespace Test.VirtualRadar.Database
{
    [TestClass]
    public class LogDatabaseTests
    {
        #region TestContext, Fields, TestInitialise, TestCleanup
        public TestContext TestContext { get; set; }

        private const string _FileName = "ConnectionLog.sqb";
        private string _FullPath;
        private ILogDatabase _LogDatabase;
        private Mock<ILogDatabaseProvider> _Provider;
        private IClassFactory _OriginalFactory;
        private Mock<IConfigurationStorage> _ConfigurationStorage;
        private readonly string[] _Cultures = new string[] { "en-GB", "de-DE", "fr-FR", "it-IT", "el-GR", "ru-RU" };

        [TestInitialize]
        public void TestInitialise()
        {
            _OriginalFactory = Factory.TakeSnapshot();

            _ConfigurationStorage = TestUtilities.CreateMockSingleton<IConfigurationStorage>();
            _ConfigurationStorage.Setup(m => m.Folder).Returns(TestContext.TestDeploymentDir);

            _FullPath = Path.Combine(_ConfigurationStorage.Object.Folder, _FileName);

            _LogDatabase = Factory.Singleton.Resolve<ILogDatabase>();

            _Provider = new Mock<ILogDatabaseProvider>() { DefaultValue = DefaultValue.Mock }.SetupAllProperties();
            _Provider.Setup(m => m.UtcNow).Returns(() => { return DateTime.UtcNow; });
            _LogDatabase.Provider = _Provider.Object;
        }

        [TestCleanup]
        public void TestCleanup()
        {
            Factory.RestoreSnapshot(_OriginalFactory);

            if(_LogDatabase != null) _LogDatabase.Dispose();
            _LogDatabase = null;

            if(File.Exists(_FullPath)) File.Delete(_FullPath);
        }
        #endregion

        #region Constructor and Properties
        [TestMethod]
        public void LogDatabase_Constructor_Initialises_To_Known_State_And_Properties_Work()
        {
            Assert.IsNotNull(_LogDatabase.Singleton);

            _LogDatabase.Dispose();
            _LogDatabase = Factory.Singleton.Resolve<ILogDatabase>();
            Assert.IsNotNull(_LogDatabase.Provider);
            TestUtilities.TestProperty(_LogDatabase, "Provider", _LogDatabase.Provider, _Provider.Object);
        }

        [TestMethod]
        public void LogDatabase_Singleton_Returns_Single_Instance_Of_Database()
        {
            var firstInstance = _LogDatabase.Singleton;
            using(var innerDatabase = Factory.Singleton.Resolve<ILogDatabase>()) {
                Assert.AreSame(firstInstance, innerDatabase.Singleton);
            }
        }
        #endregion

        #region EstablishSession
        [TestMethod]
        public void LogDatabase_EstablishSession_Creates_Database_File_If_It_Does_Not_Exist()
        {
            Assert.IsFalse(File.Exists(_FullPath));
            _LogDatabase.EstablishSession("1.2.3.4");
            Assert.IsTrue(File.Exists(_FullPath));
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Writes_Client_Record()
        {
            _LogDatabase.EstablishSession("88.77.66.55");

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(1, clients.Count);
            var client = clients[0];
            Assert.AreEqual("88.77.66.55", client.IpAddress);
            Assert.AreNotEqual(0, client.Id);
            Assert.IsNull(client.ReverseDns);
            Assert.IsNull(client.ReverseDnsDate);
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Writes_Client_Record_In_Different_Cultures()
        {
            foreach(var culture in _Cultures) {
                using(var switcher = new CultureSwitcher(culture)) {
                    TestCleanup();
                    TestInitialise();

                    try {
                        LogDatabase_EstablishSession_Writes_Client_Record();
                    } catch(Exception ex) {
                        throw new InvalidOperationException(String.Format("Exception thrown when culture was {0}", culture), ex);
                    }
                }
            }
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Writes_Different_Client_Records_For_Different_IP_Addresses()
        {
            _LogDatabase.EstablishSession("88.77.66.55");
            _LogDatabase.EstablishSession("55.66.77.88");

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(2, clients.Count);

            var client1 = clients.Where(c => c.IpAddress == "88.77.66.55").Single();
            var client2 = clients.Where(c => c.IpAddress == "55.66.77.88").Single();
            Assert.AreNotEqual(0, client1.Id);
            Assert.AreNotEqual(0, client2.Id);
            Assert.AreNotEqual(client1.Id, client2.Id);
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Does_Not_Write_Multiple_Client_Records_For_The_Same_Session()
        {
            _LogDatabase.EstablishSession("88.77.66.55");
            _LogDatabase.EstablishSession("88.77.66.55");

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(1, clients.Count);
            Assert.AreEqual("88.77.66.55", clients[0].IpAddress);
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Writes_Session_Record()
        {
            var now = new DateTime(2011, 7, 6, 5, 4, 3);
            _Provider.Setup(m => m.UtcNow).Returns(now);

            _LogDatabase.EstablishSession("88.77.66.55");

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(1, sessionsMap.Count);
            var client = clients[0];
            Assert.AreEqual(1, sessionsMap[client.Id].Count);
            var session = sessionsMap[clients[0].Id][0];

            Assert.AreNotEqual(0, session.Id);
            Assert.AreEqual(client.Id, session.Id);
            Assert.AreEqual(0L, session.AudioBytesSent);
            Assert.AreEqual(0L, session.CountRequests);
            Assert.AreEqual(new TimeSpan(0), session.Duration);
            Assert.AreEqual(now, session.EndTime);
            Assert.AreEqual(0L, session.HtmlBytesSent);
            Assert.AreEqual(0L, session.ImageBytesSent);
            Assert.AreEqual(0L, session.JsonBytesSent);
            Assert.AreEqual(0L, session.OtherBytesSent);
            Assert.AreEqual(now, session.StartTime);
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Writes_Session_Record_In_Different_Cultures()
        {
            foreach(var culture in _Cultures) {
                using(var switcher = new CultureSwitcher(culture)) {
                    TestCleanup();
                    TestInitialise();

                    try {
                        LogDatabase_EstablishSession_Writes_Session_Record();
                    } catch(Exception ex) {
                        throw new InvalidOperationException(String.Format("Exception thrown when culture was {0}", culture), ex);
                    }
                }
            }
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Writes_Multiple_Sessions_For_Same_IP_Address()
        {
            _LogDatabase.EstablishSession("88.77.66.55");
            _LogDatabase.EstablishSession("88.77.66.55");

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(1, sessionsMap.Count);
            var sessions = sessionsMap[clients[0].Id];

            Assert.AreEqual(2, sessions.Count);
            Assert.AreNotEqual(0, sessions[0].Id);
            Assert.AreNotEqual(0, sessions[1].Id);
            Assert.AreNotEqual(sessions[0].Id, sessions[1].Id);
        }

        [TestMethod]
        public void LogDatabase_EstablishSession_Returns_Session_Record()
        {
            var now = new DateTime(2011, 7, 6, 5, 4, 3);
            _Provider.Setup(m => m.UtcNow).Returns(now);

            var session = _LogDatabase.EstablishSession("88.77.66.55");

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(1, sessionsMap.Count);
            var client = clients[0];
            Assert.AreEqual(1, sessionsMap[client.Id].Count);
            var fetchedSession = sessionsMap[clients[0].Id][0];

            Assert.AreEqual(session.Id, fetchedSession.Id);
            Assert.AreEqual(client.Id, session.Id);

            Assert.AreEqual(fetchedSession.Id, session.Id);
            Assert.AreEqual(fetchedSession.AudioBytesSent, session.AudioBytesSent);
            Assert.AreEqual(fetchedSession.CountRequests, session.CountRequests);
            Assert.AreEqual(fetchedSession.Duration, session.Duration);
            Assert.AreEqual(fetchedSession.EndTime, session.EndTime);
            Assert.AreEqual(fetchedSession.HtmlBytesSent, session.HtmlBytesSent);
            Assert.AreEqual(fetchedSession.ImageBytesSent, session.ImageBytesSent);
            Assert.AreEqual(fetchedSession.JsonBytesSent, session.JsonBytesSent);
            Assert.AreEqual(fetchedSession.OtherBytesSent, session.OtherBytesSent);
            Assert.AreEqual(fetchedSession.StartTime, session.StartTime);
        }
        #endregion

        #region UpdateSession
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void LogDatabase_UpdateSession_Throws_If_Passed_Null()
        {
            var session = _LogDatabase.EstablishSession("7.6.5.4");
            _LogDatabase.UpdateSession(null);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void LogDatabase_UpdateSession_Throws_If_Database_Not_Open()
        {
            LogSession session;
            using(var otherConnection = Factory.Singleton.Resolve<ILogDatabase>()) {
                session = otherConnection.EstablishSession("1.2.3.4");
            }

            _LogDatabase.UpdateSession(session);
        }

        [TestMethod]
        public void LogDatabase_UpdateSession_Updates_Existing_Session_Record()
        {
            DateTime then = new DateTime(2010, 9, 8, 7, 6, 5);
            DateTime now = then.AddSeconds(1);
            DateTime nowPlusOne = now.AddSeconds(1);
            _Provider.Setup(m => m.UtcNow).Returns(then);

            var session = _LogDatabase.EstablishSession("7.6.5.4");
            var originalClientId = session.ClientId;
            var otherClientId = _LogDatabase.EstablishSession("8.8.4.4").ClientId;

            session.AudioBytesSent = 1L;
            session.ClientId = otherClientId;
            session.CountRequests = 2L;
            session.EndTime = nowPlusOne;
            session.HtmlBytesSent = 3L;
            session.ImageBytesSent = 4L;
            session.JsonBytesSent = 5L;
            session.OtherBytesSent = 6L;
            session.StartTime = now;

            var id = session.Id;

            _LogDatabase.UpdateSession(session);

            var clients = new List<LogClient>();
            var sessionMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionMap);

            Assert.AreEqual(2, sessionMap[otherClientId].Count);  // <-- would be 1 if we didn't allow a change of client ID
            Assert.AreEqual(false, sessionMap.ContainsKey(originalClientId));  // <-- would be true if we didn't allow a change of client ID

            var sessions = sessionMap[otherClientId];
            Assert.AreEqual(2, sessions.Count);
            var updatedSession = sessions.Where(s => s.Id == id).Single();

            Assert.AreEqual(1L, updatedSession.AudioBytesSent);
            Assert.AreEqual(otherClientId, updatedSession.ClientId); // <-- would be originalClientId if we didn't allow a change of client ID
            Assert.AreEqual(2L, updatedSession.CountRequests);
            Assert.AreEqual(nowPlusOne - now, updatedSession.Duration);
            Assert.AreEqual(nowPlusOne, updatedSession.EndTime);
            Assert.AreEqual(3L, updatedSession.HtmlBytesSent);
            Assert.AreEqual(id, updatedSession.Id);
            Assert.AreEqual(4L, updatedSession.ImageBytesSent);
            Assert.AreEqual(5L, updatedSession.JsonBytesSent);
            Assert.AreEqual(6L, updatedSession.OtherBytesSent);
            Assert.AreEqual(now, updatedSession.StartTime);
        }

        [TestMethod]
        public void LogDatabase_UpdateSession_Updates_Existing_Session_Record_In_Different_Cultures()
        {
            foreach(var culture in _Cultures) {
                using(var switcher = new CultureSwitcher(culture)) {
                    TestCleanup();
                    TestInitialise();

                    try {
                        LogDatabase_UpdateSession_Updates_Existing_Session_Record();
                    } catch(Exception ex) {
                        throw new InvalidOperationException(String.Format("Exception thrown when culture was {0}", culture), ex);
                    }
                }
            }
        }
        #endregion

        #region UpdateClient
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void LogDatabase_UpdateClient_Throws_If_Client_Is_Null()
        {
            _LogDatabase.EstablishSession("1.2.3.4");
            _LogDatabase.UpdateClient(null);
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void LogDatabase_UpdateClient_Throws_If_Database_Is_Not_Open()
        {
            LogClient client;
            using(var otherConnection = Factory.Singleton.Resolve<ILogDatabase>()) {
                otherConnection.EstablishSession("1.2.3.4");
                var clients = new List<LogClient>();
                var sessionMap = new Dictionary<long, IList<LogSession>>();
                otherConnection.FetchAll(clients, sessionMap);
                client = clients[0];
            }

            _LogDatabase.UpdateClient(client);
        }

        [TestMethod]
        public void LogDatabase_UpdateClient_Updates_Existing_Client_Records()
        {
            _LogDatabase.EstablishSession("1.2.3.4");

            var clients = new List<LogClient>();
            var sessionMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionMap);

            var now = new DateTime(2010, 9, 8, 7, 6, 5);

            var client = clients[0];
            var id = client.Id;
            client.IpAddress = "8.7.6.5";
            client.ReverseDns = "http://www.youtube.com/watch?v=QH2-TGUlwu4";
            client.ReverseDnsDate = now;

            _LogDatabase.UpdateClient(client);

            clients.Clear();
            sessionMap.Clear();

            _LogDatabase.FetchAll(clients, sessionMap);
            Assert.AreEqual(1, clients.Count);
            var updatedClient = clients[0];

            Assert.AreEqual(id, updatedClient.Id);
            Assert.AreEqual("8.7.6.5", updatedClient.IpAddress);
            Assert.AreEqual("http://www.youtube.com/watch?v=QH2-TGUlwu4", updatedClient.ReverseDns);
            Assert.AreEqual(now, updatedClient.ReverseDnsDate);
        }

        [TestMethod]
        public void LogDatabase_UpdateClient_Updates_Existing_Client_Records_In_Different_Cultures()
        {
            foreach(var culture in _Cultures) {
                using(var switcher = new CultureSwitcher(culture)) {
                    TestCleanup();
                    TestInitialise();

                    try {
                        LogDatabase_UpdateClient_Updates_Existing_Client_Records();
                    } catch(Exception ex) {
                        throw new InvalidOperationException(String.Format("Exception thrown when culture was {0}", culture), ex);
                    }
                }
            }
        }
        #endregion

        #region FetchAll
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void LogDatabase_FetchAll_Throws_If_Client_List_Is_Null()
        {
            _LogDatabase.FetchAll(null, new Dictionary<long, IList<LogSession>>());
        }

        [TestMethod]
        public void LogDatabase_FetchAll_Creates_Database_File_If_Missing()
        {
            Assert.IsFalse(File.Exists(_FullPath));
            _LogDatabase.FetchAll(new List<LogClient>(), null);
            Assert.IsTrue(File.Exists(_FullPath));
        }

        [TestMethod]
        public void LogDatabase_FetchAll_Does_Nothing_If_Database_Is_Empty()
        {
            var clients = new List<LogClient>();
            var sessionMap = new Dictionary<long, IList<LogSession>>();

            _LogDatabase.FetchAll(clients, sessionMap);

            Assert.AreEqual(0, clients.Count);
            Assert.AreEqual(0, sessionMap.Count);
        }

        [TestMethod]
        public void LogDatabase_FetchAll_Can_Return_List_Of_Every_Client()
        {
            _LogDatabase.EstablishSession("1.2.3.4");
            _LogDatabase.EstablishSession("1.0.0.1");

            var clients = new List<LogClient>();
            _LogDatabase.FetchAll(clients, null);

            Assert.AreEqual(2, clients.Count);
            Assert.IsTrue(clients.Where(c => c.IpAddress == "1.2.3.4").Any());
            Assert.IsTrue(clients.Where(c => c.IpAddress == "1.0.0.1").Any());
        }

        [TestMethod]
        public void LogDatabase_FetchAll_Clears_Collections_Before_Use()
        {
            _LogDatabase.EstablishSession("1.2.3.4");

            var clients = new List<LogClient>();
            var sessionMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionMap);
            _LogDatabase.FetchAll(clients, sessionMap);

            Assert.AreEqual(1, clients.Count);
            Assert.AreEqual(1, sessionMap.Count);
            Assert.AreEqual(1, sessionMap[clients[0].Id].Count);
        }

        [TestMethod]
        public void LogDatabase_FetchAll_Can_Return_List_Of_Every_Client_And_Session()
        {
            var now = new DateTime(2007, 6, 5, 4, 3, 2);

            _Provider.Setup(m => m.UtcNow).Returns(now);
            _LogDatabase.EstablishSession("1.2.3.4");

            _Provider.Setup(m => m.UtcNow).Returns(now.AddSeconds(1));
            _LogDatabase.EstablishSession("1.2.3.4");

            _Provider.Setup(m => m.UtcNow).Returns(now.AddSeconds(2));
            _LogDatabase.EstablishSession("8.7.6.5");

            var clients = new List<LogClient>();
            var sessionMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionMap);

            Assert.AreEqual(2, clients.Count);
            var client1 = clients.Where(c => c.IpAddress == "1.2.3.4").Single();
            var client2 = clients.Where(c => c.IpAddress == "8.7.6.5").Single();
            Assert.AreNotEqual(client1.Id, client2.Id);

            var sessions1 = sessionMap[client1.Id];
            var sessions2 = sessionMap[client2.Id];

            Assert.AreEqual(2, sessions1.Count);
            Assert.AreEqual(1, sessions2.Count);

            Assert.AreEqual(2, sessions1.Where(s => s.ClientId == client1.Id).Count());
            Assert.AreNotEqual(sessions1[0].Id, sessions1[1].Id);
            Assert.IsTrue(sessions1.Where(s => s.StartTime == now).Any());
            Assert.IsTrue(sessions1.Where(s => s.StartTime == now.AddSeconds(1)).Any());
        }
        #endregion

        #region FetchSessions
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void LogDatabase_FetchSessions_Throws_If_Sessions_List_Is_Null()
        {
            _LogDatabase.FetchSessions(new List<LogClient>(), null, DateTime.Now, DateTime.Now);
        }

        [TestMethod]
        public void LogDatabase_FetchSessions_Creates_Database_File_If_Missing()
        {
            Assert.IsFalse(File.Exists(_FullPath));
            _LogDatabase.FetchSessions(new List<LogClient>(), new List<LogSession>(), DateTime.Now, DateTime.Now);
            Assert.IsTrue(File.Exists(_FullPath));
        }

        [TestMethod]
        public void LogDatabase_FetchSessions_Can_Fetch_List_Of_Sessions_Within_Date_Range()
        {
            DateTime now = new DateTime(2001, 2, 3, 4, 5, 6);
            _Provider.Setup(m => m.UtcNow).Returns(now);

            _LogDatabase.EstablishSession("4.5.6.7");

            var sessions = new List<LogSession>();
            _LogDatabase.FetchSessions(null, sessions, now.ToLocalTime(), now.ToLocalTime());

            Assert.AreEqual(1, sessions.Count);
            Assert.AreEqual(now, sessions[0].StartTime);
        }

        [TestMethod]
        public void LogDatabase_FetchSessions_Can_Fetch_List_Of_Clients_And_Sessions_Within_Date_Range()
        {
            var now = new DateTime(2007, 6, 5, 4, 3, 2);

            _Provider.Setup(m => m.UtcNow).Returns(now);
            _LogDatabase.EstablishSession("1.2.3.4");

            _Provider.Setup(m => m.UtcNow).Returns(now.AddSeconds(1));
            _LogDatabase.EstablishSession("1.2.3.4");

            _Provider.Setup(m => m.UtcNow).Returns(now.AddSeconds(2));
            _LogDatabase.EstablishSession("8.7.6.5");

            var clients = new List<LogClient>();
            var sessions = new List<LogSession>();
            _LogDatabase.FetchSessions(clients, sessions, now.ToLocalTime(), now.ToLocalTime().AddSeconds(2));

            Assert.AreEqual(2, clients.Count);
            var client1 = clients.Where(c => c.IpAddress == "1.2.3.4").Single();
            var client2 = clients.Where(c => c.IpAddress == "8.7.6.5").Single();
            Assert.AreNotEqual(client1.Id, client2.Id);

            Assert.AreEqual(3, sessions.Count);
            Assert.AreEqual(2, sessions.Where(s => s.ClientId == client1.Id).Count());
            Assert.AreEqual(1, sessions.Where(s => s.ClientId == client2.Id).Count());

            Assert.IsTrue(sessions.Where(s => s.StartTime == now).Any());
            Assert.IsTrue(sessions.Where(s => s.StartTime == now.AddSeconds(1)).Any());
            Assert.IsTrue(sessions.Where(s => s.StartTime == now.AddSeconds(2)).Any());
        }

        [TestMethod]
        public void LogDatabase_FetchSessions_Uses_Local_Dates()
        {
            // It's a bit odd that it uses local dates, really it should be storing UTC - but the code was originally
            // written this way and I can't go changing it now without breaking existing installations...

            var dateTime = new DateTime(2008, 1, 1, 0, 0, 0);
            for(int day = 0;day < 366;++day) {
                dateTime = dateTime.AddDays(1);
                if(dateTime.ToLocalTime().Hour != dateTime.Hour) break;
            }

            _Provider.Setup(m => m.UtcNow).Returns(dateTime);

            _LogDatabase.EstablishSession("1.2.3.4");

            var sessions = new List<LogSession>();

            _LogDatabase.FetchSessions(null, sessions, dateTime, dateTime);
            Assert.AreEqual(0, sessions.Count);

            _LogDatabase.FetchSessions(null, sessions, dateTime.ToLocalTime(), dateTime.ToLocalTime());
            Assert.AreEqual(1, sessions.Count);
        }

        [TestMethod]
        public void LogDatabase_FetchSessions_Uses_Inclusive_Date_Ranges()
        {
            var before = new DateTime(2001, 2, 3, 10, 9, 8);
            var start = before.AddSeconds(1);
            var between = start.AddSeconds(1);
            var end = between.AddSeconds(1);
            var after = end.AddSeconds(1);

            _Provider.Setup(m => m.UtcNow).Returns(before);
            _LogDatabase.EstablishSession("1.1.1.1");
            _Provider.Setup(m => m.UtcNow).Returns(start);
            _LogDatabase.EstablishSession("2.2.2.2");
            _Provider.Setup(m => m.UtcNow).Returns(between);
            _LogDatabase.EstablishSession("3.3.3.3");
            _Provider.Setup(m => m.UtcNow).Returns(end);
            _LogDatabase.EstablishSession("4.4.4.4");
            _Provider.Setup(m => m.UtcNow).Returns(after);
            _LogDatabase.EstablishSession("5.5.5.5");

            var sessions = new List<LogSession>();
            _LogDatabase.FetchSessions(null, sessions, start.ToLocalTime(), end.ToLocalTime());

            Assert.AreEqual(3, sessions.Count);
            Assert.IsTrue(sessions.Where(s => s.StartTime.Second == start.Second).Any());
            Assert.IsTrue(sessions.Where(s => s.StartTime.Second == between.Second).Any());
            Assert.IsTrue(sessions.Where(s => s.StartTime.Second == end.Second).Any());
        }

        [TestMethod]
        public void LogDatabase_FetchSessions_Compares_Start_And_End_Times()
        {
            var session1 = _LogDatabase.EstablishSession("1.1.1.1");
            var session2 = _LogDatabase.EstablishSession("1.1.1.1");
            var session3 = _LogDatabase.EstablishSession("1.1.1.1");
            var session4 = _LogDatabase.EstablishSession("1.1.1.1");

            var time1 = new DateTime(2001, 1, 1, 0, 0, 0);
            var time2 = time1.AddSeconds(1);
            var time3 = time2.AddSeconds(1);
            var time4 = time3.AddSeconds(1);
            var time5 = time4.AddSeconds(1);

            session1.StartTime = time1;
            session1.EndTime = session2.StartTime = time2;
            session2.EndTime = session3.StartTime = time3;
            session3.EndTime = session4.StartTime = time4;
            session4.EndTime = time5;

            _LogDatabase.UpdateSession(session1);
            _LogDatabase.UpdateSession(session2);
            _LogDatabase.UpdateSession(session3);
            _LogDatabase.UpdateSession(session4);

            var sessions = new List<LogSession>();
            _LogDatabase.FetchSessions(null, sessions, time3.ToLocalTime(), time3.ToLocalTime());

            Assert.AreEqual(2, sessions.Count);
            Assert.IsTrue(sessions.Where(s => s.StartTime == time3).Any());
            Assert.IsTrue(sessions.Where(s => s.EndTime == time3).Any());
        }
        #endregion

        #region Transactions
        [TestMethod]
        public void LogDatabase_Transactions_Can_Commit_Operations_To_Database()
        {
            _LogDatabase.StartTransaction();
            _LogDatabase.EstablishSession("88.77.66.55");
            _LogDatabase.EndTransaction();

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(1, clients.Count);
            var client = clients[0];
            Assert.AreEqual("88.77.66.55", client.IpAddress);
            Assert.AreNotEqual(0, client.Id);
            Assert.AreEqual(1, sessionsMap.Values.Count);
        }

        [TestMethod]
        public void LogDatabase_Transactions_Can_Rollback_Inserts()
        {
            _LogDatabase.StartTransaction();
            _LogDatabase.EstablishSession("88.77.66.55");
            _LogDatabase.RollbackTransaction();

            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);

            Assert.AreEqual(0, clients.Count);
            Assert.AreEqual(0, sessionsMap.Count);
        }

        [TestMethod]
        public void LogDatabase_Transactions_Can_Be_Nested()
        {
            _LogDatabase.StartTransaction();
            _LogDatabase.EstablishSession("88.77.66.55");

            _LogDatabase.StartTransaction();
            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);
            clients[0].ReverseDns = "hello";
            _LogDatabase.UpdateClient(clients[0]);
            _LogDatabase.EndTransaction();

            _LogDatabase.EndTransaction();

            _LogDatabase.FetchAll(clients, sessionsMap);
            Assert.AreEqual(1, clients.Count);
            Assert.AreEqual("hello", clients[0].ReverseDns);
        }

        [TestMethod]
        public void LogDatabase_Transactions_Can_Rollback_Outer_Level_When_Inner_Level_Rollsback()
        {
            _LogDatabase.StartTransaction();
            _LogDatabase.EstablishSession("88.77.66.55");

            _LogDatabase.StartTransaction();
            var clients = new List<LogClient>();
            var sessionsMap = new Dictionary<long, IList<LogSession>>();
            _LogDatabase.FetchAll(clients, sessionsMap);
            clients[0].ReverseDns = "hello";
            _LogDatabase.UpdateClient(clients[0]);
            _LogDatabase.RollbackTransaction();

            _LogDatabase.EndTransaction();

            _LogDatabase.FetchAll(clients, sessionsMap);
            Assert.AreEqual(0, clients.Count);
            Assert.AreEqual(0, sessionsMap.Count);
        }
        #endregion
    }
}


text:
	#define PLATFORM_COMPILER "clang"
	#define PLATFORM_COMPILER_VERSION CONCAT_PERIOD( STRINGIZE(__clang_major__), STRINGIZE(__clang_minor__))
	#define PLATFORM_FANCY_FUNCTION __PRETTY_FUNCTION__
#elif defined( __GNUC__ )
	#define PLATFORM_COMPILER "gcc"
	#define PLATFORM_COMPILER_VERSION CONCAT_PERIOD( STRINGIZE(__GNUC__), STRINGIZE(__GNUC_MINOR__) )
	#define PLATFORM_FANCY_FUNCTION __PRETTY_FUNCTION__
#elif defined( _MSC_VER )
	#define PLATFORM_COMPILER "msvc"
	#if _MSC_VER < 1300
		#define PLATFORM_COMPILER_VERSION STRINGIZE(12.0)
	#elif _MSC_VER == 1300
		#define PLATFORM_COMPILER_VERSION STRINGIZE(13.0) // Visual Studio 2002
	#elif _MSC_VER == 1310
		#define PLATFORM_COMPILER_VERSION STRINGIZE(13.1)
	#elif _MSC_VER == 1400
		#define PLATFORM_COMPILER_VERSION STRINGIZE(14.0) // Visual Studio 2005
	#elif _MSC_VER == 1500
		#define PLATFORM_COMPILER_VERSION STRINGIZE(15.0) // Visual Studio 2008
	#elif _MSC_VER == 1600
		#define PLATFORM_COMPILER_VERSION STRINGIZE(16.0) // Visual Studio 2010
	#elif _MSC_VER == 1700
		#define PLATFORM_COMPILER_VERSION STRINGIZE(17.0) // Visual Studio 2012
	#else
		#define PLATFORM_COMPILER_VERSION "Unknown version of msvc"
	#endif
	#define PLATFORM_FANCY_FUNCTION __FUNCSIG__
#endif

// Define the following macros for this platform:
// PLATFORM_NAME - a string describing the OS at compile time (win32, linux, macosx)
#if (defined(_WIN32) || defined(_WIN64)) && defined(_MSC_VER)
	#ifndef WIN32_LEAN_AND_MEAN
		#define WIN32_LEAN_AND_MEAN 1
	#endif

	#ifndef WIN32_EXTRA_LEAN
		#define WIN32_EXTRA_LEAN 1
	#endif

	// this is copied from Wine to prevent inclusion of unused definitions
	#define NOSERVICE
	#define NOMCX
	#define NOIME
	#define NOCOMM
	#define NOKANJI
	#define NORPC
	#define NOPROXYSTUB
	#define NOIMAGE
	#define NOTAPE

	#define PLATFORM_NAME "windows"
	#define PLATFORM_WINDOWS 1
#elif __linux__
	#if RASPBERRYPI
		// specifically built for RaspberryPi
		#define PLATFORM_RASPBERRYPI 1
		#define PLATFORM_NAME "raspberrypi"
	#else
		// generic flavor
		#define PLATFORM_NAME "linux"
	#endif

	#define PLATFORM_LINUX 1
#elif __APPLE__
	#include <TargetConditionals.h>

	#define PLATFORM_APPLE 1

	#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
		#define PLATFORM_NAME "iphoneos"
		#define PLATFORM_IS_MOBILE 1
		#define PLATFORM_IOS 1
	#else
		#define PLATFORM_NAME "macosx"
		#define PLATFORM_MACOSX 1
	#endif
#elif __ANDROID__
	#define PLATFORM_NAME "Android"
	#define PLATFORM_ANDROID 1
	#define PLATFORM_IS_MOBILE 1
#else
	#error Unknown platform!
#endif


text:
USE localjira;

/*
 This INSERT fails because the user 'mate' does not exist yet

INSERT INTO issue (projectId, reporterId, assigneeId, issuetypeId, priorityId, fields)
VALUES (
  2, 2, 1, 1, 2, // 'SPLPRJ', 'mate', 'crozier', 'Bug', 'Critical'
  '{
    "summary": "MySQL issue 2",
    "description": "New reporter inserted jointly with the new issue."
  }'
);
 */

/*
 Although a TRANSACTION that INSERTs the issue 'SPLPRJ-2' then INSERTs the referenced user 'mate' before COMMIT works in SQL,
 such a transaction fails in MySQL. See https://dev.mysql.com/doc/refman/8.0/en/innodb-foreign-key-constraints.html
 */
START TRANSACTION;
INSERT INTO user (name, displayName) VALUES('mate', 'Team Mate');
INSERT INTO issue (projectId, reporterId, assigneeId, issuetypeId, priorityId, fields)
VALUES (
  2, 2, 1, 1, 2, /* 'SPLPRJ', 'mate', 'crozier', 'Bug', 'Critical' */
  '{
    "summary": "MySQL issue 2",
    "description": "New reporter inserted jointly with the new issue."
  }'
);
COMMIT;


text:
require 'test_helper'

class HttpsTest < GeocoderTestCase

  def test_uses_https_for_secure_query
    Geocoder.configure(:use_https => true)
    g = Geocoder::Lookup::Google.new
    assert_match(/^https:/, g.query_url(Geocoder::Query.new("test")))
  end

  def test_uses_http_by_default
    g = Geocoder::Lookup::Esri.new
    assert_match(/^http:/, g.query_url(Geocoder::Query.new("test")))
  end
end


text:
using Enterprise.Invoicing.Common;
using Enterprise.Invoicing.Repositories;
using Enterprise.Invoicing.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Calabonga.Mvc.PagedListExt;
using Enterprise.Invoicing.ViewModel;
using Enterprise.Invoicing.Entities.Models;
namespace Enterprise.Invoicing.Web.Controllers
{
    public class systemController : Controller
    {
        private ManageService manageService;
        private SystemService systemService;
        public systemController(IManageRepository _manageRepository, ISystemRepository _systemrepository)
        {
            manageService = new ManageService(_manageRepository);
            systemService = new SystemService(_systemrepository);
        }

        #region 用户管理

        public ActionResult user(int? page)
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            var key = WebRequest.GetString("key", true);
            data.key = key;
            var list = systemService.GetEmployeeList(key);

            int _page = page.HasValue ? page.Value : 1;
            int _pagesize = 17;
            var vs = list.ToPagedList(_page, _pagesize);
            data.list = vs;
            data.pageSize = _pagesize;
            data.pageIndex = _page;
            data.totalCount = vs.TotalCount;
            data.otherParam = "&key=" + key;
            return View(data);
        }
        [AjaxAction(ForAction = "user", ForController = "system")]
        public ActionResult userone()
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            int id = WebRequest.GetInt("id", 0);
            string type = WebRequest.GetString("type");
            EmployeeModel model = new EmployeeModel();
            if (id > 0 && type == "edit")
            {
                model = systemService.GetEmployeeList("").FirstOrDefault(p => p.staffId == id);
            }
            else type = "add";
            var roles = systemService.GetRoleList("").
                Select(p => new SelectListItem { Text = p.roleName, Value = p.roleSn.ToString() }).ToList();
            data.roles = roles;
            data.id = id;
            data.type = type;
            data.model = model;
            return View(data);
        }
        [HttpPost]
        [AjaxAction(ForAction = "user", ForController = "system")]
        public ActionResult saveuser()
        {
            //id,depId, string name, string mobile, string email, string duty, string remark
            int id = WebRequest.GetInt("id", 0);
            int rad = WebRequest.GetInt("rad", 0);
            int isuser = WebRequest.GetInt("isuser", 0);
            int utype = WebRequest.GetInt("utype", 0);
            int role = WebRequest.GetInt("role", 0);
            string userid = WebRequest.GetString("userid");
            string pwd = WebRequest.GetString("pwd");
            string remark = WebRequest.GetString("remark");
            bool valid = rad != 0 ? true : false;
            bool foruser = isuser != 0 ? true : false;
            if (pwd != "") pwd = MD5Helper.MD5_32(pwd).ToLower();
            var r = systemService.SetUser(id, foruser, userid, pwd, role, valid, remark, utype);
            return Json(r, JsonRequestBehavior.AllowGet);
        }
        #endregion

        #region 角色管理

        public ActionResult role(int? page)
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            var key = WebRequest.GetString("key", true);
            data.key = key;
            var list = systemService.GetRoleList(key);

            int _page = page.HasValue ? page.Value : 1;
            int _pagesize = 17;
            var vs = list.ToPagedList(_page, _pagesize);
            data.list = vs;
            data.pageSize = _pagesize;
            data.pageIndex = _page;
            data.totalCount = vs.TotalCount;
            data.otherParam = "&key=" + key;
            return View(data);
        }
        [AjaxAction(ForAction = "role", ForController = "system")]
        public ActionResult roleone()
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            int id = WebRequest.GetInt("id", 0);
            string type = WebRequest.GetString("type");
            Role model = new Role();
            if (id > 0 && type == "edit")
            {
                model = systemService.GetRoleList("").FirstOrDefault(p => p.roleSn == id);
            }
            else type = "add";
            data.id = id;
            data.type = type;
            data.model = model;
            return View(data);
        }
        [HttpPost]
        [AjaxAction(ForAction = "role", ForController = "system")]
        public ActionResult saverole()
        {
            //int id, string depName, string phone, string leader, string remark
            int id = WebRequest.GetInt("id", 0);
            string name = WebRequest.GetString("name");
            string remark = WebRequest.GetString("remark");
            int price = WebRequest.GetInt("price", 0);
            var r = systemService.SaveRole(id, name, remark, price == 1);
            return Json(r, JsonRequestBehavior.AllowGet);
        }
        [HttpPost]
        [AjaxAction(ForAction = "role", ForController = "system")]
        public ActionResult deleterole()
        {
            int id = WebRequest.GetInt("id", 0);
            var r = systemService.DeleteRole(id);
            return Json(r, JsonRequestBehavior.AllowGet);
        }

        [AjaxAction(ForAction = "role", ForController = "system")]
        public ActionResult rolemenu()
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            int id = WebRequest.GetInt("id", 0);
            var model = systemService.GetRoleList("").FirstOrDefault(p => p.roleSn == id);
            var all = systemService.GetAllMenu();
            var had = systemService.GetHadMenu(id);
            data.id = id;
            data.had = had;
            data.all = all;
            return View(data);
        }
        [HttpPost]
        [AjaxAction(ForAction = "role", ForController = "system")]
        public ActionResult saverolemenu(int id)
        {
            ReturnValue rv = new ReturnValue();
            try
            {
                string addstr = WebRequest.GetString("add", true);
                string delstr = WebRequest.GetString("del", true);
                if (addstr.EndsWith("#")) addstr = addstr.Substring(0, addstr.Length - 1);
                if (delstr.EndsWith("#")) delstr = delstr.Substring(0, delstr.Length - 1);
                string[] add = addstr.Split('#');
                string[] del = delstr.Split('#');
                bool b = systemService.SaveRoleMenu(id, add, del);
                if (b) rv = new ReturnValue { status = true, message = "" };
                else rv = new ReturnValue { status = false, message = "保存失败" };

            }
            catch (Exception ex)
            {
                rv = new ReturnValue { status = false, message = "程序异常:" + ex.Message };
            }
            return Json(rv, JsonRequestBehavior.AllowGet);
        }

        [AjaxAction(ForAction = "role", ForController = "system")]
        public ActionResult rolefunction()
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            int id = WebRequest.GetInt("id", 0);
            var model = systemService.GetRoleList("").FirstOrDefault(p => p.roleSn == id);
            var all = systemService.GetAllFunction();
            var had = systemService.GetHadFunction(id);
            data.id = id;
            data.had = had;
            data.all = all;
            return View(data);
        }
        [HttpPost]
        [AjaxAction(ForAction = "role", ForController = "system")]
        public ActionResult saverolefunction(int id)
        {
            ReturnValue rv = new ReturnValue();
            try
            {
                string addstr = WebRequest.GetString("add", true);
                string delstr = WebRequest.GetString("del", true);
                if (addstr.EndsWith("#")) addstr = addstr.Substring(0, addstr.Length - 1);
                if (delstr.EndsWith("#")) delstr = delstr.Substring(0, delstr.Length - 1);
                string[] add = addstr.Split('#');
                string[] del = delstr.Split('#');
                bool b = systemService.SaveRoleFunction(id, add, del);
                if (b) rv = new ReturnValue { status = true, message = "" };
                else rv = new ReturnValue { status = false, message = "保存失败" };

            }
            catch (Exception ex)
            {
                rv = new ReturnValue { status = false, message = "程序异常:" + ex.Message };
            }
            return Json(rv, JsonRequestBehavior.AllowGet);
        }
        #endregion

        #region 字典管理

        public ActionResult dictionary(int? page)
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            var key = WebRequest.GetString("key", true);
            data.key = key;
            var list = systemService.GetDictionaryList(key);

            int _page = page.HasValue ? page.Value : 1;
            int _pagesize = 17;
            var vs = list.ToPagedList(_page, _pagesize);
            data.list = vs;
            data.pageSize = _pagesize;
            data.pageIndex = _page;
            data.totalCount = vs.TotalCount;
            data.otherParam = "&key=" + key;
            return View(data);
        }
        [AjaxAction(ForAction = "dictionary", ForController = "system")]
        public ActionResult dictionaryone()
        {
            dynamic data = new System.Dynamic.ExpandoObject();
            string id = WebRequest.GetString("id");
            string type = WebRequest.GetString("type");
            Dictionary model = new Dictionary();
            if (id != "" && type == "edit")
            {
                model = systemService.GetDictionaryList("").FirstOrDefault(p => p.dictionaryKey == id);
            }
            else type = "add";
            data.id = id;
            data.type = type;
            data.model = model;
            return View(data);
        }
        [HttpPost]
        [AjaxAction(ForAction = "dictionary", ForController = "system")]
        public ActionResult savedictionary()
        {
            //id,depId, string name, string mobile, string email, string duty, string remark
            string type = WebRequest.GetString("type");
            string no = WebRequest.GetString("no");
            string name = WebRequest.GetString("name");
            string value = WebRequest.GetString("value");
            string remark = WebRequest.GetString("remark");
            var r = systemService.SaveDictionary(type, no, value, name, remark);
            return Json(r, JsonRequestBehavior.AllowGet);
        }
        [HttpPost]
        [AjaxAction(ForAction = "dictionary", ForController = "system")]
        public ActionResult deletedictionary()
        {
            string id = WebRequest.GetString("id");
            var r = systemService.DeleteDictionary(id);
            return Json(r, JsonRequestBehavior.AllowGet);
        }
        #endregion

    }
}


text:
const fs = require('fs');

const uploadedFiles = {};

module.exports.onSaved = (event) => {
  if (event.file.success) {
    const clientId = event.file.meta.clientId;

    uploadedFiles[clientId] = (uploadedFiles[clientId] === undefined ? [] : uploadedFiles[clientId]);
    uploadedFiles[clientId].push(event.file);
    console.log('new file with id', event.file.id, '; received from client id', event.file.meta.clientId, ';', event.file.name, ';', event.file.pathName);
  }
};

module.exports.onError = (event) => {
  const clientId = event.file.meta.clientId;

  uploadedFiles[clientId] = (uploadedFiles[clientId] === undefined ? [] : uploadedFiles[clientId]);
  uploadedFiles[clientId].push(event.file);
  console.log('File upload error: file id', event.file.id, '; received from client id', event.file.meta.clientId, ';', event.file.name, ';', event.file.pathName);
};

function retrieveLastUploadFromClientId(clientId) {
  const lastUpload = uploadedFiles[clientId];
  return lastUpload === undefined ? lastUpload : lastUpload[lastUpload.length - 1];
}

module.exports.getLastUploadFromClientId = (clientId) => new Promise((fulfill, reject) => {
  const file = retrieveLastUploadFromClientId(clientId);

  if (file !== undefined && file.success === true) {
    fulfill(file);
  } else {
    reject(`'${file.name}': upload error (info: max upload size 16 MB)`);
  }
});

module.exports.clearClientUploads = (clientId) => {
  if (uploadedFiles[clientId] !== undefined) {
    uploadedFiles[clientId].forEach((uploadedFile) => {
      fs.unlink(uploadedFile.pathName, () => {
        console.log(`Removed '${uploadedFile.pathName}' associated with client id '${clientId}'`);
      });
    });
  }
  uploadedFiles[clientId] = undefined;
};