From 6fa5f11376523355ed825a56eebcf8b07dc08986 Mon Sep 17 00:00:00 2001 From: Paul Varache Date: Mon, 4 Feb 2019 09:08:08 +0000 Subject: [PATCH] More types --- src/@types/flow-down/flow-down.d.ts | 9 ++ src/@types/types.d.ts | 1 + .../actions/{challenge.js => challenge.ts} | 23 +++--- src/app/lib/actions/{editor.js => editor.ts} | 11 +-- src/app/lib/actions/mode.js | 29 ------- src/app/lib/actions/{parts.js => parts.ts} | 19 +++-- .../lib/app-modules/{global.js => global.ts} | 7 +- .../lib/app-modules/{index.js => index.ts} | 15 +++- src/app/lib/app-modules/{loop.js => loop.ts} | 7 +- .../lib/app-modules/{loops.js => loops.ts} | 0 src/app/lib/app-modules/{math.js => math.ts} | 10 +-- src/app/lib/app-modules/motion.js | 7 -- src/app/lib/editor/dialogs/base.js | 13 ++- src/app/lib/editor/plugin.ts | 4 +- src/app/lib/editor/plugin/receiver.js | 25 ------ src/app/lib/editor/plugin/receiver.ts | 23 ++++++ src/app/lib/editor/{runner.js => runner.ts} | 24 ++++-- src/app/lib/i18n/index.js | 73 ----------------- src/app/lib/i18n/index.ts | 82 +++++++++++++++++++ src/app/lib/icon/{index.js => index.ts} | 12 +-- src/app/lib/log/{index.js => index.ts} | 15 ++-- src/app/lib/log/{level.js => level.ts} | 0 src/app/lib/vm.ts | 2 +- 23 files changed, 214 insertions(+), 197 deletions(-) create mode 100644 src/@types/types.d.ts rename src/app/lib/actions/{challenge.js => challenge.ts} (93%) rename src/app/lib/actions/{editor.js => editor.ts} (86%) delete mode 100644 src/app/lib/actions/mode.js rename src/app/lib/actions/{parts.js => parts.ts} (82%) rename src/app/lib/app-modules/{global.js => global.ts} (87%) rename src/app/lib/app-modules/{index.js => index.ts} (52%) rename src/app/lib/app-modules/{loop.js => loop.ts} (80%) rename src/app/lib/app-modules/{loops.js => loops.ts} (100%) rename src/app/lib/app-modules/{math.js => math.ts} (83%) delete mode 100644 src/app/lib/app-modules/motion.js delete mode 100644 src/app/lib/editor/plugin/receiver.js create mode 100644 src/app/lib/editor/plugin/receiver.ts rename src/app/lib/editor/{runner.js => runner.ts} (78%) delete mode 100644 src/app/lib/i18n/index.js create mode 100644 src/app/lib/i18n/index.ts rename src/app/lib/icon/{index.js => index.ts} (55%) rename src/app/lib/log/{index.js => index.ts} (78%) rename src/app/lib/log/{level.js => level.ts} (100%) diff --git a/src/@types/flow-down/flow-down.d.ts b/src/@types/flow-down/flow-down.d.ts index 4bbb556bf..addac387d 100644 --- a/src/@types/flow-down/flow-down.d.ts +++ b/src/@types/flow-down/flow-down.d.ts @@ -8,12 +8,21 @@ declare module 'flow-down/flow-down.js' { type ProviderMixin = >(parent : B) => B; + interface IActionEvent { + type : string; + [K : string] : any; + } + interface Store { StateReceiver : any; ReceiverBehavior : any; StateProvider : ProviderMixin; id : number; providerElement : HTMLElement; + dispatch(event : IActionEvent) : void; + getState() : any; + addMutator(mutator : (this : any, action : IActionEvent) => void) : void; + appStateComponent : PolymerElement; } export function createStore(initialState : object) : Store; } \ No newline at end of file diff --git a/src/@types/types.d.ts b/src/@types/types.d.ts new file mode 100644 index 000000000..63a4845e6 --- /dev/null +++ b/src/@types/types.d.ts @@ -0,0 +1 @@ +interface Type extends Function { new (...args : any[]) : T; } \ No newline at end of file diff --git a/src/app/lib/actions/challenge.js b/src/app/lib/actions/challenge.ts similarity index 93% rename from src/app/lib/actions/challenge.js rename to src/app/lib/actions/challenge.ts index f2506bed6..7f0d10c08 100644 --- a/src/app/lib/actions/challenge.js +++ b/src/app/lib/actions/challenge.ts @@ -1,4 +1,5 @@ import Store from '../store.js'; +import FlowDown from 'flow-down/flow-down.js'; const CONSTANTS = [ 'LOAD_CHALLENGE', @@ -21,10 +22,10 @@ const CONSTANTS = [ ]; const CHALLENGE_TYPES = Store.types(CONSTANTS); -const BANNER_ICONS = { +const BANNER_ICONS : any= { default: '/assets/avatar/judoka-face.svg', }; -const ChallengeActions = (store) => { +const ChallengeActions = (store : FlowDown.Store) => { function getProgress() { const state = store.getState(); if (!state.steps) { @@ -169,16 +170,16 @@ const ChallengeActions = (store) => { }); return { - load(challenge) { + load(challenge : any) { store.dispatch({ type: CHALLENGE_TYPES.LOAD_CHALLENGE, challenge }); }, - loadVariables(variables) { + loadVariables(variables : any[]) { store.dispatch({ type: CHALLENGE_TYPES.LOAD_VARIABLES, variables }); }, - updateStepIndex(index) { + updateStepIndex(index : number) { store.dispatch({ type: CHALLENGE_TYPES.UPDATE_STEP_INDEX, index }); }, - updateSteps(steps) { + updateSteps(steps : any[]) { store.dispatch({ type: CHALLENGE_TYPES.UPDATE_STEPS, steps }); }, disableBannerButton() { @@ -187,7 +188,7 @@ const ChallengeActions = (store) => { enableBannerButton() { store.dispatch({ type: CHALLENGE_TYPES.ENABLE_BANNER_BUTTON }); }, - updateBannerState(banner = {}) { + updateBannerState(banner : any = {}) { store.dispatch({ type: CHALLENGE_TYPES.UPDATE_BANNER_STATE, state: banner }); }, enableLockdown() { @@ -196,19 +197,19 @@ const ChallengeActions = (store) => { disableLockdown() { store.dispatch({ type: CHALLENGE_TYPES.DISABLE_LOCKDOWN }); }, - updateHistoryOptions(canGoBack, canGoForward) { + updateHistoryOptions(canGoBack : boolean, canGoForward : boolean) { store.dispatch({ type: CHALLENGE_TYPES.UPDATE_HISTORY_OPTIONS, canGoBack, canGoForward }); }, - addHistoryRecord(stepIndex, editorState) { + addHistoryRecord(stepIndex : number, editorState : any) { store.dispatch({ type: CHALLENGE_TYPES.ADD_HISTORY_RECORD, stepIndex, editorState }); }, completeChallenge() { store.dispatch({ type: CHALLENGE_TYPES.COMPLETE_CHALLENGE }); }, - updateBeacon(beacon) { + updateBeacon(beacon : any) { store.dispatch({ type: CHALLENGE_TYPES.UPDATE_BEACON, beacon }); }, - updateTooltips(tooltips) { + updateTooltips(tooltips : any[]) { store.dispatch({ type: CHALLENGE_TYPES.UPDATE_TOOLTIPS, tooltips }); }, historyBack() { diff --git a/src/app/lib/actions/editor.js b/src/app/lib/actions/editor.ts similarity index 86% rename from src/app/lib/actions/editor.js rename to src/app/lib/actions/editor.ts index fb7aff589..7eaf8faf1 100644 --- a/src/app/lib/actions/editor.js +++ b/src/app/lib/actions/editor.ts @@ -1,4 +1,5 @@ import Store from '../store.js'; +import FlowDown from 'flow-down/flow-down.js'; const CONSTANTS = [ 'SET_TOOLBOX', @@ -9,7 +10,7 @@ const CONSTANTS = [ ]; const EDITOR_TYPES = Store.types(CONSTANTS); -const EditorActions = (store) => { +const EditorActions = (store : FlowDown.Store) => { store.addMutator(function modeActions(action) { switch (action.type) { case EDITOR_TYPES.LOAD_SOURCE: { @@ -43,16 +44,16 @@ const EditorActions = (store) => { }); return { - loadSource(source) { + loadSource(source : string) { store.dispatch({ type: EDITOR_TYPES.LOAD_SOURCE, source }); }, - setToolbox(toolbox) { + setToolbox(toolbox : any) { store.dispatch({ type: EDITOR_TYPES.SET_TOOLBOX, toolbox }); }, - setFlyoutMode(isFlyoutMode) { + setFlyoutMode(isFlyoutMode : boolean) { store.dispatch({ type: EDITOR_TYPES.SET_FLYOUT_MODE, isFlyoutMode }); }, - editBackground(state) { + editBackground(state : boolean) { store.dispatch({ type: EDITOR_TYPES.EDIT_BACKGROUND, state }); }, }; diff --git a/src/app/lib/actions/mode.js b/src/app/lib/actions/mode.js deleted file mode 100644 index c1005fefd..000000000 --- a/src/app/lib/actions/mode.js +++ /dev/null @@ -1,29 +0,0 @@ -import Store from '../store.js'; - -const CONSTANTS = ['UPDATE_MODE']; -const MODE_TYPES = Store.types(CONSTANTS); - -const ModeActions = (store) => { - store.addMutator(function modeActions(action) { - switch (action.type) { - case MODE_TYPES.UPDATE_MODE: { - this.set('state.mode', action.mode); - break; - } - default: { - break; - } - } - }); - - return { - updateMode(mode) { - store.dispatch({ - type: MODE_TYPES.UPDATE_MODE, - mode, - }); - }, - }; -}; - -export default ModeActions; diff --git a/src/app/lib/actions/parts.js b/src/app/lib/actions/parts.ts similarity index 82% rename from src/app/lib/actions/parts.js rename to src/app/lib/actions/parts.ts index e793e0da1..a5e3081d1 100644 --- a/src/app/lib/actions/parts.js +++ b/src/app/lib/actions/parts.ts @@ -1,4 +1,5 @@ import Store from '../store.js'; +import FlowDown from 'flow-down/flow-down.js'; const CONSTANTS = [ 'UPDATE_PART_LIST', @@ -10,18 +11,18 @@ const CONSTANTS = [ ]; const PARTS_TYPES = Store.types(CONSTANTS); -const PartsActions = (store) => { +const PartsActions = (store : FlowDown.Store) => { store.addMutator(function partsActions(action) { switch (action.type) { case PARTS_TYPES.UPDATE_PART_LIST: { const mode = this.get('state.mode'); - const partsMap = action.parts.reduce((acc, part) => { + const partsMap = action.parts.reduce((acc : any, part : any) => { acc[part.type] = part; return acc; }, {}); this.set('state.partsMap', partsMap); if (mode) { - const parts = action.parts.filter(part => mode.parts.indexOf(part.type) !== -1); + const parts = action.parts.filter((part : any) => mode.parts.indexOf(part.type) !== -1); this.set('state.parts', parts); } break; @@ -61,22 +62,22 @@ const PartsActions = (store) => { }); return { - updatePartsList(parts) { + updatePartsList(parts : any[]) { store.dispatch({ type: PARTS_TYPES.UPDATE_PART_LIST, parts }); }, - addPart(part) { + addPart(part : any) { store.dispatch({ type: PARTS_TYPES.ADD_PART, part }); }, - removePart(part) { + removePart(part : any) { store.dispatch({ type: PARTS_TYPES.REMOVE_PART, part }); }, - loadAddedParts(part) { + loadAddedParts(part : any) { store.dispatch({ type: PARTS_TYPES.LOAD_ADDED_PARTS, part }); }, - select(index) { + select(index : number) { store.dispatch({ type: PARTS_TYPES.SELECT, index }); }, - updatePart(property, value) { + updatePart(property : string, value : any) { store.dispatch({ type: PARTS_TYPES.UPDATE, property, value }); }, }; diff --git a/src/app/lib/app-modules/global.js b/src/app/lib/app-modules/global.ts similarity index 87% rename from src/app/lib/app-modules/global.js rename to src/app/lib/app-modules/global.ts index 7c3be828f..627144aeb 100644 --- a/src/app/lib/app-modules/global.js +++ b/src/app/lib/app-modules/global.ts @@ -1,7 +1,8 @@ import { AppModule } from './app-module.js'; export class GlobalModule extends AppModule { - constructor(output) { + private _listeners : { [K : string] : Function[] } = {}; + constructor(output : any) { super(output); this.addMethod('when', '_when'); this.addMethod('emit', '_emit'); @@ -13,12 +14,12 @@ export class GlobalModule extends AppModule { static get id() { return 'global'; } - _when(name, callback) { + _when(name : string, callback : Function) { this._listeners[name] = this._listeners[name] || []; this._listeners[name].push(callback); } - _emit(name, data) { + _emit(name : string, data? : any) { const listeners = this._listeners[name]; if (!listeners || !Array.isArray(listeners)) { return; diff --git a/src/app/lib/app-modules/index.js b/src/app/lib/app-modules/index.ts similarity index 52% rename from src/app/lib/app-modules/index.js rename to src/app/lib/app-modules/index.ts index 08ab4d97b..2db61ac22 100644 --- a/src/app/lib/app-modules/index.js +++ b/src/app/lib/app-modules/index.ts @@ -1,7 +1,15 @@ import AppModules from './app-modules.js'; +import AppModule from './app-module.js'; + +type IAppModuleType = Type & { + id? : string; +} class AppModulesLoader { - constructor(output, modules) { + private output : any; + public appModules : AppModules; + private modules : IAppModuleType[]; + constructor(output : any, modules : IAppModuleType[]) { this.output = output; this.appModules = new AppModules(output); this.modules = modules; @@ -9,12 +17,13 @@ class AppModulesLoader { start() { this.appModules.init(this.output.config); this.modules.forEach((Mod) => { - this.appModules.define(Mod.id, Mod); + if (Mod.id) { + this.appModules.define(Mod.id, Mod); + } }); } dispose() { this.appModules.dispose(); - this.modules = null; } } diff --git a/src/app/lib/app-modules/loop.js b/src/app/lib/app-modules/loop.ts similarity index 80% rename from src/app/lib/app-modules/loop.js rename to src/app/lib/app-modules/loop.ts index b337d18a0..2c21c7588 100644 --- a/src/app/lib/app-modules/loop.js +++ b/src/app/lib/app-modules/loop.ts @@ -1,8 +1,9 @@ import { AppModule } from './app-module.js'; export class LoopModule extends AppModule { - constructor() { - super(); + private intervals : number[]; + constructor(output : any) { + super(output); this.intervals = []; this.addMethod('forever', '_forever'); @@ -12,7 +13,7 @@ export class LoopModule extends AppModule { static get id() { return 'loop'; } - _forever(callback) { + _forever(callback : Function) { // push the next tick to the end of the events queue const id = setInterval(callback, 10); this.intervals.push(id); diff --git a/src/app/lib/app-modules/loops.js b/src/app/lib/app-modules/loops.ts similarity index 100% rename from src/app/lib/app-modules/loops.js rename to src/app/lib/app-modules/loops.ts diff --git a/src/app/lib/app-modules/math.js b/src/app/lib/app-modules/math.ts similarity index 83% rename from src/app/lib/app-modules/math.js rename to src/app/lib/app-modules/math.ts index 944881750..ac5293f45 100644 --- a/src/app/lib/app-modules/math.js +++ b/src/app/lib/app-modules/math.ts @@ -1,15 +1,15 @@ import { AppModule } from './app-module.js'; export class MathModule extends AppModule { - constructor() { - super(); + constructor(output : any) { + super(output); this.addMethod('random', '_random'); this.addMethod('sign', '_sign'); this.addMethod('lerp', '_lerp'); } static get id() { return 'math'; } - _sign(x) { + _sign(x : number) { x = +x; // convert to a number if (x === 0 || Number.isNaN(x)) { return x; @@ -18,7 +18,7 @@ export class MathModule extends AppModule { } /* This generator is inclusive the ranges [min, max] */ - _random(min, max) { + _random(min : number, max : number) { const swap = +min; min = +min; max = +max; @@ -32,7 +32,7 @@ export class MathModule extends AppModule { } /* This generator is inclusive the ranges [min, max] */ - _lerp(from, to, percent) { + _lerp(from : number, to : number, percent : number) { const span = to - from; percent = Math.max(0, Math.min(percent, 100)); diff --git a/src/app/lib/app-modules/motion.js b/src/app/lib/app-modules/motion.js deleted file mode 100644 index c5096dc31..000000000 --- a/src/app/lib/app-modules/motion.js +++ /dev/null @@ -1,7 +0,0 @@ -import { DongleModule } from './dongle.js'; - -export class MotionModule extends DongleModule { - static get id() { return 'motionSensor'; } -} - -export default MotionModule; diff --git a/src/app/lib/editor/dialogs/base.js b/src/app/lib/editor/dialogs/base.js index d2564e971..48d490816 100644 --- a/src/app/lib/editor/dialogs/base.js +++ b/src/app/lib/editor/dialogs/base.js @@ -3,7 +3,7 @@ import EventEmitter from '../../util/event-emitter.js'; export class Base extends EventEmitter { constructor(opts = {}) { super(); - this.fitInto = opts.fitInto || window; + this.fitInto = opts.fitInto || document.body; this.overlayInto = opts.overlayInto; this.root = this.createDom(opts); } @@ -26,6 +26,9 @@ export class Base extends EventEmitter { if (!backdropElement) { return; } + if (!this.overlayInto) { + return; + } const rect = this.overlayInto.getBoundingClientRect(); backdropElement.style.top = `${rect.top}px`; backdropElement.style.left = `${rect.left}px`; @@ -42,7 +45,9 @@ export class Base extends EventEmitter { backdropElement.style.width = ''; backdropElement.style.height = ''; } - createDom(opts) { return null; } + createDom() { + return document.createElement('div'); + } createButton(label) { const button = document.createElement('button'); button.innerText = label; @@ -50,7 +55,9 @@ export class Base extends EventEmitter { return button; } dispose() { - this.root.parentNode.removeChild(this.root); + if (this.root.parentNode) { + this.root.parentNode.removeChild(this.root); + } } } diff --git a/src/app/lib/editor/plugin.ts b/src/app/lib/editor/plugin.ts index d7b1c064a..6e4951e07 100644 --- a/src/app/lib/editor/plugin.ts +++ b/src/app/lib/editor/plugin.ts @@ -1,8 +1,10 @@ /* eslint class-methods-use-this: "off" */ import EventEmitter from '../util/event-emitter.js'; +export type PluginLifecycleStep = 'onInstall'|'onInject'|'onDispose'|'onImport'|'onCreationImport'|'onExport'|'onCreationExport'; + export class Plugin extends EventEmitter { - onInstall() {} + onInstall(editorOrOutput : any) {} onInject() {} onDispose() {} onImport() {} diff --git a/src/app/lib/editor/plugin/receiver.js b/src/app/lib/editor/plugin/receiver.js deleted file mode 100644 index dc7566bb7..000000000 --- a/src/app/lib/editor/plugin/receiver.js +++ /dev/null @@ -1,25 +0,0 @@ -import EventEmitter from '../../util/event-emitter.js'; - -export class PluginReceiver extends EventEmitter { - constructor(...args) { - super(...args); - this.plugins = []; - } - addPlugin(plugin) { - this.plugins.push(plugin); - } - runPluginTask(taskName, ...args) { - this.plugins.forEach(plugin => plugin[taskName](...args)); - } - runPluginChainTask(taskName, ...args) { - return this.plugins.reduce((p, plugin) => { - if (p instanceof Promise) { - return p.then(() => plugin[taskName](...args)); - } - return plugin[taskName](...args); - }, Promise.resolve()); - } -} - - -export default PluginReceiver; diff --git a/src/app/lib/editor/plugin/receiver.ts b/src/app/lib/editor/plugin/receiver.ts new file mode 100644 index 000000000..c0ffb6eb1 --- /dev/null +++ b/src/app/lib/editor/plugin/receiver.ts @@ -0,0 +1,23 @@ +import EventEmitter from '../../util/event-emitter.js'; +import Plugin, { PluginLifecycleStep } from '../plugin.js'; + +export class PluginReceiver extends EventEmitter { + private plugins : Plugin[] = []; + addPlugin(plugin : Plugin) { + this.plugins.push(plugin); + } + runPluginTask(taskName : PluginLifecycleStep, ...args : any[]) { + this.plugins.forEach(plugin => (plugin[taskName] as any)(...args)); + } + runPluginChainTask(taskName : PluginLifecycleStep, ...args : any[]) { + return this.plugins.reduce((p, plugin) => { + if (p instanceof Promise) { + return p.then(() => (plugin[taskName] as any)(...args)); + } + return (plugin[taskName] as any)(...args); + }, Promise.resolve()); + } +} + + +export default PluginReceiver; diff --git a/src/app/lib/editor/runner.js b/src/app/lib/editor/runner.ts similarity index 78% rename from src/app/lib/editor/runner.js rename to src/app/lib/editor/runner.ts index 3fb23169d..0f6a53409 100644 --- a/src/app/lib/editor/runner.js +++ b/src/app/lib/editor/runner.ts @@ -1,19 +1,23 @@ import VM from '../vm.js'; import AppModulesLoader from '../app-modules/index.js'; import { Plugin } from './plugin.js'; +import AppModule from '../app-modules/app-module.js'; export class Runner extends Plugin { + private modules : Type[] = []; + private output? : any; + private appModulesLoader : AppModulesLoader|null = null; + private vm : VM|null = null; constructor() { super(); - this.modules = []; this._onRunningStateChange = this._onRunningStateChange.bind(this); } - addModule(mod) { + addModule(mod : AppModule) { const mods = Array.isArray(mod) ? mod : [mod]; mods.forEach(m => this.modules.push(m)); this._updateModules(); } - onInstall(output) { + onInstall(output : any) { this.output = output; this._updateModules(); this.output.on('running-state-changed', this._onRunningStateChange); @@ -39,6 +43,9 @@ export class Runner extends Plugin { } _onRunningStateChange() { const running = this.output.getRunningState(); + if (!this.appModulesLoader) { + return; + } const { appModules } = this.appModulesLoader; if (!running) { appModules.stop(); @@ -55,17 +62,22 @@ export class Runner extends Plugin { this.vm.runInContext(appCode); appModules.afterRun(); } - instrumentize(method) { + instrumentize(method : string) { + if (!this.appModulesLoader) { + return; + } const { appModules } = this.appModulesLoader; return appModules.instrumentize(method); } dispose() { - this.appModulesLoader.dispose(); + if (this.appModulesLoader) { + this.appModulesLoader.dispose(); + } if (this.vm) { this.vm.dispose(); this.vm = null; } - this.modules = null; + this.modules.length = 0; this.appModulesLoader = null; } } diff --git a/src/app/lib/i18n/index.js b/src/app/lib/i18n/index.js deleted file mode 100644 index d924c6de1..000000000 --- a/src/app/lib/i18n/index.js +++ /dev/null @@ -1,73 +0,0 @@ -const messages = {}; -const supportedLanguages = ['en-US', 'es-AR']; - -let lang = window.navigator.languages ? window.navigator.languages[0] : null; -lang = lang || window.navigator.language - || window.navigator.browserLanguage - || window.navigator.userLanguage; -if (supportedLanguages.indexOf(lang) === -1) { - lang = 'en-US'; -} - -function addBlocklyMsg(m) { - if ('Blockly' in window) { - Object.assign(window.Blockly.Msg, m); - } else { - window.CustomBlocklyMsg = window.CustomBlocklyMsg || {}; - Object.assign(window.CustomBlocklyMsg, m); - } -} - -function loadJSON(url) { - return fetch(url) - .then(r => r.json()); -} - -export function localize(key, fallback = '') { - return messages[key] || fallback; -} - -export function addMessage(key, message) { - messages[key] = message; -} - - -export function load(url) { - return loadJSON(url) - .then((m) => { - Object.assign(messages, m); - return m; - }); -} - -export function getLang() { - return lang; -} - -// Legacy I18n support -export function getMessages() { - return messages; -} - -export const I18nMixin = base => class extends base { - localize(...args) { - return localize(...args); - } -}; - -export function loadBlocklyMsg(url) { - return load(url) - .then((m) => { - addBlocklyMsg(m); - return m; - }); -} - -export default { - localize, - addMessage, - load, - getLang, - getMessages, - loadBlocklyMsg, -}; diff --git a/src/app/lib/i18n/index.ts b/src/app/lib/i18n/index.ts new file mode 100644 index 000000000..c2c8ab188 --- /dev/null +++ b/src/app/lib/i18n/index.ts @@ -0,0 +1,82 @@ +interface IMessageStore { + [K : string] : string; +} + +const messages : IMessageStore = {}; +const supportedLanguages = ['en-US', 'es-AR']; + +let lang = window.navigator.languages ? window.navigator.languages[0] : null; +lang = lang || (window as any).navigator.language + || (window as any).navigator.browserLanguage + || (window as any).navigator.userLanguage; + +if (!lang || supportedLanguages.indexOf(lang) === -1) { + lang = 'en-US'; +} + +function addBlocklyMsg(m : IMessageStore) { + if ('Blockly' in window) { + Object.assign((window as any).Blockly.Msg, m); + } else { + (window as any).CustomBlocklyMsg = (window as any).CustomBlocklyMsg || {}; + Object.assign((window as any).CustomBlocklyMsg, m); + } +} + +function loadJSON(url : string) { + return fetch(url) + .then(r => r.json()); +} + +export function localize(key : string, fallback = '') { + return messages[key] || fallback; +} + +export function addMessage(key : string, message : string) { + messages[key] = message; +} + + +export function load(url : string) { + return loadJSON(url) + .then((m) => { + Object.assign(messages, m); + return m; + }); +} + +export function getLang() { + return lang; +} + +// Legacy I18n support +export function getMessages() { + return messages; +} + +declare type Constructor = { + new(...args: any[]) : T; +} + +export const I18nMixin = >(base : B) => class extends base { + localize(key : string, fallback? : string) { + return localize(key, fallback); + } +}; + +export function loadBlocklyMsg(url : string) { + return load(url) + .then((m) => { + addBlocklyMsg(m); + return m; + }); +} + +export default { + localize, + addMessage, + load, + getLang, + getMessages, + loadBlocklyMsg, +}; diff --git a/src/app/lib/icon/index.js b/src/app/lib/icon/index.ts similarity index 55% rename from src/app/lib/icon/index.js rename to src/app/lib/icon/index.ts index bcbcfa44d..7e51ebce0 100644 --- a/src/app/lib/icon/index.js +++ b/src/app/lib/icon/index.ts @@ -1,26 +1,26 @@ -function toSrc(data) { +function toSrc(data : string) : string { return `data:image/svg+xml;base64,${btoa(data)}`; } -function toTemplate(string) { +function toTemplate(string : string) : HTMLTemplateElement { const template = document.createElement('template'); template.innerHTML = string; return template; } -function litteral(strings, ...values) { +function litteral(strings : string[], ...values : any[]) { return values.reduce((acc, v, idx) => `${acc}${v}${strings[idx + 1]}`, strings[0]).trim(); } -export const svg = (strings, ...values) => toTemplate(litteral(strings, ...values)); +export const svg = (strings : string[], ...values : any[]) => toTemplate(litteral(strings, ...values)); -export const base64 = (strings, ...values) => { +export const base64 = (strings : string[], ...values : any[]) => { const svgString = litteral(strings, ...values); return toSrc(svgString); }; -export const img = (strings, ...values) => { +export const img = (strings : string[], ...values : any[]) => { const svgString = litteral(strings, ...values); const src = toSrc(svgString); return toTemplate(``); diff --git a/src/app/lib/log/index.js b/src/app/lib/log/index.ts similarity index 78% rename from src/app/lib/log/index.js rename to src/app/lib/log/index.ts index 22f8b1f35..90cd4fecf 100644 --- a/src/app/lib/log/index.js +++ b/src/app/lib/log/index.ts @@ -1,38 +1,39 @@ import { LEVEL, LEVEL_NAMES, LEVEL_STYLES } from './level.js'; class Logger { + public level : number; constructor() { this.level = LEVEL.OFF; } - setLevel(level) { + setLevel(level : number) { this.level = level; } - log(...args) { + log(...args : any[]) { if (this.level > LEVEL.OFF) { console.log(`%c[${LEVEL_NAMES[this.level]}]`, LEVEL_STYLES[this.level], ...args); } } - trace(...args) { + trace(...args : any[]) { if (this.level <= LEVEL.TRACE) { this.log(...args); } } - debug(...args) { + debug(...args : any[]) { if (this.level <= LEVEL.DEBUG) { this.log(...args); } } - info(...args) { + info(...args : any[]) { if (this.level <= LEVEL.INFO) { this.log(...args); } } - warn(...args) { + warn(...args : any[]) { if (this.level <= LEVEL.WARN) { console.warn(...args); } } - error(...args) { + error(...args : any[]) { if (this.level <= LEVEL.ERROR) { console.error(...args); } diff --git a/src/app/lib/log/level.js b/src/app/lib/log/level.ts similarity index 100% rename from src/app/lib/log/level.js rename to src/app/lib/log/level.ts diff --git a/src/app/lib/vm.ts b/src/app/lib/vm.ts index 2c812f0ce..50a7dd611 100644 --- a/src/app/lib/vm.ts +++ b/src/app/lib/vm.ts @@ -31,7 +31,7 @@ declare global { } export interface IVMContext { - [K : string] : string; + [K : string] : any; } class VM {