Added functionality and dark mode to doctests. And other changes in maps.

This commit is contained in:
Severin Opel 2019-11-20 15:59:10 +01:00
parent 89395ba641
commit 6dcf6d38da
13 changed files with 8261 additions and 8520 deletions

View File

@ -2,12 +2,78 @@ html {
padding: 0;
font-size: 16px;
background: white;
font-family: Arial, sans-serif;
color: #000;
max-width: 932px;
font-family: 'Open Sans', Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
}
html.dark-mode {
background: rgb(51, 51, 51);
color: #ccc;
}
.dark-mode h1,
.dark-mode h2,
.dark-mode h3,
.dark-mode h4,
.dark-mode h5,
.dark-mode h6 {
color: white;
}
.title {
position: relative;
font-size: 2.5rem;
}
.description {
font-style: italic;
margin-bottom: 3rem;
}
.center {
display: block;
margin-left: auto;
margin-right: auto;
}
.doctest {
margin: 0;
padding-top: 50px;
padding-bottom: 50px;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.5);
}
.collapsed .doctest {
height: 0;
margin: 0;
padding: 0;
}
.doctest-wrapper {
overflow: hidden;
border-radius: 5px;
margin-top: 1em;
margin-bottom: 1em;
}
.doctest-collapsible-toggle {
display: flex;
align-items: center;
color: whitesmoke;
background-color: #111;
padding: 10px;
}
.doctest-section-title {
margin: 0;
margin-left: 40px;
padding: 0;
font-size: 1.3em;
}
.grayBorder {
border: 1px solid lightgray;
}
@ -34,8 +100,8 @@ body {
}
canvas {
-ms-content-zooming: none; /** Crucial for MS Edge pointer events **/
touch-action: none; /** Crucial for MS Edge? **/
-ms-content-zooming: none; /** Crucial for MS Edge pointer events **/
touch-action: none; /** Crucial for MS Edge? **/
}
#runtime-errors {

14961
dist/iwmlib.3rdparty.js vendored

File diff suppressed because one or more lines are too long

122
dist/iwmlib.js vendored
View File

@ -121,6 +121,60 @@
return stringified
}
/**
* When dealing with floating point numbers,
* floating point imprecision may occur. Therefore a simple
* equality check is not sufficient.
*
* The expectPointPrecision method compares two points by calculating their difference
* and accepting the result as far as it is lower than the defined acceptable error.
*
* @static
* @param {number} a - Number a to test.
* @param {number} b - Number b to test against.
* @param {number} accetableError - Defines the value of how much the two numbers may differ, that the test still passes.
* @memberof Doctest
*/
static expectPointPrecision(pointA, pointB, accetableError = 0.000001) {
if (Math.abs(pointA.x - pointB.x) > accetableError || Math.abs(pointA.y - pointB.y) > accetableError) {
throw new Error(
`Testing difference of Points ${this.pprint(pointA)} and ${this.pprint(
pointB
)} exceeded the acceptable error of ${accetableError} (x:${Math.abs(
pointB.x - pointA.x
)}, y: ${Math.abs(pointB.y - pointA.y)}).`
)
}
}
/**
* When dealing with floating point numbers,
* floating point imprecision may occur. Therefore a simple
* equality check is not sufficient.
*
* The expectPrecise method compares two numbers by calculating their difference
* and accepting the result as far as it is lower than the defined acceptable error.
*
* @static
* @param {number} a - Number a to test.
* @param {number} b - Number b to test against.
* @param {number} accetableError - Defines the value of how much the two numbers may differ, that the test still passes.
* @memberof Doctest
*/
static expectPrecision(a, b, accetableError = 0.000001) {
let aFloat = parseFloat(a);
let bFloat = parseFloat(b);
console.log(aFloat);
if (isNaN(aFloat) || isNaN(bFloat) || Math.abs(bFloat - aFloat) > accetableError) {
throw new Error(
`Testing difference of ${this.pprint(a)} and ${this.pprint(
b
)} exceeded the acceptable error of ${accetableError} (${Math.abs(bFloat - aFloat)}).`
)
}
}
static expect(expr, value) {
if (this.pprint(expr) != this.pprint(value)) {
//throw new Error("got `" + expr + "` but expected `" + value + "`.")
@ -181,7 +235,51 @@
let doctest = doctests[i];
let code = this.stripLeadingLines(doctest.innerHTML);
let text = this.highlight(code);
let container = document.createElement('div');
container.className = 'doctest-wrapper';
let titleParent = container;
if (doctest.hasAttribute('data-collapsible')) {
let collapsibleToggle = document.createElement('div');
let icon = document.createElement('i');
icon.className = 'material-icons';
collapsibleToggle.appendChild(icon);
collapsibleToggle.className = 'doctest-collapsible-toggle';
collapsibleToggle.style = 'min-height: 10px;';
titleParent = collapsibleToggle;
container.appendChild(collapsibleToggle);
const collapsedClass = 'collapsed';
function setToggleMode(collapse) {
if (collapse) {
container.classList.add(collapsedClass);
icon.innerText = 'arrow_drop_down';
} else {
container.classList.remove(collapsedClass);
icon.innerText = 'arrow_drop_up';
}
}
function determineToggleMode() {
setToggleMode(!container.classList.contains(collapsedClass));
}
setToggleMode(doctest.hasAttribute('data-collapsed'));
collapsibleToggle.addEventListener('click', determineToggleMode);
}
if (doctest.hasAttribute('data-title')) {
let title = document.createElement('h6');
title.innerText = doctest.getAttribute('data-title');
title.className = 'doctest-section-title';
titleParent.appendChild(title);
}
let pre = document.createElement('pre');
pre.className = 'hljs doctest';
// See http://stackoverflow.com/questions/1068280/javascript-regex-multiline-flag-doesnt-work
// let re = /Doctest\.expect\(([\s\S]*)[\,\s\S]*([\s\S]*)\)/g
let lines = text.value ? text.value.split('\n') : text.split('\n');
@ -196,7 +294,9 @@
better.push(line);
}
pre.innerHTML = better.join('\n'); // text.value.replace(re, ">>> $1\n$2")
doctest.parentNode.replaceChild(pre, doctest);
container.appendChild(pre);
doctest.parentNode.replaceChild(container, doctest);
}
}
}
@ -10962,6 +11062,7 @@
remove() {
this.button = null;
this._stopThisSpeechIfPlaying();
this.context.removeEventListener('DOMNodeRemoved', this._domWasChanged);
super.remove();
}
@ -10979,12 +11080,18 @@
context.addEventListener('DOMNodeRemoved', this._domWasChanged);
}
/**
* Don't remember why this was required - SO 20-11-2019
*/
_domWasChanged(event) {
if (this.context == null) this._stop();
else if (
this.context['lastSpeechNode'] == window.speechSynthesis['speechPluginNode'] &&
event.target == this.context
) {
if (event.target == this.context) this._stopThisSpeechIfPlaying();
}
/**
* Stops the module if it is set in the context.
*/
_stopThisSpeechIfPlaying() {
if (this.context == null || this.context['lastSpeechNode'] == window.speechSynthesis['speechPluginNode']) {
this._stop();
}
}
@ -11008,7 +11115,6 @@
* SO -17.07.19
*/
let activeNode = window.speechSynthesis['speechPluginNode'];
this._updateText();
}
@ -11050,6 +11156,8 @@
this.context.classList.add('speech-plugin-is-reading');
}
closed() {}
_cleanupText(node) {
let text = node.textContent;
text = this._removeShy(text);

1306
dist/iwmlib.pixi.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -497,6 +497,7 @@ CardPlugin.Speech = class SpeechPlugin extends CardPluginBase {
remove() {
this.button = null
this._stopThisSpeechIfPlaying()
this.context.removeEventListener('DOMNodeRemoved', this._domWasChanged)
super.remove()
}
@ -514,12 +515,18 @@ CardPlugin.Speech = class SpeechPlugin extends CardPluginBase {
context.addEventListener('DOMNodeRemoved', this._domWasChanged)
}
/**
* Don't remember why this was required - SO 20-11-2019
*/
_domWasChanged(event) {
if (this.context == null) this._stop()
else if (
this.context['lastSpeechNode'] == window.speechSynthesis['speechPluginNode'] &&
event.target == this.context
) {
if (event.target == this.context) this._stopThisSpeechIfPlaying()
}
/**
* Stops the module if it is set in the context.
*/
_stopThisSpeechIfPlaying() {
if (this.context == null || this.context['lastSpeechNode'] == window.speechSynthesis['speechPluginNode']) {
this._stop()
}
}
@ -543,7 +550,6 @@ CardPlugin.Speech = class SpeechPlugin extends CardPluginBase {
* SO -17.07.19
*/
let activeNode = window.speechSynthesis['speechPluginNode']
this._updateText()
}
@ -585,6 +591,8 @@ CardPlugin.Speech = class SpeechPlugin extends CardPluginBase {
this.context.classList.add('speech-plugin-is-reading')
}
closed() {}
_cleanupText(node) {
let text = node.textContent
text = this._removeShy(text)

View File

@ -27,6 +27,60 @@ export default class Doctest {
return stringified
}
/**
* When dealing with floating point numbers,
* floating point imprecision may occur. Therefore a simple
* equality check is not sufficient.
*
* The expectPointPrecision method compares two points by calculating their difference
* and accepting the result as far as it is lower than the defined acceptable error.
*
* @static
* @param {number} a - Number a to test.
* @param {number} b - Number b to test against.
* @param {number} accetableError - Defines the value of how much the two numbers may differ, that the test still passes.
* @memberof Doctest
*/
static expectPointPrecision(pointA, pointB, accetableError = 0.000001) {
if (Math.abs(pointA.x - pointB.x) > accetableError || Math.abs(pointA.y - pointB.y) > accetableError) {
throw new Error(
`Testing difference of Points ${this.pprint(pointA)} and ${this.pprint(
pointB
)} exceeded the acceptable error of ${accetableError} (x:${Math.abs(
pointB.x - pointA.x
)}, y: ${Math.abs(pointB.y - pointA.y)}).`
)
}
}
/**
* When dealing with floating point numbers,
* floating point imprecision may occur. Therefore a simple
* equality check is not sufficient.
*
* The expectPrecise method compares two numbers by calculating their difference
* and accepting the result as far as it is lower than the defined acceptable error.
*
* @static
* @param {number} a - Number a to test.
* @param {number} b - Number b to test against.
* @param {number} accetableError - Defines the value of how much the two numbers may differ, that the test still passes.
* @memberof Doctest
*/
static expectPrecision(a, b, accetableError = 0.000001) {
let aFloat = parseFloat(a)
let bFloat = parseFloat(b)
console.log(aFloat)
if (isNaN(aFloat) || isNaN(bFloat) || Math.abs(bFloat - aFloat) > accetableError) {
throw new Error(
`Testing difference of ${this.pprint(a)} and ${this.pprint(
b
)} exceeded the acceptable error of ${accetableError} (${Math.abs(bFloat - aFloat)}).`
)
}
}
static expect(expr, value) {
if (this.pprint(expr) != this.pprint(value)) {
//throw new Error("got `" + expr + "` but expected `" + value + "`.")
@ -87,7 +141,51 @@ export default class Doctest {
let doctest = doctests[i]
let code = this.stripLeadingLines(doctest.innerHTML)
let text = this.highlight(code)
let container = document.createElement('div')
container.className = 'doctest-wrapper'
let titleParent = container
if (doctest.hasAttribute('data-collapsible')) {
let collapsibleToggle = document.createElement('div')
let icon = document.createElement('i')
icon.className = 'material-icons'
collapsibleToggle.appendChild(icon)
collapsibleToggle.className = 'doctest-collapsible-toggle'
collapsibleToggle.style = 'min-height: 10px;'
titleParent = collapsibleToggle
container.appendChild(collapsibleToggle)
const collapsedClass = 'collapsed'
function setToggleMode(collapse) {
if (collapse) {
container.classList.add(collapsedClass)
icon.innerText = 'arrow_drop_down'
} else {
container.classList.remove(collapsedClass)
icon.innerText = 'arrow_drop_up'
}
}
function determineToggleMode() {
setToggleMode(!container.classList.contains(collapsedClass))
}
setToggleMode(doctest.hasAttribute('data-collapsed'))
collapsibleToggle.addEventListener('click', determineToggleMode)
}
if (doctest.hasAttribute('data-title')) {
let title = document.createElement('h6')
title.innerText = doctest.getAttribute('data-title')
title.className = 'doctest-section-title'
titleParent.appendChild(title)
}
let pre = document.createElement('pre')
pre.className = 'hljs doctest'
// See http://stackoverflow.com/questions/1068280/javascript-regex-multiline-flag-doesnt-work
// let re = /Doctest\.expect\(([\s\S]*)[\,\s\S]*([\s\S]*)\)/g
let lines = text.value ? text.value.split('\n') : text.split('\n')
@ -102,7 +200,9 @@ export default class Doctest {
better.push(line)
}
pre.innerHTML = better.join('\n') // text.value.replace(re, ">>> $1\n$2")
doctest.parentNode.replaceChild(pre, doctest)
container.appendChild(pre)
doctest.parentNode.replaceChild(container, doctest)
}
}
}

View File

@ -86,12 +86,20 @@ import MapApp from './maps/mapapp.js'
window.MapApp = MapApp
import { AdvancedScatterContainer, RigidContainer, CompactScatter, CoverScatter } from './maps/scatter.js'
import {
AdvancedScatterContainer,
RigidContainer,
CompactScatter,
CoverScatter,
MapObjectScatter
} from './maps/scatter.js'
window.AdvancedScatterContainer = AdvancedScatterContainer
window.RigidContainer = RigidContainer
window.CompactScatter = CompactScatter
window.CoverScatter = CoverScatter
window.MapObjectScatter = MapObjectScatter
import { GeoLayer, MapLayer } from './maps/geolayer.js'
window.GeoLayer = GeoLayer
@ -110,4 +118,5 @@ import Overlay from './maps/overlay.js'
window.Overlay = Overlay
import { MapList } from './maps/maplist.js'
window.MapList = MapList
window.MapList = MapList

View File

@ -26,7 +26,6 @@ export class GeoGraphics {
this.drawEndHandler = new EventHandler('onDrawEnd', { listeners: onDrawEnd })
this._points = null
this._position = null
this.map = null
}
clone() {
@ -92,7 +91,6 @@ export class GeoGraphics {
* Called by the containing geo layer, when the map changes.
*/
adaptTo(map) {
this.map = map
this._points = this._adaptCoordinates(map)
this._updatePosition()
this.draw()
@ -135,23 +133,21 @@ export class GeoGraphics {
this._layer = layer
}
// get map() {
// if (
// this.graphics.layer &&
// (this.graphics.layer instanceof GeoLayer || this.graphics.layer instanceof MapLayer)
// ) {
// return this.graphics.layer.map
// } else return null
// }
get map() {
let map = null
if (this.mapLayer) {
map = this.mapLayer.map
}
return map
}
// get mapLayer() {
// if (
// this.graphics.layer &&
// (this.graphics.layer instanceof GeoLayer || this.graphics.layer instanceof MapLayer)
// ) {
// return this.graphics.layer.mapLayer
// } else return null
// }
get mapLayer() {
let mapLayer = null
if (this.layer) {
mapLayer = this.layer.mapLayer
}
return mapLayer
}
/**
* Prepare draw is a private function, that prepares the graphics

View File

@ -145,6 +145,19 @@ export class GeoLayer {
} else console.warn('Tried to remove layer that was not set.', this, layer)
}
remove(graphics) {
if (graphics instanceof GeoGraphics) {
let index = this.geographics.indexOf(geographics)
if (index != -1) {
this.displayObject.removeChild(geographics)
} else {
console.error('Could not remove geographics from geolayer.', this, geographics)
}
} else {
this.displayObject.removeChild(graphics)
}
}
set parent(parent) {
this._parent = parent
}
@ -208,9 +221,10 @@ export class GeoLayer {
export class MapLayer extends GeoLayer {
constructor(
mapList,
scatterContainer,
displayObject,
{ onTransform = null, onChange = null, focus = null, zoom = null, viewport = null, mapList = null } = {}
{ onTransform = null, onChange = null, focus = null, zoom = null, viewport = null } = {}
) {
super(displayObject)
@ -231,12 +245,16 @@ export class MapLayer extends GeoLayer {
})
this.mapList = mapList
this._map = null
// //TODO Implement error handling here.
// this.maps = maps
// if (opts.map) this.placeMap(opts.map)
this.dynamicElements = new Map()
// Binds the transformed callback beforehand.
this.transformed = this.transformed.bind(this)
this.changeMap(mapList.active)
}
adapt() {
@ -258,21 +276,18 @@ export class MapLayer extends GeoLayer {
this.transformHandler.call(this)
}
clone(container = null) {
let clone = {}
for (let name of Object.keys(this.maps)) {
//console.info(this.maps[name])
clone[name] = this.maps[name].clone(container)
}
clone(scatterContainer, container = null) {
let mapList = this.mapList.clone()
container = container == null ? new PIXI.Container() : container
//console.info(this.active)
let mapLayerClone = new MapLayer(this.active, clone, container, {
let mapLayerClone = new MapLayer(mapList, scatterContainer, container, {
name: MapLayer.idx++,
viewport: this.mapview.viewport,
focus: this.mapview.focus,
zoom: this.mapview.zoom
zoom: this.mapview.zoom,
mapList
})
//mapLayerClone._map = clone['luftbild']
mapLayerClone.childrenVisibility = this.childrenVisibility
return mapLayerClone
}
@ -285,20 +300,29 @@ export class MapLayer extends GeoLayer {
* @memberof MapLayer
*/
changeMap(
map /*,
name
/* map ,
useScatterAsContainer = true // If set to false, the normal container is used. This is necessary when using submaps and the container need to be a RigidContainer.*/
) {
if (map instanceof GeoMap) {
console.log('Change map to: ', map)
let oldMap = this.map
if (oldMap) oldMap.unload()
console.log('Change map to: ', name)
let oldMap = this.map
this._map = map
this.mapList.select(name)
this.map.load()
this.scatterContainer.addChild(this.map.image)
if (oldMap) {
oldMap.unload()
oldMap.onTransform.remove(this.transformed)
}
this.mapview.apply(this.map)
let map = this.map
if (map) {
console.log('Load Map')
map.load()
console.log(this, this.scatterContainer)
this.scatterContainer.addChild(map.image)
this.mapview.apply(map)
map.image.addChild(this.displayObject)
// A geolayer's displayObject is on the parent layer.
@ -309,9 +333,9 @@ export class MapLayer extends GeoLayer {
//Call transform one time manually.
this.transformed()
this.map.onTransform.add(this.transformed.bind(this))
map.onTransform.add(this.transformed)
} else {
console.error("Could not set map, it's not of type GeoMap.", map)
console.error(`Could not change map to ${name}.`)
}
/*Logging.log(`Map change: ${key}`)
@ -368,7 +392,7 @@ export class MapLayer extends GeoLayer {
}
get map() {
return this._map
return this.mapList.map
}
/**

View File

@ -291,6 +291,10 @@ export class GeoMap {
console.error('Overload get distance in subclass.')
}
set alpha(value) {
console.error('Overload get alpha in subclass.')
}
/**
* Returns a screen point for a coordinate.
*/
@ -374,11 +378,13 @@ export class GeoMap {
* @returns {object} - Returns an object with the names as keys and the GeoMaps as value.
* @memberof GeoMap
*/
static allFromJson(json) {
static allFromJson(json, root = './') {
let error = { message: '' }
let maps = {}
if (GeoMap._validateJson(json, error)) {
for (let [mapname, data] of Object.entries(json)) {
console.log(data.tiles, data.tiles.path, root + data.tiles.path)
data.tiles.path = root + data.tiles.path
maps[mapname] = GeoMap._createMap(data)
maps[mapname].name = mapname
}
@ -563,6 +569,7 @@ export class DeepZoomMap extends GeoMap {
super.load(image, container, scatter)
console.log('LOADED: ', this.image)
if (this.debug) console.log('Loaded image: ', image, 'With options: ', this.info)
}
@ -622,7 +629,7 @@ export class DeepZoomMap extends GeoMap {
let containerCenter
if (this.frame) {
containerCenter = this.getFrame().center
containerCenter = this.getFrame().localCenter
} else {
containerCenter = {
x: this.image.parent.width / 2,
@ -691,10 +698,21 @@ export class DeepZoomMap extends GeoMap {
tint() {
let color = DeepZoomMap.tintcolors[DeepZoomMap.tintcolor++ % DeepZoomMap.tintcolors.length]
this._forEachTile(tile => {
tile.tint = color
})
}
_forEachTile(callback) {
this.image.children[0].children.forEach(tiles => {
tiles.children.forEach(tile => {
tile.tint = color
})
tiles.children.forEach(callback)
})
}
setAlpha(alpha) {
this._forEachTile(tile => {
tile.alpha = alpha
})
}
@ -769,7 +787,6 @@ export class ImageMap extends GeoMap {
* @memberof ImageMap
*/
moveTo(coordinates, zoom = null, { animate = 0 } = {}) {
if (this.image.scatter == null) {
return
}

View File

@ -178,6 +178,7 @@ export default class MapApp extends PIXIApp {
selectMap(key) {
let result = this.mapList.select(key)
console.log('Select map', key, result)
if (result && this.mapLayer) {
this.mapLayer.changeMap(this.mapList.map)
@ -287,7 +288,6 @@ export default class MapApp extends PIXIApp {
}
get map() {
console.log(this.mapList)
return this.mapList.map
}

View File

@ -1,5 +1,5 @@
export class MapList {
constructor(active, maps) {
constructor(active = null, maps = {}) {
this.maps = maps
this.active = active
@ -40,6 +40,16 @@ export class MapList {
return map
}
clone() {
let maps = {}
for (let name of Object.keys(this.maps)) {
maps[name] = this.maps[name].clone()
}
return new MapList(this.active, maps)
}
add(key, map) {
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.')
map.name = key
@ -47,15 +57,14 @@ export class MapList {
}
get map() {
console.log(this.maps, this.active)
return this.maps[this.active]
return this.maps && this.maps[this.active] ? this.maps[this.active] : null
}
next() {
let keys = Object.keys(this.maps)
let idx = keys.indexOf(this.active)
let next = idx + 1 < key.length ? keys[idx + 1] : keys[0]
let next = idx + 1 < keys.length ? keys[idx + 1] : keys[0]
console.log(keys, idx, next)
return next
}

View File

@ -89,15 +89,15 @@ export class AdvancedScatterContainer extends ScatterContainer {
let interactionManager = this.renderer.plugins.interaction
let displayObject = interactionManager.hitTest(local, this)
if (displayObject.dontBlockScatter && displayObject.parent != null) {
displayObject = interactionManager.hitTest(local, displayObject.parent)
if (displayObject != null) {
if (displayObject.dontBlockScatter && displayObject.parent != null) {
displayObject = interactionManager.hitTest(local, displayObject.parent)
}
if (displayObject.scatter != null) this.hitScatter = displayObject.scatter
if (this.claimEvents) event.claimedByScatter = this.hitScatter
}
if (displayObject != null && displayObject.scatter != null) this.hitScatter = displayObject.scatter
if (this.claimEvents) event.claimedByScatter = this.hitScatter
return this.hitScatter
}
}
@ -255,6 +255,23 @@ class AdvancedScatter extends DisplayObjectScatter {
}
export class SubmapScatter extends DisplayObjectScatter {
constructor(displayObject, renderer, opts = {}) {
/*
Currently the submaps are only working on one scale to
avoid recalculations of the shown map.
Therfore we force the scatter to not be scaleable.
*/
Object.assign(opts, {
minScale: 1,
maxScale: 1,
startScale: 1,
scalable: false
})
super(displayObject, renderer, opts)
}
get width() {
return this.displayObject.width * this.displayObject.scale.x
}