diff --git a/mostrami/demo.html b/mostrami/demo.html index a190144..9f3fd1c 100644 --- a/mostrami/demo.html +++ b/mostrami/demo.html @@ -14,7 +14,7 @@ Click/Tap to exit auto mode - + diff --git a/mostrami/index.html b/mostrami/index.html index 11c1a9a..73f153c 100644 --- a/mostrami/index.html +++ b/mostrami/index.html @@ -15,7 +15,7 @@ script Launcher variable Script - rest get Script from `/resources/ecs/mostrami.txt?v=` cat now + rest get Script from `/resources/ecs/main.txt?v=` cat now run Script diff --git a/mostrami/jsonPresenter.js b/mostrami/jsonPresenter.js new file mode 100644 index 0000000..4ae46c7 --- /dev/null +++ b/mostrami/jsonPresenter.js @@ -0,0 +1,814 @@ +// JSON::Presenter + +const JSON_Presenter = (container, script) => { + + let mode = `manual`; + let clicked = false; + + const containerStyles = [ + `border`, + `background` + ]; + const defaults = [ + `fontFace`, + `fontWeight`, + `fontStyle`, + `textAlign`, + `fontColor`, + `blockLeft`, + `blockTop`, + `blockWidth`, + `blockHeight`, + `blockBackground` + ]; + + // Initialize all the blocks + const initBlocks = () => { + const defaults = script.defaults; + const blocks = script.blocks; + for (const name in blocks) { + const block = blocks[name]; + const properties = {}; + // Set up the default properties + for (const name in defaults) { + properties[name] = defaults[name]; + } + // Override with local values + for (const name in block) { + properties[name] = block[name]; + } + block.properties = properties; + block.container = container; + } + }; + + // Preload all the images + const preloadImages = () => { + for (const item in script.content) { + if (item.type == `image`) { + item.img = document.createElement(`div`); + item.img.style[`background`] = `url("${item.url}")`; + } + } + }; + + const pause = step => { + setTimeout(() => { + step.next(); + }, script.speed === `normal` ? step.duration * 1000 : 0); + }; + + const release = step => { + container.style.cursor = 'none'; + document.removeEventListener(`click`, release); + document.onkeydown = null; + step.next(); + }; + + const doManual = step => { + container.style.cursor = 'pointer'; + document.addEventListener(`click`, release); + document.onkeydown = (event) => { + switch (event.code) { + case `Space`: + case `ArrowRight`: + document.onkeydown = null; + release(step); + break; + case `ArrowLeft`: + break; + case `Enter`: + container.style.cursor = 'none'; + document.addEventListener(`click`, onClick); + mode = `auto`; + release(step); + break; + } + return true; + }; + }; + + const onClick = () => { + clicked = true; + }; + + const hold = step => { + if (mode === `manual`) { + doManual(step); + } else { + if (clicked) { + document.removeEventListener(`click`, onClick); + clicked = false; + mode = `manual`; + doManual(step); + } else { + setTimeout(() => { + step.next(); + }, script.speed === `normal` ? step.duration * 1000 : 0); + } + } + }; + + // Create a text block. + const createTextBlock = (block) => { + const container = block.container; + if (block.element) { + container.removeChild(block.element); + } + const w = container.getBoundingClientRect().width / 1000; + const h = container.getBoundingClientRect().height / 1000; + const properties = block.properties; + const element = document.createElement(`div`); + block.element = element; + element.style[`position`] = `absolute`; + element.style[`opacity`] = `0.0`; + let val = properties.blockLeft; + if (!isNaN(val)) { + val *= w; + } + element.style[`left`] = val; + val = properties.blockTop; + if (!isNaN(val)) { + val *= h; + } + element.style[`top`] = val; + val = properties.blockWidth; + if (!isNaN(val)) { + val *= w; + } + element.style[`width`] = `${val}px`; + val = properties.blockHeight; + if (!isNaN(val)) { + val *= h; + } + element.style[`height`] = `${val}px`; + element.style[`background`] = properties.blockBackground; + element.style[`border`] = properties.blockBorder; + container.appendChild(element); + val = properties.textMarginLeft; + if (!isNaN(val)) { + val *= w; + } + const marginLeft = val; + val = properties.textMarginTop; + if (!isNaN(val)) { + val *= h; + } + const marginTop = val; + const inner = document.createElement(`div`); + inner.style[`position`] = `absolute`; + inner.style[`left`] = marginLeft; + inner.style[`top`] = marginTop; + inner.style[`width`] = `calc(100% - ${marginLeft}px - ${marginLeft}px)`; + element.appendChild(inner); + element.inner = inner; + const text = document.createElement(`div`); + text.style[`font-family`] = properties.fontFamily; + val = properties.fontSize; + if (!isNaN(val)) { + val *= h; + } + text.style[`font-size`] = `${val}px`; + text.style[`font-weight`] = properties.fontWeight; + text.style[`font-style`] = properties.fontStyle; + text.style[`color`] = properties.fontColor; + text.style[`text-align`] = properties.textAlign; + inner.appendChild(text); + inner.text = text; + if (script.speed === `scan`) { + element.style.opacity = 0; + } + }; + + // Create an image block. + const createImageBlock = (block) => { + const container = block.container; + if (block.element) { + container.removeChild(block.element); + } + const w = container.getBoundingClientRect().width / 1000; + const h = container.getBoundingClientRect().height / 1000; + const properties = block.properties; + const element = document.createElement(`div`); + block.element = element; + element.style[`position`] = `absolute`; + element.style[`opacity`] = `0.0`; + let val = properties.blockLeft; + if (!isNaN(val)) { + val *= w; + } + element.style[`left`] = val; + val = properties.blockTop; + if (!isNaN(val)) { + val *= h; + } + element.style[`top`] = val; + element.style[`top`] = val; + val = properties.blockWidth; + if (!isNaN(val)) { + val *= w; + } + element.style[`width`] = `${val}px`; + val = properties.blockHeight; + if (!isNaN(val)) { + val *= h; + } + element.style[`height`] = `${val}px`; + element.style[`background`] = properties.blockBackground; + element.style[`border`] = properties.blockBorder; + element.style[`border-radius`] = properties.blockBorderRadius; + container.appendChild(element); + if (script.speed === `scan`) { + element.style.opacity = 0; + } + }; + + // Set the content of a block + const doSetContent = (spec) => { + const block = script.blocks[spec.block]; + const contentSpec = script.content[spec.content]; + if (!block) { + throw Error(`Block '${block}' cannot be found`); + } + switch (contentSpec.type) { + case `text`: + if (!block.element) { + createTextBlock(block); + } + let content = contentSpec.content; + if (Array.isArray(content)) { + content = content.join(`

`); + } + block.element.inner.text.innerHTML = content.split(`\n`).join(`
`); + break; + case `image`: + if (!block.element) { + createImageBlock(block); + } + block.element.style[`background`] = `url("${contentSpec.url}")`; + block.element.style[`background-size`] = `cover`; + break; + } + }; + + // Set the content of a block + const setcontent = step => { + if (step.blocks) { + for (const spec of step.blocks) + { + doSetContent(spec); + } + } else { + doSetContent(step); + } + step.next(); + }; + + // Show or hide a block + const doShowHide = (step, showHide) => { + if (script.speed !== `scan`) { + if (Array.isArray(step.blocks)) { + for (const name of step.blocks) + { + script.blocks[name].opacity = showHide ? `1.0` : `0.0`; + script.blocks[name].element.style[`opacity`] = script.blocks[name].opacity; + } + } else { + script.blocks[step.blocks].opacity = showHide ? `1.0` : `0.0`; + script.blocks[step.blocks].element.style[`opacity`] = script.blocks[step.blocks].opacity; + } + } + step.next(); + }; + + const show = step => { + doShowHide(step, true); + }; + + const hide = step => { + doShowHide(step, false); + }; + + // Fade up or down + const doFade = (step, upDown) => { + const stepBlocks = step.blocks; + if (script.speed === `scan`) { + if (Array.isArray(stepBlocks)) { + for (const block of stepBlocks) + { + script.blocks[block].opacity = upDown ? 1.0 : 0.0; + script.blocks[block].element.style.opacity = 0; + } + } else { + script.blocks[stepBlocks].opacity = upDown ? 1.0 : 0.0; + script.blocks[stepBlocks].element.style.opacity = 0; + } + step.next(); + } else { + const animSteps = Math.round(step.duration * 25); + const continueFlag = step.continue; + let animStep = 0; + const interval = setInterval(() => { + if (animStep < animSteps) { + const ratio = 0.5 - Math.cos(Math.PI * animStep / animSteps) / 2; + if (Array.isArray(stepBlocks)) { + let blocks = stepBlocks.length; + for (const block of stepBlocks) + { + const element = script.blocks[block].element; + element.style[`opacity`] = upDown ? ratio : 1.0 - ratio; + } + } else { + const block = script.blocks[stepBlocks]; + if (!block.element) { + clearInterval(interval); + throw Error(`I can't fade up a block with no content`); + } + block.element.style[`opacity`] = upDown ? ratio : 1.0 - ratio; + } + animStep++; + } else { + clearInterval(interval); + if (Array.isArray(stepBlocks)) { + for (const block of stepBlocks) + { + script.blocks[block].opacity = upDown ? 1.0 : 0.0; + } + } else { + script.blocks[stepBlocks].opacity = upDown ? 1.0 : 0.0; + } + if (!continueFlag) { + step.next(); + } + } + }, 40); + if (continueFlag) { + step.next(); + } + } + }; + + const fadeup = step => { + doFade(step, true); + }; + + const fadedown = step => { + doFade(step, false); + }; + + // Handle a crossfade + const crossfade = step => { + const content = script.content[step.target]; + const block = script.blocks[step.block]; + if (script.speed === `scan`) { + switch (content.type) { + case `text`: + newText = content.content; + if (Array.isArray(newText)) { + newText = newText.join(`

`); + } + newText = newText.split(`\n`).join(`
`); + block.element.inner.text.innerHTML = newText; + break; + case `image`: + block.element.style[`background`] = `url("${content.url}")`; + break; + } + step.next(); + } else { + const continueFlag = step.continue; + let element; + let newText; + switch (content.type) { + case `text`: + element = document.createElement(`div`); + element.style[`position`] = `absolute`; + element.style[`opacity`] = `0.0`; + element.style[`left`] = block.element.style[`left`]; + element.style[`top`] = block.element.style[`top`]; + element.style[`width`] = block.element.style[`width`]; + element.style[`height`] = block.element.style[`height`]; + element.style[`background`] = block.element.style[`background`] + element.style[`border`] = block.element.style[`border`] + element.style[`border-radius`] = block.element.style[`border-radius`] + container.appendChild(element); + const inner = document.createElement(`div`); + inner.style[`position`] = `absolute`; + inner.style[`left`] = block.element.inner.style[`left`]; + inner.style[`top`] = block.element.inner.style[`top`]; + inner.style[`width`] = block.element.inner.style[`width`]; + element.appendChild(inner); + const text = document.createElement(`div`); + text.style[`font-family`] = block.element.inner.text.style[`font-family`]; + text.style[`font-size`] = block.element.inner.text.style[`font-size`]; + text.style[`font-weight`] = block.element.inner.text.style[`font-weight`]; + text.style[`font-style`] = block.element.inner.text.style[`font-style`]; + text.style[`color`] = block.element.inner.text.style[`color`]; + text.style[`text-align`] = block.element.inner.text.style[`text-align`]; + inner.appendChild(text); + newText = content.content; + if (Array.isArray(newText)) { + newText = newText.join(`

`); + } + newText = newText.split(`\n`).join(`
`); + text.innerHTML = newText; + break; + case `image`: + element = document.createElement(`div`); + element.style[`position`] = `absolute`; + element.style[`opacity`] = `0.0`; + element.style[`left`] = block.element.style[`left`]; + element.style[`top`] = block.element.style[`top`]; + element.style[`width`] = block.element.style[`width`]; + element.style[`height`] = block.element.style[`height`]; + element.style[`background`] = block.element.style[`background`]; + element.style[`border`] = block.element.style[`border`]; + element.style[`border-radius`] = block.element.style[`border-radius`]; + container.appendChild(element); + element.style[`background`] = `url("${content.url}")`; + element.style[`background-size`] = `cover`; + break; + default: + throw Error(`Unknown content type: '${content.type}'`); + } + + const animSteps = Math.round(step.duration * 25); + let animStep = 0; + const interval = setInterval(() => { + if (animStep < animSteps) { + const ratio = 0.5 - Math.cos(Math.PI * animStep / animSteps) / 2; + block.element.style[`opacity`] = 1.0 - ratio; + element.style[`opacity`] = ratio; + animStep++; + } else { + clearInterval(interval); + switch (content.type) { + case `text`: + block.element.inner.text.innerHTML = newText; + break; + case `image`: + block.element.style[`background`] = `url("${content.url}")`; + block.element.style[`background-size`] = `cover`; + break; + } + block.element.style[`opacity`] = 1.0 ; + container.removeChild(element); + if (!continueFlag) { + step.next(); + } + } + }, 40); + if (continueFlag) { + step.next(); + } + } + }; + + // Compute a block size + const setComputedBlockSize = (block, target, ratio) => { + const boundingRect = block.container.getBoundingClientRect(); + const w = boundingRect.width / 1000; + const h = boundingRect.height / 1000; + let width = block.properties.blockWidth; + if (!isNaN(width)) { + width *= w; + } + let height = block.properties.blockWidth; + if (!isNaN(height)) { + height *= h; + } + let endWidth = target.properties.blockWidth; + if (!isNaN(endWidth)) { + endWidth *= w; + } + let endHeight = target.properties.blockHeight; + if (!isNaN(endHeight)) { + endHeight *= h; + } + block.element.style[`width`] = + `${width + (endWidth - width) * ratio}px`; + block.element.style[`height`] = + `${height + (endHeight - height) * ratio}px`; + }; + + // Compute a block position + const setComputedBlockPosition = (block, target, ratio) => { + const boundingRect = block.container.getBoundingClientRect(); + const w = boundingRect.width / 1000; + const h = boundingRect.height / 1000; + let left = block.properties.blockLeft; + if (!isNaN(left)) { + left *= w; + } + let top = block.properties.blockTop; + if (!isNaN(top)) { + top *= h; + } + let endLeft = target.properties.blockLeft; + if (!isNaN(endLeft)) { + endLeft *= w; + } + let endTop = target.properties.blockTop; + if (!isNaN(endTop)) { + endTop *= h; + } + block.element.style[`left`] = + left + (endLeft - left) * ratio; + block.element.style[`top`] = + top + (endTop - top) * ratio; + }; + + // Compute a font size + const setComputedFontSize = (block, target, ratio) => { + const h = Math.round(block.container.getBoundingClientRect().height) / 1000; + let size = block.properties.fontSize; + if (!isNaN(size)) { + size *= h; + } + let endSize = target.properties.fontSize; + if (!isNaN(endSize)) { + endSize *= h; + } + block.element.inner.text.style[`font-size`] = + `${size + Math.round((endSize - size) * ratio)}px`; + }; + + // Compute a font color + const setComputedFontColor = (block, target, ratio) => { + const color = block.fontColor; + const endColor = target.fontColor; + const rStart = parseInt(color.slice(1, 3), 16); + const gStart = parseInt(color.slice(3, 5), 16); + const bStart = parseInt(color.slice(5, 7), 16); + const rFinish = parseInt(endColor.slice(1, 3), 16); + const gFinish = parseInt(endColor.slice(3, 5), 16); + const bFinish = parseInt(endColor.slice(5, 7), 16); + const red = rStart + Math.round((rFinish - rStart) * ratio); + const green = gStart + Math.round((gFinish - gStart) * ratio); + const blue = bStart + Math.round((bFinish - bStart) * ratio); + const r = ("0" + red.toString(16)).slice(-2); + const g = ("0" + green.toString(16)).slice(-2); + const b = ("0" + blue.toString(16)).slice(-2); + block.element.inner.text.style[`color`] = `#${r}${g}${b}`; + }; + + // Handle a single step of a transition + const doTransitionStep = (type, block, target, ratio) => { + switch (type) { + case `block size`: + setComputedBlockSize(block, target, ratio); + break; + case `block position`: + setComputedBlockPosition(block, target, ratio); + break; + case `font size`: + setComputedFontSize(block, target, ratio); + break; + case `font color`: + setComputedFontColor(block, target, ratio); + break; + default: + throw Error(`Unknown transition type: '${type}'`); + } + }; + + // Handle a transition + const transition = step => { + const block = script.blocks[step.block]; + const stepType = step.type; + const target = script.blocks[step.target]; + if (script.speed === `scan`) { + if (Array.isArray(stepType)) { + for (const type of stepType) { + doTransitionStep(type, block, target, 1.0); + } + } else { + doTransitionStep(type, block, target, 1.0); + } + step.next(); + } else { + const animSteps = Math.round(step.duration * 25); + let animStep = 0; + const continueFlag = step.continue; + const interval = setInterval(() => { + if (animStep < animSteps) { + const ratio = 0.5 - Math.cos(Math.PI * animStep / animSteps) / 2; + if (Array.isArray(stepType)) { + for (const type of stepType) { + doTransitionStep(type, block, target, ratio); + } + } else { + doTransitionStep(type, block, target, ratio); + } + animStep++; + } else { + clearInterval(interval); + if (!continueFlag) { + step.next(); + } + } + }, 40); + if (continueFlag) { + step.next(); + } + } + }; + + // Scan the script + const scan = () => { + script.speed = `scan`; + for (const name in script.blocks) { + const block = script.blocks[name]; + if (block.element) { + container.removeChild(block.element); + block.element = null; + } + } + doStep(script.steps[0]); + }; + + // Go to a specified label + const goto = step => { + const target = script.labels[step.target]; + if (typeof target !== `undefined`) { + script.scanTarget = target; + scan(); + } else { + throw Error(`Unknown label '${step.target}`); + } + }; + + // Load a plugin action + const load = step => { + if (script.speed === `scan`) { + step.next(); + } else { + const element = document.createElement(`script`); + element.src = step.url; + element.onload = () => { + console.log(`Plugin ${element.src} loaded`); + step.next(); + }; + element.onerror = () => { + throw Error(`Can't load plugin ${step.url}`); + }; + document.head.appendChild(element); + } + }; + + const actions = { + setcontent, + show, + hide, + pause, + hold, + fadeup, + fadedown, + crossfade, + transition, + goto, + load + }; + + // Process a single step + const doStep = step => { + if (script.speed === `scan`) { + if (step.index === script.scanTarget) { + script.speed = `normal`; + for (const name in script.blocks) { + const block = script.blocks[name]; + if (block.element) { + block.element.style.opacity = block.opacity; + } + } + } + } else { + if (step.comment) { + console.log(`Step ${step.index}: ${step.comment}`); + } else { + console.log(`Step ${step.index}: ${step.action}`); + } + } + const actionName = step.action.split(` `).join(``); + let handler = actions[actionName]; + if (typeof handler === `undefined`) { + handler = JSON_Presenter.plugins[actionName]; + if (typeof handler === `undefined`) { + throw Error(`Unknown action: '${step.action}'`); + } + } + handler(step); + }; + + // Initialization + const init = () => { + container.innerHTML = ``; + document.removeEventListener(`click`, init); + if (mode === `auto`) { + document.addEventListener(`click`, onClick); + } + document.onkeydown = null; + if (script.global.title) { + document.title = script.global.title; + } + script.container.element = container; + const height = Math.round(parseFloat(container.offsetWidth) + * script.global.aspectH / script.global.aspectW); + container.style.height = `${Math.round(height)}px`; + container.style.position = `relative`; + container.style.overflow = `hidden`; + container.style.cursor = 'none'; + container.style[`background-size`] = `cover`; + for (const property of containerStyles) { + if (typeof script.container[property] !== 'undefined') { + container.style[property] = script.container[property]; + } + } + script.speed = `normal`; + script.labels = {}; + for (const [index, step] of script.steps.entries()) { + step.index = index; + step.script = script; + if (typeof step.label !== `undefined`) { + script.labels[step.label] = index; + } + if (index < script.steps.length - 1) { + step.next = () => { + const next = step.index + 1; + setTimeout(() => { + doStep(script.steps[next]); + }, 0); + } + } + else { + step.next = () => { + console.log(`Step ${index + 1}: Finished`); + container.style.cursor = 'pointer'; + } + }; + } + JSON_Presenter.plugins = {}; + initBlocks(); + preloadImages(); + doStep(script.steps[0]); + }; + + // Wait for a click/tap or a keypress to start + document.addEventListener(`click`, init); + document.onkeydown = function (event) { + if (event.code === `Enter`) { + mode = `auto`; + } + init(); + return true; + }; +}; + +window.onload = () => { + const createCORSRequest = (url) => { + let xhr = new XMLHttpRequest(); + if (`withCredentials` in xhr) { + + // Check if the XMLHttpRequest object has a "withCredentials" property. + // "withCredentials" only exists on XMLHTTPRequest2 objects. + xhr.open(`GET`, url, true); + + } else if (typeof XDomainRequest != `undefined`) { + + // Otherwise, check if XDomainRequest. + // XDomainRequest only exists in IE, and is IE's way of making CORS requests. + xhr = new XDomainRequest(); + xhr.open(`GET`, url); + + } else { + + // Otherwise, CORS is not supported by the browser. + xhr = null; + + } + return xhr; + }; + + const scriptElement = document.getElementById(`jp-script`); + if (scriptElement) { + const request = createCORSRequest(`${scriptElement.innerText}?v=${Math.floor(Date.now())}`); + if (!request) { + throw Error(`Unable to access the JSON script`); + } + + request.onload = () => { + if (200 <= request.status && request.status < 400) { + const script = JSON.parse(request.responseText); + JSON_Presenter(document.getElementById(`jp-container`), script); + } else { + throw Error(`Unable to access the JSON script`); + } + }; + + request.onerror = () => { + throw Error(`Unable to access the JSON script`); + }; + + request.send(); + } +}; diff --git a/mostrami/resources/ecs/blocks.txt b/mostrami/resources/ecs/blocks.txt index 5991f61..327c604 100644 --- a/mostrami/resources/ecs/blocks.txt +++ b/mostrami/resources/ecs/blocks.txt @@ -11,53 +11,41 @@ table Table tr TR td TD - select PropertySelect + span BlockNameSpan + input BlockNameInput input PropertyValue button EditButton - img Add img AddBlock - img Delete + img DeleteBlock a Link variable Blocks variable Block variable BlockNames variable BlockName - variable BlockProperties variable PropertyNames variable PropertyName + variable Properties variable SelectedBlock - variable Name variable Action variable N variable B + put -1 into SelectedBlock + + rest get Properties from `/resources/json/properties.json` + put the json keys of Properties into PropertyNames + on message go to Start set ready stop - + Start: if the message is `save` begin - stop + gosub to SaveSelectedBlock + stop end - set BlockProperties to array - json add `fontFamily` to BlockProperties - json add `fontSize` to BlockProperties - json add `fontWeight` to BlockProperties - json add `fontStyle` to BlockProperties - json add `fontColor` to BlockProperties - json add `textAlign` to BlockProperties - json add `textMarginLeft` to BlockProperties - json add `textMarginTop` to BlockProperties - json add `blockLeft` to BlockProperties - json add `blockTop` to BlockProperties - json add `blockWidth` to BlockProperties - json add `blockHeight` to BlockProperties - json add `blockBackground` to BlockProperties - json add `blockBorder` to BlockProperties - json add `blockBorderRadius` to BlockProperties - Restart: put property `blocks` of Presentation into Blocks put the json keys of Blocks into BlockNames @@ -79,29 +67,42 @@ Restart: begin put prompt `Name of new block:` with `new block` into BlockName set Block to object + put 0 into N + while N is less than the json count of PropertyNames + begin + put element N of PropertyNames into PropertyName + set property PropertyName of Block to property PropertyName of Properties + add 1 to N + end set property BlockName of Blocks to Block set property `blocks` of Presentation to Blocks go to Restart end put the json count of BlockNames into N + set the elements of BlockNameSpan to N + set the elements of BlockNameInput to N set the elements of EditButton to N set the elements of Editor to N - set the elements of Add to N + set the elements of DeleteBlock to N create Table in Panel if N is greater than 0 set the style of Table to `width:100%;border:1px solid black` put 0 into B while B is less than the elements of EditButton begin - put element B of BlockNames into Name + put element B of BlockNames into BlockName create TR in Table create TD in TR - set the style of TD to `width:8em;border:1px solid black` - set the content of TD to Name + set the style of TD to `width:8em;border:1px solid black;padding-left:0.5em` + create BlockNameSpan in TD + set the content of BlockNameSpan to BlockName + create BlockNameInput in TD + set the style of BlockNameInput to `width:100%;display:none` + set the content of BlockNameInput to BlockName create TD in TR set the style of TD to `border:1px solid black` index EditButton to B - index Add to B + index DeleteBlock to B index Editor to B create Row in TD set the style of Row to `display:flex` @@ -111,15 +112,16 @@ Restart: create Cell in Row set the style of Cell to `width:1.4em;text-align:center` create Link in Cell - create Add in Link - set the style of Add to `width:1em;margin-top:0.1em;display:none` - set attribute `src` of Add to `/resources/icon/plus.png` + create DeleteBlock in Link + set the style of DeleteBlock to `width:1em;margin-top:0.1em` + set attribute `src` of DeleteBlock to `/resources/icon/stop.png` create Editor in TD set the style of Editor to `display:none` add 1 to B end on click EditButton begin + gosub to SaveSelectedBlock put the index of EditButton into SelectedBlock put the text of EditButton into Action put 0 into N @@ -128,8 +130,11 @@ Restart: index EditButton to N set style `background` of EditButton to `` set the text of EditButton to `Edit` - index Add to N - set style `display` of Add to `none` + index BlockNameSpan to N + index BlockNameInput to N + set the content of BlockNameSpan to the text of BlockNameInput + set style `display` of BlockNameSpan to `inline` + set style `display` of BlockNameInput to `none` index Editor to N set style `display` of Editor to `none` add 1 to N @@ -137,59 +142,67 @@ Restart: index EditButton to SelectedBlock if Action is `Edit` begin -RestartEditor: set style `background` of EditButton to `lightgray` - set the text of EditButton to `Close` - index Add to B - set style `display` of Add to `inline-block` - index Editor to B + set the text of EditButton to `Properties` + index BlockNameSpan to SelectedBlock + index BlockNameInput to SelectedBlock + set style `display` of BlockNameSpan to `none` + set style `display` of BlockNameInput to `inline` + index Editor to SelectedBlock set style `display` of Editor to `block` clear Editor - put element B of BlockNames into BlockName + put element SelectedBlock of BlockNames into BlockName put property BlockName of Blocks into Block - put the json keys of Block into PropertyNames put the json count of PropertyNames into N - set the elements of PropertySelect to N set the elements of PropertyValue to N - set the elements of Delete to N put 0 into N while N is less than the json count of PropertyNames begin create Row in Editor set the style of Row to `display:flex` put element N of PropertyNames into PropertyName - index PropertySelect to N - create PropertySelect in Row - set the style of PropertySelect to `flex:1` - set PropertySelect from BlockProperties as PropertyName + create Cell in Row + set the style of Cell to `width:8em;padding-left:0.5em` + set the content of Cell to PropertyName index PropertyValue to N create PropertyValue in Row set the style of PropertyValue to `flex:1` set the content of PropertyValue to property PropertyName of Block - create Cell in Row - set the style of Cell to `width:1.4em;text-align:center` - create Link in Cell - create Delete in Link - set the style of Delete to `width:1em;margin-top:0.1em` - set attribute `src` of Delete to `resources/icon/stop.png` add 1 to N end - on click Delete - begin - alert `Delete ` cat the index of Delete - end - end - else - begin end + else gosub to SaveSelectedBlock end - on click Add + on click DeleteBlock begin - index BlockNames to SelectedBlock - put property BlockNames of Blocks into Block - set property ` ` of Block to ` ` - set property BlockNames of Blocks to Block + put the index of DeleteBlock into N + put element N of BlockNames into BlockName + put property `blocks` of Presentation into Blocks + json delete property BlockName of Blocks set property `blocks` of Presentation to Blocks - go to RestartEditor + go to Restart end stop + +! Save the seleced block +SaveSelectedBlock: + if SelectedBlock is -1 return + put 0 into N + while N is less than the json count of PropertyNames + begin + put element N of PropertyNames into PropertyName + index PropertyValue to N + set property PropertyName of Block to PropertyValue + add 1 to N + end + set property BlockName of Blocks to Block + index BlockNameInput to SelectedBlock + if BlockNameInput is not BlockName + begin + json delete property BlockName of Blocks + put BlockNameInput into BlockName + set element SelectedBlock of BlockNames to BlockName + set property BlockName of Blocks to Block + end + set property `blocks` of Presentation to Blocks + return \ No newline at end of file diff --git a/mostrami/resources/ecs/content.txt b/mostrami/resources/ecs/content.txt index eff72a2..1d5e54b 100644 --- a/mostrami/resources/ecs/content.txt +++ b/mostrami/resources/ecs/content.txt @@ -4,9 +4,193 @@ import div Panel and variable Presentation + div Editor + div Row + div Cell + div Title + table Table + tr TR + td TD + span ItemNameSpan + span Span + input ItemNameInput + select TypeSelect + textarea TextArea + button EditButton + img AddItem + img DeleteItem + a Link + variable Items + variable Item + variable ItemNames + variable ItemName + variable TypeNames + variable Content + variable SelectedItem + variable Action + variable N + variable B + + put -1 into SelectedItem + + set TypeNames to array + json add `text` to TypeNames + json add `image` to TypeNames + on message go to Start set ready stop Start: + if the message is `save` + begin + if SelectedItem is -1 stop + go to SaveSelectedItem + end + +Restart: + put property `content` of Presentation into Items + put the json keys of Items into ItemNames + + clear Panel + create Row in Panel + set the style of Row to `display:flex;margin-top:0.5em` + create Title in Row + set the style of Title to + `flex:1;font-size:110%;font-weight:bold;background:lightgray;text-align:center` + set the content of Title to `Content Items` + create Cell in Row + set the style of Cell to `width:1.4em;text-align:center` + create Link in Cell + create AddItem in Link + set the style of AddItem to `width:1em;margin-top:0.1em` + set attribute `src` of AddItem to `resources/icon/plus.png` + on click AddItem + begin + put prompt `Name of new content item:` with `new content` into ItemName + set Item to object + set property ItemName of Items to Item + set property `content` of Presentation to Items + go to Restart + end + + put the json count of ItemNames into N + set the elements of EditButton to N + set the elements of Editor to N + set the elements of DeleteItem to N + set the elements of TypeSelect to N + set the elements of TextArea to N + create Table in Panel + if N is greater than 0 set the style of Table to `width:100%;border:1px solid black` + put 0 into B + while B is less than the elements of EditButton + begin + put element B of ItemNames into ItemName + create TR in Table + create TD in TR + set the style of TD to `width:8em;border:1px solid black` + create ItemNameSpan in TD + set the content of ItemNameSpan to ItemName + create ItemNameInput in TD + set the style of ItemNameInput to `width:100%;display:none` + set the content of ItemNameInput to ItemName + create TD in TR + set the style of TD to `border:1px solid black` + index EditButton to B + index DeleteItem to B + index Editor to B + create Row in TD + set the style of Row to `display:flex` + create EditButton in Row + set the style of EditButton to `flex:1` + set the text of EditButton to `Edit` + create Cell in Row + set the style of Cell to `width:1.4em;text-align:center` + create Link in Cell + create DeleteItem in Link + set the style of DeleteItem to `width:1em;margin-top:0.1em` + set attribute `src` of DeleteItem to `/resources/icon/stop.png` + create Editor in TD + set the style of Editor to `display:none` + add 1 to B + end + on click EditButton + begin + put the index of EditButton into SelectedItem + put the text of EditButton into Action + put 0 into N + while N is less than the json count of ItemNames + begin + index EditButton to N + set style `background` of EditButton to `` + set the text of EditButton to `Edit` + index ItemNameSpan to N + index ItemNameInput to N + set the content of ItemNameSpan to the text of ItemNameInput + set style `display` of ItemNameSpan to `inline` + set style `display` of ItemNameInput to `none` + index Editor to N + set style `display` of Editor to `none` + add 1 to N + end + index EditButton to SelectedItem + if Action is `Edit` + begin + set style `background` of EditButton to `lightgray` + set the text of EditButton to `Content` + index ItemNameSpan to SelectedItem + index ItemNameInput to SelectedItem + index TypeSelect to SelectedItem + index TextArea to SelectedItem + set style `display` of ItemNameSpan to `none` + set style `display` of ItemNameInput to `inline` + index Editor to SelectedItem + set style `display` of Editor to `block` + clear Editor + put property ItemName of Items into Item + create Row in Editor + set the style of Row to `width:100%;display:flex` + create Span in Row + set the style of Span to `flex:1` + set the content of Span to `Type:` + create TypeSelect in Row + set the style of TypeSelect to `flex:1` + set TypeSelect from TypeNames as property `type` of Item + create TextArea in Editor + set the style of TextArea to `width:100%;max-width:100%;height:8em` + put element SelectedItem of ItemNames into ItemName + put property `content` of Item into Content + replace `%0a` with newline in Content + set the content of TextArea to Content + put property ItemName of Items into Item + end + else go to SaveSelectedItem + end + on click DeleteItem + begin + put the index of DeleteItem into N + put element N of ItemNames into ItemName + put property `content` of Presentation into Items + json delete property ItemName of Items + set property `content` of Presentation to Items + go to Restart + end + stop + +! Save the seleced content +SaveSelectedItem: + set property `type` of Item to TypeSelect + put the content of TextArea into Content + replace newline with `%0a` in Content + set property `content` of Item to Content + set property ItemName of Items to Item + index ItemNameInput to SelectedItem + if ItemNameInput is not ItemName + begin + json delete property ItemName of Items + put ItemNameInput into ItemName + set element SelectedItem of ItemNames to ItemName + set property ItemName of Items to Item + end + set property `content` of Presentation to Items stop \ No newline at end of file diff --git a/mostrami/resources/ecs/main.txt b/mostrami/resources/ecs/main.txt new file mode 100644 index 0000000..7d89664 --- /dev/null +++ b/mostrami/resources/ecs/main.txt @@ -0,0 +1,548 @@ +! Mostrami + + script Mostrami + + div Body + div Left + div Right + div Controls + div ContentDiv + div Buttons + div Tabs + div Tab + div ScriptName + div StepsPanel + div BlocksPanel + div ContentPanel + span Status + span Span + input NameEditor + button SectionButton + img New + img Open + img Save + img RunStop + img Delete + img Cycle + a Link + module StepsModule + module BlocksModule + module ContentModule + variable Mobile + variable LastSavedState + variable Content + variable Script + variable Presentation + variable Name + variable CurrentName + variable PasswordValid + variable ShowRun + variable ReadOnly + variable PasswordRequested + variable Password + variable CallStack + variable Message + variable Section + variable Item + variable CurrentScriptName + variable N + + ! The browser + div Overlay + div Scroller + div Media + div FileListing + div FileRow + div LowerPanel + button CloseButton + a FileName + variable Alpha + variable List + variable FileList + variable FileCount + variable File + variable Files + variable FileIsOpen + +! Test if site is on a static host + clear ReadOnly + rest get List from `_list/scripts` + or begin + print `Static site` + set ReadOnly + go to L2 + end +L2: + clear PasswordRequested + put empty into CallStack + history set + on restore + begin + put the json count of CallStack into N + end + if portrait + begin + if mobile set Mobile else clear Mobile + end + + set the title to `Mostrami Pro` + create Body + if Mobile + set the style of Body to `width:100%;height:100%` + else + set the style of Body to `width:100%;height:100%;display:flex` + + create Left in Body + set the style of Left to `flex:1;height:100%;display:flex;flex-direction:column;overflow:hidden` + + create Right in Body + set the style of Right to `flex:1;height:100%;display:flex;flex-direction: column` + + create Controls in Left + set the style of Controls to `flex:1;background:lightgray;padding:0 0.5em` + + create Buttons in Controls + set the style of Buttons to `width:100%;padding:0.5em 0` + + create ContentDiv in Left + set the style of ContentDiv to `width:100%;height:100%;position:relative` + + create Link in Buttons + create New in Link + set the style of New to `width:40px;margin-right:0.5em` + set attribute `src` of New to `resources/icon/new.png` + set attribute `title` of New to `New` + create Open in Link + set the style of Open to `width:40px;margin-right:0.5em` + set attribute `src` of Open to `resources/icon/open.png` + set attribute `title` of Open to `Open` + create Link in Buttons + create Save in Link + set the style of Save to `width:40px;margin-right:1.5em` + set attribute `src` of Save to `resources/icon/save.png` + set attribute `title` of Save to `Save` + create Link in Buttons + create Delete in Link + set the style of Delete to `width:40px;margin-right:1.5em` + set attribute `src` of Delete to `resources/icon/trash.png` + set attribute `title` of Delete to `Delete` + create Link in Buttons + create RunStop in Link + set the style of RunStop to `width:40px;margin-right:1.5em` + set attribute `src` of RunStop to `resources/icon/run.png` + set attribute `title` of RunStop to `Run` + create Link in Buttons + create Cycle in Link + set the style of Cycle to `width:40px` + set attribute `src` of Cycle to `resources/icon/cycle.png` + set attribute `title` of Cycle to `Cycle screens` + + create Status in Buttons + if Mobile set the style of Status to `height:1em` + else set the style of Status to `float:right;margin:0.5em 0 0 0;color:green` + + create ScriptName in Controls + set the style of ScriptName to `display:flex` + if Mobile set style `display` of ScriptName to `none` + create Span in ScriptName + set the style of Span to `flex:15` + set the content of Span to `Script name: ` + create NameEditor in ScriptName + set the style of NameEditor to `flex:85;display:inline-block` + + create Tabs in Controls + set the style of Tabs to `width:100%;padding:0.5em 0;text-align:center` + put the width of Tabs into N + divide N by 3 + set the elements of SectionButton to 3 + create Tab in Tabs + set the style of Tab to `display:inline-block;width:` cat N cat `px` + index SectionButton to 0 + create SectionButton in Tab + set the style of SectionButton to `width:100%` + set the content of SectionButton to `Steps` + create StepsPanel in ContentDiv + set the style of StepsPanel to `position:absolute;left:0;top:0;width:100%;height:100%;` + create Tab in Tabs + set the style of Tab to `display:inline-block;width:` cat N cat `px` + index SectionButton to 1 + create SectionButton in Tab + set the style of SectionButton to `width:100%` + set the content of SectionButton to `Blocks` + create BlocksPanel in ContentDiv + set the style of BlocksPanel to `position:absolute;left:0;top:0;width:100%;height:100%;` + create Tab in Tabs + set the style of Tab to `display:inline-block;width:` cat N cat `px` + index SectionButton to 2 + create SectionButton in Tab + set the style of SectionButton to `width:100%` + set the content of SectionButton to `Content` + create ContentPanel in ContentDiv + set the style of ContentPanel to `position:absolute;left:0;top:0;width:100%;height:100%;` + create Tab in Tabs + set the style of Tab to `display:inline-block;width:` cat N cat `px` + + put 0 into Section + index SectionButton to 0 + on click SectionButton + begin + put the index of SectionButton into N + gosub to SelectSection + end + +! Create the file browser + create Overlay in Body + set the style of Overlay to + `position:absolute;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.0);display:none` + + create Media in Overlay + set style of Media to `display:none;width:100%;height:100%;text-align:center` + + create FileListing in Media + set the style of FileListing to + `display:none;width:50%;height:75%;margin:auto;background-color:white;` + cat `padding:2em 2em 3em 2em;text-align:center;position: absolute;top: 50%;left: 50%;` + cat `transform: translateX(-50%) translateY(-50%)` + + create Scroller in FileListing + set the style of Scroller to `height:100%;overflow:scroll;text-align:left` + + create LowerPanel in FileListing + + create CloseButton in LowerPanel + set the style of CloseButton to `margin-left:2em` + set the text of CloseButton to `Close` + + put empty into LastSavedState + + on click New + begin +! gosub to StopTestModule + if Presentation is not LastSavedState + begin + if confirm `Content has changed. Do you want to save it?` + begin + put the content of NameEditor into Name + if Name is empty + begin + gosub to SetStatusRed + set the content of Status to `No script name has been given` + go to ResetStatus + end + if PasswordValid rest post Presentation to `_save/json/` cat Name + else put Presentation into storage as CurrentName + end + end + clear FileIsOpen + set the content of NameEditor to empty + put empty into CurrentScriptName + gosub to CreateNewPresentation + put Presentation into LastSavedState + gosub to UpdateCurrentSection + end + + on click Open go to DoOpen + + on click Save + begin + gosub to SaveChanges + gosub to GetPassword + put the content of NameEditor into Name + if Name is empty + begin + gosub to SetStatusRed + set the content of Status to `No script name has been given` + go to ResetStatus + end + if the position of `.json` in Name is -1 put Name cat `.json` into Name + replace ` ` with `_` in Name + set the content of NameEditor to Name + if Name is not CurrentScriptName put empty into LastSavedState + if Presentation is not LastSavedState + begin + if PasswordValid rest post Presentation to `_save/json/` cat Name + else put Presentation into storage as Name + put Presentation into LastSavedState + gosub to SetStatusGreen + set the content of Status to `Presentation '` cat Name cat `' saved` + put Name into CurrentScriptName + fork to ResetStatus + end + else + begin + gosub to SetStatusGreen + set the content of Status to `Nothing has changed` + fork to ResetStatus + end + end + + on click Delete + begin + gosub to GetPassword + put the content of NameEditor into Name + if Name is empty + begin + gosub to SetStatusGreen + set the content of Status to `Nothing to delete` + go to ResetStatus + end + if confirm `Are you sure you want to delete "` cat Name cat `"?` + begin + if PasswordValid rest post to `_delete/json/` cat Name + else remove Name from storage + gosub to SetStatusGreen + set the content of Status to `Script "` cat Name cat `" deleted` + set the content of NameEditor to empty + put empty into CurrentScriptName + put empty into Presentation + put Presentation into LastSavedState + go to ResetStatus + end + end + + gosub to CreateNewPresentation + put Presentation into LastSavedState + gosub to SetupSteps + gosub to SetupBlocks + gosub to SetupContent + put 0 into N + gosub to SelectSection + stop + +CreateNewPresentation: + set Presentation to object + set Item to object + set property `blocks` of Presentation to Item + set property `content` of Presentation to Item + set Item to array + set property `steps` of Presentation to Item + return + +SetupSteps: + if StepsModule is not running + begin + rest get Script from `/resources/ecs/steps.txt` + run Script with StepsPanel and Presentation as StepsModule + end + return + +SetupBlocks: + if BlocksModule is not running + begin + rest get Script from `/resources/ecs/blocks.txt` + run Script with BlocksPanel and Presentation as BlocksModule + end + return + +SetupContent: + if ContentModule is not running + begin + rest get Script from `/resources/ecs/content.txt` + run Script with ContentPanel and Presentation as ContentModule + end + return + +! Select one of the 3 sections +SelectSection: + gosub to SaveChanges + index SectionButton to Section + set style `background` of SectionButton to `` + put N into Section + index SectionButton to Section + set style `background` of SectionButton to `lightgray` + +! Update the current section +UpdateCurrentSection: + set style `display` of StepsPanel to `none` + set style `display` of BlocksPanel to `none` + set style `display` of ContentPanel to `none` + if Section is 0 + begin + set style `display` of StepsPanel to `block` + send to StepsModule + end + else if Section is 1 + begin + set style `display` of BlocksPanel to `block` + send to BlocksModule + end + else if Section is 2 + begin + set style `display` of ContentPanel to `block` + send to ContentModule + end + return + +SaveChanges: + send `save` to StepsModule + send `save` to BlocksModule + send `save` to ContentModule + return + +DoOpen: +! gosub to StopTestModule + gosub to GetPassword + + clear FileIsOpen + if Presentation is not LastSavedState + begin + if confirm `Content has changed. Do you want to save it?` + begin + if PasswordValid rest post Content to `_save/json/` cat Name + else put Content into storage as Name + end + end + + ! Animate the background + set style `display` of Overlay to `block` + put 0 into Alpha + while Alpha is less than 8 + begin + set style `background-color` of Overlay to `rgba(0,0,0,0.` cat Alpha cat `)` + wait 4 ticks + add 1 to Alpha + end + wait 10 ticks + + ! Make the browser panel visible + set style `display` of Media to `block` + set style `display` of FileListing to `inline-block` + + ! Fill the browser with content from the server + if PasswordValid + begin + rest get Files from `_list/json` + put the json count of Files into FileCount + put empty into Content + put 0 into N + while N is less than FileCount + begin + put element N of Files into Item + if property `type` of Item is `json` json add property `name` of Item to Content + add 1 to N + end + end + else + begin + get Files from storage + put the json count of Files into FileCount + put empty into Content + put 0 into N + while N is less than FileCount + begin + put element N of Files into Item + if left 1 of Item is not `.` json add Item to Content + add 1 to N + end + end + json sort Content + put empty into FileList + put the json count of Content into FileCount + set the elements of File to FileCount + set the elements of FileName to FileCount + ! Add a row for each file + put 0 into N + while N is less than FileCount + begin + index File to N + index FileName to N + put `
` + cat `
` into File + replace `INDEX` with N in File + if N is even replace `ODDEVEN` with `ec-even` in File + else replace `ODDEVEN` with `ec-odd` in File + put FileList cat File into FileList + add 1 to N + end + + set the content of Scroller to FileList + ! Add the document names + put 0 into N + while N is less than FileCount + begin + index File to N + index FileName to N + put element N of Content into File + attach FileRow to `ec-file-row-` cat N + attach FileName to `ec-file-name-` cat N + set the content of FileName to File + if N is even set style `background` of FileRow to `lightgray` + on click FileName go to SelectFile + add 1 to N + end + on click CloseButton + begin + put LastSavedState into Content + go to CloseBrowser + end + stop + +SelectFile: + index File to the index of FileName + set the content of NameEditor to File + put File into CurrentScriptName + if PasswordValid rest get Presentation from `/resources/json/` cat File + else get Presentation from storage as File + put Presentation into LastSavedState + gosub to UpdateCurrentSection + set the content of Status to `Presentation '` cat File cat `' loaded` + fork to ResetStatus + set ShowRun + +CloseBrowser: + set style `background-color` of Overlay to `rgba(0,0,0,0.0)` + set style `display` of Overlay to `none` + set style `display` of Media to `none` + stop + +SetStatusRed: + set style `color` of Status to `red` + return + +SetStatusGreen: + set style `color` of Status to `green` + return + +ResetStatus: + wait 2 + set the content of Status to `` + stop + +GetPassword: + if ReadOnly + begin + clear PasswordValid + return + end + if the hostname is `localhost` go to SetPasswordValid + if the hostname is `127.0.0.1` go to SetPasswordValid + + if not PasswordRequested + begin + set PasswordRequested + if hostname is `localhost` goto SetPasswordValid + if hostname is `127.0.0.1` goto SetPasswordValid + get Password from storage as `.password` + if Password is empty + begin + put `Please provide the admin password` cat newline + cat `or click OK to use private browser storage.` into Message + put prompt Message with `` into Password + end + rest get PasswordValid from `_verify/` cat Password + or begin + clear PasswordValid + return + end + if PasswordValid is `yes` + begin + put Password into storage as `.password` + set PasswordValid + end + else clear PasswordValid + end + return + +SetPasswordValid: + set PasswordValid + return \ No newline at end of file diff --git a/mostrami/resources/ecs/scripted.txt b/mostrami/resources/ecs/scripted.txt index 8c82325..79bbbcc 100644 --- a/mostrami/resources/ecs/scripted.txt +++ b/mostrami/resources/ecs/scripted.txt @@ -168,21 +168,23 @@ stop DoOpen: + if FileIsOpen codemirror close ContentEditor put the content of ContentEditor into Content if Content is not Current begin if confirm `Content has changed. Do you want to save it?` begin rest post Content to `_save/ecs/' cat CurrentName + codemirror attach to ContentEditor + codemirror set content of ContentEditor to Content + end + else + begin + codemirror attach to ContentEditor + codemirror set content of ContentEditor to Content + stop end - else stop -! begin -! codemirror attach to ContentEditor -! codemirror set content of ContentEditor to Content -! stop -! end end - if FileIsOpen codemirror close ContentEditor ! Animate the background set style `display` of Overlay to `block` diff --git a/mostrami/resources/ecs/steps.txt b/mostrami/resources/ecs/steps.txt index 88d581b..1509a88 100644 --- a/mostrami/resources/ecs/steps.txt +++ b/mostrami/resources/ecs/steps.txt @@ -26,6 +26,7 @@ select TargetSelect select TransitionSelect select ContinueSelect + input InitInput input TitleInput input LabelInput input DurationInput @@ -39,11 +40,10 @@ variable Steps variable SelectedStep variable CurrentStep - variable PreviousStep - variable Comment variable Action variable Blocks variable Block + variable Contents variable Content variable Target variable Types @@ -51,16 +51,20 @@ variable URL variable Names variable ActionNames + variable InitProperties + variable InitProperty variable BlockNames variable ContentNames variable TransitionTypes variable TransitionType variable ContinueTypes variable TrueFalse - variable Array + variable Response variable N variable B - + + put -1 into SelectedStep + create StepsPanel in Panel set the style of StepsPanel to `flex:1;overflow-y:scroll;margin-top:0.5em` @@ -71,7 +75,12 @@ Start: if the message is `save` begin - stop + if SelectedStep is not -1 + begin + gosub to SaveCurrentStep + put -1 into SelectedStep + end + stop end set style `display` of Panel to `flex` @@ -91,16 +100,14 @@ Restart: create AddStep in Link set the style of AddStep to `width:1em;margin-top:0.1em` set attribute `src` of AddStep to `resources/icon/plus.png` - on click AddStep - begin - gosub to AddNewStep - go to Restart - end + on click AddStep go to AddNewStep put property `steps` of Presentation into Steps put the json count of Steps into N set the elements of StepButton to N set the elements of DeleteStep to N + set the elements of TitleInput to N + set the elements of LabelInput to N set the elements of Editor to N put 0 into N while N is less than the elements of StepButton @@ -127,7 +134,7 @@ Restart: end on click StepButton begin - gosub to SaveCurrentStep + gosub to SaveCurrentStep put SelectedStep into N put the index of StepButton into SelectedStep if SelectedStep is N @@ -136,12 +143,14 @@ Restart: set style `display` of Editor to `none` index StepButton to N set style `background` of StepButton to `` + gosub to SaveCurrentStep put -1 into SelectedStep end else begin if N is not -1 begin + gosub to SaveCurrentStep put 0 into N while N is less than the elements of StepButton begin @@ -170,6 +179,7 @@ Restart: ! Set up the fixed lists set ActionNames to array + json add `init` to ActionNames json add `set content` to ActionNames json add `show` to ActionNames json add `hide` to ActionNames @@ -181,6 +191,11 @@ Restart: json add `transition` to ActionNames json add `goto` to ActionNames json add `load` to ActionNames + set InitProperties to array + json add `title` to InitProperties + json add `aspect ratio` to InitProperties + json add `background` to InitProperties + json add `border` to InitProperties set TransitionTypes to array json add `block position` to TransitionTypes json add `block size` to TransitionTypes @@ -190,29 +205,59 @@ Restart: json add `true` to ContinueTypes json add `false` to ContinueTypes - put -1 into SelectedStep put property `steps` of Presentation into Steps stop -! Add a new step after the current one +! Add a new step to the list AddNewStep: + gosub to CreateNewStep + json add CurrentStep to Steps + go to ASA3 + +! Add a step before the current one +AddStepBefore: + put the json count of Steps into N + set CurrentStep to object + json add CurrentStep to Steps + go to ASA2 + +! Add a step after the current one +AddStepAfter: + put the json count of Steps into N + set CurrentStep to object + json add CurrentStep to Steps + add 1 to SelectedStep +ASA2: + while N is greater than SelectedStep + begin + take 1 from N + put element N of Steps into CurrentStep + add 1 to N + set element N of Steps to CurrentStep + take 1 from N + end + gosub to CreateNewStep + set element N of Steps to CurrentStep +ASA3: + put -1 into SelectedStep + set property `steps` of Presentation to Steps + go to Restart + +CreateNewStep: set CurrentStep to object set property `comment` of CurrentStep to `New step` set property `action` of CurrentStep to `` set property `label` of CurrentStep to `` - json add CurrentStep to Steps - set property `steps` of Presentation to Steps - return + return ! Edit a single step EditStep: put property `blocks` of Presentation into Blocks - put property `content` of Presentation into Content + put property `content` of Presentation into Contents put the json keys of Blocks into BlockNames - put the json keys of Content into ContentNames + put the json keys of Contents into ContentNames put element SelectedStep of Steps into CurrentStep - put CurrentStep into PreviousStep clear Editor create Table in Editor @@ -224,18 +269,29 @@ ReloadStepEditor: set the style of TD to `width:6em` set the content of TD to `Title:` create TD in TR + index TitleInput to SelectedStep create TitleInput in TD set the style of TitleInput to `width:100%` set the content of TitleInput to property `comment` of CurrentStep create TR in Table create TD in TR + set the style of TD to `width:6em` + set the content of TD to `Label:` + create TD in TR + index LabelInput to SelectedStep + create LabelInput in TD + set the style of LabelInput to `width:100%` + set the content of LabelInput to property `label` of CurrentStep + create TR in Table + create TD in TR set the content of TD to `Action:` create TD in TR create ActionSelect in TD set the style of ActionSelect to `width:100%` put property `action` of CurrentStep into Action set ActionSelect from ActionNames as Action - if Action is `set content` gosub to EditSetContent + if Action is `init` gosub to EditInit + else if Action is `set content` gosub to EditSetContent else if Action is `show` gosub to EditShow else if Action is `hide` gosub to EditHide else if Action is `pause` gosub to EditPause @@ -251,22 +307,23 @@ ReloadStepEditor: end on change ActionSelect begin - if confirm `Changing the action will remove all step data. Do you want to continue?` + set Response + if Action is not empty + begin + if not confirm `Changing the action will remove all step data. Do you want to continue?` + clear Response + end + if Response begin - put the selected item in ActionSelect into Action + index StepButton to SelectedStep + set property `comment` of CurrentStep to TitleInput + put the selected item in ActionSelect into Action gosub to CreateNewAction clear Table go to ReloadStepEditor end + else set ActionSelect from ActionNames as property `action` of CurrentStep end - create TR in Table - create TD in TR - set the style of TD to `width:6em` - set the content of TD to `Label:` - create TD in TR - create LabelInput in TD - set the style of LabelInput to `width:100%` - set the content of LabelInput to property `label` of CurrentStep ! Add the buttons below the table create TR in Table @@ -275,21 +332,41 @@ ReloadStepEditor: create InsertBefore in TD set the style of InsertBefore to `width:50%` set the text of InsertBefore to `Add Step Before` - on click InsertBefore - begin - alert `Insert Before` - end + on click InsertBefore go to AddStepBefore create InsertAfter in TD set the style of InsertAfter to `width:50%` set the text of InsertAfter to `Add Step After` - on click InsertAfter - begin - alert `Insert After` - end + on click InsertAfter go to AddStepAfter stop ! Editors for the various Action types +EditInit: + create TR in Table + create TD in TR + set the content of TD to `Properties:` + create TD in TR + create BlockTable in TD + set the style of BlockTable to `width:100%;border:1px solid gray` + put the json count of InitProperties into N + set the elements of InitInput to N + put 0 into B + while B is less than N + begin + put element B of InitProperties into InitProperty + create BlockRow in BlockTable + set the style of BlockRow to `display:flex` + create Cell in BlockRow + set the style of Cell to `width:6em;padding-left:0.2em` + set the content of Cell to InitProperty + index InitInput to B + create InitInput in BlockRow + set the style of InitInput to `flex:1` + set the content of InitInput to property InitProperty of CurrentStep + add 1 to B + end + return + EditSetContent: create TR in Table create TD in TR @@ -353,17 +430,19 @@ EditSetContent: begin put the index of BlockSelect into N put element N of Blocks into Block - set property `block` of Block to the selected item in BlockSelect + set property `block` of Block to BlockSelect set element N of Blocks to Block set property `blocks` of CurrentStep to Blocks + set element SelectedStep of Steps to CurrentStep end on change ContentSelect begin put the index of ContentSelect into N put element N of Blocks into Block - set property `content` of Block to the selected item in ContentSelect + set property `content` of Block to ContentSelect set element N of Blocks to Block set property `blocks` of CurrentStep to Blocks + set element SelectedStep of Steps to CurrentStep end on click DeleteIcon begin @@ -600,10 +679,25 @@ EditContinue: ! Create a new action with a full set of empty properties CreateNewAction: - set property `comment` of CurrentStep to `New step: ` cat Action + index TitleInput to SelectedStep + if the text of TitleInput is empty + begin + set property `comment` of CurrentStep to `New step: ` cat Action + end set property `action` of CurrentStep to Action set property `label` of CurrentStep to empty - if Action is `set content` + if Action is `init` + begin + put 0 into N + while N is less than the json count of InitProperties + begin + put element N of InitProperties into InitProperty + set property InitProperty of CurrentStep to empty + add 1 to N + end + set property `aspect ratio` of CurrentStep to `WW:HH` + end + else if Action is `set content` begin put property `blocks` of CurrentStep into Blocks if Blocks is empty @@ -700,9 +794,27 @@ CreateNewAction: ! Save the current step SaveCurrentStep: -print CurrentStep + if SelectedStep is -1 return + index TitleInput to SelectedStep + put element SelectedStep of Steps into CurrentStep + set property `comment` of CurrentStep to the text of TitleInput + index StepButton to SelectedStep + set the text of StepButton to the content of TitleInput + index LabelInput to SelectedStep + set property `label` of CurrentStep to the content of LabelInput put property `action` of CurrentStep into Action - if Action is `set content` + if Action is `init` + begin + put 0 into N + while N is less than the json count of InitProperties + begin + put element N of InitProperties into InitProperty + index InitInput to N + set property InitProperty of CurrentStep to the content of InitInput + add 1 to N + end + end + else if Action is `set content` begin put property `blocks` of CurrentStep into Blocks if Blocks is empty diff --git a/mostrami/resources/json/properties.json b/mostrami/resources/json/properties.json new file mode 100644 index 0000000..86e2c24 --- /dev/null +++ b/mostrami/resources/json/properties.json @@ -0,0 +1,17 @@ +{ + "left": 0, + "top": 0, + "width": "100%", + "height": "100%", + "background": "", + "border": "", + "borderRadius": "", + "fontFamily": "", + "fontSize": 30, + "fontWeight": "", + "fontStyle": "", + "fontColor": "", + "textAlign": "", + "textMarginLeft": "", + "textMarginTop": "" +} diff --git a/mostrami/resources/json/test.json b/mostrami/resources/json/test.json index f713a09..e097c68 100644 --- a/mostrami/resources/json/test.json +++ b/mostrami/resources/json/test.json @@ -1 +1 @@ -{"global":{},"container":{},"defaults":{},"blocks":{},"content":{},"steps":[{"comment":"Wait for user input","action":"hold","duration":0,"label":""}]} \ No newline at end of file +{"blocks":{"title":{"background":"","border":"","borderRadius":"","fontColor":"","fontFamily":"","fontSize":30,"fontStyle":"","fontWeight":"","height":"100%","left":0,"textAlign":"","textMarginLeft":"","textMarginTop":"","top":0,"width":"100%"}},"content":{"main title":{"content":"This is my title","type":"text"}},"steps":[{"comment":"Init","action":"init","label":"","aspect ratio":"16:9","background":"yellow","border":"","title":"My first presentation"},{"comment":"Setup main title","action":"set content","label":"","blocks":[{"block":"title","content":"main title"}]}]} \ No newline at end of file