<commit_before>/**
 * rh-icon
 *
 * @author Hein Bekker <hein@netbek.co.za>
 * @copyright (c) 2015 Hein Bekker
 * @license http://www.gnu.org/licenses/agpl-3.0.txt AGPLv3
 */

(function (window, rhea, undefined) {

	if (rhea.icon) {
		return;
	}

	var flags = {};

	flags.serialize = (typeof XMLSerializer == 'function');

	// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/svg.js
	flags.svg = !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;

	// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/svg/inline.js
	flags.inlinesvg = (function () {
		var div = document.createElement('div');
		div.innerHTML = '<svg/>';
		return (div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg';
	})();

	var svgSymbols = {};
	var xmlSerializer;

	if (flags.serialize) {
		xmlSerializer = new XMLSerializer();
	}

	/**
	 * Returns the child nodes of a <symbol> as a string.
	 *
	 * Symbols are "injected" because <use xlink:href="#symbol"> does not work
	 * on second-level pages, e.g. /path/to/page, if <base> is in .
	 * https://github.com/angular/angular.js/issues/8934
	 *
	 * @param {String} elmId DOM element ID
	 * @returns {Object}
	 */
	function getSvgSymbol (elmId) {
		if (elmId in svgSymbols) {
			return svgSymbols[elmId];
		}

		if (!xmlSerializer) {
			throw new Error('XMLSerializer is not instantiated.');
		}

		var viewBox;
		var inner = '';
		var elm = document.getElementById(elmId);

		// If symbol is an element node.
		if (elm && elm.nodeType === 1) {
			viewBox = elm.getAttribute('viewBox');

			_.forEach(elm.childNodes, function (node) {
				// If child node is not a text node.
				if (node.nodeType !== 3) {
					inner += xmlSerializer.serializeToString(node);
				}
			});
		}

		svgSymbols[elmId] = {
			viewBox: viewBox,
			inner: inner
		};

		return svgSymbols[elmId];
	}

	/**
	 *
	 * @param {String} color
	 * @returns {Boolean|String}
	 */
	function getFill (color) {
		var config = rhea.iconConfig.get();
		if (color && color in config.colors) {
			return config.colors[color];
		}
		return false;
	}

	/**
	 *
	 * @param {Object} opts
	 * @returns {String}
	 */
	function renderPng (opts) {
		var config = rhea.iconConfig.get();
		return '';
	}

	/**
	 *
	 * @param {Object} opts
	 * @returns {String}
	 */
	function renderSvg (opts) {
		var config = rhea.iconConfig.get();
		var symbol = getSvgSymbol(config.prefix + '-' + opts.id);
		var viewBox = symbol.viewBox || '0 0 ' + opts.width + ' ' + opts.height;
		return '<svg class="' + opts.className + '"' + (opts.fill ? ' fill="' + opts.fill + '"' : '') + ' viewBox="' + viewBox + '" preserveAspectRatio="xMidYMid meet" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' + symbol.inner + '</svg>';
	}

	/**
	 *
	 * @returns {rhIconConfig}
	 */
	function rhIconConfig () {
		this.config = {
			colors: {}, // {Object} Colors of default and hover icons, if any. Key: color, value: hexadecimal or named value (compatible with <svg> `fill` attribute)
			prefix: 'icon', // {String}
			pngUrl: '', // {String} URL of directory with PNG fallback images
			size: 128, // {Number} Width and height of SVG (viewBox)
			svg: undefined // {Boolean} Override SVG feature detection (can be used for testing fallback images)
		};
	}

	/**
	 *
	 * @param {Object} values
	 */
	rhIconConfig.prototype.set = function (values) {
		_.merge(this.config, values);
	};

	/**
	 *
	 * @returns {Object}
	 */
	rhIconConfig.prototype.get = function () {
		return this.config;
	};

	/**
	 *
	 * @returns {rhIcon}
	 */
	function rhIcon () {

	}

	/**
	 *
	 * @param {jQuery} context
	 */
	rhIcon.prototype.apply = function (context) {
		var config = rhea.iconConfig.get();
		var useSvg = (config.svg === true || (config.svg === undefined && flags.serialize && flags.svg && flags.inlinesvg));

		var $elms;
		if (context instanceof jQuery && context.is('[rh-icon]')) {
			$elms = context;
		}
		else {
			$elms = jQuery('[rh-icon]', context);
		}

		_.forEach($elms, function (elm) {
			var $elm = jQuery(elm);
			var attrs = $elm.data() || {};

			if (attrs.rhIconProcessed) {
				return;
			}
			$elm.data('rh-icon-processed', true);

			var prefix = config.prefix;
			var id = attrs.id;
			var hoverId = attrs.hoverId;
			var hoverColor = attrs.hoverColor;
			var title = attrs.title;

			$elm.attr('class', prefix + ' ' + prefix + '-' + id + (hoverId || hoverColor ? ' has-hover' : ''));
			$elm.attr('title', title);
			$elm.attr('aria-hidden', 'true');
			$elm.attr('draggable', 'false');
			$elm.attr('ondragstart', 'return false;');

			var color = attrs.color;
			var defaultIcon = {
				className: 'default' + (color ? ' ' + color : ''),
				id: attrs.id,
				color: color,
				fill: getFill(color),
				width: attrs.width || config.size,
				height: attrs.height || config.size
			};

			var hoverIcon;
			if (attrs.hoverId || attrs.hoverColor) {
				hoverColor = attrs.hoverColor || color;
				hoverIcon = {
					className: 'hover' + (hoverColor ? ' ' + hoverColor : ''),
					id: attrs.hoverId || attrs.id,
					color: hoverColor,
					fill: getFill(hoverColor),
					width: defaultIcon.width,
					height: defaultIcon.height
				};
			}

			var html;
			if (useSvg) {
				html = renderSvg(defaultIcon);
				if (hoverIcon) {
					html += renderSvg(hoverIcon);
				}
			}
			else {
				html = renderPng(defaultIcon);
				if (hoverIcon) {
					html += renderPng(hoverIcon);
				}
			}

			$elm.html(html);

			// Use padding-bottom hack on container to preserve aspect ratio.
			// @see https://css-tricks.com/scale-svg
			var ratio = Number(defaultIcon.height / defaultIcon.width * 10000) / 100;
			$elm.attr('style', (ratio === 100 ? '' : 'height: 0; padding: 0 0 ' + ratio + '% 0;'));
		});
	};

	rhea.icon = new rhIcon();
	rhea.iconConfig = new rhIconConfig();

})(window, window.rhea);<commit_msg>Replace _ with lodash<commit_after>/**
 * rh-icon
 *
 * @author Hein Bekker <hein@netbek.co.za>
 * @copyright (c) 2015 Hein Bekker
 * @license http://www.gnu.org/licenses/agpl-3.0.txt AGPLv3
 */

(function (window, rhea, undefined) {

	if (rhea.icon) {
		return;
	}

	var flags = {};

	flags.serialize = (typeof XMLSerializer == 'function');

	// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/svg.js
	flags.svg = !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;

	// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/svg/inline.js
	flags.inlinesvg = (function () {
		var div = document.createElement('div');
		div.innerHTML = '<svg/>';
		return (div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg';
	})();

	var svgSymbols = {};
	var xmlSerializer;

	if (flags.serialize) {
		xmlSerializer = new XMLSerializer();
	}

	/**
	 * Returns the child nodes of a <symbol> as a string.
	 *
	 * Symbols are "injected" because <use xlink:href="#symbol"> does not work
	 * on second-level pages, e.g. /path/to/page, if <base> is in .
	 * https://github.com/angular/angular.js/issues/8934
	 *
	 * @param {String} elmId DOM element ID
	 * @returns {Object}
	 */
	function getSvgSymbol (elmId) {
		if (elmId in svgSymbols) {
			return svgSymbols[elmId];
		}

		if (!xmlSerializer) {
			throw new Error('XMLSerializer is not instantiated.');
		}

		var viewBox;
		var inner = '';
		var elm = document.getElementById(elmId);

		// If symbol is an element node.
		if (elm && elm.nodeType === 1) {
			viewBox = elm.getAttribute('viewBox');

			lodash.forEach(elm.childNodes, function (node) {
				// If child node is not a text node.
				if (node.nodeType !== 3) {
					inner += xmlSerializer.serializeToString(node);
				}
			});
		}

		svgSymbols[elmId] = {
			viewBox: viewBox,
			inner: inner
		};

		return svgSymbols[elmId];
	}

	/**
	 *
	 * @param {String} color
	 * @returns {Boolean|String}
	 */
	function getFill (color) {
		var config = rhea.iconConfig.get();
		if (color && color in config.colors) {
			return config.colors[color];
		}
		return false;
	}

	/**
	 *
	 * @param {Object} opts
	 * @returns {String}
	 */
	function renderPng (opts) {
		var config = rhea.iconConfig.get();
		return '';
	}

	/**
	 *
	 * @param {Object} opts
	 * @returns {String}
	 */
	function renderSvg (opts) {
		var config = rhea.iconConfig.get();
		var symbol = getSvgSymbol(config.prefix + '-' + opts.id);
		var viewBox = symbol.viewBox || '0 0 ' + opts.width + ' ' + opts.height;
		return '<svg class="' + opts.className + '"' + (opts.fill ? ' fill="' + opts.fill + '"' : '') + ' viewBox="' + viewBox + '" preserveAspectRatio="xMidYMid meet" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' + symbol.inner + '</svg>';
	}

	/**
	 *
	 * @returns {rhIconConfig}
	 */
	function rhIconConfig () {
		this.config = {
			colors: {}, // {Object} Colors of default and hover icons, if any. Key: color, value: hexadecimal or named value (compatible with <svg> `fill` attribute)
			prefix: 'icon', // {String}
			pngUrl: '', // {String} URL of directory with PNG fallback images
			size: 128, // {Number} Width and height of SVG (viewBox)
			svg: undefined // {Boolean} Override SVG feature detection (can be used for testing fallback images)
		};
	}

	/**
	 *
	 * @param {Object} values
	 */
	rhIconConfig.prototype.set = function (values) {
		lodash.merge(this.config, values);
	};

	/**
	 *
	 * @returns {Object}
	 */
	rhIconConfig.prototype.get = function () {
		return this.config;
	};

	/**
	 *
	 * @returns {rhIcon}
	 */
	function rhIcon () {

	}

	/**
	 *
	 * @param {jQuery} context
	 */
	rhIcon.prototype.apply = function (context) {
		var config = rhea.iconConfig.get();
		var useSvg = (config.svg === true || (config.svg === undefined && flags.serialize && flags.svg && flags.inlinesvg));

		var $elms;
		if (context instanceof jQuery && context.is('[rh-icon]')) {
			$elms = context;
		}
		else {
			$elms = jQuery('[rh-icon]', context);
		}

		lodash.forEach($elms, function (elm) {
			var $elm = jQuery(elm);
			var attrs = $elm.data() || {};

			if (attrs.rhIconProcessed) {
				return;
			}
			$elm.data('rh-icon-processed', true);

			var prefix = config.prefix;
			var id = attrs.id;
			var hoverId = attrs.hoverId;
			var hoverColor = attrs.hoverColor;
			var title = attrs.title;

			$elm.attr('class', prefix + ' ' + prefix + '-' + id + (hoverId || hoverColor ? ' has-hover' : ''));
			$elm.attr('title', title);
			$elm.attr('aria-hidden', 'true');
			$elm.attr('draggable', 'false');
			$elm.attr('ondragstart', 'return false;');

			var color = attrs.color;
			var defaultIcon = {
				className: 'default' + (color ? ' ' + color : ''),
				id: attrs.id,
				color: color,
				fill: getFill(color),
				width: attrs.width || config.size,
				height: attrs.height || config.size
			};

			var hoverIcon;
			if (attrs.hoverId || attrs.hoverColor) {
				hoverColor = attrs.hoverColor || color;
				hoverIcon = {
					className: 'hover' + (hoverColor ? ' ' + hoverColor : ''),
					id: attrs.hoverId || attrs.id,
					color: hoverColor,
					fill: getFill(hoverColor),
					width: defaultIcon.width,
					height: defaultIcon.height
				};
			}

			var html;
			if (useSvg) {
				html = renderSvg(defaultIcon);
				if (hoverIcon) {
					html += renderSvg(hoverIcon);
				}
			}
			else {
				html = renderPng(defaultIcon);
				if (hoverIcon) {
					html += renderPng(hoverIcon);
				}
			}

			$elm.html(html);

			// Use padding-bottom hack on container to preserve aspect ratio.
			// @see https://css-tricks.com/scale-svg
			var ratio = Number(defaultIcon.height / defaultIcon.width * 10000) / 100;
			$elm.attr('style', (ratio === 100 ? '' : 'height: 0; padding: 0 0 ' + ratio + '% 0;'));
		});
	};

	rhea.icon = new rhIcon();
	rhea.iconConfig = new rhIconConfig();

})(window, window.rhea);<|endoftext|>


<commit_before>/*
	test launcher invoked by jsx --test
*/
function $__jsx_runTests(testClass, tests) {
	if (!testClass) return; // skip if there's no test class

	if(tests.length === 0) {
		var p = testClass.prototype;
		for (var m in p) {
			if (p[m] instanceof Function
				&& /^test.*[$]$/.test(m)) {
				tests.push(m);
			}
		}
	}

	if (tests.length === 0) {
		return;
	}

	var tasks = [];

	var test = new testClass();

	test.beforeClass$AS(tests);

	for(var i = 0; i < tests.length; ++i) {
		(function (m) {
			tasks.push(function() {
				test.done$ = function () {
					test.after$S(m);
					if (tasks.length !== 0) {
						var next = tasks.shift();
						next();
					}
					else { // all the tasks finished
						test.afterClass$();
					}
				};

				test.before$S(m);
				test[m]();
			});
		}(tests[i]));
	}

	var start = tasks.shift();
	start();
}
<commit_msg>Make test hooks optonal to run example/fib.jsx<commit_after>/*
	test launcher invoked by jsx --test
*/
function $__jsx_runTests(testClass, tests) {
	if (!testClass) return; // skip if there's no test class

	if(tests.length === 0) {
		var p = testClass.prototype;
		for (var m in p) {
			if (p[m] instanceof Function
				&& /^test.*[$]$/.test(m)) {
				tests.push(m);
			}
		}
	}

	if (tests.length === 0) {
		return;
	}

	var tasks = [];

	var test = new testClass();

	if (test.beforeClass$AS != null)
		test.beforeClass$AS(tests);

	for (var i = 0; i < tests.length; ++i) {
		(function (m) {
			tasks.push(function() {
				if (test.before$S != null)
					test.before$S(m);

				test.done$ = function () {
					if (test.after$S != null)
						test.after$S(m);
					if (tasks.length !== 0) {
						var next = tasks.shift();
						next();
					}
					else { // all the tasks finished
						if (test.afterClass$ != null)
							test.afterClass$();
					}
				};

				test[m]();
			});
		}(tests[i]));
	}

	var start = tasks.shift();
	start();
}
<|endoftext|>


<commit_before>// templex node module
// https://github.com/joneit/templex

(function (module) { // This closure supports NodeJS-less client side includes with <script> tags. See https://github.com/joneit/mnm.

    /**
     * Merges values of execution context properties named in template by {prop1},
     * {prop2}, etc., or any javascript expression incorporating such prop names.
     * The context always includes the global object. In addition you can specify a single
     * context or an array of contexts to search (in the order given) before finally
     * searching the global context.
     *
     * Merge expressions consisting of simple numeric terms, such as {0}, {1}, etc., deref
     * the first context given, which is assumed to be an array. As a convenience feature,
     * if additional args are given after `template`, `arguments` is unshifted onto the context
     * array, thus making first additional arg available as {1}, second as {2}, etc., as in
     * `templex('Hello, {1}!', 'World')`. ({0} is the template so consider this to be 1-based.)
     *
     * If you prefer something other than braces, redefine `templex.regexp`.
     *
     * See tests for additional examples.
     * @example templex.call({r:192,g:150,b:81}, "background-color: #{(r+256).toString(16).substr(1)}{(r+256).toString(16).substr(1)}{(r+256).toString(16).substr(1)}");
     * @example templex.call({r:192,g:150,b:81,hex2:function(n){return (n+256).toString(16).substr(1)}}, "background-color: #{hex2(r)}{hex2(g)}{hex2(b)}");
     * @example helpers = {hex2:function(n){return (n+256).toString(16).substr(1)}}; templex.call([{r:192,g:150,b:81}, helpers], "background-color: #{(hex2(r)}{(hex2(g)}{(hex2(b)}");
     *
     * @param {string} template
     * @param {...string} [args]
     */
    function templex(template) {
        var contexts = this instanceof Array ? this : [this];
        if (arguments.length > 1) { contexts.unshift(arguments); }
        return template.replace(templex.regexp, templex.merger.bind(contexts));
    }

    templex.regexp = /\{(.*?)\}/g;

    templex.with = function (i, s) {
        return 'with(this[' + i + ']){' + s + '}';
    };

    templex.cache = [];

    templex.deref = function (key) {
        if (!(this.length in templex.cache)) {
            var code = 'return eval(expr)';

            for (var i = 0; i < this.length; ++i) {
                code = templex.with(i, code);
            }

            templex.cache[this.length] = eval('(function(expr){' + code + '})');
        }
        return templex.cache[this.length].call(this, key);
    };

    templex.merger = function (match, key) {
        // Advanced features: Context can be a list of contexts which are searched in order.
        var replacement;

        try {
            if (isNaN(key)) {
                replacement = templex.deref.call(this, key);
            } else {
                replacement = this[0][key];
            }
        } catch (e) {
            replacement = '{' + key + '}';
        }

        return replacement;
    };

    // this interface consists solely of the templex function object
    module.exports = templex;

})(module, module.exports);
<commit_msg>Use window.templex when no window.module<commit_after>// templex node module
// https://github.com/joneit/templex

(function (module) { // This closure supports NodeJS-less client side includes with <script> tags. See https://github.com/joneit/mnm.

    /**
     * Merges values of execution context properties named in template by {prop1},
     * {prop2}, etc., or any javascript expression incorporating such prop names.
     * The context always includes the global object. In addition you can specify a single
     * context or an array of contexts to search (in the order given) before finally
     * searching the global context.
     *
     * Merge expressions consisting of simple numeric terms, such as {0}, {1}, etc., deref
     * the first context given, which is assumed to be an array. As a convenience feature,
     * if additional args are given after `template`, `arguments` is unshifted onto the context
     * array, thus making first additional arg available as {1}, second as {2}, etc., as in
     * `templex('Hello, {1}!', 'World')`. ({0} is the template so consider this to be 1-based.)
     *
     * If you prefer something other than braces, redefine `templex.regexp`.
     *
     * See tests for additional examples.
     * @example templex.call({r:192,g:150,b:81}, "background-color: #{(r+256).toString(16).substr(1)}{(r+256).toString(16).substr(1)}{(r+256).toString(16).substr(1)}");
     * @example templex.call({r:192,g:150,b:81,hex2:function(n){return (n+256).toString(16).substr(1)}}, "background-color: #{hex2(r)}{hex2(g)}{hex2(b)}");
     * @example helpers = {hex2:function(n){return (n+256).toString(16).substr(1)}}; templex.call([{r:192,g:150,b:81}, helpers], "background-color: #{(hex2(r)}{(hex2(g)}{(hex2(b)}");
     *
     * @param {string} template
     * @param {...string} [args]
     */
    function templex(template) {
        var contexts = this instanceof Array ? this : [this];
        if (arguments.length > 1) { contexts.unshift(arguments); }
        return template.replace(templex.regexp, templex.merger.bind(contexts));
    }

    templex.regexp = /\{(.*?)\}/g;

    templex.with = function (i, s) {
        return 'with(this[' + i + ']){' + s + '}';
    };

    templex.cache = [];

    templex.deref = function (key) {
        if (!(this.length in templex.cache)) {
            var code = 'return eval(expr)';

            for (var i = 0; i < this.length; ++i) {
                code = templex.with(i, code);
            }

            templex.cache[this.length] = eval('(function(expr){' + code + '})');
        }
        return templex.cache[this.length].call(this, key);
    };

    templex.merger = function (match, key) {
        // Advanced features: Context can be a list of contexts which are searched in order.
        var replacement;

        try {
            if (isNaN(key)) {
                replacement = templex.deref.call(this, key);
            } else {
                replacement = this[0][key];
            }
        } catch (e) {
            replacement = '{' + key + '}';
        }

        return replacement;
    };

    // this interface consists solely of the templex function object
    module.exports = templex;

})(
    window.module || (window.templex = {}),
    window.module && window.module.exports || (window.templex.exports = {})
) && (window.module || (window.templex = window.templex.exports));
<|endoftext|>


<commit_before>
import { Cfg } from '../core/lib';

function Page ( id, x18nPrefix, onTop, onLeave ) {

	const tab  = document.createElement( 'div' );
	const page = document.createElement( 'div' );

	var frame = Page.frame;
	var tabBox = Page.tabBox;

	page.classList.add( 'page' );

	tab.id = id;
	tab.classList.add( 'tab' );

	Page.addListener( tab, 'click', this.tabHandleClick );

	if ( onTop !== undefined ) {

		// callback when this page is made visible
		Page.addListener( tab, 'click', onTop );

	}

	if ( frame === null ) {

		// create UI side panel and reveal tabs
		frame = document.createElement( 'div' );

		frame.id = 'cv-frame';

		Page.frame = frame;

	}

	if ( tabBox === null ) {

		// create UI box to contain tabs - reorients for small screen widths
		tabBox = document.createElement( 'div' );

		tabBox.id = 'cv-tab-box';

		const close = document.createElement( 'div' );

		close.id = 'close';
		close.classList.add( 'tab' );

		Page.addListener( close, 'click', _closeFrame );

		tabBox.appendChild( close );

		Page.tabBox = tabBox;

	}

	tabBox.appendChild( tab );
	frame.appendChild( page );

	Page.pages.push( { tab: tab, page: page, owner: this } );

	this.page = page;
	this.onLeave = onLeave;
	this.slide = undefined;
	this.x18nPrefix = x18nPrefix + '.';
	this.onChange = null;

	function _closeFrame ( /* event */ ) {

		Page.tabBox.classList.remove( 'onscreen' );
		Page.frame.classList.remove( 'onscreen' );

	}

}

Page.pages     = [];
Page.listeners = [];
Page.inHandler = false;
Page.controls  = [];
Page.frame = null;
Page.tabBox = null;
Page.seq = 0;

Page.setParent = function ( parent ) {

	parent.appendChild( Page.tabBox );
	parent.appendChild( Page.frame );

};

Page.setControlsVisibility = function ( list, visible ) {

	const display = visible ? 'block' : 'none';

	list.forEach( function ( element ) {

		if ( element === null ) return;
		element.style.display = display;

	} );

};

Page.clear = function () {

	const frame  = Page.frame;
	const tabBox = Page.tabBox;

	if ( frame  !== null ) frame.parentElement.removeChild( frame );
	if ( tabBox !== null ) tabBox.parentElement.removeChild( tabBox );

	Page.listeners.forEach( function ( listener ) {

		listener.obj.removeEventListener( listener.name, listener.handler );

	} );

	Page.listeners = [];
	Page.pages     = [];
	Page.inHandler = false;
	Page.controls  = [];
	Page.frame     = null;
	Page.tabBox    = null;

};

Page.addFullscreenButton = function ( id, obj, property ) {

	const tabBox = this.tabBox;
	const fullscreen = document.createElement( 'div' );

	fullscreen.id = id;
	fullscreen.classList.add( 'tab' );

	Page.addListener( fullscreen, 'click', _toggleButton );

	Page.addListener( obj, 'change', _setButton );

	tabBox.appendChild( fullscreen );

	_setButton();

	return fullscreen;

	function _toggleButton () {

		obj[ property ] = ! obj[ property ];

		_setButton();

	}

	function _setButton () {

		if ( obj[ property ] ) {

			fullscreen.classList.remove( 'expand' );
			fullscreen.classList.add( 'collapse' );

		} else {

			fullscreen.classList.add( 'expand' );
			fullscreen.classList.remove( 'collapse' );

		}

	}

};

Page.addListener = function ( obj, name, handler ) {

	obj.addEventListener( name, handler, false );

	Page.listeners.push( {
		obj: obj,
		name: name,
		handler: handler
	} );

};

Page.handleChange = function ( event ) {

	const obj = event.target;
	const property = event.name;

	if ( ! Page.inHandle ) {

		if ( Page.controls[ property ] ) {

			const ctrl = Page.controls[ property ];

			switch ( ctrl.type ) {

			case 'checkbox':

				ctrl.checked = obj[ property ];

				break;

			case 'select-one':
			case 'range':

				ctrl.value = obj[ property ];

				break;

			case 'download':

				ctrl.href = obj[ property ];

				break;

			}

		}

	}

	Page.pages.forEach( function ( p ) {

		const page = p.owner;

		if ( page.onChange !== null ) page.onChange( event );

	} );

};

Page.prototype.constructor = Page;

Page.prototype.i18n = function ( text ) {

	const tr = Cfg.i18n( this.x18nPrefix + text );

	return ( tr === undefined ) ? text : tr;

};

Page.prototype.addListener = function ( obj, name, handler ) {

	Page.addListener( obj, name, handler ); // redirect to :: method - allows later rework to page specific destruction

};

Page.prototype.tabHandleClick = function ( event ) {

	const tab = event.target;
	const pages = Page.pages;

	tab.classList.add( 'toptab' );
	Page.tabBox.classList.add( 'onscreen' );
	Page.frame.classList.add( 'onscreen' );

	pages.forEach( function ( page ) {

		const otherPage = page.page;
		const otherTab = page.tab;
		const owner = page.owner;

		if ( otherTab === tab ) {

			otherPage.style.display = 'block';

		} else {

			otherPage.style.display = 'none';

			if ( otherTab.classList.contains( 'toptab' ) ) {

				otherTab.classList.remove( 'toptab' );

				if ( owner.onLeave !== undefined ) owner.onLeave();

			}

		}

	} );

};

Page.prototype.appendChild = function ( domElement ) {

	this.page.appendChild( domElement );

};

Page.prototype.addHeader = function ( text ) {

	const div = document.createElement( 'div' );

	div.classList.add( 'header' );
	div.textContent = this.i18n( text );

	this.page.appendChild( div );

	return div;

};

Page.prototype.addText = function ( text ) {

	const p = this.addLine( text );

	p.classList.add( 'spaced' );

	return p;

};

Page.prototype.addLine = function ( text ) {

	const p = document.createElement( 'p' );

	p.textContent = text;

	this.page.appendChild( p );

	return p;

};

Page.prototype.addLink = function ( url, text ) {

	const a = document.createElement( 'a' );

	a.href = url;
	a.textContent = text;
	a.target = '_blank';

	this.page.appendChild( a );

	return a;

};

Page.prototype.addSelect = function ( title, obj, trgObj, property, replace ) {

	const div    = document.createElement( 'div' );
	const label  = document.createElement( 'label' );
	const select = document.createElement( 'select' );

	div.classList.add( 'control' );

	if ( obj instanceof Array ) {

		obj.forEach( function ( element ) {

			const opt = document.createElement( 'option' );

			opt.value = element;
			opt.text = element;

			if ( opt.text === trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		} );

	} else {

		for ( var p in obj ) {

			const opt = document.createElement( 'option' );
			const self = this;

			const s = p.split( ' ' );

			if ( s.length > 1 ) {

				opt.text = s.reduce( function ( res, val) { return res + ' ' + self.i18n( val ); }, '' ).trim();

			} else {

				opt.text = self.i18n( p );

			}

			opt.value = obj[ p ];

			if ( opt.value == trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		}

	}

	this.addListener( select, 'change', function onChange ( event ) { Page.inHandler = true; trgObj[ property ] = event.target.value; Page.inHandler = false; } );

	label.textContent = this.i18n( title );

	Page.controls[ property ] = select;

	div.appendChild( label );
	div.appendChild( select );

	if ( replace === undefined ) {

		this.page.appendChild( div );

	} else {

		this.page.replaceChild( div, replace );

	}

	return div;

};

Page.prototype.addCheckbox = function ( title, obj, property ) {

	const label = document.createElement( 'label' );
	const cb    = document.createElement( 'input' );
	const div   = document.createElement( 'div' );

	const id = 'cv-' + Page.seq++;

	div.classList.add( 'cv-checkbox' );

	cb.type = 'checkbox';
	cb.checked = obj[ property ];
	cb.id = id;

	label.textContent = this.i18n( title );
	label.htmlFor = id;

	this.addListener( cb, 'change', _checkboxChanged );

	Page.controls[ property ] = cb;

	div.appendChild( cb );
	div.appendChild( label );

	this.page.appendChild( div );

	return div;

	function _checkboxChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.checked;

		Page.inHandler = false;

	}

};

Page.prototype.addRange = function ( title, obj, property ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );
	const range = document.createElement( 'input' );

	div.classList.add( 'control' );

	range.type = 'range';

	range.min = 0;
	range.max = 1;

	range.step = 0.05;
	range.value = obj[ property ];

	this.addListener( range, 'input', _rangeChanged );
	this.addListener( range, 'change', _rangeChanged ); // for IE11 support

	label.textContent = this.i18n( title );

	Page.controls[ property ] = range;

	div.appendChild( label );
	div.appendChild( range );

	this.page.appendChild( div );

	return div;

	function _rangeChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.value;

		Page.inHandler = false;

	}

};

Page.prototype.addSlide = function ( domElement, depth ) {

	const slide = document.createElement( 'div' );

	slide.classList.add( 'slide' );
	slide.style.zIndex = 200 - depth;

	slide.appendChild( domElement );

	this.page.appendChild( slide );

	this.slide = slide;
	this.slideDepth = depth;

	return slide;

};

Page.prototype.replaceSlide = function ( domElement, depth ) {

	const newSlide = document.createElement( 'div' );
	const oldSlide = this.slide;
	const page = this.page;

	var redraw; // eslint-disable-line no-unused-vars

	newSlide.classList.add( 'slide' );
	newSlide.style.zIndex = 200 - depth;

	if ( depth < this.slideDepth ) {

		newSlide.classList.add( 'slide-out' );

	}

	newSlide.appendChild( domElement );

	page.appendChild( newSlide );

	if ( depth > this.slideDepth ) {

		oldSlide.addEventListener( 'transitionend', afterSlideOut );
		oldSlide.classList.add( 'slide-out' );

		redraw = oldSlide.clientHeight;

	} else if ( depth < this.slideDepth ) {

		newSlide.addEventListener( 'transitionend', afterSlideIn );

		redraw = newSlide.clientHeight;

		newSlide.classList.remove( 'slide-out' );

	} else {

		page.removeChild( oldSlide );

	}

	this.slide = newSlide;
	this.slideDepth = depth;

	return newSlide;

	function afterSlideOut () {

		oldSlide.removeEventListener( 'transitionend', afterSlideOut );
		page.removeChild( oldSlide );

	}

	function afterSlideIn () {

		page.removeChild( oldSlide );
		newSlide.removeEventListener( 'transitionend', afterSlideIn );

	}

};

Page.prototype.addButton = function ( title, func ) {

	const button = document.createElement( 'button' );

	button.type = 'button';
	button.textContent = this.i18n( title );

	this.addListener( button, 'click', func );

	this.page.appendChild( button );

	return button;

};

Page.prototype.addTextBox = function ( labelText, placeholder, getResultGetter ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );

	label.textContent = this.i18n( labelText );

	const input = document.createElement( 'input' );

	var value;

	input.type = 'text';
	input.placeholder = placeholder;

	div.appendChild( label );
	div.appendChild( input );

	this.page.appendChild( div );

	this.addListener( input, 'change', function ( e ) { value = e.target.value; return true; } ) ;

	getResultGetter( _result );

	return div;

	function _result() {

		input.value = '';
		return value;

	}

};

Page.prototype.addDownloadButton = function ( title, urlProvider, fileName ) {

	const a = document.createElement( 'a' );

	if ( typeof a.download === 'undefined' ) return null;

	this.addListener( a, 'click', _setHref );

	a.textContent = this.i18n( title );
	a.type = 'download';
	a.download = fileName;
	a.href = 'javascript:void();';

	a.classList.add( 'download' );

	this.page.appendChild( a );

	return a;

	function _setHref() {

		a.href = urlProvider();

	}

};

export { Page };

// EOF<commit_msg>remove DOM leaks<commit_after>
import { Cfg } from '../core/lib';

function Page ( id, x18nPrefix, onTop, onLeave ) {

	const tab  = document.createElement( 'div' );
	const page = document.createElement( 'div' );

	var frame = Page.frame;
	var tabBox = Page.tabBox;

	page.classList.add( 'page' );

	tab.id = id;
	tab.classList.add( 'tab' );

	Page.addListener( tab, 'click', this.tabHandleClick );

	if ( onTop !== undefined ) {

		// callback when this page is made visible
		Page.addListener( tab, 'click', onTop );

	}

	if ( frame === null ) {

		// create UI side panel and reveal tabs
		frame = document.createElement( 'div' );

		frame.id = 'cv-frame';

		Page.frame = frame;

	}

	if ( tabBox === null ) {

		// create UI box to contain tabs - reorients for small screen widths
		tabBox = document.createElement( 'div' );

		tabBox.id = 'cv-tab-box';

		const close = document.createElement( 'div' );

		close.id = 'close';
		close.classList.add( 'tab' );

		Page.addListener( close, 'click', _closeFrame );

		tabBox.appendChild( close );

		Page.tabBox = tabBox;

	}

	tabBox.appendChild( tab );
	frame.appendChild( page );

	Page.pages.push( { tab: tab, page: page, owner: this } );

	this.page = page;
	this.onLeave = onLeave;
	this.slide = undefined;
	this.x18nPrefix = x18nPrefix + '.';
	this.onChange = null;

	function _closeFrame ( /* event */ ) {

		Page.tabBox.classList.remove( 'onscreen' );
		Page.frame.classList.remove( 'onscreen' );

	}

}

Page.pages     = [];
Page.listeners = [];
Page.inHandler = false;
Page.controls  = [];
Page.frame = null;
Page.tabBox = null;
Page.seq = 0;

Page.setParent = function ( parent ) {

	parent.appendChild( Page.tabBox );
	parent.appendChild( Page.frame );

};

Page.setControlsVisibility = function ( list, visible ) {

	const display = visible ? 'block' : 'none';

	list.forEach( function ( element ) {

		if ( element === null ) return;
		element.style.display = display;

	} );

};

Page.clear = function () {

	const frame  = Page.frame;
	const tabBox = Page.tabBox;

	if ( frame  !== null ) frame.parentElement.removeChild( frame );
	if ( tabBox !== null ) tabBox.parentElement.removeChild( tabBox );

	Page.listeners.forEach( function ( listener ) {

		listener.obj.removeEventListener( listener.name, listener.handler );

	} );

	Page.listeners = [];
	Page.pages     = [];
	Page.inHandler = false;
	Page.controls  = [];
	Page.frame     = null;
	Page.tabBox    = null;

};

Page.addFullscreenButton = function ( id, obj, property ) {

	const tabBox = this.tabBox;
	const fullscreen = document.createElement( 'div' );

	fullscreen.id = id;
	fullscreen.classList.add( 'tab' );

	Page.addListener( fullscreen, 'click', _toggleButton );

	Page.addListener( obj, 'change', _setButton );

	tabBox.appendChild( fullscreen );

	_setButton();

	return fullscreen;

	function _toggleButton () {

		obj[ property ] = ! obj[ property ];

		_setButton();

	}

	function _setButton () {

		if ( obj[ property ] ) {

			fullscreen.classList.remove( 'expand' );
			fullscreen.classList.add( 'collapse' );

		} else {

			fullscreen.classList.add( 'expand' );
			fullscreen.classList.remove( 'collapse' );

		}

	}

};

Page.addListener = function ( obj, name, handler ) {

	obj.addEventListener( name, handler, false );

	Page.listeners.push( {
		obj: obj,
		name: name,
		handler: handler
	} );

};

Page.handleChange = function ( event ) {

	const obj = event.target;
	const property = event.name;

	if ( ! Page.inHandle ) {

		if ( Page.controls[ property ] ) {

			const ctrl = Page.controls[ property ];

			switch ( ctrl.type ) {

			case 'checkbox':

				ctrl.checked = obj[ property ];

				break;

			case 'select-one':
			case 'range':

				ctrl.value = obj[ property ];

				break;

			case 'download':

				ctrl.href = obj[ property ];

				break;

			}

		}

	}

	Page.pages.forEach( function ( p ) {

		const page = p.owner;

		if ( page.onChange !== null ) page.onChange( event );

	} );

};

Page.prototype.constructor = Page;

Page.prototype.i18n = function ( text ) {

	const tr = Cfg.i18n( this.x18nPrefix + text );

	return ( tr === undefined ) ? text : tr;

};

Page.prototype.addListener = function ( obj, name, handler ) {

	Page.addListener( obj, name, handler ); // redirect to :: method - allows later rework to page specific destruction

};

Page.prototype.tabHandleClick = function ( event ) {

	const tab = event.target;
	const pages = Page.pages;

	tab.classList.add( 'toptab' );
	Page.tabBox.classList.add( 'onscreen' );
	Page.frame.classList.add( 'onscreen' );

	pages.forEach( function ( page ) {

		const otherPage = page.page;
		const otherTab = page.tab;
		const owner = page.owner;

		if ( otherTab === tab ) {

			otherPage.style.display = 'block';

		} else {

			otherPage.style.display = 'none';

			if ( otherTab.classList.contains( 'toptab' ) ) {

				otherTab.classList.remove( 'toptab' );

				if ( owner.onLeave !== undefined ) owner.onLeave();

			}

		}

	} );

};

Page.prototype.appendChild = function ( domElement ) {

	this.page.appendChild( domElement );

};

Page.prototype.addHeader = function ( text ) {

	const div = document.createElement( 'div' );

	div.classList.add( 'header' );
	div.textContent = this.i18n( text );

	this.page.appendChild( div );

	return div;

};

Page.prototype.addText = function ( text ) {

	const p = this.addLine( text );

	p.classList.add( 'spaced' );

	return p;

};

Page.prototype.addLine = function ( text ) {

	const p = document.createElement( 'p' );

	p.textContent = text;

	this.page.appendChild( p );

	return p;

};

Page.prototype.addLink = function ( url, text ) {

	const a = document.createElement( 'a' );

	a.href = url;
	a.textContent = text;
	a.target = '_blank';

	this.page.appendChild( a );

	return a;

};

Page.prototype.addSelect = function ( title, obj, trgObj, property, replace ) {

	const div    = document.createElement( 'div' );
	const label  = document.createElement( 'label' );
	const select = document.createElement( 'select' );

	div.classList.add( 'control' );

	if ( obj instanceof Array ) {

		obj.forEach( function ( element ) {

			const opt = document.createElement( 'option' );

			opt.value = element;
			opt.text = element;

			if ( opt.text === trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		} );

	} else {

		for ( var p in obj ) {

			const opt = document.createElement( 'option' );
			const self = this;

			const s = p.split( ' ' );

			if ( s.length > 1 ) {

				opt.text = s.reduce( function ( res, val) { return res + ' ' + self.i18n( val ); }, '' ).trim();

			} else {

				opt.text = self.i18n( p );

			}

			opt.value = obj[ p ];

			if ( opt.value == trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		}

	}

	this.addListener( select, 'change', function onChange ( event ) { Page.inHandler = true; trgObj[ property ] = event.target.value; Page.inHandler = false; } );

	label.textContent = this.i18n( title );

	Page.controls[ property ] = select;

	div.appendChild( label );
	div.appendChild( select );

	if ( replace === undefined ) {

		this.page.appendChild( div );

	} else {

		this.page.replaceChild( div, replace );

	}

	return div;

};

Page.prototype.addCheckbox = function ( title, obj, property ) {

	const label = document.createElement( 'label' );
	const cb    = document.createElement( 'input' );
	const div   = document.createElement( 'div' );

	const id = 'cv-' + Page.seq++;

	div.classList.add( 'cv-checkbox' );

	cb.type = 'checkbox';
	cb.checked = obj[ property ];
	cb.id = id;

	label.textContent = this.i18n( title );
	label.htmlFor = id;

	this.addListener( cb, 'change', _checkboxChanged );

	Page.controls[ property ] = cb;

	div.appendChild( cb );
	div.appendChild( label );

	this.page.appendChild( div );

	return div;

	function _checkboxChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.checked;

		Page.inHandler = false;

	}

};

Page.prototype.addRange = function ( title, obj, property ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );
	const range = document.createElement( 'input' );

	div.classList.add( 'control' );

	range.type = 'range';

	range.min = 0;
	range.max = 1;

	range.step = 0.05;
	range.value = obj[ property ];

	this.addListener( range, 'input', _rangeChanged );
	this.addListener( range, 'change', _rangeChanged ); // for IE11 support

	label.textContent = this.i18n( title );

	Page.controls[ property ] = range;

	div.appendChild( label );
	div.appendChild( range );

	this.page.appendChild( div );

	return div;

	function _rangeChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.value;

		Page.inHandler = false;

	}

};

Page.prototype.addSlide = function ( domElement, depth ) {

	const slide = document.createElement( 'div' );

	slide.classList.add( 'slide' );
	slide.style.zIndex = 200 - depth;

	slide.appendChild( domElement );

	this.page.appendChild( slide );

	this.slide = slide;
	this.slideDepth = depth;

	return slide;

};

Page.prototype.replaceSlide = function ( domElement, depth ) {

	const newSlide = document.createElement( 'div' );
	const page = this.page;

	var oldSlide = this.slide;

	var redraw; // eslint-disable-line no-unused-vars

	newSlide.classList.add( 'slide' );
	newSlide.style.zIndex = 200 - depth;

	if ( depth < this.slideDepth ) {

		newSlide.classList.add( 'slide-out' );

	}

	newSlide.appendChild( domElement );

	page.appendChild( newSlide );

	if ( depth > this.slideDepth ) {

		oldSlide.addEventListener( 'transitionend', afterSlideOut );
		oldSlide.classList.add( 'slide-out' );

		redraw = oldSlide.clientHeight;

	} else if ( depth < this.slideDepth ) {

		newSlide.addEventListener( 'transitionend', afterSlideIn );

		redraw = newSlide.clientHeight;

		newSlide.classList.remove( 'slide-out' );

	} else {

		page.removeChild( oldSlide );

	}

	this.slide = newSlide;
	this.slideDepth = depth;

	return newSlide;

	function afterSlideOut () {

		oldSlide.removeEventListener( 'transitionend', afterSlideOut );
		page.removeChild( oldSlide );

		oldSlide = null;

	}

	function afterSlideIn () {

		page.removeChild( oldSlide );
		newSlide.removeEventListener( 'transitionend', afterSlideIn );

		oldSlide = null;

	}

};

Page.prototype.addButton = function ( title, func ) {

	const button = document.createElement( 'button' );

	button.type = 'button';
	button.textContent = this.i18n( title );

	this.addListener( button, 'click', func );

	this.page.appendChild( button );

	return button;

};

Page.prototype.addTextBox = function ( labelText, placeholder, getResultGetter ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );

	label.textContent = this.i18n( labelText );

	const input = document.createElement( 'input' );

	var value;

	input.type = 'text';
	input.placeholder = placeholder;

	div.appendChild( label );
	div.appendChild( input );

	this.page.appendChild( div );

	this.addListener( input, 'change', function ( e ) { value = e.target.value; return true; } ) ;

	getResultGetter( _result );

	return div;

	function _result() {

		input.value = '';
		return value;

	}

};

Page.prototype.addDownloadButton = function ( title, urlProvider, fileName ) {

	const a = document.createElement( 'a' );

	if ( typeof a.download === 'undefined' ) return null;

	this.addListener( a, 'click', _setHref );

	a.textContent = this.i18n( title );
	a.type = 'download';
	a.download = fileName;
	a.href = 'javascript:void();';

	a.classList.add( 'download' );

	this.page.appendChild( a );

	return a;

	function _setHref() {

		a.href = urlProvider();

	}

};

export { Page };

// EOF<|endoftext|>


<commit_before>
import { Cfg } from '../core/lib';

function Page ( id, x18nPrefix, onTop, onLeave ) {

	const tab  = document.createElement( 'div' );
	const page = document.createElement( 'div' );

	var frame = Page.frame;
	var tabBox = Page.tabBox;

	page.classList.add( 'page' );

	tab.id = id;
	tab.classList.add( 'tab' );

	Page.addListener( tab, 'click', this.tabHandleClick );

	if ( onTop !== undefined ) {

		// callback when this page is made visible
		Page.addListener( tab, 'click', onTop );

	}

	if ( frame === null ) {

		// create UI side panel and reveal tabs
		frame = document.createElement( 'div' );

		frame.id = 'cv-frame';

		Page.frame = frame;

	}

	if ( tabBox === null ) {

		// create UI box to contain tabs - reorients for small screen widths
		tabBox = document.createElement( 'div' );

		tabBox.id = 'cv-tab-box';

		const close = document.createElement( 'div' );

		close.id = 'close';
		close.classList.add( 'tab' );

		Page.addListener( close, 'click', _closeFrame );

		tabBox.appendChild( close );

		Page.tabBox = tabBox;

	}

	tabBox.appendChild( tab );
	frame.appendChild( page );

	Page.pages.push( { tab: tab, page: page, owner: this } );

	this.page = page;
	this.onLeave = onLeave;
	this.slide = undefined;
	this.x18nPrefix = x18nPrefix + '.';
	this.onChange = null;

	function _closeFrame ( /* event */ ) {

		Page.tabBox.classList.remove( 'onscreen' );
		Page.frame.classList.remove( 'onscreen' );

	}

}

Page.pages     = [];
Page.listeners = [];
Page.inHandler = false;
Page.controls  = [];
Page.frame = null;
Page.tabBox = null;
Page.seq = 0;

Page.setParent = function ( parent ) {

	parent.appendChild( Page.tabBox );
	parent.appendChild( Page.frame );

};

Page.setControlsVisibility = function ( list, visible ) {

	const display = visible ? 'block' : 'none';

	list.forEach( function ( element ) {

		if ( element === null ) return;
		element.style.display = display;

	} );

};

Page.clear = function () {

	const frame  = Page.frame;
	const tabBox = Page.tabBox;

	if ( frame  !== null ) frame.parentElement.removeChild( frame );
	if ( tabBox !== null ) tabBox.parentElement.removeChild( tabBox );

	Page.listeners.forEach( function ( listener ) {

		listener.obj.removeEventListener( listener.name, listener.handler );

	} );

	Page.listeners = [];
	Page.pages     = [];
	Page.inHandler = false;
	Page.controls  = [];
	Page.frame     = null;
	Page.tabBox    = null;

};

Page.addFullscreenButton = function ( id, obj, property ) {

	const tabBox = this.tabBox;
	const fullscreen = document.createElement( 'div' );

	fullscreen.id = id;
	fullscreen.classList.add( 'tab' );

	Page.addListener( fullscreen, 'click', _toggleButton );

	Page.addListener( obj, 'change', _setButton );

	tabBox.appendChild( fullscreen );

	_setButton();

	return fullscreen;

	function _toggleButton () {

		obj[ property ] = ! obj[ property ];

		_setButton();

	}

	function _setButton () {

		if ( obj[ property ] ) {

			fullscreen.classList.remove( 'expand' );
			fullscreen.classList.add( 'collapse' );

		} else {

			fullscreen.classList.add( 'expand' );
			fullscreen.classList.remove( 'collapse' );

		}

	}

};

Page.addListener = function ( obj, name, handler ) {

	obj.addEventListener( name, handler, false );

	Page.listeners.push( {
		obj: obj,
		name: name,
		handler: handler
	} );

};

Page.handleChange = function ( event ) {

	const obj = event.target;
	const property = event.name;

	if ( ! Page.inHandle ) {

		if ( Page.controls[ property ] ) {

			const ctrl = Page.controls[ property ];

			switch ( ctrl.type ) {

			case 'checkbox':

				ctrl.checked = obj[ property ];

				break;

			case 'select-one':
			case 'range':

				ctrl.value = obj[ property ];

				break;

			case 'download':

				ctrl.href = obj[ property ];

				break;

			}

		}

	}

	Page.pages.forEach( function ( p ) {

		const page = p.owner;

		if ( page.onChange !== null ) page.onChange( event );

	} );

};

Page.prototype.constructor = Page;

Page.prototype.i18n = function ( text ) {

	const tr = Cfg.i18n( this.x18nPrefix + text );

	return ( tr === undefined ) ? text : tr;

};

Page.prototype.addListener = function ( obj, name, handler ) {

	Page.addListener( obj, name, handler ); // redirect to :: method - allows later rework to page specific destruction

};

Page.prototype.tabHandleClick = function ( event ) {

	const tab = event.target;
	const pages = Page.pages;

	tab.classList.add( 'toptab' );
	Page.tabBox.classList.add( 'onscreen' );
	Page.frame.classList.add( 'onscreen' );

	pages.forEach( function ( page ) {

		const otherPage = page.page;
		const otherTab = page.tab;
		const owner = page.owner;

		if ( otherTab === tab ) {

			otherPage.style.display = 'block';

		} else {

			otherPage.style.display = 'none';

			if ( otherTab.classList.contains( 'toptab' ) ) {

				otherTab.classList.remove( 'toptab' );

				if ( owner.onLeave !== undefined ) owner.onLeave();

			}

		}

	} );

};

Page.prototype.appendChild = function ( domElement ) {

	this.page.appendChild( domElement );

	return domElement;

};

Page.prototype.addHeader = function ( text ) {

	const div = document.createElement( 'div' );

	div.classList.add( 'header' );
	div.textContent = this.i18n( text );

	this.page.appendChild( div );

	return div;

};

Page.prototype.addText = function ( text ) {

	const p = this.addLine( text );

	p.classList.add( 'spaced' );

	return p;

};

Page.prototype.addLine = function ( text ) {

	const p = document.createElement( 'p' );

	p.textContent = text;

	this.page.appendChild( p );

	return p;

};

Page.prototype.addLink = function ( url, text ) {

	const a = document.createElement( 'a' );

	a.href = url;
	a.textContent = text;
	a.target = '_blank';

	this.page.appendChild( a );

	return a;

};

Page.prototype.addSelect = function ( title, obj, trgObj, property, replace ) {

	const div    = document.createElement( 'div' );
	const label  = document.createElement( 'label' );
	const select = document.createElement( 'select' );

	div.classList.add( 'control' );

	if ( obj instanceof Array ) {

		obj.forEach( function ( element ) {

			const opt = document.createElement( 'option' );

			opt.value = element;
			opt.text = element;

			if ( opt.text === trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		} );

	} else {

		for ( var p in obj ) {

			const opt = document.createElement( 'option' );
			const self = this;

			const s = p.split( ' ' );

			if ( s.length > 1 ) {

				opt.text = s.reduce( function ( res, val) { return res + ' ' + self.i18n( val ); }, '' ).trim();

			} else {

				opt.text = self.i18n( p );

			}

			opt.value = obj[ p ];

			if ( opt.value == trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		}

	}

	this.addListener( select, 'change', function onChange ( event ) { Page.inHandler = true; trgObj[ property ] = event.target.value; Page.inHandler = false; } );

	label.textContent = this.i18n( title );
	label.classList.add( 'cv-select' );

	Page.controls[ property ] = select;

	div.appendChild( label );
	div.appendChild( select );

	if ( replace === undefined ) {

		this.page.appendChild( div );

	} else {

		this.page.replaceChild( div, replace );

	}

	return div;

};

Page.prototype.addFileSelect = function ( title, obj, trgObj, property ) {

	const div = this.addSelect( title, obj, trgObj, property );

	const label = div.firstChild;

	label.for = 'cv-file';
	label.id = 'cv-file-label';

	const input = document.createElement( 'input' );

	input.id = 'cv-file';
	input.type = 'file';
	input.accept = '.svx,.lox,.plt';

	this.addListener( input, 'change', function _handleFileChange () {

		const count = input.files.length;
		const files = [];

		if ( count > 0 ) {

			for( var i = 0; i < count; i++ ) {

				files.push( input.files[ i ] );

			}

			trgObj[ property ] = files;

		}

	} );

	label.appendChild( input );

	return div;

};

Page.prototype.addCheckbox = function ( title, obj, property ) {

	const label = document.createElement( 'label' );
	const cb    = document.createElement( 'input' );
	const div   = document.createElement( 'div' );

	const id = 'cv-' + Page.seq++;

	div.classList.add( 'control' );

	cb.type = 'checkbox';
	cb.checked = obj[ property ];
	cb.id = id;

	label.textContent = this.i18n( title );
	label.htmlFor = id;
	label.classList.add( 'check' );

	this.addListener( cb, 'change', _checkboxChanged );

	Page.controls[ property ] = cb;

	div.appendChild( cb );
	div.appendChild( label );

	this.page.appendChild( div );

	return div;

	function _checkboxChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.checked;

		Page.inHandler = false;

	}

};

Page.prototype.addRange = function ( title, obj, property ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );
	const range = document.createElement( 'input' );

	div.classList.add( 'control' );

	range.type = 'range';

	range.min = 0;
	range.max = 1;

	range.step = 0.05;
	range.value = obj[ property ];

	this.addListener( range, 'input', _rangeChanged );
	this.addListener( range, 'change', _rangeChanged ); // for IE11 support

	label.textContent = this.i18n( title );
	label.classList.add( 'cv-range' );

	Page.controls[ property ] = range;

	div.appendChild( label );
	div.appendChild( range );

	this.page.appendChild( div );

	return div;

	function _rangeChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.value;

		Page.inHandler = false;

	}

};

Page.prototype.addSlide = function ( domElement, depth ) {

	const slide = document.createElement( 'div' );

	slide.classList.add( 'slide' );
	slide.style.zIndex = 200 - depth;

	slide.appendChild( domElement );

	this.page.appendChild( slide );

	this.slide = slide;
	this.slideDepth = depth;

	return slide;

};

Page.prototype.replaceSlide = function ( domElement, depth ) {

	const newSlide = document.createElement( 'div' );
	const page = this.page;

	var oldSlide = this.slide;

	var redraw; // eslint-disable-line no-unused-vars

	newSlide.classList.add( 'slide' );
	newSlide.style.zIndex = 200 - depth;

	if ( depth < this.slideDepth ) {

		newSlide.classList.add( 'slide-out' );

	}

	newSlide.appendChild( domElement );

	page.appendChild( newSlide );

	if ( depth > this.slideDepth ) {

		oldSlide.addEventListener( 'transitionend', afterSlideOut );
		oldSlide.classList.add( 'slide-out' );

		redraw = oldSlide.clientHeight;

	} else if ( depth < this.slideDepth ) {

		newSlide.addEventListener( 'transitionend', afterSlideIn );

		redraw = newSlide.clientHeight;

		newSlide.classList.remove( 'slide-out' );

	} else {

		page.removeChild( oldSlide );

	}

	this.slide = newSlide;
	this.slideDepth = depth;

	return newSlide;

	function afterSlideOut () {

		oldSlide.removeEventListener( 'transitionend', afterSlideOut );
		page.removeChild( oldSlide );

		oldSlide = null;

	}

	function afterSlideIn () {

		page.removeChild( oldSlide );
		newSlide.removeEventListener( 'transitionend', afterSlideIn );

		oldSlide = null;

	}

};

Page.prototype.addButton = function ( title, func ) {

	const button = document.createElement( 'button' );

	button.type = 'button';
	button.textContent = this.i18n( title );

	this.addListener( button, 'click', func );

	this.page.appendChild( button );

	return button;

};

Page.prototype.addTextBox = function ( labelText, placeholder, getResultGetter ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );

	label.textContent = this.i18n( labelText );

	const input = document.createElement( 'input' );

	var value;

	input.type = 'text';
	input.placeholder = placeholder;

	div.appendChild( label );
	div.appendChild( input );

	this.page.appendChild( div );

	this.addListener( input, 'change', function ( e ) { value = e.target.value; return true; } ) ;

	getResultGetter( _result );

	return div;

	function _result() {

		input.value = '';
		return value;

	}

};

Page.prototype.addDownloadButton = function ( title, urlProvider, fileName ) {

	const a = document.createElement( 'a' );

	if ( typeof a.download === 'undefined' ) return null;

	this.addListener( a, 'click', _setHref );

	a.textContent = this.i18n( title );
	a.type = 'download';
	a.download = fileName;
	a.href = 'javascript:void();';

	a.classList.add( 'download' );

	this.page.appendChild( a );

	return a;

	function _setHref() {

		a.href = urlProvider();

	}

};

Page.prototype.addLogo = function () {

	const img = document.createElement( 'div' );

	img.id = 'logo';
	img.title = 'logo';

	this.appendChild( img );

};

export { Page };

// EOF<commit_msg>cleanup<commit_after>
import { Cfg } from '../core/lib';

function Page ( id, x18nPrefix, onTop, onLeave ) {

	const tab  = document.createElement( 'div' );
	const page = document.createElement( 'div' );

	var frame = Page.frame;
	var tabBox = Page.tabBox;

	page.classList.add( 'page' );

	tab.id = id;
	tab.classList.add( 'tab' );

	Page.addListener( tab, 'click', this.tabHandleClick );

	if ( onTop !== undefined ) {

		// callback when this page is made visible
		Page.addListener( tab, 'click', onTop );

	}

	if ( frame === null ) {

		// create UI side panel and reveal tabs
		frame = document.createElement( 'div' );

		frame.id = 'cv-frame';

		Page.frame = frame;

	}

	if ( tabBox === null ) {

		// create UI box to contain tabs - reorients for small screen widths
		tabBox = document.createElement( 'div' );

		tabBox.id = 'cv-tab-box';

		const close = document.createElement( 'div' );

		close.id = 'close';
		close.classList.add( 'tab' );

		Page.addListener( close, 'click', _closeFrame );

		tabBox.appendChild( close );

		Page.tabBox = tabBox;

	}

	tabBox.appendChild( tab );
	frame.appendChild( page );

	Page.pages.push( { tab: tab, page: page, owner: this } );

	this.page = page;
	this.onLeave = onLeave;
	this.slide = undefined;
	this.x18nPrefix = x18nPrefix + '.';
	this.onChange = null;

	function _closeFrame ( /* event */ ) {

		Page.tabBox.classList.remove( 'onscreen' );
		Page.frame.classList.remove( 'onscreen' );

	}

}

Page.pages     = [];
Page.listeners = [];
Page.inHandler = false;
Page.controls  = [];
Page.frame = null;
Page.tabBox = null;
Page.seq = 0;

Page.setParent = function ( parent ) {

	parent.appendChild( Page.tabBox );
	parent.appendChild( Page.frame );

};

Page.setControlsVisibility = function ( list, visible ) {

	const display = visible ? 'block' : 'none';

	list.forEach( function ( element ) {

		if ( element === null ) return;
		element.style.display = display;

	} );

};

Page.clear = function () {

	const frame  = Page.frame;
	const tabBox = Page.tabBox;

	if ( frame  !== null ) frame.parentElement.removeChild( frame );
	if ( tabBox !== null ) tabBox.parentElement.removeChild( tabBox );

	Page.listeners.forEach( function ( listener ) {

		listener.obj.removeEventListener( listener.name, listener.handler );

	} );

	Page.listeners = [];
	Page.pages     = [];
	Page.inHandler = false;
	Page.controls  = [];
	Page.frame     = null;
	Page.tabBox    = null;

};

Page.addFullscreenButton = function ( id, obj, property ) {

	const tabBox = this.tabBox;
	const fullscreen = document.createElement( 'div' );

	fullscreen.id = id;
	fullscreen.classList.add( 'tab' );

	Page.addListener( fullscreen, 'click', _toggleButton );

	Page.addListener( obj, 'change', _setButton );

	tabBox.appendChild( fullscreen );

	_setButton();

	return fullscreen;

	function _toggleButton () {

		obj[ property ] = ! obj[ property ];

		_setButton();

	}

	function _setButton () {

		if ( obj[ property ] ) {

			fullscreen.classList.remove( 'expand' );
			fullscreen.classList.add( 'collapse' );

		} else {

			fullscreen.classList.add( 'expand' );
			fullscreen.classList.remove( 'collapse' );

		}

	}

};

Page.addListener = function ( obj, name, handler ) {

	obj.addEventListener( name, handler, false );

	Page.listeners.push( {
		obj: obj,
		name: name,
		handler: handler
	} );

};

Page.handleChange = function ( event ) {

	const obj = event.target;
	const property = event.name;

	if ( ! Page.inHandle ) {

		if ( Page.controls[ property ] ) {

			const ctrl = Page.controls[ property ];

			switch ( ctrl.type ) {

			case 'checkbox':

				ctrl.checked = obj[ property ];

				break;

			case 'select-one':
			case 'range':

				ctrl.value = obj[ property ];

				break;

			case 'download':

				ctrl.href = obj[ property ];

				break;

			}

		}

	}

	Page.pages.forEach( function ( p ) {

		const page = p.owner;

		if ( page.onChange !== null ) page.onChange( event );

	} );

};

Page.prototype.constructor = Page;

Page.prototype.i18n = function ( text ) {

	const tr = Cfg.i18n( this.x18nPrefix + text );

	return ( tr === undefined ) ? text : tr;

};

Page.prototype.addListener = function ( obj, name, handler ) {

	Page.addListener( obj, name, handler ); // redirect to :: method - allows later rework to page specific destruction

};

Page.prototype.tabHandleClick = function ( event ) {

	const tab = event.target;
	const pages = Page.pages;

	tab.classList.add( 'toptab' );
	Page.tabBox.classList.add( 'onscreen' );
	Page.frame.classList.add( 'onscreen' );

	pages.forEach( function ( page ) {

		const otherPage = page.page;
		const otherTab = page.tab;
		const owner = page.owner;

		if ( otherTab === tab ) {

			otherPage.style.display = 'block';

		} else {

			otherPage.style.display = 'none';

			if ( otherTab.classList.contains( 'toptab' ) ) {

				otherTab.classList.remove( 'toptab' );

				if ( owner.onLeave !== undefined ) owner.onLeave();

			}

		}

	} );

};

Page.prototype.appendChild = function ( domElement ) {

	this.page.appendChild( domElement );

	return domElement;

};

Page.prototype.addHeader = function ( text ) {

	const div = document.createElement( 'div' );

	div.classList.add( 'header' );
	div.textContent = this.i18n( text );

	this.page.appendChild( div );

	return div;

};

Page.prototype.addText = function ( text ) {

	const p = this.addLine( text );

	p.classList.add( 'spaced' );

	return p;

};

Page.prototype.addLine = function ( text ) {

	const p = document.createElement( 'p' );

	p.textContent = text;

	this.page.appendChild( p );

	return p;

};

Page.prototype.addLink = function ( url, text ) {

	const a = document.createElement( 'a' );

	a.href = url;
	a.textContent = text;
	a.target = '_blank';

	this.page.appendChild( a );

	return a;

};

Page.prototype.addSelect = function ( title, obj, trgObj, property, replace ) {

	const div    = document.createElement( 'div' );
	const label  = document.createElement( 'label' );
	const select = document.createElement( 'select' );

	div.classList.add( 'control' );

	if ( obj instanceof Array ) {

		obj.forEach( function ( element ) {

			const opt = document.createElement( 'option' );

			opt.value = element;
			opt.text = element;

			if ( opt.text === trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		} );

	} else {

		for ( var p in obj ) {

			const opt = document.createElement( 'option' );
			const self = this;

			const s = p.split( ' ' );

			if ( s.length > 1 ) {

				opt.text = s.reduce( function ( res, val) { return res + ' ' + self.i18n( val ); }, '' ).trim();

			} else {

				opt.text = self.i18n( p );

			}

			opt.value = obj[ p ];

			if ( opt.value == trgObj[ property ] ) opt.selected = true;

			select.add( opt, null );

		}

	}

	this.addListener( select, 'change', function onChange ( event ) { Page.inHandler = true; trgObj[ property ] = event.target.value; Page.inHandler = false; } );

	label.textContent = this.i18n( title );
	label.classList.add( 'cv-select' );

	Page.controls[ property ] = select;

	div.appendChild( label );
	div.appendChild( select );

	if ( replace === undefined ) {

		this.page.appendChild( div );

	} else {

		this.page.replaceChild( div, replace );

	}

	return div;

};

Page.prototype.addFileSelect = function ( title, obj, trgObj, property ) {

	const div = this.addSelect( title, obj, trgObj, property );

	const label = div.firstChild;

	label.for = 'cv-file';
	label.id = 'cv-file-label';

	const input = document.createElement( 'input' );

	input.id = 'cv-file';
	input.type = 'file';
	input.accept = '.svx,.lox,.plt';

	this.addListener( input, 'change', function _handleFileChange () {

		const count = input.files.length;
		const files = [];

		if ( count > 0 ) {

			for ( var i = 0; i < count; i++ ) files.push( input.files[ i ] );

			trgObj[ property ] = files;

		}

	} );

	label.appendChild( input );

	return div;

};

Page.prototype.addCheckbox = function ( title, obj, property ) {

	const label = document.createElement( 'label' );
	const cb    = document.createElement( 'input' );
	const div   = document.createElement( 'div' );

	const id = 'cv-' + Page.seq++;

	div.classList.add( 'control' );

	cb.type = 'checkbox';
	cb.checked = obj[ property ];
	cb.id = id;

	label.textContent = this.i18n( title );
	label.htmlFor = id;
	label.classList.add( 'check' );

	this.addListener( cb, 'change', _checkboxChanged );

	Page.controls[ property ] = cb;

	div.appendChild( cb );
	div.appendChild( label );

	this.page.appendChild( div );

	return div;

	function _checkboxChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.checked;

		Page.inHandler = false;

	}

};

Page.prototype.addRange = function ( title, obj, property ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );
	const range = document.createElement( 'input' );

	div.classList.add( 'control' );

	range.type = 'range';

	range.min = 0;
	range.max = 1;

	range.step = 0.05;
	range.value = obj[ property ];

	this.addListener( range, 'input', _rangeChanged );
	this.addListener( range, 'change', _rangeChanged ); // for IE11 support

	label.textContent = this.i18n( title );
	label.classList.add( 'cv-range' );

	Page.controls[ property ] = range;

	div.appendChild( label );
	div.appendChild( range );

	this.page.appendChild( div );

	return div;

	function _rangeChanged ( event ) {

		Page.inHandler = true;

		obj[ property ] = event.target.value;

		Page.inHandler = false;

	}

};

Page.prototype.addSlide = function ( domElement, depth ) {

	const slide = document.createElement( 'div' );

	slide.classList.add( 'slide' );
	slide.style.zIndex = 200 - depth;

	slide.appendChild( domElement );

	this.page.appendChild( slide );

	this.slide = slide;
	this.slideDepth = depth;

	return slide;

};

Page.prototype.replaceSlide = function ( domElement, depth ) {

	const newSlide = document.createElement( 'div' );
	const page = this.page;

	var oldSlide = this.slide;

	var redraw; // eslint-disable-line no-unused-vars

	newSlide.classList.add( 'slide' );
	newSlide.style.zIndex = 200 - depth;

	if ( depth < this.slideDepth ) {

		newSlide.classList.add( 'slide-out' );

	}

	newSlide.appendChild( domElement );

	page.appendChild( newSlide );

	if ( depth > this.slideDepth ) {

		oldSlide.addEventListener( 'transitionend', afterSlideOut );
		oldSlide.classList.add( 'slide-out' );

		redraw = oldSlide.clientHeight;

	} else if ( depth < this.slideDepth ) {

		newSlide.addEventListener( 'transitionend', afterSlideIn );

		redraw = newSlide.clientHeight;

		newSlide.classList.remove( 'slide-out' );

	} else {

		page.removeChild( oldSlide );

	}

	this.slide = newSlide;
	this.slideDepth = depth;

	return newSlide;

	function afterSlideOut () {

		oldSlide.removeEventListener( 'transitionend', afterSlideOut );
		page.removeChild( oldSlide );

		oldSlide = null;

	}

	function afterSlideIn () {

		page.removeChild( oldSlide );
		newSlide.removeEventListener( 'transitionend', afterSlideIn );

		oldSlide = null;

	}

};

Page.prototype.addButton = function ( title, func ) {

	const button = document.createElement( 'button' );

	button.type = 'button';
	button.textContent = this.i18n( title );

	this.addListener( button, 'click', func );

	this.page.appendChild( button );

	return button;

};

Page.prototype.addTextBox = function ( labelText, placeholder, getResultGetter ) {

	const div = document.createElement( 'div' );
	const label = document.createElement( 'label' );

	label.textContent = this.i18n( labelText );

	const input = document.createElement( 'input' );

	var value;

	input.type = 'text';
	input.placeholder = placeholder;

	div.appendChild( label );
	div.appendChild( input );

	this.page.appendChild( div );

	this.addListener( input, 'change', function ( e ) { value = e.target.value; return true; } ) ;

	getResultGetter( _result );

	return div;

	function _result() {

		input.value = '';
		return value;

	}

};

Page.prototype.addDownloadButton = function ( title, urlProvider, fileName ) {

	const a = document.createElement( 'a' );

	if ( typeof a.download === 'undefined' ) return null;

	this.addListener( a, 'click', _setHref );

	a.textContent = this.i18n( title );
	a.type = 'download';
	a.download = fileName;
	a.href = 'javascript:void();';

	a.classList.add( 'download' );

	this.page.appendChild( a );

	return a;

	function _setHref() {

		a.href = urlProvider();

	}

};

Page.prototype.addLogo = function () {

	const img = document.createElement( 'div' );

	img.id = 'logo';
	img.title = 'logo';

	this.appendChild( img );

};

export { Page };

// EOF<|endoftext|>


<commit_before>Blend.defineClass('Blend.ui.Rect', {
    extend: 'Blend.ui.Component',
    border: false,
    borderColor: '#000',
    color: 'random',
    width: 150,
    height: 50,
    gray: false,
    alias: 'ui.rect',
    text: null,
    rcnt: 0,
    lcnt: 0,
    initElement: function (el) {
        var me = this, color;
        el = me.callParent.apply(me, [el]);
        if (me.border) {
            el.style.border = "1px solid " + me.borderColor || '#000';
        }
        if (me.color === 'random') {
            color = me.gray ? me.getRandomGrayColor() : me.getRandomColor();
        }
        ///el.id = Blend.id();
        el.style['background-color'] = color;
        el.style.width = me.width;
        el.style.height = me.height;
        el.listeners = {
            click: function () {
                me.fireEvent("click");
            }
        };
        el.cls = ['rect'];
        return el;
    },
    setText: function (value) {
        var me = this;
        value = value || Blend.id();
        me.element.innerHTML = value;
    },
    getText: function () {
        var me = this;
        return me.element.innerHTML;
    },
    getRandomGrayColor: function () {
        var colors = ['#FFFFFF', '#FEFEFE', '#FDFDFD', '#FCFCFC', '#FBFBFB', '#FAFAFA', '#F9F9F9', '#F8F8F8',
            '#F7F7F7', '#F6F6F6', '#F5F5F5', '#F4F4F4', '#F3F3F3', '#F2F2F2', '#F1F1F1', '#F0F0F0'];
        return colors[Math.floor(Math.random() * colors.length)];
    },
    getRandomColor: function () {
        var letters = '0123456789ABCDEF'.split('');
        var color = '#';
        for (var i = 0; i < 6; i++) {
            color += letters[Math.floor(Math.random() * 16)];
        }
        return color;
    }
});<commit_msg>Added sample border<commit_after>Blend.defineClass('Blend.ui.Rect', {
    extend: 'Blend.ui.Component',
    border: false,
    borderColor: '#000',
    color: 'random',
    width: 150,
    height: 50,
    gray: false,
    alias: 'ui.rect',
    text: null,
    rcnt: 0,
    lcnt: 0,
    initElement: function (el) {
        var me = this, color;
        el = me.callParent.apply(me, [el]);
        if (me.border) {
            el.style.border = "1px solid " + me.borderColor || '#000';
        }
        if (me.color === 'random') {
            color = me.gray ? me.getRandomGrayColor() : me.getRandomColor();
        }
        ///el.id = Blend.id();
        el.style['background-color'] = color;
        el.style.width = me.width;
        el.style.height = me.height;
        el.style.border = '1px solid red';
        el.listeners = {
            click: function () {
                me.fireEvent("click");
            }
        };
        el.cls = ['rect'];
        return el;
    },
    setText: function (value) {
        var me = this;
        value = value || Blend.id();
        me.element.innerHTML = value;
    },
    getText: function () {
        var me = this;
        return me.element.innerHTML;
    },
    getRandomGrayColor: function () {
        var colors = ['#FFFFFF', '#FEFEFE', '#FDFDFD', '#FCFCFC', '#FBFBFB', '#FAFAFA', '#F9F9F9', '#F8F8F8',
            '#F7F7F7', '#F6F6F6', '#F5F5F5', '#F4F4F4', '#F3F3F3', '#F2F2F2', '#F1F1F1', '#F0F0F0'];
        return colors[Math.floor(Math.random() * colors.length)];
    },
    getRandomColor: function () {
        var letters = '0123456789ABCDEF'.split('');
        var color = '#';
        for (var i = 0; i < 6; i++) {
            color += letters[Math.floor(Math.random() * 16)];
        }
        return color;
    }
});<|endoftext|>


<commit_before>'use strict';

import TinyEmitter from 'tiny-emitter';

class RssFeedEmitter extends TinyEmitter {

	constructor() {
		super();
	}

	add() {

	}

	remove() {

	}

}

export default RssFeedEmitter;
<commit_msg>feat(add): adiciona as primeiras implementações do método add<commit_after>'use strict';

import TinyEmitter from 'tiny-emitter';
import * as _ from 'lodash';

class RssFeedEmitter extends TinyEmitter {

	constructor() {
		super();
		this._feedList = [];
	}

	// PUBLIC METHODS

	add(feed) {
		this._validateFeedObject(feed);

		let defaults = {
			refresh: 60000
		}

		feed = _.defaults(feed, defaults);

		this._addOrUpdateFeedList(feed);
		return this._feedList;
	}

	remove() {

	}

	list() {
		return this._feedList;
	}

	destroy() {

	}

	// PRIVATE METHODS

	_validateFeedObject(feed) {
		if ( !feed ) {
			throw new Error('You must call #add method with a feed configuration object.');
		}

		if ( !feed.url || typeof feed.url !== 'string' || feed.url === '' ) {
			throw new Error('Your configuration object should have an "url" key with a string value');
		}

		if ( feed.refresh && typeof feed.refresh !== 'number' ) {
			throw new Error('Your configuration object should have a "refresh" key with a number value');
		}
	}

	_addOrUpdateFeedList(feed) {

		let feedInList = this._findFeed(feed);

		if ( feedInList ) {
			this._removeFromFeedList(feedInList);
		}

		return this._addToFeedList(feed);

	}

	_findFeed(feed) {
		return _.find(this._feedList, { url: feed.url });
	}

	_addToFeedList(feed) {
		feed.setInterval = this._createSetInterval(feed);
		this._feedList.push(feed);

		return this._feedList;
	}

	_removeFromFeedList(feed) {
		clearInterval(feed.setInterval);
		_.remove(this._feedList, { url: feed.url } );
	}

	_createSetInterval(feed) {
		return setInterval( () => this._getFeedContent(feed), feed.refresh );
	}

	_getFeedContent(feed) {
		console.log('Getting content in rate of: ' + feed.refresh)
	}

}

export default RssFeedEmitter;
<|endoftext|>


<commit_before>d3.sankey = function() {
  var sankey = {},
      nodeWidth = 24,
      nodePadding = 8,
      size = [1, 1],
      nodes = [],
      links = [];

  sankey.nodeWidth = function(_) {
    if (!arguments.length) return nodeWidth;
    nodeWidth = +_;
    return sankey;
  };

  sankey.nodePadding = function(_) {
    if (!arguments.length) return nodePadding;
    nodePadding = +_;
    return sankey;
  };

  sankey.nodes = function(_) {
    if (!arguments.length) return nodes;
    nodes = _;
    return sankey;
  };

  sankey.links = function(_) {
    if (!arguments.length) return links;
    links = _;
    return sankey;
  };

  sankey.size = function(_) {
    if (!arguments.length) return size;
    size = _;
    return sankey;
  };

  sankey.layout = function(iterations) {
    computeNodeLinks();
    computeNodeValues();
    computeNodeBreadths();
    computeNodeDepths(iterations);
    computeLinkDepths();
    return sankey;
  };

  sankey.relayout = function() {
    computeLinkDepths();
    return sankey;
  };

  sankey.link = function() {
    var curvature = .5;

    function link(d) {
      var x0 = d.source.x + d.source.dx,
          x1 = d.target.x,
          xi = d3.interpolateNumber(x0, x1),
          x2 = xi(curvature),
          x3 = xi(1 - curvature),
          y0 = d.source.y + d.sy + d.dy / 2,
          y1 = d.target.y + d.ty + d.dy / 2;
      return "M" + x0 + "," + y0
           + "C" + x2 + "," + y0
           + " " + x3 + "," + y1
           + " " + x1 + "," + y1;
    }

    link.curvature = function(_) {
      if (!arguments.length) return curvature;
      curvature = +_;
      return link;
    };

    return link;
  };

  // Populate the sourceLinks and targetLinks for each node.
  // Also, if the source and target are not objects, assume they are indices.
  function computeNodeLinks() {
    nodes.forEach(function(node) {
      node.sourceLinks = [];
      node.targetLinks = [];
    });
    links.forEach(function(link) {
      var source = link.source,
          target = link.target;

      if (typeof source === "number") source = link.source = nodes[link.source];
      if (typeof target === "number") target = link.target = nodes[link.target];
      source.sourceLinks.push(link);
      target.targetLinks.push(link);
    });
  }

  // Compute the value (size) of each node by summing the associated links.
  function computeNodeValues() {
    nodes.forEach(function(node) {
      node.value = Math.max(
        d3.sum(node.sourceLinks, value),
        d3.sum(node.targetLinks, value)
      );
    });
  }

  // Iteratively assign the breadth (x-position) for each node.
  // Nodes are assigned the maximum breadth of incoming neighbors plus one;
  // nodes with no incoming links are assigned breadth zero, while
  // nodes with no outgoing links are assigned the maximum breadth.
  function computeNodeBreadths() {
    var remainingNodes = nodes,
        nextNodes,
        x = 0;

    while (remainingNodes.length) {
      nextNodes = [];
      remainingNodes.forEach(function(node) {
        node.x = x;
        node.dx = nodeWidth;
        node.sourceLinks.forEach(function(link) {
          nextNodes.push(link.target);
        });
      });
      remainingNodes = nextNodes;
      ++x;
    }

    //
    moveSinksRight(x);
    scaleNodeBreadths((width - nodeWidth) / (x - 1));
  }

  function moveSourcesRight() {
    nodes.forEach(function(node) {
      if (!node.targetLinks.length) {
        node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
      }
    });
  }

  function moveSinksRight(x) {
    nodes.forEach(function(node) {
      if (!node.sourceLinks.length) {
        node.x = x - 1;
      }
    });
  }

  function scaleNodeBreadths(kx) {
    nodes.forEach(function(node) {
      node.x *= kx;
    });
  }

  function computeNodeDepths(iterations) {
    var nodesByBreadth = d3.nest()
        .key(function(d) { return d.x; })
        .sortKeys(d3.ascending)
        .entries(nodes)
        .map(function(d) { return d.values; });

    //
    initializeNodeDepth();
    resolveCollisions();
    for (var alpha = 1; iterations > 0; --iterations) {
      relaxRightToLeft(alpha *= .99);
      resolveCollisions();
      relaxLeftToRight(alpha);
      resolveCollisions();
    }

    function initializeNodeDepth() {
      var ky = d3.min(nodesByBreadth, function(nodes) {
        return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
      });

      nodesByBreadth.forEach(function(nodes) {
        nodes.forEach(function(node, i) {
          node.y = i;
          node.dy = node.value * ky;
        });
      });

      links.forEach(function(link) {
        link.dy = link.value * ky;
      });
    }

    function relaxLeftToRight(alpha) {
      nodesByBreadth.forEach(function(nodes, breadth) {
        nodes.forEach(function(node) {
          if (node.targetLinks.length) {
            var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
            node.y += (y - center(node)) * alpha;
          }
        });
      });

      function weightedSource(link) {
        return center(link.source) * link.value;
      }
    }

    function relaxRightToLeft(alpha) {
      nodesByBreadth.slice().reverse().forEach(function(nodes) {
        nodes.forEach(function(node) {
          if (node.sourceLinks.length) {
            var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
            node.y += (y - center(node)) * alpha;
          }
        });
      });

      function weightedTarget(link) {
        return center(link.target) * link.value;
      }
    }

    function resolveCollisions() {
      nodesByBreadth.forEach(function(nodes) {
        var node,
            dy,
            y0 = 0,
            n = nodes.length,
            i;

        // Push any overlapping nodes down.
        nodes.sort(ascendingDepth);
        for (i = 0; i < n; ++i) {
          node = nodes[i];
          dy = y0 - node.y;
          if (dy > 0) node.y += dy;
          y0 = node.y + node.dy + nodePadding;
        }

        // If the bottommost node goes outside the bounds, push it back up.
        dy = y0 - nodePadding - size[1];
        if (dy > 0) {
          y0 = node.y -= dy;

          // Push any overlapping nodes back up.
          for (i = n - 2; i >= 0; --i) {
            node = nodes[i];
            dy = node.y + node.dy + nodePadding - y0;
            if (dy > 0) node.y -= dy;
            y0 = node.y;
          }
        }
      });
    }

    function ascendingDepth(a, b) {
      return a.y - b.y;
    }
  }

  function computeLinkDepths() {
    nodes.forEach(function(node) {
      node.sourceLinks.sort(ascendingTargetDepth);
      node.targetLinks.sort(ascendingSourceDepth);
    });
    nodes.forEach(function(node) {
      var sy = 0, ty = 0;
      node.sourceLinks.forEach(function(link) {
        link.sy = sy;
        sy += link.dy;
      });
      node.targetLinks.forEach(function(link) {
        link.ty = ty;
        ty += link.dy;
      });
    });

    function ascendingSourceDepth(a, b) {
      return a.source.y - b.source.y;
    }

    function ascendingTargetDepth(a, b) {
      return a.target.y - b.target.y;
    }
  }

  function center(node) {
    return node.y + node.dy / 2;
  }

  function value(link) {
    return link.value;
  }

  return sankey;
};
<commit_msg>fixed some linter errors, whoops<commit_after>d3.sankey = function() {
  var sankey = {},
      nodeWidth = 24,
      nodePadding = 8,
      size = [1, 1],
      nodes = [],
      links = [];

  sankey.nodeWidth = function(_) {
    if (!arguments.length) return nodeWidth;
    nodeWidth = +_;
    return sankey;
  };

  sankey.nodePadding = function(_) {
    if (!arguments.length) return nodePadding;
    nodePadding = +_;
    return sankey;
  };

  sankey.nodes = function(_) {
    if (!arguments.length) return nodes;
    nodes = _;
    return sankey;
  };

  sankey.links = function(_) {
    if (!arguments.length) return links;
    links = _;
    return sankey;
  };

  sankey.size = function(_) {
    if (!arguments.length) return size;
    size = _;
    return sankey;
  };

  sankey.layout = function(iterations) {
    computeNodeLinks();
    computeNodeValues();
    computeNodeBreadths();
    computeNodeDepths(iterations);
    computeLinkDepths();
    return sankey;
  };

  sankey.relayout = function() {
    computeLinkDepths();
    return sankey;
  };

  sankey.link = function() {
    var curvature = 0.5;

    function link(d) {
      var x0 = d.source.x + d.source.dx,
          x1 = d.target.x,
          xi = d3.interpolateNumber(x0, x1),
          x2 = xi(curvature),
          x3 = xi(1 - curvature),
          y0 = d.source.y + d.sy + d.dy / 2,
          y1 = d.target.y + d.ty + d.dy / 2;
      return "M" + x0 + "," + y0
           + "C" + x2 + "," + y0
           + " " + x3 + "," + y1
           + " " + x1 + "," + y1;
    }

    link.curvature = function(_) {
      if (!arguments.length) return curvature;
      curvature = +_;
      return link;
    };

    return link;
  };

  // Populate the sourceLinks and targetLinks for each node.
  // Also, if the source and target are not objects, assume they are indices.
  function computeNodeLinks() {
    nodes.forEach(function(node) {
      node.sourceLinks = [];
      node.targetLinks = [];
    });
    links.forEach(function(link) {
      var source = link.source,
          target = link.target;

      if (typeof source === "number") source = link.source = nodes[link.source];
      if (typeof target === "number") target = link.target = nodes[link.target];
      source.sourceLinks.push(link);
      target.targetLinks.push(link);
    });
  }

  // Compute the value (size) of each node by summing the associated links.
  function computeNodeValues() {
    nodes.forEach(function(node) {
      node.value = Math.max(
        d3.sum(node.sourceLinks, value),
        d3.sum(node.targetLinks, value)
      );
    });
  }

  // Iteratively assign the breadth (x-position) for each node.
  // Nodes are assigned the maximum breadth of incoming neighbors plus one;
  // nodes with no incoming links are assigned breadth zero, while
  // nodes with no outgoing links are assigned the maximum breadth.
  function computeNodeBreadths() {
    var remainingNodes = nodes,
        nextNodes,
        x = 0;

    var outerNodeFunction = function(node) {
      node.x = x;
      node.dx = nodeWidth;
      node.sourceLinks.forEach(function(link) {
        nextNodes.push(link.target);
      });
    };

    while (remainingNodes.length) {
      nextNodes = [];
      remainingNodes.forEach(outerNodeFunction);
      remainingNodes = nextNodes;
      ++x;
    }

    //
    moveSinksRight(x);
    scaleNodeBreadths((width - nodeWidth) / (x - 1));
  }

  function moveSourcesRight() {
    nodes.forEach(function(node) {
      if (!node.targetLinks.length) {
        node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
      }
    });
  }

  function moveSinksRight(x) {
    nodes.forEach(function(node) {
      if (!node.sourceLinks.length) {
        node.x = x - 1;
      }
    });
  }

  function scaleNodeBreadths(kx) {
    nodes.forEach(function(node) {
      node.x *= kx;
    });
  }

  function computeNodeDepths(iterations) {
    var nodesByBreadth = d3.nest()
        .key(function(d) { return d.x; })
        .sortKeys(d3.ascending)
        .entries(nodes)
        .map(function(d) { return d.values; });

    //
    initializeNodeDepth();
    resolveCollisions();
    for (var alpha = 1; iterations > 0; --iterations) {
      relaxRightToLeft(alpha *= 0.99);
      resolveCollisions();
      relaxLeftToRight(alpha);
      resolveCollisions();
    }

    function initializeNodeDepth() {
      var ky = d3.min(nodesByBreadth, function(nodes) {
        return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
      });

      nodesByBreadth.forEach(function(nodes) {
        nodes.forEach(function(node, i) {
          node.y = i;
          node.dy = node.value * ky;
        });
      });

      links.forEach(function(link) {
        link.dy = link.value * ky;
      });
    }

    function relaxLeftToRight(alpha) {
      nodesByBreadth.forEach(function(nodes, breadth) {
        nodes.forEach(function(node) {
          if (node.targetLinks.length) {
            var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
            node.y += (y - center(node)) * alpha;
          }
        });
      });

      function weightedSource(link) {
        return center(link.source) * link.value;
      }
    }

    function relaxRightToLeft(alpha) {
      nodesByBreadth.slice().reverse().forEach(function(nodes) {
        nodes.forEach(function(node) {
          if (node.sourceLinks.length) {
            var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
            node.y += (y - center(node)) * alpha;
          }
        });
      });

      function weightedTarget(link) {
        return center(link.target) * link.value;
      }
    }

    function resolveCollisions() {
      nodesByBreadth.forEach(function(nodes) {
        var node,
            dy,
            y0 = 0,
            n = nodes.length,
            i;

        // Push any overlapping nodes down.
        nodes.sort(ascendingDepth);
        for (i = 0; i < n; ++i) {
          node = nodes[i];
          dy = y0 - node.y;
          if (dy > 0) node.y += dy;
          y0 = node.y + node.dy + nodePadding;
        }

        // If the bottommost node goes outside the bounds, push it back up.
        dy = y0 - nodePadding - size[1];
        if (dy > 0) {
          y0 = node.y -= dy;

          // Push any overlapping nodes back up.
          for (i = n - 2; i >= 0; --i) {
            node = nodes[i];
            dy = node.y + node.dy + nodePadding - y0;
            if (dy > 0) node.y -= dy;
            y0 = node.y;
          }
        }
      });
    }

    function ascendingDepth(a, b) {
      return a.y - b.y;
    }
  }

  function computeLinkDepths() {
    nodes.forEach(function(node) {
      node.sourceLinks.sort(ascendingTargetDepth);
      node.targetLinks.sort(ascendingSourceDepth);
    });
    nodes.forEach(function(node) {
      var sy = 0, ty = 0;
      node.sourceLinks.forEach(function(link) {
        link.sy = sy;
        sy += link.dy;
      });
      node.targetLinks.forEach(function(link) {
        link.ty = ty;
        ty += link.dy;
      });
    });

    function ascendingSourceDepth(a, b) {
      return a.source.y - b.source.y;
    }

    function ascendingTargetDepth(a, b) {
      return a.target.y - b.target.y;
    }
  }

  function center(node) {
    return node.y + node.dy / 2;
  }

  function value(link) {
    return link.value;
  }

  return sankey;
};
<|endoftext|>


<commit_before>/*
  messages so far:

  - "active-tab" a runtime was selected array msg.activeTab
  - "show-view" a view was created id msg.view
  - "hide-view" a view was removed id msg.view
  - "setting-changed" a setting has changed msg.id view_id, msg.key key
  - "host-state" msg.state = 'disconnected' | 'ready' | waiting';
  - "element-selected" msg.obj_id, msg.rt_id

*/

var messages = new function()
{
  var __listeners = {};
  this.addListener = function(key, cb)
  {
    if( __listeners[ key ] )
    {
      __listeners[ key ].push( cb );
    }
    else
    {
      __listeners[ key ] = [ cb ];
    }
  }
  this.removeListener = function(key)
  {

  }
  this.post = function( key, msg )
  {
    var listeners = __listeners[ key ], cb = null, i = 0;
    msg.type = key;
    if( listeners )
    {
      for( ; cb = listeners[i]; i++)
      {
        cb(msg);
      }
    }
  }
}<commit_msg>commented two new messages<commit_after>/*
  messages so far:

  - "active-tab" a runtime was selected array msg.activeTab
  - "show-view" a view was created id msg.view
  - "hide-view" a view was removed id msg.view
  - "setting-changed" a setting has changed msg.id view_id, msg.key key
  - "host-state" msg.state = 'disconnected' | 'ready' | waiting';
  - "element-selected" msg.obj_id, msg.rt_id
  - "view-created" msg.id, msg.container
  - "view-destroyed" msg.id

*/

var messages = new function()
{
  var __listeners = {};
  this.addListener = function(key, cb)
  {
    if( __listeners[ key ] )
    {
      __listeners[ key ].push( cb );
    }
    else
    {
      __listeners[ key ] = [ cb ];
    }
  }
  this.removeListener = function(key)
  {

  }
  this.post = function( key, msg )
  {
    var listeners = __listeners[ key ], cb = null, i = 0;
    msg.type = key;
    if( listeners )
    {
      for( ; cb = listeners[i]; i++)
      {
        cb(msg);
      }
    }
  }
}<|endoftext|>


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

  define(function (require) {
    var languages = require('cs!configs/languages');

    return {
      // Directory from which webview is served
      root: '/',

      // Hostname and port for the cnx-archive server
      cnxarchive: {
        host: 'devarchive.cnx.org',
        port: 80
      },

      // Hostname and port for the cnx-authoring server
      cnxauthoring: {
        host: location.hostname,
        port: 8080
      },

      // Prefix to prepend to page titles
      titlePrefix: 'OpenStax CNX - ',

      // Google Analytics tracking ID
      analyticsID: 'UA-7903479-1',

      // Supported languages
      languages: languages,

      // Legacy URL
      // URLs are concatenated using the following logic: location.protocol + '//' + legacy + '/' + view.url
      //   Example: 'http:' + '//' + 'cnx.org' + '/' + 'contents'
      // Do not include the protocol or a trailing slash
      legacy: 'legacy.cnx.org',

      // Webmaster E-mail address
      webmaster: 'cnx@cnx.org',

      // Content shortcodes
      shortcodes: {
        'college_physics': '031da8d3-b525-429c-80cf-6c8ed997733a@8.1',
        'college_introduction_to_sociology': 'afe4332a-c97f-4fc4-be27-4e4d384a32d8@7.15',
        'biology': '185cbf87-c72e-48f5-b51e-f14f21b5eabd@9.44',
        'concepts_of_biology': 'b3c1e1d2-839c-42b0-a314-e119a8aafbdd@8.39',
        'anatomy_and_physiology': '14fb4ad7-39a1-4eee-ab6e-3ef2482e3e22@6.19',
        'introductory_statistics': '30189442-6998-4686-ac05-ed152b91b9de@17.20'
      }
    };

  });

})();
<commit_msg>reverting change to setting.js<commit_after>(function () {
  'use strict';

  define(function (require) {
    var languages = require('cs!configs/languages');

    return {
      // Directory from which webview is served
      root: '/',

      // Hostname and port for the cnx-archive server
      cnxarchive: {
        host: 'archive.cnx.org',
        port: 80
      },

      // Hostname and port for the cnx-authoring server
      cnxauthoring: {
        host: location.hostname,
        port: 8080
      },

      // Prefix to prepend to page titles
      titlePrefix: 'OpenStax CNX - ',

      // Google Analytics tracking ID
      analyticsID: 'UA-7903479-1',

      // Supported languages
      languages: languages,

      // Legacy URL
      // URLs are concatenated using the following logic: location.protocol + '//' + legacy + '/' + view.url
      //   Example: 'http:' + '//' + 'cnx.org' + '/' + 'contents'
      // Do not include the protocol or a trailing slash
      legacy: 'legacy.cnx.org',

      // Webmaster E-mail address
      webmaster: 'cnx@cnx.org',

      // Content shortcodes
      shortcodes: {
        'college_physics': '031da8d3-b525-429c-80cf-6c8ed997733a@8.1',
        'college_introduction_to_sociology': 'afe4332a-c97f-4fc4-be27-4e4d384a32d8@7.15',
        'biology': '185cbf87-c72e-48f5-b51e-f14f21b5eabd@9.44',
        'concepts_of_biology': 'b3c1e1d2-839c-42b0-a314-e119a8aafbdd@8.39',
        'anatomy_and_physiology': '14fb4ad7-39a1-4eee-ab6e-3ef2482e3e22@6.19',
        'introductory_statistics': '30189442-6998-4686-ac05-ed152b91b9de@17.20'
      }
    };

  });

})();
<|endoftext|>


<commit_before>/**
 * @flow
 * @desc
 */
import React from 'react'
import chalk from 'chalk'
// import _ from 'lodash'
import {render as renderToString} from 'rapscallion'
import {ServerStyleSheet, StyleSheetManager} from 'styled-components'
import {configureRootComponent, configureApp} from 'common/app'
// import {addLocaleData} from 'react-intl'
import HtmlComponent from './HtmlComponent'
// $FlowFixMe
import assets from 'webpack-assets'
// $FlowFixMe
import faviconsAssets from 'favicons-assets'
import getI18nData from 'server/i18n'

export default async (req: express$Request, res: express$Response) => {
	const {isLoggedIn, language} = req.user
	const {isMobile} = req.useragent
	console.log(chalk.cyan(`MOBILE DEVICE: ${isMobile}`))
	const meState = {auth: {isLoggedIn}}
	const layoutState = {isMobile}
	const initialState: Object = isLoggedIn
		? {me: meState, layout: layoutState}
		: {layout: layoutState}
	const i18n = getI18nData(language)
	const sheet = new ServerStyleSheet()
	const location: string = req.url
	const context = {}
	const {store, history, routes} = configureApp(initialState)
	const RootComponent: React$Node = configureRootComponent({
		store,
		history,
		routes,
		i18n,
		SSR: {location, context}
	})
	const App: string = await renderToString(
		<StyleSheetManager sheet={sheet.instance}>
			{RootComponent}
		</StyleSheetManager>
	).toPromise()
	const css: string = sheet.getStyleTags()
	const preloadedState: Object = store.getState()
	const props = {
		css,
		assets,
		faviconsAssets,
		initialState: preloadedState,
		i18n,
		App
	}

	res.writeHead(200, {
		'Content-Type': 'text/html'
	})

	HtmlComponent(props).toStream().pipe(res)
}
<commit_msg>feat(server/ssr): React 16 streaming<commit_after>/**
 * @flow
 * @desc
 */
import React from 'react'
import chalk from 'chalk'
// import _ from 'lodash'
import {renderToNodeStream, renderToStaticNodeStream} from 'react-dom/server'
import {ServerStyleSheet, StyleSheetManager} from 'styled-components'
import {configureRootComponent, configureApp} from 'common/app'
// import {addLocaleData} from 'react-intl'
import HtmlComponent from './HtmlComponent'
// $FlowFixMe
import assets from 'webpack-assets'
// $FlowFixMe
import faviconsAssets from 'favicons-assets'
import getI18nData from 'server/i18n'

export default async (req: express$Request, res: express$Response) => {
	const {isLoggedIn, language} = req.user
	const {isMobile} = req.useragent
	console.log(chalk.cyan(`MOBILE DEVICE: ${isMobile}`))
	const meState = {auth: {isLoggedIn}}
	const layoutState = {isMobile}
	const initialState: Object = isLoggedIn
		? {me: meState, layout: layoutState}
		: {layout: layoutState}
	const i18n = getI18nData(language)
	const sheet = new ServerStyleSheet()
	const location: string = req.url
	const context = {}
	const {store, history, routes} = configureApp(initialState)
	const RootComponent: React$Node = configureRootComponent({
		store,
		history,
		routes,
		i18n,
		SSR: {location, context}
	})
	const stream = renderToNodeStream(
		<StyleSheetManager sheet={sheet.instance}>
			{RootComponent}
		</StyleSheetManager>
	)

	const css: string = sheet.getStyleTags()
	const preloadedState: Object = store.getState()
	const props = {
		css,
		assets,
		faviconsAssets,
		initialState: preloadedState,
		i18n
	}
	const {beforeAppTag, afterAppTag} = HtmlComponent(props)

	res.writeHead(200, {
		'Content-Type': 'text/html'
	})
	res.write(beforeAppTag)
	stream.pipe(res, {end: false})

	stream.on('end', () => {
		res.write(afterAppTag)
		res.end()
	})
}
<|endoftext|>