<commit_before>/**
 * EnterKey.js
 *
 * Copyright 2012, Moxiecode Systems AB
 * Released under LGPL License.
 *
 * License: http://tinymce.moxiecode.com/license
 * Contributing: http://tinymce.moxiecode.com/contributing
 */

(function(tinymce) {
	/**
	 * Contains logic for handling the enter key to split/generate block elements.
	 */
	tinymce.EnterKey = function(editor) {
		var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager;

		function handleEnterKey(evt) {
			var rng = selection.getRng(true), tmpRng, container, offset, parentBlock, newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName;

			// Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image
			function moveToCaretPosition(root) {
				var walker, node, rng, y, viewPort, lastNode = root;

				rng = dom.createRng();

				if (root.hasChildNodes()) {
					walker = new tinymce.dom.TreeWalker(root, root);

					while (node = walker.current()) {
						if (node.nodeType == 3) {
							rng.setStart(node, 0);
							rng.setEnd(node, 0);
							break;
						}

						if (/^(BR|IMG)$/.test(node.nodeName)) {
							rng.setStartBefore(node);
							rng.setEndBefore(node);
							break;
						}

						lastNode = node;
						node = walker.next();
					}

					if (!node) {
						rng.setStart(lastNode, 0);
						rng.setEnd(lastNode, 0);
					}
				} else {
					if (root.nodeName == 'BR') {
						rng.setStartBefore(root);
						rng.setEndBefore(root);
					} else {
						rng.setStart(root, 0);
						rng.setEnd(root, 0);
					}
				}

				selection.setRng(rng);

				viewPort = dom.getViewPort(editor.getWin());

				// scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
				y = dom.getPos(root).y;
				if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {
					editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
				}
			};

			// Creates a new block element by cloning the current one or creating a new one if the name is specified
			// This function will also copy any text formatting from the parent block and add it to the new one
			function createNewBlock(name) {
				var node = container, block, clonedNode, caretNode;

				block = name ? dom.create(name) : parentBlock.cloneNode(false);
				caretNode = block;

				// Clone any parent styles
				do {
					if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
						clonedNode = node.cloneNode(false);
						dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique

						if (block.hasChildNodes()) {
							clonedNode.appendChild(block.firstChild);
							block.appendChild(clonedNode);
						} else {
							caretNode = clonedNode;
							block.appendChild(clonedNode);
						}
					}
				} while (node = node.parentNode);

				// BR is needed in empty blocks on non IE browsers
				if (!tinymce.isIE) {
					caretNode.innerHTML = '
';
				}

				return block;
			};

			// Returns true/false if the caret is at the start/end of the parent block element
			function isCaretAtStartOrEndOfBlock(start) {
				var walker, node;

				// Caret is in the middle of a text node like "a|b"
				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
					return false;
				}

				// Walk the DOM and look for text nodes or non empty elements
				walker = new tinymce.dom.TreeWalker(container, parentBlock);
				while (node = (start ? walker.prev() : walker.next())) {
					if (node.nodeType === 1) {
						// Ignore bogus elements
						if (node.getAttribute('data-mce-bogus')) {
							continue;
						}

						// Keep empty elements like
						name = node.nodeName.toLowerCase();
						if (name === 'IMG') {
							return false;
						}
					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
						return false;
					}
				}

				return true;
			};

			// Wraps any text nodes or inline elements in the specified forced root block name
			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
				var newBlock, parentBlock, startNode, node, next;

				// Not in a block element or in a table cell or caption
				parentBlock = dom.getParent(container, dom.isBlock);
				if (newBlockName && (!parentBlock || /^(TD|TH|CAPTION)$/.test(parentBlock.nodeName))) {
					parentBlock = parentBlock || dom.getRoot();

					if (!parentBlock.hasChildNodes()) {
						newBlock = dom.create(newBlockName);
						parentBlock.appendChild(newBlock);
						rng.setStart(newBlock, 0);
						rng.setEnd(newBlock, 0);
						return newBlock;
					}

					// Find parent that is the first child of parentBlock
					node = container;
					while (node.parentNode != parentBlock) {
						node = node.parentNode;
					}

					// Loop left to find start node start wrapping at
					while (node && !dom.isBlock(node)) {
						startNode = node;
						node = node.previousSibling;
					}

					if (startNode) {
						newBlock = dom.create(newBlockName);
						startNode.parentNode.insertBefore(newBlock, startNode);

						// Start wrapping until we hit a block
						node = startNode;
						while (node && !dom.isBlock(node)) {
							next = node.nextSibling;
							newBlock.appendChild(node);
							node = next;
						}

						// Restore range to it's past location
						rng.setStart(container, offset);
						rng.setEnd(container, offset);
					}
				}

				return container;
			};

			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
			function handleEmptyListItem() {
				function isFirstOrLastLi(first) {
					var node = containerBlock[first ? 'firstChild' : 'lastChild'];

					// Find first/last element since there might be whitespace there
					while (node) {
						if (node.nodeType == 1) {
							break;
						}

						node = node[first ? 'nextSibling' : 'previousSibling'];
					}

					return node === parentBlock;
				};

				newBlock = createNewBlock(newBlockName || 'BR');

				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
					// Is first and last list item then replace the OL/UL with a text block
					dom.replace(newBlock, containerBlock);
				} else if (isFirstOrLastLi(true)) {
					// First LI in list then remove LI and add text block before list
					containerBlock.parentNode.insertBefore(newBlock, containerBlock);
				} else if (isFirstOrLastLi()) {
					// Last LI in list then temove LI and add text block after list
					dom.insertAfter(newBlock, containerBlock);
				} else {
					// Middle LI in list the split the list and insert a text block in the middle
					// Extract after fragment and insert it after the current block
					tmpRng = rng.cloneRange();
					tmpRng.setStartAfter(parentBlock);
					tmpRng.setEndAfter(containerBlock);
					fragment = tmpRng.extractContents();
					dom.insertAfter(fragment, containerBlock);
					dom.insertAfter(newBlock, containerBlock);
				}

				dom.remove(parentBlock);
				moveToCaretPosition(newBlock);
				undoManager.add();
			};

			// Inserts a BR element if the forced_root_block option is set to false or empty string
			function insertBr() {
				var brElm = dom.create('br'), sibling, extraBr;

				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
					sibling = container.nextSibling;

					// Insert extra BR element if needed on non IE browsers since they don't get rendered
					if (!tinymce.isIE && (!sibling || dom.isBlock(sibling))) {
						brElm = dom.create('br')
						rng.insertNode(brElm);
						rng.setStartAfter(brElm);
						rng.setEndAfter(brElm);
						extraBr = true;
					}
				}

				brElm = dom.create('br')
				rng.insertNode(brElm);

				if (!extraBr) {
					rng.setStartAfter(brElm);
					rng.setEndAfter(brElm);
				} else {
					rng.setStartBefore(brElm);
					rng.setEndBefore(brElm);
				}

				selection.setRng(rng);
				undoManager.add();
			};

			// Delete any selected contents
			if (!rng.collapsed) {
				editor.execCommand('Delete');
				return;
			}

			// Setup range items and newBlockName
			container = rng.startContainer;
			offset = rng.startOffset;
			newBlockName = settings.forced_root_block;
			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';

			// Resolve node index
			if (container.nodeType == 1 && container.hasChildNodes()) {
				container = container.childNodes[offset];
				offset = 0;
			}

			// Use browsers default behavior for shift+enter in forced root block mode
			if (newBlockName && evt.shiftKey) {
				return false;
			}

			undoManager.beforeChange();

			// Wrap the current node and it's sibling in a default block if it's needed.
			// for example this text|text2	 will become this
text|text2

			container = wrapSelfAndSiblingsInDefaultBlock(container, offset);

			// Find parent block and setup empty block paddings
			parentBlock = dom.getParent(container, dom.isBlock);
			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;

			// Setup block names
			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5

			// Handle enter inside an empty list item
			if (parentBlockName == 'LI' && dom.isEmpty(parentBlock)) {
				// Let the list plugin or browser handle nested lists for now
				if (/^(UL|OL|LI)$/.test(containerBlock.parentNode.nodeName)) {
					return false;
				}

				handleEmptyListItem();
				return;
			}

			// If no root block is configured then insert a BR by default
			if (!newBlockName && !evt.shiftKey && parentBlockName != 'LI') {
				insertBr();
				return;
			}

			// Insert new block before/after the parent block depending on caret location
			if (isCaretAtStartOrEndOfBlock()) {
				// If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
				if (/^(H[1-6])$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
					newBlock = createNewBlock(newBlockName);
				} else {
					newBlock = createNewBlock();
				}

				// Split the current container block element if enter is pressed inside an empty inner block element
				if (settings.end_container_on_empty_block && /^(HGROUP|BLOCKQUOTE|SECTION|ARTICLE)$/.test(containerBlockName) && dom.isEmpty(parentBlock)) {
					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
					newBlock = dom.split(containerBlock, parentBlock);
				} else {
					dom.insertAfter(newBlock, parentBlock);
				}
			} else if (isCaretAtStartOrEndOfBlock(true)) {
				// Insert new block before
				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
			} else {
				// Extract after fragment and insert it after the current block
				tmpRng = rng.cloneRange();
				tmpRng.setEndAfter(parentBlock);
				fragment = tmpRng.extractContents();
				newBlock = fragment.firstChild;
				dom.insertAfter(fragment, parentBlock);
			}

			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
			moveToCaretPosition(newBlock);
			undoManager.add();
		}

		editor.onKeyPress.add(function(ed, evt) {
			if (evt.keyCode == 13) {
				if (handleEnterKey(evt) !== false) {
					evt.preventDefault();
				}
			}
		});
	};
})(tinymce);<commit_msg>Fixed so the end_container_on_empty_block option handles any container block type.<commit_after>/**
 * EnterKey.js
 *
 * Copyright 2012, Moxiecode Systems AB
 * Released under LGPL License.
 *
 * License: http://tinymce.moxiecode.com/license
 * Contributing: http://tinymce.moxiecode.com/contributing
 */

(function(tinymce) {
	/**
	 * Contains logic for handling the enter key to split/generate block elements.
	 */
	tinymce.EnterKey = function(editor) {
		var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager;

		function handleEnterKey(evt) {
			var rng = selection.getRng(true), tmpRng, container, offset, parentBlock, newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName;

			function canSplitBlock(node) {
				return node && dom.isBlock(node) && !/^(TD|TH|CAPTION)$/.test(node.nodeName);
			};

			// Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image
			function moveToCaretPosition(root) {
				var walker, node, rng, y, viewPort, lastNode = root;

				rng = dom.createRng();

				if (root.hasChildNodes()) {
					walker = new tinymce.dom.TreeWalker(root, root);

					while (node = walker.current()) {
						if (node.nodeType == 3) {
							rng.setStart(node, 0);
							rng.setEnd(node, 0);
							break;
						}

						if (/^(BR|IMG)$/.test(node.nodeName)) {
							rng.setStartBefore(node);
							rng.setEndBefore(node);
							break;
						}

						lastNode = node;
						node = walker.next();
					}

					if (!node) {
						rng.setStart(lastNode, 0);
						rng.setEnd(lastNode, 0);
					}
				} else {
					if (root.nodeName == 'BR') {
						rng.setStartBefore(root);
						rng.setEndBefore(root);
					} else {
						rng.setStart(root, 0);
						rng.setEnd(root, 0);
					}
				}

				selection.setRng(rng);

				viewPort = dom.getViewPort(editor.getWin());

				// scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
				y = dom.getPos(root).y;
				if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {
					editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
				}
			};

			// Creates a new block element by cloning the current one or creating a new one if the name is specified
			// This function will also copy any text formatting from the parent block and add it to the new one
			function createNewBlock(name) {
				var node = container, block, clonedNode, caretNode;

				block = name ? dom.create(name) : parentBlock.cloneNode(false);
				caretNode = block;

				// Clone any parent styles
				do {
					if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
						clonedNode = node.cloneNode(false);
						dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique

						if (block.hasChildNodes()) {
							clonedNode.appendChild(block.firstChild);
							block.appendChild(clonedNode);
						} else {
							caretNode = clonedNode;
							block.appendChild(clonedNode);
						}
					}
				} while (node = node.parentNode);

				// BR is needed in empty blocks on non IE browsers
				if (!tinymce.isIE) {
					caretNode.innerHTML = '
';
				}

				return block;
			};

			// Returns true/false if the caret is at the start/end of the parent block element
			function isCaretAtStartOrEndOfBlock(start) {
				var walker, node;

				// Caret is in the middle of a text node like "a|b"
				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
					return false;
				}

				// Walk the DOM and look for text nodes or non empty elements
				walker = new tinymce.dom.TreeWalker(container, parentBlock);
				while (node = (start ? walker.prev() : walker.next())) {
					if (node.nodeType === 1) {
						// Ignore bogus elements
						if (node.getAttribute('data-mce-bogus')) {
							continue;
						}

						// Keep empty elements like
						name = node.nodeName.toLowerCase();
						if (name === 'IMG') {
							return false;
						}
					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
						return false;
					}
				}

				return true;
			};

			// Wraps any text nodes or inline elements in the specified forced root block name
			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
				var newBlock, parentBlock, startNode, node, next;

				// Not in a block element or in a table cell or caption
				parentBlock = dom.getParent(container, dom.isBlock);
				if (newBlockName && (!parentBlock || !canSplitBlock(parentBlock))) {
					parentBlock = parentBlock || dom.getRoot();

					if (!parentBlock.hasChildNodes()) {
						newBlock = dom.create(newBlockName);
						parentBlock.appendChild(newBlock);
						rng.setStart(newBlock, 0);
						rng.setEnd(newBlock, 0);
						return newBlock;
					}

					// Find parent that is the first child of parentBlock
					node = container;
					while (node.parentNode != parentBlock) {
						node = node.parentNode;
					}

					// Loop left to find start node start wrapping at
					while (node && !dom.isBlock(node)) {
						startNode = node;
						node = node.previousSibling;
					}

					if (startNode) {
						newBlock = dom.create(newBlockName);
						startNode.parentNode.insertBefore(newBlock, startNode);

						// Start wrapping until we hit a block
						node = startNode;
						while (node && !dom.isBlock(node)) {
							next = node.nextSibling;
							newBlock.appendChild(node);
							node = next;
						}

						// Restore range to it's past location
						rng.setStart(container, offset);
						rng.setEnd(container, offset);
					}
				}

				return container;
			};

			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
			function handleEmptyListItem() {
				function isFirstOrLastLi(first) {
					var node = containerBlock[first ? 'firstChild' : 'lastChild'];

					// Find first/last element since there might be whitespace there
					while (node) {
						if (node.nodeType == 1) {
							break;
						}

						node = node[first ? 'nextSibling' : 'previousSibling'];
					}

					return node === parentBlock;
				};

				newBlock = createNewBlock(newBlockName || 'BR');

				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
					// Is first and last list item then replace the OL/UL with a text block
					dom.replace(newBlock, containerBlock);
				} else if (isFirstOrLastLi(true)) {
					// First LI in list then remove LI and add text block before list
					containerBlock.parentNode.insertBefore(newBlock, containerBlock);
				} else if (isFirstOrLastLi()) {
					// Last LI in list then temove LI and add text block after list
					dom.insertAfter(newBlock, containerBlock);
				} else {
					// Middle LI in list the split the list and insert a text block in the middle
					// Extract after fragment and insert it after the current block
					tmpRng = rng.cloneRange();
					tmpRng.setStartAfter(parentBlock);
					tmpRng.setEndAfter(containerBlock);
					fragment = tmpRng.extractContents();
					dom.insertAfter(fragment, containerBlock);
					dom.insertAfter(newBlock, containerBlock);
				}

				dom.remove(parentBlock);
				moveToCaretPosition(newBlock);
				undoManager.add();
			};

			// Inserts a BR element if the forced_root_block option is set to false or empty string
			function insertBr() {
				var brElm = dom.create('br'), sibling, extraBr;

				if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
					sibling = container.nextSibling;

					// Insert extra BR element if needed on non IE browsers since they don't get rendered
					if (!tinymce.isIE && (!sibling || dom.isBlock(sibling))) {
						brElm = dom.create('br')
						rng.insertNode(brElm);
						rng.setStartAfter(brElm);
						rng.setEndAfter(brElm);
						extraBr = true;
					}
				}

				brElm = dom.create('br')
				rng.insertNode(brElm);

				if (!extraBr) {
					rng.setStartAfter(brElm);
					rng.setEndAfter(brElm);
				} else {
					rng.setStartBefore(brElm);
					rng.setEndBefore(brElm);
				}

				selection.setRng(rng);
				undoManager.add();
			};

			// Delete any selected contents
			if (!rng.collapsed) {
				editor.execCommand('Delete');
				return;
			}

			// Setup range items and newBlockName
			container = rng.startContainer;
			offset = rng.startOffset;
			newBlockName = settings.forced_root_block;
			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';

			// Resolve node index
			if (container.nodeType == 1 && container.hasChildNodes()) {
				container = container.childNodes[offset];
				offset = 0;
			}

			// Use browsers default behavior for shift+enter in forced root block mode
			if (newBlockName && evt.shiftKey) {
				return false;
			}

			undoManager.beforeChange();

			// Wrap the current node and it's sibling in a default block if it's needed.
			// for example this text|text2	 will become this
text|text2

			container = wrapSelfAndSiblingsInDefaultBlock(container, offset);

			// Find parent block and setup empty block paddings
			parentBlock = dom.getParent(container, dom.isBlock);
			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;

			// Setup block names
			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5

			// Handle enter inside an empty list item
			if (parentBlockName == 'LI' && dom.isEmpty(parentBlock)) {
				// Let the list plugin or browser handle nested lists for now
				if (/^(UL|OL|LI)$/.test(containerBlock.parentNode.nodeName)) {
					return false;
				}

				handleEmptyListItem();
				return;
			}

			// If no root block is configured then insert a BR by default
			if (!newBlockName && !evt.shiftKey && parentBlockName != 'LI') {
				insertBr();
				return;
			}

			// Insert new block before/after the parent block depending on caret location
			if (isCaretAtStartOrEndOfBlock()) {
				// If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
				if (/^(H[1-6])$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
					newBlock = createNewBlock(newBlockName);
				} else {
					newBlock = createNewBlock();
				}

				// Split the current container block element if enter is pressed inside an empty inner block element
				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
					newBlock = dom.split(containerBlock, parentBlock);
				} else {
					dom.insertAfter(newBlock, parentBlock);
				}
			} else if (isCaretAtStartOrEndOfBlock(true)) {
				// Insert new block before
				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
			} else {
				// Extract after fragment and insert it after the current block
				tmpRng = rng.cloneRange();
				tmpRng.setEndAfter(parentBlock);
				fragment = tmpRng.extractContents();
				newBlock = fragment.firstChild;
				dom.insertAfter(fragment, parentBlock);
			}

			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
			moveToCaretPosition(newBlock);
			undoManager.add();
		}

		editor.onKeyPress.add(function(ed, evt) {
			if (evt.keyCode == 13) {
				if (handleEnterKey(evt) !== false) {
					evt.preventDefault();
				}
			}
		});
	};
})(tinymce);<|endoftext|>


<commit_before>import { ref, computed, onUnmounted } from '@vue/composition-api'
import { BridgeEvents, getStorage, parse, setStorage } from '@vue-devtools/shared-utils'
import cloneDeep from 'lodash/cloneDeep'
import Vue from 'vue'
import { formatTime } from '@front/util/format'
import { useApps, getApps } from '../apps'
import { getBridge } from '../bridge'

const STACK_DURATION = 50
const AUTOSCROLL_DURATION = 10000

const startTime = ref(0)
const endTime = ref(0)
const minTime = ref(0)
const maxTime = ref(0)

const cursorTime = ref(null)

const layersPerApp = ref({})
const hiddenLayersPerApp = ref({})
const vScrollPerApp = ref({})

const selectedEvent = ref(null)
const hoverLayerId = ref(null)

function layerFactory (options) {
  return {
    ...options,
    events: [],
    displayedEvents: [],
    eventTimeMap: {},
    groupsMap: {},
    groups: [],
    height: 1
  }
}

function builtinLayersFactory () {
  return [
    {
      id: 'mouse',
      label: 'Mouse',
      color: 0xA451AF
    },
    {
      id: 'keyboard',
      label: 'Keyboard',
      color: 0x8151AF
    },
    {
      id: 'component-event',
      label: 'Component events',
      color: 0x41B883
    }
  ].map(options => layerFactory(options))
}

const resetCbs = []

export function resetTimeline () {
  selectedEvent.value = null

  const now = Date.now()
  startTime.value = now - 1000
  endTime.value = now
  minTime.value = now - 1000
  maxTime.value = now
  layersPerApp.value = {}
  vScrollPerApp.value = {}
  hoverLayerId.value = null

  fetchLayers()

  hiddenLayersPerApp.value = getStorage('hidden-layers', {})

  for (const cb of resetCbs) {
    cb()
  }
}

export function onTimelineReset (cb) {
  onUnmounted(() => {
    const index = resetCbs.indexOf(cb)
    if (index !== -1) resetCbs.splice(cb)
  })

  resetCbs.push(cb)
}

const addEventCbs = []

export function onEventAdd (cb) {
  onUnmounted(() => {
    const index = addEventCbs.indexOf(cb)
    if (index !== -1) addEventCbs.splice(cb)
  })

  addEventCbs.push(cb)
}

function addEvent (appId, event, layer) {
  event.layer = layer
  event.appId = appId
  layer.events.push(event)
  event.stackedEvents = []

  const wasStacked = stackEvent(event)

  // Groups
  if (event.groupId) {
    let group = layer.groupsMap[event.groupId]
    if (!group) {
      group = layer.groupsMap[event.groupId] = {
        id: event.groupId,
        events: [],
        firstEvent: event,
        lastEvent: event,
        y: 0
      }
      layer.groups.push(group)
    }
    group.events.push(event)
    group.lastEvent = event
    event.group = group
  }

  // Update scrollbar
  if (!wasStacked) {
    const scrollTime = event.time + 100
    if (scrollTime > maxTime.value) {
      if (endTime.value === maxTime.value) {
        if (startTime.value !== minTime.value) {
          // Autoscroll
          startTime.value = scrollTime - (endTime.value - startTime.value)
        } else if (endTime.value - startTime.value > AUTOSCROLL_DURATION) {
          // Autoscroll
          startTime.value = scrollTime - AUTOSCROLL_DURATION
        }
        endTime.value = scrollTime
      }
      maxTime.value = scrollTime
    }
  }

  for (const cb of addEventCbs) {
    cb(event)
  }
}

function stackEvent (event) {
  const roundedTime = Math.round(event.time / STACK_DURATION)
  const wasStacked = !!_stackEvent(event, roundedTime)
  if (!wasStacked) {
    event.layer.eventTimeMap[roundedTime] = event
    event.layer.displayedEvents.push(event)
    event.stackedEvents = [event]
  }
  return wasStacked
}

function _stackEvent (event, roundedTime) {
  const existingEvent = event.layer.eventTimeMap[roundedTime]
  if (existingEvent && existingEvent.groupId === event.groupId) {
    existingEvent.stackedEvents.push(event)
    event.stackParent = existingEvent
  }
  return existingEvent
}

export function useTime () {
  return {
    startTime,
    endTime,
    minTime,
    maxTime
  }
}

function getLayers (appId) {
  let layers = layersPerApp.value[appId]
  if (!layers) {
    layers = builtinLayersFactory()
    Vue.set(layersPerApp.value, appId, layers)
    // Read the property again to make it reactive
    layers = layersPerApp.value[appId]
  }
  return layers
}

function getHiddenLayers (appId) {
  let layers = hiddenLayersPerApp.value[appId]
  if (!layers) {
    layers = []
    Vue.set(hiddenLayersPerApp.value, appId, layers)
    // Read the property again to make it reactive
    layers = hiddenLayersPerApp.value[appId]
  }
  return layers
}

export function useLayers () {
  const { currentAppId } = useApps()

  const allLayers = computed(() => getLayers(currentAppId.value))

  function isLayerHidden (layer) {
    const list = getHiddenLayers(currentAppId.value)
    return list.includes(layer.id)
  }

  function setLayerHidden (layer, hidden) {
    const list = getHiddenLayers(currentAppId.value)
    const index = list.indexOf(layer.id)
    if (hidden && index === -1) {
      list.push(layer.id)
    } else if (!hidden && index !== -1) {
      list.splice(index, 1)
    }
    setStorage('hidden-layers', hiddenLayersPerApp.value)
  }

  const layers = computed(() => allLayers.value.filter(l => !isLayerHidden(l)))

  return {
    layers,
    allLayers,
    vScroll: computed({
      get: () => vScrollPerApp.value[currentAppId.value] || 0,
      set: value => {
        Vue.set(vScrollPerApp.value, currentAppId.value, value)
      }
    }),
    isLayerHidden,
    setLayerHidden,
    hoverLayerId
  }
}

export function useSelectedEvent () {
  function mapEvent (e) {
    return Object.freeze({
      title: e.title,
      data: parse(e.data),
      time: formatTime(e.time, 'ms')
    })
  }

  return {
    selectedEvent,
    selectedStackedEvents: computed(() => selectedEvent.value.stackedEvents.map(mapEvent)),
    selectedGroupEvents: computed(() => selectedEvent.value.group ? selectedEvent.value.group.events.map(mapEvent) : [])
  }
}

export function useCursor () {
  return {
    cursorTime
  }
}

function fetchLayers () {
  getBridge().send(BridgeEvents.TO_BACK_TIMELINE_LAYER_LIST, {})
}

export function setupTimelineBridgeEvents (bridge) {
  bridge.on(BridgeEvents.TO_FRONT_TIMELINE_EVENT, ({ appId, layerId, event }) => {
    const appIds = appId === 'all' ? getApps().map(app => app.id) : [appId]
    for (const appId of appIds) {
      const layer = getLayers(appId).find(l => l.id === layerId)
      if (!layer) {
        console.error(`Layer ${layerId} not found`)
        return
      }

      addEvent(appId, cloneDeep(event), layer)
    }
  })

  bridge.on(BridgeEvents.TO_FRONT_TIMELINE_LAYER_ADD, () => {
    fetchLayers()
  })

  bridge.on(BridgeEvents.TO_FRONT_TIMELINE_LAYER_LIST, ({ layers }) => {
    for (const layer of layers) {
      const existingLayers = getLayers(layer.appId)
      if (!existingLayers.some(l => l.id === layer.id)) {
        existingLayers.push(layerFactory(layer))
      }
    }
  })

  resetTimeline()
}
<commit_msg>fix(timeline): event that were not stacked but ignored anyway<commit_after>import { ref, computed, onUnmounted } from '@vue/composition-api'
import { BridgeEvents, getStorage, parse, setStorage } from '@vue-devtools/shared-utils'
import cloneDeep from 'lodash/cloneDeep'
import Vue from 'vue'
import { formatTime } from '@front/util/format'
import { useApps, getApps } from '../apps'
import { getBridge } from '../bridge'

const STACK_DURATION = 50
const AUTOSCROLL_DURATION = 10000

const startTime = ref(0)
const endTime = ref(0)
const minTime = ref(0)
const maxTime = ref(0)

const cursorTime = ref(null)

const layersPerApp = ref({})
const hiddenLayersPerApp = ref({})
const vScrollPerApp = ref({})

const selectedEvent = ref(null)
const hoverLayerId = ref(null)

function layerFactory (options) {
  return {
    ...options,
    events: [],
    displayedEvents: [],
    eventTimeMap: {},
    groupsMap: {},
    groups: [],
    height: 1
  }
}

function builtinLayersFactory () {
  return [
    {
      id: 'mouse',
      label: 'Mouse',
      color: 0xA451AF
    },
    {
      id: 'keyboard',
      label: 'Keyboard',
      color: 0x8151AF
    },
    {
      id: 'component-event',
      label: 'Component events',
      color: 0x41B883
    }
  ].map(options => layerFactory(options))
}

const resetCbs = []

export function resetTimeline () {
  selectedEvent.value = null

  const now = Date.now()
  startTime.value = now - 1000
  endTime.value = now
  minTime.value = now - 1000
  maxTime.value = now
  layersPerApp.value = {}
  vScrollPerApp.value = {}
  hoverLayerId.value = null

  fetchLayers()

  hiddenLayersPerApp.value = getStorage('hidden-layers', {})

  for (const cb of resetCbs) {
    cb()
  }
}

export function onTimelineReset (cb) {
  onUnmounted(() => {
    const index = resetCbs.indexOf(cb)
    if (index !== -1) resetCbs.splice(cb)
  })

  resetCbs.push(cb)
}

const addEventCbs = []

export function onEventAdd (cb) {
  onUnmounted(() => {
    const index = addEventCbs.indexOf(cb)
    if (index !== -1) addEventCbs.splice(cb)
  })

  addEventCbs.push(cb)
}

function addEvent (appId, event, layer) {
  event.layer = layer
  event.appId = appId
  layer.events.push(event)
  event.stackedEvents = []

  const wasStacked = stackEvent(event)

  // Groups
  if (event.groupId) {
    let group = layer.groupsMap[event.groupId]
    if (!group) {
      group = layer.groupsMap[event.groupId] = {
        id: event.groupId,
        events: [],
        firstEvent: event,
        lastEvent: event,
        y: 0
      }
      layer.groups.push(group)
    }
    group.events.push(event)
    group.lastEvent = event
    event.group = group
  }

  // Update scrollbar
  if (!wasStacked) {
    const scrollTime = event.time + 100
    if (scrollTime > maxTime.value) {
      if (endTime.value === maxTime.value) {
        if (startTime.value !== minTime.value) {
          // Autoscroll
          startTime.value = scrollTime - (endTime.value - startTime.value)
        } else if (endTime.value - startTime.value > AUTOSCROLL_DURATION) {
          // Autoscroll
          startTime.value = scrollTime - AUTOSCROLL_DURATION
        }
        endTime.value = scrollTime
      }
      maxTime.value = scrollTime
    }
  }

  for (const cb of addEventCbs) {
    cb(event)
  }
}

function stackEvent (event) {
  const roundedTime = Math.round(event.time / STACK_DURATION)
  const wasStacked = _stackEvent(event, roundedTime)
  if (!wasStacked) {
    event.layer.eventTimeMap[roundedTime] = event
    event.layer.displayedEvents.push(event)
    event.stackedEvents = [event]
  }
  return wasStacked
}

function _stackEvent (event, roundedTime) {
  const existingEvent = event.layer.eventTimeMap[roundedTime]
  if (existingEvent && existingEvent.groupId === event.groupId) {
    existingEvent.stackedEvents.push(event)
    event.stackParent = existingEvent
    return true
  }
  return false
}

export function useTime () {
  return {
    startTime,
    endTime,
    minTime,
    maxTime
  }
}

function getLayers (appId) {
  let layers = layersPerApp.value[appId]
  if (!layers) {
    layers = builtinLayersFactory()
    Vue.set(layersPerApp.value, appId, layers)
    // Read the property again to make it reactive
    layers = layersPerApp.value[appId]
  }
  return layers
}

function getHiddenLayers (appId) {
  let layers = hiddenLayersPerApp.value[appId]
  if (!layers) {
    layers = []
    Vue.set(hiddenLayersPerApp.value, appId, layers)
    // Read the property again to make it reactive
    layers = hiddenLayersPerApp.value[appId]
  }
  return layers
}

export function useLayers () {
  const { currentAppId } = useApps()

  const allLayers = computed(() => getLayers(currentAppId.value))

  function isLayerHidden (layer) {
    const list = getHiddenLayers(currentAppId.value)
    return list.includes(layer.id)
  }

  function setLayerHidden (layer, hidden) {
    const list = getHiddenLayers(currentAppId.value)
    const index = list.indexOf(layer.id)
    if (hidden && index === -1) {
      list.push(layer.id)
    } else if (!hidden && index !== -1) {
      list.splice(index, 1)
    }
    setStorage('hidden-layers', hiddenLayersPerApp.value)
  }

  const layers = computed(() => allLayers.value.filter(l => !isLayerHidden(l)))

  return {
    layers,
    allLayers,
    vScroll: computed({
      get: () => vScrollPerApp.value[currentAppId.value] || 0,
      set: value => {
        Vue.set(vScrollPerApp.value, currentAppId.value, value)
      }
    }),
    isLayerHidden,
    setLayerHidden,
    hoverLayerId
  }
}

export function useSelectedEvent () {
  function mapEvent (e) {
    return Object.freeze({
      title: e.title,
      data: parse(e.data),
      time: formatTime(e.time, 'ms')
    })
  }

  return {
    selectedEvent,
    selectedStackedEvents: computed(() => selectedEvent.value.stackedEvents.map(mapEvent)),
    selectedGroupEvents: computed(() => selectedEvent.value.group ? selectedEvent.value.group.events.map(mapEvent) : [])
  }
}

export function useCursor () {
  return {
    cursorTime
  }
}

function fetchLayers () {
  getBridge().send(BridgeEvents.TO_BACK_TIMELINE_LAYER_LIST, {})
}

export function setupTimelineBridgeEvents (bridge) {
  bridge.on(BridgeEvents.TO_FRONT_TIMELINE_EVENT, ({ appId, layerId, event }) => {
    const appIds = appId === 'all' ? getApps().map(app => app.id) : [appId]
    for (const appId of appIds) {
      const layer = getLayers(appId).find(l => l.id === layerId)
      if (!layer) {
        console.error(`Layer ${layerId} not found`)
        return
      }

      addEvent(appId, cloneDeep(event), layer)
    }
  })

  bridge.on(BridgeEvents.TO_FRONT_TIMELINE_LAYER_ADD, () => {
    fetchLayers()
  })

  bridge.on(BridgeEvents.TO_FRONT_TIMELINE_LAYER_LIST, ({ layers }) => {
    for (const layer of layers) {
      const existingLayers = getLayers(layer.appId)
      if (!existingLayers.some(l => l.id === layer.id)) {
        existingLayers.push(layerFactory(layer))
      }
    }
  })

  resetTimeline()
}
<|endoftext|>


<commit_before>// @flow
import { isShadowRoot } from './instanceOf';

export default function contains(parent: Element, child: Element) {
  const rootNode = child.getRootNode && child.getRootNode();

  // First, attempt with faster native method
  if (parent.contains(child)) {
    return true;
  }
  // then fallback to custom implementation with Shadow DOM support
  else if (isShadowRoot(rootNode)) {
    let next = child;
    do {
      if (next && parent.isSameNode(next)) {
        return true;
      }
      // $FlowFixMe: need a better way to handle this...
      next = next.parentNode || next.host;
    } while (next);
  }

  // Give up, the result is false
  return false;
}
<commit_msg>Fixed fallback<commit_after>// @flow
import { isShadowRoot } from './instanceOf';

export default function contains(parent: Element, child: Element) {
  const rootNode = child.getRootNode && child.getRootNode();

  // First, attempt with faster native method
  if (parent.contains(child)) {
    return true;
  }
  // then fallback to custom implementation with Shadow DOM support
  else if (rootNode && isShadowRoot(rootNode)) {
    let next = child;
    do {
      if (next && parent.isSameNode(next)) {
        return true;
      }
      // $FlowFixMe: need a better way to handle this...
      next = next.parentNode || next.host;
    } while (next);
  }

  // Give up, the result is false
  return false;
}
<|endoftext|>


<commit_before>/**
 * @namespace hs.query
 * @memberOf hs
 */
define(['angular', 'ol', 'map', 'core', 'angular-sanitize'],

    function(angular, ol) {
        angular.module('hs.query', ['hs.map', 'hs.core', 'ngSanitize'])
            .directive('hs.query.directiveInfopanel', ['config', function(config) {
                return {
                    templateUrl: config.infopanel_template || hsl_path + 'components/query/partials/infopanel.html'
                };
            }])
            .directive('hs.query.infovalue', ['$compile', function($compile) {

                function link(scope, element, attrs) {
                    if (attrs.attribute == 'hstemplate') return;
                    if (attrs.template) {
                        var el = angular.element('');
                        el.attr({
                            attribute: attrs.attribute,
                            value: attrs.value
                        });
                        element.append(el);
                        $compile(el)(scope);
                    } else {
                        if (attrs.value) {
                            if (attrs.value.indexOf('http') == 0) {
                                var el = angular.element('a');
                                el.attr({
                                    target: '_blank',
                                    href: attrs.value
                                });
                                el.html(attrs.value);
                                element.html(el);
                            } else {
                                element.html(attrs.value);
                            }
                        } else {
                            element.html(attrs.attribute);
                        }
                    }
                }

                return {
                    link: link
                };
            }])
            .service("hs.query.service_getwmsfeatureinfo", [
                function() {
                    this.request = function(url, info_format, coordinate) {
                        var req_url = '';
                        if (typeof use_proxy === 'undefined' || use_proxy === true) {
                            req_url = "/cgi-bin/hsproxy.cgi?toEncoding=utf-8&url=" + window.escape(url);
                        } else {
                            req_url = url;
                        }
                        var me = this;
                        $.ajax({
                            url: req_url,
                            cache: false,
                            success: function(response) {
                                me.featureInfoReceived(response, info_format, url, coordinate)
                            }
                        });
                    };

                }
            ]).service("hs.query.service_infopanel", ['$rootScope',
                function($rootScope) {
                    var me = {
                        //Used for tool specific info, such as lodexplorer region names and values
                        attributes: [],
                        //Used for getfeatureinfo. There exists a seperate group for each feature which is found at the specific coordinates
                        groups: [],
                        setAttributes: function(j) {
                            me.attributes = j;
                            $rootScope.$broadcast('infopanel.updated');
                        },
                        setGroups: function(j) {
                            me.groups = j;
                            $rootScope.$broadcast('infopanel.updated');
                        }
                    };

                    return me;
                }
            ])

        .controller('hs.query.controller', ['$scope', 'hs.map.service', 'hs.query.service_getwmsfeatureinfo', 'hs.query.service_infopanel', 'Core', '$sce',
            function($scope, OlMap, WmsGetFeatureInfo, InfoPanelService, Core, $sce) {
                var map = OlMap.map;
                var point_clicked = new ol.geom.Point([0, 0]);
                var lyr = null;

                //For vector layers use this to get the selected features
                var selector = new ol.interaction.Select({
                    condition: ol.events.condition.click
                });

                var vectors_selected = false;
                selector.getFeatures().on('add', function(e) {
                    //if (e.element.getKeys().length == 1) e.target.remove(e.element);
                    InfoPanelService.groups = []; // We can do this, because collection add is called before singleclick event
                    if (Core.mainpanel == 'measure') return;
                    $scope.$emit('infopanel.feature_selected', e.element);
                    if (e.element.get('hs_notqueryable')) return;
                    getFeatureAttributes(e.element);
                });

                selector.getFeatures().on('remove', function(e) {
                    if (Core.mainpanel == 'measure') return;
                    $scope.$emit('infopanel.feature_deselected', e.element);
                })

                var getFeatureAttributes = function(feature) {
                    var attributes = [];
                    var groups_added = false;
                    feature.getKeys().forEach(function(key) {
                        if (key == 'gid' || key == 'geometry') return;
                        if (key == "features") {
                            for (var sub_feature in feature.get('features')) {
                                var hstemplate = null;
                                if (feature.get('features')[sub_feature].get('hstemplate')) hstemplate = feature.get('features')[sub_feature].get('hstemplate');
                                var group = {
                                    name: "Feature",
                                    attributes: [],
                                    hstemplate: hstemplate
                                };
                                feature.get('features')[sub_feature].getKeys().forEach(function(key) {
                                    if (key == 'gid' || key == 'geometry') return;
                                    if ((typeof feature.get('features')[sub_feature].get(key)).toLowerCase() == "string") {
                                        group.attributes.push({
                                            name: key,
                                            value: $sce.trustAsHtml(feature.get('features')[sub_feature].get(key))
                                        });
                                    } else {
                                        group.attributes.push({
                                            name: key,
                                            value: feature.get('features')[sub_feature].get(key)
                                        });
                                    }
                                })
                                groups_added = true;
                                InfoPanelService.groups.push(group);
                            }
                        } else {
                            var obj
                            if ((typeof feature.get(key)).toLowerCase() == "string") {
                                obj = {
                                    name: key,
                                    value: $sce.trustAsHtml(feature.get(key))
                                };
                            } else {
                                obj = {
                                    name: key,
                                    value: feature.get(key)
                                };
                            }
                            attributes.push(obj)
                        };
                    })
                    Core.setMainPanel("info");
                    InfoPanelService.setAttributes(attributes);
                    if (groups_added) InfoPanelService.setGroups(InfoPanelService.groups);
                    vectors_selected = true;
                }

                WmsGetFeatureInfo.featureInfoReceived = function(response, info_format, url, coordinate) {
                    /* Maybe this will work in future OL versions
                     *
                     * var format = new ol.format.GML();
                     *  console.log(format.readFeatures(response, {}));
                     */

                    //                    var x2js = new X2JS();
                    //                  var json = x2js.xml_str2json(response);
                    var something_updated = false;
                    if (info_format.indexOf("xml") > 0 || info_format.indexOf("gml") > 0) {
                        $("featureMember", response).each(function() {
                            var feature = $(this)[0].firstChild;
                            var group = {
                                name: "Feature",
                                attributes: []
                            };

                            for (var attribute in feature.children) {
                                if (feature.children[attribute].childElementCount == 0) {
                                    group.attributes.push({
                                        "name": feature.children[attribute].localName,
                                        "value": feature.children[attribute].innerHTML
                                    });
                                    something_updated = true;
                                }
                            }
                            if (something_updated)
                                InfoPanelService.groups.push(group);
                        });
                        $("msGMLOutput", response).each(function() {
                            for (var layer_i in $(this)[0].children) {
                                var layer = $(this)[0].children[layer_i];
                                var layer_name = "";
                                if (typeof layer.children == 'undefined') continue;
                                for (var feature_i = 0; feature_i < layer.children.length; feature_i++) {
                                    var feature = layer.children[feature_i];
                                    if (feature.nodeName == "gml:name") {
                                        layer_name = feature.innerHTML;
                                    } else {
                                        var group = {
                                            name: layer_name + " Feature",
                                            attributes: []
                                        };

                                        for (var attribute in feature.children) {
                                            if (feature.children[attribute].childElementCount == 0) {
                                                group.attributes.push({
                                                    "name": feature.children[attribute].localName,
                                                    "value": feature.children[attribute].innerHTML
                                                });
                                                something_updated = true;
                                            }
                                        }
                                        if (something_updated)
                                            InfoPanelService.groups.push(group);
                                    }

                                }
                            }
                        })
                        if (something_updated) {
                            InfoPanelService.setGroups(InfoPanelService.groups);
                            Core.setMainPanel("info");
                        }
                    }

                    if (info_format.indexOf("html") > 0) {
                        if (response.length <= 1) return;
                        fillIframeAndResize($("#invisible_popup"), response);
                        createFeatureInfoPopupIfNeeded(coordinate);
                        $(popup.getElement()).popover('show');
                        fillIframeAndResize($('.getfeatureinfo_popup'), response);
                        $('.close', popup.getElement().nextElementSibling).click(function() {
                            $(popup.getElement()).popover('hide');
                        });
                        popup.setPosition(coordinate);
                        panIntoView(coordinate);
                    }
                }

                function fillIframeAndResize($iframe, response){
                    $iframe.contents().find('body').html(response);
                    $iframe.width($iframe.contents().find('body').width() + 20);
                    if($iframe.width()==20) $iframe.width(270);
                    $iframe.height($iframe.contents().find('body').height() + 20);
                }

                var popup = null;
                function createFeatureInfoPopupIfNeeded (coordinate) {
                    if ($('.getfeatureinfo_popup').length > 0) {
                        $(popup.getElement()).popover('destroy');
                        OlMap.map.removeOverlay(popup);
                    }
                    var pop_div = document.createElement('div');
                    var element, content;
                    var width = $("#invisible_popup").width();
                    var height = $("#invisible_popup").height();
                    var close_button = '<button type="button" class="close">×Close</button>';

                    document.getElementsByTagName('body')[0].appendChild(pop_div);
                    popup = new ol.Overlay({
                        element: pop_div,
                        positioning: 'bottom-center'
                    });
                    element = popup.getElement();
                    OlMap.map.addOverlay(popup);
                    content = close_button + '';
                    $(element).popover({
                        'placement': 'top',
                        'animation': true,
                        'html': true,
                        'content': content
                    });
                    $(element).popover('show');
                    popup.setPosition(coordinate);
                }

                function panIntoView(coord) {
                    var popSize = {
                            width: $("#invisible_popup").width()+40,
                            height: $("#invisible_popup").height()+70
                        },
                        mapSize = OlMap.map.getSize();

                    var tailHeight = 20,
                        tailOffsetLeft = 60,
                        tailOffsetRight = popSize.width - tailOffsetLeft,
                        popOffset = popup.getOffset(),
                        popPx = OlMap.map.getPixelFromCoordinate(coord);

                    var fromLeft = (popPx[0] - tailOffsetLeft),
                        fromRight = mapSize[0] - (popPx[0] + tailOffsetRight);

                    var fromTop = popPx[1] - popSize.height + popOffset[1],
                        fromBottom = mapSize[1] - (popPx[1] + tailHeight) - popOffset[1];

                    var center = OlMap.map.getView().getCenter(),
                        curPx = OlMap.map.getPixelFromCoordinate(center),
                        newPx = curPx.slice();

                    if (fromRight < 0) {
                        newPx[0] -= fromRight;
                    } else if (fromLeft < 0) {
                        newPx[0] += fromLeft;
                    }

                    if (fromTop < 0) {
                        newPx[1] += fromTop;
                    } else if (fromBottom < 0) {
                        newPx[1] -= fromBottom;
                    }

                    if (newPx[0] !== curPx[0] || newPx[1] !== curPx[1]) {
                        OlMap.map.getView().setCenter(OlMap.map.getCoordinateFromPixel(newPx));
                    }

                    return OlMap.map.getView().getCenter();

                };

                $scope.InfoPanelService = InfoPanelService;

                //Example: displayGroupWithAttributes({name: "My group", attributes: [{name:"foo", value:"bar"}]
                $scope.displayGroupWithAttributes = function(group) {
                    InfoPanelService.groups.push(group);
                    if (!$scope.$$phase) $scope.$digest();
                }

                $scope.showCoordinate = function(coordinate, clear) {
                    point_clicked.setCoordinates(coordinate, 'XY');
                    var groups = clear ? [] : InfoPanelService.groups;
                    groups.push({
                        name: "Coordinates",
                        attributes: [{
                            "name": "EPSG:4326",
                            "value": ol.coordinate.toStringHDMS(ol.proj.transform(coordinate, 'EPSG:3857', 'EPSG:4326'))
                        }, {
                            "name": "EPSG:3857",
                            "value": ol.coordinate.createStringXY(7)(coordinate)
                        }]
                    });
                    InfoPanelService.setGroups(groups);
                }

                var isLayerQueriable = function(layer) {
                    if (layer instanceof ol.layer.Tile &&
                        layer.getSource() instanceof ol.source.TileWMS &&
                        layer.getSource().getParams().INFO_FORMAT) return true;
                    if (layer instanceof ol.layer.Image &&
                        layer.getSource() instanceof ol.source.ImageWMS &&
                        layer.getSource().getParams().INFO_FORMAT) return true;
                    return false;
                }

                $scope.queryWmsLayer = function(layer, coordinate) {
                    if (isLayerQueriable(layer)) {
                        var source = layer.getSource();
                        var viewResolution = map.getView().getResolution();
                        var url = source.getGetFeatureInfoUrl(
                            coordinate, viewResolution, source.getProjection() ? source.getProjection() : map.getView().getProjection(), {
                                'INFO_FORMAT': source.getParams().INFO_FORMAT
                            });
                        if (url) {
                            if (console) console.log(url);
                            if (source.getParams().INFO_FORMAT.indexOf('xml') > 0 || source.getParams().INFO_FORMAT.indexOf('html') > 0 || source.getParams().INFO_FORMAT.indexOf('gml') > 0) {
                                WmsGetFeatureInfo.request(url, source.getParams().INFO_FORMAT, coordinate);
                            }
                        }
                    }
                }

                $scope.activateFeatureQueries = function() {
                    map.addInteraction(selector);
                }

                $scope.clearInfoPanel = function() {
                    InfoPanelService.attributes = [];
                    InfoPanelService.setGroups([]);
                }

                $scope.activateFeatureQueries();

                $scope.$on('infopanel.updated', function(event) {
                    if (!$scope.$$phase) $scope.$digest();
                });

                $scope.createCurrentPointLayer = function() {
                    if (lyr) map.getLayers().remove(lyr);
                    lyr = new ol.layer.Vector({
                        title: "Point clicked",
                        source: new ol.source.Vector({
                            features: [new ol.Feature({
                                geometry: point_clicked
                            })]
                        }),
                        show_in_manager: false
                    });
                    map.addLayer(lyr);
                }

                $scope.$on('layermanager.updated', function(event, data) {
                    if (data == lyr) return; //Otherwise stack overflow
                    $scope.createCurrentPointLayer();
                });

                $scope.createCurrentPointLayer();

                //For wms layers use this to get the features at mouse coordinates
                map.on('singleclick', function(evt) {
                    if (Core.mainpanel == 'measure') return;
                    $scope.showCoordinate(evt.coordinate, !vectors_selected); //Clear the previous content if no vector feature was selected, because otherwise it would already be cleared there
                    map.getLayers().forEach(function(layer) {
                        $scope.queryWmsLayer(layer, evt.coordinate)
                    });
                    $scope.$emit('map_clicked', evt);
                    vectors_selected = false;
                });
                $scope.$emit('scope_loaded', "Query");
            }
        ]);

    })
<commit_msg>fill the popup only after its loaded and shown<commit_after>/**
 * @namespace hs.query
 * @memberOf hs
 */
define(['angular', 'ol', 'map', 'core', 'angular-sanitize'],

    function(angular, ol) {
        angular.module('hs.query', ['hs.map', 'hs.core', 'ngSanitize'])
            .directive('hs.query.directiveInfopanel', ['config', function(config) {
                return {
                    templateUrl: config.infopanel_template || hsl_path + 'components/query/partials/infopanel.html'
                };
            }])
            .directive('hs.query.infovalue', ['$compile', function($compile) {

                function link(scope, element, attrs) {
                    if (attrs.attribute == 'hstemplate') return;
                    if (attrs.template) {
                        var el = angular.element('');
                        el.attr({
                            attribute: attrs.attribute,
                            value: attrs.value
                        });
                        element.append(el);
                        $compile(el)(scope);
                    } else {
                        if (attrs.value) {
                            if (attrs.value.indexOf('http') == 0) {
                                var el = angular.element('a');
                                el.attr({
                                    target: '_blank',
                                    href: attrs.value
                                });
                                el.html(attrs.value);
                                element.html(el);
                            } else {
                                element.html(attrs.value);
                            }
                        } else {
                            element.html(attrs.attribute);
                        }
                    }
                }

                return {
                    link: link
                };
            }])
            .service("hs.query.service_getwmsfeatureinfo", [
                function() {
                    this.request = function(url, info_format, coordinate) {
                        var req_url = '';
                        if (typeof use_proxy === 'undefined' || use_proxy === true) {
                            req_url = "/cgi-bin/hsproxy.cgi?toEncoding=utf-8&url=" + window.escape(url);
                        } else {
                            req_url = url;
                        }
                        var me = this;
                        $.ajax({
                            url: req_url,
                            cache: false,
                            success: function(response) {
                                me.featureInfoReceived(response, info_format, url, coordinate)
                            }
                        });
                    };

                }
            ]).service("hs.query.service_infopanel", ['$rootScope',
                function($rootScope) {
                    var me = {
                        //Used for tool specific info, such as lodexplorer region names and values
                        attributes: [],
                        //Used for getfeatureinfo. There exists a seperate group for each feature which is found at the specific coordinates
                        groups: [],
                        setAttributes: function(j) {
                            me.attributes = j;
                            $rootScope.$broadcast('infopanel.updated');
                        },
                        setGroups: function(j) {
                            me.groups = j;
                            $rootScope.$broadcast('infopanel.updated');
                        }
                    };

                    return me;
                }
            ])

        .controller('hs.query.controller', ['$scope', 'hs.map.service', 'hs.query.service_getwmsfeatureinfo', 'hs.query.service_infopanel', 'Core', '$sce',
            function($scope, OlMap, WmsGetFeatureInfo, InfoPanelService, Core, $sce) {
                var map = OlMap.map;
                var point_clicked = new ol.geom.Point([0, 0]);
                var lyr = null;

                //For vector layers use this to get the selected features
                var selector = new ol.interaction.Select({
                    condition: ol.events.condition.click
                });

                var vectors_selected = false;
                selector.getFeatures().on('add', function(e) {
                    //if (e.element.getKeys().length == 1) e.target.remove(e.element);
                    InfoPanelService.groups = []; // We can do this, because collection add is called before singleclick event
                    if (Core.mainpanel == 'measure') return;
                    $scope.$emit('infopanel.feature_selected', e.element);
                    if (e.element.get('hs_notqueryable')) return;
                    getFeatureAttributes(e.element);
                });

                selector.getFeatures().on('remove', function(e) {
                    if (Core.mainpanel == 'measure') return;
                    $scope.$emit('infopanel.feature_deselected', e.element);
                })

                var getFeatureAttributes = function(feature) {
                    var attributes = [];
                    var groups_added = false;
                    feature.getKeys().forEach(function(key) {
                        if (key == 'gid' || key == 'geometry') return;
                        if (key == "features") {
                            for (var sub_feature in feature.get('features')) {
                                var hstemplate = null;
                                if (feature.get('features')[sub_feature].get('hstemplate')) hstemplate = feature.get('features')[sub_feature].get('hstemplate');
                                var group = {
                                    name: "Feature",
                                    attributes: [],
                                    hstemplate: hstemplate
                                };
                                feature.get('features')[sub_feature].getKeys().forEach(function(key) {
                                    if (key == 'gid' || key == 'geometry') return;
                                    if ((typeof feature.get('features')[sub_feature].get(key)).toLowerCase() == "string") {
                                        group.attributes.push({
                                            name: key,
                                            value: $sce.trustAsHtml(feature.get('features')[sub_feature].get(key))
                                        });
                                    } else {
                                        group.attributes.push({
                                            name: key,
                                            value: feature.get('features')[sub_feature].get(key)
                                        });
                                    }
                                })
                                groups_added = true;
                                InfoPanelService.groups.push(group);
                            }
                        } else {
                            var obj
                            if ((typeof feature.get(key)).toLowerCase() == "string") {
                                obj = {
                                    name: key,
                                    value: $sce.trustAsHtml(feature.get(key))
                                };
                            } else {
                                obj = {
                                    name: key,
                                    value: feature.get(key)
                                };
                            }
                            attributes.push(obj)
                        };
                    })
                    Core.setMainPanel("info");
                    InfoPanelService.setAttributes(attributes);
                    if (groups_added) InfoPanelService.setGroups(InfoPanelService.groups);
                    vectors_selected = true;
                }

                WmsGetFeatureInfo.featureInfoReceived = function(response, info_format, url, coordinate) {
                    /* Maybe this will work in future OL versions
                     *
                     * var format = new ol.format.GML();
                     *  console.log(format.readFeatures(response, {}));
                     */

                    //                    var x2js = new X2JS();
                    //                  var json = x2js.xml_str2json(response);
                    var something_updated = false;
                    if (info_format.indexOf("xml") > 0 || info_format.indexOf("gml") > 0) {
                        $("featureMember", response).each(function() {
                            var feature = $(this)[0].firstChild;
                            var group = {
                                name: "Feature",
                                attributes: []
                            };

                            for (var attribute in feature.children) {
                                if (feature.children[attribute].childElementCount == 0) {
                                    group.attributes.push({
                                        "name": feature.children[attribute].localName,
                                        "value": feature.children[attribute].innerHTML
                                    });
                                    something_updated = true;
                                }
                            }
                            if (something_updated)
                                InfoPanelService.groups.push(group);
                        });
                        $("msGMLOutput", response).each(function() {
                            for (var layer_i in $(this)[0].children) {
                                var layer = $(this)[0].children[layer_i];
                                var layer_name = "";
                                if (typeof layer.children == 'undefined') continue;
                                for (var feature_i = 0; feature_i < layer.children.length; feature_i++) {
                                    var feature = layer.children[feature_i];
                                    if (feature.nodeName == "gml:name") {
                                        layer_name = feature.innerHTML;
                                    } else {
                                        var group = {
                                            name: layer_name + " Feature",
                                            attributes: []
                                        };

                                        for (var attribute in feature.children) {
                                            if (feature.children[attribute].childElementCount == 0) {
                                                group.attributes.push({
                                                    "name": feature.children[attribute].localName,
                                                    "value": feature.children[attribute].innerHTML
                                                });
                                                something_updated = true;
                                            }
                                        }
                                        if (something_updated)
                                            InfoPanelService.groups.push(group);
                                    }

                                }
                            }
                        })
                        if (something_updated) {
                            InfoPanelService.setGroups(InfoPanelService.groups);
                            Core.setMainPanel("info");
                        }
                    }

                    if (info_format.indexOf("html") > 0) {
                        if (response.length <= 1) return;
                        fillIframeAndResize($("#invisible_popup"), response);
                        createFeatureInfoPopupIfNeeded(coordinate);
                        $(popup.getElement()).popover('show');
                        $(popup.getElement()).on('shown.bs.popover', function () {
                                  fillIframeAndResize($('.getfeatureinfo_popup'), response);
                                  $('.close', popup.getElement().nextElementSibling).click(function() {
                                      $(popup.getElement()).popover('hide');
                                  });
                                  popup.setPosition(coordinate);
                                  panIntoView(coordinate);
                        })
                    }
                }

                function fillIframeAndResize($iframe, response){
                    $iframe.contents().find('body').html(response);
                    $iframe.width($iframe.contents().find('body').width() + 20);
                    if($iframe.width()==20) $iframe.width(270);
                    $iframe.height($iframe.contents().find('body').height() + 20);
                }

                var popup = null;
                function createFeatureInfoPopupIfNeeded (coordinate) {
                    if ($('.getfeatureinfo_popup').length > 0) {
                        $(popup.getElement()).popover('destroy');
                        OlMap.map.removeOverlay(popup);
                    }
                    var pop_div = document.createElement('div');
                    var element, content;
                    var width = $("#invisible_popup").width();
                    var height = $("#invisible_popup").height();
                    var close_button = '<button type="button" class="close">×Close</button>';

                    document.getElementsByTagName('body')[0].appendChild(pop_div);
                    popup = new ol.Overlay({
                        element: pop_div,
                        positioning: 'bottom-center'
                    });
                    element = popup.getElement();
                    OlMap.map.addOverlay(popup);
                    content = close_button + '';
                    $(element).popover({
                        'placement': 'top',
                        'animation': true,
                        'html': true,
                        'content': content
                    });
                    $(element).popover('show');
                    popup.setPosition(coordinate);
                }

                function panIntoView(coord) {
                    var popSize = {
                            width: $("#invisible_popup").width()+40,
                            height: $("#invisible_popup").height()+70
                        },
                        mapSize = OlMap.map.getSize();

                    var tailHeight = 20,
                        tailOffsetLeft = 60,
                        tailOffsetRight = popSize.width - tailOffsetLeft,
                        popOffset = popup.getOffset(),
                        popPx = OlMap.map.getPixelFromCoordinate(coord);

                    var fromLeft = (popPx[0] - tailOffsetLeft),
                        fromRight = mapSize[0] - (popPx[0] + tailOffsetRight);

                    var fromTop = popPx[1] - popSize.height + popOffset[1],
                        fromBottom = mapSize[1] - (popPx[1] + tailHeight) - popOffset[1];

                    var center = OlMap.map.getView().getCenter(),
                        curPx = OlMap.map.getPixelFromCoordinate(center),
                        newPx = curPx.slice();

                    if (fromRight < 0) {
                        newPx[0] -= fromRight;
                    } else if (fromLeft < 0) {
                        newPx[0] += fromLeft;
                    }

                    if (fromTop < 0) {
                        newPx[1] += fromTop;
                    } else if (fromBottom < 0) {
                        newPx[1] -= fromBottom;
                    }

                    if (newPx[0] !== curPx[0] || newPx[1] !== curPx[1]) {
                        OlMap.map.getView().setCenter(OlMap.map.getCoordinateFromPixel(newPx));
                    }

                    return OlMap.map.getView().getCenter();

                };

                $scope.InfoPanelService = InfoPanelService;

                //Example: displayGroupWithAttributes({name: "My group", attributes: [{name:"foo", value:"bar"}]
                $scope.displayGroupWithAttributes = function(group) {
                    InfoPanelService.groups.push(group);
                    if (!$scope.$$phase) $scope.$digest();
                }

                $scope.showCoordinate = function(coordinate, clear) {
                    point_clicked.setCoordinates(coordinate, 'XY');
                    var groups = clear ? [] : InfoPanelService.groups;
                    groups.push({
                        name: "Coordinates",
                        attributes: [{
                            "name": "EPSG:4326",
                            "value": ol.coordinate.toStringHDMS(ol.proj.transform(coordinate, 'EPSG:3857', 'EPSG:4326'))
                        }, {
                            "name": "EPSG:3857",
                            "value": ol.coordinate.createStringXY(7)(coordinate)
                        }]
                    });
                    InfoPanelService.setGroups(groups);
                }

                var isLayerQueriable = function(layer) {
                    if (layer instanceof ol.layer.Tile &&
                        layer.getSource() instanceof ol.source.TileWMS &&
                        layer.getSource().getParams().INFO_FORMAT) return true;
                    if (layer instanceof ol.layer.Image &&
                        layer.getSource() instanceof ol.source.ImageWMS &&
                        layer.getSource().getParams().INFO_FORMAT) return true;
                    return false;
                }

                $scope.queryWmsLayer = function(layer, coordinate) {
                    if (isLayerQueriable(layer)) {
                        var source = layer.getSource();
                        var viewResolution = map.getView().getResolution();
                        var url = source.getGetFeatureInfoUrl(
                            coordinate, viewResolution, source.getProjection() ? source.getProjection() : map.getView().getProjection(), {
                                'INFO_FORMAT': source.getParams().INFO_FORMAT
                            });
                        if (url) {
                            if (console) console.log(url);
                            if (source.getParams().INFO_FORMAT.indexOf('xml') > 0 || source.getParams().INFO_FORMAT.indexOf('html') > 0 || source.getParams().INFO_FORMAT.indexOf('gml') > 0) {
                                WmsGetFeatureInfo.request(url, source.getParams().INFO_FORMAT, coordinate);
                            }
                        }
                    }
                }

                $scope.activateFeatureQueries = function() {
                    map.addInteraction(selector);
                }

                $scope.clearInfoPanel = function() {
                    InfoPanelService.attributes = [];
                    InfoPanelService.setGroups([]);
                }

                $scope.activateFeatureQueries();

                $scope.$on('infopanel.updated', function(event) {
                    if (!$scope.$$phase) $scope.$digest();
                });

                $scope.createCurrentPointLayer = function() {
                    if (lyr) map.getLayers().remove(lyr);
                    lyr = new ol.layer.Vector({
                        title: "Point clicked",
                        source: new ol.source.Vector({
                            features: [new ol.Feature({
                                geometry: point_clicked
                            })]
                        }),
                        show_in_manager: false
                    });
                    map.addLayer(lyr);
                }

                $scope.$on('layermanager.updated', function(event, data) {
                    if (data == lyr) return; //Otherwise stack overflow
                    $scope.createCurrentPointLayer();
                });

                $scope.createCurrentPointLayer();

                //For wms layers use this to get the features at mouse coordinates
                map.on('singleclick', function(evt) {
                    if (Core.mainpanel == 'measure') return;
                    $scope.showCoordinate(evt.coordinate, !vectors_selected); //Clear the previous content if no vector feature was selected, because otherwise it would already be cleared there
                    map.getLayers().forEach(function(layer) {
                        $scope.queryWmsLayer(layer, evt.coordinate)
                    });
                    $scope.$emit('map_clicked', evt);
                    vectors_selected = false;
                });
                $scope.$emit('scope_loaded', "Query");
            }
        ]);

    })
<|endoftext|>


<commit_before>const /* Declaring imports */
	_ = require('underscore'),
	_appRoot = require('app-root-path').path,
	_io = require('./../storage/io')
	_configFile = '/config.json',
	_fn = {

		load : function(){

			if(!_.isEmpty(_config)){

				return;
			}

			_config = _defaultConfig;

			//Read config.json from root path
			var dir = _appRoot + _configFile;
			var content = _io.readSync(dir, false);

			try {

				content = JSON.parse(content);
			} catch (e) {

				console.warn('config: founded config.json, but are invalid format, using default config.');
				content = {};
			}

			if(!_.isEmpty(content)){

				_.extend(_config, content);
			}
		},
		get : function(key, def){

			this.load();
			return _config[key] === false ?  false : _config[key] || def;
		},
		setConfigFile : function(file){

			_configFile = file;
		}
	},
	_defaultConfig = {

			"name": "JSDB",
			"logger":
			{
				"debug": false,
				"webpanel": true
			},
			"server":
			{
				"port": 5732,
				"security":
				{
					"enable": false,
					"privateKey": "",
					"certificate": ""
				},

				"cluster":
				{
					"enable": false
				}
			},
			"entitlements":
			{
				"allowRoot": true
			},
			"persistance":
			{
				"maxPersistSize": "512mb"
			},
			"monitoring": true
		};

var _config;

var console = process.console || global.console;

module.exports = {

	load : _fn.load,
	get : _fn.get,
	setConfigFile : _fn.setConfigFile
};<commit_msg>remove required config.json<commit_after>const /* Declaring imports */
	_ = require('underscore'),
	_appRoot = require('app-root-path').path,
	_io = require('./../storage/io')
	_configFile = '/config.json',
	_fn = {

		load : function(){

			if(!_.isEmpty(_config)){

				return;
			}

			_config = _defaultConfig;

			//Read config.json from root path
			var dir = _appRoot + _configFile;

			if(!_io.exist(dir)){

				return;
			}

			var content = _io.readSync(dir, false);
			try {

				content = JSON.parse(content);
			} catch (e) {

				console.warn('config: founded config.json, but are invalid format, using default config.');
				content = {};
			}

			if(!_.isEmpty(content)){

				_.extend(_config, content);
			}
		},
		get : function(key, def){

			this.load();
			return _config[key] === false ?  false : _config[key] || def;
		},
		setConfigFile : function(file){

			_configFile = file;
		}
	},
	_defaultConfig = {

			"name": "JSDB",
			"logger":
			{
				"debug": false,
				"webpanel": true
			},
			"server":
			{
				"port": 5732,
				"security":
				{
					"enable": false,
					"privateKey": "",
					"certificate": ""
				},

				"cluster":
				{
					"enable": false
				}
			},
			"entitlements":
			{
				"allowRoot": true
			},
			"persistance":
			{
				"maxPersistSize": "512mb"
			},
			"monitoring": true
		};

var _config;

var console = process.console || global.console;

module.exports = {

	load : _fn.load,
	get : _fn.get,
	setConfigFile : _fn.setConfigFile
};<|endoftext|>


<commit_before>import React from 'react';
import { c } from 'ttag';
import PropTypes from 'prop-types';
import { Alert, Label, Table, TableHeader, TableBody, TableRow, Copy } from '../../components';
import { useNotifications } from '../../hooks';

const VerifySection = ({ domain }) => {
    const { createNotification } = useNotifications();
    const handleCopy = () => createNotification({ text: c('Success').t`Verify code copied to clipboard!` });
    return (
        <>
            <Alert learnMore="https://protonmail.com/support/knowledge-base/dns-records/">
                {c('Info for domain modal')
                    .t`For security reasons, we need to verify that you are the owner of $domain. Please add the following DNS TXT record to your domain. This can typically be done in the control panel of your domain name registrar.`}
            </Alert>
            <Alert type="warning">
                {c('Warning for domain modal')
                    .t`After successful verification, do not remove this TXT record as it is needed to confirm that you continue to own the domain.`}
            </Alert>
            {c('Label for domain modal').t`Please add the following TXT record:`}

                <TableHeader
                    cells={[
                        c('Header for domain modal').t`Type`,
                        c('Header for domain modal').t`Host name`,
                        c('Header for domain modal').t`Value / Data / Points to`,
                    ]}
                />
                <TableBody>
                    <TableRow
                        cells={[
                            TXT,
                            @,

                                <Copy
                                    onCopy={handleCopy}
                                    className="flex-item-noshrink pm-button--small mr0-5"
                                    value={domain.VerifyCode}
                                />{' '}

                                    {domain.VerifyCode}

,
                        ]}
                    />
                </TableBody>

            <Alert type="warning">{c('Warning for domain modal')
                .t`It can take up to a day for DNS changes to update.`}</Alert>
        </>
    );
};

VerifySection.propTypes = {
    domain: PropTypes.object.isRequired,
};

export default VerifySection;
<commit_msg>Hotfix - domain name not injected in translation<commit_after>import React from 'react';
import { c } from 'ttag';
import PropTypes from 'prop-types';
import { Alert, Label, Table, TableHeader, TableBody, TableRow, Copy } from '../../components';
import { useNotifications } from '../../hooks';

const VerifySection = ({ domain }) => {
    const { createNotification } = useNotifications();
    const handleCopy = () => createNotification({ text: c('Success').t`Verify code copied to clipboard!` });
    const domainName = domain.Name;
    return (
        <>
            <Alert learnMore="https://protonmail.com/support/knowledge-base/dns-records/">
                {c('Info for domain modal')
                    .t`For security reasons, we need to verify that you are the owner of ${domainName}. Please add the following DNS TXT record to your domain. This can typically be done in the control panel of your domain name registrar.`}
            </Alert>
            <Alert type="warning">
                {c('Warning for domain modal')
                    .t`After successful verification, do not remove this TXT record as it is needed to confirm that you continue to own the domain.`}
            </Alert>
            {c('Label for domain modal').t`Please add the following TXT record:`}

                <TableHeader
                    cells={[
                        c('Header for domain modal').t`Type`,
                        c('Header for domain modal').t`Host name`,
                        c('Header for domain modal').t`Value / Data / Points to`,
                    ]}
                />
                <TableBody>
                    <TableRow
                        cells={[
                            TXT,
                            @,

                                <Copy
                                    onCopy={handleCopy}
                                    className="flex-item-noshrink pm-button--small mr0-5"
                                    value={domain.VerifyCode}
                                />{' '}

                                    {domain.VerifyCode}

,
                        ]}
                    />
                </TableBody>

            <Alert type="warning">{c('Warning for domain modal')
                .t`It can take up to a day for DNS changes to update.`}</Alert>
        </>
    );
};

VerifySection.propTypes = {
    domain: PropTypes.object.isRequired,
};

export default VerifySection;
<|endoftext|>