<commit_before>//All CrocRoot will do is reset the context and draw the children. Not even sure if it should reset the context.
//It also has image control so we don't load a million such images.

function CrocRoot(canvas, hitCanvas, fullscreen, eventHandlerConstructor) {
	CrocBase.call(this, this);

	eventHandlerConstructor = eventHandlerConstructor || CrocEventHandler;

	this.fullscreen = fullscreen || false;

	this.canvas = canvas;
	this.hitCanvas = hitCanvas;
	this.scaleFactor = 1.0;

	this.context = canvas.getContext("2d");
	this.hitContext = hitCanvas.getContext("2d");

	//Stupid failure to implement this
	if(this.context.mozCurrentTransform === undefined && this.context.currenTransform === undefined) {

		this.hitContext = new CanvasWrapper(this.hitContext);
		this.context = new CanvasWrapper(this.context);

		this.context.getCurrentTransform = function() {

			var matrix = this.getMatrix();

			return [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[2][0], matrix[2][1]];
		};

		this.context.getContext = function() {
			return this.canvas;
		}

		this.hitContext.getCurrentTransform = function() {

			var matrix = this.getMatrix();

			if(matrix === undefined) {
				console.trace();
			}

			return [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[2][0], matrix[2][1]];
		};

		this.hitContext.getContext = function() {
			return this.canvas;
		}

	}

	else {
		this.context.getCurrentTransform = function() {

			if(this.mozCurrentTransform !== undefined) {
				return this.mozCurrentTransform;
			}

			else  {
				return this.currenTransform;
			}

		}

		this.context.getContext = function() {
			return this;
		}

		this.hitContext.getCurrentTransform = function() {

			if(this.mozCurrentTransform !== undefined) {
				return this.mozCurrentTransform;
			}

			else  {
				return this.currenTransform;
			}

		}

		this.hitContext.getContext = function() {
			return this;
		}
	}

	if(this.context.measureText === undefined) {
		this.context.measureText = function(text) {
			return getTextWidthDOM(text, this.font);
		};
	}

	this.focusedObject = null;

	var currentCrocRoot = this;

	this.imageStore = {};
	this.imageStoreListeners = {};
	this.dirty = false;
	this.paintWarnings = [];
	this.globalPaintWarning = false;

	this.themer = new CrocThemer(this, crocThemeDefault);
	this.eventHandler = new eventHandlerConstructor(this);

	if(this.fullscreen) {
		this.canvas.width = window.innerWidth;
		this.canvas.height = window.innerHeight;

		this.hitCanvas.width = window.innerWidth;
		this.hitCanvas.height = window.innerHeight;
	}

	this.repaint();
};

CrocRoot.prototype = Object.create(CrocBase.prototype);
CrocRoot.prototype.constructor = CrocRoot;

CrocRoot.prototype.getCrocRoot = function() {
	return this;
};

CrocRoot.prototype.onCanvasResize = function() {
	this.repaint();
};

CrocRoot.prototype.setWidth = function (width) {

	if(this.fullscreen) {
		return;
	}

	this.canvas.width = width;
	this.hitCanvas.width = width;

	this.repaint();
};

CrocRoot.prototype.setHeight = function(height) {

	if(this.fullscreen) {
		return;
	}

	this.canvas.height = height;
	this.hitCanvas.height = height;

	this.repaint();
};

CrocRoot.prototype.getWidth = function () {
	if(this.fullscreen) {
		return window.innerWidth;
	}

	return this.canvas.width;
};

CrocRoot.prototype.getHeight = function() {
	if(this.fullscreen) {
		return window.innerHeight;
	}

	return this.canvas.height;
};

CrocRoot.prototype.clear = function() {
	this.hitContext.save();
	this.context.setTransform(1, 0, 0, 1, 0, 0)
	this.context.clearRect(0, 0, this.getWidth(), this.getHeight());
	this.hitContext.restore();
};

CrocRoot.prototype.clearHitContext = function() {
	this.hitContext.save();
	this.hitContext.setTransform(1, 0, 0, 1, 0, 0)
	this.hitContext.clearRect(0, 0, this.getWidth(), this.getHeight());
	this.hitContext.restore();
};

CrocRoot.prototype.setFocusedObject = function(object) {

	if(this.focusedObject !== undefined && this.focusedObject !== null) {
		this.focusedObject.event('blur', object);
	}

	this.focusedObject = object;

	return this.focusedObject;
};

CrocRoot.prototype.getFocusedObject = function() {
	return this.focusedObject;
};

//This function allows an object to request from Croc to fire an event if something is going to draw over it.
//The call back is executed and the object can request to stop all painting by returning false
//When a paint is canceled by an object a repaint should have been triggered by the object.
CrocRoot.prototype.addPaintWarning = function(object, transform, width, height) {

	var tl = this.transformPoint(transform, 0, 0);
	var tr = this.transformPoint(transform, width, 0);
	var bl = this.transformPoint(transform, 0, height);
	var br = this.transformPoint(transform, width, height);

	var sX = Math.min(tl.x, tr.x, bl.x, br.x);
	var sY = Math.min(tl.y, tr.y, bl.y, br.y);
	var mX = Math.max(tl.x, tr.x, bl.x, br.x);
	var mY = Math.max(tl.y, tr.y, bl.y, br.y);

	this.paintWarnings.push({object:object, min:{x:sX, y:sY}, max:{x:mX, y:mY}});

	return;
};

CrocRoot.prototype.clearPaintWarnings = function() {
	this.paintWarnings = [];
	return;
};

CrocRoot.prototype.setGlobalPaintWarning = function(warning) {
	this.globalPaintWarning = warning;
	this.repaint();
};

CrocRoot.prototype.testPaintWarnings = function(object, transform) {

	var tl = this.transformPoint(transform, 0, 0);
	var tr = this.transformPoint(transform, object.getWidth(), 0);
	var bl = this.transformPoint(transform, 0, object.getHeight());
	var br = this.transformPoint(transform, object.getWidth(), object.getHeight());

	var sX = Math.min(tl.x, tr.x, bl.x, br.x);
	var sY = Math.min(tl.y, tr.y, bl.y, br.y);
	var mX = Math.max(tl.x, tr.x, bl.x, br.x);
	var mY = Math.max(tl.y, tr.y, bl.y, br.y);

	for(var i = 0; i < this.paintWarnings.length; i++) {
		var currentPaintWarning = this.paintWarnings[i];

		if(this.globalPaintWarning) {
			currentPaintWarning.object.event("paintWarning", object);
		}

		else if (
			sX < currentPaintWarning.max.x &&
			mX > currentPaintWarning.min.x &&
			sY < currentPaintWarning.max.y &&
			mY > currentPaintWarning.min.y) {

			currentPaintWarning.object.event("paintWarning", object);
		}

	}
};

CrocRoot.prototype.repaint = function() {

	//A draw has already been request no need to ask again...
	if(this.dirty) {
		return;
	}

	this.dirty = true;

	var currentCrocRoot = this;
	window.requestAnimationFrame(function() {
		currentCrocRoot.paint();
	});
};

CrocRoot.prototype.loadImage = function(src, callback) {

	var currentCrocRoot = this;

	if(src in this.imageStore) {
		if(this.imageStore[src].loaded) {

			if(callback !== undefined) {
				callback.call(currentCrocRoot);
			}
		}

		else {
			if(!(src in this.imageStoreListeners)) {
				this.imageStoreListeners[src] = [];
			}

			this.imageStoreListeners[src].push(callback);
		}

		return;
	}

	var image = new Image();

	this.imageStore[src] = {image:image, loaded:false};

	if(!(src in this.imageStoreListeners)) {
		this.imageStoreListeners[src] = [];
	}

	this.imageStoreListeners[src].push(callback);

	image.onload = function() {
		currentCrocRoot.onImageLoad.call(currentCrocRoot, this.origSrc);

		if(this.origSrc in currentCrocRoot.imageStoreListeners) {
			for(var i = 0; i < currentCrocRoot.imageStoreListeners[this.origSrc].length; i++) {
				currentCrocRoot.imageStoreListeners[this.origSrc][i].call(currentCrocRoot);
			}
		}
	}

	image.origSrc = src;
	image.src = src;
};

CrocRoot.prototype.setCursor = function(type) {
	window.document.body.style.cursor = type || "";
	return;
};

CrocRoot.prototype.onImageLoad = function(src) {

	if(src === undefined) {
		return;
	}

	if(!(src in this.imageStore)) {
		this.error("Wasn't waiting for image \"" + src + "\" to load but got event anyway?!");
		return;
	}

	this.imageStore[src].loaded = true;

	this.repaint();

	this.event("imageload", {"src":src}, true);

	return;
};

CrocRoot.prototype.getImage = function(src) {

	if(src === undefined) {
		return;
	}

	if(!(src in this.imageStore)) {
		this.error("No such image \"" + src + "\" in image storage!");
		return null;
	}

	if(this.imageStore[src].loaded) {
		return this.imageStore[src].image;
	}

	return null;
};

//So hitTest is done as such.
//CrocRoot will draw a box the will be #000000ff in global space
//All UIElement will then clip mask their region checking for this box.
//If they find it then they have a hit.
//If they don't have it false.
//Everything shuold either return a single object, or list.
//Order is critical as this is the hit order and will determine who "wins" the event
CrocRoot.prototype.hitTest = function(x, y) {

	x = x || 0;
	y = y || 0;

	var hitReturn = [];
	var hitObject = null;

	this.hitContext.save();

	this.clearHitContext();

	this.hitContext.scale(this.scaleFactor, this.scaleFactor);

	CrocBase.prototype.hitTest.call(this, this.hitContext, x, y, this.getWidth(), this.getHeight())

	for(var i = 0; i < this.children.length; i++) {
		hitObject = this.children[i].hitTest(this.hitContext, x, y, this.getWidth(), this.getHeight());

		if(hitObject !== null) {
			hitReturn.push(hitObject);
		}
	}

	this.hitContext.restore();

	return hitReturn;
};

CrocRoot.prototype.setScaleFactor = function(factor) {
	this.scaleFactor = factor;
	this.repaint();
};

CrocRoot.prototype.setSmooth = function(smooth) {
	this.context['imageSmoothingEnabled'] = smooth;       /* standard */
	this.context['mozImageSmoothingEnabled'] = smooth;    /* Firefox */
	this.context['oImageSmoothingEnabled'] = smooth;      /* Opera */
	this.context['webkitImageSmoothingEnabled'] = smooth; /* Safari */
	this.context['msImageSmoothingEnabled'] = smooth;     /* IE */
};

CrocRoot.prototype.paint = function() {

	//When we initialize the context there needs to be a currenTransform value.
	//This is now standard but some browsers, like firefox call it mozCurrentTransform.
	//So we map with context.getCurrentTransform() function;

	if(!this.visible) {
		this.clear();
		this.clearPaintWarnings();
	}

	if(this.dirty && this.visible) {
		this.dirty = false;
		this.context.save();
		//Reset context transformation
		this.clear();

		this.context.scale(this.scaleFactor, this.scaleFactor);

		this.setSmooth(true);

		var i = this.children.length;
		while(i--) {
			this.children[i].paint(this.context, this.getWidth(), this.getHeight());
		}

		this.clearPaintWarnings();
		this.context.restore();
	}

	return;

};
<commit_msg>Adding an event if we end up setting the focused object at the root level.<commit_after>//All CrocRoot will do is reset the context and draw the children. Not even sure if it should reset the context.
//It also has image control so we don't load a million such images.

function CrocRoot(canvas, hitCanvas, fullscreen, eventHandlerConstructor) {
	CrocBase.call(this, this);

	eventHandlerConstructor = eventHandlerConstructor || CrocEventHandler;

	this.fullscreen = fullscreen || false;

	this.canvas = canvas;
	this.hitCanvas = hitCanvas;
	this.scaleFactor = 1.0;

	this.context = canvas.getContext("2d");
	this.hitContext = hitCanvas.getContext("2d");

	//Stupid failure to implement this
	if(this.context.mozCurrentTransform === undefined && this.context.currenTransform === undefined) {

		this.hitContext = new CanvasWrapper(this.hitContext);
		this.context = new CanvasWrapper(this.context);

		this.context.getCurrentTransform = function() {

			var matrix = this.getMatrix();

			return [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[2][0], matrix[2][1]];
		};

		this.context.getContext = function() {
			return this.canvas;
		}

		this.hitContext.getCurrentTransform = function() {

			var matrix = this.getMatrix();

			if(matrix === undefined) {
				console.trace();
			}

			return [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[2][0], matrix[2][1]];
		};

		this.hitContext.getContext = function() {
			return this.canvas;
		}

	}

	else {
		this.context.getCurrentTransform = function() {

			if(this.mozCurrentTransform !== undefined) {
				return this.mozCurrentTransform;
			}

			else  {
				return this.currenTransform;
			}

		}

		this.context.getContext = function() {
			return this;
		}

		this.hitContext.getCurrentTransform = function() {

			if(this.mozCurrentTransform !== undefined) {
				return this.mozCurrentTransform;
			}

			else  {
				return this.currenTransform;
			}

		}

		this.hitContext.getContext = function() {
			return this;
		}
	}

	if(this.context.measureText === undefined) {
		this.context.measureText = function(text) {
			return getTextWidthDOM(text, this.font);
		};
	}

	this.focusedObject = null;

	var currentCrocRoot = this;

	this.imageStore = {};
	this.imageStoreListeners = {};
	this.dirty = false;
	this.paintWarnings = [];
	this.globalPaintWarning = false;

	this.themer = new CrocThemer(this, crocThemeDefault);
	this.eventHandler = new eventHandlerConstructor(this);

	if(this.fullscreen) {
		this.canvas.width = window.innerWidth;
		this.canvas.height = window.innerHeight;

		this.hitCanvas.width = window.innerWidth;
		this.hitCanvas.height = window.innerHeight;
	}

	this.repaint();
};

CrocRoot.prototype = Object.create(CrocBase.prototype);
CrocRoot.prototype.constructor = CrocRoot;

CrocRoot.prototype.getCrocRoot = function() {
	return this;
};

CrocRoot.prototype.onCanvasResize = function() {
	this.repaint();
};

CrocRoot.prototype.setWidth = function (width) {

	if(this.fullscreen) {
		return;
	}

	this.canvas.width = width;
	this.hitCanvas.width = width;

	this.repaint();
};

CrocRoot.prototype.setHeight = function(height) {

	if(this.fullscreen) {
		return;
	}

	this.canvas.height = height;
	this.hitCanvas.height = height;

	this.repaint();
};

CrocRoot.prototype.getWidth = function () {
	if(this.fullscreen) {
		return window.innerWidth;
	}

	return this.canvas.width;
};

CrocRoot.prototype.getHeight = function() {
	if(this.fullscreen) {
		return window.innerHeight;
	}

	return this.canvas.height;
};

CrocRoot.prototype.clear = function() {
	this.hitContext.save();
	this.context.setTransform(1, 0, 0, 1, 0, 0)
	this.context.clearRect(0, 0, this.getWidth(), this.getHeight());
	this.hitContext.restore();
};

CrocRoot.prototype.clearHitContext = function() {
	this.hitContext.save();
	this.hitContext.setTransform(1, 0, 0, 1, 0, 0)
	this.hitContext.clearRect(0, 0, this.getWidth(), this.getHeight());
	this.hitContext.restore();
};

CrocRoot.prototype.setFocusedObject = function(object) {

	if(this.focusedObject !== undefined && this.focusedObject !== null) {

		this.focusedObject.event('blur', object);
	}

	this.focusedObject = object;

	this.event('objectfocusedset', object);

	return this.focusedObject;
};

CrocRoot.prototype.getFocusedObject = function() {
	return this.focusedObject;
};

//This function allows an object to request from Croc to fire an event if something is going to draw over it.
//The call back is executed and the object can request to stop all painting by returning false
//When a paint is canceled by an object a repaint should have been triggered by the object.
CrocRoot.prototype.addPaintWarning = function(object, transform, width, height) {

	var tl = this.transformPoint(transform, 0, 0);
	var tr = this.transformPoint(transform, width, 0);
	var bl = this.transformPoint(transform, 0, height);
	var br = this.transformPoint(transform, width, height);

	var sX = Math.min(tl.x, tr.x, bl.x, br.x);
	var sY = Math.min(tl.y, tr.y, bl.y, br.y);
	var mX = Math.max(tl.x, tr.x, bl.x, br.x);
	var mY = Math.max(tl.y, tr.y, bl.y, br.y);

	this.paintWarnings.push({object:object, min:{x:sX, y:sY}, max:{x:mX, y:mY}});

	return;
};

CrocRoot.prototype.clearPaintWarnings = function() {
	this.paintWarnings = [];
	return;
};

CrocRoot.prototype.setGlobalPaintWarning = function(warning) {
	this.globalPaintWarning = warning;
	this.repaint();
};

CrocRoot.prototype.testPaintWarnings = function(object, transform) {

	var tl = this.transformPoint(transform, 0, 0);
	var tr = this.transformPoint(transform, object.getWidth(), 0);
	var bl = this.transformPoint(transform, 0, object.getHeight());
	var br = this.transformPoint(transform, object.getWidth(), object.getHeight());

	var sX = Math.min(tl.x, tr.x, bl.x, br.x);
	var sY = Math.min(tl.y, tr.y, bl.y, br.y);
	var mX = Math.max(tl.x, tr.x, bl.x, br.x);
	var mY = Math.max(tl.y, tr.y, bl.y, br.y);

	for(var i = 0; i < this.paintWarnings.length; i++) {
		var currentPaintWarning = this.paintWarnings[i];

		if(this.globalPaintWarning) {
			currentPaintWarning.object.event("paintWarning", object);
		}

		else if (
			sX < currentPaintWarning.max.x &&
			mX > currentPaintWarning.min.x &&
			sY < currentPaintWarning.max.y &&
			mY > currentPaintWarning.min.y) {

			currentPaintWarning.object.event("paintWarning", object);
		}

	}
};

CrocRoot.prototype.repaint = function() {

	//A draw has already been request no need to ask again...
	if(this.dirty) {
		return;
	}

	this.dirty = true;

	var currentCrocRoot = this;
	window.requestAnimationFrame(function() {
		currentCrocRoot.paint();
	});
};

CrocRoot.prototype.loadImage = function(src, callback) {

	var currentCrocRoot = this;

	if(src in this.imageStore) {
		if(this.imageStore[src].loaded) {

			if(callback !== undefined) {
				callback.call(currentCrocRoot);
			}
		}

		else {
			if(!(src in this.imageStoreListeners)) {
				this.imageStoreListeners[src] = [];
			}

			this.imageStoreListeners[src].push(callback);
		}

		return;
	}

	var image = new Image();

	this.imageStore[src] = {image:image, loaded:false};

	if(!(src in this.imageStoreListeners)) {
		this.imageStoreListeners[src] = [];
	}

	this.imageStoreListeners[src].push(callback);

	image.onload = function() {
		currentCrocRoot.onImageLoad.call(currentCrocRoot, this.origSrc);

		if(this.origSrc in currentCrocRoot.imageStoreListeners) {
			for(var i = 0; i < currentCrocRoot.imageStoreListeners[this.origSrc].length; i++) {
				currentCrocRoot.imageStoreListeners[this.origSrc][i].call(currentCrocRoot);
			}
		}
	}

	image.origSrc = src;
	image.src = src;
};

CrocRoot.prototype.setCursor = function(type) {
	window.document.body.style.cursor = type || "";
	return;
};

CrocRoot.prototype.onImageLoad = function(src) {

	if(src === undefined) {
		return;
	}

	if(!(src in this.imageStore)) {
		this.error("Wasn't waiting for image \"" + src + "\" to load but got event anyway?!");
		return;
	}

	this.imageStore[src].loaded = true;

	this.repaint();

	this.event("imageload", {"src":src}, true);

	return;
};

CrocRoot.prototype.getImage = function(src) {

	if(src === undefined) {
		return;
	}

	if(!(src in this.imageStore)) {
		this.error("No such image \"" + src + "\" in image storage!");
		return null;
	}

	if(this.imageStore[src].loaded) {
		return this.imageStore[src].image;
	}

	return null;
};

//So hitTest is done as such.
//CrocRoot will draw a box the will be #000000ff in global space
//All UIElement will then clip mask their region checking for this box.
//If they find it then they have a hit.
//If they don't have it false.
//Everything shuold either return a single object, or list.
//Order is critical as this is the hit order and will determine who "wins" the event
CrocRoot.prototype.hitTest = function(x, y) {

	x = x || 0;
	y = y || 0;

	var hitReturn = [];
	var hitObject = null;

	this.hitContext.save();

	this.clearHitContext();

	this.hitContext.scale(this.scaleFactor, this.scaleFactor);

	CrocBase.prototype.hitTest.call(this, this.hitContext, x, y, this.getWidth(), this.getHeight())

	for(var i = 0; i < this.children.length; i++) {
		hitObject = this.children[i].hitTest(this.hitContext, x, y, this.getWidth(), this.getHeight());

		if(hitObject !== null) {
			hitReturn.push(hitObject);
		}
	}

	this.hitContext.restore();

	return hitReturn;
};

CrocRoot.prototype.setScaleFactor = function(factor) {
	this.scaleFactor = factor;
	this.repaint();
};

CrocRoot.prototype.setSmooth = function(smooth) {
	this.context['imageSmoothingEnabled'] = smooth;       /* standard */
	this.context['mozImageSmoothingEnabled'] = smooth;    /* Firefox */
	this.context['oImageSmoothingEnabled'] = smooth;      /* Opera */
	this.context['webkitImageSmoothingEnabled'] = smooth; /* Safari */
	this.context['msImageSmoothingEnabled'] = smooth;     /* IE */
};

CrocRoot.prototype.paint = function() {

	//When we initialize the context there needs to be a currenTransform value.
	//This is now standard but some browsers, like firefox call it mozCurrentTransform.
	//So we map with context.getCurrentTransform() function;

	if(!this.visible) {
		this.clear();
		this.clearPaintWarnings();
	}

	if(this.dirty && this.visible) {
		this.dirty = false;
		this.context.save();
		//Reset context transformation
		this.clear();

		this.context.scale(this.scaleFactor, this.scaleFactor);

		this.setSmooth(true);

		var i = this.children.length;
		while(i--) {
			this.children[i].paint(this.context, this.getWidth(), this.getHeight());
		}

		this.clearPaintWarnings();
		this.context.restore();
	}

	return;

};
<|endoftext|>


<commit_before>"use strict";

var oo = require('../util/oo');
var Component = require('./Component');
var $$ = Component.$$;

var ICONS_FOR_TYPE = {
  "error": "fa-exclamation-circle",
  "info": "fa-info",
  "progress": "fa-exchange",
  "success": "fa-check-circle",
};

/**
  A simple StatusBar implementation that displays a document's title and
  renders messages.

  @class
  @component

  @prop {model/Document} doc The document instance

  @state {String} message The message displayed in the status bar.
*/

function StatusBar() {
  Component.apply(this, arguments);

  var ctrl = this.context.controller;
  var logger = ctrl.getLogger();
  logger.connect(this, {
    'messages:updated': this.handleStatusUpdate
  });
}

StatusBar.Prototype = function() {

  this.render = function() {
    var meta = this.props.doc.getDocumentMeta();
    var title = meta ? meta.title : this.i18n.t('untitled');
    var message = this.state.message;

    var el = $$('div').addClass("status-bar-component fill-light");
    el.append($$("div").addClass("document-status").append(title));

    if (message) {
      el.addClass(message.type);
      el.append(
        $$('div').addClass('notifications').append(
          $$("div").addClass("icon").append(
            $$('i').addClass('fa '+ICONS_FOR_TYPE[message.type])
          )
        ),
        $$('div').addClass('message').append(message.message)
      );
    }
    return el;
  };

  this.handleStatusUpdate = function(messages) {
    var currentMessage = messages.pop();
    this.setState({
      message: currentMessage
    });
  };
};

oo.inherit(StatusBar, Component);

module.exports = StatusBar;
<commit_msg>Remove StatusBar from the docs.<commit_after>"use strict";

var oo = require('../util/oo');
var Component = require('./Component');
var $$ = Component.$$;

var ICONS_FOR_TYPE = {
  "error": "fa-exclamation-circle",
  "info": "fa-info",
  "progress": "fa-exchange",
  "success": "fa-check-circle",
};

/*
  A simple StatusBar implementation that displays a document's title and
  renders messages.

  @class
  @component

  @prop {model/Document} doc The document instance

  @state {String} message The message displayed in the status bar.
*/

function StatusBar() {
  Component.apply(this, arguments);

  var ctrl = this.context.controller;
  var logger = ctrl.getLogger();
  logger.connect(this, {
    'messages:updated': this.handleStatusUpdate
  });
}

StatusBar.Prototype = function() {

  this.render = function() {
    var meta = this.props.doc.getDocumentMeta();
    var title = meta ? meta.title : this.i18n.t('untitled');
    var message = this.state.message;

    var el = $$('div').addClass("status-bar-component fill-light");
    el.append($$("div").addClass("document-status").append(title));

    if (message) {
      el.addClass(message.type);
      el.append(
        $$('div').addClass('notifications').append(
          $$("div").addClass("icon").append(
            $$('i').addClass('fa '+ICONS_FOR_TYPE[message.type])
          )
        ),
        $$('div').addClass('message').append(message.message)
      );
    }
    return el;
  };

  this.handleStatusUpdate = function(messages) {
    var currentMessage = messages.pop();
    this.setState({
      message: currentMessage
    });
  };
};

oo.inherit(StatusBar, Component);

module.exports = StatusBar;
<|endoftext|>


<commit_before>// @flow
import { Lbryio } from 'lbryinc';
import * as Sentry from '@sentry/browser';
import MatomoTracker from '@datapunt/matomo-tracker-js';
import { history } from './store';
import { SDK_API_PATH } from './index';
// @if TARGET='app'
import Native from 'native';
import ElectronCookies from '@exponent/electron-cookies';
import { generateInitialUrl } from 'util/url';
// @endif
import { MATOMO_ID, MATOMO_URL, LBRY_WEB_BUFFER_API } from 'config';

const isProduction = process.env.NODE_ENV === 'production';
const devInternalApis = process.env.LBRY_API_URL;

export const SHARE_INTERNAL = 'shareInternal';
const SHARE_THIRD_PARTY = 'shareThirdParty';

// @if TARGET='app'
if (isProduction) {
  ElectronCookies.enable({
    origin: 'https://lbry.tv',
  });
}
// @endif

type Analytics = {
  error: (string) => Promise<any>,
  sentryError: ({} | string, {}) => Promise<any>,
  pageView: (string, ?string) => void,
  setUser: (Object) => void,
  toggleInternal: (boolean, ?boolean) => void,
  apiLogView: (string, string, string, ?number, ?() => void) => Promise<any>,
  apiLogPublish: (ChannelClaim | StreamClaim) => void,
  apiSyncTags: ({}) => void,
  tagFollowEvent: (string, boolean, ?string) => void,
  playerLoadedEvent: (?boolean) => void,
  playerStartedEvent: (?boolean) => void,
  videoFetchDuration: (string, number) => void,
  videoStartEvent: (string, number) => void,
  videoBufferEvent: (
    StreamClaim,
    {
      timeAtBuffer: number,
      bufferDuration: number,
      bitRate: number,
      duration: number,
      userId: string,
      playerPoweredBy: string,
      readyState: number,
    }
  ) => void,
  adsFetchedEvent: () => void,
  adsReceivedEvent: (any) => void,
  adsErrorEvent: (any) => void,
  emailProvidedEvent: () => void,
  emailVerifiedEvent: () => void,
  rewardEligibleEvent: () => void,
  startupEvent: () => void,
  purchaseEvent: (number) => void,
  readyEvent: (number) => void,
  openUrlEvent: (string) => void,
};

type LogPublishParams = {
  uri: string,
  claim_id: string,
  outpoint: string,
  channel_claim_id?: string,
};

let internalAnalyticsEnabled: boolean = IS_WEB || false;
// let thirdPartyAnalyticsEnabled: boolean = IS_WEB || false;
// @if TARGET='app'
if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEnabled = true;
// if (window.localStorage.getItem(SHARE_THIRD_PARTY) === 'true') thirdPartyAnalyticsEnabled = true;
// @endif

const analytics: Analytics = {
  error: (message) => {
    return new Promise((resolve) => {
      if (internalAnalyticsEnabled && isProduction) {
        return Lbryio.call('event', 'desktop_error', { error_message: message }).then(() => {
          resolve(true);
        });
      } else {
        resolve(false);
      }
    });
  },
  sentryError: (error, errorInfo) => {
    return new Promise((resolve) => {
      if (internalAnalyticsEnabled && isProduction) {
        Sentry.withScope((scope) => {
          scope.setExtras(errorInfo);
          const eventId = Sentry.captureException(error);
          resolve(eventId);
        });
      } else {
        resolve(null);
      }
    });
  },
  pageView: (path, search) => {
    if (internalAnalyticsEnabled) {
      const params: { href: string, customDimensions?: Array<{ id: number, value: ?string }> } = { href: `${path}` };
      const dimensions = [];
      const searchParams = search && new URLSearchParams(search);

      if (searchParams && searchParams.get('src')) {
        dimensions.push({ id: 1, value: searchParams.get('src') });
      }
      if (dimensions.length) {
        params['customDimensions'] = dimensions;
      }
      MatomoInstance.trackPageView(params);
    }
  },
  setUser: (userId) => {
    if (internalAnalyticsEnabled && userId) {
      window._paq.push(['setUserId', String(userId)]);
      // @if TARGET='app'
      Native.getAppVersionInfo().then(({ localVersion }) => {
        sendMatomoEvent('Version', 'Desktop-Version', localVersion);
      });
      // @endif
    }
  },
  toggleInternal: (enabled: boolean): void => {
    // Always collect analytics on lbry.tv
    // @if TARGET='app'
    internalAnalyticsEnabled = enabled;
    window.localStorage.setItem(SHARE_INTERNAL, enabled);
    // @endif
  },

  toggleThirdParty: (enabled: boolean): void => {
    // Always collect analytics on lbry.tv
    // @if TARGET='app'
    // thirdPartyAnalyticsEnabled = enabled;
    window.localStorage.setItem(SHARE_THIRD_PARTY, enabled);
    // @endif
  },

  apiLogView: (uri, outpoint, claimId, timeToStart) => {
    return new Promise((resolve, reject) => {
      if (internalAnalyticsEnabled && (isProduction || devInternalApis)) {
        const params: {
          uri: string,
          outpoint: string,
          claim_id: string,
          time_to_start?: number,
        } = {
          uri,
          outpoint,
          claim_id: claimId,
        };

        // lbry.tv streams from AWS so we don't care about the time to start
        if (timeToStart && !IS_WEB) {
          params.time_to_start = timeToStart;
        }

        resolve(Lbryio.call('file', 'view', params));
      } else {
        resolve();
      }
    });
  },
  apiLogSearch: () => {
    if (internalAnalyticsEnabled && isProduction) {
      Lbryio.call('event', 'search');
    }
  },
  apiLogPublish: (claimResult: ChannelClaim | StreamClaim) => {
    // Don't check if this is production so channels created on localhost are still linked to user
    if (internalAnalyticsEnabled) {
      const { permanent_url: uri, claim_id: claimId, txid, nout, signing_channel: signingChannel } = claimResult;
      let channelClaimId;
      if (signingChannel) {
        channelClaimId = signingChannel.claim_id;
      }
      const outpoint = `${txid}:${nout}`;
      const params: LogPublishParams = { uri, claim_id: claimId, outpoint };
      if (channelClaimId) {
        params['channel_claim_id'] = channelClaimId;
      }

      Lbryio.call('event', 'publish', params);
    }
  },

  apiSyncTags: (params) => {
    if (internalAnalyticsEnabled && isProduction) {
      Lbryio.call('content_tags', 'sync', params);
    }
  },

  videoFetchDuration: (source, duration) => {
    sendPromMetric('time_to_fetch', duration);
    sendMatomoEvent('Media', 'TimeToFetch', source, duration);
  },

  videoStartEvent: (claimId, duration) => {
    sendPromMetric('time_to_start', duration);
    sendMatomoEvent('Media', 'TimeToStart', claimId, duration);
  },

  videoBufferEvent: (claim, data) => {
    sendMatomoEvent('Media', 'BufferTimestamp', claim.claim_id, data.timeAtBuffer);

    if (LBRY_WEB_BUFFER_API) {
      fetch(LBRY_WEB_BUFFER_API, {
        method: 'post',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          device: 'web',
          type: 'buffering',
          client: data.userId,
          data: {
            url: claim.canonical_url,
            position: data.timeAtBuffer,
            duration: data.bufferDuration,
            player: data.playerPoweredBy,
            readyState: data.readyState,
            stream_duration: data.duration,
            stream_bitrate: data.bitRate,
          },
        }),
      });
    }
  },
  adsFetchedEvent: () => {
    sendMatomoEvent('Media', 'AdsFetched');
  },
  adsReceivedEvent: (response) => {
    sendMatomoEvent('Media', 'AdsReceived', JSON.stringify(response));
  },
  adsErrorEvent: (response) => {
    sendMatomoEvent('Media', 'AdsError', JSON.stringify(response));
  },
  playerLoadedEvent: (embedded) => {
    sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite');
  },
  playerStartedEvent: (embedded) => {
    sendMatomoEvent('Player', 'Started', embedded ? 'embedded' : 'onsite');
  },
  tagFollowEvent: (tag, following) => {
    sendMatomoEvent('Tag', following ? 'Tag-Follow' : 'Tag-Unfollow', tag);
  },
  channelBlockEvent: (uri, blocked, location) => {
    sendMatomoEvent(blocked ? 'Channel-Hidden' : 'Channel-Unhidden', uri);
  },
  emailProvidedEvent: () => {
    sendMatomoEvent('Engagement', 'Email-Provided');
  },
  emailVerifiedEvent: () => {
    sendMatomoEvent('Engagement', 'Email-Verified');
  },
  rewardEligibleEvent: () => {
    sendMatomoEvent('Engagement', 'Reward-Eligible');
  },
  openUrlEvent: (url: string) => {
    sendMatomoEvent('Engagement', 'Open-Url', url);
  },
  trendingAlgorithmEvent: (trendingAlgorithm: string) => {
    sendMatomoEvent('Engagement', 'Trending-Algorithm', trendingAlgorithm);
  },
  startupEvent: () => {
    sendMatomoEvent('Startup', 'Startup');
  },
  readyEvent: (timeToReady: number) => {
    sendMatomoEvent('Startup', 'App-Ready', 'Time', timeToReady);
  },
  purchaseEvent: (purchaseInt: number) => {
    sendMatomoEvent('Purchase', 'Purchase-Complete', 'someLabel', purchaseInt);
  },
};

function sendMatomoEvent(category, action, name, value) {
  if (internalAnalyticsEnabled) {
    const event = { category, action, name, value };
    MatomoInstance.trackEvent(event);
  }
}

function sendPromMetric(name: string, value?: number) {
  if (IS_WEB) {
    let url = new URL(SDK_API_PATH + '/metric/ui');
    const params = { name: name, value: value ? value.toString() : '' };
    url.search = new URLSearchParams(params).toString();
    return fetch(url, { method: 'post' });
  }
}

const MatomoInstance = new MatomoTracker({
  urlBase: MATOMO_URL,
  siteId: MATOMO_ID, // optional, default value: `1`
  // heartBeat: { // optional, enabled by default
  //   active: true, // optional, default value: true
  //   seconds: 10 // optional, default value: `15
  // },
  // linkTracking: false // optional, default value: true
});

// Manually call the first page view
// React Router doesn't include this on `history.listen`
// @if TARGET='web'
analytics.pageView(window.location.pathname + window.location.search, window.location.search);
// @endif

// @if TARGET='app'
analytics.pageView(
  window.location.pathname.split('.html')[1] + window.location.search || generateInitialUrl(window.location.hash)
);
// @endif;

// Listen for url changes and report
// This will include search queries
history.listen((location) => {
  const { pathname, search } = location;

  const page = `${pathname}${search}`;
  analytics.pageView(page, search);
});

export default analytics;
<commit_msg>Remove time to fetch metric<commit_after>// @flow
import { Lbryio } from 'lbryinc';
import * as Sentry from '@sentry/browser';
import MatomoTracker from '@datapunt/matomo-tracker-js';
import { history } from './store';
import { SDK_API_PATH } from './index';
// @if TARGET='app'
import Native from 'native';
import ElectronCookies from '@exponent/electron-cookies';
import { generateInitialUrl } from 'util/url';
// @endif
import { MATOMO_ID, MATOMO_URL, LBRY_WEB_BUFFER_API } from 'config';

const isProduction = process.env.NODE_ENV === 'production';
const devInternalApis = process.env.LBRY_API_URL;

export const SHARE_INTERNAL = 'shareInternal';
const SHARE_THIRD_PARTY = 'shareThirdParty';

// @if TARGET='app'
if (isProduction) {
  ElectronCookies.enable({
    origin: 'https://lbry.tv',
  });
}
// @endif

type Analytics = {
  error: (string) => Promise<any>,
  sentryError: ({} | string, {}) => Promise<any>,
  pageView: (string, ?string) => void,
  setUser: (Object) => void,
  toggleInternal: (boolean, ?boolean) => void,
  apiLogView: (string, string, string, ?number, ?() => void) => Promise<any>,
  apiLogPublish: (ChannelClaim | StreamClaim) => void,
  apiSyncTags: ({}) => void,
  tagFollowEvent: (string, boolean, ?string) => void,
  playerLoadedEvent: (?boolean) => void,
  playerStartedEvent: (?boolean) => void,
  videoFetchDuration: (string, number) => void,
  videoStartEvent: (string, number) => void,
  videoBufferEvent: (
    StreamClaim,
    {
      timeAtBuffer: number,
      bufferDuration: number,
      bitRate: number,
      duration: number,
      userId: string,
      playerPoweredBy: string,
      readyState: number,
    }
  ) => void,
  adsFetchedEvent: () => void,
  adsReceivedEvent: (any) => void,
  adsErrorEvent: (any) => void,
  emailProvidedEvent: () => void,
  emailVerifiedEvent: () => void,
  rewardEligibleEvent: () => void,
  startupEvent: () => void,
  purchaseEvent: (number) => void,
  readyEvent: (number) => void,
  openUrlEvent: (string) => void,
};

type LogPublishParams = {
  uri: string,
  claim_id: string,
  outpoint: string,
  channel_claim_id?: string,
};

let internalAnalyticsEnabled: boolean = IS_WEB || false;
// let thirdPartyAnalyticsEnabled: boolean = IS_WEB || false;
// @if TARGET='app'
if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEnabled = true;
// if (window.localStorage.getItem(SHARE_THIRD_PARTY) === 'true') thirdPartyAnalyticsEnabled = true;
// @endif

const analytics: Analytics = {
  error: (message) => {
    return new Promise((resolve) => {
      if (internalAnalyticsEnabled && isProduction) {
        return Lbryio.call('event', 'desktop_error', { error_message: message }).then(() => {
          resolve(true);
        });
      } else {
        resolve(false);
      }
    });
  },
  sentryError: (error, errorInfo) => {
    return new Promise((resolve) => {
      if (internalAnalyticsEnabled && isProduction) {
        Sentry.withScope((scope) => {
          scope.setExtras(errorInfo);
          const eventId = Sentry.captureException(error);
          resolve(eventId);
        });
      } else {
        resolve(null);
      }
    });
  },
  pageView: (path, search) => {
    if (internalAnalyticsEnabled) {
      const params: { href: string, customDimensions?: Array<{ id: number, value: ?string }> } = { href: `${path}` };
      const dimensions = [];
      const searchParams = search && new URLSearchParams(search);

      if (searchParams && searchParams.get('src')) {
        dimensions.push({ id: 1, value: searchParams.get('src') });
      }
      if (dimensions.length) {
        params['customDimensions'] = dimensions;
      }
      MatomoInstance.trackPageView(params);
    }
  },
  setUser: (userId) => {
    if (internalAnalyticsEnabled && userId) {
      window._paq.push(['setUserId', String(userId)]);
      // @if TARGET='app'
      Native.getAppVersionInfo().then(({ localVersion }) => {
        sendMatomoEvent('Version', 'Desktop-Version', localVersion);
      });
      // @endif
    }
  },
  toggleInternal: (enabled: boolean): void => {
    // Always collect analytics on lbry.tv
    // @if TARGET='app'
    internalAnalyticsEnabled = enabled;
    window.localStorage.setItem(SHARE_INTERNAL, enabled);
    // @endif
  },

  toggleThirdParty: (enabled: boolean): void => {
    // Always collect analytics on lbry.tv
    // @if TARGET='app'
    // thirdPartyAnalyticsEnabled = enabled;
    window.localStorage.setItem(SHARE_THIRD_PARTY, enabled);
    // @endif
  },

  apiLogView: (uri, outpoint, claimId, timeToStart) => {
    return new Promise((resolve, reject) => {
      if (internalAnalyticsEnabled && (isProduction || devInternalApis)) {
        const params: {
          uri: string,
          outpoint: string,
          claim_id: string,
          time_to_start?: number,
        } = {
          uri,
          outpoint,
          claim_id: claimId,
        };

        // lbry.tv streams from AWS so we don't care about the time to start
        if (timeToStart && !IS_WEB) {
          params.time_to_start = timeToStart;
        }

        resolve(Lbryio.call('file', 'view', params));
      } else {
        resolve();
      }
    });
  },
  apiLogSearch: () => {
    if (internalAnalyticsEnabled && isProduction) {
      Lbryio.call('event', 'search');
    }
  },
  apiLogPublish: (claimResult: ChannelClaim | StreamClaim) => {
    // Don't check if this is production so channels created on localhost are still linked to user
    if (internalAnalyticsEnabled) {
      const { permanent_url: uri, claim_id: claimId, txid, nout, signing_channel: signingChannel } = claimResult;
      let channelClaimId;
      if (signingChannel) {
        channelClaimId = signingChannel.claim_id;
      }
      const outpoint = `${txid}:${nout}`;
      const params: LogPublishParams = { uri, claim_id: claimId, outpoint };
      if (channelClaimId) {
        params['channel_claim_id'] = channelClaimId;
      }

      Lbryio.call('event', 'publish', params);
    }
  },

  apiSyncTags: (params) => {
    if (internalAnalyticsEnabled && isProduction) {
      Lbryio.call('content_tags', 'sync', params);
    }
  },

  videoStartEvent: (claimId, duration) => {
    sendPromMetric('time_to_start', duration);
    sendMatomoEvent('Media', 'TimeToStart', claimId, duration);
  },

  videoBufferEvent: (claim, data) => {
    sendMatomoEvent('Media', 'BufferTimestamp', claim.claim_id, data.timeAtBuffer);

    if (LBRY_WEB_BUFFER_API) {
      fetch(LBRY_WEB_BUFFER_API, {
        method: 'post',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          device: 'web',
          type: 'buffering',
          client: data.userId,
          data: {
            url: claim.canonical_url,
            position: data.timeAtBuffer,
            duration: data.bufferDuration,
            player: data.playerPoweredBy,
            readyState: data.readyState,
            stream_duration: data.duration,
            stream_bitrate: data.bitRate,
          },
        }),
      });
    }
  },
  adsFetchedEvent: () => {
    sendMatomoEvent('Media', 'AdsFetched');
  },
  adsReceivedEvent: (response) => {
    sendMatomoEvent('Media', 'AdsReceived', JSON.stringify(response));
  },
  adsErrorEvent: (response) => {
    sendMatomoEvent('Media', 'AdsError', JSON.stringify(response));
  },
  playerLoadedEvent: (embedded) => {
    sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite');
  },
  playerStartedEvent: (embedded) => {
    sendMatomoEvent('Player', 'Started', embedded ? 'embedded' : 'onsite');
  },
  tagFollowEvent: (tag, following) => {
    sendMatomoEvent('Tag', following ? 'Tag-Follow' : 'Tag-Unfollow', tag);
  },
  channelBlockEvent: (uri, blocked, location) => {
    sendMatomoEvent(blocked ? 'Channel-Hidden' : 'Channel-Unhidden', uri);
  },
  emailProvidedEvent: () => {
    sendMatomoEvent('Engagement', 'Email-Provided');
  },
  emailVerifiedEvent: () => {
    sendMatomoEvent('Engagement', 'Email-Verified');
  },
  rewardEligibleEvent: () => {
    sendMatomoEvent('Engagement', 'Reward-Eligible');
  },
  openUrlEvent: (url: string) => {
    sendMatomoEvent('Engagement', 'Open-Url', url);
  },
  trendingAlgorithmEvent: (trendingAlgorithm: string) => {
    sendMatomoEvent('Engagement', 'Trending-Algorithm', trendingAlgorithm);
  },
  startupEvent: () => {
    sendMatomoEvent('Startup', 'Startup');
  },
  readyEvent: (timeToReady: number) => {
    sendMatomoEvent('Startup', 'App-Ready', 'Time', timeToReady);
  },
  purchaseEvent: (purchaseInt: number) => {
    sendMatomoEvent('Purchase', 'Purchase-Complete', 'someLabel', purchaseInt);
  },
};

function sendMatomoEvent(category, action, name, value) {
  if (internalAnalyticsEnabled) {
    const event = { category, action, name, value };
    MatomoInstance.trackEvent(event);
  }
}

function sendPromMetric(name: string, value?: number) {
  if (IS_WEB) {
    let url = new URL(SDK_API_PATH + '/metric/ui');
    const params = { name: name, value: value ? value.toString() : '' };
    url.search = new URLSearchParams(params).toString();
    return fetch(url, { method: 'post' });
  }
}

const MatomoInstance = new MatomoTracker({
  urlBase: MATOMO_URL,
  siteId: MATOMO_ID, // optional, default value: `1`
  // heartBeat: { // optional, enabled by default
  //   active: true, // optional, default value: true
  //   seconds: 10 // optional, default value: `15
  // },
  // linkTracking: false // optional, default value: true
});

// Manually call the first page view
// React Router doesn't include this on `history.listen`
// @if TARGET='web'
analytics.pageView(window.location.pathname + window.location.search, window.location.search);
// @endif

// @if TARGET='app'
analytics.pageView(
  window.location.pathname.split('.html')[1] + window.location.search || generateInitialUrl(window.location.hash)
);
// @endif;

// Listen for url changes and report
// This will include search queries
history.listen((location) => {
  const { pathname, search } = location;

  const page = `${pathname}${search}`;
  analytics.pageView(page, search);
});

export default analytics;
<|endoftext|>


<commit_before>const path = require('path');
const fs = require('fs');
const util = require('util');
const j79 = require('j79-utils');
const osHomeDir = require('os-homedir');
const version = require('./../../package.json').version;

const cmdLineArgs = require('./cmd-line-args');
const utils = require('./utils');
const security = require('./security');
const logger = require('./logger');
const schemaValidation = require('./schema-validation');

const ENCODING_UTF8 = 'utf8';
const ENCODING_ASCII = 'ascii';
const AVAILABLE_ENCODINGS = [ENCODING_UTF8, ENCODING_ASCII];

const NEXL_HOME_DIR = cmdLineArgs.NEXL_HOME_DIR || path.join(osHomeDir(), '.nexl');

// --------------------------------------------------------------------------------
// files
const CONF_FILES = {
	SETTINGS: 'settings.js', // general settings
	TOKENS: 'tokens.js', // tokens to register a user and reset password
	PASSWORDS: 'passwords.js', // password for login
	ADMINS: 'admins.js', // administrators list
	PERMISSIONS: 'permissions.js' // permissions matrix
};

// --------------------------------------------------------------------------------
// available options for SETTINGS
const SETTINGS = {
	NEXL_SOURCES_DIR: 'nexl-sources-dir',
	NEXL_SOURCES_PATH: 'nexl-sources-path',
	NEXL_SOURCES_ENCODING: 'nexl-sources-encoding',
	HTTP_TIMEOUT: 'http-timeout',
	LDAP_URL: 'ldap-url',

	HTTP_BINDING: 'http-binding',
	HTTP_PORT: 'http-port',
	HTTPS_BINDING: 'https-binding',
	HTTPS_PORT: 'https-port',
	SSL_CERT_LOCATION: 'ssl-cert-location',
	SSL_KEY_LOCATION: 'ssl-key-location',

	LOG_FILE_LOCATION: 'log-file-location',
	LOG_LEVEL: 'log-level',
	LOG_ROTATE_FILE_SIZE: 'log-rotate-file-size-kb',
	LOG_ROTATE_FILES_COUNT: 'log-rotate-files-count',

	NOTIFICATIONS: 'notifications',
};

// --------------------------------------------------------------------------------
// default values
const DEF_VALUES = {};

// SETTINGS default values
DEF_VALUES[CONF_FILES.SETTINGS] = {};
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_DIR] = () => path.join(osHomeDir(), 'nexl-sources');
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_ENCODING] = ENCODING_UTF8;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTP_TIMEOUT] = 10;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTP_BINDING] = 'localhost';
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTP_PORT] = 3000;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTPS_BINDING] = 'localhost';
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTPS_PORT] = 443;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_FILE_LOCATION] = () => path.join(NEXL_HOME_DIR, 'nexl.log');
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_LEVEL] = 'info';
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILES_COUNT] = 999;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILE_SIZE] = 0; // not rotating

// TOKENS default values
DEF_VALUES[CONF_FILES.TOKENS] = [];

// PASSWORDS default values
DEF_VALUES[CONF_FILES.PASSWORDS] = [];

// ADMINS default values
DEF_VALUES[CONF_FILES.ADMINS] = [];

// PERMISSIONS default values
DEF_VALUES[CONF_FILES.PERMISSIONS] = {};

// --------------------------------------------------------------------------------
// validation schemas
const VALIDATION_SCHEMAS = {};

// --------------------------------------------------------------------------------
// SETTINGS validations
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS] = {};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_DIR] = (val) => {
	if (!j79.isString(val) || val.length < 1) {
		return 'nexl sources dir must be a non empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_PATH] = [
	(val) => {
		if (!j79.isString(val)) {
			return 'nexl sources path must be a string';
		}
	}
];
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_ENCODING] = (val) => {
	if (AVAILABLE_ENCODINGS.indexOf(val) < 0) {
		return 'nexl sources encoding must be one of the following : [' + AVAILABLE_ENCODINGS.join(',') + ']';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTP_TIMEOUT] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'HTTP timeout must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LDAP_URL] = () => true;
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTP_BINDING] = (val) => {
	if (!j79.isString(val)) {
		return 'HTTP binding must be a string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTP_PORT] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'HTTP port be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTPS_BINDING] = (val) => {
	if (val !== undefined && !j79.isString(val)) {
		return 'HTTPS binding must be a string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTPS_PORT] = (val) => {
	if (val === undefined) {
		return;
	}

	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'HTTPS port must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.SSL_CERT_LOCATION] = (val) => {
	if (val !== undefined && j79.isString(val) && val.length < 1) {
		return 'SSL certificate location must be a non empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.SSL_KEY_LOCATION] = (val) => {
	if (val !== undefined && j79.isString(val) && val.length < 1) {
		return 'SSL key location must be a non empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_FILE_LOCATION] = (val) => {
	if (!j79.isString(val) || val.length < 1) {
		return 'Log file location must be a nont empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_LEVEL] = (val) => {
	if (!j79.isString(val) && logger.getAvailLevels().indexOf(val) < 0) {
		return 'Log level must be of the following : [' + logger.getAvailLevels().join(',') + ']';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILE_SIZE] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'Log rotate file size must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILES_COUNT] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'Log rotate files count must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NOTIFICATIONS] = [
	(val) => {
		if (!j79.isString(val) && val.length < 0) {
			return 'Notification item must be a non empty string';
		}
	}
];

// --------------------------------------------------------------------------------
// TOKENS validations
VALIDATION_SCHEMAS[CONF_FILES.TOKENS] = {
	'*': (val) => {
		if (!j79.isString(val) || val.length < 1) {
			return 'Each token item must be a non empty string';
		}
	}
};

// --------------------------------------------------------------------------------
// PASSWORDS validations
VALIDATION_SCHEMAS[CONF_FILES.PASSWORDS] = {
	'*': (val) => {
		if (!j79.isString(val) || val.length < 1) {
			return 'Each password item must be a non empty string';
		}
	}
};

// --------------------------------------------------------------------------------
// ADMINS validations
VALIDATION_SCHEMAS[CONF_FILES.ADMINS] = [
	(val) => {
		if (!j79.isString(val) || val.length < 1) {
			return 'Each admin item must be a non empty string';
		}
	}
];

// --------------------------------------------------------------------------------
// PERMISSIONS validations
VALIDATION_SCHEMAS[CONF_FILES.PERMISSIONS] = {
	'*': {
		read: (val) => {
			if (!j79.isBool(val)) {
				return '[read] items must of a boolean type';
			}
		},
		write: (val) => {
			if (!j79.isBool(val)) {
				return '[write] items must of a boolean type';
			}
		}
	}
};

// --------------------------------------------------------------------------------
// api
function resolveFullPath(fileName) {
	if (path.isAbsolute(fileName)) {
		throw 'Absolute file name ???';
	}

	return path.join(NEXL_HOME_DIR, fileName);
}

function substDefValues(defValue) {
	if (j79.isPrimitive(defValue)) {
		return defValue;
	}

	if (j79.isFunction(defValue)) {
		return defValue();
	}

	if (j79.isArray(defValue)) {
		const result = [];
		for (let index in defValue) {
			let item = defValue[index];
			item = substDefValues(item);
			result.push(item);
		}
		return result;
	}

	if (j79.isObject(defValue)) {
		const result = {};
		for (let key in defValue) {
			let val = defValue[key];
			val = substDefValues(val);
			result[key] = val;
		}

		return result;
	}

	throw 'Bad default value';
}

function setupDefaultValues(data, fileName) {
	// setting upd default values for objects only ( there is no way to do this with arrays )
	if (!j79.isObject(data)) {
		return data;
	}

	const defValues = substDefValues(fileName);

	// iterating over defValues
	for (let key in defValues) {
		if (data[key] !== undefined) {
			continue;
		}

		data[key] = defValues[key];
	}

	return data;
}

function load(fileName) {
	const fullPath = resolveFullPath(fileName);
	logger.log.debug('Loading data from the [%s] file', fullPath);

	// file doesn't exist ?
	if (!fs.existsSync(fullPath)) {
		logger.log.debug('The [%s] file doesn\'t exist. Loading empty data', fullPath);
		return substDefValues(DEF_VALUES[fileName]);
	}

	// loading from file
	let conf = fs.readFileSync(fullPath, ENCODING_UTF8);
	// JSONing. The JSON must be an object which contains config version and the data itself
	conf = JSON.parse(conf);

	const version = conf['version'];
	const data = conf['data'];

	logger.log.debug('Data is loaded, config version is [%s]', version);

	// setting up default values
	return setupDefaultValues(data, fileName);
}

function save(data, fileName) {
	const fullPath = resolveFullPath(fileName);
	logger.log.debug('Saving data into the [%s] file', fullPath);

	// validating
	const schema = VALIDATION_SCHEMAS[fileName];
	const validationResult = schemaValidation(data, schema);

	if (validationResult === undefined) {
		const conf = {
			version: version,
			data: data
		};
		fs.writeFileSync(fullPath, JSON.stringify(conf, null, 2), ENCODING_UTF8);
		return;
	}

	const message = util.format('Data validation error. Reason : %s', validationResult);
	logger.log.error(message);
	throw message;
}

function isConfFileExists(fileName) {
	const fullPath = resolveFullPath(fileName);
	return fs.existsSync(fullPath);
}

function createNexlHomeDirIfNeeded() {
	// do not use logger in this function because it's not initialized yet !
	const nexlHomeDir = NEXL_HOME_DIR;

	// is nexl home dir exists ?
	if (!fs.existsSync(nexlHomeDir)) {
		console.log('Creating [%s] nexl home directory', nexlHomeDir);
		fs.mkdirSync(nexlHomeDir);
		return;
	}

	// is nexl home dir is a real dir ?
	if (!fs.lstatSync(nexlHomeDir).isDirectory()) {
		throw util.format('The [%s] nexl home directory points to existing file. Recreate it as a directory or use another nexl home directory in the following way :\nnexl --nexl-home=/path/to/nexl/home/directory', nexlHomeDir);
	}
}

function initNexlHomeDir() {
	logger.log.debug('Creating default configuration files if absent in [%s] nexl home dir', NEXL_HOME_DIR);

	// creating tokens file is not exists
	if (!isConfFileExists(CONF_FILES.TOKENS)) {
		logger.log.info('The [%s] file doesn\'t exist in [%s] directory. Creating a new one and generating token for [%s] user', CONF_FILES.TOKENS, NEXL_HOME_DIR, utils.ADMIN_USERNAME);
		const token = security.generateToken();
		const tokens = {};
		tokens[utils.ADMIN_USERNAME] = token;
		save(tokens, CONF_FILES.TOKENS);
		logger.log.info('------> Use a token stored in [%s] file located in [%s] directory to register a [%s] account', CONF_FILES.TOKENS, NEXL_HOME_DIR, utils.ADMIN_USERNAME);
	}

	// creating admins file if not exists
	if (!isConfFileExists(CONF_FILES.ADMINS)) {
		logger.log.info('The [%s] file doesn\'t exist in [%s] directory. Creating a new one with a [%s] user', CONF_FILES.ADMINS, NEXL_HOME_DIR, utils.ADMIN_USERNAME);
		const admins = [utils.ADMIN_USERNAME];
		save(admins, CONF_FILES.ADMINS);
	}

	// creating permissions file if not exists
	if (!isConfFileExists(CONF_FILES.PERMISSIONS)) {
		logger.log.info('The [%s] file doesn\'t exist in [%s] directory. Creating a new one with a default permissions for [%s] user', CONF_FILES.PERMISSIONS, NEXL_HOME_DIR, utils.UNAUTHORIZED_USERNAME);
		const permission = {};
		permission[utils.UNAUTHORIZED_USERNAME] = {
			read: true,
			write: true
		};
		save(permission, CONF_FILES.PERMISSIONS);
	}
}

// --------------------------------------------------------------------------------
module.exports.CONF_FILES = CONF_FILES;
module.exports.SETTINGS = SETTINGS;

module.exports.load = load;
module.exports.save = save;

module.exports.NEXL_HOME_DIR = NEXL_HOME_DIR;
module.exports.AVAILABLE_ENCODINGS = AVAILABLE_ENCODINGS;
module.exports.createNexlHomeDirIfNeeded = createNexlHomeDirIfNeeded;
module.exports.initNexlHomeDir = initNexlHomeDir;
// --------------------------------------------------------------------------------
<commit_msg>- removed redundant code<commit_after>const path = require('path');
const fs = require('fs');
const util = require('util');
const j79 = require('j79-utils');
const osHomeDir = require('os-homedir');
const version = require('./../../package.json').version;

const cmdLineArgs = require('./cmd-line-args');
const utils = require('./utils');
const security = require('./security');
const logger = require('./logger');
const schemaValidation = require('./schema-validation');

const ENCODING_UTF8 = 'utf8';
const ENCODING_ASCII = 'ascii';
const AVAILABLE_ENCODINGS = [ENCODING_UTF8, ENCODING_ASCII];

const NEXL_HOME_DIR = cmdLineArgs.NEXL_HOME_DIR || path.join(osHomeDir(), '.nexl');

// --------------------------------------------------------------------------------
// files
const CONF_FILES = {
	SETTINGS: 'settings.js', // general settings
	TOKENS: 'tokens.js', // tokens to register a user and reset password
	PASSWORDS: 'passwords.js', // password for login
	ADMINS: 'admins.js', // administrators list
	PERMISSIONS: 'permissions.js' // permissions matrix
};

// --------------------------------------------------------------------------------
// available options for SETTINGS
const SETTINGS = {
	NEXL_SOURCES_DIR: 'nexl-sources-dir',
	NEXL_SOURCES_PATH: 'nexl-sources-path',
	NEXL_SOURCES_ENCODING: 'nexl-sources-encoding',
	HTTP_TIMEOUT: 'http-timeout',
	LDAP_URL: 'ldap-url',

	HTTP_BINDING: 'http-binding',
	HTTP_PORT: 'http-port',
	HTTPS_BINDING: 'https-binding',
	HTTPS_PORT: 'https-port',
	SSL_CERT_LOCATION: 'ssl-cert-location',
	SSL_KEY_LOCATION: 'ssl-key-location',

	LOG_FILE_LOCATION: 'log-file-location',
	LOG_LEVEL: 'log-level',
	LOG_ROTATE_FILE_SIZE: 'log-rotate-file-size-kb',
	LOG_ROTATE_FILES_COUNT: 'log-rotate-files-count',

	NOTIFICATIONS: 'notifications',
};

// --------------------------------------------------------------------------------
// default values
const DEF_VALUES = {};

// SETTINGS default values
DEF_VALUES[CONF_FILES.SETTINGS] = {};
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_DIR] = () => path.join(osHomeDir(), 'nexl-sources');
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_ENCODING] = ENCODING_UTF8;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTP_TIMEOUT] = 10;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTP_BINDING] = 'localhost';
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTP_PORT] = 3000;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTPS_BINDING] = 'localhost';
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.HTTPS_PORT] = 443;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_FILE_LOCATION] = () => path.join(NEXL_HOME_DIR, 'nexl.log');
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_LEVEL] = 'info';
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILES_COUNT] = 999;
DEF_VALUES[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILE_SIZE] = 0; // not rotating

// TOKENS default values
DEF_VALUES[CONF_FILES.TOKENS] = [];

// PASSWORDS default values
DEF_VALUES[CONF_FILES.PASSWORDS] = [];

// ADMINS default values
DEF_VALUES[CONF_FILES.ADMINS] = [];

// PERMISSIONS default values
DEF_VALUES[CONF_FILES.PERMISSIONS] = {};

// --------------------------------------------------------------------------------
// validation schemas
const VALIDATION_SCHEMAS = {};

// --------------------------------------------------------------------------------
// SETTINGS validations
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS] = {};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_DIR] = (val) => {
	if (!j79.isString(val) || val.length < 1) {
		return 'nexl sources dir must be a non empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_PATH] = [
	(val) => {
		if (!j79.isString(val)) {
			return 'nexl sources path must be a string';
		}
	}
];
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NEXL_SOURCES_ENCODING] = (val) => {
	if (AVAILABLE_ENCODINGS.indexOf(val) < 0) {
		return 'nexl sources encoding must be one of the following : [' + AVAILABLE_ENCODINGS.join(',') + ']';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTP_TIMEOUT] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'HTTP timeout must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LDAP_URL] = () => true;
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTP_BINDING] = (val) => {
	if (!j79.isString(val)) {
		return 'HTTP binding must be a string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTP_PORT] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'HTTP port be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTPS_BINDING] = (val) => {
	if (val !== undefined && !j79.isString(val)) {
		return 'HTTPS binding must be a string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.HTTPS_PORT] = (val) => {
	if (val === undefined) {
		return;
	}

	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'HTTPS port must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.SSL_CERT_LOCATION] = (val) => {
	if (val !== undefined && j79.isString(val) && val.length < 1) {
		return 'SSL certificate location must be a non empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.SSL_KEY_LOCATION] = (val) => {
	if (val !== undefined && j79.isString(val) && val.length < 1) {
		return 'SSL key location must be a non empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_FILE_LOCATION] = (val) => {
	if (!j79.isString(val) || val.length < 1) {
		return 'Log file location must be a nont empty string';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_LEVEL] = (val) => {
	if (!j79.isString(val) && logger.getAvailLevels().indexOf(val) < 0) {
		return 'Log level must be of the following : [' + logger.getAvailLevels().join(',') + ']';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILE_SIZE] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'Log rotate file size must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.LOG_ROTATE_FILES_COUNT] = (val) => {
	if (!j79.isNumber(val) || val < 0 || !Number.isInteger(val)) {
		return 'Log rotate files count must be a positive integer';
	}
};
VALIDATION_SCHEMAS[CONF_FILES.SETTINGS][SETTINGS.NOTIFICATIONS] = [
	(val) => {
		if (!j79.isString(val) && val.length < 0) {
			return 'Notification item must be a non empty string';
		}
	}
];

// --------------------------------------------------------------------------------
// TOKENS validations
VALIDATION_SCHEMAS[CONF_FILES.TOKENS] = {
	'*': (val) => {
		if (!j79.isString(val) || val.length < 1) {
			return 'Each token item must be a non empty string';
		}
	}
};

// --------------------------------------------------------------------------------
// PASSWORDS validations
VALIDATION_SCHEMAS[CONF_FILES.PASSWORDS] = {
	'*': (val) => {
		if (!j79.isString(val) || val.length < 1) {
			return 'Each password item must be a non empty string';
		}
	}
};

// --------------------------------------------------------------------------------
// ADMINS validations
VALIDATION_SCHEMAS[CONF_FILES.ADMINS] = [
	(val) => {
		if (!j79.isString(val) || val.length < 1) {
			return 'Each admin item must be a non empty string';
		}
	}
];

// --------------------------------------------------------------------------------
// PERMISSIONS validations
VALIDATION_SCHEMAS[CONF_FILES.PERMISSIONS] = {
	'*': {
		read: (val) => {
			if (!j79.isBool(val)) {
				return '[read] items must of a boolean type';
			}
		},
		write: (val) => {
			if (!j79.isBool(val)) {
				return '[write] items must of a boolean type';
			}
		}
	}
};

// --------------------------------------------------------------------------------
// api
function resolveFullPath(fileName) {
	return path.join(NEXL_HOME_DIR, fileName);
}

function substDefValues(defValue) {
	if (j79.isPrimitive(defValue)) {
		return defValue;
	}

	if (j79.isFunction(defValue)) {
		return defValue();
	}

	if (j79.isArray(defValue)) {
		const result = [];
		for (let index in defValue) {
			let item = defValue[index];
			item = substDefValues(item);
			result.push(item);
		}
		return result;
	}

	if (j79.isObject(defValue)) {
		const result = {};
		for (let key in defValue) {
			let val = defValue[key];
			val = substDefValues(val);
			result[key] = val;
		}

		return result;
	}

	throw 'Bad default value';
}

function setupDefaultValues(data, fileName) {
	// setting upd default values for objects only ( there is no way to do this with arrays )
	if (!j79.isObject(data)) {
		return data;
	}

	const defValues = substDefValues(fileName);

	// iterating over defValues
	for (let key in defValues) {
		if (data[key] !== undefined) {
			continue;
		}

		data[key] = defValues[key];
	}

	return data;
}

function load(fileName) {
	const fullPath = resolveFullPath(fileName);
	logger.log.debug('Loading data from the [%s] file', fullPath);

	// file doesn't exist ?
	if (!fs.existsSync(fullPath)) {
		logger.log.debug('The [%s] file doesn\'t exist. Loading empty data', fullPath);
		return substDefValues(DEF_VALUES[fileName]);
	}

	// loading from file
	let conf = fs.readFileSync(fullPath, ENCODING_UTF8);
	// JSONing. The JSON must be an object which contains config version and the data itself
	conf = JSON.parse(conf);

	const version = conf['version'];
	const data = conf['data'];

	logger.log.debug('Data is loaded, config version is [%s]', version);

	// setting up default values
	return setupDefaultValues(data, fileName);
}

function save(data, fileName) {
	const fullPath = resolveFullPath(fileName);
	logger.log.debug('Saving data into the [%s] file', fullPath);

	// validating
	const schema = VALIDATION_SCHEMAS[fileName];
	const validationResult = schemaValidation(data, schema);

	if (validationResult === undefined) {
		const conf = {
			version: version,
			data: data
		};
		fs.writeFileSync(fullPath, JSON.stringify(conf, null, 2), ENCODING_UTF8);
		return;
	}

	const message = util.format('Data validation error. Reason : %s', validationResult);
	logger.log.error(message);
	throw message;
}

function isConfFileExists(fileName) {
	const fullPath = resolveFullPath(fileName);
	return fs.existsSync(fullPath);
}

function createNexlHomeDirIfNeeded() {
	// do not use logger in this function because it's not initialized yet !
	const nexlHomeDir = NEXL_HOME_DIR;

	// is nexl home dir exists ?
	if (!fs.existsSync(nexlHomeDir)) {
		console.log('Creating [%s] nexl home directory', nexlHomeDir);
		fs.mkdirSync(nexlHomeDir);
		return;
	}

	// is nexl home dir is a real dir ?
	if (!fs.lstatSync(nexlHomeDir).isDirectory()) {
		throw util.format('The [%s] nexl home directory points to existing file. Recreate it as a directory or use another nexl home directory in the following way :\nnexl --nexl-home=/path/to/nexl/home/directory', nexlHomeDir);
	}
}

function initNexlHomeDir() {
	logger.log.debug('Creating default configuration files if absent in [%s] nexl home dir', NEXL_HOME_DIR);

	// creating tokens file is not exists
	if (!isConfFileExists(CONF_FILES.TOKENS)) {
		logger.log.info('The [%s] file doesn\'t exist in [%s] directory. Creating a new one and generating token for [%s] user', CONF_FILES.TOKENS, NEXL_HOME_DIR, utils.ADMIN_USERNAME);
		const token = security.generateToken();
		const tokens = {};
		tokens[utils.ADMIN_USERNAME] = token;
		save(tokens, CONF_FILES.TOKENS);
		logger.log.info('------> Use a token stored in [%s] file located in [%s] directory to register a [%s] account', CONF_FILES.TOKENS, NEXL_HOME_DIR, utils.ADMIN_USERNAME);
	}

	// creating admins file if not exists
	if (!isConfFileExists(CONF_FILES.ADMINS)) {
		logger.log.info('The [%s] file doesn\'t exist in [%s] directory. Creating a new one with a [%s] user', CONF_FILES.ADMINS, NEXL_HOME_DIR, utils.ADMIN_USERNAME);
		const admins = [utils.ADMIN_USERNAME];
		save(admins, CONF_FILES.ADMINS);
	}

	// creating permissions file if not exists
	if (!isConfFileExists(CONF_FILES.PERMISSIONS)) {
		logger.log.info('The [%s] file doesn\'t exist in [%s] directory. Creating a new one with a default permissions for [%s] user', CONF_FILES.PERMISSIONS, NEXL_HOME_DIR, utils.UNAUTHORIZED_USERNAME);
		const permission = {};
		permission[utils.UNAUTHORIZED_USERNAME] = {
			read: true,
			write: true
		};
		save(permission, CONF_FILES.PERMISSIONS);
	}
}

// --------------------------------------------------------------------------------
module.exports.CONF_FILES = CONF_FILES;
module.exports.SETTINGS = SETTINGS;

module.exports.load = load;
module.exports.save = save;

module.exports.NEXL_HOME_DIR = NEXL_HOME_DIR;
module.exports.AVAILABLE_ENCODINGS = AVAILABLE_ENCODINGS;
module.exports.createNexlHomeDirIfNeeded = createNexlHomeDirIfNeeded;
module.exports.initNexlHomeDir = initNexlHomeDir;
// --------------------------------------------------------------------------------
<|endoftext|>


<commit_before>exports.submitOrder = function (request, response) {
    var name = request.body.name;
    var licencePlate = request.body.licencePlate;
    var mobile = request.body.mobile;
    var orderDate = request.body.orderDate;
    var order = new Order;
    order.name = name;
    order.licencePlate = licencePlate;
    order.mobile = mobile;
    order.orderDate = orderDate;
    order.save(function (err, order, numberAffected) {
        if (err) {
            console.log("error:::::::"+err);
            response.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'  });
            response.end(JSON.stringify({"error": err}), 200);
            return;
        }
        response.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'  });
        if (parseInt(numberAffected) != 1) {
            response.end(JSON.stringify({"error": "Could not insert the item:"+error+", " + numberAffected}), 500);
        }else{
            response.end(JSON.stringify({"ok":"item stored!"}, 200));
        }
    });

}<commit_msg>order persisting to db.<commit_after>exports.submitOrder = function (request, response) {
    console.dir("----   submitOrder -----");
    console.dir(request.body);
    console.log("name:"+request.body.name);
    console.log("mobile:"+request.body.mobile);
    console.log("nomer:"+request.body.licencePlate);
    console.log("orderDate:"+request.body.orderDate);
   var order = new Order;
    order.name = request.body.name;
    order.licencePlate = request.body.licencePlate;
    order.mobile = request.body.mobile;
    order.orderDate = request.body.orderDate;
    order.save(function (err, order, numberAffected) {
        if (err) {
            console.log("error:::::::"+err);
            response.jsonp(400,{error:"missing fields"});
            return;
        }
        response.jsonp(400,{result:"ok"});
    });

}<|endoftext|>


<commit_before>const SERVICE_TYPES = {
  MEDIASCAPE: '_mediascape-http._tcp.local',
  CAPI: '_capi._tcp.local'
};

var logToObject   = false,
    serviceTypes  = [SERVICE_TYPES.CAPI, SERVICE_TYPES.MEDIASCAPE],
    serviceCache  = {},
    recipients    = [];

// Override some logging functions
// to add to an array
var Logger = function (override, limit) {
  var logger = [];

  ['log', 'warn', 'error'].forEach(function(name) {
    override[name] = function () {
      logger.push(arguments);

      if (logger.length > limit) {
        logger.splice(0, logger.length - limit);
      }
    };
  });

  return logger;
};

if (logToObject) {
  var logger = Logger(console, 500);
}

/*
  Respond to other extension's requests for
  a complete service list
*/
chrome.runtime.onMessageExternal.addListener(function(message, sender) {
  if(recipients.indexOf(sender.id) == -1) {
    recipients.push(sender.id);
    sendMessage([sender.id], concatServices());
  }
});

serviceTypes.forEach(function(serviceType) {
  chrome.mdns.onServiceList.addListener(
    curryServiceListener(serviceType),
    {'serviceType': serviceType}
  );
});

function curryServiceListener(serviceType) {
  return function(services) {
    var mappedServices;

    function addServiceType(s) {
      s.serviceType = serviceType;

      return s;
    }

    mappedServices = services.map(addServiceType).map(transformTxtToKeys);

    console.log('Found %o for %s:', mappedServices.length, serviceType, mappedServices);
    updateRecipients(mappedServices, serviceType);
  }
}

function concatServices() {
  return Object.keys(serviceCache).reduce(function(prev, current) {
    return prev.concat(serviceCache[current]);
  }, []);
}

function updateRecipients(mappedServices, serviceType) {
  serviceCache[serviceType] = mappedServices;
  sendMessage(recipients, concatServices());
}

/*
  Parse a services TXT record values into
  key/value pairs on the `txt` object.
  e.g.  service.txt = ['id=15']
        => service.txt.id = 15

  Also attempts to parse JSON
  e.g.  service.txt = ['player={ id:15, name="dave"}']
        => service.txt.player.id = 15
           service.txt.player.name = "dave"
*/
function transformTxtToKeys(service) {
  var obj = {};

  service.host = service.serviceName.replace('.'+service.serviceType, '');
  service.address = service.ipAddress;
  service.port = service.serviceHostPort.split(':')[1];

  if(service.serviceData && service.serviceData.map) {
    service.serviceData.forEach(function (txt) {
      var parts = txt.split('='),
          key   = parts[0],
          value = parts[1] || true;

      try {
        value = JSON.parse(value);
      } catch (e) {
        // Value isn't JSON
      }

      obj[key] = value;
    });
    service.txt = obj;
  }

  switch(service.serviceType) {
    case SERVICE_TYPES.MEDIASCAPE:
      service.uri = 'http://' + service.serviceHostPort;
      break;
    case SERVICE_TYPES.CAPI:
      service.uri = 'ws://' + service.serviceHostPort + service.txt.Path;
      break;
  }

  return service;
}

function sendMessage(recievers, message) {
  recievers.forEach(function(recipient) {
    console.log('sending', recipient, message);
    chrome.runtime.sendMessage(
      recipient, message
    );
  });
}
<commit_msg>Minor change to comment<commit_after>const SERVICE_TYPES = {
  MEDIASCAPE: '_mediascape-http._tcp.local',
  CAPI: '_capi._tcp.local'
};

var logToObject   = false,
    serviceTypes  = [SERVICE_TYPES.CAPI, SERVICE_TYPES.MEDIASCAPE],
    serviceCache  = {},
    recipients    = [];

// Override some logging functions
// to add to an array
var Logger = function (override, limit) {
  var logger = [];

  ['log', 'warn', 'error'].forEach(function(name) {
    override[name] = function () {
      logger.push(arguments);

      if (logger.length > limit) {
        logger.splice(0, logger.length - limit);
      }
    };
  });

  return logger;
};

if (logToObject) {
  var logger = Logger(console, 500);
}

/*
  Respond to other extension's requests for
  a complete service list
*/
chrome.runtime.onMessageExternal.addListener(function(message, sender) {
  if(recipients.indexOf(sender.id) == -1) {
    recipients.push(sender.id);
    sendMessage([sender.id], concatServices());
  }
});

serviceTypes.forEach(function(serviceType) {
  chrome.mdns.onServiceList.addListener(
    curryServiceListener(serviceType),
    {'serviceType': serviceType}
  );
});

function curryServiceListener(serviceType) {
  return function(services) {
    var mappedServices;

    function addServiceType(s) {
      s.serviceType = serviceType;

      return s;
    }

    mappedServices = services.map(addServiceType).map(transformTxtToKeys);

    console.log('Found %o for %s:', mappedServices.length, serviceType, mappedServices);
    updateRecipients(mappedServices, serviceType);
  }
}

function concatServices() {
  return Object.keys(serviceCache).reduce(function(prev, current) {
    return prev.concat(serviceCache[current]);
  }, []);
}

function updateRecipients(mappedServices, serviceType) {
  serviceCache[serviceType] = mappedServices;
  sendMessage(recipients, concatServices());
}

/*
  Parse a services TXT record values into
  key/value pairs on the `txt` object.
  e.g.  service.txt = ['id=15']
        => service.txt.id = 15

  Also attempts to parse JSON
  e.g.  service.txt = ['player={ id:15, name="dave"}']
        => service.txt.player.id = 15
           service.txt.player.name = "dave"
*/
function transformTxtToKeys(service) {
  var obj = {};

  service.host = service.serviceName.replace('.'+service.serviceType, '');
  service.address = service.ipAddress;
  service.port = service.serviceHostPort.split(':')[1];

  if (service.serviceData && service.serviceData.map) {
    service.serviceData.forEach(function (txt) {
      var parts = txt.split('='),
          key   = parts[0],
          value = parts[1] || true;

      try {
        value = JSON.parse(value);
      }
      catch (e) {
        // Ignore - value isn't JSON
      }

      obj[key] = value;
    });

    service.txt = obj;
  }

  switch (service.serviceType) {
    case SERVICE_TYPES.MEDIASCAPE:
      service.uri = 'http://' + service.serviceHostPort;
      break;
    case SERVICE_TYPES.CAPI:
      service.uri = 'ws://' + service.serviceHostPort + service.txt.Path;
      break;
  }

  return service;
}

function sendMessage(recievers, message) {
  recievers.forEach(function(recipient) {
    console.log('sending', recipient, message);
    chrome.runtime.sendMessage(
      recipient, message
    );
  });
}
<|endoftext|>


<commit_before>module.exports = function(RED)
{
	"use strict";

	function HueTemperature(config)
	{
		RED.nodes.createNode(this, config);
		var bridge = RED.nodes.getNode(config.bridge);
		let huejay = require('huejay');
		var moment = require('moment');
		var context = this.context();
		var scope = this;

		//
		// CHECK CONFIG
		if(!config.sensorid || bridge == null)
		{
			this.status({fill: "red", shape: "ring", text: "not configured"});
			return false;
		}

		//
		// INITIALIZE CLIENT
		var temperatureSensorID = parseInt(config.sensorid);
		let client = new huejay.Client({
			host: (bridge.config.bridge).toString(),
			port: 80,
			username: bridge.config.key
		});

		//
		// UPDATE STATE
		scope.status({fill: "grey", shape: "dot", text: "initializing…"});
		this.recheck = setInterval(function()
		{
			client.sensors.getById(temperatureSensorID)
			.then(sensor => {
				var temperature = context.get('temperature') || false;

				if(sensor.config.reachable == false)
				{
					scope.status({fill: "red", shape: "ring", text: "not reachable"});
				}
				else if(temperature != sensor.state.temperature)
				{
					context.set('temperature', sensor.state.temperature);

					var celsius = Math.round(sensor.state.temperature * 100) / 100;
					var fahrenheit = Math.round(((celsius * 1.8)+32) * 100) / 100;

					var message = {};
					message.payload = {celsius: celsius, fahrenheit: fahrenheit, updated: moment.utc(sensor.state.lastUpdated).local().format()};

					message.info = {};
					message.info.id = sensor.id;
					message.info.uniqueId = sensor.uniqueId;
					message.info.name = sensor.name;
					message.info.type = sensor.type;
					message.info.softwareVersion = sensor.softwareVersion;
					message.info.battery = sensor.config.battery;

					message.info.model = {};
					message.info.model.id = sensor.model.id;
					message.info.model.manufacturer = sensor.model.manufacturer;
					message.info.model.name = sensor.model.name;
					message.info.model.type = sensor.model.type;

					scope.send(message);

					scope.status({fill: "yellow", shape: "dot", text: celsius+" °C / "+fahrenheit+" °F"});
				}
			})
			.catch(error => {
				score.error(error);
				scope.status({fill: "red", shape: "ring", text: "connection error"});
			});
		}, parseInt(bridge.config.interval));

		//
		// CLOSE NDOE / REMOVE RECHECK INTERVAL
		this.on('close', function()
		{
			clearInterval(scope.recheck);
		});
	}

	RED.nodes.registerType("hue-temperature", HueTemperature);
}
<commit_msg>Fix important type in temperature.js<commit_after>module.exports = function(RED)
{
	"use strict";

	function HueTemperature(config)
	{
		RED.nodes.createNode(this, config);
		var bridge = RED.nodes.getNode(config.bridge);
		let huejay = require('huejay');
		var moment = require('moment');
		var context = this.context();
		var scope = this;

		//
		// CHECK CONFIG
		if(!config.sensorid || bridge == null)
		{
			this.status({fill: "red", shape: "ring", text: "not configured"});
			return false;
		}

		//
		// INITIALIZE CLIENT
		var temperatureSensorID = parseInt(config.sensorid);
		let client = new huejay.Client({
			host: (bridge.config.bridge).toString(),
			port: 80,
			username: bridge.config.key
		});

		//
		// UPDATE STATE
		scope.status({fill: "grey", shape: "dot", text: "initializing…"});
		this.recheck = setInterval(function()
		{
			client.sensors.getById(temperatureSensorID)
			.then(sensor => {
				var temperature = context.get('temperature') || false;

				if(sensor.config.reachable == false)
				{
					scope.status({fill: "red", shape: "ring", text: "not reachable"});
				}
				else if(temperature != sensor.state.temperature)
				{
					context.set('temperature', sensor.state.temperature);

					var celsius = Math.round(sensor.state.temperature * 100) / 100;
					var fahrenheit = Math.round(((celsius * 1.8)+32) * 100) / 100;

					var message = {};
					message.payload = {celsius: celsius, fahrenheit: fahrenheit, updated: moment.utc(sensor.state.lastUpdated).local().format()};

					message.info = {};
					message.info.id = sensor.id;
					message.info.uniqueId = sensor.uniqueId;
					message.info.name = sensor.name;
					message.info.type = sensor.type;
					message.info.softwareVersion = sensor.softwareVersion;
					message.info.battery = sensor.config.battery;

					message.info.model = {};
					message.info.model.id = sensor.model.id;
					message.info.model.manufacturer = sensor.model.manufacturer;
					message.info.model.name = sensor.model.name;
					message.info.model.type = sensor.model.type;

					scope.send(message);

					scope.status({fill: "yellow", shape: "dot", text: celsius+" °C / "+fahrenheit+" °F"});
				}
			})
			.catch(error => {
				scope.error(error);
				scope.status({fill: "red", shape: "ring", text: "connection error"});
			});
		}, parseInt(bridge.config.interval));

		//
		// CLOSE NDOE / REMOVE RECHECK INTERVAL
		this.on('close', function()
		{
			clearInterval(scope.recheck);
		});
	}

	RED.nodes.registerType("hue-temperature", HueTemperature);
}
<|endoftext|>


<commit_before>var CreateClass = function(initFunction, memberMap) {
  for (mem in memberMap) {
    if (memberMap.hasOwnProperty(mem)) {
      initFunction.prototype[mem] = memberMap[mem];
    }
  }
  return initFunction;
}

var CreateSubClass = function(baseKlass, initFunction, memberMap) {
  initFunction.prototype = Object.create(baseKlass.prototype);
  initFunction.prototype.constructor = initFunction;
  for (mem in memberMap) {
    if (memberMap.hasOwnProperty(mem)) {
      initFunction.prototype[mem] = memberMap[mem];
    }
  }
  return initFunction;
}

module.exports = {
  CreateClass: CreateClass,
  CreateSubClass: CreateSubClass
}
<commit_msg>Converted memberMap traversal to use lodash<commit_after>var _ = require("lodash");

var CreateClass = function(initFunction, memberMap) {
  _.forOwn(memberMap, function(value, key) {
    initFunction.prototype[key] = value;
  });
  return initFunction;
}

var CreateSubClass = function(baseKlass, initFunction, memberMap) {
  initFunction.prototype = Object.create(baseKlass.prototype);
  initFunction.prototype.constructor = initFunction;
  _.forOwn(memberMap, function(value, key) {
    initFunction.prototype[key] = value;
  });
  return initFunction;
}

module.exports = {
  CreateClass: CreateClass,
  CreateSubClass: CreateSubClass
}
<|endoftext|>