This commit is contained in:
Azgaar 2020-06-23 02:29:39 +03:00
parent f14ca97fb4
commit f424f242da
7 changed files with 231 additions and 212 deletions

View file

@ -2089,9 +2089,10 @@ svg.button {
padding: 1.2em;
border: solid 1px #000;
font-size: 1.2em;
z-index: 1000;
}
#promptTest {
#promptText {
padding: 0 0 .6em 0;
font-weight: bold;
font-family: sans-serif;
@ -2125,6 +2126,44 @@ svg.button {
stroke-width: 0;
}
.pell {
border: 1px solid hsla(0,0%,4%,.1)
}
.pell,.pell-content {
box-sizing: border-box
}
.pell-content {
height: 14em;
outline: 0;
overflow-y: auto;
padding: .6em;
font-family: Copperplate, monospace;
background-color: #fff;
border: 1px solid #dedede;
}
.pell-actionbar {
background-color: #fff;
border: 1px solid #dedede;
border-bottom: 0;
}
.pell-button {
background-color: transparent;
border: none;
cursor: pointer;
height: 30px;
outline: 0;
width: 30px;
vertical-align: bottom
}
.pell-button-selected {
background-color: #f0f0f0
}
#debug {
font-size: 1px;
opacity: .8;

View file

@ -3211,10 +3211,7 @@
<span>Object name: </span>
<input id="notesName" data-tip="Type to change object name" autocorrect="off" spellcheck="false" style="width: 17em">
</div>
<div>
<span>Note:</span><br>
<textarea id="notesText" rows="7" data-tip="Type object description" placeholder="Type object description"></textarea>
</div>
<div id="notesText" data-tip="Type and style object description" style="padding: .4em 0"></div>
<div>
<button id="notesFocus" data-tip="Focus on selected object" class="icon-target"></button>
<button id="notesPin" data-tip="Toggle notes box dispay: hide or do not hide the box on mouse move" class="icon-pin"></button>
@ -3673,7 +3670,7 @@
<div id="prompt" style="display: none" class="dialog">
<form id="promptForm">
<div id="promptTest"></div>
<div id="promptText"></div>
<input id="promptInput" type="number" step=.01 placeholder="type value" autocomplete="off" required>
<button type="submit">Confirm</button>
<button type="button" id="promptCancel" formnovalidate>Cancel</button>
@ -3763,5 +3760,5 @@
<script defer src="modules/ui/3d.js"></script>
<script defer src="libs/rgbquant.js"></script>
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
<script defer src="libs/publicstorage.js"></script>
<script defer src="libs/pell.js"></script>
</body>

163
libs/pell.js Normal file
View file

@ -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>B</b>',
title: 'Bold',
state: () => queryCommandState('bold'),
result: () => exec('bold')
},
italic: {
icon: '<i>I</i>',
title: 'Italic',
state: () => queryCommandState('italic'),
result: () => exec('italic')
},
underline: {
icon: '<u>U</u>',
title: 'Underline',
state: () => queryCommandState('underline'),
result: () => exec('underline')
},
strikethrough: {
icon: '<strike>S</strike>',
title: 'Strike-through',
state: () => queryCommandState('strikeThrough'),
result: () => exec('strikeThrough')
},
heading1: {
icon: '<b>H<sub>1</sub></b>',
title: 'Heading 1',
result: () => exec(formatBlock, '<h1>')
},
heading2: {
icon: '<b>H<sub>2</sub></b>',
title: 'Heading 2',
result: () => exec(formatBlock, '<h2>')
},
paragraph: {
icon: '&#182;',
title: 'Paragraph',
result: () => exec(formatBlock, '<p>')
},
quote: {
icon: '&#8220; &#8221;',
title: 'Quote',
result: () => exec(formatBlock, '<blockquote>')
},
olist: {
icon: '&#35;',
title: 'Ordered List',
result: () => exec('insertOrderedList')
},
ulist: {
icon: '&#8226;',
title: 'Unordered List',
result: () => exec('insertUnorderedList')
},
code: {
icon: '&lt;/&gt;',
title: 'Code',
result: () => exec(formatBlock, '<pre>')
},
line: {
icon: '&#8213;',
title: 'Horizontal Line',
result: () => exec('insertHorizontalRule')
},
link: {
icon: '&#128279;',
title: 'Link',
result: () => navigator.clipboard.readText().then(url => exec('createLink', url))
},
image: {
icon: '&#128247;',
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 === '<br>') 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}
})));

View file

@ -1,188 +0,0 @@
// https://github.com/Highbrainer/jeevaneo-js-publicstorage. MIT
const IFRAME_ROOT_URL = "https://publicstorage.neocities.org/shared-iframe.html";
class PublicStorageAccess {
constructor ({debug=false}={}) {
this.uid = this.uniqueId();
this.debug=debug;
}
uniqueId() {
function chr4(){
return Math.random().toString(16).slice(-4);
}
return chr4() + chr4() +
'-' + chr4() +
'-' + chr4() +
'-' + chr4() +
'-' + chr4() + chr4() + chr4();
}
_debug(msg) {
if(this.debug) {
if(console && console.debug) {
console.debug(msg);
}
}
}
prepareIFrame() {
const that = this;
const iframe = document.createElement("iframe");
iframe.id=that.uid;
iframe.src=IFRAME_ROOT_URL + "?uid=init-"+that.uid;
iframe.style.cssText="display:none;";
return new Promise(function(resolve, reject) {
window.addEventListener('message', function mafunc(tkn) {
if (IFRAME_ROOT_URL.indexOf(tkn.origin)<0) {
return;
}
try {
const packet = JSON.parse(tkn.data);
if(!(packet.frameId === "init-" + that.uid)) {
// ignore
return;
}
if(packet.ready) {
resolve(iframe);
}
} catch (e) {
reject(tkn.data);
}
window.removeEventListener('message', mafunc);
});
onLoadThen().then(() => {
document.getElementsByTagName("body")[0].appendChild(iframe);
});
setTimeout(()=>reject(`Request ${that.uid} TIMEOUTED!`), 20000);
});
}
access(access, prop, value = null, level = "local") {
if(!(access === "get" || access === "set" || access === "delete")) {
throw new Error("access can only be 'set', 'get' or 'delete' - not '" + access + "'");
}
if (!prop) {
throw new Error("Prop name is mandatory");
}
if(!(level === "local" || level === "session")) {
throw new Error("level can only be 'session' or 'local' - not '" + access + "'");
}
const that = this;
const promise = new Promise(function(resolve, reject) {
that.prepareIFrame().then(iframe => {
window.addEventListener('message', function mafunc(tkn) {
if (IFRAME_ROOT_URL.indexOf(tkn.origin)<0) {
return;
}
try {
var packet = JSON.parse(tkn.data);
if(!(packet.uid === that.uid)) {
// ignore
return;
}
resolve(packet.body);
} catch (e) {
reject(tkn.data);
}
iframe.parentNode.removeChild(iframe);
window.removeEventListener('message', mafunc);
});
const request = {uid:that.uid, access:access, prop:prop, value:value, level:level};
iframe.contentWindow.postMessage(JSON.stringify(request), '*');
setTimeout(()=>reject("TIMEOUTED!"), 20000);
});
});
return promise;
}
}
function __createDebugIFrame() {
onLoadThen().then(function(){
const iframe = document.createElement("iframe");
iframe.src=IFRAME_ROOT_URL + "?for-debug-only";
iframe.style.cssText="display:none;";
document.getElementsByTagName("body")[0].appendChild(iframe);
});
}
class PublicStorage {
constructor({debug=false}={}) {
if(debug) {
__createDebugIFrame();
}
}
sessionGet(prop) {
return new PublicStorageAccess().access("get", prop, null, "session");
}
sessionSet(prop, value) {
return new PublicStorageAccess().access("set", prop, value, "session");
}
sessionUnset(prop) {
return new PublicStorageAccess().access("delete", prop, null, "session");
}
localGet(prop) {
return new PublicStorageAccess().access("get", prop, null, "local");
}
localSet(prop, value) {
return new PublicStorageAccess().access("set", prop, value, "local");
}
localUnset(prop) {
return new PublicStorageAccess().access("delete", prop, null, "local");
}
get(prop) {
return this.localGet(prop);
}
set(prop, value) {
return this.localSet(prop, value);
}
unset(prop) {
return this.localUnset(prop);
}
}
const publicstorage = new PublicStorage();
function onLoadThen() {
return new Promise(function(resolve, reject) {
if (window) {
if(document.getElementsByTagName('BODY')[0]) {
resolve();
} else {
registerOnLoad(function unregisterme() {
resolve();
window.removeEventListener('load', unregisterme);
});
}
}
setTimeout(function() {reject(new Error("Timeout waiting for onLoad!"));}, 10000);
});
}
function registerOnLoad(lambda) {
if (window.addEventListener) {
window.addEventListener('load', lambda);
} else if (window.attachEvent) {
window.attachEvent('onload', lambda);
}
}
onLoadThen().then(() => window.publicstorage = publicstorage).catch(e=> console.error(e));
//export {onLoadThen, PublicStorage, publicstorage as default}
// module.exports = onLoadThen();

View file

@ -937,7 +937,7 @@ function parseLoadedData(data) {
if (version < 1.3) {
// v 1.3 added global options object
const winds = options.slice(); // previostly wnd was saved in settings[19]
const winds = options.slice(); // previostly wind was saved in settings[19]
const year = rand(100, 2000);
const era = Names.getBaseShort(P(.7) ? 1 : rand(nameBases.length)) + " Era";
const eraShort = era[0] + "E";
@ -1047,6 +1047,11 @@ function parseLoadedData(data) {
}()
changeMapSize();
// set options
yearInput.value = options.year;
eraInput.value = options.era;
if (window.restoreDefaultEvents) restoreDefaultEvents();
focusOn(); // based on searchParams focus on point, cell or burg
invokeActiveZooming();

View file

@ -5,6 +5,18 @@ function editNotes(id, name) {
select.options.length = 0;
for (const note of notes) {select.options.add(new Option(note.id, note.id));}
// initiate pell (html editor)
const editor = Pell.init({
element: document.getElementById("notesText"),
onChange: html => {
const id = document.getElementById("notesSelect").value;
const note = notes.find(note => note.id === id);
if (!note) return;
note.legend = html;
showNote(note);
}
});
// select an object
if (notes.length || id) {
if (!id) id = notes[0].id;
@ -17,18 +29,18 @@ function editNotes(id, name) {
}
select.value = id;
notesName.value = note.name;
notesText.value = note.legend;
editor.content.innerHTML = note.legend;
showNote(note);
} else {
const value = "There are no added notes. Click on element (e.g. label) and add a free text note";
document.getElementById("notesText").value = value;
editor.content.innerHTML = "There are no added notes. Click on element (e.g. label) and add a free text note";
document.getElementById("notesName").value = "";
}
// open a dialog
$("#notesEditor").dialog({
title: "Notes Editor", minWidth: "40em",
position: {my: "center", at: "center", of: "svg"}
position: {my: "center", at: "center", of: "svg"},
close: () => notesText.innerHTML = ""
});
if (modules.editNotes) return;
@ -37,7 +49,6 @@ function editNotes(id, name) {
// add listeners
document.getElementById("notesSelect").addEventListener("change", changeObject);
document.getElementById("notesName").addEventListener("input", changeName);
document.getElementById("notesText").addEventListener("input", changeText);
document.getElementById("notesPin").addEventListener("click", () => options.pinNotes = !options.pinNotes);
document.getElementById("notesFocus").addEventListener("click", validateHighlightElement);
document.getElementById("notesDownload").addEventListener("click", downloadLegends);
@ -55,7 +66,7 @@ function editNotes(id, name) {
const note = notes.find(note => note.id === this.value);
if (!note) return;
notesName.value = note.name;
notesText.value = note.legend;
editor.content.innerHTML = note.legend;
}
function changeName() {
@ -66,14 +77,6 @@ function editNotes(id, name) {
showNote(note);
}
function changeText() {
const id = document.getElementById("notesSelect").value;
const note = notes.find(note => note.id === id);
if (!note) return;
note.legend = this.value;
showNote(note);
}
function validateHighlightElement() {
const select = document.getElementById("notesSelect");
const element = document.getElementById(select.value);

View file

@ -602,10 +602,10 @@ void function() {
const prompt = document.getElementById("prompt");
const form = prompt.querySelector("#promptForm");
window.prompt = function(promptTest = "Please provide an input", options = {default:1, step:.01, min:0, max:100}, callback) {
window.prompt = function(promptText = "Please provide an input", options = {default:1, step:.01, min:0, max:100}, callback) {
if (options.default === undefined) {console.error("Prompt: options object does not have default value defined"); return;}
const input = prompt.querySelector("#promptInput");
prompt.querySelector("#promptTest").innerHTML = promptTest;
prompt.querySelector("#promptText").innerHTML = promptText;
const type = typeof(options.default) === "number" ? "number" : "text";
input.type = type;
if (options.step !== undefined) input.step = options.step;