diff --git a/libs/pell.js b/libs/pell.js new file mode 100644 index 00000000..9274f50d --- /dev/null +++ b/libs/pell.js @@ -0,0 +1,163 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.Pell = factory()); +}(this, (function () { 'use strict'; + + const defaultParagraphSeparatorString = 'defaultParagraphSeparator' + const formatBlock = 'formatBlock' + const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener) + const appendChild = (parent, child) => parent.appendChild(child) + const createElement = tag => document.createElement(tag) + const queryCommandState = command => document.queryCommandState(command) + const queryCommandValue = command => document.queryCommandValue(command) + const exec = (command, value = null) => document.execCommand(command, false, value) + + const defaultActions = { + bold: { + icon: 'B', + title: 'Bold', + state: () => queryCommandState('bold'), + result: () => exec('bold') + }, + italic: { + icon: 'I', + title: 'Italic', + state: () => queryCommandState('italic'), + result: () => exec('italic') + }, + underline: { + icon: 'U', + title: 'Underline', + state: () => queryCommandState('underline'), + result: () => exec('underline') + }, + strikethrough: { + icon: 'S', + title: 'Strike-through', + state: () => queryCommandState('strikeThrough'), + result: () => exec('strikeThrough') + }, + heading1: { + icon: 'H1', + title: 'Heading 1', + result: () => exec(formatBlock, '

') + }, + heading2: { + icon: 'H2', + title: 'Heading 2', + result: () => exec(formatBlock, '

') + }, + paragraph: { + icon: '¶', + title: 'Paragraph', + result: () => exec(formatBlock, '

') + }, + quote: { + icon: '“ ”', + title: 'Quote', + result: () => exec(formatBlock, '

') + }, + olist: { + icon: '#', + title: 'Ordered List', + result: () => exec('insertOrderedList') + }, + ulist: { + icon: '•', + title: 'Unordered List', + result: () => exec('insertUnorderedList') + }, + code: { + icon: '</>', + title: 'Code', + result: () => exec(formatBlock, '
')
+    },
+    line: {
+      icon: '―',
+      title: 'Horizontal Line',
+      result: () => exec('insertHorizontalRule')
+    },
+    link: {
+      icon: '🔗',
+      title: 'Link',
+      result: () => navigator.clipboard.readText().then(url => exec('createLink', url))
+    },
+    image: {
+      icon: '📷',
+      title: 'Image',
+      result: () => {
+        navigator.clipboard.readText().then(url => exec('insertImage', url))
+        exec('enableObjectResizing')
+      }
+    }
+  }
+  
+  const defaultClasses = {
+    actionbar: 'pell-actionbar',
+    button: 'pell-button',
+    content: 'pell-content',
+    selected: 'pell-button-selected'
+  }
+  
+  const init = settings => {
+    const actions = settings.actions
+      ? (
+        settings.actions.map(action => {
+          if (typeof action === 'string') return defaultActions[action]
+          else if (defaultActions[action.name]) return { ...defaultActions[action.name], ...action }
+          return action
+        })
+      )
+      : Object.keys(defaultActions).map(action => defaultActions[action])
+  
+    const classes = { ...defaultClasses, ...settings.classes }
+  
+    const defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div'
+  
+    const actionbar = createElement('div')
+    actionbar.className = classes.actionbar
+    appendChild(settings.element, actionbar)
+  
+    const content = settings.element.content = createElement('div')
+    content.contentEditable = true
+    content.className = classes.content
+    content.oninput = ({ target: { firstChild } }) => {
+      if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`)
+      else if (content.innerHTML === '
') content.innerHTML = '' + settings.onChange(content.innerHTML) + } + content.onkeydown = event => { + if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') { + setTimeout(() => exec(formatBlock, `<${defaultParagraphSeparator}>`), 0) + } + } + appendChild(settings.element, content) + + actions.forEach(action => { + const button = createElement('button') + button.className = classes.button + button.innerHTML = action.icon + button.title = action.title + button.setAttribute('type', 'button') + button.onclick = () => action.result() && content.focus() + + if (action.state) { + const handler = () => button.classList[action.state() ? 'add' : 'remove'](classes.selected) + addEventListener(content, 'keyup', handler) + addEventListener(content, 'mouseup', handler) + addEventListener(button, 'click', handler) + } + + appendChild(actionbar, button) + }) + + if (settings.styleWithCSS) exec('styleWithCSS') + exec(defaultParagraphSeparatorString, defaultParagraphSeparator) + + return settings.element + } + + return {exec, init} + +}))); \ No newline at end of file diff --git a/main.js b/main.js index 37d89739..6ca7317b 100644 --- a/main.js +++ b/main.js @@ -637,6 +637,7 @@ function calculateVoronoi(graph, points) { function markFeatures() { TIME && console.time("markFeatures"); Math.random = aleaPRNG(seed); // restart Math.random() to get the same result on heightmap edit in Erase mode + const cells = grid.cells, heights = grid.cells.h; cells.f = new Uint16Array(cells.i.length); // cell feature number cells.t = new Int8Array(cells.i.length); // cell type: 1 = land coast; -1 = water near coast; diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js index d0e37161..bf9d2620 100644 --- a/modules/cultures-generator.js +++ b/modules/cultures-generator.js @@ -74,6 +74,7 @@ ERROR && console.error("Name base is empty, default nameBases will be applied"); nameBases = Names.getNameBases(); } + cultures.forEach(c => c.base = c.base % nameBases.length); function getRandomCultures(c) { @@ -380,6 +381,7 @@ } }); } + TIME && console.timeEnd('expandCultures'); } diff --git a/modules/ui/general.js b/modules/ui/general.js index 99c51095..5e6d671f 100644 --- a/modules/ui/general.js +++ b/modules/ui/general.js @@ -100,6 +100,7 @@ function showMapTooltip(point, e, i, g) { tip(e.target.parentNode.dataset.name + ". Click to edit"); return; } + if (group === "emblems" && e.target.tagName === "use") { const parent = e.target.parentNode; const [g, type] = parent.id === "burgEmblems" ? [pack.burgs, "burg"] : @@ -115,6 +116,7 @@ function showMapTooltip(point, e, i, g) { tip(`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`); return; } + if (group === "rivers") { const river = +e.target.id.slice(5); const r = pack.rivers.find(r => r.i === river); diff --git a/modules/ui/layers.js b/modules/ui/layers.js index 8cf4a2c4..ef60400e 100644 --- a/modules/ui/layers.js +++ b/modules/ui/layers.js @@ -954,7 +954,6 @@ function getProvincesVertices() { chain.push([start, province, land]); // add starting vertex to sequence to close the path return chain; } - } function toggleGrid(event) {