<commit_before>/**
 * @copyright Devexperts
 *
 * @requires DX
 * @requires DX.Dom
 * @requires DX.Bem
 * @requires DX.Event
 * @requires DX.Tmpl
 * @requires DropDown
 * @namespace
 */

var Selectbox = (function(DX) {
	'use strict';

	var CN_SELECTBOX = 'selectBox',
		CN_INNER = CN_SELECTBOX + '--inner',
		CN_LABEL = CN_SELECTBOX + '--label',
		CN_ARROW = CN_SELECTBOX + '--arrow',
		TN_OPTGROUP = 'optgroup',
		KEY_UP_CODE = 38,
		KEY_DOWN_CODE = 40,
		ENTER_KEY_CODE = 13,
		M_ACTIVE = 'active',
		M_FOCUSED = 'focused',
		UPDATE_DELAY = 100,
		defaults = {
			tmpl: [
				'
',
				'',
				'{%= arrowTmpl %}',
				'
'
			].join(''),
			arrowTmpl: '',
			emptyOption: {
				value: '',
				label: '',
				textContent: '',
				className: ''
			},
			labelTmpl: '{%= text %}'
		};

	function map(collection, callback) {
		return Array.prototype.map.call(collection, callback);
	}

	function createDataObj(select) {
		return map(select.children, function(element) {
			return (element.tagName.toLowerCase() === TN_OPTGROUP) ? parseOptgroup(element) : parseOption(element);
		});
	}

	function parseOptgroup(optgroup) {
		return {
			label: optgroup.label,
			options: map(optgroup.children, function(option) {
				return parseOption(option);
			})
		};
	}

	function parseOption(optionNode) {
		var option = {
			value: optionNode.value,
			text: optionNode.label || optionNode.innerHTML,
			modifiers: splitClassName(optionNode),
			data: DX.Dom.getData(optionNode)
		};
		option = Object.assign({}, option, option.data);
		return option;
	}

	function splitClassName(element) {
		return element ? element.className.split(' ') : [];
	}

	/**
	 * Creates new selectbox component
	 * @constructor Selectbox
	 * @param {HTMLSelectElement} select
	 * @param {Object} optionsData data as an Object, not to be taken from select element
	 * @param {Object} customDropDownConfig
	 * @param {Object} customSelectBoxConfig
	 */
	return function Selectbox(select, optionsData, customDropDownConfig, customSelectBoxConfig) {
		var block,
			label,
			dropDown,
			updateDelay,
			currentOption,
			permanentBlockClassNames,
			selectId,
			selectBoxConfig,
			data,
			flatData;

		/**
		 * Triggers when selectbox is created
		 *
		 * @event selectbox:created
		 */
		function init() {
			var dropDownClassName = splitClassName(select);
			selectBoxConfig = Object.assign({}, defaults, customSelectBoxConfig);
			permanentBlockClassNames = select.className;
			select.className = '';

			selectId = select.id || DX.String.createRandomId();
			select.id = '';

			dropDownClassName.push(CN_SELECTBOX);

			initAppearance();

			var dropDownConfig = Object.assign({}, customDropDownConfig, {modifiers: dropDownClassName});
			dropDown = new DropDown(block, dropDownConfig);
			updateData(optionsData);
			initListeners();

			DX.Event.trigger(select, Selectbox.E_CREATED, {
				detail: {
					eventTarget: select,
					block: block,
					dropDown: dropDown.getBlock()
				}
			});
		}

		function initAppearance() {
			var parent = DX.Dom.getParent(select),
				selectedIndex = select.selectedIndex;

			block = createBlock();
			label = block.querySelector('.' + CN_LABEL);

			parent.insertBefore(block, select);
			block.appendChild(select);
			select.selectedIndex = selectedIndex;
		}

		function createBlock() {
			return DX.Dom.createElement('div', {
				id: selectId,
				innerHTML: DX.Tmpl.process(selectBoxConfig.tmpl, selectBoxConfig)
			});
		}

		function updateData(newData) {
			data = newData || createDataObj(select);
			flattenData();
			dropDown.setDataList(data);
			setIndexBySelectedIndex();
		}

		function updateBlockClassNames() {
			var classNames = [permanentBlockClassNames, DX.Bem.createModifiedClassName(CN_SELECTBOX,
				currentOption.modifiers || [])];

			if (DX.Bem.hasModifier(block, M_FOCUSED)) {
				classNames.push(DX.Bem.createModifiedClassName(CN_SELECTBOX, [M_FOCUSED]));
			}

			block.className = classNames.join(' ');
		}

		/**
		 * Triggers when selectbox is changed
		 *
		 * @event selectbox:changed
		 */
		function initListeners() {
			var dropDownBlock = dropDown.getBlock();

			select.addEventListener('focus', setFocusState);

			select.addEventListener('blur', removeFocusState);

			select.addEventListener('change', setIndexBySelectedIndex);

			block.addEventListener('touchend', blockTouchendHandler, true);

			block.addEventListener('click', toggleDropdown, true);

			dropDownBlock.addEventListener(DropDown.E_CHANGED, blockChangedHandler, true);

			dropDownBlock.addEventListener(DropDown.E_SHOWN, setActiveState);
			dropDownBlock.addEventListener(DropDown.E_HIDDEN, removeActiveState);
			select.addEventListener(Selectbox.E_CHANGE_VALUE, setIndexBySelectedIndex, true);
			select.addEventListener('DOMNodeInserted', optionListModificationHandler);
			select.addEventListener('DOMNodeRemoved', optionListModificationHandler);
		}
		/**
		 * Triggers when selectbox is destroyed
		 *
		 * @event selectbox:destroyed
		 */
		function destroy() {
			removeListeners();
			DX.Event.trigger(select, Selectbox.E_DESTROYED);
			dropDown.destroy();
		}
		function removeListeners() {
			var dropDownBlock = dropDown.getBlock();

			select.removeEventListener('focus', setFocusState);

			select.removeEventListener('blur', removeFocusState);

			select.removeEventListener('change', setIndexBySelectedIndex);

			block.removeEventListener('touchend', blockTouchendHandler, true);

			block.removeEventListener('click', toggleDropdown, true);

			dropDownBlock.removeEventListener(DropDown.E_CHANGED, blockChangedHandler, true);

			dropDownBlock.removeEventListener(DropDown.E_SHOWN, setActiveState);
			dropDownBlock.removeEventListener(DropDown.E_HIDDEN, removeActiveState);
			select.removeEventListener(Selectbox.E_CHANGE_VALUE, setIndexBySelectedIndex, true);
			select.removeEventListener('DOMNodeInserted', optionListModificationHandler);
			select.removeEventListener('DOMNodeRemoved', optionListModificationHandler);
		}

		/**
		 * Show dropdown
		 * @method showDropDown
		 */
		function showDropdown() {
			if (!isDisabled()) {
				dropDown.show();
			}
		}

		/**
		 * Hide dropdown
		 * @method hideDropDown
		 */
		function hideDropdown() {
			dropDown.hide();
		}

		function keyDownHandler(e) {
			var key;

			if (dropDown.isShown()) {

				key = e.key || e.which;
				var newIndex = dropDown.getHoveredIndex();

				if (key === KEY_UP_CODE || key === 'Up') {
					newIndex--;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === KEY_DOWN_CODE || key === 'Down') {
					newIndex++;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === ENTER_KEY_CODE || key === 'Enter') {
					var triggerChangeEvent = true;
					dropDown.hide();
					dropDown.setSelectedIndex(newIndex, triggerChangeEvent);
				}

			}
		}

		function toggleDropdown() {
			if (isActiveState()) {
				hideDropdown();
			} else {
				showDropdown();
			}
		}

		function isDisabled() {
			return select.disabled;
		}

		function setActiveState() {
			DX.Bem.addModifier(block, M_ACTIVE);
			document.addEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function setFocusState() {
			DX.Bem.addModifier(block, M_FOCUSED);
		}

		function removeFocusState() {
			DX.Bem.removeModifier(block, M_FOCUSED);
		}

		function removeActiveState() {
			DX.Bem.removeModifier(block, M_ACTIVE);
			document.removeEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function isActiveState() {
			return DX.Bem.hasModifier(block, M_ACTIVE);
		}

		function optionListModificationHandler() {
			window.clearTimeout(updateDelay);

			updateDelay = window.setTimeout(updateData, UPDATE_DELAY);
		}

		function blockTouchendHandler(e) {
			toggleDropdown();

			e.preventDefault();
		}

		function blockChangedHandler() {
			var index = dropDown.getSelectedIndex();

			select.selectedIndex = index;
			setSelectedByIndex(index);
			DX.Event.trigger(select, Selectbox.E_CHANGED);
		}

		function flattenData() {
			flatData = [];
			data.forEach(function(subData) {
				if (Array.isArray(subData.options)) {
					flatData = flatData.concat(subData.options);
				} else {
					flatData.push(subData);
				}
			});
		}

		/**
		 * Should be fired after external select index change
		 *
		 * @event selectbox:changevalue
		 */
		function setIndexBySelectedIndex() {
			var index = select.options.length ? select.selectedIndex : 0;

			setSelectedByIndex(index);
			dropDown.setSelectedIndex(index);
		}

		function setSelectedByIndex(index) {
			var option = flatData[index];

			if (option) {
				currentOption = option;
			} else {
				currentOption = selectBoxConfig.emptyOption;
			}
			setLabel(option);
			updateBlockClassNames();

			if (isDisabled()) {
				Selectbox.disable(select);
			} else {
				Selectbox.enable(select);
			}
		}

		function setLabel(data) {
			label.innerHTML = data ? DX.Tmpl.process(selectBoxConfig.labelTmpl, data) : '';
		}

		/**
		 * Get current label
		 * @method getText
		 * @returns {String}
		 */
		function getText() {
			var selectedItem = flatData[dropDown.getSelectedIndex()];
			return selectedItem ? selectedItem.text : '';
		}

		/**
		 * Get current value
		 * @method getValue
		 * @returns {Number|String}
		 */
		function getValue() {
			var selectedItem = flatData[dropDown.getSelectedIndex()];
			return selectedItem ? selectedItem.value : '';
		}

		/**
		 * Destroying selectbox component and its dropdown
		 * @method destroy
		 */
		this.destroy = destroy;

		this.getValue = getValue;
		this.getText = getText;
		this.updateData = updateData;
		this.showDropdown = showDropdown;
		this.hideDropdown = hideDropdown;
		/**
		 * Get HTMLNode containing selectbox
		 * @method getBlock
		 * @returns {Node}
		 */
		this.getBlock = function() {
			return block;
		};
		/**
		 * Get element which listens to events
		 * @method getEventTarget
		 * @returns {Node}
		 */
		this.getEventTarget = function() {
			return select;
		};

		init();
	};
})(DX);

/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CREATED = 'selectbox:created';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGED = 'selectbox:changed';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_DESTROYED = 'selectbox:destroyed';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGE_VALUE = 'selectbox:changevalue';

/**
 * Disable selectbox
 * @method disable
 * @static
 * @memberof Selectbox
 * @param {Node} select containing select block
 */
Selectbox.disable = function disableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.addModifier(block, 'disabled');
	select.disabled = true;
};
/**
 * Enable selectbox
 * @method enable
 * @static
 * @memberof Selectbox
 * @param {Node} select containing select block
 */
Selectbox.enable = function enableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.removeModifier(block, 'disabled');
	select.disabled = false;
};<commit_msg>Added removal of created blocks.<commit_after>/**
 * @copyright Devexperts
 *
 * @requires DX
 * @requires DX.Dom
 * @requires DX.Bem
 * @requires DX.Event
 * @requires DX.Tmpl
 * @requires DropDown
 * @namespace
 */

var Selectbox = (function(DX) {
	'use strict';

	var CN_SELECTBOX = 'selectBox',
		CN_INNER = CN_SELECTBOX + '--inner',
		CN_LABEL = CN_SELECTBOX + '--label',
		CN_ARROW = CN_SELECTBOX + '--arrow',
		TN_OPTGROUP = 'optgroup',
		KEY_UP_CODE = 38,
		KEY_DOWN_CODE = 40,
		ENTER_KEY_CODE = 13,
		M_ACTIVE = 'active',
		M_FOCUSED = 'focused',
		UPDATE_DELAY = 100,
		defaults = {
			tmpl: [
				'
',
				'',
				'{%= arrowTmpl %}',
				'
'
			].join(''),
			arrowTmpl: '',
			emptyOption: {
				value: '',
				label: '',
				textContent: '',
				className: ''
			},
			labelTmpl: '{%= text %}'
		};

	function map(collection, callback) {
		return Array.prototype.map.call(collection, callback);
	}

	function createDataObj(select) {
		return map(select.children, function(element) {
			return (element.tagName.toLowerCase() === TN_OPTGROUP) ? parseOptgroup(element) : parseOption(element);
		});
	}

	function parseOptgroup(optgroup) {
		return {
			label: optgroup.label,
			options: map(optgroup.children, function(option) {
				return parseOption(option);
			})
		};
	}

	function parseOption(optionNode) {
		var option = {
			value: optionNode.value,
			text: optionNode.label || optionNode.innerHTML,
			modifiers: splitClassName(optionNode),
			data: DX.Dom.getData(optionNode)
		};
		option = Object.assign({}, option, option.data);
		return option;
	}

	function splitClassName(element) {
		return element ? element.className.split(' ') : [];
	}

	/**
	 * Creates new selectbox component
	 * @constructor Selectbox
	 * @param {HTMLSelectElement} select
	 * @param {Object} optionsData data as an Object, not to be taken from select element
	 * @param {Object} customDropDownConfig
	 * @param {Object} customSelectBoxConfig
	 */
	return function Selectbox(select, optionsData, customDropDownConfig, customSelectBoxConfig) {
		var block,
			label,
			dropDown,
			updateDelay,
			currentOption,
			permanentBlockClassNames,
			selectId,
			selectBoxConfig,
			data,
			flatData;

		/**
		 * Triggers when selectbox is created
		 *
		 * @event selectbox:created
		 */
		function init() {
			var dropDownClassName = splitClassName(select);
			selectBoxConfig = Object.assign({}, defaults, customSelectBoxConfig);
			permanentBlockClassNames = select.className;
			select.className = '';

			selectId = select.id || DX.String.createRandomId();
			select.id = '';

			dropDownClassName.push(CN_SELECTBOX);

			initAppearance();

			var dropDownConfig = Object.assign({}, customDropDownConfig, {modifiers: dropDownClassName});
			dropDown = new DropDown(block, dropDownConfig);
			updateData(optionsData);
			initListeners();

			DX.Event.trigger(select, Selectbox.E_CREATED, {
				detail: {
					eventTarget: select,
					block: block,
					dropDown: dropDown.getBlock()
				}
			});
		}

		function initAppearance() {
			var parent = DX.Dom.getParent(select),
				selectedIndex = select.selectedIndex;

			block = createBlock();
			label = block.querySelector('.' + CN_LABEL);

			parent.insertBefore(block, select);
			block.appendChild(select);
			select.selectedIndex = selectedIndex;
		}

		function createBlock() {
			return DX.Dom.createElement('div', {
				id: selectId,
				innerHTML: DX.Tmpl.process(selectBoxConfig.tmpl, selectBoxConfig)
			});
		}

		function removeBlock() {
			var parent = DX.Dom.getParent(block);
			parent.insertBefore(select, block);
			block.remove();
		}

		function updateData(newData) {
			data = newData || createDataObj(select);
			flattenData();
			dropDown.setDataList(data);
			setIndexBySelectedIndex();
		}

		function updateBlockClassNames() {
			var classNames = [permanentBlockClassNames, DX.Bem.createModifiedClassName(CN_SELECTBOX,
				currentOption.modifiers || [])];

			if (DX.Bem.hasModifier(block, M_FOCUSED)) {
				classNames.push(DX.Bem.createModifiedClassName(CN_SELECTBOX, [M_FOCUSED]));
			}

			block.className = classNames.join(' ');
		}

		/**
		 * Triggers when selectbox is changed
		 *
		 * @event selectbox:changed
		 */
		function initListeners() {
			var dropDownBlock = dropDown.getBlock();

			select.addEventListener('focus', setFocusState);

			select.addEventListener('blur', removeFocusState);

			select.addEventListener('change', setIndexBySelectedIndex);

			block.addEventListener('touchend', blockTouchendHandler, true);

			block.addEventListener('click', toggleDropdown, true);

			dropDownBlock.addEventListener(DropDown.E_CHANGED, blockChangedHandler, true);

			dropDownBlock.addEventListener(DropDown.E_SHOWN, setActiveState);
			dropDownBlock.addEventListener(DropDown.E_HIDDEN, removeActiveState);
			select.addEventListener(Selectbox.E_CHANGE_VALUE, setIndexBySelectedIndex, true);
			select.addEventListener('DOMNodeInserted', optionListModificationHandler);
			select.addEventListener('DOMNodeRemoved', optionListModificationHandler);
		}
		/**
		 * Triggers when selectbox is destroyed
		 *
		 * @event selectbox:destroyed
		 */
		function destroy() {
			removeListeners();
			DX.Event.trigger(select, Selectbox.E_DESTROYED);
			removeBlock();
			dropDown.destroy();
		}
		function removeListeners() {
			var dropDownBlock = dropDown.getBlock();

			select.removeEventListener('focus', setFocusState);

			select.removeEventListener('blur', removeFocusState);

			select.removeEventListener('change', setIndexBySelectedIndex);

			block.removeEventListener('touchend', blockTouchendHandler, true);

			block.removeEventListener('click', toggleDropdown, true);

			dropDownBlock.removeEventListener(DropDown.E_CHANGED, blockChangedHandler, true);

			dropDownBlock.removeEventListener(DropDown.E_SHOWN, setActiveState);
			dropDownBlock.removeEventListener(DropDown.E_HIDDEN, removeActiveState);
			select.removeEventListener(Selectbox.E_CHANGE_VALUE, setIndexBySelectedIndex, true);
			select.removeEventListener('DOMNodeInserted', optionListModificationHandler);
			select.removeEventListener('DOMNodeRemoved', optionListModificationHandler);
		}

		/**
		 * Show dropdown
		 * @method showDropDown
		 */
		function showDropdown() {
			if (!isDisabled()) {
				dropDown.show();
			}
		}

		/**
		 * Hide dropdown
		 * @method hideDropDown
		 */
		function hideDropdown() {
			dropDown.hide();
		}

		function keyDownHandler(e) {
			var key;

			if (dropDown.isShown()) {

				key = e.key || e.which;
				var newIndex = dropDown.getHoveredIndex();

				if (key === KEY_UP_CODE || key === 'Up') {
					newIndex--;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === KEY_DOWN_CODE || key === 'Down') {
					newIndex++;
					dropDown.setHoveredIndex(newIndex);
				} else if (key === ENTER_KEY_CODE || key === 'Enter') {
					var triggerChangeEvent = true;
					dropDown.hide();
					dropDown.setSelectedIndex(newIndex, triggerChangeEvent);
				}

			}
		}

		function toggleDropdown() {
			if (isActiveState()) {
				hideDropdown();
			} else {
				showDropdown();
			}
		}

		function isDisabled() {
			return select.disabled;
		}

		function setActiveState() {
			DX.Bem.addModifier(block, M_ACTIVE);
			document.addEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function setFocusState() {
			DX.Bem.addModifier(block, M_FOCUSED);
		}

		function removeFocusState() {
			DX.Bem.removeModifier(block, M_FOCUSED);
		}

		function removeActiveState() {
			DX.Bem.removeModifier(block, M_ACTIVE);
			document.removeEventListener(DX.Event.KEY_DOWN, keyDownHandler);
		}

		function isActiveState() {
			return DX.Bem.hasModifier(block, M_ACTIVE);
		}

		function optionListModificationHandler() {
			window.clearTimeout(updateDelay);

			updateDelay = window.setTimeout(updateData, UPDATE_DELAY);
		}

		function blockTouchendHandler(e) {
			toggleDropdown();

			e.preventDefault();
		}

		function blockChangedHandler() {
			var index = dropDown.getSelectedIndex();

			select.selectedIndex = index;
			setSelectedByIndex(index);
			DX.Event.trigger(select, Selectbox.E_CHANGED);
		}

		function flattenData() {
			flatData = [];
			data.forEach(function(subData) {
				if (Array.isArray(subData.options)) {
					flatData = flatData.concat(subData.options);
				} else {
					flatData.push(subData);
				}
			});
		}

		/**
		 * Should be fired after external select index change
		 *
		 * @event selectbox:changevalue
		 */
		function setIndexBySelectedIndex() {
			var index = select.options.length ? select.selectedIndex : 0;

			setSelectedByIndex(index);
			dropDown.setSelectedIndex(index);
		}

		function setSelectedByIndex(index) {
			var option = flatData[index];

			if (option) {
				currentOption = option;
			} else {
				currentOption = selectBoxConfig.emptyOption;
			}
			setLabel(option);
			updateBlockClassNames();

			if (isDisabled()) {
				Selectbox.disable(select);
			} else {
				Selectbox.enable(select);
			}
		}

		function setLabel(data) {
			label.innerHTML = data ? DX.Tmpl.process(selectBoxConfig.labelTmpl, data) : '';
		}

		/**
		 * Get current label
		 * @method getText
		 * @returns {String}
		 */
		function getText() {
			var selectedItem = flatData[dropDown.getSelectedIndex()];
			return selectedItem ? selectedItem.text : '';
		}

		/**
		 * Get current value
		 * @method getValue
		 * @returns {Number|String}
		 */
		function getValue() {
			var selectedItem = flatData[dropDown.getSelectedIndex()];
			return selectedItem ? selectedItem.value : '';
		}

		/**
		 * Destroying selectbox component and its dropdown
		 * @method destroy
		 */
		this.destroy = destroy;

		this.getValue = getValue;
		this.getText = getText;
		this.updateData = updateData;
		this.showDropdown = showDropdown;
		this.hideDropdown = hideDropdown;
		/**
		 * Get HTMLNode containing selectbox
		 * @method getBlock
		 * @returns {Node}
		 */
		this.getBlock = function() {
			return block;
		};
		/**
		 * Get element which listens to events
		 * @method getEventTarget
		 * @returns {Node}
		 */
		this.getEventTarget = function() {
			return select;
		};

		init();
	};
})(DX);

/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CREATED = 'selectbox:created';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGED = 'selectbox:changed';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_DESTROYED = 'selectbox:destroyed';
/** @constant
 * @type {string}
 * @default
 * @memberof Selectbox
 */
Selectbox.E_CHANGE_VALUE = 'selectbox:changevalue';

/**
 * Disable selectbox
 * @method disable
 * @static
 * @memberof Selectbox
 * @param {Node} select containing select block
 */
Selectbox.disable = function disableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.addModifier(block, 'disabled');
	select.disabled = true;
};
/**
 * Enable selectbox
 * @method enable
 * @static
 * @memberof Selectbox
 * @param {Node} select containing select block
 */
Selectbox.enable = function enableSelectbox(select) {
	'use strict';

	var block = DX.Dom.getParent(select);

	DX.Bem.removeModifier(block, 'disabled');
	select.disabled = false;
};<|endoftext|>


<commit_before>import nedb   from 'nedb';
import fs     from 'fs';
import path   from 'path';
import teeny  from 'teeny-conf';

import AppActions from '../actions/AppActions';

const remote = electron.remote;

const app    = remote.app;
const screen = remote.screen;

/*
|--------------------------------------------------------------------------
| Some variables
|--------------------------------------------------------------------------
*/

let browserWindows = {};
    browserWindows.main = remote.getCurrentWindow();

let pathUserData     = app.getPath('userData'),
    pathSrc          = __dirname;

/*
|--------------------------------------------------------------------------
| Config
|--------------------------------------------------------------------------
*/

//let conf = teeny.loadOrCreateSync(path.join(pathUserData, 'config.json'), {});

let conf = new teeny(path.join(pathUserData, 'config.json'));
conf.loadOrCreateSync();

/*
let conf = new teeny(path.join(pathUserData, 'config.json'));
conf.loadOrCreateSync(defaultConfig);
*/

/*
|--------------------------------------------------------------------------
| supported Formats
|--------------------------------------------------------------------------
*/

let supportedFormats = [
    'audio/mp3',
    'audio/mp4',
    'audio/mpeg3',
    'audio/x-mpeg-3',
    'audio/mpeg',

    'audio/wav',
    'audio-wave',
    'audio/x-wav',
    'audio/x-pn-wav',

    'audio/ogg'
];

/*
|--------------------------------------------------------------------------
| Audio
|--------------------------------------------------------------------------
*/

// What plays the music

let audio = new Audio();
    audio.volume = conf.get('audioVolume');
    audio.playbackRate = conf.get('audioPlaybackRate');

audio.addEventListener('ended', AppActions.player.next);
audio.addEventListener('error', AppActions.player.audioError);

/*
|--------------------------------------------------------------------------
| Database
|--------------------------------------------------------------------------
*/

let db = new nedb({
    filename: path.join(pathUserData, 'library.db'),
    autoload: true
});

db.reset = function() {
    db.remove({}, { multi: true }, function (err, numRemoved) {
        db.loadDatabase(function (err) {
            if(err) throw err;
        });
    });
};

// WTFix, db.loadDatabase() throw an error if the line below is not here
fs.writeFile(path.join(pathUserData, '.init'), "", (err) => { if(err) throw err; });

/*
|--------------------------------------------------------------------------
| Export
|--------------------------------------------------------------------------
*/

export default {
    version          : app.getVersion,   // Museeks version
    config           : conf,             // teeny-conf
    initialConfig    : conf.getAll(),    // the config at the start of the application
    db               : db,               // database
    supportedFormats : supportedFormats, // supported audio formats
    audio            : audio,            // HTML5 audio tag
    pathSrc          : pathSrc,          // path of the app
    browserWindows   : browserWindows    // Object containing all the windows
};
<commit_msg>Add missing mp4, m4a and aac in supported mime-types<commit_after>import nedb   from 'nedb';
import fs     from 'fs';
import path   from 'path';
import teeny  from 'teeny-conf';

import AppActions from '../actions/AppActions';

const remote = electron.remote;

const app    = remote.app;
const screen = remote.screen;

/*
|--------------------------------------------------------------------------
| Some variables
|--------------------------------------------------------------------------
*/

let browserWindows = {};
    browserWindows.main = remote.getCurrentWindow();

let pathUserData     = app.getPath('userData'),
    pathSrc          = __dirname;

/*
|--------------------------------------------------------------------------
| Config
|--------------------------------------------------------------------------
*/

//let conf = teeny.loadOrCreateSync(path.join(pathUserData, 'config.json'), {});

let conf = new teeny(path.join(pathUserData, 'config.json'));
conf.loadOrCreateSync();

/*
|--------------------------------------------------------------------------
| supported Formats
|--------------------------------------------------------------------------
*/

let supportedFormats = [
    'audio/mp3',
    'audio/mp4',
    'audio/mpeg3',
    'audio/x-mpeg-3',
    'audio/mpeg',
    'audio/mpeg4-generic',
    'audio/mp4a',
    'audio/mp4a-latm',
    'audio/mpga',

    'audio/aac',
    'audio/aacp',
    'audio/x-aac',
    'audio/x-m4a',
    'audio/x-m4p',
    'audio/x-m4b',

    'audio/3gpp',
    'audio/3gpp2'

    'audio/wav',
    'audio-wave',
    'audio/x-wav',
    'audio/x-pn-wav',

    'audio/ogg'
];

/*
|--------------------------------------------------------------------------
| Audio
|--------------------------------------------------------------------------
*/

// What plays the music

let audio = new Audio();
    audio.volume = conf.get('audioVolume');
    audio.playbackRate = conf.get('audioPlaybackRate');

audio.addEventListener('ended', AppActions.player.next);
audio.addEventListener('error', AppActions.player.audioError);

/*
|--------------------------------------------------------------------------
| Database
|--------------------------------------------------------------------------
*/

let db = new nedb({
    filename: path.join(pathUserData, 'library.db'),
    autoload: true
});

db.reset = function() {
    db.remove({}, { multi: true }, function (err, numRemoved) {
        db.loadDatabase(function (err) {
            if(err) throw err;
        });
    });
};

// WTFix, db.loadDatabase() throw an error if the line below is not here
fs.writeFile(path.join(pathUserData, '.init'), '', (err) => { if(err) throw err; });

/*
|--------------------------------------------------------------------------
| Export
|--------------------------------------------------------------------------
*/

export default {
    version          : app.getVersion,   // Museeks version
    config           : conf,             // teeny-conf
    initialConfig    : conf.getAll(),    // the config at the start of the application
    db               : db,               // database
    supportedFormats : supportedFormats, // supported audio formats
    audio            : audio,            // HTML5 audio tag
    pathSrc          : pathSrc,          // path of the app
    browserWindows   : browserWindows    // Object containing all the windows
};
<|endoftext|>


<commit_before>/**
* Copyright 2012-2016, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var d3 = require('d3');
var isNumeric = require('fast-isnumeric');

var Lib = require('../../lib');
var svgTextUtils = require('../../lib/svg_text_utils');

var Color = require('../../components/color');
var Drawing = require('../../components/drawing');
var ErrorBars = require('../../components/errorbars');

var arraysToCalcdata = require('./arrays_to_calcdata');

module.exports = function plot(gd, plotinfo, cdbar) {
    var xa = plotinfo.xaxis,
        ya = plotinfo.yaxis,
        fullLayout = gd._fullLayout;

    var bartraces = plotinfo.plot.select('.barlayer')
        .selectAll('g.trace.bars')
            .data(cdbar)
      .enter().append('g')
        .attr('class', 'trace bars');

    bartraces.append('g')
        .attr('class', 'points')
        .each(function(d) {
            var t = d[0].t,
                trace = d[0].trace,
                poffset = t.poffset,
                poffsetIsArray = Array.isArray(poffset),
                barwidth = t.barwidth,
                barwidthIsArray = Array.isArray(barwidth);

            arraysToCalcdata(d);

            d3.select(this).selectAll('g.point')
                .data(Lib.identity)
              .enter().append('g').classed('point', true)
                .each(function(di, i) {
                    // now display the bar
                    // clipped xf/yf (2nd arg true): non-positive
                    // log values go off-screen by plotwidth
                    // so you see them continue if you drag the plot
                    var p0 = di.p + ((poffsetIsArray) ? poffset[i] : poffset),
                        p1 = p0 + ((barwidthIsArray) ? barwidth[i] : barwidth),
                        s0 = di.b,
                        s1 = s0 + di.s;

                    var x0, x1, y0, y1;
                    if(trace.orientation === 'h') {
                        y0 = ya.c2p(p0, true);
                        y1 = ya.c2p(p1, true);
                        x0 = xa.c2p(s0, true);
                        x1 = xa.c2p(s1, true);
                    }
                    else {
                        x0 = xa.c2p(p0, true);
                        x1 = xa.c2p(p1, true);
                        y0 = ya.c2p(s0, true);
                        y1 = ya.c2p(s1, true);
                    }

                    if(!isNumeric(x0) || !isNumeric(x1) ||
                            !isNumeric(y0) || !isNumeric(y1) ||
                            x0 === x1 || y0 === y1) {
                        d3.select(this).remove();
                        return;
                    }

                    var lw = (di.mlw + 1 || trace.marker.line.width + 1 ||
                            (di.trace ? di.trace.marker.line.width : 0) + 1) - 1,
                        offset = d3.round((lw / 2) % 1, 2);

                    function roundWithLine(v) {
                        // if there are explicit gaps, don't round,
                        // it can make the gaps look crappy
                        return (fullLayout.bargap === 0 && fullLayout.bargroupgap === 0) ?
                            d3.round(Math.round(v) - offset, 2) : v;
                    }

                    function expandToVisible(v, vc) {
                        // if it's not in danger of disappearing entirely,
                        // round more precisely
                        return Math.abs(v - vc) >= 2 ? roundWithLine(v) :
                        // but if it's very thin, expand it so it's
                        // necessarily visible, even if it might overlap
                        // its neighbor
                        (v > vc ? Math.ceil(v) : Math.floor(v));
                    }

                    if(!gd._context.staticPlot) {
                        // if bars are not fully opaque or they have a line
                        // around them, round to integer pixels, mainly for
                        // safari so we prevent overlaps from its expansive
                        // pixelation. if the bars ARE fully opaque and have
                        // no line, expand to a full pixel to make sure we
                        // can see them
                        var op = Color.opacity(di.mc || trace.marker.color),
                            fixpx = (op < 1 || lw > 0.01) ?
                                roundWithLine : expandToVisible;
                        x0 = fixpx(x0, x1);
                        x1 = fixpx(x1, x0);
                        y0 = fixpx(y0, y1);
                        y1 = fixpx(y1, y0);
                    }

                    // append bar path and text
                    var bar = d3.select(this);

                    bar.append('path').attr('d',
                        'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z');

                    appendBarText(gd, bar, d, i, x0, x1, y0, y1);
                });
        });

    // error bars are on the top
    bartraces.call(ErrorBars.plot, plotinfo);

};

function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
    var trace = calcTrace[0].trace;

    // get bar text
    var traceText = trace.text;
    if(!traceText) return;

    var text;
    if(Array.isArray(traceText)) {
        if(i >= traceText.length) return;
        text = traceText[i];
    }
    else {
        text = traceText;
    }

    // get text position
    var traceTextPosition = trace.textposition,
        textPosition;
    if(Array.isArray(traceTextPosition)) {
        if(i >= traceTextPosition.length) return;
        textPosition = traceTextPosition[i];
    }
    else {
        textPosition = traceTextPosition;
    }

    if(textPosition === 'none') return;

    var barWidth = Math.abs(x1 - x0),
        barHeight = Math.abs(y1 - y0),
        barIsTooSmall = (barWidth < 8 || barHeight < 8),

        barmode = gd._fullLayout.barmode,
        inStackMode = (barmode === 'stack'),
        inRelativeMode = (barmode === 'relative'),
        inStackOrRelativeMode = inStackMode || inRelativeMode,

        calcBar = calcTrace[i],
        isOutmostBar = !inStackOrRelativeMode || calcBar._outmost;

    if(textPosition === 'auto') {
        textPosition = (barIsTooSmall && isOutmostBar) ? 'outside' : 'inside';
    }

    if(textPosition === 'outside') {
        if(!isOutmostBar) textPosition = 'inside';
    }

    if(textPosition === 'inside') {
        if(barIsTooSmall) return;
    }

    // get text font
    var textFont;

    if(textPosition === 'outside') {
        var traceOutsideTextFont = trace.outsidetextfont;
        if(Array.isArray(traceOutsideTextFont)) {
            if(i >= traceOutsideTextFont.length) return;
            textFont = traceOutsideTextFont[i];
        }
        else {
            textFont = traceOutsideTextFont;
        }
    }
    else {
        var traceInsideTextFont = trace.insidetextfont;
        if(Array.isArray(traceInsideTextFont)) {
            if(i >= traceInsideTextFont.length) return;
            textFont = traceInsideTextFont[i];
        }
        else {
            textFont = traceInsideTextFont;
        }
    }

    if(!textFont) {
        var traceTextFont = trace.textfont;
        if(Array.isArray(traceTextFont)) {
            if(i >= traceTextFont.length) return;
            textFont = traceTextFont[i];
        }
        else {
            textFont = traceTextFont;
        }
    }

    if(!textFont) {
        textFont = gd._fullLayout.font;
    }

    // append bar text
    var textSelection = bar.append('text')
        // prohibit tex interpretation until we can handle
        // tex and regular text together
        .attr('data-notex', 1)
        .text(text)
        .attr({
            'class': 'bartext',
            transform: '',
            'data-bb': '',
            'text-anchor': 'middle',
            x: 0,
            y: 0
        })
        .call(Drawing.font, textFont);

    textSelection.call(svgTextUtils.convertToTspans);
    textSelection.selectAll('tspan.line').attr({x: 0, y: 0});

    // position bar text
    var textBB = Drawing.bBox(textSelection.node()),
        textWidth = textBB.width,
        textHeight = textBB.height;
    if(!textWidth || !textHeight) {
        textSelection.remove();
        return;
    }

    // compute translate transform
    var transform;
    if(textPosition === 'outside') {
        transform = getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB,
            trace.orientation);
    }
    else {
        transform = getTransformToMoveInsideBar(x0, x1, y0, y1, textBB);
    }

    textSelection.attr('transform', transform);
}

function getTransformToMoveInsideBar(x0, x1, y0, y1, textBB) {
    // compute text and target positions
    var barWidth = Math.abs(x1 - x0),
        barHeight = Math.abs(y1 - y0),
        textWidth = textBB.width,
        textHeight = textBB.height,
        barX = (x0 + x1) / 2,
        barY = (y0 + y1) / 2,
        textX = (textBB.left + textBB.right) / 2,
        textY = (textBB.top + textBB.bottom) / 2;

    // apply 3px target padding
    var targetWidth = barWidth - 6,
        targetHeight = barHeight - 6;

    return getTransform(
        textX, textY, textWidth, textHeight,
        barX, barY, targetWidth, targetHeight);
}

function getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, orientation) {

    // compute text and target positions
    var textWidth = textBB.width,
        textHeight = textBB.height,
        textX = (textBB.left + textBB.right) / 2,
        textY = (textBB.top + textBB.bottom) / 2;

    var targetWidth, targetHeight,
        targetX, targetY;
    if(orientation === 'h') {
        if(x1 < x0) {
            // bar end is on the left hand side
            targetWidth = textWidth + 6; // 3px padding included
            targetHeight = Math.abs(y1 - y0) - 6;
            targetX = x1 - targetWidth / 2;
            targetY = (y0 + y1) / 2;
        }
        else {
            targetWidth = textWidth + 6; // padding included
            targetHeight = Math.abs(y1 - y0) - 6;
            targetX = x1 + targetWidth / 2;
            targetY = (y0 + y1) / 2;
        }
    }
    else {
        if(y1 > y0) {
            // bar end is on the bottom
            targetWidth = Math.abs(x1 - x0);
            targetHeight = 2 + textHeight; // padding included
            targetX = (x0 + x1) / 2;
            targetY = y1 + targetHeight / 2;
        }
        else {
            targetWidth = Math.abs(x1 - x0);
            targetHeight = 2 + textHeight; // padding included
            targetX = (x0 + x1) / 2;
            targetY = y1 - targetHeight / 2;
        }
    }

    return getTransform(
        textX, textY, textWidth, textHeight,
        targetX, targetY, targetWidth, targetHeight);
}

/**
 * Compute SVG transform to move a text box into a target box
 *
 * @param {number} textX         X pixel coord of the text box center
 * @param {number} textY         Y pixel coord of the text box center
 * @param {number} textWidth     text box width
 * @param {number} textHeight    text box height
 * @param {number} targetX       X pixel coord of the target box center
 * @param {number} targetY       Y pixel coord of the target box center
 * @param {number} targetWidth   target box width
 * @param {number} targetHeight  target box height
 *
 * @returns {string} SVG transform
 */
function getTransform(
    textX, textY, textWidth, textHeight,
    targetX, targetY, targetWidth, targetHeight) {

    // compute translate transform
    var translateX = targetX - textX,
        translateY = targetY - textY,
        translate = 'translate(' + translateX + ' ' + translateY + ')';

    // if bar text doesn't fit, compute rotate and scale transforms
    var doesntFit = (textWidth > targetWidth || textHeight > targetHeight),
        rotate, scale, scaleX, scaleY;

    if(doesntFit) {
        var textIsHorizontal = (textWidth > textHeight),
            targetIsHorizontal = (targetWidth > targetHeight);
        if(textIsHorizontal !== targetIsHorizontal) {
            rotate = 'rotate(-90 ' + textX + ' ' + textY + ')';
            scaleX = targetWidth / textHeight;
            scaleY = targetHeight / textWidth;
        }
        else {
            scaleX = targetWidth / textWidth;
            scaleY = targetHeight / textHeight;
        }

        if(scaleX > 1) scaleX = 1;
        if(scaleY > 1) scaleY = 1;

        if(scaleX !== 1 || scaleY !== 1) {
            scale = 'scale(' + scaleX + ' ' + scaleY + ')';
        }
    }

    // compute transform
    var transform = translate;
    if(scale) transform += ' ' + scale;
    if(rotate) transform += ' ' + rotate;

    return transform;
}
<commit_msg>bar: change criteria for `textposition: 'auto'`<commit_after>/**
* Copyright 2012-2016, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var d3 = require('d3');
var isNumeric = require('fast-isnumeric');

var Lib = require('../../lib');
var svgTextUtils = require('../../lib/svg_text_utils');

var Color = require('../../components/color');
var Drawing = require('../../components/drawing');
var ErrorBars = require('../../components/errorbars');

var arraysToCalcdata = require('./arrays_to_calcdata');

// padding in pixels around text
var TEXTPAD = 3;

module.exports = function plot(gd, plotinfo, cdbar) {
    var xa = plotinfo.xaxis,
        ya = plotinfo.yaxis,
        fullLayout = gd._fullLayout;

    var bartraces = plotinfo.plot.select('.barlayer')
        .selectAll('g.trace.bars')
            .data(cdbar)
      .enter().append('g')
        .attr('class', 'trace bars');

    bartraces.append('g')
        .attr('class', 'points')
        .each(function(d) {
            var t = d[0].t,
                trace = d[0].trace,
                poffset = t.poffset,
                poffsetIsArray = Array.isArray(poffset),
                barwidth = t.barwidth,
                barwidthIsArray = Array.isArray(barwidth);

            arraysToCalcdata(d);

            d3.select(this).selectAll('g.point')
                .data(Lib.identity)
              .enter().append('g').classed('point', true)
                .each(function(di, i) {
                    // now display the bar
                    // clipped xf/yf (2nd arg true): non-positive
                    // log values go off-screen by plotwidth
                    // so you see them continue if you drag the plot
                    var p0 = di.p + ((poffsetIsArray) ? poffset[i] : poffset),
                        p1 = p0 + ((barwidthIsArray) ? barwidth[i] : barwidth),
                        s0 = di.b,
                        s1 = s0 + di.s;

                    var x0, x1, y0, y1;
                    if(trace.orientation === 'h') {
                        y0 = ya.c2p(p0, true);
                        y1 = ya.c2p(p1, true);
                        x0 = xa.c2p(s0, true);
                        x1 = xa.c2p(s1, true);
                    }
                    else {
                        x0 = xa.c2p(p0, true);
                        x1 = xa.c2p(p1, true);
                        y0 = ya.c2p(s0, true);
                        y1 = ya.c2p(s1, true);
                    }

                    if(!isNumeric(x0) || !isNumeric(x1) ||
                            !isNumeric(y0) || !isNumeric(y1) ||
                            x0 === x1 || y0 === y1) {
                        d3.select(this).remove();
                        return;
                    }

                    var lw = (di.mlw + 1 || trace.marker.line.width + 1 ||
                            (di.trace ? di.trace.marker.line.width : 0) + 1) - 1,
                        offset = d3.round((lw / 2) % 1, 2);

                    function roundWithLine(v) {
                        // if there are explicit gaps, don't round,
                        // it can make the gaps look crappy
                        return (fullLayout.bargap === 0 && fullLayout.bargroupgap === 0) ?
                            d3.round(Math.round(v) - offset, 2) : v;
                    }

                    function expandToVisible(v, vc) {
                        // if it's not in danger of disappearing entirely,
                        // round more precisely
                        return Math.abs(v - vc) >= 2 ? roundWithLine(v) :
                        // but if it's very thin, expand it so it's
                        // necessarily visible, even if it might overlap
                        // its neighbor
                        (v > vc ? Math.ceil(v) : Math.floor(v));
                    }

                    if(!gd._context.staticPlot) {
                        // if bars are not fully opaque or they have a line
                        // around them, round to integer pixels, mainly for
                        // safari so we prevent overlaps from its expansive
                        // pixelation. if the bars ARE fully opaque and have
                        // no line, expand to a full pixel to make sure we
                        // can see them
                        var op = Color.opacity(di.mc || trace.marker.color),
                            fixpx = (op < 1 || lw > 0.01) ?
                                roundWithLine : expandToVisible;
                        x0 = fixpx(x0, x1);
                        x1 = fixpx(x1, x0);
                        y0 = fixpx(y0, y1);
                        y1 = fixpx(y1, y0);
                    }

                    // append bar path and text
                    var bar = d3.select(this);

                    bar.append('path').attr('d',
                        'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z');

                    appendBarText(gd, bar, d, i, x0, x1, y0, y1);
                });
        });

    // error bars are on the top
    bartraces.call(ErrorBars.plot, plotinfo);

};

function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
    var trace = calcTrace[0].trace;

    // get bar text
    var traceText = trace.text;
    if(!traceText) return;

    var text;
    if(Array.isArray(traceText)) {
        if(i >= traceText.length) return;
        text = traceText[i];
    }
    else {
        text = traceText;
    }

    if(!text) return;

    // get text position
    var traceTextPosition = trace.textposition,
        textPosition;
    if(Array.isArray(traceTextPosition)) {
        if(i >= traceTextPosition.length) return;
        textPosition = traceTextPosition[i];
    }
    else {
        textPosition = traceTextPosition;
    }

    if(textPosition === 'none') return;

    // get text font
    var traceTextFont = trace.textfont,
        textFont = (Array.isArray(traceTextFont)) ?
            traceTextFont[i] : traceTextFont;
    textFont = textFont || gd._fullLayout.font;

    // get outside text font
    var traceOutsideTextFont = trace.outsidetextfont,
        outsideTextFont = (Array.isArray(traceOutsideTextFont)) ?
            traceOutsideTextFont[i] : traceOutsideTextFont;
    outsideTextFont = outsideTextFont || textFont;

    // get inside text font
    var traceInsideTextFont = trace.insidetextfont,
        insideTextFont = (Array.isArray(traceInsideTextFont)) ?
            traceInsideTextFont[i] : traceInsideTextFont;
    insideTextFont = insideTextFont || textFont;

    // append text node
    function appendTextNode(bar, text, textFont) {
        var textSelection = bar.append('text')
            // prohibit tex interpretation until we can handle
            // tex and regular text together
            .attr('data-notex', 1)
            .text(text)
            .attr({
                'class': 'bartext',
                transform: '',
                'data-bb': '',
                'text-anchor': 'middle',
                x: 0,
                y: 0
            })
            .call(Drawing.font, textFont);

        textSelection.call(svgTextUtils.convertToTspans);
        textSelection.selectAll('tspan.line').attr({x: 0, y: 0});

        return textSelection;
    }

    var barmode = gd._fullLayout.barmode,
        inStackMode = (barmode === 'stack'),
        inRelativeMode = (barmode === 'relative'),
        inStackOrRelativeMode = inStackMode || inRelativeMode,

        calcBar = calcTrace[i],
        isOutmostBar = !inStackOrRelativeMode || calcBar._outmost,

        barWidth = Math.abs(x1 - x0) - 2 * TEXTPAD,  // padding excluded
        barHeight = Math.abs(y1 - y0) - 2 * TEXTPAD,  // padding excluded
        barIsTooSmall = (barWidth <= 0 || barHeight <= 0),

        textSelection,
        textBB,
        textWidth,
        textHeight;

    if(textPosition === 'outside') {
        if(!isOutmostBar) textPosition = 'inside';
    }

    if(textPosition === 'inside') {
        if(barIsTooSmall) return;
    }

    if(textPosition === 'auto') {
        if(isOutmostBar) {
            // draw text using insideTextFont and check if it fits inside bar
            textSelection = appendTextNode(bar, text, insideTextFont);

            textBB = Drawing.bBox(textSelection.node()),
            textWidth = textBB.width,
            textHeight = textBB.height;

            var textHasSize = (textWidth > 0 && textHeight > 0),
                fitsInside =
                    (textWidth <= barWidth && textHeight <= barHeight),
                fitsInsideIfRotated =
                    (textWidth <= barHeight && textHeight <= barWidth);
            if(textHasSize && (fitsInside || fitsInsideIfRotated)) {
                textPosition = 'inside';
            }
            else {
                textPosition = 'outside';
                textSelection.remove();
                textSelection = null;
            }
        }
        else if(!barIsTooSmall) textPosition = 'inside';
        else return;
    }

    if(!textSelection) {
        textSelection = appendTextNode(bar, text,
                (textPosition === 'outside') ?
                outsideTextFont : insideTextFont);

        textBB = Drawing.bBox(textSelection.node()),
        textWidth = textBB.width,
        textHeight = textBB.height;

        if(textWidth <= 0 || textHeight <= 0) {
            textSelection.remove();
            return;
        }
    }

    // compute translate transform
    var transform;
    if(textPosition === 'outside') {
        transform = getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB,
            trace.orientation);
    }
    else {
        transform = getTransformToMoveInsideBar(x0, x1, y0, y1, textBB);
    }

    textSelection.attr('transform', transform);
}

function getTransformToMoveInsideBar(x0, x1, y0, y1, textBB) {
    // compute text and target positions
    var barWidth = Math.abs(x1 - x0),
        barHeight = Math.abs(y1 - y0),
        textWidth = textBB.width,
        textHeight = textBB.height,
        barX = (x0 + x1) / 2,
        barY = (y0 + y1) / 2,
        textX = (textBB.left + textBB.right) / 2,
        textY = (textBB.top + textBB.bottom) / 2;

    // apply 3px target padding
    var targetWidth = barWidth - 2 * TEXTPAD,
        targetHeight = barHeight - 2 * TEXTPAD;

    return getTransform(
        textX, textY, textWidth, textHeight,
        barX, barY, targetWidth, targetHeight);
}

function getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, orientation) {
    // compute text and target positions
    var textWidth = textBB.width,
        textHeight = textBB.height,
        textX = (textBB.left + textBB.right) / 2,
        textY = (textBB.top + textBB.bottom) / 2;

    var targetWidth, targetHeight,
        targetX, targetY;
    if(orientation === 'h') {
        if(x1 < x0) {
            // bar end is on the left hand side
            targetWidth = textWidth + 2 * TEXTPAD; // padding included
            targetHeight = Math.abs(y1 - y0) - 2 * TEXTPAD;
            targetX = x1 - targetWidth / 2;
            targetY = (y0 + y1) / 2;
        }
        else {
            targetWidth = textWidth + 2 * TEXTPAD; // padding included
            targetHeight = Math.abs(y1 - y0) - 2 * TEXTPAD;
            targetX = x1 + targetWidth / 2;
            targetY = (y0 + y1) / 2;
        }
    }
    else {
        if(y1 > y0) {
            // bar end is on the bottom
            targetWidth = Math.abs(x1 - x0);
            targetHeight = 2 + textHeight; // padding included
            targetX = (x0 + x1) / 2;
            targetY = y1 + targetHeight / 2;
        }
        else {
            targetWidth = Math.abs(x1 - x0);
            targetHeight = 2 + textHeight; // padding included
            targetX = (x0 + x1) / 2;
            targetY = y1 - targetHeight / 2;
        }
    }

    return getTransform(
        textX, textY, textWidth, textHeight,
        targetX, targetY, targetWidth, targetHeight);
}

/**
 * Compute SVG transform to move a text box into a target box
 *
 * @param {number} textX         X pixel coord of the text box center
 * @param {number} textY         Y pixel coord of the text box center
 * @param {number} textWidth     text box width
 * @param {number} textHeight    text box height
 * @param {number} targetX       X pixel coord of the target box center
 * @param {number} targetY       Y pixel coord of the target box center
 * @param {number} targetWidth   target box width
 * @param {number} targetHeight  target box height
 *
 * @returns {string} SVG transform
 */
function getTransform(
    textX, textY, textWidth, textHeight,
    targetX, targetY, targetWidth, targetHeight) {

    // compute translate transform
    var translateX = targetX - textX,
        translateY = targetY - textY,
        translate = 'translate(' + translateX + ' ' + translateY + ')';

    // if bar text doesn't fit, compute rotate and scale transforms
    var doesntFit = (textWidth > targetWidth || textHeight > targetHeight),
        rotate, scale, scaleX, scaleY;

    if(doesntFit) {
        var textIsHorizontal = (textWidth > textHeight),
            targetIsHorizontal = (targetWidth > targetHeight);
        if(textIsHorizontal !== targetIsHorizontal) {
            rotate = 'rotate(-90 ' + textX + ' ' + textY + ')';
            scaleX = targetWidth / textHeight;
            scaleY = targetHeight / textWidth;
        }
        else {
            scaleX = targetWidth / textWidth;
            scaleY = targetHeight / textHeight;
        }

        if(scaleX > 1) scaleX = 1;
        if(scaleY > 1) scaleY = 1;

        if(scaleX !== 1 || scaleY !== 1) {
            scale = 'scale(' + scaleX + ' ' + scaleY + ')';
        }
    }

    // compute transform
    var transform = translate;
    if(scale) transform += ' ' + scale;
    if(rotate) transform += ' ' + rotate;

    return transform;
}
<|endoftext|>


<commit_before>import addElOrigin from "./addElOrigin"
import unstringTracifyArguments from "./unstringTracifyArguments"
import makeTraceObject from "./makeTraceObject"
import Origin from "../origin"
import _ from "underscore"
import stringTraceUseValue from "./stringTraceUseValue"
import {makeSureInitialHTMLHasBeenProcessed} from "./processElementsAvailableOnInitialLoad"
import processJavaScriptCode from "../../process-javascript-code"
import mapInnerHTMLAssignment from "./mapInnerHTMLAssignment"

window.fromJSDynamicFiles = {}
window.fromJSDynamicFileOrigins = {}
var tracingEnabled = false;

var originalCreateElement = document.createElement
window.originalCreateElement = originalCreateElement

var appendChildPropertyDescriptor = Object.getOwnPropertyDescriptor(Node.prototype, "appendChild");
window.originalAppendChildPropertyDescriptor = appendChildPropertyDescriptor

var nativeSetAttribute = Element.prototype.setAttribute;
window.nativeSetAttribute = nativeSetAttribute

var nativeClassNameDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, "className");
window.nativeClassNameDescriptor = nativeClassNameDescriptor

var nativeInnerHTMLDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
window.nativeInnerHTMLDescriptor = nativeInnerHTMLDescriptor;

var nativeExec = RegExp.prototype.exec;
window.nativeExec = nativeExec;

var nativeFunction = Function
window.nativeFunction = nativeFunction

var nativeJSONParse = JSON.parse
window.nativeJSONParse = nativeJSONParse

var nativeLocalStorage = window.localStorage;
window.originalLocalStorage = nativeLocalStorage

var nativeNodeTextContentDescriptor = Object.getOwnPropertyDescriptor(Node.prototype, "textContent")

export function enableTracing(){
    if (tracingEnabled){
        return
    }
    tracingEnabled = true

    document.createElement = function(tagName){

        var el = originalCreateElement.call(this, tagName)
        addElOrigin(el, "openingTagStart", {
            action: "createElement",
            inputValues: [tagName],
            value: el.tagName
        })
        addElOrigin(el, "openingTagEnd", {
            action: "createElement",
            inputValues: [tagName],
            value: el.tagName
        })
        return el;
    }

    Object.defineProperty(Node.prototype, "appendChild", {
        get: function(){
            return function(appendedEl){
                addElOrigin(this, "appendChild",{
                    action: "appendChild",
                    stack: new Error().stack.split("\n"),
                    inputValues: [appendedEl],
                    valueOfEl: appendedEl,
                    child: appendedEl
                })

                return appendChildPropertyDescriptor.value.apply(this, arguments)
            }
        }
    })

    Element.prototype.setAttribute = function(attrName, value){
        addElOrigin(this, "attribute_" + attrName.toString(), {
            action: "setAttribute",
            inputValues: [attrName, value],
            value: "not gotten around to making this work yet"
        })
        return nativeSetAttribute.apply(this, arguments)
    }

    var nativeRemoveAttribute = Element.prototype.removeAttribute;
    Element.prototype.removeAttribute = function(attrName){
        addElOrigin(this, "attribute_" +attrName.toString(), {
            action: "removeAttribute",
            inputValues: [attrName],
            value: "whateverr"
        })
        return nativeRemoveAttribute.apply(this, arguments)
    }

    Object.defineProperty(Element.prototype, "className", {
        set: function(newValue){
            addElOrigin(this, "attribute_class", {
                action: "set className",
                value: " class='" + newValue.toString() + "'",
                inputValues: [newValue]
            })
            return nativeClassNameDescriptor.set.apply(this, arguments)
        },
        get: function(){
            return nativeClassNameDescriptor.get.apply(this, arguments)
        }
    })

    JSON.parse = function(str){
        var parsedVal = nativeJSONParse.apply(this, arguments)

        for (var key in parsedVal) {
            var value = parsedVal[key]
            if (_.isArray(value) || _.isObject(value)){
                parsedVal[key] = JSON.parse(JSON.stringify(value))
            } else if (typeof value === "string" ||
                typeof value === "boolean" ||
                typeof value === "number") {
                parsedVal[key] =  makeTraceObject(
                    {
                        value: parsedVal[key],
                        origin: new Origin({
                            value: parsedVal[key],
                            inputValues: [str],
                            inputValuesCharacterIndex: [str.toString().indexOf(parsedVal[key])], // not very accurate, but better than nothing/always using char 0
                            action: "JSON.parse",
                            actionDetails: key
                        })
                    }
                )
            } else {
                throw "no"
            }
        }

        return parsedVal
    }

    Object.defineProperty(Node.prototype, "textContent", {
        get: function(){
            return nativeNodeTextContentDescriptor.get.apply(this, arguments)
        },
        set: function(newTextContent){
            var ret = nativeNodeTextContentDescriptor.set.apply(this, arguments)
            var el = this;

            addElOrigin(el, "replaceContents", el.childNodes)
            if (newTextContent !== ""){
                var childNode = el.childNodes[0];
                el.__elOrigin.contents = [childNode]

                addElOrigin(childNode, "textValue", {
                    action: "Assign textContent",
                    inputValues: [newTextContent],
                    value: newTextContent.toString()
                })
            }

            return ret;
        }
    })

    Object.defineProperty(Element.prototype, "innerHTML", {
        set: function(innerHTML){
            makeSureInitialHTMLHasBeenProcessed();

            var ret = nativeInnerHTMLDescriptor.set.apply(this, arguments)
            mapInnerHTMLAssignment(this, innerHTML, "Assign InnerHTML")
            return ret
        },
        get: function(){
            makeSureInitialHTMLHasBeenProcessed()

            var innerHTML = nativeInnerHTMLDescriptor.get.apply(this, arguments)
            return makeTraceObject({
                value: innerHTML,
                origin: new Origin({
                    value: innerHTML,
                    action: "Read Element innerHTML",
                    inputValues: []
                })
            })
        }
    })

    Object.defineProperty(window, "localStorage", {
        get: function(){
            return new Proxy(nativeLocalStorage, {
                get: function(target, name){
                    if (name === "getItem") {
                        return function getItem(key){
                            var val = nativeLocalStorage.getItem.apply(target, arguments)
                            if (typeof val === "string"){
                                val = makeTraceObject({
                                    value: val,
                                    origin: new Origin({
                                        action: "localStorage.getItem",
                                        actionDetails: key,
                                        value: val,
                                        inputValues: [key]
                                    }),
                                })
                            }
                            return val;
                        }
                    }

                    var res = nativeLocalStorage[name]
                    var propertyValueIsLocalStorageData = nativeLocalStorage.hasOwnProperty(name)
                    if (propertyValueIsLocalStorageData){
                        res = makeTraceObject({
                            value: res,
                            origin: new Origin({
                                action: "localStorage.getItem",
                                actionDetails: name,
                                value: res,
                                inputValues: [name]
                            }),
                        })
                    }

                    if (typeof res === "function"){
                        return res.bind(target)
                    }
                    return res;
                }
            })
        }
    })

    RegExp.prototype.exec = function(){
        var args = unstringTracifyArguments(arguments)
        return nativeExec.apply(this, args)
    }

    window.Function = function(code){
        var args = Array.prototype.slice.apply(arguments)
        var code = args.pop()
        var argsWithoutCode = args.slice()

        var id = _.uniqueId();
        var filename = "DynamicFunction" + id + ".js"
        var res = processJavaScriptCode(stringTraceUseValue(code), {filename: filename})
        args.push(res.code)

        var fnName = "DynamicFunction" + id
        var smFilename = filename + ".map"
        var evalCode = "function " + fnName + "(" + argsWithoutCode.join(",") + "){" + res.code + "}" +
            "\n//# sourceURL=" + filename +
            "\n//# sourceMappingURL=" + smFilename

        // create script tag instead of eval to prevent strict mode from propagating
        // (I'm guessing if you call eval from code that's in strict mode  strict mode will
        // propagate to the eval'd code.)
        var script = document.createElement("script")
        script.innerHTML = evalCode
        document.body.appendChild(script)

        script.remove();

        fromJSDynamicFiles[smFilename] = res.map
        fromJSDynamicFiles[filename] = evalCode
        fromJSDynamicFiles[filename + "?dontprocess=yes"] = code.value
        fromJSDynamicFileOrigins[filename + "?dontprocess=yes"] = new Origin({
            action: "Dynamic Script",
            value: code.value,
            inputValues: [code.origin]
        })

        return function(){
            return window[fnName].apply(this, arguments)
        }
    }

    // try to add this once, but it turned out the .dataset[sth] assignment
    // was in a chrome extension that uses a different HTMLElement object
    window.nativeDataSetDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "dataset")
    // Object.defineProperty(HTMLElement.prototype, "dataset", {
    //     set: function(){
    //         return nativeDataSetDescriptor.set.apply(this, arguments)
    //     },
    //     get: function(){
    //         var nativeRes = nativeDataSetDescriptor.get.apply(this, arguments)
    //
    //         var proxy = new Proxy(nativeRes, {
    //             set: function(target, name, value){
    //                 nativeRes[name] = value
    //             }
    //         })
    //         return proxy;
    //     }
    // })

}

export function disableTracing(){
    if (!tracingEnabled) {
        return;
    }
    window.JSON.parse = window.nativeJSONParse
    document.createElement = window.originalCreateElement
    Object.defineProperty(Node.prototype, "appendChild", window.originalAppendChildPropertyDescriptor);
    Element.prototype.setAttribute = window.nativeSetAttribute
    Object.defineProperty(Element.prototype, "innerHTML", nativeInnerHTMLDescriptor)
    Object.defineProperty(window, "localStorage", {
        get: function(){
            return window.originalLocalStorage
        }
    })
    RegExp.prototype.exec = window.nativeExec
    window.Function = nativeFunction
    Object.defineProperty(Element.prototype, "className", window.nativeClassNameDescriptor)
    Object.defineProperty(HTMLElement.prototype, "dataset", window.nativeDataSetDescriptor)
    Object.defineProperty(Node.prototype, "textContent", nativeNodeTextContentDescriptor)

    tracingEnabled = false;
}

window._disableTracing = disableTracing
<commit_msg>Better support Array.join.<commit_after>import addElOrigin from "./addElOrigin"
import unstringTracifyArguments from "./unstringTracifyArguments"
import makeTraceObject from "./makeTraceObject"
import Origin from "../origin"
import _ from "underscore"
import stringTraceUseValue from "./stringTraceUseValue"
import {makeSureInitialHTMLHasBeenProcessed} from "./processElementsAvailableOnInitialLoad"
import processJavaScriptCode from "../../process-javascript-code"
import mapInnerHTMLAssignment from "./mapInnerHTMLAssignment"

window.fromJSDynamicFiles = {}
window.fromJSDynamicFileOrigins = {}
var tracingEnabled = false;

var originalCreateElement = document.createElement
window.originalCreateElement = originalCreateElement

var appendChildPropertyDescriptor = Object.getOwnPropertyDescriptor(Node.prototype, "appendChild");
window.originalAppendChildPropertyDescriptor = appendChildPropertyDescriptor

var nativeSetAttribute = Element.prototype.setAttribute;
window.nativeSetAttribute = nativeSetAttribute

var nativeClassNameDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, "className");
window.nativeClassNameDescriptor = nativeClassNameDescriptor

var nativeInnerHTMLDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
window.nativeInnerHTMLDescriptor = nativeInnerHTMLDescriptor;

var nativeExec = RegExp.prototype.exec;
window.nativeExec = nativeExec;

var nativeFunction = Function
window.nativeFunction = nativeFunction

var nativeJSONParse = JSON.parse
window.nativeJSONParse = nativeJSONParse

var nativeLocalStorage = window.localStorage;
window.originalLocalStorage = nativeLocalStorage

var nativeArrayJoin = Array.prototype.join

var nativeNodeTextContentDescriptor = Object.getOwnPropertyDescriptor(Node.prototype, "textContent")

export function enableTracing(){
    if (tracingEnabled){
        return
    }
    tracingEnabled = true

    document.createElement = function(tagName){

        var el = originalCreateElement.call(this, tagName)
        addElOrigin(el, "openingTagStart", {
            action: "createElement",
            inputValues: [tagName],
            value: el.tagName
        })
        addElOrigin(el, "openingTagEnd", {
            action: "createElement",
            inputValues: [tagName],
            value: el.tagName
        })
        return el;
    }

    Object.defineProperty(Node.prototype, "appendChild", {
        get: function(){
            return function(appendedEl){
                addElOrigin(this, "appendChild",{
                    action: "appendChild",
                    stack: new Error().stack.split("\n"),
                    inputValues: [appendedEl],
                    valueOfEl: appendedEl,
                    child: appendedEl
                })

                return appendChildPropertyDescriptor.value.apply(this, arguments)
            }
        }
    })

    Element.prototype.setAttribute = function(attrName, value){
        addElOrigin(this, "attribute_" + attrName.toString(), {
            action: "setAttribute",
            inputValues: [attrName, value],
            value: "not gotten around to making this work yet"
        })
        return nativeSetAttribute.apply(this, arguments)
    }

    var nativeRemoveAttribute = Element.prototype.removeAttribute;
    Element.prototype.removeAttribute = function(attrName){
        addElOrigin(this, "attribute_" +attrName.toString(), {
            action: "removeAttribute",
            inputValues: [attrName],
            value: "whateverr"
        })
        return nativeRemoveAttribute.apply(this, arguments)
    }

    Object.defineProperty(Element.prototype, "className", {
        set: function(newValue){
            addElOrigin(this, "attribute_class", {
                action: "set className",
                value: " class='" + newValue.toString() + "'",
                inputValues: [newValue]
            })
            return nativeClassNameDescriptor.set.apply(this, arguments)
        },
        get: function(){
            return nativeClassNameDescriptor.get.apply(this, arguments)
        }
    })

    JSON.parse = function(str){
        var parsedVal = nativeJSONParse.apply(this, arguments)

        for (var key in parsedVal) {
            var value = parsedVal[key]
            if (_.isArray(value) || _.isObject(value)){
                parsedVal[key] = JSON.parse(JSON.stringify(value))
            } else if (typeof value === "string" ||
                typeof value === "boolean" ||
                typeof value === "number") {
                parsedVal[key] =  makeTraceObject(
                    {
                        value: parsedVal[key],
                        origin: new Origin({
                            value: parsedVal[key],
                            inputValues: [str],
                            inputValuesCharacterIndex: [str.toString().indexOf(parsedVal[key])], // not very accurate, but better than nothing/always using char 0
                            action: "JSON.parse",
                            actionDetails: key
                        })
                    }
                )
            } else {
                throw "no"
            }
        }

        return parsedVal
    }

    Array.prototype.join = function(separator){
        var stringifiedItems = this.map(function(item){
            return item.toString()
        })
        // .join already does stringification, but we may need to call .toString()
        // twice if there is an object with a toString function which returns
        // a FromJSString (an object) which needs to be converted to a native string
        return nativeArrayJoin.apply(stringifiedItems, [separator])
    }

    Object.defineProperty(Node.prototype, "textContent", {
        get: function(){
            return nativeNodeTextContentDescriptor.get.apply(this, arguments)
        },
        set: function(newTextContent){
            var ret = nativeNodeTextContentDescriptor.set.apply(this, arguments)
            var el = this;

            addElOrigin(el, "replaceContents", el.childNodes)
            if (newTextContent !== ""){
                var childNode = el.childNodes[0];
                el.__elOrigin.contents = [childNode]

                addElOrigin(childNode, "textValue", {
                    action: "Assign textContent",
                    inputValues: [newTextContent],
                    value: newTextContent.toString()
                })
            }

            return ret;
        }
    })

    Object.defineProperty(Element.prototype, "innerHTML", {
        set: function(innerHTML){
            makeSureInitialHTMLHasBeenProcessed();

            var ret = nativeInnerHTMLDescriptor.set.apply(this, arguments)
            mapInnerHTMLAssignment(this, innerHTML, "Assign InnerHTML")
            return ret
        },
        get: function(){
            makeSureInitialHTMLHasBeenProcessed()

            var innerHTML = nativeInnerHTMLDescriptor.get.apply(this, arguments)
            return makeTraceObject({
                value: innerHTML,
                origin: new Origin({
                    value: innerHTML,
                    action: "Read Element innerHTML",
                    inputValues: []
                })
            })
        }
    })

    Object.defineProperty(window, "localStorage", {
        get: function(){
            return new Proxy(nativeLocalStorage, {
                get: function(target, name){
                    if (name === "getItem") {
                        return function getItem(key){
                            var val = nativeLocalStorage.getItem.apply(target, arguments)
                            if (typeof val === "string"){
                                val = makeTraceObject({
                                    value: val,
                                    origin: new Origin({
                                        action: "localStorage.getItem",
                                        actionDetails: key,
                                        value: val,
                                        inputValues: [key]
                                    }),
                                })
                            }
                            return val;
                        }
                    }

                    var res = nativeLocalStorage[name]
                    var propertyValueIsLocalStorageData = nativeLocalStorage.hasOwnProperty(name)
                    if (propertyValueIsLocalStorageData){
                        res = makeTraceObject({
                            value: res,
                            origin: new Origin({
                                action: "localStorage.getItem",
                                actionDetails: name,
                                value: res,
                                inputValues: [name]
                            }),
                        })
                    }

                    if (typeof res === "function"){
                        return res.bind(target)
                    }
                    return res;
                }
            })
        }
    })

    RegExp.prototype.exec = function(){
        var args = unstringTracifyArguments(arguments)
        return nativeExec.apply(this, args)
    }

    window.Function = function(code){
        var args = Array.prototype.slice.apply(arguments)
        var code = args.pop()
        var argsWithoutCode = args.slice()

        var id = _.uniqueId();
        var filename = "DynamicFunction" + id + ".js"
        var res = processJavaScriptCode(stringTraceUseValue(code), {filename: filename})
        args.push(res.code)

        var fnName = "DynamicFunction" + id
        var smFilename = filename + ".map"
        var evalCode = "function " + fnName + "(" + argsWithoutCode.join(",") + "){" + res.code + "}" +
            "\n//# sourceURL=" + filename +
            "\n//# sourceMappingURL=" + smFilename

        // create script tag instead of eval to prevent strict mode from propagating
        // (I'm guessing if you call eval from code that's in strict mode  strict mode will
        // propagate to the eval'd code.)
        var script = document.createElement("script")
        script.innerHTML = evalCode
        document.body.appendChild(script)

        script.remove();

        fromJSDynamicFiles[smFilename] = res.map
        fromJSDynamicFiles[filename] = evalCode
        fromJSDynamicFiles[filename + "?dontprocess=yes"] = code.value
        fromJSDynamicFileOrigins[filename + "?dontprocess=yes"] = new Origin({
            action: "Dynamic Script",
            value: code.value,
            inputValues: [code.origin]
        })

        return function(){
            return window[fnName].apply(this, arguments)
        }
    }

    // try to add this once, but it turned out the .dataset[sth] assignment
    // was in a chrome extension that uses a different HTMLElement object
    window.nativeDataSetDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "dataset")
    // Object.defineProperty(HTMLElement.prototype, "dataset", {
    //     set: function(){
    //         return nativeDataSetDescriptor.set.apply(this, arguments)
    //     },
    //     get: function(){
    //         var nativeRes = nativeDataSetDescriptor.get.apply(this, arguments)
    //
    //         var proxy = new Proxy(nativeRes, {
    //             set: function(target, name, value){
    //                 nativeRes[name] = value
    //             }
    //         })
    //         return proxy;
    //     }
    // })

}

export function disableTracing(){
    if (!tracingEnabled) {
        return;
    }
    window.JSON.parse = window.nativeJSONParse
    document.createElement = window.originalCreateElement
    Object.defineProperty(Node.prototype, "appendChild", window.originalAppendChildPropertyDescriptor);
    Element.prototype.setAttribute = window.nativeSetAttribute
    Object.defineProperty(Element.prototype, "innerHTML", nativeInnerHTMLDescriptor)
    Object.defineProperty(window, "localStorage", {
        get: function(){
            return window.originalLocalStorage
        }
    })
    RegExp.prototype.exec = window.nativeExec
    window.Function = nativeFunction
    Object.defineProperty(Element.prototype, "className", window.nativeClassNameDescriptor)
    Object.defineProperty(HTMLElement.prototype, "dataset", window.nativeDataSetDescriptor)
    Object.defineProperty(Node.prototype, "textContent", nativeNodeTextContentDescriptor)
    Array.prototype.join = nativeArrayJoin

    tracingEnabled = false;
}

window._disableTracing = disableTracing
<|endoftext|>


<commit_before>define('apps_widget',
    ['app_selector', 'jquery', 'jquery-sortable', 'nunjucks', 'requests', 'settings', 'underscore', 'urls', 'z'],
    function(app_select, $, sortable, nunjucks, requests, settings, _, urls, z) {
    'use strict';

    function get_app_ids() {
        var ids = [];
        $('.apps-widget .result').map(function() {
            ids.push(parseInt(this.getAttribute('data-id'), 10));
        });
        return ids;
    }

    z.page.on('click', '.apps-widget .actions .delete', function() {
        /* Remove app. */
        $(this).closest('.result').remove();
        z.page.trigger('refresh_preview');
    })
    .on('click', '.apps-widget .delete-app-group', function() {
        /* Remove app group. */
        $(this).closest('.result').remove();
        z.page.trigger('refresh_preview');
    })
    .on('click', '.apps-widget .actions .reorder', function() {
        /* Reorder elements. */
        var $this = $(this);
        var $app = $this.closest('.result');
        var id = $app.data('id');
        var is_prev = $this.hasClass('prev');
        var $items = $this.closest('.apps-widget').find('.result:not(.hidden)');  // Hidden for the localized fields.
        var pos = $items.index($app);

        if ((is_prev && pos === 0) || (!is_prev && pos == $items.length - 1)) {
            // Don't swap if trying to move the first element up or last element down.
            return;
        }

        var swap_pos = is_prev ? pos - 1 : pos + 1;
        var $swap_with = $items.filter(':not(.hidden)').eq(swap_pos);

        if (is_prev) {
            $app.clone().insertBefore($swap_with);
        } else {
            $app.clone().insertAfter($swap_with);
        }
        $app.remove();
        z.page.trigger('refresh_preview');
    });

    var set = function(app) {
        /* Given an app object, render it in the widget. */
        if (_.isObject(app.name)) {
            // Choose an app name translation.
            app.name = app.name['en-US'] || app.name[Object.keys(app.name)[0]];
        }

        // Hard-code JQuery because it is run async.
        $('.apps-widget .apps').html(app_select.render_result(app));

        // Set placeholder and hide results list.
        var $app_selector = $('.app-selector');
        $app_selector.find('#app-selector').val('')
                     .attr('placeholder', app.name)
                     .text();
        $app_selector.find('.result').remove();
        $app_selector.find('input[name="app"]').val(app.id);
        z.page.trigger('refresh_preview');
    };

    var append = function(app) {
        if (get_app_ids().indexOf(parseInt(app.id, 10)) !== -1) {
            return;
        }

        // Render it in the widget.
        var $apps_widget = $('.apps-widget');
        $apps_widget.find('.apps').append(app_select.render_result(app, true));
        $apps_widget.find('.placeholder-text').hide();
        $apps_widget.find('.apps').sortable();
        z.page.trigger('refresh_preview');
    };

    var add_group = function(app_group) {
        /* Add a localizable text field as the header of an app group. */
        var $apps_widget = $('.apps-widget');
        $apps_widget.find('.apps').append(nunjucks.env.render('fields/app_group.html', {
            app_group: app_group || {},
            app_group_id: 'app-group-' + $('.app-group').length
        }));
        $apps_widget.find('.placeholder-text').hide();
        z.page.trigger('refresh_preview');
    };

    return {
        add_group: add_group,
        append: append,
        get_app_ids: get_app_ids,
        set: set,
    };
});
<commit_msg>update previews on dragon drop (bug 1032974)<commit_after>define('apps_widget',
    ['app_selector', 'jquery', 'jquery-sortable', 'nunjucks', 'requests', 'settings', 'underscore', 'urls', 'z'],
    function(app_select, $, sortable, nunjucks, requests, settings, _, urls, z) {
    'use strict';

    function get_app_ids() {
        var ids = [];
        $('.apps-widget .result').map(function() {
            ids.push(parseInt(this.getAttribute('data-id'), 10));
        });
        return ids;
    }

    z.page.on('click', '.apps-widget .actions .delete', function() {
        /* Remove app. */
        $(this).closest('.result').remove();
        z.page.trigger('refresh_preview');
    })
    .on('click', '.apps-widget .delete-app-group', function() {
        /* Remove app group. */
        $(this).closest('.result').remove();
        z.page.trigger('refresh_preview');
    })
    .on('click', '.apps-widget .actions .reorder', function() {
        /* Reorder elements. */
        var $this = $(this);
        var $app = $this.closest('.result');
        var id = $app.data('id');
        var is_prev = $this.hasClass('prev');
        var $items = $this.closest('.apps-widget').find('.result:not(.hidden)');  // Hidden for the localized fields.
        var pos = $items.index($app);

        if ((is_prev && pos === 0) || (!is_prev && pos == $items.length - 1)) {
            // Don't swap if trying to move the first element up or last element down.
            return;
        }

        var swap_pos = is_prev ? pos - 1 : pos + 1;
        var $swap_with = $items.filter(':not(.hidden)').eq(swap_pos);

        if (is_prev) {
            $app.clone().insertBefore($swap_with);
        } else {
            $app.clone().insertAfter($swap_with);
        }
        $app.remove();
        z.page.trigger('refresh_preview');
    });

    var set = function(app) {
        /* Given an app object, render it in the widget. */
        if (_.isObject(app.name)) {
            // Choose an app name translation.
            app.name = app.name['en-US'] || app.name[Object.keys(app.name)[0]];
        }

        // Hard-code JQuery because it is run async.
        $('.apps-widget .apps').html(app_select.render_result(app));

        // Set placeholder and hide results list.
        var $app_selector = $('.app-selector');
        $app_selector.find('#app-selector').val('')
                     .attr('placeholder', app.name)
                     .text();
        $app_selector.find('.result').remove();
        $app_selector.find('input[name="app"]').val(app.id);
        z.page.trigger('refresh_preview');
    };

    var append = function(app) {
        if (get_app_ids().indexOf(parseInt(app.id, 10)) !== -1) {
            return;
        }

        // Render it in the widget.
        var $apps_widget = $('.apps-widget');
        $apps_widget.find('.apps').append(app_select.render_result(app, true));
        $apps_widget.find('.placeholder-text').hide();
        $apps_widget.find('.apps').sortable({
            onDrop: function($item) {
                $item.removeClass('dragged').removeAttr('style')
                z.body.removeClass('dragging');
                z.page.trigger('refresh_preview');
            }
        });
        z.page.trigger('refresh_preview');
    };

    var add_group = function(app_group) {
        /* Add a localizable text field as the header of an app group. */
        var $apps_widget = $('.apps-widget');
        $apps_widget.find('.apps').append(nunjucks.env.render('fields/app_group.html', {
            app_group: app_group || {},
            app_group_id: 'app-group-' + $('.app-group').length
        }));
        $apps_widget.find('.placeholder-text').hide();
        z.page.trigger('refresh_preview');
    };

    return {
        add_group: add_group,
        append: append,
        get_app_ids: get_app_ids,
        set: set,
    };
});
<|endoftext|>


<commit_before>define('media-input', ['jquery', 'z'], function($, z) {

    function createInput($section) {
        $section.append($('<input>', {
            type: 'text',
            placeholder: $section.data('placeholder'),
            pattern: 'https?://.*'
        }));
    }

    z.page.on('loaded', function() {
        $('.fallback').each(function() {
            $this = $(this);
            if (!$this.parent().parent().hasClass('icon') || !$this.children().length != 0) {
                // Do not create input field if icon section already has icon value (in edit page).
                createInput($this);
            }
        });
    }).on('input', '.screenshots input[type=text], .videos input[type=text]', function(e) {
        var $input = $(e.target);
        var $allInputs = $input.parent().children('input[type=text]');
        var $emptyInputs = $allInputs.filter(function() {
            return !$(this).val();
        });

        if ($input.val() && $emptyInputs.length === 0) {
            createInput($input.parent());
        } else {
            // So that at any point in time, there will be exactly
            // ONE empty input field for user to enter more URLs.
            $emptyInputs.slice(1).remove();
        }
    });
});
<commit_msg>Changed $this to instance var<commit_after>define('media-input', ['jquery', 'z'], function($, z) {

    function createInput($section) {
        $section.append($('<input>', {
            type: 'text',
            placeholder: $section.data('placeholder'),
            pattern: 'https?://.*'
        }));
    }

    z.page.on('loaded', function() {
        $('.fallback').each(function() {
            var $this = $(this);
            if (!$this.parent().parent().hasClass('icon') || !$this.children().length != 0) {
                // Do not create input field if icon section already has icon value (in edit page).
                createInput($this);
            }
        });
    }).on('input', '.screenshots input[type=text], .videos input[type=text]', function(e) {
        var $input = $(e.target);
        var $allInputs = $input.parent().children('input[type=text]');
        var $emptyInputs = $allInputs.filter(function() {
            return !$(this).val();
        });

        if ($input.val() && $emptyInputs.length === 0) {
            createInput($input.parent());
        } else {
            // So that at any point in time, there will be exactly
            // ONE empty input field for user to enter more URLs.
            $emptyInputs.slice(1).remove();
        }
    });
});
<|endoftext|>


<commit_before>import d3 from 'd3';
import { tween } from './tween';

export function attr(nameNS, value) {
  if (arguments.length < 2) {
    for (value in nameNS) this.attr(value, nameNS[value]);
    return this;
  }

  var interpolate = nameNS === "transform" ? d3.interpolateTransform : d3.interpolate;
  var name = d3.ns.qualify(nameNS);

  function attrNull() {
    this.removeAttribute(name);
  }

  function attrNullNS() {
    this.removeAttributeNS(name.space, name.local);
  }

  function attrTween(b) {
    return b == null ? attrNull : (b += "", function() {
      var a = this.getAttribute(name), i;
      return a !== b && (i = interpolate(a, b), function(t) { this.setAttribute(name, i(t)); });
    });
  }

  function attrTweenNS(b) {
    return b == null ? attrNullNS : (b += "", function() {
      var a = this.getAttributeNS(name.space, name.local), i;
      return a !== b && (i = interpolate(a, b), function(t) { this.setAttributeNS(name.space, name.local, i(t)); });
    });
  }

  return tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
}
<commit_msg>update transition attr<commit_after>import d3 from 'd3';
import { selectionEach } from "../selection/each";

export function attr(name, value) {

  if (arguments.length < 2) {
    for (value in name) {
      this.attr(value, name[value]);
    }
    return this;
  }

  var interpolate = d3.interpolateObject;

  function attrNull() {}

  function attrTween(b) {
    if (b == null) {
      return attrNull;
    } else {
      return function () {
        let a = this[name];
        let i = interpolate(a, b);

        return function (t) {
          this[name].copy(i(t));
        };
      };
    }
  }

  return transitionTween(this, "attr." + name, value, attrTween);
}

function transitionTween(groups, name, value, tween) {
  var id = groups.id, ns = groups.namespace;
  var callback;

  if (typeof value === "function") {
    callback = function(node, i, j) {
      let d = value.call(node, node.__data__, i, j);
      node[ns][id].tween.set(name, tween(d));
    };
  } else {
    value = tween(value);
    callback = function(node) {
      node[ns][id].tween.set(name, value);
    };
  }

  return selectionEach(groups, callback);
}
<|endoftext|>