summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/components/page/page.block.vue3
-rw-r--r--src/client/components/page/page.canvas.vue29
-rw-r--r--src/client/components/page/page.vue75
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.canvas.vue45
-rw-r--r--src/client/pages/page-editor/page-editor.blocks.vue3
-rw-r--r--src/client/pages/page-editor/page-editor.vue6
-rw-r--r--src/client/scripts/aoiscript/evaluator.ts34
-rw-r--r--src/client/scripts/aoiscript/index.ts1
8 files changed, 152 insertions, 44 deletions
diff --git a/src/client/components/page/page.block.vue b/src/client/components/page/page.block.vue
index c1d046fa2e..04bbb0b858 100644
--- a/src/client/components/page/page.block.vue
+++ b/src/client/components/page/page.block.vue
@@ -17,10 +17,11 @@ import XTextarea from './page.textarea.vue';
import XPost from './page.post.vue';
import XCounter from './page.counter.vue';
import XRadioButton from './page.radio-button.vue';
+import XCanvas from './page.canvas.vue';
export default Vue.extend({
components: {
- XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton
+ XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton, XCanvas
},
props: {
value: {
diff --git a/src/client/components/page/page.canvas.vue b/src/client/components/page/page.canvas.vue
new file mode 100644
index 0000000000..edcb9cba39
--- /dev/null
+++ b/src/client/components/page/page.canvas.vue
@@ -0,0 +1,29 @@
+<template>
+<div>
+ <canvas ref="canvas" class="ysrxegms" :width="value.width" :height="value.height"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+ props: {
+ value: {
+ required: true
+ },
+ script: {
+ required: true
+ }
+ },
+ mounted() {
+ this.script.aoiScript.registerCanvas(this.value.name, this.$refs.canvas);
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.ysrxegms {
+ display: block;
+}
+</style>
diff --git a/src/client/components/page/page.vue b/src/client/components/page/page.vue
index 3723fcd3ce..99cc6e67e5 100644
--- a/src/client/components/page/page.vue
+++ b/src/client/components/page/page.vue
@@ -21,39 +21,11 @@ class Script {
public vars: Record<string, any>;
public page: Record<string, any>;
- constructor(page, aoiScript, onError, cb) {
+ constructor(page, aoiScript, onError) {
this.page = page;
this.aoiScript = aoiScript;
this.onError = onError;
-
- if (this.page.script && this.aoiScript.aiscript) {
- let ast;
- try {
- ast = parse(this.page.script);
- } catch (e) {
- console.error(e);
- /*this.$root.dialog({
- type: 'error',
- text: 'Syntax error :('
- });*/
- return;
- }
- this.aoiScript.aiscript.exec(ast).then(() => {
- this.eval();
- cb();
- }).catch(e => {
- console.error(e);
- /*this.$root.dialog({
- type: 'error',
- text: e
- });*/
- });
- } else {
- setTimeout(() => {
- this.eval();
- cb();
- }, 1);
- }
+ this.eval();
}
public eval() {
@@ -67,13 +39,15 @@ class Script {
public interpolate(str: string) {
if (str == null) return null;
return str.replace(/{(.+?)}/g, match => {
- const v = this.vars[match.slice(1, -1).trim()];
+ const v = this.vars ? this.vars[match.slice(1, -1).trim()] : null;
return v == null ? 'NULL' : v.toString();
});
}
public callAiScript(fn: string) {
- if (this.aoiScript.aiscript) this.aoiScript.aiscript.execFn(this.aoiScript.aiscript.scope.get(fn), []);
+ try {
+ if (this.aoiScript.aiscript) this.aoiScript.aiscript.execFn(this.aoiScript.aiscript.scope.get(fn), []);
+ } catch (e) {}
}
}
@@ -101,7 +75,7 @@ export default Vue.extend({
created() {
const pageVars = this.getPageVars();
- const s = new Script(this.page, new ASEvaluator(this, this.page.variables, pageVars, {
+ this.script = new Script(this.page, new ASEvaluator(this, this.page.variables, pageVars, {
randomSeed: Math.random(),
visitor: this.$store.state.i,
page: this.page,
@@ -109,15 +83,42 @@ export default Vue.extend({
enableAiScript: !this.$store.state.device.disablePagesScript
}), e => {
console.dir(e);
- }, () => {
- this.script = s;
});
- if (s.aoiScript.aiscript) s.aoiScript.aiscript.scope.opts.onUpdated = (name, value) => {
- s.eval();
+ if (this.script.aoiScript.aiscript) this.script.aoiScript.aiscript.scope.opts.onUpdated = (name, value) => {
+ this.script.eval();
};
},
+ mounted() {
+ this.$nextTick(() => {
+ if (this.script.page.script && this.script.aoiScript.aiscript) {
+ let ast;
+ try {
+ ast = parse(this.script.page.script);
+ } catch (e) {
+ console.error(e);
+ /*this.$root.dialog({
+ type: 'error',
+ text: 'Syntax error :('
+ });*/
+ return;
+ }
+ this.script.aoiScript.aiscript.exec(ast).then(() => {
+ this.script.eval();
+ }).catch(e => {
+ console.error(e);
+ /*this.$root.dialog({
+ type: 'error',
+ text: e
+ });*/
+ });
+ } else {
+ this.script.eval();
+ }
+ });
+ },
+
beforeDestroy() {
if (this.script.aoiScript.aiscript) this.script.aoiScript.aiscript.abort();
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.canvas.vue b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
new file mode 100644
index 0000000000..4977318919
--- /dev/null
+++ b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
@@ -0,0 +1,45 @@
+<template>
+<x-container @remove="() => $emit('remove')" :draggable="true">
+ <template #header><fa :icon="faPaintBrush"/> {{ $t('_pages.blocks.canvas') }}</template>
+
+ <section style="padding: 0 16px 0 16px;">
+ <mk-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._canvas.id') }}</span></mk-input>
+ <mk-input v-model="value.width" type="number"><span>{{ $t('_pages.blocks._canvas.width') }}</span><template #suffix>px</template></mk-input>
+ <mk-input v-model="value.height" type="number"><span>{{ $t('_pages.blocks._canvas.height') }}</span><template #suffix>px</template></mk-input>
+ </section>
+</x-container>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { faPaintBrush, faMagic } from '@fortawesome/free-solid-svg-icons';
+import i18n from '../../../i18n';
+import XContainer from '../page-editor.container.vue';
+import MkInput from '../../../components/ui/input.vue';
+
+export default Vue.extend({
+ i18n,
+
+ components: {
+ XContainer, MkInput
+ },
+
+ props: {
+ value: {
+ required: true
+ },
+ },
+
+ data() {
+ return {
+ faPaintBrush, faMagic
+ };
+ },
+
+ created() {
+ if (this.value.name == null) Vue.set(this.value, 'name', '');
+ if (this.value.width == null) Vue.set(this.value, 'width', 300);
+ if (this.value.height == null) Vue.set(this.value, 'height', 200);
+ },
+});
+</script>
diff --git a/src/client/pages/page-editor/page-editor.blocks.vue b/src/client/pages/page-editor/page-editor.blocks.vue
index bfc75cada4..c6ec42b8da 100644
--- a/src/client/pages/page-editor/page-editor.blocks.vue
+++ b/src/client/pages/page-editor/page-editor.blocks.vue
@@ -20,10 +20,11 @@ import XIf from './els/page-editor.el.if.vue';
import XPost from './els/page-editor.el.post.vue';
import XCounter from './els/page-editor.el.counter.vue';
import XRadioButton from './els/page-editor.el.radio-button.vue';
+import XCanvas from './els/page-editor.el.canvas.vue';
export default Vue.extend({
components: {
- XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton
+ XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton, XCanvas
},
props: {
diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue
index 6177663b71..1af8689de3 100644
--- a/src/client/pages/page-editor/page-editor.vue
+++ b/src/client/pages/page-editor/page-editor.vue
@@ -351,6 +351,7 @@ export default Vue.extend({
{ value: 'text', text: this.$t('_pages.blocks.text') },
{ value: 'image', text: this.$t('_pages.blocks.image') },
{ value: 'textarea', text: this.$t('_pages.blocks.textarea') },
+ { value: 'canvas', text: this.$t('_pages.blocks.canvas') },
]
}, {
label: this.$t('_pages.inputBlocks'),
@@ -428,8 +429,6 @@ export default Vue.extend({
margin-bottom: var(--margin);
> header {
- background: var(--faceHeader);
-
> .title {
z-index: 1;
margin: 0;
@@ -437,8 +436,7 @@ export default Vue.extend({
line-height: 42px;
font-size: 0.9em;
font-weight: bold;
- color: var(--faceHeaderText);
- box-shadow: 0 var(--lineWidth) rgba(#000, 0.07);
+ box-shadow: 0 1px rgba(#000, 0.07);
> [data-icon] {
margin-right: 6px;
diff --git a/src/client/scripts/aoiscript/evaluator.ts b/src/client/scripts/aoiscript/evaluator.ts
index cd488aeda4..e911be2caf 100644
--- a/src/client/scripts/aoiscript/evaluator.ts
+++ b/src/client/scripts/aoiscript/evaluator.ts
@@ -19,6 +19,7 @@ export class ASEvaluator {
private envVars: Record<keyof typeof envVarsDef, any>;
public aiscript?: AiScript;
private pageVarUpdatedCallback;
+ private canvases: Record<string, HTMLCanvasElement> = {};
private opts: {
randomSeed: string; visitor?: any; page?: any; url?: string;
@@ -36,6 +37,28 @@ export class ASEvaluator {
}), ...{
'MkPages:updated': values.FN_NATIVE(([callback]) => {
this.pageVarUpdatedCallback = callback;
+ }),
+ 'MkPages:get_canvas': values.FN_NATIVE(([id]) => {
+ utils.assertString(id);
+ const canvas = this.canvases[id.value];
+ const ctx = canvas.getContext('2d');
+ return values.OBJ(new Map([
+ ['clear_rect', values.FN_NATIVE(([x, y, width, height]) => { ctx.clearRect(x.value, y.value, width.value, height.value) })],
+ ['fill_rect', values.FN_NATIVE(([x, y, width, height]) => { ctx.fillRect(x.value, y.value, width.value, height.value) })],
+ ['stroke_rect', values.FN_NATIVE(([x, y, width, height]) => { ctx.strokeRect(x.value, y.value, width.value, height.value) })],
+ ['fill_text', values.FN_NATIVE(([text, x, y, width]) => { ctx.fillText(text.value, x.value, y.value, width ? width.value : undefined) })],
+ ['stroke_text', values.FN_NATIVE(([text, x, y, width]) => { ctx.strokeText(text.value, x.value, y.value, width ? width.value : undefined) })],
+ ['set_line_width', values.FN_NATIVE(([width]) => { ctx.lineWidth = width.value })],
+ ['set_font', values.FN_NATIVE(([font]) => { ctx.font = font.value })],
+ ['set_fill_style', values.FN_NATIVE(([style]) => { ctx.fillStyle = style.value })],
+ ['set_stroke_style', values.FN_NATIVE(([style]) => { ctx.strokeStyle = style.value })],
+ ['begin_path', values.FN_NATIVE(() => { ctx.beginPath() })],
+ ['close_path', values.FN_NATIVE(() => { ctx.closePath() })],
+ ['move_to', values.FN_NATIVE(([x, y]) => { ctx.moveTo(x.value, y.value) })],
+ ['line_to', values.FN_NATIVE(([x, y]) => { ctx.lineTo(x.value, y.value) })],
+ ['fill', values.FN_NATIVE(() => { ctx.fill() })],
+ ['stroke', values.FN_NATIVE(() => { ctx.stroke() })],
+ ]));
})
}}, {
in: (q) => {
@@ -73,10 +96,15 @@ export class ASEvaluator {
IS_CAT: opts.visitor ? opts.visitor.isCat : false,
SEED: opts.randomSeed ? opts.randomSeed : '',
YMD: `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`,
+ AISCRIPT_DISABLED: !this.opts.enableAiScript,
NULL: null
};
}
+ public registerCanvas(id: string, canvas: any) {
+ this.canvases[id] = canvas;
+ }
+
@autobind
public updatePageVar(name: string, value: any) {
const pageVar = this.pageVars.find(v => v.name === name);
@@ -147,7 +175,11 @@ export class ASEvaluator {
if (block.type === 'aiScriptVar') {
if (this.aiscript) {
- return utils.valToJs(this.aiscript.scope.get(block.value));
+ try {
+ return utils.valToJs(this.aiscript.scope.get(block.value));
+ } catch (e) {
+ return null;
+ }
} else {
return null;
}
diff --git a/src/client/scripts/aoiscript/index.ts b/src/client/scripts/aoiscript/index.ts
index e6de5faaae..7f34964064 100644
--- a/src/client/scripts/aoiscript/index.ts
+++ b/src/client/scripts/aoiscript/index.ts
@@ -128,6 +128,7 @@ export const envVarsDef: Record<string, Type> = {
IS_CAT: 'boolean',
SEED: null,
YMD: 'string',
+ AISCRIPT_DISABLED: 'boolean',
NULL: null,
};